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:
Richard Theis 2016-03-08 15:18:16 -06:00
parent 9da02d14ea
commit 6a55e05cbf
10 changed files with 506 additions and 0 deletions

View 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)

View File

@ -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

View 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)

View 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)

View File

@ -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):

View File

@ -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."""

View 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)

View File

@ -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')

View File

@ -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>`_]

View File

@ -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