コード例 #1
0
ファイル: __init__.py プロジェクト: anbangr/trusted-juju
 def _destroy_containers(self):
     container_map = yield get_containers(self._qualified_name)
     for container_name in container_map:
         container = LXCContainer(container_name, None, None, None)
         if container_map[container.container_name]:
             yield container.stop()
         yield container.destroy()
コード例 #2
0
ファイル: __init__.py プロジェクト: mcclurmc/juju
 def _destroy_containers(self):
     container_map = yield get_containers(self._qualified_name)
     for container_name in container_map:
         container = LXCContainer(container_name, None, None, None)
         if container_map[container.container_name]:
             yield container.stop()
         yield container.destroy()
コード例 #3
0
ファイル: unit.py プロジェクト: mcclurmc/juju
    def __init__(self, unit_name, juju_home):
        super(UnitContainerDeployment, self).__init__(unit_name, juju_home)

        self._unit_namespace = os.environ.get("JUJU_UNIT_NS")
        self._juju_origin = os.environ.get("JUJU_ORIGIN")
        assert self._unit_namespace is not None, "Required unit ns not found"

        self.pid_file = None
        self.container = LXCContainer(self.container_name, None, None, None)
コード例 #4
0
ファイル: test_lxc.py プロジェクト: anbangr/trusted-juju
    def test_container_clone(self):
        self.addCleanup(self.clean_container, DEFAULT_CONTAINER)
        self.addCleanup(self.clean_container, DEFAULT_CONTAINER + "_child")

        master_container = LXCContainer(DEFAULT_CONTAINER,
                                        origin="ppa",
                                        public_key="dsa...",
                                        series="oneiric")

        # verify that we cannot clone an unconstructed container
        failure = master_container.clone("test_lxc_fail")
        yield self.assertFailure(failure, LXCError)

        yield master_container.create()

        # Clone a child container from the template
        child_name = DEFAULT_CONTAINER + "_child"
        c = yield master_container.clone(child_name)

        self.assertEqual(c.container_name, child_name)

        running = yield c.is_running()
        self.assertFalse(running)
        yield c.run()

        running = yield c.is_running()
        self.assertTrue(running)

        output = _lxc_ls()
        self.assertIn(DEFAULT_CONTAINER, output)

        self.verify_container(c, "dsa...", "oneiric", "ppa")

        # verify that we are in containers
        containers = yield get_containers(None)
        self.assertEqual(containers[child_name], True)

        # tear it down
        yield c.destroy()
        running = yield c.is_running()
        self.assertFalse(running)

        containers = yield get_containers(None)
        self.assertNotIn(child_name, containers)

        # and its gone
        output = _lxc_ls()
        self.assertNotIn(child_name, output)

        yield master_container.destroy()
コード例 #5
0
ファイル: unit.py プロジェクト: mcclurmc/juju
    def __init__(self, unit_name, juju_home):
        super(UnitContainerDeployment, self).__init__(unit_name, juju_home)

        self._unit_namespace = os.environ.get("JUJU_UNIT_NS")
        self._juju_origin = os.environ.get("JUJU_ORIGIN")
        assert self._unit_namespace is not None, "Required unit ns not found"

        self.pid_file = None
        self.container = LXCContainer(self.container_name, None, None, None)
コード例 #6
0
ファイル: unit.py プロジェクト: mcclurmc/juju
    def _get_master_template(self, machine_id, zookeeper_hosts, public_key):
        container_template_name = "%s-%s-template" % (
            self._unit_namespace, machine_id)

        master_template = LXCContainer(
            container_template_name, origin=self._juju_origin,
            public_key=public_key)

        # Debug log for the customize script, customize is only run on master.
        customize_log_path = os.path.join(
            self.juju_home, "units", "master-customize.log")
        master_template.customize_log = customize_log_path

        if not master_template.is_constructed():
            log.debug("Creating master container...")
            yield master_template.create()
            log.debug("Created master container %s" % container_template_name)

        # it wasn't constructed and we couldn't construct it
        if not master_template.is_constructed():
            raise LXCError("Unable to create master container")

        returnValue(master_template)
コード例 #7
0
ファイル: unit.py プロジェクト: anbangr/trusted-juju
    def __init__(self, unit_name, juju_home):
        self.unit_name = unit_name
        self.juju_home = juju_home
        self.unit_path_name = unit_name.replace("/", "-")

        self._juju_origin = os.environ.get("JUJU_ORIGIN")
        self._juju_series = os.environ.get("JUJU_SERIES")
        assert self._juju_series is not None, "Required juju series not found"
        self._unit_namespace = os.environ.get("JUJU_UNIT_NS")
        assert self._unit_namespace is not None, "Required unit ns not found"
        self.container_name = "%s-%s" % (
            self._unit_namespace, self.unit_path_name)

        self.container = LXCContainer(self.container_name, None, None, None)
        self.directory = None
コード例 #8
0
ファイル: test_unit_deployment.py プロジェクト: mcclurmc/juju
    def test_get_container(self):
        rootfs = self.makeDir()
        container = LXCContainer(self.unit_name, None, None, None)

        mock_deploy = self.mocker.patch(self.unit_deploy)
        mock_deploy._get_master_template(ANY, ANY, ANY)
        self.mocker.result(container)

        mock_container = self.mocker.patch(container)
        mock_container.clone(ANY)
        self.mocker.result(container)

        self.mocker.replay()

        container, rootfs = yield self.unit_deploy._get_container(
            "0", "127.0.0.1:2181", None, "dsa...")

        output = self.output.getvalue()
        self.assertIn("Container created for %s" % self.unit_deploy.unit_name,
                      output)
コード例 #9
0
ファイル: test_lxc.py プロジェクト: mcclurmc/juju
    def test_container_clone(self):
        self.addCleanup(self.clean_container, DEFAULT_CONTAINER)
        self.addCleanup(self.clean_container, DEFAULT_CONTAINER + "_child")

        master_container = LXCContainer(DEFAULT_CONTAINER,
                                        origin="ppa",
                                        public_key="dsa...")

        # verify that we cannot clone an unconstructed container
        failure = master_container.clone("test_lxc_fail")
        yield self.assertFailure(failure, LXCError)

        yield master_container.create()

        # Clone a child container from the template
        child_name = DEFAULT_CONTAINER + "_child"
        c = yield master_container.clone(child_name)

        self.assertEqual(c.container_name, child_name)

        running = yield c.is_running()
        self.assertFalse(running)
        yield c.run()

        running = yield c.is_running()
        self.assertTrue(running)

        output = _lxc_ls()
        self.assertIn(DEFAULT_CONTAINER, output)

        self.verify_container(c, "dsa...", "ppa")

        # verify that we are in containers
        containers = yield get_containers(None)
        self.assertEqual(containers[child_name], True)

        # tear it down
        yield c.destroy()
        running = yield c.is_running()
        self.assertFalse(running)

        containers = yield get_containers(None)
        self.assertNotIn(child_name, containers)

        # and its gone
        output = _lxc_ls()
        self.assertNotIn(child_name, output)

        yield master_container.destroy()
コード例 #10
0
ファイル: unit.py プロジェクト: mcclurmc/juju
    def _get_master_template(self, machine_id, zookeeper_hosts, public_key):
        container_template_name = "%s-%s-template" % (self._unit_namespace,
                                                      machine_id)

        master_template = LXCContainer(container_template_name,
                                       origin=self._juju_origin,
                                       public_key=public_key)

        # Debug log for the customize script, customize is only run on master.
        customize_log_path = os.path.join(self.juju_home, "units",
                                          "master-customize.log")
        master_template.customize_log = customize_log_path

        if not master_template.is_constructed():
            log.debug("Creating master container...")
            yield master_template.create()
            log.debug("Created master container %s" % container_template_name)

        # it wasn't constructed and we couldn't construct it
        if not master_template.is_constructed():
            raise LXCError("Unable to create master container")

        returnValue(master_template)
コード例 #11
0
ファイル: test_unit_deployment.py プロジェクト: mcclurmc/juju
    def test_start(self):
        container = LXCContainer(self.unit_name, None, None, None)
        rootfs = self.makeDir()
        env = dict(os.environ)
        env["JUJU_PUBLIC_KEY"] = "dsa ..."
        self.change_environment(**env)

        mock_deploy = self.mocker.patch(self.unit_deploy)
        # this minimally validates that we are also called with the
        # expect public key
        mock_deploy._get_container(ANY, ANY, ANY, env["JUJU_PUBLIC_KEY"])
        self.mocker.result((container, rootfs))

        mock_container = self.mocker.patch(container)
        mock_container.run()

        self.mocker.replay()

        self.unit_deploy.directory = rootfs
        os.makedirs(os.path.join(rootfs, "etc", "init"))

        yield self.unit_deploy.start("0", "127.0.1.1:2181", self.bundle)

        # Verify the upstart job
        upstart_agent_name = "%s-unit-agent.conf" % (self.unit_name.replace(
            "/", "-"))
        content = open(os.path.join(rootfs, "etc", "init",
                                    upstart_agent_name)).read()
        job = self.get_normalized(content)
        self.assertIn('JUJU_ZOOKEEPER="127.0.1.1:2181"', job)
        self.assertIn('JUJU_MACHINE_ID="0"', job)
        self.assertIn('JUJU_UNIT_NAME="riak/0"', job)

        # Verify the symlink exists
        self.assertTrue(
            os.path.lexists(
                os.path.join(self.unit_deploy.juju_home, "units",
                             self.unit_deploy.unit_path_name, "unit.log")))

        # Verify the charm is on disk.
        self.assertTrue(
            os.path.exists(
                os.path.join(self.unit_deploy.directory, "var", "lib", "juju",
                             "units", self.unit_deploy.unit_path_name, "charm",
                             "metadata.yaml")))

        # Verify the directory structure in the unit.
        self.assertTrue(
            os.path.exists(
                os.path.join(self.unit_deploy.directory, "var", "lib", "juju",
                             "state")))
        self.assertTrue(
            os.path.exists(
                os.path.join(self.unit_deploy.directory, "var", "log",
                             "juju")))

        # Verify log output
        output = self.output.getvalue()
        self.assertIn("Charm extracted into container", output)
        self.assertIn("Started container for %s" % self.unit_deploy.unit_name,
                      output)
