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
This commit is contained in:
parent
df71ae814e
commit
6a6b192dde
@ -12,23 +12,29 @@ To address these challenges, an OpenStackClient command may
|
|||||||
be labeled as a beta command according to the guidelines
|
be labeled as a beta command according to the guidelines
|
||||||
below. Such commands may introduce backwards incompatible
|
below. Such commands may introduce backwards incompatible
|
||||||
changes and may use REST API enhancements not yet released.
|
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
|
See the examples below on how to label an entire command or
|
||||||
by updating the command documentation, help and implementation.
|
a specific option as a beta by updating the documentation
|
||||||
|
and implementation.
|
||||||
|
|
||||||
The initial release note must label the new command as a beta.
|
The initial release note must label the new command or option
|
||||||
No further release notes are required until the 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 option is no longer a beta. At which time, the beta label or
|
||||||
or the command itself must be removed and a new release note
|
the command or option itself must be removed and a new release note
|
||||||
must be provided.
|
must be provided.
|
||||||
|
|
||||||
|
Beta Command Example
|
||||||
|
--------------------
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
-------------
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
The command documentation must label the command as a beta.
|
The command documentation must label the command as a beta.
|
||||||
|
|
||||||
example list
|
example list
|
||||||
~~~~~~~~~~~~
|
++++++++++++
|
||||||
|
|
||||||
List examples
|
List examples
|
||||||
|
|
||||||
@ -42,7 +48,7 @@ List examples
|
|||||||
os example list
|
os example list
|
||||||
|
|
||||||
Help
|
Help
|
||||||
----
|
~~~~
|
||||||
|
|
||||||
The command help must label the command as a beta.
|
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
|
Implementation
|
||||||
--------------
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
The command must raise a ``CommandError`` exception if beta commands
|
The command must raise a ``CommandError`` exception if beta commands
|
||||||
are not enabled via ``--os-beta-command`` global option.
|
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):
|
def take_action(self, parsed_args):
|
||||||
self.validate_os_beta_command_enabled()
|
self.validate_os_beta_command_enabled()
|
||||||
|
|
||||||
|
Beta Option Example
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The option documentation must label the option as a beta.
|
||||||
|
|
||||||
|
.. option:: --example <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='<example>',
|
||||||
|
help=_("Example")
|
||||||
|
)
|
||||||
|
@ -28,6 +28,7 @@ Create new subnet
|
|||||||
[--ip-version {4,6}]
|
[--ip-version {4,6}]
|
||||||
[--ipv6-ra-mode {dhcpv6-stateful,dhcpv6-stateless,slaac}]
|
[--ipv6-ra-mode {dhcpv6-stateful,dhcpv6-stateless,slaac}]
|
||||||
[--ipv6-address-mode {dhcpv6-stateful,dhcpv6-stateless,slaac}]
|
[--ipv6-address-mode {dhcpv6-stateful,dhcpv6-stateless,slaac}]
|
||||||
|
[--network-segment <network-segment>]
|
||||||
--network <network>
|
--network <network>
|
||||||
<name>
|
<name>
|
||||||
|
|
||||||
@ -107,6 +108,14 @@ Create new subnet
|
|||||||
|
|
||||||
IPv6 address mode, valid modes: [dhcpv6-stateful, dhcpv6-stateless, slaac]
|
IPv6 address mode, valid modes: [dhcpv6-stateful, dhcpv6-stateless, slaac]
|
||||||
|
|
||||||
|
.. option:: --network-segment <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>
|
.. option:: --network <network>
|
||||||
|
|
||||||
Network this subnet belongs to (name or ID)
|
Network this subnet belongs to (name or ID)
|
||||||
|
@ -136,6 +136,9 @@ def _get_attrs(client_manager, parsed_args, is_create=True):
|
|||||||
attrs['ipv6_ra_mode'] = parsed_args.ipv6_ra_mode
|
attrs['ipv6_ra_mode'] = parsed_args.ipv6_ra_mode
|
||||||
if parsed_args.ipv6_address_mode is not None:
|
if parsed_args.ipv6_address_mode is not None:
|
||||||
attrs['ipv6_address_mode'] = parsed_args.ipv6_address_mode
|
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:
|
if 'gateway' in parsed_args and parsed_args.gateway is not None:
|
||||||
gateway = parsed_args.gateway.lower()
|
gateway = parsed_args.gateway.lower()
|
||||||
@ -249,6 +252,13 @@ class CreateSubnet(command.ShowOne):
|
|||||||
help=_("IPv6 address mode, "
|
help=_("IPv6 address mode, "
|
||||||
"valid modes: [dhcpv6-stateful, dhcpv6-stateless, slaac]")
|
"valid modes: [dhcpv6-stateful, dhcpv6-stateless, slaac]")
|
||||||
)
|
)
|
||||||
|
if self.app.options.os_beta_command:
|
||||||
|
parser.add_argument(
|
||||||
|
'--network-segment',
|
||||||
|
metavar='<network-segment>',
|
||||||
|
help=_("Network segment to associate with this subnet "
|
||||||
|
"(ID only)")
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--network',
|
'--network',
|
||||||
required=True,
|
required=True,
|
||||||
|
@ -352,7 +352,7 @@ class FakeNetworkSegment(object):
|
|||||||
|
|
||||||
# Set default attributes.
|
# Set default attributes.
|
||||||
network_segment_attrs = {
|
network_segment_attrs = {
|
||||||
'id': 'segment-id-' + uuid.uuid4().hex,
|
'id': 'network-segment-id-' + uuid.uuid4().hex,
|
||||||
'network_id': 'network-id-' + uuid.uuid4().hex,
|
'network_id': 'network-id-' + uuid.uuid4().hex,
|
||||||
'network_type': 'vlan',
|
'network_type': 'vlan',
|
||||||
'physical_network': 'physical-network-name-' + uuid.uuid4().hex,
|
'physical_network': 'physical-network-name-' + uuid.uuid4().hex,
|
||||||
@ -699,9 +699,10 @@ class FakeSubnet(object):
|
|||||||
'host_routes': [],
|
'host_routes': [],
|
||||||
'ip_version': 4,
|
'ip_version': 4,
|
||||||
'gateway_ip': '10.10.10.1',
|
'gateway_ip': '10.10.10.1',
|
||||||
'ipv6_address_mode': 'None',
|
'ipv6_address_mode': None,
|
||||||
'ipv6_ra_mode': 'None',
|
'ipv6_ra_mode': None,
|
||||||
'subnetpool_id': 'None',
|
'segment_id': None,
|
||||||
|
'subnetpool_id': None,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Overwrite default attributes.
|
# Overwrite default attributes.
|
||||||
|
@ -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 = (
|
columns = (
|
||||||
'allocation_pools',
|
'allocation_pools',
|
||||||
'cidr',
|
'cidr',
|
||||||
@ -102,6 +110,7 @@ class TestCreateSubnet(TestSubnet):
|
|||||||
'name',
|
'name',
|
||||||
'network_id',
|
'network_id',
|
||||||
'project_id',
|
'project_id',
|
||||||
|
'segment_id',
|
||||||
'subnetpool_id',
|
'subnetpool_id',
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -119,6 +128,7 @@ class TestCreateSubnet(TestSubnet):
|
|||||||
_subnet.name,
|
_subnet.name,
|
||||||
_subnet.network_id,
|
_subnet.network_id,
|
||||||
_subnet.project_id,
|
_subnet.project_id,
|
||||||
|
_subnet.segment_id,
|
||||||
_subnet.subnetpool_id,
|
_subnet.subnetpool_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -136,6 +146,7 @@ class TestCreateSubnet(TestSubnet):
|
|||||||
_subnet_from_pool.name,
|
_subnet_from_pool.name,
|
||||||
_subnet_from_pool.network_id,
|
_subnet_from_pool.network_id,
|
||||||
_subnet_from_pool.project_id,
|
_subnet_from_pool.project_id,
|
||||||
|
_subnet_from_pool.segment_id,
|
||||||
_subnet_from_pool.subnetpool_id,
|
_subnet_from_pool.subnetpool_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -153,6 +164,7 @@ class TestCreateSubnet(TestSubnet):
|
|||||||
_subnet_ipv6.name,
|
_subnet_ipv6.name,
|
||||||
_subnet_ipv6.network_id,
|
_subnet_ipv6.network_id,
|
||||||
_subnet_ipv6.project_id,
|
_subnet_ipv6.project_id,
|
||||||
|
_subnet_ipv6.segment_id,
|
||||||
_subnet_ipv6.subnetpool_id,
|
_subnet_ipv6.subnetpool_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -186,6 +198,15 @@ class TestCreateSubnet(TestSubnet):
|
|||||||
loaded=True,
|
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):
|
def test_create_no_options(self):
|
||||||
arglist = []
|
arglist = []
|
||||||
verifylist = []
|
verifylist = []
|
||||||
@ -196,11 +217,9 @@ class TestCreateSubnet(TestSubnet):
|
|||||||
self.check_parser, self.cmd, arglist, verifylist)
|
self.check_parser, self.cmd, arglist, verifylist)
|
||||||
|
|
||||||
def test_create_default_options(self):
|
def test_create_default_options(self):
|
||||||
# Mock create_subnet and find_network sdk calls to return the
|
# Mock SDK calls for this test.
|
||||||
# values we want for this test
|
|
||||||
self.network.create_subnet = mock.Mock(return_value=self._subnet)
|
self.network.create_subnet = mock.Mock(return_value=self._subnet)
|
||||||
self._network.id = self._subnet.network_id
|
self._network.id = self._subnet.network_id
|
||||||
self.network.find_network = mock.Mock(return_value=self._network)
|
|
||||||
|
|
||||||
arglist = [
|
arglist = [
|
||||||
"--subnet-range", self._subnet.cidr,
|
"--subnet-range", self._subnet.cidr,
|
||||||
@ -230,14 +249,10 @@ class TestCreateSubnet(TestSubnet):
|
|||||||
self.assertEqual(self.data, data)
|
self.assertEqual(self.data, data)
|
||||||
|
|
||||||
def test_create_from_subnet_pool_options(self):
|
def test_create_from_subnet_pool_options(self):
|
||||||
# Mock create_subnet, find_subnet_pool, and find_network sdk calls
|
# Mock SDK calls for this test.
|
||||||
# to return the values we want for this test
|
|
||||||
self.network.create_subnet = \
|
self.network.create_subnet = \
|
||||||
mock.Mock(return_value=self._subnet_from_pool)
|
mock.Mock(return_value=self._subnet_from_pool)
|
||||||
self._network.id = self._subnet_from_pool.network_id
|
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 = [
|
arglist = [
|
||||||
self._subnet_from_pool.name,
|
self._subnet_from_pool.name,
|
||||||
@ -290,11 +305,9 @@ class TestCreateSubnet(TestSubnet):
|
|||||||
self.assertEqual(self.data_subnet_pool, data)
|
self.assertEqual(self.data_subnet_pool, data)
|
||||||
|
|
||||||
def test_create_options_subnet_range_ipv6(self):
|
def test_create_options_subnet_range_ipv6(self):
|
||||||
# Mock create_subnet and find_network sdk calls to return the
|
# Mock SDK calls for this test.
|
||||||
# values we want for this test
|
|
||||||
self.network.create_subnet = mock.Mock(return_value=self._subnet_ipv6)
|
self.network.create_subnet = mock.Mock(return_value=self._subnet_ipv6)
|
||||||
self._network.id = self._subnet_ipv6.network_id
|
self._network.id = self._subnet_ipv6.network_id
|
||||||
self.network.find_network = mock.Mock(return_value=self._network)
|
|
||||||
|
|
||||||
arglist = [
|
arglist = [
|
||||||
self._subnet_ipv6.name,
|
self._subnet_ipv6.name,
|
||||||
@ -357,6 +370,59 @@ class TestCreateSubnet(TestSubnet):
|
|||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
self.assertEqual(self.data_ipv6, data)
|
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):
|
class TestDeleteSubnet(TestSubnet):
|
||||||
|
|
||||||
@ -593,6 +659,7 @@ class TestShowSubnet(TestSubnet):
|
|||||||
'name',
|
'name',
|
||||||
'network_id',
|
'network_id',
|
||||||
'project_id',
|
'project_id',
|
||||||
|
'segment_id',
|
||||||
'subnetpool_id',
|
'subnetpool_id',
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -610,6 +677,7 @@ class TestShowSubnet(TestSubnet):
|
|||||||
_subnet.name,
|
_subnet.name,
|
||||||
_subnet.network_id,
|
_subnet.network_id,
|
||||||
_subnet.tenant_id,
|
_subnet.tenant_id,
|
||||||
|
_subnet.segment_id,
|
||||||
_subnet.subnetpool_id,
|
_subnet.subnetpool_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -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 <https://blueprints.launchpad.net/neutron/+spec/routed-networks>`_]
|
Loading…
Reference in New Issue
Block a user