Commit e0d0abb4 authored by Florence Monna's avatar Florence Monna Committed by Swann Perarnau

[feature] add nrmd-wide support for singularity

The daemon can be configured to launch singularity containers. In such
case, the manifest must contain an image section.

Note that this doesn't support any resource management with singularity,
since that stuff is only available as root. We will add a second
container runtime option to support it later.
parent f73b4415
......@@ -45,6 +45,8 @@ def main(argv=None):
"argo_perf_wrapper": "nrm-perfwrapper",
"argo_nodeos_config": "argo_nodeos_config",
"pmpi_lib": "/usr/lib/libnrm-pmpi.so",
"singularity": "singularity",
"container_runtime": "nodeos",
}
if args.print_defaults:
......@@ -101,6 +103,20 @@ def main(argv=None):
"environment variable.",
default=os.environ.get('ARGO_PERF_WRAPPER',
'nrm-perfwrapper'))
parser.add_argument(
'--singularity',
help="Path to the singularity command. "
"Override default with the SINGULARITY environment variable.",
default=os.environ.get('SINGULARITY', defaults['singularity']))
parser.add_argument(
'--container-runtime',
help="Choice of container runtime. "
"Override default with the ARGO_CONTAINER_RUNTIME "
"environment variable.",
choices=['nodeos', 'singularity'],
default=os.environ.get('ARGO_CONTAINER_RUNTIME',
defaults['container_runtime']))
args = parser.parse_args(remaining_argv)
nrm.daemon.runner(config=args)
return(0)
......
......@@ -321,6 +321,29 @@ class App(SpecField):
return super(App, self).load(data)
class Image(SpecField):
"""Information on the container image to use."""
fields = {"path": spec(unicode, True),
"type": spec(unicode, True),
}
def __init__(self):
"""Create an empty image."""
pass
def load(self, data):
"""Load from json dict."""
ret = super(Image, self).load(data)
if not ret:
return ret
if self.type not in ['sif', 'docker']:
logger.error("Image type not recognized")
return False
return True
class ImageManifest(SpecField):
"""Represent an ACI Image Manifest."""
......@@ -329,6 +352,7 @@ class ImageManifest(SpecField):
"acVersion": spec(unicode, True),
"name": spec(unicode, True),
"app": spec(App, True),
"image": spec(Image, False),
}
def __init__(self):
......
......@@ -13,7 +13,7 @@ from __future__ import print_function
from aci import ImageManifest
from collections import namedtuple
import logging
from subprograms import ChrtClient, NodeOSClient, resources
from subprograms import ChrtClient, NodeOSClient, resources, SingularityClient
import operator
logger = logging.getLogger('nrm')
......@@ -250,6 +250,55 @@ class NodeOSRuntime(ContainerRuntime):
self.client.delete(container_uuid, kill)
class SingularityRootRuntime(ContainerRuntime):
"""Implements the container runtime interface using the singularity
subprogram."""
def __init__(self, path="argo_singularity_config"):
"""Creates the client for singularity, with an optional custom
path/command."""
self.client = SingularityClient(argo_singularity_config=path)
def create(self, container):
"""Uses the container resource allocation to create a container."""
self.client.oci_start(container.uuid, container.resources)
def execute(self, container_uuid, args, environ):
"""Launches a command in the container."""
return self.client.oci_execute(container_uuid, args, environ)
def delete(self, container_uuid, kill=False):
"""Delete the container."""
self.client.oci_delete(container_uuid, kill)
class SingularityUserRuntime(ContainerRuntime):
"""Implements the container runtime interface using the singularity
subprogram."""
def __init__(self, path="singularity"):
"""Creates the client for singularity, with an optional custom
path/command."""
self.client = SingularityClient(singularity_path=path)
self.containers = dict()
def create(self, container):
"""Uses the container resource allocation to create a container."""
self.containers[container.uuid] = container
def execute(self, container_uuid, args, environ):
"""Launches a command in the container."""
imageinfo = self.containers[container_uuid].manifest.image
# not checking image because singularity supports all types
return self.client.execute(imageinfo.path, args, environ)
def delete(self, container_uuid, kill=False):
"""Delete the container."""
del self.containers[container_uuid]
class DummyRuntime(ContainerRuntime):
"""Implements a dummy runtime that doesn't create any container, but still
......
......@@ -11,7 +11,7 @@
from __future__ import print_function
from applications import ApplicationManager
from containers import ContainerManager, NodeOSRuntime
from containers import ContainerManager, NodeOSRuntime, SingularityUserRuntime
from controller import Controller, PowerActuator
from powerpolicy import PowerPolicyManager
from functools import partial
......@@ -307,8 +307,14 @@ class Daemon(object):
# create managers
self.resource_manager = ResourceManager(hwloc=self.config.hwloc)
container_runtime = \
NodeOSRuntime(self.config.argo_nodeos_config)
container_runtime = None
if self.config.container_runtime == 'nodeos':
container_runtime = \
NodeOSRuntime(path=self.config.argo_nodeos_config)
elif self.config.container_runtime == 'singularity':
container_runtime = \
SingularityUserRuntime(self.config.singularity)
assert(container_runtime is not None)
self.container_manager = ContainerManager(
container_runtime,
self.resource_manager,
......
......@@ -141,6 +141,28 @@ class NodeOSClient(object):
cwd=environ['PWD'])
class SingularityClient(object):
"""Client to singularity."""
def __init__(self, singularity_path="singularity"):
"""Load client configuration."""
self.prefix = singularity_path
def execute(self, container_image, argv, environ):
"""Execute argv inside container.
singularity exec --bind ... container.sif <command>"""
args = [self.prefix] # singularity
args.extend(['exec', '--bind', '/tmp/nrm-downstream-event',
container_image])
args.extend(argv)
return process.Subprocess(args, env=environ,
stdout=process.Subprocess.STREAM,
stderr=process.Subprocess.STREAM,
close_fds=True)
class ChrtClient(object):
"""Client to chrt command line wrapper."""
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment