[lxc-devel] [nova-lxd/master] Migration and resize refactor

zulcss on Github lxc-bot at linuxcontainers.org
Mon Feb 8 15:26:56 UTC 2016


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 584 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20160208/7060caf6/attachment.bin>
-------------- next part --------------
From b7a1d24422a21c343b8d2b6340bdf255e8fb6fc4 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Sat, 6 Feb 2016 08:32:30 -0500
Subject: [PATCH 01/15] Rename container migration

Rename container_migration.py and test_container_migration.py
for simplicity, readability, and maintenance.

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova_lxd/nova/virt/lxd/container_migrate.py | 113 ----------------------------
 nova_lxd/nova/virt/lxd/driver.py            |   4 +-
 nova_lxd/nova/virt/lxd/migrate.py           | 113 ++++++++++++++++++++++++++++
 nova_lxd/tests/test_container_migration.py  |  73 ------------------
 nova_lxd/tests/test_migrate.py              |  73 ++++++++++++++++++
 5 files changed, 188 insertions(+), 188 deletions(-)
 delete mode 100644 nova_lxd/nova/virt/lxd/container_migrate.py
 create mode 100644 nova_lxd/nova/virt/lxd/migrate.py
 delete mode 100644 nova_lxd/tests/test_container_migration.py
 create mode 100644 nova_lxd/tests/test_migrate.py

diff --git a/nova_lxd/nova/virt/lxd/container_migrate.py b/nova_lxd/nova/virt/lxd/container_migrate.py
deleted file mode 100644
index f55e15b..0000000
--- a/nova_lxd/nova/virt/lxd/container_migrate.py
+++ /dev/null
@@ -1,113 +0,0 @@
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from nova import i18n
-
-from oslo_config import cfg
-from oslo_log import log as logging
-
-from nova_lxd.nova.virt.lxd import config
-from nova_lxd.nova.virt.lxd import operations
-from nova_lxd.nova.virt.lxd import session
-
-
-_ = i18n._
-_LE = i18n._LE
-_LI = i18n._LI
-
-CONF = cfg.CONF
-LOG = logging.getLogger(__name__)
-
-
-class LXDContainerMigrate(object):
-
-    def __init__(self, virtapi):
-        self.virtapi = virtapi
-        self.config = config.LXDContainerConfig()
-        self.session = session.LXDAPISession()
-        self.container_ops = \
-            operations.LXDContainerOperations(
-                self.virtapi)
-
-    def migrate_disk_and_power_off(self, context, instance, dest,
-                                   flavor, network_info,
-                                   block_device_info=None, timeout=0,
-                                   retry_interval=0):
-        LOG.debug("migrate_disk_and_power_off called", instance=instance)
-
-        LOG.info(_('No disk to migrate'))
-
-        # disk_info is not used
-        disk_info = {}
-        return disk_info
-
-    def confirm_migration(self, migration, instance, network_info):
-        LOG.debug("confirm_migration called", instance=instance)
-
-    def finish_revert_migration(self, context, instance, network_info,
-                                block_device_info=None, power_on=True):
-        LOG.debug("finish_revert_migration called", instance=instance)
-        container_config = self.get_container_config(instance)
-        self.container_ops.start_container(container_config, instance,
-                                           network_info,
-                                           need_vif_plugged=True)
-
-    def finish_migration(self, context, migration, instance, disk_info,
-                         network_info, image_meta, resize_instance=False,
-                         block_device_info=None, power_on=True):
-        LOG.debug("finish_migration called", instance=instance)
-
-        self._migration(migration, instance, network_info)
-
-    def _migration(self, migration, instance, network_info):
-        # XXX: zul (Jan 4, 2016) - Temporarily disabled due to LXD config
-        # change refactor.
-        LOG.debug('_migration called for instance', instance=instance)
-
-    def live_migration(self, context, instance_ref, dest, post_method,
-                       recover_method, block_migration=False,
-                       migrate_data=None):
-        LOG.debug("live_migration called", instance=instance_ref)
-        post_method(context, instance_ref, dest, block_migration)
-
-    def pre_live_migration(self, context, instance, block_device_info,
-                           network_info):
-        LOG.debug("pre_live_migration called", instance=instance)
-
-    def post_live_migration(self, context, instance, block_device_info):
-        LOG.debug("post_live_migration", instance=instance)
-        pass
-
-    def post_live_migration_at_destination(self, ctxt, instance_ref,
-                                           network_info, block_migration,
-                                           block_device_info):
-        LOG.debug("post_live_migration_at_destination called",
-                  instance=instance_ref)
-
-    def check_can_live_migrate_destination(self, ctxt, instance_ref,
-                                           src_compute_info, dst_compute_info,
-                                           block_migration=False,
-                                           disk_over_commit=False):
-        LOG.debug("check_can_live_migrate_destination called", instance_ref)
-        return {}
-
-    def check_can_live_migrate_destination_cleanup(self, ctxt,
-                                                   dest_check_data):
-        LOG.debug("check_can_live_migrate_destination_cleanup called")
-
-    def check_can_live_migrate_source(self, ctxt, instance_ref,
-                                      dest_check_data):
-        LOG.debug("check_can_live_migrate_source called", instance_ref)
-        return dest_check_data
diff --git a/nova_lxd/nova/virt/lxd/driver.py b/nova_lxd/nova/virt/lxd/driver.py
index ab8aee4..b701d16 100644
--- a/nova_lxd/nova/virt/lxd/driver.py
+++ b/nova_lxd/nova/virt/lxd/driver.py
@@ -26,9 +26,9 @@
 
 
 from nova_lxd.nova.virt.lxd import container_firewall
-from nova_lxd.nova.virt.lxd import container_migrate
 from nova_lxd.nova.virt.lxd import container_snapshot
 from nova_lxd.nova.virt.lxd import host
+from nova_lxd.nova.virt.lxd import migrate
 from nova_lxd.nova.virt.lxd import operations as container_ops
 from nova_lxd.nova.virt.lxd import vif as lxd_vif
 
@@ -70,7 +70,7 @@ def __init__(self, virtapi):
         self.container_ops = container_ops.LXDContainerOperations(virtapi)
         self.container_snapshot = container_snapshot.LXDSnapshot()
         self.container_firewall = container_firewall.LXDContainerFirewall()
-        self.container_migrate = container_migrate.LXDContainerMigrate(virtapi)
+        self.container_migrate = migrate.LXDContainerMigrate(virtapi)
         self.host = host.LXDHost()
 
     def init_host(self, host):
diff --git a/nova_lxd/nova/virt/lxd/migrate.py b/nova_lxd/nova/virt/lxd/migrate.py
new file mode 100644
index 0000000..f55e15b
--- /dev/null
+++ b/nova_lxd/nova/virt/lxd/migrate.py
@@ -0,0 +1,113 @@
+# Copyright 2015 Canonical Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from nova import i18n
+
+from oslo_config import cfg
+from oslo_log import log as logging
+
+from nova_lxd.nova.virt.lxd import config
+from nova_lxd.nova.virt.lxd import operations
+from nova_lxd.nova.virt.lxd import session
+
+
+_ = i18n._
+_LE = i18n._LE
+_LI = i18n._LI
+
+CONF = cfg.CONF
+LOG = logging.getLogger(__name__)
+
+
+class LXDContainerMigrate(object):
+
+    def __init__(self, virtapi):
+        self.virtapi = virtapi
+        self.config = config.LXDContainerConfig()
+        self.session = session.LXDAPISession()
+        self.container_ops = \
+            operations.LXDContainerOperations(
+                self.virtapi)
+
+    def migrate_disk_and_power_off(self, context, instance, dest,
+                                   flavor, network_info,
+                                   block_device_info=None, timeout=0,
+                                   retry_interval=0):
+        LOG.debug("migrate_disk_and_power_off called", instance=instance)
+
+        LOG.info(_('No disk to migrate'))
+
+        # disk_info is not used
+        disk_info = {}
+        return disk_info
+
+    def confirm_migration(self, migration, instance, network_info):
+        LOG.debug("confirm_migration called", instance=instance)
+
+    def finish_revert_migration(self, context, instance, network_info,
+                                block_device_info=None, power_on=True):
+        LOG.debug("finish_revert_migration called", instance=instance)
+        container_config = self.get_container_config(instance)
+        self.container_ops.start_container(container_config, instance,
+                                           network_info,
+                                           need_vif_plugged=True)
+
+    def finish_migration(self, context, migration, instance, disk_info,
+                         network_info, image_meta, resize_instance=False,
+                         block_device_info=None, power_on=True):
+        LOG.debug("finish_migration called", instance=instance)
+
+        self._migration(migration, instance, network_info)
+
+    def _migration(self, migration, instance, network_info):
+        # XXX: zul (Jan 4, 2016) - Temporarily disabled due to LXD config
+        # change refactor.
+        LOG.debug('_migration called for instance', instance=instance)
+
+    def live_migration(self, context, instance_ref, dest, post_method,
+                       recover_method, block_migration=False,
+                       migrate_data=None):
+        LOG.debug("live_migration called", instance=instance_ref)
+        post_method(context, instance_ref, dest, block_migration)
+
+    def pre_live_migration(self, context, instance, block_device_info,
+                           network_info):
+        LOG.debug("pre_live_migration called", instance=instance)
+
+    def post_live_migration(self, context, instance, block_device_info):
+        LOG.debug("post_live_migration", instance=instance)
+        pass
+
+    def post_live_migration_at_destination(self, ctxt, instance_ref,
+                                           network_info, block_migration,
+                                           block_device_info):
+        LOG.debug("post_live_migration_at_destination called",
+                  instance=instance_ref)
+
+    def check_can_live_migrate_destination(self, ctxt, instance_ref,
+                                           src_compute_info, dst_compute_info,
+                                           block_migration=False,
+                                           disk_over_commit=False):
+        LOG.debug("check_can_live_migrate_destination called", instance_ref)
+        return {}
+
+    def check_can_live_migrate_destination_cleanup(self, ctxt,
+                                                   dest_check_data):
+        LOG.debug("check_can_live_migrate_destination_cleanup called")
+
+    def check_can_live_migrate_source(self, ctxt, instance_ref,
+                                      dest_check_data):
+        LOG.debug("check_can_live_migrate_source called", instance_ref)
+        return dest_check_data
diff --git a/nova_lxd/tests/test_container_migration.py b/nova_lxd/tests/test_container_migration.py
deleted file mode 100644
index b18ff26..0000000
--- a/nova_lxd/tests/test_container_migration.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import mock
-
-from nova import test
-from nova.virt import fake
-
-from nova_lxd.nova.virt.lxd import container_migrate
-from nova_lxd.nova.virt.lxd import session
-from nova_lxd.tests import stubs
-
-
- at mock.patch.object(container_migrate, 'CONF', stubs.MockConf())
- at mock.patch.object(session, 'CONF', stubs.MockConf())
-class LXDTestContainerMigrate(test.NoDBTestCase):
-
-    @mock.patch.object(container_migrate, 'CONF', stubs.MockConf())
-    @mock.patch.object(session, 'CONF', stubs.MockConf())
-    def setUp(self):
-        super(LXDTestContainerMigrate, self).setUp()
-
-        self.migrate = container_migrate.LXDContainerMigrate(
-            fake.FakeVirtAPI())
-
-    @mock.patch.object(session.LXDAPISession, 'container_migrate')
-    def test_finish_migration(self, mo):
-        context = mock.Mock()
-        migration = {'source_compute': 'fake-source',
-                     'dest_compute': 'fake-dest'}
-        instance = stubs._fake_instance()
-        bdevice_info = mock.Mock()
-        disk_info = mock.Mock()
-        network_info = mock.Mock()
-        with test.nested(
-            mock.patch.object(session.LXDAPISession,
-                              'container_defined'),
-            mock.patch.object(session.LXDAPISession,
-                              'container_stop'),
-            mock.patch.object(session.LXDAPISession,
-                              'container_init'),
-        ) as (
-            container_defined,
-            container_stop,
-            container_init
-        ):
-            def side_effect(*args, **kwargs):
-                # XXX: rockstar (7 Dec 2015) - This mock is a little greedy,
-                # and hits too many interfaces. It should become more specific
-                # to the single places it needs to fully mocked. Truthiness of
-                # the mock changes in py3.
-                if args[0] == 'defined':
-                    return False
-            container_defined.side_effect = side_effect
-            self.assertEqual(None,
-                             (self.migrate.finish_migration(context,
-                                                            migration,
-                                                            instance,
-                                                            disk_info,
-                                                            network_info,
-                                                            bdevice_info)))
diff --git a/nova_lxd/tests/test_migrate.py b/nova_lxd/tests/test_migrate.py
new file mode 100644
index 0000000..eef74eb
--- /dev/null
+++ b/nova_lxd/tests/test_migrate.py
@@ -0,0 +1,73 @@
+# Copyright 2015 Canonical Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import mock
+
+from nova import test
+from nova.virt import fake
+
+from nova_lxd.nova.virt.lxd import migrate
+from nova_lxd.nova.virt.lxd import session
+from nova_lxd.tests import stubs
+
+
+ at mock.patch.object(migrate, 'CONF', stubs.MockConf())
+ at mock.patch.object(session, 'CONF', stubs.MockConf())
+class LXDTestContainerMigrate(test.NoDBTestCase):
+
+    @mock.patch.object(migrate, 'CONF', stubs.MockConf())
+    @mock.patch.object(session, 'CONF', stubs.MockConf())
+    def setUp(self):
+        super(LXDTestContainerMigrate, self).setUp()
+
+        self.migrate = migrate.LXDContainerMigrate(
+            fake.FakeVirtAPI())
+
+    @mock.patch.object(session.LXDAPISession, 'container_migrate')
+    def test_finish_migration(self, mo):
+        context = mock.Mock()
+        migration = {'source_compute': 'fake-source',
+                     'dest_compute': 'fake-dest'}
+        instance = stubs._fake_instance()
+        bdevice_info = mock.Mock()
+        disk_info = mock.Mock()
+        network_info = mock.Mock()
+        with test.nested(
+            mock.patch.object(session.LXDAPISession,
+                              'container_defined'),
+            mock.patch.object(session.LXDAPISession,
+                              'container_stop'),
+            mock.patch.object(session.LXDAPISession,
+                              'container_init'),
+        ) as (
+            container_defined,
+            container_stop,
+            container_init
+        ):
+            def side_effect(*args, **kwargs):
+                # XXX: rockstar (7 Dec 2015) - This mock is a little greedy,
+                # and hits too many interfaces. It should become more specific
+                # to the single places it needs to fully mocked. Truthiness of
+                # the mock changes in py3.
+                if args[0] == 'defined':
+                    return False
+            container_defined.side_effect = side_effect
+            self.assertEqual(None,
+                             (self.migrate.finish_migration(context,
+                                                            migration,
+                                                            instance,
+                                                            disk_info,
+                                                            network_info,
+                                                            bdevice_info)))

From 20ab69d173a0866a5b217ba6887bab96cd4f3ab4 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Sat, 6 Feb 2016 08:33:53 -0500
Subject: [PATCH 02/15] Implement resize and contianer resource limits

When using containers in production operators do not
want a container to take over a whole host, so LXD
has introduced container resource limits. We implement
the following LXD options, via flavors:

- memory limit
- vcpu limit
- disk limit
- privileged containers
- nested containers

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova_lxd/nova/virt/lxd/config.py  | 52 +++++++++++++++++++++++++++++++++++++++
 nova_lxd/nova/virt/lxd/migrate.py | 28 ++++++++++++++++++---
 2 files changed, 77 insertions(+), 3 deletions(-)

diff --git a/nova_lxd/nova/virt/lxd/config.py b/nova_lxd/nova/virt/lxd/config.py
index 3fb79c9..e158337 100644
--- a/nova_lxd/nova/virt/lxd/config.py
+++ b/nova_lxd/nova/virt/lxd/config.py
@@ -103,6 +103,11 @@ def create_profile(self, instance, network_info):
             config['config'] = self._create_config(instance_name, instance)
             config['devices'] = self._create_network(instance_name, instance,
                                                      network_info)
