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

from aci import ImageManifest
4
from collections import namedtuple
5 6
import logging
import os
7
import signal
8
from subprograms import ChrtClient, NodeOSClient, resources
9 10
import sys

11

12 13
Container = namedtuple('Container', ['uuid', 'manifest', 'pid'])

14 15 16 17 18 19

class ContainerManager(object):

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

20
    def __init__(self, rm):
21
        self.containers = dict()
22
        self.pids = dict()
23
        self.logger = logging.getLogger(__name__)
24 25 26
        self.resourcemanager = rm
        self.nodeos = NodeOSClient()
        self.chrt = ChrtClient()
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42

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

        Returns the pid of the container or a negative number for errors."""
        manifestfile = request['manifest']
        command = request['file']
        args = request['args']
        self.logger.info("run: manifest file: %s", manifestfile)
        self.logger.info("run: command:       %s", command)
        self.logger.info("run: args:          %r", args)
        manifest = ImageManifest()
        if not manifest.load(manifestfile):
            self.logger.error("Manifest is invalid")
            return -1

43 44 45 46 47
        # ask the resource manager for resources
        req = resources(int(manifest.app.isolators.container.cpus.value),
                        int(manifest.app.isolators.container.mems.value))
        allocation = self.resourcemanager.schedule(request['uuid'], req)
        self.logger.info("run: allocation: %r", allocation)
48 49 50 51 52 53 54 55 56 57 58 59 60

        # build context to execute
        environ = os.environ
        environ['PATH'] = ("/usr/local/sbin:"
                           "/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")
        environ['AC_APP_NAME'] = manifest.name
        environ['AC_METADATA_URL'] = "localhost"
        environ['container'] = 'argo'
        self.logger.info("run: environ: %r", environ)

        # create container
        container_name = request['uuid']
        self.logger.info("creating container %s", container_name)
61
        self.nodeos.create(container_name, allocation)
62 63 64 65 66 67 68
        self.logger.info("created container %s", container_name)

        newpid = os.fork()
        self.logger.info("forked: new pid: %s", newpid)
        if newpid == 0:
            # move myself to that container
            mypid = os.getpid()
69
            self.nodeos.attach(container_name, mypid)
70 71 72 73
            self.logger.info("child: attached to container %s", container_name)

            # run my command
            if hasattr(manifest.app.isolators, 'scheduler'):
74 75
                sched = manifest.app.isolators.scheduler
                argv = self.chrt.getwrappedcmd(sched)
76
            else:
77
                argv = []
78

79 80 81 82
            argv.append(command)
            argv.extend(args)
            self.logger.debug("execvpe %r", argv)
            os.execvpe(argv[0], argv, environ)
83 84 85
            # should never happen
            sys.exit(1)
        else:
86 87 88
            c = Container(container_name, manifest, newpid)
            self.pids[newpid] = c
            self.containers[container_name] = c
89 90 91 92
            return newpid

    def delete(self, uuid):
        """Delete a container and kill all related processes."""
93 94 95 96
        self.nodeos.delete(uuid, kill=True)
        c = self.containers[uuid]
        del self.containers[uuid]
        del self.pids[c.pid]
97

98 99 100 101 102 103 104 105 106 107
    def kill(self, uuid):
        """Kill all the processes of a container."""
        if uuid in self.containers:
            c = self.containers[uuid]
            self.logger.debug("killing %r:", c)
            try:
                os.kill(c.pid, signal.SIGKILL)
            except OSError:
                pass

108 109 110 111 112
    def list(self):
        """List the containers in the system."""
        fields = ['uuid', 'pid']
        ret = [c._asdict() for c in self.containers.values()]
        return [{k: d[k] for k in fields} for d in ret]