コード例 #12
0
ファイル: unit.py プロジェクト: mcclurmc/juju
class UnitContainerDeployment(UnitMachineDeployment):
    """Deploy a service unit in a container.

    Units deployed in a container have strong isolation between
    others units deployed in a container on the same machine.

    From the perspective of the service unit, the container deployment
    should be indistinguishable from a machine deployment.

    Note, strong isolation properties are still fairly trivial
    to escape for a user with a root account within the container.
    This is an ongoing development topic for LXC.
    """

    def __init__(self, unit_name, juju_home):
        super(UnitContainerDeployment, self).__init__(unit_name, juju_home)

        self._unit_namespace = os.environ.get("JUJU_UNIT_NS")
        self._juju_origin = os.environ.get("JUJU_ORIGIN")
        assert self._unit_namespace is not None, "Required unit ns not found"

        self.pid_file = None
        self.container = LXCContainer(self.container_name, None, None, None)

    @property
    def container_name(self):
        """Get a qualfied name for the container.

        The units directory for the machine points to a path like::

           /var/lib/juju/units

        In the case of the local provider this directory is qualified
        to allow for multiple users with multiple environments::

          /var/lib/juju/username-envname

        This value is passed to the agent via the JUJU_HOME environment
        variable.

        This function extracts the name qualifier for the container from
        the JUJU_HOME value.
        """
        return "%s-%s" % (self._unit_namespace,
                          self.unit_name.replace("/", "-"))

    def setup_directories(self):
        # Create state directories for unit in the container
        # Move to juju-create script
        units_dir = os.path.join(
            self.directory, "var", "lib", "juju", "units")
        if not os.path.exists(units_dir):
            os.makedirs(units_dir)

        state_dir = os.path.join(
            self.directory, "var", "lib", "juju", "state")
        if not os.path.exists(state_dir):
            os.makedirs(state_dir)

        log_dir = os.path.join(
            self.directory, "var", "log", "juju")
        if not os.path.exists(log_dir):
            os.makedirs(log_dir)

        unit_dir = os.path.join(units_dir, self.unit_path_name)
        if not os.path.exists(unit_dir):
            os.mkdir(unit_dir)

        host_unit_dir = os.path.join(
            self.juju_home, "units", self.unit_path_name)
        if not os.path.exists(host_unit_dir):
            os.makedirs(host_unit_dir)

    @inlineCallbacks
    def _get_master_template(self, machine_id, zookeeper_hosts, public_key):
        container_template_name = "%s-%s-template" % (
            self._unit_namespace, machine_id)

        master_template = LXCContainer(
            container_template_name, origin=self._juju_origin,
            public_key=public_key)

        # Debug log for the customize script, customize is only run on master.
        customize_log_path = os.path.join(
            self.juju_home, "units", "master-customize.log")
        master_template.customize_log = customize_log_path

        if not master_template.is_constructed():
            log.debug("Creating master container...")
            yield master_template.create()
            log.debug("Created master container %s" % container_template_name)

        # it wasn't constructed and we couldn't construct it
        if not master_template.is_constructed():
            raise LXCError("Unable to create master container")

        returnValue(master_template)

    @inlineCallbacks
    def _get_container(self, machine_id, zookeeper_hosts, bundle, public_key):
        master_template = yield self._get_master_template(
            machine_id, zookeeper_hosts, public_key)
        log.info(
            "Creating container %s...", os.path.basename(self.directory))

        container = yield master_template.clone(self.container_name)
        directory = container.rootfs
        log.info("Container created for %s" % self.unit_name)
        returnValue((container, directory))

    @inlineCallbacks
    def start(self, machine_id, zookeeper_hosts, bundle):
        """Start the unit.

        Creates and starts an lxc container for the unit.
        """
        # remove any quoting around the key
        public_key = os.environ.get("JUJU_PUBLIC_KEY", "")
        public_key = public_key.strip("'\"")

        # Build a template container that can be cloned in deploy
        # we leave the loosely initialized self.container in place for
        # the class as thats all we need for methods other than start.
        self.container, self.directory = yield self._get_container(machine_id,
                                                         zookeeper_hosts,
                                                         bundle,
                                                         public_key)
        # Create state directories for unit in the container
        self.setup_directories()

        # Extract the charm bundle
        charm_path = os.path.join(
            self.directory, "var", "lib", "juju", "units",
            self.unit_path_name, "charm")
        bundle.extract_to(charm_path)
        log.debug("Charm extracted into container")

        # Write upstart file for the agent into the container
        upstart_path = os.path.join(
            self.directory, "etc", "init",
            "%s-unit-agent.conf" % self.unit_path_name)
        with open(upstart_path, "w") as fh:
            fh.write(self.get_upstart_unit_job(machine_id, zookeeper_hosts))

        # Create a symlink on the host for easier access to the unit log file
        unit_log_path_host = os.path.join(
            self.juju_home, "units", self.unit_path_name, "unit.log")
        if not os.path.lexists(unit_log_path_host):
            os.symlink(
                os.path.join(self.directory, "var", "log", "juju",
                             "unit-%s.log" % self.unit_path_name),
                unit_log_path_host)

        # Debug log for the container
        container_log_path = os.path.join(
            self.juju_home, "units", self.unit_path_name, "container.log")
        self.container.debug_log = container_log_path

        log.debug("Starting container...")
        yield self.container.run()
        log.info("Started container for %s" % self.unit_name)

    @inlineCallbacks
    def destroy(self):
        """Destroy the unit container.
        """
        log.debug("Destroying container...")
        yield self.container.destroy()
        log.info("Destroyed container for %s" % self.unit_name)

    @inlineCallbacks
    def is_running(self):
        """Is the unit container running.
        """
        # TODO: container running may not imply agent running.  the
        # pid file has the pid from the container, we need a container
        # pid -> host pid mapping to query status from the machine agent.
        # alternatively querying zookeeper for the unit agent presence
        # node.
        if not self.container:
            returnValue(False)
        container_map = yield get_containers(
            prefix=self.container.container_name)
        returnValue(container_map.get(self.container.container_name, False))

    def get_upstart_unit_job(self, machine_id, zookeeper_hosts):
        """Return a string containing the  upstart job to start the unit agent.
        """
        environ = self.get_environment(machine_id, zookeeper_hosts)
        # Keep qualified locations within the container for colo support
        environ["JUJU_HOME"] = "/var/lib/juju"
        environ["UNIT_PATH_NAME"] = self.unit_path_name
        return container_upstart_job_template % environ
