[lxc-devel] [nova-lxd/master] Switch to pylxd 2.0

zulcss on Github lxc-bot at linuxcontainers.org
Wed Jun 8 01:56:06 UTC 2016


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 530 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20160608/b3faa479/attachment.bin>
-------------- next part --------------
From 445e896d2d2b24df2d4fd8c3105be7280dd7cf98 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Thu, 2 Jun 2016 11:30:17 -0400
Subject: [PATCH 01/46] Remove dead code

Remove container_running method it is not being used.

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova/tests/unit/virt/lxd/test_session.py | 26 --------------------------
 nova/virt/lxd/session.py                 | 22 ----------------------
 2 files changed, 48 deletions(-)

diff --git a/nova/tests/unit/virt/lxd/test_session.py b/nova/tests/unit/virt/lxd/test_session.py
index e142c22..06870d2 100644
--- a/nova/tests/unit/virt/lxd/test_session.py
+++ b/nova/tests/unit/virt/lxd/test_session.py
@@ -106,32 +106,6 @@ def test_container_update_fail(self, tag, side_effect,
             expected,
             self.session.container_update, config, instance)
 
-    @stubs.annotated_data(
-        ('running', True),
-        ('idle', False),
-        ('api_failure', lxd_exceptions.APIError('Fake', '500')),
-    )
-    def test_container_running(self, tag, side_effect):
-        """
-        container_running determines if the container is running
-        or not. Verify that we are returning True if the container
-        is running. False if its not, raise an exception if there
-        is an API error.
-        """
-        instance = stubs._fake_instance()
-        if side_effect:
-            self.ml.container_running.return_value = side_effect
-            self.assertTrue(self.session.container_running(instance))
-        if not side_effect:
-            self.ml.container_running.return_value = side_effect
-            self.assertFalse(self.session.container_running(instance))
-        if tag == 'api_failure':
-            self.ml.container_running.side_effect = side_effect
-            self.assertRaises(
-                exception.NovaException,
-                self.session.container_running, instance
-            )
-
     def test_container_state(self):
         """
         container_state translates LXD container status into
diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index 65d415b..3371772 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -120,28 +120,6 @@ def container_update(self, config, instance):
                           {'instance': instance.name, 'reason': e},
                           instance=instance)
 
-    def container_running(self, instance):
-        """Determine if the container is running
-
-        :param instance: nova instance object
-        :return: True if container is running otherwise false
-
-        """
-        LOG.debug('container_running for instance', instance=instance)
-        try:
-            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:'
-                    ' %(reason)s') % {'instance': instance.name,
-                                      'reason': ex}
-            raise exception.NovaException(msg)
-        except Exception as e:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Error from LXD during container_running'
-                              '%(instance)s: %(reason)s'),
-                          {'instance': instance.name, 'reason': e},
-                          instance=instance)
 
     def container_state(self, instance):
         """Determine container_state and translate state

From 4f7b2541364f0f1e5751e56737090667abb15d9b Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Thu, 2 Jun 2016 11:37:47 -0400
Subject: [PATCH 02/46] Start using pylxd 2.0

Use _get_session method to return a connection
pylxd 2.0 object.

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

diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index 3371772..e3729e0 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -25,6 +25,9 @@
 from oslo_service import loopingcall
 from oslo_utils import excutils
 
+from pylxd import client
+from pylxd import exceptions
+
 from pylxd.deprecated import api
 from pylxd.deprecated import exceptions as lxd_exceptions
 
@@ -44,6 +47,23 @@ class LXDAPISession(object):
     def __init__(self):
         super(LXDAPISession, self).__init__()
 
+        self.client = self._get_session()
+
+    def _get_session(self):
+        """Returns a connection to the LXD hypervisor
+
+        This method should be used to create a connection
+        to the LXD hypervisor via the pylxd API call.
+
+        :param host: host is the LXD daemon to connect to
+        :return: pylxd object
+        """
+        try:
+            return client.Client()
+        except Exception as ex:
+            LOG.error(_LE('Unable to connect to LXD host: %{reason}s'),
+                       {'reason': ex})
+
     def get_session(self, host=None):
         """Returns a connection to the LXD hypervisor
 

From 69d7e93b0df7762e08e24a85ef2a740d4129cf68 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Thu, 2 Jun 2016 12:35:05 -0400
Subject: [PATCH 03/46] Switch container_list method to pylxd 2.0

Switch container_list method to pylxd 2.0,
as well be less zealous when catching exceptions.

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

diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index e3729e0..1713988 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -102,13 +102,7 @@ def container_list(self):
         """
         LOG.debug('container_list called')
         try:
-            client = self.get_session()
-            return client.container_list()
-        except lxd_exceptions.APIError as ex:
-            msg = _('Failed to communicate with LXD API: %(reason)s') \
-                % {'reason': ex}
-            LOG.error(msg)
-            raise exception.NovaException(msg)
+            return [str(container.name) for container in self.client.containers.all()]
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(_LE('Error from LXD during container_list: '

From 750f1e569261c5415e48dff8876f3a58734ed6e1 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Thu, 2 Jun 2016 12:40:59 -0400
Subject: [PATCH 04/46] Switch container_defined method to pylxd2.0

Also check the exception object for the exceptions.NotFound,
if the exception is "NotFound" return False.

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

diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index 1713988..5cdde97 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -227,20 +227,17 @@ def container_defined(self, instance_name, instance):
         """
         LOG.debug('container_defined for instance', instance=instance)
         try:
-            client = self.get_session()
-            return client.container_defined(instance_name)
-        except lxd_exceptions.APIError as ex:
-            if ex.status_code == 404:
+            self.client.containers.get(instance_name)
+            return True
+        except Exception as e:
+            if isinstance(e, exceptions.NotFound):
                 return False
             else:
-                msg = _('Failed to get container status: %s') % ex
-                raise exception.NovaException(msg)
-        except Exception as e:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Error from LXD during container_defined'
-                              '%(instance)s: %(reason)s'),
-                          {'instance': instance.name, 'reason': e},
-                          instance=instance)
+                with excutils.save_and_reraise_exception():
+                    LOG.error(_LE('Error from LXD during container_defined'
+                                  '%(instance)s: %(reason)s'),
+                              {'instance': instance.name, 'reason': e},
+                            instance=instance)
 
     def container_start(self, instance_name, instance):
         """Start an LXD container

From 900b64a44948c31e951f12687280d6fe01f03298 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Thu, 2 Jun 2016 12:48:44 -0400
Subject: [PATCH 05/46] Switch profile_defined over to pylxd 2.0

Swtich to pylxd 2.0, check the exception object
for "NotFound", return False is the profile
exists.

Remove profile_list method since it is no
longer being used by the profile_defined method.

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

diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index 5cdde97..72271a8 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -614,21 +614,6 @@ def operation_info(self, operation_id, instance):
     #
     # Profile methods
     #
-    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 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})
-
     def profile_defined(self, instance_name, instance):
         """Validate if the profile is available on the LXD
            host
@@ -638,24 +623,17 @@ def profile_defined(self, instance_name, instance):
         LOG.debug('profile_defined called for instance',
                   instance=instance)
         try:
-            found = False
-            if instance_name in self.profile_list():
-                found = True
-            return found
-        except lxd_exceptions.APIError as ex:
-            if ex.status_code == 404:
+            self.client.profiles.get(instance_name)
+            return True
+        except Exception as e:
+            if isinstance(e, exceptions.NotFound):
                 return False
             else:
-                msg = _('Failed to communicate with LXD API %(instance)s:'
-                        ' %(reason)s') % {'instance': instance.name,
-                                          'reason': ex}
-                raise exception.NovaException(msg)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(
-                    _LE('Failed to determine profile %(instance)s:'
-                        ' %(reason)s'),
-                    {'instance': instance.name, 'reason': ex})
+                with excutils.save_and_reraise_exception():
+                    LOG.error(
+                        _LE('Failed to determine profile %(instance)s:'
+                            ' %(reason)s'),
+                        {'instance': instance.name, 'reason': e})
 
     def profile_create(self, config, instance):
         """Create an LXD container profile

From 6b17e9e4b609bd3dcf275d9a1e6f36f64025d037 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Thu, 2 Jun 2016 13:25:13 -0400
Subject: [PATCH 06/46] Switch host methods and usage over to pylxd 2.0

- Switch host methods to pylxd 2.0.
- Query the host only when we start nova-lxd
  so we are not constantly hitting the API
  on each instance creation.
- Check for the LXD version key in the
  'environment' rest API response to make sure that
  the host is responding when we first start nova-lxd.

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova/virt/lxd/config.py  |  6 ++----
 nova/virt/lxd/host.py    | 16 +++++++---------
 nova/virt/lxd/session.py | 36 +++++++++++++++++++++++++-----------
 3 files changed, 34 insertions(+), 24 deletions(-)

diff --git a/nova/virt/lxd/config.py b/nova/virt/lxd/config.py
index ccf5f60..d416f3a 100644
--- a/nova/virt/lxd/config.py
+++ b/nova/virt/lxd/config.py
@@ -181,8 +181,7 @@ def configure_container_root(self, instance):
                   instance=instance)
         try:
             config = {}
-            lxd_config = self.session.get_host_config(instance)
-            if str(lxd_config['storage']) in ['btrfs', 'zfs']:
+            if str(self.session.get_host_config()['storage']) in ['btrfs', 'zfs']:
                 config['root'] = {'path': '/',
                                   'type': 'disk',
                                   'size': '%sGB' % str(instance.root_gb)}
@@ -371,8 +370,7 @@ def get_container_migrate(self, container_migrate, migration,
             container_migrate = {
                 'base_image': '',
                 'mode': 'pull',
-                'certificate': str(self.session.host_certificate(instance,
-                                                                 host)),
+                'certificate': str(self.session.host_certificate()),
                 'operation': str(container_url),
                 'secrets': {
                         'control': str(container_control),
diff --git a/nova/virt/lxd/host.py b/nova/virt/lxd/host.py
index 587539c..5259b25 100644
--- a/nova/virt/lxd/host.py
+++ b/nova/virt/lxd/host.py
@@ -27,8 +27,6 @@
 from nova import utils
 import os
 import platform
-from pylxd.deprecated import api
-from pylxd.deprecated import exceptions as lxd_exceptions
 import socket
 
 from oslo_config import cfg
@@ -37,6 +35,8 @@
 from oslo_utils import units
 import psutil
 
+from nova.virt.lxd import session
+
 _ = i18n._
 _LW = i18n._LW
 CONF = cfg.CONF
@@ -46,7 +46,7 @@
 class LXDHost(object):
 
     def __init__(self):
-        self.lxd = api.API()
+        self.session = session.LXDAPISession()
 
     def get_available_resource(self, nodename):
         LOG.debug('In get_available_resource')
@@ -176,7 +176,7 @@ def _get_cpu_info(self):
         return cpuinfo
 
     def _get_hypersivor_version(self):
-        version = self.lxd.get_lxd_version()
+        version = str(self.session.get_host_config()['version'])
         return '.'.join(str(v) for v in version)
 
     def get_host_cpu_stats(self):
@@ -192,11 +192,9 @@ def get_host_cpu_stats(self):
     def init_host(self, host):
         LOG.debug('Host check')
         try:
-            if not self.lxd.host_ping():
-                msg = _('Unable to connect to LXD daemon')
-                raise exception.HostNotFound(msg)
-
+            if 'version' not in self.session.get_host_config().keys():
+                return False
             return True
-        except lxd_exceptions.APIError as ex:
+        except Exception as ex:
             msg = _('Unable to connect to LXD daemon: %s') % ex
             raise exception.HostNotFound(msg)
diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index 72271a8..a9989a7 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -14,6 +14,8 @@
 #    the License for the specific language governing permissions and
 #    limitations under the License.
 
+import ast
+
 import nova.conf
 from nova import context as nova_context
 from nova import exception
@@ -48,6 +50,9 @@ def __init__(self):
         super(LXDAPISession, self).__init__()
 
         self.client = self._get_session()
+        self.host = self.get_lxd_host()
+
+
 
     def _get_session(self):
         """Returns a connection to the LXD hypervisor
@@ -64,6 +69,16 @@ def _get_session(self):
             LOG.error(_LE('Unable to connect to LXD host: %{reason}s'),
                        {'reason': ex})
 
+    def get_lxd_host(self):
+        LOG.debug('get_lxd_host called')
+        try:
+            return self.client.host_info['environment']
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Failed to query LXD host %(reason)s'),
+                          {'reason': ex})
+
+
     def get_session(self, host=None):
         """Returns a connection to the LXD hypervisor
 
@@ -715,26 +730,25 @@ def profile_delete(self, instance):
     #
     # Host Methods
     #
-    def host_certificate(self, instance, host):
-        LOG.debug('host_certificate called for instance', instance=instance)
+    def host_certificate(self, instance):
+        """Return the certificate of the LXD host"""
+        LOG.debug('host_certificate called for host')
         try:
-            client = self.get_session(host)
-            return client.get_host_certificate()
+            return self.host['certificate']
         except lxd_exceptions.APIError as ex:
             msg = _('Failed to communicate with LXD %(instance)s:'
                     ' %(reason)s') % {'instance': instance.name,
                                       'ex': ex}
             LOG.error(msg)
 
-    def get_host_config(self, instance):
-        LOG.debug('host_config called for instance', instance=instance)
+    def get_host_config(self):
+        """Return the host configuration of the LXD host"""
+        LOG.debug('host_config called for instance')
         try:
-            client = self.get_session()
-            return client.host_config()['environment']
+            return self.host
         except lxd_exceptions.APIError as ex:
-            msg = _('Failed to communicate with LXD %(instance)s:'
-                    ' %(reason)s') % {'instance': instance.name,
-                                      'ex': ex}
+            msg = _('Failed to communicate with LXD host:'
+                    ' %(reason)s') % {'ex': ex}
             LOG.error(msg)
 
     #

From d50d08e4157c2eba7785e8f229abf48c93fbeec1 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Thu, 2 Jun 2016 14:48:00 -0400
Subject: [PATCH 07/46] Switch container_start to pylxd 2.0

Switch container_start method to pylxd 2.0

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

diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index a9989a7..c4b4e6b 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -266,21 +266,12 @@ def container_start(self, instance_name, instance):
             LOG.info(_LI('Starting instance %(instance)s with '
                          '%(image)s'), {'instance': instance.name,
                                         'image': instance.image_ref})
-            # Start the container
-            client = self.get_session()
-
-            (state, data) = client.container_start(instance_name,
-                                                   CONF.lxd.timeout)
-            self.operation_wait(data.get('operation'), instance)
 
+            container = self.client.containers.get(instance_name)
+            container.start(wait=True)
             LOG.info(_LI('Successfully started instance %(instance)s with'
-                         ' %(image)s'), {'instance': instance.name,
-                                         'image': instance.image_ref})
-        except lxd_exceptions.APIError as ex:
-            msg = _('Failed to communicate with LXD API %(instance)s:'
-                    ' %(reason)s') % {'instance': instance.name,
-                                      'reason': ex}
-            raise exception.NovaException(msg)
+                     ' %(image)s'), {'instance': instance.name,
+                                     'image': instance.image_ref})
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(

From 4fccb514ecefcab2db1aa25ed7fcd61bd9e7c95e Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Thu, 2 Jun 2016 14:50:25 -0400
Subject: [PATCH 08/46] Swith container_stop method over to pylxd2.0

Swith container_stop method over to pylxd 2.0

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

diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index c4b4e6b..4dfd3cf 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -291,20 +291,11 @@ def container_stop(self, instance_name, instance):
             LOG.info(_LI('Stopping instance %(instance)s with'
                          ' %(image)s'), {'instance': instance.name,
                                          'image': instance.image_ref})
-            # Stop the container
-            client = self.get_session()
-            (state, data) = client.container_stop(instance_name,
-                                                  CONF.lxd.timeout)
-            self.operation_wait(data.get('operation'), instance)
-
+            container = self.client.containers.get(instance_name)
+            container.stop(wait=True)
             LOG.info(_LI('Successfully stopped instance %(instance)s with'
                          ' %(image)s'), {'instance': instance.name,
                                          'image': instance.image_ref})
-        except lxd_exceptions.APIError as ex:
-            msg = _('Failed to communicate with LXD API %(instance)s:'
-                    ' %(reason)s') % {'instance': instance.name,
-                                      'reason': ex}
-            raise exception.NovaException(msg)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(

From 1d77376661a07767f61b5ad7ea70cbc1444d97e7 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Thu, 2 Jun 2016 14:53:05 -0400
Subject: [PATCH 09/46] Switch contianer_reboot over to pylxd2.0

Switch container_reboot method over to pylxd2.0

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

diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index 4dfd3cf..e9f7ea9 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -315,19 +315,12 @@ def container_reboot(self, instance):
                          ' %(image)s'), {'instance': instance.name,
                                          'image': instance.image_ref})
 
-            # Container reboot
-            client = self.get_session()
-            (state, data) = client.container_reboot(instance.name,
-                                                    CONF.lxd.timeout)
-            self.operation_wait(data.get('operation'), instance)
+            container = self.client.containers.get(instance.name)
+            container.restart(wait=True)
 
             LOG.info(_LI('Successfully rebooted instance %(instance)s with'
                          ' %(image)s'), {'instance': instance.name,
                                          'image': instance.image_ref})
-        except lxd_exceptions.APIError as ex:
-            msg = _('Failed to communicate with LXD API %(instance)s:'
-                    ' %(reason)s') % {'instance': instance.name,
-                                      'reason': ex}
             raise exception.NovaException(msg)
         except Exception as ex:
             with excutils.save_and_reraise_exception():

From 78bca8407d9a229b073e13e6f16c4e07164bf633 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Thu, 2 Jun 2016 14:54:22 -0400
Subject: [PATCH 10/46] Switch container_destroy over to pylxd2.0

Switch container_destroy over to pylxd2.0

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

diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index e9f7ea9..2cdc2e4 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -345,18 +345,12 @@ def container_destroy(self, instance_name, instance):
             # Destroying container
             self.container_stop(instance_name, instance)
 
-            client = self.get_session()
-            (state, data) = client.container_destroy(instance_name)
-            self.operation_wait(data.get('operation'), instance)
+            container = self.client.containers.get(instance_name)
+            container.delete(wait=True)
 
             LOG.info(_LI('Successfully destroyed instance %(instance)s with'
                          ' %(image)s'), {'instance': instance.name,
                                          'image': instance.image_ref})
-        except lxd_exceptions.APIError as ex:
-            msg = _('Failed to communicate with LXD API %(instance)s:'
-                    ' %(reason)s') % {'instance': instance.name,
-                                      'reason': ex}
-            raise exception.NovaException(msg)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(_LE('Failed to destroy container %(instance)s: '

From 9758d2d366f5d9ac1e268389b56af2ea03210111 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Thu, 2 Jun 2016 14:58:02 -0400
Subject: [PATCH 11/46] Switch container_pause methods over to pylxd2.0

Switch container_pause methods over to pylxd2.0

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

diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index 2cdc2e4..2f34ef2 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -370,19 +370,13 @@ def container_pause(self, instance_name, instance):
                          ' %(image)s'), {'instance': instance_name,
                                          'image': instance.image_ref})
 
-            client = self.get_session()
-            (state, data) = client.container_suspend(instance_name,
-                                                     CONF.lxd.timeout)
-            self.operation_wait(data.get('operation'), instance)
+            container = self.client.containers.get(instance_name)
+            container.freeze(wait=True)
 
             LOG.info(_LI('Successfully paused instance %(instance)s with'
                          ' %(image)s'), {'instance': instance_name,
                                          'image': instance.image_ref})
-        except lxd_exceptions.APIError as ex:
-            msg = _('Failed to communicate with LXD API %(instance)s:'
-                    ' %(reason)s') % {'instance': instance_name,
-                                      'reason': ex}
-            raise exception.NovaException(msg)
+
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(

From b25fafdbb0dc516395cd465687b8bbb29c70cb1f Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Thu, 2 Jun 2016 14:59:18 -0400
Subject: [PATCH 12/46] Switch container_unpause

Switch container_unpause methods over to pylxd2.0

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

diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index 2f34ef2..fbced51 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -398,19 +398,13 @@ def container_unpause(self, instance_name, instance):
                          ' %(image)s'), {'instance': instance.name,
                                          'image': instance.image_ref})
 
