[lxc-devel] [pylxd/master] Deprecation warning
rockstar on Github
lxc-bot at linuxcontainers.org
Wed Feb 17 03:32:28 UTC 2016
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 610 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20160217/9be7a069/attachment.bin>
-------------- next part --------------
From 28321e7b0b0dd30aa5468c3f02e87a1a24a6f9d0 Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul at eventuallyanyway.com>
Date: Tue, 16 Feb 2016 19:03:10 -0700
Subject: [PATCH 01/15] Move api to the deprecated module
---
pylxd/__init__.py | 2 +
pylxd/api.py | 302 -------------------------------------------
pylxd/deprecated/__init__.py | 0
pylxd/deprecated/api.py | 302 +++++++++++++++++++++++++++++++++++++++++++
4 files changed, 304 insertions(+), 302 deletions(-)
delete mode 100644 pylxd/api.py
create mode 100644 pylxd/deprecated/__init__.py
create mode 100644 pylxd/deprecated/api.py
diff --git a/pylxd/__init__.py b/pylxd/__init__.py
index 8de5848..0ba37c6 100644
--- a/pylxd/__init__.py
+++ b/pylxd/__init__.py
@@ -15,3 +15,5 @@
import pbr.version
__version__ = pbr.version.VersionInfo('pylxd').version_string()
+
+from pylxd.deprecated import api # NOQA
diff --git a/pylxd/api.py b/pylxd/api.py
deleted file mode 100644
index d1be4c4..0000000
--- a/pylxd/api.py
+++ /dev/null
@@ -1,302 +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 pylxd import certificate
-from pylxd import connection
-from pylxd import container
-from pylxd import hosts
-from pylxd import image
-from pylxd import network
-from pylxd import operation
-from pylxd import profiles
-
-
-class API(object):
-
- def __init__(self, host=None, port=8443):
- conn = self.connection = connection.LXDConnection(host=host, port=port)
- self.hosts = hosts.LXDHost(conn)
- self.image = image.LXDImage(conn)
- self.alias = image.LXDAlias(conn)
- self.network = network.LXDNetwork(conn)
- self.operation = operation.LXDOperation(conn)
- self.profiles = profiles.LXDProfile(conn)
- self.certificate = certificate.LXDCertificate(conn)
- self.container = container.LXDContainer(conn)
-
- # host
- def host_ping(self):
- return self.hosts.host_ping()
-
- def host_info(self):
- return self.hosts.host_info()
-
- def get_lxd_api_compat(self, data=None):
- return self.hosts.get_lxd_api_compat(data)
-
- def get_lxd_host_trust(self, data=None):
- return self.hosts.get_lxd_host_trust(data)
-
- def get_lxd_backing_fs(self, data=None):
- return self.hosts.get_lxd_backing_fs(data)
-
- def get_lxd_driver(self, data=None):
- return self.hosts.get_lxd_driver(data)
-
- def get_lxc_version(self, data=None):
- return self.hosts.get_lxc_version(data)
-
- def get_lxd_version(self, data=None):
- return self.hosts.get_lxd_version(data)
-
- def get_kernel_version(self, data=None):
- return self.hosts.get_kernel_version(data)
-
- # images
- def image_list(self):
- return self.image.image_list()
-
- def image_defined(self, image):
- return self.image.image_defined(image)
-
- def image_search(self, params):
- return self.image.image_list_by_key(params)
-
- def image_info(self, image):
- return self.image.image_info(image)
-
- def image_upload_date(self, image, data=None):
- return self.image.get_image_date(image, data, 'uploaded_at')
-
- def image_create_date(self, image, data=None):
- return self.image.get_image_date(image, data, 'created_at')
-
- def image_expire_date(self, image, data=None):
- return self.image.get_image_date(image, data, 'expires_at')
-
- def image_upload(self, path=None, data=None, headers={}):
- return self.image.image_upload(path=path, data=data, headers=headers)
-
- def image_delete(self, image):
- return self.image.image_delete(image)
-
- def image_export(self, image):
- return self.image.image_export(image)
-
- def image_update(self, image, data):
- return self.image.image_update(image, data)
-
- def image_rename(self, image, data):
- return self.image.image_rename(image, data)
-
- # alias
- def alias_list(self):
- return self.alias.alias_list()
-
- def alias_defined(self, alias):
- return self.alias.alias_defined(alias)
-
- def alias_create(self, data):
- return self.alias.alias_create(data)
-
- def alias_update(self, alias, data):
- return self.alias.alias_update(alias, data)
-
- def alias_show(self, alias):
- return self.alias.alias_show(alias)
-
- def alias_rename(self, alias, data):
- return self.alias.alias_rename(alias, data)
-
- def alias_delete(self, alias):
- return self.alias.alias_delete(alias)
-
- # containers:
- def container_list(self):
- return self.container.container_list()
-
- def container_defined(self, container):
- return self.container.container_defined(container)
-
- def container_running(self, container):
- return self.container.container_running(container)
-
- def container_init(self, container):
- return self.container.container_init(container)
-
- def container_update(self, container, config):
- return self.container.container_update(container, config)
-
- def container_state(self, container):
- return self.container.container_state(container)
-
- def container_start(self, container, timeout):
- return self.container.container_start(container, timeout)
-
- def container_stop(self, container, timeout):
- return self.container.container_stop(container, timeout)
-
- def container_suspend(self, container, timeout):
- return self.container.container_suspend(container, timeout)
-
- def container_resume(self, container, timeout):
- return self.container.container_resume(container, timeout)
-
- def container_reboot(self, container, timeout):
- return self.container.container_reboot(container, timeout)
-
- def container_destroy(self, container):
- return self.container.container_destroy(container)
-
- def get_container_log(self, container):
- return self.container.get_container_log(container)
-
- def get_container_config(self, container):
- return self.container.get_container_config(container)
-
- def get_container_websocket(self, container):
- return self.container.get_container_websocket(container)
-
- def container_info(self, container):
- return self.container.container_info(container)
-
- def container_local_copy(self, container):
- return self.container.container_local_copy(container)
-
- def container_local_move(self, instance, container):
- return self.container.container_local_move(instance, container)
-
- # file operations
- def get_container_file(self, container, filename):
- return self.container.get_container_file(container, filename)
-
- def container_publish(self, container):
- return self.container.container_publish(container)
-
- def put_container_file(self, container, src_file,
- dst_file, uid=0, gid=0, mode=0o644):
- return self.container.put_container_file(
- container, src_file, dst_file, uid, gid, mode)
-
- # snapshots
- def container_snapshot_list(self, container):
- return self.container.snapshot_list(container)
-
- def container_snapshot_create(self, container, config):
- return self.container.snapshot_create(container, config)
-
- def container_snapshot_info(self, container, snapshot):
- return self.container.snapshot_info(container, snapshot)
-
- def container_snapshot_rename(self, container, snapshot, config):
- return self.container.snapshot_rename(container, snapshot, config)
-
- def container_snapshot_delete(self, container, snapshot):
- return self.container.snapshot_delete(container, snapshot)
-
- def container_migrate(self, container):
- return self.container.container_migrate(container)
-
- def container_migrate_sync(self, operation_id, container_secret):
- return self.container.container_migrate_sync(
- operation_id, container_secret)
-
- # misc container
- def container_run_command(self, container, args, interactive=False,
- web_sockets=False, env=None):
- return self.container.run_command(container, args, interactive,
- web_sockets, env)
-
- # certificates
- def certificate_list(self):
- return self.certificate.certificate_list()
-
- def certificate_show(self, fingerprint):
- return self.certificate.certificate_show(fingerprint)
-
- def certificate_delete(self, fingerprint):
- return self.certificate.certificate_delete(fingerprint)
-
- def certificate_create(self, fingerprint):
- return self.certificate.certificate_create(fingerprint)
-
- # profiles
- def profile_create(self, profile):
- '''Create LXD profile'''
- return self.profiles.profile_create(profile)
-
- def profile_show(self, profile):
- '''Show LXD profile'''
- return self.profiles.profile_show(profile)
-
- def profile_defined(self, profile):
- '''Check to see if profile is defined'''
- return self.profiles.profile_defined(profile)
-
- def profile_list(self):
- '''List LXD profiles'''
- return self.profiles.profile_list()
-
- def profile_update(self, profile, config):
- '''Update LXD profile'''
- return self.profiles.profile_update(profile, config)
-
- def profile_rename(self, profile, config):
- '''Rename LXD profile'''
- return self.profiles.profile_rename(profile, config)
-
- def profile_delete(self, profile):
- '''Delete LXD profile'''
- return self.profiles.profile_delete(profile)
-
- # lxd operations
- def list_operations(self):
- return self.operation.operation_list()
-
- def wait_container_operation(self, operation, status_code, timeout):
- return self.operation.operation_wait(operation, status_code, timeout)
-
- def operation_delete(self, operation):
- return self.operation.operation_delete(operation)
-
- def operation_info(self, operation):
- return self.operation.operation_info(operation)
-
- def operation_show_create_time(self, operation, data=None):
- return self.operation.operation_create_time(operation, data)
-
- def operation_show_update_time(self, operation, data=None):
- return self.operation.operation_update_time(operation, data)
-
- def operation_show_status(self, operation, data=None):
- return self.operation.operation_status_code(operation, data)
-
- def operation_stream(self, operation, operation_secret):
- return self.operation.operation_stream(operation, operation_secret)
-
- # networks
- def network_list(self):
- return self.network.network_list()
-
- def network_show(self, network):
- return self.network.network_show(network)
-
- def network_show_name(self, network, data=None):
- return self.network.show_network_name(network, data)
-
- def network_show_type(self, network, data=None):
- return self.network.show_network_type(network, data)
-
- def network_show_members(self, network, data=None):
- return self.network.show_network_members(network, data)
diff --git a/pylxd/deprecated/__init__.py b/pylxd/deprecated/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/pylxd/deprecated/api.py b/pylxd/deprecated/api.py
new file mode 100644
index 0000000..d1be4c4
--- /dev/null
+++ b/pylxd/deprecated/api.py
@@ -0,0 +1,302 @@
+
+# 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 pylxd import certificate
+from pylxd import connection
+from pylxd import container
+from pylxd import hosts
+from pylxd import image
+from pylxd import network
+from pylxd import operation
+from pylxd import profiles
+
+
+class API(object):
+
+ def __init__(self, host=None, port=8443):
+ conn = self.connection = connection.LXDConnection(host=host, port=port)
+ self.hosts = hosts.LXDHost(conn)
+ self.image = image.LXDImage(conn)
+ self.alias = image.LXDAlias(conn)
+ self.network = network.LXDNetwork(conn)
+ self.operation = operation.LXDOperation(conn)
+ self.profiles = profiles.LXDProfile(conn)
+ self.certificate = certificate.LXDCertificate(conn)
+ self.container = container.LXDContainer(conn)
+
+ # host
+ def host_ping(self):
+ return self.hosts.host_ping()
+
+ def host_info(self):
+ return self.hosts.host_info()
+
+ def get_lxd_api_compat(self, data=None):
+ return self.hosts.get_lxd_api_compat(data)
+
+ def get_lxd_host_trust(self, data=None):
+ return self.hosts.get_lxd_host_trust(data)
+
+ def get_lxd_backing_fs(self, data=None):
+ return self.hosts.get_lxd_backing_fs(data)
+
+ def get_lxd_driver(self, data=None):
+ return self.hosts.get_lxd_driver(data)
+
+ def get_lxc_version(self, data=None):
+ return self.hosts.get_lxc_version(data)
+
+ def get_lxd_version(self, data=None):
+ return self.hosts.get_lxd_version(data)
+
+ def get_kernel_version(self, data=None):
+ return self.hosts.get_kernel_version(data)
+
+ # images
+ def image_list(self):
+ return self.image.image_list()
+
+ def image_defined(self, image):
+ return self.image.image_defined(image)
+
+ def image_search(self, params):
+ return self.image.image_list_by_key(params)
+
+ def image_info(self, image):
+ return self.image.image_info(image)
+
+ def image_upload_date(self, image, data=None):
+ return self.image.get_image_date(image, data, 'uploaded_at')
+
+ def image_create_date(self, image, data=None):
+ return self.image.get_image_date(image, data, 'created_at')
+
+ def image_expire_date(self, image, data=None):
+ return self.image.get_image_date(image, data, 'expires_at')
+
+ def image_upload(self, path=None, data=None, headers={}):
+ return self.image.image_upload(path=path, data=data, headers=headers)
+
+ def image_delete(self, image):
+ return self.image.image_delete(image)
+
+ def image_export(self, image):
+ return self.image.image_export(image)
+
+ def image_update(self, image, data):
+ return self.image.image_update(image, data)
+
+ def image_rename(self, image, data):
+ return self.image.image_rename(image, data)
+
+ # alias
+ def alias_list(self):
+ return self.alias.alias_list()
+
+ def alias_defined(self, alias):
+ return self.alias.alias_defined(alias)
+
+ def alias_create(self, data):
+ return self.alias.alias_create(data)
+
+ def alias_update(self, alias, data):
+ return self.alias.alias_update(alias, data)
+
+ def alias_show(self, alias):
+ return self.alias.alias_show(alias)
+
+ def alias_rename(self, alias, data):
+ return self.alias.alias_rename(alias, data)
+
+ def alias_delete(self, alias):
+ return self.alias.alias_delete(alias)
+
+ # containers:
+ def container_list(self):
+ return self.container.container_list()
+
+ def container_defined(self, container):
+ return self.container.container_defined(container)
+
+ def container_running(self, container):
+ return self.container.container_running(container)
+
+ def container_init(self, container):
+ return self.container.container_init(container)
+
+ def container_update(self, container, config):
+ return self.container.container_update(container, config)
+
+ def container_state(self, container):
+ return self.container.container_state(container)
+
+ def container_start(self, container, timeout):
+ return self.container.container_start(container, timeout)
+
+ def container_stop(self, container, timeout):
+ return self.container.container_stop(container, timeout)
+
+ def container_suspend(self, container, timeout):
+ return self.container.container_suspend(container, timeout)
+
+ def container_resume(self, container, timeout):
+ return self.container.container_resume(container, timeout)
+
+ def container_reboot(self, container, timeout):
+ return self.container.container_reboot(container, timeout)
+
+ def container_destroy(self, container):
+ return self.container.container_destroy(container)
+
+ def get_container_log(self, container):
+ return self.container.get_container_log(container)
+
+ def get_container_config(self, container):
+ return self.container.get_container_config(container)
+
+ def get_container_websocket(self, container):
+ return self.container.get_container_websocket(container)
+
+ def container_info(self, container):
+ return self.container.container_info(container)
+
+ def container_local_copy(self, container):
+ return self.container.container_local_copy(container)
+
+ def container_local_move(self, instance, container):
+ return self.container.container_local_move(instance, container)
+
+ # file operations
+ def get_container_file(self, container, filename):
+ return self.container.get_container_file(container, filename)
+
+ def container_publish(self, container):
+ return self.container.container_publish(container)
+
+ def put_container_file(self, container, src_file,
+ dst_file, uid=0, gid=0, mode=0o644):
+ return self.container.put_container_file(
+ container, src_file, dst_file, uid, gid, mode)
+
+ # snapshots
+ def container_snapshot_list(self, container):
+ return self.container.snapshot_list(container)
+
+ def container_snapshot_create(self, container, config):
+ return self.container.snapshot_create(container, config)
+
+ def container_snapshot_info(self, container, snapshot):
+ return self.container.snapshot_info(container, snapshot)
+
+ def container_snapshot_rename(self, container, snapshot, config):
+ return self.container.snapshot_rename(container, snapshot, config)
+
+ def container_snapshot_delete(self, container, snapshot):
+ return self.container.snapshot_delete(container, snapshot)
+
+ def container_migrate(self, container):
+ return self.container.container_migrate(container)
+
+ def container_migrate_sync(self, operation_id, container_secret):
+ return self.container.container_migrate_sync(
+ operation_id, container_secret)
+
+ # misc container
+ def container_run_command(self, container, args, interactive=False,
+ web_sockets=False, env=None):
+ return self.container.run_command(container, args, interactive,
+ web_sockets, env)
+
+ # certificates
+ def certificate_list(self):
+ return self.certificate.certificate_list()
+
+ def certificate_show(self, fingerprint):
+ return self.certificate.certificate_show(fingerprint)
+
+ def certificate_delete(self, fingerprint):
+ return self.certificate.certificate_delete(fingerprint)
+
+ def certificate_create(self, fingerprint):
+ return self.certificate.certificate_create(fingerprint)
+
+ # profiles
+ def profile_create(self, profile):
+ '''Create LXD profile'''
+ return self.profiles.profile_create(profile)
+
+ def profile_show(self, profile):
+ '''Show LXD profile'''
+ return self.profiles.profile_show(profile)
+
+ def profile_defined(self, profile):
+ '''Check to see if profile is defined'''
+ return self.profiles.profile_defined(profile)
+
+ def profile_list(self):
+ '''List LXD profiles'''
+ return self.profiles.profile_list()
+
+ def profile_update(self, profile, config):
+ '''Update LXD profile'''
+ return self.profiles.profile_update(profile, config)
+
+ def profile_rename(self, profile, config):
+ '''Rename LXD profile'''
+ return self.profiles.profile_rename(profile, config)
+
+ def profile_delete(self, profile):
+ '''Delete LXD profile'''
+ return self.profiles.profile_delete(profile)
+
+ # lxd operations
+ def list_operations(self):
+ return self.operation.operation_list()
+
+ def wait_container_operation(self, operation, status_code, timeout):
+ return self.operation.operation_wait(operation, status_code, timeout)
+
+ def operation_delete(self, operation):
+ return self.operation.operation_delete(operation)
+
+ def operation_info(self, operation):
+ return self.operation.operation_info(operation)
+
+ def operation_show_create_time(self, operation, data=None):
+ return self.operation.operation_create_time(operation, data)
+
+ def operation_show_update_time(self, operation, data=None):
+ return self.operation.operation_update_time(operation, data)
+
+ def operation_show_status(self, operation, data=None):
+ return self.operation.operation_status_code(operation, data)
+
+ def operation_stream(self, operation, operation_secret):
+ return self.operation.operation_stream(operation, operation_secret)
+
+ # networks
+ def network_list(self):
+ return self.network.network_list()
+
+ def network_show(self, network):
+ return self.network.network_show(network)
+
+ def network_show_name(self, network, data=None):
+ return self.network.show_network_name(network, data)
+
+ def network_show_type(self, network, data=None):
+ return self.network.show_network_type(network, data)
+
+ def network_show_members(self, network, data=None):
+ return self.network.show_network_members(network, data)
From 86cbcb420f6df4aea4793e41a8b9112b04661c54 Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul at eventuallyanyway.com>
Date: Tue, 16 Feb 2016 19:12:39 -0700
Subject: [PATCH 02/15] API now raises a DeprecationWarning
---
pylxd/deprecated/api.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/pylxd/deprecated/api.py b/pylxd/deprecated/api.py
index d1be4c4..624ce4b 100644
--- a/pylxd/deprecated/api.py
+++ b/pylxd/deprecated/api.py
@@ -12,6 +12,8 @@
# 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 warnings
+
from pylxd import certificate
from pylxd import connection
from pylxd import container
@@ -25,6 +27,9 @@
class API(object):
def __init__(self, host=None, port=8443):
+ warnings.warn(
+ "pylxd.api.API is deprecated. Please use pylxd.Client.",
+ DeprecationWarning)
conn = self.connection = connection.LXDConnection(host=host, port=port)
self.hosts = hosts.LXDHost(conn)
self.image = image.LXDImage(conn)
From 9e685fd38f87da6e88f22de9d6c60451b8d7a00c Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul at eventuallyanyway.com>
Date: Tue, 16 Feb 2016 19:19:56 -0700
Subject: [PATCH 03/15] Move base to deprecated
---
pylxd/base.py | 22 ----------------------
pylxd/certificate.py | 2 +-
pylxd/container.py | 2 +-
pylxd/deprecated/base.py | 22 ++++++++++++++++++++++
pylxd/hosts.py | 2 +-
pylxd/image.py | 2 +-
pylxd/network.py | 2 +-
pylxd/operation.py | 2 +-
pylxd/profiles.py | 2 +-
9 files changed, 29 insertions(+), 29 deletions(-)
delete mode 100644 pylxd/base.py
create mode 100644 pylxd/deprecated/base.py
diff --git a/pylxd/base.py b/pylxd/base.py
deleted file mode 100644
index 689e80e..0000000
--- a/pylxd/base.py
+++ /dev/null
@@ -1,22 +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 __future__ import print_function
-
-from pylxd import connection
-
-
-class LXDBase(object):
-
- def __init__(self, conn=None):
- self.connection = conn or connection.LXDConnection()
diff --git a/pylxd/certificate.py b/pylxd/certificate.py
index b937009..79564a0 100644
--- a/pylxd/certificate.py
+++ b/pylxd/certificate.py
@@ -14,7 +14,7 @@
import json
-from pylxd import base
+from pylxd.deprecated import base
class LXDCertificate(base.LXDBase):
diff --git a/pylxd/container.py b/pylxd/container.py
index 25c2955..e7ec13a 100644
--- a/pylxd/container.py
+++ b/pylxd/container.py
@@ -15,7 +15,7 @@
import six
-from pylxd import base
+from pylxd.deprecated import base
from pylxd import exceptions
from pylxd import mixin
from pylxd.operation import Operation
diff --git a/pylxd/deprecated/base.py b/pylxd/deprecated/base.py
new file mode 100644
index 0000000..689e80e
--- /dev/null
+++ b/pylxd/deprecated/base.py
@@ -0,0 +1,22 @@
+# 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 __future__ import print_function
+
+from pylxd import connection
+
+
+class LXDBase(object):
+
+ def __init__(self, conn=None):
+ self.connection = conn or connection.LXDConnection()
diff --git a/pylxd/hosts.py b/pylxd/hosts.py
index 64bd57b..e882bff 100644
--- a/pylxd/hosts.py
+++ b/pylxd/hosts.py
@@ -13,7 +13,7 @@
# under the License.
from __future__ import print_function
-from pylxd import base
+from pylxd.deprecated import base
from pylxd import exceptions
diff --git a/pylxd/image.py b/pylxd/image.py
index d2b3b6b..b9a3c21 100644
--- a/pylxd/image.py
+++ b/pylxd/image.py
@@ -17,7 +17,7 @@
import json
from six.moves import urllib
-from pylxd import base
+from pylxd.deprecated import base
from pylxd import connection
from pylxd import exceptions
from pylxd import mixin
diff --git a/pylxd/network.py b/pylxd/network.py
index 8743d80..3eff79e 100644
--- a/pylxd/network.py
+++ b/pylxd/network.py
@@ -12,7 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from pylxd import base
+from pylxd.deprecated import base
class LXDNetwork(base.LXDBase):
diff --git a/pylxd/operation.py b/pylxd/operation.py
index 483efd3..27766f6 100644
--- a/pylxd/operation.py
+++ b/pylxd/operation.py
@@ -14,7 +14,7 @@
from dateutil.parser import parse as parse_date
-from pylxd import base
+from pylxd.deprecated import base
class LXDOperation(base.LXDBase):
diff --git a/pylxd/profiles.py b/pylxd/profiles.py
index b4a76e3..da39c2b 100644
--- a/pylxd/profiles.py
+++ b/pylxd/profiles.py
@@ -14,7 +14,7 @@
# XXX: rockstar (15 Feb 2016) - This module should be renamed to 'profile'.
import json
-from pylxd import base
+from pylxd.deprecated import base
from pylxd import mixin
From d92152ed02acca0ac0be46f413aa32a43f30a473 Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul at eventuallyanyway.com>
Date: Tue, 16 Feb 2016 19:30:40 -0700
Subject: [PATCH 04/15] Move pylxd.container.LXDContainer to
pylxd.deprecated.container
---
pylxd/container.py | 194 +--------------------------------------
pylxd/deprecated/container.py | 206 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 208 insertions(+), 192 deletions(-)
create mode 100644 pylxd/deprecated/container.py
diff --git a/pylxd/container.py b/pylxd/container.py
index e7ec13a..2b4a93e 100644
--- a/pylxd/container.py
+++ b/pylxd/container.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2015 Canonical Ltd
+# Copyright (c) 2016 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
@@ -11,204 +11,14 @@
# 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 json
import six
-from pylxd.deprecated import base
-from pylxd import exceptions
from pylxd import mixin
+from pylxd.deprecated.container import LXDContainer # NOQA
from pylxd.operation import Operation
-class LXDContainer(base.LXDBase):
- # containers:
-
- def container_list(self):
- (state, data) = self.connection.get_object('GET', '/1.0/containers')
- return [container.split('/1.0/containers/')[-1]
- for container in data['metadata']]
-
- def container_running(self, container):
- (state, data) = self.connection.get_object(
- 'GET',
- '/1.0/containers/%s/state' % container)
- data = data.get('metadata')
- container_running = False
- if data['status'].upper() in ['RUNNING', 'STARTING', 'FREEZING',
- 'FROZEN', 'THAWED']:
- container_running = True
- return container_running
-
- def container_init(self, container):
- return self.connection.get_object('POST', '/1.0/containers',
- json.dumps(container))
-
- def container_update(self, container, config):
- return self.connection.get_object('PUT', '/1.0/containers/%s'
- % container, json.dumps(config))
-
- def container_defined(self, container):
- _, data = self.connection.get_object('GET', '/1.0/containers')
- try:
- containers = data["metadata"]
- except KeyError:
- raise exceptions.PyLXDException("no metadata in GET containers?")
-
- container_url = "/1.0/containers/%s" % container
- for ct in containers:
- if ct == container_url:
- return True
- return False
-
- def container_state(self, container):
- return self.connection.get_object(
- 'GET', '/1.0/containers/%s/state' % container)
-
- def container_start(self, container, timeout):
- action = {'action': 'start', 'force': True, 'timeout': timeout}
- return self.connection.get_object('PUT', '/1.0/containers/%s/state'
- % container,
- json.dumps(action))
-
- def container_stop(self, container, timeout):
- action = {'action': 'stop', 'force': True, 'timeout': timeout}
- return self.connection.get_object('PUT', '/1.0/containers/%s/state'
- % container,
- json.dumps(action))
-
- def container_suspend(self, container, timeout):
- action = {'action': 'freeze', 'force': True, 'timeout': timeout}
- return self.connection.get_object('PUT', '/1.0/containers/%s/state'
- % container,
- json.dumps(action))
-
- def container_resume(self, container, timeout):
- action = {'action': 'unfreeze', 'force': True, 'timeout': timeout}
- return self.connection.get_object('PUT', '/1.0/containers/%s/state'
- % container,
- json.dumps(action))
-
- def container_reboot(self, container, timeout):
- action = {'action': 'restart', 'force': True, 'timeout': timeout}
- return self.connection.get_object('PUT', '/1.0/containers/%s/state'
- % container,
- json.dumps(action))
-
- def container_destroy(self, container):
- return self.connection.get_object('DELETE', '/1.0/containers/%s'
- % container)
-
- def get_container_log(self, container):
- (state, data) = self.connection.get_object(
- 'GET', '/1.0/containers/%s?log=true' % container)
- return data['metadata']['log']
-
- def get_container_config(self, container):
- (state, data) = self.connection.get_object(
- 'GET', '/1.0/containers/%s?log=false' % container)
- return data['metadata']
-
- def get_container_websocket(self, container):
- return self.connection.get_status(
- 'GET',
- '/1.0/operations/%s/websocket?secret=%s'
- % (container['operation'], container['fs']))
-
- def container_info(self, container):
- (state, data) = self.connection.get_object(
- 'GET', '/1.0/containers/%s/state' % container)
- return data['metadata']
-
- def container_migrate(self, container):
- action = {'migration': True}
- (state, data) = self.connection.get_object(
- 'POST', '/1.0/containers/%s' % container,
- json.dumps(action))
-
- return_data = {
- 'operation': str(data['operation'].split('/1.0/operations/')[-1]),
- }
- return_data.update(data['metadata'])
- return return_data
-
- def container_migrate_sync(self, operation_id, container_secret):
- return self.connection.get_ws(
- '/1.0/operations/%s/websocket?secret=%s'
- % (operation_id, container_secret))
-
- def container_local_copy(self, container):
- return self.connection.get_object(
- 'POST',
- '/1.0/containers', json.dumps(container))
-
- def container_local_move(self, instance, config):
- return self.connection.get_object(
- 'POST',
- '/1.0/containers/%s' % instance, json.dumps(config))
-
- # file operations
- def get_container_file(self, container, filename):
- return self.connection.get_raw(
- 'GET',
- '/1.0/containers/%s/files?path=%s' % (container, filename))
-
- def put_container_file(self, container, src_file,
- dst_file, uid, gid, mode):
- with open(src_file, 'rb') as f:
- data = f.read()
- return self.connection.get_object(
- 'POST',
- '/1.0/containers/%s/files?path=%s' % (container, dst_file),
- body=data,
- headers={'X-LXD-uid': uid, 'X-LXD-gid': gid, 'X-LXD-mode': mode})
-
- def container_publish(self, container):
- return self.connection.get_object('POST', '/1.0/images',
- json.dumps(container))
-
- # misc operations
- def run_command(self, container, args, interactive, web_sockets, env):
- env = env or {}
- data = {'command': args,
- 'interactive': interactive,
- 'wait-for-websocket': web_sockets,
- 'environment': env}
- return self.connection.get_object('POST', '/1.0/containers/%s/exec'
- % container, json.dumps(data))
-
- # snapshots
- def snapshot_list(self, container):
- (state, data) = self.connection.get_object(
- 'GET',
- '/1.0/containers/%s/snapshots' % container)
- return [snapshot.split('/1.0/containers/%s/snapshots/%s/'
- % (container, container))[-1]
- for snapshot in data['metadata']]
-
- def snapshot_create(self, container, config):
- return self.connection.get_object('POST',
- '/1.0/containers/%s/snapshots'
- % container,
- json.dumps(config))
-
- def snapshot_info(self, container, snapshot):
- return self.connection.get_object('GET',
- '/1.0/containers/%s/snapshots/%s'
- % (container, snapshot))
-
- def snapshot_rename(self, container, snapshot, config):
- return self.connection.get_object('POST',
- '/1.0/containers/%s/snapshots/%s'
- % (container, snapshot),
- json.dumps(config))
-
- def snapshot_delete(self, container, snapshot):
- return self.connection.get_object('DELETE',
- '/1.0/containers/%s/snapshots/%s'
- % (container, snapshot))
-
-
class Container(mixin.Waitable, mixin.Marshallable):
"""An LXD Container.
diff --git a/pylxd/deprecated/container.py b/pylxd/deprecated/container.py
new file mode 100644
index 0000000..037ebdd
--- /dev/null
+++ b/pylxd/deprecated/container.py
@@ -0,0 +1,206 @@
+# 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 json
+
+from pylxd.deprecated import base
+from pylxd import exceptions
+
+
+class LXDContainer(base.LXDBase):
+ # containers:
+
+ def container_list(self):
+ (state, data) = self.connection.get_object('GET', '/1.0/containers')
+ return [container.split('/1.0/containers/')[-1]
+ for container in data['metadata']]
+
+ def container_running(self, container):
+ (state, data) = self.connection.get_object(
+ 'GET',
+ '/1.0/containers/%s/state' % container)
+ data = data.get('metadata')
+ container_running = False
+ if data['status'].upper() in ['RUNNING', 'STARTING', 'FREEZING',
+ 'FROZEN', 'THAWED']:
+ container_running = True
+ return container_running
+
+ def container_init(self, container):
+ return self.connection.get_object('POST', '/1.0/containers',
+ json.dumps(container))
+
+ def container_update(self, container, config):
+ return self.connection.get_object('PUT', '/1.0/containers/%s'
+ % container, json.dumps(config))
+
+ def container_defined(self, container):
+ _, data = self.connection.get_object('GET', '/1.0/containers')
+ try:
+ containers = data["metadata"]
+ except KeyError:
+ raise exceptions.PyLXDException("no metadata in GET containers?")
+
+ container_url = "/1.0/containers/%s" % container
+ for ct in containers:
+ if ct == container_url:
+ return True
+ return False
+
+ def container_state(self, container):
+ return self.connection.get_object(
+ 'GET', '/1.0/containers/%s/state' % container)
+
+ def container_start(self, container, timeout):
+ action = {'action': 'start', 'force': True, 'timeout': timeout}
+ return self.connection.get_object('PUT', '/1.0/containers/%s/state'
+ % container,
+ json.dumps(action))
+
+ def container_stop(self, container, timeout):
+ action = {'action': 'stop', 'force': True, 'timeout': timeout}
+ return self.connection.get_object('PUT', '/1.0/containers/%s/state'
+ % container,
+ json.dumps(action))
+
+ def container_suspend(self, container, timeout):
+ action = {'action': 'freeze', 'force': True, 'timeout': timeout}
+ return self.connection.get_object('PUT', '/1.0/containers/%s/state'
+ % container,
+ json.dumps(action))
+
+ def container_resume(self, container, timeout):
+ action = {'action': 'unfreeze', 'force': True, 'timeout': timeout}
+ return self.connection.get_object('PUT', '/1.0/containers/%s/state'
+ % container,
+ json.dumps(action))
+
+ def container_reboot(self, container, timeout):
+ action = {'action': 'restart', 'force': True, 'timeout': timeout}
+ return self.connection.get_object('PUT', '/1.0/containers/%s/state'
+ % container,
+ json.dumps(action))
+
+ def container_destroy(self, container):
+ return self.connection.get_object('DELETE', '/1.0/containers/%s'
+ % container)
+
+ def get_container_log(self, container):
+ (state, data) = self.connection.get_object(
+ 'GET', '/1.0/containers/%s?log=true' % container)
+ return data['metadata']['log']
+
+ def get_container_config(self, container):
+ (state, data) = self.connection.get_object(
+ 'GET', '/1.0/containers/%s?log=false' % container)
+ return data['metadata']
+
+ def get_container_websocket(self, container):
+ return self.connection.get_status(
+ 'GET',
+ '/1.0/operations/%s/websocket?secret=%s'
+ % (container['operation'], container['fs']))
+
+ def container_info(self, container):
+ (state, data) = self.connection.get_object(
+ 'GET', '/1.0/containers/%s/state' % container)
+ return data['metadata']
+
+ def container_migrate(self, container):
+ action = {'migration': True}
+ (state, data) = self.connection.get_object(
+ 'POST', '/1.0/containers/%s' % container,
+ json.dumps(action))
+
+ return_data = {
+ 'operation': str(data['operation'].split('/1.0/operations/')[-1]),
+ }
+ return_data.update(data['metadata'])
+ return return_data
+
+ def container_migrate_sync(self, operation_id, container_secret):
+ return self.connection.get_ws(
+ '/1.0/operations/%s/websocket?secret=%s'
+ % (operation_id, container_secret))
+
+ def container_local_copy(self, container):
+ return self.connection.get_object(
+ 'POST',
+ '/1.0/containers', json.dumps(container))
+
+ def container_local_move(self, instance, config):
+ return self.connection.get_object(
+ 'POST',
+ '/1.0/containers/%s' % instance, json.dumps(config))
+
+ # file operations
+ def get_container_file(self, container, filename):
+ return self.connection.get_raw(
+ 'GET',
+ '/1.0/containers/%s/files?path=%s' % (container, filename))
+
+ def put_container_file(self, container, src_file,
+ dst_file, uid, gid, mode):
+ with open(src_file, 'rb') as f:
+ data = f.read()
+ return self.connection.get_object(
+ 'POST',
+ '/1.0/containers/%s/files?path=%s' % (container, dst_file),
+ body=data,
+ headers={'X-LXD-uid': uid, 'X-LXD-gid': gid, 'X-LXD-mode': mode})
+
+ def container_publish(self, container):
+ return self.connection.get_object('POST', '/1.0/images',
+ json.dumps(container))
+
+ # misc operations
+ def run_command(self, container, args, interactive, web_sockets, env):
+ env = env or {}
+ data = {'command': args,
+ 'interactive': interactive,
+ 'wait-for-websocket': web_sockets,
+ 'environment': env}
+ return self.connection.get_object('POST', '/1.0/containers/%s/exec'
+ % container, json.dumps(data))
+
+ # snapshots
+ def snapshot_list(self, container):
+ (state, data) = self.connection.get_object(
+ 'GET',
+ '/1.0/containers/%s/snapshots' % container)
+ return [snapshot.split('/1.0/containers/%s/snapshots/%s/'
+ % (container, container))[-1]
+ for snapshot in data['metadata']]
+
+ def snapshot_create(self, container, config):
+ return self.connection.get_object('POST',
+ '/1.0/containers/%s/snapshots'
+ % container,
+ json.dumps(config))
+
+ def snapshot_info(self, container, snapshot):
+ return self.connection.get_object('GET',
+ '/1.0/containers/%s/snapshots/%s'
+ % (container, snapshot))
+
+ def snapshot_rename(self, container, snapshot, config):
+ return self.connection.get_object('POST',
+ '/1.0/containers/%s/snapshots/%s'
+ % (container, snapshot),
+ json.dumps(config))
+
+ def snapshot_delete(self, container, snapshot):
+ return self.connection.get_object('DELETE',
+ '/1.0/containers/%s/snapshots/%s'
+ % (container, snapshot))
From 35ee55764759da80614dc5b5a99bf8dbb04f4323 Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul at eventuallyanyway.com>
Date: Tue, 16 Feb 2016 19:34:58 -0700
Subject: [PATCH 05/15] Move pylxd.certificate to pylxd.deprecated.certificate
---
pylxd/certificate.py | 37 -------------------------------------
pylxd/deprecated/api.py | 4 ++--
pylxd/deprecated/certificate.py | 37 +++++++++++++++++++++++++++++++++++++
3 files changed, 39 insertions(+), 39 deletions(-)
delete mode 100644 pylxd/certificate.py
create mode 100644 pylxd/deprecated/certificate.py
diff --git a/pylxd/certificate.py b/pylxd/certificate.py
deleted file mode 100644
index 79564a0..0000000
--- a/pylxd/certificate.py
+++ /dev/null
@@ -1,37 +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 json
-
-from pylxd.deprecated import base
-
-
-class LXDCertificate(base.LXDBase):
-
- def certificate_list(self):
- (state, data) = self.connection.get_object('GET', '/1.0/certificates')
- return [certificate.split('/1.0/certificates/')[-1]
- for certificate in data['metadata']]
-
- def certificate_show(self, fingerprint):
- return self.connection.get_object('GET', '/1.0/certificates/%s'
- % fingerprint)
-
- def certificate_create(self, certificate):
- return self.connection.get_status('POST', '/1.0/certificates',
- json.dumps(certificate))
-
- def certificate_delete(self, fingerprint):
- return self.connection.get_status('DELETE', '/1.0/certificates/%s'
- % fingerprint)
diff --git a/pylxd/deprecated/api.py b/pylxd/deprecated/api.py
index 624ce4b..8ff8fbb 100644
--- a/pylxd/deprecated/api.py
+++ b/pylxd/deprecated/api.py
@@ -14,9 +14,9 @@
# under the License.
import warnings
-from pylxd import certificate
+from pylxd.deprecated import certificate
from pylxd import connection
-from pylxd import container
+from pylxd.deprecated import container
from pylxd import hosts
from pylxd import image
from pylxd import network
diff --git a/pylxd/deprecated/certificate.py b/pylxd/deprecated/certificate.py
new file mode 100644
index 0000000..79564a0
--- /dev/null
+++ b/pylxd/deprecated/certificate.py
@@ -0,0 +1,37 @@
+# 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 json
+
+from pylxd.deprecated import base
+
+
+class LXDCertificate(base.LXDBase):
+
+ def certificate_list(self):
+ (state, data) = self.connection.get_object('GET', '/1.0/certificates')
+ return [certificate.split('/1.0/certificates/')[-1]
+ for certificate in data['metadata']]
+
+ def certificate_show(self, fingerprint):
+ return self.connection.get_object('GET', '/1.0/certificates/%s'
+ % fingerprint)
+
+ def certificate_create(self, certificate):
+ return self.connection.get_status('POST', '/1.0/certificates',
+ json.dumps(certificate))
+
+ def certificate_delete(self, fingerprint):
+ return self.connection.get_status('DELETE', '/1.0/certificates/%s'
+ % fingerprint)
From 95927a07f50ebdb9aba095039f827e83205c86c1 Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul at eventuallyanyway.com>
Date: Tue, 16 Feb 2016 19:46:18 -0700
Subject: [PATCH 06/15] Move pylxd.connection import
pylxd.deprecated.connection
---
pylxd/connection.py | 222 ----------------------------------------
pylxd/deprecated/api.py | 2 +-
pylxd/deprecated/base.py | 2 +-
pylxd/deprecated/connection.py | 222 ++++++++++++++++++++++++++++++++++++++++
pylxd/image.py | 2 +-
pylxd/tests/test_certificate.py | 2 +-
pylxd/tests/test_connection.py | 10 +-
pylxd/tests/test_container.py | 2 +-
pylxd/tests/test_host.py | 2 +-
pylxd/tests/test_image.py | 2 +-
pylxd/tests/test_image_alias.py | 2 +-
pylxd/tests/test_network.py | 2 +-
pylxd/tests/test_operation.py | 2 +-
pylxd/tests/test_profiles.py | 2 +-
14 files changed, 238 insertions(+), 238 deletions(-)
delete mode 100644 pylxd/connection.py
create mode 100644 pylxd/deprecated/connection.py
diff --git a/pylxd/connection.py b/pylxd/connection.py
deleted file mode 100644
index c1b9ea1..0000000
--- a/pylxd/connection.py
+++ /dev/null
@@ -1,222 +0,0 @@
-# Copyright (c) 2015 Canonical Ltd
-# Copyright (c) 2015 Mirantis inc.
-#
-# 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 collections import namedtuple
-import copy
-import json
-import os
-import six
-import socket
-import ssl
-import threading
-
-
-from pylxd import exceptions
-from pylxd import utils
-from six.moves import http_client
-from six.moves import queue
-from ws4py import client as websocket
-
-if hasattr(ssl, 'SSLContext'):
- # For Python >= 2.7.9 and Python 3.x
- USE_STDLIB_SSL = True
-else:
- # For Python 2.6 and <= 2.7.8
- USE_STDLIB_SSL = False
-
-if not USE_STDLIB_SSL:
- import OpenSSL.SSL
-
-# Detect SSL tls version
-if hasattr(ssl, 'PROTOCOL_TLSv1_2'):
- DEFAULT_TLS_VERSION = ssl.PROTOCOL_TLSv1_2
-else:
- DEFAULT_TLS_VERSION = OpenSSL.SSL.TLSv1_2_METHOD
-
-
-class UnixHTTPConnection(http_client.HTTPConnection):
-
- def __init__(self, path, host='localhost', port=None, strict=None,
- timeout=None):
- if six.PY34:
- http_client.HTTPConnection.__init__(self, host, port=port,
- timeout=timeout)
- else:
- http_client.HTTPConnection.__init__(self, host, port=port,
- strict=strict,
- timeout=timeout)
-
- self.path = path
-
- def connect(self):
- sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
- sock.connect(self.path)
- self.sock = sock
-
-
-class HTTPSConnection(http_client.HTTPConnection):
- default_port = 8443
-
- def __init__(self, *args, **kwargs):
- http_client.HTTPConnection.__init__(self, *args, **kwargs)
-
- def connect(self):
- sock = socket.create_connection((self.host, self.port),
- self.timeout, self.source_address)
- if self._tunnel_host:
- self.sock = sock
- self._tunnel()
-
- (cert_file, key_file) = self._get_ssl_certs()
- self.sock = ssl.wrap_socket(sock, certfile=cert_file,
- keyfile=key_file,
- ssl_version=DEFAULT_TLS_VERSION)
-
- @staticmethod
- def _get_ssl_certs():
- return (os.path.join(os.environ['HOME'], '.config/lxc/client.crt'),
- os.path.join(os.environ['HOME'], '.config/lxc/client.key'))
-
-
-_LXDResponse = namedtuple('LXDResponse', ['status', 'body', 'json'])
-
-
-class WebSocketClient(websocket.WebSocketBaseClient):
- def __init__(self, url, protocols=None, extensions=None, ssl_options=None,
- headers=None):
- """WebSocket client that executes into a eventlet green thread."""
- websocket.WebSocketBaseClient.__init__(self, url, protocols,
- extensions,
- ssl_options=ssl_options,
- headers=headers)
- self._th = threading.Thread(target=self.run, name='WebSocketClient')
- self._th.daemon = True
-
- self.messages = queue.Queue()
-
- def handshake_ok(self):
- """Starts the client's thread."""
- self._th.start()
-
- def received_message(self, message):
- """Override the base class to store the incoming message."""
- self.messages.put(copy.deepcopy(message))
-
- def closed(self, code, reason=None):
- # When the connection is closed, put a StopIteration
- # on the message queue to signal there's nothing left
- # to wait for
- self.messages.put(StopIteration)
-
- def receive(self):
- # If the websocket was terminated and there are no messages
- # left in the queue, return None immediately otherwise the client
- # will block forever
- if self.terminated and self.messages.empty():
- return None
- message = self.messages.get()
- if message is StopIteration:
- return None
- return message
-
-
-class LXDConnection(object):
-
- def __init__(self, host=None, port=8443):
- if host:
- self.host = host
- self.port = port
- self.unix_socket = None
- else:
- if 'LXD_DIR' in os.environ:
- self.unix_socket = os.path.join(os.environ['LXD_DIR'],
- 'unix.socket')
- else:
- self.unix_socket = '/var/lib/lxd/unix.socket'
- self.host, self.port = None, None
- self.connection = None
-
- def _request(self, *args, **kwargs):
- if self.connection is None:
- self.connection = self.get_connection()
- self.connection.request(*args, **kwargs)
- response = self.connection.getresponse()
-
- status = response.status
- raw_body = response.read()
- try:
- if six.PY34:
- body = json.loads(raw_body.decode())
- else:
- body = json.loads(raw_body)
- except ValueError:
- body = None
-
- return _LXDResponse(status, raw_body, body)
-
- def get_connection(self):
- if self.host:
- return HTTPSConnection(self.host, self.port)
- return UnixHTTPConnection(self.unix_socket)
-
- def get_object(self, *args, **kwargs):
- response = self._request(*args, **kwargs)
-
- if not response.json:
- raise exceptions.PyLXDException('Null Data')
- elif response.status == 200 or (
- response.status == 202 and
- response.json.get('status_code') == 100):
- return response.status, response.json
- else:
- utils.get_lxd_error(response.status, response.json)
-
- def get_status(self, *args, **kwargs):
- response = self._request(*args, **kwargs)
-
- if not response.json:
- raise exceptions.PyLXDException('Null Data')
- elif response.json.get('error'):
- utils.get_lxd_error(response.status, response.json)
- elif response.status == 200 or (
- response.status == 202 and
- response.json.get('status_code') == 100):
- return True
- return False
-
- def get_raw(self, *args, **kwargs):
- response = self._request(*args, **kwargs)
-
- if not response.body:
- raise exceptions.PyLXDException('Null Body')
- elif response.status == 200:
- return response.body
- else:
- raise exceptions.PyLXDException('Failed to get raw response')
-
- def get_ws(self, path):
- if self.unix_socket:
- connection_string = 'ws+unix://%s' % self.unix_socket
- else:
- connection_string = (
- 'wss://%(host)s:%(port)s' % {'host': self.host,
- 'port': self.port}
- )
-
- ws = WebSocketClient(connection_string)
- ws.resource = path
- ws.connect()
-
- return ws
diff --git a/pylxd/deprecated/api.py b/pylxd/deprecated/api.py
index 8ff8fbb..910a870 100644
--- a/pylxd/deprecated/api.py
+++ b/pylxd/deprecated/api.py
@@ -15,7 +15,7 @@
import warnings
from pylxd.deprecated import certificate
-from pylxd import connection
+from pylxd.deprecated import connection
from pylxd.deprecated import container
from pylxd import hosts
from pylxd import image
diff --git a/pylxd/deprecated/base.py b/pylxd/deprecated/base.py
index 689e80e..b2b5802 100644
--- a/pylxd/deprecated/base.py
+++ b/pylxd/deprecated/base.py
@@ -13,7 +13,7 @@
# under the License.
from __future__ import print_function
-from pylxd import connection
+from pylxd.deprecated import connection
class LXDBase(object):
diff --git a/pylxd/deprecated/connection.py b/pylxd/deprecated/connection.py
new file mode 100644
index 0000000..c1b9ea1
--- /dev/null
+++ b/pylxd/deprecated/connection.py
@@ -0,0 +1,222 @@
+# Copyright (c) 2015 Canonical Ltd
+# Copyright (c) 2015 Mirantis inc.
+#
+# 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 collections import namedtuple
+import copy
+import json
+import os
+import six
+import socket
+import ssl
+import threading
+
+
+from pylxd import exceptions
+from pylxd import utils
+from six.moves import http_client
+from six.moves import queue
+from ws4py import client as websocket
+
+if hasattr(ssl, 'SSLContext'):
+ # For Python >= 2.7.9 and Python 3.x
+ USE_STDLIB_SSL = True
+else:
+ # For Python 2.6 and <= 2.7.8
+ USE_STDLIB_SSL = False
+
+if not USE_STDLIB_SSL:
+ import OpenSSL.SSL
+
+# Detect SSL tls version
+if hasattr(ssl, 'PROTOCOL_TLSv1_2'):
+ DEFAULT_TLS_VERSION = ssl.PROTOCOL_TLSv1_2
+else:
+ DEFAULT_TLS_VERSION = OpenSSL.SSL.TLSv1_2_METHOD
+
+
+class UnixHTTPConnection(http_client.HTTPConnection):
+
+ def __init__(self, path, host='localhost', port=None, strict=None,
+ timeout=None):
+ if six.PY34:
+ http_client.HTTPConnection.__init__(self, host, port=port,
+ timeout=timeout)
+ else:
+ http_client.HTTPConnection.__init__(self, host, port=port,
+ strict=strict,
+ timeout=timeout)
+
+ self.path = path
+
+ def connect(self):
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ sock.connect(self.path)
+ self.sock = sock
+
+
+class HTTPSConnection(http_client.HTTPConnection):
+ default_port = 8443
+
+ def __init__(self, *args, **kwargs):
+ http_client.HTTPConnection.__init__(self, *args, **kwargs)
+
+ def connect(self):
+ sock = socket.create_connection((self.host, self.port),
+ self.timeout, self.source_address)
+ if self._tunnel_host:
+ self.sock = sock
+ self._tunnel()
+
+ (cert_file, key_file) = self._get_ssl_certs()
+ self.sock = ssl.wrap_socket(sock, certfile=cert_file,
+ keyfile=key_file,
+ ssl_version=DEFAULT_TLS_VERSION)
+
+ @staticmethod
+ def _get_ssl_certs():
+ return (os.path.join(os.environ['HOME'], '.config/lxc/client.crt'),
+ os.path.join(os.environ['HOME'], '.config/lxc/client.key'))
+
+
+_LXDResponse = namedtuple('LXDResponse', ['status', 'body', 'json'])
+
+
+class WebSocketClient(websocket.WebSocketBaseClient):
+ def __init__(self, url, protocols=None, extensions=None, ssl_options=None,
+ headers=None):
+ """WebSocket client that executes into a eventlet green thread."""
+ websocket.WebSocketBaseClient.__init__(self, url, protocols,
+ extensions,
+ ssl_options=ssl_options,
+ headers=headers)
+ self._th = threading.Thread(target=self.run, name='WebSocketClient')
+ self._th.daemon = True
+
+ self.messages = queue.Queue()
+
+ def handshake_ok(self):
+ """Starts the client's thread."""
+ self._th.start()
+
+ def received_message(self, message):
+ """Override the base class to store the incoming message."""
+ self.messages.put(copy.deepcopy(message))
+
+ def closed(self, code, reason=None):
+ # When the connection is closed, put a StopIteration
+ # on the message queue to signal there's nothing left
+ # to wait for
+ self.messages.put(StopIteration)
+
+ def receive(self):
+ # If the websocket was terminated and there are no messages
+ # left in the queue, return None immediately otherwise the client
+ # will block forever
+ if self.terminated and self.messages.empty():
+ return None
+ message = self.messages.get()
+ if message is StopIteration:
+ return None
+ return message
+
+
+class LXDConnection(object):
+
+ def __init__(self, host=None, port=8443):
+ if host:
+ self.host = host
+ self.port = port
+ self.unix_socket = None
+ else:
+ if 'LXD_DIR' in os.environ:
+ self.unix_socket = os.path.join(os.environ['LXD_DIR'],
+ 'unix.socket')
+ else:
+ self.unix_socket = '/var/lib/lxd/unix.socket'
+ self.host, self.port = None, None
+ self.connection = None
+
+ def _request(self, *args, **kwargs):
+ if self.connection is None:
+ self.connection = self.get_connection()
+ self.connection.request(*args, **kwargs)
+ response = self.connection.getresponse()
+
+ status = response.status
+ raw_body = response.read()
+ try:
+ if six.PY34:
+ body = json.loads(raw_body.decode())
+ else:
+ body = json.loads(raw_body)
+ except ValueError:
+ body = None
+
+ return _LXDResponse(status, raw_body, body)
+
+ def get_connection(self):
+ if self.host:
+ return HTTPSConnection(self.host, self.port)
+ return UnixHTTPConnection(self.unix_socket)
+
+ def get_object(self, *args, **kwargs):
+ response = self._request(*args, **kwargs)
+
+ if not response.json:
+ raise exceptions.PyLXDException('Null Data')
+ elif response.status == 200 or (
+ response.status == 202 and
+ response.json.get('status_code') == 100):
+ return response.status, response.json
+ else:
+ utils.get_lxd_error(response.status, response.json)
+
+ def get_status(self, *args, **kwargs):
+ response = self._request(*args, **kwargs)
+
+ if not response.json:
+ raise exceptions.PyLXDException('Null Data')
+ elif response.json.get('error'):
+ utils.get_lxd_error(response.status, response.json)
+ elif response.status == 200 or (
+ response.status == 202 and
+ response.json.get('status_code') == 100):
+ return True
+ return False
+
+ def get_raw(self, *args, **kwargs):
+ response = self._request(*args, **kwargs)
+
+ if not response.body:
+ raise exceptions.PyLXDException('Null Body')
+ elif response.status == 200:
+ return response.body
+ else:
+ raise exceptions.PyLXDException('Failed to get raw response')
+
+ def get_ws(self, path):
+ if self.unix_socket:
+ connection_string = 'ws+unix://%s' % self.unix_socket
+ else:
+ connection_string = (
+ 'wss://%(host)s:%(port)s' % {'host': self.host,
+ 'port': self.port}
+ )
+
+ ws = WebSocketClient(connection_string)
+ ws.resource = path
+ ws.connect()
+
+ return ws
diff --git a/pylxd/image.py b/pylxd/image.py
index b9a3c21..a2cb19e 100644
--- a/pylxd/image.py
+++ b/pylxd/image.py
@@ -18,7 +18,7 @@
from six.moves import urllib
from pylxd.deprecated import base
-from pylxd import connection
+from pylxd.deprecated import connection
from pylxd import exceptions
from pylxd import mixin
from pylxd.operation import Operation
diff --git a/pylxd/tests/test_certificate.py b/pylxd/tests/test_certificate.py
index 60da277..090944b 100644
--- a/pylxd/tests/test_certificate.py
+++ b/pylxd/tests/test_certificate.py
@@ -16,7 +16,7 @@
import json
import mock
-from pylxd import connection
+from pylxd.deprecated import connection
from pylxd.tests import annotated_data
from pylxd.tests import fake_api
diff --git a/pylxd/tests/test_connection.py b/pylxd/tests/test_connection.py
index 5fd8a70..1bbfdcf 100644
--- a/pylxd/tests/test_connection.py
+++ b/pylxd/tests/test_connection.py
@@ -21,7 +21,7 @@
import socket
import unittest
-from pylxd import connection
+from pylxd.deprecated import connection
from pylxd import exceptions
from pylxd.tests import annotated_data
@@ -80,8 +80,8 @@ def test_https_proxy_connection(self, ms, ml):
keyfile='/home/foo/.config/lxc/client.key',
ssl_version=connection.DEFAULT_TLS_VERSION)
- @mock.patch('pylxd.connection.HTTPSConnection')
- @mock.patch('pylxd.connection.UnixHTTPConnection')
+ @mock.patch('pylxd.deprecated.connection.HTTPSConnection')
+ @mock.patch('pylxd.deprecated.connection.UnixHTTPConnection')
@annotated_data(
('unix', (None,), {}, '/var/lib/lxd/unix.socket'),
('unix_path', (None,),
@@ -112,7 +112,7 @@ def __init__(self, status, data):
@ddt
- at mock.patch('pylxd.connection.LXDConnection.get_connection')
+ at mock.patch('pylxd.deprecated.connection.LXDConnection.get_connection')
class LXDConnectionTest(unittest.TestCase):
def setUp(self):
@@ -159,7 +159,7 @@ def test_get_raw(self, tag, effect, result, mg):
else:
self.assertEqual(result, self.conn.get_raw())
- @mock.patch('pylxd.connection.WebSocketClient')
+ @mock.patch('pylxd.deprecated.connection.WebSocketClient')
@annotated_data(
('fake_host', 'wss://fake_host:8443'),
(None, 'ws+unix:///var/lib/lxd/unix.socket')
diff --git a/pylxd/tests/test_container.py b/pylxd/tests/test_container.py
index c7e1a9f..0385300 100644
--- a/pylxd/tests/test_container.py
+++ b/pylxd/tests/test_container.py
@@ -18,7 +18,7 @@
import mock
import tempfile
-from pylxd import connection
+from pylxd.deprecated import connection
from pylxd.tests import annotated_data
from pylxd.tests import fake_api
diff --git a/pylxd/tests/test_host.py b/pylxd/tests/test_host.py
index b7dbb6e..2002e57 100644
--- a/pylxd/tests/test_host.py
+++ b/pylxd/tests/test_host.py
@@ -16,7 +16,7 @@
from ddt import ddt
import mock
-from pylxd import connection
+from pylxd.deprecated import connection
from pylxd import exceptions
from pylxd.tests import annotated_data
diff --git a/pylxd/tests/test_image.py b/pylxd/tests/test_image.py
index 4223f6b..4848c9b 100644
--- a/pylxd/tests/test_image.py
+++ b/pylxd/tests/test_image.py
@@ -19,7 +19,7 @@
from six.moves import cStringIO
import unittest
-from pylxd import connection
+from pylxd.deprecated import connection
from pylxd import exceptions
from pylxd import image
diff --git a/pylxd/tests/test_image_alias.py b/pylxd/tests/test_image_alias.py
index 5421461..bccc44c 100644
--- a/pylxd/tests/test_image_alias.py
+++ b/pylxd/tests/test_image_alias.py
@@ -16,7 +16,7 @@
from ddt import ddt
import mock
-from pylxd import connection
+from pylxd.deprecated import connection
from pylxd.tests import annotated_data
from pylxd.tests import fake_api
diff --git a/pylxd/tests/test_network.py b/pylxd/tests/test_network.py
index 9a40754..65efdb6 100644
--- a/pylxd/tests/test_network.py
+++ b/pylxd/tests/test_network.py
@@ -15,7 +15,7 @@
from ddt import ddt
import mock
-from pylxd import connection
+from pylxd.deprecated import connection
from pylxd.tests import annotated_data
from pylxd.tests import fake_api
diff --git a/pylxd/tests/test_operation.py b/pylxd/tests/test_operation.py
index 8c7a5dd..233bb0b 100644
--- a/pylxd/tests/test_operation.py
+++ b/pylxd/tests/test_operation.py
@@ -16,7 +16,7 @@
from ddt import ddt
import mock
-from pylxd import connection
+from pylxd.deprecated import connection
from pylxd.tests import annotated_data
from pylxd.tests import fake_api
diff --git a/pylxd/tests/test_profiles.py b/pylxd/tests/test_profiles.py
index 6aa4743..1844986 100644
--- a/pylxd/tests/test_profiles.py
+++ b/pylxd/tests/test_profiles.py
@@ -16,7 +16,7 @@
from ddt import ddt
import mock
-from pylxd import connection
+from pylxd.deprecated import connection
from pylxd.tests import annotated_data
from pylxd.tests import fake_api
From 42fd3ca3499cdebfe72e01e589973ce092dd5f94 Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul at eventuallyanyway.com>
Date: Tue, 16 Feb 2016 19:48:21 -0700
Subject: [PATCH 07/15] Move pylxd.utils to pylxd.deprecated.utils
---
pylxd/deprecated/connection.py | 2 +-
pylxd/deprecated/utils.py | 29 +++++++++++++++++++++++++++++
pylxd/utils.py | 29 -----------------------------
3 files changed, 30 insertions(+), 30 deletions(-)
create mode 100644 pylxd/deprecated/utils.py
delete mode 100644 pylxd/utils.py
diff --git a/pylxd/deprecated/connection.py b/pylxd/deprecated/connection.py
index c1b9ea1..c38cf7b 100644
--- a/pylxd/deprecated/connection.py
+++ b/pylxd/deprecated/connection.py
@@ -24,7 +24,7 @@
from pylxd import exceptions
-from pylxd import utils
+from pylxd.deprecated import utils
from six.moves import http_client
from six.moves import queue
from ws4py import client as websocket
diff --git a/pylxd/deprecated/utils.py b/pylxd/deprecated/utils.py
new file mode 100644
index 0000000..4ea2841
--- /dev/null
+++ b/pylxd/deprecated/utils.py
@@ -0,0 +1,29 @@
+# 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 pylxd import exceptions
+
+
+def wait_for_container(name, timeout):
+ pass
+
+
+def block_container():
+ pass
+
+
+def get_lxd_error(state, data):
+ status_code = data.get('error_code')
+ error = data.get('error')
+ raise exceptions.APIError(error, status_code)
diff --git a/pylxd/utils.py b/pylxd/utils.py
deleted file mode 100644
index 4ea2841..0000000
--- a/pylxd/utils.py
+++ /dev/null
@@ -1,29 +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 pylxd import exceptions
-
-
-def wait_for_container(name, timeout):
- pass
-
-
-def block_container():
- pass
-
-
-def get_lxd_error(state, data):
- status_code = data.get('error_code')
- error = data.get('error')
- raise exceptions.APIError(error, status_code)
From 690b8a4ea685ae3196abe4b7f0ca037823ddc649 Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul at eventuallyanyway.com>
Date: Tue, 16 Feb 2016 19:55:02 -0700
Subject: [PATCH 08/15] Move pylxd.exceptions to pylxd.deprecated.exceptions
---
pylxd/deprecated/connection.py | 6 +++---
pylxd/deprecated/container.py | 2 +-
pylxd/deprecated/exceptions.py | 46 ++++++++++++++++++++++++++++++++++++++++++
pylxd/deprecated/utils.py | 2 +-
pylxd/exceptions.py | 46 ------------------------------------------
pylxd/hosts.py | 2 +-
pylxd/image.py | 2 +-
pylxd/tests/test_connection.py | 2 +-
pylxd/tests/test_host.py | 2 +-
pylxd/tests/test_image.py | 2 +-
10 files changed, 56 insertions(+), 56 deletions(-)
create mode 100644 pylxd/deprecated/exceptions.py
delete mode 100644 pylxd/exceptions.py
diff --git a/pylxd/deprecated/connection.py b/pylxd/deprecated/connection.py
index c38cf7b..5ef884e 100644
--- a/pylxd/deprecated/connection.py
+++ b/pylxd/deprecated/connection.py
@@ -22,13 +22,13 @@
import ssl
import threading
-
-from pylxd import exceptions
-from pylxd.deprecated import utils
from six.moves import http_client
from six.moves import queue
from ws4py import client as websocket
+from pylxd.deprecated import exceptions
+from pylxd.deprecated import utils
+
if hasattr(ssl, 'SSLContext'):
# For Python >= 2.7.9 and Python 3.x
USE_STDLIB_SSL = True
diff --git a/pylxd/deprecated/container.py b/pylxd/deprecated/container.py
index 037ebdd..19bf4b7 100644
--- a/pylxd/deprecated/container.py
+++ b/pylxd/deprecated/container.py
@@ -15,7 +15,7 @@
import json
from pylxd.deprecated import base
-from pylxd import exceptions
+from pylxd.deprecated import exceptions
class LXDContainer(base.LXDBase):
diff --git a/pylxd/deprecated/exceptions.py b/pylxd/deprecated/exceptions.py
new file mode 100644
index 0000000..6726bca
--- /dev/null
+++ b/pylxd/deprecated/exceptions.py
@@ -0,0 +1,46 @@
+# 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.
+
+
+class PyLXDException(Exception):
+ pass
+
+
+class ContainerUnDefined(PyLXDException):
+ pass
+
+
+class UntrustedHost(PyLXDException):
+ pass
+
+
+class ContainerProfileCreateFail(PyLXDException):
+ pass
+
+
+class ContainerProfileDeleteFail(PyLXDException):
+ pass
+
+
+class ImageInvalidSize(PyLXDException):
+ pass
+
+
+class APIError(PyLXDException):
+
+ def __init__(self, error, status_code):
+ msg = 'Error %s - %s.' % (status_code, error)
+ super(APIError, self).__init__(msg)
+ self.status_code = status_code
+ self.error = error
diff --git a/pylxd/deprecated/utils.py b/pylxd/deprecated/utils.py
index 4ea2841..b6c2585 100644
--- a/pylxd/deprecated/utils.py
+++ b/pylxd/deprecated/utils.py
@@ -12,7 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from pylxd import exceptions
+from pylxd.deprecated import exceptions
def wait_for_container(name, timeout):
diff --git a/pylxd/exceptions.py b/pylxd/exceptions.py
deleted file mode 100644
index 6726bca..0000000
--- a/pylxd/exceptions.py
+++ /dev/null
@@ -1,46 +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.
-
-
-class PyLXDException(Exception):
- pass
-
-
-class ContainerUnDefined(PyLXDException):
- pass
-
-
-class UntrustedHost(PyLXDException):
- pass
-
-
-class ContainerProfileCreateFail(PyLXDException):
- pass
-
-
-class ContainerProfileDeleteFail(PyLXDException):
- pass
-
-
-class ImageInvalidSize(PyLXDException):
- pass
-
-
-class APIError(PyLXDException):
-
- def __init__(self, error, status_code):
- msg = 'Error %s - %s.' % (status_code, error)
- super(APIError, self).__init__(msg)
- self.status_code = status_code
- self.error = error
diff --git a/pylxd/hosts.py b/pylxd/hosts.py
index e882bff..8e61894 100644
--- a/pylxd/hosts.py
+++ b/pylxd/hosts.py
@@ -14,7 +14,7 @@
from __future__ import print_function
from pylxd.deprecated import base
-from pylxd import exceptions
+from pylxd.deprecated import exceptions
class LXDHost(base.LXDBase):
diff --git a/pylxd/image.py b/pylxd/image.py
index a2cb19e..8a3e85c 100644
--- a/pylxd/image.py
+++ b/pylxd/image.py
@@ -19,7 +19,7 @@
from pylxd.deprecated import base
from pylxd.deprecated import connection
-from pylxd import exceptions
+from pylxd.deprecated import exceptions
from pylxd import mixin
from pylxd.operation import Operation
diff --git a/pylxd/tests/test_connection.py b/pylxd/tests/test_connection.py
index 1bbfdcf..50e8ffd 100644
--- a/pylxd/tests/test_connection.py
+++ b/pylxd/tests/test_connection.py
@@ -22,7 +22,7 @@
import unittest
from pylxd.deprecated import connection
-from pylxd import exceptions
+from pylxd.deprecated import exceptions
from pylxd.tests import annotated_data
if six.PY34:
diff --git a/pylxd/tests/test_host.py b/pylxd/tests/test_host.py
index 2002e57..80e2ad4 100644
--- a/pylxd/tests/test_host.py
+++ b/pylxd/tests/test_host.py
@@ -17,7 +17,7 @@
import mock
from pylxd.deprecated import connection
-from pylxd import exceptions
+from pylxd.deprecated import exceptions
from pylxd.tests import annotated_data
from pylxd.tests import fake_api
diff --git a/pylxd/tests/test_image.py b/pylxd/tests/test_image.py
index 4848c9b..69004f6 100644
--- a/pylxd/tests/test_image.py
+++ b/pylxd/tests/test_image.py
@@ -20,7 +20,7 @@
import unittest
from pylxd.deprecated import connection
-from pylxd import exceptions
+from pylxd.deprecated import exceptions
from pylxd import image
from pylxd.tests import annotated_data
From 8fa94f993d1b64dcea46be8a76717fa235145f45 Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul at eventuallyanyway.com>
Date: Tue, 16 Feb 2016 19:57:14 -0700
Subject: [PATCH 09/15] Move pylxd.hosts to pylxd.deprecated.hosts
---
pylxd/deprecated/api.py | 2 +-
pylxd/deprecated/hosts.py | 110 ++++++++++++++++++++++++++++++++++++++++++++++
pylxd/hosts.py | 110 ----------------------------------------------
3 files changed, 111 insertions(+), 111 deletions(-)
create mode 100644 pylxd/deprecated/hosts.py
delete mode 100644 pylxd/hosts.py
diff --git a/pylxd/deprecated/api.py b/pylxd/deprecated/api.py
index 910a870..0d432ce 100644
--- a/pylxd/deprecated/api.py
+++ b/pylxd/deprecated/api.py
@@ -17,7 +17,7 @@
from pylxd.deprecated import certificate
from pylxd.deprecated import connection
from pylxd.deprecated import container
-from pylxd import hosts
+from pylxd.deprecated import hosts
from pylxd import image
from pylxd import network
from pylxd import operation
diff --git a/pylxd/deprecated/hosts.py b/pylxd/deprecated/hosts.py
new file mode 100644
index 0000000..8e61894
--- /dev/null
+++ b/pylxd/deprecated/hosts.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.
+from __future__ import print_function
+
+from pylxd.deprecated import base
+from pylxd.deprecated import exceptions
+
+
+class LXDHost(base.LXDBase):
+
+ def host_ping(self):
+ try:
+ return self.connection.get_status('GET', '/1.0')
+ except Exception as e:
+ msg = 'LXD service is unavailable. %s' % e
+ raise exceptions.PyLXDException(msg)
+
+ def host_info(self):
+ (state, data) = self.connection.get_object('GET', '/1.0')
+
+ return {
+ 'lxd_api_compat_level':
+ self.get_lxd_api_compat(data.get('metadata')),
+ 'lxd_trusted_host':
+ self.get_lxd_host_trust(data.get('metadata')),
+ 'lxd_backing_fs':
+ self.get_lxd_backing_fs(data.get('metadata')),
+ 'lxd_driver':
+ self.get_lxd_driver(data.get('metadata')),
+ 'lxd_version':
+ self.get_lxd_version(data.get('metadata')),
+ 'lxc_version':
+ self.get_lxc_version(data.get('metadata')),
+ 'kernel_version':
+ self.get_kernel_version(data.get('metadata'))
+ }
+
+ def get_lxd_api_compat(self, data):
+ try:
+ if data is None:
+ (state, data) = self.connection.get_object('GET', '/1.0')
+ data = data.get('metadata')
+ return data['api_compat']
+ except exceptions.PyLXDException as e:
+ print('Handling run-time error: {}'.format(e))
+
+ def get_lxd_host_trust(self, data):
+ try:
+ if data is None:
+ (state, data) = self.connection.get_object('GET', '/1.0')
+ data = data.get('metadata')
+ return True if data['auth'] == 'trusted' else False
+ except exceptions.PyLXDException as e:
+ print('Handling run-time error: {}'.format(e))
+
+ def get_lxd_backing_fs(self, data):
+ try:
+ if data is None:
+ (state, data) = self.connection.get_object('GET', '/1.0')
+ data = data.get('metadata')
+ return data['environment']['backing_fs']
+ except exceptions.PyLXDException as e:
+ print('Handling run-time error: {}'.format(e))
+
+ def get_lxd_driver(self, data):
+ try:
+ if data is None:
+ (state, data) = self.connection.get_object('GET', '/1.0')
+ data = data.get('metadata')
+ return data['environment']['driver']
+ except exceptions.PyLXDException as e:
+ print('Handling run-time error: {}'.format(e))
+
+ def get_lxc_version(self, data):
+ try:
+ if data is None:
+ (state, data) = self.connection.get_object('GET', '/1.0')
+ data = data.get('metadata')
+ return data['environment']['lxc_version']
+ except exceptions.PyLXDException as e:
+ print('Handling run-time error: {}'.format(e))
+
+ def get_lxd_version(self, data):
+ try:
+ if data is None:
+ (state, data) = self.connection.get_object('GET', '/1.0')
+ data = data.get('metadata')
+ return float(data['environment']['lxd_version'])
+ except exceptions.PyLXDException as e:
+ print('Handling run-time error: {}'.format(e))
+
+ def get_kernel_version(self, data):
+ try:
+ if data is None:
+ (state, data) = self.connection.get_object('GET', '/1.0')
+ data = data.get('metadata')
+ return data['environment']['kernel_version']
+ except exceptions.PyLXDException as e:
+ print('Handling run-time error: {}'.format(e))
diff --git a/pylxd/hosts.py b/pylxd/hosts.py
deleted file mode 100644
index 8e61894..0000000
--- a/pylxd/hosts.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.
-from __future__ import print_function
-
-from pylxd.deprecated import base
-from pylxd.deprecated import exceptions
-
-
-class LXDHost(base.LXDBase):
-
- def host_ping(self):
- try:
- return self.connection.get_status('GET', '/1.0')
- except Exception as e:
- msg = 'LXD service is unavailable. %s' % e
- raise exceptions.PyLXDException(msg)
-
- def host_info(self):
- (state, data) = self.connection.get_object('GET', '/1.0')
-
- return {
- 'lxd_api_compat_level':
- self.get_lxd_api_compat(data.get('metadata')),
- 'lxd_trusted_host':
- self.get_lxd_host_trust(data.get('metadata')),
- 'lxd_backing_fs':
- self.get_lxd_backing_fs(data.get('metadata')),
- 'lxd_driver':
- self.get_lxd_driver(data.get('metadata')),
- 'lxd_version':
- self.get_lxd_version(data.get('metadata')),
- 'lxc_version':
- self.get_lxc_version(data.get('metadata')),
- 'kernel_version':
- self.get_kernel_version(data.get('metadata'))
- }
-
- def get_lxd_api_compat(self, data):
- try:
- if data is None:
- (state, data) = self.connection.get_object('GET', '/1.0')
- data = data.get('metadata')
- return data['api_compat']
- except exceptions.PyLXDException as e:
- print('Handling run-time error: {}'.format(e))
-
- def get_lxd_host_trust(self, data):
- try:
- if data is None:
- (state, data) = self.connection.get_object('GET', '/1.0')
- data = data.get('metadata')
- return True if data['auth'] == 'trusted' else False
- except exceptions.PyLXDException as e:
- print('Handling run-time error: {}'.format(e))
-
- def get_lxd_backing_fs(self, data):
- try:
- if data is None:
- (state, data) = self.connection.get_object('GET', '/1.0')
- data = data.get('metadata')
- return data['environment']['backing_fs']
- except exceptions.PyLXDException as e:
- print('Handling run-time error: {}'.format(e))
-
- def get_lxd_driver(self, data):
- try:
- if data is None:
- (state, data) = self.connection.get_object('GET', '/1.0')
- data = data.get('metadata')
- return data['environment']['driver']
- except exceptions.PyLXDException as e:
- print('Handling run-time error: {}'.format(e))
-
- def get_lxc_version(self, data):
- try:
- if data is None:
- (state, data) = self.connection.get_object('GET', '/1.0')
- data = data.get('metadata')
- return data['environment']['lxc_version']
- except exceptions.PyLXDException as e:
- print('Handling run-time error: {}'.format(e))
-
- def get_lxd_version(self, data):
- try:
- if data is None:
- (state, data) = self.connection.get_object('GET', '/1.0')
- data = data.get('metadata')
- return float(data['environment']['lxd_version'])
- except exceptions.PyLXDException as e:
- print('Handling run-time error: {}'.format(e))
-
- def get_kernel_version(self, data):
- try:
- if data is None:
- (state, data) = self.connection.get_object('GET', '/1.0')
- data = data.get('metadata')
- return data['environment']['kernel_version']
- except exceptions.PyLXDException as e:
- print('Handling run-time error: {}'.format(e))
From 57e1e681d692a2a4a119bcff9583199e1a3a63bf Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul at eventuallyanyway.com>
Date: Tue, 16 Feb 2016 19:58:44 -0700
Subject: [PATCH 10/15] Move pylxd.network import pylxd.deprecated.network
---
pylxd/deprecated/api.py | 2 +-
pylxd/deprecated/network.py | 59 +++++++++++++++++++++++++++++++++++++++++++++
pylxd/network.py | 59 ---------------------------------------------
3 files changed, 60 insertions(+), 60 deletions(-)
create mode 100644 pylxd/deprecated/network.py
delete mode 100644 pylxd/network.py
diff --git a/pylxd/deprecated/api.py b/pylxd/deprecated/api.py
index 0d432ce..bf1ddc1 100644
--- a/pylxd/deprecated/api.py
+++ b/pylxd/deprecated/api.py
@@ -19,7 +19,7 @@
from pylxd.deprecated import container
from pylxd.deprecated import hosts
from pylxd import image
-from pylxd import network
+from pylxd.deprecated import network
from pylxd import operation
from pylxd import profiles
diff --git a/pylxd/deprecated/network.py b/pylxd/deprecated/network.py
new file mode 100644
index 0000000..3eff79e
--- /dev/null
+++ b/pylxd/deprecated/network.py
@@ -0,0 +1,59 @@
+# 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 pylxd.deprecated import base
+
+
+class LXDNetwork(base.LXDBase):
+
+ def network_list(self):
+ (state, data) = self.connection.get_object('GET', '/1.0/networks')
+ return [network.split('/1.0/networks/')[-1]
+ for network in data['metadata']]
+
+ def network_show(self, network):
+ '''Show details of the LXD network'''
+ (state, data) = self.connection.get_object('GET', '/1.0/networks/%s'
+ % network)
+ return {
+ 'network_name':
+ self.show_network_name(network, data.get('metadata')),
+ 'network_type':
+ self.show_network_type(network, data.get('metadata')),
+ 'network_members':
+ self.show_network_members(network, data.get('metadata'))
+ }
+
+ def show_network_name(self, network, data):
+ '''Show the LXD network name'''
+ if data is None:
+ (state, data) = self.connection.get_object(
+ 'GET', '/1.0/networks/%s' % network)
+ data = data.get('metadata')
+ return data['name']
+
+ def show_network_type(self, network, data):
+ if data is None:
+ (state, data) = self.connection.get_object(
+ 'GET', '/1.0/networks/%s' % network)
+ data = data.get('metadata')
+ return data['type']
+
+ def show_network_members(self, network, data):
+ if data is None:
+ (state, data) = self.connection.get_object(
+ 'GET', '/1.0/networks/%s' % network)
+ data = data.get('metadata')
+ return [network_members.split('/1.0/networks/')[-1]
+ for network_members in data['members']]
diff --git a/pylxd/network.py b/pylxd/network.py
deleted file mode 100644
index 3eff79e..0000000
--- a/pylxd/network.py
+++ /dev/null
@@ -1,59 +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 pylxd.deprecated import base
-
-
-class LXDNetwork(base.LXDBase):
-
- def network_list(self):
- (state, data) = self.connection.get_object('GET', '/1.0/networks')
- return [network.split('/1.0/networks/')[-1]
- for network in data['metadata']]
-
- def network_show(self, network):
- '''Show details of the LXD network'''
- (state, data) = self.connection.get_object('GET', '/1.0/networks/%s'
- % network)
- return {
- 'network_name':
- self.show_network_name(network, data.get('metadata')),
- 'network_type':
- self.show_network_type(network, data.get('metadata')),
- 'network_members':
- self.show_network_members(network, data.get('metadata'))
- }
-
- def show_network_name(self, network, data):
- '''Show the LXD network name'''
- if data is None:
- (state, data) = self.connection.get_object(
- 'GET', '/1.0/networks/%s' % network)
- data = data.get('metadata')
- return data['name']
-
- def show_network_type(self, network, data):
- if data is None:
- (state, data) = self.connection.get_object(
- 'GET', '/1.0/networks/%s' % network)
- data = data.get('metadata')
- return data['type']
-
- def show_network_members(self, network, data):
- if data is None:
- (state, data) = self.connection.get_object(
- 'GET', '/1.0/networks/%s' % network)
- data = data.get('metadata')
- return [network_members.split('/1.0/networks/')[-1]
- for network_members in data['members']]
From f7e25fa48c3e55b5039b66a89ba5f74d61e7974b Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul at eventuallyanyway.com>
Date: Tue, 16 Feb 2016 20:02:26 -0700
Subject: [PATCH 11/15] Move pylxd.image.LXDImage to pylxd.deprecated.image
---
pylxd/deprecated/api.py | 2 +-
pylxd/deprecated/image.py | 244 ++++++++++++++++++++++++++++++++++++++++++++++
pylxd/image.py | 231 +------------------------------------------
pylxd/tests/test_image.py | 2 +-
4 files changed, 247 insertions(+), 232 deletions(-)
create mode 100644 pylxd/deprecated/image.py
diff --git a/pylxd/deprecated/api.py b/pylxd/deprecated/api.py
index bf1ddc1..48b1518 100644
--- a/pylxd/deprecated/api.py
+++ b/pylxd/deprecated/api.py
@@ -18,7 +18,7 @@
from pylxd.deprecated import connection
from pylxd.deprecated import container
from pylxd.deprecated import hosts
-from pylxd import image
+from pylxd.deprecated import image
from pylxd.deprecated import network
from pylxd import operation
from pylxd import profiles
diff --git a/pylxd/deprecated/image.py b/pylxd/deprecated/image.py
new file mode 100644
index 0000000..acfdbca
--- /dev/null
+++ b/pylxd/deprecated/image.py
@@ -0,0 +1,244 @@
+# 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 __future__ import print_function
+import datetime
+import json
+
+from six.moves import urllib
+
+from pylxd.deprecated import base
+from pylxd.deprecated import connection
+from pylxd.deprecated import exceptions
+
+image_architecture = {
+ 0: 'Unknown',
+ 1: 'i686',
+ 2: 'x86_64',
+ 3: 'armv7l',
+ 4: 'aarch64',
+ 5: 'ppc',
+ 6: 'ppc64',
+ 7: 'ppc64le'
+}
+
+
+class LXDImage(base.LXDBase):
+
+ def __init__(self, conn=None):
+ self.connection = conn or connection.LXDConnection()
+
+ # list images
+ def image_list(self):
+ try:
+ (state, data) = self.connection.get_object('GET', '/1.0/images')
+ return [image.split('/1.0/images/')[-1]
+ for image in data['metadata']]
+ except Exception as e:
+ print("Unable to fetch image info - {}".format(e))
+ raise
+
+ def image_defined(self, image):
+ try:
+ (state, data) = self.connection.get_object('GET', '/1.0/images/%s'
+ % image)
+ except exceptions.APIError as ex:
+ if ex.status_code == 404:
+ return False
+ else:
+ raise
+ else:
+ return True
+
+ def image_list_by_key(self, params):
+ try:
+ (state, data) = self.connection.get_object(
+ 'GET', '/1.0/images', urllib.parse.urlencode(params))
+ return [image.split('/1.0/images/')[-1]
+ for image in data['metadata']]
+ except Exception as e:
+ print("Unable to fetch image info - {}".format(e))
+ raise
+
+ # image info
+ def image_info(self, image):
+ try:
+ (state, data) = self.connection.get_object('GET', '/1.0/images/%s'
+ % image)
+ image = {
+ 'image_upload_date': self.get_image_date(image,
+ data.get('metadata'),
+ 'uploaded_at'),
+ 'image_created_date': self.get_image_date(image,
+ data.get('metadata'),
+ 'created_at'),
+ 'image_expires_date': self.get_image_date(image,
+ data.get('metadata'),
+ 'expires_at'),
+ 'image_public': self.get_image_permission(
+ image,
+ data.get('metadata')),
+ 'image_size': '%sMB' % self.get_image_size(
+ image,
+ data.get('metadata')),
+ 'image_fingerprint': self.get_image_fingerprint(
+ image,
+ data.get('metadata')),
+ 'image_architecture': self.get_image_architecture(
+ image,
+ data.get('metadata')),
+ }
+
+ return image
+ except Exception as e:
+ print("Unable to fetch image info - {}".format(e))
+ raise
+
+ def get_image_date(self, image, data, key):
+ try:
+ if data is None:
+ (state, data) = self.connection.get_object(
+ 'GET', '/1.0/images/%s' % image)
+ data = data.get('metadata')
+ if data[key] != 0:
+ return datetime.datetime.fromtimestamp(
+ data[key]).strftime('%Y-%m-%d %H:%M:%S')
+ else:
+ return 'Unknown'
+ except Exception as e:
+ print("Unable to fetch image info - {}".format(e))
+ raise
+
+ def get_image_permission(self, image, data):
+ try:
+ if data is None:
+ (state, data) = self.connection.get_object(
+ 'GET', '/1.0/images/%s' % image)
+ data = data.get('metadata')
+ return True if data['public'] == 1 else False
+ except Exception as e:
+ print("Unable to fetch image info - {}".format(e))
+ raise
+
+ def get_image_size(self, image, data):
+ try:
+ if data is None:
+ (state, data) = self.connection.get_object(
+ 'GET', '/1.0/images/%s' % image)
+ data = data.get('metadata')
+ image_size = data['size']
+ if image_size <= 0:
+ raise exceptions.ImageInvalidSize()
+ return image_size // 1024 ** 2
+ except Exception as e:
+ print("Unable to fetch image info - {}".format(e))
+ raise
+
+ def get_image_fingerprint(self, image, data):
+ try:
+ if data is None:
+ (state, data) = self.connection.get_object(
+ 'GET', '/1.0/images/%s' % image)
+ data = data.get('metadata')
+ return data['fingerprint']
+ except Exception as e:
+ print("Unable to fetch image info - {}".format(e))
+ raise
+
+ def get_image_architecture(self, image, data):
+ try:
+ if data is None:
+ (state, data) = self.connection.get_object(
+ 'GET', '/1.0/images/%s' % image)
+ data = data.get('metadata')
+ return image_architecture[data['architecture']]
+ except Exception as e:
+ print("Unable to fetch image info - {}".format(e))
+ raise
+
+ # image operations
+ def image_upload(self, path=None, data=None, headers={}):
+ data = data or open(path, 'rb').read()
+ try:
+ return self.connection.get_object('POST', '/1.0/images',
+ data, headers)
+ except Exception as e:
+ print("Unable to upload image - {}".format(e))
+ raise
+
+ def image_delete(self, image):
+ try:
+ return self.connection.get_status('DELETE', '/1.0/images/%s'
+ % image)
+ except Exception as e:
+ print("Unable to delete image - {}".format(e))
+ raise
+
+ def image_export(self, image):
+ try:
+ return self.connection.get_raw('GET', '/1.0/images/%s/export'
+ % image)
+ except Exception as e:
+ print("Unable to export image - {}".format(e))
+ raise
+
+ def image_update(self, image, data):
+ try:
+ return self.connection.get_status('PUT', '/1.0/images/%s' % image,
+ json.dumps(data))
+ except Exception as e:
+ print("Unable to update image - {}".format(e))
+ raise
+
+ def image_rename(self, image, data):
+ try:
+ return self.connection.get_status('POST', '/1.0/images/%s' % image,
+ json.dumps(data))
+ except Exception as e:
+ print("Unable to rename image - {}".format(e))
+ raise
+
+
+class LXDAlias(base.LXDBase):
+
+ def alias_list(self):
+ (state, data) = self.connection.get_object(
+ 'GET', '/1.0/images/aliases')
+ return [alias.split('/1.0/images/aliases/')[-1]
+ for alias in data['metadata']]
+
+ def alias_defined(self, alias):
+ return self.connection.get_status('GET', '/1.0/images/aliases/%s'
+ % alias)
+
+ def alias_show(self, alias):
+ return self.connection.get_object('GET', '/1.0/images/aliases/%s'
+ % alias)
+
+ def alias_update(self, alias, data):
+ return self.connection.get_status('PUT',
+ '/1.0/images/aliases/%s' % alias,
+ json.dumps(data))
+
+ def alias_rename(self, alias, data):
+ return self.connection.get_status('POST',
+ '/1.0/images/aliases/%s' % alias,
+ json.dumps(data))
+
+ def alias_create(self, data):
+ return self.connection.get_status('POST', '/1.0/images/aliases',
+ json.dumps(data))
+
+ def alias_delete(self, alias):
+ return self.connection.get_status('DELETE', '/1.0/images/aliases/%s'
+ % alias)
diff --git a/pylxd/image.py b/pylxd/image.py
index 8a3e85c..31a5257 100644
--- a/pylxd/image.py
+++ b/pylxd/image.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2015 Canonical Ltd
+# Copyright (c) 2016 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
@@ -11,240 +11,11 @@
# 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 print_function
-import datetime
import hashlib
-import json
-from six.moves import urllib
-from pylxd.deprecated import base
-from pylxd.deprecated import connection
-from pylxd.deprecated import exceptions
from pylxd import mixin
from pylxd.operation import Operation
-image_architecture = {
- 0: 'Unknown',
- 1: 'i686',
- 2: 'x86_64',
- 3: 'armv7l',
- 4: 'aarch64',
- 5: 'ppc',
- 6: 'ppc64',
- 7: 'ppc64le'
-}
-
-
-class LXDImage(base.LXDBase):
-
- def __init__(self, conn=None):
- self.connection = conn or connection.LXDConnection()
-
- # list images
- def image_list(self):
- try:
- (state, data) = self.connection.get_object('GET', '/1.0/images')
- return [image.split('/1.0/images/')[-1]
- for image in data['metadata']]
- except Exception as e:
- print("Unable to fetch image info - {}".format(e))
- raise
-
- def image_defined(self, image):
- try:
- (state, data) = self.connection.get_object('GET', '/1.0/images/%s'
- % image)
- except exceptions.APIError as ex:
- if ex.status_code == 404:
- return False
- else:
- raise
- else:
- return True
-
- def image_list_by_key(self, params):
- try:
- (state, data) = self.connection.get_object(
- 'GET', '/1.0/images', urllib.parse.urlencode(params))
- return [image.split('/1.0/images/')[-1]
- for image in data['metadata']]
- except Exception as e:
- print("Unable to fetch image info - {}".format(e))
- raise
-
- # image info
- def image_info(self, image):
- try:
- (state, data) = self.connection.get_object('GET', '/1.0/images/%s'
- % image)
- image = {
- 'image_upload_date': self.get_image_date(image,
- data.get('metadata'),
- 'uploaded_at'),
- 'image_created_date': self.get_image_date(image,
- data.get('metadata'),
- 'created_at'),
- 'image_expires_date': self.get_image_date(image,
- data.get('metadata'),
- 'expires_at'),
- 'image_public': self.get_image_permission(
- image,
- data.get('metadata')),
- 'image_size': '%sMB' % self.get_image_size(
- image,
- data.get('metadata')),
- 'image_fingerprint': self.get_image_fingerprint(
- image,
- data.get('metadata')),
- 'image_architecture': self.get_image_architecture(
- image,
- data.get('metadata')),
- }
-
- return image
- except Exception as e:
- print("Unable to fetch image info - {}".format(e))
- raise
-
- def get_image_date(self, image, data, key):
- try:
- if data is None:
- (state, data) = self.connection.get_object(
- 'GET', '/1.0/images/%s' % image)
- data = data.get('metadata')
- if data[key] != 0:
- return datetime.datetime.fromtimestamp(
- data[key]).strftime('%Y-%m-%d %H:%M:%S')
- else:
- return 'Unknown'
- except Exception as e:
- print("Unable to fetch image info - {}".format(e))
- raise
-
- def get_image_permission(self, image, data):
- try:
- if data is None:
- (state, data) = self.connection.get_object(
- 'GET', '/1.0/images/%s' % image)
- data = data.get('metadata')
- return True if data['public'] == 1 else False
- except Exception as e:
- print("Unable to fetch image info - {}".format(e))
- raise
-
- def get_image_size(self, image, data):
- try:
- if data is None:
- (state, data) = self.connection.get_object(
- 'GET', '/1.0/images/%s' % image)
- data = data.get('metadata')
- image_size = data['size']
- if image_size <= 0:
- raise exceptions.ImageInvalidSize()
- return image_size // 1024 ** 2
- except Exception as e:
- print("Unable to fetch image info - {}".format(e))
- raise
-
- def get_image_fingerprint(self, image, data):
- try:
- if data is None:
- (state, data) = self.connection.get_object(
- 'GET', '/1.0/images/%s' % image)
- data = data.get('metadata')
- return data['fingerprint']
- except Exception as e:
- print("Unable to fetch image info - {}".format(e))
- raise
-
- def get_image_architecture(self, image, data):
- try:
- if data is None:
- (state, data) = self.connection.get_object(
- 'GET', '/1.0/images/%s' % image)
- data = data.get('metadata')
- return image_architecture[data['architecture']]
- except Exception as e:
- print("Unable to fetch image info - {}".format(e))
- raise
-
- # image operations
- def image_upload(self, path=None, data=None, headers={}):
- data = data or open(path, 'rb').read()
- try:
- return self.connection.get_object('POST', '/1.0/images',
- data, headers)
- except Exception as e:
- print("Unable to upload image - {}".format(e))
- raise
-
- def image_delete(self, image):
- try:
- return self.connection.get_status('DELETE', '/1.0/images/%s'
- % image)
- except Exception as e:
- print("Unable to delete image - {}".format(e))
- raise
-
- def image_export(self, image):
- try:
- return self.connection.get_raw('GET', '/1.0/images/%s/export'
- % image)
- except Exception as e:
- print("Unable to export image - {}".format(e))
- raise
-
- def image_update(self, image, data):
- try:
- return self.connection.get_status('PUT', '/1.0/images/%s' % image,
- json.dumps(data))
- except Exception as e:
- print("Unable to update image - {}".format(e))
- raise
-
- def image_rename(self, image, data):
- try:
- return self.connection.get_status('POST', '/1.0/images/%s' % image,
- json.dumps(data))
- except Exception as e:
- print("Unable to rename image - {}".format(e))
- raise
-
-
-class LXDAlias(base.LXDBase):
-
- def alias_list(self):
- (state, data) = self.connection.get_object(
- 'GET', '/1.0/images/aliases')
- return [alias.split('/1.0/images/aliases/')[-1]
- for alias in data['metadata']]
-
- def alias_defined(self, alias):
- return self.connection.get_status('GET', '/1.0/images/aliases/%s'
- % alias)
-
- def alias_show(self, alias):
- return self.connection.get_object('GET', '/1.0/images/aliases/%s'
- % alias)
-
- def alias_update(self, alias, data):
- return self.connection.get_status('PUT',
- '/1.0/images/aliases/%s' % alias,
- json.dumps(data))
-
- def alias_rename(self, alias, data):
- return self.connection.get_status('POST',
- '/1.0/images/aliases/%s' % alias,
- json.dumps(data))
-
- def alias_create(self, data):
- return self.connection.get_status('POST', '/1.0/images/aliases',
- json.dumps(data))
-
- def alias_delete(self, alias):
- return self.connection.get_status('DELETE', '/1.0/images/aliases/%s'
- % alias)
-
class Image(mixin.Waitable, mixin.Marshallable):
"""A LXD Image."""
diff --git a/pylxd/tests/test_image.py b/pylxd/tests/test_image.py
index 69004f6..93b33f1 100644
--- a/pylxd/tests/test_image.py
+++ b/pylxd/tests/test_image.py
@@ -21,7 +21,7 @@
from pylxd.deprecated import connection
from pylxd.deprecated import exceptions
-from pylxd import image
+from pylxd.deprecated import image
from pylxd.tests import annotated_data
from pylxd.tests import fake_api
From 763d04b3f40f47490419690f8589b2a22523e748 Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul at eventuallyanyway.com>
Date: Tue, 16 Feb 2016 20:05:22 -0700
Subject: [PATCH 12/15] Move pylxd.operation.LXDOperation to
pylxd.deprecated.operation
---
pylxd/deprecated/api.py | 2 +-
pylxd/deprecated/operation.py | 73 +++++++++++++++++++++++++++++++++++++++++++
pylxd/operation.py | 63 +------------------------------------
3 files changed, 75 insertions(+), 63 deletions(-)
create mode 100644 pylxd/deprecated/operation.py
diff --git a/pylxd/deprecated/api.py b/pylxd/deprecated/api.py
index 48b1518..5458612 100644
--- a/pylxd/deprecated/api.py
+++ b/pylxd/deprecated/api.py
@@ -20,7 +20,7 @@
from pylxd.deprecated import hosts
from pylxd.deprecated import image
from pylxd.deprecated import network
-from pylxd import operation
+from pylxd.deprecated import operation
from pylxd import profiles
diff --git a/pylxd/deprecated/operation.py b/pylxd/deprecated/operation.py
new file mode 100644
index 0000000..48fd81b
--- /dev/null
+++ b/pylxd/deprecated/operation.py
@@ -0,0 +1,73 @@
+# 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 dateutil.parser import parse as parse_date
+
+from pylxd.deprecated import base
+
+
+class LXDOperation(base.LXDBase):
+
+ def operation_list(self):
+ (state, data) = self.connection.get_object('GET', '/1.0/operations')
+ return data['metadata']
+
+ def operation_show(self, operation):
+ (state, data) = self.connection.get_object('GET', operation)
+
+ return {
+ 'operation_create_time':
+ self.operation_create_time(operation, data.get('metadata')),
+ 'operation_update_time':
+ self.operation_update_time(operation, data.get('metadata')),
+ 'operation_status_code':
+ self.operation_status_code(operation, data.get('metadata'))
+ }
+
+ def operation_info(self, operation):
+ return self.connection.get_object('GET', operation)
+
+ def operation_create_time(self, operation, data):
+ if data is None:
+ (state, data) = self.connection.get_object('GET', operation)
+ data = data.get('metadata')
+ return parse_date(data['created_at']).strftime('%Y-%m-%d %H:%M:%S')
+
+ def operation_update_time(self, operation, data):
+ if data is None:
+ (state, data) = self.connection.get_object('GET', operation)
+ data = data.get('metadata')
+ return parse_date(data['updated_at']).strftime('%Y-%m-%d %H:%M:%S')
+
+ def operation_status_code(self, operation, data):
+ if data is None:
+ (state, data) = self.connection.get_object('GET', operation)
+ data = data.get('metadata')
+ return data['status']
+
+ def operation_wait(self, operation, status_code, timeout):
+ if timeout == -1:
+ return self.connection.get_status(
+ 'GET', '%s/wait?status_code=%s'
+ % (operation, status_code))
+ else:
+ return self.connection.get_status(
+ 'GET', '%s/wait?status_code=%s&timeout=%s'
+ % (operation, status_code, timeout))
+
+ def operation_stream(self, operation, operation_secret):
+ return self.connection.get_ws(
+ '%s/websocket?secret=%s' % (operation, operation_secret))
+
+ def operation_delete(self, operation):
+ return self.connection.get_status('DELETE', operation)
diff --git a/pylxd/operation.py b/pylxd/operation.py
index 27766f6..b5c7d67 100644
--- a/pylxd/operation.py
+++ b/pylxd/operation.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2015 Canonical Ltd
+# Copyright (c) 2016 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
@@ -12,67 +12,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-from dateutil.parser import parse as parse_date
-
-from pylxd.deprecated import base
-
-
-class LXDOperation(base.LXDBase):
-
- def operation_list(self):
- (state, data) = self.connection.get_object('GET', '/1.0/operations')
- return data['metadata']
-
- def operation_show(self, operation):
- (state, data) = self.connection.get_object('GET', operation)
-
- return {
- 'operation_create_time':
- self.operation_create_time(operation, data.get('metadata')),
- 'operation_update_time':
- self.operation_update_time(operation, data.get('metadata')),
- 'operation_status_code':
- self.operation_status_code(operation, data.get('metadata'))
- }
-
- def operation_info(self, operation):
- return self.connection.get_object('GET', operation)
-
- def operation_create_time(self, operation, data):
- if data is None:
- (state, data) = self.connection.get_object('GET', operation)
- data = data.get('metadata')
- return parse_date(data['created_at']).strftime('%Y-%m-%d %H:%M:%S')
-
- def operation_update_time(self, operation, data):
- if data is None:
- (state, data) = self.connection.get_object('GET', operation)
- data = data.get('metadata')
- return parse_date(data['updated_at']).strftime('%Y-%m-%d %H:%M:%S')
-
- def operation_status_code(self, operation, data):
- if data is None:
- (state, data) = self.connection.get_object('GET', operation)
- data = data.get('metadata')
- return data['status']
-
- def operation_wait(self, operation, status_code, timeout):
- if timeout == -1:
- return self.connection.get_status(
- 'GET', '%s/wait?status_code=%s'
- % (operation, status_code))
- else:
- return self.connection.get_status(
- 'GET', '%s/wait?status_code=%s&timeout=%s'
- % (operation, status_code, timeout))
-
- def operation_stream(self, operation, operation_secret):
- return self.connection.get_ws(
- '%s/websocket?secret=%s' % (operation, operation_secret))
-
- def operation_delete(self, operation):
- return self.connection.get_status('DELETE', operation)
-
class Operation(object):
"""A LXD operation."""
From 8fdaae803971241008ee657c8d75fc27219d5124 Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul at eventuallyanyway.com>
Date: Tue, 16 Feb 2016 20:08:14 -0700
Subject: [PATCH 13/15] Move pylxd.profiles.LXDProfile to
pylxd.deprecated.profiles
---
pylxd/deprecated/api.py | 2 +-
pylxd/deprecated/profiles.py | 57 ++++++++++++++++++++++++++++++++++++++++++++
pylxd/profiles.py | 46 +----------------------------------
3 files changed, 59 insertions(+), 46 deletions(-)
create mode 100644 pylxd/deprecated/profiles.py
diff --git a/pylxd/deprecated/api.py b/pylxd/deprecated/api.py
index 5458612..bb0f115 100644
--- a/pylxd/deprecated/api.py
+++ b/pylxd/deprecated/api.py
@@ -21,7 +21,7 @@
from pylxd.deprecated import image
from pylxd.deprecated import network
from pylxd.deprecated import operation
-from pylxd import profiles
+from pylxd.deprecated import profiles
class API(object):
diff --git a/pylxd/deprecated/profiles.py b/pylxd/deprecated/profiles.py
new file mode 100644
index 0000000..12c1300
--- /dev/null
+++ b/pylxd/deprecated/profiles.py
@@ -0,0 +1,57 @@
+# 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 json
+
+from pylxd.deprecated import base
+
+
+class LXDProfile(base.LXDBase):
+
+ def profile_list(self):
+ '''List profiles on the LXD daemon as an array.'''
+ (state, data) = self.connection.get_object('GET', '/1.0/profiles')
+ return [profiles.split('/1.0/profiles/')[-1]
+ for profiles in data['metadata']]
+
+ def profile_create(self, profile):
+ '''Create an LXD Profile'''
+ return self.connection.get_status('POST', '/1.0/profiles',
+ json.dumps(profile))
+
+ def profile_show(self, profile):
+ '''Display the LXD profile'''
+ return self.connection.get_object('GET', '/1.0/profiles/%s'
+ % profile)
+
+ def profile_defined(self, profile):
+ '''Check for an LXD profile'''
+ return self.connection.get_status('GET', '/1.0/profiles/%s'
+ % profile)
+
+ def profile_update(self, profile, config):
+ '''Update the LXD profile (not implemented)'''
+ return self.connection.get_status('PUT', '/1.0/profiles/%s'
+ % profile,
+ json.dumps(config))
+
+ def profile_rename(self, profile, config):
+ '''Rename the LXD profile'''
+ return self.connection.get_status('POST', '/1.0/profiles/%s'
+ % profile,
+ json.dumps(config))
+
+ def profile_delete(self, profile):
+ '''Delete the LXD profile'''
+ return self.connection.get_status('DELETE', '/1.0/profiles/%s'
+ % profile)
diff --git a/pylxd/profiles.py b/pylxd/profiles.py
index da39c2b..2567dc8 100644
--- a/pylxd/profiles.py
+++ b/pylxd/profiles.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2015 Canonical Ltd
+# Copyright (c) 2016 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
@@ -12,53 +12,9 @@
# License for the specific language governing permissions and limitations
# under the License.
# XXX: rockstar (15 Feb 2016) - This module should be renamed to 'profile'.
-import json
-
-from pylxd.deprecated import base
from pylxd import mixin
-class LXDProfile(base.LXDBase):
-
- def profile_list(self):
- '''List profiles on the LXD daemon as an array.'''
- (state, data) = self.connection.get_object('GET', '/1.0/profiles')
- return [profiles.split('/1.0/profiles/')[-1]
- for profiles in data['metadata']]
-
- def profile_create(self, profile):
- '''Create an LXD Profile'''
- return self.connection.get_status('POST', '/1.0/profiles',
- json.dumps(profile))
-
- def profile_show(self, profile):
- '''Display the LXD profile'''
- return self.connection.get_object('GET', '/1.0/profiles/%s'
- % profile)
-
- def profile_defined(self, profile):
- '''Check for an LXD profile'''
- return self.connection.get_status('GET', '/1.0/profiles/%s'
- % profile)
-
- def profile_update(self, profile, config):
- '''Update the LXD profile (not implemented)'''
- return self.connection.get_status('PUT', '/1.0/profiles/%s'
- % profile,
- json.dumps(config))
-
- def profile_rename(self, profile, config):
- '''Rename the LXD profile'''
- return self.connection.get_status('POST', '/1.0/profiles/%s'
- % profile,
- json.dumps(config))
-
- def profile_delete(self, profile):
- '''Delete the LXD profile'''
- return self.connection.get_status('DELETE', '/1.0/profiles/%s'
- % profile)
-
-
class Profile(mixin.Marshallable):
"""A LXD profile."""
From d9664c0e3e074a7975eb5f268f0d1366053da1ce Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul at eventuallyanyway.com>
Date: Tue, 16 Feb 2016 20:15:48 -0700
Subject: [PATCH 14/15] Move pylxd.profiles pylxd.profile
---
pylxd/client.py | 2 +-
pylxd/profile.py | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
pylxd/profiles.py | 78 -------------------------------------------------------
3 files changed, 78 insertions(+), 79 deletions(-)
create mode 100644 pylxd/profile.py
delete mode 100644 pylxd/profiles.py
diff --git a/pylxd/client.py b/pylxd/client.py
index b2d5fc3..33a0e3d 100644
--- a/pylxd/client.py
+++ b/pylxd/client.py
@@ -21,7 +21,7 @@
from pylxd.container import Container
from pylxd.image import Image
from pylxd.operation import Operation
-from pylxd.profiles import Profile
+from pylxd.profile import Profile
requests_unixsocket.monkeypatch()
diff --git a/pylxd/profile.py b/pylxd/profile.py
new file mode 100644
index 0000000..13c3847
--- /dev/null
+++ b/pylxd/profile.py
@@ -0,0 +1,77 @@
+# Copyright (c) 2016 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 pylxd import mixin
+
+
+class Profile(mixin.Marshallable):
+ """A LXD profile."""
+
+ __slots__ = [
+ '_client',
+ 'config', 'devices', 'name'
+ ]
+
+ @classmethod
+ def get(cls, client, name):
+ """Get a profile."""
+ response = client.api.profiles[name].get()
+
+ if response.status_code == 404:
+ raise NameError('No profile with name "{}"'.format(name))
+ return cls(_client=client, **response.json()['metadata'])
+
+ @classmethod
+ def all(cls, client):
+ """Get all profiles."""
+ response = client.api.profiles.get()
+
+ profiles = []
+ for url in response.json()['metadata']:
+ name = url.split('/')[-1]
+ profiles.append(cls(_client=client, name=name))
+ return profiles
+
+ @classmethod
+ def create(cls, client, name, config):
+ """Create a profile."""
+ client.api.profiles.post(json={
+ 'name': name,
+ 'config': config
+ })
+
+ return cls.get(client, name)
+
+ def __init__(self, **kwargs):
+ super(Profile, self).__init__()
+ for key, value in kwargs.iteritems():
+ setattr(self, key, value)
+
+ def update(self):
+ """Update the profile in LXD based on local changes."""
+ marshalled = self.marshall()
+ # The name property cannot be updated.
+ del marshalled['name']
+
+ self._client.api.profiles[self.name].put(json=marshalled)
+
+ def rename(self, new):
+ """Rename the profile."""
+ raise NotImplementedError(
+ 'LXD does not currently support renaming profiles')
+ self._client.api.profiles[self.name].post(json={'name': new})
+ self.name = new
+
+ def delete(self):
+ """Delete a profile."""
+ self._client.api.profiles[self.name].delete()
diff --git a/pylxd/profiles.py b/pylxd/profiles.py
deleted file mode 100644
index 2567dc8..0000000
--- a/pylxd/profiles.py
+++ /dev/null
@@ -1,78 +0,0 @@
-# Copyright (c) 2016 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.
-# XXX: rockstar (15 Feb 2016) - This module should be renamed to 'profile'.
-from pylxd import mixin
-
-
-class Profile(mixin.Marshallable):
- """A LXD profile."""
-
- __slots__ = [
- '_client',
- 'config', 'devices', 'name'
- ]
-
- @classmethod
- def get(cls, client, name):
- """Get a profile."""
- response = client.api.profiles[name].get()
-
- if response.status_code == 404:
- raise NameError('No profile with name "{}"'.format(name))
- return cls(_client=client, **response.json()['metadata'])
-
- @classmethod
- def all(cls, client):
- """Get all profiles."""
- response = client.api.profiles.get()
-
- profiles = []
- for url in response.json()['metadata']:
- name = url.split('/')[-1]
- profiles.append(cls(_client=client, name=name))
- return profiles
-
- @classmethod
- def create(cls, client, name, config):
- """Create a profile."""
- client.api.profiles.post(json={
- 'name': name,
- 'config': config
- })
-
- return cls.get(client, name)
-
- def __init__(self, **kwargs):
- super(Profile, self).__init__()
- for key, value in kwargs.iteritems():
- setattr(self, key, value)
-
- def update(self):
- """Update the profile in LXD based on local changes."""
- marshalled = self.marshall()
- # The name property cannot be updated.
- del marshalled['name']
-
- self._client.api.profiles[self.name].put(json=marshalled)
-
- def rename(self, new):
- """Rename the profile."""
- raise NotImplementedError(
- 'LXD does not currently support renaming profiles')
- self._client.api.profiles[self.name].post(json={'name': new})
- self.name = new
-
- def delete(self):
- """Delete a profile."""
- self._client.api.profiles[self.name].delete()
From 73b3a6405d6dd13fdf135992f0fc5ebb4e965d6c Mon Sep 17 00:00:00 2001
From: Paul Hummer <paul at eventuallyanyway.com>
Date: Tue, 16 Feb 2016 20:22:11 -0700
Subject: [PATCH 15/15] Move pylxd.tests to pylxd.deprecated.tests
---
pylxd/deprecated/tests/__init__.py | 40 +++++
pylxd/deprecated/tests/fake_api.py | 272 +++++++++++++++++++++++++++++
pylxd/deprecated/tests/test_certificate.py | 57 ++++++
pylxd/deprecated/tests/test_client.py | 90 ++++++++++
pylxd/deprecated/tests/test_connection.py | 172 ++++++++++++++++++
pylxd/deprecated/tests/test_container.py | 222 +++++++++++++++++++++++
pylxd/deprecated/tests/test_host.py | 82 +++++++++
pylxd/deprecated/tests/test_image.py | 241 +++++++++++++++++++++++++
pylxd/deprecated/tests/test_image_alias.py | 66 +++++++
pylxd/deprecated/tests/test_network.py | 57 ++++++
pylxd/deprecated/tests/test_operation.py | 75 ++++++++
pylxd/deprecated/tests/test_profiles.py | 69 ++++++++
pylxd/deprecated/tests/utils.py | 42 +++++
pylxd/tests/__init__.py | 40 -----
pylxd/tests/fake_api.py | 272 -----------------------------
pylxd/tests/test_certificate.py | 57 ------
pylxd/tests/test_client.py | 90 ----------
pylxd/tests/test_connection.py | 172 ------------------
pylxd/tests/test_container.py | 222 -----------------------
pylxd/tests/test_host.py | 82 ---------
pylxd/tests/test_image.py | 241 -------------------------
pylxd/tests/test_image_alias.py | 66 -------
pylxd/tests/test_network.py | 57 ------
pylxd/tests/test_operation.py | 75 --------
pylxd/tests/test_profiles.py | 69 --------
pylxd/tests/utils.py | 42 -----
26 files changed, 1485 insertions(+), 1485 deletions(-)
create mode 100644 pylxd/deprecated/tests/__init__.py
create mode 100644 pylxd/deprecated/tests/fake_api.py
create mode 100644 pylxd/deprecated/tests/test_certificate.py
create mode 100644 pylxd/deprecated/tests/test_client.py
create mode 100644 pylxd/deprecated/tests/test_connection.py
create mode 100644 pylxd/deprecated/tests/test_container.py
create mode 100644 pylxd/deprecated/tests/test_host.py
create mode 100644 pylxd/deprecated/tests/test_image.py
create mode 100644 pylxd/deprecated/tests/test_image_alias.py
create mode 100644 pylxd/deprecated/tests/test_network.py
create mode 100644 pylxd/deprecated/tests/test_operation.py
create mode 100644 pylxd/deprecated/tests/test_profiles.py
create mode 100644 pylxd/deprecated/tests/utils.py
delete mode 100644 pylxd/tests/__init__.py
delete mode 100644 pylxd/tests/fake_api.py
delete mode 100644 pylxd/tests/test_certificate.py
delete mode 100644 pylxd/tests/test_client.py
delete mode 100644 pylxd/tests/test_connection.py
delete mode 100644 pylxd/tests/test_container.py
delete mode 100644 pylxd/tests/test_host.py
delete mode 100644 pylxd/tests/test_image.py
delete mode 100644 pylxd/tests/test_image_alias.py
delete mode 100644 pylxd/tests/test_network.py
delete mode 100644 pylxd/tests/test_operation.py
delete mode 100644 pylxd/tests/test_profiles.py
delete mode 100644 pylxd/tests/utils.py
diff --git a/pylxd/deprecated/tests/__init__.py b/pylxd/deprecated/tests/__init__.py
new file mode 100644
index 0000000..4780a2b
--- /dev/null
+++ b/pylxd/deprecated/tests/__init__.py
@@ -0,0 +1,40 @@
+# 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 ddt import data
+from ddt import unpack
+import unittest
+
+from pylxd import api
+
+
+class LXDAPITestBase(unittest.TestCase):
+
+ def setUp(self):
+ super(LXDAPITestBase, self).setUp()
+ self.lxd = api.API()
+
+
+def annotated_data(*args):
+ class List(list):
+ pass
+
+ new_args = []
+
+ for arg in args:
+ new_arg = List(arg)
+ new_arg.__name__ = arg[0]
+ new_args.append(new_arg)
+
+ return lambda func: data(*new_args)(unpack(func))
diff --git a/pylxd/deprecated/tests/fake_api.py b/pylxd/deprecated/tests/fake_api.py
new file mode 100644
index 0000000..849246e
--- /dev/null
+++ b/pylxd/deprecated/tests/fake_api.py
@@ -0,0 +1,272 @@
+# 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": status
+ }
+ }
+
+
+def fake_container_log():
+ return {
+ "type": "sync",
+ "status": "Success",
+ "status_code": 200,
+ "metadata": {
+ "log": "fake log"
+ }
+ }
+
+
+def fake_container_migrate():
+ return {
+ "type": "sync",
+ "status": "Success",
+ "status_code": 200,
+ "operation": "/1.0/operations/1234",
+ "metadata": {
+ "control": "fake_control",
+ "fs": "fake_fs",
+ "criu": "fake_criu",
+ }
+ }
+
+
+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_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"]
+ }
+ }
diff --git a/pylxd/deprecated/tests/test_certificate.py b/pylxd/deprecated/tests/test_certificate.py
new file mode 100644
index 0000000..1dab145
--- /dev/null
+++ b/pylxd/deprecated/tests/test_certificate.py
@@ -0,0 +1,57 @@
+# 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 ddt import ddt
+import json
+import mock
+
+from pylxd.deprecated import connection
+
+from pylxd.deprecated.tests import annotated_data
+from pylxd.deprecated.tests import fake_api
+from pylxd.deprecated.tests import LXDAPITestBase
+
+
+ at ddt
+class LXDAPICertificateTest(LXDAPITestBase):
+
+ def test_list_certificates(self):
+ with mock.patch.object(connection.LXDConnection, 'get_object') as ms:
+ ms.return_value = ('200', fake_api.fake_certificate_list())
+ self.assertEqual(
+ ['ABCDEF01'],
+ self.lxd.certificate_list())
+ ms.assert_called_with('GET',
+ '/1.0/certificates')
+
+ def test_certificate_show(self):
+ with mock.patch.object(connection.LXDConnection, 'get_object') as ms:
+ ms.return_value = ('200', fake_api.fake_certificate())
+ self.assertEqual(
+ ms.return_value, self.lxd.certificate_show('ABCDEF01'))
+ ms.assert_called_with('GET',
+ '/1.0/certificates/ABCDEF01')
+
+ @annotated_data(
+ ('delete', 'DELETE', '/ABCDEF01'),
+ ('create', 'POST', '', (json.dumps('ABCDEF01'),)),
+ )
+ def test_certificate_operations(self, method, http, path, call_args=()):
+ with mock.patch.object(connection.LXDConnection, 'get_status') as ms:
+ ms.return_value = True
+ self.assertTrue(
+ getattr(self.lxd, 'certificate_' + method)('ABCDEF01'))
+ ms.assert_called_with(http,
+ '/1.0/certificates' + path,
+ *call_args)
diff --git a/pylxd/deprecated/tests/test_client.py b/pylxd/deprecated/tests/test_client.py
new file mode 100644
index 0000000..821c59d
--- /dev/null
+++ b/pylxd/deprecated/tests/test_client.py
@@ -0,0 +1,90 @@
+# Copyright (c) 2016 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.
+"""Tests for pylxd.client."""
+import unittest
+
+import mock
+
+from pylxd import client
+
+
+class Test_APINode(unittest.TestCase):
+ """Tests for pylxd.client._APINode."""
+ ROOT = 'http://lxd/api'
+
+ def test_getattr(self):
+ """`__getattr__` returns a nested node."""
+ lxd = client._APINode(self.ROOT)
+
+ self.assertEqual('{}/fake'.format(self.ROOT), lxd.fake._api_endpoint)
+
+ def test_getattr_nested(self):
+ """Nested objects return a more detailed path."""
+ lxd = client._APINode(self.ROOT) # NOQA
+
+ self.assertEqual(
+ '{}/fake/path'.format(self.ROOT),
+ lxd.fake.path._api_endpoint)
+
+ def test_getitem(self):
+ """`__getitem__` enables dynamic url parts."""
+ lxd = client._APINode(self.ROOT) # NOQA
+
+ self.assertEqual(
+ '{}/fake/path'.format(self.ROOT),
+ lxd.fake['path']._api_endpoint)
+
+ def test_getitem_integer(self):
+ """`__getitem__` with an integer allows dynamic integer url parts."""
+ lxd = client._APINode(self.ROOT) # NOQA
+
+ self.assertEqual(
+ '{}/fake/0'.format(self.ROOT),
+ lxd.fake[0]._api_endpoint)
+
+ @mock.patch('pylxd.client.requests.get')
+ def test_get(self, _get):
+ """`get` will make a request to the smart url."""
+ lxd = client._APINode(self.ROOT)
+
+ lxd.fake.get()
+
+ _get.assert_called_once_with('{}/{}'.format(self.ROOT, 'fake'))
+
+ @mock.patch('pylxd.client.requests.post')
+ def test_post(self, _post):
+ """`post` will POST to the smart url."""
+ lxd = client._APINode(self.ROOT)
+
+ lxd.fake.post()
+
+ _post.assert_called_once_with('{}/{}'.format(self.ROOT, 'fake'))
+
+ @mock.patch('pylxd.client.requests.put')
+ def test_put(self, _put):
+ """`put` will PUT to the smart url."""
+ lxd = client._APINode(self.ROOT)
+
+ lxd.fake.put()
+
+ _put.assert_called_once_with('{}/{}'.format(self.ROOT, 'fake'))
+
+ @mock.patch('pylxd.client.requests.delete')
+ def test_delete(self, _delete):
+ """`delete` will DELETE to the smart url."""
+ lxd = client._APINode(self.ROOT)
+
+ lxd.fake.delete()
+
+ _delete.assert_called_once_with('{}/{}'.format(self.ROOT, 'fake'))
diff --git a/pylxd/deprecated/tests/test_connection.py b/pylxd/deprecated/tests/test_connection.py
new file mode 100644
index 0000000..33c4ce9
--- /dev/null
+++ b/pylxd/deprecated/tests/test_connection.py
@@ -0,0 +1,172 @@
+# 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 ddt import ddt
+import inspect
+import mock
+import six
+from six.moves import cStringIO
+from six.moves import http_client
+import socket
+import unittest
+
+from pylxd.deprecated import connection
+from pylxd.deprecated import exceptions
+from pylxd.deprecated.tests import annotated_data
+
+if six.PY34:
+ from io import BytesIO
+
+
+ at ddt
+class LXDInitConnectionTest(unittest.TestCase):
+
+ @mock.patch('socket.socket')
+ @mock.patch.object(http_client.HTTPConnection, '__init__')
+ def test_http_connection(self, mc, ms):
+ conn = connection.UnixHTTPConnection('/', 'host', 1234)
+ if six.PY34:
+ mc.assert_called_once_with(
+ conn, 'host', port=1234, timeout=None)
+ else:
+ mc.assert_called_once_with(
+ conn, 'host', port=1234, strict=None, timeout=None)
+ conn.connect()
+ ms.assert_called_once_with(socket.AF_UNIX, socket.SOCK_STREAM)
+ ms.return_value.connect.assert_called_once_with('/')
+
+ @mock.patch('os.environ', {'HOME': '/home/foo'})
+ @mock.patch('ssl.wrap_socket')
+ @mock.patch('socket.create_connection')
+ def test_https_connection(self, ms, ml):
+ conn = connection.HTTPSConnection('host', 1234)
+ with mock.patch.object(conn, '_tunnel') as mc:
+ conn.connect()
+ self.assertFalse(mc.called)
+ ms.assert_called_once_with(
+ ('host', 1234), socket._GLOBAL_DEFAULT_TIMEOUT, None)
+ ml.assert_called_once_with(
+ ms.return_value,
+ certfile='/home/foo/.config/lxc/client.crt',
+ keyfile='/home/foo/.config/lxc/client.key',
+ ssl_version=connection.DEFAULT_TLS_VERSION,
+ )
+
+ @mock.patch('os.environ', {'HOME': '/home/foo'})
+ @mock.patch('ssl.wrap_socket')
+ @mock.patch('socket.create_connection')
+ def test_https_proxy_connection(self, ms, ml):
+ conn = connection.HTTPSConnection('host', 1234)
+ conn._tunnel_host = 'host'
+ with mock.patch.object(conn, '_tunnel') as mc:
+ conn.connect()
+ self.assertTrue(mc.called)
+ ms.assert_called_once_with(
+ ('host', 1234), socket._GLOBAL_DEFAULT_TIMEOUT, None)
+ ml.assert_called_once_with(
+ ms.return_value,
+ certfile='/home/foo/.config/lxc/client.crt',
+ keyfile='/home/foo/.config/lxc/client.key',
+ ssl_version=connection.DEFAULT_TLS_VERSION)
+
+ @mock.patch('pylxd.deprecated.connection.HTTPSConnection')
+ @mock.patch('pylxd.deprecated.connection.UnixHTTPConnection')
+ @annotated_data(
+ ('unix', (None,), {}, '/var/lib/lxd/unix.socket'),
+ ('unix_path', (None,),
+ {'LXD_DIR': '/fake/'}, '/fake/unix.socket'),
+ ('https', ('host',), {}, ''),
+ ('https_port', ('host', 1234), {}, ''),
+ )
+ def test_get_connection(self, mode, args, env, path, mc, ms):
+ with mock.patch('os.environ', env):
+ conn = connection.LXDConnection(*args).get_connection()
+ if mode.startswith('unix'):
+ self.assertEqual(mc.return_value, conn)
+ mc.assert_called_once_with(path)
+ elif mode.startswith('https'):
+ self.assertEqual(ms.return_value, conn)
+ ms.assert_called_once_with(
+ args[0], len(args) == 2 and args[1] or 8443)
+
+
+class FakeResponse(object):
+
+ def __init__(self, status, data):
+ self.status = status
+ if six.PY34:
+ self.read = BytesIO(six.b(data)).read
+ else:
+ self.read = cStringIO(data).read
+
+
+ at ddt
+ at mock.patch('pylxd.deprecated.connection.LXDConnection.get_connection')
+class LXDConnectionTest(unittest.TestCase):
+
+ def setUp(self):
+ super(LXDConnectionTest, self).setUp()
+ self.conn = connection.LXDConnection()
+
+ @annotated_data(
+ ('null', (200, '{}'), exceptions.PyLXDException),
+ ('200', (200, '{"foo": "bar"}'), (200, {'foo': 'bar'})),
+ ('202', (202, '{"status_code": 100}'), (202, {'status_code': 100})),
+ ('500', (500, '{"foo": "bar"}'), exceptions.APIError),
+ )
+ def test_get_object(self, tag, effect, result, mg):
+ mg.return_value.getresponse.return_value = FakeResponse(*effect)
+ if inspect.isclass(result):
+ self.assertRaises(result, self.conn.get_object)
+ else:
+ self.assertEqual(result, self.conn.get_object())
+
+ @annotated_data(
+ ('null', (200, '{}'), exceptions.PyLXDException),
+ ('200', (200, '{"foo": "bar"}'), True),
+ ('202', (202, '{"status_code": 100}'), True),
+ ('200', (200, '{"error": "bar"}'), exceptions.APIError),
+ ('500', (500, '{"foo": "bar"}'), False),
+ )
+ def test_get_status(self, tag, effect, result, mg):
+ mg.return_value.getresponse.return_value = FakeResponse(*effect)
+ if inspect.isclass(result):
+ self.assertRaises(result, self.conn.get_status)
+ else:
+ self.assertEqual(result, self.conn.get_status())
+
+ @annotated_data(
+ ('null', (200, ''), exceptions.PyLXDException),
+ ('200', (200, '{"foo": "bar"}'), six.b('{"foo": "bar"}')),
+ ('500', (500, '{"foo": "bar"}'),
+ exceptions.PyLXDException),
+ )
+ def test_get_raw(self, tag, effect, result, mg):
+ mg.return_value.getresponse.return_value = FakeResponse(*effect)
+ if inspect.isclass(result):
+ self.assertRaises(result, self.conn.get_raw)
+ else:
+ self.assertEqual(result, self.conn.get_raw())
+
+ @mock.patch('pylxd.deprecated.connection.WebSocketClient')
+ @annotated_data(
+ ('fake_host', 'wss://fake_host:8443'),
+ (None, 'ws+unix:///var/lib/lxd/unix.socket')
+ )
+ def test_get_ws(self, host, result, mock_ws, _):
+ conn = connection.LXDConnection(host)
+
+ conn.get_ws('/fake/path')
+
+ mock_ws.assert_has_calls([mock.call(result), mock.call().connect()])
diff --git a/pylxd/deprecated/tests/test_container.py b/pylxd/deprecated/tests/test_container.py
new file mode 100644
index 0000000..5c57f36
--- /dev/null
+++ b/pylxd/deprecated/tests/test_container.py
@@ -0,0 +1,222 @@
+# 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 collections import OrderedDict
+from ddt import ddt
+import json
+import mock
+import tempfile
+
+from pylxd.deprecated import connection
+
+from pylxd.deprecated.tests import annotated_data
+from pylxd.deprecated.tests import fake_api
+from pylxd.deprecated.tests import LXDAPITestBase
+
+
+ at ddt
+ at mock.patch.object(connection.LXDConnection, 'get_object',
+ return_value=('200', fake_api.fake_operation()))
+class LXDAPIContainerTestObject(LXDAPITestBase):
+
+ def test_list_containers(self, ms):
+ ms.return_value = ('200', fake_api.fake_container_list())
+ self.assertEqual(
+ ['trusty-1'],
+ self.lxd.container_list())
+ ms.assert_called_once_with('GET',
+ '/1.0/containers')
+
+ @annotated_data(
+ ('STOPPED', False),
+ ('STOPPING', False),
+ ('ABORTING', False),
+ ('RUNNING', True),
+ ('STARTING', True),
+ ('FREEZING', True),
+ ('FROZEN', True),
+ ('THAWED', True),
+ )
+ def test_container_running(self, status, running, ms):
+ with mock.patch.object(connection.LXDConnection, 'get_object') as ms:
+ ms.return_value = ('200', fake_api.fake_container_state(status))
+ self.assertEqual(running, self.lxd.container_running('trusty-1'))
+ ms.assert_called_once_with('GET',
+ '/1.0/containers/trusty-1/state')
+
+ def test_container_init(self, ms):
+ self.assertEqual(ms.return_value, self.lxd.container_init('fake'))
+ ms.assert_called_once_with('POST',
+ '/1.0/containers',
+ '"fake"')
+
+ def test_container_update(self, ms):
+ self.assertEqual(ms.return_value,
+ self.lxd.container_update('trusty-1',
+ 'fake'))
+ ms.assert_called_once_with('PUT',
+ '/1.0/containers/trusty-1',
+ '"fake"')
+
+ def test_container_state(self, ms):
+ ms.return_value = ('200', fake_api.fake_container_state('RUNNING'))
+ self.assertEqual(ms.return_value, self.lxd.container_state('trusty-1'))
+ ms.assert_called_with('GET',
+ '/1.0/containers/trusty-1/state')
+
+ @annotated_data(
+ ('start', 'start'),
+ ('stop', 'stop'),
+ ('suspend', 'freeze'),
+ ('resume', 'unfreeze'),
+ ('reboot', 'restart'),
+ )
+ def test_container_actions(self, method, action, ms):
+ self.assertEqual(
+ ms.return_value,
+ getattr(self.lxd, 'container_' + method)('trusty-1', 30))
+ ms.assert_called_once_with('PUT',
+ '/1.0/containers/trusty-1/state',
+ json.dumps({'action': action,
+ 'force': True,
+ 'timeout': 30, }))
+
+ def test_container_destroy(self, ms):
+ self.assertEqual(
+ ms.return_value, self.lxd.container_destroy('trusty-1'))
+ ms.assert_called_once_with('DELETE',
+ '/1.0/containers/trusty-1')
+
+ def test_container_log(self, ms):
+ ms.return_value = ('200', fake_api.fake_container_log())
+ self.assertEqual(
+ 'fake log', self.lxd.get_container_log('trusty-1'))
+ ms.assert_called_once_with('GET',
+ '/1.0/containers/trusty-1?log=true')
+
+ def test_container_config(self, ms):
+ ms.return_value = ('200', fake_api.fake_container_state('fake'))
+ self.assertEqual(
+ {'status': 'fake'}, self.lxd.get_container_config('trusty-1'))
+ ms.assert_called_once_with('GET',
+ '/1.0/containers/trusty-1?log=false')
+
+ def test_container_info(self, ms):
+ ms.return_value = ('200', fake_api.fake_container_state('fake'))
+ self.assertEqual(
+ {'status': 'fake'}, self.lxd.container_info('trusty-1'))
+ ms.assert_called_once_with('GET',
+ '/1.0/containers/trusty-1/state')
+
+ def test_container_migrate(self, ms):
+ ms.return_value = ('200', fake_api.fake_container_migrate())
+ self.assertEqual(
+ {'control': 'fake_control',
+ 'criu': 'fake_criu',
+ 'fs': 'fake_fs',
+ 'operation': '1234'},
+ self.lxd.container_migrate('trusty-1'))
+ ms.assert_called_once_with('POST',
+ '/1.0/containers/trusty-1',
+ '{"migration": true}')
+
+ def test_container_publish(self, ms):
+ ms.return_value = ('200', fake_api.fake_operation())
+ self.assertEqual(
+ ms.return_value, self.lxd.container_publish('trusty-1'))
+ ms.assert_called_once_with('POST',
+ '/1.0/images',
+ '"trusty-1"')
+
+ def test_container_put_file(self, ms):
+ temp_file = tempfile.NamedTemporaryFile()
+ ms.return_value = ('200', fake_api.fake_standard_return())
+ self.assertEqual(
+ ms.return_value, self.lxd.put_container_file('trusty-1',
+ temp_file.name,
+ 'dst_file'))
+ ms.assert_called_once_with(
+ 'POST',
+ '/1.0/containers/trusty-1/files?path=dst_file',
+ body=b'',
+ headers={'X-LXD-gid': 0, 'X-LXD-mode': 0o644, 'X-LXD-uid': 0})
+
+ def test_list_snapshots(self, ms):
+ ms.return_value = ('200', fake_api.fake_snapshots_list())
+ self.assertEqual(
+ ['/1.0/containers/trusty-1/snapshots/first'],
+ self.lxd.container_snapshot_list('trusty-1'))
+ ms.assert_called_once_with('GET',
+ '/1.0/containers/trusty-1/snapshots')
+
+ @annotated_data(
+ ('create', 'POST', '', ('fake config',), ('"fake config"',)),
+ ('info', 'GET', '/first', ('first',), ()),
+ ('rename', 'POST', '/first',
+ ('first', 'fake config'), ('"fake config"',)),
+ ('delete', 'DELETE', '/first', ('first',), ()),
+ )
+ def test_snapshot_operations(self, method, http, path,
+ args, call_args, ms):
+ self.assertEqual(
+ ms.return_value,
+ getattr(self.lxd,
+ 'container_snapshot_' + method)('trusty-1', *args))
+ ms.assert_called_once_with(http,
+ '/1.0/containers/trusty-1/snapshots' +
+ path,
+ *call_args)
+
+ def test_container_run_command(self, ms):
+ data = OrderedDict((
+ ('command', ['/fake/command']),
+ ('interactive', False),
+ ('wait-for-websocket', False),
+ ('environment', {'FAKE_ENV': 'fake'})
+ ))
+
+ self.assertEqual(
+ ms.return_value,
+ self.lxd.container_run_command('trusty-1', *data.values()))
+ self.assertEqual(1, ms.call_count)
+ self.assertEqual(
+ ms.call_args[0][:2],
+ ('POST', '/1.0/containers/trusty-1/exec'))
+ self.assertEqual(
+ json.loads(ms.call_args[0][2]),
+ dict(data)
+ )
+
+
+ at ddt
+ at mock.patch.object(connection.LXDConnection, 'get_object',
+ return_value=('200', fake_api.fake_container_list()))
+class LXDAPIContainerTestStatus(LXDAPITestBase):
+
+ def test_container_defined(self, ms):
+ self.assertTrue(self.lxd.container_defined('trusty-1'))
+ ms.assert_called_once_with('GET', '/1.0/containers')
+
+
+ at ddt
+ at mock.patch.object(connection.LXDConnection, 'get_raw',
+ return_value='fake contents')
+class LXDAPIContainerTestRaw(LXDAPITestBase):
+
+ def test_container_file(self, ms):
+ self.assertEqual(
+ 'fake contents', self.lxd.get_container_file('trusty-1',
+ '/file/name'))
+ ms.assert_called_once_with(
+ 'GET', '/1.0/containers/trusty-1/files?path=/file/name')
diff --git a/pylxd/deprecated/tests/test_host.py b/pylxd/deprecated/tests/test_host.py
new file mode 100644
index 0000000..4fc8fd1
--- /dev/null
+++ b/pylxd/deprecated/tests/test_host.py
@@ -0,0 +1,82 @@
+# 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 ddt import data
+from ddt import ddt
+import mock
+
+from pylxd.deprecated import connection
+from pylxd.deprecated import exceptions
+
+from pylxd.deprecated.tests import annotated_data
+from pylxd.deprecated.tests import fake_api
+from pylxd.deprecated.tests import LXDAPITestBase
+
+
+ at ddt
+ at mock.patch.object(connection.LXDConnection, 'get_object',
+ return_value=('200', fake_api.fake_host()))
+class LXDAPIHostTestObject(LXDAPITestBase):
+
+ def test_get_host_info(self, ms):
+ result = self.lxd.host_info()
+ self.assertEqual(result, {
+ 'lxd_api_compat_level': 1,
+ 'lxd_trusted_host': True,
+ 'lxd_backing_fs': 'ext4',
+ 'lxd_driver': 'lxc',
+ 'lxd_version': 0.12,
+ 'lxc_version': '1.1.2',
+ 'kernel_version': '3.19.0-22-generic',
+ })
+ ms.assert_called_once_with('GET', '/1.0')
+
+ host_data = (
+ ('lxd_api_compat', 1),
+ ('lxd_host_trust', True),
+ ('lxd_backing_fs', 'ext4'),
+ ('lxd_driver', 'lxc'),
+ ('lxc_version', '1.1.2'),
+ ('lxd_version', 0.12),
+ ('kernel_version', '3.19.0-22-generic'),
+ )
+
+ @annotated_data(*host_data)
+ def test_get_host_data(self, method, expected, ms):
+ result = getattr(self.lxd, 'get_' + method)(data=None)
+ self.assertEqual(expected, result)
+ ms.assert_called_once_with('GET', '/1.0')
+
+ @annotated_data(*host_data)
+ def test_get_host_data_fail(self, method, expected, ms):
+ ms.side_effect = exceptions.PyLXDException
+ result = getattr(self.lxd, 'get_' + method)(data=None)
+ self.assertEqual(None, result)
+ ms.assert_called_once_with('GET', '/1.0')
+
+
+ at ddt
+ at mock.patch.object(connection.LXDConnection, 'get_status')
+class LXDAPIHostTestStatus(LXDAPITestBase):
+
+ @data(True, False)
+ def test_get_host_ping(self, value, ms):
+ ms.return_value = value
+ self.assertEqual(value, self.lxd.host_ping())
+ ms.assert_called_once_with('GET', '/1.0')
+
+ def test_get_host_ping_fail(self, ms):
+ ms.side_effect = Exception
+ self.assertRaises(exceptions.PyLXDException, self.lxd.host_ping)
+ ms.assert_called_once_with('GET', '/1.0')
diff --git a/pylxd/deprecated/tests/test_image.py b/pylxd/deprecated/tests/test_image.py
new file mode 100644
index 0000000..56bdc84
--- /dev/null
+++ b/pylxd/deprecated/tests/test_image.py
@@ -0,0 +1,241 @@
+# 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 datetime
+from ddt import ddt
+import mock
+from six.moves import builtins
+from six.moves import cStringIO
+import unittest
+
+from pylxd.deprecated import connection
+from pylxd.deprecated import exceptions
+from pylxd.deprecated import image
+
+from pylxd.deprecated.tests import annotated_data
+from pylxd.deprecated.tests import fake_api
+from pylxd.deprecated.tests import LXDAPITestBase
+
+
+ at ddt
+ at mock.patch.object(connection.LXDConnection, 'get_object',
+ return_value=('200', fake_api.fake_image_info()))
+class LXDAPIImageTestObject(LXDAPITestBase):
+
+ list_data = (
+ ('list', (), ()),
+ ('search', ({'foo': 'bar'},), ('foo=bar',)),
+ )
+
+ @annotated_data(*list_data)
+ def test_list_images(self, method, args, call_args, ms):
+ ms.return_value = ('200', fake_api.fake_image_list())
+ self.assertEqual(
+ ['trusty'], getattr(self.lxd, 'image_' + method)(*args))
+ ms.assert_called_once_with('GET', '/1.0/images', *call_args)
+
+ @annotated_data(*list_data)
+ def test_list_images_fail(self, method, args, call_args, ms):
+ ms.side_effect = exceptions.PyLXDException
+ self.assertRaises(exceptions.PyLXDException,
+ getattr(self.lxd, 'image_' + method),
+ *args)
+ ms.assert_called_once_with('GET', '/1.0/images', *call_args)
+
+ @annotated_data(
+ (True, (('200', fake_api.fake_image_info()),)),
+ (False, exceptions.APIError("404", 404)),
+ )
+ def test_image_defined(self, expected, side_effect, ms):
+ ms.side_effect = side_effect
+ self.assertEqual(expected, self.lxd.image_defined('test-image'))
+ ms.assert_called_once_with('GET', '/1.0/images/test-image')
+
+ @annotated_data(
+ ('APIError', exceptions.APIError("500", 500), exceptions.APIError),
+ ('PyLXDException', exceptions.PyLXDException,
+ exceptions.PyLXDException)
+ )
+ def test_image_defined_fail(self, tag, side_effect, expected, ms):
+ ms.side_effect = side_effect
+ self.assertRaises(expected,
+ self.lxd.image_defined, ('test-image',))
+ ms.assert_called_once_with('GET', '/1.0/images/test-image')
+
+ def test_image_info(self, ms):
+ self.assertEqual({
+ 'image_upload_date': (datetime.datetime
+ .fromtimestamp(1435669853)
+ .strftime('%Y-%m-%d %H:%M:%S')),
+ 'image_created_date': 'Unknown',
+ 'image_expires_date': 'Unknown',
+ 'image_public': False,
+ 'image_size': '63MB',
+ 'image_fingerprint': '04aac4257341478b49c25d22cea8a6ce'
+ '0489dc6c42d835367945e7596368a37f',
+ 'image_architecture': 'x86_64',
+ }, self.lxd.image_info('test-image'))
+ ms.assert_called_once_with('GET', '/1.0/images/test-image')
+
+ def test_image_info_fail(self, ms):
+ ms.side_effect = exceptions.PyLXDException
+ self.assertRaises(exceptions.PyLXDException,
+ self.lxd.image_info, ('test-image',))
+ ms.assert_called_once_with('GET', '/1.0/images/test-image')
+
+ dates_data = (
+ ('upload', (datetime.datetime.fromtimestamp(1435669853)
+ .strftime('%Y-%m-%d %H:%M:%S'))),
+ ('create', 'Unknown'),
+ ('expire', 'Unknown'),
+ )
+
+ @annotated_data(*dates_data)
+ def test_image_date(self, method, expected, ms):
+ self.assertEqual(expected, getattr(
+ self.lxd, 'image_{}_date'.format(method))('test-image',
+ data=None))
+ ms.assert_called_once_with('GET', '/1.0/images/test-image')
+
+ @annotated_data(*dates_data)
+ def test_image_date_fail(self, method, expected, ms):
+ ms.side_effect = exceptions.PyLXDException
+ self.assertRaises(exceptions.PyLXDException, getattr(
+ self.lxd,
+ 'image_{}_date'.format(method)),
+ 'test-image',
+ data=None)
+ ms.assert_called_once_with('GET', '/1.0/images/test-image')
+
+
+ at ddt
+ at mock.patch.object(connection.LXDConnection, 'get_status', return_value=True)
+class LXDAPIImageTestStatus(LXDAPITestBase):
+ operations_data = (
+ ('delete', 'DELETE', '/test-image', ('test-image',), ()),
+ ('update', 'PUT', '/test-image', ('test-image', 'fake',), ('"fake"',)),
+ ('rename', 'POST', '/test-image',
+ ('test-image', 'fake',), ('"fake"',)),
+ )
+
+ @annotated_data(*operations_data)
+ def test_image_operations(self, method, http, path, args, call_args, ms):
+ self.assertTrue(
+ getattr(self.lxd, 'image_' + method)(*args))
+ ms.assert_called_once_with(
+ http,
+ '/1.0/images' + path,
+ *call_args
+ )
+
+ @annotated_data(*operations_data)
+ def test_image_operations_fail(self, method, http, path,
+ args, call_args, ms):
+ ms.side_effect = exceptions.PyLXDException
+ self.assertRaises(exceptions.PyLXDException,
+ getattr(self.lxd, 'image_' + method),
+ *args)
+ ms.assert_called_once_with(
+ http,
+ '/1.0/images' + path,
+ *call_args
+ )
+
+
+ at mock.patch.object(connection.LXDConnection, 'get_object',
+ return_value=('200', fake_api.fake_image_info()))
+class LXDAPAPIImageTestUpload(LXDAPITestBase):
+ @mock.patch.object(builtins, 'open', return_value=cStringIO('fake'))
+ def test_image_upload_file(self, mo, ms):
+ self.assertTrue(self.lxd.image_upload(path='/fake/path'))
+ mo.assert_called_once_with('/fake/path', 'rb')
+ ms.assert_called_once_with('POST', '/1.0/images', 'fake', {})
+
+
+ at mock.patch.object(connection.LXDConnection, 'get_raw')
+class LXDAPIImageTestRaw(LXDAPITestBase):
+
+ def test_image_export(self, ms):
+ ms.return_value = 'fake contents'
+ self.assertEqual('fake contents', self.lxd.image_export('fake'))
+ ms.assert_called_once_with('GET', '/1.0/images/fake/export')
+
+ def test_image_export_fail(self, ms):
+ ms.side_effect = exceptions.PyLXDException
+ self.assertRaises(exceptions.PyLXDException,
+ self.lxd.image_export, 'fake')
+ ms.assert_called_once_with('GET', '/1.0/images/fake/export')
+
+
+ at ddt
+ at mock.patch.object(connection.LXDConnection, 'get_object',
+ return_value=(200, fake_api.fake_image_info()))
+class LXDAPIImageInfoTest(unittest.TestCase):
+
+ def setUp(self):
+ super(LXDAPIImageInfoTest, self).setUp()
+ self.image = image.LXDImage()
+
+ info_list = (
+ ('permission', False),
+ ('size', 63),
+ ('fingerprint', '04aac4257341478b49c25d22cea8a6ce'
+ '0489dc6c42d835367945e7596368a37f'),
+ ('architecture', 'x86_64'),
+ )
+
+ @annotated_data(*info_list)
+ def test_info_no_data(self, method, expected, mc):
+ self.assertEqual(expected,
+ (getattr(self.image, 'get_image_' + method)
+ ('test-image', data=None)))
+ mc.assert_called_once_with('GET', '/1.0/images/test-image')
+
+ @annotated_data(*info_list)
+ def test_info_no_data_fail(self, method, expected, mc):
+ mc.side_effect = exceptions.PyLXDException
+ self.assertRaises(exceptions.PyLXDException,
+ getattr(self.image, 'get_image_' + method),
+ 'test-image',
+ data=None)
+
+ @annotated_data(
+ ('permission_true', 'permission', {'public': 0}, False),
+ ('permission_false', 'permission', {'public': 1}, True),
+ ('size', 'size', {'size': 52428800}, 50),
+ ('fingerprint', 'fingerprint', {'fingerprint': 'AAAA'}, 'AAAA'),
+ *[('architecture_' + v, 'architecture', {'architecture': k}, v)
+ for k, v in image.image_architecture.items()]
+ )
+ def test_info_data(self, tag, method, metadata, expected, mc):
+ self.assertEqual(
+ expected, getattr(self.image, 'get_image_' + method)
+ ('test-image', data=metadata))
+ self.assertFalse(mc.called)
+
+ @annotated_data(
+ ('permission', 'permission', {}, KeyError),
+ ('size', 'size', {'size': 0}, exceptions.ImageInvalidSize),
+ ('size', 'size', {'size': -1}, exceptions.ImageInvalidSize),
+ ('fingerprint', 'fingerprint', {}, KeyError),
+ ('architecture', 'architecture', {}, KeyError),
+ ('architecture_invalid', 'architecture',
+ {'architecture': -1}, KeyError)
+ )
+ def test_info_data_fail(self, tag, method, metadata, expected, mc):
+ self.assertRaises(expected,
+ getattr(self.image, 'get_image_' + method),
+ 'test-image',
+ data=metadata)
+ self.assertFalse(mc.called)
diff --git a/pylxd/deprecated/tests/test_image_alias.py b/pylxd/deprecated/tests/test_image_alias.py
new file mode 100644
index 0000000..74caec6
--- /dev/null
+++ b/pylxd/deprecated/tests/test_image_alias.py
@@ -0,0 +1,66 @@
+# 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 ddt import data
+from ddt import ddt
+import mock
+
+from pylxd.deprecated import connection
+
+from pylxd.deprecated.tests import annotated_data
+from pylxd.deprecated.tests import fake_api
+from pylxd.deprecated.tests import LXDAPITestBase
+
+
+ at ddt
+ at mock.patch.object(connection.LXDConnection, 'get_object')
+class LXDAPIImageAliasTestObject(LXDAPITestBase):
+
+ def test_alias_list(self, ms):
+ ms.return_value = ('200', fake_api.fake_alias_list())
+ self.assertEqual(['ubuntu'], self.lxd.alias_list())
+ ms.assert_called_once_with('GET', '/1.0/images/aliases')
+
+ def test_alias_show(self, ms):
+ ms.return_value = ('200', fake_api.fake_alias())
+ self.assertEqual(
+ fake_api.fake_alias(), self.lxd.alias_show('fake')[1])
+ ms.assert_called_once_with('GET', '/1.0/images/aliases/fake')
+
+
+ at ddt
+ at mock.patch.object(connection.LXDConnection, 'get_status')
+class LXDAPIImageAliasTestStatus(LXDAPITestBase):
+
+ @data(True, False)
+ def test_alias_defined(self, expected, ms):
+ ms.return_value = expected
+ self.assertEqual(expected, self.lxd.alias_defined('fake'))
+ ms.assert_called_once_with('GET', '/1.0/images/aliases/fake')
+
+ @annotated_data(
+ ('create', 'POST', '', ('fake',), ('"fake"',)),
+ ('update', 'PUT', '/test-alias',
+ ('test-alias', 'fake',), ('"fake"',)),
+ ('rename', 'POST', '/test-alias',
+ ('test-alias', 'fake',), ('"fake"',)),
+ ('delete', 'DELETE', '/test-alias', ('test-alias',), ()),
+ )
+ def test_alias_operations(self, method, http, path, args, call_args, ms):
+ self.assertTrue(getattr(self.lxd, 'alias_' + method)(*args))
+ ms.assert_called_once_with(
+ http,
+ '/1.0/images/aliases' + path,
+ *call_args
+ )
diff --git a/pylxd/deprecated/tests/test_network.py b/pylxd/deprecated/tests/test_network.py
new file mode 100644
index 0000000..e455923
--- /dev/null
+++ b/pylxd/deprecated/tests/test_network.py
@@ -0,0 +1,57 @@
+# 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 ddt import ddt
+import mock
+
+from pylxd.deprecated import connection
+
+from pylxd.deprecated.tests import annotated_data
+from pylxd.deprecated.tests import fake_api
+from pylxd.deprecated.tests import LXDAPITestBase
+
+
+ at ddt
+ at mock.patch.object(connection.LXDConnection, 'get_object',
+ return_value=(200, fake_api.fake_network()))
+class LXDAPINetworkTest(LXDAPITestBase):
+
+ def test_list_networks(self, ms):
+ ms.return_value = ('200', fake_api.fake_network_list())
+ self.assertEqual(
+ ['lxcbr0'],
+ self.lxd.network_list())
+ ms.assert_called_with('GET',
+ '/1.0/networks')
+
+ def test_network_show(self, ms):
+ self.assertEqual({
+ 'network_name': 'lxcbr0',
+ 'network_type': 'bridge',
+ 'network_members': ['/1.0/containers/trusty-1'],
+ }, self.lxd.network_show('lxcbr0'))
+ ms.assert_called_with('GET',
+ '/1.0/networks/lxcbr0')
+
+ @annotated_data(
+ ('name', 'lxcbr0'),
+ ('type', 'bridge'),
+ ('members', ['/1.0/containers/trusty-1']),
+ )
+ def test_network_data(self, method, expected, ms):
+ self.assertEqual(
+ expected, getattr(self.lxd,
+ 'network_show_' + method)('lxcbr0'))
+ ms.assert_called_with('GET',
+ '/1.0/networks/lxcbr0')
diff --git a/pylxd/deprecated/tests/test_operation.py b/pylxd/deprecated/tests/test_operation.py
new file mode 100644
index 0000000..fb275e5
--- /dev/null
+++ b/pylxd/deprecated/tests/test_operation.py
@@ -0,0 +1,75 @@
+# 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 datetime
+from ddt import ddt
+import mock
+
+from pylxd.deprecated import connection
+
+from pylxd.deprecated.tests import annotated_data
+from pylxd.deprecated.tests import fake_api
+from pylxd.deprecated.tests import LXDAPITestBase
+
+
+ at ddt
+ at mock.patch.object(connection.LXDConnection, 'get_object',
+ return_value=(200, fake_api.fake_operation()))
+class LXDAPIOperationTestObject(LXDAPITestBase):
+
+ def test_list_operations(self, ms):
+ ms.return_value = ('200', fake_api.fake_operation_list())
+ self.assertEqual(
+ ['/1.0/operations/1234'],
+ self.lxd.list_operations())
+ ms.assert_called_with('GET',
+ '/1.0/operations')
+
+ def test_operation_info(self, ms):
+ ms.return_value = ('200', fake_api.fake_operation())
+ self.assertEqual(
+ ms.return_value, self.lxd.operation_info('/1.0/operations/1234'))
+ ms.assert_called_with('GET',
+ '/1.0/operations/1234')
+
+ @annotated_data(
+ ('create_time',
+ datetime.datetime.utcfromtimestamp(1433876844)
+ .strftime('%Y-%m-%d %H:%M:%S')),
+ ('update_time',
+ datetime.datetime.utcfromtimestamp(1433876843)
+ .strftime('%Y-%m-%d %H:%M:%S')),
+ ('status', 'Running'),
+ )
+ def test_operation_show(self, method, expected, ms):
+ call = getattr(self.lxd, 'operation_show_' + method)
+ self.assertEqual(expected, call('/1.0/operations/1234'))
+ ms.assert_called_with('GET',
+ '/1.0/operations/1234')
+
+
+ at ddt
+ at mock.patch.object(connection.LXDConnection, 'get_status', return_value=True)
+class LXDAPIOperationTestStatus(LXDAPITestBase):
+
+ @annotated_data(
+ ('operation_delete', 'DELETE', '', ()),
+ ('wait_container_operation', 'GET',
+ '/wait?status_code=200&timeout=30', ('200', '30')),
+ )
+ def test_operation_actions(self, method, http, path, args, ms):
+ self.assertTrue(
+ getattr(self.lxd, method)('/1.0/operations/1234', *args))
+ ms.assert_called_with(http,
+ '/1.0/operations/1234' + path)
diff --git a/pylxd/deprecated/tests/test_profiles.py b/pylxd/deprecated/tests/test_profiles.py
new file mode 100644
index 0000000..6e5e8c8
--- /dev/null
+++ b/pylxd/deprecated/tests/test_profiles.py
@@ -0,0 +1,69 @@
+# 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 ddt import data
+from ddt import ddt
+import mock
+
+from pylxd.deprecated import connection
+
+from pylxd.deprecated.tests import annotated_data
+from pylxd.deprecated.tests import fake_api
+from pylxd.deprecated.tests import LXDAPITestBase
+
+
+ at mock.patch.object(connection.LXDConnection, 'get_object',
+ return_value=(200, fake_api.fake_profile()))
+class LXDAPIProfilesTestObject(LXDAPITestBase):
+
+ def test_list_profiles(self, ms):
+ ms.return_value = ('200', fake_api.fake_profile_list())
+ self.assertEqual(
+ ['fake-profile'],
+ self.lxd.profile_list())
+ ms.assert_called_with('GET',
+ '/1.0/profiles')
+
+ def test_profile_show(self, ms):
+ self.assertEqual(
+ ms.return_value, self.lxd.profile_show('fake-profile'))
+ ms.assert_called_with('GET',
+ '/1.0/profiles/fake-profile')
+
+
+ at ddt
+ at mock.patch.object(connection.LXDConnection, 'get_status', return_value=True)
+class LXDAPIProfilesTestStatus(LXDAPITestBase):
+
+ @data(True, False)
+ def test_profile_defined(self, defined, ms):
+ ms.return_value = defined
+ self.assertEqual(defined, self.lxd.profile_defined('fake-profile'))
+ ms.assert_called_with('GET',
+ '/1.0/profiles/fake-profile')
+
+ @annotated_data(
+ ('create', 'POST', '', ('fake config',), ('"fake config"',)),
+ ('update', 'PUT', '/fake-profile',
+ ('fake-profile', 'fake config',), ('"fake config"',)),
+ ('rename', 'POST', '/fake-profile',
+ ('fake-profile', 'fake config',), ('"fake config"',)),
+ ('delete', 'DELETE', '/fake-profile', ('fake-profile',), ()),
+ )
+ def test_profile_operations(self, method, http, path, args, call_args, ms):
+ self.assertTrue(
+ getattr(self.lxd, 'profile_' + method)(*args))
+ ms.assert_called_with(http,
+ '/1.0/profiles' + path,
+ *call_args)
diff --git a/pylxd/deprecated/tests/utils.py b/pylxd/deprecated/tests/utils.py
new file mode 100644
index 0000000..ec424bf
--- /dev/null
+++ b/pylxd/deprecated/tests/utils.py
@@ -0,0 +1,42 @@
+# 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 pylxd import api
+from pylxd import exceptions as lxd_exceptions
+
+
+def upload_image(image):
+ alias = '{}/{}/{}/{}'.format(image['os'],
+ image['release'],
+ image['arch'],
+ image['variant'])
+ lxd = api.API()
+ imgs = api.API(host='images.linuxcontainers.org')
+ d = imgs.alias_show(alias)
+
+ meta = d[1]['metadata']
+ tgt = meta['target']
+
+ try:
+ lxd.alias_update(meta)
+ except lxd_exceptions.APIError as ex:
+ if ex.status_code == 404:
+ lxd.alias_create(meta)
+
+ return tgt
+
+
+def delete_image(image):
+ lxd = api.API()
+ lxd.image_delete(image)
diff --git a/pylxd/tests/__init__.py b/pylxd/tests/__init__.py
deleted file mode 100644
index 4780a2b..0000000
--- a/pylxd/tests/__init__.py
+++ /dev/null
@@ -1,40 +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 ddt import data
-from ddt import unpack
-import unittest
-
-from pylxd import api
-
-
-class LXDAPITestBase(unittest.TestCase):
-
- def setUp(self):
- super(LXDAPITestBase, self).setUp()
- self.lxd = api.API()
-
-
-def annotated_data(*args):
- class List(list):
- pass
-
- new_args = []
-
- for arg in args:
- new_arg = List(arg)
- new_arg.__name__ = arg[0]
- new_args.append(new_arg)
-
- return lambda func: data(*new_args)(unpack(func))
diff --git a/pylxd/tests/fake_api.py b/pylxd/tests/fake_api.py
deleted file mode 100644
index 849246e..0000000
--- a/pylxd/tests/fake_api.py
+++ /dev/null
@@ -1,272 +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": status
- }
- }
-
-
-def fake_container_log():
- return {
- "type": "sync",
- "status": "Success",
- "status_code": 200,
- "metadata": {
- "log": "fake log"
- }
- }
-
-
-def fake_container_migrate():
- return {
- "type": "sync",
- "status": "Success",
- "status_code": 200,
- "operation": "/1.0/operations/1234",
- "metadata": {
- "control": "fake_control",
- "fs": "fake_fs",
- "criu": "fake_criu",
- }
- }
-
-
-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_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"]
- }
- }
diff --git a/pylxd/tests/test_certificate.py b/pylxd/tests/test_certificate.py
deleted file mode 100644
index 090944b..0000000
--- a/pylxd/tests/test_certificate.py
+++ /dev/null
@@ -1,57 +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 ddt import ddt
-import json
-import mock
-
-from pylxd.deprecated import connection
-
-from pylxd.tests import annotated_data
-from pylxd.tests import fake_api
-from pylxd.tests import LXDAPITestBase
-
-
- at ddt
-class LXDAPICertificateTest(LXDAPITestBase):
-
- def test_list_certificates(self):
- with mock.patch.object(connection.LXDConnection, 'get_object') as ms:
- ms.return_value = ('200', fake_api.fake_certificate_list())
- self.assertEqual(
- ['ABCDEF01'],
- self.lxd.certificate_list())
- ms.assert_called_with('GET',
- '/1.0/certificates')
-
- def test_certificate_show(self):
- with mock.patch.object(connection.LXDConnection, 'get_object') as ms:
- ms.return_value = ('200', fake_api.fake_certificate())
- self.assertEqual(
- ms.return_value, self.lxd.certificate_show('ABCDEF01'))
- ms.assert_called_with('GET',
- '/1.0/certificates/ABCDEF01')
-
- @annotated_data(
- ('delete', 'DELETE', '/ABCDEF01'),
- ('create', 'POST', '', (json.dumps('ABCDEF01'),)),
- )
- def test_certificate_operations(self, method, http, path, call_args=()):
- with mock.patch.object(connection.LXDConnection, 'get_status') as ms:
- ms.return_value = True
- self.assertTrue(
- getattr(self.lxd, 'certificate_' + method)('ABCDEF01'))
- ms.assert_called_with(http,
- '/1.0/certificates' + path,
- *call_args)
diff --git a/pylxd/tests/test_client.py b/pylxd/tests/test_client.py
deleted file mode 100644
index 821c59d..0000000
--- a/pylxd/tests/test_client.py
+++ /dev/null
@@ -1,90 +0,0 @@
-# Copyright (c) 2016 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.
-"""Tests for pylxd.client."""
-import unittest
-
-import mock
-
-from pylxd import client
-
-
-class Test_APINode(unittest.TestCase):
- """Tests for pylxd.client._APINode."""
- ROOT = 'http://lxd/api'
-
- def test_getattr(self):
- """`__getattr__` returns a nested node."""
- lxd = client._APINode(self.ROOT)
-
- self.assertEqual('{}/fake'.format(self.ROOT), lxd.fake._api_endpoint)
-
- def test_getattr_nested(self):
- """Nested objects return a more detailed path."""
- lxd = client._APINode(self.ROOT) # NOQA
-
- self.assertEqual(
- '{}/fake/path'.format(self.ROOT),
- lxd.fake.path._api_endpoint)
-
- def test_getitem(self):
- """`__getitem__` enables dynamic url parts."""
- lxd = client._APINode(self.ROOT) # NOQA
-
- self.assertEqual(
- '{}/fake/path'.format(self.ROOT),
- lxd.fake['path']._api_endpoint)
-
- def test_getitem_integer(self):
- """`__getitem__` with an integer allows dynamic integer url parts."""
- lxd = client._APINode(self.ROOT) # NOQA
-
- self.assertEqual(
- '{}/fake/0'.format(self.ROOT),
- lxd.fake[0]._api_endpoint)
-
- @mock.patch('pylxd.client.requests.get')
- def test_get(self, _get):
- """`get` will make a request to the smart url."""
- lxd = client._APINode(self.ROOT)
-
- lxd.fake.get()
-
- _get.assert_called_once_with('{}/{}'.format(self.ROOT, 'fake'))
-
- @mock.patch('pylxd.client.requests.post')
- def test_post(self, _post):
- """`post` will POST to the smart url."""
- lxd = client._APINode(self.ROOT)
-
- lxd.fake.post()
-
- _post.assert_called_once_with('{}/{}'.format(self.ROOT, 'fake'))
-
- @mock.patch('pylxd.client.requests.put')
- def test_put(self, _put):
- """`put` will PUT to the smart url."""
- lxd = client._APINode(self.ROOT)
-
- lxd.fake.put()
-
- _put.assert_called_once_with('{}/{}'.format(self.ROOT, 'fake'))
-
- @mock.patch('pylxd.client.requests.delete')
- def test_delete(self, _delete):
- """`delete` will DELETE to the smart url."""
- lxd = client._APINode(self.ROOT)
-
- lxd.fake.delete()
-
- _delete.assert_called_once_with('{}/{}'.format(self.ROOT, 'fake'))
diff --git a/pylxd/tests/test_connection.py b/pylxd/tests/test_connection.py
deleted file mode 100644
index 50e8ffd..0000000
--- a/pylxd/tests/test_connection.py
+++ /dev/null
@@ -1,172 +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 ddt import ddt
-import inspect
-import mock
-import six
-from six.moves import cStringIO
-from six.moves import http_client
-import socket
-import unittest
-
-from pylxd.deprecated import connection
-from pylxd.deprecated import exceptions
-from pylxd.tests import annotated_data
-
-if six.PY34:
- from io import BytesIO
-
-
- at ddt
-class LXDInitConnectionTest(unittest.TestCase):
-
- @mock.patch('socket.socket')
- @mock.patch.object(http_client.HTTPConnection, '__init__')
- def test_http_connection(self, mc, ms):
- conn = connection.UnixHTTPConnection('/', 'host', 1234)
- if six.PY34:
- mc.assert_called_once_with(
- conn, 'host', port=1234, timeout=None)
- else:
- mc.assert_called_once_with(
- conn, 'host', port=1234, strict=None, timeout=None)
- conn.connect()
- ms.assert_called_once_with(socket.AF_UNIX, socket.SOCK_STREAM)
- ms.return_value.connect.assert_called_once_with('/')
-
- @mock.patch('os.environ', {'HOME': '/home/foo'})
- @mock.patch('ssl.wrap_socket')
- @mock.patch('socket.create_connection')
- def test_https_connection(self, ms, ml):
- conn = connection.HTTPSConnection('host', 1234)
- with mock.patch.object(conn, '_tunnel') as mc:
- conn.connect()
- self.assertFalse(mc.called)
- ms.assert_called_once_with(
- ('host', 1234), socket._GLOBAL_DEFAULT_TIMEOUT, None)
- ml.assert_called_once_with(
- ms.return_value,
- certfile='/home/foo/.config/lxc/client.crt',
- keyfile='/home/foo/.config/lxc/client.key',
- ssl_version=connection.DEFAULT_TLS_VERSION,
- )
-
- @mock.patch('os.environ', {'HOME': '/home/foo'})
- @mock.patch('ssl.wrap_socket')
- @mock.patch('socket.create_connection')
- def test_https_proxy_connection(self, ms, ml):
- conn = connection.HTTPSConnection('host', 1234)
- conn._tunnel_host = 'host'
- with mock.patch.object(conn, '_tunnel') as mc:
- conn.connect()
- self.assertTrue(mc.called)
- ms.assert_called_once_with(
- ('host', 1234), socket._GLOBAL_DEFAULT_TIMEOUT, None)
- ml.assert_called_once_with(
- ms.return_value,
- certfile='/home/foo/.config/lxc/client.crt',
- keyfile='/home/foo/.config/lxc/client.key',
- ssl_version=connection.DEFAULT_TLS_VERSION)
-
- @mock.patch('pylxd.deprecated.connection.HTTPSConnection')
- @mock.patch('pylxd.deprecated.connection.UnixHTTPConnection')
- @annotated_data(
- ('unix', (None,), {}, '/var/lib/lxd/unix.socket'),
- ('unix_path', (None,),
- {'LXD_DIR': '/fake/'}, '/fake/unix.socket'),
- ('https', ('host',), {}, ''),
- ('https_port', ('host', 1234), {}, ''),
- )
- def test_get_connection(self, mode, args, env, path, mc, ms):
- with mock.patch('os.environ', env):
- conn = connection.LXDConnection(*args).get_connection()
- if mode.startswith('unix'):
- self.assertEqual(mc.return_value, conn)
- mc.assert_called_once_with(path)
- elif mode.startswith('https'):
- self.assertEqual(ms.return_value, conn)
- ms.assert_called_once_with(
- args[0], len(args) == 2 and args[1] or 8443)
-
-
-class FakeResponse(object):
-
- def __init__(self, status, data):
- self.status = status
- if six.PY34:
- self.read = BytesIO(six.b(data)).read
- else:
- self.read = cStringIO(data).read
-
-
- at ddt
- at mock.patch('pylxd.deprecated.connection.LXDConnection.get_connection')
-class LXDConnectionTest(unittest.TestCase):
-
- def setUp(self):
- super(LXDConnectionTest, self).setUp()
- self.conn = connection.LXDConnection()
-
- @annotated_data(
- ('null', (200, '{}'), exceptions.PyLXDException),
- ('200', (200, '{"foo": "bar"}'), (200, {'foo': 'bar'})),
- ('202', (202, '{"status_code": 100}'), (202, {'status_code': 100})),
- ('500', (500, '{"foo": "bar"}'), exceptions.APIError),
- )
- def test_get_object(self, tag, effect, result, mg):
- mg.return_value.getresponse.return_value = FakeResponse(*effect)
- if inspect.isclass(result):
- self.assertRaises(result, self.conn.get_object)
- else:
- self.assertEqual(result, self.conn.get_object())
-
- @annotated_data(
- ('null', (200, '{}'), exceptions.PyLXDException),
- ('200', (200, '{"foo": "bar"}'), True),
- ('202', (202, '{"status_code": 100}'), True),
- ('200', (200, '{"error": "bar"}'), exceptions.APIError),
- ('500', (500, '{"foo": "bar"}'), False),
- )
- def test_get_status(self, tag, effect, result, mg):
- mg.return_value.getresponse.return_value = FakeResponse(*effect)
- if inspect.isclass(result):
- self.assertRaises(result, self.conn.get_status)
- else:
- self.assertEqual(result, self.conn.get_status())
-
- @annotated_data(
- ('null', (200, ''), exceptions.PyLXDException),
- ('200', (200, '{"foo": "bar"}'), six.b('{"foo": "bar"}')),
- ('500', (500, '{"foo": "bar"}'),
- exceptions.PyLXDException),
- )
- def test_get_raw(self, tag, effect, result, mg):
- mg.return_value.getresponse.return_value = FakeResponse(*effect)
- if inspect.isclass(result):
- self.assertRaises(result, self.conn.get_raw)
- else:
- self.assertEqual(result, self.conn.get_raw())
-
- @mock.patch('pylxd.deprecated.connection.WebSocketClient')
- @annotated_data(
- ('fake_host', 'wss://fake_host:8443'),
- (None, 'ws+unix:///var/lib/lxd/unix.socket')
- )
- def test_get_ws(self, host, result, mock_ws, _):
- conn = connection.LXDConnection(host)
-
- conn.get_ws('/fake/path')
-
- mock_ws.assert_has_calls([mock.call(result), mock.call().connect()])
diff --git a/pylxd/tests/test_container.py b/pylxd/tests/test_container.py
deleted file mode 100644
index 0385300..0000000
--- a/pylxd/tests/test_container.py
+++ /dev/null
@@ -1,222 +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 collections import OrderedDict
-from ddt import ddt
-import json
-import mock
-import tempfile
-
-from pylxd.deprecated import connection
-
-from pylxd.tests import annotated_data
-from pylxd.tests import fake_api
-from pylxd.tests import LXDAPITestBase
-
-
- at ddt
- at mock.patch.object(connection.LXDConnection, 'get_object',
- return_value=('200', fake_api.fake_operation()))
-class LXDAPIContainerTestObject(LXDAPITestBase):
-
- def test_list_containers(self, ms):
- ms.return_value = ('200', fake_api.fake_container_list())
- self.assertEqual(
- ['trusty-1'],
- self.lxd.container_list())
- ms.assert_called_once_with('GET',
- '/1.0/containers')
-
- @annotated_data(
- ('STOPPED', False),
- ('STOPPING', False),
- ('ABORTING', False),
- ('RUNNING', True),
- ('STARTING', True),
- ('FREEZING', True),
- ('FROZEN', True),
- ('THAWED', True),
- )
- def test_container_running(self, status, running, ms):
- with mock.patch.object(connection.LXDConnection, 'get_object') as ms:
- ms.return_value = ('200', fake_api.fake_container_state(status))
- self.assertEqual(running, self.lxd.container_running('trusty-1'))
- ms.assert_called_once_with('GET',
- '/1.0/containers/trusty-1/state')
-
- def test_container_init(self, ms):
- self.assertEqual(ms.return_value, self.lxd.container_init('fake'))
- ms.assert_called_once_with('POST',
- '/1.0/containers',
- '"fake"')
-
- def test_container_update(self, ms):
- self.assertEqual(ms.return_value,
- self.lxd.container_update('trusty-1',
- 'fake'))
- ms.assert_called_once_with('PUT',
- '/1.0/containers/trusty-1',
- '"fake"')
-
- def test_container_state(self, ms):
- ms.return_value = ('200', fake_api.fake_container_state('RUNNING'))
- self.assertEqual(ms.return_value, self.lxd.container_state('trusty-1'))
- ms.assert_called_with('GET',
- '/1.0/containers/trusty-1/state')
-
- @annotated_data(
- ('start', 'start'),
- ('stop', 'stop'),
- ('suspend', 'freeze'),
- ('resume', 'unfreeze'),
- ('reboot', 'restart'),
- )
- def test_container_actions(self, method, action, ms):
- self.assertEqual(
- ms.return_value,
- getattr(self.lxd, 'container_' + method)('trusty-1', 30))
- ms.assert_called_once_with('PUT',
- '/1.0/containers/trusty-1/state',
- json.dumps({'action': action,
- 'force': True,
- 'timeout': 30, }))
-
- def test_container_destroy(self, ms):
- self.assertEqual(
- ms.return_value, self.lxd.container_destroy('trusty-1'))
- ms.assert_called_once_with('DELETE',
- '/1.0/containers/trusty-1')
-
- def test_container_log(self, ms):
- ms.return_value = ('200', fake_api.fake_container_log())
- self.assertEqual(
- 'fake log', self.lxd.get_container_log('trusty-1'))
- ms.assert_called_once_with('GET',
- '/1.0/containers/trusty-1?log=true')
-
- def test_container_config(self, ms):
- ms.return_value = ('200', fake_api.fake_container_state('fake'))
- self.assertEqual(
- {'status': 'fake'}, self.lxd.get_container_config('trusty-1'))
- ms.assert_called_once_with('GET',
- '/1.0/containers/trusty-1?log=false')
-
- def test_container_info(self, ms):
- ms.return_value = ('200', fake_api.fake_container_state('fake'))
- self.assertEqual(
- {'status': 'fake'}, self.lxd.container_info('trusty-1'))
- ms.assert_called_once_with('GET',
- '/1.0/containers/trusty-1/state')
-
- def test_container_migrate(self, ms):
- ms.return_value = ('200', fake_api.fake_container_migrate())
- self.assertEqual(
- {'control': 'fake_control',
- 'criu': 'fake_criu',
- 'fs': 'fake_fs',
- 'operation': '1234'},
- self.lxd.container_migrate('trusty-1'))
- ms.assert_called_once_with('POST',
- '/1.0/containers/trusty-1',
- '{"migration": true}')
-
- def test_container_publish(self, ms):
- ms.return_value = ('200', fake_api.fake_operation())
- self.assertEqual(
- ms.return_value, self.lxd.container_publish('trusty-1'))
- ms.assert_called_once_with('POST',
- '/1.0/images',
- '"trusty-1"')
-
- def test_container_put_file(self, ms):
- temp_file = tempfile.NamedTemporaryFile()
- ms.return_value = ('200', fake_api.fake_standard_return())
- self.assertEqual(
- ms.return_value, self.lxd.put_container_file('trusty-1',
- temp_file.name,
- 'dst_file'))
- ms.assert_called_once_with(
- 'POST',
- '/1.0/containers/trusty-1/files?path=dst_file',
- body=b'',
- headers={'X-LXD-gid': 0, 'X-LXD-mode': 0o644, 'X-LXD-uid': 0})
-
- def test_list_snapshots(self, ms):
- ms.return_value = ('200', fake_api.fake_snapshots_list())
- self.assertEqual(
- ['/1.0/containers/trusty-1/snapshots/first'],
- self.lxd.container_snapshot_list('trusty-1'))
- ms.assert_called_once_with('GET',
- '/1.0/containers/trusty-1/snapshots')
-
- @annotated_data(
- ('create', 'POST', '', ('fake config',), ('"fake config"',)),
- ('info', 'GET', '/first', ('first',), ()),
- ('rename', 'POST', '/first',
- ('first', 'fake config'), ('"fake config"',)),
- ('delete', 'DELETE', '/first', ('first',), ()),
- )
- def test_snapshot_operations(self, method, http, path,
- args, call_args, ms):
- self.assertEqual(
- ms.return_value,
- getattr(self.lxd,
- 'container_snapshot_' + method)('trusty-1', *args))
- ms.assert_called_once_with(http,
- '/1.0/containers/trusty-1/snapshots' +
- path,
- *call_args)
-
- def test_container_run_command(self, ms):
- data = OrderedDict((
- ('command', ['/fake/command']),
- ('interactive', False),
- ('wait-for-websocket', False),
- ('environment', {'FAKE_ENV': 'fake'})
- ))
-
- self.assertEqual(
- ms.return_value,
- self.lxd.container_run_command('trusty-1', *data.values()))
- self.assertEqual(1, ms.call_count)
- self.assertEqual(
- ms.call_args[0][:2],
- ('POST', '/1.0/containers/trusty-1/exec'))
- self.assertEqual(
- json.loads(ms.call_args[0][2]),
- dict(data)
- )
-
-
- at ddt
- at mock.patch.object(connection.LXDConnection, 'get_object',
- return_value=('200', fake_api.fake_container_list()))
-class LXDAPIContainerTestStatus(LXDAPITestBase):
-
- def test_container_defined(self, ms):
- self.assertTrue(self.lxd.container_defined('trusty-1'))
- ms.assert_called_once_with('GET', '/1.0/containers')
-
-
- at ddt
- at mock.patch.object(connection.LXDConnection, 'get_raw',
- return_value='fake contents')
-class LXDAPIContainerTestRaw(LXDAPITestBase):
-
- def test_container_file(self, ms):
- self.assertEqual(
- 'fake contents', self.lxd.get_container_file('trusty-1',
- '/file/name'))
- ms.assert_called_once_with(
- 'GET', '/1.0/containers/trusty-1/files?path=/file/name')
diff --git a/pylxd/tests/test_host.py b/pylxd/tests/test_host.py
deleted file mode 100644
index 80e2ad4..0000000
--- a/pylxd/tests/test_host.py
+++ /dev/null
@@ -1,82 +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 ddt import data
-from ddt import ddt
-import mock
-
-from pylxd.deprecated import connection
-from pylxd.deprecated import exceptions
-
-from pylxd.tests import annotated_data
-from pylxd.tests import fake_api
-from pylxd.tests import LXDAPITestBase
-
-
- at ddt
- at mock.patch.object(connection.LXDConnection, 'get_object',
- return_value=('200', fake_api.fake_host()))
-class LXDAPIHostTestObject(LXDAPITestBase):
-
- def test_get_host_info(self, ms):
- result = self.lxd.host_info()
- self.assertEqual(result, {
- 'lxd_api_compat_level': 1,
- 'lxd_trusted_host': True,
- 'lxd_backing_fs': 'ext4',
- 'lxd_driver': 'lxc',
- 'lxd_version': 0.12,
- 'lxc_version': '1.1.2',
- 'kernel_version': '3.19.0-22-generic',
- })
- ms.assert_called_once_with('GET', '/1.0')
-
- host_data = (
- ('lxd_api_compat', 1),
- ('lxd_host_trust', True),
- ('lxd_backing_fs', 'ext4'),
- ('lxd_driver', 'lxc'),
- ('lxc_version', '1.1.2'),
- ('lxd_version', 0.12),
- ('kernel_version', '3.19.0-22-generic'),
- )
-
- @annotated_data(*host_data)
- def test_get_host_data(self, method, expected, ms):
- result = getattr(self.lxd, 'get_' + method)(data=None)
- self.assertEqual(expected, result)
- ms.assert_called_once_with('GET', '/1.0')
-
- @annotated_data(*host_data)
- def test_get_host_data_fail(self, method, expected, ms):
- ms.side_effect = exceptions.PyLXDException
- result = getattr(self.lxd, 'get_' + method)(data=None)
- self.assertEqual(None, result)
- ms.assert_called_once_with('GET', '/1.0')
-
-
- at ddt
- at mock.patch.object(connection.LXDConnection, 'get_status')
-class LXDAPIHostTestStatus(LXDAPITestBase):
-
- @data(True, False)
- def test_get_host_ping(self, value, ms):
- ms.return_value = value
- self.assertEqual(value, self.lxd.host_ping())
- ms.assert_called_once_with('GET', '/1.0')
-
- def test_get_host_ping_fail(self, ms):
- ms.side_effect = Exception
- self.assertRaises(exceptions.PyLXDException, self.lxd.host_ping)
- ms.assert_called_once_with('GET', '/1.0')
diff --git a/pylxd/tests/test_image.py b/pylxd/tests/test_image.py
deleted file mode 100644
index 93b33f1..0000000
--- a/pylxd/tests/test_image.py
+++ /dev/null
@@ -1,241 +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 datetime
-from ddt import ddt
-import mock
-from six.moves import builtins
-from six.moves import cStringIO
-import unittest
-
-from pylxd.deprecated import connection
-from pylxd.deprecated import exceptions
-from pylxd.deprecated import image
-
-from pylxd.tests import annotated_data
-from pylxd.tests import fake_api
-from pylxd.tests import LXDAPITestBase
-
-
- at ddt
- at mock.patch.object(connection.LXDConnection, 'get_object',
- return_value=('200', fake_api.fake_image_info()))
-class LXDAPIImageTestObject(LXDAPITestBase):
-
- list_data = (
- ('list', (), ()),
- ('search', ({'foo': 'bar'},), ('foo=bar',)),
- )
-
- @annotated_data(*list_data)
- def test_list_images(self, method, args, call_args, ms):
- ms.return_value = ('200', fake_api.fake_image_list())
- self.assertEqual(
- ['trusty'], getattr(self.lxd, 'image_' + method)(*args))
- ms.assert_called_once_with('GET', '/1.0/images', *call_args)
-
- @annotated_data(*list_data)
- def test_list_images_fail(self, method, args, call_args, ms):
- ms.side_effect = exceptions.PyLXDException
- self.assertRaises(exceptions.PyLXDException,
- getattr(self.lxd, 'image_' + method),
- *args)
- ms.assert_called_once_with('GET', '/1.0/images', *call_args)
-
- @annotated_data(
- (True, (('200', fake_api.fake_image_info()),)),
- (False, exceptions.APIError("404", 404)),
- )
- def test_image_defined(self, expected, side_effect, ms):
- ms.side_effect = side_effect
- self.assertEqual(expected, self.lxd.image_defined('test-image'))
- ms.assert_called_once_with('GET', '/1.0/images/test-image')
-
- @annotated_data(
- ('APIError', exceptions.APIError("500", 500), exceptions.APIError),
- ('PyLXDException', exceptions.PyLXDException,
- exceptions.PyLXDException)
- )
- def test_image_defined_fail(self, tag, side_effect, expected, ms):
- ms.side_effect = side_effect
- self.assertRaises(expected,
- self.lxd.image_defined, ('test-image',))
- ms.assert_called_once_with('GET', '/1.0/images/test-image')
-
- def test_image_info(self, ms):
- self.assertEqual({
- 'image_upload_date': (datetime.datetime
- .fromtimestamp(1435669853)
- .strftime('%Y-%m-%d %H:%M:%S')),
- 'image_created_date': 'Unknown',
- 'image_expires_date': 'Unknown',
- 'image_public': False,
- 'image_size': '63MB',
- 'image_fingerprint': '04aac4257341478b49c25d22cea8a6ce'
- '0489dc6c42d835367945e7596368a37f',
- 'image_architecture': 'x86_64',
- }, self.lxd.image_info('test-image'))
- ms.assert_called_once_with('GET', '/1.0/images/test-image')
-
- def test_image_info_fail(self, ms):
- ms.side_effect = exceptions.PyLXDException
- self.assertRaises(exceptions.PyLXDException,
- self.lxd.image_info, ('test-image',))
- ms.assert_called_once_with('GET', '/1.0/images/test-image')
-
- dates_data = (
- ('upload', (datetime.datetime.fromtimestamp(1435669853)
- .strftime('%Y-%m-%d %H:%M:%S'))),
- ('create', 'Unknown'),
- ('expire', 'Unknown'),
- )
-
- @annotated_data(*dates_data)
- def test_image_date(self, method, expected, ms):
- self.assertEqual(expected, getattr(
- self.lxd, 'image_{}_date'.format(method))('test-image',
- data=None))
- ms.assert_called_once_with('GET', '/1.0/images/test-image')
-
- @annotated_data(*dates_data)
- def test_image_date_fail(self, method, expected, ms):
- ms.side_effect = exceptions.PyLXDException
- self.assertRaises(exceptions.PyLXDException, getattr(
- self.lxd,
- 'image_{}_date'.format(method)),
- 'test-image',
- data=None)
- ms.assert_called_once_with('GET', '/1.0/images/test-image')
-
-
- at ddt
- at mock.patch.object(connection.LXDConnection, 'get_status', return_value=True)
-class LXDAPIImageTestStatus(LXDAPITestBase):
- operations_data = (
- ('delete', 'DELETE', '/test-image', ('test-image',), ()),
- ('update', 'PUT', '/test-image', ('test-image', 'fake',), ('"fake"',)),
- ('rename', 'POST', '/test-image',
- ('test-image', 'fake',), ('"fake"',)),
- )
-
- @annotated_data(*operations_data)
- def test_image_operations(self, method, http, path, args, call_args, ms):
- self.assertTrue(
- getattr(self.lxd, 'image_' + method)(*args))
- ms.assert_called_once_with(
- http,
- '/1.0/images' + path,
- *call_args
- )
-
- @annotated_data(*operations_data)
- def test_image_operations_fail(self, method, http, path,
- args, call_args, ms):
- ms.side_effect = exceptions.PyLXDException
- self.assertRaises(exceptions.PyLXDException,
- getattr(self.lxd, 'image_' + method),
- *args)
- ms.assert_called_once_with(
- http,
- '/1.0/images' + path,
- *call_args
- )
-
-
- at mock.patch.object(connection.LXDConnection, 'get_object',
- return_value=('200', fake_api.fake_image_info()))
-class LXDAPAPIImageTestUpload(LXDAPITestBase):
- @mock.patch.object(builtins, 'open', return_value=cStringIO('fake'))
- def test_image_upload_file(self, mo, ms):
- self.assertTrue(self.lxd.image_upload(path='/fake/path'))
- mo.assert_called_once_with('/fake/path', 'rb')
- ms.assert_called_once_with('POST', '/1.0/images', 'fake', {})
-
-
- at mock.patch.object(connection.LXDConnection, 'get_raw')
-class LXDAPIImageTestRaw(LXDAPITestBase):
-
- def test_image_export(self, ms):
- ms.return_value = 'fake contents'
- self.assertEqual('fake contents', self.lxd.image_export('fake'))
- ms.assert_called_once_with('GET', '/1.0/images/fake/export')
-
- def test_image_export_fail(self, ms):
- ms.side_effect = exceptions.PyLXDException
- self.assertRaises(exceptions.PyLXDException,
- self.lxd.image_export, 'fake')
- ms.assert_called_once_with('GET', '/1.0/images/fake/export')
-
-
- at ddt
- at mock.patch.object(connection.LXDConnection, 'get_object',
- return_value=(200, fake_api.fake_image_info()))
-class LXDAPIImageInfoTest(unittest.TestCase):
-
- def setUp(self):
- super(LXDAPIImageInfoTest, self).setUp()
- self.image = image.LXDImage()
-
- info_list = (
- ('permission', False),
- ('size', 63),
- ('fingerprint', '04aac4257341478b49c25d22cea8a6ce'
- '0489dc6c42d835367945e7596368a37f'),
- ('architecture', 'x86_64'),
- )
-
- @annotated_data(*info_list)
- def test_info_no_data(self, method, expected, mc):
- self.assertEqual(expected,
- (getattr(self.image, 'get_image_' + method)
- ('test-image', data=None)))
- mc.assert_called_once_with('GET', '/1.0/images/test-image')
-
- @annotated_data(*info_list)
- def test_info_no_data_fail(self, method, expected, mc):
- mc.side_effect = exceptions.PyLXDException
- self.assertRaises(exceptions.PyLXDException,
- getattr(self.image, 'get_image_' + method),
- 'test-image',
- data=None)
-
- @annotated_data(
- ('permission_true', 'permission', {'public': 0}, False),
- ('permission_false', 'permission', {'public': 1}, True),
- ('size', 'size', {'size': 52428800}, 50),
- ('fingerprint', 'fingerprint', {'fingerprint': 'AAAA'}, 'AAAA'),
- *[('architecture_' + v, 'architecture', {'architecture': k}, v)
- for k, v in image.image_architecture.items()]
- )
- def test_info_data(self, tag, method, metadata, expected, mc):
- self.assertEqual(
- expected, getattr(self.image, 'get_image_' + method)
- ('test-image', data=metadata))
- self.assertFalse(mc.called)
-
- @annotated_data(
- ('permission', 'permission', {}, KeyError),
- ('size', 'size', {'size': 0}, exceptions.ImageInvalidSize),
- ('size', 'size', {'size': -1}, exceptions.ImageInvalidSize),
- ('fingerprint', 'fingerprint', {}, KeyError),
- ('architecture', 'architecture', {}, KeyError),
- ('architecture_invalid', 'architecture',
- {'architecture': -1}, KeyError)
- )
- def test_info_data_fail(self, tag, method, metadata, expected, mc):
- self.assertRaises(expected,
- getattr(self.image, 'get_image_' + method),
- 'test-image',
- data=metadata)
- self.assertFalse(mc.called)
diff --git a/pylxd/tests/test_image_alias.py b/pylxd/tests/test_image_alias.py
deleted file mode 100644
index bccc44c..0000000
--- a/pylxd/tests/test_image_alias.py
+++ /dev/null
@@ -1,66 +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 ddt import data
-from ddt import ddt
-import mock
-
-from pylxd.deprecated import connection
-
-from pylxd.tests import annotated_data
-from pylxd.tests import fake_api
-from pylxd.tests import LXDAPITestBase
-
-
- at ddt
- at mock.patch.object(connection.LXDConnection, 'get_object')
-class LXDAPIImageAliasTestObject(LXDAPITestBase):
-
- def test_alias_list(self, ms):
- ms.return_value = ('200', fake_api.fake_alias_list())
- self.assertEqual(['ubuntu'], self.lxd.alias_list())
- ms.assert_called_once_with('GET', '/1.0/images/aliases')
-
- def test_alias_show(self, ms):
- ms.return_value = ('200', fake_api.fake_alias())
- self.assertEqual(
- fake_api.fake_alias(), self.lxd.alias_show('fake')[1])
- ms.assert_called_once_with('GET', '/1.0/images/aliases/fake')
-
-
- at ddt
- at mock.patch.object(connection.LXDConnection, 'get_status')
-class LXDAPIImageAliasTestStatus(LXDAPITestBase):
-
- @data(True, False)
- def test_alias_defined(self, expected, ms):
- ms.return_value = expected
- self.assertEqual(expected, self.lxd.alias_defined('fake'))
- ms.assert_called_once_with('GET', '/1.0/images/aliases/fake')
-
- @annotated_data(
- ('create', 'POST', '', ('fake',), ('"fake"',)),
- ('update', 'PUT', '/test-alias',
- ('test-alias', 'fake',), ('"fake"',)),
- ('rename', 'POST', '/test-alias',
- ('test-alias', 'fake',), ('"fake"',)),
- ('delete', 'DELETE', '/test-alias', ('test-alias',), ()),
- )
- def test_alias_operations(self, method, http, path, args, call_args, ms):
- self.assertTrue(getattr(self.lxd, 'alias_' + method)(*args))
- ms.assert_called_once_with(
- http,
- '/1.0/images/aliases' + path,
- *call_args
- )
diff --git a/pylxd/tests/test_network.py b/pylxd/tests/test_network.py
deleted file mode 100644
index 65efdb6..0000000
--- a/pylxd/tests/test_network.py
+++ /dev/null
@@ -1,57 +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 ddt import ddt
-import mock
-
-from pylxd.deprecated import connection
-
-from pylxd.tests import annotated_data
-from pylxd.tests import fake_api
-from pylxd.tests import LXDAPITestBase
-
-
- at ddt
- at mock.patch.object(connection.LXDConnection, 'get_object',
- return_value=(200, fake_api.fake_network()))
-class LXDAPINetworkTest(LXDAPITestBase):
-
- def test_list_networks(self, ms):
- ms.return_value = ('200', fake_api.fake_network_list())
- self.assertEqual(
- ['lxcbr0'],
- self.lxd.network_list())
- ms.assert_called_with('GET',
- '/1.0/networks')
-
- def test_network_show(self, ms):
- self.assertEqual({
- 'network_name': 'lxcbr0',
- 'network_type': 'bridge',
- 'network_members': ['/1.0/containers/trusty-1'],
- }, self.lxd.network_show('lxcbr0'))
- ms.assert_called_with('GET',
- '/1.0/networks/lxcbr0')
-
- @annotated_data(
- ('name', 'lxcbr0'),
- ('type', 'bridge'),
- ('members', ['/1.0/containers/trusty-1']),
- )
- def test_network_data(self, method, expected, ms):
- self.assertEqual(
- expected, getattr(self.lxd,
- 'network_show_' + method)('lxcbr0'))
- ms.assert_called_with('GET',
- '/1.0/networks/lxcbr0')
diff --git a/pylxd/tests/test_operation.py b/pylxd/tests/test_operation.py
deleted file mode 100644
index 233bb0b..0000000
--- a/pylxd/tests/test_operation.py
+++ /dev/null
@@ -1,75 +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 datetime
-from ddt import ddt
-import mock
-
-from pylxd.deprecated import connection
-
-from pylxd.tests import annotated_data
-from pylxd.tests import fake_api
-from pylxd.tests import LXDAPITestBase
-
-
- at ddt
- at mock.patch.object(connection.LXDConnection, 'get_object',
- return_value=(200, fake_api.fake_operation()))
-class LXDAPIOperationTestObject(LXDAPITestBase):
-
- def test_list_operations(self, ms):
- ms.return_value = ('200', fake_api.fake_operation_list())
- self.assertEqual(
- ['/1.0/operations/1234'],
- self.lxd.list_operations())
- ms.assert_called_with('GET',
- '/1.0/operations')
-
- def test_operation_info(self, ms):
- ms.return_value = ('200', fake_api.fake_operation())
- self.assertEqual(
- ms.return_value, self.lxd.operation_info('/1.0/operations/1234'))
- ms.assert_called_with('GET',
- '/1.0/operations/1234')
-
- @annotated_data(
- ('create_time',
- datetime.datetime.utcfromtimestamp(1433876844)
- .strftime('%Y-%m-%d %H:%M:%S')),
- ('update_time',
- datetime.datetime.utcfromtimestamp(1433876843)
- .strftime('%Y-%m-%d %H:%M:%S')),
- ('status', 'Running'),
- )
- def test_operation_show(self, method, expected, ms):
- call = getattr(self.lxd, 'operation_show_' + method)
- self.assertEqual(expected, call('/1.0/operations/1234'))
- ms.assert_called_with('GET',
- '/1.0/operations/1234')
-
-
- at ddt
- at mock.patch.object(connection.LXDConnection, 'get_status', return_value=True)
-class LXDAPIOperationTestStatus(LXDAPITestBase):
-
- @annotated_data(
- ('operation_delete', 'DELETE', '', ()),
- ('wait_container_operation', 'GET',
- '/wait?status_code=200&timeout=30', ('200', '30')),
- )
- def test_operation_actions(self, method, http, path, args, ms):
- self.assertTrue(
- getattr(self.lxd, method)('/1.0/operations/1234', *args))
- ms.assert_called_with(http,
- '/1.0/operations/1234' + path)
diff --git a/pylxd/tests/test_profiles.py b/pylxd/tests/test_profiles.py
deleted file mode 100644
index 1844986..0000000
--- a/pylxd/tests/test_profiles.py
+++ /dev/null
@@ -1,69 +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 ddt import data
-from ddt import ddt
-import mock
-
-from pylxd.deprecated import connection
-
-from pylxd.tests import annotated_data
-from pylxd.tests import fake_api
-from pylxd.tests import LXDAPITestBase
-
-
- at mock.patch.object(connection.LXDConnection, 'get_object',
- return_value=(200, fake_api.fake_profile()))
-class LXDAPIProfilesTestObject(LXDAPITestBase):
-
- def test_list_profiles(self, ms):
- ms.return_value = ('200', fake_api.fake_profile_list())
- self.assertEqual(
- ['fake-profile'],
- self.lxd.profile_list())
- ms.assert_called_with('GET',
- '/1.0/profiles')
-
- def test_profile_show(self, ms):
- self.assertEqual(
- ms.return_value, self.lxd.profile_show('fake-profile'))
- ms.assert_called_with('GET',
- '/1.0/profiles/fake-profile')
-
-
- at ddt
- at mock.patch.object(connection.LXDConnection, 'get_status', return_value=True)
-class LXDAPIProfilesTestStatus(LXDAPITestBase):
-
- @data(True, False)
- def test_profile_defined(self, defined, ms):
- ms.return_value = defined
- self.assertEqual(defined, self.lxd.profile_defined('fake-profile'))
- ms.assert_called_with('GET',
- '/1.0/profiles/fake-profile')
-
- @annotated_data(
- ('create', 'POST', '', ('fake config',), ('"fake config"',)),
- ('update', 'PUT', '/fake-profile',
- ('fake-profile', 'fake config',), ('"fake config"',)),
- ('rename', 'POST', '/fake-profile',
- ('fake-profile', 'fake config',), ('"fake config"',)),
- ('delete', 'DELETE', '/fake-profile', ('fake-profile',), ()),
- )
- def test_profile_operations(self, method, http, path, args, call_args, ms):
- self.assertTrue(
- getattr(self.lxd, 'profile_' + method)(*args))
- ms.assert_called_with(http,
- '/1.0/profiles' + path,
- *call_args)
diff --git a/pylxd/tests/utils.py b/pylxd/tests/utils.py
deleted file mode 100644
index ec424bf..0000000
--- a/pylxd/tests/utils.py
+++ /dev/null
@@ -1,42 +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 pylxd import api
-from pylxd import exceptions as lxd_exceptions
-
-
-def upload_image(image):
- alias = '{}/{}/{}/{}'.format(image['os'],
- image['release'],
- image['arch'],
- image['variant'])
- lxd = api.API()
- imgs = api.API(host='images.linuxcontainers.org')
- d = imgs.alias_show(alias)
-
- meta = d[1]['metadata']
- tgt = meta['target']
-
- try:
- lxd.alias_update(meta)
- except lxd_exceptions.APIError as ex:
- if ex.status_code == 404:
- lxd.alias_create(meta)
-
- return tgt
-
-
-def delete_image(image):
- lxd = api.API()
- lxd.image_delete(image)
More information about the lxc-devel
mailing list