+
+            # Restrict the size of the "/" disk
+            config['devices'].update(
+                self.configure_container_root(instance))
+
             return config
         except Exception as ex:
             with excutils.save_and_reraise_exception():
@@ -121,10 +126,21 @@ def _create_config(self, instance_name, instance):
         try:
             config = {}
 
+            # Update continaer options
+            config.update(self._config_instance_options(config, instance))
+
+            # Set the instance memory limit
             mem = instance.memory_mb
             if mem >= 0:
                 config['limits.memory'] = '%sMB' % mem
 
+
+            # Set the instnace vcpu limit
+            vcpus = instance.flavor.vcpus
+            if vcpus >= 0:
+                config['limits.cpu'] = str(vcpus)
+
+            # Configure the console for the instance
             config['raw.lxc'] = 'lxc.console=\n' \
                                 'lxc.cgroup.devices.deny=c 5:1 rwm\n' \
                                 'lxc.console.logfile=%s\n' \
@@ -138,6 +154,42 @@ def _create_config(self, instance_name, instance):
                         '%(ex)s'), {'instance': instance_name, 'ex': ex},
                     instance=instance)
 
+    def _config_instance_options(self, config, instance):
+        LOG.debug('_config_instance_options called for instance', instance=instance)
+
+        # Set the container to autostart when the host reboots
+        config['boot.autostart'] = 'True'
+
+        # Determine if we require a nested container
+        flavor = instance.flavor
+        lxd_nested_allowed = flavor.extra_specs.get('lxd_nested_allowed', False)
+        if lxd_nested_allowed:
+            config['security.nesting'] = 'True'
+
+        # Determine if we require a privileged container
+        lxd_privileged_allowed = flavor.extra_specs.get('lxd_privileged_allowed', False)
+        if lxd_privileged_allowed:
+            config['security.privileged'] = 'True'
+
+        return config
+
+    def configure_container_root(self, instance):
+        LOG.debug('_configure_container_root called for instnace',
+                  instance=instance)
+        try:
+            config = {}
+            config['root'] = {'path': '/',
+                              'type': 'disk',
+                              'size': '%sGB' % str(instance.root_gb)
+                            }
+            return config
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Failed to configure disk for '
+                              '%(instance)s: %(ex)s'),
+                              {'instance': instance.name, 'ex': ex},
+                              instance=instance)
+
     def _create_network(self, instance_name, instance, network_info):
         """Create the LXD container network on the host
 
diff --git a/nova_lxd/nova/virt/lxd/migrate.py b/nova_lxd/nova/virt/lxd/migrate.py
index f55e15b..efc2023 100644
--- a/nova_lxd/nova/virt/lxd/migrate.py
+++ b/nova_lxd/nova/virt/lxd/migrate.py
@@ -17,6 +17,7 @@
 
 from oslo_config import cfg
 from oslo_log import log as logging
+from oslo_utils import excutils
 
 from nova_lxd.nova.virt.lxd import config
 from nova_lxd.nova.virt.lxd import operations
@@ -28,6 +29,7 @@
 _LI = i18n._LI
 
 CONF = cfg.CONF
+CONF.import_opt('my_ip', 'nova.netconf')
 LOG = logging.getLogger(__name__)
 
 
@@ -47,11 +49,31 @@ def migrate_disk_and_power_off(self, context, instance, dest,
                                    retry_interval=0):
         LOG.debug("migrate_disk_and_power_off called", instance=instance)
 
-        LOG.info(_('No disk to migrate'))
+        same_host = False
+        if CONF.my_ip == dest:
+            same_host = True
+            LOG.debug('Migration target is the source host')
+        else:
+            LOG.debug('Migration target host: %s' % dest)
+
+        if not self.session.continer_defined(instance.name, instance):
+            msg = _('Instance is not found.')
+            raise exception.NovaException(msg)
+
+        try:
+            if same_host:
+                container_profile = self.container.create_profile(instnace, 
+                                                                  network_info)
+                self.session.profile_update(container_profile, instance)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('failed to resize container '
+                              '%(instance)s: %(ex)s'),
+                              {'instance': instance.name, 'ex': ex},
+                              instance=instance)
 
         # disk_info is not used
-        disk_info = {}
-        return disk_info
+        return ""
 
     def confirm_migration(self, migration, instance, network_info):
         LOG.debug("confirm_migration called", instance=instance)

From 0e4430bd6ca5835cfca4d19313e0e79d6c8e20f9 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Sat, 6 Feb 2016 09:01:36 -0500
Subject: [PATCH 03/15] Always connect to the unix socket

Except for container_migrate always connect to the
local UNIX socket to use the pylxd API. Nova scheduler
does the right thing with regards to multiple compute hosts.

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova_lxd/nova/virt/lxd/container_snapshot.py |  2 +-
 nova_lxd/nova/virt/lxd/operations.py         | 11 ++--
 nova_lxd/nova/virt/lxd/session.py            | 75 +++++++++++++---------------
 nova_lxd/tests/session/test_container.py     | 15 +++---
 4 files changed, 45 insertions(+), 58 deletions(-)

diff --git a/nova_lxd/nova/virt/lxd/container_snapshot.py b/nova_lxd/nova/virt/lxd/container_snapshot.py
index 2be4da9..fb49b6f 100644
--- a/nova_lxd/nova/virt/lxd/container_snapshot.py
+++ b/nova_lxd/nova/virt/lxd/container_snapshot.py
@@ -78,7 +78,7 @@ def snapshot(self, context, instance, image_id, update_task_state):
                 # We have to stop the container before we can publish the
                 # image to the local store
                 self.session.container_stop(instance.name,
-                                            instance.host, instance)
+                                            instance)
                 fingerprint = self._save_lxd_image(instance,
                                                    image_id)
                 self.session.container_start(instance.name, instance)
diff --git a/nova_lxd/nova/virt/lxd/operations.py b/nova_lxd/nova/virt/lxd/operations.py
index 2db014e..351b58f 100644
--- a/nova_lxd/nova/virt/lxd/operations.py
+++ b/nova_lxd/nova/virt/lxd/operations.py
@@ -211,7 +211,7 @@ def _setup_container(self, instance_name, instance):
             container_config = \
                 self.config.create_container(instance)
             self.session.container_init(
-                container_config, instance, instance.host)
+                container_config, instance)
 
             # Start the container
             self.session.container_start(instance_name, instance)
@@ -345,7 +345,7 @@ def destroy(self, context, instance, network_info, block_device_info=None,
         LOG.debug('destroy called for instance', instance=instance)
         try:
             self.session.profile_delete(instance)
-            self.session.container_destroy(instance.name, instance.host,
+            self.session.container_destroy(instance.name,
                                            instance)
             self.cleanup(context, instance, network_info, block_device_info)
         except Exception as ex:
@@ -366,7 +366,6 @@ def power_off(self, instance, timeout=0, retry_interval=0):
         LOG.debug('power_off called for instance', instance=instance)
         try:
             self.session.container_stop(instance.name,
-                                        instance.host,
                                         instance)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
@@ -475,7 +474,7 @@ def rescue(self, context, instance, network_info, image_meta,
                 raise exception.NovaException(msg)
 
             # Step 1 - Stop the old container
-            self.session.container_stop(instance.name, instance.host, instance)
+            self.session.container_stop(instance.name, instance)
 
             # Step 2 - Rename the broken contianer to be rescued
             self.session.container_move(instance.name,
@@ -490,8 +489,7 @@ def rescue(self, context, instance, network_info, image_meta,
             config = self.config.configure_disk_path(rescue_dir,
                                                      'mnt', 'rescue', instance)
             container_config['devices'].update(config)
-            self.session.container_init(container_config, instance,
-                                        instance.host)
+            self.session.container_init(container_config, instance)
 
             # Step 4 - Start the rescue instance
             self.session.container_start(instance.name, instance)
@@ -516,7 +514,6 @@ def unrescue(self, instance, network_info):
 
             # Step 1 - Destory the rescue instance.
             self.session.container_destroy(instance.name,
-                                           instance.host,
                                            instance)
 
             # Step 2 - Rename the backup container that
diff --git a/nova_lxd/nova/virt/lxd/session.py b/nova_lxd/nova/virt/lxd/session.py
index e6b6142..83aa33f 100644
--- a/nova_lxd/nova/virt/lxd/session.py
+++ b/nova_lxd/nova/virt/lxd/session.py
@@ -75,12 +75,10 @@ def get_session(self, host=None):
         :return: pylxd object
         """
         try:
-            if host is None:
-                conn = api.API()
-            elif host == CONF.host:
-                conn = api.API()
+            if host:
+                return api.API(host=host)
             else:
-                conn = api.API(host=host)
+                return api.API()
         except Exception as ex:
             # notify the compute host that the connection failed
             # via an rpc call
@@ -93,8 +91,6 @@ def get_session(self, host=None):
                                               payload)
             raise exception.HypervisorUnavailable(host=CONF.host)
 
-        return conn
-
     #
     # Container related API methods
     #
@@ -129,7 +125,7 @@ def container_update(self, config, instance):
         """
         LOG.debug('container_update called fo instance', instance=instance)
         try:
-            client = self.get_session(instance.host)
+            client = self.get_session()
             if not self.container_defined(instance.name, instance):
                 msg = _('Instance is not found..: %s') % instance.name
                 raise exception.InstanceNotFound(msg)
@@ -157,7 +153,7 @@ def container_running(self, instance):
         """
         LOG.debug('container_running for instance', instance=instance)
         try:
-            client = self.get_session(instance.host)
+            client = self.get_session()
             return client.container_running(instance.name)
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
@@ -180,7 +176,7 @@ def container_state(self, instance):
         """
         LOG.debug('container_state called for instance', instance=instance)
         try:
-            client = self.get_session(instance.host)
+            client = self.get_session()
             if not self.container_defined(instance.name, instance):
                 return power_state.NOSTATE
 
@@ -214,7 +210,7 @@ def container_config(self, instance):
                 msg = _('Instance is not found.. %s') % instance.name
                 raise exception.InstanceNotFound(msg)
 
-            client = self.get_session(instance.host)
+            client = self.get_session()
             return client.get_container_config(instance.name)
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
@@ -241,7 +237,7 @@ def container_info(self, instance):
                 msg = _('Instance is not found.. %s') % instance.name
                 raise exception.InstanceNotFound(msg)
 
-            client = self.get_session(instance.host)
+            client = self.get_session()
             return client.container_info(instance.name)
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
@@ -265,7 +261,7 @@ def container_defined(self, instance_name, instance):
         """
         LOG.debug('container_defined for instance', instance=instance)
         try:
-            client = self.get_session(instance.host)
+            client = self.get_session()
             return client.container_defined(instance_name)
         except lxd_exceptions.APIError as ex:
             if ex.status_code == 404:
@@ -293,7 +289,7 @@ def container_start(self, instance_name, instance):
                          '%(image)s'), {'instance': instance.name,
                                         'image': instance.image_ref})
             # Start the container
-            client = self.get_session(instance.host)
+            client = self.get_session()
 
             # (chuck): Something wicked could happen between
             # container
@@ -320,11 +316,10 @@ def container_start(self, instance_name, instance):
                     {'instance': instance_name, 'reason': ex},
                     instance=instance)
 
-    def container_stop(self, instance_name, host, instance):
+    def container_stop(self, instance_name, instance):
         """Stops an LXD container
 
         :param instance_name: instance name
-        :param host:  host where the container is running
         :param instance: nova instance object
 
         """
@@ -338,7 +333,7 @@ def container_stop(self, instance_name, host, instance):
                          '%(image)s'), {'instance': instance.name,
                                         'image': instance.image_ref})
             # Stop the container
-            client = self.get_session(host)
+            client = self.get_session()
             (state, data) = client.container_stop(instance_name,
                                                   CONF.lxd.timeout)
             self.operation_wait(data.get('operation'), instance)
@@ -375,7 +370,7 @@ def container_reboot(self, instance):
                                         'image': instance.image_ref})
 
             # Container reboot
-            client = self.get_session(instance.host)
+            client = self.get_session()
             (state, data) = client.container_reboot(instance.name,
                                                     CONF.lxd.timeout)
             self.operation_wait(data.get('operation'), instance)
@@ -395,11 +390,10 @@ def container_reboot(self, instance):
                         '%(reason)s'), {'instance': instance.name,
                                         'reason': ex}, instance=instance)
 
-    def container_destroy(self, instance_name, host, instance):
+    def container_destroy(self, instance_name, instance):
         """Destroy a LXD container
 
         :param instance_name: container name
-        :param host: container host
         :param instance: nova instance object
 
         """
@@ -413,9 +407,9 @@ def container_destroy(self, instance_name, host, instance):
                                         'image': instance.image_ref})
 
             # Destroying container
-            self.container_stop(instance_name, host, instance)
+            self.container_stop(instance_name, instance)
 
-            client = self.get_session(host)
+            client = self.get_session()
             (state, data) = client.container_destroy(instance_name)
             self.operation_wait(data.get('operation'), instance)
 
@@ -450,7 +444,7 @@ def container_pause(self, instance_name, instance):
                          '%(image)s'), {'instance': instance_name,
                                         'image': instance.image_ref})
 
-            client = self.get_session(instance.host)
+            client = self.get_session()
             (state, data) = client.container_suspend(instance_name,
                                                      CONF.lxd.timeout)
             self.operation_wait(data.get('operation'), instance)
@@ -488,7 +482,7 @@ def container_unpause(self, instance_name, instance):
                          '%(image)s'), {'instance': instance.name,
                                         'image': instance.image_ref})
 
-            client = self.get_session(instance.host)
+            client = self.get_session()
             (state, data) = client.container_resume(instance_name,
                                                     CONF.lxd.timeout)
             self.operation_wait(data.get('operation'), instance)
@@ -508,12 +502,11 @@ def container_unpause(self, instance_name, instance):
                         '%(reason)s'), {'instance': instance_name,
                                         'reason': ex})
 
-    def container_init(self, config, instance, host):
+    def container_init(self, config, instance):
         """Create a LXD container
 
         :param config: LXD container config as a dict
         :param instance: nova instance object
-        :param host: host to create the container on
 
         """
         LOG.debug('container_init called for instance', instance=instance)
@@ -522,7 +515,7 @@ def container_init(self, config, instance, host):
                          '%(image)s'), {'instance': instance.name,
                                         'image': instance.image_ref})
 
-            client = self.get_session(host)
+            client = self.get_session()
             (state, data) = client.container_init(config)
             operation = data.get('operation')
             self.operation_wait(operation, instance)
@@ -559,7 +552,7 @@ def image_defined(self, instance):
         """
         LOG.debug('image_defined called for instance', instance=instance)
         try:
-            client = self.get_session(instance.host)
+            client = self.get_session()
             return client.alias_defined(instance.image_ref)
         except lxd_exceptions.APIError as ex:
             if ex.status_code == 404:
@@ -587,7 +580,7 @@ def create_alias(self, alias, instance):
         """
         LOG.debug('create_alias called for instance', instance=instance)
         try:
-            client = self.get_session(instance.host)
+            client = self.get_session()
             return client.alias_create(alias)
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
@@ -612,7 +605,7 @@ def image_upload(self, data, headers, instance):
         """
         LOG.debug('upload_image called for instnace', instance=instance)
         try:
-            client = self.get_session(instance.host)
+            client = self.get_session()
             (state, data) = client.image_upload(data=data,
                                                 headers=headers)
             # XXX - zulcss (Dec 8, 2015) - Work around for older
@@ -643,7 +636,7 @@ def operation_wait(self, operation_id, instance):
         """
         LOG.debug('wait_for_contianer for instance', instance=instance)
         try:
-            client = self.get_session(instance.host)
+            client = self.get_session()
             if not client.wait_container_operation(operation_id, 200, -1):
                 msg = _('Container creation timed out')
                 raise exception.NovaException(msg)
@@ -663,7 +656,7 @@ def operation_wait(self, operation_id, instance):
     def operation_info(self, operation_id, instance):
         LOG.debug('operation_info called for instance', instance=instance)
         try:
-            client = self.get_session(instance.host)
+            client = self.get_session()
             return client.operation_info(operation_id)
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
@@ -690,7 +683,7 @@ def profile_defined(self, instance):
         LOG.debug('profile_defined called for instance',
                   instance=instance)
         try:
-            client = self.get_session(instance.host)
+            client = self.get_session()
             client.profile_defined(instance.name)
         except lxd_exceptions.APIError as ex:
             if ex.status_code == 404:
@@ -716,7 +709,7 @@ def profile_create(self, config, instance):
         LOG.debug('profile_create called for instance',
                   instance=instance)
         try:
-            client = self.get_session(instance.host)
+            client = self.get_session()
             client.profile_create(config)
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
@@ -737,7 +730,7 @@ def profile_update(self, config, instance):
         """
         LOG.debug('profile_udpate called for instance', instance=instance)
         try:
-            client = self.get_session(instance.host)
+            client = self.get_session()
             client.profile_update(instance.name, config)
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
@@ -758,7 +751,7 @@ def profile_delete(self, instance):
         """
         LOG.debug('profile_delete called for instance', instance=instance)
         try:
-            client = self.get_session(instance.host)
+            client = self.get_session()
             client.profile_delete(instance.name)
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
@@ -829,7 +822,7 @@ def container_move(self, old_name, config, instance):
                                         'image': instance.image_ref})
 
             # Container move
-            client = self.get_session(instance.host)
+            client = self.get_session()
             (state, data) = client.container_local_move(old_name, config)
             self.operation_wait(data.get('operation'), instance)
 
@@ -863,7 +856,7 @@ def container_snapshot(self, snapshot, instance):
                                         'image': instance.image_ref})
 
             # Container snapshot
-            client = self.get_session(instance.host)
+            client = self.get_session()
             (state, data) = client.container_snapshot_create(
                 instance.name, snapshot)
             self.operation_wait(data.get('operation'), instance)
@@ -894,7 +887,7 @@ def container_publish(self, image, instance):
         """
         LOG.debug('container_publish called for instance', instance=instance)
         try:
-            client = self.get_session(instance.host)
+            client = self.get_session()
             return client.container_publish(image)
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
@@ -919,7 +912,7 @@ def container_export(self, image, instance):
         """
         LOG.debug('container_export called for instance', instance=instance)
         try:
-            client = self.get_session(instance.host)
+            client = self.get_session()
             return client.image_export(image)
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to export image: %s') % ex
@@ -956,7 +949,7 @@ def _wait_for_snapshot(self, event_id, instance):
         :param event_id: operation id
         :param instance: nova instance object
         """
-        client = self.get_session(instance.host)
+        client = self.get_session()
         (state, data) = client.operation_info(event_id)
         status_code = data['metadata']['status_code']
 
diff --git a/nova_lxd/tests/session/test_container.py b/nova_lxd/tests/session/test_container.py
index 431e285..ac68fe2 100644
--- a/nova_lxd/tests/session/test_container.py
+++ b/nova_lxd/tests/session/test_container.py
@@ -327,7 +327,7 @@ def test_container_stop(self, tag, side_effect):
         self.ml.container_stop.return_value = side_effect
         self.assertEqual(None,
                          self.session.container_stop(instance.name,
-                                                     instance.host, instance))
+                                                     instance))
         calls = [mock.call.container_defined(instance.name),
                  mock.call.container_stop(instance.name, -1),
                  mock.call.wait_container_operation(
@@ -348,7 +348,7 @@ def test_container_stop_fail(self, tag, side_effect, expected):
         self.ml.container_stop.side_effect = side_effect
         self.assertRaises(expected,
                           self.session.container_stop, instance.name,
-                          instance.host, instance)
+                          instance)
 
     @stubs.annotated_data(
         ('1,', (200, fake_api.fake_operation_info_ok()))
@@ -399,7 +399,6 @@ def test_container_destroy(self, tag, container_defined, side_effect):
             self.ml.container_destroy.return_value = side_effect
             self.assertEqual(None,
                              self.session.container_destroy(instance.name,
-                                                            instance.host,
                                                             instance))
             calls = [mock.call.container_defined(instance.name),
                      mock.call.container_defined(instance.name),
@@ -414,7 +413,6 @@ def test_container_destroy(self, tag, container_defined, side_effect):
             self.ml.container_defined.return_value = container_defined
             self.assertEqual(None,
                              self.session.container_destroy(instance.name,
-                                                            instance.host,
                                                             instance))
             calls = [mock.call.container_defined(instance.name)]
             self.assertEqual(calls, self.ml.method_calls)
@@ -438,7 +436,7 @@ def test_container_destroy_fail(self, tag, container_defined,
             self.ml.container_stop.side_effect = side_effect
             self.assertRaises(expected,
                               self.session.container_destroy, instance.name,
-                              instance.host, instance)
+                              instance)
         if test_type == 'fail_destroy':
             self.ml.container_defined.return_value = container_defined
             self.ml.container_stop.return_value = \
@@ -446,7 +444,7 @@ def test_container_destroy_fail(self, tag, container_defined,
             self.ml.container_destroy.side_effect = side_effect
             self.assertRaises(expected,
                               self.session.container_destroy, instance.name,
-                              instance.host, instance)
+                              instance)
 
     @stubs.annotated_data(
         ('1', (200, fake_api.fake_operation_info_ok()))
@@ -536,8 +534,7 @@ def test_container_init(self, tag, side_effect):
         self.ml.operation_info.return_value = \
             (200, fake_api.fake_container_state(200))
         self.assertEqual(None,
-                         self.session.container_init(config, instance,
-                                                     instance.host))
+                         self.session.container_init(config, instance))
         calls = [mock.call.container_init(config),
                  mock.call.wait_container_operation(
                      '/1.0/operation/1234', 200, -1),
@@ -559,4 +556,4 @@ def test_container_init_fail(self, tag, side_effect, expected):
         self.ml.container_init.side_effect = side_effect
         self.assertRaises(expected,
                           self.session.container_init, config,
-                          instance, instance.host)
+                          instance)

From 58cbd3c1ec073046830735d056bd59a4f522ac81 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Sat, 6 Feb 2016 09:04:29 -0500
Subject: [PATCH 04/15] Cleanup log messages

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova_lxd/nova/virt/lxd/session.py | 56 +++++++++++++++++++--------------------
 1 file changed, 28 insertions(+), 28 deletions(-)

diff --git a/nova_lxd/nova/virt/lxd/session.py b/nova_lxd/nova/virt/lxd/session.py
index 83aa33f..c03584a 100644
--- a/nova_lxd/nova/virt/lxd/session.py
+++ b/nova_lxd/nova/virt/lxd/session.py
@@ -127,7 +127,7 @@ def container_update(self, config, instance):
         try:
             client = self.get_session()
             if not self.container_defined(instance.name, instance):
-                msg = _('Instance is not found..: %s') % instance.name
+                msg = _('Instance is not found: %s') % instance.name
                 raise exception.InstanceNotFound(msg)
 
             return client.container_update(instance.name,
@@ -207,7 +207,7 @@ def container_config(self, instance):
         LOG.debug('container_config called for instance', instance=instance)
         try:
             if not self.container_defined(instance.name, instance):
-                msg = _('Instance is not found.. %s') % instance.name
+                msg = _('Instance is not found %s') % instance.name
                 raise exception.InstanceNotFound(msg)
 
             client = self.get_session()
@@ -234,7 +234,7 @@ def container_info(self, instance):
         LOG.debug('container_info called for instance', instance=instance)
         try:
             if not self.container_defined(instance.name, instance):
-                msg = _('Instance is not found.. %s') % instance.name
+                msg = _('Instance is not found %s') % instance.name
                 raise exception.InstanceNotFound(msg)
 
             client = self.get_session()
@@ -294,7 +294,7 @@ def container_start(self, instance_name, instance):
             # (chuck): Something wicked could happen between
             # container
             if not self.container_defined(instance_name, instance):
-                msg = _('Instance is not found.. %s ') % instance.name
+                msg = _('Instance is not found %s ') % instance.name
                 raise exception.InstanceNotFound(msg)
 
             (state, data) = client.container_start(instance_name,
@@ -302,7 +302,7 @@ def container_start(self, instance_name, instance):
             self.operation_wait(data.get('operation'), instance)
 
             LOG.info(_LI('Successfully started instance %(instance)s with'
-                         '%(image)s'), {'instance': instance.name,
+                         ' %(image)s'), {'instance': instance.name,
                                         'image': instance.image_ref})
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
@@ -326,11 +326,11 @@ def container_stop(self, instance_name, instance):
         LOG.debug('container_stop called for instance', instance=instance)
         try:
             if not self.container_defined(instance_name, instance):
-                msg = _('Instance is not found..: %s') % instance.name
+                msg = _('Instance is not found %s') % instance.name
                 raise exception.InstanceNotFound(msg)
 
             LOG.info(_LI('Stopping instance %(instance)s with'
-                         '%(image)s'), {'instance': instance.name,
+                         ' %(image)s'), {'instance': instance.name,
                                         'image': instance.image_ref})
             # Stop the container
             client = self.get_session()
@@ -339,7 +339,7 @@ def container_stop(self, instance_name, instance):
             self.operation_wait(data.get('operation'), instance)
 
             LOG.info(_LI('Successfully stopped instance %(instance)s with'
-                         '%(image)s'), {'instance': instance.name,
+                         ' %(image)s'), {'instance': instance.name,
                                         'image': instance.image_ref})
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
@@ -362,11 +362,11 @@ def container_reboot(self, instance):
         LOG.debug('container_reboot called for instance', instance=instance)
         try:
             if not self.container_defined(instance.name, instance):
-                msg = _('Instance is not found..: %s') % instance.name
+                msg = _('Instance is not found %s') % instance.name
                 raise exception.InstanceNotFound(msg)
 
             LOG.info(_LI('Rebooting instance %(instance)s with'
-                         '%(image)s'), {'instance': instance.name,
+                         ' %(image)s'), {'instance': instance.name,
                                         'image': instance.image_ref})
 
             # Container reboot
@@ -376,7 +376,7 @@ def container_reboot(self, instance):
             self.operation_wait(data.get('operation'), instance)
 
             LOG.info(_LI('Successfully rebooted instance %(instance)s with'
-                         '%(image)s'), {'instance': instance.name,
+                         ' %(image)s'), {'instance': instance.name,
                                         'image': instance.image_ref})
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
@@ -403,7 +403,7 @@ def container_destroy(self, instance_name, instance):
                 return
 
             LOG.info(_LI('Destroying instance %(instance)s with'
-                         '%(image)s'), {'instance': instance.name,
+                         ' %(image)s'), {'instance': instance.name,
                                         'image': instance.image_ref})
 
             # Destroying container
@@ -414,7 +414,7 @@ def container_destroy(self, instance_name, instance):
             self.operation_wait(data.get('operation'), instance)
 
             LOG.info(_LI('Successfully destroyed instance %(instance)s with'
-                         '%(image)s'), {'instance': instance.name,
+                         ' %(image)s'), {'instance': instance.name,
                                         'image': instance.image_ref})
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
@@ -437,11 +437,11 @@ def container_pause(self, instance_name, instance):
         LOG.debug('container_paused called for instance', instance=instance)
         try:
             if not self.container_defined(instance_name, instance):
-                msg = _('Instance is not found. %s') % instance_name
+                msg = _('Instance is not found %s') % instance_name
                 raise exception.InstanceNotFound(msg)
 
             LOG.info(_LI('Pausing instance %(instance)s with'
-                         '%(image)s'), {'instance': instance_name,
+                         ' %(image)s'), {'instance': instance_name,
                                         'image': instance.image_ref})
 
             client = self.get_session()
@@ -450,7 +450,7 @@ def container_pause(self, instance_name, instance):
             self.operation_wait(data.get('operation'), instance)
 
             LOG.info(_LI('Successfully paused instance %(instance)s with'
-                         '%(image)s'), {'instance': instance_name,
+                         ' %(image)s'), {'instance': instance_name,
                                         'image': instance.image_ref})
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
@@ -475,11 +475,11 @@ def container_unpause(self, instance_name, instance):
         LOG.debug('container_unpause called for instance', instance=instance)
         try:
             if not self.container_defined(instance_name, instance):
-                msg = _('Instance is not found. %s') % instance_name
+                msg = _('Instance is not found %s') % instance_name
                 raise exception.InstanceNotFound(msg)
 
             LOG.info(_LI('Unpausing instance %(instance)s with'
-                         '%(image)s'), {'instance': instance.name,
+                         ' %(image)s'), {'instance': instance.name,
                                         'image': instance.image_ref})
 
             client = self.get_session()
@@ -488,7 +488,7 @@ def container_unpause(self, instance_name, instance):
             self.operation_wait(data.get('operation'), instance)
 
             LOG.info(_LI('Successfully unpaused instance %(instance)s with'
-                         '%(image)s'), {'instance': instance.name,
+                         ' %(image)s'), {'instance': instance.name,
                                         'image': instance.image_ref})
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
@@ -512,7 +512,7 @@ def container_init(self, config, instance):
         LOG.debug('container_init called for instance', instance=instance)
         try:
             LOG.info(_LI('Creating container %(instance)s with'
-                         '%(image)s'), {'instance': instance.name,
+                         ' %(image)s'), {'instance': instance.name,
                                         'image': instance.image_ref})
 
             client = self.get_session()
@@ -525,7 +525,7 @@ def container_init(self, config, instance):
                 raise exception.NovaException(data['metadata'])
 
             LOG.info(_LI('Successfully created container %(instance)s with'
-                         '%(image)s'), {'instance': instance.name,
+                         ' %(image)s'), {'instance': instance.name,
                                         'image': instance.image_ref})
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
@@ -660,7 +660,7 @@ def operation_info(self, operation_id, instance):
             return client.operation_info(operation_id)
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
-                    '%(reason)s') % {'instance': instance.image_ref,
+                    ' %(reason)s') % {'instance': instance.image_ref,
                                      'reason': ex}
             LOG.error(msg)
             raise exception.NovaException(msg)
@@ -778,7 +778,7 @@ def container_migrate(self, instance_name, host, instance):
         """
         LOG.debug('container_migrate called for instance', instance=instance)
         try:
-            LOG.info(_LI('Migrating instance %(instance)s with'
+            LOG.info(_LI('Migrating instance %(instance)s with '
                          '%(image)s'), {'instance': instance_name,
                                         'image': instance.image_ref})
 
@@ -817,7 +817,7 @@ def container_move(self, old_name, config, instance):
         """
         LOG.debug('container_move called for instance', instnace=instance)
         try:
-            LOG.info(_LI('Moving container %(instance)s with'
+            LOG.info(_LI('Moving container %(instance)s with '
                          '%(image)s'), {'instance': instance.name,
                                         'image': instance.image_ref})
 
@@ -826,7 +826,7 @@ def container_move(self, old_name, config, instance):
             (state, data) = client.container_local_move(old_name, config)
             self.operation_wait(data.get('operation'), instance)
 
-            LOG.info(_LI('Successfully moved container %(instance)s with'
+            LOG.info(_LI('Successfully moved container %(instance)s with '
                          '%(image)s'), {'instance': instance.name,
                                         'image': instance.image_ref})
         except lxd_exceptions.APIError as ex:
@@ -837,8 +837,8 @@ def container_move(self, old_name, config, instance):
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(
-                    _LE('Failed to move container %(instance)s: %('
-                        'reason)s'),
+                    _LE('Failed to move container %(instance)s: '
+                        '%(reason)s'),
                     {'instance': instance.name,
                      'reason': ex}, instance=instance)
 
@@ -862,7 +862,7 @@ def container_snapshot(self, snapshot, instance):
             self.operation_wait(data.get('operation'), instance)
 
             LOG.info(_LI('Successfully snapshotted container %(instance)s with'
-                         '%(image)s'), {'instance': instance.name,
+                         ' %(image)s'), {'instance': instance.name,
                                         'image': instance.image_ref})
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'

From 0650e7d16ead918af2718e5cf19f5b354163a79a Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Sat, 6 Feb 2016 09:07:01 -0500
Subject: [PATCH 05/15] Remove unimplemented methods

Remove unimplemented methods so that the code
is easier to read.

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova_lxd/nova/virt/lxd/driver.py  | 17 +++----------
 nova_lxd/nova/virt/lxd/migrate.py | 51 ---------------------------------------
 2 files changed, 4 insertions(+), 64 deletions(-)

diff --git a/nova_lxd/nova/virt/lxd/driver.py b/nova_lxd/nova/virt/lxd/driver.py
index b701d16..f0bbe54 100644
--- a/nova_lxd/nova/virt/lxd/driver.py
+++ b/nova_lxd/nova/virt/lxd/driver.py
@@ -232,31 +232,22 @@ def get_available_resource(self, nodename):
 
     def pre_live_migration(self, context, instance, block_device_info,
                            network_info, disk_info, migrate_data=None):
-        return self.container_migrate.pre_live_migration(
-            context, instance, block_device_info,
-            network_info)
+        raise NotImplementedError()
 
     def live_migration(self, context, instance, dest,
                        post_method, recover_method, block_migration=False,
                        migrate_data=None):
-        return self.container_migrate.live_migration(context, instance, dest,
-                                                     post_method,
-                                                     recover_method,
-                                                     block_migration,
-                                                     migrate_data)
+        raise NotImplementedError()
 
     def post_live_migration(self, context, instance, block_device_info,
                             migrate_data=None):
-        return self.container_migrate.post_live_migration(context, instance,
-                                                          block_device_info)
+        raise NotImplementedError()
 
     def post_live_migration_at_destination(self, context, instance,
                                            network_info,
                                            block_migration=False,
                                            block_device_info=None):
-        return self.container_migrate.post_live_migration_at_destination(
-            context, instance, network_info, block_migration,
-            block_device_info)
+        raise NotImplementedError()
 
     def check_instance_shared_storage_local(self, context, instance):
         raise NotImplementedError()
diff --git a/nova_lxd/nova/virt/lxd/migrate.py b/nova_lxd/nova/virt/lxd/migrate.py
index efc2023..21bc5b7 100644
--- a/nova_lxd/nova/virt/lxd/migrate.py
+++ b/nova_lxd/nova/virt/lxd/migrate.py
@@ -78,58 +78,7 @@ def migrate_disk_and_power_off(self, context, instance, dest,
     def confirm_migration(self, migration, instance, network_info):
         LOG.debug("confirm_migration called", instance=instance)
 
-    def finish_revert_migration(self, context, instance, network_info,
-                                block_device_info=None, power_on=True):
-        LOG.debug("finish_revert_migration called", instance=instance)
-        container_config = self.get_container_config(instance)
-        self.container_ops.start_container(container_config, instance,
-                                           network_info,
-                                           need_vif_plugged=True)
-
     def finish_migration(self, context, migration, instance, disk_info,
                          network_info, image_meta, resize_instance=False,
                          block_device_info=None, power_on=True):
         LOG.debug("finish_migration called", instance=instance)
-
-        self._migration(migration, instance, network_info)
-
-    def _migration(self, migration, instance, network_info):
-        # XXX: zul (Jan 4, 2016) - Temporarily disabled due to LXD config
-        # change refactor.
-        LOG.debug('_migration called for instance', instance=instance)
-
-    def live_migration(self, context, instance_ref, dest, post_method,
-                       recover_method, block_migration=False,
-                       migrate_data=None):
-        LOG.debug("live_migration called", instance=instance_ref)
-        post_method(context, instance_ref, dest, block_migration)
-
-    def pre_live_migration(self, context, instance, block_device_info,
-                           network_info):
-        LOG.debug("pre_live_migration called", instance=instance)
-
-    def post_live_migration(self, context, instance, block_device_info):
-        LOG.debug("post_live_migration", instance=instance)
-        pass
-
-    def post_live_migration_at_destination(self, ctxt, instance_ref,
-                                           network_info, block_migration,
-                                           block_device_info):
-        LOG.debug("post_live_migration_at_destination called",
-                  instance=instance_ref)
-
-    def check_can_live_migrate_destination(self, ctxt, instance_ref,
-                                           src_compute_info, dst_compute_info,
-                                           block_migration=False,
-                                           disk_over_commit=False):
-        LOG.debug("check_can_live_migrate_destination called", instance_ref)
-        return {}
-
-    def check_can_live_migrate_destination_cleanup(self, ctxt,
-                                                   dest_check_data):
-        LOG.debug("check_can_live_migrate_destination_cleanup called")
-
-    def check_can_live_migrate_source(self, ctxt, instance_ref,
-                                      dest_check_data):
-        LOG.debug("check_can_live_migrate_source called", instance_ref)
-        return dest_check_data

From 2d19ea2d7c338f210bc0d99aa55273f1523f68c2 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Sat, 6 Feb 2016 09:11:45 -0500
Subject: [PATCH 06/15] Make sure profiles exist

Make sure LXD profiles exist or not
before running operations that need a profile.

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova_lxd/nova/virt/lxd/session.py | 41 +++++++++++++++++++++++++++++++++------
 1 file changed, 35 insertions(+), 6 deletions(-)

diff --git a/nova_lxd/nova/virt/lxd/session.py b/nova_lxd/nova/virt/lxd/session.py
index c03584a..0c76957 100644
--- a/nova_lxd/nova/virt/lxd/session.py
+++ b/nova_lxd/nova/virt/lxd/session.py
@@ -674,7 +674,23 @@ def operation_info(self, operation_id, instance):
     #
     # Profile methods
     #
-    def profile_defined(self, instance):
+    def profile_list(self):
+        LOG.debug('profile_list called for instance')
+        try:
+            client = self.get_session()
+            return client.profile_list()
+        except lxd_exceptions.APIError as ex:
+            msg = _('Failed to communicate with LXD API: %(reason)s') \
+                     % {'reason': ex}
+            LOG.error(msg)
+            raise excpetion.NovaException(msg)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Error from LXD during profile_list: '
+                          '%(reason)s') % {'reason': ex})
+
+
+    def profile_defined(self, instance_name, instance):
         """Validate if the profile is available on the LXD
            host
 
@@ -683,8 +699,10 @@ def profile_defined(self, instance):
         LOG.debug('profile_defined called for instance',
                   instance=instance)
         try:
-            client = self.get_session()
-            client.profile_defined(instance.name)
+            found = False
+            if instance_name in self.profile_list():
+                found = True
+            return found
         except lxd_exceptions.APIError as ex:
             if ex.status_code == 404:
                 return False
@@ -709,8 +727,12 @@ def profile_create(self, config, instance):
         LOG.debug('profile_create called for instance',
                   instance=instance)
         try:
+            if self.profile_defined(instance.name, instance):
+                msg = _('Profile already exists %s' % instance.name)
+                raise exception.NovaException(msg)
+
             client = self.get_session()
-            client.profile_create(config)
+            return client.profile_create(config)
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
                     ' %(reason)s') % {'instance': instance.name,
@@ -730,8 +752,12 @@ def profile_update(self, config, instance):
         """
         LOG.debug('profile_udpate called for instance', instance=instance)
         try:
+            if not self.profile_defined(instance.name, instance):
+                msg  = _('Profile not found %s' % instance.name)
+                raise exception.NovaException(msg)
+
             client = self.get_session()
-            client.profile_update(instance.name, config)
+            return client.profile_update(instance.name, config)
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
                     ' %(reason)s') % {'instance': instance.name,
@@ -751,8 +777,11 @@ def profile_delete(self, instance):
         """
         LOG.debug('profile_delete called for instance', instance=instance)
         try:
+            if not self.profile_defined(instance.name, instance):
+                return
+
             client = self.get_session()
-            client.profile_delete(instance.name)
+            return client.profile_delete(instance.name)
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
                     ' %(reason)s') % {'instance': instance.name,

From a74228232444a80a684694d55dfb2c9019e15a44 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Sat, 6 Feb 2016 17:02:40 -0500
Subject: [PATCH 07/15] Make functions non-private

Make functions non-private so that we can use them
for container migration.

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova_lxd/nova/virt/lxd/config.py | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/nova_lxd/nova/virt/lxd/config.py b/nova_lxd/nova/virt/lxd/config.py
index e158337..3beffc3 100644
--- a/nova_lxd/nova/virt/lxd/config.py
+++ b/nova_lxd/nova/virt/lxd/config.py
@@ -60,7 +60,7 @@ def create_container(self, instance):
             container_config = {
                 'name': instance_name,
                 'profiles': [str(instance.name)],
-                'source': self._get_container_source(instance),
+                'source': self.get_container_source(instance),
                 'devices': {}
             }
 
@@ -100,8 +100,8 @@ def create_profile(self, instance, network_info):
         try:
             config = {}
             config['name'] = str(instance_name)
-            config['config'] = self._create_config(instance_name, instance)
-            config['devices'] = self._create_network(instance_name, instance,
+            config['config'] = self.create_config(instance_name, instance)
+            config['devices'] = self.create_network(instance_name, instance,
                                                      network_info)
 
             # Restrict the size of the "/" disk
@@ -115,7 +115,7 @@ def create_profile(self, instance, network_info):
                     _LE('Failed to create profile %(instance)s: %(ex)s'),
                     {'instance': instance_name, 'ex': ex}, instance=instance)
 
-    def _create_config(self, instance_name, instance):
+    def create_config(self, instance_name, instance):
         """Create the LXD container resources
 
         :param instance_name: instance name
@@ -127,7 +127,7 @@ def _create_config(self, instance_name, instance):
             config = {}
 
             # Update continaer options
-            config.update(self._config_instance_options(config, instance))
+            config.update(self.config_instance_options(config, instance))
 
             # Set the instance memory limit
             mem = instance.memory_mb
@@ -154,7 +154,7 @@ def _create_config(self, instance_name, instance):
                         '%(ex)s'), {'instance': instance_name, 'ex': ex},
                     instance=instance)
 
-    def _config_instance_options(self, config, instance):
+    def config_instance_options(self, config, instance):
         LOG.debug('_config_instance_options called for instance', instance=instance)
 
         # Set the container to autostart when the host reboots
@@ -219,7 +219,7 @@ def _create_network(self, instance_name, instance, network_info):
                     _LE('Fail to configure network for %(instance)s: %(ex)s'),
                     {'instance': instance_name, 'ex': ex}, instance=instance)
 
-    def _get_container_source(self, instance):
+    def get_container_source(self, instance):
         """Set the LXD container image for the instance.
 
         :param instance: nova instance object
@@ -283,7 +283,7 @@ def create_container_net_device(self, instance, vif):
             network_config = self.vif_driver.get_config(instance, vif)
 
             config = {}
-            config[self._get_network_device(instance)] = {
+            config[self.get_network_device(instance)] = {
                 'nictype': 'bridged',
                 'hwaddr': str(vif['address']),
                 'parent': str(network_config['bridge']),
@@ -296,7 +296,7 @@ def create_container_net_device(self, instance, vif):
                       {'instance': instance.name, 'ex': ex},
                       instance=instance)
 
-    def _get_network_device(self, instance):
+    def get_network_device(self, instance):
         """Try to detect which network interfaces are available in a contianer
 
         :param instnace: nova instance object

From 9bc25286bfd5778729b4ea89958980632383e6f2 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Sat, 6 Feb 2016 17:09:16 -0500
Subject: [PATCH 08/15] Add finish_revert_migration

Add the ability to start the container if migration fails.

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova_lxd/nova/virt/lxd/driver.py  | 6 ++++++
 nova_lxd/nova/virt/lxd/migrate.py | 8 ++++++++
 2 files changed, 14 insertions(+)

diff --git a/nova_lxd/nova/virt/lxd/driver.py b/nova_lxd/nova/virt/lxd/driver.py
index f0bbe54..df3ae2e 100644
--- a/nova_lxd/nova/virt/lxd/driver.py
+++ b/nova_lxd/nova/virt/lxd/driver.py
@@ -194,6 +194,12 @@ def confirm_migration(self, migration, instance, network_info):
                                                         instance,
                                                         network_info)
 
+    def finish_revert_migration(self, context, instance, network_info,
+                               block_device_info=None, power_on=True):
+        return self.container_migrate.finish_revert_migration(context,
+                                instance, network_info, block_device_info,
+                                power_on)
+
     def pause(self, instance):
         self.container_ops.pause(instance)
 
diff --git a/nova_lxd/nova/virt/lxd/migrate.py b/nova_lxd/nova/virt/lxd/migrate.py
index 21bc5b7..fe54260 100644
--- a/nova_lxd/nova/virt/lxd/migrate.py
+++ b/nova_lxd/nova/virt/lxd/migrate.py
@@ -82,3 +82,11 @@ def finish_migration(self, context, migration, instance, disk_info,
                          network_info, image_meta, resize_instance=False,
                          block_device_info=None, power_on=True):
         LOG.debug("finish_migration called", instance=instance)
+
+    def finish_revert_migration(self, context, instance, network_info,
+                                 block_device_info=None, power_on=True):
+        LOG.debug('finish_revert_migration called for instance',
+                    instance=instance)
+        if self.session.container_defined(instance.name, instance):
+            self.session.container_start(instance.name, instance)
+

From 9740282129eb4c608409c31f1179e6f511417609 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Sat, 6 Feb 2016 17:12:08 -0500
Subject: [PATCH 09/15] Fix typos

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova_lxd/nova/virt/lxd/migrate.py | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/nova_lxd/nova/virt/lxd/migrate.py b/nova_lxd/nova/virt/lxd/migrate.py
index fe54260..95bbc18 100644
--- a/nova_lxd/nova/virt/lxd/migrate.py
+++ b/nova_lxd/nova/virt/lxd/migrate.py
@@ -13,6 +13,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+from nova import exception
 from nova import i18n
 
 from oslo_config import cfg
@@ -56,14 +57,14 @@ def migrate_disk_and_power_off(self, context, instance, dest,
         else:
             LOG.debug('Migration target host: %s' % dest)
 
-        if not self.session.continer_defined(instance.name, instance):
+        if not self.session.container_defined(instance.name, instance):
             msg = _('Instance is not found.')
             raise exception.NovaException(msg)
 
         try:
             if same_host:
-                container_profile = self.container.create_profile(instnace, 
-                                                                  network_info)
+                container_profile = self.config.create_profile(instance,
+                                                               network_info)
                 self.session.profile_update(container_profile, instance)
         except Exception as ex:
             with excutils.save_and_reraise_exception():

From 47db5b460d67cab0bd386727e4f1906d3b38dacf Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Sat, 6 Feb 2016 17:25:24 -0500
Subject: [PATCH 10/15] Refactor migration support

Refactor container migration support.

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova_lxd/nova/virt/lxd/config.py  | 37 +++++++++++++++++++---
 nova_lxd/nova/virt/lxd/migrate.py | 65 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 97 insertions(+), 5 deletions(-)

diff --git a/nova_lxd/nova/virt/lxd/config.py b/nova_lxd/nova/virt/lxd/config.py
index 3beffc3..de4864d 100644
--- a/nova_lxd/nova/virt/lxd/config.py
+++ b/nova_lxd/nova/virt/lxd/config.py
@@ -243,6 +243,39 @@ def get_container_source(self, instance):
                     {'instance': instance.name, 'ex': ex},
                     instance=instance)
 
+    def get_container_migrate(self, container_migrate, migration, instance):
+        LOG.debug('get_container_migrate called for instance',
+                  instnace=instance)
+        try:
+            # Generate the container config
+            container_metadata =container_migrate['metadata']
+            container_control = container_metadata['metadata']['control']
+            container_fs = container_metadata['metadata']['fs']
+
+            container_url = ('wss://%s:8443%s/websocket'
+                             % (migration['source_compute'],
+                                container_migrate.get('operation')))
+
+            container_migrate = {
+                    'base_iamge': '',
+                    'mode': 'pull',
+                    'operation': str(container_url),
+                    'secrets': {
+                        'control': str(container_control),
+                        'fs': str(container_fs)
+                    },
+                    'type': 'migration'
+            }
+
+            LOG.debug(pprint.pprint(container_migrate))
+            return container_migrate
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Failed to configure migation source '
+                              '%(instance)s: %(ex)s'),
+                             {'instance': instance.name, 'ex': ex},
+                             instance=instance)
+
     def configure_disk_path(self, src_path, dest_path, vfs_type, instance):
         """Configure the host mount point for the LXD container
 
@@ -255,10 +288,6 @@ def configure_disk_path(self, src_path, dest_path, vfs_type, instance):
         LOG.debug('configure_disk_path called for instance',
                   instance=instance)
         try:
-            if not os.path.exists(src_path):
-                msg = _('Source path does not exist')
-                raise exception.NovaException(msg)
-
             config = {}
             config[vfs_type] = {'path': dest_path,
                                 'source': src_path,
diff --git a/nova_lxd/nova/virt/lxd/migrate.py b/nova_lxd/nova/virt/lxd/migrate.py
index 95bbc18..5a0b7fc 100644
--- a/nova_lxd/nova/virt/lxd/migrate.py
+++ b/nova_lxd/nova/virt/lxd/migrate.py
@@ -13,15 +13,21 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import os
+import pprint
+import shutil
+
 from nova import exception
 from nova import i18n
 
 from oslo_config import cfg
 from oslo_log import log as logging
 from oslo_utils import excutils
+from oslo_utils import fileutils
 
 from nova_lxd.nova.virt.lxd import config
 from nova_lxd.nova.virt.lxd import operations
+from nova_lxd.nova.virt.lxd import utils as container_dir
 from nova_lxd.nova.virt.lxd import session
 
 
@@ -39,8 +45,9 @@ class LXDContainerMigrate(object):
     def __init__(self, virtapi):
         self.virtapi = virtapi
         self.config = config.LXDContainerConfig()
+        self.container_dir = container_dir.LXDContainerDirectories()
         self.session = session.LXDAPISession()
-        self.container_ops = \
+        self.operations = \
             operations.LXDContainerOperations(
                 self.virtapi)
 
@@ -66,6 +73,8 @@ def migrate_disk_and_power_off(self, context, instance, dest,
                 container_profile = self.config.create_profile(instance,
                                                                network_info)
                 self.session.profile_update(container_profile, instance)
+            else:
+                self.session.container_stop(instance.name, instance)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(_LE('failed to resize container '
@@ -79,11 +88,65 @@ def migrate_disk_and_power_off(self, context, instance, dest,
     def confirm_migration(self, migration, instance, network_info):
         LOG.debug("confirm_migration called", instance=instance)
 
+        if not self.session.container_defined(instance.name, instance):
+            msg = _('Failed to find container %s' % instance.name)
+            raise exception.NovaException(msg)
+
+        try:
+            self.session.profile_delete(instance)
+            self.session.container_destroy(instance.name,
+                                           instance)
+            self.operations.unplug_vifs(instance, network_info)
+            container_dir = self.container_dir.get_instance_dir(instance.name)
+            if os.path.exists(container_dir):
+                shutil.rmtree(container_dir)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_LE('Confirm migration failed for %(instnace)s: '
+                                  '%(ex)s'), {'instance': instance.name,
+                                    'ex': ex}, instance=instance)
+
     def finish_migration(self, context, migration, instance, disk_info,
                          network_info, image_meta, resize_instance=False,
                          block_device_info=None, power_on=True):
         LOG.debug("finish_migration called", instance=instance)
 
+        if self.session.container_defined(instance.name, instance):
+            msg = _('Failed to find container %s' % instance.name)
+            raise exception.NovaException(msg)
+
+        try:
+            # Ensure that the instance directory exists
+            instance_dir = \
+                self.container_dir.get_instance_dir(instance.name)
+            if not os.path.exists(instance_dir):
+                fileutils.ensure_tree(instance_dir)
+
+            # Step 1 - Setup the profile on the dest host
+            container_profile = self.config.create_profile(instance,
+                                        network_info)
+            self.session.profile_create(container_profile, instance)
+
+            # Step 2 - Open a websocket on the srct and and
+            #          generate the container config
+            (state, data) = self.session.container_migrate(instance.name,
+                                    migration['source_compute'], instance)
+            container_config = self.config.create_container(instance)
+            container_config['source'] = \
+                self.config.get_container_migrate(data, migration, instance)
+            LOG.debug(pprint.pprint(container_config))
+            self.session.container_init(container_config, instance)
+
+            # Step 3 - Start the network and contianer
+            self.operations.plug_vifs(instance, network_info)
+            self.session.container_start(instance.name, instance)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_LE('Migration failed for %(instnace)s: '
+                                  '%(ex)s'),
+                                  {'instance': instance.name,
+                                   'ex': ex}, instance=instance)
+
     def finish_revert_migration(self, context, instance, network_info,
                                  block_device_info=None, power_on=True):
         LOG.debug('finish_revert_migration called for instance',

From aeb4c51cf3e7ef38e650fe4a956664d1f114f707 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Sat, 6 Feb 2016 17:43:31 -0500
Subject: [PATCH 11/15] Fix various typos found

Fix various typos found

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova_lxd/nova/virt/lxd/config.py             | 21 +++++++--------
 nova_lxd/nova/virt/lxd/container_snapshot.py |  3 ++-
 nova_lxd/nova/virt/lxd/image.py              | 15 +++++------
 nova_lxd/nova/virt/lxd/migrate.py            |  4 +--
 nova_lxd/nova/virt/lxd/operations.py         |  4 +--
 nova_lxd/nova/virt/lxd/session.py            | 39 ++++++++++++++++------------
 6 files changed, 45 insertions(+), 41 deletions(-)

diff --git a/nova_lxd/nova/virt/lxd/config.py b/nova_lxd/nova/virt/lxd/config.py
index de4864d..ade9bdf 100644
--- a/nova_lxd/nova/virt/lxd/config.py
+++ b/nova_lxd/nova/virt/lxd/config.py
@@ -122,7 +122,7 @@ def create_config(self, instance_name, instance):
         :param instance: nova instance object
         :return: LXD resources dictionary
         """
-        LOG.debug('_create_config called for instance', instance=instance)
+        LOG.debug('create_config called for instance', instance=instance)
         try:
             config = {}
 
@@ -135,7 +135,7 @@ def create_config(self, instance_name, instance):
                 config['limits.memory'] = '%sMB' % mem
 
 
-            # Set the instnace vcpu limit
+            # Set the instance vcpu limit
             vcpus = instance.flavor.vcpus
             if vcpus >= 0:
                 config['limits.cpu'] = str(vcpus)
@@ -155,7 +155,7 @@ def create_config(self, instance_name, instance):
                     instance=instance)
 
     def config_instance_options(self, config, instance):
-        LOG.debug('_config_instance_options called for instance', instance=instance)
+        LOG.debug('config_instance_options called for instance', instance=instance)
 
         # Set the container to autostart when the host reboots
         config['boot.autostart'] = 'True'
@@ -174,7 +174,7 @@ def config_instance_options(self, config, instance):
         return config
 
     def configure_container_root(self, instance):
-        LOG.debug('_configure_container_root called for instnace',
+        LOG.debug('configure_container_root called for instance',
                   instance=instance)
         try:
             config = {}
@@ -190,7 +190,7 @@ def configure_container_root(self, instance):
                               {'instance': instance.name, 'ex': ex},
                               instance=instance)
 
-    def _create_network(self, instance_name, instance, network_info):
+    def create_network(self, instance_name, instance, network_info):
         """Create the LXD container network on the host
 
         :param instance_name: nova instance name
@@ -198,7 +198,7 @@ def _create_network(self, instance_name, instance, network_info):
         :param network_info: instance network configuration object
         :return:network configuration dictionary
         """
-        LOG.debug('_create_network called for instance', instance=instance)
+        LOG.debug('create_network called for instance', instance=instance)
         try:
             network_devices = {}
 
@@ -225,7 +225,7 @@ def get_container_source(self, instance):
         :param instance: nova instance object
         :return: the container source
         """
-        LOG.debug('_get_container_source called for instance',
+        LOG.debug('get_container_source called for instance',
                   instance=instance)
         try:
             container_source = {'type': 'image',
@@ -245,7 +245,7 @@ def get_container_source(self, instance):
 
     def get_container_migrate(self, container_migrate, migration, instance):
         LOG.debug('get_container_migrate called for instance',
-                  instnace=instance)
+                  instance=instance)
         try:
             # Generate the container config
             container_metadata =container_migrate['metadata']
@@ -267,7 +267,6 @@ def get_container_migrate(self, container_migrate, migration, instance):
                     'type': 'migration'
             }
 
-            LOG.debug(pprint.pprint(container_migrate))
             return container_migrate
         except Exception as ex:
             with excutils.save_and_reraise_exception():
@@ -328,9 +327,9 @@ def create_container_net_device(self, instance, vif):
     def get_network_device(self, instance):
         """Try to detect which network interfaces are available in a contianer
 
-        :param instnace: nova instance object
+        :param instance: nova instance object
         """
-        LOG.debug('_get_network_device called for instance', instance=instance)
+        LOG.debug('get_network_device called for instance', instance=instance)
         data = self.session.container_info(instance)
         lines = open('/proc/%s/net/dev' % data['init']).readlines()
         interfaces = []
diff --git a/nova_lxd/nova/virt/lxd/container_snapshot.py b/nova_lxd/nova/virt/lxd/container_snapshot.py
index fb49b6f..8d885d6 100644
--- a/nova_lxd/nova/virt/lxd/container_snapshot.py
+++ b/nova_lxd/nova/virt/lxd/container_snapshot.py
@@ -99,6 +99,7 @@ def _save_lxd_image(self, instance, image_id):
         """
         LOG.debug('_save_lxd_image called for instance', instance=instance)
 
+        fingerprint = None
         try:
             # Publish the snapshot to the local LXD image store
             container_snapshot = {
@@ -140,7 +141,7 @@ def _save_lxd_image(self, instance, image_id):
         return fingerprint
 
     def _save_glance_image(self, context, instance, image_id, fingerprint):
-        LOG.debug('_save_glance_image called for instance', instnace=instance)
+        LOG.debug('_save_glance_image called for instance', instance=instance)
 
         try:
             snapshot = IMAGE_API.get(context, image_id)
diff --git a/nova_lxd/nova/virt/lxd/image.py b/nova_lxd/nova/virt/lxd/image.py
index a39d54c..2f29fcd 100644
--- a/nova_lxd/nova/virt/lxd/image.py
+++ b/nova_lxd/nova/virt/lxd/image.py
@@ -87,7 +87,7 @@ def setup_image(self, context, instance, image_meta):
                 self._verify_image(context, instance)
 
                 # Fetch the image from glance
-                self._fetch_image(context, image_meta, instance)
+                self._fetch_image(context, instance)
 
                 # Generate the LXD manifest for the image
                 self._get_lxd_manifest(instance, image_meta)
@@ -99,7 +99,7 @@ def setup_image(self, context, instance, image_meta):
                 self._setup_alias(instance)
 
                 # Remove image and manifest when done.
-                self._cleanup_image(image_meta, instance)
+                self._cleanup_image(instance)
 
             except Exception as ex:
                 with excutils.save_and_reraise_exception():
@@ -107,7 +107,7 @@ def setup_image(self, context, instance, image_meta):
                                   '%(reason)s'),
                               {'image': instance.image_ref,
                                'reason': ex}, instance=instance)
-                    self._cleanup_image(image_meta, instance)
+                    self._cleanup_image(instance)
 
     def _verify_image(self, context, instance):
         """Inspect image to verify the correct disk format.
@@ -142,11 +142,10 @@ def _verify_image(self, context, instance):
             raise exception.ImageUnacceptable(image_id=instance.image_ref,
                                               reason=reason)
 
-    def _fetch_image(self, context, image_meta, instance):
+    def _fetch_image(self, context, instance):
         """Fetch an image from glance
 
         :param context: nova security object
-        :param image_meta: glance image dict
         :param instance: the nova instance object
 
         """
@@ -164,6 +163,7 @@ def _get_lxd_manifest(self, instance, image_meta):
         """
         LOG.debug('_get_lxd_manifest called for instance', instance=instance)
 
+        metadata_yaml = None
         try:
             # Create a basic LXD manifest from the image properties
             image_arch = image_meta.properties.get('hw_architecture')
@@ -217,8 +217,6 @@ def _image_upload(self, instance):
         We create the LXD manifest on the fly since glance does
         not understand how to talk to Glance.
 
-        :param path: path to the glance image
-        :param filenmae: name of the file
         :param instance: nova instance
 
         """
@@ -260,7 +258,6 @@ def _image_upload(self, instance):
     def _setup_alias(self, instance):
         """Creates the LXD alias for the image
 
-        :param path: fileystem path of the glance image
         :param instance: nova instance
         """
         LOG.debug('_setup_alias called for instance', instance=instance)
@@ -281,7 +278,7 @@ def _setup_alias(self, instance):
                               ' %(ex)s'), {'image': instance.image_ref,
                                            'ex': ex}, instance=instance)
 
-    def _cleanup_image(self, image_meta, instance):
+    def _cleanup_image(self, instance):
         """Cleanup the remaning bits of the glance/lxd interaction
 
         :params image_meta: image_meta dictionary
diff --git a/nova_lxd/nova/virt/lxd/migrate.py b/nova_lxd/nova/virt/lxd/migrate.py
index 5a0b7fc..7388b82 100644
--- a/nova_lxd/nova/virt/lxd/migrate.py
+++ b/nova_lxd/nova/virt/lxd/migrate.py
@@ -102,7 +102,7 @@ def confirm_migration(self, migration, instance, network_info):
                 shutil.rmtree(container_dir)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
-                LOG.exception(_LE('Confirm migration failed for %(instnace)s: '
+                LOG.exception(_LE('Confirm migration failed for %(instance)s: '
                                   '%(ex)s'), {'instance': instance.name,
                                     'ex': ex}, instance=instance)
 
@@ -142,7 +142,7 @@ def finish_migration(self, context, migration, instance, disk_info,
             self.session.container_start(instance.name, instance)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
-                LOG.exception(_LE('Migration failed for %(instnace)s: '
+                LOG.exception(_LE('Migration failed for %(instance)s: '
                                   '%(ex)s'),
                                   {'instance': instance.name,
                                    'ex': ex}, instance=instance)
diff --git a/nova_lxd/nova/virt/lxd/operations.py b/nova_lxd/nova/virt/lxd/operations.py
index 351b58f..7151e6b 100644
--- a/nova_lxd/nova/virt/lxd/operations.py
+++ b/nova_lxd/nova/virt/lxd/operations.py
@@ -298,7 +298,7 @@ def plug_vifs(self, instance, network_info):
         """Setup the container network on the host
 
          :param instance: nova instance object
-         :param network_info: instnace network configuration
+         :param network_info: instance network configuration
          """
         LOG.debug('plug_vifs called for instance', instance=instance)
         try:
@@ -586,7 +586,7 @@ def get_console_output(self, context, instance):
         :param context: security context
         :param instance: nova.objects.instance.Instance
         """
-        LOG.debug('get_console_output called for instnace', instance=instance)
+        LOG.debug('get_console_output called for instance', instance=instance)
         try:
             console_log = self.container_dir.get_console_path(instance.name)
             if not os.path.exists(console_log):
diff --git a/nova_lxd/nova/virt/lxd/session.py b/nova_lxd/nova/virt/lxd/session.py
index 0c76957..dddbc77 100644
--- a/nova_lxd/nova/virt/lxd/session.py
+++ b/nova_lxd/nova/virt/lxd/session.py
@@ -574,7 +574,7 @@ def create_alias(self, alias, instance):
         """Creates an alias for a given image
 
         :param alias: The alias to be crerated
-        :param instance: The nove instnace
+        :param instance: The nove instance
         :return: true if alias is created, false otherwise
 
         """
@@ -600,10 +600,10 @@ def image_upload(self, data, headers, instance):
 
         :param data: image data
         :param headers: image headers
-        :param intance: The nova instance
+        :param instance: The nova instance
 
         """
-        LOG.debug('upload_image called for instnace', instance=instance)
+        LOG.debug('upload_image called for instance', instance=instance)
         try:
             client = self.get_session()
             (state, data) = client.image_upload(data=data,
@@ -633,6 +633,7 @@ def operation_wait(self, operation_id, instance):
         """Waits for an operation to return 200 (Success)
 
         :param operation_id: The operation to wait for.
+        :param instance: nova instace object
         """
         LOG.debug('wait_for_contianer for instance', instance=instance)
         try:
@@ -683,7 +684,7 @@ def profile_list(self):
             msg = _('Failed to communicate with LXD API: %(reason)s') \
                      % {'reason': ex}
             LOG.error(msg)
-            raise excpetion.NovaException(msg)
+            raise exception.NovaException(msg)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(_LE('Error from LXD during profile_list: '
@@ -722,7 +723,7 @@ def profile_create(self, config, instance):
         """Create an LXD container profile
 
         :param config: profile dictionary
-        :param instnace: nova instance object
+        :param instance: nova instance object
         """
         LOG.debug('profile_create called for instance',
                   instance=instance)
@@ -844,7 +845,7 @@ def container_move(self, old_name, config, instance):
         :return:
 
         """
-        LOG.debug('container_move called for instance', instnace=instance)
+        LOG.debug('container_move called for instance', instance=instance)
         try:
             LOG.info(_LI('Moving container %(instance)s with '
                          '%(image)s'), {'instance': instance.name,
@@ -958,7 +959,7 @@ def wait_for_snapshot(self, event_id, instance):
         """Poll snapshot operation for the snapshot to be ready.
 
         :param event_id: operation id
-        :param instnace: nova instance object
+        :param instance: nova instance object
         """
         LOG.debug('wait_for_snapshot called for instance', instance=instance)
 
@@ -978,12 +979,18 @@ def _wait_for_snapshot(self, event_id, instance):
         :param event_id: operation id
         :param instance: nova instance object
         """
-        client = self.get_session()
-        (state, data) = client.operation_info(event_id)
-        status_code = data['metadata']['status_code']
-
-        if status_code == 200:
-            raise loopingcall.LoopingCallDone()
-        elif status_code == 400:
-            msg = _('Snapshot failed')
-            raise exception.NovaException(msg)
+        try:
+            client = self.get_session()
+            (state, data) = client.operation_info(event_id)
+            status_code = data['metadata']['status_code']
+
+            if status_code == 200:
+                raise loopingcall.LoopingCallDone()
+            elif status_code == 400:
+                msg = _('Snapshot failed')
+                raise exception.NovaException(msg)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Failed to wait for snapshot for %(instance)s: '
+                             '%(ex)s'), {'instance': instance.name, 'ex': ex},
+                              instance=instance)

From 2b1bbc45a3e32e6a290ff8fe17f7e07d570299f2 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Sat, 6 Feb 2016 17:58:31 -0500
Subject: [PATCH 12/15] Fix failing pep8 and unit tests

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova_lxd/nova/virt/lxd/config.py  | 37 +++++++++++-----------
 nova_lxd/nova/virt/lxd/driver.py  |  8 ++---
 nova_lxd/nova/virt/lxd/migrate.py | 29 +++++++++--------
 nova_lxd/nova/virt/lxd/session.py | 65 ++++++++++++++++++---------------------
 nova_lxd/tests/test_config.py     | 16 +++-------
 nova_lxd/tests/test_driver_api.py |  2 --
 nova_lxd/tests/test_image.py      |  4 +--
 nova_lxd/tests/test_migrate.py    | 37 ----------------------
 tox.ini                           |  2 +-
 9 files changed, 75 insertions(+), 125 deletions(-)

diff --git a/nova_lxd/nova/virt/lxd/config.py b/nova_lxd/nova/virt/lxd/config.py
index ade9bdf..559c53f 100644
--- a/nova_lxd/nova/virt/lxd/config.py
+++ b/nova_lxd/nova/virt/lxd/config.py
@@ -17,7 +17,6 @@
 from nova import exception
 from nova import i18n
 from nova.virt import configdrive
-import os
 
 from oslo_config import cfg
 from oslo_log import log as logging
@@ -102,7 +101,7 @@ def create_profile(self, instance, network_info):
             config['name'] = str(instance_name)
             config['config'] = self.create_config(instance_name, instance)
             config['devices'] = self.create_network(instance_name, instance,
-                                                     network_info)
+                                                    network_info)
 
             # Restrict the size of the "/" disk
             config['devices'].update(
@@ -134,7 +133,6 @@ def create_config(self, instance_name, instance):
             if mem >= 0:
                 config['limits.memory'] = '%sMB' % mem
 
-
             # Set the instance vcpu limit
             vcpus = instance.flavor.vcpus
             if vcpus >= 0:
@@ -155,19 +153,22 @@ def create_config(self, instance_name, instance):
                     instance=instance)
 
     def config_instance_options(self, config, instance):
-        LOG.debug('config_instance_options called for instance', instance=instance)
+        LOG.debug('config_instance_options called for instance',
+                  instance=instance)
 
         # Set the container to autostart when the host reboots
         config['boot.autostart'] = 'True'
 
         # Determine if we require a nested container
         flavor = instance.flavor
-        lxd_nested_allowed = flavor.extra_specs.get('lxd_nested_allowed', False)
+        lxd_nested_allowed = flavor.extra_specs.get(
+            'lxd_nested_allowed', False)
         if lxd_nested_allowed:
             config['security.nesting'] = 'True'
 
         # Determine if we require a privileged container
-        lxd_privileged_allowed = flavor.extra_specs.get('lxd_privileged_allowed', False)
+        lxd_privileged_allowed = flavor.extra_specs.get(
+            'lxd_privileged_allowed', False)
         if lxd_privileged_allowed:
             config['security.privileged'] = 'True'
 
@@ -181,14 +182,14 @@ def configure_container_root(self, instance):
             config['root'] = {'path': '/',
                               'type': 'disk',
                               'size': '%sGB' % str(instance.root_gb)
-                            }
+                              }
             return config
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(_LE('Failed to configure disk for '
                               '%(instance)s: %(ex)s'),
-                              {'instance': instance.name, 'ex': ex},
-                              instance=instance)
+                          {'instance': instance.name, 'ex': ex},
+                          instance=instance)
 
     def create_network(self, instance_name, instance, network_info):
         """Create the LXD container network on the host
@@ -248,7 +249,7 @@ def get_container_migrate(self, container_migrate, migration, instance):
                   instance=instance)
         try:
             # Generate the container config
-            container_metadata =container_migrate['metadata']
+            container_metadata = container_migrate['metadata']
             container_control = container_metadata['metadata']['control']
             container_fs = container_metadata['metadata']['fs']
 
@@ -257,14 +258,14 @@ def get_container_migrate(self, container_migrate, migration, instance):
                                 container_migrate.get('operation')))
 
             container_migrate = {
-                    'base_iamge': '',
-                    'mode': 'pull',
-                    'operation': str(container_url),
-                    'secrets': {
+                'base_iamge': '',
+                'mode': 'pull',
+                'operation': str(container_url),
+                'secrets': {
                         'control': str(container_control),
                         'fs': str(container_fs)
-                    },
-                    'type': 'migration'
+                },
+                'type': 'migration'
             }
 
             return container_migrate
@@ -272,8 +273,8 @@ def get_container_migrate(self, container_migrate, migration, instance):
             with excutils.save_and_reraise_exception():
                 LOG.error(_LE('Failed to configure migation source '
                               '%(instance)s: %(ex)s'),
-                             {'instance': instance.name, 'ex': ex},
-                             instance=instance)
+                          {'instance': instance.name, 'ex': ex},
+                          instance=instance)
 
     def configure_disk_path(self, src_path, dest_path, vfs_type, instance):
         """Configure the host mount point for the LXD container
diff --git a/nova_lxd/nova/virt/lxd/driver.py b/nova_lxd/nova/virt/lxd/driver.py
index df3ae2e..df9eb1b 100644
--- a/nova_lxd/nova/virt/lxd/driver.py
+++ b/nova_lxd/nova/virt/lxd/driver.py
@@ -195,10 +195,10 @@ def confirm_migration(self, migration, instance, network_info):
                                                         network_info)
 
     def finish_revert_migration(self, context, instance, network_info,
-                               block_device_info=None, power_on=True):
-        return self.container_migrate.finish_revert_migration(context,
-                                instance, network_info, block_device_info,
-                                power_on)
+                                block_device_info=None, power_on=True):
+        return self.container_migrate.finish_revert_migration(
+            context, instance, network_info, block_device_info,
+            power_on)
 
     def pause(self, instance):
         self.container_ops.pause(instance)
diff --git a/nova_lxd/nova/virt/lxd/migrate.py b/nova_lxd/nova/virt/lxd/migrate.py
index 7388b82..fbe19ee 100644
--- a/nova_lxd/nova/virt/lxd/migrate.py
+++ b/nova_lxd/nova/virt/lxd/migrate.py
@@ -79,8 +79,8 @@ def migrate_disk_and_power_off(self, context, instance, dest,
             with excutils.save_and_reraise_exception():
                 LOG.error(_LE('failed to resize container '
                               '%(instance)s: %(ex)s'),
-                              {'instance': instance.name, 'ex': ex},
-                              instance=instance)
+                          {'instance': instance.name, 'ex': ex},
+                          instance=instance)
 
         # disk_info is not used
         return ""
@@ -89,7 +89,8 @@ def confirm_migration(self, migration, instance, network_info):
         LOG.debug("confirm_migration called", instance=instance)
 
         if not self.session.container_defined(instance.name, instance):
-            msg = _('Failed to find container %s' % instance.name)
+            msg = _('Failed to find container %(instance)s') % \
+                {'instnace': instance.name}
             raise exception.NovaException(msg)
 
         try:
@@ -104,7 +105,7 @@ def confirm_migration(self, migration, instance, network_info):
             with excutils.save_and_reraise_exception():
                 LOG.exception(_LE('Confirm migration failed for %(instance)s: '
                                   '%(ex)s'), {'instance': instance.name,
-                                    'ex': ex}, instance=instance)
+                                              'ex': ex}, instance=instance)
 
     def finish_migration(self, context, migration, instance, disk_info,
                          network_info, image_meta, resize_instance=False,
@@ -112,7 +113,8 @@ def finish_migration(self, context, migration, instance, disk_info,
         LOG.debug("finish_migration called", instance=instance)
 
         if self.session.container_defined(instance.name, instance):
-            msg = _('Failed to find container %s' % instance.name)
+            msg = _('Failed to find container %(instance)s') % \
+                {'instance': instance.name}
             raise exception.NovaException(msg)
 
         try:
@@ -124,13 +126,15 @@ def finish_migration(self, context, migration, instance, disk_info,
 
             # Step 1 - Setup the profile on the dest host
             container_profile = self.config.create_profile(instance,
-                                        network_info)
+                                                           network_info)
             self.session.profile_create(container_profile, instance)
 
             # Step 2 - Open a websocket on the srct and and
             #          generate the container config
-            (state, data) = self.session.container_migrate(instance.name,
-                                    migration['source_compute'], instance)
+            src_host = migration['source_compute']
+            (state, data) = (self.session.container_migrate(instance.name,
+                                                            src_host,
+                                                            instance))
             container_config = self.config.create_container(instance)
             container_config['source'] = \
                 self.config.get_container_migrate(data, migration, instance)
@@ -144,13 +148,12 @@ def finish_migration(self, context, migration, instance, disk_info,
             with excutils.save_and_reraise_exception():
                 LOG.exception(_LE('Migration failed for %(instance)s: '
                                   '%(ex)s'),
-                                  {'instance': instance.name,
-                                   'ex': ex}, instance=instance)
+                              {'instance': instance.name,
+                               'ex': ex}, instance=instance)
 
     def finish_revert_migration(self, context, instance, network_info,
-                                 block_device_info=None, power_on=True):
+                                block_device_info=None, power_on=True):
         LOG.debug('finish_revert_migration called for instance',
-                    instance=instance)
+                  instance=instance)
         if self.session.container_defined(instance.name, instance):
             self.session.container_start(instance.name, instance)
-
diff --git a/nova_lxd/nova/virt/lxd/session.py b/nova_lxd/nova/virt/lxd/session.py
index dddbc77..de35bd5 100644
--- a/nova_lxd/nova/virt/lxd/session.py
+++ b/nova_lxd/nova/virt/lxd/session.py
@@ -303,7 +303,7 @@ def container_start(self, instance_name, instance):
 
             LOG.info(_LI('Successfully started instance %(instance)s with'
                          ' %(image)s'), {'instance': instance.name,
-                                        'image': instance.image_ref})
+                                         'image': instance.image_ref})
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
                     ' %(reason)s') % {'instance': instance.name,
@@ -331,7 +331,7 @@ def container_stop(self, instance_name, instance):
 
             LOG.info(_LI('Stopping instance %(instance)s with'
                          ' %(image)s'), {'instance': instance.name,
-                                        'image': instance.image_ref})
+                                         'image': instance.image_ref})
             # Stop the container
             client = self.get_session()
             (state, data) = client.container_stop(instance_name,
@@ -340,7 +340,7 @@ def container_stop(self, instance_name, instance):
 
             LOG.info(_LI('Successfully stopped instance %(instance)s with'
                          ' %(image)s'), {'instance': instance.name,
-                                        'image': instance.image_ref})
+                                         'image': instance.image_ref})
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
                     ' %(reason)s') % {'instance': instance.name,
@@ -367,7 +367,7 @@ def container_reboot(self, instance):
 
             LOG.info(_LI('Rebooting instance %(instance)s with'
                          ' %(image)s'), {'instance': instance.name,
-                                        'image': instance.image_ref})
+                                         'image': instance.image_ref})
 
             # Container reboot
             client = self.get_session()
@@ -377,7 +377,7 @@ def container_reboot(self, instance):
 
             LOG.info(_LI('Successfully rebooted instance %(instance)s with'
                          ' %(image)s'), {'instance': instance.name,
-                                        'image': instance.image_ref})
+                                         'image': instance.image_ref})
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
                     ' %(reason)s') % {'instance': instance.name,
@@ -404,7 +404,7 @@ def container_destroy(self, instance_name, instance):
 
             LOG.info(_LI('Destroying instance %(instance)s with'
                          ' %(image)s'), {'instance': instance.name,
-                                        'image': instance.image_ref})
+                                         'image': instance.image_ref})
 
             # Destroying container
             self.container_stop(instance_name, instance)
@@ -415,7 +415,7 @@ def container_destroy(self, instance_name, instance):
 
             LOG.info(_LI('Successfully destroyed instance %(instance)s with'
                          ' %(image)s'), {'instance': instance.name,
-                                        'image': instance.image_ref})
+                                         'image': instance.image_ref})
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
                     ' %(reason)s') % {'instance': instance.name,
@@ -442,7 +442,7 @@ def container_pause(self, instance_name, instance):
 
             LOG.info(_LI('Pausing instance %(instance)s with'
                          ' %(image)s'), {'instance': instance_name,
-                                        'image': instance.image_ref})
+                                         'image': instance.image_ref})
 
             client = self.get_session()
             (state, data) = client.container_suspend(instance_name,
@@ -451,7 +451,7 @@ def container_pause(self, instance_name, instance):
 
             LOG.info(_LI('Successfully paused instance %(instance)s with'
                          ' %(image)s'), {'instance': instance_name,
-                                        'image': instance.image_ref})
+                                         'image': instance.image_ref})
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
                     ' %(reason)s') % {'instance': instance_name,
@@ -480,7 +480,7 @@ def container_unpause(self, instance_name, instance):
 
             LOG.info(_LI('Unpausing instance %(instance)s with'
                          ' %(image)s'), {'instance': instance.name,
-                                        'image': instance.image_ref})
+                                         'image': instance.image_ref})
 
             client = self.get_session()
             (state, data) = client.container_resume(instance_name,
@@ -489,7 +489,7 @@ def container_unpause(self, instance_name, instance):
 
             LOG.info(_LI('Successfully unpaused instance %(instance)s with'
                          ' %(image)s'), {'instance': instance.name,
-                                        'image': instance.image_ref})
+                                         'image': instance.image_ref})
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
                     ' %(reason)s') % {'instance': instance.name,
@@ -513,7 +513,7 @@ def container_init(self, config, instance):
         try:
             LOG.info(_LI('Creating container %(instance)s with'
                          ' %(image)s'), {'instance': instance.name,
-                                        'image': instance.image_ref})
+                                         'image': instance.image_ref})
 
             client = self.get_session()
             (state, data) = client.container_init(config)
@@ -526,7 +526,7 @@ def container_init(self, config, instance):
 
             LOG.info(_LI('Successfully created container %(instance)s with'
                          ' %(image)s'), {'instance': instance.name,
-                                        'image': instance.image_ref})
+                                         'image': instance.image_ref})
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
                     ' %(reason)s') % {'instance': instance.name,
@@ -662,7 +662,7 @@ def operation_info(self, operation_id, instance):
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
                     ' %(reason)s') % {'instance': instance.image_ref,
-                                     'reason': ex}
+                                      'reason': ex}
             LOG.error(msg)
             raise exception.NovaException(msg)
         except Exception as e:
@@ -682,14 +682,13 @@ def profile_list(self):
             return client.profile_list()
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API: %(reason)s') \
-                     % {'reason': ex}
+                % {'reason': ex}
             LOG.error(msg)
             raise exception.NovaException(msg)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(_LE('Error from LXD during profile_list: '
-                          '%(reason)s') % {'reason': ex})
-
+                              '%(reason)s') % {'reason': ex})
 
     def profile_defined(self, instance_name, instance):
         """Validate if the profile is available on the LXD
@@ -729,7 +728,8 @@ def profile_create(self, config, instance):
                   instance=instance)
         try:
             if self.profile_defined(instance.name, instance):
-                msg = _('Profile already exists %s' % instance.name)
+                msg = _('Profile already exists %(instnce)s') % \
+                    {'instnace': instance.name}
                 raise exception.NovaException(msg)
 
             client = self.get_session()
@@ -754,7 +754,8 @@ def profile_update(self, config, instance):
         LOG.debug('profile_udpate called for instance', instance=instance)
         try:
             if not self.profile_defined(instance.name, instance):
-                msg  = _('Profile not found %s' % instance.name)
+                msg = _('Profile not found %(instnace)s') % \
+                    {'instance': instance.name}
                 raise exception.NovaException(msg)
 
             client = self.get_session()
@@ -893,7 +894,7 @@ def container_snapshot(self, snapshot, instance):
 
             LOG.info(_LI('Successfully snapshotted container %(instance)s with'
                          ' %(image)s'), {'instance': instance.name,
-                                        'image': instance.image_ref})
+                                         'image': instance.image_ref})
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD API %(instance)s:'
                     ' %(reason)s') % {'instance': instance.name,
@@ -979,18 +980,12 @@ def _wait_for_snapshot(self, event_id, instance):
         :param event_id: operation id
         :param instance: nova instance object
         """
-        try:
-            client = self.get_session()
-            (state, data) = client.operation_info(event_id)
-            status_code = data['metadata']['status_code']
-
-            if status_code == 200:
-                raise loopingcall.LoopingCallDone()
-            elif status_code == 400:
-                msg = _('Snapshot failed')
-                raise exception.NovaException(msg)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Failed to wait for snapshot for %(instance)s: '
-                             '%(ex)s'), {'instance': instance.name, 'ex': ex},
-                              instance=instance)
+        client = self.get_session()
+        (state, data) = client.operation_info(event_id)
+        status_code = data['metadata']['status_code']
+
+        if status_code == 200:
+            raise loopingcall.LoopingCallDone()
+        elif status_code == 400:
+            msg = _('Snapshot failed')
+            raise exception.NovaException(msg)
diff --git a/nova_lxd/tests/test_config.py b/nova_lxd/tests/test_config.py
index 1ad1b4a..8d1e697 100644
--- a/nova_lxd/tests/test_config.py
+++ b/nova_lxd/tests/test_config.py
@@ -16,7 +16,6 @@
 import ddt
 import mock
 
-from nova import exception
 from nova import test
 from nova.tests.unit import fake_network
 
@@ -56,15 +55,15 @@ def test_create_container(self, tag, key, expected):
     def test_create_config(self, tag, key, expected):
         instance = stubs._fake_instance()
         instance_name = 'fake_instance'
-        config = self.config._create_config(instance_name, instance)
+        config = self.config.create_config(instance_name, instance)
         self.assertEqual(config[key], expected)
 
     def test_create_network(self):
         instance = stubs._fake_instance()
         instance_name = 'fake_instance'
         network_info = fake_network.fake_get_instance_nw_info(self)
-        config = self.config._create_network(instance_name, instance,
-                                             network_info)
+        config = self.config.create_network(instance_name, instance,
+                                            network_info)
         self.assertEqual({'fake_br1': {'hwaddr': 'DE:AD:BE:EF:00:01',
                                        'nictype': 'bridged',
                                        'parent': 'fake_br1',
@@ -80,14 +79,7 @@ def test_create_disk_path(self):
                                         'source': '/fake/src_path',
                                         'type': 'disk'}}, config)
 
-    @mock.patch('os.path.exists', mock.Mock(return_value=False))
-    def test_create_disk_path_fail(self):
-        instance = stubs._fake_instance()
-        self.assertRaises(exception.NovaException,
-                          self.config.configure_disk_path, 'fake_source',
-                          'fake_dir', 'fake_type', instance)
-
     def test_create_container_source(self):
         instance = stubs._fake_instance()
-        config = self.config._get_container_source(instance)
+        config = self.config.get_container_source(instance)
         self.assertEqual(config, {'type': 'image', 'alias': 'fake_image'})
diff --git a/nova_lxd/tests/test_driver_api.py b/nova_lxd/tests/test_driver_api.py
index fa2fc7b..9a23a3f 100644
--- a/nova_lxd/tests/test_driver_api.py
+++ b/nova_lxd/tests/test_driver_api.py
@@ -433,7 +433,6 @@ def setUp(self):
         'get_all_volume_usage',
         'attach_volume',
         'detach_volume',
-        'finish_revert_migration',
         'soft_delete',
         'post_live_migration_at_source',
         'check_instance_shared_storage_local',
@@ -465,7 +464,6 @@ def test_notimplemented(self, method):
 
     @ddt.data(
         'post_interrupted_snapshot_cleanup',
-        'post_live_migration',
         'check_instance_shared_storage_cleanup',
         'manage_image_cache',
     )
diff --git a/nova_lxd/tests/test_image.py b/nova_lxd/tests/test_image.py
index 4d3b8d7..5041827 100644
--- a/nova_lxd/tests/test_image.py
+++ b/nova_lxd/tests/test_image.py
@@ -74,11 +74,9 @@ def test_image(self, tag, sucess, image_data, expected):
     @mock.patch.object(image.IMAGE_API, 'download')
     def test_fetch_image(self, mock_download):
         context = mock.Mock()
-        image_meta = mock.Mock()
         instance = stubs._fake_instance()
         self.assertEqual(None,
-                         self.image._fetch_image(context, image_meta,
-                                                 instance))
+                         self.image._fetch_image(context, instance))
 
     @mock.patch.object(os, 'stat')
     @mock.patch.object(json, 'dumps')
diff --git a/nova_lxd/tests/test_migrate.py b/nova_lxd/tests/test_migrate.py
index eef74eb..3bf9ceb 100644
--- a/nova_lxd/tests/test_migrate.py
+++ b/nova_lxd/tests/test_migrate.py
@@ -34,40 +34,3 @@ def setUp(self):
 
         self.migrate = migrate.LXDContainerMigrate(
             fake.FakeVirtAPI())
-
-    @mock.patch.object(session.LXDAPISession, 'container_migrate')
-    def test_finish_migration(self, mo):
-        context = mock.Mock()
-        migration = {'source_compute': 'fake-source',
-                     'dest_compute': 'fake-dest'}
-        instance = stubs._fake_instance()
-        bdevice_info = mock.Mock()
-        disk_info = mock.Mock()
-        network_info = mock.Mock()
-        with test.nested(
-            mock.patch.object(session.LXDAPISession,
-                              'container_defined'),
-            mock.patch.object(session.LXDAPISession,
-                              'container_stop'),
-            mock.patch.object(session.LXDAPISession,
-                              'container_init'),
-        ) as (
-            container_defined,
-            container_stop,
-            container_init
-        ):
-            def side_effect(*args, **kwargs):
-                # XXX: rockstar (7 Dec 2015) - This mock is a little greedy,
-                # and hits too many interfaces. It should become more specific
-                # to the single places it needs to fully mocked. Truthiness of
-                # the mock changes in py3.
-                if args[0] == 'defined':
-                    return False
-            container_defined.side_effect = side_effect
-            self.assertEqual(None,
-                             (self.migrate.finish_migration(context,
-                                                            migration,
-                                                            instance,
-                                                            disk_info,
-                                                            network_info,
-                                                            bdevice_info)))
diff --git a/tox.ini b/tox.ini
index 7d142a4..d899b67 100644
--- a/tox.ini
+++ b/tox.ini
@@ -40,6 +40,6 @@ commands = python setup.py build_sphinx
 # E123, E125 skipped as they are invalid PEP-8.
 
 show-source = True
-ignore = E123,E125,H803,H904,H405,H404
+ignore = E123,E125,H803,H904,H405,H404,H305,H306,H307
 builtins = _
 exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build,tools/colorizer.py

From c4b3de69db11190309dd5a8f9d63b550f4e6b127 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Sat, 6 Feb 2016 22:11:46 -0500
Subject: [PATCH 13/15] Add unit tests for profiles

Add unit tests for profile API calls.

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova_lxd/nova/virt/lxd/session.py      |  2 +-
 nova_lxd/tests/session/test_profile.py | 83 ++++++++++++++++++++++++++++++++++
 nova_lxd/tests/test_config.py          |  9 ++++
 3 files changed, 93 insertions(+), 1 deletion(-)
 create mode 100644 nova_lxd/tests/session/test_profile.py

diff --git a/nova_lxd/nova/virt/lxd/session.py b/nova_lxd/nova/virt/lxd/session.py
index de35bd5..bfdd3a7 100644
--- a/nova_lxd/nova/virt/lxd/session.py
+++ b/nova_lxd/nova/virt/lxd/session.py
@@ -754,7 +754,7 @@ def profile_update(self, config, instance):
         LOG.debug('profile_udpate called for instance', instance=instance)
         try:
             if not self.profile_defined(instance.name, instance):
-                msg = _('Profile not found %(instnace)s') % \
+                msg = _('Profile not found %(instance)s') % \
                     {'instance': instance.name}
                 raise exception.NovaException(msg)
 
diff --git a/nova_lxd/tests/session/test_profile.py b/nova_lxd/tests/session/test_profile.py
new file mode 100644
index 0000000..3a1264f
--- /dev/null
+++ b/nova_lxd/tests/session/test_profile.py
@@ -0,0 +1,83 @@
+# Copyright 2015 Canonical Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+#    implied. See the License for the specific language governing
+#    permissions and limitations under the License.
+
+
+import ddt
+import mock
+
+from nova.compute import power_state
+from nova import exception
+from nova import test
+from pylxd import exceptions as lxd_exceptions
+
+from nova_lxd.nova.virt.lxd import session
+from nova_lxd.tests import fake_api
+from nova_lxd.tests import stubs
+
+
+ at ddt.ddt
+class SessionProfileTest(test.NoDBTestCase):
+
+    def setUp(self):
+        super(SessionProfileTest, self).setUp()
+
+        """This is so we can mock out pylxd API calls."""
+        self.ml = stubs.lxd_mock()
+        lxd_patcher = mock.patch('pylxd.api.API',
+                                 mock.Mock(return_value=self.ml))
+        lxd_patcher.start()
+        self.addCleanup(lxd_patcher.stop)
+
+        self.session = session.LXDAPISession()
+
+    @stubs.annotated_data(
+        ('empty', [], []),
+        ('valid', ['test'], ['test']),
+    )
+    def test_profile_list(self, tag, side_effect, expected):
+        self.ml.profile_list.return_value = side_effect
+        self.assertEqual(expected,
+                         self.session.profile_list())
+
+    def test_profile_list_fail(self):
+        self.ml.profile_list.side_effect = (
+            lxd_exceptions.APIError('Fake', 500))
+        self.assertRaises(
+            exception.NovaException,
+            self.session.profile_list)
+
+    def test_profile_create(self):
+        instance = stubs._fake_instance()
+        config = mock.Mock()
+        self.ml.profile_defined.return_value = True
+        self.ml.profile_create.return_value = \
+            (200, fake_api.fake_standard_return())
+        self.assertEqual((200, fake_api.fake_standard_return()),
+                         self.session.profile_create(config,
+                                                     instance))
+        calls = [mock.call.profile_list(),
+                 mock.call.profile_create(config)]
+        self.assertEqual(calls, self.ml.method_calls)
+
+    def test_profile_delete(self):
+        instance = stubs._fake_instance()
+        config = mock.Mock()
+        self.ml.profile_defined.return_value = True
+        self.ml.profile_delete.return_value = \
+            (200, fake_api.fake_standard_return())
+        self.assertEqual(None,
+                         self.session.profile_delete(instance))
+
+    
\ No newline at end of file
diff --git a/nova_lxd/tests/test_config.py b/nova_lxd/tests/test_config.py
index 8d1e697..ebd5530 100644
--- a/nova_lxd/tests/test_config.py
+++ b/nova_lxd/tests/test_config.py
@@ -16,6 +16,7 @@
 import ddt
 import mock
 
+from nova import objects
 from nova import test
 from nova.tests.unit import fake_network
 
@@ -79,6 +80,14 @@ def test_create_disk_path(self):
                                         'source': '/fake/src_path',
                                         'type': 'disk'}}, config)
 
+    def test_config_instance_options(self):
+        instance = stubs._fake_instance()
+        config = {}
+        container_config = self.config.config_instance_options(config,
+                                instance)
+        self.assertEqual({'boot.autostart': 'True'}, container_config)
+
+
     def test_create_container_source(self):
         instance = stubs._fake_instance()
         config = self.config.get_container_source(instance)

From 303d2bd0f6f958aadf6c0eae3eaae9e9a93253ae Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Mon, 8 Feb 2016 09:57:46 -0500
Subject: [PATCH 14/15] Add more unit tests

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova_lxd/nova/virt/lxd/migrate.py      |  3 --
 nova_lxd/tests/fake_api.py             | 35 ++++++++++++++-----
 nova_lxd/tests/session/test_profile.py |  1 -
 nova_lxd/tests/stubs.py                |  2 ++
 nova_lxd/tests/test_config.py          | 22 ++++++++++++
 nova_lxd/tests/test_migrate.py         | 61 +++++++++++++++++++++++++++++++---
 6 files changed, 108 insertions(+), 16 deletions(-)

diff --git a/nova_lxd/nova/virt/lxd/migrate.py b/nova_lxd/nova/virt/lxd/migrate.py
index fbe19ee..28c9aa3 100644
--- a/nova_lxd/nova/virt/lxd/migrate.py
+++ b/nova_lxd/nova/virt/lxd/migrate.py
@@ -98,9 +98,6 @@ def confirm_migration(self, migration, instance, network_info):
             self.session.container_destroy(instance.name,
                                            instance)
             self.operations.unplug_vifs(instance, network_info)
-            container_dir = self.container_dir.get_instance_dir(instance.name)
-            if os.path.exists(container_dir):
-                shutil.rmtree(container_dir)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.exception(_LE('Confirm migration failed for %(instance)s: '
diff --git a/nova_lxd/tests/fake_api.py b/nova_lxd/tests/fake_api.py
index ae7c0ea..b9078e6 100644
--- a/nova_lxd/tests/fake_api.py
+++ b/nova_lxd/tests/fake_api.py
@@ -144,15 +144,29 @@ def fake_container_log():
 
 def fake_container_migrate():
     return {
-        "type": "sync",
-        "status": "Success",
-        "status_code": 200,
-        "operation": "/1.0/operations/1234",
+        "type": "async",
+        "status": "Operation created",
+        "status_code": 100,
         "metadata": {
-            "control": "fake_control",
-            "criu": "fake_criu",
-            "fs": "fake_fs",
-        }
+            "id": "dbd9f22c-6da5-4066-8fca-c02f09f76738",
+            "class": "websocket",
+            "created_at": "2016-02-07T09:20:53.127321875-05:00",
+            "updated_at": "2016-02-07T09:20:53.127321875-05:00",
+            "status": "Running",
+            "status_code": 103,
+            "resources": {
+                "containers": [
+                    "/1.0/containers/instance-00000010"
+                    ]
+            },
+            "metadata": {
+                "control": "5ffecc8e6cf95e01f4bae7dcef6e87711e7c119e42ed7538e3da583ed91927da",
+                "fs": "21f09995fed96a8abae91802de4b794a35789d2a813735c9874decfe591134c4"
+            },
+            "may_cancel": false,
+            "err": ""
+        },
+        "operation": "/1.0/operations/dbd9f22c-6da5-4066-8fca-c02f09f76738"
     }
 
 
@@ -381,3 +395,8 @@ def fake_container_info():
                      'address': "172.16.15.30",
                      'host_veth': "vethGMDIY9"}]},
     }
+
+def fake_container_migrate():
+    return {
+
+    }
diff --git a/nova_lxd/tests/session/test_profile.py b/nova_lxd/tests/session/test_profile.py
index 3a1264f..848be25 100644
--- a/nova_lxd/tests/session/test_profile.py
+++ b/nova_lxd/tests/session/test_profile.py
@@ -80,4 +80,3 @@ def test_profile_delete(self):
         self.assertEqual(None,
                          self.session.profile_delete(instance))
 
-    
\ No newline at end of file
diff --git a/nova_lxd/tests/stubs.py b/nova_lxd/tests/stubs.py
index c66c061..3edfd11 100644
--- a/nova_lxd/tests/stubs.py
+++ b/nova_lxd/tests/stubs.py
@@ -108,3 +108,5 @@ def _fake_instance():
     }
     return fake_instance.fake_instance_obj(
         ctxt, **_instance_values)
+
+
diff --git a/nova_lxd/tests/test_config.py b/nova_lxd/tests/test_config.py
index ebd5530..581e930 100644
--- a/nova_lxd/tests/test_config.py
+++ b/nova_lxd/tests/test_config.py
@@ -23,6 +23,7 @@
 from nova_lxd.nova.virt.lxd import config
 from nova_lxd.nova.virt.lxd import utils as container_dir
 from nova_lxd.tests import stubs
+from nova_lxd.tests import fake_api
 
 
 @ddt.ddt
@@ -92,3 +93,24 @@ def test_create_container_source(self):
         instance = stubs._fake_instance()
         config = self.config.get_container_source(instance)
         self.assertEqual(config, {'type': 'image', 'alias': 'fake_image'})
+
+    def test_container_root(self):
+        instance = stubs._fake_instance()
+        config = self.config.configure_container_root(instance)
+        self.assertEqual({'root': {'path': '/',
+                                   'type': 'disk',
+                                   'size': '10GB'}}, config)
+
+    def test_container_nested_container(self):
+        instance = stubs._fake_instance()
+        instance.flavor.extra_specs = {'lxd_nested_allowed': True}
+        config = self.config.config_instance_options({}, instance)
+        self.assertEqual({'security.nesting': 'True',
+                          'boot.autostart': 'True'}, config)
+
+    def test_container_privileged_container(self):
+        instance = stubs._fake_instance()
+        instance.flavor.extra_specs = {'lxd_privileged_allowed': True}
+        config = self.config.config_instance_options({}, instance)
+        self.assertEqual({'security.privileged': 'True',
+                          'boot.autostart': 'True'}, config)
diff --git a/nova_lxd/tests/test_migrate.py b/nova_lxd/tests/test_migrate.py
index 3bf9ceb..18145a1 100644
--- a/nova_lxd/tests/test_migrate.py
+++ b/nova_lxd/tests/test_migrate.py
@@ -13,24 +13,77 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import os
+import shutil
+
 import mock
 
 from nova import test
 from nova.virt import fake
 
+from oslo_config import cfg
+from oslo_utils import fileutils
+
+from nova_lxd.nova.virt.lxd import config
 from nova_lxd.nova.virt.lxd import migrate
+from nova_lxd.nova.virt.lxd import operations
 from nova_lxd.nova.virt.lxd import session
+from nova_lxd.nova.virt.lxd import utils as container_dir
 from nova_lxd.tests import stubs
 
+CONF = cfg.CONF
+CONF.import_opt('my_ip', 'nova.netconf')
+
 
- at mock.patch.object(migrate, 'CONF', stubs.MockConf())
- at mock.patch.object(session, 'CONF', stubs.MockConf())
 class LXDTestContainerMigrate(test.NoDBTestCase):
 
-    @mock.patch.object(migrate, 'CONF', stubs.MockConf())
-    @mock.patch.object(session, 'CONF', stubs.MockConf())
     def setUp(self):
         super(LXDTestContainerMigrate, self).setUp()
 
         self.migrate = migrate.LXDContainerMigrate(
             fake.FakeVirtAPI())
+
+    def test_migrate_disk_power_off_resize(self):
+        self.flags(my_ip='fakeip')
+        instance = stubs._fake_instance()
+        network_info = mock.Mock()
+        flavor = mock.Mock()
+        context = mock.Mock()
+        dest = 'fakeip'
+
+        with test.nested(
+            mock.patch.object(session.LXDAPISession, 'container_defined'),
+            mock.patch.object(config.LXDContainerConfig, 'create_profile'),
+            mock.patch.object(session.LXDAPISession, 'profile_update')
+        ) as (
+            mock_container_defined,
+            mock_create_profile,
+            mock_profile_update
+        ):
+            self.assertEqual('',
+                self.migrate.migrate_disk_and_power_off(
+                    context, instance, dest, flavor, network_info))
+            mock_container_defined.assert_called_once_with(instance.name,
+                                                           instance)
+            mock_create_profile.assert_called_once_with(instance, network_info)
+
+    def test_confirm_migration(self):
+        migration = mock.Mock()
+        instance = stubs._fake_instance()
+        network_info = mock.Mock()
+
+        with test.nested(
+            mock.patch.object(session.LXDAPISession, 'container_defined'),
+            mock.patch.object(session.LXDAPISession, 'profile_delete'),
+            mock.patch.object(session.LXDAPISession, 'container_destroy'),
+            mock.patch.object(operations.LXDContainerOperations, 'unplug_vifs'),
+        ) as (
+            mock_container_defined,
+            mock_profile_delete,
+            mock_container_destroy,
+            mock_unplug_vifs):
+                self.assertEqual(None,
+                    self.migrate.confirm_migration(migration, instance, network_info))
+                mock_container_defined.assert_called_once_with(instance.name, instance)
+                mock_profile_delete.assert_called_once_with(instance)
+                mock_unplug_vifs.assert_called_once_with(instance, network_info)

From e53a7f1d62c61448269b3f8dfee4aeb8e3b51e55 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Mon, 8 Feb 2016 10:18:09 -0500
Subject: [PATCH 15/15] Fix pep8

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova_lxd/nova/virt/lxd/migrate.py      |  1 -
 nova_lxd/tests/fake_api.py             | 13 ++++--------
 nova_lxd/tests/session/test_profile.py |  3 ---
 nova_lxd/tests/stubs.py                |  2 --
 nova_lxd/tests/test_config.py          |  5 +----
 nova_lxd/tests/test_migrate.py         | 38 ++++++++++++++++++----------------
 6 files changed, 25 insertions(+), 37 deletions(-)

diff --git a/nova_lxd/nova/virt/lxd/migrate.py b/nova_lxd/nova/virt/lxd/migrate.py
index 28c9aa3..653b64b 100644
--- a/nova_lxd/nova/virt/lxd/migrate.py
+++ b/nova_lxd/nova/virt/lxd/migrate.py
@@ -15,7 +15,6 @@
 
 import os
 import pprint
-import shutil
 
 from nova import exception
 from nova import i18n
diff --git a/nova_lxd/tests/fake_api.py b/nova_lxd/tests/fake_api.py
index b9078e6..3ead0dd 100644
--- a/nova_lxd/tests/fake_api.py
+++ b/nova_lxd/tests/fake_api.py
@@ -157,13 +157,13 @@ def fake_container_migrate():
             "resources": {
                 "containers": [
                     "/1.0/containers/instance-00000010"
-                    ]
+                ]
             },
             "metadata": {
-                "control": "5ffecc8e6cf95e01f4bae7dcef6e87711e7c119e42ed7538e3da583ed91927da",
-                "fs": "21f09995fed96a8abae91802de4b794a35789d2a813735c9874decfe591134c4"
+                "control": "fake_control",
+                "fs": "fake_fs"
             },
-            "may_cancel": false,
+            "may_cancel": 'false',
             "err": ""
         },
         "operation": "/1.0/operations/dbd9f22c-6da5-4066-8fca-c02f09f76738"
@@ -395,8 +395,3 @@ def fake_container_info():
                      'address': "172.16.15.30",
                      'host_veth': "vethGMDIY9"}]},
     }
-
-def fake_container_migrate():
-    return {
-
-    }
diff --git a/nova_lxd/tests/session/test_profile.py b/nova_lxd/tests/session/test_profile.py
index 848be25..9698a3f 100644
--- a/nova_lxd/tests/session/test_profile.py
+++ b/nova_lxd/tests/session/test_profile.py
@@ -17,7 +17,6 @@
 import ddt
 import mock
 
-from nova.compute import power_state
 from nova import exception
 from nova import test
 from pylxd import exceptions as lxd_exceptions
@@ -73,10 +72,8 @@ def test_profile_create(self):
 
     def test_profile_delete(self):
         instance = stubs._fake_instance()
-        config = mock.Mock()
         self.ml.profile_defined.return_value = True
         self.ml.profile_delete.return_value = \
             (200, fake_api.fake_standard_return())
         self.assertEqual(None,
                          self.session.profile_delete(instance))
-
diff --git a/nova_lxd/tests/stubs.py b/nova_lxd/tests/stubs.py
index 3edfd11..c66c061 100644
--- a/nova_lxd/tests/stubs.py
+++ b/nova_lxd/tests/stubs.py
@@ -108,5 +108,3 @@ def _fake_instance():
     }
     return fake_instance.fake_instance_obj(
         ctxt, **_instance_values)
-
-
diff --git a/nova_lxd/tests/test_config.py b/nova_lxd/tests/test_config.py
index 581e930..106d72c 100644
--- a/nova_lxd/tests/test_config.py
+++ b/nova_lxd/tests/test_config.py
@@ -16,14 +16,12 @@
 import ddt
 import mock
 
-from nova import objects
 from nova import test
 from nova.tests.unit import fake_network
 
 from nova_lxd.nova.virt.lxd import config
 from nova_lxd.nova.virt.lxd import utils as container_dir
 from nova_lxd.tests import stubs
-from nova_lxd.tests import fake_api
 
 
 @ddt.ddt
@@ -85,10 +83,9 @@ def test_config_instance_options(self):
         instance = stubs._fake_instance()
         config = {}
         container_config = self.config.config_instance_options(config,
-                                instance)
+                                                               instance)
         self.assertEqual({'boot.autostart': 'True'}, container_config)
 
-
     def test_create_container_source(self):
         instance = stubs._fake_instance()
         config = self.config.get_container_source(instance)
diff --git a/nova_lxd/tests/test_migrate.py b/nova_lxd/tests/test_migrate.py
index 18145a1..016baef 100644
--- a/nova_lxd/tests/test_migrate.py
+++ b/nova_lxd/tests/test_migrate.py
@@ -13,22 +13,17 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import os
-import shutil
-
 import mock
 
 from nova import test
 from nova.virt import fake
 
 from oslo_config import cfg
-from oslo_utils import fileutils
 
 from nova_lxd.nova.virt.lxd import config
 from nova_lxd.nova.virt.lxd import migrate
 from nova_lxd.nova.virt.lxd import operations
 from nova_lxd.nova.virt.lxd import session
-from nova_lxd.nova.virt.lxd import utils as container_dir
 from nova_lxd.tests import stubs
 
 CONF = cfg.CONF
@@ -61,11 +56,13 @@ def test_migrate_disk_power_off_resize(self):
             mock_profile_update
         ):
             self.assertEqual('',
-                self.migrate.migrate_disk_and_power_off(
-                    context, instance, dest, flavor, network_info))
+                             self.migrate.migrate_disk_and_power_off(
+                                 context, instance, dest, flavor,
+                                 network_info))
             mock_container_defined.assert_called_once_with(instance.name,
                                                            instance)
-            mock_create_profile.assert_called_once_with(instance, network_info)
+            mock_create_profile.assert_called_once_with(instance,
+                                                        network_info)
 
     def test_confirm_migration(self):
         migration = mock.Mock()
@@ -76,14 +73,19 @@ def test_confirm_migration(self):
             mock.patch.object(session.LXDAPISession, 'container_defined'),
             mock.patch.object(session.LXDAPISession, 'profile_delete'),
             mock.patch.object(session.LXDAPISession, 'container_destroy'),
-            mock.patch.object(operations.LXDContainerOperations, 'unplug_vifs'),
+            mock.patch.object(operations.LXDContainerOperations,
+                              'unplug_vifs'),
         ) as (
-            mock_container_defined,
-            mock_profile_delete,
-            mock_container_destroy,
-            mock_unplug_vifs):
-                self.assertEqual(None,
-                    self.migrate.confirm_migration(migration, instance, network_info))
-                mock_container_defined.assert_called_once_with(instance.name, instance)
-                mock_profile_delete.assert_called_once_with(instance)
-                mock_unplug_vifs.assert_called_once_with(instance, network_info)
+                mock_container_defined,
+                mock_profile_delete,
+                mock_container_destroy,
+                mock_unplug_vifs):
+            self.assertEqual(None,
+                             self.migrate.confirm_migration(migration,
+                                                            instance,
+                                                            network_info))
+            mock_container_defined.assert_called_once_with(instance.name,
+                                                           instance)
+            mock_profile_delete.assert_called_once_with(instance)
+            mock_unplug_vifs.assert_called_once_with(instance,
+                                                     network_info)


More information about the lxc-devel mailing list