-            client = self.get_session()
-            (state, data) = client.container_resume(instance_name,
-                                                    CONF.lxd.timeout)
-            self.operation_wait(data.get('operation'), instance)
+            container = self.client.containers.get(instance_name)
+            container.unfreeze(wait=True)
+
 
             LOG.info(_LI('Successfully unpaused instance %(instance)s with'
                          ' %(image)s'), {'instance': instance.name,
                                          'image': instance.image_ref})
-        except lxd_exceptions.APIError as ex:
-            msg = _('Failed to communicate with LXD API %(instance)s:'
-                    ' %(reason)s') % {'instance': instance.name,
-                                      'reason': ex}
-            raise exception.NovaException(msg)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(

From c877871630bc08464e160aa4591feecd5016f199 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Thu, 2 Jun 2016 15:01:05 -0400
Subject: [PATCH 13/46] Switch image_defined

Switch image_defined method to pylxd2.0

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

diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index fbced51..972dc34 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -454,12 +454,8 @@ def container_init(self, config, instance):
     # Image related API methods.
     #
 
-    def image_defined(self, instance):
-        """Checks existence of an image on the local LXD image store
-
-        :param instance: The nova instance
-
-        Returns True if supplied image exists on the host, False otherwise
+    def image_defined(self):
+        """ Returns an array of images found on the LXD image store
         """
         LOG.debug('image_defined called for instance', instance=instance)
         try:

From 3ca6759ee63faee2e2d6203b5ce283400e2d95df Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Thu, 2 Jun 2016 17:24:15 -0400
Subject: [PATCH 14/46] Switch image_defined over to pylxd 2.0

Switch image_defined method over to pylxd2.0

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova/virt/lxd/image.py      |  2 +-
 nova/virt/lxd/operations.py |  2 +-
 nova/virt/lxd/session.py    | 22 +++++++---------------
 3 files changed, 9 insertions(+), 17 deletions(-)

diff --git a/nova/virt/lxd/image.py b/nova/virt/lxd/image.py
index 90de759..ef3a330 100644
--- a/nova/virt/lxd/image.py
+++ b/nova/virt/lxd/image.py
@@ -75,7 +75,7 @@ def setup_image(self, context, instance, image_meta):
                                               instance.image_ref),
                             external=True):
 
-            if self.client.image_defined(instance):
+            if instance.image_ref in self.client.image_defined():
                 return
 
             base_dir = self.container_dir.get_base_dir()
diff --git a/nova/virt/lxd/operations.py b/nova/virt/lxd/operations.py
index baa2cfa..9cf0bd2 100644
--- a/nova/virt/lxd/operations.py
+++ b/nova/virt/lxd/operations.py
@@ -159,7 +159,7 @@ def _fetch_image(self, context, instance, image_meta):
                               'for %(image)s: %(e)s'),
                           {'instance': instance.name,
                            'image': instance.image_ref,
-                           'ex': ex}, instance=instance)
+                           'ex': e}, instance=instance)
 
     def _setup_network(self, instance_name, instance, network_info):
         """Setup the network when creating the lXD container
diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index 972dc34..d5fd7c6 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -457,26 +457,18 @@ def container_init(self, config, instance):
     def image_defined(self):
         """ Returns an array of images found on the LXD image store
         """
-        LOG.debug('image_defined called for instance', instance=instance)
         try:
-            client = self.get_session()
-            return client.alias_defined(instance.image_ref)
-        except lxd_exceptions.APIError as ex:
-            if ex.status_code == 404:
-                return False
-            else:
-                msg = _('Failed to communicate with LXD API %(instance)s:'
-                        ' %(reason)s') % {'instance': instance.image_ref,
-                                          'reason': ex}
-                LOG.error(msg)
-                raise exception.NovaException(msg)
+            root='-rootfs.tar.gz'
+            fingerprints = [str(image.fingerprint)
+                            for image in self.client.images.all()]
+            return [str(
+                    self.client.images.get(fingerprint).filename.split(
+                        root)[0]) for fingerprint in fingerprints]
         except Exception as e:
             with excutils.save_and_reraise_exception():
                 LOG.error(_LE('Error from LXD during image_defined '
                               '%(instance)s: %(reason)s'),
-                          {'instance': instance.image_ref, 'reason': e},
-                          instance=instance)
-
+                          {'reason': e})
     def create_alias(self, alias, instance):
         """Creates an alias for a given image
 

From f7bef772a33992f2c930202b09b8e28525f0d404 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Thu, 2 Jun 2016 20:46:51 -0400
Subject: [PATCH 15/46] Check exception

Check for an pylxd.exceptions.NotFound and ignore
it just in case the instance is not there, if the database
and LXD is out of sync.

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

diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index d5fd7c6..38b0542 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -273,6 +273,8 @@ def container_start(self, instance_name, instance):
                      ' %(image)s'), {'instance': instance.name,
                                      'image': instance.image_ref})
         except Exception as ex:
+            if isinstance(ex, exceptions.NotFound):
+                return
             with excutils.save_and_reraise_exception():
                 LOG.error(
                     _LE('Failed to start container %(instance)s: %(reason)s'),
@@ -297,6 +299,8 @@ def container_stop(self, instance_name, instance):
                          ' %(image)s'), {'instance': instance.name,
                                          'image': instance.image_ref})
         except Exception as ex:
+            if isinstance(ex, exceptions.NotFound):
+                return
             with excutils.save_and_reraise_exception():
                 LOG.error(
                     _LE('Failed to stop container %(instance)s: '
@@ -323,6 +327,8 @@ def container_reboot(self, instance):
                                          'image': instance.image_ref})
             raise exception.NovaException(msg)
         except Exception as ex:
+            if isinstance(ex, exceptions.NotFound):
+                return
             with excutils.save_and_reraise_exception():
                 LOG.error(
                     _LE('Failed to reboot container %(instance)s: '
@@ -352,6 +358,8 @@ def container_destroy(self, instance_name, instance):
                          ' %(image)s'), {'instance': instance.name,
                                          'image': instance.image_ref})
         except Exception as ex:
+            if isinstance(ex, exceptions.NotFound):
+                return
             with excutils.save_and_reraise_exception():
                 LOG.error(_LE('Failed to destroy container %(instance)s: '
                               '%(reason)s'), {'instance': instance_name,
@@ -378,6 +386,8 @@ def container_pause(self, instance_name, instance):
                                          'image': instance.image_ref})
 
         except Exception as ex:
+            if isinstance(ex, exceptions.NotFound):
+                return
             with excutils.save_and_reraise_exception():
                 LOG.error(
                     _LE('Failed to pause container %(instance)s: '
@@ -406,6 +416,8 @@ def container_unpause(self, instance_name, instance):
                          ' %(image)s'), {'instance': instance.name,
                                          'image': instance.image_ref})
         except Exception as ex:
+            if isinstance(ex, exceptions.NotFound):
+                return
             with excutils.save_and_reraise_exception():
                 LOG.error(
                     _LE('Failed to unpause container %(instance)s: '

From 37205b77cf08852b5e8efd603f3c159201ef2cfc Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Thu, 2 Jun 2016 23:04:59 -0400
Subject: [PATCH 16/46] Fix contanier_iamge

Container image was not supplying the right data.

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova/virt/lxd/operations.py | 2 +-
 nova/virt/lxd/session.py    | 8 ++------
 2 files changed, 3 insertions(+), 7 deletions(-)

diff --git a/nova/virt/lxd/operations.py b/nova/virt/lxd/operations.py
index 9cf0bd2..baa2cfa 100644
--- a/nova/virt/lxd/operations.py
+++ b/nova/virt/lxd/operations.py
@@ -159,7 +159,7 @@ def _fetch_image(self, context, instance, image_meta):
                               'for %(image)s: %(e)s'),
                           {'instance': instance.name,
                            'image': instance.image_ref,
-                           'ex': e}, instance=instance)
+                           'ex': ex}, instance=instance)
 
     def _setup_network(self, instance_name, instance, network_info):
         """Setup the network when creating the lXD container
diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index 38b0542..431a411 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -470,12 +470,8 @@ def image_defined(self):
         """ Returns an array of images found on the LXD image store
         """
         try:
-            root='-rootfs.tar.gz'
-            fingerprints = [str(image.fingerprint)
-                            for image in self.client.images.all()]
-            return [str(
-                    self.client.images.get(fingerprint).filename.split(
-                        root)[0]) for fingerprint in fingerprints]
+            fingerprints = [str(image.fingerprint) for image in self.client.images.all()]
+            return [ str(self.client.images.get(image).aliases[0]['name']) for image in fingerprints ]
         except Exception as e:
             with excutils.save_and_reraise_exception():
                 LOG.error(_LE('Error from LXD during image_defined '

From 33e912a534114b1c7eb81223ebd6d183b3d265e5 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Fri, 3 Jun 2016 08:15:40 -0400
Subject: [PATCH 17/46] Siwtch create_alias to pylxd 2.0

Switch create_alias to pylxd2.0

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova/virt/lxd/image.py   |  6 +-----
 nova/virt/lxd/session.py | 13 ++++---------
 2 files changed, 5 insertions(+), 14 deletions(-)

diff --git a/nova/virt/lxd/image.py b/nova/virt/lxd/image.py
index ef3a330..17e4e43 100644
--- a/nova/virt/lxd/image.py
+++ b/nova/virt/lxd/image.py
@@ -267,11 +267,7 @@ def _setup_alias(self, instance):
                 with open(self.container_image, "rb") as rootfs_fd:
                     fingerprint = hashlib.sha256(meta_fd.read() +
                                                  rootfs_fd.read()).hexdigest()
-            alias_config = {
-                'name': instance.image_ref,
-                'target': fingerprint
-            }
-            self.client.create_alias(alias_config, instance)
+            self.client.create_alias(fingerprint, instance)
         except Exception as ex:
             with excutils.save_and_reraise_exception:
                 LOG.error(_LE('Failed to setup alias for %(image)s:'
diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index 431a411..6d6c3c5 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -477,7 +477,7 @@ def image_defined(self):
                 LOG.error(_LE('Error from LXD during image_defined '
                               '%(instance)s: %(reason)s'),
                           {'reason': e})
-    def create_alias(self, alias, instance):
+    def create_alias(self, fingerprint, instance):
         """Creates an alias for a given image
 
         :param alias: The alias to be crerated
@@ -487,14 +487,9 @@ def create_alias(self, alias, instance):
         """
         LOG.debug('create_alias called for instance', instance=instance)
         try:
-            client = self.get_session()
-            return client.alias_create(alias)
-        except lxd_exceptions.APIError as ex:
-            msg = _('Failed to communicate with LXD API %(instance)s:'
-                    ' %(reason)s') % {'instance': instance.image_ref,
-                                      'reason': ex}
-            LOG.error(msg)
-            raise exception.NovaException(msg)
+            lxd = self.client.api
+            lxd.images.aliases.post(json={'target': fingerprint,
+                                          'name': instance.image_ref})
         except Exception as e:
             with excutils.save_and_reraise_exception():
                 LOG.error(_LE('Error from LXD during create alias'

From 26d4f2ff1173f75bdc455e97e98a5cd6dd0ac630 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Fri, 3 Jun 2016 08:53:26 -0400
Subject: [PATCH 18/46] Switch image_upload to pylxd 2.0

Switch image_upload method to pylxd2.0

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova/virt/lxd/image.py   |  3 +--
 nova/virt/lxd/session.py | 20 ++++++--------------
 2 files changed, 7 insertions(+), 16 deletions(-)

diff --git a/nova/virt/lxd/image.py b/nova/virt/lxd/image.py
index 17e4e43..db68b2e 100644
--- a/nova/virt/lxd/image.py
+++ b/nova/virt/lxd/image.py
@@ -251,8 +251,7 @@ def _image_upload(self, instance):
             % boundary
 
         # Upload the file to LXD and then remove the tmpdir.
-        self.client.image_upload(data=open(upload_path, 'rb'),
-                                 headers=headers, instance=instance)
+        self.client.image_upload(upload_path, headers, instance)
         shutil.rmtree(tmpdir)
 
     def _setup_alias(self, instance):
diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index 6d6c3c5..3ccb458 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -497,7 +497,7 @@ def create_alias(self, fingerprint, instance):
                           {'instance': instance.image_ref, 'reason': e},
                           instance=instance)
 
-    def image_upload(self, data, headers, instance):
+    def image_upload(self, image, headers, instance):
         """Upload an image to the local LXD image store
 
         :param data: image data
@@ -507,19 +507,11 @@ def image_upload(self, data, headers, instance):
         """
         LOG.debug('upload_image called for instance', instance=instance)
         try:
-            client = self.get_session()
-            (state, data) = client.image_upload(data=data,
-                                                headers=headers)
-            # XXX - zulcss (Dec 8, 2015) - Work around for older
-            # versions of LXD.
-            if 'operation' in data:
-                self.operation_wait(data.get('operation'), instance)
-        except lxd_exceptions.APIError as ex:
-            msg = _('Failed to communicate with LXD API %(instance)s:'
-                    '%(reason)s') % {'instance': instance.image_ref,
-                                     'reason': ex}
-            LOG.error(msg)
-            raise exception.NovaException(msg)
+            lxd = self.client.api
+            with open(image, 'rb') as f:
+                response = lxd.images.post(data=f.read(), headers=headers)
+                operation_uuid = response.json()['operation'].split('/')[-1]
+                lxd.operations[operation_uuid].wait.get()
         except Exception as e:
             with excutils.save_and_reraise_exception():
                 LOG.error(_LE('Error from LXD during image upload'

From dd4ccac672cf5611090f038cca1a8b3c7621d304 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Fri, 3 Jun 2016 09:10:56 -0400
Subject: [PATCH 19/46] Remove more dead code

Remove more dead code

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

diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index 3ccb458..fea215e 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -209,29 +209,6 @@ def container_config(self, instance):
                           {'instance': instance.name, 'reason': e},
                           instance=instance)
 
-    def container_info(self, instance):
-        """Returns basic information about a LXD container
-
-        :param instance: nova instance object
-        :return: LXD container information
-
-        """
-        LOG.debug('container_info called for instance', instance=instance)
-        try:
-            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:'
-                    ' %(reason)s') % {'instance': instance.name,
-                                      'reason': ex}
-            raise exception.NovaException(msg)
-        except Exception as e:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Error from LXD during container_info'
-                              '%(instance)s: %(reason)s'),
-                          {'instance': instance.name, 'reason': e},
-                          instance=instance)
-
     def container_defined(self, instance_name, instance):
         """Determine if the container exists
 
@@ -606,11 +583,6 @@ def profile_create(self, config, instance):
 
             client = self.get_session()
             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,
-                                      'reason': ex}
-            raise exception.NovaException(msg)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(
@@ -632,11 +604,6 @@ def profile_update(self, config, instance):
 
             client = self.get_session()
             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,
-                                      'reason': ex}
-            raise exception.NovaException(msg)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(
@@ -656,11 +623,6 @@ def profile_delete(self, instance):
 
             client = self.get_session()
             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,
-                                      'reason': ex}
-            raise exception.NovaException(msg)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(
@@ -717,11 +679,6 @@ def container_migrate(self, instance_name, host, instance):
                      {'instance': instance.name,
                       'image': instance.image_ref})
             return (state, data)
-        except lxd_exceptions.APIError as ex:
-            msg = _('Failed to communicate with LXD API %(instance)s:'
-                    ' %(reason)s') % {'instance': instance.name,
-                                      'reason': ex}
-            raise exception.NovaException(msg)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(
@@ -756,11 +713,6 @@ def container_move(self, old_name, config, instance):
             LOG.info(_LI('Successfully moved container %(instance)s with '
                          '%(image)s'), {'instance': instance.name,
                                         'image': instance.image_ref})
-        except lxd_exceptions.APIError as ex:
-            msg = _('Failed to communicate with LXD API %(instance)s:'
-                    ' %(reason)s') % {'instance': instance.name,
-                                      'reason': ex}
-            raise exception.NovaException(msg)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(
@@ -769,41 +721,6 @@ def container_move(self, old_name, config, instance):
                     {'instance': instance.name,
                      'reason': ex}, instance=instance)
 
-    def container_snapshot(self, snapshot, instance):
-        """Snapshot a LXD container
-
-        :param snapshot: snapshot config dictionary
-        :param instance: nova instance object
-
-        """
-        LOG.debug('container_snapshot called for instance', instance=instance)
-        try:
-            LOG.info(_LI('Snapshotting container %(instance)s with '
-                         '%(image)s'), {'instance': instance.name,
-                                        'image': instance.image_ref})
-
-            # Container snapshot
-            client = self.get_session()
-            (state, data) = client.container_snapshot_create(
-                instance.name, snapshot)
-            self.operation_wait(data.get('operation'), instance)
-
-            LOG.info(_LI('Successfully snapshotted container %(instance)s with'
-                         ' %(image)s'), {'instance': instance.name,
-                                         'image': instance.image_ref})
-        except lxd_exceptions.APIError as ex:
-            msg = _('Failed to communicate with LXD API %(instance)s:'
-                    ' %(reason)s') % {'instance': instance.name,
-                                      'reason': ex}
-            raise exception.NovaException(msg)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(
-                    _LE('Failed to snapshot container %(instance)s: '
-                        '%(reason)s'),
-                    {'instance': instance.name,
-                     'reason': ex}, instance=instance)
-
     def container_publish(self, image, instance):
         """Publish a container to the local LXD image store
 
@@ -816,11 +733,6 @@ def container_publish(self, image, instance):
         try:
             client = self.get_session()
             return client.container_publish(image)
-        except lxd_exceptions.APIError as ex:
-            msg = _('Failed to communicate with LXD API %(instance)s:'
-                    ' %(reason)s') % {'instance': instance.name,
-                                      'reason': ex}
-            raise exception.NovaException(msg)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(
@@ -841,9 +753,6 @@ def container_export(self, image, instance):
         try:
             client = self.get_session()
             return client.image_export(image)
-        except lxd_exceptions.APIError as ex:
-            msg = _('Failed to export image: %s') % ex
-            raise exception.NovaException(msg)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(
@@ -851,37 +760,3 @@ def container_export(self, image, instance):
                         '%(reason)s'),
                     {'instance': instance.name,
                      'reason': ex}, instance=instance)
-
-    def wait_for_snapshot(self, event_id, instance):
-        """Poll snapshot operation for the snapshot to be ready.
-
-        :param event_id: operation id
-        :param instance: nova instance object
-        """
-        LOG.debug('wait_for_snapshot called for instance', instance=instance)
-
-        timer = loopingcall.FixedIntervalLoopingCall(self._wait_for_snapshot,
-                                                     event_id, instance)
-        try:
-            timer.start(interval=2).wait()
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Failed to create snapshot for %(instance)s: '
-                              '%(ex)s'), {'instance': instance.name, 'ex': ex},
-                          instance=instance)
-
-    def _wait_for_snapshot(self, event_id, instance):
-        """Check the status code of the opeation id.
-
-        :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)

From 2550a3c40210ac6c42809e2e68b20a6bd1dba19b Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Fri, 3 Jun 2016 09:43:58 -0400
Subject: [PATCH 20/46] Switch container_state to pylxd 2.0

Switch container_state method to pylxd2.0

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

diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index fea215e..6e7aa2f 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -158,26 +158,20 @@ def container_state(self, instance):
 
         """
         LOG.debug('container_state called for instance', instance=instance)
+        mem = 0
+        max_mem = 0
         try:
-            mem = 0
-            max_mem = 0
-
-            client = self.get_session()
+            container = self.client.containers.get(instance.name)
+            container_state = container.state()
 
-            (state, data) = client.container_state(instance.name)
-            state = constants.LXD_POWER_STATES[data['metadata']['status_code']]
+            state = constants.LXD_POWER_STATES[container_state.status_code]
 
-            container_state = self.container_info(instance)
-            mem = int(container_state['memory']['usage']) >> 10
-            max_mem = int(container_state['memory']['usage_peak']) >> 10
+            mem = int(container_state.memory['usage']) >> 10
+            max_mem = int(container_state.memory['usage_peak']) >> 10
 
-        except lxd_exceptions.APIError as ex:
-            msg = _('Failed to communicate with LXD API %(instance)s:'
-                    ' %(reason)s') % {'instance': instance.name,
-                                      'reason': ex}
-            LOG.error(msg)
-            state = power_state.NOSTATE
         except Exception as e:
+            if isinstance(e, exceptions.NotFound):
+                state = power_state.NOSTATE
             with excutils.save_and_reraise_exception():
                 LOG.error(_LE('Error from LXD during container_state'
                               '%(instance)s: %(reason)s'),

From c08757ecdcc7e5061af135d55370e08ffda5f6ad Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Fri, 3 Jun 2016 10:11:38 -0400
Subject: [PATCH 21/46] Switch profile_create to pylxd2.0

- Centralize all container configuration when the container
  profile is created.
- Switch profile_Create method to pylxd2.0.
- Update profile creation to take account of pylxd2.0 changes

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova/virt/lxd/config.py     | 73 +++++++++++++++++++--------------------------
 nova/virt/lxd/operations.py |  4 +--
 nova/virt/lxd/session.py    | 22 +++++++++-----
 3 files changed, 46 insertions(+), 53 deletions(-)

diff --git a/nova/virt/lxd/config.py b/nova/virt/lxd/config.py
index d416f3a..0bcf98c 100644
--- a/nova/virt/lxd/config.py
+++ b/nova/virt/lxd/config.py
@@ -28,6 +28,7 @@
 from nova.virt.lxd import session
 from nova.virt.lxd import utils as container_dir
 from nova.virt.lxd import vif
+import six
 
 _ = i18n._
 _LE = i18n._LE
@@ -65,21 +66,6 @@ def create_container(self, instance):
                 'devices': {}
             }
 
-            # if a configdrive is required, setup the mount point for
-            # the container
-            if configdrive.required_by(instance):
-                configdrive_dir = \
-                    self.container_dir.get_container_configdrive(
-                        instance.name)
-                config = self.configure_disk_path(configdrive_dir,
-                                                  'var/lib/cloud/data',
-                                                  'configdrive', instance)
-                container_config['devices'].update(config)
-
-            if container_config is None:
-                msg = _('Failed to get container configuration for %s') \
-                    % instance_name
-                raise exception.NovaException(msg)
             return container_config
         except Exception as ex:
             with excutils.save_and_reraise_exception():
@@ -100,18 +86,28 @@ def create_profile(self, instance, network_info):
         instance_name = instance.name
         try:
             config = {}
-            config['name'] = str(instance_name)
-            config['config'] = self.create_config(instance_name, instance)
+            devices = {}
+
+            config = self.create_config(instance_name, instance)
 
             # Restrict the size of the "/" disk
-            config['devices'] = self.configure_container_root(instance)
+            devices = (self.configure_container_root(instance))
 
             if network_info:
-                config['devices'].update(self.create_network(instance_name,
-                                                             instance,
-                                                             network_info))
+                devices.update(self.create_network(instance_name,
+                                                   instance,
+                                                   network_info))
 
-            return config
+            # if a configdrive is required, setup the mount point for
+            # the container
+            if configdrive.required_by(instance):
+                configdrive_dir = \
+                    self.container_dir.get_container_configdrive(instance_name)
+                devices.update(self.configure_disk_path(configdrive_dir,
+                                                        'var/lib/cloud/data',
+                                                        'configdrive', instance))
+
+            return (config, devices)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(
@@ -422,13 +418,13 @@ def create_container_net_device(self, instance, vif):
                   insance=instance)
         try:
             network_config = self.vif_driver.get_config(instance, vif)
-
-            config = {}
-            config[self.get_network_device(instance)] = {
+            device_label = self._get_network_device(instance)
+            config =  {
                 'nictype': 'bridged',
                 'hwaddr': str(vif['address']),
                 'parent': str(network_config['bridge']),
                 'type': 'nic'}
+            return (device_label, config)
 
             return config
         except Exception as ex:
@@ -437,23 +433,14 @@ def create_container_net_device(self, instance, vif):
                       {'instance': instance.name, 'ex': ex},
                       instance=instance)
 
-    def get_network_device(self, instance):
-        """Try to detect which network interfaces are available in a contianer
-
-        :param instance: nova instance object
-        """
-        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 = []
-        for line in lines[2:]:
-            if line.find(':') < 0:
-                continue
-            face, _ = line.split(':')
-            if 'eth' in face:
-                interfaces.append(face.strip())
-
-        if len(interfaces) == 1:
+    def _get_network_device(self, instance):
+        LOG.debug('_get_network_device called for instance', instance=instance)
+        devices = []
+        container_info = self.session.container_info(instance.name, instance)
+        for k, v in six.iteritems(container_info.network):
+            if k not in ['lo', 'lxdbr0', 'lxcbr0']:
+                devices.append(str(k))
+        if len(devices) == 1:
             return 'eth1'
         else:
-            return 'eth%s' % int(len(interfaces) - 1)
+            return 'eth%s' % len(devices)
diff --git a/nova/virt/lxd/operations.py b/nova/virt/lxd/operations.py
index baa2cfa..b94af13 100644
--- a/nova/virt/lxd/operations.py
+++ b/nova/virt/lxd/operations.py
@@ -189,9 +189,9 @@ def _setup_profile(self, instance_name, instance, network_info):
         try:
             # Setup the container profile based on the nova
             # instance object and network objects
-            container_profile = self.config.create_profile(instance,
+            (config, devices) = self.config.create_profile(instance,
                                                            network_info)
-            self.session.profile_create(container_profile, instance)
+            self.session.profile_create(config, devices, instance)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(_LE('Failed to create a profile for'
diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index 6e7aa2f..d240e07 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -203,6 +203,18 @@ def container_config(self, instance):
                           {'instance': instance.name, 'reason': e},
                           instance=instance)
 
+    def container_info(self, instance_name, instance):
+        LOG.debug('container-devices called for instnace', instance=instance)
+        try:
+            container = self.client.containers.get(instance_name)
+            return container.state()
+        except Exception as e:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Error from LXD during container_info'
+                              '%(instance)s: %(reason)s'),
+                              {'instance': instance_name, 'reason': e},
+                               instance=instance)
+
     def container_defined(self, instance_name, instance):
         """Determine if the container exists
 
@@ -561,7 +573,7 @@ def profile_defined(self, instance_name, instance):
                             ' %(reason)s'),
                         {'instance': instance.name, 'reason': e})
 
-    def profile_create(self, config, instance):
+    def profile_create(self, config, devices, instance):
         """Create an LXD container profile
 
         :param config: profile dictionary
@@ -570,13 +582,7 @@ 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 %(instance)s') % \
-                    {'instance': instance.name}
-                raise exception.NovaException(msg)
-
-            client = self.get_session()
-            return client.profile_create(config)
+            self.client.profiles.create(instance.name, config=config, devices=devices)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(

From 69cd45c91610d8924dd110832c8bec0f7c592b9c Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Fri, 3 Jun 2016 10:20:38 -0400
Subject: [PATCH 22/46] Switch container_config to pylxd 2.0

Switch container_config method to pylxd2.0

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

diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index d240e07..41d21dc 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -189,13 +189,7 @@ def container_config(self, instance):
         """
         LOG.debug('container_config called for instance', instance=instance)
         try:
-            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:'
-                    ' %(reason)s') % {'instance': instance.name,
-                                      'reason': ex}
-            raise exception.NovaException(msg)
+            return self.client.containers.get(instance.name)
         except Exception as e:
             with excutils.save_and_reraise_exception():
                 LOG.error(_LE('Error from LXD during container_config'

From 9ac59473f5a5ae9858f64fe9e2bc7cb72364d467 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Fri, 3 Jun 2016 10:26:01 -0400
Subject: [PATCH 23/46] Switch container_init to pylxd2.0

Switch container_init method to pylxd2.0

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

diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index 41d21dc..d5e10b3 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -414,24 +414,12 @@ def container_init(self, config, instance):
                          ' %(image)s'), {'instance': instance.name,
                                          'image': instance.image_ref})
 
-            client = self.get_session()
-            (state, data) = client.container_init(config)
-            operation = data.get('operation')
-            self.operation_wait(operation, instance)
-            status, data = self.operation_info(operation, instance)
-            data = data.get('metadata')
-            if not data['status_code'] == 200:
-                msg = data.get('err') or data['metadata']
-                raise exception.NovaException(msg)
+            self.client.containers.create(config, wait=True)
 
             LOG.info(_LI('Successfully created container %(instance)s with'
                          ' %(image)s'), {'instance': instance.name,
                                          'image': instance.image_ref})
-        except lxd_exceptions.APIError as ex:
-            msg = _('Failed to communicate with LXD API %(instance)s:'
-                    ' %(reason)s') % {'instance': instance.name,
-                                      'reason': ex}
-            raise exception.NovaException(msg)
+
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(

From f0285a39deef550e405ed09fa4402e3cefd2f6dc Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Fri, 3 Jun 2016 10:42:43 -0400
Subject: [PATCH 24/46] Split out profile devices and configuriton ops

Due to the pylxd2.0 switch over we can
split out profile devices and configuration
operations so we can speed up operations
also it makes migration easier since
we dont have to query the whole container
configuration to manipulate it.

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

diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index d5e10b3..a854eed 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -123,9 +123,49 @@ def container_list(self):
                 LOG.error(_LE('Error from LXD during container_list: '
                               '%(reason)s') % {'reason': ex})
 
-    def container_update(self, config, instance):
+    def container_config_update(self, label, config, instance):
+        """ Manipulate the container configuration
+
+        :param label: device label
+        :param config: Container configuration
+        :param instance:  instance object
+        """
+        LOG.debug('container_config_update called for instance',
+                  instance=instance)
+        try:
+            profile = self.client.profiles.get(instance.name)
+            profile.config[label] = config
+            profile.update()
+        except Exception as e:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Error from LXD during container_update'
+                              '%(instance)s: %(reason)s'),
+                          {'instance': instance.name, 'reason': e},
+                          instance=instance)
+
+    def container_config_remove(self, label, instance):
+        """ Remove configuration from an LXD container
+
+        :param label: device label
+        :param instance: nova instance object
+        """
+        LOG.debug('container_config_remove called for instance',
+                  instance=instance)
+        try:
+            profile = self.client.profiles.get(instance.name)
+            del profile.config[label]
+            profile.update()
+        except Exception as e:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Error from LXD during container_config_remove'
+                              '%(instance)s: %(reason)s'),
+                          {'instance': instance.name, 'reason': e},
+                          instance=instance)
+
+    def container_device_update(self, label, config, instance):
         """Update the LXD configuration of a given container
 
+        ;param label: device label
         :param config: LXD configuration dictionary
         :param instance: nova instance object
         :return: an update LXD configuration dictionary
@@ -133,15 +173,11 @@ def container_update(self, config, instance):
         """
         LOG.debug('container_update called fo instance', instance=instance)
         try:
-            client = self.get_session()
-
-            return client.container_update(instance.name,
-                                           config)
-        except lxd_exceptions.APIError as ex:
-            msg = _('Failed to communicate with LXD API %(instance)s:'
-                    ' %(reason)s') % {'instance': instance.name,
-                                      'reason': ex}
-            raise exception.NovaException(msg)
+            profile = self.client.profiles.get(instance.name)
+            profile.devices[label] = config
+            profile.update()
+        except exceptions.NotFound:
+            LOG.warning(_LW('Profile not found'))
         except Exception as e:
             with excutils.save_and_reraise_exception():
                 LOG.error(_LE('Error from LXD during container_update'
@@ -149,6 +185,24 @@ def container_update(self, config, instance):
                           {'instance': instance.name, 'reason': e},
                           instance=instance)
 
+    def container_device_delete(self, label, instance):
+        """ Remove a device from the container
+
+        :param label: device label
+        :param instance: nova instance object
+        """
+        LOG.debug('container_device_delete called for instance',
+                  instance=instance)
+        try:
+            profile = self.client.profiles.get(instance.name)
+            del profile.devices[label]
+            profile.update()
+        except Exception as e:
+                LOG.error(_LE('Error from LXD during container_update'
+                              '%(instance)s: %(reason)s'),
+                          {'instance': instance.name, 'reason': e},
+                          instance=instance)
+
 
     def container_state(self, instance):
         """Determine container_state and translate state

From f0fa70de3d866d19741d50db4463db9b83eca05e Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Fri, 3 Jun 2016 10:49:11 -0400
Subject: [PATCH 25/46] Update container rescue/unrescue

Update and simplifyt container rescue/unrescue to use
pylxd2.0 operations.

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova/virt/lxd/operations.py | 47 +++++++++++++++++++++++++--------------------
 1 file changed, 26 insertions(+), 21 deletions(-)

diff --git a/nova/virt/lxd/operations.py b/nova/virt/lxd/operations.py
index b94af13..93e96bf 100644
--- a/nova/virt/lxd/operations.py
+++ b/nova/virt/lxd/operations.py
@@ -476,20 +476,21 @@ def rescue(self, context, instance, network_info, image_meta,
             # Step 1 - Stop the old container
             self.session.container_stop(instance.name, instance)
 
-            # Step 2 - Rename the broken contianer to be rescued
-            self.session.container_move(instance.name,
-                                        {'name': '%s-backup' % instance.name},
+            # Step 2 - Update the old container profile
+            rescue_dir = self.container_dir.get_container_rescue(instance.name)
+            disk_path = self.config.configure_disk_path(rescue_dir, 'mnt', 'rescue',
                                         instance)
-
-            # Step 3 - Re use the old instance object and confiugre
-            #          the disk mount point and create a new container.
-            container_config = self.config.create_container(instance)
-            rescue_dir = self.container_dir.get_container_rescue(
-                instance.name + '-backup')
-            config = self.config.configure_disk_path(rescue_dir,
-                                                     'mnt', 'rescue', instance)
-            container_config['devices'].update(config)
+            self.session.container_device_update('rescue',
+                                                 disk_path['rescue'], instance)
+
+            # Step 3 - Create the container
+            instance_name = '%s-backup' % instance.name
+            image_source = {'type': 'image', 'alias': instance.image_ref}
+            container_config = {'name': instance_name,
+                                'profiles': [str(instance.name)],
+                                'source': image_source}
             self.session.container_init(container_config, instance)
+            self.session.container_start(instance_name, instance)
 
             # Step 4 - Start the rescue instance
             self.session.container_start(instance.name, instance)
@@ -508,17 +509,21 @@ def unrescue(self, instance, network_info):
         """
         LOG.debug('unrescue called for instance', instance=instance)
         try:
-            # Step 1 - Destory the rescue instance.
-            self.session.container_destroy(instance.name,
-                                           instance)
+            instance_name = '%s-backup' % instance.name
+            if not self.session.container_defined(instance_name, instance):
+                msg = _('Unable to find instance')
+                raise exception.NovaException(msg)
 
-            # Step 2 - Rename the backup container that
-            #          the user was working on.
-            self.session.container_move(instance.name + '-backup',
-                                        {'name': instance.name},
-                                        instance)
+            # Step 1 - Stop the rescue container
+            self.session.container_stop(instance_name, instance)
+
+            # Step 2 - Remove recsue partiton from the profile
+            self.session.container_device_delete('rescue', instance)
+
+            # Step 3 - Destroy the rescue contianer.
+            self.session.container_destroy(instance_name, instance)
 
-            # Step 3 - Start the old contianer
+            # Step 4 - Start the old container
             self.session.container_start(instance.name, instance)
         except Exception as ex:
             with excutils.save_and_reraise_exception():

From 4406aa0e44eed7f7c3f92a2ddca0145e9a3c9797 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Fri, 3 Jun 2016 10:55:59 -0400
Subject: [PATCH 26/46] Update container attach/detach to use pylxd2.0

Upate interface attach/detac to use pylxd2.0

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova/virt/lxd/operations.py | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/nova/virt/lxd/operations.py b/nova/virt/lxd/operations.py
index 93e96bf..c0e43ea 100644
--- a/nova/virt/lxd/operations.py
+++ b/nova/virt/lxd/operations.py
@@ -21,6 +21,7 @@
 import os
 import pwd
 import shutil
+import six
 
 from oslo_config import cfg
 from oslo_log import log as logging
@@ -626,11 +627,11 @@ def container_attach_interface(self, instance, image_meta, vif):
             self.vif_driver.plug(instance, vif)
             self.firewall_driver.setup_basic_filtering(instance, vif)
 
-            container_config = self.config.create_container(instance)
-            container_network = self.config.create_container_net_device(
-                instance, vif)
-            container_config['devices'].update(container_network)
-            self.session.container_update(container_config, instance)
+            (device_label, container_network) = (
+                    self.config.create_container_net_device(
+                        instance, vif))
+            self.session.container_device_update(device_label, container_network,
+                                                 instance)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 self.vif_driver.unplug(instance, vif)
@@ -644,6 +645,12 @@ def container_detach_interface(self, instance, vif):
                   instance=instance)
         try:
             self.vif_driver.unplug(instance, vif)
+
+            container = self.session.container_config(instance)
+            for k, v in six.iteritems(container.devices):
+                if k.startswith('eth'):
+                    if container.devices[k]['parent'] == parent:
+                        self.session.container_device_delete(k, instance)
         except exception.NovaException:
             pass
 

From 55061fbef871292a6b79ad0ad1df50b995a8e136 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Fri, 3 Jun 2016 11:29:14 -0400
Subject: [PATCH 27/46] Fix pep8

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova/virt/lxd/config.py     |  8 +++--
 nova/virt/lxd/operations.py | 12 +++++---
 nova/virt/lxd/session.py    | 75 +++++++++++++++------------------------------
 3 files changed, 37 insertions(+), 58 deletions(-)

diff --git a/nova/virt/lxd/config.py b/nova/virt/lxd/config.py
index 0bcf98c..ca90c32 100644
--- a/nova/virt/lxd/config.py
+++ b/nova/virt/lxd/config.py
@@ -105,7 +105,8 @@ def create_profile(self, instance, network_info):
                     self.container_dir.get_container_configdrive(instance_name)
                 devices.update(self.configure_disk_path(configdrive_dir,
                                                         'var/lib/cloud/data',
-                                                        'configdrive', instance))
+                                                        'configdrive',
+                                                        instance))
 
             return (config, devices)
         except Exception as ex:
@@ -177,7 +178,8 @@ def configure_container_root(self, instance):
                   instance=instance)
         try:
             config = {}
-            if str(self.session.get_host_config()['storage']) in ['btrfs', 'zfs']:
+            if (str(self.session.get_host_config()['storage'])
+                    in ['btrfs', 'zfs']):
                 config['root'] = {'path': '/',
                                   'type': 'disk',
                                   'size': '%sGB' % str(instance.root_gb)}
@@ -419,7 +421,7 @@ def create_container_net_device(self, instance, vif):
         try:
             network_config = self.vif_driver.get_config(instance, vif)
             device_label = self._get_network_device(instance)
-            config =  {
+            config = {
                 'nictype': 'bridged',
                 'hwaddr': str(vif['address']),
                 'parent': str(network_config['bridge']),
diff --git a/nova/virt/lxd/operations.py b/nova/virt/lxd/operations.py
index c0e43ea..e727d85 100644
--- a/nova/virt/lxd/operations.py
+++ b/nova/virt/lxd/operations.py
@@ -479,8 +479,9 @@ def rescue(self, context, instance, network_info, image_meta,
 
             # Step 2 - Update the old container profile
             rescue_dir = self.container_dir.get_container_rescue(instance.name)
-            disk_path = self.config.configure_disk_path(rescue_dir, 'mnt', 'rescue',
-                                        instance)
+            disk_path = self.config.configure_disk_path(rescue_dir, 'mnt',
+                                                        'rescue',
+                                                        instance)
             self.session.container_device_update('rescue',
                                                  disk_path['rescue'], instance)
 
@@ -628,9 +629,10 @@ def container_attach_interface(self, instance, image_meta, vif):
             self.firewall_driver.setup_basic_filtering(instance, vif)
 
             (device_label, container_network) = (
-                    self.config.create_container_net_device(
-                        instance, vif))
-            self.session.container_device_update(device_label, container_network,
+                self.config.create_container_net_device(
+                    instance, vif))
+            self.session.container_device_update(device_label,
+                                                 container_network,
                                                  instance)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index a854eed..42c55cd 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -14,8 +14,6 @@
 #    the License for the specific language governing permissions and
 #    limitations under the License.
 
-import ast
-
 import nova.conf
 from nova import context as nova_context
 from nova import exception
@@ -24,7 +22,6 @@
 from nova.compute import power_state
 
 from oslo_log import log as logging
-from oslo_service import loopingcall
 from oslo_utils import excutils
 
 from pylxd import client
@@ -52,8 +49,6 @@ def __init__(self):
         self.client = self._get_session()
         self.host = self.get_lxd_host()
 
-
-
     def _get_session(self):
         """Returns a connection to the LXD hypervisor
 
@@ -67,7 +62,7 @@ def _get_session(self):
             return client.Client()
         except Exception as ex:
             LOG.error(_LE('Unable to connect to LXD host: %{reason}s'),
-                       {'reason': ex})
+                      {'reason': ex})
 
     def get_lxd_host(self):
         LOG.debug('get_lxd_host called')
@@ -78,7 +73,6 @@ def get_lxd_host(self):
                 LOG.error(_LE('Failed to query LXD host %(reason)s'),
                           {'reason': ex})
 
-
     def get_session(self, host=None):
         """Returns a connection to the LXD hypervisor
 
@@ -117,14 +111,15 @@ def container_list(self):
         """
         LOG.debug('container_list called')
         try:
-            return [str(container.name) for container in self.client.containers.all()]
+            return [str(container.name)
+                    for container in self.client.containers.all()]
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(_LE('Error from LXD during container_list: '
                               '%(reason)s') % {'reason': ex})
 
     def container_config_update(self, label, config, instance):
-        """ Manipulate the container configuration
+        """Manipulate the container configuration
 
         :param label: device label
         :param config: Container configuration
@@ -144,7 +139,7 @@ def container_config_update(self, label, config, instance):
                           instance=instance)
 
     def container_config_remove(self, label, instance):
-        """ Remove configuration from an LXD container
+        """Remove configuration from an LXD container
 
         :param label: device label
         :param instance: nova instance object
@@ -176,8 +171,6 @@ def container_device_update(self, label, config, instance):
             profile = self.client.profiles.get(instance.name)
             profile.devices[label] = config
             profile.update()
-        except exceptions.NotFound:
-            LOG.warning(_LW('Profile not found'))
         except Exception as e:
             with excutils.save_and_reraise_exception():
                 LOG.error(_LE('Error from LXD during container_update'
@@ -186,7 +179,7 @@ def container_device_update(self, label, config, instance):
                           instance=instance)
 
     def container_device_delete(self, label, instance):
-        """ Remove a device from the container
+        """Remove a device from the container
 
         :param label: device label
         :param instance: nova instance object
@@ -198,11 +191,10 @@ def container_device_delete(self, label, instance):
             del profile.devices[label]
             profile.update()
         except Exception as e:
-                LOG.error(_LE('Error from LXD during container_update'
-                              '%(instance)s: %(reason)s'),
-                          {'instance': instance.name, 'reason': e},
-                          instance=instance)
-
+            LOG.error(_LE('Error from LXD during container_update'
+                          '%(instance)s: %(reason)s'),
+                      {'instance': instance.name, 'reason': e},
+                      instance=instance)
 
     def container_state(self, instance):
         """Determine container_state and translate state
@@ -260,8 +252,8 @@ def container_info(self, instance_name, instance):
             with excutils.save_and_reraise_exception():
                 LOG.error(_LE('Error from LXD during container_info'
                               '%(instance)s: %(reason)s'),
-                              {'instance': instance_name, 'reason': e},
-                               instance=instance)
+                          {'instance': instance_name, 'reason': e},
+                          instance=instance)
 
     def container_defined(self, instance_name, instance):
         """Determine if the container exists
@@ -283,7 +275,7 @@ def container_defined(self, instance_name, instance):
                     LOG.error(_LE('Error from LXD during container_defined'
                                   '%(instance)s: %(reason)s'),
                               {'instance': instance.name, 'reason': e},
-                            instance=instance)
+                              instance=instance)
 
     def container_start(self, instance_name, instance):
         """Start an LXD container
@@ -301,8 +293,8 @@ def container_start(self, instance_name, instance):
             container = self.client.containers.get(instance_name)
             container.start(wait=True)
             LOG.info(_LI('Successfully started instance %(instance)s with'
-                     ' %(image)s'), {'instance': instance.name,
-                                     'image': instance.image_ref})
+                         ' %(image)s'), {'instance': instance.name,
+                                         'image': instance.image_ref})
         except Exception as ex:
             if isinstance(ex, exceptions.NotFound):
                 return
@@ -356,7 +348,6 @@ def container_reboot(self, instance):
             LOG.info(_LI('Successfully rebooted instance %(instance)s with'
                          ' %(image)s'), {'instance': instance.name,
                                          'image': instance.image_ref})
-            raise exception.NovaException(msg)
         except Exception as ex:
             if isinstance(ex, exceptions.NotFound):
                 return
@@ -442,7 +433,6 @@ def container_unpause(self, instance_name, instance):
             container = self.client.containers.get(instance_name)
             container.unfreeze(wait=True)
 
-
             LOG.info(_LI('Successfully unpaused instance %(instance)s with'
                          ' %(image)s'), {'instance': instance.name,
                                          'image': instance.image_ref})
@@ -486,16 +476,18 @@ def container_init(self, config, instance):
     #
 
     def image_defined(self):
-        """ Returns an array of images found on the LXD image store
-        """
+        """Returns an array of images found on the LXD image store."""
         try:
-            fingerprints = [str(image.fingerprint) for image in self.client.images.all()]
-            return [ str(self.client.images.get(image).aliases[0]['name']) for image in fingerprints ]
+            fingerprints = [str(image.fingerprint)
+                            for image in self.client.images.all()]
+            return [str(self.client.images.get(image).aliases[0]['name'])
+                    for image in fingerprints]
         except Exception as e:
             with excutils.save_and_reraise_exception():
                 LOG.error(_LE('Error from LXD during image_defined '
                               '%(instance)s: %(reason)s'),
                           {'reason': e})
+
     def create_alias(self, fingerprint, instance):
         """Creates an alias for a given image
 
@@ -567,24 +559,6 @@ def operation_wait(self, operation_id, instance):
                           {'instance': instance.image_ref, 'reason': e},
                           instance=instance)
 
-    def operation_info(self, operation_id, instance):
-        LOG.debug('operation_info called for instance', instance=instance)
-        try:
-            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:'
-                    ' %(reason)s') % {'instance': instance.image_ref,
-                                      'reason': ex}
-            LOG.error(msg)
-            raise exception.NovaException(msg)
-        except Exception as e:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Error from LXD during operation_info '
-                              '%(instance)s: %(reason)s'),
-                          {'instance': instance.image_ref, 'reason': e},
-                          instance=instance)
-
     #
     # Profile methods
     #
