taas OSC commands

Add possibility to tap-as-a-service to use openstack commands instead
of deprecated neutron commands.

Closes-Bug: #1874701

Change-Id: I339f42a63789a946011700902724a58647c80b9d
This commit is contained in:
elajkat 2020-04-17 08:34:12 +02:00
parent 51b36d9c9c
commit 62b8bb7213
10 changed files with 1057 additions and 0 deletions

View File

@ -236,6 +236,42 @@ extension
TaaS CLI Reference
==================
Openstack CLI
-------------
OpenStackClient provides
`the basic network commands <https://docs.openstack.org/python-openstackclient/latest/cli/command-list.html>`__
and tap-as-a-service has an extension for taas related commands.
* Create tap service: **openstack tap service create** --name <name of the tap service> --port <name or ID of the port on which the traffic is delivered>
* List tap services: **openstack tap service list**
* Show tap service: **openstack tap service show** <tap service id/tap service name>
* Delete tap service: **openstack tap service delete** <tap service id/tap service name>
* Update tap service: **openstack tap service update** <tap service id/tap service name> --name <new name of the tap service> --description <new description of the tap service>
* Create tap flow: **openstack tap flow create** --name <name of the tap flow> --port <name or ID of the Source port to which the Tap Flow is connected> --tap-service <name or ID of the tap service> --direction <Direction of the Tap flow. Possible options are: IN, OUT, BOTH> --vlan-filter <LAN Ids to be mirrored in the form of range string>
* List tap flows **openstack tap flow list**
* Show tap flow **openstack tap flow show** <tap flow id/tap flow name>
* Delete tap flow **openstack tap flow delete** <tap flow id/tap flow name>
* Update tap flow **openstack tap flow update** <tap flow id/tap flow name> --name <new name of the tap flow> --description <new description of the tap flow>
Neutron CLI
-----------
.. warning::
neutron CLI is now deprecated, and will be removed in the future.
Use openstack CLI instead.
The TaaS commands can be executed using TaaS CLI, which is integrated with neutron.
It can be used to send REST request and interact with the TaaS
extension. Given below are the detail of the CLIs:

View File

View File