コード例 #13
0
ファイル: test_lxc.py プロジェクト: mcclurmc/juju
    def test_lxc_container(self):
        self.addCleanup(self.clean_container, DEFAULT_CONTAINER)
        customize_log = self.makeFile()
        c = LXCContainer(
            DEFAULT_CONTAINER, "dsa...", "ppa", customize_log=customize_log)

        running = yield c.is_running()
        self.assertFalse(running)
        self.assertFalse(c.is_constructed())
        # verify we can't run a non-constructed container
        failure = c.run()
        yield self.assertFailure(failure, LXCError)

        yield c.create()

        self.assertFalse(running)
        self.assertTrue(c.is_constructed())
        yield c.run()

        running = yield c.is_running()
        self.assertTrue(running)
        self.assertTrue(c.is_constructed())

        output = _lxc_ls()
        self.assertIn(DEFAULT_CONTAINER, output)

        # verify we have a path into the container
        self.assertTrue(os.path.exists(c.rootfs))
        self.assertTrue(c.is_constructed())

        self.verify_container(c, "dsa...", "ppa")

        # verify that we are in containers
        containers = yield get_containers(None)
        self.assertEqual(containers[DEFAULT_CONTAINER], True)

        # tear it down
        yield c.destroy()
        running = yield c.is_running()
        self.assertFalse(running)

        containers = yield get_containers(None)
        self.assertNotIn(DEFAULT_CONTAINER, containers)

        # Verify the customize log file.
        self.assertTrue(os.path.exists(customize_log))

        # and its gone
        output = _lxc_ls()
        self.assertNotIn(DEFAULT_CONTAINER, output)