@@ -618,7 +592,8 @@ def profile_create(self, config, devices, instance):
         LOG.debug('profile_create called for instance',
                   instance=instance)
         try:
-            self.client.profiles.create(instance.name, config=config, devices=devices)
+            self.client.profiles.create(
+                instance.name, config=config, devices=devices)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(
@@ -669,7 +644,7 @@ def profile_delete(self, instance):
     # Host Methods
     #
     def host_certificate(self, instance):
-        """Return the certificate of the LXD host"""
+        """Return the certificate of the LXD host."""
         LOG.debug('host_certificate called for host')
         try:
             return self.host['certificate']
@@ -680,7 +655,7 @@ def host_certificate(self, instance):
             LOG.error(msg)
 
     def get_host_config(self):
-        """Return the host configuration of the LXD host"""
+        """Return the host configuration of the LXD host."""
         LOG.debug('host_config called for instance')
         try:
             return self.host

From 902f637ad09ae9c68537a3bd6ac911acc8d86fce Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Fri, 3 Jun 2016 11:50:24 -0400
Subject: [PATCH 28/46] Remove old session unit tests

Remvoe old session unit tests since they no
longer valid.

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova/tests/unit/virt/lxd/test_session.py | 565 +------------------------------
 1 file changed, 1 insertion(+), 564 deletions(-)

diff --git a/nova/tests/unit/virt/lxd/test_session.py b/nova/tests/unit/virt/lxd/test_session.py
index 06870d2..89e46ec 100644
--- a/nova/tests/unit/virt/lxd/test_session.py
+++ b/nova/tests/unit/virt/lxd/test_session.py
@@ -39,434 +39,8 @@ class SessionContainerTest(test.NoDBTestCase):
     def setUp(self):
         super(SessionContainerTest, 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_container_list(self, tag, side_effect, expected):
-        """
-        container_list returns a list of LXD containers
-        found on an LXD host.
-        """
-        self.ml.container_list.return_value = side_effect
-        self.assertEqual(expected,
-                         self.session.container_list())
-
-    def test_container_list_fail(self):
-        """
-        container_list returns an exception.NovaException,
-        if pylxd raises an APIError.
-        """
-        self.ml.container_list.side_effect = (
-            lxd_exceptions.APIError('Fake', 500))
-        self.assertRaises(
-            exception.NovaException,
-            self.session.container_list)
-
-    def test_container_update(self):
-        """
-        container_update updates the LXD container configuration,
-        so verify that the correct pylxd calls are made.
-        """
-        config = mock.Mock()
-        instance = stubs._fake_instance()
-        self.ml.container_update.return_value = \
-            (200, fake_api.fake_container_config())
-        self.assertEqual((200, fake_api.fake_container_config()),
-                         self.session.container_update(config, instance))
-        calls = [
-            mock.call.container_update(instance.name, config)]
-        self.assertEqual(calls, self.ml.method_calls)
-
-    @stubs.annotated_data(
-        ('api_fail', lxd_exceptions.APIError('Fake', 500),
-         exception.NovaException),
-    )
-    def test_container_update_fail(self, tag, side_effect,
-                                   expected):
-        """
-        container_update will fail if the container is not found, or the
-        LXD raises an API error. Verify that the exceptions are raised
-        in both scenarios.
-        """
-        config = mock.Mock()
-        instance = stubs._fake_instance()
-        self.ml.container_update.side_effect = (
-            lxd_exceptions.APIError('Fake', 500))
-        self.assertRaises(
-            expected,
-            self.session.container_update, config, instance)
-
-    def test_container_state(self):
-        """
-        container_state translates LXD container status into
-        what nova understands. Verify that status_code sends
-        a power_state.RUNNING and a 108 sends a
-        power_state.CRASHED.
-        """
-        calls = []
-        self.assertEqual(calls, self.ml.method_calls)
-
-    @stubs.annotated_data(
-        ('api_fail', True, lxd_exceptions.APIError('Fake', 500),
-         {'state': power_state.NOSTATE, 'mem': 0, 'max_mem': 0})
-    )
-    def test_container_state_fail(self, tag, container_defined, side_effect,
-                                  expected):
-        """
-        container_state translates LXD container status into
-        what nova understands. If the API sends an APIError
-        then raise an power_state.NOSTATE, same if the
-        the container goes missing.
-        """
-        instance = stubs._fake_instance()
-        if container_defined:
-            self.ml.container_defined.return_value = container_defined
-            self.ml.container_state.side_effect = (
-                lxd_exceptions.APIError('Fake', 500))
-            self.assertEqual(
-                expected,
-                self.session.container_state(instance))
-        if not container_defined:
-            self.ml.container_defined.return_value = container_defined
-            self.assertEqual(
-                expected,
-                self.session.container_state(instance))
-
-    def test_container_config(self):
-        """
-        container_config returns a dictionary representation
-        of the LXD container. Verify that the funciton returns
-        a container_config
-        """
-        instance = stubs._fake_instance()
-        self.ml.get_container_config.return_value = \
-            (200, fake_api.fake_container_config())
-        self.assertEqual(
-            (200, fake_api.fake_container_config()),
-            self.session.container_config(instance))
-
-    @stubs.annotated_data(
-        ('api_fail', True, lxd_exceptions.APIError('Fake', 500),
-         exception.NovaException),
-    )
-    def test_container_config_fail(self, tag, container_defined, side_effect,
-                                   expected):
-        """
-        container_config returns a dictionary represeation of the
-        LXD container. Verify that the function raises an
-        exception.NovaException when there is a APIError.
-        """
-        instance = stubs._fake_instance()
-        if container_defined:
-            self.ml.container_defined.return_value = container_defined
-            self.ml.get_container_config.side_effect = side_effect
-            self.assertRaises(
-                expected,
-                self.session.container_config, instance)
-
-    def test_container_info(self):
-        """
-        container_info returns a dictonary represenation of
-        useful information about a container, (ip address, pid, etc).
-        Verify that the function returns the approiate dictionary
-        representation for the LXD API.
-        """
-        instance = stubs._fake_instance()
-        self.ml.container_info.return_value = \
-            (200, fake_api.fake_container_info())
-        self.assertEqual(
-            (200, fake_api.fake_container_info()),
-            self.session.container_info(instance))
-
-    def test_container_info_fail(self):
-        """
-        container_info returns a dictionary reprsentation of
-        userful information about a container (ip address, pid, etc).
-        Verify that the container_info returns an exception.NovaException
-        when there is an APIError.
-        """
-        instance = stubs._fake_instance()
-        self.ml.container_info.side_effect = (
-            lxd_exceptions.APIError('Fake', 500))
-        self.assertRaises(
-            exception.NovaException,
-            self.session.container_info, instance)
-
-    @stubs.annotated_data(
-        ('exists', True),
-        ('missing', False),
-    )
-    def test_container_defined(self, tag, side_effect):
-        """
-        container_defined returns True if the container
-        exists on an LXD host, False otherwise, verify
-        the apporiate return value is returned.
-        """
-        instance = stubs._fake_instance()
-        self.ml.container_defined.return_value = side_effect
-        if side_effect:
-            self.assertTrue(self.session.container_defined(
-                instance.name, instance))
-        if not side_effect:
-            self.assertFalse(self.session.container_defined(
-                instance.name, instance))
-
-    @stubs.annotated_data(
-        ('1', True, (200, fake_api.fake_operation_info_ok()))
-    )
-    def test_container_start(self, tag, defined, side_effect=None):
-        """
-        containser_start starts a container on a given LXD host.
-        Verify that the correct pyLXD calls are made.
-        """
-        instance = stubs._fake_instance()
-        self.ml.container_start.return_value = side_effect
-        self.assertEqual(None,
-                         self.session.container_start(instance.name,
-                                                      instance))
-        calls = [mock.call.container_start(instance.name, -1),
-                 mock.call.wait_container_operation(
-            '/1.0/operation/1234', 200, -1)]
-        self.assertEqual(calls, self.ml.method_calls)
-
-    @stubs.annotated_data(
-        ('1', (200, fake_api.fake_operation_info_ok()))
-    )
-    def test_container_stop(self, tag, side_effect):
-        """
-        container_stop stops a container on a given LXD ost.
-        Verifty that that the apprioated pylxd calls are
-        made to the LXD api.
-        """
-        instance = stubs._fake_instance()
-        self.ml.container_stop.return_value = side_effect
-        self.assertEqual(None,
-                         self.session.container_stop(instance.name,
-                                                     instance))
-        calls = [mock.call.container_stop(instance.name, -1),
-                 mock.call.wait_container_operation(
-            '/1.0/operation/1234', 200, -1)]
-        self.assertEqual(calls, self.ml.method_calls)
-
-    @stubs.annotated_data(
-        ('api_fail', lxd_exceptions.APIError('Fake', 500),
-         exception.NovaException)
-    )
-    def test_container_stop_fail(self, tag, side_effect, expected):
-        """
-        contianer_stop stops a container on a given LXD host.
-        Verifty that we raise an exception.NovaException when there is an
-        APIError.
-        """
-        instance = stubs._fake_instance()
-        self.ml.container_stop.side_effect = side_effect
-        self.assertRaises(expected,
-                          self.session.container_stop, instance.name,
-                          instance)
-
-    @stubs.annotated_data(
-        ('1,', (200, fake_api.fake_operation_info_ok()))
-    )
-    def test_continer_reboot(self, tag, side_effect):
-        """"
-        container_reboot reboots a container on a given LXD host.
-        Verify that the right pylxd calls are made to the LXD host.
-        """
-        instance = stubs._fake_instance()
-        self.ml.container_reboot.return_value = side_effect
-        self.assertEqual(None,
-                         self.session.container_reboot(instance))
-        calls = [mock.call.container_reboot(instance.name, -1),
-                 mock.call.wait_container_operation(
-                     '/1.0/operation/1234', 200, -1)]
-        self.assertEqual(calls, self.ml.method_calls)
-
-    @stubs.annotated_data(
-        ('api_fail', lxd_exceptions.APIError('Fake', 500),
-         exception.NovaException)
-    )
-    def test_container_reboot_fail(self, tag, side_effect, expected):
-        """
-        container_reboot reboots a container on a given LXD host.
-        Check that an exception.NovaException is raised when
-        there is an LXD API error.
-        """
-        instance = stubs._fake_instance()
-        self.ml.container_reboot.side_effect = side_effect
-        self.assertRaises(expected,
-                          self.session.container_reboot, instance)
-
-    @stubs.annotated_data(
-        ('exists', (200, fake_api.fake_operation_info_ok())),
-    )
-    def test_container_destroy(self, tag, side_effect):
-        """
-        container_destroy delete a container from the LXD Host. Check
-        that the approiate pylxd calls are made.
-        """
-        instance = stubs._fake_instance()
-        self.ml.container_stop.return_value = side_effect
-        self.ml.container_destroy.return_value = side_effect
-        self.assertEqual(None,
-                         self.session.container_destroy(instance.name,
-                                                        instance))
-        calls = [mock.call.container_stop(instance.name, -1),
-                 mock.call.wait_container_operation(
-            '/1.0/operation/1234', 200, -1),
-            mock.call.container_destroy(instance.name),
-            mock.call.wait_container_operation(
-            '/1.0/operation/1234', 200, -1)]
-        self.assertEqual(calls, self.ml.method_calls)
-
-    @stubs.annotated_data(
-        ('fail_to_stop', True, 'fail_stop',
-         lxd_exceptions.APIError('Fake', '500'), exception.NovaException),
-        ('fail_to_destroy', True, 'fail_destroy',
-         lxd_exceptions.APIError('Fake', '500'), exception.NovaException)
-    )
-    def test_container_destroy_fail(self, tag, container_defined,
-                                    test_type, side_effect, expected):
-        """
-        container_destroy deletes a container on the LXD host.
-        Check whether an exeption.NovaException is raised when
-        there is an APIError or when the container fails to stop.
-        """
-        instance = stubs._fake_instance()
-        self.ml.cotnainer_defined.return_value = container_defined
-        if test_type == 'fail_stop':
-            self.ml.container_stop.side_effect = side_effect
-            self.assertRaises(expected,
-                              self.session.container_destroy, instance.name,
-                              instance)
-        if test_type == 'fail_destroy':
-            self.ml.container_stop.return_value = \
-                (200, fake_api.fake_operation_info_ok())
-            self.ml.container_destroy.side_effect = side_effect
-            self.assertRaises(expected,
-                              self.session.container_destroy, instance.name,
-                              instance)
-
-    @stubs.annotated_data(
-        ('1', (200, fake_api.fake_operation_info_ok()))
-    )
-    def fake_container_pause(self, tag, side_effect):
-        """
-        container_pause pauses a container on a given LXD host.
-        Verify that the appropiate pylxd API calls are made.
-        """
-        instance = stubs._fake_instance()
-        self.ml.container_suspend.return_value = side_effect
-        self.assertEqual(None,
-                         self.session.container_pause(instance.name,
-                                                      instance))
-        calls = [
-            mock.call.container_susepnd(instance.name, -1),
-            mock.call.wait_container_operation(
-                '/1.0/operation/1234', 200, -1)]
-        self.assertEqual(calls, self.ml.method_calls)
-
-    @stubs.annotated_data(
-        ('api_fail', lxd_exceptions.APIError(500, 'Fake'),
-         exception.NovaException)
-    )
-    def test_container_pause_fail(self, tag, side_effect, expected):
-        """
-        container_pause pauses a contianer on a LXD host. Verify
-        that an exception.NovaException is raised when there
-        is an APIError.
-        """
-        instance = stubs._fake_instance()
-        instance = stubs._fake_instance()
-        self.ml.container_suspend.side_effect = side_effect
-        self.assertRaises(expected,
-                          self.session.container_pause,
-                          instance.name, instance)
-
-    @stubs.annotated_data(
-        ('1', (200, fake_api.fake_operation_info_ok()))
-    )
-    def test_container_unpause(self, tag, side_effect):
-        """
-        container_unpase unpauses a continer on a LXD host.
-        Check that the right pylxd calls are being sent
-        to the LXD API server.
-        """
-        instance = stubs._fake_instance()
-        self.ml.container_resume.return_value = side_effect
-        self.assertEqual(None,
-                         self.session.container_unpause(instance.name,
-                                                        instance))
-        calls = [
-            mock.call.container_resume(instance.name, -1),
-            mock.call.wait_container_operation(
-                '/1.0/operation/1234', 200, -1)]
-        self.assertEqual(calls, self.ml.method_calls)
-
-    @stubs.annotated_data(
-        ('api_fail', lxd_exceptions.APIError(500, 'Fake'),
-         exception.NovaException)
-    )
-    def test_container_unpause_fail(self, tag, side_effect, expected):
-        """
-        container_unpause resumes a previously suespended container.
-        Validate that an exception.NovaException is raised when a
-        APIError is sent by the API.
-        """
-        instance = stubs._fake_instance()
-        self.ml.container_resume.side_effect = side_effect
-        self.assertRaises(expected,
-                          self.session.container_unpause,
-                          instance.name, instance)
-
-    @stubs.annotated_data(
-        ('1', (200, fake_api.fake_operation_info_ok()))
-    )
-    def test_container_init(self, tag, side_effect):
-        """
-        conatainer_init creates a container based on given config
-        for a container. Check to see if we are returning the right
-        pylxd calls for the LXD API.
-        """
-        config = mock.Mock()
-        instance = stubs._fake_instance()
-        self.ml.container_init.return_value = side_effect
-        self.ml.operation_info.return_value = \
-            (200, fake_api.fake_container_state(200))
-        self.assertEqual(None,
-                         self.session.container_init(config, instance))
-        calls = [mock.call.container_init(config),
-                 mock.call.wait_container_operation(
-                     '/1.0/operation/1234', 200, -1),
-                 mock.call.operation_info('/1.0/operation/1234')]
-        self.assertEqual(calls, self.ml.method_calls)
-
-    @stubs.annotated_data(
-        ('api_fail', lxd_exceptions.APIError(500, 'Fake'),
-         exception.NovaException),
-    )
-    def test_container_init_fail(self, tag, side_effect, expected):
-        """
-        continer_init create as container on a given LXD host. Make
-        sure that we reaise an exception.NovaException if there is
-        an APIError from the LXD API.
-        """
-        config = mock.Mock()
-        instance = stubs._fake_instance()
-        self.ml.container_init.side_effect = side_effect
-        self.assertRaises(expected,
-                          self.session.container_init, config,
-                          instance)
-
 
 @ddt.ddt
 class SessionEventTest(test.NoDBTestCase):
@@ -474,55 +48,8 @@ class SessionEventTest(test.NoDBTestCase):
     def setUp(self):
         super(SessionEventTest, self).setUp()
 
-        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()
 
-    def test_container_wait(self):
-        instance = stubs._fake_instance()
-        operation_id = mock.Mock()
-        self.ml.wait_container_operation.return_value = True
-        self.assertEqual(None,
-                         self.session.operation_wait(operation_id, instance))
-        self.ml.wait_container_operation.assert_called_with(operation_id,
-                                                            200, -1)
-
-
- at ddt.ddt
-class SessionImageTest(test.NoDBTestCase):
-
-    def setUp(self):
-        super(SessionImageTest, self).setUp()
-
-        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()
-
-    def test_image_defined(self):
-        """Test the image is defined in the LXD hypervisor."""
-        instance = stubs._fake_instance()
-        self.ml.alias_defined.return_value = True
-        self.assertTrue(self.session.image_defined(instance))
-        calls = [mock.call.alias_defined(instance.image_ref)]
-        self.assertEqual(calls, self.ml.method_calls)
-
-    def test_alias_create(self):
-        """Test the alias is created."""
-        instance = stubs._fake_instance()
-        alias = mock.Mock()
-        self.ml.alias_create.return_value = True
-        self.assertTrue(self.session.create_alias(alias, instance))
-        calls = [mock.call.alias_create(alias)]
-        self.assertEqual(calls, self.ml.method_calls)
-
 
 @ddt.ddt
 class SessionProfileTest(test.NoDBTestCase):
@@ -530,52 +57,8 @@ 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()
-        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))
-
 
 @ddt.ddt
 class SessionSnapshotTest(test.NoDBTestCase):
