From 6a6b192ddeb80b516778b1d6e3d34f4261dca85d Mon Sep 17 00:00:00 2001 From: Richard Theis Date: Fri, 10 Jun 2016 16:01:31 -0500 Subject: [PATCH] Add "--network-segment" option to "subnet create" Add "--network-segment" option to the "subnet create" command. This is a beta command option and subject to change. Use global option "--os-beta-command" to enable this option. This patch set also provides a devref update for beta command options. Change-Id: I4d0fbe079b2a873307364c41c22ce9ba88e632e6 Partially-Implements: blueprint routed-networks --- doc/source/command-beta.rst | 58 +++++++++--- doc/source/command-objects/subnet.rst | 9 ++ openstackclient/network/v2/subnet.py | 10 +++ openstackclient/tests/network/v2/fakes.py | 9 +- .../tests/network/v2/test_subnet.py | 90 ++++++++++++++++--- .../bp-routed-networks-86a24f34d86fca53.yaml | 6 ++ 6 files changed, 157 insertions(+), 25 deletions(-) create mode 100644 releasenotes/notes/bp-routed-networks-86a24f34d86fca53.yaml diff --git a/doc/source/command-beta.rst b/doc/source/command-beta.rst index f0a4f85102..bc8c04c5e7 100644 --- a/doc/source/command-beta.rst +++ b/doc/source/command-beta.rst @@ -12,23 +12,29 @@ To address these challenges, an OpenStackClient command may be labeled as a beta command according to the guidelines below. Such commands may introduce backwards incompatible changes and may use REST API enhancements not yet released. +This also applies to command options associated with the beta +command object. -See the examples below on how to label a command as a beta -by updating the command documentation, help and implementation. +See the examples below on how to label an entire command or +a specific option as a beta by updating the documentation +and implementation. -The initial release note must label the new command as a beta. -No further release notes are required until the command -is no longer a beta. At which time, the command beta label -or the command itself must be removed and a new release note +The initial release note must label the new command or option +as a beta. No further release notes are required until the command +or option is no longer a beta. At which time, the beta label or +the command or option itself must be removed and a new release note must be provided. +Beta Command Example +-------------------- + Documentation -------------- +~~~~~~~~~~~~~ The command documentation must label the command as a beta. example list -~~~~~~~~~~~~ +++++++++++++ List examples @@ -42,7 +48,7 @@ List examples os example list Help ----- +~~~~ The command help must label the command as a beta. @@ -57,7 +63,7 @@ The command help must label the command as a beta. """ Implementation --------------- +~~~~~~~~~~~~~~ The command must raise a ``CommandError`` exception if beta commands are not enabled via ``--os-beta-command`` global option. @@ -66,3 +72,35 @@ are not enabled via ``--os-beta-command`` global option. def take_action(self, parsed_args): self.validate_os_beta_command_enabled() + +Beta Option Example +------------------- + +Documentation +~~~~~~~~~~~~~ + +The option documentation must label the option as a beta. + +.. option:: --example + + Example + + .. caution:: This is a beta command option and subject + to change. Use global option ``--os-beta-command`` + to enable this command option. + +Implementation +~~~~~~~~~~~~~~ + +The option must not be added if beta commands are not +enabled via ``--os-beta-command`` global option. + +.. code-block:: python + + def get_parser(self, prog_name): + if self.app.options.os_beta_command: + parser.add_argument( + '--example', + metavar='', + help=_("Example") + ) diff --git a/doc/source/command-objects/subnet.rst b/doc/source/command-objects/subnet.rst index ff6354e658..dd54408b71 100644 --- a/doc/source/command-objects/subnet.rst +++ b/doc/source/command-objects/subnet.rst @@ -28,6 +28,7 @@ Create new subnet [--ip-version {4,6}] [--ipv6-ra-mode {dhcpv6-stateful,dhcpv6-stateless,slaac}] [--ipv6-address-mode {dhcpv6-stateful,dhcpv6-stateless,slaac}] + [--network-segment ] --network @@ -107,6 +108,14 @@ Create new subnet IPv6 address mode, valid modes: [dhcpv6-stateful, dhcpv6-stateless, slaac] +.. option:: --network-segment + + Network segment to associate with this subnet (ID only) + + .. caution:: This is a beta command option and subject + to change. Use global option ``--os-beta-command`` + to enable this command option. + .. option:: --network Network this subnet belongs to (name or ID) diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py index 8e378c7e19..ee5b7df0ce 100644 --- a/openstackclient/network/v2/subnet.py +++ b/openstackclient/network/v2/subnet.py @@ -136,6 +136,9 @@ def _get_attrs(client_manager, parsed_args, is_create=True): attrs['ipv6_ra_mode'] = parsed_args.ipv6_ra_mode if parsed_args.ipv6_address_mode is not None: attrs['ipv6_address_mode'] = parsed_args.ipv6_address_mode + if 'network_segment' in parsed_args: + attrs['segment_id'] = client.find_segment( + parsed_args.network_segment, ignore_missing=False).id if 'gateway' in parsed_args and parsed_args.gateway is not None: gateway = parsed_args.gateway.lower() @@ -249,6 +252,13 @@ class CreateSubnet(command.ShowOne): help=_("IPv6 address mode, " "valid modes: [dhcpv6-stateful, dhcpv6-stateless, slaac]") ) + if self.app.options.os_beta_command: + parser.add_argument( + '--network-segment', + metavar='', + help=_("Network segment to associate with this subnet " + "(ID only)") + ) parser.add_argument( '--network', required=True, diff --git a/openstackclient/tests/network/v2/fakes.py b/openstackclient/tests/network/v2/fakes.py index 9efbe8c608..41ff10238c 100644 --- a/openstackclient/tests/network/v2/fakes.py +++ b/openstackclient/tests/network/v2/fakes.py @@ -352,7 +352,7 @@ class FakeNetworkSegment(object): # Set default attributes. network_segment_attrs = { - 'id': 'segment-id-' + uuid.uuid4().hex, + 'id': 'network-segment-id-' + uuid.uuid4().hex, 'network_id': 'network-id-' + uuid.uuid4().hex, 'network_type': 'vlan', 'physical_network': 'physical-network-name-' + uuid.uuid4().hex, @@ -699,9 +699,10 @@ class FakeSubnet(object): 'host_routes': [], 'ip_version': 4, 'gateway_ip': '10.10.10.1', - 'ipv6_address_mode': 'None', - 'ipv6_ra_mode': 'None', - 'subnetpool_id': 'None', + 'ipv6_address_mode': None, + 'ipv6_ra_mode': None, + 'segment_id': None, + 'subnetpool_id': None, } # Overwrite default attributes. diff --git a/openstackclient/tests/network/v2/test_subnet.py b/openstackclient/tests/network/v2/test_subnet.py index 25684d63e0..0c1854c102 100644 --- a/openstackclient/tests/network/v2/test_subnet.py +++ b/openstackclient/tests/network/v2/test_subnet.py @@ -88,6 +88,14 @@ class TestCreateSubnet(TestSubnet): } ) + # The network segment to be returned from find_segment + _network_segment = \ + network_fakes.FakeNetworkSegment.create_one_network_segment( + attrs={ + 'network_id': _subnet.network_id, + } + ) + columns = ( 'allocation_pools', 'cidr', @@ -102,6 +110,7 @@ class TestCreateSubnet(TestSubnet): 'name', 'network_id', 'project_id', + 'segment_id', 'subnetpool_id', ) @@ -119,6 +128,7 @@ class TestCreateSubnet(TestSubnet): _subnet.name, _subnet.network_id, _subnet.project_id, + _subnet.segment_id, _subnet.subnetpool_id, ) @@ -136,6 +146,7 @@ class TestCreateSubnet(TestSubnet): _subnet_from_pool.name, _subnet_from_pool.network_id, _subnet_from_pool.project_id, + _subnet_from_pool.segment_id, _subnet_from_pool.subnetpool_id, ) @@ -153,6 +164,7 @@ class TestCreateSubnet(TestSubnet): _subnet_ipv6.name, _subnet_ipv6.network_id, _subnet_ipv6.project_id, + _subnet_ipv6.segment_id, _subnet_ipv6.subnetpool_id, ) @@ -186,6 +198,15 @@ class TestCreateSubnet(TestSubnet): loaded=True, ) + # Mock SDK calls for all tests. + self.network.find_network = mock.Mock(return_value=self._network) + self.network.find_segment = mock.Mock( + return_value=self._network_segment + ) + self.network.find_subnet_pool = mock.Mock( + return_value=self._subnet_pool + ) + def test_create_no_options(self): arglist = [] verifylist = [] @@ -196,11 +217,9 @@ class TestCreateSubnet(TestSubnet): self.check_parser, self.cmd, arglist, verifylist) def test_create_default_options(self): - # Mock create_subnet and find_network sdk calls to return the - # values we want for this test + # Mock SDK calls for this test. self.network.create_subnet = mock.Mock(return_value=self._subnet) self._network.id = self._subnet.network_id - self.network.find_network = mock.Mock(return_value=self._network) arglist = [ "--subnet-range", self._subnet.cidr, @@ -230,14 +249,10 @@ class TestCreateSubnet(TestSubnet): self.assertEqual(self.data, data) def test_create_from_subnet_pool_options(self): - # Mock create_subnet, find_subnet_pool, and find_network sdk calls - # to return the values we want for this test + # Mock SDK calls for this test. self.network.create_subnet = \ mock.Mock(return_value=self._subnet_from_pool) self._network.id = self._subnet_from_pool.network_id - self.network.find_network = mock.Mock(return_value=self._network) - self.network.find_subnet_pool = \ - mock.Mock(return_value=self._subnet_pool) arglist = [ self._subnet_from_pool.name, @@ -290,11 +305,9 @@ class TestCreateSubnet(TestSubnet): self.assertEqual(self.data_subnet_pool, data) def test_create_options_subnet_range_ipv6(self): - # Mock create_subnet and find_network sdk calls to return the - # values we want for this test + # Mock SDK calls for this test. self.network.create_subnet = mock.Mock(return_value=self._subnet_ipv6) self._network.id = self._subnet_ipv6.network_id - self.network.find_network = mock.Mock(return_value=self._network) arglist = [ self._subnet_ipv6.name, @@ -357,6 +370,59 @@ class TestCreateSubnet(TestSubnet): self.assertEqual(self.columns, columns) self.assertEqual(self.data_ipv6, data) + def test_create_no_beta_command_options(self): + arglist = [ + "--subnet-range", self._subnet.cidr, + "--network-segment", self._network_segment.id, + "--network", self._subnet.network_id, + self._subnet.name, + ] + verifylist = [ + ('name', self._subnet.name), + ('subnet_range', self._subnet.cidr), + ('network-segment', self._network_segment.id), + ('network', self._subnet.network_id), + ] + self.app.options.os_beta_command = False + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, arglist, verifylist) + + def test_create_with_network_segment(self): + # Mock SDK calls for this test. + self.network.create_subnet = mock.Mock(return_value=self._subnet) + self._network.id = self._subnet.network_id + + arglist = [ + "--subnet-range", self._subnet.cidr, + "--network-segment", self._network_segment.id, + "--network", self._subnet.network_id, + self._subnet.name, + ] + verifylist = [ + ('name', self._subnet.name), + ('subnet_range', self._subnet.cidr), + ('network_segment', self._network_segment.id), + ('network', self._subnet.network_id), + ('ip_version', self._subnet.ip_version), + ('gateway', 'auto'), + + ] + + self.app.options.os_beta_command = True + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_subnet.assert_called_once_with(**{ + 'cidr': self._subnet.cidr, + 'enable_dhcp': self._subnet.enable_dhcp, + 'ip_version': self._subnet.ip_version, + 'name': self._subnet.name, + 'network_id': self._subnet.network_id, + 'segment_id': self._network_segment.id, + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + class TestDeleteSubnet(TestSubnet): @@ -593,6 +659,7 @@ class TestShowSubnet(TestSubnet): 'name', 'network_id', 'project_id', + 'segment_id', 'subnetpool_id', ) @@ -610,6 +677,7 @@ class TestShowSubnet(TestSubnet): _subnet.name, _subnet.network_id, _subnet.tenant_id, + _subnet.segment_id, _subnet.subnetpool_id, ) diff --git a/releasenotes/notes/bp-routed-networks-86a24f34d86fca53.yaml b/releasenotes/notes/bp-routed-networks-86a24f34d86fca53.yaml new file mode 100644 index 0000000000..6c4a185c7a --- /dev/null +++ b/releasenotes/notes/bp-routed-networks-86a24f34d86fca53.yaml @@ -0,0 +1,6 @@ +--- +features: + - Add ``--network-segment`` option to the ``subnet create`` command. + This is a beta command option and subject to change. Use global option + ``--os-beta-command`` to enable this option. + [Blueprint `routed-networks `_]