コード例 #14
0
ファイル: unit.py プロジェクト: mcclurmc/juju
class UnitContainerDeployment(UnitMachineDeployment):
    """Deploy a service unit in a container.

    Units deployed in a container have strong isolation between
    others units deployed in a container on the same machine.

    From the perspective of the service unit, the container deployment
    should be indistinguishable from a machine deployment.

    Note, strong isolation properties are still fairly trivial
    to escape for a user with a root account within the container.
    This is an ongoing development topic for LXC.
    """
    def __init__(self, unit_name, juju_home):
        super(UnitContainerDeployment, self).__init__(unit_name, juju_home)

        self._unit_namespace = os.environ.get("JUJU_UNIT_NS")
        self._juju_origin = os.environ.get("JUJU_ORIGIN")
        assert self._unit_namespace is not None, "Required unit ns not found"

        self.pid_file = None
        self.container = LXCContainer(self.container_name, None, None, None)

    @property
    def container_name(self):
        """Get a qualfied name for the container.

        The units directory for the machine points to a path like::

           /var/lib/juju/units

        In the case of the local provider this directory is qualified
        to allow for multiple users with multiple environments::

          /var/lib/juju/username-envname

        This value is passed to the agent via the JUJU_HOME environment
        variable.

        This function extracts the name qualifier for the container from
        the JUJU_HOME value.
        """
        return "%s-%s" % (self._unit_namespace, self.unit_name.replace(
            "/", "-"))

    def setup_directories(self):
        # Create state directories for unit in the container
        # Move to juju-create script
        units_dir = os.path.join(self.directory, "var", "lib", "juju", "units")
        if not os.path.exists(units_dir):
            os.makedirs(units_dir)

        state_dir = os.path.join(self.directory, "var", "lib", "juju", "state")
        if not os.path.exists(state_dir):
            os.makedirs(state_dir)

        log_dir = os.path.join(self.directory, "var", "log", "juju")
        if not os.path.exists(log_dir):
            os.makedirs(log_dir)

        unit_dir = os.path.join(units_dir, self.unit_path_name)
        if not os.path.exists(unit_dir):
            os.mkdir(unit_dir)

        host_unit_dir = os.path.join(self.juju_home, "units",
                                     self.unit_path_name)
        if not os.path.exists(host_unit_dir):
            os.makedirs(host_unit_dir)

    @inlineCallbacks
    def _get_master_template(self, machine_id, zookeeper_hosts, public_key):
        container_template_name = "%s-%s-template" % (self._unit_namespace,
                                                      machine_id)

        master_template = LXCContainer(container_template_name,
                                       origin=self._juju_origin,
                                       public_key=public_key)

        # Debug log for the customize script, customize is only run on master.
        customize_log_path = os.path.join(self.juju_home, "units",
                                          "master-customize.log")
        master_template.customize_log = customize_log_path

        if not master_template.is_constructed():
            log.debug("Creating master container...")
            yield master_template.create()
            log.debug("Created master container %s" % container_template_name)

        # it wasn't constructed and we couldn't construct it
        if not master_template.is_constructed():
            raise LXCError("Unable to create master container")

        returnValue(master_template)

    @inlineCallbacks
    def _get_container(self, machine_id, zookeeper_hosts, bundle, public_key):
        master_template = yield self._get_master_template(
            machine_id, zookeeper_hosts, public_key)
        log.info("Creating container %s...", os.path.basename(self.directory))

        container = yield master_template.clone(self.container_name)
        directory = container.rootfs
        log.info("Container created for %s" % self.unit_name)
        returnValue((container, directory))

    @inlineCallbacks
    def start(self, machine_id, zookeeper_hosts, bundle):
        """Start the unit.

        Creates and starts an lxc container for the unit.
        """
        # remove any quoting around the key
        public_key = os.environ.get("JUJU_PUBLIC_KEY", "")
        public_key = public_key.strip("'\"")

        # Build a template container that can be cloned in deploy
        # we leave the loosely initialized self.container in place for
        # the class as thats all we need for methods other than start.
        self.container, self.directory = yield self._get_container(
            machine_id, zookeeper_hosts, bundle, public_key)
        # Create state directories for unit in the container
        self.setup_directories()

        # Extract the charm bundle
        charm_path = os.path.join(self.directory, "var", "lib", "juju",
                                  "units", self.unit_path_name, "charm")
        bundle.extract_to(charm_path)
        log.debug("Charm extracted into container")

        # Write upstart file for the agent into the container
        upstart_path = os.path.join(self.directory, "etc", "init",
                                    "%s-unit-agent.conf" % self.unit_path_name)
        with open(upstart_path, "w") as fh:
            fh.write(self.get_upstart_unit_job(machine_id, zookeeper_hosts))

        # Create a symlink on the host for easier access to the unit log file
        unit_log_path_host = os.path.join(self.juju_home, "units",
                                          self.unit_path_name, "unit.log")
        if not os.path.lexists(unit_log_path_host):
            os.symlink(
                os.path.join(self.directory, "var", "log", "juju",
                             "unit-%s.log" % self.unit_path_name),
                unit_log_path_host)

        # Debug log for the container
        container_log_path = os.path.join(self.juju_home, "units",
                                          self.unit_path_name, "container.log")
        self.container.debug_log = container_log_path

        log.debug("Starting container...")
        yield self.container.run()
        log.info("Started container for %s" % self.unit_name)

    @inlineCallbacks
    def destroy(self):
        """Destroy the unit container.
        """
        log.debug("Destroying container...")
        yield self.container.destroy()
        log.info("Destroyed container for %s" % self.unit_name)

    @inlineCallbacks
    def is_running(self):
        """Is the unit container running.
        """
        # TODO: container running may not imply agent running.  the
        # pid file has the pid from the container, we need a container
        # pid -> host pid mapping to query status from the machine agent.
        # alternatively querying zookeeper for the unit agent presence
        # node.
        if not self.container:
            returnValue(False)
        container_map = yield get_containers(
            prefix=self.container.container_name)
        returnValue(container_map.get(self.container.container_name, False))

    def get_upstart_unit_job(self, machine_id, zookeeper_hosts):
        """Return a string containing the  upstart job to start the unit agent.
        """
        environ = self.get_environment(machine_id, zookeeper_hosts)
        # Keep qualified locations within the container for colo support
        environ["JUJU_HOME"] = "/var/lib/juju"
        environ["UNIT_PATH_NAME"] = self.unit_path_name
        return container_upstart_job_template % environ