@@ -583,50 +66,4 @@ class SessionSnapshotTest(test.NoDBTestCase):
     def setUp(self):
         super(SessionSnapshotTest, 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(
-        ('1,', (200, fake_api.fake_operation_info_ok()))
-    )
-    def test_container_snapshot(self, tag, side_effect):
-        snapshot = mock.Mock()
-        instance = stubs._fake_instance()
-        self.ml.container_snapshot_create.return_value = side_effect
-        self.assertEqual(None,
-                         self.session.container_snapshot(snapshot, instance))
-        calls = [
-            mock.call.container_snapshot_create(instance.name, snapshot),
-            mock.call.wait_container_operation('/1.0/operation/1234', 200, -1)]
-        self.assertEqual(calls, self.ml.method_calls)
-
-    @stubs.annotated_data(
-        ('api_fail', lxd_exceptions.APIError(500, 'Fake'),
-         exception.NovaException)
-    )
-    def test_container_snapshot_fail(self, tag, side_effect, expected):
-        snapshot = mock.Mock()
-        instance = stubs._fake_instance()
-        self.ml.container_snapshot_create.side_effect = side_effect
-        self.assertRaises(expected,
-                          self.session.container_snapshot,
-                          instance.name, snapshot)
-
-    @stubs.annotated_data(
-        (1, (200, fake_api.fake_operation_info_ok()))
-    )
-    def test_container_publish(self, tag, side_effect):
-        image = mock.Mock()
-        instance = stubs._fake_instance()
-        self.ml.image_export.return_value = True
-        self.assertTrue(
-            self.session.container_publish(image, instance))
-        calls = [
-            mock.call.container_publish(image)]
-        self.assertEqual(calls, self.ml.method_calls)
+        self.session = session.LXDAPISession()
\ No newline at end of file

From 79f40c746b4dabee79a6304a56c632502ecf9212 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Fri, 3 Jun 2016 13:33:30 -0400
Subject: [PATCH 29/46] More dead code remove

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova/tests/unit/virt/lxd/test_driver_api.py | 16 ----------------
 nova/tests/unit/virt/lxd/test_session.py    |  2 +-
 nova/virt/lxd/driver.py                     |  6 ------
 3 files changed, 1 insertion(+), 23 deletions(-)

diff --git a/nova/tests/unit/virt/lxd/test_driver_api.py b/nova/tests/unit/virt/lxd/test_driver_api.py
index ff5fdd5..b226a12 100644
--- a/nova/tests/unit/virt/lxd/test_driver_api.py
+++ b/nova/tests/unit/virt/lxd/test_driver_api.py
@@ -82,13 +82,6 @@ def test_init_host(self):
             self.connection.init_host(None)
         )
 
