diff options
| author | Terry Howe <terrylhowe@gmail.com> | 2014-02-13 08:07:51 -0700 |
|---|---|---|
| committer | Terry Howe <terrylhowe@gmail.com> | 2014-06-24 15:00:25 -0600 |
| commit | 8f59524c3e1ff3f34474d0cfcd217bc9fe0cbfc1 (patch) | |
| tree | cbacaf79a662ea9d16231d5c40d7cc59f827abef | |
| parent | adf9349c40683eb53886365f6df65b8d8515e52c (diff) | |
| download | python-openstackclient-8f59524c3e1ff3f34474d0cfcd217bc9fe0cbfc1.tar.gz | |
Network CRUD
bp/neutron
https://wiki.openstack.org/wiki/OpenStackClient/Commands#Network_2
Change-Id: I89ee083154afa544b03587e84becace36d9d522a
| -rw-r--r-- | openstackclient/common/utils.py | 10 | ||||
| -rw-r--r-- | openstackclient/network/__init__.py | 0 | ||||
| -rw-r--r-- | openstackclient/network/client.py | 61 | ||||
| -rw-r--r-- | openstackclient/network/common.py | 43 | ||||
| -rw-r--r-- | openstackclient/network/v2_0/__init__.py | 0 | ||||
| -rw-r--r-- | openstackclient/network/v2_0/network.py | 236 | ||||
| -rw-r--r-- | openstackclient/tests/fakes.py | 1 | ||||
| -rw-r--r-- | openstackclient/tests/network/__init__.py | 0 | ||||
| -rw-r--r-- | openstackclient/tests/network/common.py | 52 | ||||
| -rw-r--r-- | openstackclient/tests/network/v2_0/__init__.py | 0 | ||||
| -rw-r--r-- | openstackclient/tests/network/v2_0/test_network.py | 319 | ||||
| -rw-r--r-- | openstackclient/tests/test_shell.py | 11 | ||||
| -rw-r--r-- | requirements.txt | 1 | ||||
| -rw-r--r-- | setup.cfg | 8 |
14 files changed, 740 insertions, 2 deletions
diff --git a/openstackclient/common/utils.py b/openstackclient/common/utils.py index a420dd51..0258f931 100644 --- a/openstackclient/common/utils.py +++ b/openstackclient/common/utils.py @@ -84,6 +84,16 @@ def format_dict(data): return output[:-2] +def format_list(data): + """Return a formatted strings + + :param data: a list of strings + :rtype: a string formatted to a,b,c + """ + + return ', '.join(data) + + def get_item_properties(item, fields, mixed_case_fields=[], formatters={}): """Return a tuple containing the item properties. diff --git a/openstackclient/network/__init__.py b/openstackclient/network/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/openstackclient/network/__init__.py diff --git a/openstackclient/network/client.py b/openstackclient/network/client.py new file mode 100644 index 00000000..3c87e135 --- /dev/null +++ b/openstackclient/network/client.py @@ -0,0 +1,61 @@ +# 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 logging + +from openstackclient.common import utils + + +LOG = logging.getLogger(__name__) + +DEFAULT_NETWORK_API_VERSION = '2.0' +API_VERSION_OPTION = 'os_network_api_version' +API_NAME = "network" +API_VERSIONS = { + "2.0": "neutronclient.v2_0.client.Client", +} + + +def make_client(instance): + """Returns an network service client.""" + network_client = utils.get_client_class( + API_NAME, + instance._api_version[API_NAME], + API_VERSIONS) + if not instance._url: + instance._url = instance.get_endpoint_for_service_type("network") + return network_client( + username=instance._username, + tenant_name=instance._project_name, + password=instance._password, + region_name=instance._region_name, + auth_url=instance._auth_url, + endpoint_url=instance._url, + token=instance._token, + insecure=instance._insecure, + ca_cert=instance._cacert, + ) + + +def build_option_parser(parser): + """Hook to add global options""" + parser.add_argument( + '--os-network-api-version', + metavar='<network-api-version>', + default=utils.env( + 'OS_NETWORK_API_VERSION', + default=DEFAULT_NETWORK_API_VERSION), + help='Network API version, default=' + + DEFAULT_NETWORK_API_VERSION + + ' (Env: OS_NETWORK_API_VERSION)') + return parser diff --git a/openstackclient/network/common.py b/openstackclient/network/common.py new file mode 100644 index 00000000..5ba44f7b --- /dev/null +++ b/openstackclient/network/common.py @@ -0,0 +1,43 @@ +# 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 openstackclient.common import exceptions + + +def find(client, resource, resources, name_or_id): + """Find a network resource + + :param client: network client + :param resource: name of the resource + :param resources: plural name of resource + :param name_or_id: name or id of resource user is looking for + + For example: + n = find(netclient, 'network', 'networks', 'matrix') + """ + list_method = getattr(client, "list_%s" % resources) + # Search for by name + data = list_method(name=name_or_id, fields='id') + info = data[resources] + if len(info) == 1: + return info[0]['id'] + if len(info) > 1: + msg = "More than one %s exists with the name '%s'." + raise exceptions.CommandError(msg % (resource, name_or_id)) + # Search for by id + data = list_method(id=name_or_id, fields='id') + info = data[resources] + if len(info) == 1: + return info[0]['id'] + msg = "No %s with a name or ID of '%s' exists." % (resource, name_or_id) + raise exceptions.CommandError(msg) diff --git a/openstackclient/network/v2_0/__init__.py b/openstackclient/network/v2_0/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/openstackclient/network/v2_0/__init__.py diff --git a/openstackclient/network/v2_0/network.py b/openstackclient/network/v2_0/network.py new file mode 100644 index 00000000..c0c25e71 --- /dev/null +++ b/openstackclient/network/v2_0/network.py @@ -0,0 +1,236 @@ +# 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. +# + +"""Network action implementations""" + +import logging +import six + +from cliff import command +from cliff import lister +from cliff import show + +from openstackclient.common import exceptions +from openstackclient.common import utils +from openstackclient.network import common + + +def filters(data): + if 'subnets' in data: + data['subnets'] = utils.format_list(data['subnets']) + return data + + +class CreateNetwork(show.ShowOne): + """Create a network""" + + log = logging.getLogger(__name__ + '.CreateNetwork') + + def get_parser(self, prog_name): + parser = super(CreateNetwork, self).get_parser(prog_name) + parser.add_argument( + 'name', metavar='<network_name>', + help='Name of network to create') + admin_group = parser.add_mutually_exclusive_group() + admin_group.add_argument( + '--admin-state-up', + dest='admin_state', action='store_true', + default=True, help='Set Admin State Up') + admin_group.add_argument( + '--admin-state-down', + dest='admin_state', action='store_false', + help='Set Admin State Down') + share_group = parser.add_mutually_exclusive_group() + share_group.add_argument( + '--share', + dest='shared', action='store_true', + default=None, + help='Share the network across tenants') + share_group.add_argument( + '--no-share', + dest='shared', action='store_false', + help='Do not share the network across tenants') + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + client = self.app.client_manager.network + body = self.get_body(parsed_args) + create_method = getattr(client, "create_network") + data = create_method(body)['network'] + if data: + data = filters(data) + else: + data = {'': ''} + return zip(*sorted(six.iteritems(data))) + + def get_body(self, parsed_args): + body = {'name': str(parsed_args.name), + 'admin_state_up': parsed_args.admin_state} + if parsed_args.shared is not None: + body['shared'] = parsed_args.shared + return {'network': body} + + +class DeleteNetwork(command.Command): + + log = logging.getLogger(__name__ + '.DeleteNetwork') + + def get_parser(self, prog_name): + parser = super(DeleteNetwork, self).get_parser(prog_name) + parser.add_argument( + 'identifier', + metavar="<network>", + help=("Name or identifier of network to delete") + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + client = self.app.client_manager.network + _id = common.find(client, 'network', 'networks', + parsed_args.identifier) + delete_method = getattr(client, "delete_network") + delete_method(_id) + return + + +class ListNetwork(lister.Lister): + """List networks""" + + log = logging.getLogger(__name__ + '.ListNetwork') + + def get_parser(self, prog_name): + parser = super(ListNetwork, self).get_parser(prog_name) + parser.add_argument( + '--external', + action='store_true', + default=False, + help='List external networks', + ) + parser.add_argument( + '--dhcp', + help='ID of the DHCP agent') + parser.add_argument( + '--long', + action='store_true', + default=False, + help='Long listing', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + client = self.app.client_manager.network + if parsed_args.dhcp: + list_method = getattr(client, 'list_networks_on_dhcp_agent') + resources = 'networks_on_dhcp_agent' + report_filter = {'dhcp_agent': parsed_args.dhcp} + data = list_method(**report_filter)[resources] + else: + list_method = getattr(client, "list_networks") + report_filter = {} + if parsed_args.external: + report_filter = {'router:external': True} + data = list_method(**report_filter)['networks'] + columns = len(data) > 0 and sorted(data[0].keys()) or [] + if parsed_args.columns: + list_columns = parsed_args.columns + else: + list_columns = ['id', 'name', 'subnets'] + if not parsed_args.long and not parsed_args.dhcp: + columns = [x for x in list_columns if x in columns] + formatters = {'subnets': utils.format_list} + return (columns, + (utils.get_dict_properties(s, columns, formatters=formatters) + for s in data)) + + +class SetNetwork(command.Command): + + log = logging.getLogger(__name__ + '.SetNetwork') + + def get_parser(self, prog_name): + parser = super(SetNetwork, self).get_parser(prog_name) + parser.add_argument( + 'identifier', + metavar="<network>", + help=("Name or identifier of network to set") + ) + admin_group = parser.add_mutually_exclusive_group() + admin_group.add_argument( + '--admin-state-up', + dest='admin_state', action='store_true', + default=None, + help='Set Admin State Up') + admin_group.add_argument( + '--admin-state-down', + dest='admin_state', action='store_false', + help='Set Admin State Down') + parser.add_argument( + '--name', + metavar='<network_name>', + help='New name for the network') + share_group = parser.add_mutually_exclusive_group() + share_group.add_argument( + '--share', + dest='shared', action='store_true', + default=None, + help='Share the network across tenants') + share_group.add_argument( + '--no-share', + dest='shared', action='store_false', + help='Do not share the network across tenants') + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + client = self.app.client_manager.network + _id = common.find(client, 'network', 'networks', + parsed_args.identifier) + body = {} + if parsed_args.name is not None: + body['name'] = str(parsed_args.name) + if parsed_args.admin_state is not None: + body['admin_state_up'] = parsed_args.admin_state + if parsed_args.shared is not None: + body['shared'] = parsed_args.shared + if body == {}: + raise exceptions.CommandError("Nothing specified to be set") + update_method = getattr(client, "update_network") + update_method(_id, {'network': body}) + return + + +class ShowNetwork(show.ShowOne): + + log = logging.getLogger(__name__ + '.ShowNetwork') + + def get_parser(self, prog_name): + parser = super(ShowNetwork, self).get_parser(prog_name) + parser.add_argument( + 'identifier', + metavar="<network>", + help=("Name or identifier of network to show") + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + client = self.app.client_manager.network + _id = common.find(client, 'network', 'networks', + parsed_args.identifier) + show_method = getattr(client, "show_network") + data = show_method(_id)['network'] + data = filters(data) + return zip(*sorted(six.iteritems(data))) diff --git a/openstackclient/tests/fakes.py b/openstackclient/tests/fakes.py index 4c50c0be..fb27ef94 100644 --- a/openstackclient/tests/fakes.py +++ b/openstackclient/tests/fakes.py @@ -52,6 +52,7 @@ class FakeClientManager(object): self.image = None self.object = None self.volume = None + self.network = None self.auth_ref = None diff --git a/openstackclient/tests/network/__init__.py b/openstackclient/tests/network/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/openstackclient/tests/network/__init__.py diff --git a/openstackclient/tests/network/common.py b/openstackclient/tests/network/common.py new file mode 100644 index 00000000..9c5a5ce9 --- /dev/null +++ b/openstackclient/tests/network/common.py @@ -0,0 +1,52 @@ +# 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 argparse +import mock + +from openstackclient.tests import utils + + +class TestNetworkBase(utils.TestCommand): + def setUp(self): + super(TestNetworkBase, self).setUp() + self.app = mock.Mock(name='app') + self.app.client_manager = mock.Mock(name='client_manager') + self.namespace = argparse.Namespace() + + given_show_options = [ + '-f', + 'shell', + '-c', + 'id', + '--prefix', + 'TST', + ] + then_show_options = [ + ('formatter', 'shell'), + ('columns', ['id']), + ('prefix', 'TST'), + ] + given_list_options = [ + '-f', + 'csv', + '-c', + 'id', + '--quote', + 'all', + ] + then_list_options = [ + ('formatter', 'csv'), + ('columns', ['id']), + ('quote_mode', 'all'), + ] diff --git a/openstackclient/tests/network/v2_0/__init__.py b/openstackclient/tests/network/v2_0/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/openstackclient/tests/network/v2_0/__init__.py diff --git a/openstackclient/tests/network/v2_0/test_network.py b/openstackclient/tests/network/v2_0/test_network.py new file mode 100644 index 00000000..ef7d24ee --- /dev/null +++ b/openstackclient/tests/network/v2_0/test_network.py @@ -0,0 +1,319 @@ +# 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_0 import network +from openstackclient.tests.network import common + +RESOURCE = 'network' +RESOURCES = 'networks' +FAKE_ID = 'iditty' +FAKE_NAME = 'noo' +RECORD = { + 'id': FAKE_ID, + 'name': FAKE_NAME, + 'router:external': True, + 'subnets': ['a', 'b'], +} +COLUMNS = ['id', 'name', 'subnets'] +RESPONSE = {RESOURCE: RECORD} +FILTERED = [('id', 'name', 'router:external', 'subnets'), + (FAKE_ID, FAKE_NAME, True, 'a, b')] + + +class TestCreateNetwork(common.TestNetworkBase): + def test_create_no_options(self): + arglist = [ + FAKE_NAME, + ] + verifylist = [ + ('name', FAKE_NAME), + ('admin_state', True), + ('shared', None), + ] + mocker = mock.Mock(return_value=copy.deepcopy(RESPONSE)) + self.app.client_manager.network.create_network = mocker + cmd = network.CreateNetwork(self.app, self.namespace) + + parsed_args = self.check_parser(cmd, arglist, verifylist) + result = cmd.take_action(parsed_args) + + mocker.assert_called_with({ + RESOURCE: { + 'admin_state_up': True, + 'name': FAKE_NAME, + } + }) + self.assertEqual(FILTERED, result) + + def test_create_all_options(self): + arglist = [ + "--admin-state-down", + "--share", + FAKE_NAME, + ] + self.given_show_options + verifylist = [ + ('admin_state', False), + ('shared', True), + ('name', FAKE_NAME), + ] + self.then_show_options + mocker = mock.Mock(return_value=copy.deepcopy(RESPONSE)) + self.app.client_manager.network.create_network = mocker + cmd = network.CreateNetwork(self.app, self.namespace) + + parsed_args = self.check_parser(cmd, arglist, verifylist) + result = cmd.take_action(parsed_args) + + mocker.assert_called_with({ + RESOURCE: { + 'admin_state_up': False, + 'name': FAKE_NAME, + 'shared': True, + } + }) + self.assertEqual(FILTERED, result) + + def test_create_other_options(self): + arglist = [ + "--admin-state-up", + "--no-share", + FAKE_NAME, + ] + verifylist = [ + ('admin_state', True), + ('shared', False), + ('name', FAKE_NAME), + ] + mocker = mock.Mock(return_value=copy.deepcopy(RESPONSE)) + self.app.client_manager.network.create_network = mocker + cmd = network.CreateNetwork(self.app, self.namespace) + + parsed_args = self.check_parser(cmd, arglist, verifylist) + result = cmd.take_action(parsed_args) + + mocker.assert_called_with({ + RESOURCE: { + 'admin_state_up': True, + 'name': FAKE_NAME, + 'shared': False, + } + }) + self.assertEqual(FILTERED, result) + + +class TestDeleteNetwork(common.TestNetworkBase): + def test_delete(self): + arglist = [ + FAKE_NAME, + ] + verifylist = [ + ('identifier', FAKE_NAME), + ] + lister = mock.Mock(return_value={RESOURCES: [RECORD]}) + self.app.client_manager.network.list_networks = lister + mocker = mock.Mock(return_value=None) + self.app.client_manager.network.delete_network = mocker + cmd = network.DeleteNetwork(self.app, self.namespace) + + parsed_args = self.check_parser(cmd, arglist, verifylist) + result = cmd.take_action(parsed_args) + + mocker.assert_called_with(FAKE_ID) + self.assertEqual(None, result) + + +class TestListNetwork(common.TestNetworkBase): + def test_list_no_options(self): + arglist = [] + verifylist = [ + ('long', False), + ('dhcp', None), + ('external', False), + ] + lister = mock.Mock(return_value={RESOURCES: [RECORD]}) + self.app.client_manager.network.list_networks = lister + cmd = network.ListNetwork(self.app, self.namespace) + + parsed_args = self.check_parser(cmd, arglist, verifylist) + result = cmd.take_action(parsed_args) + + lister.assert_called_with() + self.assertEqual(COLUMNS, result[0]) + self.assertEqual((FAKE_ID, FAKE_NAME, 'a, b'), next(result[1])) + self.assertRaises(StopIteration, next, result[1]) + + def test_list_long(self): + arglist = ['--long'] + verifylist = [ + ('long', True), + ('dhcp', None), + ('external', False), + ] + lister = mock.Mock(return_value={RESOURCES: [RECORD]}) + self.app.client_manager.network.list_networks = lister + cmd = network.ListNetwork(self.app, self.namespace) + + parsed_args = self.check_parser(cmd, arglist, verifylist) + result = cmd.take_action(parsed_args) + + lister.assert_called_with() + headings = ['id', 'name', 'router:external', 'subnets'] + self.assertEqual(headings, result[0]) + data = (FAKE_ID, FAKE_NAME, True, 'a, b') + self.assertEqual(data, next(result[1])) + self.assertRaises(StopIteration, next, result[1]) + + def test_list_dhcp(self): + arglist = [ + '--dhcp', + 'dhcpid', + ] + self.given_list_options + verifylist = [ + ('dhcp', 'dhcpid'), + ] + self.then_list_options + fake_dhcp_data = [{'id': '1'}, {'id': '2'}] + fake_dhcp_response = {'networks_on_dhcp_agent': fake_dhcp_data} + lister = mock.Mock(return_value=fake_dhcp_response) + netty = self.app.client_manager.network + netty.list_networks_on_dhcp_agent = lister + cmd = network.ListNetwork(self.app, self.namespace) + + parsed_args = self.check_parser(cmd, arglist, verifylist) + result = cmd.take_action(parsed_args) + + lister.assert_called_with(dhcp_agent='dhcpid') + self.assertEqual(['id'], result[0]) + self.assertEqual(('1',), next(result[1])) + self.assertEqual(('2',), next(result[1])) + self.assertRaises(StopIteration, next, result[1]) + + def test_list_external(self): + arglist = ['--external', '-c', 'id'] + verifylist = [('external', True)] + lister = mock.Mock(return_value={RESOURCES: [RECORD]}) + self.app.client_manager.network.list_networks = lister + cmd = network.ListNetwork(self.app, self.namespace) + + parsed_args = self.check_parser(cmd, arglist, verifylist) + result = cmd.take_action(parsed_args) + + lister.assert_called_with(**{'router:external': True}) + self.assertEqual(['id'], result[0]) + self.assertEqual((FAKE_ID,), next(result[1])) + self.assertRaises(StopIteration, next, result[1]) + + +class TestSetNetwork(common.TestNetworkBase): + def test_set_this(self): + arglist = [ + FAKE_NAME, + '--admin-state-up', + '--name', 'noob', + '--share', + ] + verifylist = [ + ('identifier', FAKE_NAME), + ('admin_state', True), + ('name', 'noob'), + ('shared', True), + ] + lister = mock.Mock(return_value={RESOURCES: [RECORD]}) + self.app.client_manager.network.list_networks = lister + mocker = mock.Mock(return_value=None) + self.app.client_manager.network.update_network = mocker + cmd = network.SetNetwork(self.app, self.namespace) + + parsed_args = self.check_parser(cmd, arglist, verifylist) + result = cmd.take_action(parsed_args) + + exp = {'admin_state_up': True, 'name': 'noob', 'shared': True} + exp_record = {RESOURCE: exp} + mocker.assert_called_with(FAKE_ID, exp_record) + self.assertEqual(None, result) + + def test_set_that(self): + arglist = [ + FAKE_NAME, + '--admin-state-down', + '--no-share', + ] + verifylist = [ + ('identifier', FAKE_NAME), + ('admin_state', False), + ('shared', False), + ] + lister = mock.Mock(return_value={RESOURCES: [RECORD]}) + self.app.client_manager.network.list_networks = lister + mocker = mock.Mock(return_value=None) + self.app.client_manager.network.update_network = mocker + cmd = network.SetNetwork(self.app, self.namespace) + + parsed_args = self.check_parser(cmd, arglist, verifylist) + result = cmd.take_action(parsed_args) + + exp = {'admin_state_up': False, 'shared': False} + exp_record = {RESOURCE: exp} + mocker.assert_called_with(FAKE_ID, exp_record) + self.assertEqual(None, result) + + def test_set_nothing(self): + arglist = [FAKE_NAME, ] + verifylist = [('identifier', FAKE_NAME), ] + lister = mock.Mock(return_value={RESOURCES: [RECORD]}) + self.app.client_manager.network.list_networks = lister + mocker = mock.Mock(return_value=None) + self.app.client_manager.network.update_network = mocker + cmd = network.SetNetwork(self.app, self.namespace) + + parsed_args = self.check_parser(cmd, arglist, verifylist) + self.assertRaises(exceptions.CommandError, cmd.take_action, + parsed_args) + + +class TestShowNetwork(common.TestNetworkBase): + def test_show_no_options(self): + arglist = [ + FAKE_NAME, + ] + verifylist = [ + ('identifier', FAKE_NAME), + ] + lister = mock.Mock(return_value={RESOURCES: [RECORD]}) + self.app.client_manager.network.list_networks = lister + mocker = mock.Mock(return_value=copy.deepcopy(RESPONSE)) + self.app.client_manager.network.show_network = mocker + cmd = network.ShowNetwork(self.app, self.namespace) + + parsed_args = self.check_parser(cmd, arglist, verifylist) + result = cmd.take_action(parsed_args) + + mocker.assert_called_with(FAKE_ID) + self.assertEqual(FILTERED, result) + + def test_show_all_options(self): + arglist = [FAKE_NAME] + self.given_show_options + verifylist = [('identifier', FAKE_NAME)] + self.then_show_options + lister = mock.Mock(return_value={RESOURCES: [RECORD]}) + self.app.client_manager.network.list_networks = lister + mocker = mock.Mock(return_value=copy.deepcopy(RESPONSE)) + self.app.client_manager.network.show_network = mocker + cmd = network.ShowNetwork(self.app, self.namespace) + + parsed_args = self.check_parser(cmd, arglist, verifylist) + result = cmd.take_action(parsed_args) + + mocker.assert_called_with(FAKE_ID) + self.assertEqual(FILTERED, result) diff --git a/openstackclient/tests/test_shell.py b/openstackclient/tests/test_shell.py index 9253f701..2ee8503a 100644 --- a/openstackclient/tests/test_shell.py +++ b/openstackclient/tests/test_shell.py @@ -39,11 +39,13 @@ DEFAULT_COMPUTE_API_VERSION = "2" DEFAULT_IDENTITY_API_VERSION = "2.0" DEFAULT_IMAGE_API_VERSION = "v2" DEFAULT_VOLUME_API_VERSION = "1" +DEFAULT_NETWORK_API_VERSION = "2.0" LIB_COMPUTE_API_VERSION = "2" LIB_IDENTITY_API_VERSION = "2.0" LIB_IMAGE_API_VERSION = "1" LIB_VOLUME_API_VERSION = "1" +LIB_NETWORK_API_VERSION = "2.0" def make_shell(): @@ -128,6 +130,8 @@ class TestShell(utils.TestCase): default_args["image_api_version"]) self.assertEqual(_shell.options.os_volume_api_version, default_args["volume_api_version"]) + self.assertEqual(_shell.options.os_network_api_version, + default_args["network_api_version"]) class TestShellHelp(TestShell): @@ -455,6 +459,7 @@ class TestShellCli(TestShell): "OS_IDENTITY_API_VERSION": DEFAULT_IDENTITY_API_VERSION, "OS_IMAGE_API_VERSION": DEFAULT_IMAGE_API_VERSION, "OS_VOLUME_API_VERSION": DEFAULT_VOLUME_API_VERSION, + "OS_NETWORK_API_VERSION": DEFAULT_NETWORK_API_VERSION, } self.orig_env, os.environ = os.environ, env.copy() @@ -475,7 +480,8 @@ class TestShellCli(TestShell): "compute_api_version": DEFAULT_COMPUTE_API_VERSION, "identity_api_version": DEFAULT_IDENTITY_API_VERSION, "image_api_version": DEFAULT_IMAGE_API_VERSION, - "volume_api_version": DEFAULT_VOLUME_API_VERSION + "volume_api_version": DEFAULT_VOLUME_API_VERSION, + "network_api_version": DEFAULT_NETWORK_API_VERSION, } self._assert_cli(flag, kwargs) @@ -486,6 +492,7 @@ class TestShellCli(TestShell): "compute_api_version": LIB_COMPUTE_API_VERSION, "identity_api_version": LIB_IDENTITY_API_VERSION, "image_api_version": LIB_IMAGE_API_VERSION, - "volume_api_version": LIB_VOLUME_API_VERSION + "volume_api_version": LIB_VOLUME_API_VERSION, + "network_api_version": LIB_NETWORK_API_VERSION } self._assert_cli(flag, kwargs) diff --git a/requirements.txt b/requirements.txt index e0f47677..8ab9089f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,5 +6,6 @@ python-glanceclient>=0.9.0 python-keystoneclient>=0.9.0 python-novaclient>=2.17.0 python-cinderclient>=1.0.6 +python-neutronclient>=2.3.4,<3 requests>=1.1 six>=1.7.0 @@ -33,6 +33,7 @@ openstack.cli.extension = image = openstackclient.image.client object_store = openstackclient.object.client volume = openstackclient.volume.client + network = openstackclient.network.client openstack.common = limits_show = openstackclient.common.limits:ShowLimits @@ -289,6 +290,13 @@ openstack.volume.v1 = volume_type_set = openstackclient.volume.v1.type:SetVolumeType volume_type_unset = openstackclient.volume.v1.type:UnsetVolumeType +openstack.network.v2_0 = + network_create = openstackclient.network.v2_0.network:CreateNetwork + network_delete = openstackclient.network.v2_0.network:DeleteNetwork + network_list = openstackclient.network.v2_0.network:ListNetwork + network_set = openstackclient.network.v2_0.network:SetNetwork + network_show = openstackclient.network.v2_0.network:ShowNetwork + [build_sphinx] source-dir = doc/source build-dir = doc/build |
