[lxc-devel] [nova-lxd/master] Newton bootstrap

zulcss on Github lxc-bot at linuxcontainers.org
Wed May 11 18:13:50 UTC 2016


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 826 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20160511/0d25f35b/attachment.bin>
-------------- next part --------------
From 8d634ee967a864c0d5ac05e0c3b1ad0c48028b81 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Wed, 11 May 2016 09:54:02 -0400
Subject: [PATCH 01/11] Fix nova-lxd driver loading

Recent nova commit 8eb03de1eb83a6cd2d4d41804e1b8253f94e5400 removed the
mechanism by which nova-lxd was loading its ComputeDriver from out
of tree.

With this change we can now add a nova.virt.lxd
to the nova.virt namespace from the nova-lxd package.
This makes it easier to maintain and easier to merge
into the nova tree when it is ready.

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova/__init__.py                                   |    1 +
 nova/tests/unit/virt/lxd/__init__.py               |    0
 nova/tests/unit/virt/lxd/fake_api.py               |  397 ++++++++
 nova/tests/unit/virt/lxd/session/__init__.py       |    0
 nova/tests/unit/virt/lxd/session/test_container.py |  550 +++++++++++
 nova/tests/unit/virt/lxd/session/test_event.py     |   46 +
 nova/tests/unit/virt/lxd/session/test_image.py     |   54 ++
 nova/tests/unit/virt/lxd/session/test_migrate.py   |   38 +
 nova/tests/unit/virt/lxd/session/test_profile.py   |   79 ++
 nova/tests/unit/virt/lxd/session/test_snapshot.py  |   81 ++
 nova/tests/unit/virt/lxd/stubs.py                  |  110 +++
 nova/tests/unit/virt/lxd/test_config.py            |  134 +++
 nova/tests/unit/virt/lxd/test_driver_api.py        |  493 ++++++++++
 nova/tests/unit/virt/lxd/test_image.py             |   92 ++
 nova/tests/unit/virt/lxd/test_migrate.py           |   91 ++
 nova/tests/unit/virt/lxd/test_operations.py        |  241 +++++
 nova/tests/unit/virt/lxd/test_vif_api.py           |  164 ++++
 nova/virt/__init__.py                              |    1 +
 nova/virt/lxd/__init__.py                          |    3 +
 nova/virt/lxd/config.py                            |  357 +++++++
 nova/virt/lxd/constants.py                         |   34 +
 nova/virt/lxd/container_firewall.py                |   68 ++
 nova/virt/lxd/container_snapshot.py                |  157 +++
 nova/virt/lxd/driver.py                            |  388 ++++++++
 nova/virt/lxd/host.py                              |  202 ++++
 nova/virt/lxd/image.py                             |  293 ++++++
 nova/virt/lxd/migrate.py                           |  169 ++++
 nova/virt/lxd/operations.py                        |  678 +++++++++++++
 nova/virt/lxd/session.py                           | 1026 ++++++++++++++++++++
 nova/virt/lxd/utils.py                             |   93 ++
 nova/virt/lxd/vif.py                               |  223 +++++
 nova_lxd/__init__.py                               |    5 -
 nova_lxd/nova/__init__.py                          |    1 -
 nova_lxd/nova/virt/__init__.py                     |    0
 nova_lxd/nova/virt/lxd/__init__.py                 |    3 -
 nova_lxd/nova/virt/lxd/config.py                   |  357 -------
 nova_lxd/nova/virt/lxd/constants.py                |   34 -
 nova_lxd/nova/virt/lxd/container_firewall.py       |   68 --
 nova_lxd/nova/virt/lxd/container_snapshot.py       |  157 ---
 nova_lxd/nova/virt/lxd/driver.py                   |  388 --------
 nova_lxd/nova/virt/lxd/host.py                     |  202 ----
 nova_lxd/nova/virt/lxd/image.py                    |  293 ------
 nova_lxd/nova/virt/lxd/migrate.py                  |  169 ----
 nova_lxd/nova/virt/lxd/operations.py               |  678 -------------
 nova_lxd/nova/virt/lxd/session.py                  | 1026 --------------------
 nova_lxd/nova/virt/lxd/utils.py                    |   93 --
 nova_lxd/nova/virt/lxd/vif.py                      |  223 -----
 nova_lxd/tests/__init__.py                         |    0
 nova_lxd/tests/fake_api.py                         |  397 --------
 nova_lxd/tests/session/__init__.py                 |    0
 nova_lxd/tests/session/test_container.py           |  550 -----------
 nova_lxd/tests/session/test_event.py               |   46 -
 nova_lxd/tests/session/test_image.py               |   54 --
 nova_lxd/tests/session/test_migrate.py             |   38 -
 nova_lxd/tests/session/test_profile.py             |   79 --
 nova_lxd/tests/session/test_snapshot.py            |   81 --
 nova_lxd/tests/stubs.py                            |  110 ---
 nova_lxd/tests/test_config.py                      |  134 ---
 nova_lxd/tests/test_driver_api.py                  |  493 ----------
 nova_lxd/tests/test_image.py                       |   92 --
 nova_lxd/tests/test_migrate.py                     |   91 --
 nova_lxd/tests/test_operations.py                  |  241 -----
 nova_lxd/tests/test_vif_api.py                     |  164 ----
 setup.cfg                                          |    2 +-
 64 files changed, 6264 insertions(+), 6268 deletions(-)
 create mode 100644 nova/__init__.py
 create mode 100644 nova/tests/unit/virt/lxd/__init__.py
 create mode 100644 nova/tests/unit/virt/lxd/fake_api.py
 create mode 100644 nova/tests/unit/virt/lxd/session/__init__.py
 create mode 100644 nova/tests/unit/virt/lxd/session/test_container.py
 create mode 100644 nova/tests/unit/virt/lxd/session/test_event.py
 create mode 100644 nova/tests/unit/virt/lxd/session/test_image.py
 create mode 100644 nova/tests/unit/virt/lxd/session/test_migrate.py
 create mode 100644 nova/tests/unit/virt/lxd/session/test_profile.py
 create mode 100644 nova/tests/unit/virt/lxd/session/test_snapshot.py
 create mode 100644 nova/tests/unit/virt/lxd/stubs.py
 create mode 100644 nova/tests/unit/virt/lxd/test_config.py
 create mode 100644 nova/tests/unit/virt/lxd/test_driver_api.py
 create mode 100644 nova/tests/unit/virt/lxd/test_image.py
 create mode 100644 nova/tests/unit/virt/lxd/test_migrate.py
 create mode 100644 nova/tests/unit/virt/lxd/test_operations.py
 create mode 100644 nova/tests/unit/virt/lxd/test_vif_api.py
 create mode 100644 nova/virt/__init__.py
 create mode 100644 nova/virt/lxd/__init__.py
 create mode 100644 nova/virt/lxd/config.py
 create mode 100644 nova/virt/lxd/constants.py
 create mode 100644 nova/virt/lxd/container_firewall.py
 create mode 100644 nova/virt/lxd/container_snapshot.py
 create mode 100644 nova/virt/lxd/driver.py
 create mode 100644 nova/virt/lxd/host.py
 create mode 100644 nova/virt/lxd/image.py
 create mode 100644 nova/virt/lxd/migrate.py
 create mode 100644 nova/virt/lxd/operations.py
 create mode 100644 nova/virt/lxd/session.py
 create mode 100644 nova/virt/lxd/utils.py
 create mode 100644 nova/virt/lxd/vif.py
 delete mode 100644 nova_lxd/__init__.py
 delete mode 100644 nova_lxd/nova/__init__.py
 delete mode 100644 nova_lxd/nova/virt/__init__.py
 delete mode 100644 nova_lxd/nova/virt/lxd/__init__.py
 delete mode 100644 nova_lxd/nova/virt/lxd/config.py
 delete mode 100644 nova_lxd/nova/virt/lxd/constants.py
 delete mode 100644 nova_lxd/nova/virt/lxd/container_firewall.py
 delete mode 100644 nova_lxd/nova/virt/lxd/container_snapshot.py
 delete mode 100644 nova_lxd/nova/virt/lxd/driver.py
 delete mode 100644 nova_lxd/nova/virt/lxd/host.py
 delete mode 100644 nova_lxd/nova/virt/lxd/image.py
 delete mode 100644 nova_lxd/nova/virt/lxd/migrate.py
 delete mode 100644 nova_lxd/nova/virt/lxd/operations.py
 delete mode 100644 nova_lxd/nova/virt/lxd/session.py
 delete mode 100644 nova_lxd/nova/virt/lxd/utils.py
 delete mode 100644 nova_lxd/nova/virt/lxd/vif.py
 delete mode 100644 nova_lxd/tests/__init__.py
 delete mode 100644 nova_lxd/tests/fake_api.py
 delete mode 100644 nova_lxd/tests/session/__init__.py
 delete mode 100644 nova_lxd/tests/session/test_container.py
 delete mode 100644 nova_lxd/tests/session/test_event.py
 delete mode 100644 nova_lxd/tests/session/test_image.py
 delete mode 100644 nova_lxd/tests/session/test_migrate.py
 delete mode 100644 nova_lxd/tests/session/test_profile.py
 delete mode 100644 nova_lxd/tests/session/test_snapshot.py
 delete mode 100644 nova_lxd/tests/stubs.py
 delete mode 100644 nova_lxd/tests/test_config.py
 delete mode 100644 nova_lxd/tests/test_driver_api.py
 delete mode 100644 nova_lxd/tests/test_image.py
 delete mode 100644 nova_lxd/tests/test_migrate.py
 delete mode 100644 nova_lxd/tests/test_operations.py
 delete mode 100644 nova_lxd/tests/test_vif_api.py

diff --git a/nova/__init__.py b/nova/__init__.py
new file mode 100644
index 0000000..de40ea7
--- /dev/null
+++ b/nova/__init__.py
@@ -0,0 +1 @@
+__import__('pkg_resources').declare_namespace(__name__)
diff --git a/nova/tests/unit/virt/lxd/__init__.py b/nova/tests/unit/virt/lxd/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/nova/tests/unit/virt/lxd/fake_api.py b/nova/tests/unit/virt/lxd/fake_api.py
new file mode 100644
index 0000000..3ead0dd
--- /dev/null
+++ b/nova/tests/unit/virt/lxd/fake_api.py
@@ -0,0 +1,397 @@
+# Copyright (c) 2015 Canonical Ltd
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+
+def fake_standard_return():
+    return {
+        "type": "sync",
+        "status": "Success",
+        "status_code": 200,
+        "metadata": {}
+    }
+
+
+def fake_host():
+    return {
+        "type": "sync",
+        "status": "Success",
+        "status_code": 200,
+        "metadata": {
+                "api_compat": 1,
+                "auth": "trusted",
+                "config": {},
+                "environment": {
+                    "backing_fs": "ext4",
+                    "driver": "lxc",
+                    "kernel_version": "3.19.0-22-generic",
+                    "lxc_version": "1.1.2",
+                    "lxd_version": "0.12"
+                }
+        }
+    }
+
+
+def fake_image_list_empty():
+    return {
+        "type": "sync",
+        "status": "Success",
+        "status_code": 200,
+        "metadata": []
+    }
+
+
+def fake_image_list():
+    return {
+        "type": "sync",
+        "status": "Success",
+        "status_code": 200,
+        "metadata": ['/1.0/images/trusty']
+    }
+
+
+def fake_image_info():
+    return {
+        "type": "sync",
+        "status": "Success",
+        "status_code": 200,
+        "metadata": {
+            "aliases": [
+                {
+                    "target": "ubuntu",
+                    "description": "ubuntu"
+                }
+            ],
+            "architecture": 2,
+            "fingerprint": "04aac4257341478b49c25d22cea8a6ce"
+                           "0489dc6c42d835367945e7596368a37f",
+            "filename": "",
+            "properties": {},
+            "public": 0,
+            "size": 67043148,
+            "created_at": 0,
+            "expires_at": 0,
+            "uploaded_at": 1435669853
+        }
+    }
+
+
+def fake_alias():
+    return {
+        "type": "sync",
+        "status": "Success",
+        "status_code": 200,
+        "metadata": {
+                "target": "ubuntu",
+                "description": "ubuntu"
+        }
+    }
+
+
+def fake_alias_list():
+    return {
+        "type": "sync",
+        "status": "Success",
+        "status_code": 200,
+        "metadata": [
+            "/1.0/images/aliases/ubuntu"
+        ]
+    }
+
+
+def fake_container_list():
+    return {
+        "type": "sync",
+        "status": "Success",
+        "status_code": 200,
+        "metadata": [
+            "/1.0/containers/trusty-1"
+        ]
+    }
+
+
+def fake_container_state(status):
+    return {
+        "type": "sync",
+        "status": "Success",
+        "status_code": 200,
+        "metadata": {
+            "status_code": status
+        }
+    }
+
+
+def fake_container_log():
+    return {
+        "type": "sync",
+        "status": "Success",
+        "status_code": 200,
+        "metadata": {
+            "log": "fake log"
+        }
+    }
+
+
+def fake_container_migrate():
+    return {
+        "type": "async",
+        "status": "Operation created",
+        "status_code": 100,
+        "metadata": {
+            "id": "dbd9f22c-6da5-4066-8fca-c02f09f76738",
+            "class": "websocket",
+            "created_at": "2016-02-07T09:20:53.127321875-05:00",
+            "updated_at": "2016-02-07T09:20:53.127321875-05:00",
+            "status": "Running",
+            "status_code": 103,
+            "resources": {
+                "containers": [
+                    "/1.0/containers/instance-00000010"
+                ]
+            },
+            "metadata": {
+                "control": "fake_control",
+                "fs": "fake_fs"
+            },
+            "may_cancel": 'false',
+            "err": ""
+        },
+        "operation": "/1.0/operations/dbd9f22c-6da5-4066-8fca-c02f09f76738"
+    }
+
+
+def fake_snapshots_list():
+    return {
+        "type": "sync",
+        "status": "Success",
+        "status_code": 200,
+        "metadata": [
+            "/1.0/containers/trusty-1/snapshots/first"
+        ]
+    }
+
+
+def fake_certificate_list():
+    return {
+        "type": "sync",
+        "status": "Success",
+        "status_code": 200,
+        "metadata": [
+            "/1.0/certificates/ABCDEF01"
+        ]
+    }
+
+
+def fake_certificate():
+    return {
+        "type": "sync",
+        "status": "Success",
+        "status_code": 200,
+        "metadata": {
+            "type": "client",
+            "certificate": "ABCDEF01"
+        }
+    }
+
+
+def fake_profile_list():
+    return {
+        "type": "sync",
+        "status": "Success",
+        "status_code": 200,
+        "metadata": [
+            "/1.0/profiles/fake-profile"
+        ]
+    }
+
+
+def fake_profile():
+    return {
+        "type": "sync",
+        "status": "Success",
+        "status_code": 200,
+        "metadata": {
+            "name": "fake-profile",
+            "config": {
+                "resources.memory": "2GB",
+                "network.0.bridge": "lxcbr0"
+            }
+        }
+    }
+
+
+def fake_operation_list():
+    return {
+        "type": "sync",
+        "status": "Success",
+        "status_code": 200,
+        "metadata": [
+            "/1.0/operations/1234"
+        ]
+    }
+
+
+def fake_operation():
+    return {
+        "type": "async",
+        "status": "OK",
+        "status_code": 100,
+        "operation": "/1.0/operation/1234",
+        "metadata": {
+            "created_at": "2015-06-09T19:07:24.379615253-06:00",
+            "updated_at": "2015-06-09T19:07:23.379615253-06:00",
+            "status": "Running",
+            "status_code": 103,
+            "resources": {
+                "containers": ["/1.0/containers/1"]
+            },
+            "metadata": {},
+            "may_cancel": True
+        }
+    }
+
+
+def fake_operation_info_ok():
+    return {
+        "type": "async",
+        "status": "OK",
+        "status_code": 200,
+        "operation": "/1.0/operation/1234",
+        "metadata": {
+            "created_at": "2015-06-09T19:07:24.379615253-06:00",
+            "updated_at": "2015-06-09T19:07:23.379615253-06:00",
+            "status": "Completed",
+            "status_code": 200,
+            "resources": {
+                "containers": ["/1.0/containers/1"]
+            },
+            "metadata": {},
+            "may_cancel": True
+        }
+    }
+
+
+def fake_operation_info_failed():
+    return {
+        "type": "async",
+        "status": "OK",
+        "status_code": 200,
+        "operation": "/1.0/operation/1234",
+        "metadata": {
+            "created_at": "2015-06-09T19:07:24.379615253-06:00",
+            "updated_at": "2015-06-09T19:07:23.379615253-06:00",
+            "status": "Failure",
+            "status_code": 400,
+            "resources": {
+                "containers": ["/1.0/containers/1"]
+            },
+            "metadata": "Invalid container name",
+            "may_cancel": True
+        }
+    }
+
+
+def fake_network_list():
+    return {
+        "type": "sync",
+        "status": "Success",
+        "status_code": 200,
+        "metadata": [
+            "/1.0/networks/lxcbr0"
+        ]
+    }
+
+
+def fake_network():
+    return {
+        "type": "async",
+        "status": "OK",
+        "status_code": 100,
+        "operation": "/1.0/operation/1234",
+        "metadata": {
+            "name": "lxcbr0",
+            "type": "bridge",
+            "members": ["/1.0/containers/trusty-1"]
+        }
+    }
+
+
+def fake_container_config():
+    return {
+        'name': "my-container",
+        'profiles': ["default"],
+        'architecture': 2,
+        'config': {"limits.cpus": "3"},
+        'expanded_config': {"limits.cpus": "3"},
+        'devices': {
+            'rootfs': {
+                'type': "disk",
+                'path': "/",
+                'source': "UUID=8f7fdf5e-dc60-4524-b9fe-634f82ac2fb6"
+            }
+        },
+        'expanded_devices': {
+            'rootfs': {
+                'type': "disk",
+                'path': "/",
+                'source': "UUID=8f7fdf5e-dc60-4524-b9fe-634f82ac2fb6"}
+        },
+        "eth0": {
+            "type": "nic",
+            "parent": "lxcbr0",
+            "hwaddr": "00:16:3e:f4:e7:1c",
+            "name": "eth0",
+            "nictype": "bridged",
+        }
+    }
+
+
+def fake_container_info():
+    return {
+        'name': "my-container",
+        'profiles': ["default"],
+        'architecture': 2,
+        'config': {"limits.cpus": "3"},
+        'expanded_config': {"limits.cpus": "3"},
+        'devices': {
+            'rootfs': {
+                'type': "disk",
+                'path': "/",
+                'source': "UUID=8f7fdf5e-dc60-4524-b9fe-634f82ac2fb6"
+            }
+        },
+        'expanded_devices': {
+            'rootfs': {
+                'type': "disk",
+                'path': "/",
+                'source': "UUID=8f7fdf5e-dc60-4524-b9fe-634f82ac2fb6"}
+        },
+        "eth0": {
+            "type": "nic",
+            "parent": "lxcbr0",
+            "hwaddr": "00:16:3e:f4:e7:1c",
+            "name": "eth0",
+            "nictype": "bridged",
+        },
+        'status': {
+            'status': "Running",
+            'status_code': 103,
+            'ips': [{'interface': "eth0",
+                     'protocol': "INET6",
+                     'address': "2001:470:b368:1020:1::2",
+                     'host_veth': "vethGMDIY9"},
+                    {'interface': "eth0",
+                     'protocol': "INET",
+                     'address': "172.16.15.30",
+                     'host_veth': "vethGMDIY9"}]},
+    }
diff --git a/nova/tests/unit/virt/lxd/session/__init__.py b/nova/tests/unit/virt/lxd/session/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/nova/tests/unit/virt/lxd/session/test_container.py b/nova/tests/unit/virt/lxd/session/test_container.py
new file mode 100644
index 0000000..69690f7
--- /dev/null
+++ b/nova/tests/unit/virt/lxd/session/test_container.py
@@ -0,0 +1,550 @@
+# Copyright 2015 Canonical Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+#    implied. See the License for the specific language governing
+#    permissions and limitations under the License.
+
+"""
+Unit tests for ContinerMixin class
+
+The following tests the ContainerMixin class
+for nova-lxd.
+"""
+
+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_lxd.nova.virt.lxd import session
+from nova_lxd.tests import fake_api
+from nova_lxd.tests import stubs
+
+
+ at ddt.ddt
+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_defined(instance.name),
+            mock.call.container_update(instance.name, config)]
+        self.assertEqual(calls, self.ml.method_calls)
+
+    @stubs.annotated_data(
+        ('api_fail', True, lxd_exceptions.APIError('Fake', 500),
+         exception.NovaException),
+        ('missing_container', False, None,
+         exception.InstanceNotFound)
+    )
+    def test_container_update_fail(self, tag, container_defined, 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()
+        if container_defined:
+            self.ml.container_defined.return_value = container_defined
+            self.ml.container_update.side_effect = (
+                lxd_exceptions.APIError('Fake', 500))
+            self.assertRaises(
+                expected,
+                self.session.container_update, config, instance)
+        if not container_defined:
+            self.ml.container_defined.return_value = container_defined
+            self.assertRaises(
+                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
+        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_defined.return_value = defined
+        self.ml.container_start.return_value = side_effect
+        self.assertEqual(None,
+                         self.session.container_start(instance.name,
+                                                      instance))
+        calls = [mock.call.container_defined(instance.name),
+                 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(
+        ('container_missing', False,
+         exception.InstanceNotFound),
+        ('api_error', True,
+         exception.NovaException,
+         lxd_exceptions.APIError('Fake', 500)),
+    )
+    def test_container_start_fail(self, tag, container_defined,
+                                  expected, side_effect=None):
+        """
+        container_start starts a container on a given LXD host.
+        Veify that an exception.InstanceNotFound when the container
+        is not found on an LXD host. Raises an exception.NovaException
+        when there is an APIError.
+        """
+        instance = stubs._fake_instance()
+        if container_defined:
+            self.ml.container_defined.return_value = container_defined
+            self.ml.container_start.side_effect = side_effect
+            self.assertRaises(expected,
+                              self.session.container_start,
+                              instance.name, instance)
+        if not container_defined:
+            self.ml.container_defined.return_value = container_defined
+            self.assertRaises(expected,
+                              self.session.container_start, instance.name,
+                              instance)
+
+    @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_defined(instance.name),
+                 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_defined(instance.name),
+                 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', True, (200, fake_api.fake_operation_info_ok())),
+        ('missing', False, (200, fake_api.fake_operation_info_ok()))
+    )
+    def test_container_destroy(self, tag, container_defined, side_effect):
+        """
+        container_destroy delete a container from the LXD Host. Check
+        that the approiate pylxd calls are made.
+        """
+        instance = stubs._fake_instance()
+        if container_defined:
+            self.ml.container_defined.return_value = container_defined
+            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_defined(instance.name),
+                     mock.call.container_defined(instance.name),
+                     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)
+        if not container_defined:
+            self.ml.container_defined.return_value = container_defined
+            self.assertEqual(None,
+                             self.session.container_destroy(instance.name,
+                                                            instance))
+            calls = [mock.call.container_defined(instance.name)]
+            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_defined.return_value = container_defined
+            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_defined(instance.name),
+            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)
diff --git a/nova/tests/unit/virt/lxd/session/test_event.py b/nova/tests/unit/virt/lxd/session/test_event.py
new file mode 100644
index 0000000..f50b879
--- /dev/null
+++ b/nova/tests/unit/virt/lxd/session/test_event.py
@@ -0,0 +1,46 @@
+# Copyright 2015 Canonical Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+#    implied. See the License for the specific language governing
+#    permissions and limitations under the License.
+
+import ddt
+import mock
+
+from nova import test
+
+from nova_lxd.nova.virt.lxd import session
+from nova_lxd.tests import stubs
+
+
+ at ddt.ddt
+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)
diff --git a/nova/tests/unit/virt/lxd/session/test_image.py b/nova/tests/unit/virt/lxd/session/test_image.py
new file mode 100644
index 0000000..fa0c6dc
--- /dev/null
+++ b/nova/tests/unit/virt/lxd/session/test_image.py
@@ -0,0 +1,54 @@
+# Copyright 2015 Canonical Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+#    implied. See the License for the specific language governing
+#    permissions and limitations under the License.
+
+import ddt
+import mock
+
+from nova import test
+
+from nova_lxd.nova.virt.lxd import session
+from nova_lxd.tests import stubs
+
+
+ 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)
diff --git a/nova/tests/unit/virt/lxd/session/test_migrate.py b/nova/tests/unit/virt/lxd/session/test_migrate.py
new file mode 100644
index 0000000..9f96638
--- /dev/null
+++ b/nova/tests/unit/virt/lxd/session/test_migrate.py
@@ -0,0 +1,38 @@
+# Copyright 2015 Canonical Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+#    implied. See the License for the specific language governing
+#    permissions and limitations under the License.
+
+import ddt
+import mock
+
+from nova import test
+
+from nova_lxd.nova.virt.lxd import session
+from nova_lxd.tests import stubs
+
+
+ at ddt.ddt
+class SessionMigrateTest(test.NoDBTestCase):
+
+    def setUp(self):
+        super(SessionMigrateTest, 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()
diff --git a/nova/tests/unit/virt/lxd/session/test_profile.py b/nova/tests/unit/virt/lxd/session/test_profile.py
new file mode 100644
index 0000000..2680849
--- /dev/null
+++ b/nova/tests/unit/virt/lxd/session/test_profile.py
@@ -0,0 +1,79 @@
+# Copyright 2015 Canonical Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+#    implied. See the License for the specific language governing
+#    permissions and limitations under the License.
+
+
+import ddt
+import mock
+
+from nova import exception
+from nova import test
+from pylxd.deprecated import exceptions as lxd_exceptions
+
+from nova_lxd.nova.virt.lxd import session
+from nova_lxd.tests import fake_api
+from nova_lxd.tests import stubs
+
+
+ at ddt.ddt
+class SessionProfileTest(test.NoDBTestCase):
+
+    def setUp(self):
+        super(SessionProfileTest, self).setUp()
+
+        """This is so we can mock out pylxd API calls."""
+        self.ml = stubs.lxd_mock()
+        lxd_patcher = mock.patch('pylxd.api.API',
+                                 mock.Mock(return_value=self.ml))
+        lxd_patcher.start()
+        self.addCleanup(lxd_patcher.stop)
+
+        self.session = session.LXDAPISession()
+
+    @stubs.annotated_data(
+        ('empty', [], []),
+        ('valid', ['test'], ['test']),
+    )
+    def test_profile_list(self, tag, side_effect, expected):
+        self.ml.profile_list.return_value = side_effect
+        self.assertEqual(expected,
+                         self.session.profile_list())
+
+    def test_profile_list_fail(self):
+        self.ml.profile_list.side_effect = (
+            lxd_exceptions.APIError('Fake', 500))
+        self.assertRaises(
+            exception.NovaException,
+            self.session.profile_list)
+
+    def test_profile_create(self):
+        instance = stubs._fake_instance()
+        config = mock.Mock()
+        self.ml.profile_defined.return_value = True
+        self.ml.profile_create.return_value = \
+            (200, fake_api.fake_standard_return())
+        self.assertEqual((200, fake_api.fake_standard_return()),
+                         self.session.profile_create(config,
+                                                     instance))
+        calls = [mock.call.profile_list(),
+                 mock.call.profile_create(config)]
+        self.assertEqual(calls, self.ml.method_calls)
+
+    def test_profile_delete(self):
+        instance = stubs._fake_instance()
+        self.ml.profile_defined.return_value = True
+        self.ml.profile_delete.return_value = \
+            (200, fake_api.fake_standard_return())
+        self.assertEqual(None,
+                         self.session.profile_delete(instance))
diff --git a/nova/tests/unit/virt/lxd/session/test_snapshot.py b/nova/tests/unit/virt/lxd/session/test_snapshot.py
new file mode 100644
index 0000000..61759d6
--- /dev/null
+++ b/nova/tests/unit/virt/lxd/session/test_snapshot.py
@@ -0,0 +1,81 @@
+# Copyright 2015 Canonical Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+#    implied. See the License for the specific language governing
+#    permissions and limitations under the License.
+
+import ddt
+import mock
+
+from nova import exception
+from nova import test
+from pylxd.deprecated import exceptions as lxd_exceptions
+
+from nova_lxd.nova.virt.lxd import session
+from nova_lxd.tests import fake_api
+from nova_lxd.tests import stubs
+
+
+ at ddt.ddt
+class 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)
diff --git a/nova/tests/unit/virt/lxd/stubs.py b/nova/tests/unit/virt/lxd/stubs.py
new file mode 100644
index 0000000..c66c061
--- /dev/null
+++ b/nova/tests/unit/virt/lxd/stubs.py
@@ -0,0 +1,110 @@
+# Copyright (c) 2015 Canonical Ltd
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import ddt
+import mock
+
+from nova import context
+from nova.tests.unit import fake_instance
+
+
+class MockConf(mock.Mock):
+
+    def __init__(self, lxd_args=(), lxd_kwargs={}, *args, **kwargs):
+        default = {
+            'config_drive_format': None,
+            'instances_path': '/fake/instances/path',
+            'image_cache_subdirectory_name': '/fake/image/cache',
+            'vif_plugging_timeout': 10,
+            'my_ip': '1.2.3.4',
+            'vlan_interface': 'vlanif',
+            'flat_interface': 'flatif',
+        }
+
+        default.update(kwargs)
+        super(MockConf, self).__init__(*args, **default)
+
+        lxd_default = {
+            'root_dir': '/fake/lxd/root',
+            'timeout': 20,
+            'retry_interval': 2
+        }
+        lxd_default.update(lxd_kwargs)
+        self.lxd = mock.Mock(lxd_args, **lxd_default)
+
+
+class MockInstance(mock.Mock):
+
+    def __init__(self, name='fake-uuid', uuid='fake-uuid',
+                 image_ref='mock_image', ephemeral_gb=0, memory_mb=-1,
+                 vcpus=0, *args, **kwargs):
+        super(MockInstance, self).__init__(
+            uuid=uuid,
+            image_ref=image_ref,
+            ephemeral_gb=ephemeral_gb,
+            *args, **kwargs)
+        self.uuid = uuid
+        self.name = name
+        self.flavor = mock.Mock(memory_mb=memory_mb, vcpus=vcpus)
+
+
+def lxd_mock(*args, **kwargs):
+    default = {
+        'profile_list.return_value': ['fake_profile'],
+        'container_list.return_value': ['mock-instance-1', 'mock-instance-2'],
+        'host_ping.return_value': True,
+    }
+    default.update(kwargs)
+    return mock.Mock(*args, **default)
+
+
+def annotated_data(*args):
+    class List(list):
+        pass
+
+    class Dict(dict):
+        pass
+
+    new_args = []
+
+    for arg in args:
+        if isinstance(arg, (list, tuple)):
+            new_arg = List(arg)
+            new_arg.__name__ = arg[0]
+        elif isinstance(arg, dict):
+            new_arg = Dict(arg)
+            new_arg.__name__ = arg['tag']
+        else:
+            raise TypeError('annotate_data can only handle dicts, '
+                            'lists and tuples')
+        new_args.append(new_arg)
+
+    return lambda func: ddt.data(*new_args)(ddt.unpack(func))
+
+
+def _fake_instance():
+    ctxt = context.get_admin_context()
+    _instance_values = {
+        'display_name': 'fake_display_name',
+        'name': 'fake_name',
+        'uuid': 'fake_uuid',
+        'image_ref': 'fake_image',
+        'vcpus': 1,
+        'memory_mb': 512,
+        'root_gb': 10,
+        'host': 'fake_host',
+        'expected_attrs': ['system_metadata'],
+    }
+    return fake_instance.fake_instance_obj(
+        ctxt, **_instance_values)
diff --git a/nova/tests/unit/virt/lxd/test_config.py b/nova/tests/unit/virt/lxd/test_config.py
new file mode 100644
index 0000000..19b658d
--- /dev/null
+++ b/nova/tests/unit/virt/lxd/test_config.py
@@ -0,0 +1,134 @@
+# Copyright 2015 Canonical Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import ddt
+import mock
+
+from nova import test
+from nova.tests.unit import fake_network
+
+from nova.virt.lxd import config
+from nova.virt.lxd import session
+from nova.virt.lxd import utils as container_dir
+import stubs
+
+
+ at ddt.ddt
+ at mock.patch.object(config, 'CONF', stubs.MockConf())
+ at mock.patch.object(container_dir, 'CONF', stubs.MockConf())
+class LXDTestContainerConfig(test.NoDBTestCase):
+    """LXD Container configuration unit tests."""
+
+    def setUp(self):
+        super(LXDTestContainerConfig, self).setUp()
+        self.config = config.LXDContainerConfig()
+
+    @stubs.annotated_data(
+        ('test_name', 'name', 'instance-00000001'),
+        ('test_source', 'source', {'type': 'image',
+                                   'alias': 'fake_image'}),
+        ('test_devices', 'devices', {})
+    )
+    def test_create_container(self, tag, key, expected):
+        """Tests the create_container methond on LXDContainerConfig.
+           Inspect that the correct dictionary is returned for a given
+           instance.
+        """
+        instance = stubs._fake_instance()
+        container_config = self.config.create_container(instance)
+        self.assertEqual(container_config[key], expected)
+
+    @stubs.annotated_data(
+        ('test_memmoy', 'limits.memory', '512MB')
+    )
+    def test_create_config(self, tag, key, expected):
+        instance = stubs._fake_instance()
+        instance_name = 'fake_instance'
+        config = self.config.create_config(instance_name, instance)
+        self.assertEqual(config[key], expected)
+
+    def test_create_network(self):
+        instance = stubs._fake_instance()
+        instance_name = 'fake_instance'
+        network_info = fake_network.fake_get_instance_nw_info(self)
+        config = self.config.create_network(instance_name, instance,
+                                            network_info)
+        self.assertEqual({'fake_br1': {'hwaddr': 'DE:AD:BE:EF:00:01',
+                                       'nictype': 'bridged',
+                                       'parent': 'fake_br1',
+                                       'type': 'nic'}}, config)
+
+    @mock.patch('os.path.exists', mock.Mock(return_value=True))
+    def test_create_disk_path(self):
+        instance = stubs._fake_instance()
+        config = self.config.configure_disk_path('/fake/src_path',
+                                                 '/fake/dest_path',
+                                                 'fake_disk', instance)
+        self.assertEqual({'fake_disk': {'path': '/fake/dest_path',
+                                        'source': '/fake/src_path',
+                                        'type': 'disk',
+                                        'optional': 'True'}}, config)
+
+    def test_config_instance_options(self):
+        instance = stubs._fake_instance()
+        config = {}
+        container_config = self.config.config_instance_options(config,
+                                                               instance)
+        self.assertEqual({'boot.autostart': 'True'}, container_config)
+
+    def test_create_container_source(self):
+        instance = stubs._fake_instance()
+        config = self.config.get_container_source(instance)
+        self.assertEqual(config, {'type': 'image', 'alias': 'fake_image'})
+
+    @mock.patch.object(session.LXDAPISession, 'get_host_config',
+                       mock.Mock(return_value={'storage': 'btrfs'}))
+    def test_container_root_btrfs(self):
+        instance = stubs._fake_instance()
+        config = self.config.configure_container_root(instance)
+        self.assertEqual({'root': {'path': '/',
+                                   'type': 'disk',
+                                   'size': '10GB'}}, config)
+
+    @mock.patch.object(session.LXDAPISession, 'get_host_config',
+                       mock.Mock(return_value={'storage': 'zfs'}))
+    def test_container_root_zfs(self):
+        instance = stubs._fake_instance()
+        config = self.config.configure_container_root(instance)
+        self.assertEqual({'root': {'path': '/',
+                                   'type': 'disk',
+                                   'size': '10GB'}}, config)
+
+    @mock.patch.object(session.LXDAPISession, 'get_host_config',
+                       mock.Mock(return_value={'storage': 'lvm'}))
+    def test_container_root_lvm(self):
+        instance = stubs._fake_instance()
+        config = self.config.configure_container_root(instance)
+        self.assertEqual({'root': {'path': '/',
+                                   'type': 'disk'}}, config)
+
+    def test_container_nested_container(self):
+        instance = stubs._fake_instance()
+        instance.flavor.extra_specs = {'lxd:nested_allowed': True}
+        config = self.config.config_instance_options({}, instance)
+        self.assertEqual({'security.nesting': 'True',
+                          'boot.autostart': 'True'}, config)
+
+    def test_container_privileged_container(self):
+        instance = stubs._fake_instance()
+        instance.flavor.extra_specs = {'lxd:privileged_allowed': True}
+        config = self.config.config_instance_options({}, instance)
+        self.assertEqual({'security.privileged': 'True',
+                          'boot.autostart': 'True'}, config)
diff --git a/nova/tests/unit/virt/lxd/test_driver_api.py b/nova/tests/unit/virt/lxd/test_driver_api.py
new file mode 100644
index 0000000..7b6ad58
--- /dev/null
+++ b/nova/tests/unit/virt/lxd/test_driver_api.py
@@ -0,0 +1,493 @@
+# Copyright 2015 Canonical Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import inspect
+import json
+import os
+import platform
+from pylxd.deprecated import exceptions as lxd_exceptions
+
+import ddt
+import mock
+from oslo_config import cfg
+import six
+
+from nova.compute import arch
+from nova.compute import hv_type
+from nova.compute import power_state
+from nova.compute import vm_mode
+from nova import exception
+from nova import test
+from nova.virt import fake
+from nova.virt import hardware
+
+from nova.virt.lxd import driver
+from nova.virt.lxd import host
+from nova.virt.lxd import operations as container_ops
+from nova.virt.lxd import session
+from nova.virt.lxd import utils as container_dir
+import stubs
+
+
+class LXDTestConfig(test.NoDBTestCase):
+
+    def test_config(self):
+        self.assertIsInstance(driver.CONF.lxd, cfg.ConfigOpts.GroupAttr)
+        self.assertEqual(os.path.abspath('/var/lib/lxd'),
+                         os.path.abspath(driver.CONF.lxd.root_dir))
+        self.assertEqual(-1, driver.CONF.lxd.timeout)
+
+
+ at ddt.ddt
+ at mock.patch.object(container_ops, 'CONF', stubs.MockConf())
+ at mock.patch.object(container_dir, 'CONF', stubs.MockConf())
+ at mock.patch.object(driver, 'CONF', stubs.MockConf())
+ at mock.patch.object(host, 'CONF', stubs.MockConf())
+class LXDTestDriver(test.NoDBTestCase):
+
+    @mock.patch.object(driver, 'CONF', stubs.MockConf())
+    def setUp(self):
+        super(LXDTestDriver, 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.connection = driver.LXDDriver(fake.FakeVirtAPI())
+
+    def test_capabilities(self):
+        self.assertFalse(self.connection.capabilities['has_imagecache'])
+        self.assertFalse(self.connection.capabilities['supports_recreate'])
+        self.assertFalse(
+            self.connection.capabilities['supports_migrate_to_same_host'])
+
+    def test_init_host(self):
+        self.assertEqual(
+            True,
+            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.
+                                                 APIError('Fake',
+                                                          500))}),
+    )
+    def test_init_host_fail(self, tag, config):
+        self.ml.configure_mock(**config)
+        self.assertRaises(
+            exception.HostNotFound,
+            self.connection.init_host,
+            None
+        )
+
+    @stubs.annotated_data(
+        ('running', {'state': 200, 'mem': 0, 'max_mem': 0},
+         power_state.RUNNING),
+        ('shutdown', {'state': 102, 'mem': 0, 'max_mem': 0},
+         power_state.SHUTDOWN),
+        ('crashed', {'state': 108, 'mem': 0, 'max_mem': 0},
+         power_state.CRASHED),
+        ('suspend', {'state': 109, 'mem': 0, 'max_mem': 0},
+         power_state.SUSPENDED),
+        ('no_state', {'state': 401, 'mem': 0, 'max_mem': 0},
+         power_state.NOSTATE),
+    )
+    def test_get_info(self, tag, side_effect, expected):
+        instance = stubs._fake_instance()
+        with mock.patch.object(session.LXDAPISession,
+                               "container_state",
+                               ) as state:
+            state.return_value = side_effect
+            info = self.connection.get_info(instance)
+            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},
+            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
+        )
+
+    @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')
+
+    @stubs.annotated_data(
+        ('undefined', False),
+        ('404', lxd_exceptions.APIError('Not found', 404)),
+    )
+    @mock.patch('oslo_concurrency.lockutils.lock')
+    def test_spawn_new(self, tag, side_effect, mc):
+        context = mock.Mock()
+        instance = stubs.MockInstance()
+        image_meta = mock.Mock()
+        injected_files = mock.Mock()
+        network_info = mock.Mock()
+        block_device_info = mock.Mock()
+        self.ml.container_defined.side_effect = [side_effect]
+
+        with test.nested(
+                mock.patch.object(self.connection.container_ops,
+                                  'spawn'),
+        ) as (
+                create_container
+        ):
+            self.connection.spawn(context, instance, image_meta,
+                                  injected_files, None, network_info,
+                                  block_device_info)
+            self.assertTrue(create_container)
+
+    def test_destroy_fail(self):
+        instance = stubs._fake_instance()
+        context = mock.Mock()
+        network_info = mock.Mock()
+        self.ml.container_destroy.side_effect = (
+            lxd_exceptions.APIError('Fake', 500))
+        with test.nested(
+            mock.patch.object(session.LXDAPISession,
+                              'container_destroy'),
+            mock.patch.object(session.LXDAPISession,
+                              'container_stop'),
+            mock.patch.object(self.connection, 'cleanup'),
+            mock.patch.object(container_ops.LXDContainerOperations,
+                              'unplug_vifs'),
+
+        ) as (
+            container_destroy,
+            container_stop,
+            cleanup,
+            unplug_vifs
+        ):
+            self.connection.destroy(context, instance, network_info)
+
+    def test_destroy(self):
+        instance = stubs._fake_instance()
+        context = mock.Mock()
+        network_info = mock.Mock()
+        with test.nested(
+                mock.patch.object(session.LXDAPISession,
+                                  'container_stop'),
+                mock.patch.object(session.LXDAPISession,
+                                  'container_destroy'),
+                mock.patch.object(self.connection,
+                                  'cleanup'),
+                mock.patch.object(container_ops.LXDContainerOperations,
+                                  'unplug_vifs'),
+        ) as (
+                container_stop,
+                container_destroy,
+                cleanup,
+                unplug_vifs
+        ):
+            self.connection.destroy(context, instance, network_info)
+            self.assertTrue(container_stop)
+            self.assertTrue(container_destroy)
+            self.assertTrue(cleanup)
+            unplug_vifs.assert_called_with(instance, network_info)
+
+    @mock.patch('os.path.exists', mock.Mock(return_value=True))
+    @mock.patch('shutil.rmtree')
+    @mock.patch('pwd.getpwuid', mock.Mock(return_value=mock.Mock(pw_uid=1234)))
+    @mock.patch.object(container_ops.utils, 'execute')
+    def test_cleanup(self, mr, mu):
+        instance = stubs.MockInstance()
+        self.assertEqual(
+            None,
+            self.connection.cleanup({}, instance, [], [], None, None, None))
+
+    @mock.patch('six.moves.builtins.open')
+    @mock.patch.object(container_ops.utils, 'execute')
+    @mock.patch('pwd.getpwuid', mock.Mock(return_value=mock.Mock(pw_uid=1234)))
+    @mock.patch('os.getuid', mock.Mock())
+    @mock.patch('os.path.exists', mock.Mock(return_value=True))
+    def test_get_console_output(self, me, mo):
+        instance = stubs.MockInstance()
+        mo.return_value.__enter__.return_value = six.BytesIO(b'fake contents')
+        self.assertEqual(b'fake contents',
+                         self.connection.get_console_output({}, instance))
+        calls = [
+            mock.call('chown', '1234:1234',
+                      '/var/log/lxd/fake-uuid/console.log',
+                      run_as_root=True),
+            mock.call('chmod', '755',
+                      '/fake/lxd/root/containers/fake-uuid',
+                      run_as_root=True)
+        ]
+        self.assertEqual(calls, me.call_args_list)
+
+    @mock.patch.object(host.compute_utils, 'get_machine_ips')
+    @stubs.annotated_data(
+        ('found', ['1.2.3.4']),
+        ('not-found', ['4.3.2.1']),
+    )
+    def test_get_host_ip_addr(self, tag, return_value, mi):
+        mi.return_value = return_value
+        self.assertEqual('1.2.3.4', self.connection.get_host_ip_addr())
+
+    @mock.patch('socket.gethostname', mock.Mock(return_value='fake_hostname'))
+    @mock.patch('os.statvfs', return_value=mock.Mock(f_blocks=131072000,
+                                                     f_bsize=8192,
+                                                     f_bavail=65536000))
+    @mock.patch('six.moves.builtins.open')
+    @mock.patch.object(container_ops.utils, 'execute')
+    def test_get_available_resource(self, me, mo, ms):
+        me.return_value = ('Model name:          Fake CPU\n'
+                           'Vendor ID:           FakeVendor\n'
+                           'Socket(s):           10\n'
+                           'Core(s) per socket:  5\n'
+                           'Thread(s) per core:  4\n'
+                           '\n',
+                           None)
+        meminfo = mock.MagicMock()
+        meminfo.__enter__.return_value = six.moves.cStringIO(
+            'MemTotal: 10240000 kB\n'
+            'MemFree:   2000000 kB\n'
+            'Buffers:     24000 kB\n'
+            'Cached:      24000 kB\n')
+
+        mo.side_effect = [
+            six.moves.cStringIO('flags: fake flag goes here\n'
+                                'processor: 2\n'
+                                '\n'),
+            meminfo,
+        ]
+        value = self.connection.get_available_resource(None)
+        value['cpu_info'] = json.loads(value['cpu_info'])
+        value['supported_instances'] = [[arch.I686, hv_type.LXD,
+                                         vm_mode.EXE],
+                                        [arch.X86_64, hv_type.LXD,
+                                         vm_mode.EXE],
+                                        [arch.I686, hv_type.LXC,
+                                         vm_mode.EXE],
+                                        [arch.X86_64, hv_type.LXC,
+                                         vm_mode.EXE]]
+        expected = {'cpu_info': {u'arch': platform.uname()[5],
+                                 u'features': u'fake flag goes here',
+                                 u'model': u'Fake CPU',
+                                 u'topology': {u'cores': u'5',
+                                               u'sockets': u'10',
+                                               u'threads': u'4'},
+                                 u'vendor': u'FakeVendor'},
+                    'hypervisor_hostname': 'fake_hostname',
+                    'hypervisor_type': 'lxd',
+                    'hypervisor_version': '011',
+                    'local_gb': 1000,
+                    'local_gb_used': 500,
+                    'memory_mb': 10000,
+                    'memory_mb_used': 8000,
+                    'numa_topology': None,
+                    'supported_instances': [[arch.I686, hv_type.LXD,
+                                             vm_mode.EXE],
+                                            [arch.X86_64, hv_type.LXD,
+                                             vm_mode.EXE],
+                                            [arch.I686, hv_type.LXC,
+                                             vm_mode.EXE],
+                                            [arch.X86_64, hv_type.LXC,
+                                             vm_mode.EXE]],
+                    'vcpus': 200,
+                    'vcpus_used': 0}
+        self.assertEqual(expected, value)
+        me.assert_called_once_with('lscpu')
+        self.assertEqual([mock.call('/proc/cpuinfo', 'r'),
+                          mock.call('/proc/meminfo')],
+                         mo.call_args_list)
+        ms.assert_called_once_with('/fake/lxd/root')
+
+    def test_container_reboot(self):
+        instance = stubs._fake_instance()
+        context = mock.Mock()
+        network_info = mock.Mock()
+        reboot_type = 'SOFT'
+        with test.nested(
+                mock.patch.object(self.connection.container_ops,
+                                  'reboot')
+        ) as (
+                reboot
+        ):
+            self.connection.reboot(context, instance,
+                                   network_info, reboot_type)
+            self.assertTrue(reboot)
+
+    def test_container_power_off(self):
+        instance = stubs._fake_instance()
+        with test.nested(
+                mock.patch.object(self.connection.container_ops,
+                                  'power_off')
+        ) as (
+                power_off
+        ):
+            self.connection.power_off(instance)
+            self.assertTrue(power_off)
+
+    def test_container_power_on(self):
+        context = mock.Mock()
+        instance = stubs._fake_instance()
+        network_info = mock.Mock()
+        with test.nested(
+                mock.patch.object(self.connection.container_ops,
+                                  'power_on')
+        ) as (
+                power_on
+        ):
+            self.connection.power_on(context, instance, network_info)
+            self.assertTrue(power_on)
+
+    @stubs.annotated_data(
+        ('refresh_security_group_rules', (mock.Mock(),)),
+        ('refresh_security_group_members', (mock.Mock(),)),
+        ('refresh_provider_fw_rules',),
+        ('refresh_instance_security_rules', (mock.Mock(),)),
+        ('ensure_filtering_rules_for_instance', (mock.Mock(), mock.Mock())),
+        ('filter_defer_apply_on',),
+        ('filter_defer_apply_off',),
+        ('unfilter_instance', (mock.Mock(), mock.Mock())),
+    )
+    def test_firewall_calls(self, name, args=()):
+        with mock.patch.object(self.connection.container_firewall,
+                               'firewall_driver') as mf:
+            driver_method = getattr(self.connection, name)
+            firewall_method = getattr(mf, name)
+            self.assertEqual(
+                firewall_method.return_value,
+                driver_method(*args))
+            firewall_method.assert_called_once_with(*args)
+
+    @mock.patch.object(host.utils, 'execute')
+    def test_get_host_uptime(self, me):
+        me.return_value = ('out', 'err')
+        self.assertEqual('out',
+                         self.connection.get_host_uptime())
+
+    @mock.patch('socket.gethostname', mock.Mock(return_value='mock_hostname'))
+    def test_get_available_nodes(self):
+        self.assertEqual(
+            ['mock_hostname'], self.connection.get_available_nodes())
+
+    @mock.patch('socket.gethostname', mock.Mock(return_value='mock_hostname'))
+    @stubs.annotated_data(
+        ('mock_hostname', True),
+        ('wrong_hostname', False),
+    )
+    def test_node_is_available(self, nodename, available):
+        self.assertEqual(available,
+                         self.connection.node_is_available(nodename))
+
+
+ at ddt.ddt
+class LXDTestDriverNoops(test.NoDBTestCase):
+
+    def setUp(self):
+        super(LXDTestDriverNoops, self).setUp()
+        self.connection = driver.LXDDriver(fake.FakeVirtAPI())
+
+    @ddt.data(
+        'list_instance_uuids',
+        'get_diagnostics',
+        'get_instance_diagnostics',
+        'get_all_bw_counters',
+        'get_all_volume_usage',
+        'attach_volume',
+        'detach_volume',
+        'soft_delete',
+        'post_live_migration_at_source',
+        'check_instance_shared_storage_local',
+        'check_instance_shared_storage_remote',
+        'check_can_live_migrate_destination',
+        'check_can_live_migrate_destination_cleanup',
+        'check_can_live_migrate_source',
+        'get_instance_disk_info',
+        'poll_rebooting_instances',
+        'host_power_action',
+        'host_maintenance_mode',
+        'set_host_enabled',
+        'block_stats',
+        'add_to_aggregate',
+        'remove_from_aggregate',
+        'undo_aggregate_operation',
+        'volume_snapshot_create',
+        'volume_snapshot_delete',
+        'quiesce',
+        'unquiesce',
+    )
+    def test_notimplemented(self, method):
+        call = getattr(self.connection, method)
+        argspec = inspect.getargspec(call)
+        self.assertRaises(
+            NotImplementedError,
+            call,
+            *([None] * (len(argspec.args) - 1)))
+
+    @ddt.data(
+        'post_interrupted_snapshot_cleanup',
+        'check_instance_shared_storage_cleanup',
+        'manage_image_cache',
+    )
+    def test_pass(self, method):
+        call = getattr(self.connection, method)
+        argspec = inspect.getargspec(call)
+        self.assertEqual(
+            None,
+            call(*([None] * (len(argspec.args) - 1))))
+
+    @stubs.annotated_data(
+        ('deallocate_networks_on_reschedule', False),
+        ('macs_for_instance', None),
+        ('get_per_instance_usage', {}),
+        ('instance_on_disk', False),
+    )
+    def test_return(self, method, expected):
+        call = getattr(self.connection, method)
+        argspec = inspect.getargspec(call)
+        self.assertEqual(
+            expected,
+            call(*([None] * (len(argspec.args) - 1))))
diff --git a/nova/tests/unit/virt/lxd/test_image.py b/nova/tests/unit/virt/lxd/test_image.py
new file mode 100644
index 0000000..3ea7fa1
--- /dev/null
+++ b/nova/tests/unit/virt/lxd/test_image.py
@@ -0,0 +1,92 @@
+# Copyright 2015 Canonical Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import io
+import json
+from nova import exception
+from nova import test
+import os
+import tarfile
+
+import ddt
+import fixtures
+import mock
+from oslo_concurrency import lockutils
+from oslo_config import fixture as config_fixture
+
+
+from nova.virt.lxd import image
+from nova.virt.lxd import session
+import stubs
+
+
+ at ddt.ddt
+class LXDTestContainerImage(test.NoDBTestCase):
+
+    @mock.patch.object(session, 'CONF', stubs.MockConf())
+    def setUp(self):
+        super(LXDTestContainerImage, self).setUp()
+
+        self.tempdir = self.useFixture(fixtures.TempDir()).path
+        self.fixture = self.useFixture(config_fixture.Config(lockutils.CONF))
+        self.fixture.config(lock_path=self.tempdir,
+                            group='oslo_concurrency')
+        self.fixture.config(disable_process_locking=True,
+                            group='oslo_concurrency')
+
+        self.image = image.LXDContainerImage()
+
+    @stubs.annotated_data(
+        ('valid_image_raw', True, {'disk_format': 'raw'}, None),
+        ('valid_image_root-tar', True, {'disk_format': 'root-tar'}, None),
+        ('qcow2_image', False, {'disk_format': 'qcow2'},
+            exception.ImageUnacceptable),
+        ('iso_image', False, {'disk_format': 'iso'},
+            exception.ImageUnacceptable),
+        ('image_unacceptable', False, {'disk_format': ''},
+            exception.ImageUnacceptable),
+        ('bad_meta', False, {},
+            exception.ImageUnacceptable),
+    )
+    def test_image(self, tag, sucess, image_data, expected):
+        context = mock.Mock
+        instance = stubs._fake_instance()
+        with mock.patch.object(image.IMAGE_API, 'get',
+                               return_value=image_data):
+            if sucess:
+                self.assertEqual(expected,
+                                 self.image._verify_image(context, instance))
+            else:
+                self.assertRaises(expected,
+                                  self.image._verify_image, context, instance)
+
+    @mock.patch.object(image.IMAGE_API, 'download')
+    def test_fetch_image(self, mock_download):
+        context = mock.Mock()
+        instance = stubs._fake_instance()
+        self.assertEqual(None,
+                         self.image._fetch_image(context, instance))
+
+    @mock.patch.object(os, 'stat')
+    @mock.patch.object(json, 'dumps')
+    @mock.patch.object(tarfile, 'open')
+    @mock.patch.object(io, 'BytesIO')
+    @mock.patch.object(image.IMAGE_API, 'get')
+    def test_get_lxd_manifest(self, mock_stat, mock_json, mock_tarfile,
+                              mock_io, mock_image):
+        instance = stubs._fake_instance()
+        image_meta = mock.Mock()
+        self.assertEqual(None,
+                         self.image._get_lxd_manifest(instance, image_meta))
diff --git a/nova/tests/unit/virt/lxd/test_migrate.py b/nova/tests/unit/virt/lxd/test_migrate.py
new file mode 100644
index 0000000..6cf3255
--- /dev/null
+++ b/nova/tests/unit/virt/lxd/test_migrate.py
@@ -0,0 +1,91 @@
+# Copyright 2015 Canonical Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import mock
+
+from nova import test
+from nova.virt import fake
+
+from oslo_config import cfg
+
+from nova.virt.lxd import config
+from nova.virt.lxd import migrate
+from nova.virt.lxd import operations
+from nova.virt.lxd import session
+import stubs
+
+CONF = cfg.CONF
+CONF.import_opt('my_ip', 'nova.netconf')
+
+
+class LXDTestContainerMigrate(test.NoDBTestCase):
+
+    def setUp(self):
+        super(LXDTestContainerMigrate, self).setUp()
+
+        self.migrate = migrate.LXDContainerMigrate(
+            fake.FakeVirtAPI())
+
+    def test_migrate_disk_power_off_resize(self):
+        self.flags(my_ip='fakeip')
+        instance = stubs._fake_instance()
+        network_info = mock.Mock()
+        flavor = mock.Mock()
+        context = mock.Mock()
+        dest = 'fakeip'
+
+        with test.nested(
+            mock.patch.object(session.LXDAPISession, 'container_defined'),
+            mock.patch.object(config.LXDContainerConfig, 'create_profile'),
+            mock.patch.object(session.LXDAPISession, 'profile_update')
+        ) as (
+            mock_container_defined,
+            mock_create_profile,
+            mock_profile_update
+        ):
+            self.assertEqual('',
+                             self.migrate.migrate_disk_and_power_off(
+                                 context, instance, dest, flavor,
+                                 network_info))
+            mock_container_defined.assert_called_once_with(instance.name,
+                                                           instance)
+            mock_create_profile.assert_called_once_with(instance,
+                                                        network_info)
+
+    def test_confirm_migration(self):
+        migration = mock.Mock()
+        instance = stubs._fake_instance()
+        network_info = mock.Mock()
+
+        with test.nested(
+            mock.patch.object(session.LXDAPISession, 'container_defined'),
+            mock.patch.object(session.LXDAPISession, 'profile_delete'),
+            mock.patch.object(session.LXDAPISession, 'container_destroy'),
+            mock.patch.object(operations.LXDContainerOperations,
+                              'unplug_vifs'),
+        ) as (
+                mock_container_defined,
+                mock_profile_delete,
+                mock_container_destroy,
+                mock_unplug_vifs):
+            self.assertEqual(None,
+                             self.migrate.confirm_migration(migration,
+                                                            instance,
+                                                            network_info))
+            mock_container_defined.assert_called_once_with(instance.name,
+                                                           instance)
+            mock_profile_delete.assert_called_once_with(instance)
+            mock_unplug_vifs.assert_called_once_with(instance,
+                                                     network_info)
diff --git a/nova/tests/unit/virt/lxd/test_operations.py b/nova/tests/unit/virt/lxd/test_operations.py
new file mode 100644
index 0000000..163f203
--- /dev/null
+++ b/nova/tests/unit/virt/lxd/test_operations.py
@@ -0,0 +1,241 @@
+# Copyright 2015 Canonical Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import ddt
+import mock
+
+from nova import test
+from nova.virt import fake
+
+from nova.virt.lxd import config
+from nova.virt.lxd import image
+from nova.virt.lxd import operations as container_ops
+from nova.virt.lxd import session
+import stubs
+
+
+ at ddt.ddt
+ at mock.patch.object(container_ops, 'CONF', stubs.MockConf())
+class LXDTestContainerOps(test.NoDBTestCase):
+    """LXD Container operations unit tests."""
+
+    def setUp(self):
+        super(LXDTestContainerOps, 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.operations = (
+            container_ops.LXDContainerOperations(fake.FakeVirtAPI()))
+        self.mv = mock.MagicMock()
+        vif_patcher = mock.patch.object(self.operations,
+                                        'vif_driver',
+                                        self.mv)
+        vif_patcher.start()
+        self.addCleanup(vif_patcher.stop)
+
+    def test_spawn_container(self):
+        """Test spawn method. Ensure that the right calls
+           are made when creating a container.
+        """
+        context = mock.Mock()
+        instance = stubs._fake_instance()
+        image_meta = mock.Mock()
+        injected_files = mock.Mock()
+        admin_password = mock.Mock()
+        network_info = mock.Mock()
+        block_device_info = mock.Mock()
+
+        with test.nested(
+            mock.patch.object(session.LXDAPISession, 'container_defined'),
+            mock.patch.object(container_ops.LXDContainerOperations,
+                              '_fetch_image'),
+            mock.patch.object(container_ops.LXDContainerOperations,
+                              '_setup_network'),
+            mock.patch.object(container_ops.LXDContainerOperations,
+                              '_setup_profile'),
+            mock.patch.object(container_ops.LXDContainerOperations,
+                              '_add_configdrive'),
+            mock.patch.object(container_ops.LXDContainerOperations,
+                              '_setup_container')
+        ) as (
+            mock_container_defined,
+            mock_fetch_image,
+            mock_setup_network,
+            mock_setup_profile,
+            mock_add_configdrive,
+            mock_setup_container
+        ):
+            mock_container_defined.return_value = False
+            self.assertEqual(None,
+                             self.operations.spawn(context, instance,
+                                                   image_meta,
+                                                   injected_files,
+                                                   admin_password,
+                                                   network_info,
+                                                   block_device_info))
+
+    def test_reboot_container(self):
+        """Test the reboot method. Ensure that the proper
+           calls are made when rebooting a continer.
+        """
+        instance = stubs._fake_instance()
+        context = mock.Mock()
+        with test.nested(
+            mock.patch.object(session.LXDAPISession, 'container_reboot')
+        ) as (container_reboot):
+            self.assertEqual(None,
+                             self.operations.reboot(context, instance, {},
+                                                    None, None, None))
+            self.assertTrue(container_reboot)
+
+    def test_destroy_container(self):
+        """Test the destroy conainer method. Ensure that
+           the correct calls are made when removing
+           the contianer.
+        """
+        context = mock.Mock()
+        instance = stubs._fake_instance()
+        network_info = mock.Mock()
+
+        with test.nested(
+            mock.patch.object(session.LXDAPISession, 'profile_delete'),
+            mock.patch.object(session.LXDAPISession, 'container_destroy'),
+            mock.patch.object(container_ops.LXDContainerOperations, 'cleanup'),
+        ) as (
+            mock_profile_delete,
+            mock_container_destroy,
+            mock_cleanup
+        ):
+            self.assertEqual(None,
+                             self.operations.destroy(context,
+                                                     instance, network_info))
+            self.assertTrue(mock_profile_delete)
+            self.assertTrue(mock_container_destroy)
+
+    def test_power_off(self):
+        """Test the power_off method. Ensure that the proper
+           calls are made when the container is powered
+           off.
+        """
+        instance = stubs._fake_instance()
+        with test.nested(
+            mock.patch.object(session.LXDAPISession, 'container_stop')
+        ) as (mock_container_stop):
+            self.assertEqual(None,
+                             self.operations.power_off(instance))
+            self.assertTrue(mock_container_stop)
+
+    def test_power_on(self):
+        """test the power_on method. Ensure that the proper
+           calls are made when the container is powered on.
+        """
+        instance = stubs._fake_instance()
+        network_info = mock.Mock()
+        context = mock.Mock()
+        block_device_info = mock.Mock()
+        with test.nested(
+            mock.patch.object(session.LXDAPISession, 'container_start')
+        ) as (mock_container_start):
+            self.assertEqual(None,
+                             self.operations.power_on(context, instance,
+                                                      network_info,
+                                                      block_device_info))
+            self.assertTrue(mock_container_start)
+
+    def test_pause_container(self):
+        """Test the pause container method. Ensure that that
+           the proper calls are made when pausing the container.
+        """
+        instance = stubs._fake_instance()
+        with test.nested(
+            mock.patch.object(session.LXDAPISession, 'container_pause')
+        ) as (mock_container_pause):
+            self.assertEqual(None,
+                             self.operations.pause(instance))
+            self.assertTrue(mock_container_pause)
+
+    def test_unpause_container(self):
+        """Test the unapuse continaer. Ensure that the proper
+           calls are made when unpausing a container.
+        """
+        instance = stubs._fake_instance()
+        with test.nested(
+            mock.patch.object(session.LXDAPISession, 'container_unpause')
+        ) as (mock_container_unpause):
+            self.assertEqual(None,
+                             self.operations.unpause(instance))
+            self.assertTrue(mock_container_unpause)
+
+    def test_container_suspend(self):
+        instance = stubs._fake_instance()
+        context = mock.Mock()
+        with test.nested(
+            mock.patch.object(session.LXDAPISession, 'container_pause')
+        ) as (mock_container_suspend):
+            self.assertEqual(None,
+                             self.operations.suspend(context, instance))
+            self.assertTrue(mock_container_suspend)
+
+    def test_container_resume(self):
+        instance = stubs._fake_instance()
+        context = mock.Mock()
+        network_info = mock.Mock()
+        with test.nested(
+            mock.patch.object(session.LXDAPISession, 'container_unpause')
+        ) as (mock_container_resume):
+            self.assertEqual(None,
+                             self.operations.resume(context, instance,
+                                                    network_info))
+            self.assertTrue(mock_container_resume)
+
+    @mock.patch.object(image.LXDContainerImage, 'setup_image')
+    def test_fetch_image(self, mock_fetch_image):
+        instance = stubs._fake_instance()
+        context = mock.Mock()
+        self.operations._fetch_image(context, instance, {})
+        mock_fetch_image.assert_called_once_with(context, instance, {})
+
+    @mock.patch.object(container_ops.LXDContainerOperations, 'plug_vifs')
+    def test_setup_network(self, mock_plug_vifs):
+        instance = stubs._fake_instance()
+
+        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')
+    def test_setup_container(self, mock_create_container, mock_container_init,
+                             mock_container_start):
+        instance = stubs._fake_instance()
+        self.assertEqual(None,
+                         self.operations._setup_container(instance.name,
+                                                          instance))
diff --git a/nova/tests/unit/virt/lxd/test_vif_api.py b/nova/tests/unit/virt/lxd/test_vif_api.py
new file mode 100644
index 0000000..e76304c
--- /dev/null
+++ b/nova/tests/unit/virt/lxd/test_vif_api.py
@@ -0,0 +1,164 @@
+# Copyright 2015 Canonical Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import copy
+
+import ddt
+import mock
+from oslo_concurrency import processutils
+
+from nova import exception
+from nova.network import model as network_model
+from nova import test
+
+from nova.virt.lxd import vif
+import stubs
+
+
+ at ddt.ddt
+class LXDTestNetworkDriver(test.NoDBTestCase):
+
+    vif_data = {
+        'id': '0123456789abcdef',
+        'type': network_model.VIF_TYPE_OVS,
+        'address': '00:11:22:33:44:55',
+        'network': {
+            'bridge': 'fakebr'}}
+
+    def setUp(self):
+        super(LXDTestNetworkDriver, self).setUp()
+
+        self.vif_driver = vif.LXDGenericDriver()
+
+        mn = mock.Mock()
+        net_patcher = mock.patch.object(vif, 'linux_net', mn)
+        net_patcher.start()
+        self.addCleanup(net_patcher.stop)
+
+        me = mock.Mock()
+        net_patcher = mock.patch.object(vif.utils, 'execute', me)
+        net_patcher.start()
+        self.addCleanup(net_patcher.stop)
+
+        self.mgr = mock.Mock()
+        self.mgr.attach_mock(mn, 'net')
+        self.mgr.attach_mock(me, 'ex')
+
+    def test_nonetype(self):
+        instance = stubs.MockInstance()
+        vif_data = {'type': None}
+        self.assertRaises(
+            exception.NovaException,
+            self.vif_driver.plug,
+            instance, vif_data)
+
+    def test_get_config_ovs(self):
+        instance = stubs._fake_instance()
+        vif_data = copy.deepcopy(self.vif_data)
+
+        vif_type = self.vif_driver.get_config(instance, vif_data)
+        self.assertEqual(vif_type, {'bridge': 'qbr0123456789a',
+                                    'mac_address': '00:11:22:33:44:55'})
+
+    def test_get_config_bridge(self):
+        instance = stubs._fake_instance()
+        vif_data = copy.deepcopy(self.vif_data)
+
+        vif_type = self.vif_driver.get_config(instance, vif_data)
+        self.assertEqual(vif_type, {'bridge': 'qbr0123456789a',
+                                    'mac_address': '00:11:22:33:44:55'})
+
+    @stubs.annotated_data(
+        ('id', {}, [True, True]),
+        ('ovs-id', {'ovs_interfaceid': '123456789abcdef0'}, [True, True]),
+        ('no-bridge', {}, [False, True]),
+        ('no-v2', {}, [True, False]),
+        ('no-bridge-or-v2', {}, [False, False]),
+    )
+    def test_plug(self, tag, vif_data, exists):
+        instance = stubs.MockInstance()
+        vif_data = copy.deepcopy(self.vif_data)
+        vif_data.update(vif_data)
+        self.mgr.net.device_exists.side_effect = exists
+        self.assertEqual(
+            None,
+            self.vif_driver.plug(instance, vif_data))
+        calls = [
+            mock.call.net.device_exists('qbr0123456789a'),
+            mock.call.net.device_exists('qvo0123456789a')
+        ]
+        if not exists[0]:
+            calls[1:1] = [
+                mock.call.ex(
+                    'brctl', 'addbr', 'qbr0123456789a', run_as_root=True),
+                mock.call.ex(
+                    'brctl', 'setfd', 'qbr0123456789a', 0, run_as_root=True),
+                mock.call.ex('brctl', 'stp', 'qbr0123456789a', 'off',
+                             run_as_root=True),
+                mock.call.ex('tee',
+                             '/sys/class/net/qbr0123456789a/'
+                             'bridge/multicast_snooping',
+                             process_input='0', run_as_root=True,
+                             check_exit_code=[0, 1]),
+            ]
+        if not exists[1]:
+            calls.extend([
+                mock.call.net._create_veth_pair('qvb0123456789a',
+                                                'qvo0123456789a'),
+                mock.call.ex('ip', 'link', 'set', 'qbr0123456789a', 'up',
+                             run_as_root=True),
+                mock.call.ex('brctl', 'addif', 'qbr0123456789a',
+                             'qvb0123456789a', run_as_root=True)])
+            calls.append(mock.call.net.create_ovs_vif_port(
+                'fakebr', 'qvo0123456789a', '0123456789abcdef',
+                '00:11:22:33:44:55', 'fake-uuid'))
+        self.assertEqual(calls, self.mgr.method_calls)
+
+    def test_unplug_fail(self):
+        instance = stubs.MockInstance()
+        vif_data = copy.deepcopy(self.vif_data)
+        self.mgr.net.device_exists.side_effect = (
+            processutils.ProcessExecutionError)
+        self.assertEqual(
+            None,
+            self.vif_driver.unplug(instance, vif_data))
+
+    @stubs.annotated_data(
+        ('id', {}, [True, True]),
+        ('ovs-id', {'ovs_interfaceid': '123456789abcdef0'}, [True, True]),
+        ('no-bridge', {}, [False, True]),
+        ('no-v2', {}, [True, False]),
+        ('no-bridge-or-v2', {}, [False, False]),
+    )
+    def test_unplug(self, tag, vif_data, exists):
+        instance = stubs.MockInstance()
+        vif = copy.deepcopy(self.vif_data)
+        self.mgr.net.device_exists.side_effect = exists
+        self.assertEqual(
+            None,
+            self.vif_driver.unplug(instance, vif))
+
+        calls = [mock.call.net.device_exists('qbr0123456789a')]
+        if exists[0]:
+            calls[1:1] = [
+                mock.call.ex('brctl', 'delif', 'qbr0123456789a',
+                             'qvb0123456789a', run_as_root=True),
+                mock.call.ex('ip', 'link', 'set', 'qbr0123456789a',
+                             'down', run_as_root=True),
+                mock.call.ex('brctl', 'delbr', 'qbr0123456789a',
+                             run_as_root=True),
+                mock.call.net.delete_ovs_vif_port('fakebr', 'qvo0123456789a')
+            ]
+        self.assertEqual(calls, self.mgr.method_calls)
diff --git a/nova/virt/__init__.py b/nova/virt/__init__.py
new file mode 100644
index 0000000..de40ea7
--- /dev/null
+++ b/nova/virt/__init__.py
@@ -0,0 +1 @@
+__import__('pkg_resources').declare_namespace(__name__)
diff --git a/nova/virt/lxd/__init__.py b/nova/virt/lxd/__init__.py
new file mode 100644
index 0000000..4c10407
--- /dev/null
+++ b/nova/virt/lxd/__init__.py
@@ -0,0 +1,3 @@
+from nova.virt.lxd import driver
+
+LXDDriver = driver.LXDDriver
diff --git a/nova/virt/lxd/config.py b/nova/virt/lxd/config.py
new file mode 100644
index 0000000..8641d06
--- /dev/null
+++ b/nova/virt/lxd/config.py
@@ -0,0 +1,357 @@
+# Copyright 2011 Justin Santa Barbara
+# Copyright 2015 Canonical Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import socket
+
+from nova import exception
+from nova import i18n
+from nova.virt import configdrive
+
+from oslo_config import cfg
+from oslo_log import log as logging
+from oslo_utils import excutils
+
+from nova.virt.lxd import session
+from nova.virt.lxd import utils as container_dir
+from nova.virt.lxd import vif
+
+_ = i18n._
+_LE = i18n._LE
+_LI = i18n._LI
+
+CONF = cfg.CONF
+CONF.import_opt('my_ip', 'nova.netconf')
+LOG = logging.getLogger(__name__)
+
+
+class LXDContainerConfig(object):
+    """LXD configuration methods."""
+
+    def __init__(self):
+        self.container_dir = container_dir.LXDContainerDirectories()
+        self.session = session.LXDAPISession()
+        self.vif_driver = vif.LXDGenericDriver()
+
+    def create_container(self, instance):
+        """Create a LXD container dictionary so that we can
+           use it to initialize a container
+
+           :param instance: nova instance object
+        """
+        LOG.debug('create_container called for instance', instance=instance)
+
+        instance_name = instance.name
+        try:
+
+            # Fetch the container configuration from the current nova
+            # instance object
+            container_config = {
+                'name': instance_name,
+                'profiles': [str(instance.name)],
+                'source': self.get_container_source(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():
+                LOG.error('Failed to get container configuration'
+                          ' %(instance)s: %(ex)s',
+                          {'instance': instance_name, 'ex': ex},
+                          instance=instance)
+
+    def create_profile(self, instance, network_info):
+        """Create a LXD container profile configuration
+
+        :param instance: nova instance object
+        :param network_info: nova network configuration object
+        :return: LXD container profile dictionary
+        """
+        LOG.debug('create_container_profile called for instance',
+                  instance=instance)
+        instance_name = instance.name
+        try:
+            config = {}
+            config['name'] = str(instance_name)
+            config['config'] = self.create_config(instance_name, instance)
+
+            # Restrict the size of the "/" disk
+            config['devices'] = self.configure_container_root(instance)
+
+            if network_info:
+                config['devices'].update(self.create_network(instance_name,
+                                                             instance,
+                                                             network_info))
+
+            return config
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(
+                    _LE('Failed to create profile %(instance)s: %(ex)s'),
+                    {'instance': instance_name, 'ex': ex}, instance=instance)
+
+    def create_config(self, instance_name, instance):
+        """Create the LXD container resources
+
+        :param instance_name: instance name
+        :param instance: nova instance object
+        :return: LXD resources dictionary
+        """
+        LOG.debug('create_config called for instance', instance=instance)
+        try:
+            config = {}
+
+            # Update continaer options
+            config.update(self.config_instance_options(config, instance))
+
+            # Set the instance memory limit
+            mem = instance.memory_mb
+            if mem >= 0:
+                config['limits.memory'] = '%sMB' % mem
+
+            # Set the instance vcpu limit
+            vcpus = instance.flavor.vcpus
+            if vcpus >= 0:
+                config['limits.cpu'] = str(vcpus)
+
+            # Configure the console for the instance
+            config['raw.lxc'] = 'lxc.console.logfile=%s\n' \
+                % self.container_dir.get_console_path(instance_name)
+
+            return config
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(
+                    _LE('Failed to set container resources %(instance)s: '
+                        '%(ex)s'), {'instance': instance_name, 'ex': ex},
+                    instance=instance)
+
+    def config_instance_options(self, config, instance):
+        LOG.debug('config_instance_options called for instance',
+                  instance=instance)
+
+        # Set the container to autostart when the host reboots
+        config['boot.autostart'] = 'True'
+
+        # Determine if we require a nested container
+        flavor = instance.flavor
+        lxd_nested_allowed = flavor.extra_specs.get(
+            'lxd:nested_allowed', False)
+        if lxd_nested_allowed:
+            config['security.nesting'] = 'True'
+
+        # Determine if we require a privileged container
+        lxd_privileged_allowed = flavor.extra_specs.get(
+            'lxd:privileged_allowed', False)
+        if lxd_privileged_allowed:
+            config['security.privileged'] = 'True'
+
+        return config
+
+    def configure_container_root(self, instance):
+        LOG.debug('configure_container_root called for instance',
+                  instance=instance)
+        try:
+            config = {}
+            lxd_config = self.session.get_host_config(instance)
+            if str(lxd_config['storage']) in ['btrfs', 'zfs']:
+                config['root'] = {'path': '/',
+                                  'type': 'disk',
+                                  'size': '%sGB' % str(instance.root_gb)}
+            else:
+                config['root'] = {'path': '/',
+                                  'type': 'disk'}
+            return config
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Failed to configure disk for '
+                              '%(instance)s: %(ex)s'),
+                          {'instance': instance.name, 'ex': ex},
+                          instance=instance)
+
+    def create_network(self, instance_name, instance, network_info):
+        """Create the LXD container network on the host
+
+        :param instance_name: nova instance name
+        :param instance: nova instance object
+        :param network_info: instance network configuration object
+        :return:network configuration dictionary
+        """
+        LOG.debug('create_network called for instance', instance=instance)
+        try:
+            network_devices = {}
+
+            if not network_info:
+                return
+
+            for vifaddr in network_info:
+                cfg = self.vif_driver.get_config(instance, vifaddr)
+                network_devices[str(cfg['bridge'])] = \
+                    {'nictype': 'bridged',
+                     'hwaddr': str(cfg['mac_address']),
+                     'parent': str(cfg['bridge']),
+                     'type': 'nic'}
+                return network_devices
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(
+                    _LE('Fail to configure network for %(instance)s: %(ex)s'),
+                    {'instance': instance_name, 'ex': ex}, instance=instance)
+
+    def get_container_source(self, instance):
+        """Set the LXD container image for the instance.
+
+        :param instance: nova instance object
+        :return: the container source
+        """
+        LOG.debug('get_container_source called for instance',
+                  instance=instance)
+        try:
+            container_source = {'type': 'image',
+                                'alias': str(instance.image_ref)}
+            if container_source is None:
+                msg = _('Failed to determine container source for %s') \
+                    % instance.name
+                raise exception.NovaException(msg)
+            return container_source
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(
+                    _LE('Failed to configure container source '
+                        '%(instance)s: %(ex)s'),
+                    {'instance': instance.name, 'ex': ex},
+                    instance=instance)
+
+    def get_container_migrate(self, container_migrate, migration,
+                              host, instance):
+        LOG.debug('get_container_migrate called for instance',
+                  instance=instance)
+        try:
+            # Generate the container config
+            host = socket.gethostbyname(host)
+            container_metadata = container_migrate['metadata']
+            container_control = container_metadata['metadata']['control']
+            container_fs = container_metadata['metadata']['fs']
+
+            container_url = 'https://%s:8443%s' \
+                % (host, container_migrate.get('operation'))
+
+            container_migrate = {
+                'base_image': '',
+                'mode': 'pull',
+                'certificate': str(self.session.host_certificate(instance,
+                                                                 host)),
+                'operation': str(container_url),
+                'secrets': {
+                        'control': str(container_control),
+                        'fs': str(container_fs)
+                },
+                'type': 'migration'
+            }
+
+            return container_migrate
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Failed to configure migation source '
+                              '%(instance)s: %(ex)s'),
+                          {'instance': instance.name, 'ex': ex},
+                          instance=instance)
+
+    def configure_disk_path(self, src_path, dest_path, vfs_type, instance):
+        """Configure the host mount point for the LXD container
+
+        :param src_path: source path on the house
+        :param dest_path: destination path on the LXD container
+        :param vfs_type: dictionary identifier
+        :param instance: nova instance object
+        :return: container disk paths
+        """
+        LOG.debug('configure_disk_path called for instance',
+                  instance=instance)
+        try:
+            config = {}
+            config[vfs_type] = {'path': dest_path,
+                                'source': src_path,
+                                'type': 'disk',
+                                'optional': 'True'}
+            return config
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Failed to configure disk for '
+                              '%(instance)s: %(ex)s'),
+                          {'instance': instance.name, 'ex': ex},
+                          instance=instance)
+
+    def create_container_net_device(self, instance, vif):
+        """Translate nova network object into a LXD interface
+
+        :param instance: nova instance object
+        :param vif: network instaance object
+        """
+        LOG.debug('create_container_net_device called for instance',
+                  insance=instance)
+        try:
+            network_config = self.vif_driver.get_config(instance, vif)
+
+            config = {}
+            config[self.get_network_device(instance)] = {
+                'nictype': 'bridged',
+                'hwaddr': str(vif['address']),
+                'parent': str(network_config['bridge']),
+                'type': 'nic'}
+
+            return config
+        except Exception as ex:
+            LOG.error(_LE('Failed to configure network for '
+                          '%(instance)s: %(ex)s'),
+                      {'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:
+            return 'eth1'
+        else:
+            return 'eth%s' % int(len(interfaces) - 1)
diff --git a/nova/virt/lxd/constants.py b/nova/virt/lxd/constants.py
new file mode 100644
index 0000000..29dd485
--- /dev/null
+++ b/nova/virt/lxd/constants.py
@@ -0,0 +1,34 @@
+# Copyright 2015 Canonical Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#    See the License for the specific language governing permissions and
+#    limitations under the License.
+
+from nova.compute import power_state
+
+LXD_POWER_STATES = {
+    100: power_state.RUNNING,
+    101: power_state.RUNNING,
+    102: power_state.SHUTDOWN,
+    103: power_state.RUNNING,
+    104: power_state.SHUTDOWN,
+    105: power_state.NOSTATE,
+    106: power_state.NOSTATE,
+    107: power_state.SHUTDOWN,
+    108: power_state.CRASHED,
+    109: power_state.SUSPENDED,
+    110: power_state.SUSPENDED,
+    111: power_state.SUSPENDED,
+    200: power_state.RUNNING,
+    400: power_state.CRASHED,
+    401: power_state.NOSTATE
+}
diff --git a/nova/virt/lxd/container_firewall.py b/nova/virt/lxd/container_firewall.py
new file mode 100644
index 0000000..9f3a11f
--- /dev/null
+++ b/nova/virt/lxd/container_firewall.py
@@ -0,0 +1,68 @@
+# Copyright 2015 Canonical Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from nova.virt import firewall
+
+from oslo_config import cfg
+from oslo_log import log as logging
+
+CONF = cfg.CONF
+LOG = logging.getLogger(__name__)
+
+
+class LXDContainerFirewall(object):
+
+    def __init__(self):
+        self.firewall_driver = firewall.load_driver(
+            default='nova.virt.firewall.NoopFirewallDriver')
+
+    def refresh_security_group_rules(self, security_group_id):
+        return (self.firewall_driver
+                .refresh_security_group_rules(security_group_id))
+
+    def refresh_security_group_members(self, security_group_id):
+        return (self.firewall_driver
+                .refresh_security_group_members(security_group_id))
+
+    def refresh_provider_fw_rules(self):
+        return self.firewall_driver.refresh_provider_fw_rules()
+
+    def refresh_instance_security_rules(self, instance):
+        return self.firewall_driver.refresh_instance_security_rules(instance)
+
+    def ensure_filtering_rules_for_instance(self, instance, network_info):
+        return (self.firewall_driver
+                .ensure_filtering_rules_for_instance(instance, network_info))
+
+    def filter_defer_apply_on(self):
+        return self.firewall_driver.filter_defer_apply_on()
+
+    def filter_defer_apply_off(self):
+        return self.firewall_driver.filter_defer_apply_off()
+
+    def unfilter_instance(self, instance, network_info):
+        return self.firewall_driver.unfilter_instance(instance, network_info)
+
+    def setup_basic_filtering(self, instance, network_info):
+        return self.firewall_driver.setup_basic_filtering(instance,
+                                                          network_info)
+
+    def prepare_instance_filter(self, instance, network_info):
+        return self.firewall_driver.prepare_instance_filter(instance,
+                                                            network_info)
+
+    def apply_instance_filter(self, instance, network_info):
+        return self.firewall_driver.apply_instance_filter(instance,
+                                                          network_info)
diff --git a/nova/virt/lxd/container_snapshot.py b/nova/virt/lxd/container_snapshot.py
new file mode 100644
index 0000000..376261f
--- /dev/null
+++ b/nova/virt/lxd/container_snapshot.py
@@ -0,0 +1,157 @@
+# Copyright 2011 Justin Santa Barbara
+# Copyright 2015 Canonical Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.from oslo_config import cfg
+
+from nova.compute import task_states
+from nova import exception
+from nova import i18n
+from nova import image
+import os
+
+from oslo_concurrency import lockutils
+from oslo_config import cfg
+from oslo_log import log as logging
+from oslo_utils import excutils
+
+from nova.virt.lxd import session
+
+_ = i18n._
+_LE = i18n._LE
+
+CONF = cfg.CONF
+LOG = logging.getLogger(__name__)
+
+IMAGE_API = image.API()
+
+
+class LXDSnapshot(object):
+
+    def __init__(self):
+        self.session = session.LXDAPISession()
+        self.lock_path = str(os.path.join(CONF.instances_path, 'locks'))
+
+    def snapshot(self, context, instance, image_id, update_task_state):
+        """Create a LXD snapshot  of the instance
+
+           Steps involved in creating an LXD Snapshot:
+
+           1. Ensure the container exists
+           2. Stop the LXD container: LXD requires a container
+              to be stopped in or
+           3. Publish the container: Run the API equivalent to
+              'lxd publish container --alias <image_name>' to create
+              a snapshot and upload it to the local LXD image store.
+           4. Create an alias for the image: Create an alias so that
+              nova-lxd can re-use the image that was created.
+           5. Upload the image to glance so that it can bed on other
+              compute hosts.
+
+          :param context: nova security context
+          :param instance: nova instance object
+          :param image_id: glance image id
+        """
+        LOG.debug('snapshot called for instance', instance=instance)
+
+        try:
+            if not self.session.container_defined(instance.name, instance):
+                raise exception.InstanceNotFound(instance_id=instance.name)
+
+            with lockutils.lock(self.lock_path,
+                                lock_file_prefix=('lxd-snapshot-%s' %
+                                                  instance.name),
+                                external=True):
+
+                update_task_state(task_state=task_states.IMAGE_PENDING_UPLOAD)
+
+                # We have to stop the container before we can publish the
+                # image to the local store
+                self.session.container_stop(instance.name,
+                                            instance)
+                fingerprint = self._save_lxd_image(instance,
+                                                   image_id)
+                self.session.container_start(instance.name, instance)
+
+                update_task_state(task_state=task_states.IMAGE_UPLOADING,
+                                  expected_state=task_states.IMAGE_PENDING_UPLOAD)  # noqa
+                self._save_glance_image(context, instance, image_id,
+                                        fingerprint)
+        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 _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)
+            image_meta = {'name': snapshot['name'],
+                          'disk_format': 'raw'}
+            IMAGE_API.update(context, image_id, image_meta, data)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Failed to upload image to glance for '
+                              '%(instance)s:  %(ex)s'),
+                          {'instance': instance.name, 'ex': ex},
+                          instance=instance)
diff --git a/nova/virt/lxd/driver.py b/nova/virt/lxd/driver.py
new file mode 100644
index 0000000..5778f06
--- /dev/null
+++ b/nova/virt/lxd/driver.py
@@ -0,0 +1,388 @@
+# Copyright 2011 Justin Santa Barbara
+# Copyright 2015 Canonical Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from __future__ import absolute_import
+
+from nova import exception
+from nova import i18n
+from nova.virt import driver
+import socket
+
+from oslo_config import cfg
+from oslo_log import log as logging
+
+
+from nova.virt.lxd import container_firewall
+from nova.virt.lxd import container_snapshot
+from nova.virt.lxd import host
+from nova.virt.lxd import migrate
+from nova.virt.lxd import operations as container_ops
+from nova.virt.lxd import vif as lxd_vif
+
+_ = i18n._
+
+lxd_opts = [
+    cfg.StrOpt('root_dir',
+               default='/var/lib/lxd/',
+               help='Default LXD directory'),
+    cfg.IntOpt('timeout',
+               default=-1,
+               help='Default LXD timeout'),
+    cfg.IntOpt('retry_interval',
+               default=2,
+               help='How often to retry in seconds when a'
+                    'request does conflict'),
+]
+
+CONF = cfg.CONF
+CONF.register_opts(lxd_opts, 'lxd')
+LOG = logging.getLogger(__name__)
+
+
+class LXDDriver(driver.ComputeDriver):
+
+    """LXD Lightervisor."""
+
+    capabilities = {
+        "has_imagecache": False,
+        "supports_recreate": False,
+        "supports_migrate_to_same_host": False,
+    }
+
+    def __init__(self, virtapi):
+        self.virtapi = virtapi
+
+        self.vif_driver = lxd_vif.LXDGenericDriver()
+
+        self.container_ops = container_ops.LXDContainerOperations(virtapi)
+        self.container_snapshot = container_snapshot.LXDSnapshot()
+        self.container_firewall = container_firewall.LXDContainerFirewall()
+        self.container_migrate = migrate.LXDContainerMigrate(virtapi)
+        self.host = host.LXDHost()
+
+    def init_host(self, host):
+        return self.host.init_host(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:
+            self.vif_driver.plug(instance, vif)
+
+    def unplug_vifs(self, instance, network_info):
+        """Unplug VIFs from networks."""
+        for vif in network_info:
+            try:
+                self.vif_driver.unplug(instance, vif)
+            except exception.NovaException:
+                pass
+
+    def estimate_instance_overhead(self, instance_info):
+        return {'memory_mb': 0}
+
+    def list_instances(self):
+        return self.container_ops.list_instances()
+
+    def list_instance_uuids(self):
+        raise NotImplementedError()
+
+    def spawn(self, context, instance, image_meta, injected_files,
+              admin_password, network_info=None, block_device_info=None):
+        self.container_ops.spawn(context, instance, image_meta,
+                                 injected_files, admin_password,
+                                 network_info, block_device_info)
+
+    def destroy(self, context, instance, network_info, block_device_info=None,
+                destroy_disks=True, migrate_data=None):
+        self.container_ops.destroy(context, instance, network_info,
+                                   block_device_info, destroy_disks,
+                                   migrate_data)
+
+    def cleanup(self, context, instance, network_info, block_device_info=None,
+                destroy_disks=True, migrate_data=None, destroy_vifs=True):
+        self.container_ops.cleanup(context, instance, network_info,
+                                   block_device_info, destroy_disks,
+                                   migrate_data, destroy_vifs)
+
+    def reboot(self, context, instance, network_info, reboot_type,
+               block_device_info=None, bad_volumes_callback=None):
+        self.container_ops.reboot(context, instance, network_info,
+                                  reboot_type, block_device_info,
+                                  bad_volumes_callback)
+
+    def get_console_output(self, context, instance):
+        return self.container_ops.get_console_output(context, instance)
+
+    def get_diagnostics(self, instance):
+        raise NotImplementedError()
+
+    def get_instance_diagnostics(self, instance):
+        raise NotImplementedError()
+
+    def get_all_bw_counters(self, instances):
+        raise NotImplementedError()
+
+    def get_all_volume_usage(self, context, compute_host_bdms):
+        raise NotImplementedError()
+
+    def get_host_ip_addr(self):
+        return self.host.get_host_ip_addr()
+
+    def attach_volume(self, context, connection_info, instance, mountpoint,
+                      disk_bus=None, device_type=None, encryption=None):
+        raise NotImplementedError()
+
+    def detach_volume(self, connection_info, instance, mountpoint,
+                      encryption=None):
+        raise NotImplementedError()
+
+    def attach_interface(self, instance, image_meta, vif):
+        return self.container_ops.container_attach_interface(instance,
+                                                             image_meta,
+                                                             vif)
+
+    def detach_interface(self, instance, vif):
+        return self.container_ops.container_detach_interface(instance, vif)
+
+    def migrate_disk_and_power_off(self, context, instance, dest,
+                                   flavor, network_info,
+                                   block_device_info=None,
+                                   timeout=0, retry_interval=0):
+        return self.container_migrate.migrate_disk_and_power_off(
+            context, instance, dest, flavor,
+            network_info, block_device_info, timeout,
+            retry_interval)
+
+    def snapshot(self, context, instance, image_id, update_task_state):
+        return self.container_snapshot.snapshot(context, instance, image_id,
+                                                update_task_state)
+
+    def post_interrupted_snapshot_cleanup(self, context, instance):
+        pass
+
+    def finish_migration(self, context, migration, instance, disk_info,
+                         network_info, image_meta, resize_instance,
+                         block_device_info=None, power_on=True):
+        return self.container_migrate.finish_migration(
+            context, migration, instance, disk_info,
+            network_info, image_meta, resize_instance,
+            block_device_info, power_on)
+
+    def confirm_migration(self, migration, instance, network_info):
+        return self.container_migrate.confirm_migration(migration,
+                                                        instance,
+                                                        network_info)
+
+    def finish_revert_migration(self, context, instance, network_info,
+                                block_device_info=None, power_on=True):
+        return self.container_migrate.finish_revert_migration(
+            context, instance, network_info, block_device_info,
+            power_on)
+
+    def pause(self, instance):
+        self.container_ops.pause(instance)
+
+    def unpause(self, instance):
+        self.container_ops.unpause(instance)
+
+    def suspend(self, context, instance):
+        self.container_ops.suspend(context, instance)
+
+    def resume(self, context, instance, network_info, block_device_info=None):
+        self.container_ops.resume(context, instance, network_info,
+                                  block_device_info)
+
+    def rescue(self, context, instance, network_info, image_meta,
+               rescue_password):
+        self.container_ops.rescue(context, instance, network_info,
+                                  image_meta, rescue_password)
+
+    def unrescue(self, instance, network_info):
+        return self.container_ops.unrescue(instance, network_info)
+
+    def power_off(self, instance, timeout=0, retry_interval=0):
+        self.container_ops.power_off(instance, timeout=0,
+                                     retry_interval=0)
+
+    def power_on(self, context, instance, network_info,
+                 block_device_info=None):
+        self.container_ops.power_on(context, instance, network_info,
+                                    block_device_info)
+
+    def soft_delete(self, instance):
+        raise NotImplementedError()
+
+    def get_available_resource(self, nodename):
+        return self.host.get_available_resource(nodename)
+
+    def pre_live_migration(self, context, instance, block_device_info,
+                           network_info, disk_info, migrate_data=None):
+        raise NotImplementedError()
+
+    def live_migration(self, context, instance, dest,
+                       post_method, recover_method, block_migration=False,
+                       migrate_data=None):
+        raise NotImplementedError()
+
+    def post_live_migration(self, context, instance, block_device_info,
+                            migrate_data=None):
+        raise NotImplementedError()
+
+    def post_live_migration_at_destination(self, context, instance,
+                                           network_info,
+                                           block_migration=False,
+                                           block_device_info=None):
+        raise NotImplementedError()
+
+    def check_instance_shared_storage_local(self, context, instance):
+        raise NotImplementedError()
+
+    def check_instance_shared_storage_remote(self, context, data):
+        raise NotImplementedError()
+
+    def check_instance_shared_storage_cleanup(self, context, data):
+        pass
+
+    def check_can_live_migrate_destination(self, context, instance,
+                                           src_compute_info, dst_compute_info,
+                                           block_migration=False,
+                                           disk_over_commit=False):
+        raise NotImplementedError()
+
+    def check_can_live_migrate_destination_cleanup(self, context,
+                                                   dest_check_data):
+        raise NotImplementedError()
+
+    def check_can_live_migrate_source(self, context, instance,
+                                      dest_check_data, block_device_info=None):
+        raise NotImplementedError()
+
+    def get_instance_disk_info(self, instance,
+                               block_device_info=None):
+        raise NotImplementedError()
+
+    def refresh_security_group_rules(self, security_group_id):
+        return (self.container_firewall
+                .refresh_security_group_rules(security_group_id))
+
+    def refresh_security_group_members(self, security_group_id):
+        return (self.container_firewall
+                .refresh_security_group_members(security_group_id))
+
+    def refresh_provider_fw_rules(self):
+        return self.container_firewall.refresh_provider_fw_rules()
+
+    def refresh_instance_security_rules(self, instance):
+        return (self.container_firewall
+                .refresh_instance_security_rules(instance))
+
+    def ensure_filtering_rules_for_instance(self, instance, network_info):
+        return (self.container_firewall
+                .ensure_filtering_rules_for_instance(instance, network_info))
+
+    def filter_defer_apply_on(self):
+        return self.container_firewall.filter_defer_apply_on()
+
+    def filter_defer_apply_off(self):
+        return self.container_firewall.filter_defer_apply_off()
+
+    def unfilter_instance(self, instance, network_info):
+        return self.container_firewall.unfilter_instance(instance,
+                                                         network_info)
+
+    def poll_rebooting_instances(self, timeout, instances):
+        raise NotImplementedError()
+
+    def host_power_action(self, action):
+        raise NotImplementedError()
+
+    def host_maintenance_mode(self, host, mode):
+        raise NotImplementedError()
+
+    def set_host_enabled(self, enabled):
+        raise NotImplementedError()
+
+    def get_host_uptime(self):
+        return self.host.get_host_uptime()
+
+    def get_host_cpu_stats(self):
+        return self.host.get_host_cpu_stats()
+
+    def block_stats(self, instance, disk_id):
+        raise NotImplementedError()
+
+    def deallocate_networks_on_reschedule(self, instance):
+        """Does the driver want networks deallocated on reschedule?"""
+        return False
+
+    def macs_for_instance(self, instance):
+        return None
+
+    def manage_image_cache(self, context, all_instances):
+        pass
+
+    def add_to_aggregate(self, context, aggregate, host, **kwargs):
+        raise NotImplementedError()
+
+    def remove_from_aggregate(self, context, aggregate, host, **kwargs):
+        raise NotImplementedError()
+
+    def undo_aggregate_operation(self, context, op, aggregate,
+                                 host, set_error=True):
+        raise NotImplementedError()
+
+    def get_volume_connector(self, instance):
+        return {'ip': CONF.my_block_storage_ip,
+                'initiator': 'fake',
+                'host': 'fakehost'}
+
+    def get_available_nodes(self, refresh=False):
+        hostname = socket.gethostname()
+        return [hostname]
+
+    def node_is_available(self, nodename):
+        if nodename in self.get_available_nodes():
+            return True
+        # Refresh and check again.
+        return nodename in self.get_available_nodes(refresh=True)
+
+    def get_per_instance_usage(self):
+        return {}
+
+    def instance_on_disk(self, instance):
+        return False
+
+    def volume_snapshot_create(self, context, instance, volume_id,
+                               create_info):
+        raise NotImplementedError()
+
+    def volume_snapshot_delete(self, context, instance, volume_id,
+                               snapshot_id, delete_info):
+        raise NotImplementedError()
+
+    def quiesce(self, context, instance, image_meta):
+        raise NotImplementedError()
+
+    def unquiesce(self, context, instance, image_meta):
+        raise NotImplementedError()
diff --git a/nova/virt/lxd/host.py b/nova/virt/lxd/host.py
new file mode 100644
index 0000000..587539c
--- /dev/null
+++ b/nova/virt/lxd/host.py
@@ -0,0 +1,202 @@
+# Copyright 2010 United States Government as represented by the
+# Administrator of the National Aeronautics and Space Administration.
+# Copyright (c) 2010 Citrix Systems, Inc.
+# Copyright 2011 Justin Santa Barbara
+# Copyright 2015 Canonical Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+
+from nova.compute import arch
+from nova.compute import hv_type
+from nova.compute import utils as compute_utils
+from nova.compute import vm_mode
+from nova import exception
+from nova import i18n
+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
+from oslo_log import log as logging
+from oslo_serialization import jsonutils
+from oslo_utils import units
+import psutil
+
+_ = i18n._
+_LW = i18n._LW
+CONF = cfg.CONF
+LOG = logging.getLogger(__name__)
+
+
+class LXDHost(object):
+
+    def __init__(self):
+        self.lxd = api.API()
+
+    def get_available_resource(self, nodename):
+        LOG.debug('In get_available_resource')
+
+        local_cpu_info = self._get_cpuinfo()
+        cpu_topology = local_cpu_info['topology']
+        vcpus = (int(cpu_topology['cores']) *
+                 int(cpu_topology['sockets']) *
+                 int(cpu_topology['threads']))
+
+        local_memory_info = self._get_memory_mb_usage()
+        local_disk_info = self._get_fs_info(CONF.lxd.root_dir)
+
+        data = {
+            'vcpus': vcpus,
+            'memory_mb': local_memory_info['total'] / units.Mi,
+            'memory_mb_used': local_memory_info['used'] / units.Mi,
+            'local_gb': local_disk_info['total'] / units.Gi,
+            'local_gb_used': local_disk_info['used'] / units.Gi,
+            'vcpus_used': 0,
+            'hypervisor_type': 'lxd',
+            'hypervisor_version': '011',
+            'cpu_info': jsonutils.dumps(local_cpu_info),
+            'hypervisor_hostname': socket.gethostname(),
+            'supported_instances':
+                [(arch.I686, hv_type.LXD, vm_mode.EXE),
+                    (arch.X86_64, hv_type.LXD, vm_mode.EXE),
+                    (arch.I686, hv_type.LXC, vm_mode.EXE),
+                    (arch.X86_64, hv_type.LXC, vm_mode.EXE)],
+            'numa_topology': None,
+        }
+
+        return data
+
+    def get_host_ip_addr(self):
+        ips = compute_utils.get_machine_ips()
+        if CONF.my_ip not in ips:
+            LOG.warn(_LW('my_ip address (%(my_ip)s) was not found on '
+                         'any of the interfaces: %(ifaces)s'),
+                     {'my_ip': CONF.my_ip, 'ifaces': ", ".join(ips)})
+        return CONF.my_ip
+
+    def get_host_uptime(self):
+        out, err = utils.execute('env', 'LANG=C', 'uptime')
+        return out
+
+    def _get_fs_info(self, path):
+        """Get free/used/total space info for a filesystem
+
+        :param path: Any dirent on the filesystem
+        :returns: A dict containing
+              :free: How much space is free (in bytes)
+              :used: How much space is used (in bytes)
+              :total: How big the filesytem is (in bytes)
+        """
+        hddinfo = os.statvfs(path)
+        total = hddinfo.f_blocks * hddinfo.f_bsize
+        available = hddinfo.f_bavail * hddinfo.f_bsize
+        used = total - available
+        return {'total': total,
+                'available': available,
+                'used': used}
+
+    def _get_memory_mb_usage(self):
+        """Get the used memory size(MB) of the host.
+
+        :returns: the total usage of memory(MB)
+        """
+
+        with open('/proc/meminfo') as fp:
+            m = fp.read().split()
+            idx1 = m.index('MemTotal:')
+            idx2 = m.index('MemFree:')
+            idx3 = m.index('Buffers:')
+            idx4 = m.index('Cached:')
+
+            total = int(m[idx1 + 1])
+            avail = int(m[idx2 + 1]) + int(m[idx3 + 1]) + int(m[idx4 + 1])
+
+        return {
+            'total': total * 1024,
+            'used': (total - avail) * 1024
+        }
+
+    def _get_cpuinfo(self):
+        cpuinfo = self._get_cpu_info()
+
+        cpu_info = dict()
+
+        cpu_info['arch'] = platform.uname()[5]
+        cpu_info['model'] = cpuinfo.get('model name', 'unknown')
+        cpu_info['vendor'] = cpuinfo.get('vendor id', 'unknown')
+
+        topology = dict()
+        topology['sockets'] = cpuinfo.get('socket(s)', 1)
+        topology['cores'] = cpuinfo.get('core(s) per socket', 1)
+        topology['threads'] = cpuinfo.get('thread(s) per core', 1)
+        cpu_info['topology'] = topology
+        cpu_info['features'] = cpuinfo.get('flags', 'unknown')
+
+        return cpu_info
+
+    def _get_cpu_info(self):
+        '''Parse the output of lscpu.'''
+        cpuinfo = {}
+        out, err = utils.execute('lscpu')
+        if err:
+            msg = _('Unable to parse lscpu output.')
+            raise exception.NovaException(msg)
+
+        cpu = [line.strip('\n') for line in out.splitlines()]
+        for line in cpu:
+            if line.strip():
+                name, value = line.split(':', 1)
+                name = name.strip().lower()
+                cpuinfo[name] = value.strip()
+
+        f = open('/proc/cpuinfo', 'r')
+        features = [line.strip('\n') for line in f.readlines()]
+        for line in features:
+            if line.strip():
+                if line.startswith('flags'):
+                    name, value = line.split(':', 1)
+                    name = name.strip().lower()
+                    cpuinfo[name] = value.strip()
+
+        return cpuinfo
+
+    def _get_hypersivor_version(self):
+        version = self.lxd.get_lxd_version()
+        return '.'.join(str(v) for v in version)
+
+    def get_host_cpu_stats(self):
+        cpuinfo = self._get_cpu_info()
+        return {
+            'kernel': int(psutil.cpu_times()[2]),
+            'idle': int(psutil.cpu_times()[3]),
+            'user': int(psutil.cpu_times()[0]),
+            'iowait': int(psutil.cpu_times()[4]),
+            'frequency': cpuinfo.get('cpu mhz', 0)
+        }
+
+    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)
+
+            return True
+        except lxd_exceptions.APIError as ex:
+            msg = _('Unable to connect to LXD daemon: %s') % ex
+            raise exception.HostNotFound(msg)
diff --git a/nova/virt/lxd/image.py b/nova/virt/lxd/image.py
new file mode 100644
index 0000000..90de759
--- /dev/null
+++ b/nova/virt/lxd/image.py
@@ -0,0 +1,293 @@
+# Copyright 2015 Canonical Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import hashlib
+import io
+import json
+from nova.compute import arch
+from nova import exception
+from nova import i18n
+from nova import image
+from nova import utils
+import os
+import shutil
+import tarfile
+import tempfile
+import uuid
+
+from oslo_concurrency import lockutils
+from oslo_concurrency import processutils
+from oslo_config import cfg
+from oslo_log import log as logging
+from oslo_utils import excutils
+from oslo_utils import fileutils
+
+from nova.virt.lxd import session
+from nova.virt.lxd import utils as container_dir
+
+_ = i18n._
+_LE = i18n._LE
+
+CONF = cfg.CONF
+LOG = logging.getLogger(__name__)
+IMAGE_API = image.API()
+
+
+class LXDContainerImage(object):
+    """Upload an image from glance to the local LXD image store."""
+
+    def __init__(self):
+        self.client = session.LXDAPISession()
+        self.container_dir = container_dir.LXDContainerDirectories()
+        self.lock_path = str(os.path.join(CONF.instances_path, 'locks'))
+
+        self.container_image = None
+        self.container_manifest = None
+
+    def setup_image(self, context, instance, image_meta):
+        """Download an image from glance and upload it to LXD
+
+        :param context: context object
+        :param instance: The nova instance
+        :param image_meta: Image dict returned by nova.image.glance
+        """
+        LOG.debug('setup_image called for instance', instance=instance)
+
+        self.container_image = \
+            self.container_dir.get_container_rootfs_image(image_meta)
+        self.container_manifest = \
+            self.container_dir.get_container_manifest_image(image_meta)
+
+        with lockutils.lock(self.lock_path,
+                            lock_file_prefix=('lxd-image-%s' %
+                                              instance.image_ref),
+                            external=True):
+
+            if self.client.image_defined(instance):
+                return
+
+            base_dir = self.container_dir.get_base_dir()
+            if not os.path.exists(base_dir):
+                fileutils.ensure_tree(base_dir)
+
+            try:
+                # Inspect image for the correct format
+                self._verify_image(context, instance)
+
+                # Fetch the image from glance
+                self._fetch_image(context, instance)
+
+                # Generate the LXD manifest for the image
+                self._get_lxd_manifest(instance, image_meta)
+
+                # Upload the image to the local LXD image store
+                self._image_upload(instance)
+
+                # Setup the LXD alias for the image
+                self._setup_alias(instance)
+
+                # Remove image and manifest when done.
+                self._cleanup_image(instance)
+
+            except Exception as ex:
+                with excutils.save_and_reraise_exception():
+                    LOG.error(_LE('Failed to upload %(image)s to LXD: '
+                                  '%(reason)s'),
+                              {'image': instance.image_ref,
+                               'reason': ex}, instance=instance)
+                    self._cleanup_image(instance)
+
+    def _verify_image(self, context, instance):
+        """Inspect image to verify the correct disk format.
+
+          Inspect and verify and the image that will downloaded
+          from glance is the correct image. The image must be in
+          a raw disk format in order for the LXD daemon to import
+          it into the local image store.
+
+          :param context: nova security context
+          ;param instance: nova instance object
+        """
+        LOG.debug('_verify_image called for instance', instance=instance)
+        try:
+            # grab the disk format of the image
+            img_meta = IMAGE_API.get(context, instance.image_ref)
+            disk_format = img_meta.get('disk_format')
+            if not disk_format:
+                reason = _('Bad image format')
+                raise exception.ImageUnacceptable(image_id=instance.image_ref,
+                                                  reason=reason)
+
+            if disk_format not in ['raw', 'root-tar']:
+                reason = _('nova-lxd does not support images in %s format. '
+                           'You should upload an image in raw or root-tar '
+                           'format.') % disk_format
+                raise exception.ImageUnacceptable(image_id=instance.image_ref,
+                                                  reason=reason)
+        except Exception as ex:
+            reason = _('Bad Image format: %(ex)s') \
+                % {'ex': ex}
+            raise exception.ImageUnacceptable(image_id=instance.image_ref,
+                                              reason=reason)
+
+    def _fetch_image(self, context, instance):
+        """Fetch an image from glance
+
+        :param context: nova security object
+        :param instance: the nova instance object
+
+        """
+        LOG.debug('_fetch_image called for instance', instance=instance)
+        with fileutils.remove_path_on_error(self.container_image):
+            IMAGE_API.download(context, instance.image_ref,
+                               dest_path=self.container_image)
+
+    def _get_lxd_manifest(self, instance, image_meta):
+        """Creates the LXD manifest, needed for split images
+
+        :param instance: nova instance
+        :param image_meta: image metadata dictionary
+
+        """
+        LOG.debug('_get_lxd_manifest called for instance', instance=instance)
+
+        metadata_yaml = None
+        try:
+            # Create a basic LXD manifest from the image properties
+            image_arch = image_meta.properties.get('hw_architecture')
+            if image_arch is None:
+                image_arch = arch.from_host()
+            metadata = {
+                'architecture': image_arch,
+                'creation_date': int(os.stat(self.container_image).st_ctime)
+            }
+
+            metadata_yaml = (json.dumps(metadata, sort_keys=True,
+                                        indent=4, separators=(',', ': '),
+                                        ensure_ascii=False).encode('utf-8')
+                             + b"\n")
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Failed to generate manifest for %(image)s: '
+                              '%(reason)s'),
+                          {'image': instance.name, 'ex': ex},
+                          instance=instance)
+        try:
+            # Compress the manifest using tar
+            target_tarball = tarfile.open(self.container_manifest, "w:")
+            metadata_file = tarfile.TarInfo()
+            metadata_file.size = len(metadata_yaml)
+            metadata_file.name = "metadata.yaml"
+            target_tarball.addfile(metadata_file,
+                                   io.BytesIO(metadata_yaml))
+            target_tarball.close()
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Failed to generate manifest tarball for'
+                              ' %(image)s: %(reason)s'),
+                          {'image': instance.name, 'ex': ex},
+                          instance=instance)
+
+        try:
+            # Compress the manifest further using xz
+            with fileutils.remove_path_on_error(self.container_manifest):
+                utils.execute('xz', '-9', self.container_manifest,
+                              check_exit_code=[0, 1])
+        except processutils.ProcessExecutionError as ex:
+            with excutils.save_and_reraise_exception:
+                LOG.error(_LE('Failed to compress manifest for %(image)s:'
+                              ' %(ex)s'), {'image': instance.image_ref,
+                                           'ex': ex}, instance=instance)
+
+    def _image_upload(self, instance):
+        """Upload an image to the LXD image store
+
+        We create the LXD manifest on the fly since glance does
+        not understand how to talk to Glance.
+
+        :param instance: nova instance
+
+        """
+        LOG.debug('image_upload called for instance', instance=instance)
+        headers = {}
+
+        boundary = str(uuid.uuid1())
+
+        # Create the binary blob to upload the file to LXD
+        tmpdir = tempfile.mkdtemp()
+        upload_path = os.path.join(tmpdir, "upload")
+        body = open(upload_path, 'wb+')
+
+        for name, path in [("metadata", (self.container_manifest + '.xz')),
+                           ("rootfs", self.container_image)]:
+            filename = os.path.basename(path)
+            body.write(bytearray("--%s\r\n" % boundary, "utf-8"))
+            body.write(bytearray("Content-Disposition: form-data; "
+                                 "name=%s; filename=%s\r\n" %
+                                 (name, filename), "utf-8"))
+            body.write("Content-Type: application/octet-stream\r\n")
+            body.write("\r\n")
+            with open(path, "rb") as fd:
+                shutil.copyfileobj(fd, body)
+            body.write("\r\n")
+
+        body.write(bytearray("--%s--\r\n" % boundary, "utf-8"))
+        body.write('\r\n')
+        body.close()
+
+        headers['Content-Type'] = "multipart/form-data; boundary=%s" \
+            % boundary
+
+        # Upload the file to LXD and then remove the tmpdir.
+        self.client.image_upload(data=open(upload_path, 'rb'),
+                                 headers=headers, instance=instance)
+        shutil.rmtree(tmpdir)
+
+    def _setup_alias(self, instance):
+        """Creates the LXD alias for the image
+
+        :param instance: nova instance
+        """
+        LOG.debug('_setup_alias called for instance', instance=instance)
+
+        try:
+            with open((self.container_manifest + '.xz'), 'rb') as meta_fd:
+                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)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception:
+                LOG.error(_LE('Failed to setup alias for %(image)s:'
+                              ' %(ex)s'), {'image': instance.image_ref,
+                                           'ex': ex}, instance=instance)
+
+    def _cleanup_image(self, instance):
+        """Cleanup the remaning bits of the glance/lxd interaction
+
+        :params image_meta: image_meta dictionary
+
+        """
+        LOG.debug('_cleanup_image called for instance', instance=instance)
+
+        if os.path.exists(self.container_image):
+            os.unlink(self.container_image)
+
+        if os.path.exists(self.container_manifest):
+            os.unlink(self.container_manifest)
diff --git a/nova/virt/lxd/migrate.py b/nova/virt/lxd/migrate.py
new file mode 100644
index 0000000..ab1927c
--- /dev/null
+++ b/nova/virt/lxd/migrate.py
@@ -0,0 +1,169 @@
+# Copyright 2016 Canonical Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import os
+
+from nova import exception
+from nova import i18n
+from nova import utils
+from nova.virt import configdrive
+
+from oslo_config import cfg
+from oslo_log import log as logging
+from oslo_utils import excutils
+from oslo_utils import fileutils
+
+from nova.virt.lxd import config
+from nova.virt.lxd import operations
+from nova.virt.lxd import utils as container_dir
+from nova.virt.lxd import session
+
+
+_ = i18n._
+_LE = i18n._LE
+_LI = i18n._LI
+
+CONF = cfg.CONF
+CONF.import_opt('my_ip', 'nova.netconf')
+LOG = logging.getLogger(__name__)
+
+
+class LXDContainerMigrate(object):
+
+    def __init__(self, virtapi):
+        self.virtapi = virtapi
+        self.config = config.LXDContainerConfig()
+        self.container_dir = container_dir.LXDContainerDirectories()
+        self.session = session.LXDAPISession()
+        self.operations = \
+            operations.LXDContainerOperations(
+                self.virtapi)
+
+    def migrate_disk_and_power_off(self, context, instance, dest,
+                                   flavor, network_info,
+                                   block_device_info=None, timeout=0,
+                                   retry_interval=0):
+        LOG.debug("migrate_disk_and_power_off called", instance=instance)
+
+        same_host = False
+        if CONF.my_ip == dest:
+            same_host = True
+            LOG.debug('Migration target is the source host')
+        else:
+            LOG.debug('Migration target host: %s' % dest)
+
+        if not self.session.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)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('failed to resize container '
+                              '%(instance)s: %(ex)s'),
+                          {'instance': instance.name, 'ex': ex},
+                          instance=instance)
+
+        # disk_info is not used
+        return ""
+
+    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)
+
+    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
+            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)
+
+    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
diff --git a/nova/virt/lxd/operations.py b/nova/virt/lxd/operations.py
new file mode 100644
index 0000000..84fb80c
--- /dev/null
+++ b/nova/virt/lxd/operations.py
@@ -0,0 +1,678 @@
+# Copyright 2011 Justin Santa Barbara
+# Copyright 2015 Canonical Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+
+from nova.api.metadata import base as instance_metadata
+from nova.virt import configdrive
+from nova.virt import hardware
+import os
+import pwd
+import shutil
+
+from oslo_config import cfg
+from oslo_log import log as logging
+from oslo_utils import excutils
+from oslo_utils import fileutils
+from oslo_utils import units
+
+from nova import exception
+from nova import i18n
+from nova import utils
+from nova.compute import power_state
+
+from nova.virt.lxd import config as container_config
+from nova.virt.lxd import container_firewall
+from nova.virt.lxd import image
+from nova.virt.lxd import session
+from nova.virt.lxd import utils as container_dir
+from nova.virt.lxd import vif
+
+_ = i18n._
+_LE = i18n._LE
+_LW = i18n._LW
+_LI = i18n._LI
+
+CONF = cfg.CONF
+CONF.import_opt('vif_plugging_timeout', 'nova.virt.driver')
+CONF.import_opt('vif_plugging_is_fatal', 'nova.virt.driver')
+LOG = logging.getLogger(__name__)
+
+MAX_CONSOLE_BYTES = 100 * units.Ki
+
+
+class LXDContainerOperations(object):
+    """LXD container operations."""
+
+    def __init__(self, virtapi):
+        self.virtapi = virtapi
+
+        self.config = container_config.LXDContainerConfig()
+        self.container_dir = container_dir.LXDContainerDirectories()
+        self.image = image.LXDContainerImage()
+        self.firewall_driver = container_firewall.LXDContainerFirewall()
+        self.session = session.LXDAPISession()
+
+        self.vif_driver = vif.LXDGenericDriver()
+        self.instance_dir = None
+
+    def list_instances(self):
+        return self.session.container_list()
+
+    def spawn(self, context, instance, image_meta, injected_files,
+              admin_password=None, network_info=None, block_device_info=None):
+        """Start the LXD container
+
+        Once this successfully completes, the instance should be
+        running (power_state.RUNNING).
+
+        If this fails, any partial instance should be completely
+        cleaned up, and the virtualization platform should be in the state
+        that it was before this call began.
+
+        :param context: security context
+        :param instance: nova.objects.instance.Instance
+                         This function should use the data there to guide
+                         the creation of the new instance.
+        :param image_meta: image object returned by nova.image.glance that
+                           defines the image from which to boot this instance
+        :param injected_files: User files to inject into instance.
+        :param admin_password: Administrator password to set in instance.
+        :param network_info:
+            :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info`
+        :param block_device_info: Information about block devices to be
+                                  attached to the instance
+        """
+        msg = ('Spawning container '
+               'network_info=%(network_info)s '
+               'image_meta=%(image_meta)s '
+               'instance=%(instance)s '
+               'block_device_info=%(block_device_info)s' %
+               {'network_info': network_info,
+                'instance': instance,
+                'image_meta': image_meta,
+                'block_device_info': block_device_info})
+        LOG.debug(msg, instance=instance)
+
+        instance_name = instance.name
+
+        if self.session.container_defined(instance_name, instance):
+            raise exception.InstanceExists(name=instance.name)
+
+        try:
+
+            # Ensure that the instance directory exists
+            self.instance_dir = \
+                self.container_dir.get_instance_dir(instance_name)
+            if not os.path.exists(self.instance_dir):
+                fileutils.ensure_tree(self.instance_dir)
+
+            # Step 1 - Fetch the image from glance
+            self._fetch_image(context, instance, image_meta)
+
+            # Step 2 - Setup the container network
+            self._setup_network(instance_name, instance, network_info)
+
+            # Step 3 - Create the container profile
+            self._setup_profile(instance_name, instance, network_info)
+
+            # Step 4 - Create a config drive (optional)
+            if configdrive.required_by(instance):
+                self._add_configdrive(instance, injected_files)
+
+            # Step 5 - Configure and start the container
+            self._setup_container(instance_name, instance)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Faild to start container '
+                              '%(instance)s: %(ex)s'),
+                          {'instance': instance.name, 'ex': ex},
+                          instance=instance)
+                self.destroy(context, instance, network_info)
+
+    def _fetch_image(self, context, instance, image_meta):
+        """Fetch the LXD image from glance
+
+        :param context: nova security context
+        :param instance: nova instance object
+        :param image_meta: nova image opbject
+        """
+        LOG.debug('_fetch_image called for instance', instance=instance)
+        try:
+            # Download the image from glance and upload the image
+            # to the local LXD image store.
+            self.image.setup_image(context, instance, image_meta)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Upload image failed for %(instance)s '
+                              'for %(image)s: %(e)s'),
+                          {'instance': instance.name,
+                           'image': instance.image_ref,
+                           'ex': ex}, instance=instance)
+
+    def _setup_network(self, instance_name, instance, network_info):
+        """Setup the network when creating the lXD container
+
+        :param instance_name: nova instance name
+        :param instance: nova instance object
+        :param network_info: instance network configuration
+        """
+        LOG.debug('_setup_network called for instance', instance=instance)
+        try:
+            self.plug_vifs(instance, network_info)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Failed to create container network for '
+                              '%(instance)s: %(ex)s'),
+                          {'instance': instance_name, 'ex': ex},
+                          instance=instance)
+
+    def _setup_profile(self, instance_name, instance, network_info):
+        """Create an LXD container profile for the nova intsance
+
+        :param instance_name: nova instance name
+        :param instance: nova instance object
+        :param network_info: nova instance netowkr configuration
+        """
+        LOG.debug('_setup_profile called for instance', instance=instance)
+        try:
+            # Setup the container profile based on the nova
+            # instance object and network objects
+            container_profile = self.config.create_profile(instance,
+                                                           network_info)
+            self.session.profile_create(container_profile, instance)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Failed to create a profile for'
+                              ' %(instance)s: %(ex)s'),
+                          {'instance': instance_name,
+                           'ex': ex}, instance=instance)
+
+    def _setup_container(self, instance_name, instance):
+        """Create and start the LXD container.
+
+        :param instance_name: nova instjace name
+        :param instance: nova instance object
+        """
+        LOG.debug('_setup_container called for instance', instance=instance)
+        try:
+            # Create the container
+            container_config = \
+                self.config.create_container(instance)
+            self.session.container_init(
+                container_config, instance)
+
+            # Start the container
+            self.session.container_start(instance_name, instance)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_LE('Container creation failed for '
+                                  '%(instance)s: %(ex)s'),
+                              {'instance': instance.name,
+                               'ex': ex}, instance=instance)
+
+    def _add_configdrive(self, instance, injected_files):
+        """Configure the config drive for the container
+
+        :param instance: nova instance object
+        :param injected_files: instance injected files
+        """
+        LOG.debug('add_configdrive called for instance', instance=instance)
+
+        extra_md = {}
+        inst_md = instance_metadata.InstanceMetadata(instance,
+                                                     content=injected_files,
+                                                     extra_md=extra_md)
+        # Create the ISO image so we can inject the contents of the ISO
+        # into the container
+        iso_path = os.path.join(self.instance_dir, 'configdirve.iso')
+        with configdrive.ConfigDriveBuilder(instance_md=inst_md) as cdb:
+            try:
+                cdb.make_drive(iso_path)
+            except Exception as e:
+                with excutils.save_and_reraise_exception():
+                    LOG.error(_LE('Creating config drive failed with error: '
+                                  '%s'), e, instance=instance)
+
+        # Copy the metadata info from the ISO into the container
+        configdrive_dir = \
+            self.container_dir.get_container_configdrive(instance.name)
+        with utils.tempdir() as tmpdir:
+            mounted = False
+            try:
+                _, err = utils.execute('mount',
+                                       '-o',
+                                       'loop,uid=%d,gid=%d' % (os.getuid(),
+                                                               os.getgid()),
+                                       iso_path, tmpdir,
+                                       run_as_root=True)
+                mounted = True
+
+                # Copy and adjust the files from the ISO so that we
+                # dont have the ISO mounted during the life cycle of the
+                # instance and the directory can be removed once the instance
+                # is terminated
+                for ent in os.listdir(tmpdir):
+                    shutil.copytree(os.path.join(tmpdir, ent),
+                                    os.path.join(configdrive_dir, ent))
+                utils.execute('chmod', '-R', '775', configdrive_dir,
+                              run_as_root=True)
+                utils.execute('chown', '-R', '%s:%s'
+                              % (self._uid_map('/etc/subuid').rstrip(),
+                                 self._uid_map('/etc/subgid').rstrip()),
+                              configdrive_dir, run_as_root=True)
+            finally:
+                if mounted:
+                    utils.execute('umount', tmpdir, run_as_root=True)
+
+    def reboot(self, context, instance, network_info, reboot_type,
+               block_device_info=None, bad_volumes_callback=None):
+        """Reboot a instance on a LXD host
+
+        :param instance: nova.objects.instance.Instance
+        :param network_info:
+           :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info`
+        :param reboot_type: Either a HARD or SOFT reboot
+        :param block_device_info: Info pertaining to attached volumes
+        :param bad_volumes_callback: Function to handle any bad volumes
+            encountered
+        """
+        LOG.debug('reboot called for instance', instance=instance)
+        try:
+            self.session.container_reboot(instance)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_LE('Container reboot failed for '
+                                  '%(instance)s: %(ex)s'),
+                              {'instance': instance.name,
+                               'ex': ex}, instance=instance)
+
+    def plug_vifs(self, instance, network_info):
+        """Setup the container network on the host
+
+         :param instance: nova instance object
+         :param network_info: instance network configuration
+         """
+        LOG.debug('plug_vifs called for instance', instance=instance)
+        try:
+            for viface in network_info:
+                self.vif_driver.plug(instance, viface)
+            self.start_firewall(instance, network_info)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Failed to configure container network'
+                              ' for %(instance)s: %(ex)s'),
+                          {'instance': instance.name, 'ex': ex},
+                          instance=instance)
+
+    def unplug_vifs(self, instance, network_info):
+        """Unconfigure the LXD container network
+
+           :param instance: nova intance object
+           :param network_info: instance network confiugration
+        """
+        try:
+            for viface in network_info:
+                self.vif_driver.unplug(instance, viface)
+            self.stop_firewall(instance, network_info)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Failed to remove container network'
+                              ' for %(instance)s: %(ex)s'),
+                          {'instance': instance.name, 'ex': ex},
+                          instance=instance)
+
+    def destroy(self, context, instance, network_info, block_device_info=None,
+                destroy_disks=True, migrate_data=None):
+        """Destroy the instance on the LXD host
+
+        :param context: security context
+        :param instance: Instance object as returned by DB layer.
+        :param network_info:
+           :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info`
+        :param block_device_info: Information about block devices that should
+                                  be detached from the instance.
+        :param destroy_disks: Indicates if disks should be destroyed
+        :param migrate_data: implementation specific params
+        """
+        LOG.debug('destroy called for instance', instance=instance)
+        try:
+            self.session.profile_delete(instance)
+            self.session.container_destroy(instance.name,
+                                           instance)
+            self.cleanup(context, instance, network_info, block_device_info)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Failed to remove container'
+                              ' for %(instance)s: %(ex)s'),
+                          {'instance': instance.name, 'ex': ex},
+                          instance=instance)
+
+    def power_off(self, instance, timeout=0, retry_interval=0):
+        """Power off an instance
+
+        :param instance: nova.objects.instance.Instance
+        :param timeout: time to wait for GuestOS to shutdown
+        :param retry_interval: How often to signal guest while
+                               waiting for it to shutdown
+        """
+        LOG.debug('power_off called for instance', instance=instance)
+        try:
+            self.session.container_stop(instance.name,
+                                        instance)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Failed to power_off container'
+                              ' for %(instance)s: %(ex)s'),
+                          {'instance': instance.name, 'ex': ex},
+                          instance=instance)
+
+    def power_on(self, context, instance, network_info,
+                 block_device_info=None):
+        """Power on instance
+
+        :param instance: nova.objects.instance.Instance
+        """
+        LOG.debug('power_on called for instance', instance=instance)
+        try:
+            self.session.container_start(instance.name, instance)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_LE('Container power off for '
+                                  '%(instance)s: %(ex)s'),
+                              {'instance': instance.name,
+                               'ex': ex}, instance=instance)
+
+    def pause(self, instance):
+        """Pause an instance
+
+        :param nova.objects.instance.Instance instance:
+            The instance which should be paused.
+        """
+        LOG.debug('pause called for instance', instance=instance)
+        try:
+            self.session.container_pause(instance.name, instance)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Failed to pause container'
+                              ' for %(instance)s: %(ex)s'),
+                          {'instance': instance.name, 'ex': ex},
+                          instance=instance)
+
+    def unpause(self, instance):
+        """Unpause an instance
+
+        :param nova.objects.instance.Instance instance:
+            The instance which should be paused.
+        """
+        LOG.debug('unpause called for instance', instance=instance)
+        try:
+            self.session.container_unpause(instance.name, instance)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Failed to unpause container'
+                              ' for %(instance)s: %(ex)s'),
+                          {'instance': instance.name, 'ex': ex},
+                          instance=instance)
+
+    def suspend(self, context, instance):
+        """Suspend an instance
+
+        :param context: nova security context
+        :param nova.objects.instance.Instance instance:
+            The instance which should be paused.
+        """
+        LOG.debug('suspend called for instance', instance=instance)
+        try:
+            self.session.container_pause(instance.name, instance)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_LE('Container suspend failed for '
+                                  '%(instance)s: %(ex)s'),
+                              {'instance': instance.name,
+                               'ex': ex}, instance=instance)
+
+    def resume(self, context, instance, network_info, block_device_info=None):
+        """Resume an instance on an LXD host
+
+        :param nova.context.RequestContext context:
+            The context for the resume.
+        :param nova.objects.instance.Instance instance:
+            The suspended instance to resume.
+        :param nova.network.model.NetworkInfo network_info:
+            Necessary network information for the resume.
+        :param dict block_device_info:
+            Instance volume block device info.
+        """
+        LOG.debug('resume called for instance', instance=instance)
+        try:
+            self.session.container_unpause(instance.name, instance)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Failed to resume container'
+                              ' for %(instance)s: %(ex)s'),
+                          {'instance': instance.name, 'ex': ex},
+                          instance=instance)
+
+    def rescue(self, context, instance, network_info, image_meta,
+               rescue_password):
+        """Rescue an instance
+
+        :param instance: nova.objects.instance.Instance
+        """
+        LOG.debug('rescue called for instance', instance=instance)
+        try:
+            if not self.session.container_defined(instance.name, instance):
+                msg = _('Unable to find instance')
+                raise exception.NovaException(msg)
+
+            # 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},
+                                        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_init(container_config, instance)
+
+            # Step 4 - Start the rescue instance
+            self.session.container_start(instance.name, instance)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_LE('Container rescue failed for '
+                                  '%(instance)s: %(ex)s'),
+                              {'instance': instance.name,
+                               'ex': ex}, instance=instance)
+
+    def unrescue(self, instance, network_info):
+        """Unrescue a LXD host
+
+        :param instance: nova instance object
+        :param network_info: nova network configuration
+        """
+        LOG.debug('unrescue called for instance', instance=instance)
+        try:
+            if not self.session.container_defined(instance.name, instance):
+                msg = _('Unable to find instance')
+                raise exception.NovaException(msg)
+
+            # Step 1 - Destory the rescue instance.
+            self.session.container_destroy(instance.name,
+                                           instance)
+
+            # Step 2 - Rename the backup container that
+            #          the user was working on.
+            self.session.container_move(instance.name + '-backup',
+                                        {'name': instance.name},
+                                        instance)
+
+            # Step 3 - Start the old contianer
+            self.session.container_start(instance.name, instance)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_LE('Container unrescue failed for '
+                                  '%(instance)s: %(ex)s'),
+                              {'instance': instance.name,
+                               'ex': ex}, instance=instance)
+
+    def cleanup(self, context, instance, network_info, block_device_info=None,
+                destroy_disks=True, migrate_data=None, destroy_vifs=True):
+        """Cleanup a contianer after its been deleted.
+
+        :param context: security context
+        :param instance: Instance object as returned by DB layer.
+        :param network_info:
+           :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info`
+        :param block_device_info: Information about block devices that should
+                                  be detached from the instance.
+        :param destroy_disks: Indicates if disks should be destroyed
+        :param migrate_data: implementation specific params
+        """
+        LOG.debug('cleanup called for instance', instance=instance)
+        try:
+            if destroy_vifs:
+                self.unplug_vifs(instance, network_info)
+
+            name = pwd.getpwuid(os.getuid()).pw_name
+            configdrive_dir = \
+                self.container_dir.get_container_configdrive(instance.name)
+            if os.path.exists(configdrive_dir):
+                utils.execute('chown', '-R', '%s:%s' % (name, name),
+                              configdrive_dir, run_as_root=True)
+                shutil.rmtree(configdrive_dir)
+
+            container_dir = self.container_dir.get_instance_dir(instance.name)
+            if os.path.exists(container_dir):
+                shutil.rmtree(container_dir)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.exception(_LE('Container cleanup failed for '
+                                  '%(instance)s: %(ex)s'),
+                              {'instance': instance.name,
+                               'ex': ex}, instance=instance)
+
+    def get_info(self, instance):
+        """Get the current status of an instance, by name (not ID!)
+
+        :param instance: nova.objects.instance.Instance object
+
+        Returns a InstanceInfo object
+        """
+        LOG.debug('get_info called for instance', instance=instance)
+        try:
+            if not self.session.container_defined(instance.name, instance):
+                return hardware.InstanceInfo(state=power_state.NOSTATE)
+
+            container_state = self.session.container_state(instance)
+            return hardware.InstanceInfo(state=container_state['state'],
+                                         max_mem_kb=container_state['max_mem'],
+                                         mem_kb=container_state['mem'],
+                                         num_cpu=instance.flavor.vcpus,
+                                         cpu_time_ns=0)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Failed to get container info'
+                              ' for %(instance)s: %(ex)s'),
+                          {'instance': instance.name, 'ex': ex},
+                          instance=instance)
+
+    def get_console_output(self, context, instance):
+        """Get console output for an instance
+        :param context: security context
+        :param instance: nova.objects.instance.Instance
+        """
+        LOG.debug('get_console_output called for instance', instance=instance)
+        try:
+            console_log = self.container_dir.get_console_path(instance.name)
+            if not os.path.exists(console_log):
+                return ""
+            uid = pwd.getpwuid(os.getuid()).pw_uid
+            utils.execute('chown', '%s:%s' % (uid, uid),
+                          console_log, run_as_root=True)
+            utils.execute('chmod', '755',
+                          os.path.join(
+                              self.container_dir.get_container_dir(
+                                  instance.name), instance.name),
+                          run_as_root=True)
+            with open(console_log, 'rb') as fp:
+                log_data, remaning = utils.last_bytes(fp,
+                                                      MAX_CONSOLE_BYTES)
+                return log_data
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Failed to get container output'
+                              ' for %(instance)s: %(ex)s'),
+                          {'instance': instance.name, 'ex': ex},
+                          instance=instance)
+
+    def container_attach_interface(self, instance, image_meta, vif):
+        LOG.debug('container_attach_interface called for instance',
+                  instance=instance)
+        try:
+            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)
+        except Exception as ex:
+            with excutils.save_and_reraise_exception():
+                self.vif_driver.unplug(instance, vif)
+                LOG.error(_LE('Failed to configure network'
+                              ' for %(instance)s: %(ex)s'),
+                          {'instance': instance.name, 'ex': ex},
+                          instance=instance)
+
+    def container_detach_interface(self, instance, vif):
+        LOG.debug('container_defatch_interface called for instance',
+                  instance=instance)
+        try:
+            self.vif_driver.unplug(instance, vif)
+        except exception.NovaException:
+            pass
+
+    def start_firewall(self, instance, network_info):
+        self.firewall_driver.setup_basic_filtering(instance, network_info)
+        self.firewall_driver.prepare_instance_filter(instance, network_info)
+        self.firewall_driver.apply_instance_filter(instance, network_info)
+
+    def stop_firewall(self, instance, network_info):
+        self.firewall_driver.unfilter_instance(instance, network_info)
+
+    def _uid_map(self, subuid_f):
+        LOG.debug('Checking for subuid')
+
+        line = None
+        with open(subuid_f, 'r') as fp:
+            name = pwd.getpwuid(os.getuid()).pw_name
+            for cline in fp:
+                if cline.startswith(name + ":"):
+                    line = cline
+                    break
+            if line is None:
+                raise ValueError("%s not found in %s" % (name, subuid_f))
+            toks = line.split(":")
+            return toks[1]
diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
new file mode 100644
index 0000000..e1298eb
--- /dev/null
+++ b/nova/virt/lxd/session.py
@@ -0,0 +1,1026 @@
+# Copyright 2015 Canonical Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
+#    the License for the specific language governing permissions and
+#    limitations under the License.
+
+from nova import context as nova_context
+from nova import exception
+from nova import i18n
+from nova import rpc
+from nova import utils
+from nova.compute import power_state
+
+from oslo_concurrency import processutils
+from oslo_config import cfg
+from oslo_log import log as logging
+from oslo_service import loopingcall
+from oslo_utils import excutils
+
+from pylxd.deprecated import api
+from pylxd.deprecated import exceptions as lxd_exceptions
+import six
+
+from nova.virt.lxd import constants
+
+_ = i18n._
+_LE = i18n._LE
+_LI = i18n._LI
+
+CONF = cfg.CONF
+CONF.import_opt('host', 'nova.netconf')
+LOG = logging.getLogger(__name__)
+
+
+def mount_filesystem(self, dev_path, dir_path):
+    try:
+        _out, err = utils.execute('mount',
+                                  '-t', 'ext4',
+                                  dev_path, dir_path, run_as_root=True)
+    except processutils.ProcessExecutionError as e:
+        err = six.text_type(e)
+    return err
+
+
+def umount_filesystem(self, dir_path):
+    try:
+        _out, err = utils.execute('umount',
+                                  dir_path, run_as_root=True)
+    except processutils.ProcessExecutionError as e:
+        err = six.text_type(e)
+    return err
+
+
+class LXDAPISession(object):
+    """The session to invoke the LXD API session."""
+
+    def __init__(self):
+        super(LXDAPISession, self).__init__()
+
+    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
+    #
+
+    def container_list(self):
+        """List of containers running on a given host
+
+        Returns a list of running containers
+
+        """
+        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)
+        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_update(self, config, instance):
+        """Update the LXD configuration of a given container
+
+        :param config: LXD configuration dictionary
+        :param instance: nova instance object
+        :return: an update LXD configuration dictionary
+
+        """
+        LOG.debug('container_update called fo instance', instance=instance)
+        try:
+            client = self.get_session()
+            if not self.container_defined(instance.name, instance):
+                msg = _('Instance is not found: %s') % instance.name
+                raise exception.InstanceNotFound(msg)
+
+            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)
+        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_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
+
+        :param instance: nova instance object
+        :return: nova power_state
+
+        """
+        LOG.debug('container_state called for instance', instance=instance)
+        try:
+            mem = 0
+            max_mem = 0
+
+            client = self.get_session()
+            if not self.container_defined(instance.name, instance):
+                return
+
+            (state, data) = client.container_state(instance.name)
+            state = constants.LXD_POWER_STATES[data['metadata']['status_code']]
+
+            container_state = self.container_info(instance)
+            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:
+            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}
+
+    def container_config(self, instance):
+        """Fetches the configuration of a given LXD container
+
+        :param instance: nova instance object
+        :return: dictionary represenation of a LXD container
+
+        """
+        LOG.debug('container_config called for instance', instance=instance)
+        try:
+            if not self.container_defined(instance.name, instance):
+                msg = _('Instance is not found %s') % instance.name
+                raise exception.InstanceNotFound(msg)
+
+            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)
+        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)
+
+    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:
+            if not self.container_defined(instance.name, instance):
+                msg = _('Instance is not found %s') % instance.name
+                raise exception.InstanceNotFound(msg)
+
+            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
+
+        :param instance_name: container anme
+        :param instance: nova instance opbject
+        :return: True if exists otherwise False
+
+        """
+        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:
+                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)
+
+    def container_start(self, instance_name, instance):
+        """Start an LXD container
+
+        :param instance_name: name of container
+        :param instance: nova instance object
+
+        """
+        LOG.debug('container_start called for instance', instance=instance)
+        try:
+            LOG.info(_LI('Starting instance %(instance)s with '
+                         '%(image)s'), {'instance': instance.name,
+                                        'image': instance.image_ref})
+            # Start the container
+            client = self.get_session()
+
+            # (chuck): Something wicked could happen between
+            # container
+            if not self.container_defined(instance_name, instance):
+                msg = _('Instance is not found %s ') % instance.name
+                raise exception.InstanceNotFound(msg)
+
+            (state, data) = client.container_start(instance_name,
+                                                   CONF.lxd.timeout)
+            self.operation_wait(data.get('operation'), instance)
+
+            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)
+        except Exception as ex:
+            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
+
+        :param instance_name: instance name
+        :param instance: nova instance object
+
+        """
+        LOG.debug('container_stop called for instance', instance=instance)
+        try:
+            if not self.container_defined(instance_name, instance):
+                msg = _('Instance is not found %s') % instance.name
+                raise exception.InstanceNotFound(msg)
+
+            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)
+
+            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(
+                    _LE('Failed to stop container %(instance)s: '
+                        '%(reason)s'), {'instance': instance_name,
+                                        'reason': ex})
+
+    def container_reboot(self, instance):
+        """Reboot a LXD container
+
+        :param instance: nova instance object
+
+        """
+        LOG.debug('container_reboot called for instance', instance=instance)
+        try:
+            if not self.container_defined(instance.name, instance):
+                msg = _('Instance is not found %s') % instance.name
+                raise exception.InstanceNotFound(msg)
+
+            LOG.info(_LI('Rebooting instance %(instance)s with'
+                         ' %(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)
+
+            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():
+                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
+
+        :param instance_name: container name
+        :param instance: nova instance object
+
+        """
+        LOG.debug('container_destroy for instance', instance=instance)
+        try:
+            if not self.container_defined(instance_name, instance):
+                return
+
+            LOG.info(_LI('Destroying instance %(instance)s with'
+                         ' %(image)s'), {'instance': instance.name,
+                                         'image': instance.image_ref})
+
+            # 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)
+
+            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: '
+                              '%(reason)s'), {'instance': instance_name,
+                                              'reason': ex})
+
+    def container_pause(self, instance_name, instance):
+        """Pause a LXD container
+
+        :param instance_name: container name
+        :param instance: nova instance object
+
+        """
+        LOG.debug('container_paused called for instance', instance=instance)
+        try:
+            if not self.container_defined(instance_name, instance):
+                msg = _('Instance is not found %s') % instance_name
+                raise exception.InstanceNotFound(msg)
+
+            LOG.info(_LI('Pausing instance %(instance)s with'
+                         ' %(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)
+
+            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(
+                    _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
+
+        :param instance_name: container name
+        :param instance: nova instance object
+
+        """
+        LOG.debug('container_unpause called for instance', instance=instance)
+        try:
+            if not self.container_defined(instance_name, instance):
+                msg = _('Instance is not found %s') % instance_name
+                raise exception.InstanceNotFound(msg)
+
+            LOG.info(_LI('Unpausing instance %(instance)s with'
+                         ' %(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)
+
+            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(
+                    _LE('Failed to unpause container %(instance)s: '
+                        '%(reason)s'), {'instance': instance_name,
+                                        'reason': ex})
+
+    def container_init(self, config, instance):
+        """Create a LXD container
+
+        :param config: LXD container config as a dict
+        :param instance: nova instance object
+
+        """
+        LOG.debug('container_init called for instance', instance=instance)
+        try:
+            LOG.info(_LI('Creating container %(instance)s with'
+                         ' %(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:
+                raise exception.NovaException(data['metadata'])
+
+            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(
+                    _LE('Failed to create container %(instance)s: %(reason)s'),
+                    {'instance': instance.name,
+                     'reason': ex}, instance=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
+        """
+        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)
+        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)
+
+    def create_alias(self, alias, instance):
+        """Creates an alias for a given image
+
+        :param alias: The alias to be crerated
+        :param instance: The nove instance
+        :return: true if alias is created, false otherwise
+
+        """
+        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)
+        except Exception as e:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Error from LXD during create alias'
+                              '%(instance)s: %(reason)s'),
+                          {'instance': instance.image_ref, 'reason': e},
+                          instance=instance)
+
+    def image_upload(self, data, headers, instance):
+        """Upload an image to the local LXD image store
+
+        :param data: image data
+        :param headers: image headers
+        :param instance: The nova 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)
+        except Exception as e:
+            with excutils.save_and_reraise_exception():
+                LOG.error(_LE('Error from LXD during image upload'
+                              '%(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)
+
+    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
+    #
+    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
+
+           :param instance: nova instance object
+        """
+        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:
+                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})
+
+    def profile_create(self, config, instance):
+        """Create an LXD container profile
+
+        :param config: profile dictionary
+        :param instance: nova instance object
+        """
+        LOG.debug('profile_create called for instance',
+                  instance=instance)
+        try:
+            if self.profile_defined(instance.name, instance):
+                msg = _('Profile already exists %(instnce)s') % \
+                    {'instance': instance.name}
+                raise exception.NovaException(msg)
+
+            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(
+                    _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 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 update profile %(instance)s: '
+                        '%(reason)s'),
+                    {'instance': instance.name, 'reason': ex})
+
+    def profile_delete(self, instance):
+        """Delete a LXD container profile.
+
+           :param instance: nova instance object
+        """
+        LOG.debug('profile_delete called for instance', instance=instance)
+        try:
+            if not self.profile_defined(instance.name, instance):
+                return
+
+            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(
+                    _LE('Failed to delete profile %(instance)s: %(reason)s'),
+                    {'instance': instance.name, 'reason': ex})
+
+    #
+    # Host Methods
+    #
+    def host_certificate(self, instance, host):
+        LOG.debug('host_certificate called for instance', instance=instance)
+        try:
+            client = self.get_session(host)
+            return client.get_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)
+        try:
+            client = self.get_session()
+            return client.host_config()['environment']
+        except lxd_exceptions.APIError as ex:
+            msg = _('Failed to communicate with LXD %(instance)s:'
+                    ' %(reason)s') % {'instance': instance.name,
+                                      'ex': ex}
+            LOG.error(msg)
+
+    #
+    # Migrate methods
+    #
+    def container_migrate(self, instance_name, host, instance):
+        """Initialize a container migration for LXD
+
+        :param instance_name: container name
+        :param host: host to move container from
+        :param instance: nova instance object
+        :return: dictionary of the container keys
+
+        """
+        LOG.debug('container_migrate called for instance', instance=instance)
+        try:
+            LOG.info(_LI('Migrating instance %(instance)s with '
+                         '%(image)s'), {'instance': instance_name,
+                                        'image': instance.image_ref})
+
+            client = self.get_session(host)
+            (state, data) = client.container_migrate(instance_name)
+
+            LOG.info(_LI('Successfully initialized migration for instance '
+                         '%(instance)s with %(image)s'),
+                     {'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(
+                    _LE('Failed to migrate container %(instance)s: %('
+                        'reason)s'), {'instance': instance.name,
+                                      'reason': ex}, instance=instance)
+
+    #
+    # Snapshot methods
+    #
+
+    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 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 move container %(instance)s: '
+                        '%(reason)s'),
+                    {'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
+
+        :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)
+        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 publish container %(instance)s: '
+                        '%(reason)s'),
+                    {'instance': instance.name,
+                     'reason': ex}, instance=instance)
+
+    def container_export(self, image, instance):
+        """
+        Export an image from the local LXD image store into
+        an file.
+
+        :param image: image dictionary
+        :param instance: nova instance object
+        """
+        LOG.debug('container_export called for instance', instance=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(
+                    _LE('Failed to export container %(instance)s: '
+                        '%(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)
diff --git a/nova/virt/lxd/utils.py b/nova/virt/lxd/utils.py
new file mode 100644
index 0000000..7ad822f
--- /dev/null
+++ b/nova/virt/lxd/utils.py
@@ -0,0 +1,93 @@
+# Copyright 2015 Canonical Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import os
+
+from oslo_config import cfg
+
+CONF = cfg.CONF
+
+
+class LXDContainerDirectories(object):
+
+    def __init__(self):
+        self.base_dir = os.path.join(CONF.instances_path,
+                                     CONF.image_cache_subdirectory_name)
+
+    def get_base_dir(self):
+        return self.base_dir
+
+    def get_instance_dir(self, instance):
+        return os.path.join(CONF.instances_path,
+                            instance)
+
+    def get_container_rootfs_image(self, image_meta):
+        return os.path.join(self.base_dir,
+                            '%s-rootfs.tar.gz' % image_meta.id)
+
+    def get_container_manifest_image(self, image_meta):
+        return os.path.join(self.base_dir,
+                            '%s-manifest.tar' % image_meta.id)
+
+    def get_container_metadata(self, image_meta):
+        return os.path.join(self.base_dir,
+                            '%s-lxd.tar.xz' % image_meta.id)
+
+    def get_container_rootfsImg(self, image_meta):
+        return os.path.join(self.base_dir,
+                            '%s-root.tar.gz' % image_meta.id)
+
+    def get_container_configdrive(self, instance):
+        return os.path.join(CONF.instances_path,
+                            instance,
+                            'configdrive')
+
+    def get_console_path(self, instance):
+        return os.path.join('/var/log/lxd/',
+                            instance,
+                            'console.log')
+
+    def get_container_dir(self, instance):
+        return os.path.join(CONF.lxd.root_dir,
+                            'containers')
+
+    def get_container_rootfs(self, instance):
+        return os.path.join(CONF.lxd.root_dir,
+                            'containers',
+                            instance,
+                            'rootfs')
+
+    def get_container_rescue(self, instance):
+        if self.is_lvm(instance):
+            return os.path.join(CONF.lxd.root_dir,
+                                'containers',
+                                instance)
+        else:
+            return os.path.join(CONF.lxd.root_dir,
+                                'containers',
+                                instance,
+                                'rootfs')
+
+    def get_container_lvm(self, instance):
+        return '%s/%s.lv' % (self.get_container_dir(instance),
+                             instance)
+
+    def is_lvm(self, instance):
+        try:
+            if os.path.exists(os.readlink(
+                self.get_container_lvm(instance))):
+                return True
+        except Exception:
+            return False
diff --git a/nova/virt/lxd/vif.py b/nova/virt/lxd/vif.py
new file mode 100644
index 0000000..deada73
--- /dev/null
+++ b/nova/virt/lxd/vif.py
@@ -0,0 +1,223 @@
+# Copyright (c) 2015 Canonical Ltd
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_concurrency import processutils
+from oslo_config import cfg
+from oslo_log import log as logging
+
+from nova import exception
+from nova import i18n
+from nova.network import linux_net
+from nova.network import model as network_model
+from nova import utils
+
+_ = i18n._
+_LE = i18n._LE
+
+CONF = cfg.CONF
+
+LOG = logging.getLogger(__name__)
+
+
+class LXDGenericDriver(object):
+
+    def get_vif_devname(self, vif):
+        if 'devname' in vif:
+            return vif['devname']
+        return ("nic" + vif['id'])[:network_model.NIC_NAME_LEN]
+
+    def get_vif_devname_with_prefix(self, vif, prefix):
+        devname = self.get_vif_devname(vif)
+        return prefix + devname[3:]
+
+    def get_bridge_name(self, vif):
+        return vif['network']['bridge']
+
+    def get_ovs_interfaceid(self, vif):
+        return vif.get('ovs_interfaceid') or vif['id']
+
+    def get_br_name(self, iface_id):
+        return ("qbr" + iface_id)[:network_model.NIC_NAME_LEN]
+
+    def get_veth_pair_names(self, iface_id):
+        return (("qvb%s" % iface_id)[:network_model.NIC_NAME_LEN],
+                ("qvo%s" % iface_id)[:network_model.NIC_NAME_LEN])
+
+    def get_firewall_required(self, vif):
+        if CONF.firewall_driver != "nova.virt.firewall.NoopFirewallDriver":
+            return True
+        return False
+
+    def get_config(self, instance, vif):
+        vif_type = vif['type']
+
+        LOG.debug('vif_type=%(vif_type)s instance=%(instance)s '
+                  'vif=%(vif)s',
+                  {'vif_type': vif_type, 'instance': instance,
+                   'vif': vif})
+
+        if vif_type is None:
+            raise exception.NovaException(
+                _("vif_type parameter must be present "
+                  "for this vif_driver implementation"))
+        func = getattr(self, 'get_config_%s' % vif_type, None)
+        if not func:
+            raise exception.NovaException(
+                _("Unexpected vif_type=%s") % vif_type)
+        return func(instance, vif)
+
+    def get_config_bridge(self, instance, vif):
+        conf = {'bridge': self.get_bridge_name(vif),
+                'mac_address': vif['address']}
+        return conf
+
+    def get_config_ovs_hybrid(self, instance, vif):
+        conf = {'bridge': self.get_br_name(vif['id']),
+                'mac_address': vif['address']}
+
+        return conf
+
+    def get_config_ovs_bridge(self, instance, vif):
+        conf = {'bridge': self.get_bridge_name(vif),
+                'mac_address': vif['address']}
+
+        return conf
+
+    def get_config_ovs(self, instance, vif):
+        if self.get_firewall_required(vif) or vif.is_hybrid_plug_enabled():
+            return self.get_config_ovs_hybrid(instance, vif)
+        else:
+            return self.get_config_ovs_bridge(instance, vif)
+
+    def plug(self, instance, vif):
+        vif_type = vif['type']
+
+        LOG.debug('vif_type=%(vif_type)s instance=%(instance)s '
+                  'vif=%(vif)s',
+                  {'vif_type': vif_type, 'instance': instance,
+                   'vif': vif})
+
+        if vif_type is None:
+            raise exception.NovaException(
+                _("vif_type parameter must be present "
+                  "for this vif_driver implementation"))
+        func = getattr(self, 'plug_%s' % vif_type, None)
+        if not func:
+            raise exception.NovaException(
+                _("Unexpected vif_type=%s") % vif_type)
+        return func(instance, vif)
+
+    def plug_bridge(self, instance, vif):
+        network = vif['network']
+        if (not network.get_meta('multi_host', False) and
+                network.get_meta('should_create_bridge', False)):
+            if network.get_meta('should_create_vlan', False):
+                iface = (CONF.vlan_interface or
+                         network.get_meta('bridge_interface'))
+                LOG.debug('Ensuring vlan %(vlan)s and bridge %(bridge)s',
+                          {'vlan': network.get_meta('vlan'),
+                           'bridge': self.get_bridge_name(vif)},
+                          instance=instance)
+                linux_net.LinuxBridgeInterfaceDriver.ensure_vlan_bridge(
+                    network.get_meta('vlan'),
+                    self.get_bridge_name(vif), iface)
+            else:
+                iface = (CONF.flat_interface or
+                         network.get_meta('bridge_interface'))
+                LOG.debug("Ensuring bridge %s",
+                          self.get_bridge_name(vif), instance=instance)
+                linux_net.LinuxBridgeInterfaceDriver.ensure_bridge(
+                    self.get_bridge_name(vif), iface)
+
+    def plug_ovs(self, instance, vif):
+        if self.get_firewall_required(vif) or vif.is_hybrid_plug_enabled():
+            self.plug_ovs_hybrid(instance, vif)
+        else:
+            self.plug_ovs_bridge(instance, vif)
+
+    def plug_ovs_bridge(self, instance, vif):
+        pass
+
+    def plug_ovs_hybrid(self, instance, vif):
+        iface_id = self.get_ovs_interfaceid(vif)
+        br_name = self.get_br_name(vif['id'])
+        v1_name, v2_name = self.get_veth_pair_names(vif['id'])
+
+        if not linux_net.device_exists(br_name):
+            utils.execute('brctl', 'addbr', br_name, run_as_root=True)
+            utils.execute('brctl', 'setfd', br_name, 0, run_as_root=True)
+            utils.execute('brctl', 'stp', br_name, 'off', run_as_root=True)
+            utils.execute('tee',
+                          ('/sys/class/net/%s/bridge/multicast_snooping' %
+                           br_name),
+                          process_input='0',
+                          run_as_root=True,
+                          check_exit_code=[0, 1])
+
+        if not linux_net.device_exists(v2_name):
+            linux_net._create_veth_pair(v1_name, v2_name)
+            utils.execute('ip', 'link', 'set', br_name, 'up', run_as_root=True)
+            utils.execute('brctl', 'addif', br_name, v1_name, run_as_root=True)
+            linux_net.create_ovs_vif_port(self.get_bridge_name(vif),
+                                          v2_name, iface_id,
+                                          vif['address'], instance.name)
+
+    def unplug(self, instance, vif):
+        vif_type = vif['type']
+
+        LOG.debug('vif_type=%(vif_type)s instance=%(instance)s '
+                  'vif=%(vif)s',
+                  {'vif_type': vif_type, 'instance': instance,
+                   'vif': vif})
+
+        if vif_type is None:
+            raise exception.NovaException(
+                _("vif_type parameter must be present "
+                  "for this vif_driver implementation"))
+        func = getattr(self, 'unplug_%s' % vif_type, None)
+        if not func:
+            raise exception.NovaException(
+                _("Unexpected vif_type=%s") % vif_type)
+        return func(instance, vif)
+
+    def unplug_ovs(self, instance, vif):
+        if self.get_firewall_required(vif) or vif.is_hybrid_plug_enabled():
+            self.unplug_ovs_hybrid(instance, vif)
+        else:
+            self.unplug_ovs_bridge(instance, vif)
+
+    def unplug_ovs_hybrid(self, instance, vif):
+        try:
+            br_name = self.get_br_name(vif['id'])
+            v1_name, v2_name = self.get_veth_pair_names(vif['id'])
+
+            if linux_net.device_exists(br_name):
+                utils.execute('brctl', 'delif', br_name, v1_name,
+                              run_as_root=True)
+                utils.execute('ip', 'link', 'set', br_name, 'down',
+                              run_as_root=True)
+                utils.execute('brctl', 'delbr', br_name,
+                              run_as_root=True)
+
+                linux_net.delete_ovs_vif_port(self.get_bridge_name(vif),
+                                              v2_name)
+        except processutils.ProcessExecutionError:
+            LOG.exception(_LE("Failed while unplugging vif"),
+                          instance=instance)
+
+    def unplug_ovs_bridge(self, instance, vif):
+        pass
+
+    def unplug_bridge(self, instance, vif):
+        pass
diff --git a/nova_lxd/__init__.py b/nova_lxd/__init__.py
deleted file mode 100644
index ceba8b6..0000000
--- a/nova_lxd/__init__.py
+++ /dev/null
@@ -1,5 +0,0 @@
-__import__('pkg_resources').declare_namespace(__name__)
-
-import os
-
-os.environ['EVENTLET_NO_GREENDNS'] = 'yes'
diff --git a/nova_lxd/nova/__init__.py b/nova_lxd/nova/__init__.py
deleted file mode 100644
index de40ea7..0000000
--- a/nova_lxd/nova/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-__import__('pkg_resources').declare_namespace(__name__)
diff --git a/nova_lxd/nova/virt/__init__.py b/nova_lxd/nova/virt/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/nova_lxd/nova/virt/lxd/__init__.py b/nova_lxd/nova/virt/lxd/__init__.py
deleted file mode 100644
index 64dafc6..0000000
--- a/nova_lxd/nova/virt/lxd/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from nova_lxd.nova.virt.lxd import driver
-
-LXDDriver = driver.LXDDriver
diff --git a/nova_lxd/nova/virt/lxd/config.py b/nova_lxd/nova/virt/lxd/config.py
deleted file mode 100644
index df7f31c..0000000
--- a/nova_lxd/nova/virt/lxd/config.py
+++ /dev/null
@@ -1,357 +0,0 @@
-# Copyright 2011 Justin Santa Barbara
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import socket
-
-from nova import exception
-from nova import i18n
-from nova.virt import configdrive
-
-from oslo_config import cfg
-from oslo_log import log as logging
-from oslo_utils import excutils
-
-from nova_lxd.nova.virt.lxd import session
-from nova_lxd.nova.virt.lxd import utils as container_dir
-from nova_lxd.nova.virt.lxd import vif
-
-_ = i18n._
-_LE = i18n._LE
-_LI = i18n._LI
-
-CONF = cfg.CONF
-CONF.import_opt('my_ip', 'nova.netconf')
-LOG = logging.getLogger(__name__)
-
-
-class LXDContainerConfig(object):
-    """LXD configuration methods."""
-
-    def __init__(self):
-        self.container_dir = container_dir.LXDContainerDirectories()
-        self.session = session.LXDAPISession()
-        self.vif_driver = vif.LXDGenericDriver()
-
-    def create_container(self, instance):
-        """Create a LXD container dictionary so that we can
-           use it to initialize a container
-
-           :param instance: nova instance object
-        """
-        LOG.debug('create_container called for instance', instance=instance)
-
-        instance_name = instance.name
-        try:
-
-            # Fetch the container configuration from the current nova
-            # instance object
-            container_config = {
-                'name': instance_name,
-                'profiles': [str(instance.name)],
-                'source': self.get_container_source(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():
-                LOG.error('Failed to get container configuration'
-                          ' %(instance)s: %(ex)s',
-                          {'instance': instance_name, 'ex': ex},
-                          instance=instance)
-
-    def create_profile(self, instance, network_info):
-        """Create a LXD container profile configuration
-
-        :param instance: nova instance object
-        :param network_info: nova network configuration object
-        :return: LXD container profile dictionary
-        """
-        LOG.debug('create_container_profile called for instance',
-                  instance=instance)
-        instance_name = instance.name
-        try:
-            config = {}
-            config['name'] = str(instance_name)
-            config['config'] = self.create_config(instance_name, instance)
-
-            # Restrict the size of the "/" disk
-            config['devices'] = self.configure_container_root(instance)
-
-            if network_info:
-                config['devices'].update(self.create_network(instance_name,
-                                                             instance,
-                                                             network_info))
-
-            return config
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(
-                    _LE('Failed to create profile %(instance)s: %(ex)s'),
-                    {'instance': instance_name, 'ex': ex}, instance=instance)
-
-    def create_config(self, instance_name, instance):
-        """Create the LXD container resources
-
-        :param instance_name: instance name
-        :param instance: nova instance object
-        :return: LXD resources dictionary
-        """
-        LOG.debug('create_config called for instance', instance=instance)
-        try:
-            config = {}
-
-            # Update continaer options
-            config.update(self.config_instance_options(config, instance))
-
-            # Set the instance memory limit
-            mem = instance.memory_mb
-            if mem >= 0:
-                config['limits.memory'] = '%sMB' % mem
-
-            # Set the instance vcpu limit
-            vcpus = instance.flavor.vcpus
-            if vcpus >= 0:
-                config['limits.cpu'] = str(vcpus)
-
-            # Configure the console for the instance
-            config['raw.lxc'] = 'lxc.console.logfile=%s\n' \
-                % self.container_dir.get_console_path(instance_name)
-
-            return config
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(
-                    _LE('Failed to set container resources %(instance)s: '
-                        '%(ex)s'), {'instance': instance_name, 'ex': ex},
-                    instance=instance)
-
-    def config_instance_options(self, config, instance):
-        LOG.debug('config_instance_options called for instance',
-                  instance=instance)
-
-        # Set the container to autostart when the host reboots
-        config['boot.autostart'] = 'True'
-
-        # Determine if we require a nested container
-        flavor = instance.flavor
-        lxd_nested_allowed = flavor.extra_specs.get(
-            'lxd:nested_allowed', False)
-        if lxd_nested_allowed:
-            config['security.nesting'] = 'True'
-
-        # Determine if we require a privileged container
-        lxd_privileged_allowed = flavor.extra_specs.get(
-            'lxd:privileged_allowed', False)
-        if lxd_privileged_allowed:
-            config['security.privileged'] = 'True'
-
-        return config
-
-    def configure_container_root(self, instance):
-        LOG.debug('configure_container_root called for instance',
-                  instance=instance)
-        try:
-            config = {}
-            lxd_config = self.session.get_host_config(instance)
-            if str(lxd_config['storage']) in ['btrfs', 'zfs']:
-                config['root'] = {'path': '/',
-                                  'type': 'disk',
-                                  'size': '%sGB' % str(instance.root_gb)}
-            else:
-                config['root'] = {'path': '/',
-                                  'type': 'disk'}
-            return config
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Failed to configure disk for '
-                              '%(instance)s: %(ex)s'),
-                          {'instance': instance.name, 'ex': ex},
-                          instance=instance)
-
-    def create_network(self, instance_name, instance, network_info):
-        """Create the LXD container network on the host
-
-        :param instance_name: nova instance name
-        :param instance: nova instance object
-        :param network_info: instance network configuration object
-        :return:network configuration dictionary
-        """
-        LOG.debug('create_network called for instance', instance=instance)
-        try:
-            network_devices = {}
-
-            if not network_info:
-                return
-
-            for vifaddr in network_info:
-                cfg = self.vif_driver.get_config(instance, vifaddr)
-                network_devices[str(cfg['bridge'])] = \
-                    {'nictype': 'bridged',
-                     'hwaddr': str(cfg['mac_address']),
-                     'parent': str(cfg['bridge']),
-                     'type': 'nic'}
-                return network_devices
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(
-                    _LE('Fail to configure network for %(instance)s: %(ex)s'),
-                    {'instance': instance_name, 'ex': ex}, instance=instance)
-
-    def get_container_source(self, instance):
-        """Set the LXD container image for the instance.
-
-        :param instance: nova instance object
-        :return: the container source
-        """
-        LOG.debug('get_container_source called for instance',
-                  instance=instance)
-        try:
-            container_source = {'type': 'image',
-                                'alias': str(instance.image_ref)}
-            if container_source is None:
-                msg = _('Failed to determine container source for %s') \
-                    % instance.name
-                raise exception.NovaException(msg)
-            return container_source
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(
-                    _LE('Failed to configure container source '
-                        '%(instance)s: %(ex)s'),
-                    {'instance': instance.name, 'ex': ex},
-                    instance=instance)
-
-    def get_container_migrate(self, container_migrate, migration,
-                              host, instance):
-        LOG.debug('get_container_migrate called for instance',
-                  instance=instance)
-        try:
-            # Generate the container config
-            host = socket.gethostbyname(host)
-            container_metadata = container_migrate['metadata']
-            container_control = container_metadata['metadata']['control']
-            container_fs = container_metadata['metadata']['fs']
-
-            container_url = 'https://%s:8443%s' \
-                % (host, container_migrate.get('operation'))
-
-            container_migrate = {
-                'base_image': '',
-                'mode': 'pull',
-                'certificate': str(self.session.host_certificate(instance,
-                                                                 host)),
-                'operation': str(container_url),
-                'secrets': {
-                        'control': str(container_control),
-                        'fs': str(container_fs)
-                },
-                'type': 'migration'
-            }
-
-            return container_migrate
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Failed to configure migation source '
-                              '%(instance)s: %(ex)s'),
-                          {'instance': instance.name, 'ex': ex},
-                          instance=instance)
-
-    def configure_disk_path(self, src_path, dest_path, vfs_type, instance):
-        """Configure the host mount point for the LXD container
-
-        :param src_path: source path on the house
-        :param dest_path: destination path on the LXD container
-        :param vfs_type: dictionary identifier
-        :param instance: nova instance object
-        :return: container disk paths
-        """
-        LOG.debug('configure_disk_path called for instance',
-                  instance=instance)
-        try:
-            config = {}
-            config[vfs_type] = {'path': dest_path,
-                                'source': src_path,
-                                'type': 'disk',
-                                'optional': 'True'}
-            return config
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Failed to configure disk for '
-                              '%(instance)s: %(ex)s'),
-                          {'instance': instance.name, 'ex': ex},
-                          instance=instance)
-
-    def create_container_net_device(self, instance, vif):
-        """Translate nova network object into a LXD interface
-
-        :param instance: nova instance object
-        :param vif: network instaance object
-        """
-        LOG.debug('create_container_net_device called for instance',
-                  insance=instance)
-        try:
-            network_config = self.vif_driver.get_config(instance, vif)
-
-            config = {}
-            config[self.get_network_device(instance)] = {
-                'nictype': 'bridged',
-                'hwaddr': str(vif['address']),
-                'parent': str(network_config['bridge']),
-                'type': 'nic'}
-
-            return config
-        except Exception as ex:
-            LOG.error(_LE('Failed to configure network for '
-                          '%(instance)s: %(ex)s'),
-                      {'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:
-            return 'eth1'
-        else:
-            return 'eth%s' % int(len(interfaces) - 1)
diff --git a/nova_lxd/nova/virt/lxd/constants.py b/nova_lxd/nova/virt/lxd/constants.py
deleted file mode 100644
index 29dd485..0000000
--- a/nova_lxd/nova/virt/lxd/constants.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-
-from nova.compute import power_state
-
-LXD_POWER_STATES = {
-    100: power_state.RUNNING,
-    101: power_state.RUNNING,
-    102: power_state.SHUTDOWN,
-    103: power_state.RUNNING,
-    104: power_state.SHUTDOWN,
-    105: power_state.NOSTATE,
-    106: power_state.NOSTATE,
-    107: power_state.SHUTDOWN,
-    108: power_state.CRASHED,
-    109: power_state.SUSPENDED,
-    110: power_state.SUSPENDED,
-    111: power_state.SUSPENDED,
-    200: power_state.RUNNING,
-    400: power_state.CRASHED,
-    401: power_state.NOSTATE
-}
diff --git a/nova_lxd/nova/virt/lxd/container_firewall.py b/nova_lxd/nova/virt/lxd/container_firewall.py
deleted file mode 100644
index 9f3a11f..0000000
--- a/nova_lxd/nova/virt/lxd/container_firewall.py
+++ /dev/null
@@ -1,68 +0,0 @@
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from nova.virt import firewall
-
-from oslo_config import cfg
-from oslo_log import log as logging
-
-CONF = cfg.CONF
-LOG = logging.getLogger(__name__)
-
-
-class LXDContainerFirewall(object):
-
-    def __init__(self):
-        self.firewall_driver = firewall.load_driver(
-            default='nova.virt.firewall.NoopFirewallDriver')
-
-    def refresh_security_group_rules(self, security_group_id):
-        return (self.firewall_driver
-                .refresh_security_group_rules(security_group_id))
-
-    def refresh_security_group_members(self, security_group_id):
-        return (self.firewall_driver
-                .refresh_security_group_members(security_group_id))
-
-    def refresh_provider_fw_rules(self):
-        return self.firewall_driver.refresh_provider_fw_rules()
-
-    def refresh_instance_security_rules(self, instance):
-        return self.firewall_driver.refresh_instance_security_rules(instance)
-
-    def ensure_filtering_rules_for_instance(self, instance, network_info):
-        return (self.firewall_driver
-                .ensure_filtering_rules_for_instance(instance, network_info))
-
-    def filter_defer_apply_on(self):
-        return self.firewall_driver.filter_defer_apply_on()
-
-    def filter_defer_apply_off(self):
-        return self.firewall_driver.filter_defer_apply_off()
-
-    def unfilter_instance(self, instance, network_info):
-        return self.firewall_driver.unfilter_instance(instance, network_info)
-
-    def setup_basic_filtering(self, instance, network_info):
-        return self.firewall_driver.setup_basic_filtering(instance,
-                                                          network_info)
-
-    def prepare_instance_filter(self, instance, network_info):
-        return self.firewall_driver.prepare_instance_filter(instance,
-                                                            network_info)
-
-    def apply_instance_filter(self, instance, network_info):
-        return self.firewall_driver.apply_instance_filter(instance,
-                                                          network_info)
diff --git a/nova_lxd/nova/virt/lxd/container_snapshot.py b/nova_lxd/nova/virt/lxd/container_snapshot.py
deleted file mode 100644
index 8d885d6..0000000
--- a/nova_lxd/nova/virt/lxd/container_snapshot.py
+++ /dev/null
@@ -1,157 +0,0 @@
-# Copyright 2011 Justin Santa Barbara
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.from oslo_config import cfg
-
-from nova.compute import task_states
-from nova import exception
-from nova import i18n
-from nova import image
-import os
-
-from oslo_concurrency import lockutils
-from oslo_config import cfg
-from oslo_log import log as logging
-from oslo_utils import excutils
-
-from nova_lxd.nova.virt.lxd import session
-
-_ = i18n._
-_LE = i18n._LE
-
-CONF = cfg.CONF
-LOG = logging.getLogger(__name__)
-
-IMAGE_API = image.API()
-
-
-class LXDSnapshot(object):
-
-    def __init__(self):
-        self.session = session.LXDAPISession()
-        self.lock_path = str(os.path.join(CONF.instances_path, 'locks'))
-
-    def snapshot(self, context, instance, image_id, update_task_state):
-        """Create a LXD snapshot  of the instance
-
-           Steps involved in creating an LXD Snapshot:
-
-           1. Ensure the container exists
-           2. Stop the LXD container: LXD requires a container
-              to be stopped in or
-           3. Publish the container: Run the API equivalent to
-              'lxd publish container --alias <image_name>' to create
-              a snapshot and upload it to the local LXD image store.
-           4. Create an alias for the image: Create an alias so that
-              nova-lxd can re-use the image that was created.
-           5. Upload the image to glance so that it can bed on other
-              compute hosts.
-
-          :param context: nova security context
-          :param instance: nova instance object
-          :param image_id: glance image id
-        """
-        LOG.debug('snapshot called for instance', instance=instance)
-
-        try:
-            if not self.session.container_defined(instance.name, instance):
-                raise exception.InstanceNotFound(instance_id=instance.name)
-
-            with lockutils.lock(self.lock_path,
-                                lock_file_prefix=('lxd-snapshot-%s' %
-                                                  instance.name),
-                                external=True):
-
-                update_task_state(task_state=task_states.IMAGE_PENDING_UPLOAD)
-
-                # We have to stop the container before we can publish the
-                # image to the local store
-                self.session.container_stop(instance.name,
-                                            instance)
-                fingerprint = self._save_lxd_image(instance,
-                                                   image_id)
-                self.session.container_start(instance.name, instance)
-
-                update_task_state(task_state=task_states.IMAGE_UPLOADING,
-                                  expected_state=task_states.IMAGE_PENDING_UPLOAD)  # noqa
-                self._save_glance_image(context, instance, image_id,
-                                        fingerprint)
-        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 _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)
-            image_meta = {'name': snapshot['name'],
-                          'disk_format': 'raw'}
-            IMAGE_API.update(context, image_id, image_meta, data)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Failed to upload image to glance for '
-                              '%(instance)s:  %(ex)s'),
-                          {'instance': instance.name, 'ex': ex},
-                          instance=instance)
diff --git a/nova_lxd/nova/virt/lxd/driver.py b/nova_lxd/nova/virt/lxd/driver.py
deleted file mode 100644
index df9eb1b..0000000
--- a/nova_lxd/nova/virt/lxd/driver.py
+++ /dev/null
@@ -1,388 +0,0 @@
-# Copyright 2011 Justin Santa Barbara
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from __future__ import absolute_import
-
-from nova import exception
-from nova import i18n
-from nova.virt import driver
-import socket
-
-from oslo_config import cfg
-from oslo_log import log as logging
-
-
-from nova_lxd.nova.virt.lxd import container_firewall
-from nova_lxd.nova.virt.lxd import container_snapshot
-from nova_lxd.nova.virt.lxd import host
-from nova_lxd.nova.virt.lxd import migrate
-from nova_lxd.nova.virt.lxd import operations as container_ops
-from nova_lxd.nova.virt.lxd import vif as lxd_vif
-
-_ = i18n._
-
-lxd_opts = [
-    cfg.StrOpt('root_dir',
-               default='/var/lib/lxd/',
-               help='Default LXD directory'),
-    cfg.IntOpt('timeout',
-               default=-1,
-               help='Default LXD timeout'),
-    cfg.IntOpt('retry_interval',
-               default=2,
-               help='How often to retry in seconds when a'
-                    'request does conflict'),
-]
-
-CONF = cfg.CONF
-CONF.register_opts(lxd_opts, 'lxd')
-LOG = logging.getLogger(__name__)
-
-
-class LXDDriver(driver.ComputeDriver):
-
-    """LXD Lightervisor."""
-
-    capabilities = {
-        "has_imagecache": False,
-        "supports_recreate": False,
-        "supports_migrate_to_same_host": False,
-    }
-
-    def __init__(self, virtapi):
-        self.virtapi = virtapi
-
-        self.vif_driver = lxd_vif.LXDGenericDriver()
-
-        self.container_ops = container_ops.LXDContainerOperations(virtapi)
-        self.container_snapshot = container_snapshot.LXDSnapshot()
-        self.container_firewall = container_firewall.LXDContainerFirewall()
-        self.container_migrate = migrate.LXDContainerMigrate(virtapi)
-        self.host = host.LXDHost()
-
-    def init_host(self, host):
-        return self.host.init_host(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:
-            self.vif_driver.plug(instance, vif)
-
-    def unplug_vifs(self, instance, network_info):
-        """Unplug VIFs from networks."""
-        for vif in network_info:
-            try:
-                self.vif_driver.unplug(instance, vif)
-            except exception.NovaException:
-                pass
-
-    def estimate_instance_overhead(self, instance_info):
-        return {'memory_mb': 0}
-
-    def list_instances(self):
-        return self.container_ops.list_instances()
-
-    def list_instance_uuids(self):
-        raise NotImplementedError()
-
-    def spawn(self, context, instance, image_meta, injected_files,
-              admin_password, network_info=None, block_device_info=None):
-        self.container_ops.spawn(context, instance, image_meta,
-                                 injected_files, admin_password,
-                                 network_info, block_device_info)
-
-    def destroy(self, context, instance, network_info, block_device_info=None,
-                destroy_disks=True, migrate_data=None):
-        self.container_ops.destroy(context, instance, network_info,
-                                   block_device_info, destroy_disks,
-                                   migrate_data)
-
-    def cleanup(self, context, instance, network_info, block_device_info=None,
-                destroy_disks=True, migrate_data=None, destroy_vifs=True):
-        self.container_ops.cleanup(context, instance, network_info,
-                                   block_device_info, destroy_disks,
-                                   migrate_data, destroy_vifs)
-
-    def reboot(self, context, instance, network_info, reboot_type,
-               block_device_info=None, bad_volumes_callback=None):
-        self.container_ops.reboot(context, instance, network_info,
-                                  reboot_type, block_device_info,
-                                  bad_volumes_callback)
-
-    def get_console_output(self, context, instance):
-        return self.container_ops.get_console_output(context, instance)
-
-    def get_diagnostics(self, instance):
-        raise NotImplementedError()
-
-    def get_instance_diagnostics(self, instance):
-        raise NotImplementedError()
-
-    def get_all_bw_counters(self, instances):
-        raise NotImplementedError()
-
-    def get_all_volume_usage(self, context, compute_host_bdms):
-        raise NotImplementedError()
-
-    def get_host_ip_addr(self):
-        return self.host.get_host_ip_addr()
-
-    def attach_volume(self, context, connection_info, instance, mountpoint,
-                      disk_bus=None, device_type=None, encryption=None):
-        raise NotImplementedError()
-
-    def detach_volume(self, connection_info, instance, mountpoint,
-                      encryption=None):
-        raise NotImplementedError()
-
-    def attach_interface(self, instance, image_meta, vif):
-        return self.container_ops.container_attach_interface(instance,
-                                                             image_meta,
-                                                             vif)
-
-    def detach_interface(self, instance, vif):
-        return self.container_ops.container_detach_interface(instance, vif)
-
-    def migrate_disk_and_power_off(self, context, instance, dest,
-                                   flavor, network_info,
-                                   block_device_info=None,
-                                   timeout=0, retry_interval=0):
-        return self.container_migrate.migrate_disk_and_power_off(
-            context, instance, dest, flavor,
-            network_info, block_device_info, timeout,
-            retry_interval)
-
-    def snapshot(self, context, instance, image_id, update_task_state):
-        return self.container_snapshot.snapshot(context, instance, image_id,
-                                                update_task_state)
-
-    def post_interrupted_snapshot_cleanup(self, context, instance):
-        pass
-
-    def finish_migration(self, context, migration, instance, disk_info,
-                         network_info, image_meta, resize_instance,
-                         block_device_info=None, power_on=True):
-        return self.container_migrate.finish_migration(
-            context, migration, instance, disk_info,
-            network_info, image_meta, resize_instance,
-            block_device_info, power_on)
-
-    def confirm_migration(self, migration, instance, network_info):
-        return self.container_migrate.confirm_migration(migration,
-                                                        instance,
-                                                        network_info)
-
-    def finish_revert_migration(self, context, instance, network_info,
-                                block_device_info=None, power_on=True):
-        return self.container_migrate.finish_revert_migration(
-            context, instance, network_info, block_device_info,
-            power_on)
-
-    def pause(self, instance):
-        self.container_ops.pause(instance)
-
-    def unpause(self, instance):
-        self.container_ops.unpause(instance)
-
-    def suspend(self, context, instance):
-        self.container_ops.suspend(context, instance)
-
-    def resume(self, context, instance, network_info, block_device_info=None):
-        self.container_ops.resume(context, instance, network_info,
-                                  block_device_info)
-
-    def rescue(self, context, instance, network_info, image_meta,
-               rescue_password):
-        self.container_ops.rescue(context, instance, network_info,
-                                  image_meta, rescue_password)
-
-    def unrescue(self, instance, network_info):
-        return self.container_ops.unrescue(instance, network_info)
-
-    def power_off(self, instance, timeout=0, retry_interval=0):
-        self.container_ops.power_off(instance, timeout=0,
-                                     retry_interval=0)
-
-    def power_on(self, context, instance, network_info,
-                 block_device_info=None):
-        self.container_ops.power_on(context, instance, network_info,
-                                    block_device_info)
-
-    def soft_delete(self, instance):
-        raise NotImplementedError()
-
-    def get_available_resource(self, nodename):
-        return self.host.get_available_resource(nodename)
-
-    def pre_live_migration(self, context, instance, block_device_info,
-                           network_info, disk_info, migrate_data=None):
-        raise NotImplementedError()
-
-    def live_migration(self, context, instance, dest,
-                       post_method, recover_method, block_migration=False,
-                       migrate_data=None):
-        raise NotImplementedError()
-
-    def post_live_migration(self, context, instance, block_device_info,
-                            migrate_data=None):
-        raise NotImplementedError()
-
-    def post_live_migration_at_destination(self, context, instance,
-                                           network_info,
-                                           block_migration=False,
-                                           block_device_info=None):
-        raise NotImplementedError()
-
-    def check_instance_shared_storage_local(self, context, instance):
-        raise NotImplementedError()
-
-    def check_instance_shared_storage_remote(self, context, data):
-        raise NotImplementedError()
-
-    def check_instance_shared_storage_cleanup(self, context, data):
-        pass
-
-    def check_can_live_migrate_destination(self, context, instance,
-                                           src_compute_info, dst_compute_info,
-                                           block_migration=False,
-                                           disk_over_commit=False):
-        raise NotImplementedError()
-
-    def check_can_live_migrate_destination_cleanup(self, context,
-                                                   dest_check_data):
-        raise NotImplementedError()
-
-    def check_can_live_migrate_source(self, context, instance,
-                                      dest_check_data, block_device_info=None):
-        raise NotImplementedError()
-
-    def get_instance_disk_info(self, instance,
-                               block_device_info=None):
-        raise NotImplementedError()
-
-    def refresh_security_group_rules(self, security_group_id):
-        return (self.container_firewall
-                .refresh_security_group_rules(security_group_id))
-
-    def refresh_security_group_members(self, security_group_id):
-        return (self.container_firewall
-                .refresh_security_group_members(security_group_id))
-
-    def refresh_provider_fw_rules(self):
-        return self.container_firewall.refresh_provider_fw_rules()
-
-    def refresh_instance_security_rules(self, instance):
-        return (self.container_firewall
-                .refresh_instance_security_rules(instance))
-
-    def ensure_filtering_rules_for_instance(self, instance, network_info):
-        return (self.container_firewall
-                .ensure_filtering_rules_for_instance(instance, network_info))
-
-    def filter_defer_apply_on(self):
-        return self.container_firewall.filter_defer_apply_on()
-
-    def filter_defer_apply_off(self):
-        return self.container_firewall.filter_defer_apply_off()
-
-    def unfilter_instance(self, instance, network_info):
-        return self.container_firewall.unfilter_instance(instance,
-                                                         network_info)
-
-    def poll_rebooting_instances(self, timeout, instances):
-        raise NotImplementedError()
-
-    def host_power_action(self, action):
-        raise NotImplementedError()
-
-    def host_maintenance_mode(self, host, mode):
-        raise NotImplementedError()
-
-    def set_host_enabled(self, enabled):
-        raise NotImplementedError()
-
-    def get_host_uptime(self):
-        return self.host.get_host_uptime()
-
-    def get_host_cpu_stats(self):
-        return self.host.get_host_cpu_stats()
-
-    def block_stats(self, instance, disk_id):
-        raise NotImplementedError()
-
-    def deallocate_networks_on_reschedule(self, instance):
-        """Does the driver want networks deallocated on reschedule?"""
-        return False
-
-    def macs_for_instance(self, instance):
-        return None
-
-    def manage_image_cache(self, context, all_instances):
-        pass
-
-    def add_to_aggregate(self, context, aggregate, host, **kwargs):
-        raise NotImplementedError()
-
-    def remove_from_aggregate(self, context, aggregate, host, **kwargs):
-        raise NotImplementedError()
-
-    def undo_aggregate_operation(self, context, op, aggregate,
-                                 host, set_error=True):
-        raise NotImplementedError()
-
-    def get_volume_connector(self, instance):
-        return {'ip': CONF.my_block_storage_ip,
-                'initiator': 'fake',
-                'host': 'fakehost'}
-
-    def get_available_nodes(self, refresh=False):
-        hostname = socket.gethostname()
-        return [hostname]
-
-    def node_is_available(self, nodename):
-        if nodename in self.get_available_nodes():
-            return True
-        # Refresh and check again.
-        return nodename in self.get_available_nodes(refresh=True)
-
-    def get_per_instance_usage(self):
-        return {}
-
-    def instance_on_disk(self, instance):
-        return False
-
-    def volume_snapshot_create(self, context, instance, volume_id,
-                               create_info):
-        raise NotImplementedError()
-
-    def volume_snapshot_delete(self, context, instance, volume_id,
-                               snapshot_id, delete_info):
-        raise NotImplementedError()
-
-    def quiesce(self, context, instance, image_meta):
-        raise NotImplementedError()
-
-    def unquiesce(self, context, instance, image_meta):
-        raise NotImplementedError()
diff --git a/nova_lxd/nova/virt/lxd/host.py b/nova_lxd/nova/virt/lxd/host.py
deleted file mode 100644
index 587539c..0000000
--- a/nova_lxd/nova/virt/lxd/host.py
+++ /dev/null
@@ -1,202 +0,0 @@
-# Copyright 2010 United States Government as represented by the
-# Administrator of the National Aeronautics and Space Administration.
-# Copyright (c) 2010 Citrix Systems, Inc.
-# Copyright 2011 Justin Santa Barbara
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-
-from nova.compute import arch
-from nova.compute import hv_type
-from nova.compute import utils as compute_utils
-from nova.compute import vm_mode
-from nova import exception
-from nova import i18n
-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
-from oslo_log import log as logging
-from oslo_serialization import jsonutils
-from oslo_utils import units
-import psutil
-
-_ = i18n._
-_LW = i18n._LW
-CONF = cfg.CONF
-LOG = logging.getLogger(__name__)
-
-
-class LXDHost(object):
-
-    def __init__(self):
-        self.lxd = api.API()
-
-    def get_available_resource(self, nodename):
-        LOG.debug('In get_available_resource')
-
-        local_cpu_info = self._get_cpuinfo()
-        cpu_topology = local_cpu_info['topology']
-        vcpus = (int(cpu_topology['cores']) *
-                 int(cpu_topology['sockets']) *
-                 int(cpu_topology['threads']))
-
-        local_memory_info = self._get_memory_mb_usage()
-        local_disk_info = self._get_fs_info(CONF.lxd.root_dir)
-
-        data = {
-            'vcpus': vcpus,
-            'memory_mb': local_memory_info['total'] / units.Mi,
-            'memory_mb_used': local_memory_info['used'] / units.Mi,
-            'local_gb': local_disk_info['total'] / units.Gi,
-            'local_gb_used': local_disk_info['used'] / units.Gi,
-            'vcpus_used': 0,
-            'hypervisor_type': 'lxd',
-            'hypervisor_version': '011',
-            'cpu_info': jsonutils.dumps(local_cpu_info),
-            'hypervisor_hostname': socket.gethostname(),
-            'supported_instances':
-                [(arch.I686, hv_type.LXD, vm_mode.EXE),
-                    (arch.X86_64, hv_type.LXD, vm_mode.EXE),
-                    (arch.I686, hv_type.LXC, vm_mode.EXE),
-                    (arch.X86_64, hv_type.LXC, vm_mode.EXE)],
-            'numa_topology': None,
-        }
-
-        return data
-
-    def get_host_ip_addr(self):
-        ips = compute_utils.get_machine_ips()
-        if CONF.my_ip not in ips:
-            LOG.warn(_LW('my_ip address (%(my_ip)s) was not found on '
-                         'any of the interfaces: %(ifaces)s'),
-                     {'my_ip': CONF.my_ip, 'ifaces': ", ".join(ips)})
-        return CONF.my_ip
-
-    def get_host_uptime(self):
-        out, err = utils.execute('env', 'LANG=C', 'uptime')
-        return out
-
-    def _get_fs_info(self, path):
-        """Get free/used/total space info for a filesystem
-
-        :param path: Any dirent on the filesystem
-        :returns: A dict containing
-              :free: How much space is free (in bytes)
-              :used: How much space is used (in bytes)
-              :total: How big the filesytem is (in bytes)
-        """
-        hddinfo = os.statvfs(path)
-        total = hddinfo.f_blocks * hddinfo.f_bsize
-        available = hddinfo.f_bavail * hddinfo.f_bsize
-        used = total - available
-        return {'total': total,
-                'available': available,
-                'used': used}
-
-    def _get_memory_mb_usage(self):
-        """Get the used memory size(MB) of the host.
-
-        :returns: the total usage of memory(MB)
-        """
-
-        with open('/proc/meminfo') as fp:
-            m = fp.read().split()
-            idx1 = m.index('MemTotal:')
-            idx2 = m.index('MemFree:')
-            idx3 = m.index('Buffers:')
-            idx4 = m.index('Cached:')
-
-            total = int(m[idx1 + 1])
-            avail = int(m[idx2 + 1]) + int(m[idx3 + 1]) + int(m[idx4 + 1])
-
-        return {
-            'total': total * 1024,
-            'used': (total - avail) * 1024
-        }
-
-    def _get_cpuinfo(self):
-        cpuinfo = self._get_cpu_info()
-
-        cpu_info = dict()
-
-        cpu_info['arch'] = platform.uname()[5]
-        cpu_info['model'] = cpuinfo.get('model name', 'unknown')
-        cpu_info['vendor'] = cpuinfo.get('vendor id', 'unknown')
-
-        topology = dict()
-        topology['sockets'] = cpuinfo.get('socket(s)', 1)
-        topology['cores'] = cpuinfo.get('core(s) per socket', 1)
-        topology['threads'] = cpuinfo.get('thread(s) per core', 1)
-        cpu_info['topology'] = topology
-        cpu_info['features'] = cpuinfo.get('flags', 'unknown')
-
-        return cpu_info
-
-    def _get_cpu_info(self):
-        '''Parse the output of lscpu.'''
-        cpuinfo = {}
-        out, err = utils.execute('lscpu')
-        if err:
-            msg = _('Unable to parse lscpu output.')
-            raise exception.NovaException(msg)
-
-        cpu = [line.strip('\n') for line in out.splitlines()]
-        for line in cpu:
-            if line.strip():
-                name, value = line.split(':', 1)
-                name = name.strip().lower()
-                cpuinfo[name] = value.strip()
-
-        f = open('/proc/cpuinfo', 'r')
-        features = [line.strip('\n') for line in f.readlines()]
-        for line in features:
-            if line.strip():
-                if line.startswith('flags'):
-                    name, value = line.split(':', 1)
-                    name = name.strip().lower()
-                    cpuinfo[name] = value.strip()
-
-        return cpuinfo
-
-    def _get_hypersivor_version(self):
-        version = self.lxd.get_lxd_version()
-        return '.'.join(str(v) for v in version)
-
-    def get_host_cpu_stats(self):
-        cpuinfo = self._get_cpu_info()
-        return {
-            'kernel': int(psutil.cpu_times()[2]),
-            'idle': int(psutil.cpu_times()[3]),
-            'user': int(psutil.cpu_times()[0]),
-            'iowait': int(psutil.cpu_times()[4]),
-            'frequency': cpuinfo.get('cpu mhz', 0)
-        }
-
-    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)
-
-            return True
-        except lxd_exceptions.APIError as ex:
-            msg = _('Unable to connect to LXD daemon: %s') % ex
-            raise exception.HostNotFound(msg)
diff --git a/nova_lxd/nova/virt/lxd/image.py b/nova_lxd/nova/virt/lxd/image.py
deleted file mode 100644
index 9988adb..0000000
--- a/nova_lxd/nova/virt/lxd/image.py
+++ /dev/null
@@ -1,293 +0,0 @@
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import hashlib
-import io
-import json
-from nova.compute import arch
-from nova import exception
-from nova import i18n
-from nova import image
-from nova import utils
-import os
-import shutil
-import tarfile
-import tempfile
-import uuid
-
-from oslo_concurrency import lockutils
-from oslo_concurrency import processutils
-from oslo_config import cfg
-from oslo_log import log as logging
-from oslo_utils import excutils
-from oslo_utils import fileutils
-
-from nova_lxd.nova.virt.lxd import session
-from nova_lxd.nova.virt.lxd import utils as container_dir
-
-_ = i18n._
-_LE = i18n._LE
-
-CONF = cfg.CONF
-LOG = logging.getLogger(__name__)
-IMAGE_API = image.API()
-
-
-class LXDContainerImage(object):
-    """Upload an image from glance to the local LXD image store."""
-
-    def __init__(self):
-        self.client = session.LXDAPISession()
-        self.container_dir = container_dir.LXDContainerDirectories()
-        self.lock_path = str(os.path.join(CONF.instances_path, 'locks'))
-
-        self.container_image = None
-        self.container_manifest = None
-
-    def setup_image(self, context, instance, image_meta):
-        """Download an image from glance and upload it to LXD
-
-        :param context: context object
-        :param instance: The nova instance
-        :param image_meta: Image dict returned by nova.image.glance
-        """
-        LOG.debug('setup_image called for instance', instance=instance)
-
-        self.container_image = \
-            self.container_dir.get_container_rootfs_image(image_meta)
-        self.container_manifest = \
-            self.container_dir.get_container_manifest_image(image_meta)
-
-        with lockutils.lock(self.lock_path,
-                            lock_file_prefix=('lxd-image-%s' %
-                                              instance.image_ref),
-                            external=True):
-
-            if self.client.image_defined(instance):
-                return
-
-            base_dir = self.container_dir.get_base_dir()
-            if not os.path.exists(base_dir):
-                fileutils.ensure_tree(base_dir)
-
-            try:
-                # Inspect image for the correct format
-                self._verify_image(context, instance)
-
-                # Fetch the image from glance
-                self._fetch_image(context, instance)
-
-                # Generate the LXD manifest for the image
-                self._get_lxd_manifest(instance, image_meta)
-
-                # Upload the image to the local LXD image store
-                self._image_upload(instance)
-
-                # Setup the LXD alias for the image
-                self._setup_alias(instance)
-
-                # Remove image and manifest when done.
-                self._cleanup_image(instance)
-
-            except Exception as ex:
-                with excutils.save_and_reraise_exception():
-                    LOG.error(_LE('Failed to upload %(image)s to LXD: '
-                                  '%(reason)s'),
-                              {'image': instance.image_ref,
-                               'reason': ex}, instance=instance)
-                    self._cleanup_image(instance)
-
-    def _verify_image(self, context, instance):
-        """Inspect image to verify the correct disk format.
-
-          Inspect and verify and the image that will downloaded
-          from glance is the correct image. The image must be in
-          a raw disk format in order for the LXD daemon to import
-          it into the local image store.
-
-          :param context: nova security context
-          ;param instance: nova instance object
-        """
-        LOG.debug('_verify_image called for instance', instance=instance)
-        try:
-            # grab the disk format of the image
-            img_meta = IMAGE_API.get(context, instance.image_ref)
-            disk_format = img_meta.get('disk_format')
-            if not disk_format:
-                reason = _('Bad image format')
-                raise exception.ImageUnacceptable(image_id=instance.image_ref,
-                                                  reason=reason)
-
-            if disk_format not in ['raw', 'root-tar']:
-                reason = _('nova-lxd does not support images in %s format. '
-                           'You should upload an image in raw or root-tar '
-                           'format.') % disk_format
-                raise exception.ImageUnacceptable(image_id=instance.image_ref,
-                                                  reason=reason)
-        except Exception as ex:
-            reason = _('Bad Image format: %(ex)s') \
-                % {'ex': ex}
-            raise exception.ImageUnacceptable(image_id=instance.image_ref,
-                                              reason=reason)
-
-    def _fetch_image(self, context, instance):
-        """Fetch an image from glance
-
-        :param context: nova security object
-        :param instance: the nova instance object
-
-        """
-        LOG.debug('_fetch_image called for instance', instance=instance)
-        with fileutils.remove_path_on_error(self.container_image):
-            IMAGE_API.download(context, instance.image_ref,
-                               dest_path=self.container_image)
-
-    def _get_lxd_manifest(self, instance, image_meta):
-        """Creates the LXD manifest, needed for split images
-
-        :param instance: nova instance
-        :param image_meta: image metadata dictionary
-
-        """
-        LOG.debug('_get_lxd_manifest called for instance', instance=instance)
-
-        metadata_yaml = None
-        try:
-            # Create a basic LXD manifest from the image properties
-            image_arch = image_meta.properties.get('hw_architecture')
-            if image_arch is None:
-                image_arch = arch.from_host()
-            metadata = {
-                'architecture': image_arch,
-                'creation_date': int(os.stat(self.container_image).st_ctime)
-            }
-
-            metadata_yaml = (json.dumps(metadata, sort_keys=True,
-                                        indent=4, separators=(',', ': '),
-                                        ensure_ascii=False).encode('utf-8')
-                             + b"\n")
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Failed to generate manifest for %(image)s: '
-                              '%(reason)s'),
-                          {'image': instance.name, 'ex': ex},
-                          instance=instance)
-        try:
-            # Compress the manifest using tar
-            target_tarball = tarfile.open(self.container_manifest, "w:")
-            metadata_file = tarfile.TarInfo()
-            metadata_file.size = len(metadata_yaml)
-            metadata_file.name = "metadata.yaml"
-            target_tarball.addfile(metadata_file,
-                                   io.BytesIO(metadata_yaml))
-            target_tarball.close()
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Failed to generate manifest tarball for'
-                              ' %(image)s: %(reason)s'),
-                          {'image': instance.name, 'ex': ex},
-                          instance=instance)
-
-        try:
-            # Compress the manifest further using xz
-            with fileutils.remove_path_on_error(self.container_manifest):
-                utils.execute('xz', '-9', self.container_manifest,
-                              check_exit_code=[0, 1])
-        except processutils.ProcessExecutionError as ex:
-            with excutils.save_and_reraise_exception:
-                LOG.error(_LE('Failed to compress manifest for %(image)s:'
-                              ' %(ex)s'), {'image': instance.image_ref,
-                                           'ex': ex}, instance=instance)
-
-    def _image_upload(self, instance):
-        """Upload an image to the LXD image store
-
-        We create the LXD manifest on the fly since glance does
-        not understand how to talk to Glance.
-
-        :param instance: nova instance
-
-        """
-        LOG.debug('image_upload called for instance', instance=instance)
-        headers = {}
-
-        boundary = str(uuid.uuid1())
-
-        # Create the binary blob to upload the file to LXD
-        tmpdir = tempfile.mkdtemp()
-        upload_path = os.path.join(tmpdir, "upload")
-        body = open(upload_path, 'wb+')
-
-        for name, path in [("metadata", (self.container_manifest + '.xz')),
-                           ("rootfs", self.container_image)]:
-            filename = os.path.basename(path)
-            body.write(bytearray("--%s\r\n" % boundary, "utf-8"))
-            body.write(bytearray("Content-Disposition: form-data; "
-                                 "name=%s; filename=%s\r\n" %
-                                 (name, filename), "utf-8"))
-            body.write("Content-Type: application/octet-stream\r\n")
-            body.write("\r\n")
-            with open(path, "rb") as fd:
-                shutil.copyfileobj(fd, body)
-            body.write("\r\n")
-
-        body.write(bytearray("--%s--\r\n" % boundary, "utf-8"))
-        body.write('\r\n')
-        body.close()
-
-        headers['Content-Type'] = "multipart/form-data; boundary=%s" \
-            % boundary
-
-        # Upload the file to LXD and then remove the tmpdir.
-        self.client.image_upload(data=open(upload_path, 'rb'),
-                                 headers=headers, instance=instance)
-        shutil.rmtree(tmpdir)
-
-    def _setup_alias(self, instance):
-        """Creates the LXD alias for the image
-
-        :param instance: nova instance
-        """
-        LOG.debug('_setup_alias called for instance', instance=instance)
-
-        try:
-            with open((self.container_manifest + '.xz'), 'rb') as meta_fd:
-                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)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception:
-                LOG.error(_LE('Failed to setup alias for %(image)s:'
-                              ' %(ex)s'), {'image': instance.image_ref,
-                                           'ex': ex}, instance=instance)
-
-    def _cleanup_image(self, instance):
-        """Cleanup the remaning bits of the glance/lxd interaction
-
-        :params image_meta: image_meta dictionary
-
-        """
-        LOG.debug('_cleanup_image called for instance', instance=instance)
-
-        if os.path.exists(self.container_image):
-            os.unlink(self.container_image)
-
-        if os.path.exists(self.container_manifest):
-            os.unlink(self.container_manifest)
diff --git a/nova_lxd/nova/virt/lxd/migrate.py b/nova_lxd/nova/virt/lxd/migrate.py
deleted file mode 100644
index d780ce7..0000000
--- a/nova_lxd/nova/virt/lxd/migrate.py
+++ /dev/null
@@ -1,169 +0,0 @@
-# Copyright 2016 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import os
-
-from nova import exception
-from nova import i18n
-from nova import utils
-from nova.virt import configdrive
-
-from oslo_config import cfg
-from oslo_log import log as logging
-from oslo_utils import excutils
-from oslo_utils import fileutils
-
-from nova_lxd.nova.virt.lxd import config
-from nova_lxd.nova.virt.lxd import operations
-from nova_lxd.nova.virt.lxd import utils as container_dir
-from nova_lxd.nova.virt.lxd import session
-
-
-_ = i18n._
-_LE = i18n._LE
-_LI = i18n._LI
-
-CONF = cfg.CONF
-CONF.import_opt('my_ip', 'nova.netconf')
-LOG = logging.getLogger(__name__)
-
-
-class LXDContainerMigrate(object):
-
-    def __init__(self, virtapi):
-        self.virtapi = virtapi
-        self.config = config.LXDContainerConfig()
-        self.container_dir = container_dir.LXDContainerDirectories()
-        self.session = session.LXDAPISession()
-        self.operations = \
-            operations.LXDContainerOperations(
-                self.virtapi)
-
-    def migrate_disk_and_power_off(self, context, instance, dest,
-                                   flavor, network_info,
-                                   block_device_info=None, timeout=0,
-                                   retry_interval=0):
-        LOG.debug("migrate_disk_and_power_off called", instance=instance)
-
-        same_host = False
-        if CONF.my_ip == dest:
-            same_host = True
-            LOG.debug('Migration target is the source host')
-        else:
-            LOG.debug('Migration target host: %s' % dest)
-
-        if not self.session.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)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('failed to resize container '
-                              '%(instance)s: %(ex)s'),
-                          {'instance': instance.name, 'ex': ex},
-                          instance=instance)
-
-        # disk_info is not used
-        return ""
-
-    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)
-
-    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
-            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)
-
-    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
diff --git a/nova_lxd/nova/virt/lxd/operations.py b/nova_lxd/nova/virt/lxd/operations.py
deleted file mode 100644
index 639eee5..0000000
--- a/nova_lxd/nova/virt/lxd/operations.py
+++ /dev/null
@@ -1,678 +0,0 @@
-# Copyright 2011 Justin Santa Barbara
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-
-from nova.api.metadata import base as instance_metadata
-from nova.virt import configdrive
-from nova.virt import hardware
-import os
-import pwd
-import shutil
-
-from oslo_config import cfg
-from oslo_log import log as logging
-from oslo_utils import excutils
-from oslo_utils import fileutils
-from oslo_utils import units
-
-from nova import exception
-from nova import i18n
-from nova import utils
-from nova.compute import power_state
-
-from nova_lxd.nova.virt.lxd import config as container_config
-from nova_lxd.nova.virt.lxd import container_firewall
-from nova_lxd.nova.virt.lxd import image
-from nova_lxd.nova.virt.lxd import session
-from nova_lxd.nova.virt.lxd import utils as container_dir
-from nova_lxd.nova.virt.lxd import vif
-
-_ = i18n._
-_LE = i18n._LE
-_LW = i18n._LW
-_LI = i18n._LI
-
-CONF = cfg.CONF
-CONF.import_opt('vif_plugging_timeout', 'nova.virt.driver')
-CONF.import_opt('vif_plugging_is_fatal', 'nova.virt.driver')
-LOG = logging.getLogger(__name__)
-
-MAX_CONSOLE_BYTES = 100 * units.Ki
-
-
-class LXDContainerOperations(object):
-    """LXD container operations."""
-
-    def __init__(self, virtapi):
-        self.virtapi = virtapi
-
-        self.config = container_config.LXDContainerConfig()
-        self.container_dir = container_dir.LXDContainerDirectories()
-        self.image = image.LXDContainerImage()
-        self.firewall_driver = container_firewall.LXDContainerFirewall()
-        self.session = session.LXDAPISession()
-
-        self.vif_driver = vif.LXDGenericDriver()
-        self.instance_dir = None
-
-    def list_instances(self):
-        return self.session.container_list()
-
-    def spawn(self, context, instance, image_meta, injected_files,
-              admin_password=None, network_info=None, block_device_info=None):
-        """Start the LXD container
-
-        Once this successfully completes, the instance should be
-        running (power_state.RUNNING).
-
-        If this fails, any partial instance should be completely
-        cleaned up, and the virtualization platform should be in the state
-        that it was before this call began.
-
-        :param context: security context
-        :param instance: nova.objects.instance.Instance
-                         This function should use the data there to guide
-                         the creation of the new instance.
-        :param image_meta: image object returned by nova.image.glance that
-                           defines the image from which to boot this instance
-        :param injected_files: User files to inject into instance.
-        :param admin_password: Administrator password to set in instance.
-        :param network_info:
-            :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info`
-        :param block_device_info: Information about block devices to be
-                                  attached to the instance
-        """
-        msg = ('Spawning container '
-               'network_info=%(network_info)s '
-               'image_meta=%(image_meta)s '
-               'instance=%(instance)s '
-               'block_device_info=%(block_device_info)s' %
-               {'network_info': network_info,
-                'instance': instance,
-                'image_meta': image_meta,
-                'block_device_info': block_device_info})
-        LOG.debug(msg, instance=instance)
-
-        instance_name = instance.name
-
-        if self.session.container_defined(instance_name, instance):
-            raise exception.InstanceExists(name=instance.name)
-
-        try:
-
-            # Ensure that the instance directory exists
-            self.instance_dir = \
-                self.container_dir.get_instance_dir(instance_name)
-            if not os.path.exists(self.instance_dir):
-                fileutils.ensure_tree(self.instance_dir)
-
-            # Step 1 - Fetch the image from glance
-            self._fetch_image(context, instance, image_meta)
-
-            # Step 2 - Setup the container network
-            self._setup_network(instance_name, instance, network_info)
-
-            # Step 3 - Create the container profile
-            self._setup_profile(instance_name, instance, network_info)
-
-            # Step 4 - Create a config drive (optional)
-            if configdrive.required_by(instance):
-                self._add_configdrive(instance, injected_files)
-
-            # Step 5 - Configure and start the container
-            self._setup_container(instance_name, instance)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Faild to start container '
-                              '%(instance)s: %(ex)s'),
-                          {'instance': instance.name, 'ex': ex},
-                          instance=instance)
-                self.destroy(context, instance, network_info)
-
-    def _fetch_image(self, context, instance, image_meta):
-        """Fetch the LXD image from glance
-
-        :param context: nova security context
-        :param instance: nova instance object
-        :param image_meta: nova image opbject
-        """
-        LOG.debug('_fetch_image called for instance', instance=instance)
-        try:
-            # Download the image from glance and upload the image
-            # to the local LXD image store.
-            self.image.setup_image(context, instance, image_meta)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Upload image failed for %(instance)s '
-                              'for %(image)s: %(e)s'),
-                          {'instance': instance.name,
-                           'image': instance.image_ref,
-                           'ex': ex}, instance=instance)
-
-    def _setup_network(self, instance_name, instance, network_info):
-        """Setup the network when creating the lXD container
-
-        :param instance_name: nova instance name
-        :param instance: nova instance object
-        :param network_info: instance network configuration
-        """
-        LOG.debug('_setup_network called for instance', instance=instance)
-        try:
-            self.plug_vifs(instance, network_info)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Failed to create container network for '
-                              '%(instance)s: %(ex)s'),
-                          {'instance': instance_name, 'ex': ex},
-                          instance=instance)
-
-    def _setup_profile(self, instance_name, instance, network_info):
-        """Create an LXD container profile for the nova intsance
-
-        :param instance_name: nova instance name
-        :param instance: nova instance object
-        :param network_info: nova instance netowkr configuration
-        """
-        LOG.debug('_setup_profile called for instance', instance=instance)
-        try:
-            # Setup the container profile based on the nova
-            # instance object and network objects
-            container_profile = self.config.create_profile(instance,
-                                                           network_info)
-            self.session.profile_create(container_profile, instance)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Failed to create a profile for'
-                              ' %(instance)s: %(ex)s'),
-                          {'instance': instance_name,
-                           'ex': ex}, instance=instance)
-
-    def _setup_container(self, instance_name, instance):
-        """Create and start the LXD container.
-
-        :param instance_name: nova instjace name
-        :param instance: nova instance object
-        """
-        LOG.debug('_setup_container called for instance', instance=instance)
-        try:
-            # Create the container
-            container_config = \
-                self.config.create_container(instance)
-            self.session.container_init(
-                container_config, instance)
-
-            # Start the container
-            self.session.container_start(instance_name, instance)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.exception(_LE('Container creation failed for '
-                                  '%(instance)s: %(ex)s'),
-                              {'instance': instance.name,
-                               'ex': ex}, instance=instance)
-
-    def _add_configdrive(self, instance, injected_files):
-        """Configure the config drive for the container
-
-        :param instance: nova instance object
-        :param injected_files: instance injected files
-        """
-        LOG.debug('add_configdrive called for instance', instance=instance)
-
-        extra_md = {}
-        inst_md = instance_metadata.InstanceMetadata(instance,
-                                                     content=injected_files,
-                                                     extra_md=extra_md)
-        # Create the ISO image so we can inject the contents of the ISO
-        # into the container
-        iso_path = os.path.join(self.instance_dir, 'configdirve.iso')
-        with configdrive.ConfigDriveBuilder(instance_md=inst_md) as cdb:
-            try:
-                cdb.make_drive(iso_path)
-            except Exception as e:
-                with excutils.save_and_reraise_exception():
-                    LOG.error(_LE('Creating config drive failed with error: '
-                                  '%s'), e, instance=instance)
-
-        # Copy the metadata info from the ISO into the container
-        configdrive_dir = \
-            self.container_dir.get_container_configdrive(instance.name)
-        with utils.tempdir() as tmpdir:
-            mounted = False
-            try:
-                _, err = utils.execute('mount',
-                                       '-o',
-                                       'loop,uid=%d,gid=%d' % (os.getuid(),
-                                                               os.getgid()),
-                                       iso_path, tmpdir,
-                                       run_as_root=True)
-                mounted = True
-
-                # Copy and adjust the files from the ISO so that we
-                # dont have the ISO mounted during the life cycle of the
-                # instance and the directory can be removed once the instance
-                # is terminated
-                for ent in os.listdir(tmpdir):
-                    shutil.copytree(os.path.join(tmpdir, ent),
-                                    os.path.join(configdrive_dir, ent))
-                utils.execute('chmod', '-R', '775', configdrive_dir,
-                              run_as_root=True)
-                utils.execute('chown', '-R', '%s:%s'
-                              % (self._uid_map('/etc/subuid').rstrip(),
-                                 self._uid_map('/etc/subgid').rstrip()),
-                              configdrive_dir, run_as_root=True)
-            finally:
-                if mounted:
-                    utils.execute('umount', tmpdir, run_as_root=True)
-
-    def reboot(self, context, instance, network_info, reboot_type,
-               block_device_info=None, bad_volumes_callback=None):
-        """Reboot a instance on a LXD host
-
-        :param instance: nova.objects.instance.Instance
-        :param network_info:
-           :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info`
-        :param reboot_type: Either a HARD or SOFT reboot
-        :param block_device_info: Info pertaining to attached volumes
-        :param bad_volumes_callback: Function to handle any bad volumes
-            encountered
-        """
-        LOG.debug('reboot called for instance', instance=instance)
-        try:
-            self.session.container_reboot(instance)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.exception(_LE('Container reboot failed for '
-                                  '%(instance)s: %(ex)s'),
-                              {'instance': instance.name,
-                               'ex': ex}, instance=instance)
-
-    def plug_vifs(self, instance, network_info):
-        """Setup the container network on the host
-
-         :param instance: nova instance object
-         :param network_info: instance network configuration
-         """
-        LOG.debug('plug_vifs called for instance', instance=instance)
-        try:
-            for viface in network_info:
-                self.vif_driver.plug(instance, viface)
-            self.start_firewall(instance, network_info)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Failed to configure container network'
-                              ' for %(instance)s: %(ex)s'),
-                          {'instance': instance.name, 'ex': ex},
-                          instance=instance)
-
-    def unplug_vifs(self, instance, network_info):
-        """Unconfigure the LXD container network
-
-           :param instance: nova intance object
-           :param network_info: instance network confiugration
-        """
-        try:
-            for viface in network_info:
-                self.vif_driver.unplug(instance, viface)
-            self.stop_firewall(instance, network_info)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Failed to remove container network'
-                              ' for %(instance)s: %(ex)s'),
-                          {'instance': instance.name, 'ex': ex},
-                          instance=instance)
-
-    def destroy(self, context, instance, network_info, block_device_info=None,
-                destroy_disks=True, migrate_data=None):
-        """Destroy the instance on the LXD host
-
-        :param context: security context
-        :param instance: Instance object as returned by DB layer.
-        :param network_info:
-           :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info`
-        :param block_device_info: Information about block devices that should
-                                  be detached from the instance.
-        :param destroy_disks: Indicates if disks should be destroyed
-        :param migrate_data: implementation specific params
-        """
-        LOG.debug('destroy called for instance', instance=instance)
-        try:
-            self.session.profile_delete(instance)
-            self.session.container_destroy(instance.name,
-                                           instance)
-            self.cleanup(context, instance, network_info, block_device_info)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Failed to remove container'
-                              ' for %(instance)s: %(ex)s'),
-                          {'instance': instance.name, 'ex': ex},
-                          instance=instance)
-
-    def power_off(self, instance, timeout=0, retry_interval=0):
-        """Power off an instance
-
-        :param instance: nova.objects.instance.Instance
-        :param timeout: time to wait for GuestOS to shutdown
-        :param retry_interval: How often to signal guest while
-                               waiting for it to shutdown
-        """
-        LOG.debug('power_off called for instance', instance=instance)
-        try:
-            self.session.container_stop(instance.name,
-                                        instance)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Failed to power_off container'
-                              ' for %(instance)s: %(ex)s'),
-                          {'instance': instance.name, 'ex': ex},
-                          instance=instance)
-
-    def power_on(self, context, instance, network_info,
-                 block_device_info=None):
-        """Power on instance
-
-        :param instance: nova.objects.instance.Instance
-        """
-        LOG.debug('power_on called for instance', instance=instance)
-        try:
-            self.session.container_start(instance.name, instance)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.exception(_LE('Container power off for '
-                                  '%(instance)s: %(ex)s'),
-                              {'instance': instance.name,
-                               'ex': ex}, instance=instance)
-
-    def pause(self, instance):
-        """Pause an instance
-
-        :param nova.objects.instance.Instance instance:
-            The instance which should be paused.
-        """
-        LOG.debug('pause called for instance', instance=instance)
-        try:
-            self.session.container_pause(instance.name, instance)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Failed to pause container'
-                              ' for %(instance)s: %(ex)s'),
-                          {'instance': instance.name, 'ex': ex},
-                          instance=instance)
-
-    def unpause(self, instance):
-        """Unpause an instance
-
-        :param nova.objects.instance.Instance instance:
-            The instance which should be paused.
-        """
-        LOG.debug('unpause called for instance', instance=instance)
-        try:
-            self.session.container_unpause(instance.name, instance)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Failed to unpause container'
-                              ' for %(instance)s: %(ex)s'),
-                          {'instance': instance.name, 'ex': ex},
-                          instance=instance)
-
-    def suspend(self, context, instance):
-        """Suspend an instance
-
-        :param context: nova security context
-        :param nova.objects.instance.Instance instance:
-            The instance which should be paused.
-        """
-        LOG.debug('suspend called for instance', instance=instance)
-        try:
-            self.session.container_pause(instance.name, instance)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.exception(_LE('Container suspend failed for '
-                                  '%(instance)s: %(ex)s'),
-                              {'instance': instance.name,
-                               'ex': ex}, instance=instance)
-
-    def resume(self, context, instance, network_info, block_device_info=None):
-        """Resume an instance on an LXD host
-
-        :param nova.context.RequestContext context:
-            The context for the resume.
-        :param nova.objects.instance.Instance instance:
-            The suspended instance to resume.
-        :param nova.network.model.NetworkInfo network_info:
-            Necessary network information for the resume.
-        :param dict block_device_info:
-            Instance volume block device info.
-        """
-        LOG.debug('resume called for instance', instance=instance)
-        try:
-            self.session.container_unpause(instance.name, instance)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Failed to resume container'
-                              ' for %(instance)s: %(ex)s'),
-                          {'instance': instance.name, 'ex': ex},
-                          instance=instance)
-
-    def rescue(self, context, instance, network_info, image_meta,
-               rescue_password):
-        """Rescue an instance
-
-        :param instance: nova.objects.instance.Instance
-        """
-        LOG.debug('rescue called for instance', instance=instance)
-        try:
-            if not self.session.container_defined(instance.name, instance):
-                msg = _('Unable to find instance')
-                raise exception.NovaException(msg)
-
-            # 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},
-                                        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_init(container_config, instance)
-
-            # Step 4 - Start the rescue instance
-            self.session.container_start(instance.name, instance)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.exception(_LE('Container rescue failed for '
-                                  '%(instance)s: %(ex)s'),
-                              {'instance': instance.name,
-                               'ex': ex}, instance=instance)
-
-    def unrescue(self, instance, network_info):
-        """Unrescue a LXD host
-
-        :param instance: nova instance object
-        :param network_info: nova network configuration
-        """
-        LOG.debug('unrescue called for instance', instance=instance)
-        try:
-            if not self.session.container_defined(instance.name, instance):
-                msg = _('Unable to find instance')
-                raise exception.NovaException(msg)
-
-            # Step 1 - Destory the rescue instance.
-            self.session.container_destroy(instance.name,
-                                           instance)
-
-            # Step 2 - Rename the backup container that
-            #          the user was working on.
-            self.session.container_move(instance.name + '-backup',
-                                        {'name': instance.name},
-                                        instance)
-
-            # Step 3 - Start the old contianer
-            self.session.container_start(instance.name, instance)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.exception(_LE('Container unrescue failed for '
-                                  '%(instance)s: %(ex)s'),
-                              {'instance': instance.name,
-                               'ex': ex}, instance=instance)
-
-    def cleanup(self, context, instance, network_info, block_device_info=None,
-                destroy_disks=True, migrate_data=None, destroy_vifs=True):
-        """Cleanup a contianer after its been deleted.
-
-        :param context: security context
-        :param instance: Instance object as returned by DB layer.
-        :param network_info:
-           :py:meth:`~nova.network.manager.NetworkManager.get_instance_nw_info`
-        :param block_device_info: Information about block devices that should
-                                  be detached from the instance.
-        :param destroy_disks: Indicates if disks should be destroyed
-        :param migrate_data: implementation specific params
-        """
-        LOG.debug('cleanup called for instance', instance=instance)
-        try:
-            if destroy_vifs:
-                self.unplug_vifs(instance, network_info)
-
-            name = pwd.getpwuid(os.getuid()).pw_name
-            configdrive_dir = \
-                self.container_dir.get_container_configdrive(instance.name)
-            if os.path.exists(configdrive_dir):
-                utils.execute('chown', '-R', '%s:%s' % (name, name),
-                              configdrive_dir, run_as_root=True)
-                shutil.rmtree(configdrive_dir)
-
-            container_dir = self.container_dir.get_instance_dir(instance.name)
-            if os.path.exists(container_dir):
-                shutil.rmtree(container_dir)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.exception(_LE('Container cleanup failed for '
-                                  '%(instance)s: %(ex)s'),
-                              {'instance': instance.name,
-                               'ex': ex}, instance=instance)
-
-    def get_info(self, instance):
-        """Get the current status of an instance, by name (not ID!)
-
-        :param instance: nova.objects.instance.Instance object
-
-        Returns a InstanceInfo object
-        """
-        LOG.debug('get_info called for instance', instance=instance)
-        try:
-            if not self.session.container_defined(instance.name, instance):
-                return hardware.InstanceInfo(state=power_state.NOSTATE)
-
-            container_state = self.session.container_state(instance)
-            return hardware.InstanceInfo(state=container_state['state'],
-                                         max_mem_kb=container_state['max_mem'],
-                                         mem_kb=container_state['mem'],
-                                         num_cpu=instance.flavor.vcpus,
-                                         cpu_time_ns=0)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Failed to get container info'
-                              ' for %(instance)s: %(ex)s'),
-                          {'instance': instance.name, 'ex': ex},
-                          instance=instance)
-
-    def get_console_output(self, context, instance):
-        """Get console output for an instance
-        :param context: security context
-        :param instance: nova.objects.instance.Instance
-        """
-        LOG.debug('get_console_output called for instance', instance=instance)
-        try:
-            console_log = self.container_dir.get_console_path(instance.name)
-            if not os.path.exists(console_log):
-                return ""
-            uid = pwd.getpwuid(os.getuid()).pw_uid
-            utils.execute('chown', '%s:%s' % (uid, uid),
-                          console_log, run_as_root=True)
-            utils.execute('chmod', '755',
-                          os.path.join(
-                              self.container_dir.get_container_dir(
-                                  instance.name), instance.name),
-                          run_as_root=True)
-            with open(console_log, 'rb') as fp:
-                log_data, remaning = utils.last_bytes(fp,
-                                                      MAX_CONSOLE_BYTES)
-                return log_data
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Failed to get container output'
-                              ' for %(instance)s: %(ex)s'),
-                          {'instance': instance.name, 'ex': ex},
-                          instance=instance)
-
-    def container_attach_interface(self, instance, image_meta, vif):
-        LOG.debug('container_attach_interface called for instance',
-                  instance=instance)
-        try:
-            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)
-        except Exception as ex:
-            with excutils.save_and_reraise_exception():
-                self.vif_driver.unplug(instance, vif)
-                LOG.error(_LE('Failed to configure network'
-                              ' for %(instance)s: %(ex)s'),
-                          {'instance': instance.name, 'ex': ex},
-                          instance=instance)
-
-    def container_detach_interface(self, instance, vif):
-        LOG.debug('container_defatch_interface called for instance',
-                  instance=instance)
-        try:
-            self.vif_driver.unplug(instance, vif)
-        except exception.NovaException:
-            pass
-
-    def start_firewall(self, instance, network_info):
-        self.firewall_driver.setup_basic_filtering(instance, network_info)
-        self.firewall_driver.prepare_instance_filter(instance, network_info)
-        self.firewall_driver.apply_instance_filter(instance, network_info)
-
-    def stop_firewall(self, instance, network_info):
-        self.firewall_driver.unfilter_instance(instance, network_info)
-
-    def _uid_map(self, subuid_f):
-        LOG.debug('Checking for subuid')
-
-        line = None
-        with open(subuid_f, 'r') as fp:
-            name = pwd.getpwuid(os.getuid()).pw_name
-            for cline in fp:
-                if cline.startswith(name + ":"):
-                    line = cline
-                    break
-            if line is None:
-                raise ValueError("%s not found in %s" % (name, subuid_f))
-            toks = line.split(":")
-            return toks[1]
diff --git a/nova_lxd/nova/virt/lxd/session.py b/nova_lxd/nova/virt/lxd/session.py
deleted file mode 100644
index 46738c9..0000000
--- a/nova_lxd/nova/virt/lxd/session.py
+++ /dev/null
@@ -1,1026 +0,0 @@
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
-#    the License for the specific language governing permissions and
-#    limitations under the License.
-
-from nova import context as nova_context
-from nova import exception
-from nova import i18n
-from nova import rpc
-from nova import utils
-from nova.compute import power_state
-
-from oslo_concurrency import processutils
-from oslo_config import cfg
-from oslo_log import log as logging
-from oslo_service import loopingcall
-from oslo_utils import excutils
-
-from pylxd.deprecated import api
-from pylxd.deprecated import exceptions as lxd_exceptions
-import six
-
-from nova_lxd.nova.virt.lxd import constants
-
-_ = i18n._
-_LE = i18n._LE
-_LI = i18n._LI
-
-CONF = cfg.CONF
-CONF.import_opt('host', 'nova.netconf')
-LOG = logging.getLogger(__name__)
-
-
-def mount_filesystem(self, dev_path, dir_path):
-    try:
-        _out, err = utils.execute('mount',
-                                  '-t', 'ext4',
-                                  dev_path, dir_path, run_as_root=True)
-    except processutils.ProcessExecutionError as e:
-        err = six.text_type(e)
-    return err
-
-
-def umount_filesystem(self, dir_path):
-    try:
-        _out, err = utils.execute('umount',
-                                  dir_path, run_as_root=True)
-    except processutils.ProcessExecutionError as e:
-        err = six.text_type(e)
-    return err
-
-
-class LXDAPISession(object):
-    """The session to invoke the LXD API session."""
-
-    def __init__(self):
-        super(LXDAPISession, self).__init__()
-
-    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
-    #
-
-    def container_list(self):
-        """List of containers running on a given host
-
-        Returns a list of running containers
-
-        """
-        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)
-        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_update(self, config, instance):
-        """Update the LXD configuration of a given container
-
-        :param config: LXD configuration dictionary
-        :param instance: nova instance object
-        :return: an update LXD configuration dictionary
-
-        """
-        LOG.debug('container_update called fo instance', instance=instance)
-        try:
-            client = self.get_session()
-            if not self.container_defined(instance.name, instance):
-                msg = _('Instance is not found: %s') % instance.name
-                raise exception.InstanceNotFound(msg)
-
-            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)
-        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_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
-
-        :param instance: nova instance object
-        :return: nova power_state
-
-        """
-        LOG.debug('container_state called for instance', instance=instance)
-        try:
-            mem = 0
-            max_mem = 0
-
-            client = self.get_session()
-            if not self.container_defined(instance.name, instance):
-                return
-
-            (state, data) = client.container_state(instance.name)
-            state = constants.LXD_POWER_STATES[data['metadata']['status_code']]
-
-            container_state = self.container_info(instance)
-            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:
-            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}
-
-    def container_config(self, instance):
-        """Fetches the configuration of a given LXD container
-
-        :param instance: nova instance object
-        :return: dictionary represenation of a LXD container
-
-        """
-        LOG.debug('container_config called for instance', instance=instance)
-        try:
-            if not self.container_defined(instance.name, instance):
-                msg = _('Instance is not found %s') % instance.name
-                raise exception.InstanceNotFound(msg)
-
-            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)
-        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)
-
-    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:
-            if not self.container_defined(instance.name, instance):
-                msg = _('Instance is not found %s') % instance.name
-                raise exception.InstanceNotFound(msg)
-
-            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
-
-        :param instance_name: container anme
-        :param instance: nova instance opbject
-        :return: True if exists otherwise False
-
-        """
-        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:
-                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)
-
-    def container_start(self, instance_name, instance):
-        """Start an LXD container
-
-        :param instance_name: name of container
-        :param instance: nova instance object
-
-        """
-        LOG.debug('container_start called for instance', instance=instance)
-        try:
-            LOG.info(_LI('Starting instance %(instance)s with '
-                         '%(image)s'), {'instance': instance.name,
-                                        'image': instance.image_ref})
-            # Start the container
-            client = self.get_session()
-
-            # (chuck): Something wicked could happen between
-            # container
-            if not self.container_defined(instance_name, instance):
-                msg = _('Instance is not found %s ') % instance.name
-                raise exception.InstanceNotFound(msg)
-
-            (state, data) = client.container_start(instance_name,
-                                                   CONF.lxd.timeout)
-            self.operation_wait(data.get('operation'), instance)
-
-            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)
-        except Exception as ex:
-            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
-
-        :param instance_name: instance name
-        :param instance: nova instance object
-
-        """
-        LOG.debug('container_stop called for instance', instance=instance)
-        try:
-            if not self.container_defined(instance_name, instance):
-                msg = _('Instance is not found %s') % instance.name
-                raise exception.InstanceNotFound(msg)
-
-            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)
-
-            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(
-                    _LE('Failed to stop container %(instance)s: '
-                        '%(reason)s'), {'instance': instance_name,
-                                        'reason': ex})
-
-    def container_reboot(self, instance):
-        """Reboot a LXD container
-
-        :param instance: nova instance object
-
-        """
-        LOG.debug('container_reboot called for instance', instance=instance)
-        try:
-            if not self.container_defined(instance.name, instance):
-                msg = _('Instance is not found %s') % instance.name
-                raise exception.InstanceNotFound(msg)
-
-            LOG.info(_LI('Rebooting instance %(instance)s with'
-                         ' %(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)
-
-            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():
-                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
-
-        :param instance_name: container name
-        :param instance: nova instance object
-
-        """
-        LOG.debug('container_destroy for instance', instance=instance)
-        try:
-            if not self.container_defined(instance_name, instance):
-                return
-
-            LOG.info(_LI('Destroying instance %(instance)s with'
-                         ' %(image)s'), {'instance': instance.name,
-                                         'image': instance.image_ref})
-
-            # 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)
-
-            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: '
-                              '%(reason)s'), {'instance': instance_name,
-                                              'reason': ex})
-
-    def container_pause(self, instance_name, instance):
-        """Pause a LXD container
-
-        :param instance_name: container name
-        :param instance: nova instance object
-
-        """
-        LOG.debug('container_paused called for instance', instance=instance)
-        try:
-            if not self.container_defined(instance_name, instance):
-                msg = _('Instance is not found %s') % instance_name
-                raise exception.InstanceNotFound(msg)
-
-            LOG.info(_LI('Pausing instance %(instance)s with'
-                         ' %(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)
-
-            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(
-                    _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
-
-        :param instance_name: container name
-        :param instance: nova instance object
-
-        """
-        LOG.debug('container_unpause called for instance', instance=instance)
-        try:
-            if not self.container_defined(instance_name, instance):
-                msg = _('Instance is not found %s') % instance_name
-                raise exception.InstanceNotFound(msg)
-
-            LOG.info(_LI('Unpausing instance %(instance)s with'
-                         ' %(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)
-
-            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(
-                    _LE('Failed to unpause container %(instance)s: '
-                        '%(reason)s'), {'instance': instance_name,
-                                        'reason': ex})
-
-    def container_init(self, config, instance):
-        """Create a LXD container
-
-        :param config: LXD container config as a dict
-        :param instance: nova instance object
-
-        """
-        LOG.debug('container_init called for instance', instance=instance)
-        try:
-            LOG.info(_LI('Creating container %(instance)s with'
-                         ' %(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:
-                raise exception.NovaException(data['metadata'])
-
-            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(
-                    _LE('Failed to create container %(instance)s: %(reason)s'),
-                    {'instance': instance.name,
-                     'reason': ex}, instance=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
-        """
-        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)
-        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)
-
-    def create_alias(self, alias, instance):
-        """Creates an alias for a given image
-
-        :param alias: The alias to be crerated
-        :param instance: The nove instance
-        :return: true if alias is created, false otherwise
-
-        """
-        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)
-        except Exception as e:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Error from LXD during create alias'
-                              '%(instance)s: %(reason)s'),
-                          {'instance': instance.image_ref, 'reason': e},
-                          instance=instance)
-
-    def image_upload(self, data, headers, instance):
-        """Upload an image to the local LXD image store
-
-        :param data: image data
-        :param headers: image headers
-        :param instance: The nova 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)
-        except Exception as e:
-            with excutils.save_and_reraise_exception():
-                LOG.error(_LE('Error from LXD during image upload'
-                              '%(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)
-
-    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
-    #
-    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
-
-           :param instance: nova instance object
-        """
-        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:
-                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})
-
-    def profile_create(self, config, instance):
-        """Create an LXD container profile
-
-        :param config: profile dictionary
-        :param instance: nova instance object
-        """
-        LOG.debug('profile_create called for instance',
-                  instance=instance)
-        try:
-            if self.profile_defined(instance.name, instance):
-                msg = _('Profile already exists %(instnce)s') % \
-                    {'instance': instance.name}
-                raise exception.NovaException(msg)
-
-            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(
-                    _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 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 update profile %(instance)s: '
-                        '%(reason)s'),
-                    {'instance': instance.name, 'reason': ex})
-
-    def profile_delete(self, instance):
-        """Delete a LXD container profile.
-
-           :param instance: nova instance object
-        """
-        LOG.debug('profile_delete called for instance', instance=instance)
-        try:
-            if not self.profile_defined(instance.name, instance):
-                return
-
-            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(
-                    _LE('Failed to delete profile %(instance)s: %(reason)s'),
-                    {'instance': instance.name, 'reason': ex})
-
-    #
-    # Host Methods
-    #
-    def host_certificate(self, instance, host):
-        LOG.debug('host_certificate called for instance', instance=instance)
-        try:
-            client = self.get_session(host)
-            return client.get_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)
-        try:
-            client = self.get_session()
-            return client.host_config()['environment']
-        except lxd_exceptions.APIError as ex:
-            msg = _('Failed to communicate with LXD %(instance)s:'
-                    ' %(reason)s') % {'instance': instance.name,
-                                      'ex': ex}
-            LOG.error(msg)
-
-    #
-    # Migrate methods
-    #
-    def container_migrate(self, instance_name, host, instance):
-        """Initialize a container migration for LXD
-
-        :param instance_name: container name
-        :param host: host to move container from
-        :param instance: nova instance object
-        :return: dictionary of the container keys
-
-        """
-        LOG.debug('container_migrate called for instance', instance=instance)
-        try:
-            LOG.info(_LI('Migrating instance %(instance)s with '
-                         '%(image)s'), {'instance': instance_name,
-                                        'image': instance.image_ref})
-
-            client = self.get_session(host)
-            (state, data) = client.container_migrate(instance_name)
-
-            LOG.info(_LI('Successfully initialized migration for instance '
-                         '%(instance)s with %(image)s'),
-                     {'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(
-                    _LE('Failed to migrate container %(instance)s: %('
-                        'reason)s'), {'instance': instance.name,
-                                      'reason': ex}, instance=instance)
-
-    #
-    # Snapshot methods
-    #
-
-    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 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 move container %(instance)s: '
-                        '%(reason)s'),
-                    {'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
-
-        :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)
-        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 publish container %(instance)s: '
-                        '%(reason)s'),
-                    {'instance': instance.name,
-                     'reason': ex}, instance=instance)
-
-    def container_export(self, image, instance):
-        """
-        Export an image from the local LXD image store into
-        an file.
-
-        :param image: image dictionary
-        :param instance: nova instance object
-        """
-        LOG.debug('container_export called for instance', instance=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(
-                    _LE('Failed to export container %(instance)s: '
-                        '%(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)
diff --git a/nova_lxd/nova/virt/lxd/utils.py b/nova_lxd/nova/virt/lxd/utils.py
deleted file mode 100644
index 7ad822f..0000000
--- a/nova_lxd/nova/virt/lxd/utils.py
+++ /dev/null
@@ -1,93 +0,0 @@
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import os
-
-from oslo_config import cfg
-
-CONF = cfg.CONF
-
-
-class LXDContainerDirectories(object):
-
-    def __init__(self):
-        self.base_dir = os.path.join(CONF.instances_path,
-                                     CONF.image_cache_subdirectory_name)
-
-    def get_base_dir(self):
-        return self.base_dir
-
-    def get_instance_dir(self, instance):
-        return os.path.join(CONF.instances_path,
-                            instance)
-
-    def get_container_rootfs_image(self, image_meta):
-        return os.path.join(self.base_dir,
-                            '%s-rootfs.tar.gz' % image_meta.id)
-
-    def get_container_manifest_image(self, image_meta):
-        return os.path.join(self.base_dir,
-                            '%s-manifest.tar' % image_meta.id)
-
-    def get_container_metadata(self, image_meta):
-        return os.path.join(self.base_dir,
-                            '%s-lxd.tar.xz' % image_meta.id)
-
-    def get_container_rootfsImg(self, image_meta):
-        return os.path.join(self.base_dir,
-                            '%s-root.tar.gz' % image_meta.id)
-
-    def get_container_configdrive(self, instance):
-        return os.path.join(CONF.instances_path,
-                            instance,
-                            'configdrive')
-
-    def get_console_path(self, instance):
-        return os.path.join('/var/log/lxd/',
-                            instance,
-                            'console.log')
-
-    def get_container_dir(self, instance):
-        return os.path.join(CONF.lxd.root_dir,
-                            'containers')
-
-    def get_container_rootfs(self, instance):
-        return os.path.join(CONF.lxd.root_dir,
-                            'containers',
-                            instance,
-                            'rootfs')
-
-    def get_container_rescue(self, instance):
-        if self.is_lvm(instance):
-            return os.path.join(CONF.lxd.root_dir,
-                                'containers',
-                                instance)
-        else:
-            return os.path.join(CONF.lxd.root_dir,
-                                'containers',
-                                instance,
-                                'rootfs')
-
-    def get_container_lvm(self, instance):
-        return '%s/%s.lv' % (self.get_container_dir(instance),
-                             instance)
-
-    def is_lvm(self, instance):
-        try:
-            if os.path.exists(os.readlink(
-                self.get_container_lvm(instance))):
-                return True
-        except Exception:
-            return False
diff --git a/nova_lxd/nova/virt/lxd/vif.py b/nova_lxd/nova/virt/lxd/vif.py
deleted file mode 100644
index deada73..0000000
--- a/nova_lxd/nova/virt/lxd/vif.py
+++ /dev/null
@@ -1,223 +0,0 @@
-# Copyright (c) 2015 Canonical Ltd
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-from oslo_concurrency import processutils
-from oslo_config import cfg
-from oslo_log import log as logging
-
-from nova import exception
-from nova import i18n
-from nova.network import linux_net
-from nova.network import model as network_model
-from nova import utils
-
-_ = i18n._
-_LE = i18n._LE
-
-CONF = cfg.CONF
-
-LOG = logging.getLogger(__name__)
-
-
-class LXDGenericDriver(object):
-
-    def get_vif_devname(self, vif):
-        if 'devname' in vif:
-            return vif['devname']
-        return ("nic" + vif['id'])[:network_model.NIC_NAME_LEN]
-
-    def get_vif_devname_with_prefix(self, vif, prefix):
-        devname = self.get_vif_devname(vif)
-        return prefix + devname[3:]
-
-    def get_bridge_name(self, vif):
-        return vif['network']['bridge']
-
-    def get_ovs_interfaceid(self, vif):
-        return vif.get('ovs_interfaceid') or vif['id']
-
-    def get_br_name(self, iface_id):
-        return ("qbr" + iface_id)[:network_model.NIC_NAME_LEN]
-
-    def get_veth_pair_names(self, iface_id):
-        return (("qvb%s" % iface_id)[:network_model.NIC_NAME_LEN],
-                ("qvo%s" % iface_id)[:network_model.NIC_NAME_LEN])
-
-    def get_firewall_required(self, vif):
-        if CONF.firewall_driver != "nova.virt.firewall.NoopFirewallDriver":
-            return True
-        return False
-
-    def get_config(self, instance, vif):
-        vif_type = vif['type']
-
-        LOG.debug('vif_type=%(vif_type)s instance=%(instance)s '
-                  'vif=%(vif)s',
-                  {'vif_type': vif_type, 'instance': instance,
-                   'vif': vif})
-
-        if vif_type is None:
-            raise exception.NovaException(
-                _("vif_type parameter must be present "
-                  "for this vif_driver implementation"))
-        func = getattr(self, 'get_config_%s' % vif_type, None)
-        if not func:
-            raise exception.NovaException(
-                _("Unexpected vif_type=%s") % vif_type)
-        return func(instance, vif)
-
-    def get_config_bridge(self, instance, vif):
-        conf = {'bridge': self.get_bridge_name(vif),
-                'mac_address': vif['address']}
-        return conf
-
-    def get_config_ovs_hybrid(self, instance, vif):
-        conf = {'bridge': self.get_br_name(vif['id']),
-                'mac_address': vif['address']}
-
-        return conf
-
-    def get_config_ovs_bridge(self, instance, vif):
-        conf = {'bridge': self.get_bridge_name(vif),
-                'mac_address': vif['address']}
-
-        return conf
-
-    def get_config_ovs(self, instance, vif):
-        if self.get_firewall_required(vif) or vif.is_hybrid_plug_enabled():
-            return self.get_config_ovs_hybrid(instance, vif)
-        else:
-            return self.get_config_ovs_bridge(instance, vif)
-
-    def plug(self, instance, vif):
-        vif_type = vif['type']
-
-        LOG.debug('vif_type=%(vif_type)s instance=%(instance)s '
-                  'vif=%(vif)s',
-                  {'vif_type': vif_type, 'instance': instance,
-                   'vif': vif})
-
-        if vif_type is None:
-            raise exception.NovaException(
-                _("vif_type parameter must be present "
-                  "for this vif_driver implementation"))
-        func = getattr(self, 'plug_%s' % vif_type, None)
-        if not func:
-            raise exception.NovaException(
-                _("Unexpected vif_type=%s") % vif_type)
-        return func(instance, vif)
-
-    def plug_bridge(self, instance, vif):
-        network = vif['network']
-        if (not network.get_meta('multi_host', False) and
-                network.get_meta('should_create_bridge', False)):
-            if network.get_meta('should_create_vlan', False):
-                iface = (CONF.vlan_interface or
-                         network.get_meta('bridge_interface'))
-                LOG.debug('Ensuring vlan %(vlan)s and bridge %(bridge)s',
-                          {'vlan': network.get_meta('vlan'),
-                           'bridge': self.get_bridge_name(vif)},
-                          instance=instance)
-                linux_net.LinuxBridgeInterfaceDriver.ensure_vlan_bridge(
-                    network.get_meta('vlan'),
-                    self.get_bridge_name(vif), iface)
-            else:
-                iface = (CONF.flat_interface or
-                         network.get_meta('bridge_interface'))
-                LOG.debug("Ensuring bridge %s",
-                          self.get_bridge_name(vif), instance=instance)
-                linux_net.LinuxBridgeInterfaceDriver.ensure_bridge(
-                    self.get_bridge_name(vif), iface)
-
-    def plug_ovs(self, instance, vif):
-        if self.get_firewall_required(vif) or vif.is_hybrid_plug_enabled():
-            self.plug_ovs_hybrid(instance, vif)
-        else:
-            self.plug_ovs_bridge(instance, vif)
-
-    def plug_ovs_bridge(self, instance, vif):
-        pass
-
-    def plug_ovs_hybrid(self, instance, vif):
-        iface_id = self.get_ovs_interfaceid(vif)
-        br_name = self.get_br_name(vif['id'])
-        v1_name, v2_name = self.get_veth_pair_names(vif['id'])
-
-        if not linux_net.device_exists(br_name):
-            utils.execute('brctl', 'addbr', br_name, run_as_root=True)
-            utils.execute('brctl', 'setfd', br_name, 0, run_as_root=True)
-            utils.execute('brctl', 'stp', br_name, 'off', run_as_root=True)
-            utils.execute('tee',
-                          ('/sys/class/net/%s/bridge/multicast_snooping' %
-                           br_name),
-                          process_input='0',
-                          run_as_root=True,
-                          check_exit_code=[0, 1])
-
-        if not linux_net.device_exists(v2_name):
-            linux_net._create_veth_pair(v1_name, v2_name)
-            utils.execute('ip', 'link', 'set', br_name, 'up', run_as_root=True)
-            utils.execute('brctl', 'addif', br_name, v1_name, run_as_root=True)
-            linux_net.create_ovs_vif_port(self.get_bridge_name(vif),
-                                          v2_name, iface_id,
-                                          vif['address'], instance.name)
-
-    def unplug(self, instance, vif):
-        vif_type = vif['type']
-
-        LOG.debug('vif_type=%(vif_type)s instance=%(instance)s '
-                  'vif=%(vif)s',
-                  {'vif_type': vif_type, 'instance': instance,
-                   'vif': vif})
-
-        if vif_type is None:
-            raise exception.NovaException(
-                _("vif_type parameter must be present "
-                  "for this vif_driver implementation"))
-        func = getattr(self, 'unplug_%s' % vif_type, None)
-        if not func:
-            raise exception.NovaException(
-                _("Unexpected vif_type=%s") % vif_type)
-        return func(instance, vif)
-
-    def unplug_ovs(self, instance, vif):
-        if self.get_firewall_required(vif) or vif.is_hybrid_plug_enabled():
-            self.unplug_ovs_hybrid(instance, vif)
-        else:
-            self.unplug_ovs_bridge(instance, vif)
-
-    def unplug_ovs_hybrid(self, instance, vif):
-        try:
-            br_name = self.get_br_name(vif['id'])
-            v1_name, v2_name = self.get_veth_pair_names(vif['id'])
-
-            if linux_net.device_exists(br_name):
-                utils.execute('brctl', 'delif', br_name, v1_name,
-                              run_as_root=True)
-                utils.execute('ip', 'link', 'set', br_name, 'down',
-                              run_as_root=True)
-                utils.execute('brctl', 'delbr', br_name,
-                              run_as_root=True)
-
-                linux_net.delete_ovs_vif_port(self.get_bridge_name(vif),
-                                              v2_name)
-        except processutils.ProcessExecutionError:
-            LOG.exception(_LE("Failed while unplugging vif"),
-                          instance=instance)
-
-    def unplug_ovs_bridge(self, instance, vif):
-        pass
-
-    def unplug_bridge(self, instance, vif):
-        pass
diff --git a/nova_lxd/tests/__init__.py b/nova_lxd/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/nova_lxd/tests/fake_api.py b/nova_lxd/tests/fake_api.py
deleted file mode 100644
index 3ead0dd..0000000
--- a/nova_lxd/tests/fake_api.py
+++ /dev/null
@@ -1,397 +0,0 @@
-# Copyright (c) 2015 Canonical Ltd
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-
-def fake_standard_return():
-    return {
-        "type": "sync",
-        "status": "Success",
-        "status_code": 200,
-        "metadata": {}
-    }
-
-
-def fake_host():
-    return {
-        "type": "sync",
-        "status": "Success",
-        "status_code": 200,
-        "metadata": {
-                "api_compat": 1,
-                "auth": "trusted",
-                "config": {},
-                "environment": {
-                    "backing_fs": "ext4",
-                    "driver": "lxc",
-                    "kernel_version": "3.19.0-22-generic",
-                    "lxc_version": "1.1.2",
-                    "lxd_version": "0.12"
-                }
-        }
-    }
-
-
-def fake_image_list_empty():
-    return {
-        "type": "sync",
-        "status": "Success",
-        "status_code": 200,
-        "metadata": []
-    }
-
-
-def fake_image_list():
-    return {
-        "type": "sync",
-        "status": "Success",
-        "status_code": 200,
-        "metadata": ['/1.0/images/trusty']
-    }
-
-
-def fake_image_info():
-    return {
-        "type": "sync",
-        "status": "Success",
-        "status_code": 200,
-        "metadata": {
-            "aliases": [
-                {
-                    "target": "ubuntu",
-                    "description": "ubuntu"
-                }
-            ],
-            "architecture": 2,
-            "fingerprint": "04aac4257341478b49c25d22cea8a6ce"
-                           "0489dc6c42d835367945e7596368a37f",
-            "filename": "",
-            "properties": {},
-            "public": 0,
-            "size": 67043148,
-            "created_at": 0,
-            "expires_at": 0,
-            "uploaded_at": 1435669853
-        }
-    }
-
-
-def fake_alias():
-    return {
-        "type": "sync",
-        "status": "Success",
-        "status_code": 200,
-        "metadata": {
-                "target": "ubuntu",
-                "description": "ubuntu"
-        }
-    }
-
-
-def fake_alias_list():
-    return {
-        "type": "sync",
-        "status": "Success",
-        "status_code": 200,
-        "metadata": [
-            "/1.0/images/aliases/ubuntu"
-        ]
-    }
-
-
-def fake_container_list():
-    return {
-        "type": "sync",
-        "status": "Success",
-        "status_code": 200,
-        "metadata": [
-            "/1.0/containers/trusty-1"
-        ]
-    }
-
-
-def fake_container_state(status):
-    return {
-        "type": "sync",
-        "status": "Success",
-        "status_code": 200,
-        "metadata": {
-            "status_code": status
-        }
-    }
-
-
-def fake_container_log():
-    return {
-        "type": "sync",
-        "status": "Success",
-        "status_code": 200,
-        "metadata": {
-            "log": "fake log"
-        }
-    }
-
-
-def fake_container_migrate():
-    return {
-        "type": "async",
-        "status": "Operation created",
-        "status_code": 100,
-        "metadata": {
-            "id": "dbd9f22c-6da5-4066-8fca-c02f09f76738",
-            "class": "websocket",
-            "created_at": "2016-02-07T09:20:53.127321875-05:00",
-            "updated_at": "2016-02-07T09:20:53.127321875-05:00",
-            "status": "Running",
-            "status_code": 103,
-            "resources": {
-                "containers": [
-                    "/1.0/containers/instance-00000010"
-                ]
-            },
-            "metadata": {
-                "control": "fake_control",
-                "fs": "fake_fs"
-            },
-            "may_cancel": 'false',
-            "err": ""
-        },
-        "operation": "/1.0/operations/dbd9f22c-6da5-4066-8fca-c02f09f76738"
-    }
-
-
-def fake_snapshots_list():
-    return {
-        "type": "sync",
-        "status": "Success",
-        "status_code": 200,
-        "metadata": [
-            "/1.0/containers/trusty-1/snapshots/first"
-        ]
-    }
-
-
-def fake_certificate_list():
-    return {
-        "type": "sync",
-        "status": "Success",
-        "status_code": 200,
-        "metadata": [
-            "/1.0/certificates/ABCDEF01"
-        ]
-    }
-
-
-def fake_certificate():
-    return {
-        "type": "sync",
-        "status": "Success",
-        "status_code": 200,
-        "metadata": {
-            "type": "client",
-            "certificate": "ABCDEF01"
-        }
-    }
-
-
-def fake_profile_list():
-    return {
-        "type": "sync",
-        "status": "Success",
-        "status_code": 200,
-        "metadata": [
-            "/1.0/profiles/fake-profile"
-        ]
-    }
-
-
-def fake_profile():
-    return {
-        "type": "sync",
-        "status": "Success",
-        "status_code": 200,
-        "metadata": {
-            "name": "fake-profile",
-            "config": {
-                "resources.memory": "2GB",
-                "network.0.bridge": "lxcbr0"
-            }
-        }
-    }
-
-
-def fake_operation_list():
-    return {
-        "type": "sync",
-        "status": "Success",
-        "status_code": 200,
-        "metadata": [
-            "/1.0/operations/1234"
-        ]
-    }
-
-
-def fake_operation():
-    return {
-        "type": "async",
-        "status": "OK",
-        "status_code": 100,
-        "operation": "/1.0/operation/1234",
-        "metadata": {
-            "created_at": "2015-06-09T19:07:24.379615253-06:00",
-            "updated_at": "2015-06-09T19:07:23.379615253-06:00",
-            "status": "Running",
-            "status_code": 103,
-            "resources": {
-                "containers": ["/1.0/containers/1"]
-            },
-            "metadata": {},
-            "may_cancel": True
-        }
-    }
-
-
-def fake_operation_info_ok():
-    return {
-        "type": "async",
-        "status": "OK",
-        "status_code": 200,
-        "operation": "/1.0/operation/1234",
-        "metadata": {
-            "created_at": "2015-06-09T19:07:24.379615253-06:00",
-            "updated_at": "2015-06-09T19:07:23.379615253-06:00",
-            "status": "Completed",
-            "status_code": 200,
-            "resources": {
-                "containers": ["/1.0/containers/1"]
-            },
-            "metadata": {},
-            "may_cancel": True
-        }
-    }
-
-
-def fake_operation_info_failed():
-    return {
-        "type": "async",
-        "status": "OK",
-        "status_code": 200,
-        "operation": "/1.0/operation/1234",
-        "metadata": {
-            "created_at": "2015-06-09T19:07:24.379615253-06:00",
-            "updated_at": "2015-06-09T19:07:23.379615253-06:00",
-            "status": "Failure",
-            "status_code": 400,
-            "resources": {
-                "containers": ["/1.0/containers/1"]
-            },
-            "metadata": "Invalid container name",
-            "may_cancel": True
-        }
-    }
-
-
-def fake_network_list():
-    return {
-        "type": "sync",
-        "status": "Success",
-        "status_code": 200,
-        "metadata": [
-            "/1.0/networks/lxcbr0"
-        ]
-    }
-
-
-def fake_network():
-    return {
-        "type": "async",
-        "status": "OK",
-        "status_code": 100,
-        "operation": "/1.0/operation/1234",
-        "metadata": {
-            "name": "lxcbr0",
-            "type": "bridge",
-            "members": ["/1.0/containers/trusty-1"]
-        }
-    }
-
-
-def fake_container_config():
-    return {
-        'name': "my-container",
-        'profiles': ["default"],
-        'architecture': 2,
-        'config': {"limits.cpus": "3"},
-        'expanded_config': {"limits.cpus": "3"},
-        'devices': {
-            'rootfs': {
-                'type': "disk",
-                'path': "/",
-                'source': "UUID=8f7fdf5e-dc60-4524-b9fe-634f82ac2fb6"
-            }
-        },
-        'expanded_devices': {
-            'rootfs': {
-                'type': "disk",
-                'path': "/",
-                'source': "UUID=8f7fdf5e-dc60-4524-b9fe-634f82ac2fb6"}
-        },
-        "eth0": {
-            "type": "nic",
-            "parent": "lxcbr0",
-            "hwaddr": "00:16:3e:f4:e7:1c",
-            "name": "eth0",
-            "nictype": "bridged",
-        }
-    }
-
-
-def fake_container_info():
-    return {
-        'name': "my-container",
-        'profiles': ["default"],
-        'architecture': 2,
-        'config': {"limits.cpus": "3"},
-        'expanded_config': {"limits.cpus": "3"},
-        'devices': {
-            'rootfs': {
-                'type': "disk",
-                'path': "/",
-                'source': "UUID=8f7fdf5e-dc60-4524-b9fe-634f82ac2fb6"
-            }
-        },
-        'expanded_devices': {
-            'rootfs': {
-                'type': "disk",
-                'path': "/",
-                'source': "UUID=8f7fdf5e-dc60-4524-b9fe-634f82ac2fb6"}
-        },
-        "eth0": {
-            "type": "nic",
-            "parent": "lxcbr0",
-            "hwaddr": "00:16:3e:f4:e7:1c",
-            "name": "eth0",
-            "nictype": "bridged",
-        },
-        'status': {
-            'status': "Running",
-            'status_code': 103,
-            'ips': [{'interface': "eth0",
-                     'protocol': "INET6",
-                     'address': "2001:470:b368:1020:1::2",
-                     'host_veth': "vethGMDIY9"},
-                    {'interface': "eth0",
-                     'protocol': "INET",
-                     'address': "172.16.15.30",
-                     'host_veth': "vethGMDIY9"}]},
-    }
diff --git a/nova_lxd/tests/session/__init__.py b/nova_lxd/tests/session/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/nova_lxd/tests/session/test_container.py b/nova_lxd/tests/session/test_container.py
deleted file mode 100644
index 69690f7..0000000
--- a/nova_lxd/tests/session/test_container.py
+++ /dev/null
@@ -1,550 +0,0 @@
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-#    implied. See the License for the specific language governing
-#    permissions and limitations under the License.
-
-"""
-Unit tests for ContinerMixin class
-
-The following tests the ContainerMixin class
-for nova-lxd.
-"""
-
-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_lxd.nova.virt.lxd import session
-from nova_lxd.tests import fake_api
-from nova_lxd.tests import stubs
-
-
- at ddt.ddt
-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_defined(instance.name),
-            mock.call.container_update(instance.name, config)]
-        self.assertEqual(calls, self.ml.method_calls)
-
-    @stubs.annotated_data(
-        ('api_fail', True, lxd_exceptions.APIError('Fake', 500),
-         exception.NovaException),
-        ('missing_container', False, None,
-         exception.InstanceNotFound)
-    )
-    def test_container_update_fail(self, tag, container_defined, 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()
-        if container_defined:
-            self.ml.container_defined.return_value = container_defined
-            self.ml.container_update.side_effect = (
-                lxd_exceptions.APIError('Fake', 500))
-            self.assertRaises(
-                expected,
-                self.session.container_update, config, instance)
-        if not container_defined:
-            self.ml.container_defined.return_value = container_defined
-            self.assertRaises(
-                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
-        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_defined.return_value = defined
-        self.ml.container_start.return_value = side_effect
-        self.assertEqual(None,
-                         self.session.container_start(instance.name,
-                                                      instance))
-        calls = [mock.call.container_defined(instance.name),
-                 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(
-        ('container_missing', False,
-         exception.InstanceNotFound),
-        ('api_error', True,
-         exception.NovaException,
-         lxd_exceptions.APIError('Fake', 500)),
-    )
-    def test_container_start_fail(self, tag, container_defined,
-                                  expected, side_effect=None):
-        """
-        container_start starts a container on a given LXD host.
-        Veify that an exception.InstanceNotFound when the container
-        is not found on an LXD host. Raises an exception.NovaException
-        when there is an APIError.
-        """
-        instance = stubs._fake_instance()
-        if container_defined:
-            self.ml.container_defined.return_value = container_defined
-            self.ml.container_start.side_effect = side_effect
-            self.assertRaises(expected,
-                              self.session.container_start,
-                              instance.name, instance)
-        if not container_defined:
-            self.ml.container_defined.return_value = container_defined
-            self.assertRaises(expected,
-                              self.session.container_start, instance.name,
-                              instance)
-
-    @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_defined(instance.name),
-                 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_defined(instance.name),
-                 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', True, (200, fake_api.fake_operation_info_ok())),
-        ('missing', False, (200, fake_api.fake_operation_info_ok()))
-    )
-    def test_container_destroy(self, tag, container_defined, side_effect):
-        """
-        container_destroy delete a container from the LXD Host. Check
-        that the approiate pylxd calls are made.
-        """
-        instance = stubs._fake_instance()
-        if container_defined:
-            self.ml.container_defined.return_value = container_defined
-            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_defined(instance.name),
-                     mock.call.container_defined(instance.name),
-                     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)
-        if not container_defined:
-            self.ml.container_defined.return_value = container_defined
-            self.assertEqual(None,
-                             self.session.container_destroy(instance.name,
-                                                            instance))
-            calls = [mock.call.container_defined(instance.name)]
-            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_defined.return_value = container_defined
-            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_defined(instance.name),
-            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)
diff --git a/nova_lxd/tests/session/test_event.py b/nova_lxd/tests/session/test_event.py
deleted file mode 100644
index f50b879..0000000
--- a/nova_lxd/tests/session/test_event.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-#    implied. See the License for the specific language governing
-#    permissions and limitations under the License.
-
-import ddt
-import mock
-
-from nova import test
-
-from nova_lxd.nova.virt.lxd import session
-from nova_lxd.tests import stubs
-
-
- at ddt.ddt
-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)
diff --git a/nova_lxd/tests/session/test_image.py b/nova_lxd/tests/session/test_image.py
deleted file mode 100644
index fa0c6dc..0000000
--- a/nova_lxd/tests/session/test_image.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-#    implied. See the License for the specific language governing
-#    permissions and limitations under the License.
-
-import ddt
-import mock
-
-from nova import test
-
-from nova_lxd.nova.virt.lxd import session
-from nova_lxd.tests import stubs
-
-
- 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)
diff --git a/nova_lxd/tests/session/test_migrate.py b/nova_lxd/tests/session/test_migrate.py
deleted file mode 100644
index 9f96638..0000000
--- a/nova_lxd/tests/session/test_migrate.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-#    implied. See the License for the specific language governing
-#    permissions and limitations under the License.
-
-import ddt
-import mock
-
-from nova import test
-
-from nova_lxd.nova.virt.lxd import session
-from nova_lxd.tests import stubs
-
-
- at ddt.ddt
-class SessionMigrateTest(test.NoDBTestCase):
-
-    def setUp(self):
-        super(SessionMigrateTest, 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()
diff --git a/nova_lxd/tests/session/test_profile.py b/nova_lxd/tests/session/test_profile.py
deleted file mode 100644
index 2680849..0000000
--- a/nova_lxd/tests/session/test_profile.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-#    implied. See the License for the specific language governing
-#    permissions and limitations under the License.
-
-
-import ddt
-import mock
-
-from nova import exception
-from nova import test
-from pylxd.deprecated import exceptions as lxd_exceptions
-
-from nova_lxd.nova.virt.lxd import session
-from nova_lxd.tests import fake_api
-from nova_lxd.tests import stubs
-
-
- at ddt.ddt
-class SessionProfileTest(test.NoDBTestCase):
-
-    def setUp(self):
-        super(SessionProfileTest, self).setUp()
-
-        """This is so we can mock out pylxd API calls."""
-        self.ml = stubs.lxd_mock()
-        lxd_patcher = mock.patch('pylxd.api.API',
-                                 mock.Mock(return_value=self.ml))
-        lxd_patcher.start()
-        self.addCleanup(lxd_patcher.stop)
-
-        self.session = session.LXDAPISession()
-
-    @stubs.annotated_data(
-        ('empty', [], []),
-        ('valid', ['test'], ['test']),
-    )
-    def test_profile_list(self, tag, side_effect, expected):
-        self.ml.profile_list.return_value = side_effect
-        self.assertEqual(expected,
-                         self.session.profile_list())
-
-    def test_profile_list_fail(self):
-        self.ml.profile_list.side_effect = (
-            lxd_exceptions.APIError('Fake', 500))
-        self.assertRaises(
-            exception.NovaException,
-            self.session.profile_list)
-
-    def test_profile_create(self):
-        instance = stubs._fake_instance()
-        config = mock.Mock()
-        self.ml.profile_defined.return_value = True
-        self.ml.profile_create.return_value = \
-            (200, fake_api.fake_standard_return())
-        self.assertEqual((200, fake_api.fake_standard_return()),
-                         self.session.profile_create(config,
-                                                     instance))
-        calls = [mock.call.profile_list(),
-                 mock.call.profile_create(config)]
-        self.assertEqual(calls, self.ml.method_calls)
-
-    def test_profile_delete(self):
-        instance = stubs._fake_instance()
-        self.ml.profile_defined.return_value = True
-        self.ml.profile_delete.return_value = \
-            (200, fake_api.fake_standard_return())
-        self.assertEqual(None,
-                         self.session.profile_delete(instance))
diff --git a/nova_lxd/tests/session/test_snapshot.py b/nova_lxd/tests/session/test_snapshot.py
deleted file mode 100644
index 61759d6..0000000
--- a/nova_lxd/tests/session/test_snapshot.py
+++ /dev/null
@@ -1,81 +0,0 @@
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-#    implied. See the License for the specific language governing
-#    permissions and limitations under the License.
-
-import ddt
-import mock
-
-from nova import exception
-from nova import test
-from pylxd.deprecated import exceptions as lxd_exceptions
-
-from nova_lxd.nova.virt.lxd import session
-from nova_lxd.tests import fake_api
-from nova_lxd.tests import stubs
-
-
- at ddt.ddt
-class 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)
diff --git a/nova_lxd/tests/stubs.py b/nova_lxd/tests/stubs.py
deleted file mode 100644
index c66c061..0000000
--- a/nova_lxd/tests/stubs.py
+++ /dev/null
@@ -1,110 +0,0 @@
-# Copyright (c) 2015 Canonical Ltd
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import ddt
-import mock
-
-from nova import context
-from nova.tests.unit import fake_instance
-
-
-class MockConf(mock.Mock):
-
-    def __init__(self, lxd_args=(), lxd_kwargs={}, *args, **kwargs):
-        default = {
-            'config_drive_format': None,
-            'instances_path': '/fake/instances/path',
-            'image_cache_subdirectory_name': '/fake/image/cache',
-            'vif_plugging_timeout': 10,
-            'my_ip': '1.2.3.4',
-            'vlan_interface': 'vlanif',
-            'flat_interface': 'flatif',
-        }
-
-        default.update(kwargs)
-        super(MockConf, self).__init__(*args, **default)
-
-        lxd_default = {
-            'root_dir': '/fake/lxd/root',
-            'timeout': 20,
-            'retry_interval': 2
-        }
-        lxd_default.update(lxd_kwargs)
-        self.lxd = mock.Mock(lxd_args, **lxd_default)
-
-
-class MockInstance(mock.Mock):
-
-    def __init__(self, name='fake-uuid', uuid='fake-uuid',
-                 image_ref='mock_image', ephemeral_gb=0, memory_mb=-1,
-                 vcpus=0, *args, **kwargs):
-        super(MockInstance, self).__init__(
-            uuid=uuid,
-            image_ref=image_ref,
-            ephemeral_gb=ephemeral_gb,
-            *args, **kwargs)
-        self.uuid = uuid
-        self.name = name
-        self.flavor = mock.Mock(memory_mb=memory_mb, vcpus=vcpus)
-
-
-def lxd_mock(*args, **kwargs):
-    default = {
-        'profile_list.return_value': ['fake_profile'],
-        'container_list.return_value': ['mock-instance-1', 'mock-instance-2'],
-        'host_ping.return_value': True,
-    }
-    default.update(kwargs)
-    return mock.Mock(*args, **default)
-
-
-def annotated_data(*args):
-    class List(list):
-        pass
-
-    class Dict(dict):
-        pass
-
-    new_args = []
-
-    for arg in args:
-        if isinstance(arg, (list, tuple)):
-            new_arg = List(arg)
-            new_arg.__name__ = arg[0]
-        elif isinstance(arg, dict):
-            new_arg = Dict(arg)
-            new_arg.__name__ = arg['tag']
-        else:
-            raise TypeError('annotate_data can only handle dicts, '
-                            'lists and tuples')
-        new_args.append(new_arg)
-
-    return lambda func: ddt.data(*new_args)(ddt.unpack(func))
-
-
-def _fake_instance():
-    ctxt = context.get_admin_context()
-    _instance_values = {
-        'display_name': 'fake_display_name',
-        'name': 'fake_name',
-        'uuid': 'fake_uuid',
-        'image_ref': 'fake_image',
-        'vcpus': 1,
-        'memory_mb': 512,
-        'root_gb': 10,
-        'host': 'fake_host',
-        'expected_attrs': ['system_metadata'],
-    }
-    return fake_instance.fake_instance_obj(
-        ctxt, **_instance_values)
diff --git a/nova_lxd/tests/test_config.py b/nova_lxd/tests/test_config.py
deleted file mode 100644
index af4373d..0000000
--- a/nova_lxd/tests/test_config.py
+++ /dev/null
@@ -1,134 +0,0 @@
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import ddt
-import mock
-
-from nova import test
-from nova.tests.unit import fake_network
-
-from nova_lxd.nova.virt.lxd import config
-from nova_lxd.nova.virt.lxd import session
-from nova_lxd.nova.virt.lxd import utils as container_dir
-from nova_lxd.tests import stubs
-
-
- at ddt.ddt
- at mock.patch.object(config, 'CONF', stubs.MockConf())
- at mock.patch.object(container_dir, 'CONF', stubs.MockConf())
-class LXDTestContainerConfig(test.NoDBTestCase):
-    """LXD Container configuration unit tests."""
-
-    def setUp(self):
-        super(LXDTestContainerConfig, self).setUp()
-        self.config = config.LXDContainerConfig()
-
-    @stubs.annotated_data(
-        ('test_name', 'name', 'instance-00000001'),
-        ('test_source', 'source', {'type': 'image',
-                                   'alias': 'fake_image'}),
-        ('test_devices', 'devices', {})
-    )
-    def test_create_container(self, tag, key, expected):
-        """Tests the create_container methond on LXDContainerConfig.
-           Inspect that the correct dictionary is returned for a given
-           instance.
-        """
-        instance = stubs._fake_instance()
-        container_config = self.config.create_container(instance)
-        self.assertEqual(container_config[key], expected)
-
-    @stubs.annotated_data(
-        ('test_memmoy', 'limits.memory', '512MB')
-    )
-    def test_create_config(self, tag, key, expected):
-        instance = stubs._fake_instance()
-        instance_name = 'fake_instance'
-        config = self.config.create_config(instance_name, instance)
-        self.assertEqual(config[key], expected)
-
-    def test_create_network(self):
-        instance = stubs._fake_instance()
-        instance_name = 'fake_instance'
-        network_info = fake_network.fake_get_instance_nw_info(self)
-        config = self.config.create_network(instance_name, instance,
-                                            network_info)
-        self.assertEqual({'fake_br1': {'hwaddr': 'DE:AD:BE:EF:00:01',
-                                       'nictype': 'bridged',
-                                       'parent': 'fake_br1',
-                                       'type': 'nic'}}, config)
-
-    @mock.patch('os.path.exists', mock.Mock(return_value=True))
-    def test_create_disk_path(self):
-        instance = stubs._fake_instance()
-        config = self.config.configure_disk_path('/fake/src_path',
-                                                 '/fake/dest_path',
-                                                 'fake_disk', instance)
-        self.assertEqual({'fake_disk': {'path': '/fake/dest_path',
-                                        'source': '/fake/src_path',
-                                        'type': 'disk',
-                                        'optional': 'True'}}, config)
-
-    def test_config_instance_options(self):
-        instance = stubs._fake_instance()
-        config = {}
-        container_config = self.config.config_instance_options(config,
-                                                               instance)
-        self.assertEqual({'boot.autostart': 'True'}, container_config)
-
-    def test_create_container_source(self):
-        instance = stubs._fake_instance()
-        config = self.config.get_container_source(instance)
-        self.assertEqual(config, {'type': 'image', 'alias': 'fake_image'})
-
-    @mock.patch.object(session.LXDAPISession, 'get_host_config',
-                       mock.Mock(return_value={'storage': 'btrfs'}))
-    def test_container_root_btrfs(self):
-        instance = stubs._fake_instance()
-        config = self.config.configure_container_root(instance)
-        self.assertEqual({'root': {'path': '/',
-                                   'type': 'disk',
-                                   'size': '10GB'}}, config)
-
-    @mock.patch.object(session.LXDAPISession, 'get_host_config',
-                       mock.Mock(return_value={'storage': 'zfs'}))
-    def test_container_root_zfs(self):
-        instance = stubs._fake_instance()
-        config = self.config.configure_container_root(instance)
-        self.assertEqual({'root': {'path': '/',
-                                   'type': 'disk',
-                                   'size': '10GB'}}, config)
-
-    @mock.patch.object(session.LXDAPISession, 'get_host_config',
-                       mock.Mock(return_value={'storage': 'lvm'}))
-    def test_container_root_lvm(self):
-        instance = stubs._fake_instance()
-        config = self.config.configure_container_root(instance)
-        self.assertEqual({'root': {'path': '/',
-                                   'type': 'disk'}}, config)
-
-    def test_container_nested_container(self):
-        instance = stubs._fake_instance()
-        instance.flavor.extra_specs = {'lxd:nested_allowed': True}
-        config = self.config.config_instance_options({}, instance)
-        self.assertEqual({'security.nesting': 'True',
-                          'boot.autostart': 'True'}, config)
-
-    def test_container_privileged_container(self):
-        instance = stubs._fake_instance()
-        instance.flavor.extra_specs = {'lxd:privileged_allowed': True}
-        config = self.config.config_instance_options({}, instance)
-        self.assertEqual({'security.privileged': 'True',
-                          'boot.autostart': 'True'}, config)
diff --git a/nova_lxd/tests/test_driver_api.py b/nova_lxd/tests/test_driver_api.py
deleted file mode 100644
index 9263ffc..0000000
--- a/nova_lxd/tests/test_driver_api.py
+++ /dev/null
@@ -1,493 +0,0 @@
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import inspect
-import json
-import os
-import platform
-from pylxd.deprecated import exceptions as lxd_exceptions
-
-import ddt
-import mock
-from oslo_config import cfg
-import six
-
-from nova.compute import arch
-from nova.compute import hv_type
-from nova.compute import power_state
-from nova.compute import vm_mode
-from nova import exception
-from nova import test
-from nova.virt import fake
-from nova.virt import hardware
-
-from nova_lxd.nova.virt.lxd import driver
-from nova_lxd.nova.virt.lxd import host
-from nova_lxd.nova.virt.lxd import operations as container_ops
-from nova_lxd.nova.virt.lxd import session
-from nova_lxd.nova.virt.lxd import utils as container_dir
-from nova_lxd.tests import stubs
-
-
-class LXDTestConfig(test.NoDBTestCase):
-
-    def test_config(self):
-        self.assertIsInstance(driver.CONF.lxd, cfg.ConfigOpts.GroupAttr)
-        self.assertEqual(os.path.abspath('/var/lib/lxd'),
-                         os.path.abspath(driver.CONF.lxd.root_dir))
-        self.assertEqual(-1, driver.CONF.lxd.timeout)
-
-
- at ddt.ddt
- at mock.patch.object(container_ops, 'CONF', stubs.MockConf())
- at mock.patch.object(container_dir, 'CONF', stubs.MockConf())
- at mock.patch.object(driver, 'CONF', stubs.MockConf())
- at mock.patch.object(host, 'CONF', stubs.MockConf())
-class LXDTestDriver(test.NoDBTestCase):
-
-    @mock.patch.object(driver, 'CONF', stubs.MockConf())
-    def setUp(self):
-        super(LXDTestDriver, 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.connection = driver.LXDDriver(fake.FakeVirtAPI())
-
-    def test_capabilities(self):
-        self.assertFalse(self.connection.capabilities['has_imagecache'])
-        self.assertFalse(self.connection.capabilities['supports_recreate'])
-        self.assertFalse(
-            self.connection.capabilities['supports_migrate_to_same_host'])
-
-    def test_init_host(self):
-        self.assertEqual(
-            True,
-            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.
-                                                 APIError('Fake',
-                                                          500))}),
-    )
-    def test_init_host_fail(self, tag, config):
-        self.ml.configure_mock(**config)
-        self.assertRaises(
-            exception.HostNotFound,
-            self.connection.init_host,
-            None
-        )
-
-    @stubs.annotated_data(
-        ('running', {'state': 200, 'mem': 0, 'max_mem': 0},
-         power_state.RUNNING),
-        ('shutdown', {'state': 102, 'mem': 0, 'max_mem': 0},
-         power_state.SHUTDOWN),
-        ('crashed', {'state': 108, 'mem': 0, 'max_mem': 0},
-         power_state.CRASHED),
-        ('suspend', {'state': 109, 'mem': 0, 'max_mem': 0},
-         power_state.SUSPENDED),
-        ('no_state', {'state': 401, 'mem': 0, 'max_mem': 0},
-         power_state.NOSTATE),
-    )
-    def test_get_info(self, tag, side_effect, expected):
-        instance = stubs._fake_instance()
-        with mock.patch.object(session.LXDAPISession,
-                               "container_state",
-                               ) as state:
-            state.return_value = side_effect
-            info = self.connection.get_info(instance)
-            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},
-            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
-        )
-
-    @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')
-
-    @stubs.annotated_data(
-        ('undefined', False),
-        ('404', lxd_exceptions.APIError('Not found', 404)),
-    )
-    @mock.patch('oslo_concurrency.lockutils.lock')
-    def test_spawn_new(self, tag, side_effect, mc):
-        context = mock.Mock()
-        instance = stubs.MockInstance()
-        image_meta = mock.Mock()
-        injected_files = mock.Mock()
-        network_info = mock.Mock()
-        block_device_info = mock.Mock()
-        self.ml.container_defined.side_effect = [side_effect]
-
-        with test.nested(
-                mock.patch.object(self.connection.container_ops,
-                                  'spawn'),
-        ) as (
-                create_container
-        ):
-            self.connection.spawn(context, instance, image_meta,
-                                  injected_files, None, network_info,
-                                  block_device_info)
-            self.assertTrue(create_container)
-
-    def test_destroy_fail(self):
-        instance = stubs._fake_instance()
-        context = mock.Mock()
-        network_info = mock.Mock()
-        self.ml.container_destroy.side_effect = (
-            lxd_exceptions.APIError('Fake', 500))
-        with test.nested(
-            mock.patch.object(session.LXDAPISession,
-                              'container_destroy'),
-            mock.patch.object(session.LXDAPISession,
-                              'container_stop'),
-            mock.patch.object(self.connection, 'cleanup'),
-            mock.patch.object(container_ops.LXDContainerOperations,
-                              'unplug_vifs'),
-
-        ) as (
-            container_destroy,
-            container_stop,
-            cleanup,
-            unplug_vifs
-        ):
-            self.connection.destroy(context, instance, network_info)
-
-    def test_destroy(self):
-        instance = stubs._fake_instance()
-        context = mock.Mock()
-        network_info = mock.Mock()
-        with test.nested(
-                mock.patch.object(session.LXDAPISession,
-                                  'container_stop'),
-                mock.patch.object(session.LXDAPISession,
-                                  'container_destroy'),
-                mock.patch.object(self.connection,
-                                  'cleanup'),
-                mock.patch.object(container_ops.LXDContainerOperations,
-                                  'unplug_vifs'),
-        ) as (
-                container_stop,
-                container_destroy,
-                cleanup,
-                unplug_vifs
-        ):
-            self.connection.destroy(context, instance, network_info)
-            self.assertTrue(container_stop)
-            self.assertTrue(container_destroy)
-            self.assertTrue(cleanup)
-            unplug_vifs.assert_called_with(instance, network_info)
-
-    @mock.patch('os.path.exists', mock.Mock(return_value=True))
-    @mock.patch('shutil.rmtree')
-    @mock.patch('pwd.getpwuid', mock.Mock(return_value=mock.Mock(pw_uid=1234)))
-    @mock.patch.object(container_ops.utils, 'execute')
-    def test_cleanup(self, mr, mu):
-        instance = stubs.MockInstance()
-        self.assertEqual(
-            None,
-            self.connection.cleanup({}, instance, [], [], None, None, None))
-
-    @mock.patch('six.moves.builtins.open')
-    @mock.patch.object(container_ops.utils, 'execute')
-    @mock.patch('pwd.getpwuid', mock.Mock(return_value=mock.Mock(pw_uid=1234)))
-    @mock.patch('os.getuid', mock.Mock())
-    @mock.patch('os.path.exists', mock.Mock(return_value=True))
-    def test_get_console_output(self, me, mo):
-        instance = stubs.MockInstance()
-        mo.return_value.__enter__.return_value = six.BytesIO(b'fake contents')
-        self.assertEqual(b'fake contents',
-                         self.connection.get_console_output({}, instance))
-        calls = [
-            mock.call('chown', '1234:1234',
-                      '/var/log/lxd/fake-uuid/console.log',
-                      run_as_root=True),
-            mock.call('chmod', '755',
-                      '/fake/lxd/root/containers/fake-uuid',
-                      run_as_root=True)
-        ]
-        self.assertEqual(calls, me.call_args_list)
-
-    @mock.patch.object(host.compute_utils, 'get_machine_ips')
-    @stubs.annotated_data(
-        ('found', ['1.2.3.4']),
-        ('not-found', ['4.3.2.1']),
-    )
-    def test_get_host_ip_addr(self, tag, return_value, mi):
-        mi.return_value = return_value
-        self.assertEqual('1.2.3.4', self.connection.get_host_ip_addr())
-
-    @mock.patch('socket.gethostname', mock.Mock(return_value='fake_hostname'))
-    @mock.patch('os.statvfs', return_value=mock.Mock(f_blocks=131072000,
-                                                     f_bsize=8192,
-                                                     f_bavail=65536000))
-    @mock.patch('six.moves.builtins.open')
-    @mock.patch.object(container_ops.utils, 'execute')
-    def test_get_available_resource(self, me, mo, ms):
-        me.return_value = ('Model name:          Fake CPU\n'
-                           'Vendor ID:           FakeVendor\n'
-                           'Socket(s):           10\n'
-                           'Core(s) per socket:  5\n'
-                           'Thread(s) per core:  4\n'
-                           '\n',
-                           None)
-        meminfo = mock.MagicMock()
-        meminfo.__enter__.return_value = six.moves.cStringIO(
-            'MemTotal: 10240000 kB\n'
-            'MemFree:   2000000 kB\n'
-            'Buffers:     24000 kB\n'
-            'Cached:      24000 kB\n')
-
-        mo.side_effect = [
-            six.moves.cStringIO('flags: fake flag goes here\n'
-                                'processor: 2\n'
-                                '\n'),
-            meminfo,
-        ]
-        value = self.connection.get_available_resource(None)
-        value['cpu_info'] = json.loads(value['cpu_info'])
-        value['supported_instances'] = [[arch.I686, hv_type.LXD,
-                                         vm_mode.EXE],
-                                        [arch.X86_64, hv_type.LXD,
-                                         vm_mode.EXE],
-                                        [arch.I686, hv_type.LXC,
-                                         vm_mode.EXE],
-                                        [arch.X86_64, hv_type.LXC,
-                                         vm_mode.EXE]]
-        expected = {'cpu_info': {u'arch': platform.uname()[5],
-                                 u'features': u'fake flag goes here',
-                                 u'model': u'Fake CPU',
-                                 u'topology': {u'cores': u'5',
-                                               u'sockets': u'10',
-                                               u'threads': u'4'},
-                                 u'vendor': u'FakeVendor'},
-                    'hypervisor_hostname': 'fake_hostname',
-                    'hypervisor_type': 'lxd',
-                    'hypervisor_version': '011',
-                    'local_gb': 1000,
-                    'local_gb_used': 500,
-                    'memory_mb': 10000,
-                    'memory_mb_used': 8000,
-                    'numa_topology': None,
-                    'supported_instances': [[arch.I686, hv_type.LXD,
-                                             vm_mode.EXE],
-                                            [arch.X86_64, hv_type.LXD,
-                                             vm_mode.EXE],
-                                            [arch.I686, hv_type.LXC,
-                                             vm_mode.EXE],
-                                            [arch.X86_64, hv_type.LXC,
-                                             vm_mode.EXE]],
-                    'vcpus': 200,
-                    'vcpus_used': 0}
-        self.assertEqual(expected, value)
-        me.assert_called_once_with('lscpu')
-        self.assertEqual([mock.call('/proc/cpuinfo', 'r'),
-                          mock.call('/proc/meminfo')],
-                         mo.call_args_list)
-        ms.assert_called_once_with('/fake/lxd/root')
-
-    def test_container_reboot(self):
-        instance = stubs._fake_instance()
-        context = mock.Mock()
-        network_info = mock.Mock()
-        reboot_type = 'SOFT'
-        with test.nested(
-                mock.patch.object(self.connection.container_ops,
-                                  'reboot')
-        ) as (
-                reboot
-        ):
-            self.connection.reboot(context, instance,
-                                   network_info, reboot_type)
-            self.assertTrue(reboot)
-
-    def test_container_power_off(self):
-        instance = stubs._fake_instance()
-        with test.nested(
-                mock.patch.object(self.connection.container_ops,
-                                  'power_off')
-        ) as (
-                power_off
-        ):
-            self.connection.power_off(instance)
-            self.assertTrue(power_off)
-
-    def test_container_power_on(self):
-        context = mock.Mock()
-        instance = stubs._fake_instance()
-        network_info = mock.Mock()
-        with test.nested(
-                mock.patch.object(self.connection.container_ops,
-                                  'power_on')
-        ) as (
-                power_on
-        ):
-            self.connection.power_on(context, instance, network_info)
-            self.assertTrue(power_on)
-
-    @stubs.annotated_data(
-        ('refresh_security_group_rules', (mock.Mock(),)),
-        ('refresh_security_group_members', (mock.Mock(),)),
-        ('refresh_provider_fw_rules',),
-        ('refresh_instance_security_rules', (mock.Mock(),)),
-        ('ensure_filtering_rules_for_instance', (mock.Mock(), mock.Mock())),
-        ('filter_defer_apply_on',),
-        ('filter_defer_apply_off',),
-        ('unfilter_instance', (mock.Mock(), mock.Mock())),
-    )
-    def test_firewall_calls(self, name, args=()):
-        with mock.patch.object(self.connection.container_firewall,
-                               'firewall_driver') as mf:
-            driver_method = getattr(self.connection, name)
-            firewall_method = getattr(mf, name)
-            self.assertEqual(
-                firewall_method.return_value,
-                driver_method(*args))
-            firewall_method.assert_called_once_with(*args)
-
-    @mock.patch.object(host.utils, 'execute')
-    def test_get_host_uptime(self, me):
-        me.return_value = ('out', 'err')
-        self.assertEqual('out',
-                         self.connection.get_host_uptime())
-
-    @mock.patch('socket.gethostname', mock.Mock(return_value='mock_hostname'))
-    def test_get_available_nodes(self):
-        self.assertEqual(
-            ['mock_hostname'], self.connection.get_available_nodes())
-
-    @mock.patch('socket.gethostname', mock.Mock(return_value='mock_hostname'))
-    @stubs.annotated_data(
-        ('mock_hostname', True),
-        ('wrong_hostname', False),
-    )
-    def test_node_is_available(self, nodename, available):
-        self.assertEqual(available,
-                         self.connection.node_is_available(nodename))
-
-
- at ddt.ddt
-class LXDTestDriverNoops(test.NoDBTestCase):
-
-    def setUp(self):
-        super(LXDTestDriverNoops, self).setUp()
-        self.connection = driver.LXDDriver(fake.FakeVirtAPI())
-
-    @ddt.data(
-        'list_instance_uuids',
-        'get_diagnostics',
-        'get_instance_diagnostics',
-        'get_all_bw_counters',
-        'get_all_volume_usage',
-        'attach_volume',
-        'detach_volume',
-        'soft_delete',
-        'post_live_migration_at_source',
-        'check_instance_shared_storage_local',
-        'check_instance_shared_storage_remote',
-        'check_can_live_migrate_destination',
-        'check_can_live_migrate_destination_cleanup',
-        'check_can_live_migrate_source',
-        'get_instance_disk_info',
-        'poll_rebooting_instances',
-        'host_power_action',
-        'host_maintenance_mode',
-        'set_host_enabled',
-        'block_stats',
-        'add_to_aggregate',
-        'remove_from_aggregate',
-        'undo_aggregate_operation',
-        'volume_snapshot_create',
-        'volume_snapshot_delete',
-        'quiesce',
-        'unquiesce',
-    )
-    def test_notimplemented(self, method):
-        call = getattr(self.connection, method)
-        argspec = inspect.getargspec(call)
-        self.assertRaises(
-            NotImplementedError,
-            call,
-            *([None] * (len(argspec.args) - 1)))
-
-    @ddt.data(
-        'post_interrupted_snapshot_cleanup',
-        'check_instance_shared_storage_cleanup',
-        'manage_image_cache',
-    )
-    def test_pass(self, method):
-        call = getattr(self.connection, method)
-        argspec = inspect.getargspec(call)
-        self.assertEqual(
-            None,
-            call(*([None] * (len(argspec.args) - 1))))
-
-    @stubs.annotated_data(
-        ('deallocate_networks_on_reschedule', False),
-        ('macs_for_instance', None),
-        ('get_per_instance_usage', {}),
-        ('instance_on_disk', False),
-    )
-    def test_return(self, method, expected):
-        call = getattr(self.connection, method)
-        argspec = inspect.getargspec(call)
-        self.assertEqual(
-            expected,
-            call(*([None] * (len(argspec.args) - 1))))
diff --git a/nova_lxd/tests/test_image.py b/nova_lxd/tests/test_image.py
deleted file mode 100644
index 61519dc..0000000
--- a/nova_lxd/tests/test_image.py
+++ /dev/null
@@ -1,92 +0,0 @@
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import io
-import json
-from nova import exception
-from nova import test
-import os
-import tarfile
-
-import ddt
-import fixtures
-import mock
-from oslo_concurrency import lockutils
-from oslo_config import fixture as config_fixture
-
-
-from nova_lxd.nova.virt.lxd import image
-from nova_lxd.nova.virt.lxd import session
-from nova_lxd.tests import stubs
-
-
- at ddt.ddt
-class LXDTestContainerImage(test.NoDBTestCase):
-
-    @mock.patch.object(session, 'CONF', stubs.MockConf())
-    def setUp(self):
-        super(LXDTestContainerImage, self).setUp()
-
-        self.tempdir = self.useFixture(fixtures.TempDir()).path
-        self.fixture = self.useFixture(config_fixture.Config(lockutils.CONF))
-        self.fixture.config(lock_path=self.tempdir,
-                            group='oslo_concurrency')
-        self.fixture.config(disable_process_locking=True,
-                            group='oslo_concurrency')
-
-        self.image = image.LXDContainerImage()
-
-    @stubs.annotated_data(
-        ('valid_image_raw', True, {'disk_format': 'raw'}, None),
-        ('valid_image_root-tar', True, {'disk_format': 'root-tar'}, None),
-        ('qcow2_image', False, {'disk_format': 'qcow2'},
-            exception.ImageUnacceptable),
-        ('iso_image', False, {'disk_format': 'iso'},
-            exception.ImageUnacceptable),
-        ('image_unacceptable', False, {'disk_format': ''},
-            exception.ImageUnacceptable),
-        ('bad_meta', False, {},
-            exception.ImageUnacceptable),
-    )
-    def test_image(self, tag, sucess, image_data, expected):
-        context = mock.Mock
-        instance = stubs._fake_instance()
-        with mock.patch.object(image.IMAGE_API, 'get',
-                               return_value=image_data):
-            if sucess:
-                self.assertEqual(expected,
-                                 self.image._verify_image(context, instance))
-            else:
-                self.assertRaises(expected,
-                                  self.image._verify_image, context, instance)
-
-    @mock.patch.object(image.IMAGE_API, 'download')
-    def test_fetch_image(self, mock_download):
-        context = mock.Mock()
-        instance = stubs._fake_instance()
-        self.assertEqual(None,
-                         self.image._fetch_image(context, instance))
-
-    @mock.patch.object(os, 'stat')
-    @mock.patch.object(json, 'dumps')
-    @mock.patch.object(tarfile, 'open')
-    @mock.patch.object(io, 'BytesIO')
-    @mock.patch.object(image.IMAGE_API, 'get')
-    def test_get_lxd_manifest(self, mock_stat, mock_json, mock_tarfile,
-                              mock_io, mock_image):
-        instance = stubs._fake_instance()
-        image_meta = mock.Mock()
-        self.assertEqual(None,
-                         self.image._get_lxd_manifest(instance, image_meta))
diff --git a/nova_lxd/tests/test_migrate.py b/nova_lxd/tests/test_migrate.py
deleted file mode 100644
index 016baef..0000000
--- a/nova_lxd/tests/test_migrate.py
+++ /dev/null
@@ -1,91 +0,0 @@
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import mock
-
-from nova import test
-from nova.virt import fake
-
-from oslo_config import cfg
-
-from nova_lxd.nova.virt.lxd import config
-from nova_lxd.nova.virt.lxd import migrate
-from nova_lxd.nova.virt.lxd import operations
-from nova_lxd.nova.virt.lxd import session
-from nova_lxd.tests import stubs
-
-CONF = cfg.CONF
-CONF.import_opt('my_ip', 'nova.netconf')
-
-
-class LXDTestContainerMigrate(test.NoDBTestCase):
-
-    def setUp(self):
-        super(LXDTestContainerMigrate, self).setUp()
-
-        self.migrate = migrate.LXDContainerMigrate(
-            fake.FakeVirtAPI())
-
-    def test_migrate_disk_power_off_resize(self):
-        self.flags(my_ip='fakeip')
-        instance = stubs._fake_instance()
-        network_info = mock.Mock()
-        flavor = mock.Mock()
-        context = mock.Mock()
-        dest = 'fakeip'
-
-        with test.nested(
-            mock.patch.object(session.LXDAPISession, 'container_defined'),
-            mock.patch.object(config.LXDContainerConfig, 'create_profile'),
-            mock.patch.object(session.LXDAPISession, 'profile_update')
-        ) as (
-            mock_container_defined,
-            mock_create_profile,
-            mock_profile_update
-        ):
-            self.assertEqual('',
-                             self.migrate.migrate_disk_and_power_off(
-                                 context, instance, dest, flavor,
-                                 network_info))
-            mock_container_defined.assert_called_once_with(instance.name,
-                                                           instance)
-            mock_create_profile.assert_called_once_with(instance,
-                                                        network_info)
-
-    def test_confirm_migration(self):
-        migration = mock.Mock()
-        instance = stubs._fake_instance()
-        network_info = mock.Mock()
-
-        with test.nested(
-            mock.patch.object(session.LXDAPISession, 'container_defined'),
-            mock.patch.object(session.LXDAPISession, 'profile_delete'),
-            mock.patch.object(session.LXDAPISession, 'container_destroy'),
-            mock.patch.object(operations.LXDContainerOperations,
-                              'unplug_vifs'),
-        ) as (
-                mock_container_defined,
-                mock_profile_delete,
-                mock_container_destroy,
-                mock_unplug_vifs):
-            self.assertEqual(None,
-                             self.migrate.confirm_migration(migration,
-                                                            instance,
-                                                            network_info))
-            mock_container_defined.assert_called_once_with(instance.name,
-                                                           instance)
-            mock_profile_delete.assert_called_once_with(instance)
-            mock_unplug_vifs.assert_called_once_with(instance,
-                                                     network_info)
diff --git a/nova_lxd/tests/test_operations.py b/nova_lxd/tests/test_operations.py
deleted file mode 100644
index 378fe3d..0000000
--- a/nova_lxd/tests/test_operations.py
+++ /dev/null
@@ -1,241 +0,0 @@
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import ddt
-import mock
-
-from nova import test
-from nova.virt import fake
-
-from nova_lxd.nova.virt.lxd import config
-from nova_lxd.nova.virt.lxd import image
-from nova_lxd.nova.virt.lxd import operations as container_ops
-from nova_lxd.nova.virt.lxd import session
-from nova_lxd.tests import stubs
-
-
- at ddt.ddt
- at mock.patch.object(container_ops, 'CONF', stubs.MockConf())
-class LXDTestContainerOps(test.NoDBTestCase):
-    """LXD Container operations unit tests."""
-
-    def setUp(self):
-        super(LXDTestContainerOps, 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.operations = (
-            container_ops.LXDContainerOperations(fake.FakeVirtAPI()))
-        self.mv = mock.MagicMock()
-        vif_patcher = mock.patch.object(self.operations,
-                                        'vif_driver',
-                                        self.mv)
-        vif_patcher.start()
-        self.addCleanup(vif_patcher.stop)
-
-    def test_spawn_container(self):
-        """Test spawn method. Ensure that the right calls
-           are made when creating a container.
-        """
-        context = mock.Mock()
-        instance = stubs._fake_instance()
-        image_meta = mock.Mock()
-        injected_files = mock.Mock()
-        admin_password = mock.Mock()
-        network_info = mock.Mock()
-        block_device_info = mock.Mock()
-
-        with test.nested(
-            mock.patch.object(session.LXDAPISession, 'container_defined'),
-            mock.patch.object(container_ops.LXDContainerOperations,
-                              '_fetch_image'),
-            mock.patch.object(container_ops.LXDContainerOperations,
-                              '_setup_network'),
-            mock.patch.object(container_ops.LXDContainerOperations,
-                              '_setup_profile'),
-            mock.patch.object(container_ops.LXDContainerOperations,
-                              '_add_configdrive'),
-            mock.patch.object(container_ops.LXDContainerOperations,
-                              '_setup_container')
-        ) as (
-            mock_container_defined,
-            mock_fetch_image,
-            mock_setup_network,
-            mock_setup_profile,
-            mock_add_configdrive,
-            mock_setup_container
-        ):
-            mock_container_defined.return_value = False
-            self.assertEqual(None,
-                             self.operations.spawn(context, instance,
-                                                   image_meta,
-                                                   injected_files,
-                                                   admin_password,
-                                                   network_info,
-                                                   block_device_info))
-
-    def test_reboot_container(self):
-        """Test the reboot method. Ensure that the proper
-           calls are made when rebooting a continer.
-        """
-        instance = stubs._fake_instance()
-        context = mock.Mock()
-        with test.nested(
-            mock.patch.object(session.LXDAPISession, 'container_reboot')
-        ) as (container_reboot):
-            self.assertEqual(None,
-                             self.operations.reboot(context, instance, {},
-                                                    None, None, None))
-            self.assertTrue(container_reboot)
-
-    def test_destroy_container(self):
-        """Test the destroy conainer method. Ensure that
-           the correct calls are made when removing
-           the contianer.
-        """
-        context = mock.Mock()
-        instance = stubs._fake_instance()
-        network_info = mock.Mock()
-
-        with test.nested(
-            mock.patch.object(session.LXDAPISession, 'profile_delete'),
-            mock.patch.object(session.LXDAPISession, 'container_destroy'),
-            mock.patch.object(container_ops.LXDContainerOperations, 'cleanup'),
-        ) as (
-            mock_profile_delete,
-            mock_container_destroy,
-            mock_cleanup
-        ):
-            self.assertEqual(None,
-                             self.operations.destroy(context,
-                                                     instance, network_info))
-            self.assertTrue(mock_profile_delete)
-            self.assertTrue(mock_container_destroy)
-
-    def test_power_off(self):
-        """Test the power_off method. Ensure that the proper
-           calls are made when the container is powered
-           off.
-        """
-        instance = stubs._fake_instance()
-        with test.nested(
-            mock.patch.object(session.LXDAPISession, 'container_stop')
-        ) as (mock_container_stop):
-            self.assertEqual(None,
-                             self.operations.power_off(instance))
-            self.assertTrue(mock_container_stop)
-
-    def test_power_on(self):
-        """test the power_on method. Ensure that the proper
-           calls are made when the container is powered on.
-        """
-        instance = stubs._fake_instance()
-        network_info = mock.Mock()
-        context = mock.Mock()
-        block_device_info = mock.Mock()
-        with test.nested(
-            mock.patch.object(session.LXDAPISession, 'container_start')
-        ) as (mock_container_start):
-            self.assertEqual(None,
-                             self.operations.power_on(context, instance,
-                                                      network_info,
-                                                      block_device_info))
-            self.assertTrue(mock_container_start)
-
-    def test_pause_container(self):
-        """Test the pause container method. Ensure that that
-           the proper calls are made when pausing the container.
-        """
-        instance = stubs._fake_instance()
-        with test.nested(
-            mock.patch.object(session.LXDAPISession, 'container_pause')
-        ) as (mock_container_pause):
-            self.assertEqual(None,
-                             self.operations.pause(instance))
-            self.assertTrue(mock_container_pause)
-
-    def test_unpause_container(self):
-        """Test the unapuse continaer. Ensure that the proper
-           calls are made when unpausing a container.
-        """
-        instance = stubs._fake_instance()
-        with test.nested(
-            mock.patch.object(session.LXDAPISession, 'container_unpause')
-        ) as (mock_container_unpause):
-            self.assertEqual(None,
-                             self.operations.unpause(instance))
-            self.assertTrue(mock_container_unpause)
-
-    def test_container_suspend(self):
-        instance = stubs._fake_instance()
-        context = mock.Mock()
-        with test.nested(
-            mock.patch.object(session.LXDAPISession, 'container_pause')
-        ) as (mock_container_suspend):
-            self.assertEqual(None,
-                             self.operations.suspend(context, instance))
-            self.assertTrue(mock_container_suspend)
-
-    def test_container_resume(self):
-        instance = stubs._fake_instance()
-        context = mock.Mock()
-        network_info = mock.Mock()
-        with test.nested(
-            mock.patch.object(session.LXDAPISession, 'container_unpause')
-        ) as (mock_container_resume):
-            self.assertEqual(None,
-                             self.operations.resume(context, instance,
-                                                    network_info))
-            self.assertTrue(mock_container_resume)
-
-    @mock.patch.object(image.LXDContainerImage, 'setup_image')
-    def test_fetch_image(self, mock_fetch_image):
-        instance = stubs._fake_instance()
-        context = mock.Mock()
-        self.operations._fetch_image(context, instance, {})
-        mock_fetch_image.assert_called_once_with(context, instance, {})
-
-    @mock.patch.object(container_ops.LXDContainerOperations, 'plug_vifs')
-    def test_setup_network(self, mock_plug_vifs):
-        instance = stubs._fake_instance()
-
-        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')
-    def test_setup_container(self, mock_create_container, mock_container_init,
-                             mock_container_start):
-        instance = stubs._fake_instance()
-        self.assertEqual(None,
-                         self.operations._setup_container(instance.name,
-                                                          instance))
diff --git a/nova_lxd/tests/test_vif_api.py b/nova_lxd/tests/test_vif_api.py
deleted file mode 100644
index 1951ef7..0000000
--- a/nova_lxd/tests/test_vif_api.py
+++ /dev/null
@@ -1,164 +0,0 @@
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import copy
-
-import ddt
-import mock
-from oslo_concurrency import processutils
-
-from nova import exception
-from nova.network import model as network_model
-from nova import test
-
-from nova_lxd.nova.virt.lxd import vif
-from nova_lxd.tests import stubs
-
-
- at ddt.ddt
-class LXDTestNetworkDriver(test.NoDBTestCase):
-
-    vif_data = {
-        'id': '0123456789abcdef',
-        'type': network_model.VIF_TYPE_OVS,
-        'address': '00:11:22:33:44:55',
-        'network': {
-            'bridge': 'fakebr'}}
-
-    def setUp(self):
-        super(LXDTestNetworkDriver, self).setUp()
-
-        self.vif_driver = vif.LXDGenericDriver()
-
-        mn = mock.Mock()
-        net_patcher = mock.patch.object(vif, 'linux_net', mn)
-        net_patcher.start()
-        self.addCleanup(net_patcher.stop)
-
-        me = mock.Mock()
-        net_patcher = mock.patch.object(vif.utils, 'execute', me)
-        net_patcher.start()
-        self.addCleanup(net_patcher.stop)
-
-        self.mgr = mock.Mock()
-        self.mgr.attach_mock(mn, 'net')
-        self.mgr.attach_mock(me, 'ex')
-
-    def test_nonetype(self):
-        instance = stubs.MockInstance()
-        vif_data = {'type': None}
-        self.assertRaises(
-            exception.NovaException,
-            self.vif_driver.plug,
-            instance, vif_data)
-
-    def test_get_config_ovs(self):
-        instance = stubs._fake_instance()
-        vif_data = copy.deepcopy(self.vif_data)
-
-        vif_type = self.vif_driver.get_config(instance, vif_data)
-        self.assertEqual(vif_type, {'bridge': 'qbr0123456789a',
-                                    'mac_address': '00:11:22:33:44:55'})
-
-    def test_get_config_bridge(self):
-        instance = stubs._fake_instance()
-        vif_data = copy.deepcopy(self.vif_data)
-
-        vif_type = self.vif_driver.get_config(instance, vif_data)
-        self.assertEqual(vif_type, {'bridge': 'qbr0123456789a',
-                                    'mac_address': '00:11:22:33:44:55'})
-
-    @stubs.annotated_data(
-        ('id', {}, [True, True]),
-        ('ovs-id', {'ovs_interfaceid': '123456789abcdef0'}, [True, True]),
-        ('no-bridge', {}, [False, True]),
-        ('no-v2', {}, [True, False]),
-        ('no-bridge-or-v2', {}, [False, False]),
-    )
-    def test_plug(self, tag, vif_data, exists):
-        instance = stubs.MockInstance()
-        vif_data = copy.deepcopy(self.vif_data)
-        vif_data.update(vif_data)
-        self.mgr.net.device_exists.side_effect = exists
-        self.assertEqual(
-            None,
-            self.vif_driver.plug(instance, vif_data))
-        calls = [
-            mock.call.net.device_exists('qbr0123456789a'),
-            mock.call.net.device_exists('qvo0123456789a')
-        ]
-        if not exists[0]:
-            calls[1:1] = [
-                mock.call.ex(
-                    'brctl', 'addbr', 'qbr0123456789a', run_as_root=True),
-                mock.call.ex(
-                    'brctl', 'setfd', 'qbr0123456789a', 0, run_as_root=True),
-                mock.call.ex('brctl', 'stp', 'qbr0123456789a', 'off',
-                             run_as_root=True),
-                mock.call.ex('tee',
-                             '/sys/class/net/qbr0123456789a/'
-                             'bridge/multicast_snooping',
-                             process_input='0', run_as_root=True,
-                             check_exit_code=[0, 1]),
-            ]
-        if not exists[1]:
-            calls.extend([
-                mock.call.net._create_veth_pair('qvb0123456789a',
-                                                'qvo0123456789a'),
-                mock.call.ex('ip', 'link', 'set', 'qbr0123456789a', 'up',
-                             run_as_root=True),
-                mock.call.ex('brctl', 'addif', 'qbr0123456789a',
-                             'qvb0123456789a', run_as_root=True)])
-            calls.append(mock.call.net.create_ovs_vif_port(
-                'fakebr', 'qvo0123456789a', '0123456789abcdef',
-                '00:11:22:33:44:55', 'fake-uuid'))
-        self.assertEqual(calls, self.mgr.method_calls)
-
-    def test_unplug_fail(self):
-        instance = stubs.MockInstance()
-        vif_data = copy.deepcopy(self.vif_data)
-        self.mgr.net.device_exists.side_effect = (
-            processutils.ProcessExecutionError)
-        self.assertEqual(
-            None,
-            self.vif_driver.unplug(instance, vif_data))
-
-    @stubs.annotated_data(
-        ('id', {}, [True, True]),
-        ('ovs-id', {'ovs_interfaceid': '123456789abcdef0'}, [True, True]),
-        ('no-bridge', {}, [False, True]),
-        ('no-v2', {}, [True, False]),
-        ('no-bridge-or-v2', {}, [False, False]),
-    )
-    def test_unplug(self, tag, vif_data, exists):
-        instance = stubs.MockInstance()
-        vif = copy.deepcopy(self.vif_data)
-        self.mgr.net.device_exists.side_effect = exists
-        self.assertEqual(
-            None,
-            self.vif_driver.unplug(instance, vif))
-
-        calls = [mock.call.net.device_exists('qbr0123456789a')]
-        if exists[0]:
-            calls[1:1] = [
-                mock.call.ex('brctl', 'delif', 'qbr0123456789a',
-                             'qvb0123456789a', run_as_root=True),
-                mock.call.ex('ip', 'link', 'set', 'qbr0123456789a',
-                             'down', run_as_root=True),
-                mock.call.ex('brctl', 'delbr', 'qbr0123456789a',
-                             run_as_root=True),
-                mock.call.net.delete_ovs_vif_port('fakebr', 'qvo0123456789a')
-            ]
-        self.assertEqual(calls, self.mgr.method_calls)
diff --git a/setup.cfg b/setup.cfg
index fd84427..8d18bf1 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -23,7 +23,7 @@ classifier =
 
 [files]
 packages =
-    nova_lxd
+    nova.virt.lxd
 namespace_packages = 
 	nova_lxd
 

From fa110127120d111b7b9614a65dfa84f1f0c62126 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Wed, 11 May 2016 09:58:26 -0400
Subject: [PATCH 02/11] Fix typo in devstack

Fix typo in devstack configuration

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 devstack/plugin.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/devstack/plugin.sh b/devstack/plugin.sh
index bab1818..09699a2 100755
--- a/devstack/plugin.sh
+++ b/devstack/plugin.sh
@@ -13,7 +13,7 @@ NOVA_CONF_DIR=${NOVA_CONF_DIR:-/etc/nova}
 NOVA_CONF=${NOVA_CONF:-NOVA_CONF_DIR/nova.conf}
 
 # nova-powervm directories
-NOVA_COMPUTE_LXD_DIR=${NOVA_POWERVM_DIR:-${DEST}/nova-lxd}
+NOVA_COMPUTE_LXD_DIR=${NOVA_COMPUTE_LXD_DIR:-${DEST}/nova-lxd}
 NOVA_COMPUTE_LXD_PLUGIN_DIR=$(readlink -f $(dirname ${BASH_SOURCE[0]}))
 
 source $NOVA_COMPUTE_LXD_PLUGIN_DIR/nova-lxd-functions.sh

From e905682bcac593e25d578a11154e3acc6db0368b Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Wed, 11 May 2016 10:00:30 -0400
Subject: [PATCH 03/11] Update devstack configuration

Update devstack configuration for newton change

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 devstack/plugin.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/devstack/plugin.sh b/devstack/plugin.sh
index 09699a2..8ddd461 100755
--- a/devstack/plugin.sh
+++ b/devstack/plugin.sh
@@ -31,7 +31,7 @@ function install_nova-lxd() {
 
 function configure_nova-lxd() {
     # Configure the service.
-    iniset $NOVA_CONF DEFAULT compute_driver nova_lxd.nova.virt.lxd.LXDDriver
+    iniset $NOVA_CONF DEFAULT compute_driver lxd.LXDDriver
 }
 
 function init_nova-lxd() {

From ca8aff5729e1d341328850bdf8f5159242ae487f Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Wed, 11 May 2016 10:03:30 -0400
Subject: [PATCH 04/11] Remove linux-image-converter

Remve the linux-image-converter from entry-points since
it no longer exists.

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 setup.cfg | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/setup.cfg b/setup.cfg
index 8d18bf1..e734b11 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -27,10 +27,6 @@ packages =
 namespace_packages = 
 	nova_lxd
 
-[entry_points]
-console_scripts =
-   lxc-image-converter = nova_lxd.cmd.converter:main
-
 [build_sphinx]
 source-dir = doc/source
 build-dir = doc/build

From 6efec0121c9ae1261235caa45d76c806b9b48f68 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Wed, 11 May 2016 10:21:19 -0400
Subject: [PATCH 05/11] Add sample local.conf.sample

Add sample devstack local.config. This configuration
enables neutron and most services.

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 devstack/local.conf.sample | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)
 create mode 100644 devstack/local.conf.sample

diff --git a/devstack/local.conf.sample b/devstack/local.conf.sample
new file mode 100644
index 0000000..f9a6162
--- /dev/null
+++ b/devstack/local.conf.sample
@@ -0,0 +1,24 @@
+[[local|localrc]]
+
+HOST_IP=10.5.18.185 # set this to your IP
+FLAT_INTERFACE=ens2 # change this to your eth0
+
+DATABASE_PASSWORD=password
+RABBIT_PASSWORD=password
+SERVICE_PASSWORD=password
+SERVICE_TOKEN=password
+ADMIN_PASSWORD=password
+
+# run the services you want to use
+ENABLED_SERVICES=rabbit,mysql,key
+ENABLED_SERVICES+=,g-api,g-reg
+ENABLED_SERVICES+=,n-cpu,n-api,n-crt,n-obj,n-cond,n-sch,n-novnc,n-cauth
+ENABLED_SERVICES+=,neutron,q-svc,q-agt,q-dhcp,q-meta
+ENABLED_SERVICES+=,cinder,c-sch,c-api,c-vol
+ENABLED_SERVICES+=,horizon
+
+# disabled services
+disable_service n-net
+
+# enable nova-lxd
+enable_plugin nova-lxd https://github.com/lxc/nova-lxd 

From 3457fa29b43f26ff3104ddabe7d1384202e01fe2 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Wed, 11 May 2016 11:38:15 -0400
Subject: [PATCH 06/11] Clean up setup.cfg

Clean up setup.cfg a bit more so that we arent using
the nova_lxd namespace.

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 setup.cfg | 2 --
 1 file changed, 2 deletions(-)

diff --git a/setup.cfg b/setup.cfg
index e734b11..dd8432d 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -24,8 +24,6 @@ classifier =
 [files]
 packages =
     nova.virt.lxd
-namespace_packages = 
-	nova_lxd
 
 [build_sphinx]
 source-dir = doc/source

From 88e7fdd5682709440ba06c5e066806402be1e98e Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Wed, 11 May 2016 12:57:49 -0400
Subject: [PATCH 07/11] Fix unit tests with py27

Fix building unit tests with py27.

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 .testr.conf                                        |   6 +-
 nova/tests/unit/virt/lxd/session/__init__.py       |   0
 nova/tests/unit/virt/lxd/session/test_container.py | 550 ----------------
 nova/tests/unit/virt/lxd/session/test_event.py     |  46 --
 nova/tests/unit/virt/lxd/session/test_image.py     |  54 --
 nova/tests/unit/virt/lxd/session/test_migrate.py   |  38 --
 nova/tests/unit/virt/lxd/session/test_profile.py   |  79 ---
 nova/tests/unit/virt/lxd/session/test_snapshot.py  |  81 ---
 nova/tests/unit/virt/lxd/test_migrate.py           |   7 +-
 nova/tests/unit/virt/lxd/test_session.py           | 711 +++++++++++++++++++++
 nova/virt/lxd/config.py                            |   5 +-
 nova/virt/lxd/migrate.py                           |   4 +-
 nova/virt/lxd/session.py                           |   4 +-
 tox.ini                                            |   4 +
 14 files changed, 727 insertions(+), 862 deletions(-)
 delete mode 100644 nova/tests/unit/virt/lxd/session/__init__.py
 delete mode 100644 nova/tests/unit/virt/lxd/session/test_container.py
 delete mode 100644 nova/tests/unit/virt/lxd/session/test_event.py
 delete mode 100644 nova/tests/unit/virt/lxd/session/test_image.py
 delete mode 100644 nova/tests/unit/virt/lxd/session/test_migrate.py
 delete mode 100644 nova/tests/unit/virt/lxd/session/test_profile.py
 delete mode 100644 nova/tests/unit/virt/lxd/session/test_snapshot.py
 create mode 100644 nova/tests/unit/virt/lxd/test_session.py

diff --git a/.testr.conf b/.testr.conf
index fb62267..cd0c12c 100644
--- a/.testr.conf
+++ b/.testr.conf
@@ -2,6 +2,8 @@
 test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
              OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
              OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \
-             ${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION
+             ${PYTHON:-python} -m subunit.run discover \
+             -t ./nova/tests/unit/virt/lxd/ ./nova/tests/unit/virt/lxd \
+             $LISTOPT $IDOPTION
 test_id_option=--load-list $IDFILE
-test_list_option=--list
\ No newline at end of file
+test_list_option=--list
diff --git a/nova/tests/unit/virt/lxd/session/__init__.py b/nova/tests/unit/virt/lxd/session/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/nova/tests/unit/virt/lxd/session/test_container.py b/nova/tests/unit/virt/lxd/session/test_container.py
deleted file mode 100644
index 69690f7..0000000
--- a/nova/tests/unit/virt/lxd/session/test_container.py
+++ /dev/null
@@ -1,550 +0,0 @@
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-#    implied. See the License for the specific language governing
-#    permissions and limitations under the License.
-
-"""
-Unit tests for ContinerMixin class
-
-The following tests the ContainerMixin class
-for nova-lxd.
-"""
-
-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_lxd.nova.virt.lxd import session
-from nova_lxd.tests import fake_api
-from nova_lxd.tests import stubs
-
-
- at ddt.ddt
-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_defined(instance.name),
-            mock.call.container_update(instance.name, config)]
-        self.assertEqual(calls, self.ml.method_calls)
-
-    @stubs.annotated_data(
-        ('api_fail', True, lxd_exceptions.APIError('Fake', 500),
-         exception.NovaException),
-        ('missing_container', False, None,
-         exception.InstanceNotFound)
-    )
-    def test_container_update_fail(self, tag, container_defined, 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()
-        if container_defined:
-            self.ml.container_defined.return_value = container_defined
-            self.ml.container_update.side_effect = (
-                lxd_exceptions.APIError('Fake', 500))
-            self.assertRaises(
-                expected,
-                self.session.container_update, config, instance)
-        if not container_defined:
-            self.ml.container_defined.return_value = container_defined
-            self.assertRaises(
-                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
-        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_defined.return_value = defined
-        self.ml.container_start.return_value = side_effect
-        self.assertEqual(None,
-                         self.session.container_start(instance.name,
-                                                      instance))
-        calls = [mock.call.container_defined(instance.name),
-                 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(
-        ('container_missing', False,
-         exception.InstanceNotFound),
-        ('api_error', True,
-         exception.NovaException,
-         lxd_exceptions.APIError('Fake', 500)),
-    )
-    def test_container_start_fail(self, tag, container_defined,
-                                  expected, side_effect=None):
-        """
-        container_start starts a container on a given LXD host.
-        Veify that an exception.InstanceNotFound when the container
-        is not found on an LXD host. Raises an exception.NovaException
-        when there is an APIError.
-        """
-        instance = stubs._fake_instance()
-        if container_defined:
-            self.ml.container_defined.return_value = container_defined
-            self.ml.container_start.side_effect = side_effect
-            self.assertRaises(expected,
-                              self.session.container_start,
-                              instance.name, instance)
-        if not container_defined:
-            self.ml.container_defined.return_value = container_defined
-            self.assertRaises(expected,
-                              self.session.container_start, instance.name,
-                              instance)
-
-    @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_defined(instance.name),
-                 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_defined(instance.name),
-                 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', True, (200, fake_api.fake_operation_info_ok())),
-        ('missing', False, (200, fake_api.fake_operation_info_ok()))
-    )
-    def test_container_destroy(self, tag, container_defined, side_effect):
-        """
-        container_destroy delete a container from the LXD Host. Check
-        that the approiate pylxd calls are made.
-        """
-        instance = stubs._fake_instance()
-        if container_defined:
-            self.ml.container_defined.return_value = container_defined
-            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_defined(instance.name),
-                     mock.call.container_defined(instance.name),
-                     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)
-        if not container_defined:
-            self.ml.container_defined.return_value = container_defined
-            self.assertEqual(None,
-                             self.session.container_destroy(instance.name,
-                                                            instance))
-            calls = [mock.call.container_defined(instance.name)]
-            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_defined.return_value = container_defined
-            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_defined(instance.name),
-            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)
diff --git a/nova/tests/unit/virt/lxd/session/test_event.py b/nova/tests/unit/virt/lxd/session/test_event.py
deleted file mode 100644
index f50b879..0000000
--- a/nova/tests/unit/virt/lxd/session/test_event.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-#    implied. See the License for the specific language governing
-#    permissions and limitations under the License.
-
-import ddt
-import mock
-
-from nova import test
-
-from nova_lxd.nova.virt.lxd import session
-from nova_lxd.tests import stubs
-
-
- at ddt.ddt
-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)
diff --git a/nova/tests/unit/virt/lxd/session/test_image.py b/nova/tests/unit/virt/lxd/session/test_image.py
deleted file mode 100644
index fa0c6dc..0000000
--- a/nova/tests/unit/virt/lxd/session/test_image.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-#    implied. See the License for the specific language governing
-#    permissions and limitations under the License.
-
-import ddt
-import mock
-
-from nova import test
-
-from nova_lxd.nova.virt.lxd import session
-from nova_lxd.tests import stubs
-
-
- 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)
diff --git a/nova/tests/unit/virt/lxd/session/test_migrate.py b/nova/tests/unit/virt/lxd/session/test_migrate.py
deleted file mode 100644
index 9f96638..0000000
--- a/nova/tests/unit/virt/lxd/session/test_migrate.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-#    implied. See the License for the specific language governing
-#    permissions and limitations under the License.
-
-import ddt
-import mock
-
-from nova import test
-
-from nova_lxd.nova.virt.lxd import session
-from nova_lxd.tests import stubs
-
-
- at ddt.ddt
-class SessionMigrateTest(test.NoDBTestCase):
-
-    def setUp(self):
-        super(SessionMigrateTest, 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()
diff --git a/nova/tests/unit/virt/lxd/session/test_profile.py b/nova/tests/unit/virt/lxd/session/test_profile.py
deleted file mode 100644
index 2680849..0000000
--- a/nova/tests/unit/virt/lxd/session/test_profile.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-#    implied. See the License for the specific language governing
-#    permissions and limitations under the License.
-
-
-import ddt
-import mock
-
-from nova import exception
-from nova import test
-from pylxd.deprecated import exceptions as lxd_exceptions
-
-from nova_lxd.nova.virt.lxd import session
-from nova_lxd.tests import fake_api
-from nova_lxd.tests import stubs
-
-
- at ddt.ddt
-class SessionProfileTest(test.NoDBTestCase):
-
-    def setUp(self):
-        super(SessionProfileTest, self).setUp()
-
-        """This is so we can mock out pylxd API calls."""
-        self.ml = stubs.lxd_mock()
-        lxd_patcher = mock.patch('pylxd.api.API',
-                                 mock.Mock(return_value=self.ml))
-        lxd_patcher.start()
-        self.addCleanup(lxd_patcher.stop)
-
-        self.session = session.LXDAPISession()
-
-    @stubs.annotated_data(
-        ('empty', [], []),
-        ('valid', ['test'], ['test']),
-    )
-    def test_profile_list(self, tag, side_effect, expected):
-        self.ml.profile_list.return_value = side_effect
-        self.assertEqual(expected,
-                         self.session.profile_list())
-
-    def test_profile_list_fail(self):
-        self.ml.profile_list.side_effect = (
-            lxd_exceptions.APIError('Fake', 500))
-        self.assertRaises(
-            exception.NovaException,
-            self.session.profile_list)
-
-    def test_profile_create(self):
-        instance = stubs._fake_instance()
-        config = mock.Mock()
-        self.ml.profile_defined.return_value = True
-        self.ml.profile_create.return_value = \
-            (200, fake_api.fake_standard_return())
-        self.assertEqual((200, fake_api.fake_standard_return()),
-                         self.session.profile_create(config,
-                                                     instance))
-        calls = [mock.call.profile_list(),
-                 mock.call.profile_create(config)]
-        self.assertEqual(calls, self.ml.method_calls)
-
-    def test_profile_delete(self):
-        instance = stubs._fake_instance()
-        self.ml.profile_defined.return_value = True
-        self.ml.profile_delete.return_value = \
-            (200, fake_api.fake_standard_return())
-        self.assertEqual(None,
-                         self.session.profile_delete(instance))
diff --git a/nova/tests/unit/virt/lxd/session/test_snapshot.py b/nova/tests/unit/virt/lxd/session/test_snapshot.py
deleted file mode 100644
index 61759d6..0000000
--- a/nova/tests/unit/virt/lxd/session/test_snapshot.py
+++ /dev/null
@@ -1,81 +0,0 @@
-# Copyright 2015 Canonical Ltd
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-#    implied. See the License for the specific language governing
-#    permissions and limitations under the License.
-
-import ddt
-import mock
-
-from nova import exception
-from nova import test
-from pylxd.deprecated import exceptions as lxd_exceptions
-
-from nova_lxd.nova.virt.lxd import session
-from nova_lxd.tests import fake_api
-from nova_lxd.tests import stubs
-
-
- at ddt.ddt
-class 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)
diff --git a/nova/tests/unit/virt/lxd/test_migrate.py b/nova/tests/unit/virt/lxd/test_migrate.py
index 6cf3255..fc20bf0 100644
--- a/nova/tests/unit/virt/lxd/test_migrate.py
+++ b/nova/tests/unit/virt/lxd/test_migrate.py
@@ -15,20 +15,17 @@
 
 import mock
 
+import nova.conf
 from nova import test
 from nova.virt import fake
 
-from oslo_config import cfg
-
 from nova.virt.lxd import config
 from nova.virt.lxd import migrate
 from nova.virt.lxd import operations
 from nova.virt.lxd import session
 import stubs
 
-CONF = cfg.CONF
-CONF.import_opt('my_ip', 'nova.netconf')
-
+CONF = nova.conf.CONF
 
 class LXDTestContainerMigrate(test.NoDBTestCase):
 
diff --git a/nova/tests/unit/virt/lxd/test_session.py b/nova/tests/unit/virt/lxd/test_session.py
new file mode 100644
index 0000000..d665441
--- /dev/null
+++ b/nova/tests/unit/virt/lxd/test_session.py
@@ -0,0 +1,711 @@
+# Copyright 2015 Canonical Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS,
+#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+#    implied. See the License for the specific language governing
+#    permissions and limitations under the License.
+
+"""
+Unit tests for ContinerMixin class
+
+The following tests the ContainerMixin class
+for nova-lxd.
+"""
+
+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
+
+
+ at ddt.ddt
+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_defined(instance.name),
+            mock.call.container_update(instance.name, config)]
+        self.assertEqual(calls, self.ml.method_calls)
+
+    @stubs.annotated_data(
+        ('api_fail', True, lxd_exceptions.APIError('Fake', 500),
+         exception.NovaException),
+        ('missing_container', False, None,
+         exception.InstanceNotFound)
+    )
+    def test_container_update_fail(self, tag, container_defined, 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()
+        if container_defined:
+            self.ml.container_defined.return_value = container_defined
+            self.ml.container_update.side_effect = (
+                lxd_exceptions.APIError('Fake', 500))
+            self.assertRaises(
+                expected,
+                self.session.container_update, config, instance)
+        if not container_defined:
+            self.ml.container_defined.return_value = container_defined
+            self.assertRaises(
+                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
+        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_defined.return_value = defined
+        self.ml.container_start.return_value = side_effect
+        self.assertEqual(None,
+                         self.session.container_start(instance.name,
+                                                      instance))
+        calls = [mock.call.container_defined(instance.name),
+                 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(
+        ('container_missing', False,
+         exception.InstanceNotFound),
+        ('api_error', True,
+         exception.NovaException,
+         lxd_exceptions.APIError('Fake', 500)),
+    )
+    def test_container_start_fail(self, tag, container_defined,
+                                  expected, side_effect=None):
+        """
+        container_start starts a container on a given LXD host.
+        Veify that an exception.InstanceNotFound when the container
+        is not found on an LXD host. Raises an exception.NovaException
+        when there is an APIError.
+        """
+        instance = stubs._fake_instance()
+        if container_defined:
+            self.ml.container_defined.return_value = container_defined
+            self.ml.container_start.side_effect = side_effect
+            self.assertRaises(expected,
+                              self.session.container_start,
+                              instance.name, instance)
+        if not container_defined:
+            self.ml.container_defined.return_value = container_defined
+            self.assertRaises(expected,
+                              self.session.container_start, instance.name,
+                              instance)
+
+    @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_defined(instance.name),
+                 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_defined(instance.name),
+                 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', True, (200, fake_api.fake_operation_info_ok())),
+        ('missing', False, (200, fake_api.fake_operation_info_ok()))
+    )
+    def test_container_destroy(self, tag, container_defined, side_effect):
+        """
+        container_destroy delete a container from the LXD Host. Check
+        that the approiate pylxd calls are made.
+        """
+        instance = stubs._fake_instance()
+        if container_defined:
+            self.ml.container_defined.return_value = container_defined
+            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_defined(instance.name),
+                     mock.call.container_defined(instance.name),
+                     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)
+        if not container_defined:
+            self.ml.container_defined.return_value = container_defined
+            self.assertEqual(None,
+                             self.session.container_destroy(instance.name,
+                                                            instance))
+            calls = [mock.call.container_defined(instance.name)]
+            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_defined.return_value = container_defined
+            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_defined(instance.name),
+            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):
+
+        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)
+
+    @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):
+
+        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):
+
+        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)
diff --git a/nova/virt/lxd/config.py b/nova/virt/lxd/config.py
index 8641d06..555f5cb 100644
--- a/nova/virt/lxd/config.py
+++ b/nova/virt/lxd/config.py
@@ -16,11 +16,11 @@
 
 import socket
 
+import nova.conf
 from nova import exception
 from nova import i18n
 from nova.virt import configdrive
 
-from oslo_config import cfg
 from oslo_log import log as logging
 from oslo_utils import excutils
 
@@ -32,8 +32,7 @@
 _LE = i18n._LE
 _LI = i18n._LI
 
-CONF = cfg.CONF
-CONF.import_opt('my_ip', 'nova.netconf')
+CONF = nova.conf.CONF
 LOG = logging.getLogger(__name__)
 
 
diff --git a/nova/virt/lxd/migrate.py b/nova/virt/lxd/migrate.py
index ab1927c..a5a3344 100644
--- a/nova/virt/lxd/migrate.py
+++ b/nova/virt/lxd/migrate.py
@@ -15,6 +15,7 @@
 
 import os
 
+import nova.conf
 from nova import exception
 from nova import i18n
 from nova import utils
@@ -35,8 +36,7 @@
 _LE = i18n._LE
 _LI = i18n._LI
 
-CONF = cfg.CONF
-CONF.import_opt('my_ip', 'nova.netconf')
+CONF = nova.conf.CONF
 LOG = logging.getLogger(__name__)
 
 
diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index e1298eb..f572d6d 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -14,6 +14,7 @@
 #    the License for the specific language governing permissions and
 #    limitations under the License.
 
+import nova.conf
 from nova import context as nova_context
 from nova import exception
 from nova import i18n
@@ -37,8 +38,7 @@
 _LE = i18n._LE
 _LI = i18n._LI
 
-CONF = cfg.CONF
-CONF.import_opt('host', 'nova.netconf')
+CONF = nova.conf.CONF
 LOG = logging.getLogger(__name__)
 
 
diff --git a/tox.ini b/tox.ini
index b412b99..9426e2c 100644
--- a/tox.ini
+++ b/tox.ini
@@ -18,6 +18,10 @@ deps = -r{toxinidir}/requirements.txt
        -egit+https://github.com/openstack/nova#egg=nova
 commands = ostestr {posargs}
 
+[testenv:py27]
+commands = /bin/cp -r {toxinidir}/nova/virt/lxd/ {toxinidir}/.tox/py27/src/nova/nova/virt/
+           python setup.py testr --slowest --testr-args='{posargs}'
+
 [testenv:pep8]
 commands = flake8
 

From 7c4d949f6fd14576237f707458dbff5853659ef8 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Wed, 11 May 2016 13:30:10 -0400
Subject: [PATCH 08/11] Fid unit tests to build with py34

Update tox.ini to work with py34.

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 tox.ini | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/tox.ini b/tox.ini
index 9426e2c..7f15fa7 100644
--- a/tox.ini
+++ b/tox.ini
@@ -22,6 +22,10 @@ commands = ostestr {posargs}
 commands = /bin/cp -r {toxinidir}/nova/virt/lxd/ {toxinidir}/.tox/py27/src/nova/nova/virt/
            python setup.py testr --slowest --testr-args='{posargs}'
 
+[testenv:py34]
+commands = /bin/cp -r {toxinidir}/nova/virt/lxd/ {toxinidir}/.tox/py35/src/nova/nova/virt/
+           python setup.py testr --slowest --testr-args='{posargs}'
+
 [testenv:pep8]
 commands = flake8
 

From 4d9972412d8206dca61d93e33ead5d5a8621f6e0 Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Wed, 11 May 2016 13:52:02 -0400
Subject: [PATCH 09/11] Fix pep8

Copy and paste can be hard.

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 nova/tests/unit/virt/lxd/test_migrate.py |   1 +
 nova/tests/unit/virt/lxd/test_session.py | 297 ++++++++++++++++---------------
 nova/virt/lxd/migrate.py                 |   1 -
 nova/virt/lxd/session.py                 |   1 -
 4 files changed, 151 insertions(+), 149 deletions(-)

diff --git a/nova/tests/unit/virt/lxd/test_migrate.py b/nova/tests/unit/virt/lxd/test_migrate.py
index fc20bf0..fe8e4dc 100644
--- a/nova/tests/unit/virt/lxd/test_migrate.py
+++ b/nova/tests/unit/virt/lxd/test_migrate.py
@@ -27,6 +27,7 @@
 
 CONF = nova.conf.CONF
 
+
 class LXDTestContainerMigrate(test.NoDBTestCase):
 
     def setUp(self):
diff --git a/nova/tests/unit/virt/lxd/test_session.py b/nova/tests/unit/virt/lxd/test_session.py
index d665441..e1ff300 100644
--- a/nova/tests/unit/virt/lxd/test_session.py
+++ b/nova/tests/unit/virt/lxd/test_session.py
@@ -549,163 +549,166 @@ def test_container_init_fail(self, tag, side_effect, expected):
                           self.session.container_init, config,
                           instance)
 
-    @ddt.ddt
-    class SessionEventTest(test.NoDBTestCase):
 
-        def setUp(self):
-            super(SessionEventTest, self).setUp()
+ at ddt.ddt
+class SessionEventTest(test.NoDBTestCase):
 
-            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)
+    def setUp(self):
+        super(SessionEventTest, self).setUp()
 
-            self.session = session.LXDAPISession()
+        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)
 
-        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)
-
-    @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)
+        self.session = session.LXDAPISession()
 
-        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)
+    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)
 
-    @ddt.ddt
-    class SessionProfileTest(test.NoDBTestCase):
 
-        def setUp(self):
-            super(SessionProfileTest, self).setUp()
+ at ddt.ddt
+class SessionImageTest(test.NoDBTestCase):
 
-            """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)
+    def setUp(self):
+        super(SessionImageTest, self).setUp()
 
-            self.session = session.LXDAPISession()
+        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)
 
-        @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())
+        self.session = session.LXDAPISession()
 
-        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_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_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):
-
-        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)
+    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)
 
-        @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)
+
+ at ddt.ddt
+class SessionProfileTest(test.NoDBTestCase):
+
+    def setUp(self):
+        super(SessionProfileTest, self).setUp()
+
+        """This is so we can mock out pylxd API calls."""
+        self.ml = stubs.lxd_mock()
+        lxd_patcher = mock.patch('pylxd.api.API',
+                                 mock.Mock(return_value=self.ml))
+        lxd_patcher.start()
+        self.addCleanup(lxd_patcher.stop)
+
+        self.session = session.LXDAPISession()
+
+    @stubs.annotated_data(
+        ('empty', [], []),
+        ('valid', ['test'], ['test']),
+    )
+    def test_profile_list(self, tag, side_effect, expected):
+        self.ml.profile_list.return_value = side_effect
+        self.assertEqual(expected,
+                         self.session.profile_list())
+
+    def test_profile_list_fail(self):
+        self.ml.profile_list.side_effect = (
+            lxd_exceptions.APIError('Fake', 500))
+        self.assertRaises(
+            exception.NovaException,
+            self.session.profile_list)
+
+    def test_profile_create(self):
+        instance = stubs._fake_instance()
+        config = mock.Mock()
+        self.ml.profile_defined.return_value = True
+        self.ml.profile_create.return_value = \
+            (200, fake_api.fake_standard_return())
+        self.assertEqual((200, fake_api.fake_standard_return()),
+                         self.session.profile_create(config,
+                                                     instance))
+        calls = [mock.call.profile_list(),
+                 mock.call.profile_create(config)]
+        self.assertEqual(calls, self.ml.method_calls)
+
+    def test_profile_delete(self):
+        instance = stubs._fake_instance()
+        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))
+
+
+ at ddt.ddt
+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)
diff --git a/nova/virt/lxd/migrate.py b/nova/virt/lxd/migrate.py
index a5a3344..d8978d2 100644
--- a/nova/virt/lxd/migrate.py
+++ b/nova/virt/lxd/migrate.py
@@ -21,7 +21,6 @@
 from nova import utils
 from nova.virt import configdrive
 
-from oslo_config import cfg
 from oslo_log import log as logging
 from oslo_utils import excutils
 from oslo_utils import fileutils
diff --git a/nova/virt/lxd/session.py b/nova/virt/lxd/session.py
index f572d6d..d7584ff 100644
--- a/nova/virt/lxd/session.py
+++ b/nova/virt/lxd/session.py
@@ -23,7 +23,6 @@
 from nova.compute import power_state
 
 from oslo_concurrency import processutils
-from oslo_config import cfg
 from oslo_log import log as logging
 from oslo_service import loopingcall
 from oslo_utils import excutils

From db347f38ed374320b6b8310a6117604be5c42bce Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Wed, 11 May 2016 14:04:47 -0400
Subject: [PATCH 10/11] Disable config drive by default

Turn off config drive since there is a buglet with
the config drive enabled.

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 devstack/plugin.sh | 1 +
 1 file changed, 1 insertion(+)

diff --git a/devstack/plugin.sh b/devstack/plugin.sh
index 8ddd461..b0aeb92 100755
--- a/devstack/plugin.sh
+++ b/devstack/plugin.sh
@@ -32,6 +32,7 @@ function install_nova-lxd() {
 function configure_nova-lxd() {
     # Configure the service.
     iniset $NOVA_CONF DEFAULT compute_driver lxd.LXDDriver
+    iniset $NOVA_CONF DEFAULT force_config_drive False
 }
 
 function init_nova-lxd() {

From b546d6e2428497054adcd2b0c9f3f595261ae66c Mon Sep 17 00:00:00 2001
From: Chuck Short <chuck.short at canonical.com>
Date: Wed, 11 May 2016 14:07:14 -0400
Subject: [PATCH 11/11] Update travis

Update travis to use tox.

Signed-off-by: Chuck Short <chuck.short at canonical.com>
---
 .travis.yml | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 0b2e499..c1c5082 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,10 +1,9 @@
 language: python
-python:
-  - "3.4"
-  - "2.7"
-install: pip install -U -r requirements.txt -r test-requirements.txt git+https://github.com/lxc/pylxd#egg=pylxd git+https://github.com/openstack/nova#egg=nova
+install:
+ - pip install tox 
+ - pip install -U -r requirements.txt -r test-requirements.txt git+https://github.com/lxc/pylxd#egg=pylxd git+https://github.com/openstack/nova#egg=nova
 before_script: flake8 --ignore=E123,E125,H904,H405,H404,H305,H306,H307
-script: ostestr
+script: tox
 cache:
     directories:
         - $HOME/.cache/pip


More information about the lxc-devel mailing list