containers.py 7.35 KB
Newer Older
1 2 3
from __future__ import print_function

from aci import ImageManifest
4
from collections import namedtuple
5
import logging
6
from subprograms import ChrtClient, NodeOSClient, resources
7

8
logger = logging.getLogger('nrm')
9
Container = namedtuple('Container', ['uuid', 'manifest', 'resources',
10
                                     'power', 'processes', 'clientids'])
11

12 13 14 15 16 17

class ContainerManager(object):

    """Manages the creation, listing and deletion of containers, using a
    container runtime underneath."""

18 19 20
    def __init__(self, rm,
                 perfwrapper="argo-perf-wrapper",
                 linuxperf="perf",
21 22
                 argo_nodeos_config="argo_nodeos_config",
                 pmpi_lib="/usr/lib/libnrm-pmpi.so"):
23 24 25
        self.linuxperf = linuxperf
        self.perfwrapper = perfwrapper
        self.nodeos = NodeOSClient(argo_nodeos_config=argo_nodeos_config)
26
        self.containers = dict()
27 28 29
        self.pids = dict()
        self.resourcemanager = rm
        self.chrt = ChrtClient()
30
        self.pmpi_lib = pmpi_lib
31 32 33 34 35

    def create(self, request):
        """Create a container according to the request.

        Returns the pid of the container or a negative number for errors."""
36 37 38 39
        container = None
        containerexistsflag = False
        processes = None
        clientids = None
40
        pp = None
41

42 43 44
        manifestfile = request['manifest']
        command = request['file']
        args = request['args']
45
        environ = request['environ']
46
        container_name = request['uuid']
47 48 49
        logger.info("run: manifest file:  %s", manifestfile)
        logger.info("run: command:        %s", command)
        logger.info("run: args:           %r", args)
50
        logger.info("run: container name: %s", container_name)
51 52

        # TODO: Application library to load must be set during configuration
53
        apppreloadlibrary = self.pmpi_lib
54

55 56
        manifest = ImageManifest()
        if not manifest.load(manifestfile):
57
            logger.error("Manifest is invalid")
58
            return None
59

60 61 62 63 64 65
        if hasattr(manifest.app.isolators, 'scheduler'):
            sched = manifest.app.isolators.scheduler
            argv = self.chrt.getwrappedcmd(sched)
        else:
            argv = []

66 67 68
        # Check if container exists else create it
        if container_name in self.containers:
                container = self.containers[container_name]
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
                containerexistsflag = True
                processes = container.processes
                clientids = container.clientids
        else:
            processes = dict()
            clientids = dict()

            # ask the resource manager for resources
            req = resources(int(manifest.app.isolators.container.cpus.value),
                            int(manifest.app.isolators.container.mems.value))
            alloc = self.resourcemanager.schedule(container_name, req)
            logger.info("run: allocation: %r", alloc)

            # create container
            logger.info("creating container %s", container_name)
            self.nodeos.create(container_name, alloc)
            container_resources = dict()
            container_resources['cpus'], container_resources['mems'] = alloc

            # Container power settings
            container_power = dict()
            container_power['profile'] = None
            container_power['policy'] = None
            container_power['damper'] = None
            container_power['slowdown'] = None
            container_power['manager'] = None

            # It would've been better if argo-perf-wrapper wrapped around
            # argo-nodeos-config and not the final command -- that way it would
            # be running outside of the container.  However, because
            # argo-nodeos-config is suid root, perf can't monitor it.
            if hasattr(manifest.app.isolators, 'perfwrapper'):
                manifest_perfwrapper = manifest.app.isolators.perfwrapper
                if hasattr(manifest_perfwrapper, 'enabled'):
                    if manifest_perfwrapper.enabled in ["1", "True"]:
104
                        argv.append(self.perfwrapper)
105 106 107 108 109 110 111 112 113 114 115 116 117

            if hasattr(manifest.app.isolators, 'power'):
                if hasattr(manifest.app.isolators.power, 'enabled'):
                        pp = manifest.app.isolators.power
                        if pp.enabled in ["1", "True"]:
                            if pp.profile in ["1", "True"]:
                                container_power['profile'] = dict()
                                container_power['profile']['start'] = dict()
                                container_power['profile']['end'] = dict()
                            if pp.policy != "NONE":
                                container_power['policy'] = pp.policy
                                container_power['damper'] = pp.damper
                                container_power['slowdown'] = pp.slowdown
118 119

        # build context to execute
120 121
        # environ['PATH'] = ("/usr/local/sbin:"
        #                   "/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")
122
        environ['ARGO_CONTAINER_UUID'] = container_name
123
        environ['PERF'] = self.linuxperf
124 125
        environ['AC_APP_NAME'] = manifest.name
        environ['AC_METADATA_URL'] = "localhost"
126 127 128 129 130 131 132 133
        if (containerexistsflag and container.power['policy'] is not None) or (
                pp is not None and pp.policy != "NONE"):
            environ['LD_PRELOAD'] = apppreloadlibrary
            environ['NRM_TRANSMIT'] = '1'
            if containerexistsflag:
                environ['NRM_DAMPER'] = container.power['damper']
            else:
                environ['NRM_DAMPER'] = pp.damper
134

135 136
        argv.append(command)
        argv.extend(args)
137

138
        # run my command
139 140 141 142 143 144 145 146 147
        process = self.nodeos.execute(container_name, argv, environ)
        processes[process.pid] = process
        clientids[process.pid] = request['clientid']

        if containerexistsflag:
            container.processes[process.pid] = process
            self.pids[process.pid] = container
            logger.info("Created process %s in container %s", process.pid,
                        container_name)
148
        else:
149 150 151 152 153 154 155
            container = Container(container_name, manifest,
                                  container_resources, container_power,
                                  processes, clientids)
            self.pids[process.pid] = container
            self.containers[container_name] = container
            logger.info("Container %s created and running : %r",
                        container_name, container)
156

157
        return process.pid, container
158 159 160

    def delete(self, uuid):
        """Delete a container and kill all related processes."""
161
        self.nodeos.delete(uuid, kill=True)
162
        self.resourcemanager.update(uuid)
163 164
        c = self.containers[uuid]
        del self.containers[uuid]
165
        map(lambda i: self.pids.pop(c.processes[i].pid, None), c.processes)
166

Swann Perarnau's avatar
Swann Perarnau committed
167 168 169 170
    def kill(self, uuid):
        """Kill all the processes of a container."""
        if uuid in self.containers:
            c = self.containers[uuid]
171
            logger.debug("killing %r:", c)
172 173
            for p in c.processes.values():
                try:
174
                    p.proc.terminate()
175 176
                except OSError:
                    logging.error("OS error: could not terminate process.")
Swann Perarnau's avatar
Swann Perarnau committed
177

178 179
    def list(self):
        """List the containers in the system."""
180 181
        return [{'uuid': c.uuid, 'pid': c.processes.keys()}
                for c in self.containers.values()]