diff --git a/openstack/python-openstackclient/centos/build_srpm.data b/openstack/python-openstackclient/centos/build_srpm.data index e1a5865a..49716585 100644 --- a/openstack/python-openstackclient/centos/build_srpm.data +++ b/openstack/python-openstackclient/centos/build_srpm.data @@ -1,4 +1,3 @@ TAR_NAME=python-openstackclient SRC_DIR="$CGCS_BASE/git/python-openstackclient" -COPY_LIST="$DISTRO/patches/*" -TIS_PATCH_VER=1 +TIS_PATCH_VER=2 diff --git a/openstack/python-openstackclient/centos/patches/0001-Add-network-segment-range-command-object.patch b/openstack/python-openstackclient/centos/patches/0001-Add-network-segment-range-command-object.patch deleted file mode 100644 index 61e7ec45..00000000 --- a/openstack/python-openstackclient/centos/patches/0001-Add-network-segment-range-command-object.patch +++ /dev/null @@ -1,1488 +0,0 @@ -From c61224f6227585819c313072133bb5a15ca8957d Mon Sep 17 00:00:00 2001 -From: Kailun Qin -Date: Tue, 25 Dec 2018 07:20:59 +0800 -Subject: [PATCH] Add network segment range command object - -Add network segment range command object in support of network segment -range management. - -This patch set includes documentation, unit tests and functional tests -(currently skipped unit network segment range enabled in Neutron by -default) for the following new commands: - - "os network segment range create" - - "os network segment range delete" - - "os network segment range list" - - "os network segment range set" - - "os network segment range show" - -Co-authored-by: Allain Legacy - -Partially-implements: blueprint network-segment-range-management -Change-Id: I335692f2db5be07c1c164f09b13f1abb80b7ba33 -Depends-on: https://review.openstack.org/624708 -Depends-on: https://review.openstack.org/624709 ---- - -diff --git a/doc/source/cli/command-objects/network_segment_range.rst b/doc/source/cli/command-objects/network_segment_range.rst -new file mode 100644 -index 0000000..31fabbf ---- /dev/null -+++ b/doc/source/cli/command-objects/network_segment_range.rst -@@ -0,0 +1,168 @@ -+===================== -+network segment range -+===================== -+ -+A **network segment range** is a resource for tenant network segment -+allocation. -+A network segment range exposes the segment range management to be administered -+via the Neutron API. In addition, it introduces the ability for the -+administrator to control the segment ranges globally or on a per-tenant basis. -+ -+Network v2 -+ -+network segment range create -+---------------------------- -+ -+Create new network segment range -+ -+.. program:: network segment range create -+.. code:: bash -+ -+ openstack network segment range create -+ (--private | --shared) -+ [--project [--project-domain ]] -+ --network-type -+ [--physical-network ] -+ --minimum -+ --maximum -+ -+ -+.. option:: --private -+ -+ Network segment range is assigned specifically to the project -+ -+.. option:: --shared -+ -+ Network segment range is shared with other projects -+ -+.. option:: --project -+ -+ Network segment range owner (name or ID). Optional when the segment -+ range is shared -+ -+.. option:: --project-domain -+ -+ Domain the project belongs to (name or ID). -+ This can be used in case collisions between project names exist. -+ -+.. option:: --physical-network -+ -+ Physical network name of this network segment range -+ -+.. option:: --network-type -+ -+ Network type of this network segment range -+ (geneve, gre, vlan or vxlan) -+ -+.. option:: --minimum -+ -+ Minimum segment identifier for this network segment range which is based -+ on the network type, VLAN ID for vlan network type and tunnel ID for -+ geneve, gre and vxlan network types -+ -+.. option:: --maximum -+ -+ Maximum segment identifier for this network segment range which is based -+ on the network type, VLAN ID for vlan network type and tunnel ID for -+ geneve, gre and vxlan network types -+ -+.. _network_segment_range_create-name: -+.. describe:: -+ -+ Name of new network segment range -+ -+network segment range delete -+---------------------------- -+ -+Delete network segment range(s) -+ -+.. program:: network segment range delete -+.. code:: bash -+ -+ openstack network segment range delete -+ [ ...] -+ -+.. _network_segment_range_delete-network-segment-range: -+.. describe:: -+ -+ Network segment range (s) to delete (name or ID) -+ -+network segment range list -+-------------------------- -+ -+List network segment ranges -+ -+.. program:: network segment range list -+.. code:: bash -+ -+ openstack network segment range list -+ [--long] -+ [--used | --unused] -+ [--available | --unavailable] -+ -+.. option:: --long -+ -+ List additional fields in output -+ -+.. option:: --used -+ -+ List network segment ranges that have segments not in use -+ -+.. option:: --unused -+ -+ List network segment ranges that do not have segments not in use -+ -+.. option:: --available -+ -+ List network segment ranges that have available segments -+ -+.. option:: --unavailable -+ -+ List network segment ranges without available segments -+ -+network segment range set -+------------------------- -+ -+Set network segment range properties -+ -+.. program:: network segment range set -+.. code:: bash -+ -+ openstack network segment range set -+ [--name ] -+ [--minimum ] -+ [--maximum ] -+ -+ -+.. option:: --name -+ -+ Set network segment range name -+ -+.. option:: --minimum -+ -+ Set network segment range minimum segment identifier -+ -+.. option:: --maximum -+ -+ Set network segment range maximum segment identifier -+ -+.. _network_segment_range_set-network-segment-range: -+.. describe:: -+ -+ Network segment range to modify (name or ID) -+ -+network segment range show -+-------------------------- -+ -+Display network segment range details -+ -+.. program:: network segment range show -+.. code:: bash -+ -+ openstack network segment range show -+ -+ -+.. _network_segment_range_show-network-segment-range: -+.. describe:: -+ -+ Network segment range to display (name or ID) -diff --git a/doc/source/cli/commands.rst b/doc/source/cli/commands.rst -index cdd5e63..e302fda 100644 ---- a/doc/source/cli/commands.rst -+++ b/doc/source/cli/commands.rst -@@ -125,6 +125,7 @@ - * ``network qos policy``: (**Network**) - a QoS policy for network resources - * ``network qos rule type``: (**Network**) - list of QoS available rule types - * ``network segment``: (**Network**) - a segment of a virtual network -+* ``network segment range``: (**Network**) - a segment range for tenant network segment allocation - * ``network service provider``: (**Network**) - a driver providing a network service - * ``object``: (**Object Storage**) a single file in the Object Storage - * ``object store account``: (**Object Storage**) owns a group of Object Storage resources -diff --git a/openstackclient/network/v2/network_segment_range.py b/openstackclient/network/v2/network_segment_range.py -new file mode 100644 -index 0000000..9e5e2e9 ---- /dev/null -+++ b/openstackclient/network/v2/network_segment_range.py -@@ -0,0 +1,450 @@ -+# Copyright (c) 2018, Intel 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. -+# -+ -+"""Network segment action implementations""" -+ -+import itertools -+import logging -+ -+from osc_lib.command import command -+from osc_lib import exceptions -+from osc_lib import utils -+import six -+ -+from openstackclient.i18n import _ -+from openstackclient.identity import common as identity_common -+from openstackclient.network import sdk_utils -+ -+ -+LOG = logging.getLogger(__name__) -+ -+ -+def _get_columns(item): -+ return sdk_utils.get_osc_show_columns_for_sdk_resource(item, {}) -+ -+ -+def _get_ranges(item): -+ item = [int(i) if isinstance(i, six.string_types) else i for i in item] -+ for a, b in itertools.groupby(enumerate(item), lambda xy: xy[1] - xy[0]): -+ b = list(b) -+ yield "%s-%s" % (b[0][1], b[-1][1]) if b[0][1] != b[-1][1] else \ -+ str(b[0][1]) -+ -+ -+def _hack_tuple_value_update_by_index(tup, index, value): -+ lot = list(tup) -+ lot[index] = value -+ return tuple(lot) -+ -+ -+def _is_prop_empty(columns, props, prop_name): -+ return True if not props[columns.index(prop_name)] else False -+ -+ -+def _exchange_dict_keys_with_values(orig_dict): -+ updated_dict = dict() -+ for k, v in six.iteritems(orig_dict): -+ k = [k] -+ if not updated_dict.get(v): -+ updated_dict[v] = k -+ else: -+ updated_dict[v].extend(k) -+ return updated_dict -+ -+ -+def _update_available_from_props(columns, props): -+ index_available = columns.index('available') -+ props = _hack_tuple_value_update_by_index( -+ props, index_available, list(_get_ranges(props[index_available]))) -+ return props -+ -+ -+def _update_used_from_props(columns, props): -+ index_used = columns.index('used') -+ updated_used = _exchange_dict_keys_with_values(props[index_used]) -+ for k, v in six.iteritems(updated_used): -+ updated_used[k] = list(_get_ranges(v)) -+ props = _hack_tuple_value_update_by_index( -+ props, index_used, updated_used) -+ return props -+ -+ -+def _update_additional_fields_from_props(columns, props): -+ props = _update_available_from_props(columns, props) -+ props = _update_used_from_props(columns, props) -+ return props -+ -+ -+class CreateNetworkSegmentRange(command.ShowOne): -+ _description = _("Create new network segment range") -+ -+ def get_parser(self, prog_name): -+ parser = super(CreateNetworkSegmentRange, self).get_parser(prog_name) -+ shared_group = parser.add_mutually_exclusive_group(required=True) -+ shared_group.add_argument( -+ "--private", -+ dest="private", -+ action="store_true", -+ help=_('Network segment range is assigned specifically to the ' -+ 'project'), -+ ) -+ shared_group.add_argument( -+ "--shared", -+ dest="shared", -+ action="store_true", -+ help=_('Network segment range is shared with other projects'), -+ ) -+ parser.add_argument( -+ 'name', -+ metavar='', -+ help=_('Name of new network segment range') -+ ) -+ parser.add_argument( -+ '--project', -+ metavar='', -+ help=_('Network segment range owner (name or ID). Optional when ' -+ 'the segment range is shared'), -+ ) -+ identity_common.add_project_domain_option_to_parser(parser) -+ parser.add_argument( -+ '--network-type', -+ metavar='', -+ choices=['geneve', 'gre', 'vlan', 'vxlan'], -+ required=True, -+ help=_('Network type of this network segment range ' -+ '(geneve, gre, vlan or vxlan)'), -+ ) -+ parser.add_argument( -+ '--physical-network', -+ metavar='', -+ help=_('Physical network name of this network segment range'), -+ ) -+ parser.add_argument( -+ '--minimum', -+ metavar='', -+ type=int, -+ required=True, -+ help=_('Minimum segment identifier for this network segment ' -+ 'range which is based on the network type, VLAN ID for ' -+ 'vlan network type and tunnel ID for geneve, gre and vxlan ' -+ 'network types'), -+ ) -+ parser.add_argument( -+ '--maximum', -+ metavar='', -+ type=int, -+ required=True, -+ help=_('Maximum segment identifier for this network segment ' -+ 'range which is based on the network type, VLAN ID for ' -+ 'vlan network type and tunnel ID for geneve, gre and vxlan ' -+ 'network types'), -+ ) -+ -+ return parser -+ -+ def take_action(self, parsed_args): -+ network_client = self.app.client_manager.network -+ try: -+ # Verify that the extension exists. -+ network_client.find_extension('network-segment-range', -+ ignore_missing=False) -+ except Exception as e: -+ msg = (_('Network segment range create not supported by ' -+ 'Network API: %(e)s') % {'e': e}) -+ raise exceptions.CommandError(msg) -+ -+ identity_client = self.app.client_manager.identity -+ -+ if parsed_args.shared and parsed_args.project: -+ msg = _("--project is only allowed with --private") -+ raise exceptions.CommandError(msg) -+ -+ if parsed_args.private and not parsed_args.project: -+ msg = _("--project is required with --private") -+ raise exceptions.CommandError(msg) -+ -+ if (parsed_args.network_type.lower() != 'vlan' and -+ parsed_args.physical_network): -+ msg = _("--physical-network is only allowed with --network-type " -+ "vlan") -+ raise exceptions.CommandError(msg) -+ -+ attrs = {} -+ attrs['shared'] = parsed_args.shared -+ attrs['network_type'] = parsed_args.network_type -+ attrs['minimum'] = parsed_args.minimum -+ attrs['maximum'] = parsed_args.maximum -+ if parsed_args.name: -+ attrs['name'] = parsed_args.name -+ -+ if parsed_args.project: -+ project_id = identity_common.find_project( -+ identity_client, -+ parsed_args.project, -+ parsed_args.project_domain, -+ ).id -+ if project_id: -+ attrs['project_id'] = project_id -+ else: -+ msg = (_("Failed to create the network segment range for " -+ "project %(project_id)s") % parsed_args.project_id) -+ raise exceptions.CommandError(msg) -+ if parsed_args.physical_network: -+ attrs['physical_network'] = parsed_args.physical_network -+ obj = network_client.create_network_segment_range(**attrs) -+ display_columns, columns = _get_columns(obj) -+ data = utils.get_item_properties(obj, columns) -+ data = _update_additional_fields_from_props(columns, props=data) -+ return (display_columns, data) -+ -+ -+class DeleteNetworkSegmentRange(command.Command): -+ _description = _("Delete network segment range(s)") -+ -+ def get_parser(self, prog_name): -+ parser = super(DeleteNetworkSegmentRange, self).get_parser(prog_name) -+ parser.add_argument( -+ 'network_segment_range', -+ metavar='', -+ nargs='+', -+ help=_('Network segment range(s) to delete (name or ID)'), -+ ) -+ return parser -+ -+ def take_action(self, parsed_args): -+ network_client = self.app.client_manager.network -+ try: -+ # Verify that the extension exists. -+ network_client.find_extension('network-segment-range', -+ ignore_missing=False) -+ except Exception as e: -+ msg = (_('Network segment range delete not supported by ' -+ 'Network API: %(e)s') % {'e': e}) -+ raise exceptions.CommandError(msg) -+ -+ result = 0 -+ for network_segment_range in parsed_args.network_segment_range: -+ try: -+ obj = network_client.find_network_segment_range( -+ network_segment_range, ignore_missing=False) -+ network_client.delete_network_segment_range(obj) -+ except Exception as e: -+ result += 1 -+ LOG.error(_("Failed to delete network segment range with " -+ "ID '%(network_segment_range)s': %(e)s"), -+ {'network_segment_range': network_segment_range, -+ 'e': e}) -+ -+ if result > 0: -+ total = len(parsed_args.network_segment_range) -+ msg = (_("%(result)s of %(total)s network segment ranges failed " -+ "to delete.") % {'result': result, 'total': total}) -+ raise exceptions.CommandError(msg) -+ -+ -+class ListNetworkSegmentRange(command.Lister): -+ _description = _("List network segment ranges") -+ -+ def get_parser(self, prog_name): -+ parser = super(ListNetworkSegmentRange, self).get_parser(prog_name) -+ parser.add_argument( -+ '--long', -+ action='store_true', -+ help=_('List additional fields in output'), -+ ) -+ used_group = parser.add_mutually_exclusive_group() -+ used_group.add_argument( -+ '--used', -+ action='store_true', -+ help=_('List network segment ranges that have segments in use'), -+ ) -+ used_group.add_argument( -+ '--unused', -+ action='store_true', -+ help=_('List network segment ranges that have segments ' -+ 'not in use'), -+ ) -+ available_group = parser.add_mutually_exclusive_group() -+ available_group.add_argument( -+ '--available', -+ action='store_true', -+ help=_('List network segment ranges that have available segments'), -+ ) -+ available_group.add_argument( -+ '--unavailable', -+ action='store_true', -+ help=_('List network segment ranges without available segments'), -+ ) -+ return parser -+ -+ def take_action(self, parsed_args): -+ network_client = self.app.client_manager.network -+ try: -+ # Verify that the extension exists. -+ network_client.find_extension('network-segment-range', -+ ignore_missing=False) -+ except Exception as e: -+ msg = (_('Network segment ranges list not supported by ' -+ 'Network API: %(e)s') % {'e': e}) -+ raise exceptions.CommandError(msg) -+ -+ filters = {} -+ data = network_client.network_segment_ranges(**filters) -+ -+ headers = ( -+ 'ID', -+ 'Name', -+ 'Default', -+ 'Shared', -+ 'Project ID', -+ 'Network Type', -+ 'Physical Network', -+ 'Minimum ID', -+ 'Maximum ID' -+ ) -+ columns = ( -+ 'id', -+ 'name', -+ 'default', -+ 'shared', -+ 'project_id', -+ 'network_type', -+ 'physical_network', -+ 'minimum', -+ 'maximum', -+ ) -+ if parsed_args.available or parsed_args.unavailable or \ -+ parsed_args.used or parsed_args.unused: -+ # If one of `--available`, `--unavailable`, `--used`, -+ # `--unused` is specified, we assume that additional fields -+ # should be listed in output. -+ parsed_args.long = True -+ if parsed_args.long: -+ headers = headers + ( -+ 'Used', -+ 'Available', -+ ) -+ columns = columns + ( -+ 'used', -+ 'available', -+ ) -+ -+ display_props = tuple() -+ for s in data: -+ props = utils.get_item_properties(s, columns) -+ if parsed_args.available and \ -+ _is_prop_empty(columns, props, 'available') or \ -+ parsed_args.unavailable and \ -+ not _is_prop_empty(columns, props, 'available') or \ -+ parsed_args.used and _is_prop_empty(columns, props, 'used') or \ -+ parsed_args.unused and \ -+ not _is_prop_empty(columns, props, 'used'): -+ continue -+ if parsed_args.long: -+ props = _update_additional_fields_from_props(columns, props) -+ display_props += (props,) -+ -+ return headers, display_props -+ -+ -+class SetNetworkSegmentRange(command.Command): -+ _description = _("Set network segment range properties") -+ -+ def get_parser(self, prog_name): -+ parser = super(SetNetworkSegmentRange, self).get_parser(prog_name) -+ parser.add_argument( -+ 'network_segment_range', -+ metavar='', -+ help=_('Network segment range to modify (name or ID)'), -+ ) -+ parser.add_argument( -+ '--name', -+ metavar='', -+ help=_('Set network segment name'), -+ ) -+ parser.add_argument( -+ '--minimum', -+ metavar='', -+ type=int, -+ help=_('Set network segment range minimum segment identifier'), -+ ) -+ parser.add_argument( -+ '--maximum', -+ metavar='', -+ type=int, -+ help=_('Set network segment range maximum segment identifier'), -+ ) -+ return parser -+ -+ def take_action(self, parsed_args): -+ network_client = self.app.client_manager.network -+ try: -+ # Verify that the extension exists. -+ network_client.find_extension('network-segment-range', -+ ignore_missing=False) -+ except Exception as e: -+ msg = (_('Network segment range set not supported by ' -+ 'Network API: %(e)s') % {'e': e}) -+ raise exceptions.CommandError(msg) -+ -+ if (parsed_args.minimum and not parsed_args.maximum) or \ -+ (parsed_args.maximum and not parsed_args.minimum): -+ msg = _("--minimum and --maximum are both required") -+ raise exceptions.CommandError(msg) -+ -+ obj = network_client.find_network_segment_range( -+ parsed_args.network_segment_range, ignore_missing=False) -+ attrs = {} -+ if parsed_args.name: -+ attrs['name'] = parsed_args.name -+ if parsed_args.minimum: -+ attrs['minimum'] = parsed_args.minimum -+ if parsed_args.maximum: -+ attrs['maximum'] = parsed_args.maximum -+ network_client.update_network_segment_range(obj, **attrs) -+ -+ -+class ShowNetworkSegmentRange(command.ShowOne): -+ _description = _("Display network segment range details") -+ -+ def get_parser(self, prog_name): -+ parser = super(ShowNetworkSegmentRange, self).get_parser(prog_name) -+ parser.add_argument( -+ 'network_segment_range', -+ metavar='', -+ help=_('Network segment range to display (name or ID)'), -+ ) -+ return parser -+ -+ def take_action(self, parsed_args): -+ network_client = self.app.client_manager.network -+ try: -+ # Verify that the extension exists. -+ network_client.find_extension('network-segment-range', -+ ignore_missing=False) -+ except Exception as e: -+ msg = (_('Network segment range show not supported by ' -+ 'Network API: %(e)s') % {'e': e}) -+ raise exceptions.CommandError(msg) -+ -+ obj = network_client.find_network_segment_range( -+ parsed_args.network_segment_range, -+ ignore_missing=False -+ ) -+ display_columns, columns = _get_columns(obj) -+ data = utils.get_item_properties(obj, columns) -+ data = _update_additional_fields_from_props(columns, props=data) -+ return (display_columns, data) -diff --git a/openstackclient/tests/functional/network/v2/test_network_segment_range.py b/openstackclient/tests/functional/network/v2/test_network_segment_range.py -new file mode 100644 -index 0000000..435cbe1 ---- /dev/null -+++ b/openstackclient/tests/functional/network/v2/test_network_segment_range.py -@@ -0,0 +1,152 @@ -+# Copyright (c) 2018, Intel 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. -+# -+ -+import json -+import uuid -+ -+import testtools -+ -+from openstackclient.tests.functional.network.v2 import common -+ -+ -+# NOTE(kailun): Network segment range management is still a WIP and not -+# enabled by default. -+@testtools.skip("bp/network-segment-range-management") -+class NetworkSegmentRangeTests(common.NetworkTests): -+ """Functional tests for network segment range""" -+ -+ PROJECT_NAME = uuid.uuid4().hex -+ -+ @classmethod -+ def setUpClass(cls): -+ super(NetworkSegmentRangeTests, cls).setUpClass() -+ # Make a project -+ cmd_output = json.loads(cls.openstack( -+ "project create -f json --enable " + cls.PROJECT_NAME -+ )) -+ cls.project_id = cmd_output["id"] -+ -+ @classmethod -+ def tearDownClass(cls): -+ try: -+ raw_output = cls.openstack("project delete " + cls.PROJECT_NAME) -+ cls.assertOutput('', raw_output) -+ finally: -+ super(NetworkSegmentRangeTests, cls).tearDownClass() -+ -+ def setUp(self): -+ super(NetworkSegmentRangeTests, self).setUp() -+ # Nothing in this class works with Nova Network -+ if not self.haz_network: -+ self.skipTest("No Network service present") -+ -+ def test_network_segment_range_create_delete(self): -+ name = uuid.uuid4().hex -+ json_output = json.loads(self.openstack( -+ ' network segment range create -f json ' + -+ '--private ' + -+ "--project " + self.PROJECT_NAME + " " + -+ '--network-type vxlan ' + -+ '--minimum 2018 ' + -+ '--maximum 2055 ' + -+ name -+ )) -+ self.assertEqual( -+ name, -+ json_output["name"], -+ ) -+ self.assertEqual( -+ self.project_id, -+ json_output["project_id"], -+ ) -+ -+ raw_output = self.openstack( -+ 'network segment range delete ' + name, -+ ) -+ self.assertOutput('', raw_output) -+ -+ def test_network_segment_range_list(self): -+ name = uuid.uuid4().hex -+ json_output = json.loads(self.openstack( -+ ' network segment range create -f json ' + -+ '--shared ' + -+ '--network-type geneve ' + -+ '--minimum 2018 ' + -+ '--maximum 2055 ' + -+ name -+ )) -+ network_segment_range_id = json_output.get('id') -+ network_segment_range_name = json_output.get('name') -+ self.addCleanup( -+ self.openstack, -+ 'network segment range delete ' + network_segment_range_id -+ ) -+ self.assertEqual( -+ name, -+ json_output["name"], -+ ) -+ -+ json_output = json.loads(self.openstack( -+ 'network segment list -f json' -+ )) -+ item_map = { -+ item.get('ID'): item.get('Name') for item in json_output -+ } -+ self.assertIn(network_segment_range_id, item_map.keys()) -+ self.assertIn(network_segment_range_name, item_map.values()) -+ -+ def test_network_segment_range_set_show(self): -+ name = uuid.uuid4().hex -+ json_output = json.loads(self.openstack( -+ ' network segment range create -f json ' + -+ '--private ' + -+ "--project " + self.PROJECT_NAME + " " + -+ '--network-type geneve ' + -+ '--minimum 2018 ' + -+ '--maximum 2055 ' + -+ name -+ )) -+ self.addCleanup( -+ self.openstack, -+ 'network segment range delete ' + name -+ ) -+ self.assertEqual( -+ name, -+ json_output["name"], -+ ) -+ -+ new_minimum = '2010' -+ new_maximum = '2060' -+ cmd_output = self.openstack( -+ 'network segment range set ' + -+ '--minimum ' + new_minimum + ' ' + -+ '--maximum ' + new_maximum + ' ' + -+ name -+ ) -+ self.assertOutput('', cmd_output) -+ -+ json_output = json.loads(self.openstack( -+ 'network segment range show -f json ' + -+ name -+ )) -+ self.assertEqual( -+ new_minimum, -+ json_output["minimum"], -+ ) -+ self.assertEqual( -+ new_maximum, -+ json_output["maximum"], -+ ) -diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py -index 28e92d1..2397cc5 100644 ---- a/openstackclient/tests/unit/network/v2/fakes.py -+++ b/openstackclient/tests/unit/network/v2/fakes.py -@@ -538,6 +538,66 @@ - return network_segments - - -+class FakeNetworkSegmentRange(object): -+ """Fake one or more network segment ranges.""" -+ -+ @staticmethod -+ def create_one_network_segment_range(attrs=None): -+ """Create a fake network segment range. -+ -+ :param Dictionary attrs: -+ A dictionary with all attributes -+ :return: -+ A FakeResource object faking the network segment range -+ """ -+ attrs = attrs or {} -+ -+ # Set default attributes. -+ fake_uuid = uuid.uuid4().hex -+ network_segment_range_attrs = { -+ 'id': 'network-segment-range-id-' + fake_uuid, -+ 'name': 'network-segment-name-' + fake_uuid, -+ 'default': False, -+ 'shared': False, -+ 'project_id': 'project-id-' + fake_uuid, -+ 'network_type': 'vlan', -+ 'physical_network': 'physical-network-name-' + fake_uuid, -+ 'minimum': 64, -+ 'maximum': 256, -+ 'used': {104: '3312e4ba67864b2eb53f3f41432f8efc', -+ 106: '3312e4ba67864b2eb53f3f41432f8efc'}, -+ 'available': [100, 101, 102, 103, 105], -+ } -+ -+ # Overwrite default attributes. -+ network_segment_range_attrs.update(attrs) -+ -+ network_segment_range = fakes.FakeResource( -+ info=copy.deepcopy(network_segment_range_attrs), -+ loaded=True -+ ) -+ -+ return network_segment_range -+ -+ @staticmethod -+ def create_network_segment_ranges(attrs=None, count=2): -+ """Create multiple fake network segment ranges. -+ -+ :param Dictionary attrs: -+ A dictionary with all attributes -+ :param int count: -+ The number of network segment ranges to fake -+ :return: -+ A list of FakeResource objects faking the network segment ranges -+ """ -+ network_segment_ranges = [] -+ for i in range(0, count): -+ network_segment_ranges.append( -+ FakeNetworkSegmentRange.create_one_network_segment_range(attrs) -+ ) -+ return network_segment_ranges -+ -+ - class FakePort(object): - """Fake one or more ports.""" - -diff --git a/openstackclient/tests/unit/network/v2/test_network_segment_range.py b/openstackclient/tests/unit/network/v2/test_network_segment_range.py -new file mode 100644 -index 0000000..bb2b302 ---- /dev/null -+++ b/openstackclient/tests/unit/network/v2/test_network_segment_range.py -@@ -0,0 +1,557 @@ -+# Copyright (c) 2018, Intel 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. -+# -+ -+import mock -+from mock import call -+ -+from osc_lib import exceptions -+ -+from openstackclient.network.v2 import network_segment_range -+from openstackclient.tests.unit.network.v2 import fakes as network_fakes -+from openstackclient.tests.unit import utils as tests_utils -+ -+ -+class TestNetworkSegmentRange(network_fakes.TestNetworkV2): -+ -+ def setUp(self): -+ super(TestNetworkSegmentRange, self).setUp() -+ -+ # Get a shortcut to the network client -+ self.network = self.app.client_manager.network -+ -+ -+class TestCreateNetworkSegmentRange(TestNetworkSegmentRange): -+ -+ # The network segment range to create. -+ _network_segment_range = network_fakes.FakeNetworkSegmentRange.\ -+ create_one_network_segment_range() -+ -+ columns = ( -+ 'available', -+ 'default', -+ 'id', -+ 'maximum', -+ 'minimum', -+ 'name', -+ 'network_type', -+ 'physical_network', -+ 'project_id', -+ 'shared', -+ 'used', -+ ) -+ -+ data = ( -+ ['100-103', '105'], -+ _network_segment_range.default, -+ _network_segment_range.id, -+ _network_segment_range.maximum, -+ _network_segment_range.minimum, -+ _network_segment_range.name, -+ _network_segment_range.network_type, -+ _network_segment_range.physical_network, -+ _network_segment_range.project_id, -+ _network_segment_range.shared, -+ {'3312e4ba67864b2eb53f3f41432f8efc': ['104', '106']}, -+ ) -+ -+ def setUp(self): -+ super(TestCreateNetworkSegmentRange, self).setUp() -+ -+ self.network.find_extension = mock.Mock() -+ self.network.create_network_segment_range = mock.Mock( -+ return_value=self._network_segment_range -+ ) -+ -+ # Get the command object to test -+ self.cmd = network_segment_range.CreateNetworkSegmentRange( -+ self.app, -+ self.namespace -+ ) -+ -+ def test_create_no_options(self): -+ # Missing required args should bail here -+ self.assertRaises(tests_utils.ParserException, self.check_parser, -+ self.cmd, [], []) -+ -+ def test_create_invalid_network_type(self): -+ arglist = [ -+ '--private', -+ '--project', self._network_segment_range.project_id, -+ '--network-type', 'foo', -+ '--minimum', str(self._network_segment_range.minimum), -+ '--maximum', str(self._network_segment_range.maximum), -+ self._network_segment_range.name, -+ ] -+ self.assertRaises(tests_utils.ParserException, self.check_parser, -+ self.cmd, arglist, []) -+ -+ def test_create_shared_with_project_id(self): -+ arglist = [ -+ '--shared', -+ '--project', self._network_segment_range.project_id, -+ '--network-type', 'vxlan', -+ '--minimum', str(self._network_segment_range.minimum), -+ '--maximum', str(self._network_segment_range.maximum), -+ self._network_segment_range.name, -+ ] -+ verifylist = [ -+ ('shared', True), -+ ('project', self._network_segment_range.project_id), -+ ('network_type', 'vxlan'), -+ ('minimum', self._network_segment_range.minimum), -+ ('maximum', self._network_segment_range.maximum), -+ ('name', self._network_segment_range.name), -+ ] -+ -+ parsed_args = self.check_parser(self.cmd, arglist, verifylist) -+ self.assertRaises(exceptions.CommandError, -+ self.cmd.take_action, -+ parsed_args) -+ -+ def test_create_non_shared_with_no_project_id(self): -+ arglist = [ -+ '--private', -+ '--network-type', 'vxlan', -+ '--minimum', str(self._network_segment_range.minimum), -+ '--maximum', str(self._network_segment_range.maximum), -+ self._network_segment_range.name, -+ ] -+ verifylist = [ -+ ('shared', self._network_segment_range.shared), -+ ('network_type', 'vxlan'), -+ ('minimum', self._network_segment_range.minimum), -+ ('maximum', self._network_segment_range.maximum), -+ ('name', self._network_segment_range.name), -+ ] -+ -+ parsed_args = self.check_parser(self.cmd, arglist, verifylist) -+ self.assertRaises(exceptions.CommandError, -+ self.cmd.take_action, -+ parsed_args) -+ -+ def test_create_tunnel_with_physical_network(self): -+ arglist = [ -+ '--shared', -+ '--network-type', 'vxlan', -+ '--physical-network', self._network_segment_range.physical_network, -+ '--minimum', str(self._network_segment_range.minimum), -+ '--maximum', str(self._network_segment_range.maximum), -+ self._network_segment_range.name, -+ ] -+ verifylist = [ -+ ('shared', True), -+ ('network_type', 'vxlan'), -+ ('physical_network', self._network_segment_range.physical_network), -+ ('minimum', self._network_segment_range.minimum), -+ ('maximum', self._network_segment_range.maximum), -+ ('name', self._network_segment_range.name), -+ ] -+ -+ parsed_args = self.check_parser(self.cmd, arglist, verifylist) -+ self.assertRaises(exceptions.CommandError, -+ self.cmd.take_action, -+ parsed_args) -+ -+ def test_create_minimum_options(self): -+ arglist = [ -+ '--private', -+ '--project', self._network_segment_range.project_id, -+ '--network-type', self._network_segment_range.network_type, -+ '--minimum', str(self._network_segment_range.minimum), -+ '--maximum', str(self._network_segment_range.maximum), -+ self._network_segment_range.name, -+ ] -+ verifylist = [ -+ ('shared', self._network_segment_range.shared), -+ ('project', self._network_segment_range.project_id), -+ ('network_type', self._network_segment_range.network_type), -+ ('minimum', self._network_segment_range.minimum), -+ ('maximum', self._network_segment_range.maximum), -+ ('name', self._network_segment_range.name), -+ ] -+ -+ parsed_args = self.check_parser(self.cmd, arglist, verifylist) -+ columns, data = self.cmd.take_action(parsed_args) -+ -+ self.network.create_network_segment_range.assert_called_once_with(**{ -+ 'shared': self._network_segment_range.shared, -+ 'project_id': mock.ANY, -+ 'network_type': self._network_segment_range.network_type, -+ 'minimum': self._network_segment_range.minimum, -+ 'maximum': self._network_segment_range.maximum, -+ 'name': self._network_segment_range.name, -+ }) -+ -+ self.assertEqual(self.columns, columns) -+ self.assertEqual(self.data, data) -+ -+ def test_create_all_options(self): -+ arglist = [ -+ '--private', -+ '--project', self._network_segment_range.project_id, -+ '--network-type', self._network_segment_range.network_type, -+ '--physical-network', self._network_segment_range.physical_network, -+ '--minimum', str(self._network_segment_range.minimum), -+ '--maximum', str(self._network_segment_range.maximum), -+ self._network_segment_range.name, -+ ] -+ verifylist = [ -+ ('shared', self._network_segment_range.shared), -+ ('project', self._network_segment_range.project_id), -+ ('network_type', self._network_segment_range.network_type), -+ ('physical_network', self._network_segment_range.physical_network), -+ ('minimum', self._network_segment_range.minimum), -+ ('maximum', self._network_segment_range.maximum), -+ ('name', self._network_segment_range.name), -+ ] -+ -+ parsed_args = self.check_parser(self.cmd, arglist, verifylist) -+ columns, data = self.cmd.take_action(parsed_args) -+ -+ self.network.create_network_segment_range.assert_called_once_with(**{ -+ 'shared': self._network_segment_range.shared, -+ 'project_id': mock.ANY, -+ 'network_type': self._network_segment_range.network_type, -+ 'physical_network': self._network_segment_range.physical_network, -+ 'minimum': self._network_segment_range.minimum, -+ 'maximum': self._network_segment_range.maximum, -+ 'name': self._network_segment_range.name, -+ }) -+ -+ self.assertEqual(self.columns, columns) -+ self.assertEqual(self.data, data) -+ -+ -+class TestDeleteNetworkSegmentRange(TestNetworkSegmentRange): -+ -+ # The network segment ranges to delete. -+ _network_segment_ranges = \ -+ network_fakes.FakeNetworkSegmentRange.create_network_segment_ranges() -+ -+ def setUp(self): -+ super(TestDeleteNetworkSegmentRange, self).setUp() -+ -+ self.network.find_extension = mock.Mock() -+ self.network.delete_network_segment_range = mock.Mock( -+ return_value=None) -+ self.network.find_network_segment_range = mock.Mock( -+ side_effect=self._network_segment_ranges -+ ) -+ -+ # Get the command object to test -+ self.cmd = network_segment_range.DeleteNetworkSegmentRange( -+ self.app, -+ self.namespace -+ ) -+ -+ def test_delete(self): -+ arglist = [ -+ self._network_segment_ranges[0].id, -+ ] -+ verifylist = [ -+ ('network_segment_range', [self._network_segment_ranges[0].id]), -+ ] -+ parsed_args = self.check_parser(self.cmd, arglist, verifylist) -+ -+ result = self.cmd.take_action(parsed_args) -+ -+ self.network.delete_network_segment_range.assert_called_once_with( -+ self._network_segment_ranges[0] -+ ) -+ self.assertIsNone(result) -+ -+ def test_delete_multiple(self): -+ arglist = [] -+ for _network_segment_range in self._network_segment_ranges: -+ arglist.append(_network_segment_range.id) -+ verifylist = [ -+ ('network_segment_range', arglist), -+ ] -+ parsed_args = self.check_parser(self.cmd, arglist, verifylist) -+ -+ result = self.cmd.take_action(parsed_args) -+ -+ calls = [] -+ for _network_segment_range in self._network_segment_ranges: -+ calls.append(call(_network_segment_range)) -+ self.network.delete_network_segment_range.assert_has_calls(calls) -+ self.assertIsNone(result) -+ -+ def test_delete_multiple_with_exception(self): -+ arglist = [ -+ self._network_segment_ranges[0].id, -+ 'doesnotexist' -+ ] -+ verifylist = [ -+ ('network_segment_range', -+ [self._network_segment_ranges[0].id, 'doesnotexist']), -+ ] -+ parsed_args = self.check_parser(self.cmd, arglist, verifylist) -+ -+ find_mock_result = [self._network_segment_ranges[0], -+ exceptions.CommandError] -+ self.network.find_network_segment_range = ( -+ mock.Mock(side_effect=find_mock_result) -+ ) -+ -+ try: -+ self.cmd.take_action(parsed_args) -+ self.fail('CommandError should be raised.') -+ except exceptions.CommandError as e: -+ self.assertEqual('1 of 2 network segment ranges failed to delete.', -+ str(e)) -+ -+ self.network.find_network_segment_range.assert_any_call( -+ self._network_segment_ranges[0].id, ignore_missing=False) -+ self.network.find_network_segment_range.assert_any_call( -+ 'doesnotexist', ignore_missing=False) -+ self.network.delete_network_segment_range.assert_called_once_with( -+ self._network_segment_ranges[0] -+ ) -+ -+ -+class TestListNetworkSegmentRange(TestNetworkSegmentRange): -+ _network_segment_ranges = network_fakes.FakeNetworkSegmentRange.\ -+ create_network_segment_ranges(count=3) -+ -+ columns = ( -+ 'ID', -+ 'Name', -+ 'Default', -+ 'Shared', -+ 'Project ID', -+ 'Network Type', -+ 'Physical Network', -+ 'Minimum ID', -+ 'Maximum ID' -+ ) -+ columns_long = columns + ( -+ 'Used', -+ 'Available', -+ ) -+ -+ data = [] -+ for _network_segment_range in _network_segment_ranges: -+ data.append(( -+ _network_segment_range.id, -+ _network_segment_range.name, -+ _network_segment_range.default, -+ _network_segment_range.shared, -+ _network_segment_range.project_id, -+ _network_segment_range.network_type, -+ _network_segment_range.physical_network, -+ _network_segment_range.minimum, -+ _network_segment_range.maximum, -+ )) -+ -+ data_long = [] -+ for _network_segment_range in _network_segment_ranges: -+ data_long.append(( -+ _network_segment_range.id, -+ _network_segment_range.name, -+ _network_segment_range.default, -+ _network_segment_range.shared, -+ _network_segment_range.project_id, -+ _network_segment_range.network_type, -+ _network_segment_range.physical_network, -+ _network_segment_range.minimum, -+ _network_segment_range.maximum, -+ {'3312e4ba67864b2eb53f3f41432f8efc': ['104', '106']}, -+ ['100-103', '105'], -+ )) -+ -+ def setUp(self): -+ super(TestListNetworkSegmentRange, self).setUp() -+ -+ # Get the command object to test -+ self.cmd = network_segment_range.ListNetworkSegmentRange( -+ self.app, self.namespace) -+ -+ self.network.find_extension = mock.Mock() -+ self.network.network_segment_ranges = mock.Mock( -+ return_value=self._network_segment_ranges) -+ -+ def test_list_no_option(self): -+ arglist = [] -+ verifylist = [ -+ ('long', False), -+ ('available', False), -+ ('unavailable', False), -+ ('used', False), -+ ('unused', False), -+ ] -+ parsed_args = self.check_parser(self.cmd, arglist, verifylist) -+ -+ columns, data = self.cmd.take_action(parsed_args) -+ -+ self.network.network_segment_ranges.assert_called_once_with() -+ self.assertEqual(self.columns, columns) -+ self.assertEqual(self.data, list(data)) -+ -+ def test_list_long(self): -+ arglist = [ -+ '--long', -+ ] -+ verifylist = [ -+ ('long', True), -+ ('available', False), -+ ('unavailable', False), -+ ('used', False), -+ ('unused', False), -+ ] -+ parsed_args = self.check_parser(self.cmd, arglist, verifylist) -+ -+ columns, data = self.cmd.take_action(parsed_args) -+ -+ self.network.network_segment_ranges.assert_called_once_with() -+ self.assertEqual(self.columns_long, columns) -+ self.assertEqual(self.data_long, list(data)) -+ -+ -+class TestSetNetworkSegmentRange(TestNetworkSegmentRange): -+ -+ # The network segment to show. -+ _network_segment_range = network_fakes.FakeNetworkSegmentRange.\ -+ create_one_network_segment_range() -+ -+ def setUp(self): -+ super(TestSetNetworkSegmentRange, self).setUp() -+ -+ self.network.find_extension = mock.Mock() -+ self.network.find_network_segment_range = mock.Mock( -+ return_value=self._network_segment_range -+ ) -+ self.network.update_network_segment_range = mock.Mock( -+ return_value=self._network_segment_range -+ ) -+ -+ # Get the command object to test -+ self.cmd = network_segment_range.SetNetworkSegmentRange(self.app, -+ self.namespace) -+ -+ def test_set_no_options(self): -+ arglist = [ -+ self._network_segment_range.id, -+ ] -+ verifylist = [ -+ ('network_segment_range', self._network_segment_range.id), -+ ] -+ -+ parsed_args = self.check_parser(self.cmd, arglist, verifylist) -+ result = self.cmd.take_action(parsed_args) -+ -+ self.network.update_network_segment_range.assert_called_once_with( -+ self._network_segment_range, **{} -+ ) -+ self.assertIsNone(result) -+ -+ def test_set_all_options(self): -+ arglist = [ -+ '--name', 'new name', -+ '--minimum', str(32), -+ '--maximum', str(512), -+ self._network_segment_range.id, -+ ] -+ verifylist = [ -+ ('name', 'new name'), -+ ('minimum', 32), -+ ('maximum', 512), -+ ('network_segment_range', self._network_segment_range.id), -+ ] -+ -+ parsed_args = self.check_parser(self.cmd, arglist, verifylist) -+ result = self.cmd.take_action(parsed_args) -+ -+ attrs = { -+ 'name': 'new name', -+ 'minimum': 32, -+ 'maximum': 512, -+ } -+ self.network.update_network_segment_range.assert_called_once_with( -+ self._network_segment_range, **attrs -+ ) -+ self.assertIsNone(result) -+ -+ -+class TestShowNetworkSegmentRange(TestNetworkSegmentRange): -+ -+ # The network segment range to show. -+ _network_segment_range = network_fakes.FakeNetworkSegmentRange.\ -+ create_one_network_segment_range() -+ -+ columns = ( -+ 'available', -+ 'default', -+ 'id', -+ 'maximum', -+ 'minimum', -+ 'name', -+ 'network_type', -+ 'physical_network', -+ 'project_id', -+ 'shared', -+ 'used', -+ ) -+ -+ data = ( -+ ['100-103', '105'], -+ _network_segment_range.default, -+ _network_segment_range.id, -+ _network_segment_range.maximum, -+ _network_segment_range.minimum, -+ _network_segment_range.name, -+ _network_segment_range.network_type, -+ _network_segment_range.physical_network, -+ _network_segment_range.project_id, -+ _network_segment_range.shared, -+ {'3312e4ba67864b2eb53f3f41432f8efc': ['104', '106']}, -+ ) -+ -+ def setUp(self): -+ super(TestShowNetworkSegmentRange, self).setUp() -+ -+ self.network.find_extension = mock.Mock() -+ self.network.find_network_segment_range = mock.Mock( -+ return_value=self._network_segment_range -+ ) -+ -+ # Get the command object to test -+ self.cmd = network_segment_range.ShowNetworkSegmentRange( -+ self.app, self.namespace) -+ -+ def test_show_no_options(self): -+ # Missing required args should bail here -+ self.assertRaises(tests_utils.ParserException, self.check_parser, -+ self.cmd, [], []) -+ -+ def test_show_all_options(self): -+ arglist = [ -+ self._network_segment_range.id, -+ ] -+ verifylist = [ -+ ('network_segment_range', self._network_segment_range.id), -+ ] -+ -+ parsed_args = self.check_parser(self.cmd, arglist, verifylist) -+ columns, data = self.cmd.take_action(parsed_args) -+ -+ self.network.find_network_segment_range.assert_called_once_with( -+ self._network_segment_range.id, -+ ignore_missing=False -+ ) -+ -+ self.assertEqual(self.columns, columns) -+ self.assertEqual(self.data, data) -diff --git a/releasenotes/notes/bp-network-segment-range-management-0abf03fe03eea149.yaml b/releasenotes/notes/bp-network-segment-range-management-0abf03fe03eea149.yaml -new file mode 100644 -index 0000000..4ff4f57 ---- /dev/null -+++ b/releasenotes/notes/bp-network-segment-range-management-0abf03fe03eea149.yaml -@@ -0,0 +1,6 @@ -+--- -+features: -+ - Add ``network segment range create``, ``network segment range delete``, -+ ``network segment range list``, ``network segment range show`` and -+ ``network segment range set`` commands. -+ [Blueprint `network-segment-range-management `_] -diff --git a/setup.cfg b/setup.cfg -index 48c2247..c73f2ce 100644 ---- a/setup.cfg -+++ b/setup.cfg -@@ -463,6 +463,12 @@ - network_segment_set = openstackclient.network.v2.network_segment:SetNetworkSegment - network_segment_show = openstackclient.network.v2.network_segment:ShowNetworkSegment - -+ network_segment_range_create = openstackclient.network.v2.network_segment_range:CreateNetworkSegmentRange -+ network_segment_range_delete = openstackclient.network.v2.network_segment_range:DeleteNetworkSegmentRange -+ network_segment_range_list = openstackclient.network.v2.network_segment_range:ListNetworkSegmentRange -+ network_segment_range_set = openstackclient.network.v2.network_segment_range:SetNetworkSegmentRange -+ network_segment_range_show = openstackclient.network.v2.network_segment_range:ShowNetworkSegmentRange -+ - network_service_provider_list = openstackclient.network.v2.network_service_provider:ListNetworkServiceProvider - - port_create = openstackclient.network.v2.port:CreatePort diff --git a/openstack/python-openstackclient/centos/python-openstackclient.spec b/openstack/python-openstackclient/centos/python-openstackclient.spec index b8e40965..60b969fc 100644 --- a/openstack/python-openstackclient/centos/python-openstackclient.spec +++ b/openstack/python-openstackclient/centos/python-openstackclient.spec @@ -23,7 +23,6 @@ Summary: OpenStack Command-line Client License: ASL 2.0 URL: http://launchpad.net/%{name} Source0: https://tarballs.openstack.org/%{name}/%{name}-%{upstream_version}.tar.gz -Patch0001: 0001-Add-network-segment-range-command-object.patch BuildArch: noarch