Add network segment command object
Add network segment command object in support of routed networks. This patch set includes documentation, unit tests and functional tests (currently skipped until segments enabled in neutron by default) for the following new commands: - "os network segment list" - "os network segment show" These new commands are currently marked as beta commands. Change-Id: I1a79b48dc6820fe2a39fcceb11c8cae3bda413a0 Partially-Implements: blueprint routed-networks
This commit is contained in:
parent
9da02d14ea
commit
6a55e05cbf
54
doc/source/command-objects/network-segment.rst
Normal file
54
doc/source/command-objects/network-segment.rst
Normal file
@ -0,0 +1,54 @@
|
||||
===============
|
||||
network segment
|
||||
===============
|
||||
|
||||
A **network segment** is an isolated Layer 2 segment within a network.
|
||||
A network may contain multiple network segments. Depending on the
|
||||
network configuration, Layer 2 connectivity between network segments
|
||||
within a network may not be guaranteed.
|
||||
|
||||
Network v2
|
||||
|
||||
network segment list
|
||||
--------------------
|
||||
|
||||
List network segments
|
||||
|
||||
.. caution:: This is a beta command and subject to change.
|
||||
Use global option ``--enable-beta-commands`` to
|
||||
enable this command.
|
||||
|
||||
.. program:: network segment list
|
||||
.. code:: bash
|
||||
|
||||
os network segment list
|
||||
[--long]
|
||||
[--network <network>]
|
||||
|
||||
.. option:: --long
|
||||
|
||||
List additional fields in output
|
||||
|
||||
.. option:: --network <network>
|
||||
|
||||
List network segments that belong to this network (name or ID)
|
||||
|
||||
network segment show
|
||||
--------------------
|
||||
|
||||
Display network segment details
|
||||
|
||||
.. caution:: This is a beta command and subject to change.
|
||||
Use global option ``--enable-beta-commands`` to
|
||||
enable this command.
|
||||
|
||||
.. program:: network segment show
|
||||
.. code:: bash
|
||||
|
||||
os network segment show
|
||||
<network-segment>
|
||||
|
||||
.. _network_segment_show-segment:
|
||||
.. describe:: <network-segment>
|
||||
|
||||
Network segment to display (ID only)
|
@ -104,6 +104,7 @@ referring to both Compute and Volume quotas.
|
||||
* ``mapping``: (**Identity**) a definition to translate identity provider attributes to Identity concepts
|
||||
* ``module``: (**Internal**) - installed Python modules in the OSC process
|
||||
* ``network``: (**Compute**, **Network**) - a virtual network for connecting servers and other resources
|
||||
* ``network segment``: (**Network**) - a segment of a virtual network
|
||||
* ``object``: (**Object Storage**) a single file in the Object Storage
|
||||
* ``object store account``: (**Object Storage**) owns a group of Object Storage resources
|
||||
* ``policy``: (**Identity**) determines authorization
|
||||
|
60
functional/tests/network/v2/test_network_segment.py
Normal file
60
functional/tests/network/v2/test_network_segment.py
Normal file
@ -0,0 +1,60 @@
|
||||
# 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 testtools
|
||||
import uuid
|
||||
|
||||
from functional.common import test
|
||||
|
||||
|
||||
# NOTE(rtheis): Routed networks is still a WIP and not enabled by default.
|
||||
@testtools.skip("bp/routed-networks")
|
||||
class NetworkSegmentTests(test.TestCase):
|
||||
"""Functional tests for network segment. """
|
||||
NETWORK_NAME = uuid.uuid4().hex
|
||||
PHYSICAL_NETWORK_NAME = uuid.uuid4().hex
|
||||
NETWORK_SEGMENT_ID = None
|
||||
NETWORK_ID = None
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
# Create a network for the segment.
|
||||
opts = cls.get_show_opts(['id'])
|
||||
raw_output = cls.openstack('network create ' + cls.NETWORK_NAME + opts)
|
||||
cls.NETWORK_ID = raw_output.strip('\n')
|
||||
|
||||
# Get the segment for the network.
|
||||
opts = cls.get_show_opts(['ID', 'Network'])
|
||||
raw_output = cls.openstack('--enable-beta-commands '
|
||||
'network segment list '
|
||||
' --network ' + cls.NETWORK_NAME +
|
||||
' ' + opts)
|
||||
raw_output_row = raw_output.split('\n')[0]
|
||||
cls.NETWORK_SEGMENT_ID = raw_output_row.split(' ')[0]
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
raw_output = cls.openstack('network delete ' + cls.NETWORK_NAME)
|
||||
cls.assertOutput('', raw_output)
|
||||
|
||||
def test_network_segment_list(self):
|
||||
opts = self.get_list_opts(['ID'])
|
||||
raw_output = self.openstack('--enable-beta-commands '
|
||||
'network segment list' + opts)
|
||||
self.assertIn(self.NETWORK_SEGMENT_ID, raw_output)
|
||||
|
||||
def test_network_segment_show(self):
|
||||
opts = self.get_show_opts(['network_id'])
|
||||
raw_output = self.openstack('--enable-beta-commands '
|
||||
'network segment show ' +
|
||||
self.NETWORK_SEGMENT_ID + opts)
|
||||
self.assertEqual(self.NETWORK_ID + "\n", raw_output)
|
124
openstackclient/network/v2/network_segment.py
Normal file
124
openstackclient/network/v2/network_segment.py
Normal file
@ -0,0 +1,124 @@
|
||||
# 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"""
|
||||
|
||||
# TODO(rtheis): Add description and name properties when support is available.
|
||||
|
||||
from openstackclient.common import command
|
||||
from openstackclient.common import exceptions
|
||||
from openstackclient.common import utils
|
||||
from openstackclient.i18n import _
|
||||
|
||||
|
||||
class ListNetworkSegment(command.Lister):
|
||||
"""List network segments
|
||||
|
||||
(Caution: This is a beta command and subject to change.
|
||||
Use global option --enable-beta-commands to enable
|
||||
this command)
|
||||
"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListNetworkSegment, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--long',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=_('List additional fields in output'),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--network',
|
||||
metavar='<network>',
|
||||
help=_('List network segments that belong to this '
|
||||
'network (name or ID)'),
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
if not self.app.options.enable_beta_commands:
|
||||
msg = _('Caution: This is a beta command and subject to '
|
||||
'change. Use global option --enable-beta-commands '
|
||||
'to enable this command.')
|
||||
raise exceptions.CommandError(msg)
|
||||
|
||||
network_client = self.app.client_manager.network
|
||||
|
||||
filters = {}
|
||||
if parsed_args.network:
|
||||
_network = network_client.find_network(
|
||||
parsed_args.network,
|
||||
ignore_missing=False
|
||||
)
|
||||
filters = {'network_id': _network.id}
|
||||
data = network_client.segments(**filters)
|
||||
|
||||
headers = (
|
||||
'ID',
|
||||
'Network',
|
||||
'Network Type',
|
||||
'Segment',
|
||||
)
|
||||
columns = (
|
||||
'id',
|
||||
'network_id',
|
||||
'network_type',
|
||||
'segmentation_id',
|
||||
)
|
||||
if parsed_args.long:
|
||||
headers = headers + (
|
||||
'Physical Network',
|
||||
)
|
||||
columns = columns + (
|
||||
'physical_network',
|
||||
)
|
||||
|
||||
return (headers,
|
||||
(utils.get_item_properties(
|
||||
s, columns,
|
||||
formatters={},
|
||||
) for s in data))
|
||||
|
||||
|
||||
class ShowNetworkSegment(command.ShowOne):
|
||||
"""Display network segment details
|
||||
|
||||
(Caution: This is a beta command and subject to change.
|
||||
Use global option --enable-beta-commands to enable
|
||||
this command)
|
||||
"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ShowNetworkSegment, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'network_segment',
|
||||
metavar='<network-segment>',
|
||||
help=_('Network segment to display (ID only)'),
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
if not self.app.options.enable_beta_commands:
|
||||
msg = _('Caution: This is a beta command and subject to '
|
||||
'change. Use global option --enable-beta-commands '
|
||||
'to enable this command.')
|
||||
raise exceptions.CommandError(msg)
|
||||
|
||||
client = self.app.client_manager.network
|
||||
obj = client.find_segment(
|
||||
parsed_args.network_segment,
|
||||
ignore_missing=False
|
||||
)
|
||||
columns = tuple(sorted(obj.keys()))
|
||||
data = utils.get_item_properties(obj, columns)
|
||||
return (columns, data)
|
@ -97,6 +97,11 @@ class FakeApp(object):
|
||||
self.log = _log
|
||||
|
||||
|
||||
class FakeOptions(object):
|
||||
def __init__(self, **kwargs):
|
||||
self.enable_beta_commands = False
|
||||
|
||||
|
||||
class FakeClient(object):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
@ -256,6 +256,58 @@ class FakeNetwork(object):
|
||||
return mock.MagicMock(side_effect=networks)
|
||||
|
||||
|
||||
class FakeNetworkSegment(object):
|
||||
"""Fake one or more network segments."""
|
||||
|
||||
@staticmethod
|
||||
def create_one_network_segment(attrs=None):
|
||||
"""Create a fake network segment.
|
||||
|
||||
:param Dictionary attrs:
|
||||
A dictionary with all attributes
|
||||
:return:
|
||||
A FakeResource object faking the network segment
|
||||
"""
|
||||
attrs = attrs or {}
|
||||
|
||||
# Set default attributes.
|
||||
network_segment_attrs = {
|
||||
'id': 'segment-id-' + uuid.uuid4().hex,
|
||||
'network_id': 'network-id-' + uuid.uuid4().hex,
|
||||
'network_type': 'vlan',
|
||||
'physical_network': 'physical-network-name-' + uuid.uuid4().hex,
|
||||
'segmentation_id': 1024,
|
||||
}
|
||||
|
||||
# Overwrite default attributes.
|
||||
network_segment_attrs.update(attrs)
|
||||
|
||||
network_segment = fakes.FakeResource(
|
||||
info=copy.deepcopy(network_segment_attrs),
|
||||
loaded=True
|
||||
)
|
||||
|
||||
return network_segment
|
||||
|
||||
@staticmethod
|
||||
def create_network_segments(attrs=None, count=2):
|
||||
"""Create multiple fake network segments.
|
||||
|
||||
:param Dictionary attrs:
|
||||
A dictionary with all attributes
|
||||
:param int count:
|
||||
The number of network segments to fake
|
||||
:return:
|
||||
A list of FakeResource objects faking the network segments
|
||||
"""
|
||||
network_segments = []
|
||||
for i in range(0, count):
|
||||
network_segments.append(
|
||||
FakeNetworkSegment.create_one_network_segment(attrs)
|
||||
)
|
||||
return network_segments
|
||||
|
||||
|
||||
class FakePort(object):
|
||||
"""Fake one or more ports."""
|
||||
|
||||
|
199
openstackclient/tests/network/v2/test_network_segment.py
Normal file
199
openstackclient/tests/network/v2/test_network_segment.py
Normal file
@ -0,0 +1,199 @@
|
||||
# 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 openstackclient.common import exceptions
|
||||
from openstackclient.network.v2 import network_segment
|
||||
from openstackclient.tests.network.v2 import fakes as network_fakes
|
||||
from openstackclient.tests import utils as tests_utils
|
||||
|
||||
|
||||
class TestNetworkSegment(network_fakes.TestNetworkV2):
|
||||
|
||||
def setUp(self):
|
||||
super(TestNetworkSegment, self).setUp()
|
||||
|
||||
# Enable beta commands.
|
||||
self.app.options.enable_beta_commands = True
|
||||
|
||||
# Get a shortcut to the network client
|
||||
self.network = self.app.client_manager.network
|
||||
|
||||
|
||||
class TestListNetworkSegment(TestNetworkSegment):
|
||||
_network = network_fakes.FakeNetwork.create_one_network()
|
||||
_network_segments = \
|
||||
network_fakes.FakeNetworkSegment.create_network_segments(count=3)
|
||||
|
||||
columns = (
|
||||
'ID',
|
||||
'Network',
|
||||
'Network Type',
|
||||
'Segment',
|
||||
)
|
||||
columns_long = columns + (
|
||||
'Physical Network',
|
||||
)
|
||||
|
||||
data = []
|
||||
for _network_segment in _network_segments:
|
||||
data.append((
|
||||
_network_segment.id,
|
||||
_network_segment.network_id,
|
||||
_network_segment.network_type,
|
||||
_network_segment.segmentation_id,
|
||||
))
|
||||
|
||||
data_long = []
|
||||
for _network_segment in _network_segments:
|
||||
data_long.append((
|
||||
_network_segment.id,
|
||||
_network_segment.network_id,
|
||||
_network_segment.network_type,
|
||||
_network_segment.segmentation_id,
|
||||
_network_segment.physical_network,
|
||||
))
|
||||
|
||||
def setUp(self):
|
||||
super(TestListNetworkSegment, self).setUp()
|
||||
|
||||
# Get the command object to test
|
||||
self.cmd = network_segment.ListNetworkSegment(self.app, self.namespace)
|
||||
|
||||
self.network.find_network = mock.Mock(return_value=self._network)
|
||||
self.network.segments = mock.Mock(return_value=self._network_segments)
|
||||
|
||||
def test_list_no_option(self):
|
||||
arglist = []
|
||||
verifylist = [
|
||||
('long', False),
|
||||
('network', None),
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
|
||||
self.network.segments.assert_called_once_with()
|
||||
self.assertEqual(self.columns, columns)
|
||||
self.assertEqual(self.data, list(data))
|
||||
|
||||
def test_list_no_beta_commands(self):
|
||||
self.app.options.enable_beta_commands = False
|
||||
parsed_args = self.check_parser(self.cmd, [], [])
|
||||
self.assertRaises(exceptions.CommandError, self.cmd.take_action,
|
||||
parsed_args)
|
||||
|
||||
def test_list_long(self):
|
||||
arglist = [
|
||||
'--long',
|
||||
]
|
||||
verifylist = [
|
||||
('long', True),
|
||||
('network', None),
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
|
||||
self.network.segments.assert_called_once_with()
|
||||
self.assertEqual(self.columns_long, columns)
|
||||
self.assertEqual(self.data_long, list(data))
|
||||
|
||||
def test_list_network(self):
|
||||
arglist = [
|
||||
'--network',
|
||||
self._network.id,
|
||||
]
|
||||
verifylist = [
|
||||
('long', False),
|
||||
('network', self._network.id)
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
|
||||
self.network.segments.assert_called_once_with(
|
||||
**{'network_id': self._network.id}
|
||||
)
|
||||
self.assertEqual(self.columns, columns)
|
||||
self.assertEqual(self.data, list(data))
|
||||
|
||||
|
||||
class TestShowNetworkSegment(TestNetworkSegment):
|
||||
|
||||
# The network segment to show.
|
||||
_network_segment = \
|
||||
network_fakes.FakeNetworkSegment.create_one_network_segment()
|
||||
|
||||
columns = (
|
||||
'id',
|
||||
'network_id',
|
||||
'network_type',
|
||||
'physical_network',
|
||||
'segmentation_id',
|
||||
)
|
||||
|
||||
data = (
|
||||
_network_segment.id,
|
||||
_network_segment.network_id,
|
||||
_network_segment.network_type,
|
||||
_network_segment.physical_network,
|
||||
_network_segment.segmentation_id,
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
super(TestShowNetworkSegment, self).setUp()
|
||||
|
||||
self.network.find_segment = mock.Mock(
|
||||
return_value=self._network_segment
|
||||
)
|
||||
|
||||
# Get the command object to test
|
||||
self.cmd = network_segment.ShowNetworkSegment(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_no_beta_commands(self):
|
||||
arglist = [
|
||||
self._network_segment.id,
|
||||
]
|
||||
verifylist = [
|
||||
('network_segment', self._network_segment.id),
|
||||
]
|
||||
self.app.options.enable_beta_commands = False
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
self.assertRaises(exceptions.CommandError, self.cmd.take_action,
|
||||
parsed_args)
|
||||
|
||||
def test_show_all_options(self):
|
||||
arglist = [
|
||||
self._network_segment.id,
|
||||
]
|
||||
verifylist = [
|
||||
('network_segment', self._network_segment.id),
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
|
||||
self.network.find_segment.assert_called_once_with(
|
||||
self._network_segment.id,
|
||||
ignore_missing=False
|
||||
)
|
||||
|
||||
self.assertEqual(self.columns, columns)
|
||||
self.assertEqual(self.data, data)
|
@ -60,6 +60,7 @@ class TestCommand(TestCase):
|
||||
self.fake_log = fakes.FakeLog()
|
||||
self.app = fakes.FakeApp(self.fake_stdout, self.fake_log)
|
||||
self.app.client_manager = fakes.FakeClientManager()
|
||||
self.app.options = fakes.FakeOptions()
|
||||
|
||||
def check_parser(self, cmd, args, verify_args):
|
||||
cmd_parser = cmd.get_parser('check_parser')
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- Add support for the ``network segment`` command object via the
|
||||
``network segment list`` and ``network segment show`` commands.
|
||||
These are beta commands and subject to change. Use global option
|
||||
``--enable-beta-commands`` to enable these commands.
|
||||
[Blueprint `routed-networks <https://blueprints.launchpad.net/neutron/+spec/routed-networks>`_]
|
@ -341,6 +341,9 @@ openstack.network.v2 =
|
||||
network_set = openstackclient.network.v2.network:SetNetwork
|
||||
network_show = openstackclient.network.v2.network:ShowNetwork
|
||||
|
||||
network_segment_list = openstackclient.network.v2.network_segment:ListNetworkSegment
|
||||
network_segment_show = openstackclient.network.v2.network_segment:ShowNetworkSegment
|
||||
|
||||
port_create = openstackclient.network.v2.port:CreatePort
|
||||
port_delete = openstackclient.network.v2.port:DeletePort
|
||||
port_list = openstackclient.network.v2.port:ListPort
|
||||
|
Loading…
Reference in New Issue
Block a user