-    def test_init_host_new_profile(self):
-        self.ml.profile_list.return_value = []
-        self.assertEqual(
-            True,
-            self.connection.init_host(None)
-        )
-
     @stubs.annotated_data(
         ('no_ping', {'host_ping.return_value': False}),
         ('ping_fail', {'host_ping.side_effect': (lxd_exceptions.
@@ -125,15 +118,6 @@ def test_get_info(self, tag, side_effect, expected):
             self.assertEqual(dir(hardware.InstanceInfo(state=expected,
                                                        num_cpu=2)), dir(info))
 
-    @stubs.annotated_data(
-        (True, 'mock-instance-1'),
-        (False, 'fake-instance'),
-    )
-    def test_instance_exists(self, expected, name):
-        self.assertEqual(
-            expected,
-            self.connection.instance_exists(stubs.MockInstance(name=name)))
-
     def test_estimate_instance_overhead(self):
         self.assertEqual(
             {'memory_mb': 0},
diff --git a/nova/tests/unit/virt/lxd/test_session.py b/nova/tests/unit/virt/lxd/test_session.py
index 89e46ec..290476d 100644
--- a/nova/tests/unit/virt/lxd/test_session.py
+++ b/nova/tests/unit/virt/lxd/test_session.py
@@ -66,4 +66,4 @@ class SessionSnapshotTest(test.NoDBTestCase):
     def setUp(self):
         super(SessionSnapshotTest, self).setUp()
 
-        self.session = session.LXDAPISession()
\ No newline at end of file
+        self.session = session.LXDAPISession()
diff --git a/nova/virt/lxd/driver.py b/nova/virt/lxd/driver.py
index 7dcbfde..ee7ed30 100644
--- a/nova/virt/lxd/driver.py
+++ b/nova/virt/lxd/driver.py
@@ -80,12 +80,6 @@ def init_host(self, host):
     def get_info(self, instance):
         return self.container_ops.get_info(instance)
 
-    def instance_exists(self, instance):
-        try:
-            return instance.name in self.list_instance_uuids()
-        except NotImplementedError:
-            return instance.name in self.list_instances()
-
     def plug_vifs(self, instance, network_info):
         """Plug VIFs into networks."""
         for vif in network_info:

From 67900363f81f91eaa4d5af6af110912420b9a029 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Fri, 3 Jun 2016 14:10:28 -0400
Subject: [PATCH 30/46] Fix unit tests.

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova/tests/unit/virt/lxd/test_driver_api.py | 59 +++++++++++++----------------
 nova/tests/unit/virt/lxd/test_operations.py | 13 -------
 2 files changed, 27 insertions(+), 45 deletions(-)

diff --git a/nova/tests/unit/virt/lxd/test_driver_api.py b/nova/tests/unit/virt/lxd/test_driver_api.py
index b226a12..11f1a89 100644
--- a/nova/tests/unit/virt/lxd/test_driver_api.py
+++ b/nova/tests/unit/virt/lxd/test_driver_api.py
@@ -77,24 +77,22 @@ def test_capabilities(self):
             self.connection.capabilities['supports_attach_interface'])
 
     def test_init_host(self):
-        self.assertEqual(
-            True,
-            self.connection.init_host(None)
-        )
+        with mock.patch.object(session.LXDAPISession,
+                              'get_host_config') as host_config:
+            host_config.return_value = {'version': '2.0.1'}
+            self.assertEqual(
+                True,
+                self.connection.init_host(None)
+            )
 
     @stubs.annotated_data(
-        ('no_ping', {'host_ping.return_value': False}),
-        ('ping_fail', {'host_ping.side_effect': (lxd_exceptions.
-                                                 APIError('Fake',
-                                                          500))}),
+        ('no_version', {}),
     )
     def test_init_host_fail(self, tag, config):
-        self.ml.configure_mock(**config)
-        self.assertRaises(
-            exception.HostNotFound,
-            self.connection.init_host,
-            None
-        )
+        with mock.patch.object(session.LXDAPISession,
+                              'get_host_config') as host_config:
+                host_config.return_value = config
+                self.assertFalse(self.connection.init_host(None))
 
     @stubs.annotated_data(
         ('running', {'state': 200, 'mem': 0, 'max_mem': 0},
@@ -124,29 +122,26 @@ def test_estimate_instance_overhead(self):
             self.connection.estimate_instance_overhead(mock.Mock()))
 
     def test_list_instances(self):
-        self.assertEqual(['mock-instance-1', 'mock-instance-2'],
-                         self.connection.list_instances())
-
-    def test_list_instances_fail(self):
-        self.ml.container_list.side_effect = (
-            lxd_exceptions.APIError('Fake', 500))
-        self.assertRaises(
-            exception.NovaException,
-            self.connection.list_instances
-        )
+        with mock.patch.object(session.LXDAPISession,
+                               "container_list",
+                               ) as container_list:
+            container_list.return_value = ['mock-instance-1', 'mock-instance-2']
+            self.assertEqual(['mock-instance-1', 'mock-instance-2'],
+                             self.connection.list_instances())
 
     @stubs.annotated_data(
         ('exists', [True], exception.InstanceExists),
-        ('fail', lxd_exceptions.APIError('Fake', 500), exception.NovaException)
     )
     def test_spawn_defined(self, tag, side_effect, expected):
-        instance = stubs.MockInstance()
-        self.ml.container_defined.side_effect = side_effect
-        self.assertRaises(
-            expected,
-            self.connection.spawn,
-            {}, instance, {}, [], 'secret')
-        self.ml.container_defined.called_once_with('mock_instance')
+        with mock.patch.object(session.LXDAPISession,
+                               "container_defined",
+                               ) as container_defined:
+            instance = stubs.MockInstance()
+            self.assertRaises(
+                expected,
+                self.connection.spawn,
+                {}, instance, {}, [], 'secret')
+            container_defined.called_once_with('mock_instance')
 
     @stubs.annotated_data(
         ('undefined', False),
diff --git a/nova/tests/unit/virt/lxd/test_operations.py b/nova/tests/unit/virt/lxd/test_operations.py
index 163f203..ffde24d 100644
--- a/nova/tests/unit/virt/lxd/test_operations.py
+++ b/nova/tests/unit/virt/lxd/test_operations.py
@@ -217,19 +217,6 @@ def test_setup_network(self, mock_plug_vifs):
         self.operations._setup_network(instance.name, [], instance)
         mock_plug_vifs.assert_called_once_with([], instance)
 
-    @mock.patch.object(session.LXDAPISession, 'profile_create')
-    @mock.patch.object(config.LXDContainerConfig, 'create_profile')
-    def test_setup_profile(self, mock_profile_create, mock_create_profile):
-        instance = stubs._fake_instance()
-        network_info = mock.Mock()
-        container_profile = mock.Mock()
-        self.operations._setup_profile(instance.name, instance, network_info)
-        mock_profile_create.assert_has_calls(
-            [mock.call(instance, network_info)])
-        container_profile = mock_profile_create.return_value
-        mock_create_profile.assert_has_calls(
-            [mock.call(container_profile, instance)])
-
     @mock.patch.object(config.LXDContainerConfig, 'create_container')
     @mock.patch.object(session.LXDAPISession, 'container_init')
     @mock.patch.object(session.LXDAPISession, 'container_start')

From e5cfe093a6f2262c2877de5fb7cbf10fe9d9f82c Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Sat, 4 Jun 2016 14:36:47 -0400
Subject: [PATCH 31/46] More dead code removal

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

diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index 42c55cd..4f6f364 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -600,28 +600,6 @@ def profile_create(self, config, devices, instance):
                     _LE('Failed to create profile %(instance)s: %(reason)s'),
                     {'instance': instance.name, 'reason': ex})
 
-    def profile_update(self, config, instance):
-        """Update an LXD container profile
-
-          :param config: LXD profile dictironary
-          :param instance: nova instance object
-        """
-        LOG.debug('profile_udpate called for instance', instance=instance)
-        try:
-            if not self.profile_defined(instance.name, instance):
-                msg = _('Profile not found %(instance)s') % \
-                    {'instance': instance.name}
-                raise exception.NovaException(msg)
-
-            client = self.get_session()
-            return client.profile_update(instance.name, config)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(
-                    _LE('Failed to update profile %(instance)s: '
-                        '%(reason)s'),
-                    {'instance': instance.name, 'reason': ex})
-
     def profile_delete(self, instance):
         """Delete a LXD container profile.
 

From 5f0e54c925512725e84e194c016b199e7187467c Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Sat, 4 Jun 2016 14:43:52 -0400
Subject: [PATCH 32/46] Dont raise an exception if exceptions.NotFound

To keep the Nova database and the LXD database
in sync, dont raise an exception if the container
is not Found.

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

diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index 4f6f364..f394abc 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -218,11 +218,12 @@ def container_state(self, instance):
         except Exception as e:
             if isinstance(e, exceptions.NotFound):
                 state = power_state.NOSTATE
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Error from LXD during container_state'
-                              '%(instance)s: %(reason)s'),
-                          {'instance': instance.name, 'reason': e},
-                          instance=instance)
+            else:
+                with excutils.save_and_reraise_exception():
+                    LOG.error(_LE('Error from LXD during container_state'
+                                  '%(instance)s: %(reason)s'),
+                              {'instance': instance.name, 'reason': e},
+                              instance=instance)
                 state = power_state.NOSTATE
         return {'state': state, 'mem': mem, 'max_mem': max_mem}
 
@@ -237,11 +238,14 @@ def container_config(self, instance):
         try:
             return self.client.containers.get(instance.name)
         except Exception as e:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Error from LXD during container_config'
-                              '%(instance)s: %(reason)s'),
-                          {'instance': instance.name, 'reason': e},
-                          instance=instance)
+            if isinstance(e, exceptions.NotFound):
+                pass
+            else:
+                with excutils.save_and_reraise_exception():
+                    LOG.error(_LE('Error from LXD during container_config'
+                                  '%(instance)s: %(reason)s'),
+                              {'instance': instance.name, 'reason': e},
+                              instance=instance)
 
     def container_info(self, instance_name, instance):
         LOG.debug('container-devices called for instnace', instance=instance)
@@ -249,6 +253,8 @@ def container_info(self, instance_name, instance):
             container = self.client.containers.get(instance_name)
             return container.state()
         except Exception as e:
+            if isinstance(e, exceptions.NotFound):
+                pass
             with excutils.save_and_reraise_exception():
                 LOG.error(_LE('Error from LXD during container_info'
                               '%(instance)s: %(reason)s'),
@@ -297,12 +303,13 @@ def container_start(self, instance_name, instance):
                                          'image': instance.image_ref})
         except Exception as ex:
             if isinstance(ex, exceptions.NotFound):
-                return
-            with excutils.save_and_reraise_exception():
-                LOG.error(
-                    _LE('Failed to start container %(instance)s: %(reason)s'),
-                    {'instance': instance_name, 'reason': ex},
-                    instance=instance)
+                pass
+            else:
+                with excutils.save_and_reraise_exception():
+                    LOG.error(
+                        _LE('Failed to start container %(instance)s: %(reason)s'),
+                        {'instance': instance_name, 'reason': ex},
+                        instance=instance)
 
     def container_stop(self, instance_name, instance):
         """Stops an LXD container
@@ -323,11 +330,12 @@ def container_stop(self, instance_name, instance):
                                          'image': instance.image_ref})
         except Exception as ex:
             if isinstance(ex, exceptions.NotFound):
-                return
-            with excutils.save_and_reraise_exception():
-                LOG.error(
-                    _LE('Failed to stop container %(instance)s: '
-                        '%(reason)s'), {'instance': instance_name,
+                pass
+            else:
+                with excutils.save_and_reraise_exception():
+                    LOG.error(
+                        _LE('Failed to stop container %(instance)s: '
+                            '%(reason)s'), {'instance': instance_name,
                                         'reason': ex})
 
     def container_reboot(self, instance):
@@ -350,12 +358,13 @@ def container_reboot(self, instance):
                                          'image': instance.image_ref})
         except Exception as ex:
             if isinstance(ex, exceptions.NotFound):
-                return
-            with excutils.save_and_reraise_exception():
-                LOG.error(
-                    _LE('Failed to reboot container %(instance)s: '
-                        '%(reason)s'), {'instance': instance.name,
-                                        'reason': ex}, instance=instance)
+                pass
+            else:
+                with excutils.save_and_reraise_exception():
+                    LOG.error(
+                        _LE('Failed to reboot container %(instance)s: '
+                            '%(reason)s'), {'instance': instance.name,
+                                           'reason': ex}, instance=instance)
 
     def container_destroy(self, instance_name, instance):
         """Destroy a LXD container
@@ -381,11 +390,12 @@ def container_destroy(self, instance_name, instance):
                                          'image': instance.image_ref})
         except Exception as ex:
             if isinstance(ex, exceptions.NotFound):
-                return
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Failed to destroy container %(instance)s: '
-                              '%(reason)s'), {'instance': instance_name,
-                                              'reason': ex})
+                pass
+            else:
+                with excutils.save_and_reraise_exception():
+                    LOG.error(_LE('Failed to destroy container %(instance)s: '
+                                  '%(reason)s'), {'instance': instance_name,
+                                                  'reason': ex})
 
     def container_pause(self, instance_name, instance):
         """Pause a LXD container
@@ -409,13 +419,14 @@ def container_pause(self, instance_name, instance):
 
         except Exception as ex:
             if isinstance(ex, exceptions.NotFound):
-                return
-            with excutils.save_and_reraise_exception():
-                LOG.error(
-                    _LE('Failed to pause container %(instance)s: '
-                        '%(reason)s'),
-                    {'instance': instance_name,
-                     'reason': ex}, instance=instance)
+                pass
+            else:
+                with excutils.save_and_reraise_exception():
+                    LOG.error(
+                        _LE('Failed to pause container %(instance)s: '
+                            '%(reason)s'),
+                        {'instance': instance_name,
+                         'reason': ex}, instance=instance)
 
     def container_unpause(self, instance_name, instance):
         """Unpause a LXD container
@@ -438,12 +449,13 @@ def container_unpause(self, instance_name, instance):
                                          'image': instance.image_ref})
         except Exception as ex:
             if isinstance(ex, exceptions.NotFound):
-                return
-            with excutils.save_and_reraise_exception():
-                LOG.error(
-                    _LE('Failed to unpause container %(instance)s: '
-                        '%(reason)s'), {'instance': instance_name,
-                                        'reason': ex})
+                pass
+            else:
+                with excutils.save_and_reraise_exception():
+                    LOG.error(
+                        _LE('Failed to unpause container %(instance)s: '
+                            '%(reason)s'), {'instance': instance_name,
+                                            'reason': ex})
 
     def container_init(self, config, instance):
         """Create a LXD container

From bc7988bd836d1058e72dd0516d93440b9a6a1d26 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Sat, 4 Jun 2016 14:46:25 -0400
Subject: [PATCH 33/46] Remove more dead code

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

diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index f394abc..ba9706d 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -541,36 +541,6 @@ def image_upload(self, image, headers, instance):
                               '%(instance)s: %(reason)s'),
                           {'instance': instance.image_ref, 'reason': e},
                           instance=instance)
-
-    #
-    # Operation methods
-    #
-
-    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:
-            client = self.get_session()
-            if not client.wait_container_operation(operation_id, 200, -1):
-                msg = _('Container creation timed out')
-                raise exception.NovaException(msg)
-        except lxd_exceptions.APIError as ex:
-            msg = _('Failed to communicate with LXD API %(instance)s:'
-                    '%(reason)s') % {'instance': instance.image_ref,
-                                     'reason': ex}
-            LOG.error(msg)
-            raise exception.NovaException(msg)
-        except Exception as e:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Error from LXD during operation wait'
-                              '%(instance)s: %(reason)s'),
-                          {'instance': instance.image_ref, 'reason': e},
-                          instance=instance)
-
     #
     # Profile methods
     #

From 8a4b5d7bb2a11b436ea877f1196d963671bc76a5 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Sat, 4 Jun 2016 14:53:32 -0400
Subject: [PATCH 34/46] Switch container_publish to pylxd2.0

- Switch container_alias to pylxd2.0
- Allow to specify a different alias when creating
  an alias for the image.

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

diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index ba9706d..639207a 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -500,7 +500,7 @@ def image_defined(self):
                               '%(instance)s: %(reason)s'),
                           {'reason': e})
 
-    def create_alias(self, fingerprint, instance):
+    def create_alias(self, fingerprint, instance, alias=None):
         """Creates an alias for a given image
 
         :param alias: The alias to be crerated
@@ -510,9 +510,11 @@ def create_alias(self, fingerprint, instance):
         """
         LOG.debug('create_alias called for instance', instance=instance)
         try:
+            if alias is None:
+                alias = instance.image_ref
             lxd = self.client.api
             lxd.images.aliases.post(json={'target': fingerprint,
-                                          'name': instance.image_ref})
+                                          'name': alias})
         except Exception as e:
             with excutils.save_and_reraise_exception():
                 LOG.error(_LE('Error from LXD during create alias'
@@ -692,18 +694,32 @@ def container_move(self, old_name, config, instance):
                     {'instance': instance.name,
                      'reason': ex}, instance=instance)
 
-    def container_publish(self, image, instance):
+    def container_publish(self, instance):
         """Publish a container to the local LXD image store
 
-        :param image: LXD fingerprint
         :param instance: nova instance object
         :return: True if published, False otherwise
 
         """
         LOG.debug('container_publish called for instance', instance=instance)
         try:
-            client = self.get_session()
-            return client.container_publish(image)
+            lxd = self.client.api
+            container_snapshot={
+                'properties': {},
+                'public': False,
+                'source': {
+                    'name': instance.name,
+                    'type': 'container'
+                }
+            }
+            response = lxd.images.post(json=container_snapshot)
+            operation_uuid = response.json()['operation'].split('/')[-1]
+            lxd.operations[operation_uuid].wait.get()
+
+            event = self.client.operations.get(operation_uuid)
+            finerprint = event.metadata('fingerprint')
+
+            self.create_alias(fingerprint, instance, alias=instance.name)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(

From a38378143c0490856fe27b347cfbc0c2dc5cec29 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Sat, 4 Jun 2016 14:55:42 -0400
Subject: [PATCH 35/46] Switch container_export to pylxd2.0

Switch container_export method to pylxd2.0

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

diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index 639207a..aef084c 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -728,7 +728,7 @@ def container_publish(self, instance):
                     {'instance': instance.name,
                      'reason': ex}, instance=instance)
 
-    def container_export(self, image, instance):
+    def container_export(self, fingerprint, instance):
         """
         Export an image from the local LXD image store into
         an file.
@@ -738,8 +738,8 @@ def container_export(self, image, instance):
         """
         LOG.debug('container_export called for instance', instance=instance)
         try:
-            client = self.get_session()
-            return client.image_export(image)
+            image = self.client.images.get(fingerprint)
+            return image.export()
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(

From 210fcc26da6a107ea413dbf036712d804733de39 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Sat, 4 Jun 2016 14:59:58 -0400
Subject: [PATCH 36/46] Reactor container_snapshot.

Remove dead code that has been moved to pylxd2.0

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova/virt/lxd/container_snapshot.py | 52 ++-----------------------------------
 1 file changed, 2 insertions(+), 50 deletions(-)

diff --git a/nova/virt/lxd/container_snapshot.py b/nova/virt/lxd/container_snapshot.py
index 376261f..2e89074 100644
--- a/nova/virt/lxd/container_snapshot.py
+++ b/nova/virt/lxd/container_snapshot.py
@@ -79,8 +79,7 @@ def snapshot(self, context, instance, image_id, update_task_state):
                 # image to the local store
                 self.session.container_stop(instance.name,
                                             instance)
-                fingerprint = self._save_lxd_image(instance,
-                                                   image_id)
+                fingerprnit = self.session.container_publish(instance)
                 self.session.container_start(instance.name, instance)
 
                 update_task_state(task_state=task_states.IMAGE_UPLOADING,
@@ -93,59 +92,12 @@ def snapshot(self, context, instance, image_id, update_task_state):
                               '%(ex)s'), {'instance': instance.name, 'ex': ex},
                           instance=instance)
 
-    def _save_lxd_image(self, instance, image_id):
-        """Creates an LXD image from the LXD continaer
-
-        """
-        LOG.debug('_save_lxd_image called for instance', instance=instance)
-
-        fingerprint = None
-        try:
-            # Publish the snapshot to the local LXD image store
-            container_snapshot = {
-                "properties": {},
-                "public": False,
-                "source": {
-                    "name": instance.name,
-                    "type": "container"
-                }
-            }
-            (state, data) = self.session.container_publish(container_snapshot,
-                                                           instance)
-            event_id = data.get('operation')
-            self.session.wait_for_snapshot(event_id, instance)
-
-            # Image has been create but the fingerprint is buried deep
-            # in the metadata when the snapshot is complete
-            (state, data) = self.session.operation_info(event_id, instance)
-            fingerprint = data['metadata']['metadata']['fingerprint']
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Failed to publish snapshot for %(instance)s: '
-                              '%(ex)s'), {'instance': instance.name,
-                                          'ex': ex}, instance=instance)
-
-        try:
-            # Set the alias for the LXD image
-            alias_config = {
-                'name': image_id,
-                'target': fingerprint
-            }
-            self.session.create_alias(alias_config, instance)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Failed to create alias for %(instance)s: '
-                              '%(ex)s'), {'instance': instance.name,
-                                          'ex': ex}, instance=instance)
-
-        return fingerprint
-
     def _save_glance_image(self, context, instance, image_id, fingerprint):
         LOG.debug('_save_glance_image called for instance', instance=instance)
 
         try:
             snapshot = IMAGE_API.get(context, image_id)
-            data = self.session.container_export(fingerprint, instance)
+            data = self.session.container_expor(fingerprint, intsance)
             image_meta = {'name': snapshot['name'],
                           'disk_format': 'raw'}
             IMAGE_API.update(context, image_id, image_meta, data)

From 333b425c812d2e4140fc4ce4d5966d510f9db833 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Sat, 4 Jun 2016 15:02:21 -0400
Subject: [PATCH 37/46] Remove deprecated pylxd usage

Remove deprecated pylxd

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

diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index aef084c..b990079 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -27,9 +27,6 @@
 from pylxd import client
 from pylxd import exceptions
 
-from pylxd.deprecated import api
-from pylxd.deprecated import exceptions as lxd_exceptions
-
 from nova.virt.lxd import constants
 
 _ = i18n._
@@ -46,7 +43,7 @@ class LXDAPISession(object):
     def __init__(self):
         super(LXDAPISession, self).__init__()
 
-        self.client = self._get_session()
+        self.client = self.get_session()
         self.host = self.get_lxd_host()
 
     def _get_session(self):
@@ -73,32 +70,6 @@ def get_lxd_host(self):
                 LOG.error(_LE('Failed to query LXD host %(reason)s'),
                           {'reason': ex})
 
-    def get_session(self, host=None):
-        """Returns a connection to the LXD hypervisor
-
-        This method should be used to create a connection
-        to the LXD hypervisor via the pylxd API call.
-
-        :param host: host is the LXD daemon to connect to
-        :return: pylxd object
-        """
-        try:
-            if host:
-                return api.API(host=host)
-            else:
-                return api.API()
-        except Exception as ex:
-            # notify the compute host that the connection failed
-            # via an rpc call
-            LOG.exception(_LE('Connection to LXD failed'))
-            payload = dict(ip=CONF.host,
-                           method='_connect',
-                           reason=ex)
-            rpc.get_notifier('compute').error(nova_context.get_admin_context,
-                                              'compute.nova_lxd.error',
-                                              payload)
-            raise exception.HypervisorUnavailable(host=CONF.host)
-
     #
     # Container related API methods
     #

From 201bdb9c5d181ee14489a0ae838057c638edcdc3 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Sat, 4 Jun 2016 15:58:38 -0400
Subject: [PATCH 38/46] Fix detaching network device

Query the parent bridge and then delete it.

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova/virt/lxd/operations.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/nova/virt/lxd/operations.py b/nova/virt/lxd/operations.py
index e727d85..c4aca94 100644
--- a/nova/virt/lxd/operations.py
+++ b/nova/virt/lxd/operations.py
@@ -646,12 +646,13 @@ def container_detach_interface(self, instance, vif):
         LOG.debug('container_defatch_interface called for instance',
                   instance=instance)
         try:
+            config = self.vif_driver.config(instance, vif)
             self.vif_driver.unplug(instance, vif)
 
             container = self.session.container_config(instance)
             for k, v in six.iteritems(container.devices):
                 if k.startswith('eth'):
-                    if container.devices[k]['parent'] == parent:
+                    if container.devices[k]['parent'] == config['bridge']:
                         self.session.container_device_delete(k, instance)
         except exception.NovaException:
             pass

From e60ea134ab5af48579a559e206e363c2e907dff6 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Sat, 4 Jun 2016 16:11:42 -0400
Subject: [PATCH 39/46] remove more dead code

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

diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index b990079..a5081e2 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -565,8 +565,8 @@ def profile_delete(self, instance):
             if not self.profile_defined(instance.name, instance):
                 return
 
-            client = self.get_session()
-            return client.profile_delete(instance.name)
+            profile = self.profiles.get(isntance.name)
+            profile.delete()
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(
@@ -631,40 +631,9 @@ def container_migrate(self, instance_name, host, instance):
                                       'reason': ex}, instance=instance)
 
     #
-    # Snapshot methods
+    # Snapshots
     #
 
-    def container_move(self, old_name, config, instance):
-        """Move a container from one host to another
-
-        :param old_name: Old container name
-        :param config:  Old container config
-        :param instance: nova instance object
-        :return:
-
-        """
-        LOG.debug('container_move called for instance', instance=instance)
-        try:
-            LOG.info(_LI('Moving container %(instance)s with '
-                         '%(image)s'), {'instance': instance.name,
-                                        'image': instance.image_ref})
-
-            # Container move
-            client = self.get_session()
-            (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 '
-                         '%(image)s'), {'instance': instance.name,
-                                        'image': instance.image_ref})
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(
-                    _LE('Failed to move container %(instance)s: '
-                        '%(reason)s'),
-                    {'instance': instance.name,
-                     'reason': ex}, instance=instance)
-
     def container_publish(self, instance):
         """Publish a container to the local LXD image store
 

From abf3919db986c42001d6d2ccc7fe33be16b10b1b Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Sun, 5 Jun 2016 13:35:09 +0000
Subject: [PATCH 40/46] Fix typo

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova/virt/lxd/session.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index a5081e2..74b401d 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -46,7 +46,7 @@ def __init__(self):
         self.client = self.get_session()
         self.host = self.get_lxd_host()
 
-    def _get_session(self):
+    def get_session(self):
         """Returns a connection to the LXD hypervisor
 
         This method should be used to create a connection

From b7d3873d2b7315c4be808f022f57d6f80a8c8890 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Sun, 5 Jun 2016 15:03:01 -0400
Subject: [PATCH 41/46] Allow passing of host to LXDAPISession

If the host is specified then we are connecting
to the remote host rather than the unix socket.

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

diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index 74b401d..866b3c9 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -40,13 +40,37 @@
 class LXDAPISession(object):
     """The session to invoke the LXD API session."""
 
-    def __init__(self):
+    def __init__(self, host=None):
         super(LXDAPISession, self).__init__()
 
+        self.host = host
+
         self.client = self.get_session()
-        self.host = self.get_lxd_host()
+        self.hostConfig = self.get_lxd_host()
 
     def get_session(self):
+        try:
+            if self.host is None:
+                return self._get_local_session()
+            else:
+                return self._get_remote_session()
+        except Exception as ex:
+            LOG.exception(_LE('Unable to connect to host'))
+
+    def _get_remote_session(self):
+        try:
+            lxc_cert = os.path.join(os.path.expanduser('~'),
+                                    '.config/lxc/client.crt')
+            lxc_key = os.path.join(os.path.expanduser('~'),
+                                    '.config/lxc/client.key')
+            endpoint = 'https://%s:8443' % self.host
+            return client.Client(endpoint=endpoint,
+                        cert=(lxc_cert, lxc_key),
+                        verify=False)
+        except Exception as ex:
+            LOG.error(_LE('Unable to connect to LXD host: %s') % ex)
+
+    def _get_local_session(self):
         """Returns a connection to the LXD hypervisor
 
         This method should be used to create a connection
@@ -58,8 +82,7 @@ def get_session(self):
         try:
             return client.Client()
         except Exception as ex:
-            LOG.error(_LE('Unable to connect to LXD host: %{reason}s'),
-                      {'reason': ex})
+            LOG.error(_LE('Unable to connect to LXD host: %s') % ex)
 
     def get_lxd_host(self):
         LOG.debug('get_lxd_host called')
@@ -580,8 +603,8 @@ def host_certificate(self, instance):
         """Return the certificate of the LXD host."""
         LOG.debug('host_certificate called for host')
         try:
-            return self.host['certificate']
-        except lxd_exceptions.APIError as ex:
+            return self.hostConfig['certificate']
+        except Exception as ex:
             msg = _('Failed to communicate with LXD %(instance)s:'
                     ' %(reason)s') % {'instance': instance.name,
                                       'ex': ex}
@@ -591,8 +614,8 @@ def get_host_config(self):
         """Return the host configuration of the LXD host."""
         LOG.debug('host_config called for instance')
         try:
-            return self.host
-        except lxd_exceptions.APIError as ex:
+            return self.hostConfig
+        except Exception as ex:
             msg = _('Failed to communicate with LXD host:'
                     ' %(reason)s') % {'ex': ex}
             LOG.error(msg)

From ca2601d9a5f6946cd102d655a0ce5131466b0a7a Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Sun, 5 Jun 2016 15:13:05 -0400
Subject: [PATCH 42/46] Fix typo

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova/virt/lxd/session.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index 866b3c9..aafd538 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -588,7 +588,7 @@ def profile_delete(self, instance):
             if not self.profile_defined(instance.name, instance):
                 return
 
-            profile = self.profiles.get(isntance.name)
+            profile = self.client.profiles.get(instance.name)
             profile.delete()
         except Exception as ex:
             with excutils.save_and_reraise_exception():

From 066c2ea295d049dee6da0186ee7c9d1761b3fada Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Mon, 6 Jun 2016 08:45:57 -0400
Subject: [PATCH 43/46] Switch resize to pylxd2.0

- Update profile_update to pylxd2.0
- Update resize to use pylxd2.0

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova/virt/lxd/migrate.py | 89 +++++++++++-------------------------------------
 nova/virt/lxd/session.py | 21 ++++++++++++
 2 files changed, 40 insertions(+), 70 deletions(-)

diff --git a/nova/virt/lxd/migrate.py b/nova/virt/lxd/migrate.py
index d8978d2..13f3afc 100644
--- a/nova/virt/lxd/migrate.py
+++ b/nova/virt/lxd/migrate.py
@@ -50,12 +50,17 @@ def __init__(self, virtapi):
             operations.LXDContainerOperations(
                 self.virtapi)
 
+        self.host = None
+        self.event_id = None
+        self.remote = None
+
     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)
 
+
         same_host = False
         if CONF.my_ip == dest:
             same_host = True
@@ -63,17 +68,8 @@ def migrate_disk_and_power_off(self, context, instance, dest,
         else:
             LOG.debug('Migration target host: %s' % dest)
 
-        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.config.create_profile(instance,
-                                                               network_info)
-                self.session.profile_update(container_profile, instance)
-            else:
-                self.session.container_stop(instance.name, instance)
+            self.session.container_stop(instance.name, instance)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(_LE('failed to resize container '
@@ -87,82 +83,35 @@ 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 %(instance)s') % \
-                {'instance': 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)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
-                LOG.exception(_LE('Confirm migration failed for %(instance)s: '
-                                  '%(ex)s'), {'instance': instance.name,
-                                              'ex': ex}, instance=instance)
+                LOG.error(_LE('failed to resize container '
+                              '%(instance)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):
-            return
-
         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)
-
-            if configdrive.required_by(instance):
-                configdrive_dir = \
-                    self.container_dir.get_container_configdrive(
-                        instance.name)
-                fileutils.ensure_tree(configdrive_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
-            src_host = self._get_hostname(
-                migration['source_compute'], instance)
-            (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, src_host, instance)
-            self.session.container_init(container_config, instance)
-
-            # Step 3 - Start the network and contianer
+            if resize_instance:
+                (config, devices) = self.config.create_profile(instance, network_info)
+                self.session.profile_update(config, devices, instance.name, instance)
+
             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 %(instance)s: '
-                                  '%(ex)s'),
-                              {'instance': instance.name,
-                               'ex': ex}, instance=instance)
+                    LOG.error(_LE('failed to resize container '
+                                  '%(instance)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',
-                  instance=instance)
-        if self.session.container_defined(instance.name, instance):
-            self.session.container_start(instance.name, instance)
-
-    def _get_hostname(self, host, instance):
-        LOG.debug('_get_hostname called for instance', instance=instance)
-        out, err = utils.execute('env', 'LANG=C', 'dnsdomainname')
-        if out != '':
-            return '%s.%s' % (host, out.rstrip('\n'))
-        else:
-            return host
+                  instance=instance)
\ No newline at end of file
diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index aafd538..2161b0b 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -596,6 +596,27 @@ def profile_delete(self, instance):
                     _LE('Failed to delete profile %(instance)s: %(reason)s'),
                     {'instance': instance.name, 'reason': ex})
 
+    def profile_update(self, config, devices, my_profile, instance):
+        """Update container profile
+
+        :param: config: instance config object
+        :param devices: instance devices object
+        :param my_profile: profile to update
+        :param instance:  nova icnesta
+        """
+        LOG.debug('profile_update called for instance', instance=instance)
+        try:
+            profile = self.client.profiles.get(my_profile)
+            profile.config = config.copy()
+            profile.devices = devices.copy()
+            profile.update()
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(
+                    _LE('Failed to delete profile %(instance)s: %(reason)s'),
+                    {'instance': instance.name, 'reason': ex})
+
+
     #
     # Host Methods
     #

From 652ad8383cc0ee5608d3bf3b9bbeb89811e681dc Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Tue, 7 Jun 2016 20:51:29 -0400
Subject: [PATCH 44/46] Switch migration and resize to pylxd 2.0

- Switch operation_info and container_migration to use pylxd2.0.
- Update migration to use pylxd2.0.
- Simplify migration to make it easier to use.

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova/virt/lxd/migrate.py | 31 ++++++++++++++++++++++++++++---
 nova/virt/lxd/session.py | 30 +++++++++++++++++++++---------
 2 files changed, 49 insertions(+), 12 deletions(-)

diff --git a/nova/virt/lxd/migrate.py b/nova/virt/lxd/migrate.py
index 13f3afc..70b79e2 100644
--- a/nova/virt/lxd/migrate.py
+++ b/nova/virt/lxd/migrate.py
@@ -70,6 +70,23 @@ def migrate_disk_and_power_off(self, context, instance, dest,
 
         try:
             self.session.container_stop(instance.name, instance)
+            if not same_host:
+                self.host = dest
+                self.remote = session.LXDAPISession(host=self.host)
+
+                # Step 0 - Copy the profile over to the dest host
+                profile = self.session.container_config(instance)
+                self.remote.profile_create(profile.config, profile.devices,
+                                           instance)
+
+                # Step 1 - Setup the websocket for the contianer to migrate
+                #          and migrate it
+                migrate = self.session.container_migrate(instance.name, instance)
+                certificate = self.session.get_host_config()['certificate']
+                container_config = self.config.create_container(instance)
+                container_config['source'] = self.config.get_container_migrate(migrate,
+                                                certificate, CONF.my_ip, instance)
+                self.remote.container_init(container_config, instance)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(_LE('failed to resize container '
@@ -85,6 +102,8 @@ def confirm_migration(self, migration, instance, network_info):
 
         try:
             self.operations.unplug_vifs(instance, network_info)
+            self.session.profile_delete(instance)
+            self.session.container_destroy(instance.name, instance)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(_LE('failed to resize container '
@@ -102,8 +121,13 @@ def finish_migration(self, context, migration, instance, disk_info,
                 (config, devices) = self.config.create_profile(instance, network_info)
                 self.session.profile_update(config, devices, instance.name, instance)
 
-            self.operations.plug_vifs(instance, network_info)
-            self.session.container_start(instance.name, instance)
+            if power_on:
+                if configdrive.required_by(instance):
+                    configdrive_dir = \
+                        self.container_dir.get_container_configdrive(instance.name)
+                    fileutils.ensure_tree(configdrive_dir)
+                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.error(_LE('failed to resize container '
@@ -114,4 +138,5 @@ def finish_migration(self, context, migration, instance, disk_info,
     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)
\ No newline at end of file
+                  instance=instance)
+        self.session.container_start(instance.name, instance)
\ No newline at end of file
diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index 2161b0b..59f41fc 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -14,6 +14,8 @@
 #    the License for the specific language governing permissions and
 #    limitations under the License.
 
+import os
+
 import nova.conf
 from nova import context as nova_context
 from nova import exception
@@ -230,7 +232,7 @@ def container_config(self, instance):
         """
         LOG.debug('container_config called for instance', instance=instance)
         try:
