From cc62efef4175a7f46c8829f7a25430a033ac30c8 Mon Sep 17 00:00:00 2001 From: Andrey Kurilin Date: Wed, 3 Dec 2014 14:11:39 +0200 Subject: [PATCH] Remove code related to V3 Since V3 API is not supported, code related to it should be removed. Discussion in mailing: http://lists.openstack.org/pipermail/openstack-dev/2014-December/052095.html Change-Id: Iac5c5e6d81479cbeb8bf10cfcda1cc5617680de8 --- novaclient/client.py | 2 +- novaclient/shell.py | 3 +- .../tests/fixture_data/availability_zones.py | 6 - novaclient/tests/fixture_data/client.py | 18 - novaclient/tests/fixture_data/hosts.py | 20 - novaclient/tests/fixture_data/hypervisors.py | 33 - novaclient/tests/fixture_data/keypairs.py | 5 - novaclient/tests/fixture_data/quotas.py | 18 - novaclient/tests/fixture_data/servers.py | 128 - novaclient/tests/test_client.py | 52 +- novaclient/tests/v3/__init__.py | 0 novaclient/tests/v3/fakes.py | 388 -- novaclient/tests/v3/test_agents.py | 29 - novaclient/tests/v3/test_aggregates.py | 22 - novaclient/tests/v3/test_availability_zone.py | 36 - novaclient/tests/v3/test_certs.py | 21 - novaclient/tests/v3/test_flavor_access.py | 63 - novaclient/tests/v3/test_flavors.py | 74 - novaclient/tests/v3/test_hosts.py | 99 - novaclient/tests/v3/test_hypervisors.py | 47 - novaclient/tests/v3/test_images.py | 57 - novaclient/tests/v3/test_keypairs.py | 30 - novaclient/tests/v3/test_limits.py | 88 - novaclient/tests/v3/test_list_extensions.py | 33 - novaclient/tests/v3/test_quotas.py | 43 - novaclient/tests/v3/test_servers.py | 478 --- novaclient/tests/v3/test_services.py | 38 - novaclient/tests/v3/test_shell.py | 766 ---- novaclient/tests/v3/test_usage.py | 30 - novaclient/tests/v3/test_volumes.py | 64 - novaclient/v3/__init__.py | 17 - novaclient/v3/agents.py | 34 - novaclient/v3/aggregates.py | 26 - novaclient/v3/availability_zones.py | 33 - novaclient/v3/certs.py | 30 - novaclient/v3/client.py | 196 - novaclient/v3/flavor_access.py | 45 - novaclient/v3/flavors.py | 100 - novaclient/v3/hosts.py | 51 - novaclient/v3/hypervisors.py | 49 - novaclient/v3/images.py | 107 - novaclient/v3/keypairs.py | 28 - novaclient/v3/limits.py | 50 - novaclient/v3/list_extensions.py | 26 - novaclient/v3/quotas.py | 42 - novaclient/v3/servers.py | 1022 ----- novaclient/v3/services.py | 34 - novaclient/v3/shell.py | 3415 ----------------- novaclient/v3/usage.py | 27 - novaclient/v3/volumes.py | 75 - 50 files changed, 3 insertions(+), 8095 deletions(-) delete mode 100644 novaclient/tests/v3/__init__.py delete mode 100644 novaclient/tests/v3/fakes.py delete mode 100644 novaclient/tests/v3/test_agents.py delete mode 100644 novaclient/tests/v3/test_aggregates.py delete mode 100644 novaclient/tests/v3/test_availability_zone.py delete mode 100644 novaclient/tests/v3/test_certs.py delete mode 100644 novaclient/tests/v3/test_flavor_access.py delete mode 100644 novaclient/tests/v3/test_flavors.py delete mode 100644 novaclient/tests/v3/test_hosts.py delete mode 100644 novaclient/tests/v3/test_hypervisors.py delete mode 100644 novaclient/tests/v3/test_images.py delete mode 100644 novaclient/tests/v3/test_keypairs.py delete mode 100644 novaclient/tests/v3/test_limits.py delete mode 100644 novaclient/tests/v3/test_list_extensions.py delete mode 100644 novaclient/tests/v3/test_quotas.py delete mode 100644 novaclient/tests/v3/test_servers.py delete mode 100644 novaclient/tests/v3/test_services.py delete mode 100644 novaclient/tests/v3/test_shell.py delete mode 100644 novaclient/tests/v3/test_usage.py delete mode 100644 novaclient/tests/v3/test_volumes.py delete mode 100644 novaclient/v3/__init__.py delete mode 100644 novaclient/v3/agents.py delete mode 100644 novaclient/v3/aggregates.py delete mode 100644 novaclient/v3/availability_zones.py delete mode 100644 novaclient/v3/certs.py delete mode 100644 novaclient/v3/client.py delete mode 100644 novaclient/v3/flavor_access.py delete mode 100644 novaclient/v3/flavors.py delete mode 100644 novaclient/v3/hosts.py delete mode 100644 novaclient/v3/hypervisors.py delete mode 100644 novaclient/v3/images.py delete mode 100644 novaclient/v3/keypairs.py delete mode 100644 novaclient/v3/limits.py delete mode 100644 novaclient/v3/list_extensions.py delete mode 100644 novaclient/v3/quotas.py delete mode 100644 novaclient/v3/servers.py delete mode 100644 novaclient/v3/services.py delete mode 100644 novaclient/v3/shell.py delete mode 100644 novaclient/v3/usage.py delete mode 100644 novaclient/v3/volumes.py diff --git a/novaclient/client.py b/novaclient/client.py index 9256dbfff..be31686a3 100644 --- a/novaclient/client.py +++ b/novaclient/client.py @@ -763,7 +763,7 @@ def get_client_class(version): version_map = { '1.1': 'novaclient.v1_1.client.Client', '2': 'novaclient.v1_1.client.Client', - '3': 'novaclient.v3.client.Client', + '3': 'novaclient.v1_1.client.Client', } try: client_path = version_map[str(version)] diff --git a/novaclient/shell.py b/novaclient/shell.py index bc88283e0..bb79d6379 100644 --- a/novaclient/shell.py +++ b/novaclient/shell.py @@ -56,7 +56,6 @@ from novaclient.i18n import _ from novaclient.openstack.common import cliutils from novaclient import utils from novaclient.v1_1 import shell as shell_v1_1 -from novaclient.v3 import shell as shell_v3 DEFAULT_OS_COMPUTE_API_VERSION = "1.1" DEFAULT_NOVA_ENDPOINT_TYPE = 'publicURL' @@ -431,7 +430,7 @@ class OpenStackComputeShell(object): actions_module = { '1.1': shell_v1_1, '2': shell_v1_1, - '3': shell_v3, + '3': shell_v1_1, }[version] except KeyError: actions_module = shell_v1_1 diff --git a/novaclient/tests/fixture_data/availability_zones.py b/novaclient/tests/fixture_data/availability_zones.py index 451a64b2a..008cea2a9 100644 --- a/novaclient/tests/fixture_data/availability_zones.py +++ b/novaclient/tests/fixture_data/availability_zones.py @@ -89,9 +89,3 @@ class V1(base.Fixture): self.requests.register_uri('GET', self.url('detail'), json=get_os_zone_detail, headers=self.json_headers) - - -class V3(V1): - zone_info_key = 'availability_zone_info' - zone_name_key = 'zone_name' - zone_state_key = 'zone_state' diff --git a/novaclient/tests/fixture_data/client.py b/novaclient/tests/fixture_data/client.py index 094bc969f..c6ddad3b7 100644 --- a/novaclient/tests/fixture_data/client.py +++ b/novaclient/tests/fixture_data/client.py @@ -16,7 +16,6 @@ from keystoneclient import fixture from keystoneclient import session from novaclient.v1_1 import client as v1_1client -from novaclient.v3 import client as v3client IDENTITY_URL = 'http://identityserver:5000/v2.0' COMPUTE_URL = 'http://compute.host' @@ -58,26 +57,9 @@ class V1(fixtures.Fixture): auth_url=self.identity_url) -class V3(V1): - - def new_client(self): - return v3client.Client(username='xx', - password='xx', - project_id='xx', - auth_url=self.identity_url) - - class SessionV1(V1): def new_client(self): self.session = session.Session() self.session.auth = v2.Password(self.identity_url, 'xx', 'xx') return v1_1client.Client(session=self.session) - - -class SessionV3(V1): - - def new_client(self): - self.session = session.Session() - self.session.auth = v2.Password(self.identity_url, 'xx', 'xx') - return v3client.Client(session=self.session) diff --git a/novaclient/tests/fixture_data/hosts.py b/novaclient/tests/fixture_data/hosts.py index 0235117a8..e7684f606 100644 --- a/novaclient/tests/fixture_data/hosts.py +++ b/novaclient/tests/fixture_data/hosts.py @@ -147,23 +147,3 @@ class V1(BaseFixture): def get_host_shutdown(self): return {'host': 'sample_host', 'power_action': 'shutdown'} - - -class V3(V1): - def put_host_1(self): - return {'host': super(V3, self).put_host_1()} - - def put_host_2(self): - return {'host': super(V3, self).put_host_2()} - - def put_host_3(self): - return {'host': super(V3, self).put_host_3()} - - def get_host_reboot(self): - return {'host': super(V3, self).get_host_reboot()} - - def get_host_startup(self): - return {'host': super(V3, self).get_host_startup()} - - def get_host_shutdown(self): - return {'host': super(V3, self).get_host_shutdown()} diff --git a/novaclient/tests/fixture_data/hypervisors.py b/novaclient/tests/fixture_data/hypervisors.py index c3f6f7210..83a37d58c 100644 --- a/novaclient/tests/fixture_data/hypervisors.py +++ b/novaclient/tests/fixture_data/hypervisors.py @@ -180,36 +180,3 @@ class V1(base.Fixture): self.requests.register_uri('GET', self.url(1234, 'uptime'), json=get_os_hypervisors_uptime, headers=self.headers) - - -class V3(V1): - - def setUp(self): - super(V3, self).setUp() - - get_os_hypervisors_search = { - 'hypervisors': [ - {'id': 1234, 'hypervisor_hostname': 'hyper1'}, - {'id': 5678, 'hypervisor_hostname': 'hyper2'} - ] - } - - self.requests.register_uri('GET', - self.url('search', query='hyper'), - json=get_os_hypervisors_search, - headers=self.headers) - - get_1234_servers = { - 'hypervisor': { - 'id': 1234, - 'hypervisor_hostname': 'hyper1', - 'servers': [ - {'name': 'inst1', 'id': 'uuid1'}, - {'name': 'inst2', 'id': 'uuid2'} - ] - }, - } - - self.requests.register_uri('GET', self.url(1234, 'servers'), - json=get_1234_servers, - headers=self.headers) diff --git a/novaclient/tests/fixture_data/keypairs.py b/novaclient/tests/fixture_data/keypairs.py index 97cf24992..9017d1cff 100644 --- a/novaclient/tests/fixture_data/keypairs.py +++ b/novaclient/tests/fixture_data/keypairs.py @@ -45,8 +45,3 @@ class V1(base.Fixture): self.requests.register_uri('POST', self.url(), json=post_os_keypairs, headers=headers) - - -class V3(V1): - - base_url = 'keypairs' diff --git a/novaclient/tests/fixture_data/quotas.py b/novaclient/tests/fixture_data/quotas.py index 3250f8843..3972d20ff 100644 --- a/novaclient/tests/fixture_data/quotas.py +++ b/novaclient/tests/fixture_data/quotas.py @@ -64,21 +64,3 @@ class V1(base.Fixture): 'security_groups': 1, 'security_group_rules': 1 } - - -class V3(V1): - - def setUp(self): - super(V3, self).setUp() - - get_detail = { - 'quota_set': { - 'cores': {'reserved': 0, 'in_use': 0, 'limit': 10}, - 'instances': {'reserved': 0, 'in_use': 4, 'limit': 50}, - 'ram': {'reserved': 0, 'in_use': 1024, 'limit': 51200} - } - } - - self.requests.register_uri('GET', self.url('test', 'detail'), - json=get_detail, - headers=self.headers) diff --git a/novaclient/tests/fixture_data/servers.py b/novaclient/tests/fixture_data/servers.py index d316d4527..c728f2ff3 100644 --- a/novaclient/tests/fixture_data/servers.py +++ b/novaclient/tests/fixture_data/servers.py @@ -493,131 +493,3 @@ class V1(Base): else: raise AssertionError("Unexpected server action: %s" % action) return {'server': _body} - - -class V3(Base): - - def setUp(self): - super(V3, self).setUp() - - get_interfaces = { - "interface_attachments": [ - { - "port_state": "ACTIVE", - "net_id": "net-id-1", - "port_id": "port-id-1", - "mac_address": "aa:bb:cc:dd:ee:ff", - "fixed_ips": [{"ip_address": "1.2.3.4"}], - }, - { - "port_state": "ACTIVE", - "net_id": "net-id-1", - "port_id": "port-id-1", - "mac_address": "aa:bb:cc:dd:ee:ff", - "fixed_ips": [{"ip_address": "1.2.3.4"}], - } - ] - } - - self.requests.register_uri('GET', - self.url('1234', 'os-attach-interfaces'), - json=get_interfaces, - headers=self.json_headers) - - attach_body = {'interface_attachment': {}} - self.requests.register_uri('POST', - self.url('1234', 'os-attach-interfaces'), - json=attach_body, - headers=self.json_headers) - - self.requests.register_uri('GET', - self.url('1234', 'os-server-diagnostics'), - json=self.diagnostic) - - url = self.url('1234', 'os-attach-interfaces', 'port-id') - self.requests.register_uri('DELETE', url) - - self.requests.register_uri('GET', - self.url(1234, 'os-server-password'), - json={'password': ''}) - - def post_servers(self, request, context): - body = jsonutils.loads(request.body) - assert set(body.keys()) <= set(['server']) - fakes.assert_has_keys( - body['server'], - required=['name', 'image_ref', 'flavor_ref'], - optional=['metadata', 'personality', - 'os-scheduler-hints:scheduler_hints']) - if body['server']['name'] == 'some-bad-server': - body = self.server_1235 - else: - body = self.server_1234 - - context.status_code = 202 - return {'server': body} - - def post_servers_1234_action(self, request, context): - context.status_code = 202 - body_is_none_list = [ - 'revert_resize', 'migrate', 'stop', 'start', 'force_delete', - 'restore', 'pause', 'unpause', 'lock', 'unlock', 'unrescue', - 'resume', 'suspend', 'lock', 'unlock', 'shelve', 'shelve_offload', - 'unshelve', 'reset_network', 'rescue', 'confirm_resize'] - body_return_map = { - 'rescue': {'admin_password': 'RescuePassword'}, - 'get_console_output': {'output': 'foo'}, - 'rebuild': {'server': self.server_1234}, - } - body_param_check_exists = { - 'rebuild': 'image_ref', - 'resize': 'flavor_ref'} - body_params_check_exact = { - 'reboot': ['type'], - 'add_fixed_ip': ['network_id'], - 'evacuate': ['host', 'on_shared_storage'], - 'remove_fixed_ip': ['address'], - 'change_password': ['admin_password'], - 'get_console_output': ['length'], - 'get_vnc_console': ['type'], - 'get_spice_console': ['type'], - 'get_serial_console': ['type'], - 'reset_state': ['state'], - 'create_image': ['name', 'metadata'], - 'migrate_live': ['host', 'block_migration', 'disk_over_commit'], - 'create_backup': ['name', 'backup_type', 'rotation'], - 'attach': ['volume_id', 'device'], - 'detach': ['volume_id'], - 'swap_volume_attachment': ['old_volume_id', 'new_volume_id']} - - body = jsonutils.loads(request.body) - assert len(body.keys()) == 1 - action = list(body)[0] - _body = body_return_map.get(action, '') - - if action in body_is_none_list: - assert body[action] is None - - if action in body_param_check_exists: - assert body_param_check_exists[action] in body[action] - - if action == 'evacuate': - body[action].pop('admin_password', None) - - if action in body_params_check_exact: - assert set(body[action]) == set(body_params_check_exact[action]) - - if action == 'reboot': - assert body[action]['type'] in ['HARD', 'SOFT'] - elif action == 'confirm_resize': - # This one method returns a different response code - context.status_code = 204 - elif action == 'create_image': - context.headers['location'] = "http://blah/images/456" - - if action not in set.union(set(body_is_none_list), - set(body_params_check_exact.keys()), - set(body_param_check_exists.keys())): - raise AssertionError("Unexpected server action: %s" % action) - - return _body diff --git a/novaclient/tests/test_client.py b/novaclient/tests/test_client.py index 7322b66ac..e5a8db5f8 100644 --- a/novaclient/tests/test_client.py +++ b/novaclient/tests/test_client.py @@ -23,10 +23,8 @@ import requests import novaclient.client import novaclient.extension -import novaclient.tests.fakes as fakes from novaclient.tests import utils import novaclient.v1_1.client -import novaclient.v3.client class ClientConnectionPoolTest(utils.TestCase): @@ -140,7 +138,7 @@ class ClientTest(utils.TestCase): def test_get_client_class_v3(self): output = novaclient.client.get_client_class('3') - self.assertEqual(output, novaclient.v3.client.Client) + self.assertEqual(output, novaclient.v1_1.client.Client) def test_get_client_class_v2(self): output = novaclient.client.get_client_class('2') @@ -199,44 +197,6 @@ class ClientTest(utils.TestCase): cs.reset_timings() self.assertEqual(0, len(cs.get_timings())) - def test_client_set_management_url_v3(self): - cs = novaclient.v3.client.Client("user", "password", "project_id", - auth_url="foo/v2") - cs.set_management_url("blabla") - self.assertEqual("blabla", cs.client.management_url) - - def test_client_get_reset_timings_v3(self): - cs = novaclient.v3.client.Client("user", "password", "project_id", - auth_url="foo/v2") - self.assertEqual(0, len(cs.get_timings())) - cs.client.times.append("somevalue") - self.assertEqual(["somevalue"], cs.get_timings()) - - cs.reset_timings() - self.assertEqual(0, len(cs.get_timings())) - - def test_clent_extensions_v3(self): - fake_attribute_name1 = "FakeAttribute1" - fake_attribute_name2 = "FakeAttribute2" - extensions = [ - novaclient.extension.Extension(fake_attribute_name1, fakes), - novaclient.extension.Extension(fake_attribute_name2, utils), - ] - - cs = novaclient.v3.client.Client("user", "password", "project_id", - auth_url="foo/v2", - extensions=extensions) - self.assertIsInstance(getattr(cs, fake_attribute_name1, None), - fakes.FakeManager) - self.assertFalse(hasattr(cs, fake_attribute_name2)) - - @mock.patch.object(novaclient.client.HTTPClient, 'authenticate') - def test_authenticate_call_v3(self, mock_authenticate): - cs = novaclient.v3.client.Client("user", "password", "project_id", - auth_url="foo/v2") - cs.authenticate() - self.assertTrue(mock_authenticate.called) - @mock.patch('novaclient.client.HTTPClient') def test_contextmanager_v1_1(self, mock_http_client): fake_client = mock.Mock() @@ -247,16 +207,6 @@ class ClientTest(utils.TestCase): self.assertTrue(fake_client.open_session.called) self.assertTrue(fake_client.close_session.called) - @mock.patch('novaclient.client.HTTPClient') - def test_contextmanager_v3(self, mock_http_client): - fake_client = mock.Mock() - mock_http_client.return_value = fake_client - with novaclient.v3.client.Client("user", "password", "project_id", - auth_url="foo/v2"): - pass - self.assertTrue(fake_client.open_session.called) - self.assertTrue(fake_client.close_session.called) - def test_get_password_simple(self): cs = novaclient.client.HTTPClient("user", "password", "", "") cs.password_func = mock.Mock() diff --git a/novaclient/tests/v3/__init__.py b/novaclient/tests/v3/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/novaclient/tests/v3/fakes.py b/novaclient/tests/v3/fakes.py deleted file mode 100644 index f0c57f76b..000000000 --- a/novaclient/tests/v3/fakes.py +++ /dev/null @@ -1,388 +0,0 @@ -# Copyright (c) 2011 X.commerce, a business unit of eBay Inc. -# Copyright 2011 OpenStack Foundation -# Copyright 2013 IBM Corp. -# -# 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 oslo.utils import strutils - -from novaclient.tests import fakes -from novaclient.tests.v1_1 import fakes as fakes_v1_1 -from novaclient.v3 import client - - -class FakeClient(fakes.FakeClient, client.Client): - - def __init__(self, *args, **kwargs): - client.Client.__init__(self, 'username', 'password', - 'project_id', 'auth_url', - extensions=kwargs.get('extensions')) - self.client = FakeHTTPClient(**kwargs) - - -class FakeHTTPClient(fakes_v1_1.FakeHTTPClient): - # - # Hosts - # - def put_os_hosts_sample_host_1(self, body, **kw): - return (200, {}, {'host': {'host': 'sample-host_1', - 'status': 'enabled'}}) - - def put_os_hosts_sample_host_2(self, body, **kw): - return (200, {}, {'host': {'host': 'sample-host_2', - 'maintenance_mode': 'on_maintenance'}}) - - def put_os_hosts_sample_host_3(self, body, **kw): - return (200, {}, {'host': {'host': 'sample-host_3', - 'status': 'enabled', - 'maintenance_mode': 'on_maintenance'}}) - - def get_os_hosts_sample_host_reboot(self, **kw): - return (200, {}, {'host': {'host': 'sample_host', - 'power_action': 'reboot'}}) - - def get_os_hosts_sample_host_startup(self, **kw): - return (200, {}, {'host': {'host': 'sample_host', - 'power_action': 'startup'}}) - - def get_os_hosts_sample_host_shutdown(self, **kw): - return (200, {}, {'host': {'host': 'sample_host', - 'power_action': 'shutdown'}}) - - # - # Flavors - # - post_flavors_1_flavor_extra_specs = ( - fakes_v1_1.FakeHTTPClient.post_flavors_1_os_extra_specs) - - post_flavors_4_flavor_extra_specs = ( - fakes_v1_1.FakeHTTPClient.post_flavors_4_os_extra_specs) - - delete_flavors_1_flavor_extra_specs_k1 = ( - fakes_v1_1.FakeHTTPClient.delete_flavors_1_os_extra_specs_k1) - - def get_flavors_detail(self, **kw): - flavors = {'flavors': [ - {'id': 1, 'name': '256 MB Server', 'ram': 256, 'disk': 10, - 'ephemeral': 10, - 'flavor-access:is_public': True, - 'links': {}}, - {'id': 2, 'name': '512 MB Server', 'ram': 512, 'disk': 20, - 'ephemeral': 20, - 'flavor-access:is_public': False, - 'links': {}}, - {'id': 4, 'name': '1024 MB Server', 'ram': 1024, 'disk': 10, - 'ephemeral': 10, - 'flavor-access:is_public': True, - 'links': {}}, - {'id': 'aa1', 'name': '128 MB Server', 'ram': 128, 'disk': 0, - 'ephemeral': 0, - 'flavor-access:is_public': True, - 'links': {}} - ]} - - if 'is_public' not in kw: - filter_is_public = True - else: - if kw['is_public'].lower() == 'none': - filter_is_public = None - else: - filter_is_public = strutils.bool_from_string(kw['is_public'], - True) - - if filter_is_public is not None: - if filter_is_public: - flavors['flavors'] = [ - v for v in flavors['flavors'] - if v['flavor-access:is_public'] - ] - else: - flavors['flavors'] = [ - v for v in flavors['flavors'] - if not v['flavor-access:is_public'] - ] - - return (200, {}, flavors) - - # - # Flavor access - # - get_flavors_2_flavor_access = ( - fakes_v1_1.FakeHTTPClient.get_flavors_2_os_flavor_access) - get_flavors_2_flavor_extra_specs = ( - fakes_v1_1.FakeHTTPClient.get_flavors_2_os_extra_specs) - get_flavors_aa1_flavor_extra_specs = ( - fakes_v1_1.FakeHTTPClient.get_flavors_aa1_os_extra_specs) - - # - # Images - # - get_v1_images_detail = fakes_v1_1.FakeHTTPClient.get_images_detail - get_v1_images = fakes_v1_1.FakeHTTPClient.get_images - - def head_v1_images_1(self, **kw): - headers = { - 'x-image-meta-id': '1', - 'x-image-meta-name': 'CentOS 5.2', - 'x-image-meta-updated': '2010-10-10T12:00:00Z', - 'x-image-meta-created': '2010-10-10T12:00:00Z', - 'x-image-meta-status': 'ACTIVE', - 'x-image-meta-property-test_key': 'test_value'} - return 200, headers, '' - - # - # Servers - # - get_servers_1234_os_server_diagnostics = ( - fakes_v1_1.FakeHTTPClient.get_servers_1234_diagnostics) - - delete_servers_1234_os_attach_interfaces_port_id = ( - fakes_v1_1.FakeHTTPClient.delete_servers_1234_os_interface_port_id) - - def get_servers_1234_os_attach_interfaces(self, **kw): - return (200, {}, { - "interface_attachments": [ - {"port_state": "ACTIVE", - "net_id": "net-id-1", - "port_id": "port-id-1", - "mac_address": "aa:bb:cc:dd:ee:ff", - "fixed_ips": [{"ip_address": "1.2.3.4"}], - }, - {"port_state": "ACTIVE", - "net_id": "net-id-1", - "port_id": "port-id-1", - "mac_address": "aa:bb:cc:dd:ee:ff", - "fixed_ips": [{"ip_address": "1.2.3.4"}]}] - }) - - def post_servers_1234_os_attach_interfaces(self, **kw): - return (200, {}, {'interface_attachment': {}}) - - def post_servers(self, body, **kw): - assert set(body.keys()) <= set(['server']) - fakes.assert_has_keys(body['server'], - required=['name', 'image_ref', 'flavor_ref'], - optional=['metadata', 'personality', - 'os-scheduler-hints:scheduler_hints']) - if body['server']['name'] == 'some-bad-server': - return (202, {}, self.get_servers_1235()[2]) - else: - return (202, {}, self.get_servers_1234()[2]) - - # - # Server Actions - # - def post_servers_1234_action(self, body, **kw): - _headers = None - resp = 202 - body_is_none_list = [ - 'revert_resize', 'migrate', 'stop', 'start', 'force_delete', - 'restore', 'pause', 'unpause', 'lock', 'unlock', 'unrescue', - 'resume', 'suspend', 'lock', 'unlock', 'shelve', 'shelve_offload', - 'unshelve', 'reset_network', 'rescue', 'confirm_resize'] - body_return_map = { - 'rescue': {'admin_password': 'RescuePassword'}, - 'get_console_output': {'output': 'foo'}, - 'rebuild': self.get_servers_1234()[2], - } - body_param_check_exists = { - 'rebuild': 'image_ref', - 'resize': 'flavor_ref', - 'evacuate': 'on_shared_storage'} - body_params_check_exact = { - 'reboot': ['type'], - 'add_fixed_ip': ['network_id'], - 'remove_fixed_ip': ['address'], - 'change_password': ['admin_password'], - 'get_console_output': ['length'], - 'get_vnc_console': ['type'], - 'get_spice_console': ['type'], - 'reset_state': ['state'], - 'create_image': ['name', 'metadata'], - 'migrate_live': ['host', 'block_migration', 'disk_over_commit'], - 'create_backup': ['name', 'backup_type', 'rotation'], - 'detach': ['volume_id'], - 'swap_volume_attachment': ['old_volume_id', 'new_volume_id']} - body_params_check_superset = { - 'attach': ['volume_id', 'device']} - - assert len(body.keys()) == 1 - action = list(body)[0] - _body = body_return_map.get(action) - - if action in body_is_none_list: - assert body[action] is None - - if action in body_param_check_exists: - assert body_param_check_exists[action] in body[action] - - if action in body_params_check_exact: - assert set(body[action]) == set(body_params_check_exact[action]) - - if action in body_params_check_superset: - assert set(body[action]) >= set(body_params_check_superset[action]) - - if action == 'reboot': - assert body[action]['type'] in ['HARD', 'SOFT'] - elif action == 'confirm_resize': - # This one method returns a different response code - resp = 204 - elif action == 'create_image': - _headers = dict(location="http://blah/images/456") - - if action not in set.union(set(body_is_none_list), - set(body_params_check_exact.keys()), - set(body_param_check_exists.keys()), - set(body_params_check_superset.keys())): - raise AssertionError("Unexpected server action: %s" % action) - - return (resp, _headers, _body) - - # - # Server password - # - - def get_servers_1234_os_server_password(self, **kw): - return (200, {}, {'password': ''}) - - def delete_servers_1234_os_server_password(self, **kw): - return (202, {}, None) - - # - # Availability Zones - # - def get_os_availability_zone(self, **kw): - return (200, {}, { - "availability_zone_info": [ - {"zone_name": "zone-1", "zone_state": {"available": True}, - "hosts": None}, - {"zone_name": "zone-2", "zone_state": {"available": False}, - "hosts": None}]}) - - def get_os_availability_zone_detail(self, **kw): - return (200, {}, { - "availability_zone_info": [ - {"zone_name": "zone-1", - "zone_state": {"available": True}, - "hosts": { - "fake_host-1": { - "nova-compute": { - "active": True, - "available": True, - "updated_at": datetime.datetime( - 2012, 12, 26, 14, 45, 25, 0)}}}}, - {"zone_name": "internal", - "zone_state": { - "available": True}, - "hosts": { - "fake_host-1": { - "nova-sched": { - "active": True, - "available": True, - "updated_at": datetime.datetime( - 2012, 12, 26, 14, 45, 25, 0)}}, - "fake_host-2": { - "nova-network": { - "active": True, - "available": False, - "updated_at": datetime.datetime( - 2012, 12, 26, 14, 45, 24, 0)}}}}, - {"zone_name": "zone-2", - "zone_state": {"available": False}, - "hosts": None}]}) - - # - # Quotas - # - def put_os_quota_sets_97f4c221bff44578b0300df4ef119353(self, body, **kw): - assert list(body) == ['quota_set'] - return (200, {}, { - 'quota_set': { - 'tenant_id': '97f4c221bff44578b0300df4ef119353', - 'metadata_items': [], - 'injected_file_content_bytes': 1, - 'injected_file_path_bytes': 1, - 'ram': 1, - 'floating_ips': 1, - 'instances': 1, - 'injected_files': 1, - 'cores': 1, - 'keypairs': 1, - 'security_groups': 1, - 'security_group_rules': 1, - 'server_groups': 1, - 'server_group_members': 1}}) - - def get_os_quota_sets_test_detail(self, **kw): - return (200, {}, {'quota_set': { - 'cores': {'reserved': 0, 'in_use': 0, 'limit': 10}, - 'instances': {'reserved': 0, 'in_use': 4, 'limit': 50}, - 'ram': {'reserved': 0, 'in_use': 1024, 'limit': 51200}}}) - - # - # Hypervisors - # - def get_os_hypervisors_search(self, **kw): - if kw['query'] == 'hyper1': - return (200, {}, {'hypervisors': [ - {'id': 1234, 'hypervisor_hostname': 'hyper1'}]}) - return (200, {}, { - 'hypervisors': [ - {'id': 1234, 'hypervisor_hostname': 'hyper1'}, - {'id': 5678, 'hypervisor_hostname': 'hyper2'}]}) - - def get_os_hypervisors_1234_servers(self, **kw): - return (200, {}, { - 'hypervisor': - {'id': 1234, 'hypervisor_hostname': 'hyper1', - 'servers': [ - {'name': 'inst1', 'id': 'uuid1'}, - {'name': 'inst2', 'id': 'uuid2'}]}}) - - # - # Keypairs - # - get_keypairs_test = fakes_v1_1.FakeHTTPClient.get_os_keypairs_test - get_keypairs = fakes_v1_1.FakeHTTPClient.get_os_keypairs - delete_keypairs_test = fakes_v1_1.FakeHTTPClient.delete_os_keypairs_test - post_keypairs = fakes_v1_1.FakeHTTPClient.post_os_keypairs - - # - # List all extensions - # - def get_extensions(self, **kw): - exts = [ - { - "alias": "os-multinic", - "description": "Multiple network support", - "name": "Multinic", - "version": 1, - }, - { - "alias": "os-extended-server-attributes", - "description": "Extended Server Attributes support.", - "name": "ExtendedServerAttributes", - "version": 1, - }, - { - "alias": "os-extended-status", - "description": "Extended Status support", - "name": "ExtendedStatus", - "version": 1, - }, - ] - return (200, {}, { - "extensions": exts, - }) diff --git a/novaclient/tests/v3/test_agents.py b/novaclient/tests/v3/test_agents.py deleted file mode 100644 index bdd101bdc..000000000 --- a/novaclient/tests/v3/test_agents.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2012 IBM Corp. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from novaclient.tests.fixture_data import client -from novaclient.tests.v1_1 import test_agents - - -class AgentsTest(test_agents.AgentsTest): - - scenarios = [('original', {'client_fixture_class': client.V3}), - ('session', {'client_fixture_class': client.SessionV3})] - - def _build_example_update_body(self): - return {"agent": { - "url": "/yyy/yyyy/yyyy", - "version": "8.0", - "md5hash": "add6bb58e139be103324d04d82d8f546"}} diff --git a/novaclient/tests/v3/test_aggregates.py b/novaclient/tests/v3/test_aggregates.py deleted file mode 100644 index 17e27d162..000000000 --- a/novaclient/tests/v3/test_aggregates.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2013 IBM Corp. -# -# 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 novaclient.tests.fixture_data import client -from novaclient.tests.v1_1 import test_aggregates - - -class AggregatesTest(test_aggregates.AggregatesTest): - - scenarios = [('original', {'client_fixture_class': client.V3}), - ('session', {'client_fixture_class': client.SessionV3})] diff --git a/novaclient/tests/v3/test_availability_zone.py b/novaclient/tests/v3/test_availability_zone.py deleted file mode 100644 index 67a4415bf..000000000 --- a/novaclient/tests/v3/test_availability_zone.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# Copyright 2013 IBM Corp. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from novaclient.tests.fixture_data import availability_zones as data -from novaclient.tests.fixture_data import client -from novaclient.tests.v1_1 import test_availability_zone -from novaclient.v3 import availability_zones - - -class AvailabilityZoneTest(test_availability_zone.AvailabilityZoneTest): - from novaclient.v3 import shell # noqa - - data_fixture_class = data.V3 - - scenarios = [('original', {'client_fixture_class': client.V3}), - ('session', {'client_fixture_class': client.SessionV3})] - - def _assertZone(self, zone, name, status): - self.assertEqual(zone.zone_name, name) - self.assertEqual(zone.zone_state, status) - - def _get_availability_zone_type(self): - return availability_zones.AvailabilityZone diff --git a/novaclient/tests/v3/test_certs.py b/novaclient/tests/v3/test_certs.py deleted file mode 100644 index 30597fc10..000000000 --- a/novaclient/tests/v3/test_certs.py +++ /dev/null @@ -1,21 +0,0 @@ -# -# 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 novaclient.tests.fixture_data import client -from novaclient.tests.v1_1 import test_certs - - -class CertsTest(test_certs.CertsTest): - - scenarios = [('original', {'client_fixture_class': client.V3}), - ('session', {'client_fixture_class': client.SessionV3})] diff --git a/novaclient/tests/v3/test_flavor_access.py b/novaclient/tests/v3/test_flavor_access.py deleted file mode 100644 index cd5914012..000000000 --- a/novaclient/tests/v3/test_flavor_access.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# Copyright 2013 IBM Corp. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from novaclient.tests import utils -from novaclient.tests.v3 import fakes -from novaclient.v3 import flavor_access - - -cs = fakes.FakeClient() - - -class FlavorAccessTest(utils.TestCase): - - def test_list_access_by_flavor_private(self): - kwargs = {'flavor': cs.flavors.get(2)} - r = cs.flavor_access.list(**kwargs) - cs.assert_called('GET', '/flavors/2/flavor-access') - for access_list in r: - self.assertIsInstance(access_list, - flavor_access.FlavorAccess) - - def test_add_tenant_access(self): - flavor = cs.flavors.get(2) - tenant = 'proj2' - r = cs.flavor_access.add_tenant_access(flavor, tenant) - - body = { - "add_tenant_access": { - "tenant_id": "proj2" - } - } - - cs.assert_called('POST', '/flavors/2/action', body) - for a in r: - self.assertIsInstance(a, flavor_access.FlavorAccess) - - def test_remove_tenant_access(self): - flavor = cs.flavors.get(2) - tenant = 'proj2' - r = cs.flavor_access.remove_tenant_access(flavor, tenant) - - body = { - "remove_tenant_access": { - "tenant_id": "proj2" - } - } - - cs.assert_called('POST', '/flavors/2/action', body) - for a in r: - self.assertIsInstance(a, flavor_access.FlavorAccess) diff --git a/novaclient/tests/v3/test_flavors.py b/novaclient/tests/v3/test_flavors.py deleted file mode 100644 index 03dee061d..000000000 --- a/novaclient/tests/v3/test_flavors.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) 2013, OpenStack -# Copyright 2013 IBM Corp. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import mock - -from novaclient.tests.v1_1 import test_flavors -from novaclient.tests.v3 import fakes -from novaclient.v3 import flavors - - -class FlavorsTest(test_flavors.FlavorsTest): - def _get_fake_client(self): - return fakes.FakeClient() - - def _get_flavor_type(self): - return flavors.Flavor - - def _create_body(self, name, ram, vcpus, disk, ephemeral, id, swap, - rxtx_factor, is_public): - return { - "flavor": { - "name": name, - "ram": ram, - "vcpus": vcpus, - "disk": disk, - "ephemeral": ephemeral, - "id": id, - "swap": swap, - "os-flavor-rxtx:rxtx_factor": rxtx_factor, - "flavor-access:is_public": is_public, - } - } - - def test_set_keys(self): - f = self.cs.flavors.get(1) - f.set_keys({'k1': 'v1'}) - self.cs.assert_called('POST', '/flavors/1/flavor-extra-specs', - {"extra_specs": {'k1': 'v1'}}) - - def test_set_with_valid_keys(self): - valid_keys = ['key4', 'month.price', 'I-Am:AK-ey.44-', - 'key with spaces and _'] - - f = self.cs.flavors.get(4) - for key in valid_keys: - f.set_keys({key: 'v4'}) - self.cs.assert_called('POST', '/flavors/4/flavor-extra-specs', - {"extra_specs": {key: 'v4'}}) - - @mock.patch.object(flavors.FlavorManager, '_delete') - def test_unset_keys(self, mock_delete): - f = self.cs.flavors.get(1) - keys = ['k1', 'k2'] - f.unset_keys(keys) - mock_delete.assert_has_calls([ - mock.call("/flavors/1/flavor-extra-specs/k1"), - mock.call("/flavors/1/flavor-extra-specs/k2") - ]) - - def test_get_flavor_details_diablo(self): - # Don't need for V3 API to work against diablo - pass diff --git a/novaclient/tests/v3/test_hosts.py b/novaclient/tests/v3/test_hosts.py deleted file mode 100644 index 56579330a..000000000 --- a/novaclient/tests/v3/test_hosts.py +++ /dev/null @@ -1,99 +0,0 @@ -# Copyright 2013 OpenStack Foundation -# -# 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 novaclient.tests.fixture_data import client -from novaclient.tests.fixture_data import hosts as data -from novaclient.tests import utils -from novaclient.v3 import hosts - - -class HostsTest(utils.FixturedTestCase): - - client_fixture_class = client.V3 - data_fixture_class = data.V3 - - def test_describe_resource(self): - hs = self.cs.hosts.get('host') - self.assert_called('GET', '/os-hosts/host') - for h in hs: - self.assertIsInstance(h, hosts.Host) - - def test_list_host(self): - hs = self.cs.hosts.list() - self.assert_called('GET', '/os-hosts') - for h in hs: - self.assertIsInstance(h, hosts.Host) - self.assertEqual('nova1', h.zone) - - def test_list_host_with_zone(self): - hs = self.cs.hosts.list('nova') - self.assert_called('GET', '/os-hosts?zone=nova') - for h in hs: - self.assertIsInstance(h, hosts.Host) - self.assertEqual('nova', h.zone) - - def test_list_host_with_service(self): - hs = self.cs.hosts.list(service='nova-compute') - self.assert_called('GET', '/os-hosts?service=nova-compute') - for h in hs: - self.assertIsInstance(h, hosts.Host) - self.assertEqual(h.service, 'nova-compute') - - def test_list_host_with_zone_and_service(self): - hs = self.cs.hosts.list(service='nova-compute', zone='nova') - self.assert_called('GET', '/os-hosts?zone=nova&service=nova-compute') - for h in hs: - self.assertIsInstance(h, hosts.Host) - self.assertEqual(h.zone, 'nova') - self.assertEqual(h.service, 'nova-compute') - - def test_update_enable(self): - host = self.cs.hosts.get('sample_host')[0] - values = {"status": "enabled"} - result = host.update(values) - self.assert_called('PUT', '/os-hosts/sample_host', {"host": values}) - self.assertIsInstance(result, hosts.Host) - - def test_update_maintenance(self): - host = self.cs.hosts.get('sample_host')[0] - values = {"maintenance_mode": "enable"} - result = host.update(values) - self.assert_called('PUT', '/os-hosts/sample_host', {"host": values}) - self.assertIsInstance(result, hosts.Host) - - def test_update_both(self): - host = self.cs.hosts.get('sample_host')[0] - values = {"status": "enabled", - "maintenance_mode": "enable"} - result = host.update(values) - self.assert_called('PUT', '/os-hosts/sample_host', {"host": values}) - self.assertIsInstance(result, hosts.Host) - - def test_host_startup(self): - host = self.cs.hosts.get('sample_host')[0] - host.startup() - self.assert_called( - 'GET', '/os-hosts/sample_host/startup') - - def test_host_reboot(self): - host = self.cs.hosts.get('sample_host')[0] - host.reboot() - self.assert_called( - 'GET', '/os-hosts/sample_host/reboot') - - def test_host_shutdown(self): - host = self.cs.hosts.get('sample_host')[0] - host.shutdown() - self.assert_called( - 'GET', '/os-hosts/sample_host/shutdown') diff --git a/novaclient/tests/v3/test_hypervisors.py b/novaclient/tests/v3/test_hypervisors.py deleted file mode 100644 index 94ea3fad4..000000000 --- a/novaclient/tests/v3/test_hypervisors.py +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from novaclient.tests.fixture_data import client -from novaclient.tests.fixture_data import hypervisors as data -from novaclient.tests.v1_1 import test_hypervisors - - -class HypervisorsTest(test_hypervisors.HypervisorsTest): - - client_fixture_class = client.V3 - data_fixture_class = data.V3 - - def test_hypervisor_search(self): - expected = [ - dict(id=1234, hypervisor_hostname='hyper1'), - dict(id=5678, hypervisor_hostname='hyper2')] - - result = self.cs.hypervisors.search('hyper') - self.assert_called('GET', '/os-hypervisors/search?query=hyper') - - for idx, hyper in enumerate(result): - self.compare_to_expected(expected[idx], hyper) - - def test_hypervisor_servers(self): - expected = dict(id=1234, - hypervisor_hostname='hyper1', - servers=[ - dict(name='inst1', id='uuid1'), - dict(name='inst2', id='uuid2')]) - - result = self.cs.hypervisors.servers('1234') - self.assert_called('GET', '/os-hypervisors/1234/servers') - - self.compare_to_expected(expected, result) diff --git a/novaclient/tests/v3/test_images.py b/novaclient/tests/v3/test_images.py deleted file mode 100644 index be8260d8f..000000000 --- a/novaclient/tests/v3/test_images.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright 2013 IBM Corp. -# -# 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 novaclient.tests.fixture_data import client -from novaclient.tests.fixture_data import images as data -from novaclient.tests import utils -from novaclient.v3 import images - - -class ImagesTest(utils.FixturedTestCase): - - client_fixture_class = client.V3 - data_fixture_class = data.V3 - - def test_list_images(self): - il = self.cs.images.list() - self.assert_called('GET', '/v1/images/detail') - for i in il: - self.assertIsInstance(i, images.Image) - - def test_list_images_undetailed(self): - il = self.cs.images.list(detailed=False) - self.assert_called('GET', '/v1/images') - for i in il: - self.assertIsInstance(i, images.Image) - - def test_list_images_with_limit(self): - self.cs.images.list(limit=4) - self.assert_called('GET', '/v1/images/detail?limit=4') - - def test_get_image_details(self): - i = self.cs.images.get(1) - self.assert_called('HEAD', '/v1/images/1') - self.assertIsInstance(i, images.Image) - self.assertEqual('1', i.id) - self.assertEqual('CentOS 5.2', i.name) - - def test_find(self): - i = self.cs.images.find(name="CentOS 5.2") - self.assertEqual('1', i.id) - self.assert_called('HEAD', '/v1/images/1') - - iml = self.cs.images.findall(status='SAVING') - self.assertEqual(1, len(iml)) - self.assertEqual('My Server Backup', iml[0].name) diff --git a/novaclient/tests/v3/test_keypairs.py b/novaclient/tests/v3/test_keypairs.py deleted file mode 100644 index 157042fce..000000000 --- a/novaclient/tests/v3/test_keypairs.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2013 IBM Corp. -# -# 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 novaclient.tests.fixture_data import client -from novaclient.tests.fixture_data import keypairs as data -from novaclient.tests.v1_1 import test_keypairs -from novaclient.v3 import keypairs - - -class KeypairsTest(test_keypairs.KeypairsTest): - - client_fixture_class = client.V3 - data_fixture_class = data.V3 - - def _get_keypair_type(self): - return keypairs.Keypair - - def _get_keypair_prefix(self): - return keypairs.KeypairManager.keypair_prefix diff --git a/novaclient/tests/v3/test_limits.py b/novaclient/tests/v3/test_limits.py deleted file mode 100644 index 51b575269..000000000 --- a/novaclient/tests/v3/test_limits.py +++ /dev/null @@ -1,88 +0,0 @@ -# -# 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 novaclient.tests.fixture_data import client -from novaclient.tests.fixture_data import limits as data -from novaclient.tests import utils -from novaclient.v3 import limits - - -class LimitsTest(utils.FixturedTestCase): - - client_fixture_class = client.V3 - data_fixture_class = data.Fixture - - def test_get_limits(self): - obj = self.cs.limits.get() - self.assert_called('GET', '/limits') - self.assertIsInstance(obj, limits.Limits) - - def test_get_limits_for_a_tenant(self): - obj = self.cs.limits.get(tenant_id=1234) - self.assert_called('GET', '/limits?tenant_id=1234') - self.assertIsInstance(obj, limits.Limits) - - def test_absolute_limits(self): - obj = self.cs.limits.get() - - expected = ( - limits.AbsoluteLimit("maxTotalRAMSize", 51200), - limits.AbsoluteLimit("maxServerMeta", 5), - limits.AbsoluteLimit("maxImageMeta", 5), - limits.AbsoluteLimit("maxPersonality", 5), - limits.AbsoluteLimit("maxPersonalitySize", 10240), - ) - - abs_limits = list(obj.absolute) - self.assertEqual(len(abs_limits), len(expected)) - - for limit in abs_limits: - self.assertTrue(limit in expected) - - def test_absolute_limits_reserved(self): - obj = self.cs.limits.get(reserved=True) - - expected = ( - limits.AbsoluteLimit("maxTotalRAMSize", 51200), - limits.AbsoluteLimit("maxServerMeta", 5), - limits.AbsoluteLimit("maxImageMeta", 5), - limits.AbsoluteLimit("maxPersonality", 5), - limits.AbsoluteLimit("maxPersonalitySize", 10240), - ) - - self.assert_called('GET', '/limits?reserved=1') - abs_limits = list(obj.absolute) - self.assertEqual(len(abs_limits), len(expected)) - - for limit in abs_limits: - self.assertTrue(limit in expected) - - def test_rate_limits(self): - obj = self.cs.limits.get() - - expected = ( - limits.RateLimit('POST', '*', '.*', 10, 2, 'MINUTE', - '2011-12-15T22:42:45Z'), - limits.RateLimit('PUT', '*', '.*', 10, 2, 'MINUTE', - '2011-12-15T22:42:45Z'), - limits.RateLimit('DELETE', '*', '.*', 100, 100, 'MINUTE', - '2011-12-15T22:42:45Z'), - limits.RateLimit('POST', '*/servers', '^/servers', 25, 24, 'DAY', - '2011-12-15T22:42:45Z'), - ) - - rate_limits = list(obj.rate) - self.assertEqual(len(rate_limits), len(expected)) - - for limit in rate_limits: - self.assertTrue(limit in expected) diff --git a/novaclient/tests/v3/test_list_extensions.py b/novaclient/tests/v3/test_list_extensions.py deleted file mode 100644 index 61d387c3c..000000000 --- a/novaclient/tests/v3/test_list_extensions.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright 2014 NEC Corporation. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from novaclient import extension -from novaclient.tests import utils -from novaclient.tests.v3 import fakes -from novaclient.v3 import list_extensions - - -extensions = [ - extension.Extension("list_extensions", list_extensions), -] -cs = fakes.FakeClient(extensions=extensions) - - -class ListExtensionsTests(utils.TestCase): - def test_list_extensions(self): - all_exts = cs.list_extensions.show_all() - cs.assert_called('GET', '/extensions') - self.assertTrue(len(all_exts) > 0) - for r in all_exts: - self.assertTrue(len(r.summary) > 0) diff --git a/novaclient/tests/v3/test_quotas.py b/novaclient/tests/v3/test_quotas.py deleted file mode 100644 index 53ca3258e..000000000 --- a/novaclient/tests/v3/test_quotas.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright IBM Corp. 2013 -# -# 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 novaclient.tests.fixture_data import client -from novaclient.tests.fixture_data import quotas as data -from novaclient.tests.v1_1 import test_quotas - - -class QuotaSetsTest(test_quotas.QuotaSetsTest): - - client_fixture_class = client.V3 - data_fixture_class = data.V3 - - def test_force_update_quota(self): - q = self.cs.quotas.get('97f4c221bff44578b0300df4ef119353') - q.update(cores=2, force=True) - self.assert_called( - 'PUT', '/os-quota-sets/97f4c221bff44578b0300df4ef119353', - {'quota_set': {'force': True, - 'cores': 2}}) - - def test_tenant_quotas_get_detail(self): - tenant_id = 'test' - self.cs.quotas.get(tenant_id, detail=True) - self.assert_called('GET', '/os-quota-sets/%s/detail' % tenant_id) - - def test_user_quotas_get_detail(self): - tenant_id = 'test' - user_id = 'fake_user' - self.cs.quotas.get(tenant_id, user_id=user_id, detail=True) - url = '/os-quota-sets/%s/detail?user_id=%s' % (tenant_id, user_id) - self.assert_called('GET', url) diff --git a/novaclient/tests/v3/test_servers.py b/novaclient/tests/v3/test_servers.py deleted file mode 100644 index c9357b565..000000000 --- a/novaclient/tests/v3/test_servers.py +++ /dev/null @@ -1,478 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2013 IBM Corp. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import mock -import six - -from novaclient import exceptions -from novaclient.tests.fixture_data import client -from novaclient.tests.fixture_data import servers as data -from novaclient.tests import utils -from novaclient.v3 import servers - - -class ServersTest(utils.FixturedTestCase): - - client_fixture_class = client.V3 - data_fixture_class = data.V3 - - def test_list_servers(self): - sl = self.cs.servers.list() - self.assert_called('GET', '/servers/detail') - for s in sl: - self.assertIsInstance(s, servers.Server) - - def test_list_servers_undetailed(self): - sl = self.cs.servers.list(detailed=False) - self.assert_called('GET', '/servers') - for s in sl: - self.assertIsInstance(s, servers.Server) - - def test_list_servers_with_marker_limit(self): - sl = self.cs.servers.list(marker=1234, limit=2) - self.assert_called('GET', '/servers/detail?limit=2&marker=1234') - for s in sl: - self.assertIsInstance(s, servers.Server) - - def test_get_server_details(self): - s = self.cs.servers.get(1234) - self.assert_called('GET', '/servers/1234') - self.assertIsInstance(s, servers.Server) - self.assertEqual(1234, s.id) - self.assertEqual('BUILD', s.status) - - def test_get_server_promote_details(self): - s1 = self.cs.servers.list(detailed=False)[0] - s2 = self.cs.servers.list(detailed=True)[0] - self.assertNotEqual(s1._info, s2._info) - s1.get() - self.assertEqual(s1._info, s2._info) - - def test_create_server(self): - s = self.cs.servers.create( - name="My server", - image=1, - flavor=1, - meta={'foo': 'bar'}, - userdata="hello moto", - key_name="fakekey", - files={ - '/etc/passwd': 'some data', # a file - '/tmp/foo.txt': six.StringIO('data'), # a stream - } - ) - self.assert_called('POST', '/servers') - self.assertIsInstance(s, servers.Server) - - def test_create_server_boot_with_nics_ipv4(self): - old_boot = self.cs.servers._boot - nics = [{'net-id': '11111111-1111-1111-1111-111111111111', - 'v4-fixed-ip': '10.10.0.7'}] - - def wrapped_boot(url, key, *boot_args, **boot_kwargs): - self.assertEqual(boot_kwargs['nics'], nics) - return old_boot(url, key, *boot_args, **boot_kwargs) - - with mock.patch.object(self.cs.servers, '_boot', wrapped_boot): - s = self.cs.servers.create( - name="My server", - image=1, - flavor=1, - meta={'foo': 'bar'}, - userdata="hello moto", - key_name="fakekey", - nics=nics - ) - self.assert_called('POST', '/servers') - self.assertIsInstance(s, servers.Server) - - def test_create_server_boot_with_nics_ipv6(self): - old_boot = self.cs.servers._boot - nics = [{'net-id': '11111111-1111-1111-1111-111111111111', - 'v6-fixed-ip': '2001:db9:0:1::10'}] - - def wrapped_boot(url, key, *boot_args, **boot_kwargs): - self.assertEqual(nics, boot_kwargs['nics']) - return old_boot(url, key, *boot_args, **boot_kwargs) - - with mock.patch.object(self.cs.servers, '_boot', wrapped_boot): - s = self.cs.servers.create( - name="My server", - image=1, - flavor=1, - meta={'foo': 'bar'}, - userdata="hello moto", - key_name="fakekey", - nics=nics - ) - self.assert_called('POST', '/servers') - self.assertIsInstance(s, servers.Server) - - def test_create_server_userdata_file_object(self): - s = self.cs.servers.create( - name="My server", - image=1, - flavor=1, - meta={'foo': 'bar'}, - userdata=six.StringIO('hello moto'), - files={ - '/etc/passwd': 'some data', # a file - '/tmp/foo.txt': six.StringIO('data'), # a stream - }, - ) - self.assert_called('POST', '/servers') - self.assertIsInstance(s, servers.Server) - - def test_create_server_userdata_unicode(self): - s = self.cs.servers.create( - name="My server", - image=1, - flavor=1, - meta={'foo': 'bar'}, - userdata=six.u('こんにちは'), - key_name="fakekey", - files={ - '/etc/passwd': 'some data', # a file - '/tmp/foo.txt': six.StringIO('data'), # a stream - }, - ) - self.assert_called('POST', '/servers') - self.assertIsInstance(s, servers.Server) - - def test_create_server_userdata_utf8(self): - s = self.cs.servers.create( - name="My server", - image=1, - flavor=1, - meta={'foo': 'bar'}, - userdata='こんにちは', - key_name="fakekey", - files={ - '/etc/passwd': 'some data', # a file - '/tmp/foo.txt': six.StringIO('data'), # a stream - }, - ) - self.assert_called('POST', '/servers') - self.assertIsInstance(s, servers.Server) - - def test_create_server_return_reservation_id(self): - s = self.cs.servers.create( - name="My server", - image=1, - flavor=1, - reservation_id=True - ) - expected_body = { - 'server': { - 'name': 'My server', - 'image_ref': '1', - 'flavor_ref': '1', - 'os-multiple-create:min_count': 1, - 'os-multiple-create:max_count': 1, - 'os-multiple-create:return_reservation_id': True, - } - } - self.assert_called('POST', '/servers', expected_body) - self.assertIsInstance(s, servers.Server) - - def test_update_server(self): - s = self.cs.servers.get(1234) - - # Update via instance - s.update(name='hi') - self.assert_called('PUT', '/servers/1234') - s.update(name='hi') - self.assert_called('PUT', '/servers/1234') - - # Silly, but not an error - s.update() - - # Update via manager - self.cs.servers.update(s, name='hi') - self.assert_called('PUT', '/servers/1234') - - def test_delete_server(self): - s = self.cs.servers.get(1234) - s.delete() - self.assert_called('DELETE', '/servers/1234') - self.cs.servers.delete(1234) - self.assert_called('DELETE', '/servers/1234') - self.cs.servers.delete(s) - self.assert_called('DELETE', '/servers/1234') - - def test_delete_server_meta(self): - self.cs.servers.delete_meta(1234, ['test_key']) - self.assert_called('DELETE', '/servers/1234/metadata/test_key') - - def test_set_server_meta(self): - self.cs.servers.set_meta(1234, {'test_key': 'test_value'}) - self.assert_called('POST', '/servers/1234/metadata', - {'metadata': {'test_key': 'test_value'}}) - - def test_find(self): - server = self.cs.servers.find(name='sample-server') - self.assert_called('GET', '/servers/1234') - self.assertEqual('sample-server', server.name) - - self.assertRaises(exceptions.NoUniqueMatch, self.cs.servers.find, - flavor={"id": 1, "name": "256 MB Server"}) - - sl = self.cs.servers.findall(flavor={"id": 1, "name": "256 MB Server"}) - self.assertEqual([1234, 5678, 9012], [s.id for s in sl]) - - def test_reboot_server(self): - s = self.cs.servers.get(1234) - s.reboot() - self.assert_called('POST', '/servers/1234/action') - self.cs.servers.reboot(s, reboot_type='HARD') - self.assert_called('POST', '/servers/1234/action') - - def test_rebuild_server(self): - s = self.cs.servers.get(1234) - s.rebuild(image=1) - self.assert_called('POST', '/servers/1234/action') - self.cs.servers.rebuild(s, image=1) - self.assert_called('POST', '/servers/1234/action') - s.rebuild(image=1, password='5678') - self.assert_called('POST', '/servers/1234/action') - self.cs.servers.rebuild(s, image=1, password='5678') - self.assert_called('POST', '/servers/1234/action') - - def test_resize_server(self): - s = self.cs.servers.get(1234) - s.resize(flavor=1) - self.assert_called('POST', '/servers/1234/action') - self.cs.servers.resize(s, flavor=1) - self.assert_called('POST', '/servers/1234/action') - - def test_confirm_resized_server(self): - s = self.cs.servers.get(1234) - s.confirm_resize() - self.assert_called('POST', '/servers/1234/action') - self.cs.servers.confirm_resize(s) - self.assert_called('POST', '/servers/1234/action') - - def test_revert_resized_server(self): - s = self.cs.servers.get(1234) - s.revert_resize() - self.assert_called('POST', '/servers/1234/action') - self.cs.servers.revert_resize(s) - self.assert_called('POST', '/servers/1234/action') - - def test_migrate_server(self): - s = self.cs.servers.get(1234) - s.migrate() - self.assert_called('POST', '/servers/1234/action') - self.cs.servers.migrate(s) - self.assert_called('POST', '/servers/1234/action') - - def test_add_fixed_ip(self): - s = self.cs.servers.get(1234) - s.add_fixed_ip(1) - self.assert_called('POST', '/servers/1234/action') - self.cs.servers.add_fixed_ip(s, 1) - self.assert_called('POST', '/servers/1234/action') - - def test_remove_fixed_ip(self): - s = self.cs.servers.get(1234) - s.remove_fixed_ip('10.0.0.1') - self.assert_called('POST', '/servers/1234/action') - self.cs.servers.remove_fixed_ip(s, '10.0.0.1') - self.assert_called('POST', '/servers/1234/action') - - def test_stop(self): - s = self.cs.servers.get(1234) - s.stop() - self.assert_called('POST', '/servers/1234/action') - self.cs.servers.stop(s) - self.assert_called('POST', '/servers/1234/action') - - def test_force_delete(self): - s = self.cs.servers.get(1234) - s.force_delete() - self.assert_called('POST', '/servers/1234/action') - self.cs.servers.force_delete(s) - self.assert_called('POST', '/servers/1234/action') - - def test_restore(self): - s = self.cs.servers.get(1234) - s.restore() - self.assert_called('POST', '/servers/1234/action') - self.cs.servers.restore(s) - self.assert_called('POST', '/servers/1234/action') - - def test_start(self): - s = self.cs.servers.get(1234) - s.start() - self.assert_called('POST', '/servers/1234/action') - self.cs.servers.start(s) - self.assert_called('POST', '/servers/1234/action') - - def test_rescue(self): - s = self.cs.servers.get(1234) - s.rescue() - self.assert_called('POST', '/servers/1234/action') - self.cs.servers.rescue(s) - self.assert_called('POST', '/servers/1234/action') - - def test_unrescue(self): - s = self.cs.servers.get(1234) - s.unrescue() - self.assert_called('POST', '/servers/1234/action') - self.cs.servers.unrescue(s) - self.assert_called('POST', '/servers/1234/action') - - def test_lock(self): - s = self.cs.servers.get(1234) - s.lock() - self.assert_called('POST', '/servers/1234/action') - self.cs.servers.lock(s) - self.assert_called('POST', '/servers/1234/action') - - def test_unlock(self): - s = self.cs.servers.get(1234) - s.unlock() - self.assert_called('POST', '/servers/1234/action') - self.cs.servers.unlock(s) - self.assert_called('POST', '/servers/1234/action') - - def test_backup(self): - s = self.cs.servers.get(1234) - s.backup('back1', 'daily', 1) - self.assert_called('POST', '/servers/1234/action') - self.cs.servers.backup(s, 'back1', 'daily', 2) - self.assert_called('POST', '/servers/1234/action') - - def test_get_console_output_without_length(self): - success = 'foo' - s = self.cs.servers.get(1234) - s.get_console_output() - self.assertEqual(success, s.get_console_output()) - self.assert_called('POST', '/servers/1234/action') - - self.cs.servers.get_console_output(s) - self.assertEqual(success, self.cs.servers.get_console_output(s)) - self.assert_called('POST', '/servers/1234/action', - {'get_console_output': {'length': -1}}) - - def test_get_console_output_with_length(self): - success = 'foo' - - s = self.cs.servers.get(1234) - s.get_console_output(length=50) - self.assertEqual(success, s.get_console_output(length=50)) - self.assert_called('POST', '/servers/1234/action', - {'get_console_output': {'length': 50}}) - - self.cs.servers.get_console_output(s, length=50) - self.assertEqual(success, - self.cs.servers.get_console_output(s, length=50)) - self.assert_called('POST', '/servers/1234/action', - {'get_console_output': {'length': 50}}) - - def test_get_password(self): - s = self.cs.servers.get(1234) - self.assertEqual('', s.get_password('/foo/id_rsa')) - self.assert_called('GET', '/servers/1234/os-server-password') - - def test_clear_password(self): - s = self.cs.servers.get(1234) - s.clear_password() - self.assert_called('DELETE', '/servers/1234/os-server-password') - - def test_get_server_diagnostics(self): - s = self.cs.servers.get(1234) - diagnostics = s.diagnostics() - self.assertTrue(diagnostics is not None) - self.assert_called('GET', '/servers/1234/os-server-diagnostics') - - diagnostics_from_manager = self.cs.servers.diagnostics(1234) - self.assertTrue(diagnostics_from_manager is not None) - self.assert_called('GET', '/servers/1234/os-server-diagnostics') - - self.assertEqual(diagnostics_from_manager[1], diagnostics[1]) - - def test_get_vnc_console(self): - s = self.cs.servers.get(1234) - s.get_vnc_console('fake') - self.assert_called('POST', '/servers/1234/action') - - self.cs.servers.get_vnc_console(s, 'fake') - self.assert_called('POST', '/servers/1234/action') - - def test_get_spice_console(self): - s = self.cs.servers.get(1234) - s.get_spice_console('fake') - self.assert_called('POST', '/servers/1234/action') - - self.cs.servers.get_spice_console(s, 'fake') - self.assert_called('POST', '/servers/1234/action') - - def test_create_image(self): - s = self.cs.servers.get(1234) - s.create_image('123') - self.assert_called('POST', '/servers/1234/action') - s.create_image('123', {}) - self.assert_called('POST', '/servers/1234/action') - self.cs.servers.create_image(s, '123') - self.assert_called('POST', '/servers/1234/action') - self.cs.servers.create_image(s, '123', {}) - - def test_live_migrate_server(self): - s = self.cs.servers.get(1234) - s.live_migrate(host='hostname', block_migration=False, - disk_over_commit=False) - self.assert_called('POST', '/servers/1234/action') - self.cs.servers.live_migrate(s, host='hostname', block_migration=False, - disk_over_commit=False) - self.assert_called('POST', '/servers/1234/action') - - def test_reset_state(self): - s = self.cs.servers.get(1234) - s.reset_state('newstate') - self.assert_called('POST', '/servers/1234/action') - self.cs.servers.reset_state(s, 'newstate') - self.assert_called('POST', '/servers/1234/action') - - def test_reset_network(self): - s = self.cs.servers.get(1234) - s.reset_network() - self.assert_called('POST', '/servers/1234/action') - self.cs.servers.reset_network(s) - self.assert_called('POST', '/servers/1234/action') - - def test_evacuate(self): - s = self.cs.servers.get(1234) - s.evacuate('fake_target_host', 'True') - self.assert_called('POST', '/servers/1234/action') - self.cs.servers.evacuate(s, 'fake_target_host', - 'False', 'NewAdminPassword') - self.assert_called('POST', '/servers/1234/action') - - def test_interface_list(self): - s = self.cs.servers.get(1234) - s.interface_list() - self.assert_called('GET', '/servers/1234/os-attach-interfaces') - - def test_interface_attach(self): - s = self.cs.servers.get(1234) - s.interface_attach(None, None, None) - self.assert_called('POST', '/servers/1234/os-attach-interfaces') - - def test_interface_detach(self): - s = self.cs.servers.get(1234) - s.interface_detach('port-id') - self.assert_called('DELETE', - '/servers/1234/os-attach-interfaces/port-id') diff --git a/novaclient/tests/v3/test_services.py b/novaclient/tests/v3/test_services.py deleted file mode 100644 index 0fd2fc0cf..000000000 --- a/novaclient/tests/v3/test_services.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright 2012 IBM Corp. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from novaclient.tests import utils -from novaclient.tests.v3 import fakes -from novaclient.v3 import services - - -class ServicesTest(utils.TestCase): - def setUp(self): - super(ServicesTest, self).setUp() - self.cs = self._get_fake_client() - self.service_type = self._get_service_type() - - def _get_fake_client(self): - return fakes.FakeClient() - - def _get_service_type(self): - return services.Service - - def _update_body(self, host, binary, disabled_reason=None): - body = {"host": host, - "binary": binary} - if disabled_reason is not None: - body["disabled_reason"] = disabled_reason - return body diff --git a/novaclient/tests/v3/test_shell.py b/novaclient/tests/v3/test_shell.py deleted file mode 100644 index 2418aed7c..000000000 --- a/novaclient/tests/v3/test_shell.py +++ /dev/null @@ -1,766 +0,0 @@ -# Copyright 2013 Cloudwatt -# Copyright 2010 Jacob Kaplan-Moss -# Copyright 2011 OpenStack Foundation -# Copyright 2012 IBM Corp. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import base64 -import os - -import fixtures -import mock -import six - -from novaclient import exceptions -import novaclient.shell -from novaclient.tests import utils -from novaclient.tests.v3 import fakes - - -class ShellFixture(fixtures.Fixture): - def setUp(self): - super(ShellFixture, self).setUp() - self.shell = novaclient.shell.OpenStackComputeShell() - - def tearDown(self): - # For some method like test_image_meta_bad_action we are - # testing a SystemExit to be thrown and object self.shell has - # no time to get instantatiated which is OK in this case, so - # we make sure the method is there before launching it. - if hasattr(self.shell, 'cs'): - self.shell.cs.clear_callstack() - super(ShellFixture, self).tearDown() - - -class ShellTest(utils.TestCase): - FAKE_ENV = { - 'NOVA_USERNAME': 'username', - 'NOVA_PASSWORD': 'password', - 'NOVA_PROJECT_ID': 'project_id', - 'OS_COMPUTE_API_VERSION': '3', - 'NOVA_URL': 'http://no.where', - 'OS_AUTH_URL': 'http://no.where/v2.0', - } - - def setUp(self): - """Run before each test.""" - super(ShellTest, self).setUp() - - for var in self.FAKE_ENV: - self.useFixture(fixtures.EnvironmentVariable(var, - self.FAKE_ENV[var])) - self.shell = self.useFixture(ShellFixture()).shell - - self.useFixture(fixtures.MonkeyPatch( - 'novaclient.client.get_client_class', - lambda *_: fakes.FakeClient)) - - @mock.patch('sys.stdout', new_callable=six.StringIO) - def run_command(self, cmd, mock_stdout): - if isinstance(cmd, list): - self.shell.main(cmd) - else: - self.shell.main(cmd.split()) - return mock_stdout.getvalue() - - def assert_called(self, method, url, body=None, **kwargs): - return self.shell.cs.assert_called(method, url, body, **kwargs) - - def assert_called_anytime(self, method, url, body=None): - return self.shell.cs.assert_called_anytime(method, url, body) - - def test_list_deleted(self): - self.run_command('list --deleted') - self.assert_called('GET', '/servers/detail?deleted=True') - - def test_aggregate_list(self): - self.run_command('aggregate-list') - self.assert_called('GET', '/os-aggregates') - - def test_aggregate_create(self): - self.run_command('aggregate-create test_name nova1') - body = {"aggregate": {"name": "test_name", - "availability_zone": "nova1"}} - self.assert_called('POST', '/os-aggregates', body, pos=-2) - self.assert_called('GET', '/os-aggregates/1', pos=-1) - - def test_aggregate_delete_by_id(self): - self.run_command('aggregate-delete 1') - self.assert_called('DELETE', '/os-aggregates/1') - - def test_aggregate_delete_by_name(self): - self.run_command('aggregate-delete test') - self.assert_called('DELETE', '/os-aggregates/1') - - def test_aggregate_update_by_id(self): - self.run_command('aggregate-update 1 new_name') - body = {"aggregate": {"name": "new_name"}} - self.assert_called('PUT', '/os-aggregates/1', body, pos=-2) - self.assert_called('GET', '/os-aggregates/1', pos=-1) - - def test_aggregate_update_by_name(self): - self.run_command('aggregate-update test new_name') - body = {"aggregate": {"name": "new_name"}} - self.assert_called('PUT', '/os-aggregates/1', body, pos=-2) - self.assert_called('GET', '/os-aggregates/1', pos=-1) - - def test_aggregate_update_with_availability_zone_by_id(self): - self.run_command('aggregate-update 1 foo new_zone') - body = {"aggregate": {"name": "foo", "availability_zone": "new_zone"}} - self.assert_called('PUT', '/os-aggregates/1', body, pos=-2) - self.assert_called('GET', '/os-aggregates/1', pos=-1) - - def test_aggregate_update_with_availability_zone_by_name(self): - self.run_command('aggregate-update test foo new_zone') - body = {"aggregate": {"name": "foo", "availability_zone": "new_zone"}} - self.assert_called('PUT', '/os-aggregates/1', body, pos=-2) - self.assert_called('GET', '/os-aggregates/1', pos=-1) - - def test_aggregate_set_metadata_add_by_id(self): - self.run_command('aggregate-set-metadata 3 foo=bar') - body = {"set_metadata": {"metadata": {"foo": "bar"}}} - self.assert_called('POST', '/os-aggregates/3/action', body, pos=-2) - self.assert_called('GET', '/os-aggregates/3', pos=-1) - - def test_aggregate_set_metadata_add_duplicate_by_id(self): - cmd = 'aggregate-set-metadata 3 test=dup' - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - - def test_aggregate_set_metadata_delete_by_id(self): - self.run_command('aggregate-set-metadata 3 none_key') - body = {"set_metadata": {"metadata": {"none_key": None}}} - self.assert_called('POST', '/os-aggregates/3/action', body, pos=-2) - self.assert_called('GET', '/os-aggregates/3', pos=-1) - - def test_aggregate_set_metadata_delete_missing_by_id(self): - cmd = 'aggregate-set-metadata 3 delete_key2' - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - - def test_aggregate_set_metadata_by_name(self): - self.run_command('aggregate-set-metadata test foo=bar') - body = {"set_metadata": {"metadata": {"foo": "bar"}}} - self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2) - self.assert_called('GET', '/os-aggregates/1', pos=-1) - - def test_aggregate_add_host_by_id(self): - self.run_command('aggregate-add-host 1 host1') - body = {"add_host": {"host": "host1"}} - self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2) - self.assert_called('GET', '/os-aggregates/1', pos=-1) - - def test_aggregate_add_host_by_name(self): - self.run_command('aggregate-add-host test host1') - body = {"add_host": {"host": "host1"}} - self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2) - self.assert_called('GET', '/os-aggregates/1', pos=-1) - - def test_aggregate_remove_host_by_id(self): - self.run_command('aggregate-remove-host 1 host1') - body = {"remove_host": {"host": "host1"}} - self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2) - self.assert_called('GET', '/os-aggregates/1', pos=-1) - - def test_aggregate_remove_host_by_name(self): - self.run_command('aggregate-remove-host test host1') - body = {"remove_host": {"host": "host1"}} - self.assert_called('POST', '/os-aggregates/1/action', body, pos=-2) - self.assert_called('GET', '/os-aggregates/1', pos=-1) - - def test_aggregate_details_by_id(self): - self.run_command('aggregate-details 1') - self.assert_called('GET', '/os-aggregates/1') - - def test_aggregate_details_by_name(self): - self.run_command('aggregate-details test') - self.assert_called('GET', '/os-aggregates') - - def test_boot(self): - self.run_command('boot --flavor 1 --image 1 some-server') - self.assert_called_anytime( - 'POST', '/servers', - {'server': { - 'flavor_ref': '1', - 'name': 'some-server', - 'image_ref': '1', - 'os-multiple-create:min_count': 1, - 'os-multiple-create:max_count': 1, - }}, - ) - - def test_boot_image_with(self): - self.run_command("boot --flavor 1" - " --image-with test_key=test_value some-server") - self.assert_called_anytime( - 'POST', '/servers', - {'server': { - 'flavor_ref': '1', - 'name': 'some-server', - 'image_ref': '1', - 'os-multiple-create:min_count': 1, - 'os-multiple-create:max_count': 1, - }}, - ) - - def test_boot_key(self): - self.run_command('boot --flavor 1 --image 1 --key_name 1 some-server') - self.assert_called_anytime( - 'POST', '/servers', - {'server': { - 'flavor_ref': '1', - 'name': 'some-server', - 'image_ref': '1', - 'key_name': '1', - 'os-multiple-create:min_count': 1, - 'os-multiple-create:max_count': 1, - }}, - ) - - def test_boot_user_data(self): - file_text = 'text' - - with mock.patch('novaclient.v3.shell.open', create=True) as mock_open: - mock_open.return_value = file_text - testfile = 'some_dir/some_file.txt' - - self.run_command('boot --flavor 1 --image 1 --user_data %s ' - 'some-server' % testfile) - - mock_open.assert_called_once_with(testfile) - - user_data = base64.b64encode(file_text.encode('utf-8')).decode('utf-8') - self.assert_called_anytime( - 'POST', '/servers', - {'server': { - 'flavor_ref': '1', - 'name': 'some-server', - 'image_ref': '1', - 'os-multiple-create:min_count': 1, - 'os-multiple-create:max_count': 1, - 'user_data': user_data}}, - ) - - def test_boot_avzone(self): - self.run_command( - 'boot --flavor 1 --image 1 --availability-zone avzone ' - 'some-server') - self.assert_called_anytime( - 'POST', '/servers', - {'server': { - 'flavor_ref': '1', - 'name': 'some-server', - 'image_ref': '1', - 'os-availability-zone:availability_zone': 'avzone', - 'os-multiple-create:min_count': 1, - 'os-multiple-create:max_count': 1 - }}, - ) - - def test_boot_secgroup(self): - self.run_command( - 'boot --flavor 1 --image 1 --security-groups secgroup1,' - 'secgroup2 some-server') - self.assert_called_anytime( - 'POST', '/servers', - {'server': { - 'security_groups': [{'name': 'secgroup1'}, - {'name': 'secgroup2'}], - 'flavor_ref': '1', - 'name': 'some-server', - 'image_ref': '1', - 'os-multiple-create:min_count': 1, - 'os-multiple-create:max_count': 1, - }}, - ) - - def test_boot_config_drive(self): - self.run_command( - 'boot --flavor 1 --image 1 --config-drive 1 some-server') - self.assert_called_anytime( - 'POST', '/servers', - {'server': { - 'flavor_ref': '1', - 'name': 'some-server', - 'image_ref': '1', - 'os-multiple-create:min_count': 1, - 'os-multiple-create:max_count': 1, - 'os-config-drive:config_drive': True - }}, - ) - - def test_boot_config_drive_custom(self): - self.run_command( - 'boot --flavor 1 --image 1 --config-drive /dev/hda some-server') - self.assert_called_anytime( - 'POST', '/servers', - {'server': { - 'flavor_ref': '1', - 'name': 'some-server', - 'image_ref': '1', - 'os-multiple-create:min_count': 1, - 'os-multiple-create:max_count': 1, - 'os-config-drive:config_drive': '/dev/hda' - }}, - ) - - def test_boot_invalid_user_data(self): - invalid_file = os.path.join(os.path.dirname(__file__), - 'no_such_file') - cmd = ('boot some-server --flavor 1 --image 1' - ' --user_data %s' % invalid_file) - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - - def test_boot_no_image_no_bdms(self): - cmd = 'boot --flavor 1 some-server' - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - - def test_boot_no_flavor(self): - cmd = 'boot --image 1 some-server' - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - - def test_boot_no_image_bdms(self): - self.run_command( - 'boot --flavor 1 --block_device_mapping vda=blah:::0 some-server' - ) - self.assert_called_anytime( - 'POST', '/servers', - {'server': { - 'flavor_ref': '1', - 'name': 'some-server', - 'block_device_mapping': [ - { - 'volume_id': 'blah', - 'delete_on_termination': '0', - 'device_name': 'vda', - 'boot_index': 0, - 'uuid': 'blah', - 'source_type': '' - } - ], - 'image_ref': '', - 'os-multiple-create:min_count': 1, - 'os-multiple-create:max_count': 1, - }}, - ) - - def test_boot_image_bdms(self): - self.run_command( - 'boot --flavor 1 --image 1 --block-device id=fake-id,' - 'source=volume,dest=volume,device=vda,size=1,format=ext4,' - 'type=disk,shutdown=preserve some-server' - ) - id = ('fake-id,source=volume,dest=volume,device=vda,size=1,' - 'format=ext4,type=disk,shutdown=preserve') - self.assert_called_anytime( - 'POST', '/servers', - {'server': { - 'flavor_ref': '1', - 'name': 'some-server', - 'block_device_mapping': [ - {'device_name': 'id', 'volume_id': id, - 'source_type': 'volume', 'boot_index': 0, 'uuid': id}], - 'image_ref': '1', - 'os-multiple-create:min_count': 1, - 'os-multiple-create:max_count': 1, - }}, - ) - - def test_boot_metadata(self): - self.run_command('boot --image 1 --flavor 1 --meta foo=bar=pants' - ' --meta spam=eggs some-server ') - self.assert_called_anytime( - 'POST', '/servers', - {'server': { - 'flavor_ref': '1', - 'name': 'some-server', - 'image_ref': '1', - 'metadata': {'foo': 'bar=pants', 'spam': 'eggs'}, - 'os-multiple-create:min_count': 1, - 'os-multiple-create:max_count': 1, - }}, - ) - - def test_boot_hints(self): - self.run_command('boot --image 1 --flavor 1 ' - '--hint a=b1=c1 --hint a2=b2=c2 --hint a=b0=c0 ' - 'some-server') - self.assert_called_anytime( - 'POST', '/servers', - { - 'server': { - 'flavor_ref': '1', - 'name': 'some-server', - 'image_ref': '1', - 'os-multiple-create:min_count': 1, - 'os-multiple-create:max_count': 1, - 'os-scheduler-hints:scheduler_hints': { - 'a': ['b1=c1', 'b0=c0'], 'a2': 'b2=c2'}, - }, - }, - ) - - def test_boot_nics(self): - cmd = ('boot --image 1 --flavor 1 ' - '--nic net-id=a=c,v4-fixed-ip=10.0.0.1 some-server') - self.run_command(cmd) - self.assert_called_anytime( - 'POST', '/servers', - { - 'server': { - 'flavor_ref': '1', - 'name': 'some-server', - 'image_ref': '1', - 'os-multiple-create:min_count': 1, - 'os-multiple-create:max_count': 1, - 'networks': [ - {'uuid': 'a=c', 'fixed_ip': '10.0.0.1'}, - ], - }, - }, - ) - - def test_boot_nics_ipv6(self): - cmd = ('boot --image 1 --flavor 1 ' - '--nic net-id=a=c,v6-fixed-ip=2001:db9:0:1::10 some-server') - self.run_command(cmd) - self.assert_called_anytime( - 'POST', '/servers', - { - 'server': { - 'flavor_ref': '1', - 'name': 'some-server', - 'image_ref': '1', - 'os-multiple-create:min_count': 1, - 'os-multiple-create:max_count': 1, - 'networks': [ - {'uuid': 'a=c', 'fixed_ip': '2001:db9:0:1::10'}, - ], - }, - }, - ) - - def test_boot_nics_both_ipv4_and_ipv6(self): - cmd = ('boot --image 1 --flavor 1 ' - '--nic net-id=a=c,v4-fixed-ip=10.0.0.1,' - 'v6-fixed-ip=2001:db9:0:1::10 some-server') - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - - def test_boot_nics_no_value(self): - cmd = ('boot --image 1 --flavor 1 ' - '--nic net-id some-server') - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - - def test_boot_nics_random_key(self): - cmd = ('boot --image 1 --flavor 1 ' - '--nic net-id=a=c,v4-fixed-ip=10.0.0.1,foo=bar some-server') - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - - def test_boot_nics_no_netid_or_portid(self): - cmd = ('boot --image 1 --flavor 1 ' - '--nic v4-fixed-ip=10.0.0.1 some-server') - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - - def test_boot_nics_netid_and_portid(self): - cmd = ('boot --image 1 --flavor 1 ' - '--nic port-id=some=port,net-id=some=net some-server') - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - - def test_boot_num_instances(self): - self.run_command('boot --image 1 --flavor 1 --num-instances 3 server') - self.assert_called_anytime( - 'POST', '/servers', - { - 'server': { - 'flavor_ref': '1', - 'name': 'server', - 'image_ref': '1', - 'os-multiple-create:min_count': 1, - 'os-multiple-create:max_count': 3, - } - }) - - def test_boot_invalid_num_instances(self): - cmd = 'boot --image 1 --flavor 1 --num-instances 0 server' - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - - def test_boot_num_instances_and_count(self): - cmd = 'boot --image 1 --flavor 1 --num-instances 3 --min-count 3 serv' - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - cmd = 'boot --image 1 --flavor 1 --num-instances 3 --max-count 3 serv' - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - - def test_boot_min_max_count(self): - self.run_command('boot --image 1 --flavor 1 --max-count 3 server') - self.assert_called_anytime( - 'POST', '/servers', - { - 'server': { - 'flavor_ref': '1', - 'name': 'server', - 'image_ref': '1', - 'os-multiple-create:min_count': 1, - 'os-multiple-create:max_count': 3, - } - }) - self.run_command('boot --image 1 --flavor 1 --min-count 3 server') - self.assert_called_anytime( - 'POST', '/servers', - { - 'server': { - 'flavor_ref': '1', - 'name': 'server', - 'image_ref': '1', - 'os-multiple-create:min_count': 3, - 'os-multiple-create:max_count': 3, - } - }) - self.run_command('boot --image 1 --flavor 1 ' - '--min-count 3 --max-count 3 server') - self.assert_called_anytime( - 'POST', '/servers', - { - 'server': { - 'flavor_ref': '1', - 'name': 'server', - 'image_ref': '1', - 'os-multiple-create:min_count': 3, - 'os-multiple-create:max_count': 3, - } - }) - self.run_command('boot --image 1 --flavor 1 ' - '--min-count 3 --max-count 5 server') - self.assert_called_anytime( - 'POST', '/servers', - { - 'server': { - 'flavor_ref': '1', - 'name': 'server', - 'image_ref': '1', - 'os-multiple-create:min_count': 3, - 'os-multiple-create:max_count': 5, - } - }) - cmd = 'boot --image 1 --flavor 1 --min-count 3 --max-count 1 serv' - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - - @mock.patch('novaclient.v3.shell._poll_for_status') - def test_boot_with_poll(self, poll_method): - self.run_command('boot --flavor 1 --image 1 some-server --poll') - self.assert_called_anytime( - 'POST', '/servers', - {'server': { - 'flavor_ref': '1', - 'name': 'some-server', - 'image_ref': '1', - 'os-multiple-create:min_count': 1, - 'os-multiple-create:max_count': 1, - }}, - ) - self.assertEqual(1, poll_method.call_count) - poll_method.assert_has_calls( - [mock.call(self.shell.cs.servers.get, 1234, 'building', - ['active'])]) - - def test_boot_with_poll_to_check_VM_state_error(self): - self.assertRaises(exceptions.InstanceInErrorState, self.run_command, - 'boot --flavor 1 --image 1 some-bad-server --poll') - - def test_evacuate(self): - self.run_command('evacuate sample-server new_host') - self.assert_called('POST', '/servers/1234/action', - {'evacuate': {'host': 'new_host', - 'on_shared_storage': False}}) - self.run_command('evacuate sample-server new_host ' - '--password NewAdminPass') - self.assert_called('POST', '/servers/1234/action', - {'evacuate': {'host': 'new_host', - 'on_shared_storage': False, - 'admin_password': 'NewAdminPass'}}) - self.run_command('evacuate sample-server new_host') - self.assert_called('POST', '/servers/1234/action', - {'evacuate': {'host': 'new_host', - 'on_shared_storage': False}}) - self.run_command('evacuate sample-server new_host ' - '--on-shared-storage') - self.assert_called('POST', '/servers/1234/action', - {'evacuate': {'host': 'new_host', - 'on_shared_storage': True}}) - - def test_evacuate_with_no_target_host(self): - self.run_command('evacuate sample-server') - self.assert_called('POST', '/servers/1234/action', - {'evacuate': {'on_shared_storage': False}}) - self.run_command('evacuate sample-server --password NewAdminPass') - self.assert_called('POST', '/servers/1234/action', - {'evacuate': {'on_shared_storage': False, - 'admin_password': 'NewAdminPass'}}) - self.run_command('evacuate sample-server --on-shared-storage') - self.assert_called('POST', '/servers/1234/action', - {'evacuate': {'on_shared_storage': True}}) - - def test_boot_named_flavor(self): - self.run_command(["boot", "--image", "1", - "--flavor", "512 MB Server", - "--max-count", "3", "server"]) - self.assert_called('GET', '/flavors/512 MB Server', pos=0) - self.assert_called('GET', '/flavors?is_public=None', pos=1) - self.assert_called('GET', '/flavors?is_public=None', pos=2) - self.assert_called('GET', '/flavors/2', pos=3) - self.assert_called( - 'POST', '/servers', - { - 'server': { - 'flavor_ref': '2', - 'name': 'server', - 'image_ref': '1', - 'os-multiple-create:min_count': 1, - 'os-multiple-create:max_count': 3, - } - }, pos=4) - - def test_flavor_show_by_name(self): - self.run_command(['flavor-show', '128 MB Server']) - self.assert_called('GET', '/flavors/128 MB Server', pos=0) - self.assert_called('GET', '/flavors?is_public=None', pos=1) - self.assert_called('GET', '/flavors?is_public=None', pos=2) - self.assert_called('GET', '/flavors/aa1', pos=3) - self.assert_called('GET', '/flavors/aa1/flavor-extra-specs', pos=4) - - def test_flavor_show_by_name_priv(self): - self.run_command(['flavor-show', '512 MB Server']) - self.assert_called('GET', '/flavors/512 MB Server', pos=0) - self.assert_called('GET', '/flavors?is_public=None', pos=1) - self.assert_called('GET', '/flavors?is_public=None', pos=2) - self.assert_called('GET', '/flavors/2', pos=3) - self.assert_called('GET', '/flavors/2/flavor-extra-specs', pos=4) - - def test_host_evacuate_live_with_no_target_host(self): - self.run_command('host-evacuate-live hyper1') - self.assert_called('GET', '/os-hypervisors/search?query=hyper1', pos=0) - self.assert_called('GET', '/os-hypervisors/1234/servers', pos=1) - body = {'migrate_live': {'host': None, - 'block_migration': False, - 'disk_over_commit': False}} - self.assert_called('POST', '/servers/uuid1/action', body, pos=2) - self.assert_called('POST', '/servers/uuid2/action', body, pos=3) - - def test_host_evacuate_live_with_target_host(self): - self.run_command('host-evacuate-live hyper1 ' - '--target-host hostname') - self.assert_called('GET', '/os-hypervisors/search?query=hyper1', pos=0) - self.assert_called('GET', '/os-hypervisors/1234/servers', pos=1) - body = {'migrate_live': {'host': 'hostname', - 'block_migration': False, - 'disk_over_commit': False}} - self.assert_called('POST', '/servers/uuid1/action', body, pos=2) - self.assert_called('POST', '/servers/uuid2/action', body, pos=3) - - def test_host_evacuate_live_with_block_migration(self): - self.run_command('host-evacuate-live --block-migrate hyper1') - self.assert_called('GET', '/os-hypervisors/search?query=hyper1', pos=0) - self.assert_called('GET', '/os-hypervisors/1234/servers', pos=1) - body = {'migrate_live': {'host': None, - 'block_migration': True, - 'disk_over_commit': False}} - self.assert_called('POST', '/servers/uuid1/action', body, pos=2) - self.assert_called('POST', '/servers/uuid2/action', body, pos=3) - - def test_host_evacuate_live_with_disk_over_commit(self): - self.run_command('host-evacuate-live --disk-over-commit hyper1') - self.assert_called('GET', '/os-hypervisors/search?query=hyper1', pos=0) - self.assert_called('GET', '/os-hypervisors/1234/servers', pos=1) - body = {'migrate_live': {'host': None, - 'block_migration': False, - 'disk_over_commit': True}} - self.assert_called('POST', '/servers/uuid1/action', body, pos=2) - self.assert_called('POST', '/servers/uuid2/action', body, pos=3) - - def test_delete(self): - self.run_command('delete 1234') - self.assert_called('DELETE', '/servers/1234') - self.run_command('delete sample-server') - self.assert_called('DELETE', '/servers/1234') - - def test_delete_two_with_two_existent(self): - self.run_command('delete 1234 5678') - self.assert_called('DELETE', '/servers/1234', pos=-3) - self.assert_called('DELETE', '/servers/5678', pos=-1) - self.run_command('delete sample-server sample-server2') - self.assert_called('GET', '/servers?name=sample-server', pos=-6) - self.assert_called('GET', '/servers/1234', pos=-5) - self.assert_called('DELETE', '/servers/1234', pos=-4) - self.assert_called('GET', '/servers?name=sample-server2', pos=-3) - self.assert_called('GET', '/servers/5678', pos=-2) - self.assert_called('DELETE', '/servers/5678', pos=-1) - - def test_delete_two_with_one_nonexistent(self): - cmd = 'delete 1234 123456789' - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - self.assert_called_anytime('DELETE', '/servers/1234') - cmd = 'delete sample-server nonexistentserver' - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - self.assert_called_anytime('DELETE', '/servers/1234') - - def test_delete_one_with_one_nonexistent(self): - cmd = 'delete 123456789' - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - cmd = 'delete nonexistent-server1' - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - - def test_delete_two_with_two_nonexistent(self): - cmd = 'delete 123456789 987654321' - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - cmd = 'delete nonexistent-server1 nonexistent-server2' - self.assertRaises(exceptions.CommandError, self.run_command, cmd) - - -class GetFirstEndpointTest(utils.TestCase): - def test_only_one_endpoint(self): - """If there is only one endpoint, it is returned.""" - endpoint = {"url": "test"} - result = novaclient.v3.shell._get_first_endpoint([endpoint], "XYZ") - self.assertEqual(endpoint, result) - - def test_multiple_endpoints(self): - """If there are multiple endpoints, the first one of the appropriate - region is returned. - - """ - endpoints = [ - {"region": "XYZ"}, - {"region": "ORD", "number": 1}, - {"region": "ORD", "number": 2} - ] - result = novaclient.v3.shell._get_first_endpoint(endpoints, "ORD") - self.assertEqual(endpoints[1], result) - - def test_multiple_endpoints_but_none_suitable(self): - """If there are multiple endpoints but none of them are suitable, an - exception is raised. - - """ - endpoints = [ - {"region": "XYZ"}, - {"region": "PQR"}, - {"region": "STU"} - ] - self.assertRaises(LookupError, - novaclient.v3.shell._get_first_endpoint, - endpoints, "ORD") - - def test_no_endpoints(self): - """If there are no endpoints available, an exception is raised.""" - self.assertRaises(LookupError, - novaclient.v3.shell._get_first_endpoint, - [], "ORD") diff --git a/novaclient/tests/v3/test_usage.py b/novaclient/tests/v3/test_usage.py deleted file mode 100644 index b4ffab79e..000000000 --- a/novaclient/tests/v3/test_usage.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2013 IBM Corp. -# -# 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 novaclient.tests.v1_1 import fakes -from novaclient.tests.v1_1 import test_usage -from novaclient.v3 import usage - - -class UsageTest(test_usage.UsageTest): - def setUp(self): - super(UsageTest, self).setUp() - self.cs = self._get_fake_client() - self.usage_type = self._get_usage_type() - - def _get_fake_client(self): - return fakes.FakeClient() - - def _get_usage_type(self): - return usage.Usage diff --git a/novaclient/tests/v3/test_volumes.py b/novaclient/tests/v3/test_volumes.py deleted file mode 100644 index cfe6d9eb3..000000000 --- a/novaclient/tests/v3/test_volumes.py +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright 2013 IBM Corp. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from novaclient.tests import utils -from novaclient.tests.v3 import fakes - - -class VolumesTest(utils.TestCase): - def setUp(self): - super(VolumesTest, self).setUp() - self.cs = self._get_fake_client() - - def _get_fake_client(self): - return fakes.FakeClient() - - def test_attach_server_volume(self): - self.cs.volumes.attach_server_volume( - server=1234, - volume_id='15e59938-07d5-11e1-90e3-e3dffe0c5983', - device='/dev/vdb' - ) - self.cs.assert_called('POST', '/servers/1234/action') - - def test_attach_server_volume_disk_bus_device_type(self): - volume_id = '15e59938-07d5-11e1-90e3-e3dffe0c5983' - device = '/dev/vdb' - disk_bus = 'ide' - device_type = 'cdrom' - self.cs.volumes.attach_server_volume(server=1234, - volume_id=volume_id, - device=device, - disk_bus=disk_bus, - device_type=device_type) - body_params = {'volume_id': volume_id, - 'device': device, - 'disk_bus': disk_bus, - 'device_type': device_type} - body = {'attach': body_params} - self.cs.assert_called('POST', '/servers/1234/action', body) - - def test_update_server_volume(self): - vol_id = '15e59938-07d5-11e1-90e3-e3dffe0c5983' - self.cs.volumes.update_server_volume( - server=1234, - old_volume_id='Work', - new_volume_id=vol_id - ) - self.cs.assert_called('POST', '/servers/1234/action') - - def test_delete_server_volume(self): - self.cs.volumes.delete_server_volume(1234, 'Work') - self.cs.assert_called('POST', '/servers/1234/action') diff --git a/novaclient/v3/__init__.py b/novaclient/v3/__init__.py deleted file mode 100644 index a442be109..000000000 --- a/novaclient/v3/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) 2012 OpenStack Foundation -# -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from novaclient.v3.client import Client # noqa diff --git a/novaclient/v3/agents.py b/novaclient/v3/agents.py deleted file mode 100644 index dacaf7671..000000000 --- a/novaclient/v3/agents.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2012 IBM Corp. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -agent interface -""" - -from novaclient.v1_1 import agents - - -class Agent(agents.Agent): - pass - - -class AgentsManager(agents.AgentsManager): - resource_class = Agent - - def _build_update_body(self, version, url, md5hash): - return {'agent': { - 'version': version, - 'url': url, - 'md5hash': md5hash}} diff --git a/novaclient/v3/aggregates.py b/novaclient/v3/aggregates.py deleted file mode 100644 index b2728d6d1..000000000 --- a/novaclient/v3/aggregates.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Aggregate interface.""" - -from novaclient.v1_1 import aggregates - - -class Aggregate(aggregates.Aggregate): - pass - - -class AggregateManager(aggregates.AggregateManager): - resource_class = Aggregate diff --git a/novaclient/v3/availability_zones.py b/novaclient/v3/availability_zones.py deleted file mode 100644 index bd2a9d239..000000000 --- a/novaclient/v3/availability_zones.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# Copyright 2013 IBM Corp. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Availability Zone interface. -""" - -from novaclient.v1_1 import availability_zones - - -class AvailabilityZone(availability_zones.AvailabilityZone): - pass - - -class AvailabilityZoneManager(availability_zones.AvailabilityZoneManager): - """ - Manage :class:`AvailabilityZone` resources. - """ - resource_class = AvailabilityZone - return_parameter_name = 'availability_zone_info' diff --git a/novaclient/v3/certs.py b/novaclient/v3/certs.py deleted file mode 100644 index 5e6785108..000000000 --- a/novaclient/v3/certs.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2010 Jacob Kaplan-Moss - -# Copyright 2011 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Certificate interface. -""" - -from novaclient.v1_1 import certs - - -class Certificate(certs.Certificate): - pass - - -class CertificateManager(certs.CertificateManager): - pass diff --git a/novaclient/v3/client.py b/novaclient/v3/client.py deleted file mode 100644 index 21663ebc5..000000000 --- a/novaclient/v3/client.py +++ /dev/null @@ -1,196 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# Copyright 2013 IBM Corp. -# -# 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 novaclient import client -from novaclient.v3 import agents -from novaclient.v3 import aggregates -from novaclient.v3 import availability_zones -from novaclient.v3 import certs -from novaclient.v3 import flavor_access -from novaclient.v3 import flavors -from novaclient.v3 import hosts -from novaclient.v3 import hypervisors -from novaclient.v3 import images -from novaclient.v3 import keypairs -from novaclient.v3 import limits -from novaclient.v3 import list_extensions -from novaclient.v3 import quotas -from novaclient.v3 import servers -from novaclient.v3 import services -from novaclient.v3 import usage -from novaclient.v3 import volumes - - -class Client(object): - """ - Top-level object to access the OpenStack Compute API. - - Create an instance with your creds:: - - >>> client = Client(USERNAME, PASSWORD, PROJECT_ID, AUTH_URL) - - Or, alternatively, you can create a client instance using the - keystoneclient.session API:: - - >>> from keystoneclient.auth.identity import v2 - >>> from keystoneclient import session - >>> from novaclient.client import Client - >>> auth = v2.Password(auth_url=AUTH_URL, - username=USERNAME, - password=PASSWORD, - tenant_name=PROJECT_ID) - >>> sess = session.Session(auth=auth) - >>> nova = client.Client(VERSION, session=sess) - - Then call methods on its managers:: - - >>> client.servers.list() - ... - >>> client.flavors.list() - ... - - It is also possible to use an instance as a context manager in which - case there will be a session kept alive for the duration of the with - statement:: - - >>> with Client(USERNAME, PASSWORD, PROJECT_ID, AUTH_URL) as client: - ... client.servers.list() - ... client.flavors.list() - ... - - It is also possible to have a permanent (process-long) connection pool, - by passing a connection_pool=True:: - - >>> client = Client(USERNAME, PASSWORD, PROJECT_ID, - ... AUTH_URL, connection_pool=True) - """ - - def __init__(self, username=None, password=None, project_id=None, - auth_url=None, insecure=False, timeout=None, - proxy_tenant_id=None, proxy_token=None, region_name=None, - endpoint_type='publicURL', extensions=None, - service_type='computev3', service_name=None, - volume_service_name=None, timings=False, bypass_url=None, - os_cache=False, no_cache=True, http_log_debug=False, - auth_system='keystone', auth_plugin=None, auth_token=None, - cacert=None, tenant_id=None, user_id=None, - connection_pool=False, session=None, auth=None, - completion_cache=None): - # NOTE(cyeoh): In the novaclient context (unlike Nova) the - # project_id is not the same as the tenant_id. Here project_id - # is a name (what the Nova API often refers to as a project or - # tenant name) and tenant_id is a UUID (what the Nova API - # often refers to as a project_id or tenant_id). - - self.projectid = project_id - self.tenant_id = tenant_id - self.user_id = user_id - self.os_cache = os_cache or not no_cache - # TODO(bnemec): Add back in v3 extensions - self.agents = agents.AgentsManager(self) - self.aggregates = aggregates.AggregateManager(self) - self.availability_zones = \ - availability_zones.AvailabilityZoneManager(self) - self.certs = certs.CertificateManager(self) - self.list_extensions = list_extensions.ListExtManager(self) - self.hosts = hosts.HostManager(self) - self.flavors = flavors.FlavorManager(self) - self.flavor_access = flavor_access.FlavorAccessManager(self) - self.hypervisors = hypervisors.HypervisorManager(self) - self.images = images.ImageManager(self) - self.keypairs = keypairs.KeypairManager(self) - self.limits = limits.LimitsManager(self) - self.quotas = quotas.QuotaSetManager(self) - self.servers = servers.ServerManager(self) - self.services = services.ServiceManager(self) - self.usage = usage.UsageManager(self) - self.volumes = volumes.VolumeManager(self) - - # Add in any extensions... - if extensions: - for extension in extensions: - if extension.manager_class: - setattr(self, extension.name, - extension.manager_class(self)) - - self.client = client._construct_http_client( - username=username, - password=password, - user_id=user_id, - project_id=project_id, - tenant_id=tenant_id, - auth_url=auth_url, - auth_token=auth_token, - insecure=insecure, - timeout=timeout, - auth_system=auth_system, - auth_plugin=auth_plugin, - proxy_token=proxy_token, - proxy_tenant_id=proxy_tenant_id, - region_name=region_name, - endpoint_type=endpoint_type, - service_type=service_type, - service_name=service_name, - volume_service_name=volume_service_name, - timings=timings, - bypass_url=bypass_url, - os_cache=self.os_cache, - http_log_debug=http_log_debug, - cacert=cacert, - connection_pool=connection_pool, - session=session, - auth=auth) - - self.completion_cache = completion_cache - - def write_object_to_completion_cache(self, obj): - if self.completion_cache: - self.completion_cache.write_object(obj) - - def clear_completion_cache_for_class(self, obj_class): - if self.completion_cache: - self.completion_cache.clear_class(obj_class) - - @client._original_only - def __enter__(self): - self.client.open_session() - return self - - @client._original_only - def __exit__(self, t, v, tb): - self.client.close_session() - - @client._original_only - def set_management_url(self, url): - self.client.set_management_url(url) - - def get_timings(self): - return self.client.get_timings() - - def reset_timings(self): - self.client.reset_timings() - - @client._original_only - def authenticate(self): - """ - Authenticate against the server. - - Normally this is called automatically when you first access the API, - but you can call this method to force authentication right now. - - Returns on success; raises :exc:`exceptions.Unauthorized` if the - credentials are wrong. - """ - self.client.authenticate() diff --git a/novaclient/v3/flavor_access.py b/novaclient/v3/flavor_access.py deleted file mode 100644 index 6896dac36..000000000 --- a/novaclient/v3/flavor_access.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# Copyright 2013 IBM Corp. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -"""Flavor access interface.""" - -from novaclient import base -from novaclient.v1_1 import flavor_access - - -class FlavorAccess(flavor_access.FlavorAccess): - pass - - -class FlavorAccessManager(flavor_access.FlavorAccessManager): - """ - Manage :class:`FlavorAccess` resources. - """ - resource_class = FlavorAccess - - def _list_by_flavor(self, flavor): - return self._list('/flavors/%s/flavor-access' % base.getid(flavor), - 'flavor_access') - - def add_tenant_access(self, flavor, tenant): - """Add a tenant to the given flavor access list.""" - info = {'tenant_id': tenant} - return self._action('add_tenant_access', flavor, info) - - def remove_tenant_access(self, flavor, tenant): - """Remove a tenant from the given flavor access list.""" - info = {'tenant_id': tenant} - return self._action('remove_tenant_access', flavor, info) diff --git a/novaclient/v3/flavors.py b/novaclient/v3/flavors.py deleted file mode 100644 index 19f4a3f28..000000000 --- a/novaclient/v3/flavors.py +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright 2010 Jacob Kaplan-Moss -# Copyright 2013 IBM Corp. -# -# 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. - -""" -Flavor interface. -""" - -from novaclient import base -from novaclient import utils -from novaclient.v1_1 import flavors - - -class Flavor(base.Resource): - """ - A flavor is an available hardware configuration for a server. - """ - HUMAN_ID = True - - def __repr__(self): - return "" % self.name - - @property - def is_public(self): - """ - Provide a user-friendly accessor to flavor-access:is_public - """ - return self._info.get("flavor-access:is_public", 'N/A') - - def get_keys(self): - """ - Get extra specs from a flavor. - - :param flavor: The :class:`Flavor` to get extra specs from - """ - _resp, body = self.manager.api.client.get( - "/flavors/%s/flavor-extra-specs" % base.getid(self)) - return body["extra_specs"] - - def set_keys(self, metadata): - """ - Set extra specs on a flavor. - - :param flavor: The :class:`Flavor` to set extra spec on - :param metadata: A dict of key/value pairs to be set - """ - utils.validate_flavor_metadata_keys(metadata.keys()) - - body = {'extra_specs': metadata} - return self.manager._create( - "/flavors/%s/flavor-extra-specs" % base.getid(self), body, - "extra_specs", return_raw=True) - - def unset_keys(self, keys): - """ - Unset extra specs on a flavor. - - :param flavor: The :class:`Flavor` to unset extra spec on - :param keys: A list of keys to be unset - """ - for k in keys: - self.manager._delete( - "/flavors/%s/flavor-extra-specs/%s" % (base.getid(self), k)) - - def delete(self): - """ - Delete this flavor. - """ - self.manager.delete(self) - - -class FlavorManager(flavors.FlavorManager): - resource_class = Flavor - - def _build_body(self, name, ram, vcpus, disk, id, swap, - ephemeral, rxtx_factor, is_public): - return { - "flavor": { - "name": name, - "ram": ram, - "vcpus": vcpus, - "disk": disk, - "id": id, - "swap": swap, - "ephemeral": ephemeral, - "os-flavor-rxtx:rxtx_factor": rxtx_factor, - "flavor-access:is_public": is_public, - } - } diff --git a/novaclient/v3/hosts.py b/novaclient/v3/hosts.py deleted file mode 100644 index 174b62334..000000000 --- a/novaclient/v3/hosts.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright 2013 OpenStack Foundation -# -# 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. - -""" V3 API versions of the Hosts interface. - -Inherits from the 1.1 code because a lot of the functionality is shared. -""" - -from novaclient.v1_1 import hosts - - -Host = hosts.Host - - -class HostManager(hosts.HostManager): - def update(self, host, values): - """Update status or maintenance mode for the host.""" - body = dict(host=values) - return self._update("/os-hosts/%s" % host, body, response_key='host') - - def host_action(self, host, action): - """Perform an action on a host.""" - url = '/os-hosts/{0}/{1}'.format(host, action) - return self._get(url, response_key='host') - - def list(self, zone=None, service=None): - """List cloud hosts.""" - - filters = [] - if zone: - filters.append('zone=%s' % zone) - if service: - filters.append('service=%s' % service) - - if filters: - url = '/os-hosts?%s' % '&'.join(filters) - else: - url = '/os-hosts' - - return self._list(url, "hosts") diff --git a/novaclient/v3/hypervisors.py b/novaclient/v3/hypervisors.py deleted file mode 100644 index b04a60080..000000000 --- a/novaclient/v3/hypervisors.py +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright 2013 IBM Corp -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Hypervisors interface -""" - -from six.moves.urllib import parse - -from novaclient.v1_1 import hypervisors - - -class Hypervisor(hypervisors.Hypervisor): - pass - - -class HypervisorManager(hypervisors.HypervisorManager): - resource_class = Hypervisor - - def search(self, hypervisor_match): - """ - Get a list of matching hypervisors. - - :param servers: If True, server information is also retrieved. - """ - url = ('/os-hypervisors/search?query=%s' % - parse.quote(hypervisor_match, safe='')) - return self._list(url, 'hypervisors') - - def servers(self, hypervisor): - """ - Get servers for a specific hypervisor - - :param hypervisor: ID of hypervisor to get list of servers for. - """ - return self._get('/os-hypervisors/%s/servers' % hypervisor, - 'hypervisor') diff --git a/novaclient/v3/images.py b/novaclient/v3/images.py deleted file mode 100644 index 7bc3d584c..000000000 --- a/novaclient/v3/images.py +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright 2010 Jacob Kaplan-Moss -# Copyright 2013 IBM Corp. -# -# 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. - -""" -Image interface. -""" - -from oslo.utils import encodeutils -from oslo.utils import strutils -from six.moves.urllib import parse - -from novaclient import base - - -class Image(base.Resource): - """ - An image is a collection of files used to create or rebuild a server. - """ - HUMAN_ID = True - - def __repr__(self): - return "" % self.name - - def delete(self): - """ - Delete this image. - """ - self.manager.delete(self) - - -class ImageManager(base.ManagerWithFind): - """ - Manage :class:`Image` resources. - """ - resource_class = Image - # NOTE(cyeoh): Eventually we'll want novaclient to be smart - # enough to do version discovery, but for now we just request - # the v1 image API - image_api_prefix = '/v1' - - def _image_meta_from_headers(self, headers): - meta = {'properties': {}} - safe_decode = encodeutils.safe_decode - for key, value in headers.items(): - value = safe_decode(value, incoming='utf-8') - if key.startswith('x-image-meta-property-'): - _key = safe_decode(key[22:], incoming='utf-8') - meta['properties'][_key] = value - elif key.startswith('x-image-meta-'): - _key = safe_decode(key[13:], incoming='utf-8') - meta[_key] = value - - for key in ['is_public', 'protected', 'deleted']: - if key in meta: - meta[key] = strutils.bool_from_string(meta[key]) - - return self._format_image_meta_for_user(meta) - - @staticmethod - def _format_image_meta_for_user(meta): - for key in ['size', 'min_ram', 'min_disk']: - if key in meta: - try: - meta[key] = int(meta[key]) - except ValueError: - pass - return meta - - def get(self, image): - """ - Get an image. - - :param image: The ID of the image to get. - :rtype: :class:`Image` - """ - url = "%s/images/%s" % (self.image_api_prefix, base.getid(image)) - resp, _ = self.api.client._cs_request(url, 'HEAD') - foo = self._image_meta_from_headers(resp.headers) - return Image(self, foo) - - def list(self, detailed=True, limit=None): - """ - Get a list of all images. - - :rtype: list of :class:`Image` - :param limit: maximum number of images to return. - """ - params = {} - detail = '' - if detailed: - detail = '/detail' - if limit: - params['limit'] = int(limit) - query = '?%s' % parse.urlencode(params) if params else '' - return self._list('/v1/images%s%s' % (detail, query), 'images') diff --git a/novaclient/v3/keypairs.py b/novaclient/v3/keypairs.py deleted file mode 100644 index f1ef8ac58..000000000 --- a/novaclient/v3/keypairs.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright 2013 IBM Corp. -# -# 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. - -""" -Keypair interface -""" - -from novaclient.v1_1 import keypairs - - -class Keypair(keypairs.Keypair): - pass - - -class KeypairManager(keypairs.KeypairManager): - resource_class = Keypair - keypair_prefix = "keypairs" diff --git a/novaclient/v3/limits.py b/novaclient/v3/limits.py deleted file mode 100644 index ab8a4a279..000000000 --- a/novaclient/v3/limits.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# -# 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 six.moves.urllib import parse - -from novaclient.v1_1 import limits - - -class Limits(limits.Limits): - pass - - -class RateLimit(limits.RateLimit): - pass - - -class AbsoluteLimit(limits.AbsoluteLimit): - pass - - -class LimitsManager(limits.LimitsManager): - """Manager object used to interact with limits resource.""" - - resource_class = Limits - - def get(self, reserved=False, tenant_id=None): - """ - Get a specific extension. - - :rtype: :class:`Limits` - """ - opts = {} - if reserved: - opts['reserved'] = 1 - if tenant_id: - opts['tenant_id'] = tenant_id - query_string = "?%s" % parse.urlencode(opts) if opts else "" - - return self._get("/limits%s" % query_string, "limits") diff --git a/novaclient/v3/list_extensions.py b/novaclient/v3/list_extensions.py deleted file mode 100644 index bcc187494..000000000 --- a/novaclient/v3/list_extensions.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2014 NEC Corporation. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -""" -extension interface -""" - -from novaclient.v1_1.contrib import list_extensions - - -class ListExtResource(list_extensions.ListExtResource): - pass - - -class ListExtManager(list_extensions.ListExtManager): - pass diff --git a/novaclient/v3/quotas.py b/novaclient/v3/quotas.py deleted file mode 100644 index b55842daa..000000000 --- a/novaclient/v3/quotas.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from novaclient.v1_1 import quotas - - -class QuotaSet(quotas.QuotaSet): - pass - - -class QuotaSetManager(quotas.QuotaSetManager): - resource_class = QuotaSet - - def get(self, tenant_id, user_id=None, detail=False): - if detail: - detail_string = '/detail' - else: - detail_string = '' - - if hasattr(tenant_id, 'tenant_id'): - tenant_id = tenant_id.tenant_id - if user_id: - url = '/os-quota-sets/%s%s?user_id=%s' % (tenant_id, detail_string, - user_id) - else: - url = '/os-quota-sets/%s%s' % (tenant_id, detail_string) - return self._get(url, "quota_set") - - def _update_body(self, tenant_id, **kwargs): - return {'quota_set': kwargs} diff --git a/novaclient/v3/servers.py b/novaclient/v3/servers.py deleted file mode 100644 index 9ddc92fed..000000000 --- a/novaclient/v3/servers.py +++ /dev/null @@ -1,1022 +0,0 @@ -# Copyright 2010 Jacob Kaplan-Moss - -# Copyright 2011 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -Server interface. -""" - -import base64 - -from oslo.utils import encodeutils -import six -from six.moves.urllib import parse - -from novaclient import base -from novaclient import crypto -from novaclient.i18n import _ - - -REBOOT_SOFT, REBOOT_HARD = 'SOFT', 'HARD' - - -class Server(base.Resource): - HUMAN_ID = True - - def __repr__(self): - return "" % self.name - - def delete(self): - """ - Delete (i.e. shut down and delete the image) this server. - """ - self.manager.delete(self) - - def update(self, name=None): - """ - Update the name or the password for this server. - - :param name: Update the server's name. - :param password: Update the root password. - """ - self.manager.update(self, name=name) - - def get_console_output(self, length=None): - """ - Get text console log output from Server. - - :param length: The number of lines you would like to retrieve (as int) - """ - return self.manager.get_console_output(self, length) - - def get_vnc_console(self, console_type): - """ - Get vnc console for a Server. - - :param console_type: Type of console ('novnc' or 'xvpvnc') - """ - return self.manager.get_vnc_console(self, console_type) - - def get_spice_console(self, console_type): - """ - Get spice console for a Server. - - :param console_type: Type of console ('spice-html5') - """ - return self.manager.get_spice_console(self, console_type) - - def get_password(self, private_key): - """ - Get password for a Server. - - :param private_key: Path to private key file for decryption - """ - return self.manager.get_password(self, private_key) - - def clear_password(self): - """ - Get password for a Server. - - """ - return self.manager.clear_password(self) - - def add_fixed_ip(self, network_id): - """ - Add an IP address on a network. - - :param network_id: The ID of the network the IP should be on. - """ - self.manager.add_fixed_ip(self, network_id) - - def remove_floating_ip(self, address): - """ - Remove floating IP from an instance - - :param address: The ip address or FloatingIP to remove - """ - self.manager.remove_floating_ip(self, address) - - def stop(self): - """ - Stop -- Stop the running server. - """ - self.manager.stop(self) - - def force_delete(self): - """ - Force delete -- Force delete a server. - """ - self.manager.force_delete(self) - - def restore(self): - """ - Restore -- Restore a server in 'soft-deleted' state. - """ - self.manager.restore(self) - - def start(self): - """ - Start -- Start the paused server. - """ - self.manager.start(self) - - def pause(self): - """ - Pause -- Pause the running server. - """ - self.manager.pause(self) - - def unpause(self): - """ - Unpause -- Unpause the paused server. - """ - self.manager.unpause(self) - - def lock(self): - """ - Lock -- Lock the instance from certain operations. - """ - self.manager.lock(self) - - def unlock(self): - """ - Unlock -- Remove instance lock. - """ - self.manager.unlock(self) - - def suspend(self): - """ - Suspend -- Suspend the running server. - """ - self.manager.suspend(self) - - def resume(self): - """ - Resume -- Resume the suspended server. - """ - self.manager.resume(self) - - def rescue(self): - """ - Rescue -- Rescue the problematic server. - """ - return self.manager.rescue(self) - - def unrescue(self): - """ - Unrescue -- Unrescue the rescued server. - """ - self.manager.unrescue(self) - - def shelve(self): - """ - Shelve -- Shelve the server. - """ - self.manager.shelve(self) - - def shelve_offload(self): - """ - Shelve_offload -- Remove a shelved server from the compute node. - """ - self.manager.shelve_offload(self) - - def unshelve(self): - """ - Unshelve -- Unshelve the server. - """ - self.manager.unshelve(self) - - def diagnostics(self): - """Diagnostics -- Retrieve server diagnostics.""" - return self.manager.diagnostics(self) - - def migrate(self): - """ - Migrate a server to a new host. - """ - self.manager.migrate(self) - - def remove_fixed_ip(self, address): - """ - Remove an IP address. - - :param address: The IP address to remove. - """ - self.manager.remove_fixed_ip(self, address) - - def change_password(self, password): - """ - Update the password for a server. - """ - self.manager.change_password(self, password) - - def reboot(self, reboot_type=REBOOT_SOFT): - """ - Reboot the server. - - :param reboot_type: either :data:`REBOOT_SOFT` for a software-level - reboot, or `REBOOT_HARD` for a virtual power cycle hard reboot. - """ - self.manager.reboot(self, reboot_type) - - def rebuild(self, image, password=None, **kwargs): - """ - Rebuild -- shut down and then re-image -- this server. - - :param image: the :class:`Image` (or its ID) to re-image with. - :param password: string to set as password on the rebuilt server. - """ - return self.manager.rebuild(self, image, password=password, **kwargs) - - def resize(self, flavor, **kwargs): - """ - Resize the server's resources. - - :param flavor: the :class:`Flavor` (or its ID) to resize to. - - Until a resize event is confirmed with :meth:`confirm_resize`, the old - server will be kept around and you'll be able to roll back to the old - flavor quickly with :meth:`revert_resize`. All resizes are - automatically confirmed after 24 hours. - """ - self.manager.resize(self, flavor, **kwargs) - - def create_image(self, image_name, metadata=None): - """ - Create an image based on this server. - - :param image_name: The name to assign the newly create image. - :param metadata: Metadata to assign to the image. - """ - return self.manager.create_image(self, image_name, metadata) - - def backup(self, backup_name, backup_type, rotation): - """ - Backup a server instance. - - :param backup_name: Name of the backup image - :param backup_type: The backup type, like 'daily' or 'weekly' - :param rotation: Int parameter representing how many backups to - keep around. - """ - self.manager.backup(self, backup_name, backup_type, rotation) - - def confirm_resize(self): - """ - Confirm that the resize worked, thus removing the original server. - """ - self.manager.confirm_resize(self) - - def revert_resize(self): - """ - Revert a previous resize, switching back to the old server. - """ - self.manager.revert_resize(self) - - @property - def networks(self): - """ - Generate a simplified list of addresses - """ - networks = {} - try: - for network_label, address_list in self.addresses.items(): - networks[network_label] = [a['addr'] for a in address_list] - return networks - except Exception: - return {} - - def live_migrate(self, host=None, - block_migration=False, - disk_over_commit=False): - """ - Migrates a running instance to a new machine. - """ - self.manager.live_migrate(self, host, - block_migration, - disk_over_commit) - - def reset_state(self, state='error'): - """ - Reset the state of an instance to active or error. - """ - self.manager.reset_state(self, state) - - def reset_network(self): - """ - Reset network of an instance. - """ - self.manager.reset_network(self) - - def evacuate(self, host=None, on_shared_storage=True, password=None): - """ - Evacuate an instance from failed host to specified host. - - :param host: Name of the target host - :param on_shared_storage: Specifies whether instance files located - on shared storage - :param password: string to set as password on the evacuated server. - """ - return self.manager.evacuate(self, host, on_shared_storage, password) - - def interface_list(self): - """ - List interfaces attached to an instance. - """ - return self.manager.interface_list(self) - - def interface_attach(self, port_id, net_id, fixed_ip): - """ - Attach a network interface to an instance. - """ - return self.manager.interface_attach(self, port_id, net_id, fixed_ip) - - def interface_detach(self, port_id): - """ - Detach a network interface from an instance. - """ - return self.manager.interface_detach(self, port_id) - - -class ServerManager(base.BootingManagerWithFind): - resource_class = Server - - def _boot(self, resource_url, response_key, name, image, flavor, - meta=None, userdata=None, - reservation_id=None, return_raw=False, min_count=None, - max_count=None, security_groups=None, key_name=None, - availability_zone=None, block_device_mapping=None, - block_device_mapping_v2=None, nics=None, scheduler_hints=None, - config_drive=None, admin_pass=None, **kwargs): - """ - Create (boot) a new server. - - :param name: Something to name the server. - :param image: The :class:`Image` to boot with. - :param flavor: The :class:`Flavor` to boot onto. - :param meta: A dict of arbitrary key/value metadata to store for this - server. A maximum of five entries is allowed, and both - keys and values must be 255 characters or less. - :param reservation_id: a UUID for the set of servers being requested. - :param return_raw: If True, don't try to coearse the result into - a Resource object. - :param security_groups: list of security group names - :param key_name: (optional extension) name of keypair to inject into - the instance - :param availability_zone: Name of the availability zone for instance - placement. - :param block_device_mapping: A dict of block device mappings for this - server. - :param block_device_mapping_v2: A dict of block device mappings V2 for - this server. - :param nics: (optional extension) an ordered list of nics to be - added to this server, with information about - connected networks, fixed ips, etc. - :param scheduler_hints: (optional extension) arbitrary key-value pairs - specified by the client to help boot an instance. - :param config_drive: (optional extension) value for config drive - either boolean, or volume-id - :param admin_pass: admin password for the server. - """ - body = {"server": { - "name": name, - "image_ref": str(base.getid(image)) if image else '', - "flavor_ref": str(base.getid(flavor)), - }} - if userdata: - if hasattr(userdata, 'read'): - userdata = userdata.read() - - if six.PY3: - userdata = userdata.encode("utf-8") - else: - userdata = encodeutils.safe_encode(userdata) - - data = base64.b64encode(userdata).decode('utf-8') - body["server"]["user_data"] = data - if meta: - body["server"]["metadata"] = meta - if reservation_id: - body["server"][ - "os-multiple-create:return_reservation_id"] = reservation_id - if key_name: - body["server"]["key_name"] = key_name - if scheduler_hints: - body["server"][ - "os-scheduler-hints:scheduler_hints"] = scheduler_hints - if config_drive: - body["server"]["os-config-drive:config_drive"] = config_drive - if admin_pass: - body["server"]["admin_password"] = admin_pass - if not min_count: - min_count = 1 - if not max_count: - max_count = min_count - body["server"]["os-multiple-create:min_count"] = min_count - body["server"]["os-multiple-create:max_count"] = max_count - - if security_groups: - body["server"]["security_groups"] = [{'name': sg} - for sg in security_groups] - - if availability_zone: - body["server"][ - "os-availability-zone:availability_zone"] = availability_zone - - # Block device mappings are passed as a list of dictionaries - if block_device_mapping: - bdm_param = 'block_device_mapping' - body['server'][bdm_param] = \ - self._parse_block_device_mapping(block_device_mapping) - elif block_device_mapping_v2: - # Append the image to the list only if we have new style BDMs - bdm_param = 'block_device_mapping_v2' - if image: - bdm_dict = {'uuid': image.id, 'source_type': 'image', - 'destination_type': 'local', 'boot_index': 0, - 'delete_on_termination': True} - block_device_mapping_v2.insert(0, bdm_dict) - - body['server'][bdm_param] = block_device_mapping_v2 - - if nics is not None: - # NOTE(tr3buchet): nics can be an empty list - all_net_data = [] - for nic_info in nics: - net_data = {} - # if value is empty string, do not send value in body - if nic_info.get('net-id'): - net_data['uuid'] = nic_info['net-id'] - if (nic_info.get('v4-fixed-ip') and - nic_info.get('v6-fixed-ip')): - raise base.exceptions.CommandError(_( - "Only one of 'v4-fixed-ip' and 'v6-fixed-ip' may be" - " provided.")) - elif nic_info.get('v4-fixed-ip'): - net_data['fixed_ip'] = nic_info['v4-fixed-ip'] - elif nic_info.get('v6-fixed-ip'): - net_data['fixed_ip'] = nic_info['v6-fixed-ip'] - if nic_info.get('port-id'): - net_data['port'] = nic_info['port-id'] - all_net_data.append(net_data) - body['server']['networks'] = all_net_data - - return self._create(resource_url, body, response_key, - return_raw=return_raw, **kwargs) - - def get(self, server): - """ - Get a server. - - :param server: ID of the :class:`Server` to get. - :rtype: :class:`Server` - """ - return self._get("/servers/%s" % base.getid(server), "server") - - def list(self, detailed=True, search_opts=None, marker=None, limit=None): - """ - Get a list of servers. - - :param detailed: Whether to return detailed server info (optional). - :param search_opts: Search options to filter out servers (optional). - :param marker: Begin returning servers that appear later in the server - list than that represented by this server id (optional). - :param limit: Maximum number of servers to return (optional). - - :rtype: list of :class:`Server` - """ - if search_opts is None: - search_opts = {} - - qparams = {} - - for opt, val in six.iteritems(search_opts): - if val: - qparams[opt] = val - - if marker: - qparams['marker'] = marker - - if limit: - qparams['limit'] = limit - - # Transform the dict to a sequence of two-element tuples in fixed - # order, then the encoded string will be consistent in Python 2&3. - if qparams: - new_qparams = sorted(qparams.items(), key=lambda x: x[0]) - query_string = "?%s" % parse.urlencode(new_qparams) - else: - query_string = "" - - detail = "" - if detailed: - detail = "/detail" - return self._list("/servers%s%s" % (detail, query_string), "servers") - - def add_fixed_ip(self, server, network_id): - """ - Add an IP address on a network. - - :param server: The :class:`Server` (or its ID) to add an IP to. - :param network_id: The ID of the network the IP should be on. - """ - self._action('add_fixed_ip', server, {'network_id': network_id}) - - def remove_fixed_ip(self, server, address): - """ - Remove an IP address. - - :param server: The :class:`Server` (or its ID) to add an IP to. - :param address: The IP address to remove. - """ - self._action('remove_fixed_ip', server, {'address': address}) - - def get_vnc_console(self, server, console_type): - """ - Get a vnc console for an instance - - :param server: The :class:`Server` (or its ID) to add an IP to. - :param console_type: Type of vnc console to get ('novnc' or 'xvpvnc') - """ - - return self._action('get_vnc_console', server, - {'type': console_type})[1] - - def get_spice_console(self, server, console_type): - """ - Get a spice console for an instance - - :param server: The :class:`Server` (or its ID) to add an IP to. - :param console_type: Type of spice console to get ('spice-html5') - """ - - return self._action('get_spice_console', server, - {'type': console_type})[1] - - def get_password(self, server, private_key): - """ - Get password for an instance - - Requires that openssl is installed and in the path - - :param server: The :class:`Server` (or its ID) to add an IP to. - :param private_key: The private key to decrypt password - """ - - _resp, body = self.api.client.get("/servers/%s/os-server-password" - % base.getid(server)) - if body and body.get('password'): - try: - return crypto.decrypt_password(private_key, body['password']) - except Exception as exc: - return '%sFailed to decrypt:\n%s' % (exc, body['password']) - return '' - - def clear_password(self, server): - """ - Clear password for an instance - - :param server: The :class:`Server` (or its ID) to add an IP to. - """ - - return self._delete("/servers/%s/os-server-password" - % base.getid(server)) - - def stop(self, server): - """ - Stop the server. - """ - return self._action('stop', server, None) - - def force_delete(self, server): - """ - Force delete the server. - """ - return self._action('force_delete', server, None) - - def restore(self, server): - """ - Restore soft-deleted server. - """ - return self._action('restore', server, None) - - def start(self, server): - """ - Start the server. - """ - self._action('start', server, None) - - def pause(self, server): - """ - Pause the server. - """ - self._action('pause', server, None) - - def unpause(self, server): - """ - Unpause the server. - """ - self._action('unpause', server, None) - - def lock(self, server): - """ - Lock the server. - """ - self._action('lock', server, None) - - def unlock(self, server): - """ - Unlock the server. - """ - self._action('unlock', server, None) - - def suspend(self, server): - """ - Suspend the server. - """ - self._action('suspend', server, None) - - def resume(self, server): - """ - Resume the server. - """ - self._action('resume', server, None) - - def rescue(self, server): - """ - Rescue the server. - """ - return self._action('rescue', server, None) - - def unrescue(self, server): - """ - Unrescue the server. - """ - self._action('unrescue', server, None) - - def shelve(self, server): - """ - Shelve the server. - """ - self._action('shelve', server, None) - - def shelve_offload(self, server): - """ - Remove a shelved instance from the compute node. - """ - self._action('shelve_offload', server, None) - - def unshelve(self, server): - """ - Unshelve the server. - """ - self._action('unshelve', server, None) - - def diagnostics(self, server): - """Retrieve server diagnostics.""" - return self.api.client.get("/servers/%s/os-server-diagnostics" % - base.getid(server)) - - def create(self, name, image, flavor, meta=None, files=None, - reservation_id=None, min_count=None, - max_count=None, security_groups=None, userdata=None, - key_name=None, availability_zone=None, - block_device_mapping=None, block_device_mapping_v2=None, - nics=None, scheduler_hints=None, - config_drive=None, **kwargs): - # TODO(anthony): indicate in doc string if param is an extension - # and/or optional - """ - Create (boot) a new server. - - :param name: Something to name the server. - :param image: The :class:`Image` to boot with. - :param flavor: The :class:`Flavor` to boot onto. - :param meta: A dict of arbitrary key/value metadata to store for this - server. A maximum of five entries is allowed, and both - keys and values must be 255 characters or less. - :param files: A dict of files to overrwrite on the server upon boot. - Keys are file names (i.e. ``/etc/passwd``) and values - are the file contents (either as a string or as a - file-like object). A maximum of five entries is allowed, - and each file must be 10k or less. - :param userdata: user data to pass to be exposed by the metadata - server this can be a file type object as well or a - string. - :param reservation_id: a UUID for the set of servers being requested. - :param key_name: (optional extension) name of previously created - keypair to inject into the instance. - :param availability_zone: Name of the availability zone for instance - placement. - :param block_device_mapping: (optional extension) A dict of block - device mappings for this server. - :param block_device_mapping_v2: (optional extension) A dict of block - device mappings for this server. - :param nics: (optional extension) an ordered list of nics to be - added to this server, with information about - connected networks, fixed ips, port etc. - :param scheduler_hints: (optional extension) arbitrary key-value pairs - specified by the client to help boot an instance - :param config_drive: (optional extension) value for config drive - either boolean, or volume-id - """ - if not min_count: - min_count = 1 - if not max_count: - max_count = min_count - if min_count > max_count: - min_count = max_count - - boot_args = [name, image, flavor] - - boot_kwargs = dict( - meta=meta, files=files, userdata=userdata, - reservation_id=reservation_id, min_count=min_count, - max_count=max_count, security_groups=security_groups, - key_name=key_name, availability_zone=availability_zone, - scheduler_hints=scheduler_hints, config_drive=config_drive, - **kwargs) - - if block_device_mapping: - boot_kwargs['block_device_mapping'] = block_device_mapping - elif block_device_mapping_v2: - boot_kwargs['block_device_mapping_v2'] = block_device_mapping_v2 - resource_url = "/servers" - if nics: - boot_kwargs['nics'] = nics - - response_key = "server" - return self._boot(resource_url, response_key, *boot_args, - **boot_kwargs) - - def update(self, server, name=None): - """ - Update the name or the password for a server. - - :param server: The :class:`Server` (or its ID) to update. - :param name: Update the server's name. - """ - if name is None: - return - - body = { - "server": { - "name": name, - }, - } - - return self._update("/servers/%s" % base.getid(server), body, "server") - - def change_password(self, server, password): - """ - Update the password for a server. - """ - self._action("change_password", server, {"admin_password": password}) - - def delete(self, server): - """ - Delete (i.e. shut down and delete the image) this server. - """ - self._delete("/servers/%s" % base.getid(server)) - - def reboot(self, server, reboot_type=REBOOT_SOFT): - """ - Reboot a server. - - :param server: The :class:`Server` (or its ID) to share onto. - :param reboot_type: either :data:`REBOOT_SOFT` for a software-level - reboot, or `REBOOT_HARD` for a virtual power cycle hard reboot. - """ - self._action('reboot', server, {'type': reboot_type}) - - def rebuild(self, server, image, password=None, **kwargs): - """ - Rebuild -- shut down and then re-image -- a server. - - :param server: The :class:`Server` (or its ID) to share onto. - :param image: the :class:`Image` (or its ID) to re-image with. - :param password: string to set as password on the rebuilt server. - """ - body = {'image_ref': base.getid(image)} - if password is not None: - body['admin_password'] = password - - _resp, body = self._action('rebuild', server, body, **kwargs) - return Server(self, body['server']) - - def migrate(self, server): - """ - Migrate a server to a new host. - - :param server: The :class:`Server` (or its ID). - """ - self._action('migrate', server) - - def resize(self, server, flavor, **kwargs): - """ - Resize a server's resources. - - :param server: The :class:`Server` (or its ID) to share onto. - :param flavor: the :class:`Flavor` (or its ID) to resize to. - - Until a resize event is confirmed with :meth:`confirm_resize`, the old - server will be kept around and you'll be able to roll back to the old - flavor quickly with :meth:`revert_resize`. All resizes are - automatically confirmed after 24 hours. - """ - info = {'flavor_ref': base.getid(flavor)} - - self._action('resize', server, info=info, **kwargs) - - def confirm_resize(self, server): - """ - Confirm that the resize worked, thus removing the original server. - - :param server: The :class:`Server` (or its ID) to share onto. - """ - self._action('confirm_resize', server) - - def revert_resize(self, server): - """ - Revert a previous resize, switching back to the old server. - - :param server: The :class:`Server` (or its ID) to share onto. - """ - self._action('revert_resize', server) - - def create_image(self, server, image_name, metadata=None): - """ - Snapshot a server. - - :param server: The :class:`Server` (or its ID) to share onto. - :param image_name: Name to give the snapshot image - :param meta: Metadata to give newly-created image entity - """ - body = {'name': image_name, 'metadata': metadata or {}} - resp = self._action('create_image', server, body)[0] - location = resp.headers['location'] - image_uuid = location.split('/')[-1] - return image_uuid - - def backup(self, server, backup_name, backup_type, rotation): - """ - Backup a server instance. - - :param server: The :class:`Server` (or its ID) to share onto. - :param backup_name: Name of the backup image - :param backup_type: The backup type, like 'daily' or 'weekly' - :param rotation: Int parameter representing how many backups to - keep around. - """ - body = {'name': backup_name, - 'backup_type': backup_type, - 'rotation': rotation} - self._action('create_backup', server, body) - - def set_meta(self, server, metadata): - """ - Set a servers metadata - :param server: The :class:`Server` to add metadata to - :param metadata: A dict of metadata to add to the server - """ - body = {'metadata': metadata} - return self._create( - "/servers/%s/metadata" % base.getid(server), body, "metadata") - - def get_console_output(self, server, length=None): - """ - Get text console log output from Server. - - :param server: The :class:`Server` (or its ID) whose console output - you would like to retrieve. - :param length: The number of tail loglines you would like to retrieve. - """ - if length is None: - # NOTE: On v3 get_console_output API, -1 means an unlimited length. - # Here translates None, which means an unlimited in the internal - # implementation, to -1. - length = -1 - return self._action('get_console_output', - server, {'length': length})[1]['output'] - - def delete_meta(self, server, keys): - """ - Delete metadata from an server - :param server: The :class:`Server` to add metadata to - :param keys: A list of metadata keys to delete from the server - """ - for k in keys: - self._delete("/servers/%s/metadata/%s" % (base.getid(server), k)) - - def live_migrate(self, server, host, block_migration, disk_over_commit): - """ - Migrates a running instance to a new machine. - - :param server: instance id which comes from nova list. - :param host: destination host name. - :param block_migration: if True, do block_migration. - :param disk_over_commit: if True, Allow overcommit. - - """ - self._action('migrate_live', server, - {'host': host, - 'block_migration': block_migration, - 'disk_over_commit': disk_over_commit}) - - def reset_state(self, server, state='error'): - """ - Reset the state of an instance to active or error. - - :param server: ID of the instance to reset the state of. - :param state: Desired state; either 'active' or 'error'. - Defaults to 'error'. - """ - self._action('reset_state', server, dict(state=state)) - - def reset_network(self, server): - """ - Reset network of an instance. - """ - self._action('reset_network', server) - - def evacuate(self, server, host=None, - on_shared_storage=True, password=None): - """ - Evacuate a server instance. - - :param server: The :class:`Server` (or its ID) to share onto. - :param host: Name of the target host. - :param on_shared_storage: Specifies whether instance files located - on shared storage - :param password: string to set as password on the evacuated server. - """ - body = {'on_shared_storage': on_shared_storage} - if host is not None: - body['host'] = host - - if password is not None: - body['admin_password'] = password - - return self._action('evacuate', server, body) - - def interface_list(self, server): - """ - List attached network interfaces - - :param server: The :class:`Server` (or its ID) to query. - """ - return self._list('/servers/%s/os-attach-interfaces' - % base.getid(server), 'interface_attachments') - - def interface_attach(self, server, port_id, net_id, fixed_ip): - """ - Attach a network_interface to an instance. - - :param server: The :class:`Server` (or its ID) to attach to. - :param port_id: The port to attach. - """ - - body = {'interface_attachment': {}} - if port_id: - body['interface_attachment']['port_id'] = port_id - if net_id: - body['interface_attachment']['net_id'] = net_id - if fixed_ip: - body['interface_attachment']['fixed_ips'] = [ - {'ip_address': fixed_ip}] - - return self._create('/servers/%s/os-attach-interfaces' - % base.getid(server), - body, 'interface_attachment') - - def interface_detach(self, server, port_id): - """ - Detach a network_interface from an instance. - - :param server: The :class:`Server` (or its ID) to detach from. - :param port_id: The port to detach. - """ - self._delete('/servers/%s/os-attach-interfaces/%s' - % (base.getid(server), port_id)) - - def _action(self, action, server, info=None, **kwargs): - """ - Perform a server "action" -- reboot/rebuild/resize/etc. - """ - body = {action: info} - self.run_hooks('modify_body_for_action', body, **kwargs) - url = '/servers/%s/action' % base.getid(server) - return self.api.client.post(url, body=body) diff --git a/novaclient/v3/services.py b/novaclient/v3/services.py deleted file mode 100644 index fe0f3a6a4..000000000 --- a/novaclient/v3/services.py +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright 2013 IBM Corp. -# -# 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. - -""" -service interface -""" -from novaclient.v1_1 import services - - -class Service(services.Service): - pass - - -class ServiceManager(services.ServiceManager): - resource_class = Service - - def _update_body(self, host, binary, disabled_reason=None): - body = {"service": - {"host": host, - "binary": binary}} - if disabled_reason is not None: - body["service"]["disabled_reason"] = disabled_reason - return body diff --git a/novaclient/v3/shell.py b/novaclient/v3/shell.py deleted file mode 100644 index 1c35ed6d5..000000000 --- a/novaclient/v3/shell.py +++ /dev/null @@ -1,3415 +0,0 @@ -# Copyright 2010 Jacob Kaplan-Moss - -# Copyright 2011 OpenStack Foundation -# Copyright 2013 IBM Corp. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from __future__ import print_function - -import argparse -import copy -import datetime -import getpass -import locale -import os -import sys -import time - -from oslo.utils import strutils -from oslo.utils import timeutils -import six - -from novaclient import client -from novaclient import exceptions -from novaclient.i18n import _ -from novaclient.openstack.common import cliutils -from novaclient.openstack.common import uuidutils -from novaclient import utils -from novaclient.v3 import availability_zones -from novaclient.v3 import servers - - -def _key_value_pairing(text): - try: - (k, v) = text.split('=', 1) - return (k, v) - except ValueError: - msg = "%r is not in the format of key=value" % text - raise argparse.ArgumentTypeError(msg) - - -def _match_image(cs, wanted_properties): - image_list = cs.images.list() - images_matched = [] - match = set(wanted_properties) - for img in image_list: - try: - if match == match.intersection(set(img.metadata.items())): - images_matched.append(img) - except AttributeError: - pass - return images_matched - - -def _boot(cs, args): - """Boot a new server.""" - if args.image: - image = _find_image(cs.image_cs, args.image) - else: - image = None - - if not image and args.image_with: - images = _match_image(cs.image_cs, args.image_with) - if images: - # TODO(harlowja): log a warning that we - # are selecting the first of many? - image = images[0] - - if not image and not args.block_device_mapping: - raise exceptions.CommandError("you need to specify an Image ID " - "or a block device mapping " - "or provide a set of properties to match" - " against an image") - if not args.flavor: - raise exceptions.CommandError("you need to specify a Flavor ID ") - - min_count = 1 - max_count = 1 - # Don't let user mix num_instances and max_count/min_count. - if (args.num_instances is not None and - args.min_count is None and - args.max_count is None): - if args.num_instances < 1: - raise exceptions.CommandError("num_instances should be >= 1") - max_count = args.num_instances - elif (args.num_instances is not None and - (args.min_count is not None or args.max_count is not None)): - raise exceptions.CommandError( - "Don't mix num-instances and max/min-count") - if args.min_count is not None: - if args.min_count < 1: - raise exceptions.CommandError("min_count should be >= 1") - min_count = args.min_count - max_count = min_count - if args.max_count is not None: - if args.max_count < 1: - raise exceptions.CommandError("max_count should be >= 1") - max_count = args.max_count - if (args.min_count is not None and - args.max_count is not None and - args.min_count > args.max_count): - raise exceptions.CommandError("min_count should be <= max_count") - - flavor = _find_flavor(cs, args.flavor) - - meta = dict(v.split('=', 1) for v in args.meta) - - files = {} - for f in args.files: - try: - dst, src = f.split('=', 1) - files[dst] = open(src) - except IOError as e: - raise exceptions.CommandError("Can't open '%s': %s" % (src, e)) - except ValueError as e: - raise exceptions.CommandError( - "Invalid file argument '%s'. File " - "arguments must be of the form '--file '" - % f) - - # use the os-keypair extension - key_name = None - if args.key_name is not None: - key_name = args.key_name - - if args.user_data: - try: - userdata = open(args.user_data) - except IOError as e: - raise exceptions.CommandError("Can't open '%s': %s" % - (args.user_data, e)) - else: - userdata = None - - if args.availability_zone: - availability_zone = args.availability_zone - else: - availability_zone = None - - if args.security_groups: - security_groups = args.security_groups.split(',') - else: - security_groups = None - - block_device_mapping = {} - for bdm in args.block_device_mapping: - device_name, mapping = bdm.split('=', 1) - block_device_mapping[device_name] = mapping - - nics = [] - for nic_str in args.nics: - err_msg = ("Invalid nic argument '%s'. Nic arguments must be of the " - "form --nic , with at minimum " - "net-id or port-id (but not both) specified." % nic_str) - nic_info = {"net-id": "", "v4-fixed-ip": "", "v6-fixed-ip": "", - "port-id": ""} - - for kv_str in nic_str.split(","): - try: - k, v = kv_str.split("=", 1) - except ValueError as e: - raise exceptions.CommandError(err_msg) - - if k in nic_info: - nic_info[k] = v - else: - raise exceptions.CommandError(err_msg) - - if bool(nic_info['net-id']) == bool(nic_info['port-id']): - raise exceptions.CommandError(err_msg) - - nics.append(nic_info) - - hints = {} - if args.scheduler_hints: - for hint in args.scheduler_hints: - key, _sep, value = hint.partition('=') - # NOTE(vish): multiple copies of the same hint will - # result in a list of values - if key in hints: - if isinstance(hints[key], six.string_types): - hints[key] = [hints[key]] - hints[key] += [value] - else: - hints[key] = value - boot_args = [args.name, image, flavor] - - if str(args.config_drive).lower() in ("true", "1"): - config_drive = True - elif str(args.config_drive).lower() in ("false", "0", "", "none"): - config_drive = None - else: - config_drive = args.config_drive - - boot_kwargs = dict( - meta=meta, - files=files, - key_name=key_name, - min_count=min_count, - max_count=max_count, - userdata=userdata, - availability_zone=availability_zone, - security_groups=security_groups, - block_device_mapping=block_device_mapping, - nics=nics, - scheduler_hints=hints, - config_drive=config_drive) - - return boot_args, boot_kwargs - - -@utils.arg( - '--flavor', - default=None, - metavar='', - help="Flavor ID (see 'nova flavor-list').") -@utils.arg( - '--image', - default=None, - metavar='', - help="Image ID (see 'nova image-list'). ") -@utils.arg( - '--image-with', - default=[], - type=_key_value_pairing, - action='append', - metavar='', - help="Image metadata property (see 'nova image-show'). ") -@utils.arg( - '--num-instances', - default=None, - type=int, - metavar='', - help=argparse.SUPPRESS) -@utils.arg( - '--min-count', - default=None, - type=int, - metavar='', - help="Boot at least servers (limited by quota).") -@utils.arg( - '--max-count', - default=None, - type=int, - metavar='', - help="Boot up to servers (limited by quota).") -@utils.arg( - '--meta', - metavar="", - action='append', - default=[], - help="Record arbitrary key/value metadata to /meta.js " - "on the new server. Can be specified multiple times.") -@utils.arg( - '--file', - metavar="", - action='append', - dest='files', - default=[], - help="Store arbitrary files from locally to " - "on the new server. You may store up to 5 files.") -@utils.arg( - '--key-name', - default=os.environ.get('NOVACLIENT_DEFAULT_KEY_NAME'), - metavar='', - help="Key name of keypair that should be created earlier with \ - the command keypair-add") -@utils.arg( - '--key_name', - help=argparse.SUPPRESS) -@utils.arg('name', metavar='', help='Name for the new server') -@utils.arg( - '--user-data', - default=None, - metavar='', - help="user data file to pass to be exposed by the metadata server.") -@utils.arg( - '--user_data', - help=argparse.SUPPRESS) -@utils.arg( - '--availability-zone', - default=None, - metavar='', - help="The availability zone for server placement.") -@utils.arg( - '--availability_zone', - help=argparse.SUPPRESS) -@utils.arg( - '--security-groups', - default=None, - metavar='', - help="Comma separated list of security group names.") -@utils.arg( - '--security_groups', - help=argparse.SUPPRESS) -@utils.arg( - '--block-device-mapping', - metavar="", - action='append', - default=[], - help="Block device mapping in the format " - "=:::.") -@utils.arg( - '--block_device_mapping', - action='append', - help=argparse.SUPPRESS) -@utils.arg( - '--hint', - action='append', - dest='scheduler_hints', - default=[], - metavar='', - help="Send arbitrary key/value pairs to the scheduler for custom use.") -@utils.arg( - '--nic', - metavar="", - action='append', - dest='nics', - default=[], - help="Create a NIC on the server. " - "Specify option multiple times to create multiple NICs. " - "net-id: attach NIC to network with this UUID " - "(either port-id or net-id must be provided), " - "v4-fixed-ip: IPv4 fixed address for NIC (optional), " - "v6-fixed-ip: IPv6 fixed address for NIC (optional), " - "port-id: attach NIC to port with this UUID " - "(either port-id or net-id must be provided).") -@utils.arg( - '--config-drive', - metavar="", - dest='config_drive', - default=False, - help="Enable config drive") -@utils.arg( - '--poll', - dest='poll', - action="store_true", - default=False, - help='Report the new server boot progress until it completes.') -def do_boot(cs, args): - """Boot a new server.""" - boot_args, boot_kwargs = _boot(cs, args) - - extra_boot_kwargs = utils.get_resource_manager_extra_kwargs(do_boot, args) - boot_kwargs.update(extra_boot_kwargs) - - server = cs.servers.create(*boot_args, **boot_kwargs) - _print_server(cs, args, server) - - if args.poll: - _poll_for_status(cs.servers.get, server.id, 'building', ['active']) - - -def _poll_for_status(poll_fn, obj_id, action, final_ok_states, - poll_period=5, show_progress=True, - status_field="status", silent=False): - """Block while an action is being performed, periodically printing - progress. - """ - def print_progress(progress): - if show_progress: - msg = ('\rServer %(action)s... %(progress)s%% complete' - % dict(action=action, progress=progress)) - else: - msg = '\rServer %(action)s...' % dict(action=action) - - sys.stdout.write(msg) - sys.stdout.flush() - - if not silent: - print() - - while True: - obj = poll_fn(obj_id) - - status = getattr(obj, status_field) - - if status: - status = status.lower() - - progress = getattr(obj, 'progress', None) or 0 - if status in final_ok_states: - if not silent: - print_progress(100) - print("\nFinished") - break - elif status == "error": - if not silent: - print("\nError %s server" % action) - raise exceptions.InstanceInErrorState(obj.fault['message']) - - if not silent: - print_progress(progress) - - time.sleep(poll_period) - - -def _translate_keys(collection, convert): - for item in collection: - keys = item.__dict__.keys() - for from_key, to_key in convert: - if from_key in keys and to_key not in keys: - setattr(item, to_key, item._info[from_key]) - - -def _translate_extended_states(collection): - power_states = [ - 'NOSTATE', # 0x00 - 'Running', # 0x01 - '', # 0x02 - 'Paused', # 0x03 - 'Shutdown', # 0x04 - '', # 0x05 - 'Crashed', # 0x06 - 'Suspended' # 0x07 - ] - - for item in collection: - try: - setattr(item, 'power_state', - power_states[getattr(item, 'power_state')]) - except AttributeError: - setattr(item, 'power_state', "N/A") - try: - getattr(item, 'task_state') - except AttributeError: - setattr(item, 'task_state', "N/A") - - -def _translate_flavor_keys(collection): - _translate_keys(collection, [('ram', 'memory_mb')]) - - -def _print_flavor_extra_specs(flavor): - try: - return flavor.get_keys() - except exceptions.NotFound: - return "N/A" - - -def _print_flavor_list(flavors, show_extra_specs=False): - _translate_flavor_keys(flavors) - - headers = [ - 'ID', - 'Name', - 'Memory_MB', - 'Disk', - 'Ephemeral', - 'Swap', - 'VCPUs', - 'RXTX_Factor', - 'Is_Public', - ] - - if show_extra_specs: - formatters = {'extra_specs': _print_flavor_extra_specs} - headers.append('extra_specs') - else: - formatters = {} - - utils.print_list(flavors, headers, formatters) - - -@utils.arg('--extra-specs', - dest='extra_specs', - action='store_true', - default=False, - help='Get extra-specs of each flavor.') -@utils.arg('--all', - dest='all', - action='store_true', - default=False, - help='Display all flavors (Admin only).') -def do_flavor_list(cs, args): - """Print a list of available 'flavors' (sizes of servers).""" - if args.all: - flavors = cs.flavors.list(is_public=None) - else: - flavors = cs.flavors.list() - _print_flavor_list(flavors, args.extra_specs) - - -@utils.arg( - 'flavor', - metavar='', - help="Name or ID of the flavor to delete") -def do_flavor_delete(cs, args): - """Delete a specific flavor""" - flavorid = _find_flavor(cs, args.flavor) - cs.flavors.delete(flavorid) - _print_flavor_list([flavorid]) - - -@utils.arg( - 'flavor', - metavar='', - help="Name or ID of flavor") -def do_flavor_show(cs, args): - """Show details about the given flavor.""" - flavor = _find_flavor(cs, args.flavor) - _print_flavor(flavor) - - -@utils.arg( - 'name', - metavar='', - help="Name of the new flavor") -@utils.arg( - 'id', - metavar='', - help="Unique ID (integer or UUID) for the new flavor." - " If specifying 'auto', a UUID will be generated as id") -@utils.arg( - 'ram', - metavar='', - help="Memory size in MB") -@utils.arg( - 'disk', - metavar='', - help="Disk size in GB") -@utils.arg( - '--ephemeral', - metavar='', - help="Ephemeral space size in GB (default 0)", - default=0) -@utils.arg( - 'vcpus', - metavar='', - help="Number of vcpus") -@utils.arg( - '--swap', - metavar='', - help="Swap space size in MB (default 0)", - default=0) -@utils.arg( - '--rxtx-factor', - metavar='', - help="RX/TX factor (default 1)", - default=1.0) -@utils.arg( - '--is-public', - metavar='', - help="Make flavor accessible to the public (default true)", - type=lambda v: strutils.bool_from_string(v, True), - default=True) -def do_flavor_create(cs, args): - """Create a new flavor""" - f = cs.flavors.create(args.name, args.ram, args.vcpus, args.disk, args.id, - args.ephemeral, args.swap, args.rxtx_factor, - args.is_public) - _print_flavor_list([f]) - - -@utils.arg( - 'flavor', - metavar='', - help="Name or ID of flavor") -@utils.arg( - 'action', - metavar='', - choices=['set', 'unset'], - help="Actions: 'set' or 'unset'") -@utils.arg( - 'metadata', - metavar='', - nargs='+', - action='append', - default=[], - help='Extra_specs to set/unset (only key is necessary on unset)') -def do_flavor_key(cs, args): - """Set or unset extra_spec for a flavor.""" - flavor = _find_flavor(cs, args.flavor) - keypair = _extract_metadata(args) - - if args.action == 'set': - flavor.set_keys(keypair) - elif args.action == 'unset': - flavor.unset_keys(keypair.keys()) - - -@utils.arg( - '--flavor', - metavar='', - help="Filter results by flavor name or ID.") -@utils.arg('--tenant', metavar='', - help='Filter results by tenant ID.') -def do_flavor_access_list(cs, args): - """Print access information about the given flavor.""" - if args.flavor and args.tenant: - raise exceptions.CommandError("Unable to filter results by " - "both --flavor and --tenant.") - elif args.flavor: - flavor = _find_flavor(cs, args.flavor) - if flavor.is_public: - raise exceptions.CommandError("Failed to get access list " - "for public flavor type.") - kwargs = {'flavor': flavor} - elif args.tenant: - kwargs = {'tenant': args.tenant} - else: - raise exceptions.CommandError("Unable to get all access lists. " - "Specify --flavor or --tenant") - - try: - access_list = cs.flavor_access.list(**kwargs) - except NotImplementedError as e: - raise exceptions.CommandError("%s" % str(e)) - - columns = ['Flavor_ID', 'Tenant_ID'] - utils.print_list(access_list, columns) - - -@utils.arg( - 'flavor', - metavar='', - help="Flavor name or ID to add access for the given tenant.") -@utils.arg('tenant', metavar='', - help='Tenant ID to add flavor access for.') -def do_flavor_access_add(cs, args): - """Add flavor access for the given tenant.""" - flavor = _find_flavor(cs, args.flavor) - access_list = cs.flavor_access.add_tenant_access(flavor, args.tenant) - columns = ['Flavor_ID', 'Tenant_ID'] - utils.print_list(access_list, columns) - - -@utils.arg( - 'flavor', - metavar='', - help="Flavor name or ID to remove access for the given tenant.") -@utils.arg('tenant', metavar='', - help='Tenant ID to remove flavor access for.') -def do_flavor_access_remove(cs, args): - """Remove flavor access for the given tenant.""" - flavor = _find_flavor(cs, args.flavor) - access_list = cs.flavor_access.remove_tenant_access(flavor, args.tenant) - columns = ['Flavor_ID', 'Tenant_ID'] - utils.print_list(access_list, columns) - - -@utils.arg('project_id', metavar='', - help='The ID of the project.') -def do_scrub(cs, args): - """Delete networks and security groups associated with a project.""" - networks_list = cs.networks.list() - networks_list = [network for network in networks_list - if getattr(network, 'project_id', '') == args.project_id] - search_opts = {'all_tenants': 1} - groups = cs.security_groups.list(search_opts) - groups = [group for group in groups - if group.tenant_id == args.project_id] - for network in networks_list: - cs.networks.disassociate(network) - for group in groups: - cs.security_groups.delete(group) - - -def do_network_list(cs, _args): - """Print a list of available networks.""" - network_list = cs.networks.list() - columns = ['ID', 'Label', 'Cidr'] - utils.print_list(network_list, columns) - - -@utils.arg( - 'network', - metavar='', - help="uuid or label of network") -def do_network_show(cs, args): - """Show details about the given network.""" - network = utils.find_resource(cs.networks, args.network) - utils.print_dict(network._info) - - -@utils.arg('--host-only', - dest='host_only', - metavar='<0|1>', - nargs='?', - type=int, - const=1, - default=0) -@utils.arg('--project-only', - dest='project_only', - metavar='<0|1>', - nargs='?', - type=int, - const=1, - default=0) -@utils.arg( - 'network', - metavar='', - help="uuid of network") -def do_network_disassociate(cs, args): - """Disassociate host and/or project from the given network.""" - if args.host_only: - cs.networks.disassociate(args.network, True, False) - elif args.project_only: - cs.networks.disassociate(args.network, False, True) - else: - cs.networks.disassociate(args.network, True, True) - - -@utils.arg( - 'network', - metavar='', - help="uuid of network") -@utils.arg( - 'host', - metavar='', - help="Name of host") -def do_network_associate_host(cs, args): - """Associate host with network.""" - cs.networks.associate_host(args.network, args.host) - - -@utils.arg( - 'network', - metavar='', - help="uuid of network") -def do_network_associate_project(cs, args): - """Associate project with network.""" - cs.networks.associate_project(args.network) - - -def _filter_network_create_options(args): - valid_args = ['label', 'cidr', 'vlan_start', 'vpn_start', 'cidr_v6', - 'gateway', 'gateway_v6', 'bridge', 'bridge_interface', - 'multi_host', 'dns1', 'dns2', 'uuid', 'fixed_cidr', - 'project_id', 'priority'] - kwargs = {} - for k, v in args.__dict__.items(): - if k in valid_args and v is not None: - kwargs[k] = v - - return kwargs - - -@utils.arg( - 'label', - metavar='', - help="Label for network") -@utils.arg( - '--fixed-range-v4', - dest='cidr', - metavar='', - help="IPv4 subnet (ex: 10.0.0.0/8)") -@utils.arg( - '--fixed-range-v6', - dest="cidr_v6", - help='IPv6 subnet (ex: fe80::/64') -@utils.arg( - '--vlan', - dest='vlan_start', - metavar='', - help="vlan id") -@utils.arg( - '--vpn', - dest='vpn_start', - metavar='', - help="vpn start") -@utils.arg( - '--gateway', - dest="gateway", - help='gateway') -@utils.arg( - '--gateway-v6', - dest="gateway_v6", - help='IPv6 gateway') -@utils.arg( - '--bridge', - dest="bridge", - metavar='', - help='VIFs on this network are connected to this bridge.') -@utils.arg( - '--bridge-interface', - dest="bridge_interface", - metavar='', - help='The bridge is connected to this interface.') -@utils.arg( - '--multi-host', - dest="multi_host", - metavar="<'T'|'F'>", - help='Multi host') -@utils.arg( - '--dns1', - dest="dns1", - metavar="", help='First DNS') -@utils.arg( - '--dns2', - dest="dns2", - metavar="", - help='Second DNS') -@utils.arg( - '--uuid', - dest="uuid", - metavar="", - help='Network UUID') -@utils.arg( - '--fixed-cidr', - dest="fixed_cidr", - metavar='', - help='IPv4 subnet for fixed IPs (ex: 10.20.0.0/16)') -@utils.arg( - '--project-id', - dest="project_id", - metavar="", - help='Project ID') -@utils.arg( - '--priority', - dest="priority", - metavar="", - help='Network interface priority') -def do_network_create(cs, args): - """Create a network.""" - - if not (args.cidr or args.cidr_v6): - raise exceptions.CommandError( - "Must specify either fixed_range_v4 or fixed_range_v6") - kwargs = _filter_network_create_options(args) - if args.multi_host is not None: - kwargs['multi_host'] = bool(args.multi_host == 'T' or - strutils.bool_from_string(args.multi_host)) - - cs.networks.create(**kwargs) - - -@utils.arg( - '--limit', - dest="limit", - metavar="", - help='Number of images to return per request.') -@cliutils.service_type('image') -def do_image_list(cs, _args): - """Print a list of available images to boot from.""" - limit = _args.limit - image_list = cs.images.list(limit=limit) - - def parse_server_name(image): - try: - return image.server['id'] - except (AttributeError, KeyError): - return '' - - fmts = {'Server': parse_server_name} - utils.print_list(image_list, ['ID', 'Name', 'Status', 'Server'], - fmts, sortby_index=1) - - -@utils.arg( - 'image', - metavar='', - help="Name or ID of image") -@utils.arg( - 'action', - metavar='', - choices=['set', 'delete'], - help="Actions: 'set' or 'delete'") -@utils.arg( - 'metadata', - metavar='', - nargs='+', - action='append', - default=[], - help='Metadata to add/update or delete (only key is necessary on delete)') -def do_image_meta(cs, args): - """Set or Delete metadata on an image.""" - image = _find_image(cs, args.image) - metadata = _extract_metadata(args) - - if args.action == 'set': - cs.images.set_meta(image, metadata) - elif args.action == 'delete': - cs.images.delete_meta(image, metadata.keys()) - - -def _extract_metadata(args): - metadata = {} - for metadatum in args.metadata[0]: - # Can only pass the key in on 'delete' - # So this doesn't have to have '=' - if metadatum.find('=') > -1: - (key, value) = metadatum.split('=', 1) - else: - key = metadatum - value = None - - metadata[key] = value - return metadata - - -def _print_image(image): - info = image._info.copy() - - # try to replace a server entity to just an id - server = info.pop('server', None) - try: - info['server'] = server['id'] - except (KeyError, TypeError): - pass - - # break up metadata and display each on its own row - properties = info.pop('properties', {}) - try: - for key, value in properties.items(): - _key = 'Property %s' % key - info[_key] = value - except AttributeError: - pass - - utils.print_dict(info) - - -def _print_flavor(flavor): - info = flavor._info.copy() - # ignore links, we don't need to present those - info.pop('links') - info.update({"extra_specs": _print_flavor_extra_specs(flavor)}) - utils.print_dict(info) - - -@utils.arg( - 'image', - metavar='', - help="Name or ID of image") -@cliutils.service_type('image') -def do_image_show(cs, args): - """Show details about the given image.""" - image = _find_image(cs, args.image) - _print_image(image) - - -@utils.arg('image', metavar='', nargs='+', - help='Name or ID of image(s).') -def do_image_delete(cs, args): - """Delete specified image(s).""" - for image in args.image: - try: - _find_image(cs, image).delete() - except Exception as e: - print("Delete for image %s failed: %s" % (image, e)) - - -@utils.arg( - '--reservation-id', - dest='reservation_id', - metavar='', - default=None, - help='Only return servers that match reservation-id.') -@utils.arg( - '--reservation_id', - help=argparse.SUPPRESS) -@utils.arg( - '--ip', - dest='ip', - metavar='', - default=None, - help='Search with regular expression match by IP address.') -@utils.arg( - '--ip6', - dest='ip6', - metavar='', - default=None, - help='Search with regular expression match by IPv6 address.') -@utils.arg( - '--name', - dest='name', - metavar='', - default=None, - help='Search with regular expression match by name') -@utils.arg( - '--instance-name', - dest='instance_name', - metavar='', - default=None, - help='Search with regular expression match by server name.') -@utils.arg( - '--instance_name', - help=argparse.SUPPRESS) -@utils.arg( - '--status', - dest='status', - metavar='', - default=None, - help='Search by server status') -@utils.arg( - '--flavor', - dest='flavor', - metavar='', - default=None, - help='Search by flavor name or ID') -@utils.arg( - '--image', - dest='image', - metavar='', - default=None, - help='Search by image name or ID') -@utils.arg( - '--host', - dest='host', - metavar='', - default=None, - help='Search servers by hostname to which they are assigned ' - '(Admin only).') -@utils.arg( - '--all-tenants', - dest='all_tenants', - metavar='<0|1>', - nargs='?', - type=int, - const=1, - default=int(strutils.bool_from_string( - os.environ.get("ALL_TENANTS", 'false'), True)), - help='Display information from all tenants (Admin only).') -@utils.arg( - '--all_tenants', - nargs='?', - type=int, - const=1, - help=argparse.SUPPRESS) -@utils.arg( - '--tenant', - # nova db searches by project_id - dest='tenant', - metavar='', - nargs='?', - help='Display information from single tenant (Admin only).') -@utils.arg( - '--fields', - default=None, - metavar='', - help='Comma-separated list of fields to display. ' - 'Use the show command to see which fields are available.') -@utils.arg( - '--deleted', - dest='deleted', - action="store_true", - default=False, - help='Only display deleted servers (Admin only).') -@utils.arg( - '--minimal', - dest='minimal', - action="store_true", - default=False, - help='Get only uuid and name.') -def do_list(cs, args): - """List active servers.""" - imageid = None - flavorid = None - if args.image: - imageid = _find_image(cs, args.image).id - if args.flavor: - flavorid = _find_flavor(cs, args.flavor).id - search_opts = { - 'all_tenants': args.all_tenants, - 'reservation_id': args.reservation_id, - 'ip': args.ip, - 'ip6': args.ip6, - 'name': args.name, - 'image': imageid, - 'flavor': flavorid, - 'status': args.status, - 'tenant_id': args.tenant, - 'host': args.host, - 'deleted': args.deleted, - 'instance_name': args.instance_name} - - filters = {'flavor': lambda f: f['id'], - 'security_groups': utils._format_security_groups} - - formatters = {} - field_titles = [] - if args.fields: - for field in args.fields.split(','): - field_title, formatter = utils._make_field_formatter(field, - filters) - field_titles.append(field_title) - formatters[field_title] = formatter - - id_col = 'ID' - - detailed = not args.minimal - - servers = cs.servers.list(detailed=detailed, - search_opts=search_opts) - convert = [('os-extended-server-attributes:hypervisor_hostname', 'host'), - ('os-extended-status:task_state', 'task_state'), - ('os-extended-server-attributes:instance_name', - 'instance_name'), - ('os-extended-status:power_state', 'power_state')] - _translate_keys(servers, convert) - _translate_extended_states(servers) - if args.minimal: - columns = [ - id_col, - 'Name'] - elif field_titles: - columns = [id_col] + field_titles - else: - columns = [ - id_col, - 'Name', - 'Status', - 'Task State', - 'Power State', - 'Networks' - ] - formatters['Networks'] = utils._format_servers_list_networks - utils.print_list(servers, columns, - formatters, sortby_index=1) - - -@utils.arg( - '--hard', - dest='reboot_type', - action='store_const', - const=servers.REBOOT_HARD, - default=servers.REBOOT_SOFT, - help='Perform a hard reboot (instead of a soft one).') -@utils.arg('server', metavar='', help='Name or ID of server.') -@utils.arg( - '--poll', - dest='poll', - action="store_true", - default=False, - help='Poll until reboot is complete.') -def do_reboot(cs, args): - """Reboot a server.""" - server = _find_server(cs, args.server) - server.reboot(args.reboot_type) - - if args.poll: - _poll_for_status(cs.servers.get, server.id, 'rebooting', ['active'], - show_progress=False) - - -@utils.arg('server', metavar='', help='Name or ID of server.') -@utils.arg('image', metavar='', help="Name or ID of new image.") -@utils.arg( - '--rebuild-password', - dest='rebuild_password', - metavar='', - default=False, - help="Set the provided password on the rebuild server.") -@utils.arg( - '--rebuild_password', - help=argparse.SUPPRESS) -@utils.arg( - '--poll', - dest='poll', - action="store_true", - default=False, - help='Report the server rebuild progress until it completes.') -@utils.arg( - '--minimal', - dest='minimal', - action="store_true", - default=False, - help='Skips flavor/image lookups when showing servers') -def do_rebuild(cs, args): - """Shutdown, re-image, and re-boot a server.""" - server = _find_server(cs, args.server) - image = _find_image(cs, args.image) - - if args.rebuild_password is not False: - _password = args.rebuild_password - else: - _password = None - - kwargs = utils.get_resource_manager_extra_kwargs(do_rebuild, args) - server = server.rebuild(image, _password, **kwargs) - _print_server(cs, args, server) - - if args.poll: - _poll_for_status(cs.servers.get, server.id, 'rebuilding', ['active']) - - -@utils.arg('server', metavar='', - help='Name (old name) or ID of server.') -@utils.arg('name', metavar='', help='New name for the server.') -def do_rename(cs, args): - """Rename a server.""" - _find_server(cs, args.server).update(name=args.name) - - -@utils.arg('server', metavar='', help='Name or ID of server.') -@utils.arg('flavor', metavar='', help="Name or ID of new flavor.") -@utils.arg( - '--poll', - dest='poll', - action="store_true", - default=False, - help='Report the server resize progress until it completes.') -def do_resize(cs, args): - """Resize a server.""" - server = _find_server(cs, args.server) - flavor = _find_flavor(cs, args.flavor) - kwargs = utils.get_resource_manager_extra_kwargs(do_resize, args) - server.resize(flavor, **kwargs) - if args.poll: - _poll_for_status(cs.servers.get, server.id, 'resizing', - ['active', 'verify_resize']) - - -@utils.arg('server', metavar='', help='Name or ID of server.') -def do_resize_confirm(cs, args): - """Confirm a previous resize.""" - _find_server(cs, args.server).confirm_resize() - - -@utils.arg('server', metavar='', help='Name or ID of server.') -def do_resize_revert(cs, args): - """Revert a previous resize (and return to the previous VM).""" - _find_server(cs, args.server).revert_resize() - - -@utils.arg('server', metavar='', help='Name or ID of server.') -@utils.arg( - '--poll', - dest='poll', - action="store_true", - default=False, - help='Report the server migration progress until it completes.') -def do_migrate(cs, args): - """Migrate a server. The new host will be selected by the scheduler.""" - server = _find_server(cs, args.server) - server.migrate() - - if args.poll: - _poll_for_status(cs.servers.get, server.id, 'migrating', - ['active', 'verify_resize']) - - -@utils.arg('server', metavar='', help='Name or ID of server.') -def do_pause(cs, args): - """Pause a server.""" - _find_server(cs, args.server).pause() - - -@utils.arg('server', metavar='', help='Name or ID of server.') -def do_unpause(cs, args): - """Unpause a server.""" - _find_server(cs, args.server).unpause() - - -@utils.arg('server', metavar='', help='Name or ID of server.') -def do_stop(cs, args): - """Stop a server.""" - _find_server(cs, args.server).stop() - - -@utils.arg('server', metavar='', help='Name or ID of server.') -def do_start(cs, args): - """Start a server.""" - _find_server(cs, args.server).start() - - -@utils.arg('server', metavar='', help='Name or ID of server.') -def do_lock(cs, args): - """Lock a server.""" - _find_server(cs, args.server).lock() - - -@utils.arg('server', metavar='', help='Name or ID of server.') -def do_unlock(cs, args): - """Unlock a server.""" - _find_server(cs, args.server).unlock() - - -@utils.arg('server', metavar='', help='Name or ID of server.') -def do_suspend(cs, args): - """Suspend a server.""" - _find_server(cs, args.server).suspend() - - -@utils.arg('server', metavar='', help='Name or ID of server.') -def do_resume(cs, args): - """Resume a server.""" - _find_server(cs, args.server).resume() - - -@utils.arg('server', metavar='', help='Name or ID of server.') -def do_rescue(cs, args): - """Reboots a server into rescue mode, which starts the machine - from the initial image, attaching the current boot disk as secondary. - """ - utils.print_dict(_find_server(cs, args.server).rescue()[1]) - - -@utils.arg('server', metavar='', help='Name or ID of server.') -def do_unrescue(cs, args): - """Restart the server from normal boot disk again.""" - _find_server(cs, args.server).unrescue() - - -@utils.arg('server', metavar='', help='Name or ID of server.') -def do_diagnostics(cs, args): - """Retrieve server diagnostics.""" - server = _find_server(cs, args.server) - utils.print_dict(cs.servers.diagnostics(server)[1], wrap=80) - - -@utils.arg('server', metavar='', help='Name or ID of server.') -def do_root_password(cs, args): - """ - Change the root password for a server. - """ - server = _find_server(cs, args.server) - p1 = getpass.getpass('New password: ') - p2 = getpass.getpass('Again: ') - if p1 != p2: - raise exceptions.CommandError("Passwords do not match.") - server.change_password(p1) - - -@utils.arg('server', metavar='', help='Name or ID of server.') -@utils.arg('name', metavar='', help='Name of snapshot.') -@utils.arg( - '--show', - dest='show', - action="store_true", - default=False, - help='Print image info.') -@utils.arg( - '--poll', - dest='poll', - action="store_true", - default=False, - help='Report the snapshot progress and poll until image creation is ' - 'complete.') -def do_image_create(cs, args): - """Create a new image by taking a snapshot of a running server.""" - server = _find_server(cs, args.server) - image_uuid = cs.servers.create_image(server, args.name) - - if args.poll: - _poll_for_status(cs.images.get, image_uuid, 'snapshotting', - ['active']) - - # NOTE(sirp): A race-condition exists between when the image finishes - # uploading and when the servers's `task_state` is cleared. To account - # for this, we need to poll a second time to ensure the `task_state` is - # cleared before returning, ensuring that a snapshot taken immediately - # after this function returns will succeed. - # - # A better long-term solution will be to separate 'snapshotting' and - # 'image-uploading' in Nova and clear the task-state once the VM - # snapshot is complete but before the upload begins. - task_state_field = "OS-EXT-STS:task_state" - if hasattr(server, task_state_field): - _poll_for_status(cs.servers.get, server.id, 'image_snapshot', - [None], status_field=task_state_field, - show_progress=False, silent=True) - - if args.show: - _print_image(cs.images.get(image_uuid)) - - -@utils.arg('server', metavar='', help='Name or ID of server.') -@utils.arg('name', metavar='', help='Name of the backup image.') -@utils.arg('backup_type', metavar='', - help='The backup type, like "daily" or "weekly".') -@utils.arg('rotation', metavar='', - help='Int parameter representing how many backups to keep around.') -def do_backup(cs, args): - """Backup a server by creating a 'backup' type snapshot.""" - _find_server(cs, args.server).backup(args.name, - args.backup_type, - args.rotation) - - -@utils.arg( - 'server', - metavar='', - help="Name or ID of server") -@utils.arg( - 'action', - metavar='', - choices=['set', 'delete'], - help="Actions: 'set' or 'delete'") -@utils.arg( - 'metadata', - metavar='', - nargs='+', - action='append', - default=[], - help='Metadata to set or delete (only key is necessary on delete)') -def do_meta(cs, args): - """Set or Delete metadata on a server.""" - server = _find_server(cs, args.server) - metadata = _extract_metadata(args) - - if args.action == 'set': - cs.servers.set_meta(server, metadata) - elif args.action == 'delete': - cs.servers.delete_meta(server, metadata.keys()) - - -def _print_server(cs, args, server=None): - # By default when searching via name we will do a - # findall(name=blah) and due a REST /details which is not the same - # as a .get() and doesn't get the information about flavors and - # images. This fix it as we redo the call with the id which does a - # .get() to get all informations. - if not server: - server = _find_server(cs, args.server) - - minimal = getattr(args, "minimal", False) - - networks = server.networks - info = server._info.copy() - for network_label, address_list in networks.items(): - info['%s network' % network_label] = ', '.join(address_list) - - flavor = info.get('flavor', {}) - flavor_id = flavor.get('id', '') - if minimal: - info['flavor'] = flavor_id - else: - info['flavor'] = '%s (%s)' % (_find_flavor(cs, flavor_id).name, - flavor_id) - - if 'security_groups' in info: - # when we have multiple nics the info will include the - # security groups N times where N == number of nics. Be nice - # and only display it once. - info['security_groups'] = ', '.join( - sorted(set(group['name'] for group in info['security_groups']))) - - image = info.get('image', {}) - if image: - image_id = image.get('id', '') - if minimal: - info['image'] = image_id - else: - try: - info['image'] = '%s (%s)' % (_find_image(cs, image_id).name, - image_id) - except Exception: - info['image'] = '%s (%s)' % ("Image not found", image_id) - else: # Booted from volume - info['image'] = "Attempt to boot from volume - no image supplied" - - info.pop('links', None) - info.pop('addresses', None) - - utils.print_dict(info) - - -@utils.arg( - '--minimal', - dest='minimal', - action="store_true", - default=False, - help='Skips flavor/image lookups when showing servers') -@utils.arg('server', metavar='', help='Name or ID of server.') -def do_show(cs, args): - """Show details about the given server.""" - _print_server(cs, args) - - -@utils.arg('server', metavar='', nargs='+', - help='Name or ID of server(s).') -def do_delete(cs, args): - """Immediately shut down and delete specified server(s).""" - failure_flag = False - - for server in args.server: - try: - _find_server(cs, server).delete() - print("Request to delete server %s has been accepted." % server) - except Exception as e: - failure_flag = True - print(e) - - if failure_flag: - raise exceptions.CommandError("Unable to delete the specified " - "server(s).") - - -def _find_server(cs, server): - """Get a server by name or ID.""" - return utils.find_resource(cs.servers, server) - - -def _find_image(cs, image): - """Get an image by name or ID.""" - return utils.find_resource(cs.images, image) - - -def _find_flavor(cs, flavor): - """Get a flavor by name, ID, or RAM size.""" - try: - return utils.find_resource(cs.flavors, flavor, is_public=None) - except exceptions.NotFound: - return cs.flavors.find(ram=flavor) - - -@utils.arg('server', metavar='', help='Name or ID of server.') -@utils.arg( - 'network_id', - metavar='', - help='Network ID.') -def do_add_fixed_ip(cs, args): - """Add new IP address on a network to server.""" - server = _find_server(cs, args.server) - server.add_fixed_ip(args.network_id) - - -@utils.arg('server', metavar='', help='Name or ID of server.') -@utils.arg('address', metavar='
', help='IP Address.') -def do_remove_fixed_ip(cs, args): - """Remove an IP address from a server.""" - server = _find_server(cs, args.server) - server.remove_fixed_ip(args.address) - - -def _translate_availability_zone_keys(collection): - _translate_keys(collection, - [('zone_name', 'name'), ('zone_state', 'status')]) - - -@utils.arg( - 'server', - metavar='', - help='Name or ID of server.') -@utils.arg( - 'volume', - metavar='', - help='ID of the volume to attach.') -@utils.arg( - 'device', metavar='', default=None, nargs='?', - help='Name of the device e.g. /dev/vdb. ' - 'Use "auto" for autoassign (if supported)') -@utils.arg( - 'disk_bus', - metavar='', - default=None, - nargs='?', - help='The disk bus e.g. ide of the volume (optional).') -@utils.arg( - 'device_type', - metavar='', - default=None, - nargs='?', - help='The device type e.g. cdrom of the volume (optional).') -def do_volume_attach(cs, args): - """Attach a volume to a server.""" - if args.device == 'auto': - args.device = None - - cs.volumes.attach_server_volume(_find_server(cs, args.server).id, - args.volume, args.device, args.disk_bus, - args.device_type) - - -@utils.arg( - 'server', - metavar='', - help='Name or ID of server.') -@utils.arg( - 'attachment_id', - metavar='', - help='Attachment ID of the volume.') -@utils.arg( - 'new_volume', - metavar='', - help='ID of the volume to attach.') -def do_volume_update(cs, args): - """Update volume attachment.""" - cs.volumes.update_server_volume(_find_server(cs, args.server).id, - args.attachment_id, args.new_volume) - - -@utils.arg( - 'server', - metavar='', - help='Name or ID of server.') -@utils.arg( - 'attachment_id', - metavar='', - help='ID of the volume to detach.') -def do_volume_detach(cs, args): - """Detach a volume from a server.""" - cs.volumes.delete_server_volume(_find_server(cs, args.server).id, - args.attachment_id) - - -@utils.arg('server', metavar='', help='Name or ID of server.') -@utils.arg( - 'console_type', - metavar='', - help='Type of vnc console ("novnc" or "xvpvnc").') -def do_get_vnc_console(cs, args): - """Get a vnc console to a server.""" - server = _find_server(cs, args.server) - data = server.get_vnc_console(args.console_type) - - class VNCConsole: - def __init__(self, console_dict): - self.type = console_dict['type'] - self.url = console_dict['url'] - - utils.print_list([VNCConsole(data['console'])], ['Type', 'Url']) - - -@utils.arg('server', metavar='', help='Name or ID of server.') -@utils.arg( - 'console_type', - metavar='', - help='Type of spice console ("spice-html5").') -def do_get_spice_console(cs, args): - """Get a spice console to a server.""" - server = _find_server(cs, args.server) - data = server.get_spice_console(args.console_type) - - class SPICEConsole: - def __init__(self, console_dict): - self.type = console_dict['type'] - self.url = console_dict['url'] - - utils.print_list([SPICEConsole(data['console'])], ['Type', 'Url']) - - -@utils.arg('server', metavar='', help='Name or ID of server.') -@utils.arg( - 'console_type', - metavar='', - help='Type of rdp console ("rdp-html5").') -def do_get_rdp_console(cs, args): - """Get a rdp console to a server.""" - server = _find_server(cs, args.server) - data = server.get_rdp_console(args.console_type) - - class RDPConsole: - def __init__(self, console_dict): - self.type = console_dict['type'] - self.url = console_dict['url'] - - utils.print_list([RDPConsole(data['console'])], ['Type', 'Url']) - - -@utils.arg('server', metavar='', help='Name or ID of server.') -@utils.arg( - 'private_key', - metavar='', - help='Private key (used locally to decrypt password) (Optional). ' - 'When specified, the command displays the clear (decrypted) VM ' - 'password. When not specified, the ciphered VM password is ' - 'displayed.', - nargs='?', - default=None) -def do_get_password(cs, args): - """Get password for a server.""" - server = _find_server(cs, args.server) - data = server.get_password(args.private_key) - print(data) - - -@utils.arg('server', metavar='', help='Name or ID of server.') -def do_clear_password(cs, args): - """Clear password for a server.""" - server = _find_server(cs, args.server) - server.clear_password() - - -def _print_floating_ip_list(floating_ips): - convert = [('instance_id', 'server_id')] - _translate_keys(floating_ips, convert) - - utils.print_list(floating_ips, ['Ip', 'Server Id', 'Fixed Ip', 'Pool']) - - -@utils.arg('server', metavar='', help='Name or ID of server.') -@utils.arg('--length', - metavar='', - default=None, - help='Length in lines to tail.') -def do_console_log(cs, args): - """Get console log output of a server.""" - server = _find_server(cs, args.server) - data = server.get_console_output(length=args.length) - print(data) - - -@utils.arg('server', metavar='', help='Name or ID of server.') -@utils.arg('address', metavar='
', help='IP Address.') -@utils.arg('--fixed-address', - metavar='', - default=None, - help='Fixed IP Address to associate with.') -def do_add_floating_ip(cs, args): - """Add a floating IP address to a server.""" - server = _find_server(cs, args.server) - server.add_floating_ip(args.address, args.fixed_address) - - -@utils.arg('server', metavar='', help='Name or ID of server.') -@utils.arg('address', metavar='
', help='IP Address.') -def do_remove_floating_ip(cs, args): - """Remove a floating IP address from a server.""" - server = _find_server(cs, args.server) - server.remove_floating_ip(args.address) - - -@utils.arg('server', metavar='', help='Name or ID of server.') -@utils.arg('secgroup', metavar='', help='Name of Security Group.') -def do_add_secgroup(cs, args): - """Add a Security Group to a server.""" - server = _find_server(cs, args.server) - server.add_security_group(args.secgroup) - - -@utils.arg('server', metavar='', help='Name or ID of server.') -@utils.arg('secgroup', metavar='', help='Name of Security Group.') -def do_remove_secgroup(cs, args): - """Remove a Security Group from a server.""" - server = _find_server(cs, args.server) - server.remove_security_group(args.secgroup) - - -@utils.arg('pool', - metavar='', - help='Name of Floating IP Pool. (Optional)', - nargs='?', - default=None) -def do_floating_ip_create(cs, args): - """Allocate a floating IP for the current tenant.""" - _print_floating_ip_list([cs.floating_ips.create(pool=args.pool)]) - - -@utils.arg('address', metavar='
', help='IP of Floating Ip.') -def do_floating_ip_delete(cs, args): - """De-allocate a floating IP.""" - floating_ips = cs.floating_ips.list() - for floating_ip in floating_ips: - if floating_ip.ip == args.address: - return cs.floating_ips.delete(floating_ip.id) - raise exceptions.CommandError("Floating ip %s not found." % args.address) - - -def do_floating_ip_list(cs, _args): - """List floating ips for this tenant.""" - _print_floating_ip_list(cs.floating_ips.list()) - - -def do_floating_ip_pool_list(cs, _args): - """List all floating ip pools.""" - utils.print_list(cs.floating_ip_pools.list(), ['name']) - - -@utils.arg('--host', dest='host', metavar='', default=None, - help='Filter by host') -def do_floating_ip_bulk_list(cs, args): - """List all floating ips.""" - utils.print_list(cs.floating_ips_bulk.list(args.host), ['project_id', - 'address', - 'instance_uuid', - 'pool', - 'interface']) - - -@utils.arg('ip_range', metavar='', help='Address range to create') -@utils.arg('--pool', dest='pool', metavar='', default=None, - help='Pool for new Floating IPs') -@utils.arg('--interface', metavar='', default=None, - help='Interface for new Floating IPs') -def do_floating_ip_bulk_create(cs, args): - """Bulk create floating ips by range.""" - cs.floating_ips_bulk.create(args.ip_range, args.pool, args.interface) - - -@utils.arg('ip_range', metavar='', help='Address range to delete') -def do_floating_ip_bulk_delete(cs, args): - """Bulk delete floating ips by range.""" - cs.floating_ips_bulk.delete(args.ip_range) - - -def _print_dns_list(dns_entries): - utils.print_list(dns_entries, ['ip', 'name', 'domain']) - - -def _print_domain_list(domain_entries): - utils.print_list(domain_entries, ['domain', 'scope', - 'project', 'availability_zone']) - - -def do_dns_domains(cs, args): - """Print a list of available dns domains.""" - domains = cs.dns_domains.domains() - _print_domain_list(domains) - - -@utils.arg('domain', metavar='', help='DNS domain') -@utils.arg('--ip', metavar='', help='ip address', default=None) -@utils.arg('--name', metavar='', help='DNS name', default=None) -def do_dns_list(cs, args): - """List current DNS entries for domain and ip or domain and name.""" - if not (args.ip or args.name): - raise exceptions.CommandError("You must specify either --ip or --name") - if args.name: - entry = cs.dns_entries.get(args.domain, args.name) - _print_dns_list([entry]) - else: - entries = cs.dns_entries.get_for_ip(args.domain, - ip=args.ip) - _print_dns_list(entries) - - -@utils.arg('ip', metavar='', help='ip address') -@utils.arg('name', metavar='', help='DNS name') -@utils.arg('domain', metavar='', help='DNS domain') -@utils.arg('--type', metavar='', help='dns type (e.g. "A")', default='A') -def do_dns_create(cs, args): - """Create a DNS entry for domain, name and ip.""" - cs.dns_entries.create(args.domain, args.name, args.ip, args.type) - - -@utils.arg('domain', metavar='', help='DNS domain') -@utils.arg('name', metavar='', help='DNS name') -def do_dns_delete(cs, args): - """Delete the specified DNS entry.""" - cs.dns_entries.delete(args.domain, args.name) - - -@utils.arg('domain', metavar='', help='DNS domain') -def do_dns_delete_domain(cs, args): - """Delete the specified DNS domain.""" - cs.dns_domains.delete(args.domain) - - -@utils.arg('domain', metavar='', help='DNS domain') -@utils.arg( - '--availability-zone', - metavar='', - default=None, - help='Limit access to this domain to servers ' - 'in the specified availability zone.') -@utils.arg( - '--availability_zone', - help=argparse.SUPPRESS) -def do_dns_create_private_domain(cs, args): - """Create the specified DNS domain.""" - cs.dns_domains.create_private(args.domain, - args.availability_zone) - - -@utils.arg('domain', metavar='', help='DNS domain') -@utils.arg('--project', metavar='', - help='Limit access to this domain to users ' - 'of the specified project.', - default=None) -def do_dns_create_public_domain(cs, args): - """Create the specified DNS domain.""" - cs.dns_domains.create_public(args.domain, - args.project) - - -def _print_secgroup_rules(rules): - class FormattedRule: - def __init__(self, obj): - items = (obj if isinstance(obj, dict) else obj._info).items() - for k, v in items: - if k == 'ip_range': - v = v.get('cidr') - elif k == 'group': - k = 'source_group' - v = v.get('name') - if v is None: - v = '' - - setattr(self, k, v) - - rules = [FormattedRule(rule) for rule in rules] - utils.print_list(rules, ['IP Protocol', 'From Port', 'To Port', - 'IP Range', 'Source Group']) - - -def _print_secgroups(secgroups): - utils.print_list(secgroups, ['Id', 'Name', 'Description']) - - -def _get_secgroup(cs, secgroup): - # Check secgroup is an ID - if uuidutils.is_uuid_like(strutils.safe_encode(secgroup)): - try: - return cs.security_groups.get(secgroup) - except exceptions.NotFound: - pass - - # Check secgroup as a name - match_found = False - for s in cs.security_groups.list(): - encoding = (locale.getpreferredencoding() or - sys.stdin.encoding or 'UTF-8') - s.name = s.name.encode(encoding) - if secgroup == s.name: - if match_found is not False: - msg = ("Multiple security group matches found for name" - " '%s', use an ID to be more specific." % secgroup) - raise exceptions.NoUniqueMatch(msg) - match_found = s - if match_found is False: - raise exceptions.CommandError("Secgroup ID or name '%s' not found." - % secgroup) - return match_found - - -@utils.arg( - 'secgroup', - metavar='', - help='ID or name of security group.') -@utils.arg( - 'ip_proto', - metavar='', - help='IP protocol (icmp, tcp, udp).') -@utils.arg( - 'from_port', - metavar='', - help='Port at start of range.') -@utils.arg( - 'to_port', - metavar='', - help='Port at end of range.') -@utils.arg('cidr', metavar='', help='CIDR for address range.') -def do_secgroup_add_rule(cs, args): - """Add a rule to a security group.""" - secgroup = _get_secgroup(cs, args.secgroup) - rule = cs.security_group_rules.create(secgroup.id, - args.ip_proto, - args.from_port, - args.to_port, - args.cidr) - _print_secgroup_rules([rule]) - - -@utils.arg( - 'secgroup', - metavar='', - help='ID or name of security group.') -@utils.arg( - 'ip_proto', - metavar='', - help='IP protocol (icmp, tcp, udp).') -@utils.arg( - 'from_port', - metavar='', - help='Port at start of range.') -@utils.arg( - 'to_port', - metavar='', - help='Port at end of range.') -@utils.arg('cidr', metavar='', help='CIDR for address range.') -def do_secgroup_delete_rule(cs, args): - """Delete a rule from a security group.""" - secgroup = _get_secgroup(cs, args.secgroup) - for rule in secgroup.rules: - if (rule['ip_protocol'] and - rule['ip_protocol'].upper() == args.ip_proto.upper() and - rule['from_port'] == int(args.from_port) and - rule['to_port'] == int(args.to_port) and - rule['ip_range']['cidr'] == args.cidr): - _print_secgroup_rules([rule]) - return cs.security_group_rules.delete(rule['id']) - - raise exceptions.CommandError("Rule not found") - - -@utils.arg('name', metavar='', help='Name of security group.') -@utils.arg('description', metavar='', - help='Description of security group.') -def do_secgroup_create(cs, args): - """Create a security group.""" - secgroup = cs.security_groups.create(args.name, args.description) - _print_secgroups([secgroup]) - - -@utils.arg( - 'secgroup', - metavar='', - help='ID or name of security group.') -@utils.arg('name', metavar='', help='Name of security group.') -@utils.arg('description', metavar='', - help='Description of security group.') -def do_secgroup_update(cs, args): - """Update a security group.""" - sg = _get_secgroup(cs, args.secgroup) - secgroup = cs.security_groups.update(sg, args.name, args.description) - _print_secgroups([secgroup]) - - -@utils.arg( - 'secgroup', - metavar='', - help='ID or name of security group.') -def do_secgroup_delete(cs, args): - """Delete a security group.""" - secgroup = _get_secgroup(cs, args.secgroup) - cs.security_groups.delete(secgroup) - _print_secgroups([secgroup]) - - -@utils.arg( - '--all-tenants', - dest='all_tenants', - metavar='<0|1>', - nargs='?', - type=int, - const=1, - default=int(strutils.bool_from_string( - os.environ.get("ALL_TENANTS", 'false'), True)), - help='Display information from all tenants (Admin only).') -@utils.arg( - '--all_tenants', - nargs='?', - type=int, - const=1, - help=argparse.SUPPRESS) -def do_secgroup_list(cs, args): - """List security groups for the current tenant.""" - search_opts = {'all_tenants': args.all_tenants} - columns = ['Id', 'Name', 'Description'] - if args.all_tenants: - columns.append('Tenant_ID') - groups = cs.security_groups.list(search_opts=search_opts) - utils.print_list(groups, columns) - - -@utils.arg( - 'secgroup', - metavar='', - help='ID or name of security group.') -def do_secgroup_list_rules(cs, args): - """List rules for a security group.""" - secgroup = _get_secgroup(cs, args.secgroup) - _print_secgroup_rules(secgroup.rules) - - -@utils.arg( - 'secgroup', - metavar='', - help='ID or name of security group.') -@utils.arg( - 'source_group', - metavar='', - help='ID or name of source group.') -@utils.arg( - 'ip_proto', - metavar='', - help='IP protocol (icmp, tcp, udp).') -@utils.arg( - 'from_port', - metavar='', - help='Port at start of range.') -@utils.arg( - 'to_port', - metavar='', - help='Port at end of range.') -def do_secgroup_add_group_rule(cs, args): - """Add a source group rule to a security group.""" - secgroup = _get_secgroup(cs, args.secgroup) - source_group = _get_secgroup(cs, args.source_group) - params = {} - params['group_id'] = source_group.id - - if args.ip_proto or args.from_port or args.to_port: - if not (args.ip_proto and args.from_port and args.to_port): - raise exceptions.CommandError( - "ip_proto, from_port, and to_port must be specified together") - params['ip_protocol'] = args.ip_proto.upper() - params['from_port'] = args.from_port - params['to_port'] = args.to_port - - rule = cs.security_group_rules.create(secgroup.id, **params) - _print_secgroup_rules([rule]) - - -@utils.arg( - 'secgroup', - metavar='', - help='ID or name of security group.') -@utils.arg( - 'source_group', - metavar='', - help='ID or name of source group.') -@utils.arg( - 'ip_proto', - metavar='', - help='IP protocol (icmp, tcp, udp).') -@utils.arg( - 'from_port', - metavar='', - help='Port at start of range.') -@utils.arg( - 'to_port', - metavar='', - help='Port at end of range.') -def do_secgroup_delete_group_rule(cs, args): - """Delete a source group rule from a security group.""" - secgroup = _get_secgroup(cs, args.secgroup) - source_group = _get_secgroup(cs, args.source_group) - params = {} - params['group_name'] = source_group.name - - if args.ip_proto or args.from_port or args.to_port: - if not (args.ip_proto and args.from_port and args.to_port): - raise exceptions.CommandError( - "ip_proto, from_port, and to_port must be specified together") - params['ip_protocol'] = args.ip_proto.upper() - params['from_port'] = int(args.from_port) - params['to_port'] = int(args.to_port) - - for rule in secgroup.rules: - if (rule.get('ip_protocol').upper() == params.get( - 'ip_protocol').upper() and - rule.get('from_port') == params.get('from_port') and - rule.get('to_port') == params.get('to_port') and - rule.get('group', {}).get('name') == params.get('group_name')): - return cs.security_group_rules.delete(rule['id']) - - raise exceptions.CommandError("Rule not found") - - -@utils.arg('name', metavar='', help='Name of key.') -@utils.arg( - '--pub-key', - metavar='', - default=None, - help='Path to a public ssh key.') -@utils.arg( - '--pub_key', - help=argparse.SUPPRESS) -def do_keypair_add(cs, args): - """Create a new key pair for use with servers.""" - name = args.name - pub_key = args.pub_key - - if pub_key: - try: - with open(os.path.expanduser(pub_key)) as f: - pub_key = f.read() - except IOError as e: - raise exceptions.CommandError( - "Can't open or read '%s': %s" % (pub_key, e)) - - keypair = cs.keypairs.create(name, pub_key) - - if not pub_key: - private_key = keypair.private_key - print(private_key) - - -@utils.arg('name', metavar='', help='Keypair name to delete.') -def do_keypair_delete(cs, args): - """Delete keypair given by its name.""" - name = _find_keypair(cs, args.name) - cs.keypairs.delete(name) - - -def do_keypair_list(cs, args): - """Print a list of keypairs for a user""" - keypairs = cs.keypairs.list() - columns = ['Name', 'Fingerprint'] - utils.print_list(keypairs, columns) - - -def _print_keypair(keypair): - kp = keypair._info.copy() - pk = kp.pop('public_key') - utils.print_dict(kp) - print("Public key: %s" % pk) - - -@utils.arg( - 'keypair', - metavar='', - help="Name or ID of keypair") -def do_keypair_show(cs, args): - """Show details about the given keypair.""" - keypair = _find_keypair(cs, args.keypair) - _print_keypair(keypair) - - -def _find_keypair(cs, keypair): - """Get a keypair by name or ID.""" - return utils.find_resource(cs.keypairs, keypair) - - -@utils.arg('--start', metavar='', - help='Usage range start date ex 2012-01-20 (default: 4 weeks ago)', - default=None) -@utils.arg('--end', metavar='', - help='Usage range end date, ex 2012-01-20 (default: tomorrow) ', - default=None) -def do_usage_list(cs, args): - """List usage data for all tenants.""" - dateformat = "%Y-%m-%d" - rows = ["Tenant ID", "Servers", "RAM MB-Hours", "CPU Hours", - "Disk GB-Hours"] - - now = timeutils.utcnow() - - if args.start: - start = datetime.datetime.strptime(args.start, dateformat) - else: - start = now - datetime.timedelta(weeks=4) - - if args.end: - end = datetime.datetime.strptime(args.end, dateformat) - else: - end = now + datetime.timedelta(days=1) - - def simplify_usage(u): - simplerows = map(lambda x: x.lower().replace(" ", "_"), rows) - - setattr(u, simplerows[0], u.tenant_id) - setattr(u, simplerows[1], "%d" % len(u.server_usages)) - setattr(u, simplerows[2], "%.2f" % u.total_memory_mb_usage) - setattr(u, simplerows[3], "%.2f" % u.total_vcpus_usage) - setattr(u, simplerows[4], "%.2f" % u.total_local_gb_usage) - - usage_list = cs.usage.list(start, end, detailed=True) - - print("Usage from %s to %s:" % (start.strftime(dateformat), - end.strftime(dateformat))) - - for usage in usage_list: - simplify_usage(usage) - - utils.print_list(usage_list, rows) - - -@utils.arg('--start', metavar='', - help='Usage range start date ex 2012-01-20 (default: 4 weeks ago)', - default=None) -@utils.arg('--end', metavar='', - help='Usage range end date, ex 2012-01-20 (default: tomorrow) ', - default=None) -@utils.arg('--tenant', metavar='', - default=None, - help='UUID or name of tenant to get usage for.') -def do_usage(cs, args): - """Show usage data for a single tenant.""" - dateformat = "%Y-%m-%d" - rows = ["Servers", "RAM MB-Hours", "CPU Hours", "Disk GB-Hours"] - - now = timeutils.utcnow() - - if args.start: - start = datetime.datetime.strptime(args.start, dateformat) - else: - start = now - datetime.timedelta(weeks=4) - - if args.end: - end = datetime.datetime.strptime(args.end, dateformat) - else: - end = now + datetime.timedelta(days=1) - - def simplify_usage(u): - simplerows = map(lambda x: x.lower().replace(" ", "_"), rows) - - setattr(u, simplerows[0], "%d" % len(u.server_usages)) - setattr(u, simplerows[1], "%.2f" % u.total_memory_mb_usage) - setattr(u, simplerows[2], "%.2f" % u.total_vcpus_usage) - setattr(u, simplerows[3], "%.2f" % u.total_local_gb_usage) - - if args.tenant: - usage = cs.usage.get(args.tenant, start, end) - else: - if isinstance(cs.client, client.SessionClient): - auth = cs.client.auth - project_id = auth.get_auth_ref(cs.client.session).project_id - usage = cs.usage.get(project_id, start, end) - else: - usage = cs.usage.get(cs.client.tenant_id, start, end) - - print("Usage from %s to %s:" % (start.strftime(dateformat), - end.strftime(dateformat))) - - if getattr(usage, 'total_vcpus_usage', None): - simplify_usage(usage) - utils.print_list([usage], rows) - else: - print('None') - - -@utils.arg( - 'pk_filename', - metavar='', - nargs='?', - default='pk.pem', - help='Filename for the private key [Default: pk.pem]') -@utils.arg( - 'cert_filename', - metavar='', - nargs='?', - default='cert.pem', - help='Filename for the X.509 certificate [Default: cert.pem]') -def do_x509_create_cert(cs, args): - """Create x509 cert for a user in tenant.""" - - if os.path.exists(args.pk_filename): - raise exceptions.CommandError("Unable to write privatekey - %s exists." - % args.pk_filename) - if os.path.exists(args.cert_filename): - raise exceptions.CommandError("Unable to write x509 cert - %s exists." - % args.cert_filename) - - certs = cs.certs.create() - - try: - old_umask = os.umask(0o377) - with open(args.pk_filename, 'w') as private_key: - private_key.write(certs.private_key) - print("Wrote private key to %s" % args.pk_filename) - finally: - os.umask(old_umask) - - with open(args.cert_filename, 'w') as cert: - cert.write(certs.data) - print("Wrote x509 certificate to %s" % args.cert_filename) - - -@utils.arg('filename', - metavar='', - nargs='?', - default='cacert.pem', - help='Filename to write the x509 root cert.') -def do_x509_get_root_cert(cs, args): - """Fetch the x509 root cert.""" - if os.path.exists(args.filename): - raise exceptions.CommandError("Unable to write x509 root cert - \ - %s exists." % args.filename) - - with open(args.filename, 'w') as cert: - cacert = cs.certs.get() - cert.write(cacert.data) - print("Wrote x509 root cert to %s" % args.filename) - - -@utils.arg('--hypervisor', metavar='', default=None, - help='type of hypervisor.') -def do_agent_list(cs, args): - """List all builds.""" - result = cs.agents.list(args.hypervisor) - columns = ["Agent_id", "Hypervisor", "OS", "Architecture", "Version", - 'Md5hash', 'Url'] - utils.print_list(result, columns) - - -@utils.arg('os', metavar='', help='type of os.') -@utils.arg('architecture', metavar='', - help='type of architecture') -@utils.arg('version', metavar='', help='version') -@utils.arg('url', metavar='', help='url') -@utils.arg('md5hash', metavar='', help='md5 hash') -@utils.arg('hypervisor', metavar='', default='xen', - help='type of hypervisor.') -def do_agent_create(cs, args): - """Create new agent build.""" - result = cs.agents.create(args.os, args.architecture, - args.version, args.url, - args.md5hash, args.hypervisor) - utils.print_dict(result._info.copy()) - - -@utils.arg('id', metavar='', help='id of the agent-build') -def do_agent_delete(cs, args): - """Delete existing agent build.""" - cs.agents.delete(args.id) - - -@utils.arg('id', metavar='', help='id of the agent-build') -@utils.arg('version', metavar='', help='version') -@utils.arg('url', metavar='', help='url') -@utils.arg('md5hash', metavar='', help='md5hash') -def do_agent_modify(cs, args): - """Modify existing agent build.""" - result = cs.agents.update(args.id, args.version, - args.url, args.md5hash) - utils.print_dict(result._info) - - -def _find_aggregate(cs, aggregate): - """Get a aggregate by name or ID.""" - return utils.find_resource(cs.aggregates, aggregate) - - -def do_aggregate_list(cs, args): - """Print a list of all aggregates.""" - aggregates = cs.aggregates.list() - columns = ['Id', 'Name', 'Availability Zone'] - utils.print_list(aggregates, columns) - - -@utils.arg('name', metavar='', help='Name of aggregate.') -@utils.arg( - 'availability_zone', - metavar='', - default=None, - nargs='?', - help='The availability zone of the aggregate (optional).') -def do_aggregate_create(cs, args): - """Create a new aggregate with the specified details.""" - aggregate = cs.aggregates.create(args.name, args.availability_zone) - _print_aggregate_details(aggregate) - - -@utils.arg('aggregate', metavar='', - help='Name or ID of aggregate to delete.') -def do_aggregate_delete(cs, args): - """Delete the aggregate.""" - aggregate = _find_aggregate(cs, args.aggregate) - cs.aggregates.delete(aggregate) - print("Aggregate %s has been successfully deleted." % aggregate.id) - - -@utils.arg('aggregate', metavar='', - help='Name or ID of aggregate to update.') -@utils.arg('name', metavar='', help='Name of aggregate.') -@utils.arg( - 'availability_zone', - metavar='', - nargs='?', - default=None, - help='The availability zone of the aggregate.') -def do_aggregate_update(cs, args): - """Update the aggregate's name and optionally availability zone.""" - aggregate = _find_aggregate(cs, args.aggregate) - updates = {"name": args.name} - if args.availability_zone: - updates["availability_zone"] = args.availability_zone - - aggregate = cs.aggregates.update(aggregate.id, updates) - print("Aggregate %s has been successfully updated." % aggregate.id) - _print_aggregate_details(aggregate) - - -@utils.arg('aggregate', metavar='', - help='Name or ID of aggregate to update.') -@utils.arg('metadata', - metavar='', - nargs='+', - action='append', - default=[], - help='Metadata to add/update to aggregate') -def do_aggregate_set_metadata(cs, args): - """Update the metadata associated with the aggregate.""" - aggregate = _find_aggregate(cs, args.aggregate) - metadata = _extract_metadata(args) - currentmetadata = getattr(aggregate, 'metadata', {}) - if set(metadata.items()) & set(currentmetadata.items()): - raise exceptions.CommandError("metadata already exists") - for key, value in metadata.items(): - if value is None and key not in currentmetadata: - raise exceptions.CommandError("metadata key %s does not exist" - " hence can not be deleted" - % key) - aggregate = cs.aggregates.set_metadata(aggregate.id, metadata) - print("Metadata has been successfully updated for aggregate %s." % - aggregate.id) - _print_aggregate_details(aggregate) - - -@utils.arg('aggregate', metavar='', help='Name or ID of aggregate.') -@utils.arg('host', metavar='', help='The host to add to the aggregate.') -def do_aggregate_add_host(cs, args): - """Add the host to the specified aggregate.""" - aggregate = _find_aggregate(cs, args.aggregate) - aggregate = cs.aggregates.add_host(aggregate.id, args.host) - print("Host %s has been successfully added for aggregate %s " % - (args.host, aggregate.id)) - _print_aggregate_details(aggregate) - - -@utils.arg('aggregate', metavar='', help='Name or ID of aggregate.') -@utils.arg('host', metavar='', - help='The host to remove from the aggregate.') -def do_aggregate_remove_host(cs, args): - """Remove the specified host from the specified aggregate.""" - aggregate = _find_aggregate(cs, args.aggregate) - aggregate = cs.aggregates.remove_host(aggregate.id, args.host) - print("Host %s has been successfully removed from aggregate %s " % - (args.host, aggregate.id)) - _print_aggregate_details(aggregate) - - -@utils.arg('aggregate', metavar='', help='Name or ID of aggregate.') -def do_aggregate_details(cs, args): - """Show details of the specified aggregate.""" - aggregate = _find_aggregate(cs, args.aggregate) - _print_aggregate_details(aggregate) - - -def _print_aggregate_details(aggregate): - columns = ['Id', 'Name', 'Availability Zone', 'Hosts', 'Metadata'] - - def parser_metadata(fields): - return utils.pretty_choice_dict(getattr(fields, 'metadata', {}) or {}) - - def parser_hosts(fields): - return cliutils.pretty_choice_list(getattr(fields, 'hosts', [])) - - formatters = { - 'Metadata': parser_metadata, - 'Hosts': parser_hosts, - } - utils.print_list([aggregate], columns, formatters=formatters) - - -@utils.arg('server', metavar='', help='Name or ID of server.') -@utils.arg( - 'host', metavar='', default=None, nargs='?', - help='destination host name.') -@utils.arg( - '--block-migrate', - action='store_true', - dest='block_migrate', - default=False, - help='True in case of block_migration.\ - (Default=False:live_migration)') -@utils.arg( - '--block_migrate', - action='store_true', - help=argparse.SUPPRESS) -@utils.arg( - '--disk-over-commit', - action='store_true', - dest='disk_over_commit', - default=False, - help='Allow overcommit.(Default=False)') -@utils.arg( - '--disk_over_commit', - action='store_true', - help=argparse.SUPPRESS) -def do_live_migration(cs, args): - """Migrate running server to a new machine.""" - _find_server(cs, args.server).live_migrate(args.host, - args.block_migrate, - args.disk_over_commit) - - -def _server_live_migrate(cs, server, args): - class HostServersLiveMigrateResponse(object): - def __init__(self, server_uuid, live_migration_accepted, - error_message): - self.server_uuid = server_uuid - self.live_migration_accepted = live_migration_accepted - self.error_message = error_message - success = True - error_message = "" - try: - cs.servers.live_migrate(server['id'], args.target_host, - args.block_migrate, args.disk_over_commit) - except Exception as e: - success = False - error_message = "Error while live migrating instance: %s" % e - return HostServersLiveMigrateResponse(server['id'], - success, - error_message) - - -@utils.arg('host', metavar='', help='Name of host.') -@utils.arg( - '--target-host', - metavar='', - default=None, - help='Name of target host.') -@utils.arg( - '--block-migrate', - action='store_true', - default=False, - help='Enable block migration.') -@utils.arg( - '--disk-over-commit', - action='store_true', - default=False, - help='Enable disk overcommit.') -def do_host_evacuate_live(cs, args): - """Live Migrate all instances of the specified host - to other available hosts. - """ - hypervisors = cs.hypervisors.search(args.host) - response = [] - for hyper in hypervisors: - servers = getattr(cs.hypervisors.servers(hyper.id), 'servers', []) - for server in servers: - response.append(_server_live_migrate(cs, server, args)) - utils.print_list(response, ["Server UUID", "Live Migration Accepted", - "Error Message"]) - - -@utils.arg('server', metavar='', nargs='+', - help='Name or ID of server(s).') -@utils.arg('--active', action='store_const', dest='state', - default='error', const='active', - help='Request the server be reset to "active" state instead ' - 'of "error" state (the default).') -def do_reset_state(cs, args): - """Reset the state of a server.""" - failure_flag = False - - for server in args.server: - try: - _find_server(cs, server).reset_state(args.state) - except Exception as e: - failure_flag = True - msg = "Reset state for server %s failed: %s" % (server, e) - print(msg) - - if failure_flag: - msg = "Unable to reset the state for the specified server(s)." - raise exceptions.CommandError(msg) - - -@utils.arg('server', metavar='', help='Name or ID of server.') -def do_reset_network(cs, args): - """Reset network of a server.""" - _find_server(cs, args.server).reset_network() - - -@utils.arg('--host', metavar='', default=None, - help='Name of host.') -@utils.arg('--binary', metavar='', default=None, - help='Service binary.') -def do_service_list(cs, args): - """Show a list of all running services. Filter by host & binary.""" - result = cs.services.list(host=args.host, binary=args.binary) - columns = ["Binary", "Host", "Zone", "Status", "State", "Updated_at"] - # NOTE(sulo): we check if the response has disabled_reason - # so as not to add the column when the extended ext is not enabled. - if hasattr(result[0], 'disabled_reason'): - columns.append("Disabled Reason") - - # NOTE(gtt): After https://review.openstack.org/#/c/39998/ nova will - # show id in response. - if result and hasattr(result[0], 'id'): - columns.insert(0, "Id") - - utils.print_list(result, columns) - - -@utils.arg('host', metavar='', help='Name of host.') -@utils.arg('binary', metavar='', help='Service binary.') -def do_service_enable(cs, args): - """Enable the service.""" - result = cs.services.enable(args.host, args.binary) - utils.print_list([result], ['Host', 'Binary', 'Status']) - - -@utils.arg('host', metavar='', help='Name of host.') -@utils.arg('binary', metavar='', help='Service binary.') -@utils.arg('--reason', metavar='', - help='Reason for disabling service.') -def do_service_disable(cs, args): - """Disable the service.""" - if args.reason: - result = cs.services.disable_log_reason(args.host, args.binary, - args.reason) - utils.print_list([result], ['Host', 'Binary', 'Status', - 'Disabled Reason']) - else: - result = cs.services.disable(args.host, args.binary) - utils.print_list([result], ['Host', 'Binary', 'Status']) - - -@utils.arg('id', metavar='', help='Id of service.') -def do_service_delete(cs, args): - """Delete the service.""" - cs.services.delete(args.id) - - -@utils.arg('fixed_ip', metavar='', help='Fixed IP Address.') -def do_fixed_ip_get(cs, args): - """Retrieve info on a fixed ip.""" - result = cs.fixed_ips.get(args.fixed_ip) - utils.print_list([result], ['address', 'cidr', 'hostname', 'host']) - - -@utils.arg('fixed_ip', metavar='', help='Fixed IP Address.') -def do_fixed_ip_reserve(cs, args): - """Reserve a fixed IP.""" - cs.fixed_ips.reserve(args.fixed_ip) - - -@utils.arg('fixed_ip', metavar='', help='Fixed IP Address.') -def do_fixed_ip_unreserve(cs, args): - """Unreserve a fixed IP.""" - cs.fixed_ips.unreserve(args.fixed_ip) - - -@utils.arg('host', metavar='', help='Name of host.') -def do_host_describe(cs, args): - """Describe a specific host.""" - result = cs.hosts.get(args.host) - columns = ["HOST", "PROJECT", "cpu", "memory_mb", "disk_gb"] - utils.print_list(result, columns) - - -@utils.arg('--zone', metavar='', default=None, - help='Filters the list, returning only those ' - 'hosts in the availability zone .') -@utils.arg('--service-name', metavar='', default=None, - help='Filters the list, returning only those ' - 'hosts providing service .') -def do_host_list(cs, args): - """List all hosts by service.""" - columns = ["host_name", "service", "zone"] - result = cs.hosts.list(args.zone, args.service_name) - utils.print_list(result, columns) - - -@utils.arg('host', metavar='', help='Name of host.') -@utils.arg('--status', metavar='', default=None, dest='status', - help='Either enable or disable a host.') -@utils.arg( - '--maintenance', - metavar='', - default=None, - dest='maintenance', - help='Either put or resume host to/from maintenance.') -def do_host_update(cs, args): - """Update host settings.""" - updates = {} - columns = ["HOST"] - if args.status: - updates['status'] = args.status - columns.append("status") - if args.maintenance: - updates['maintenance_mode'] = args.maintenance - columns.append("maintenance_mode") - result = cs.hosts.update(args.host, updates) - utils.print_list([result], columns) - - -@utils.arg('host', metavar='', help='Name of host.') -@utils.arg('--action', metavar='', dest='action', - choices=['startup', 'shutdown', 'reboot'], - help='A power action: startup, reboot, or shutdown.') -def do_host_action(cs, args): - """Perform a power action on a host.""" - result = cs.hosts.host_action(args.host, args.action) - utils.print_list([result], ['HOST', 'power_action']) - - -def _find_hypervisor(cs, hypervisor): - """Get a hypervisor by name or ID.""" - return utils.find_resource(cs.hypervisors, hypervisor) - - -@utils.arg('--matching', metavar='', default=None, - help='List hypervisors matching the given .') -def do_hypervisor_list(cs, args): - """List hypervisors.""" - columns = ['ID', 'Hypervisor hostname'] - if args.matching: - utils.print_list(cs.hypervisors.search(args.matching), columns) - else: - # Since we're not outputting detail data, choose - # detailed=False for server-side efficiency - utils.print_list(cs.hypervisors.list(False), columns) - - -@utils.arg('hostname', metavar='', - help='The hypervisor hostname (or pattern) to search for.') -def do_hypervisor_servers(cs, args): - """List servers belonging to specific hypervisors.""" - # Get a list of hypervisors first - hypers = cs.hypervisors.search(args.hostname) - - class InstanceOnHyper(object): - def __init__(self, **kwargs): - self.__dict__.update(kwargs) - - # Massage the result into a list to be displayed - servers = [] - for hyper in hypers: - # Get a list of servers for each hypervisor - hyper_host = hyper.hypervisor_hostname - hyper_id = hyper.id - - hyper_servers = cs.hypervisors.servers(hyper_id) - if hasattr(hyper_servers, 'servers'): - print(hyper_servers.servers) - servers.extend([InstanceOnHyper(id=serv['id'], - name=serv['name'], - hypervisor_hostname=hyper_host, - hypervisor_id=hyper_id) - for serv in hyper_servers.servers]) - - # Output the data - utils.print_list(servers, ['ID', 'Name', 'Hypervisor ID', - 'Hypervisor Hostname']) - - -@utils.arg( - 'hypervisor', - metavar='', - help='Name or ID of the hypervisor to show the details of.') -def do_hypervisor_show(cs, args): - """Display the details of the specified hypervisor.""" - hyper = _find_hypervisor(cs, args.hypervisor) - - # Build up the dict - info = hyper._info.copy() - info['service_id'] = info['service']['id'] - info['service_host'] = info['service']['host'] - del info['service'] - - utils.print_dict(info) - - -@utils.arg( - 'hypervisor', - metavar='', - help='Name or ID of the hypervisor to show the uptime of.') -def do_hypervisor_uptime(cs, args): - """Display the uptime of the specified hypervisor.""" - hyper = _find_hypervisor(cs, args.hypervisor) - hyper = cs.hypervisors.uptime(hyper) - - # Output the uptime information - utils.print_dict(hyper._info.copy()) - - -def do_hypervisor_stats(cs, args): - """Get hypervisor statistics over all compute nodes.""" - stats = cs.hypervisors.statistics() - utils.print_dict(stats._info.copy()) - - -def ensure_service_catalog_present(cs): - if not hasattr(cs.client, 'service_catalog'): - # Turn off token caching and re-auth - cs.client.unauthenticate() - cs.client.use_token_cache(False) - cs.client.authenticate() - - -def do_endpoints(cs, _args): - """Discover endpoints that get returned from the authenticate services.""" - if isinstance(cs.client, client.SessionClient): - auth = cs.client.auth - sc = auth.get_Access(cs.client.session).service_catalog - for service in sc.get_data(): - _print_endpoints(service, cs.client.region_name) - else: - ensure_service_catalog_present(cs) - - catalog = cs.client.service_catalog.catalog - region = cs.client.region_name - - for service in catalog['access']['serviceCatalog']: - _print_endpoints(service, region) - - -def _print_endpoints(service, region): - name, endpoints = service["name"], service["endpoints"] - - try: - endpoint = _get_first_endpoint(endpoints, region) - utils.print_dict(endpoint, name) - except LookupError: - print(_("WARNING: %(service)s has no endpoint in %(region)s! " - "Available endpoints for this service:") % - {'service': name, 'region': region}) - for other_endpoint in endpoints: - utils.print_dict(other_endpoint, name) - - -def _get_first_endpoint(endpoints, region): - """Find the first suitable endpoint in endpoints. - - If there is only one endpoint, return it. If there is more than - one endpoint, return the first one with the given region. If there - are no endpoints, or there is more than one endpoint but none of - them match the given region, raise KeyError. - - """ - if len(endpoints) == 1: - return endpoints[0] - else: - for candidate_endpoint in endpoints: - if candidate_endpoint["region"] == region: - return candidate_endpoint - - raise LookupError("No suitable endpoint found") - - -@utils.arg('--wrap', dest='wrap', metavar='', default=64, - help='wrap PKI tokens to a specified length, or 0 to disable') -def do_credentials(cs, _args): - """Show user credentials returned from auth.""" - if isinstance(cs.client, client.SessionClient): - auth = cs.client.auth - sc = auth.get_access(cs.client.session).service_catalog - utils.print_dict(sc.catalog['user'], 'User Credentials', - wrap=int(_args.wrap)) - utils.print_dict(sc.get_token(), 'Token', wrap=int(_args.wrap)) - else: - ensure_service_catalog_present(cs) - catalog = cs.client.service_catalog.catalog - utils.print_dict(catalog['access']['user'], "User Credentials", - wrap=int(_args.wrap)) - utils.print_dict(catalog['access']['token'], "Token", - wrap=int(_args.wrap)) - - -def do_extension_list(cs, _args): - """ - List all the os-api extensions that are available. - """ - extensions = cs.list_extensions.show_all() - fields = ["Name", "Summary", "Alias", "Version"] - utils.print_list(extensions, fields) - - -@utils.arg('server', metavar='', help='Name or ID of server.') -@utils.arg( - '--port', - dest='port', - action='store', - type=int, - default=22, - help='Optional flag to indicate which port to use for ssh. ' - '(Default=22)') -@utils.arg( - '--private', - dest='private', - action='store_true', - default=False, - help=argparse.SUPPRESS) -@utils.arg( - '--address-type', - dest='address_type', - action='store', - type=str, - default='floating', - help='Optional flag to indicate which IP type to use. Possible values ' - 'includes fixed and floating (the Default).') -@utils.arg('--network', metavar='', - help='Network to use for the ssh.', default=None) -@utils.arg( - '--ipv6', - dest='ipv6', - action='store_true', - default=False, - help='Optional flag to indicate whether to use an IPv6 address ' - 'attached to a server. (Defaults to IPv4 address)') -@utils.arg('--login', metavar='', help='Login to use.', default="root") -@utils.arg( - '-i', '--identity', - dest='identity', - help='Private key file, same as the -i option to the ssh command.', - default='') -@utils.arg( - '--extra-opts', - dest='extra', - help='Extra options to pass to ssh. see: man ssh', - default='') -def do_ssh(cs, args): - """SSH into a server.""" - if '@' in args.server: - user, server = args.server.split('@', 1) - args.login = user - args.server = server - - addresses = _find_server(cs, args.server).addresses - address_type = "fixed" if args.private else args.address_type - version = 6 if args.ipv6 else 4 - pretty_version = 'IPv%d' % version - - # Select the network to use. - if args.network: - network_addresses = addresses.get(args.network) - if not network_addresses: - msg = _("Server '%(server)s' is not attached to network " - "'%(network)s'") - raise exceptions.ResourceNotFound( - msg % {'server': args.server, 'network': args.network}) - else: - if len(addresses) > 1: - msg = _("Server '%(server)s' is attached to more than one network." - " Please pick the network to use.") - raise exceptions.CommandError(msg % {'server': args.server}) - elif not addresses: - msg = _("Server '%(server)s' is not attached to any network.") - raise exceptions.CommandError(msg % {'server': args.server}) - else: - network_addresses = list(six.itervalues(addresses))[0] - - # Select the address in the selected network. - # If the extension is not present, we assume the address to be floating. - match = lambda addr: all(( - addr.get('version') == version, - addr.get('OS-EXT-IPS:type', 'floating') == address_type)) - matching_addresses = [address.get('addr') - for address in network_addresses if match(address)] - if not any(matching_addresses): - msg = _("No address that would match network '%(network)s'" - " and type '%(address_type)s' of version %(pretty_version)s " - "has been found for server '%(server)s'.") - raise exceptions.ResourceNotFound(msg % { - 'network': args.network, 'address_type': address_type, - 'pretty_version': pretty_version, 'server': args.server}) - elif len(matching_addresses) > 1: - msg = _("More than one %(pretty_version)s %(address_type)s address" - "found.") - raise exceptions.CommandError(msg % {'pretty_version': pretty_version, - 'address_type': address_type}) - else: - ip_address = matching_addresses[0] - - identity = '-i %s' % args.identity if len(args.identity) else '' - - cmd = "ssh -%d -p%d %s %s@%s %s" % (version, args.port, identity, - args.login, ip_address, args.extra) - logger.debug("Executing cmd '%s'", cmd) - os.system(cmd) - - -_quota_resources = ['instances', 'cores', 'ram', - 'fixed_ips', 'metadata_items', 'key_pairs', - 'server_groups', 'server_group_members'] - - -def _quota_show(quotas): - quota_dict = {} - for resource in _quota_resources: - try: - quota_dict[resource] = getattr(quotas, resource) - except AttributeError: - pass - utils.print_dict(quota_dict) - - -def _quota_usage(quotas): - class QuotaObj(object): - def __init__(self, resource, quota_dict): - setattr(self, 'resource', resource) - for (k, v) in six.iteritems(quota_dict): - setattr(self, k, v) - - quota_list = [] - for resource in _quota_resources: - try: - quota_list.append(QuotaObj(resource, getattr(quotas, resource))) - except AttributeError: - pass - utils.print_list(quota_list, ['resource', 'in use', 'limit'], - sortby_index=0) - - -def _quota_update(manager, identifier, args): - updates = {} - for resource in _quota_resources: - val = getattr(args, resource, None) - if val is not None: - updates[resource] = val - - if updates: - # default value of force is None to make sure this client - # will be compatibile with old nova server - manager.update(identifier, **updates) - - -@utils.arg( - '--tenant', - metavar='', - default=None, - help='ID of tenant to list the quotas for.') -def do_quota_show(cs, args): - """List the quotas for a tenant.""" - - if not args.tenant: - _quota_show(cs.quotas.get(cs.client.tenant_id)) - else: - _quota_show(cs.quotas.get(args.tenant)) - - -@utils.arg( - '--tenant', - metavar='', - default=None, - help='ID of tenant to list the quotas for.') -@utils.arg( - '--user', - metavar='', - default=None, - help='ID of user to list the quotas for.') -def do_quota_usage(cs, args): - """List the quotas for a tenant.""" - - tenant = args.tenant or cs.client.tenant_id - _quota_usage(cs.quotas.get(tenant, user_id=args.user, detail=True)) - - -@utils.arg( - '--tenant', - metavar='', - default=None, - help='ID of tenant to list the default quotas for.') -def do_quota_defaults(cs, args): - """List the default quotas for a tenant.""" - - if not args.tenant: - _quota_show(cs.quotas.defaults(cs.client.tenant_id)) - else: - _quota_show(cs.quotas.defaults(args.tenant)) - - -@utils.arg( - 'tenant', - metavar='', - help='ID of tenant to set the quotas for.') -@utils.arg('--instances', - metavar='', - type=int, default=None, - help='New value for the "instances" quota.') -@utils.arg('--cores', - metavar='', - type=int, default=None, - help='New value for the "cores" quota.') -@utils.arg('--ram', - metavar='', - type=int, default=None, - help='New value for the "ram" quota.') -@utils.arg( - '--fixed-ips', - metavar='', - type=int, - default=None, - help='New value for the "fixed-ips" quota.') -@utils.arg( - '--metadata-items', - metavar='', - type=int, - default=None, - help='New value for the "metadata-items" quota.') -@utils.arg( - '--metadata_items', - type=int, - help=argparse.SUPPRESS) -@utils.arg( - '--key-pairs', - metavar='', - type=int, - default=None, - help='New value for the "key-pairs" quota.') -@utils.arg( - '--server-groups', - metavar='', - type=int, - default=None, - help='New value for the "server-groups" quota.') -@utils.arg( - '--server-group-members', - metavar='', - type=int, - default=None, - help='New value for the "server-group-members" quota.') -@utils.arg( - '--force', - dest='force', - action="store_true", - default=None, - help='Whether force update the quota even if the already used and ' - 'reserved exceeds the new quota') -def do_quota_update(cs, args): - """Update the quotas for a tenant.""" - - _quota_update(cs.quotas, args.tenant, args) - - -@utils.arg('--tenant', - metavar='', - required=True, - help='ID of tenant to delete quota for.') -def do_quota_delete(cs, args): - """Delete quota for a tenant so their quota will revert back to default.""" - - cs.quotas.delete(args.tenant) - - -@utils.arg('server', metavar='', help='Name or ID of server.') -@utils.arg( - 'host', metavar='', nargs='?', - help="Name or ID of the target host. " - "If no host is specified, the scheduler will choose one.") -@utils.arg( - '--password', - dest='password', - metavar='', - help="Set the provided password on the evacuated server. Not applicable " - "with on-shared-storage flag") -@utils.arg( - '--on-shared-storage', - dest='on_shared_storage', - action="store_true", - default=False, - help='Specifies whether server files are located on shared storage') -def do_evacuate(cs, args): - """Evacuate server from failed host to specified one.""" - server = _find_server(cs, args.server) - - res = server.evacuate(args.host, args.on_shared_storage, args.password)[1] - if type(res) is dict: - utils.print_dict(res) - - -def _print_interfaces(interfaces): - columns = ['Port State', 'Port ID', 'Net ID', 'IP addresses', - 'MAC Addr'] - - class FormattedInterface(object): - def __init__(self, interface): - for col in columns: - key = col.lower().replace(" ", "_") - if hasattr(interface, key): - setattr(self, key, getattr(interface, key)) - self.ip_addresses = ",".join([fip['ip_address'] - for fip in interface.fixed_ips]) - utils.print_list([FormattedInterface(i) for i in interfaces], columns) - - -@utils.arg('server', metavar='', help='Name or ID of server.') -def do_interface_list(cs, args): - """List interfaces attached to a server.""" - server = _find_server(cs, args.server) - - res = server.interface_list() - if type(res) is list: - _print_interfaces(res) - - -@utils.arg('server', metavar='', help='Name or ID of server.') -@utils.arg('--port-id', metavar='', help='Port ID.', dest="port_id") -@utils.arg('--net-id', metavar='', help='Network ID', - default=None, dest="net_id") -@utils.arg('--fixed-ip', metavar='', help='Requested fixed IP.', - default=None, dest="fixed_ip") -def do_interface_attach(cs, args): - """Attach a network interface to a server.""" - server = _find_server(cs, args.server) - - res = server.interface_attach(args.port_id, args.net_id, args.fixed_ip) - if type(res) is dict: - utils.print_dict(res) - - -@utils.arg('server', metavar='', help='Name or ID of server.') -@utils.arg('port_id', metavar='', help='Port ID.') -def do_interface_detach(cs, args): - """Detach a network interface from a server.""" - server = _find_server(cs, args.server) - - res = server.interface_detach(args.port_id) - if type(res) is dict: - utils.print_dict(res) - - -def _treeizeAvailabilityZone(zone): - """Build a tree view for availability zones.""" - AvailabilityZone = availability_zones.AvailabilityZone - - az = AvailabilityZone(zone.manager, - copy.deepcopy(zone._info), zone._loaded) - result = [] - - # Zone tree view item - az.zone_name = zone.zone_name - az.zone_state = ('available' - if zone.zone_state['available'] else 'not available') - az._info['zone_name'] = az.zone_name - az._info['zone_state'] = az.zone_state - result.append(az) - - if zone.hosts is not None: - zone_hosts = sorted(zone.hosts.items(), key=lambda x: x[0]) - for (host, services) in zone_hosts: - # Host tree view item - az = AvailabilityZone(zone.manager, - copy.deepcopy(zone._info), zone._loaded) - az.zone_name = '|- %s' % host - az.zone_state = '' - az._info['zone_name'] = az.zone_name - az._info['zone_state'] = az.zone_state - result.append(az) - - for (svc, state) in services.items(): - # Service tree view item - az = AvailabilityZone(zone.manager, - copy.deepcopy(zone._info), zone._loaded) - az.zone_name = '| |- %s' % svc - az.zone_state = '%s %s %s' % ( - 'enabled' if state['active'] else 'disabled', - ':-)' if state['available'] else 'XXX', - state['updated_at']) - az._info['zone_name'] = az.zone_name - az._info['zone_state'] = az.zone_state - result.append(az) - return result - - -def do_availability_zone_list(cs, _args): - """List all the availability zones.""" - try: - availability_zones = cs.availability_zones.list() - except exceptions.Forbidden as e: # policy doesn't allow probably - try: - availability_zones = cs.availability_zones.list(detailed=False) - except Exception: - raise e - - result = [] - for zone in availability_zones: - result += _treeizeAvailabilityZone(zone) - _translate_availability_zone_keys(result) - utils.print_list(result, ['Name', 'Status'], - sortby_index=None) - - -@utils.arg('--tenant', - # nova db searches by project_id - dest='tenant', - metavar='', - nargs='?', - help=_('Display information from single tenant (Admin only).')) -@utils.arg('--reserved', - dest='reserved', - action='store_true', - default=False, - help=_('Include reservations count.')) -def do_absolute_limits(cs, args): - """Print a list of absolute limits for a user""" - limits = cs.limits.get(args.reserved, args.tenant).absolute - - class Limit(object): - def __init__(self, name, used, max, other): - self.name = name - self.used = used - self.max = max - self.other = other - - limit_map = { - 'maxServerMeta': {'name': 'Server Meta', 'type': 'max'}, - 'maxPersonality': {'name': 'Personality', 'type': 'max'}, - 'maxPersonalitySize': {'name': 'Personality Size', 'type': 'max'}, - 'maxImageMeta': {'name': 'ImageMeta', 'type': 'max'}, - 'maxTotalKeypairs': {'name': 'Keypairs', 'type': 'max'}, - 'totalCoresUsed': {'name': 'Cores', 'type': 'used'}, - 'maxTotalCores': {'name': 'Cores', 'type': 'max'}, - 'totalRAMUsed': {'name': 'RAM', 'type': 'used'}, - 'maxTotalRAMSize': {'name': 'RAM', 'type': 'max'}, - 'totalInstancesUsed': {'name': 'Instances', 'type': 'used'}, - 'maxTotalInstances': {'name': 'Instances', 'type': 'max'}, - 'totalFloatingIpsUsed': {'name': 'FloatingIps', 'type': 'used'}, - 'maxTotalFloatingIps': {'name': 'FloatingIps', 'type': 'max'}, - 'totalSecurityGroupsUsed': {'name': 'SecurityGroups', 'type': 'used'}, - 'maxSecurityGroups': {'name': 'SecurityGroups', 'type': 'max'}, - 'maxSecurityGroupRules': {'name': 'SecurityGroupRules', 'type': 'max'}, - 'maxServerGroups': {'name': 'ServerGroups', 'type': 'max'}, - 'totalServerGroupsUsed': {'name': 'ServerGroups', 'type': 'used'}, - 'maxServerGroupMembers': {'name': 'ServerGroupMembers', 'type': 'max'}, - } - - max = {} - used = {} - other = {} - limit_names = [] - columns = ['Name', 'Used', 'Max'] - for l in limits: - map = limit_map.get(l.name, {'name': l.name, 'type': 'other'}) - name = map['name'] - if map['type'] == 'max': - max[name] = l.value - elif map['type'] == 'used': - used[name] = l.value - else: - other[name] = l.value - columns.append('Other') - if name not in limit_names: - limit_names.append(name) - - limit_names.sort() - - limit_list = [] - for name in limit_names: - l = Limit(name, - used.get(name, "-"), - max.get(name, "-"), - other.get(name, "-")) - limit_list.append(l) - - utils.print_list(limit_list, columns) - - -def do_rate_limits(cs, args): - """Print a list of rate limits for a user""" - limits = cs.limits.get().rate - columns = ['Verb', 'URI', 'Value', 'Remain', 'Unit', 'Next_Available'] - utils.print_list(limits, columns) diff --git a/novaclient/v3/usage.py b/novaclient/v3/usage.py deleted file mode 100644 index dd16997c6..000000000 --- a/novaclient/v3/usage.py +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright 2013 IBM Corp. -# -# 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. - -""" -Usage interface. -""" - -from novaclient.v1_1 import usage - - -class Usage(usage.Usage): - pass - - -class UsageManager(usage.UsageManager): - pass diff --git a/novaclient/v3/volumes.py b/novaclient/v3/volumes.py deleted file mode 100644 index 53cc05f6c..000000000 --- a/novaclient/v3/volumes.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright 2013 IBM Corp. -# -# 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. - -""" -Volume interface -""" - -from novaclient import base - - -class VolumeManager(base.Manager): - """ - Manage :class:`Volume` resources. - """ - - def attach_server_volume(self, server, volume_id, device, - disk_bus=None, device_type=None): - """ - Attach a volume identified by the volume ID to the given server ID - - :param server: The server (or it's ID) - :param volume_id: The ID of the volume to attach. - :param device: The device name - :param disk_bus: The disk bus of the volume - :param device_type: The device type of the volume - :rtype: :class:`Volume` - """ - body = {'volume_id': volume_id, 'device': device} - if disk_bus: - body['disk_bus'] = disk_bus - if device_type: - body['device_type'] = device_type - return self._action('attach', server, body) - - def update_server_volume(self, server, old_volume_id, new_volume_id): - """ - Update the volume identified by the attachment ID, that is attached to - the given server ID - - :param server_id: The server (or it's ID) - :param old_volume_id: The ID of the attachment - :param new_volume_id: The ID of the new volume to attach - :rtype: :class:`Volume` - """ - body = {'new_volume_id': new_volume_id, 'old_volume_id': old_volume_id} - return self._action('swap_volume_attachment', server, body) - - def delete_server_volume(self, server, volume_id): - """ - Detach a volume identified by the attachment ID from the given server - - :param server_id: The ID of the server - :param volume_id: The ID of the attachment - """ - return self._action('detach', server, {'volume_id': volume_id}) - - def _action(self, action, server, info=None, **kwargs): - """ - Perform a server "action" -- reboot/rebuild/resize/etc. - """ - body = {action: info} - self.run_hooks('modify_body_for_action', body, **kwargs) - url = '/servers/%s/action' % base.getid(server) - return self.api.client.post(url, body=body)