diff options
Diffstat (limited to 'openstackclient')
| -rw-r--r-- | openstackclient/common/clientmanager.py | 6 | ||||
| -rw-r--r-- | openstackclient/compute/v2/server.py | 5 | ||||
| -rw-r--r-- | openstackclient/network/v2/address_scope.py | 214 | ||||
| -rw-r--r-- | openstackclient/tests/compute/v2/fakes.py | 3 | ||||
| -rw-r--r-- | openstackclient/tests/compute/v2/test_server.py | 5 | ||||
| -rw-r--r-- | openstackclient/tests/network/v2/fakes.py | 18 | ||||
| -rw-r--r-- | openstackclient/tests/network/v2/test_address_scope.py | 357 |
7 files changed, 604 insertions, 4 deletions
diff --git a/openstackclient/common/clientmanager.py b/openstackclient/common/clientmanager.py index c29bf224..9c2b320c 100644 --- a/openstackclient/common/clientmanager.py +++ b/openstackclient/common/clientmanager.py @@ -200,12 +200,12 @@ class ClientManager(object): # ignore all domain related configs. if (self._api_version.get('identity') == '2.0' and self.auth_plugin_name.endswith('password')): - LOG.warning("Ignoring domain related configs " - "because identity API version is 2.0") domain_props = ['project_domain_name', 'project_domain_id', 'user_domain_name', 'user_domain_id'] for prop in domain_props: - self._auth_params.pop(prop, None) + if self._auth_params.pop(prop, None) is not None: + LOG.warning("Ignoring domain related configs " + + prop + " because identity API version is 2.0") # For compatibility until all clients can be updated if 'project_name' in self._auth_params: diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index d32f7ade..40c3c2a2 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -152,6 +152,11 @@ def _prep_server_detail(compute_client, server): if 'tenant_id' in info: info['project_id'] = info.pop('tenant_id') + # Map power state num to meanful string + if 'OS-EXT-STS:power_state' in info: + info['OS-EXT-STS:power_state'] = _format_servers_list_power_state( + info['OS-EXT-STS:power_state']) + # Remove values that are long and not too useful info.pop('links', None) diff --git a/openstackclient/network/v2/address_scope.py b/openstackclient/network/v2/address_scope.py new file mode 100644 index 00000000..fac0849f --- /dev/null +++ b/openstackclient/network/v2/address_scope.py @@ -0,0 +1,214 @@ +# 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. +# + +"""Address scope action implementations""" + +from openstackclient.common import command +from openstackclient.common import exceptions +from openstackclient.common import utils +from openstackclient.i18n import _ +from openstackclient.identity import common as identity_common + + +def _get_columns(item): + columns = list(item.keys()) + if 'tenant_id' in columns: + columns.remove('tenant_id') + columns.append('project_id') + + return tuple(sorted(columns)) + + +def _get_attrs(client_manager, parsed_args): + attrs = {} + attrs['name'] = parsed_args.name + attrs['ip_version'] = parsed_args.ip_version + if parsed_args.share: + attrs['shared'] = True + if parsed_args.no_share: + attrs['shared'] = False + if 'project' in parsed_args and parsed_args.project is not None: + identity_client = client_manager.identity + project_id = identity_common.find_project( + identity_client, + parsed_args.project, + parsed_args.project_domain, + ).id + attrs['tenant_id'] = project_id + + return attrs + + +class CreateAddressScope(command.ShowOne): + """Create a new Address Scope""" + + def get_parser(self, prog_name): + parser = super(CreateAddressScope, self).get_parser(prog_name) + parser.add_argument( + 'name', + metavar="<name>", + help=_("New address scope name") + ) + parser.add_argument( + '--ip-version', + type=int, + default=4, + choices=[4, 6], + help=_("IP version (default is 4)") + ) + parser.add_argument( + '--project', + metavar="<project>", + help=_("Owner's project (name or ID)") + ) + identity_common.add_project_domain_option_to_parser(parser) + + share_group = parser.add_mutually_exclusive_group() + share_group.add_argument( + '--share', + action='store_true', + help=_('Share the address scope between projects') + ) + share_group.add_argument( + '--no-share', + action='store_true', + help=_('Do not share the address scope between projects (default)') + ) + + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + attrs = _get_attrs(self.app.client_manager, parsed_args) + obj = client.create_address_scope(**attrs) + columns = _get_columns(obj) + data = utils.get_item_properties(obj, columns, formatters={}) + + return columns, data + + +class DeleteAddressScope(command.Command): + """Delete an address scope""" + + def get_parser(self, prog_name): + parser = super(DeleteAddressScope, self).get_parser(prog_name) + parser.add_argument( + 'address_scope', + metavar="<address-scope>", + help=_("Address scope to delete (name or ID)") + ) + + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + obj = client.find_address_scope(parsed_args.address_scope) + client.delete_address_scope(obj) + + +class ListAddressScope(command.Lister): + """List address scopes""" + + def take_action(self, parsed_args): + client = self.app.client_manager.network + columns = ( + 'id', + 'name', + 'ip_version', + 'shared', + 'tenant_id', + ) + column_headers = ( + 'ID', + 'Name', + 'IP Version', + 'Shared', + 'Project', + ) + data = client.address_scopes() + + return (column_headers, + (utils.get_item_properties( + s, columns, formatters={}, + ) for s in data)) + + +class SetAddressScope(command.Command): + """Set address scope properties""" + + def get_parser(self, prog_name): + parser = super(SetAddressScope, self).get_parser(prog_name) + parser.add_argument( + 'address_scope', + metavar="<address-scope>", + help=_("Address scope to modify (name or ID)") + ) + parser.add_argument( + '--name', + metavar="<name>", + help=_('Set address scope name') + ) + share_group = parser.add_mutually_exclusive_group() + share_group.add_argument( + '--share', + action='store_true', + help=_('Share the address scope between projects') + ) + share_group.add_argument( + '--no-share', + action='store_true', + help=_('Do not share the address scope between projects') + ) + + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + obj = client.find_address_scope( + parsed_args.address_scope, + ignore_missing=False) + attrs = {} + if parsed_args.name is not None: + attrs['name'] = parsed_args.name + if parsed_args.share: + attrs['shared'] = True + if parsed_args.no_share: + attrs['shared'] = False + if attrs == {}: + msg = "Nothing specified to be set." + raise exceptions.CommandError(msg) + client.update_address_scope(obj, **attrs) + + +class ShowAddressScope(command.ShowOne): + """Display address scope details""" + + def get_parser(self, prog_name): + parser = super(ShowAddressScope, self).get_parser(prog_name) + parser.add_argument( + 'address_scope', + metavar="<address-scope>", + help=_("Address scope to display (name or ID)") + ) + + return parser + + def take_action(self, parsed_args): + client = self.app.client_manager.network + obj = client.find_address_scope( + parsed_args.address_scope, + ignore_missing=False) + columns = _get_columns(obj) + data = utils.get_item_properties(obj, columns, formatters={}) + + return columns, data diff --git a/openstackclient/tests/compute/v2/fakes.py b/openstackclient/tests/compute/v2/fakes.py index 23957738..b3f3fb49 100644 --- a/openstackclient/tests/compute/v2/fakes.py +++ b/openstackclient/tests/compute/v2/fakes.py @@ -472,7 +472,8 @@ class FakeServer(object): }, 'flavor': { 'id': 'flavor-id-' + uuid.uuid4().hex, - } + }, + 'OS-EXT-STS:power_state': 1, } # Overwrite default attributes. diff --git a/openstackclient/tests/compute/v2/test_server.py b/openstackclient/tests/compute/v2/test_server.py index dfb7649e..cc0ac053 100644 --- a/openstackclient/tests/compute/v2/test_server.py +++ b/openstackclient/tests/compute/v2/test_server.py @@ -89,6 +89,7 @@ class TestServer(compute_fakes.TestComputev2): class TestServerCreate(TestServer): columns = ( + 'OS-EXT-STS:power_state', 'addresses', 'flavor', 'id', @@ -100,6 +101,8 @@ class TestServerCreate(TestServer): def datalist(self): datalist = ( + server._format_servers_list_power_state( + getattr(self.new_server, 'OS-EXT-STS:power_state')), '', self.flavor.name + ' (' + self.new_server.flavor.get('id') + ')', self.new_server.id, @@ -1473,6 +1476,8 @@ class TestServerGeneral(TestServer): 'image': u'%s (%s)' % (_image.name, _image.id), 'project_id': u'tenant-id-xxx', 'properties': '', + 'OS-EXT-STS:power_state': server._format_servers_list_power_state( + getattr(_server, 'OS-EXT-STS:power_state')), } # Call _prep_server_detail(). diff --git a/openstackclient/tests/network/v2/fakes.py b/openstackclient/tests/network/v2/fakes.py index 1989b515..7c4604bd 100644 --- a/openstackclient/tests/network/v2/fakes.py +++ b/openstackclient/tests/network/v2/fakes.py @@ -106,6 +106,24 @@ class FakeAddressScope(object): return address_scope + @staticmethod + def create_address_scopes(attrs=None, count=2): + """Create multiple fake address scopes. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of address scopes to fake + :return: + A list of FakeResource objects faking the address scopes + """ + address_scopes = [] + for i in range(0, count): + address_scopes.append( + FakeAddressScope.create_one_address_scope(attrs)) + + return address_scopes + class FakeAvailabilityZone(object): """Fake one or more network availability zones (AZs).""" diff --git a/openstackclient/tests/network/v2/test_address_scope.py b/openstackclient/tests/network/v2/test_address_scope.py new file mode 100644 index 00000000..ac94b489 --- /dev/null +++ b/openstackclient/tests/network/v2/test_address_scope.py @@ -0,0 +1,357 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +import copy +import mock + +from openstackclient.common import exceptions +from openstackclient.network.v2 import address_scope +from openstackclient.tests import fakes +from openstackclient.tests.identity.v3 import fakes as identity_fakes_v3 +from openstackclient.tests.network.v2 import fakes as network_fakes +from openstackclient.tests import utils as tests_utils + + +class TestAddressScope(network_fakes.TestNetworkV2): + + def setUp(self): + super(TestAddressScope, self).setUp() + + # Get a shortcut to the network client + self.network = self.app.client_manager.network + + +class TestCreateAddressScope(TestAddressScope): + + # The new address scope created. + new_address_scope = ( + network_fakes.FakeAddressScope.create_one_address_scope( + attrs={ + 'tenant_id': identity_fakes_v3.project_id, + } + )) + columns = ( + 'id', + 'ip_version', + 'name', + 'project_id', + 'shared' + ) + data = ( + new_address_scope.id, + new_address_scope.ip_version, + new_address_scope.name, + new_address_scope.project_id, + new_address_scope.shared, + ) + + def setUp(self): + super(TestCreateAddressScope, self).setUp() + self.network.create_address_scope = mock.Mock( + return_value=self.new_address_scope) + + # Get the command object to test + self.cmd = address_scope.CreateAddressScope(self.app, self.namespace) + + # Set identity client v3. And get a shortcut to Identity client. + identity_client = identity_fakes_v3.FakeIdentityv3Client( + endpoint=fakes.AUTH_URL, + token=fakes.AUTH_TOKEN, + ) + self.app.client_manager.identity = identity_client + self.identity = self.app.client_manager.identity + + # Get a shortcut to the ProjectManager Mock + self.projects_mock = self.identity.projects + self.projects_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes_v3.PROJECT), + loaded=True, + ) + + # Get a shortcut to the DomainManager Mock + self.domains_mock = self.identity.domains + self.domains_mock.get.return_value = fakes.FakeResource( + None, + copy.deepcopy(identity_fakes_v3.DOMAIN), + loaded=True, + ) + + def test_create_no_options(self): + arglist = [] + verifylist = [] + + # Missing required args should bail here + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_create_default_options(self): + arglist = [ + self.new_address_scope.name, + ] + verifylist = [ + ('project', None), + ('ip_version', self.new_address_scope.ip_version), + ('name', self.new_address_scope.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = (self.cmd.take_action(parsed_args)) + + self.network.create_address_scope.assert_called_once_with(**{ + 'ip_version': self.new_address_scope.ip_version, + 'name': self.new_address_scope.name, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_all_options(self): + arglist = [ + '--ip-version', str(self.new_address_scope.ip_version), + '--share', + '--project', identity_fakes_v3.project_name, + '--project-domain', identity_fakes_v3.domain_name, + self.new_address_scope.name, + ] + verifylist = [ + ('ip_version', self.new_address_scope.ip_version), + ('share', True), + ('project', identity_fakes_v3.project_name), + ('project_domain', identity_fakes_v3.domain_name), + ('name', self.new_address_scope.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = (self.cmd.take_action(parsed_args)) + + self.network.create_address_scope.assert_called_once_with(**{ + 'ip_version': self.new_address_scope.ip_version, + 'shared': True, + 'tenant_id': identity_fakes_v3.project_id, + 'name': self.new_address_scope.name, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_create_no_share(self): + arglist = [ + '--no-share', + self.new_address_scope.name, + ] + verifylist = [ + ('no_share', True), + ('name', self.new_address_scope.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_address_scope.assert_called_once_with(**{ + 'ip_version': self.new_address_scope.ip_version, + 'shared': False, + 'name': self.new_address_scope.name, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestDeleteAddressScope(TestAddressScope): + + # The address scope to delete. + _address_scope = ( + network_fakes.FakeAddressScope.create_one_address_scope()) + + def setUp(self): + super(TestDeleteAddressScope, self).setUp() + self.network.delete_address_scope = mock.Mock(return_value=None) + self.network.find_address_scope = mock.Mock( + return_value=self._address_scope) + + # Get the command object to test + self.cmd = address_scope.DeleteAddressScope(self.app, self.namespace) + + def test_delete(self): + arglist = [ + self._address_scope.name, + ] + verifylist = [ + ('address_scope', self._address_scope.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.network.delete_address_scope.assert_called_once_with( + self._address_scope) + self.assertIsNone(result) + + +class TestListAddressScope(TestAddressScope): + + # The address scopes to list up. + address_scopes = ( + network_fakes.FakeAddressScope.create_address_scopes(count=3)) + columns = ( + 'ID', + 'Name', + 'IP Version', + 'Shared', + 'Project', + ) + data = [] + for scope in address_scopes: + data.append(( + scope.id, + scope.name, + scope.ip_version, + scope.shared, + scope.project_id, + )) + + def setUp(self): + super(TestListAddressScope, self).setUp() + self.network.address_scopes = mock.Mock( + return_value=self.address_scopes) + + # Get the command object to test + self.cmd = address_scope.ListAddressScope(self.app, self.namespace) + + def test_address_scope_list(self): + arglist = [] + verifylist = [] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.network.address_scopes.assert_called_once_with(**{}) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + +class TestSetAddressScope(TestAddressScope): + + # The address scope to set. + _address_scope = network_fakes.FakeAddressScope.create_one_address_scope() + + def setUp(self): + super(TestSetAddressScope, self).setUp() + self.network.update_address_scope = mock.Mock(return_value=None) + self.network.find_address_scope = mock.Mock( + return_value=self._address_scope) + + # Get the command object to test + self.cmd = address_scope.SetAddressScope(self.app, self.namespace) + + def test_set_nothing(self): + arglist = [self._address_scope.name, ] + verifylist = [ + ('address_scope', self._address_scope.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises(exceptions.CommandError, self.cmd.take_action, + parsed_args) + + def test_set_name_and_share(self): + arglist = [ + '--name', 'new_address_scope', + '--share', + self._address_scope.name, + ] + verifylist = [ + ('name', 'new_address_scope'), + ('share', True), + ('address_scope', self._address_scope.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + attrs = { + 'name': "new_address_scope", + 'shared': True, + } + self.network.update_address_scope.assert_called_with( + self._address_scope, **attrs) + self.assertIsNone(result) + + def test_set_no_share(self): + arglist = [ + '--no-share', + self._address_scope.name, + ] + verifylist = [ + ('no_share', True), + ('address_scope', self._address_scope.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + attrs = { + 'shared': False, + } + self.network.update_address_scope.assert_called_with( + self._address_scope, **attrs) + self.assertIsNone(result) + + +class TestShowAddressScope(TestAddressScope): + + # The address scope to show. + _address_scope = ( + network_fakes.FakeAddressScope.create_one_address_scope()) + columns = ( + 'id', + 'ip_version', + 'name', + 'project_id', + 'shared', + ) + data = ( + _address_scope.id, + _address_scope.ip_version, + _address_scope.name, + _address_scope.project_id, + _address_scope.shared, + ) + + def setUp(self): + super(TestShowAddressScope, self).setUp() + self.network.find_address_scope = mock.Mock( + return_value=self._address_scope) + + # Get the command object to test + self.cmd = address_scope.ShowAddressScope(self.app, self.namespace) + + def test_show_no_options(self): + arglist = [] + verifylist = [] + + # Missing required args should bail here + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + def test_show_all_options(self): + arglist = [ + self._address_scope.name, + ] + verifylist = [ + ('address_scope', self._address_scope.name), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.network.find_address_scope.assert_called_once_with( + self._address_scope.name, ignore_missing=False) + self.assertEqual(self.columns, columns) + self.assertEqual(list(self.data), list(data)) |