-            return self.client.containers.get(instance.name)
+            return self.client.profiles.get(instance.name)
         except Exception as e:
             if isinstance(e, exceptions.NotFound):
                 pass
@@ -620,15 +622,13 @@ def profile_update(self, config, devices, my_profile, instance):
     #
     # Host Methods
     #
-    def host_certificate(self, instance):
+    def host_certificate(self):
         """Return the certificate of the LXD host."""
-        LOG.debug('host_certificate called for host')
         try:
             return self.hostConfig['certificate']
         except Exception as ex:
-            msg = _('Failed to communicate with LXD %(instance)s:'
-                    ' %(reason)s') % {'instance': instance.name,
-                                      'ex': ex}
+            msg = _('Failed to communicate with LXD:'
+                    ' %(reason)s') % {'ex': ex}
             LOG.error(msg)
 
     def get_host_config(self):
@@ -644,7 +644,7 @@ def get_host_config(self):
     #
     # Migrate methods
     #
-    def container_migrate(self, instance_name, host, instance):
+    def container_migrate(self, instance_name, instance):
         """Initialize a container migration for LXD
 
         :param instance_name: container name
@@ -659,8 +659,8 @@ def container_migrate(self, instance_name, host, instance):
                          '%(image)s'), {'instance': instance_name,
                                         'image': instance.image_ref})
 
-            client = self.get_session(host)
-            (state, data) = client.container_migrate(instance_name)
+            container = self.client.containers.get(instance_name)
+            return container.migrate()
 
             LOG.info(_LI('Successfully initialized migration for instance '
                          '%(instance)s with %(image)s'),
@@ -673,6 +673,18 @@ def container_migrate(self, instance_name, host, instance):
                     _LE('Failed to migrate container %(instance)s: %('
                         'reason)s'), {'instance': instance.name,
                                       'reason': ex}, instance=instance)
+    #
+    # Operations
+    #
+    def operation_info(self, operation_id, instance):
+        LOG.debug('operation_info called for instance', instance=instance)
+        try:
+            return self.client.operations.get(operation_id)
+        except Exception as ex:
+            LOG.error(
+                _LE('Failed to retrieve operation for %(instance)s '
+                    '%(reason)s'), {'instance': instance.name,
+                                    'reason': ex})
 
     #
     # Snapshots

From e9612c092ff36174d59242dd76c12677a6f640e5 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Tue, 7 Jun 2016 21:26:08 -0400
Subject: [PATCH 45/46] Update unit tests

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova/tests/unit/virt/lxd/test_migrate.py | 33 +++++++++++++++++---------------
 1 file changed, 18 insertions(+), 15 deletions(-)

diff --git a/nova/tests/unit/virt/lxd/test_migrate.py b/nova/tests/unit/virt/lxd/test_migrate.py
index fe8e4dc..1165fcc 100644
--- a/nova/tests/unit/virt/lxd/test_migrate.py
+++ b/nova/tests/unit/virt/lxd/test_migrate.py
@@ -42,25 +42,32 @@ def test_migrate_disk_power_off_resize(self):
         network_info = mock.Mock()
         flavor = mock.Mock()
         context = mock.Mock()
-        dest = 'fakeip'
+        dest = 'fakeip1'
 
         with test.nested(
-            mock.patch.object(session.LXDAPISession, 'container_defined'),
-            mock.patch.object(config.LXDContainerConfig, 'create_profile'),
-            mock.patch.object(session.LXDAPISession, 'profile_update')
+            mock.patch.object(session.LXDAPISession, 'container_stop'),
+            mock.patch.object(session.LXDAPISession, 'container_config'),
+            mock.patch.object(config.LXDContainerConfig, 'create_container'),
+            mock.patch.object(config.LXDContainerConfig, 'get_container_migrate'),
+            mock.patch.object(session.LXDAPISession, 'container_init'),
+            mock.patch.object(session.LXDAPISession, 'get_lxd_host'),
+            mock.patch.object(session.LXDAPISession, 'profile_create'),
+            mock.patch.object(session.LXDAPISession, 'container_migrate')
         ) as (
-            mock_container_defined,
-            mock_create_profile,
-            mock_profile_update
+            mock_container_config,
+            mock_container_stop,
+            mock_container_config_container,
+            mock_get_migrate,
+            mock_container_init,
+            mock_host_config,
+            mock_profile_create,
+            mock_container_migrate
         ):
+            mock_host_config.return_value = {'certificate': 'adfadf'}
             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()
@@ -68,13 +75,11 @@ def test_confirm_migration(self):
         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):
@@ -82,8 +87,6 @@ def test_confirm_migration(self):
                              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 9c6d0f85c21e5c8950b6a9f9d07cc4cc3a0a1afa Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Tue, 7 Jun 2016 21:48:45 -0400
Subject: [PATCH 46/46] Fix pep8

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova/tests/unit/virt/lxd/test_driver_api.py | 11 +++++----
 nova/tests/unit/virt/lxd/test_migrate.py    |  3 ++-
 nova/tests/unit/virt/lxd/test_session.py    |  6 -----
 nova/virt/lxd/container_snapshot.py         |  4 ++--
 nova/virt/lxd/migrate.py                    | 36 ++++++++++++++---------------
 nova/virt/lxd/session.py                    | 28 +++++++++++-----------
 6 files changed, 42 insertions(+), 46 deletions(-)

diff --git a/nova/tests/unit/virt/lxd/test_driver_api.py b/nova/tests/unit/virt/lxd/test_driver_api.py
index 11f1a89..d907a36 100644
--- a/nova/tests/unit/virt/lxd/test_driver_api.py
+++ b/nova/tests/unit/virt/lxd/test_driver_api.py
@@ -78,7 +78,7 @@ def test_capabilities(self):
 
     def test_init_host(self):
         with mock.patch.object(session.LXDAPISession,
-                              'get_host_config') as host_config:
+                               'get_host_config') as host_config:
             host_config.return_value = {'version': '2.0.1'}
             self.assertEqual(
                 True,
@@ -90,9 +90,9 @@ def test_init_host(self):
     )
     def test_init_host_fail(self, tag, config):
         with mock.patch.object(session.LXDAPISession,
-                              'get_host_config') as host_config:
-                host_config.return_value = config
-                self.assertFalse(self.connection.init_host(None))
+                               'get_host_config') as host_config:
+            host_config.return_value = config
+            self.assertFalse(self.connection.init_host(None))
 
     @stubs.annotated_data(
         ('running', {'state': 200, 'mem': 0, 'max_mem': 0},
@@ -125,7 +125,8 @@ def test_list_instances(self):
         with mock.patch.object(session.LXDAPISession,
                                "container_list",
                                ) as container_list:
-            container_list.return_value = ['mock-instance-1', 'mock-instance-2']
+            container_list.return_value = [
+                'mock-instance-1', 'mock-instance-2']
             self.assertEqual(['mock-instance-1', 'mock-instance-2'],
                              self.connection.list_instances())
 
diff --git a/nova/tests/unit/virt/lxd/test_migrate.py b/nova/tests/unit/virt/lxd/test_migrate.py
index 1165fcc..2493bb2 100644
--- a/nova/tests/unit/virt/lxd/test_migrate.py
+++ b/nova/tests/unit/virt/lxd/test_migrate.py
@@ -48,7 +48,8 @@ def test_migrate_disk_power_off_resize(self):
             mock.patch.object(session.LXDAPISession, 'container_stop'),
             mock.patch.object(session.LXDAPISession, 'container_config'),
             mock.patch.object(config.LXDContainerConfig, 'create_container'),
-            mock.patch.object(config.LXDContainerConfig, 'get_container_migrate'),
+            mock.patch.object(config.LXDContainerConfig,
+                              'get_container_migrate'),
             mock.patch.object(session.LXDAPISession, 'container_init'),
             mock.patch.object(session.LXDAPISession, 'get_lxd_host'),
             mock.patch.object(session.LXDAPISession, 'profile_create'),
diff --git a/nova/tests/unit/virt/lxd/test_session.py b/nova/tests/unit/virt/lxd/test_session.py
index 290476d..250630e 100644
--- a/nova/tests/unit/virt/lxd/test_session.py
+++ b/nova/tests/unit/virt/lxd/test_session.py
@@ -21,16 +21,10 @@
 """
 
 import ddt
-import mock
 
-from nova.compute import power_state
-from nova import exception
 from nova import test
-from pylxd.deprecated import exceptions as lxd_exceptions
 
 from nova.virt.lxd import session
-import fake_api
-import stubs
 
 
 @ddt.ddt
diff --git a/nova/virt/lxd/container_snapshot.py b/nova/virt/lxd/container_snapshot.py
index 2e89074..88845f2 100644
--- a/nova/virt/lxd/container_snapshot.py
+++ b/nova/virt/lxd/container_snapshot.py
@@ -79,7 +79,7 @@ def snapshot(self, context, instance, image_id, update_task_state):
                 # image to the local store
                 self.session.container_stop(instance.name,
                                             instance)
-                fingerprnit = self.session.container_publish(instance)
+                fingerprint = self.session.container_publish(instance)
                 self.session.container_start(instance.name, instance)
 
                 update_task_state(task_state=task_states.IMAGE_UPLOADING,
@@ -97,7 +97,7 @@ def _save_glance_image(self, context, instance, image_id, fingerprint):
 
         try:
             snapshot = IMAGE_API.get(context, image_id)
-            data = self.session.container_expor(fingerprint, intsance)
+            data = self.session.container_expor(fingerprint, instance)
             image_meta = {'name': snapshot['name'],
                           'disk_format': 'raw'}
             IMAGE_API.update(context, image_id, image_meta, data)
diff --git a/nova/virt/lxd/migrate.py b/nova/virt/lxd/migrate.py
index 70b79e2..c2bc252 100644
--- a/nova/virt/lxd/migrate.py
+++ b/nova/virt/lxd/migrate.py
@@ -13,12 +13,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
-import os
-
 import nova.conf
-from nova import exception
 from nova import i18n
-from nova import utils
 from nova.virt import configdrive
 
 from oslo_log import log as logging
@@ -60,7 +56,6 @@ def migrate_disk_and_power_off(self, context, instance, dest,
                                    retry_interval=0):
         LOG.debug("migrate_disk_and_power_off called", instance=instance)
 
-
         same_host = False
         if CONF.my_ip == dest:
             same_host = True
@@ -81,11 +76,13 @@ def migrate_disk_and_power_off(self, context, instance, dest,
 
                 # Step 1 - Setup the websocket for the contianer to migrate
                 #          and migrate it
-                migrate = self.session.container_migrate(instance.name, instance)
+                migrate = self.session.container_migrate(
+                    instance.name, instance)
                 certificate = self.session.get_host_config()['certificate']
                 container_config = self.config.create_container(instance)
-                container_config['source'] = self.config.get_container_migrate(migrate,
-                                                certificate, CONF.my_ip, instance)
+                container_config['source'] = self.config.get_container_migrate(
+                    migrate, certificate,
+                    CONF.my_ip, instance)
                 self.remote.container_init(container_config, instance)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
@@ -108,8 +105,8 @@ def confirm_migration(self, migration, instance, network_info):
             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)
 
     def finish_migration(self, context, migration, instance, disk_info,
                          network_info, image_meta, resize_instance=False,
@@ -118,25 +115,28 @@ def finish_migration(self, context, migration, instance, disk_info,
 
         try:
             if resize_instance:
-                (config, devices) = self.config.create_profile(instance, network_info)
-                self.session.profile_update(config, devices, instance.name, instance)
+                (config, devices) = self.config.create_profile(
+                    instance, network_info)
+                self.session.profile_update(
+                    config, devices, instance.name, instance)
 
             if power_on:
                 if configdrive.required_by(instance):
                     configdrive_dir = \
-                        self.container_dir.get_container_configdrive(instance.name)
+                        self.container_dir.get_container_configdrive(
+                            instance.name)
                     fileutils.ensure_tree(configdrive_dir)
                 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.error(_LE('failed to resize container '
-                                  '%(instance)s: %(ex)s'),
-                                  {'instance': instance.name, 'ex': ex},
-                                  instance=instance)
+                LOG.error(_LE('failed to resize container '
+                              '%(instance)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',
                   instance=instance)
-        self.session.container_start(instance.name, instance)
\ No newline at end of file
+        self.session.container_start(instance.name, instance)
diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index 59f41fc..5d30540 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -17,10 +17,7 @@
 import os
 
 import nova.conf
-from nova import context as nova_context
-from nova import exception
 from nova import i18n
-from nova import rpc
 from nova.compute import power_state
 
 from oslo_log import log as logging
@@ -57,18 +54,19 @@ def get_session(self):
             else:
                 return self._get_remote_session()
         except Exception as ex:
-            LOG.exception(_LE('Unable to connect to host'))
+            LOG.exception(_LE('Unable to connect to host: %(reason)s'),
+                          {'reason': ex})
 
     def _get_remote_session(self):
         try:
             lxc_cert = os.path.join(os.path.expanduser('~'),
                                     '.config/lxc/client.crt')
             lxc_key = os.path.join(os.path.expanduser('~'),
-                                    '.config/lxc/client.key')
+                                   '.config/lxc/client.key')
             endpoint = 'https://%s:8443' % self.host
             return client.Client(endpoint=endpoint,
-                        cert=(lxc_cert, lxc_key),
-                        verify=False)
+                                 cert=(lxc_cert, lxc_key),
+                                 verify=False)
         except Exception as ex:
             LOG.error(_LE('Unable to connect to LXD host: %s') % ex)
 
@@ -303,7 +301,8 @@ def container_start(self, instance_name, instance):
             else:
                 with excutils.save_and_reraise_exception():
                     LOG.error(
-                        _LE('Failed to start container %(instance)s: %(reason)s'),
+                        _LE('Failed to start container %(instance)s: '
+                            '%(reason)s'),
                         {'instance': instance_name, 'reason': ex},
                         instance=instance)
 
@@ -332,7 +331,7 @@ def container_stop(self, instance_name, instance):
                     LOG.error(
                         _LE('Failed to stop container %(instance)s: '
                             '%(reason)s'), {'instance': instance_name,
-                                        'reason': ex})
+                                            'reason': ex})
 
     def container_reboot(self, instance):
         """Reboot a LXD container
@@ -360,7 +359,7 @@ def container_reboot(self, instance):
                     LOG.error(
                         _LE('Failed to reboot container %(instance)s: '
                             '%(reason)s'), {'instance': instance.name,
-                                           'reason': ex}, instance=instance)
+                                            'reason': ex}, instance=instance)
 
     def container_destroy(self, instance_name, instance):
         """Destroy a LXD container
@@ -542,6 +541,7 @@ def image_upload(self, image, headers, instance):
     #
     # Profile methods
     #
+
     def profile_defined(self, instance_name, instance):
         """Validate if the profile is available on the LXD
            host
@@ -618,10 +618,10 @@ def profile_update(self, config, devices, my_profile, instance):
                     _LE('Failed to delete profile %(instance)s: %(reason)s'),
                     {'instance': instance.name, 'reason': ex})
 
-
     #
     # Host Methods
     #
+
     def host_certificate(self):
         """Return the certificate of the LXD host."""
         try:
@@ -666,7 +666,6 @@ def container_migrate(self, instance_name, instance):
                          '%(instance)s with %(image)s'),
                      {'instance': instance.name,
                       'image': instance.image_ref})
-            return (state, data)
         except Exception as ex:
             with excutils.save_and_reraise_exception():
                 LOG.error(
@@ -676,6 +675,7 @@ def container_migrate(self, instance_name, instance):
     #
     # Operations
     #
+
     def operation_info(self, operation_id, instance):
         LOG.debug('operation_info called for instance', instance=instance)
         try:
@@ -700,7 +700,7 @@ def container_publish(self, instance):
         LOG.debug('container_publish called for instance', instance=instance)
         try:
             lxd = self.client.api
-            container_snapshot={
+            container_snapshot = {
                 'properties': {},
                 'public': False,
                 'source': {
@@ -713,7 +713,7 @@ def container_publish(self, instance):
             lxd.operations[operation_uuid].wait.get()
 
             event = self.client.operations.get(operation_uuid)
-            finerprint = event.metadata('fingerprint')
+            fingerprint = event.metadata('fingerprint')
 
             self.create_alias(fingerprint, instance, alias=instance.name)
         except Exception as ex:


More information about the lxc-devel mailing list