@ -0,0 +1,234 @@
# All Rights Reserved 2020
#
# 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 logging
from osc_lib.cli import format_columns
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils as osc_utils
from osc_lib.utils import columns as column_util
from neutronclient._i18n import _
from neutronclient.common import utils
from neutronclient.osc import utils as nc_osc_utils
LOG = logging.getLogger(__name__)
TAP_FLOW = 'tap_flow'
TAP_FLOWS = '%ss' % TAP_FLOW
path = 'taas'
object_path = '/%s/' % path
resource_path = '/%s/%%s/%%s' % path
_attr_map = (
('id', 'ID', column_util.LIST_BOTH),
('tenant_id', 'Tenant', column_util.LIST_LONG_ONLY),
('name', 'Name', column_util.LIST_BOTH),
('status', 'Status', column_util.LIST_BOTH),
('source_port', 'source_port', column_util.LIST_BOTH),
('tap_service_id', 'tap_service_id', column_util.LIST_BOTH),
('direction', 'Direction', column_util.LIST_BOTH),
)
_formatters = {
'vlan_filter': format_columns.ListColumn,
}
def _add_updatable_args(parser):
parser.add_argument(
'--name',
help=_('Name of this Tap service.'))
parser.add_argument(
'--description',
help=_('Description for this Tap service.'))
class CreateTapFlow(command.ShowOne):
_description = _("Create a tap flow")
def get_parser(self, prog_name):
parser = super(CreateTapFlow, self).get_parser(prog_name)
nc_osc_utils.add_project_owner_option_to_parser(parser)
_add_updatable_args(parser)
parser.add_argument(
'--port',
required=True,
metavar="SOURCE_PORT",
help=_('Source port to which the Tap Flow is connected.'))
parser.add_argument(
'--tap-service',
required=True,
metavar="TAP_SERVICE",
help=_('Tap Service to which the Tap Flow belongs.'))
parser.add_argument(
'--direction',
required=True,
metavar="DIRECTION",
choices=['IN', 'OUT', 'BOTH'],
type=utils.convert_to_uppercase,
help=_('Direction of the Tap flow. Possible options are: '
'IN, OUT, BOTH'))
parser.add_argument(
'--vlan-filter',
required=False,
metavar="VLAN_FILTER",
help=_('VLAN Ids to be mirrored in the form of range string.'))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.neutronclient
attrs = {}
if parsed_args.name is not None:
attrs['name'] = str(parsed_args.name)
if parsed_args.description is not None:
attrs['description'] = str(parsed_args.description)
if parsed_args.port is not None:
source_port = client.find_resource('port',
parsed_args.port)['id']
attrs['source_port'] = source_port
if parsed_args.tap_service is not None:
tap_service_id = client.find_resource(
'tap_service', parsed_args.tap_service)['id']
attrs['tap_service_id'] = tap_service_id
if parsed_args.direction is not None:
attrs['direction'] = parsed_args.direction
if parsed_args.vlan_filter is not None:
attrs['vlan_filter'] = parsed_args.vlan_filter
if 'project' in parsed_args and parsed_args.project is not None:
project_id = nc_osc_utils.find_project(
self.app.client_manager.identity,
parsed_args.project,
parsed_args.project_domain,
).id
attrs['tenant_id'] = project_id
body = {TAP_FLOW: attrs}
obj = client.post('%s%s' % (object_path, TAP_FLOWS),
body=body)[TAP_FLOW]
columns, display_columns = column_util.get_columns(obj, _attr_map)
data = osc_utils.get_dict_properties(obj, columns)
return display_columns, data
class ListTapFlow(command.Lister):
_description = _("List tap flows that belong to a given tenant")
def get_parser(self, prog_name):
parser = super(ListTapFlow, self).get_parser(prog_name)
nc_osc_utils.add_project_owner_option_to_parser(parser)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.neutronclient
params = {}
if parsed_args.project is not None:
project_id = nc_osc_utils.find_project(
self.app.client_manager.identity,
parsed_args.project,
parsed_args.project_domain,
).id
params['tenant_id'] = project_id
objs = client.list(TAP_FLOWS, '%s%s' % (object_path, TAP_FLOWS),
retrieve_all=True, params=params)[TAP_FLOWS]
headers, columns = column_util.get_column_definitions(
_attr_map, long_listing=True)
print('XXXXX headers=%s ' % headers)
print('XXXXX columns=%s ' % columns)
print('XXXXX objs=%s ' % objs)
return (headers, (osc_utils.get_dict_properties(
s, columns, formatters=_formatters) for s in objs))
class ShowTapFlow(command.ShowOne):
_description = _("Show information of a given tap flow")
def get_parser(self, prog_name):
parser = super(ShowTapFlow, self).get_parser(prog_name)
parser.add_argument(
TAP_FLOW,
metavar="<%s>" % TAP_FLOW,
help=_("ID or name of tap flow to look up."),
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.neutronclient
id = client.find_resource(TAP_FLOW, parsed_args.tap_flow)['id']
obj = client.get(resource_path % (TAP_FLOWS, id))[TAP_FLOW]
columns, display_columns = column_util.get_columns(obj, _attr_map)
data = osc_utils.get_dict_properties(obj, columns)
return display_columns, data
class DeleteTapFlow(command.Command):
_description = _("Delete a tap flow")
def get_parser(self, prog_name):
parser = super(DeleteTapFlow, self).get_parser(prog_name)
parser.add_argument(
TAP_FLOW,
metavar="<%s>" % TAP_FLOW,
nargs="+",
help=_("ID(s) or name(s) of tap flow to delete."),
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.neutronclient
fails = 0
for id_or_name in parsed_args.tap_flow:
try:
id = client.find_resource(TAP_FLOW, id_or_name)['id']
client.delete(resource_path % (TAP_FLOWS, id))
LOG.warning("Tap flow %(id)s deleted", {'id': id})
except Exception as e:
fails += 1
LOG.error("Failed to delete tap flow with name or ID "
"'%(id_or_name)s': %(e)s",
{'id_or_name': id_or_name, 'e': e})
if fails > 0:
msg = (_("Failed to delete %(fails)s of %(total)s tap flow.") %
{'fails': fails, 'total': len(parsed_args.tap_service)})
raise exceptions.CommandError(msg)
class UpdateTapFlow(command.ShowOne):
_description = _("Update a tap flow.")
def get_parser(self, prog_name):
parser = super(UpdateTapFlow, self).get_parser(prog_name)
parser.add_argument(
TAP_FLOW,
metavar="<%s>" % TAP_FLOW,
help=_("ID or name of tap flow to update."),
)
_add_updatable_args(parser)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.neutronclient
id = client.find_resource(TAP_FLOW, parsed_args.tap_flow)['id']
attrs = {}
if parsed_args.name is not None:
attrs['name'] = str(parsed_args.name)
if parsed_args.description is not None:
attrs['description'] = str(parsed_args.description)
body = {TAP_FLOW: attrs}
obj = client.put(resource_path % (TAP_FLOWS, id), body)[TAP_FLOW]
columns, display_columns = column_util.get_columns(obj, _attr_map)
data = osc_utils.get_dict_properties(obj, columns)
return display_columns, data

View File

@ -0,0 +1,203 @@
# All Rights Reserved 2020
#
# 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 logging
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils as osc_utils
from osc_lib.utils import columns as column_util
from neutronclient._i18n import _
from neutronclient.neutron import v2_0 as neutronv20
from neutronclient.osc import utils as nc_osc_utils
LOG = logging.getLogger(__name__)
TAP_SERVICE = 'tap_service'
TAP_SERVICES = '%ss' % TAP_SERVICE
path = 'taas'
object_path = '/%s/' % path
resource_path = '/%s/%%s/%%s' % path
_attr_map = (
('id', 'ID', column_util.LIST_BOTH),
('tenant_id', 'Tenant', column_util.LIST_LONG_ONLY),
('name', 'Name', column_util.LIST_BOTH),
('port_id', 'Port', column_util.LIST_BOTH),
('status', 'Status', column_util.LIST_BOTH),
)
def _add_updatable_args(parser):
parser.add_argument(
'--name',
help=_('Name of this Tap service.'))
parser.add_argument(
'--description',
help=_('Description for this Tap service.'))
def _updatable_args2body(parsed_args, body):
neutronv20.update_dict(parsed_args, body, ['name', 'description'])
class CreateTapService(command.ShowOne):
_description = _("Create a tap service")
def get_parser(self, prog_name):
parser = super(CreateTapService, self).get_parser(prog_name)
nc_osc_utils.add_project_owner_option_to_parser(parser)
_add_updatable_args(parser)
parser.add_argument(
'--port',
dest='port_id',
required=True,
metavar="PORT",
help=_('Port to which the Tap service is connected.'))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.neutronclient
attrs = {}
if parsed_args.name is not None:
attrs['name'] = str(parsed_args.name)
if parsed_args.description is not None:
attrs['description'] = str(parsed_args.description)
if parsed_args.port_id is not None:
port_id = client.find_resource('port', parsed_args.port_id)['id']
attrs['port_id'] = port_id
if 'project' in parsed_args and parsed_args.project is not None:
project_id = nc_osc_utils.find_project(
self.app.client_manager.identity,
parsed_args.project,
parsed_args.project_domain,
).id
attrs['tenant_id'] = project_id
body = {TAP_SERVICE: attrs}
obj = client.post('%s%s' % (object_path, TAP_SERVICES),
body=body)[TAP_SERVICE]
columns, display_columns = column_util.get_columns(obj, _attr_map)
data = osc_utils.get_dict_properties(obj, columns)
return display_columns, data
class ListTapService(command.Lister):
_description = _("List tap services that belong to a given tenant")
def get_parser(self, prog_name):
parser = super(ListTapService, self).get_parser(prog_name)
nc_osc_utils.add_project_owner_option_to_parser(parser)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.neutronclient
params = {}
if parsed_args.project is not None:
project_id = nc_osc_utils.find_project(
self.app.client_manager.identity,
parsed_args.project,
parsed_args.project_domain,
).id
params['tenant_id'] = project_id
objs = client.list(TAP_SERVICES, '%s%s' % (object_path, TAP_SERVICES),
retrieve_all=True, params=params)[TAP_SERVICES]
headers, columns = column_util.get_column_definitions(
_attr_map, long_listing=True)
return (headers, (osc_utils.get_dict_properties(
s, columns) for s in objs))
class ShowTapService(command.ShowOne):
_description = _("Show information of a given tap service")
def get_parser(self, prog_name):
parser = super(ShowTapService, self).get_parser(prog_name)
parser.add_argument(
TAP_SERVICE,
metavar="<%s>" % TAP_SERVICE,
help=_("ID or name of tap service to look up."),
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.neutronclient
id = client.find_resource(TAP_SERVICE, parsed_args.tap_service)['id']
obj = client.get(resource_path % (TAP_SERVICES, id))[TAP_SERVICE]
columns, display_columns = column_util.get_columns(obj, _attr_map)
data = osc_utils.get_dict_properties(obj, columns)
return display_columns, data
class DeleteTapService(command.Command):
_description = _("Delete a tap service")
def get_parser(self, prog_name):
parser = super(DeleteTapService, self).get_parser(prog_name)
parser.add_argument(
TAP_SERVICE,
metavar="<%s>" % TAP_SERVICE,
nargs="+",
help=_("ID(s) or name(s) of tap service to delete."),
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.neutronclient
fails = 0
for id_or_name in parsed_args.tap_service:
try:
id = client.find_resource(TAP_SERVICE, id_or_name)['id']
client.delete(resource_path % (TAP_SERVICES, id))
LOG.warning("Tap service %(id)s deleted", {'id': id})
except Exception as e:
fails += 1
LOG.error("Failed to delete tap service with name or ID "
"'%(id_or_name)s': %(e)s",
{'id_or_name': id_or_name, 'e': e})
if fails > 0:
msg = (_("Failed to delete %(fails)s of %(total)s tap service.") %
{'fails': fails, 'total': len(parsed_args.tap_service)})
raise exceptions.CommandError(msg)
class UpdateTapService(command.ShowOne):
_description = _("Update a tap service.")
def get_parser(self, prog_name):
parser = super(UpdateTapService, self).get_parser(prog_name)
parser.add_argument(
TAP_SERVICE,
metavar="<%s>" % TAP_SERVICE,
help=_("ID or name of tap service to update."),
)
_add_updatable_args(parser)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.neutronclient
id = client.find_resource(TAP_SERVICE, parsed_args.tap_service)['id']
attrs = {}
if parsed_args.name is not None:
attrs['name'] = str(parsed_args.name)
if parsed_args.description is not None:
attrs['description'] = str(parsed_args.description)
body = {TAP_SERVICE: attrs}
obj = client.put(resource_path % (TAP_SERVICES, id), body)[TAP_SERVICE]
columns, display_columns = column_util.get_columns(obj, _attr_map)
data = osc_utils.get_dict_properties(obj, columns)
return display_columns, data

View File

@ -0,0 +1,86 @@
# All Rights Reserved 2020
#
# 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 copy
from oslo_utils import uuidutils
from neutron_taas.taas_client.osc import tap_flow as osc_tap_flow
from neutron_taas.taas_client.osc import tap_service as osc_tap_service
class FakeTapService(object):
@staticmethod
def create_tap_service(attrs=None):
"""Create a fake tap service."""
attrs = attrs or {}
tap_service_attrs = {
'id': uuidutils.generate_uuid(),
'tenant_id': uuidutils.generate_uuid(),
'name': 'test_tap_service' + uuidutils.generate_uuid(),
'status': 'ACTIVE',
}
tap_service_attrs.update(attrs)
return copy.deepcopy(tap_service_attrs)
@staticmethod
def create_tap_services(attrs=None, count=1):
"""Create multiple fake tap services."""
tap_services = []
for i in range(0, count):
if attrs is None:
attrs = {'id': 'fake_id%d' % i}
elif getattr(attrs, 'id', None) is None:
attrs['id'] = 'fake_id%d' % i
tap_services.append(FakeTapService.create_tap_service(
attrs=attrs))
return {osc_tap_service.TAP_SERVICES: tap_services}
class FakeTapFlow(object):
@staticmethod
def create_tap_flow(attrs=None):
"""Create a fake tap service."""
attrs = attrs or {}
tap_flow_attrs = {
'id': uuidutils.generate_uuid(),
'tenant_id': uuidutils.generate_uuid(),
'name': 'test_tap_flow' + uuidutils.generate_uuid(),
'status': 'ACTIVE',
'direction': 'BOTH',
}
tap_flow_attrs.update(attrs)
return copy.deepcopy(tap_flow_attrs)
@staticmethod
def create_tap_flows(attrs=None, count=1):
"""Create multiple fake tap flows."""
tap_flows = []
for i in range(0, count):
if attrs is None:
attrs = {
'id': 'fake_id%d' % i,
'source_port': uuidutils.generate_uuid(),
'tap_service_id': uuidutils.generate_uuid()
}
elif getattr(attrs, 'id', None) is None:
attrs['id'] = 'fake_id%d' % i
tap_flows.append(FakeTapFlow.create_tap_flow(attrs=attrs))
return {osc_tap_flow.TAP_FLOWS: tap_flows}

View File

@ -0,0 +1,257 @@
# All Rights Reserved 2020
#
# 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 copy
import operator
from unittest import mock
from neutronclient.tests.unit.osc.v2 import fakes as test_fakes
from osc_lib import utils as osc_utils
from osc_lib.utils import columns as column_util
from oslo_utils import uuidutils
from neutron_taas.taas_client.osc import tap_flow as osc_tap_flow
from neutron_taas.tests.unit.taas_client.osc import fakes
columns_long = tuple(col for col, _, listing_mode in osc_tap_flow._attr_map
if listing_mode in (column_util.LIST_BOTH,
column_util.LIST_LONG_ONLY))
headers_long = tuple(head for _, head, listing_mode in
osc_tap_flow._attr_map if listing_mode in
(column_util.LIST_BOTH, column_util.LIST_LONG_ONLY))
sorted_attr_map = sorted(osc_tap_flow._attr_map, key=operator.itemgetter(1))
sorted_columns = tuple(col for col, _, _ in sorted_attr_map)
sorted_headers = tuple(head for _, head, _ in sorted_attr_map)
def _get_data(attrs, columns=sorted_columns):
return osc_utils.get_dict_properties(attrs, columns)
class TestCreateTapService(test_fakes.TestNeutronClientOSCV2):
columns = (
'Direction',
'ID',
'Name',
'Status',
'Tenant',
'source_port',
'tap_service_id',
)
def setUp(self):
super(TestCreateTapService, self).setUp()
self.cmd = osc_tap_flow.CreateTapFlow(self.app, self.namespace)
def test_create_tap_flow(self):
"""Test Create Tap Flow."""
fake_tap_flow = fakes.FakeTapFlow.create_tap_flow(
attrs={
'source_port': uuidutils.generate_uuid(),
'tap_service_id': uuidutils.generate_uuid()
}
)
self.neutronclient.post = mock.Mock(
return_value={osc_tap_flow.TAP_FLOW: fake_tap_flow})
arg_list = [
'--name', fake_tap_flow['name'],
'--port', fake_tap_flow['source_port'],
'--tap-service', fake_tap_flow['tap_service_id'],
'--direction', fake_tap_flow['direction'],
]
verify_list = [
('name', fake_tap_flow['name']),
('port', fake_tap_flow['source_port']),
('tap_service', fake_tap_flow['tap_service_id']),
]
parsed_args = self.check_parser(self.cmd, arg_list, verify_list)
with mock.patch.object(self.neutronclient, 'find_resource') as nc_find:
nc_find.side_effect = [
{'id': fake_tap_flow['source_port']},
{'id': fake_tap_flow['tap_service_id']}
]
columns, data = self.cmd.take_action(parsed_args)
self.neutronclient.post.assert_called_once_with(
'/taas/tap_flows',
body={
osc_tap_flow.TAP_FLOW:
{
'name': fake_tap_flow['name'],
'source_port': fake_tap_flow['source_port'],
'tap_service_id': fake_tap_flow['tap_service_id'],
'direction': fake_tap_flow['direction']
}
}
)
self.assertEqual(self.columns, columns)
self.assertItemEqual(_get_data(fake_tap_flow), data)
class TestListTapFlow(test_fakes.TestNeutronClientOSCV2):
def setUp(self):
super(TestListTapFlow, self).setUp()
self.cmd = osc_tap_flow.ListTapFlow(self.app, self.namespace)
def test_list_tap_flows(self):
"""Test List Tap Flow."""
fake_tap_flows = fakes.FakeTapFlow.create_tap_flows(
attrs={
'source_port': uuidutils.generate_uuid(),
'tap_service_id': uuidutils.generate_uuid(),
},
count=2)
self.neutronclient.list = mock.Mock(return_value=fake_tap_flows)
arg_list = []
verify_list = []
parsed_args = self.check_parser(self.cmd, arg_list, verify_list)
headers, data = self.cmd.take_action(parsed_args)
self.neutronclient.list.assert_called_once()
self.assertEqual(headers, list(headers_long))
self.assertListItemEqual(
list(data),
[_get_data(fake_tap_flow, columns_long) for fake_tap_flow
in fake_tap_flows[osc_tap_flow.TAP_FLOWS]]
)
class TestDeleteTapFlow(test_fakes.TestNeutronClientOSCV2):
def setUp(self):
super(TestDeleteTapFlow, self).setUp()
self.neutronclient.find_resource = mock.Mock(
side_effect=lambda _, name_or_id: {'id': name_or_id})
self.cmd = osc_tap_flow.DeleteTapFlow(self.app, self.namespace)
def test_delete_tap_flow(self):
"""Test Delete tap flow."""
fake_tap_flow = fakes.FakeTapFlow.create_tap_flow(
attrs={
'source_port': uuidutils.generate_uuid(),
'tap_service_id': uuidutils.generate_uuid(),
}
)
self.neutronclient.delete = mock.Mock()
arg_list = [
fake_tap_flow['id'],
]
verify_list = [
(osc_tap_flow.TAP_FLOW, [fake_tap_flow['id']]),
]
parsed_args = self.check_parser(self.cmd, arg_list, verify_list)
result = self.cmd.take_action(parsed_args)
self.neutronclient.delete.assert_called_once_with(
osc_tap_flow.resource_path % ('tap_flows',
fake_tap_flow['id']))
self.assertIsNone(result)
class TestShowTapFlow(test_fakes.TestNeutronClientOSCV2):
def setUp(self):
super(TestShowTapFlow, self).setUp()
self.neutronclient.find_resource = mock.Mock(
side_effect=lambda _, name_or_id: {'id': name_or_id})
self.cmd = osc_tap_flow.ShowTapFlow(self.app, self.namespace)
def test_show_tap_flow(self):
"""Test Show tap flow."""
fake_tap_flow = fakes.FakeTapFlow.create_tap_flow(
attrs={
'source_port': uuidutils.generate_uuid(),
'tap_service_id': uuidutils.generate_uuid(),
}
)
self.neutronclient.get = mock.Mock(
return_value={osc_tap_flow.TAP_FLOW: fake_tap_flow})
arg_list = [
fake_tap_flow['id'],
]
verify_list = [
(osc_tap_flow.TAP_FLOW, fake_tap_flow['id']),
]
parsed_args = self.check_parser(self.cmd, arg_list, verify_list)
headers, data = self.cmd.take_action(parsed_args)
self.neutronclient.get.assert_called_once_with(
osc_tap_flow.resource_path % ('tap_flows',
fake_tap_flow['id']))
self.assertEqual(sorted_headers, headers)
self.assertItemEqual(_get_data(fake_tap_flow), data)
class TestUpdateTapFlow(test_fakes.TestNeutronClientOSCV2):
_new_name = 'new_name'
columns = (
'Direction',
'ID',
'Name',
'Status',
'Tenant',
'source_port',
'tap_service_id',
)
def setUp(self):
super(TestUpdateTapFlow, self).setUp()
self.cmd = osc_tap_flow.UpdateTapFlow(self.app, self.namespace)
self.neutronclient.find_resource = mock.Mock(
side_effect=lambda _, name_or_id: {'id': name_or_id})
def test_update_tap_flow(self):
"""Test update tap service"""
fake_tap_flow = fakes.FakeTapFlow.create_tap_flow(
attrs={
'source_port': uuidutils.generate_uuid(),
'tap_service_id': uuidutils.generate_uuid(),
}
)
new_tap_flow = copy.deepcopy(fake_tap_flow)
new_tap_flow['name'] = self._new_name
self.neutronclient.put = mock.Mock(
return_value={osc_tap_flow.TAP_FLOW: new_tap_flow})
arg_list = [
fake_tap_flow['id'],
'--name', self._new_name,
]
verify_list = [('name', self._new_name)]
parsed_args = self.check_parser(self.cmd, arg_list, verify_list)
columns, data = self.cmd.take_action(parsed_args)
attrs = {'name': self._new_name}
self.neutronclient.put.assert_called_once_with(
osc_tap_flow.resource_path % ('tap_flows',
new_tap_flow['id']),
{osc_tap_flow.TAP_FLOW: attrs})
self.assertEqual(self.columns, columns)
self.assertItemEqual(_get_data(new_tap_flow), data)

View File

@ -0,0 +1,229 @@
# All Rights Reserved 2020
#
# 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 copy
import operator
from unittest import mock
from neutronclient.tests.unit.osc.v2 import fakes as test_fakes
from osc_lib import utils as osc_utils
from osc_lib.utils import columns as column_util
from oslo_utils import uuidutils
from neutron_taas.taas_client.osc import tap_service as osc_tap_service
from neutron_taas.tests.unit.taas_client.osc import fakes
columns_long = tuple(col for col, _, listing_mode in osc_tap_service._attr_map
if listing_mode in (column_util.LIST_BOTH,
column_util.LIST_LONG_ONLY))
headers_long = tuple(head for _, head, listing_mode in
osc_tap_service._attr_map if listing_mode in
(column_util.LIST_BOTH, column_util.LIST_LONG_ONLY))
sorted_attr_map = sorted(osc_tap_service._attr_map, key=operator.itemgetter(1))
sorted_columns = tuple(col for col, _, _ in sorted_attr_map)
sorted_headers = tuple(head for _, head, _ in sorted_attr_map)
def _get_data(attrs, columns=sorted_columns):
return osc_utils.get_dict_properties(attrs, columns)
class TestCreateTapService(test_fakes.TestNeutronClientOSCV2):
columns = (
'ID',
'Name',
'Port',
'Status',
'Tenant',
)
def setUp(self):
super(TestCreateTapService, self).setUp()
self.cmd = osc_tap_service.CreateTapService(self.app, self.namespace)
def test_create_tap_service(self):
"""Test Create Tap Service."""
fake_tap_service = fakes.FakeTapService.create_tap_service(
attrs={'port_id': uuidutils.generate_uuid()}
)
self.neutronclient.post = mock.Mock(
return_value={osc_tap_service.TAP_SERVICE: fake_tap_service})
arg_list = [
'--name', fake_tap_service['name'],
'--port', fake_tap_service['port_id'],
]
verify_list = [
('name', fake_tap_service['name']),
('port_id', fake_tap_service['port_id']),
]
parsed_args = self.check_parser(self.cmd, arg_list, verify_list)
self.neutronclient.find_resource = mock.Mock(
return_value={'id': fake_tap_service['port_id']})
columns, data = self.cmd.take_action(parsed_args)
self.neutronclient.post.assert_called_once_with(
'/taas/tap_services',
body={
osc_tap_service.TAP_SERVICE:
{
'name': fake_tap_service['name'],
'port_id': fake_tap_service['port_id']
}
}
)
self.assertEqual(self.columns, columns)
self.assertItemEqual(_get_data(fake_tap_service), data)
class TestListTapService(test_fakes.TestNeutronClientOSCV2):
def setUp(self):
super(TestListTapService, self).setUp()
self.cmd = osc_tap_service.ListTapService(self.app, self.namespace)
def test_list_tap_service(self):
"""Test List Tap Service."""
fake_tap_services = fakes.FakeTapService.create_tap_services(
attrs={'port_id': uuidutils.generate_uuid()},
count=4)
self.neutronclient.list = mock.Mock(return_value=fake_tap_services)
arg_list = []
verify_list = []
parsed_args = self.check_parser(self.cmd, arg_list, verify_list)
headers, data = self.cmd.take_action(parsed_args)
self.neutronclient.list.assert_called_once()
self.assertEqual(headers, list(headers_long))
self.assertListItemEqual(
list(data),
[_get_data(fake_tap_service, columns_long) for fake_tap_service
in fake_tap_services[osc_tap_service.TAP_SERVICES]]
)
class TestDeleteTapService(test_fakes.TestNeutronClientOSCV2):
def setUp(self):
super(TestDeleteTapService, self).setUp()
self.neutronclient.find_resource = mock.Mock(
side_effect=lambda _, name_or_id: {'id': name_or_id})
self.cmd = osc_tap_service.DeleteTapService(self.app, self.namespace)
def test_delete_tap_service(self):
"""Test Delete tap service."""
fake_tap_service = fakes.FakeTapService.create_tap_service(
attrs={'port_id': uuidutils.generate_uuid()}
)
self.neutronclient.delete = mock.Mock()
arg_list = [
fake_tap_service['id'],
]
verify_list = [
(osc_tap_service.TAP_SERVICE, [fake_tap_service['id']]),
]
parsed_args = self.check_parser(self.cmd, arg_list, verify_list)
result = self.cmd.take_action(parsed_args)
self.neutronclient.delete.assert_called_once_with(
osc_tap_service.resource_path % ('tap_services',
fake_tap_service['id']))
self.assertIsNone(result)
class TestShowTapService(test_fakes.TestNeutronClientOSCV2):
def setUp(self):
super(TestShowTapService, self).setUp()
self.neutronclient.find_resource = mock.Mock(
side_effect=lambda _, name_or_id: {'id': name_or_id})
self.cmd = osc_tap_service.ShowTapService(self.app, self.namespace)
def test_show_tap_service(self):
"""Test Show tap service."""
fake_tap_service = fakes.FakeTapService.create_tap_service(
attrs={'port_id': uuidutils.generate_uuid()}
)
self.neutronclient.get = mock.Mock(
return_value={osc_tap_service.TAP_SERVICE: fake_tap_service})
arg_list = [
fake_tap_service['id'],
]
verify_list = [
(osc_tap_service.TAP_SERVICE, fake_tap_service['id']),
]
parsed_args = self.check_parser(self.cmd, arg_list, verify_list)
headers, data = self.cmd.take_action(parsed_args)
self.neutronclient.get.assert_called_once_with(
osc_tap_service.resource_path % ('tap_services',
fake_tap_service['id']))
self.assertEqual(sorted_headers, headers)
self.assertItemEqual(_get_data(fake_tap_service), data)
class TestUpdateTapService(test_fakes.TestNeutronClientOSCV2):
_new_name = 'new_name'
columns = (
'ID',
'Name',
'Port',
'Status',
'Tenant',
)
def setUp(self):
super(TestUpdateTapService, self).setUp()
self.cmd = osc_tap_service.UpdateTapService(self.app, self.namespace)
self.neutronclient.find_resource = mock.Mock(
side_effect=lambda _, name_or_id: {'id': name_or_id})
def test_update_tap_service(self):
"""Test update tap service"""
fake_tap_service = fakes.FakeTapService.create_tap_service(
attrs={'port_id': uuidutils.generate_uuid()}
)
new_tap_service = copy.deepcopy(fake_tap_service)
new_tap_service['name'] = self._new_name
self.neutronclient.put = mock.Mock(
return_value={osc_tap_service.TAP_SERVICE: new_tap_service})
arg_list = [
fake_tap_service['id'],
'--name', self._new_name,
]
verify_list = [('name', self._new_name)]
parsed_args = self.check_parser(self.cmd, arg_list, verify_list)
columns, data = self.cmd.take_action(parsed_args)
attrs = {'name': self._new_name}
self.neutronclient.put.assert_called_once_with(
osc_tap_service.resource_path % ('tap_services',
new_tap_service['id']),
{osc_tap_service.TAP_SERVICE: attrs})
self.assertEqual(self.columns, columns)
self.assertItemEqual(_get_data(new_tap_service), data)

View File

@ -76,6 +76,17 @@ oslo.policy.policies =
tap-as-a-service = neutron_taas.policies:list_rules
neutron.policies =
tap-as-a-service = neutron_taas.policies:list_rules
openstack.neutronclient.v2 =
tap_service_create = neutron_taas.taas_client.osc.tap_service:CreateTapService
tap_service_list = neutron_taas.taas_client.osc.tap_service:ListTapService
tap_service_show = neutron_taas.taas_client.osc.tap_service:ShowTapService
tap_service_delete = neutron_taas.taas_client.osc.tap_service:DeleteTapService
tap_service_update = neutron_taas.taas_client.osc.tap_service:UpdateTapService
tap_flow_create = neutron_taas.taas_client.osc.tap_flow:CreateTapFlow
tap_flow_list = neutron_taas.taas_client.osc.tap_flow:ListTapFlow
tap_flow_show = neutron_taas.taas_client.osc.tap_flow:ShowTapFlow
tap_flow_delete = neutron_taas.taas_client.osc.tap_flow:DeleteTapFlow
tap_flow_update = neutron_taas.taas_client.osc.tap_flow:UpdateTapFlow
[pbr]

View File

@ -9,6 +9,7 @@ python-subunit>=1.4.0 # Apache-2.0/BSD
psycopg2>=2.8.5 # LGPL/ZPL
PyMySQL>=0.10.0 # MIT License
oslotest>=4.4.1 # Apache-2.0
requests_mock>=1.5.0 # Apache-2.0
stestr>=3.0.1 # Apache-2.0
testresources>=2.0.1 # Apache-2.0/BSD
testscenarios>=0.5.0 # Apache-2.0/BSD