コード例 #15
0
ファイル: unit.py プロジェクト: anbangr/trusted-juju
class UnitContainerDeployment(object):
    """Deploy a service unit in a container.

    Units deployed in a container have strong isolation between
    others units deployed in a container on the same machine.

    From the perspective of the service unit, the container deployment
    should be indistinguishable from a machine deployment.

    Note, strong isolation properties are still fairly trivial
    to escape for a user with a root account within the container.
    This is an ongoing development topic for LXC.
    """

    def __init__(self, unit_name, juju_home):
        self.unit_name = unit_name
        self.juju_home = juju_home
        self.unit_path_name = unit_name.replace("/", "-")

        self._juju_origin = os.environ.get("JUJU_ORIGIN")
        self._juju_series = os.environ.get("JUJU_SERIES")
        assert self._juju_series is not None, "Required juju series not found"
        self._unit_namespace = os.environ.get("JUJU_UNIT_NS")
        assert self._unit_namespace is not None, "Required unit ns not found"
        self.container_name = "%s-%s" % (
            self._unit_namespace, self.unit_path_name)

        self.container = LXCContainer(self.container_name, None, None, None)
        self.directory = None

    def setup_directories(self):
        # Create state directories for unit in the container
        # Move to juju-create script
        base = self.directory
        dirs = ((base, "var", "lib", "juju", "units", self.unit_path_name),
                (base, "var", "lib", "juju", "state"),
                (base, "var", "log", "juju"),
                (self.juju_home, "units", self.unit_path_name))

        for parts in dirs:
            dir_ = os.path.join(*parts)
            if not os.path.exists(dir_):
                os.makedirs(dir_)

    @inlineCallbacks
    def _get_master_template(self, machine_id, public_key):
        container_template_name = "%s-%s-template" % (
            self._unit_namespace, machine_id)

        master_template = LXCContainer(
            container_template_name, origin=self._juju_origin,
            public_key=public_key, series=self._juju_series)

        # Debug log for the customize script, customize is only run on master.
        customize_log_path = os.path.join(
            self.juju_home, "units", "master-customize.log")
        master_template.customize_log = customize_log_path

        if not master_template.is_constructed():
            log.debug("Creating master container...")
            yield master_template.create()
            log.debug("Created master container %s", container_template_name)

        # it wasn't constructed and we couldn't construct it
        if not master_template.is_constructed():
            raise LXCError("Unable to create master container")

        returnValue(master_template)

    @inlineCallbacks
    def _get_container(self, machine_id, bundle, public_key):
        master_template = yield self._get_master_template(
            machine_id, public_key)
        log.info(
            "Creating container %s...", self.unit_path_name)

        container = yield master_template.clone(self.container_name)
        directory = container.rootfs
        log.info("Container created for %s", self.unit_name)
        returnValue((container, directory))

    @inlineCallbacks
    def start(self, machine_id, zookeeper_hosts, bundle):
        """Start the unit.

        Creates and starts an lxc container for the unit.
        """
        # remove any quoting around the key
        public_key = os.environ.get("JUJU_PUBLIC_KEY", "")
        public_key = public_key.strip("'\"")

        # Build a template container that can be cloned in deploy
        # we leave the loosely initialized self.container in place for
        # the class as thats all we need for methods other than start.
        self.container, self.directory = yield self._get_container(
            machine_id, bundle, public_key)

        # Create state directories for unit in the container
        self.setup_directories()

        # Extract the charm bundle
        charm_path = os.path.join(
            self.directory, "var", "lib", "juju", "units",
            self.unit_path_name, "charm")
        bundle.extract_to(charm_path)
        log.debug("Charm extracted into container")

        # Write upstart file for the agent into the container
        service_name = "juju-%s" % self.unit_path_name
        init_dir = os.path.join(self.directory, "etc", "init")
        service = UpstartService(service_name, init_dir=init_dir)
        service.set_description(
            "Juju unit agent for %s" % self.unit_name)
        service.set_environ(_get_environment(
            self.unit_name, "/var/lib/juju", machine_id, zookeeper_hosts))
        service.set_output_path(
            "/var/log/juju/unit-%s-output.log" % self.unit_path_name)
        service.set_command(" ".join((
            "/usr/bin/python",
            "-m", "juju.agents.unit",
            "--nodaemon",
            "--logfile", "/var/log/juju/unit-%s.log" % self.unit_path_name,
            "--session-file",
            "/var/run/juju/unit-%s-agent.zksession" % self.unit_path_name)))
        yield service.install()

        # Create symlinks on the host for easier access to the unit log files
        unit_log_path_host = os.path.join(
            self.juju_home, "units", self.unit_path_name, "unit.log")
        if not os.path.lexists(unit_log_path_host):
            os.symlink(
                os.path.join(self.directory, "var", "log", "juju",
                             "unit-%s.log" % self.unit_path_name),
                unit_log_path_host)
        unit_output_path_host = os.path.join(
            self.juju_home, "units", self.unit_path_name, "output.log")
        if not os.path.lexists(unit_output_path_host):
            os.symlink(
                os.path.join(self.directory, "var", "log", "juju",
                             "unit-%s-output.log" % self.unit_path_name),
                unit_output_path_host)

        # Debug log for the container
        container_log_path = os.path.join(
            self.juju_home, "units", self.unit_path_name, "container.log")
        self.container.debug_log = container_log_path

        log.debug("Starting container...")
        yield self.container.run()
        log.info("Started container for %s", self.unit_name)

    @inlineCallbacks
    def destroy(self):
        """Destroy the unit container."""
        log.debug("Destroying container...")
        yield self.container.destroy()
        log.info("Destroyed container for %s", self.unit_name)

    @inlineCallbacks
    def is_running(self):
        """Is the unit container running?"""
        # TODO: container running may not imply agent running.
        # query zookeeper for the unit agent presence node?
        if not self.container:
            returnValue(False)
        container_map = yield get_containers(
            prefix=self.container.container_name)
        returnValue(container_map.get(self.container.container_name, False))