From f269444ccc3f30d83c03283847bbbb4ba7dc82e7 Mon Sep 17 00:00:00 2001 From: Sumit Naiksatam Date: Tue, 21 Oct 2014 15:40:40 -0700 Subject: [PATCH] GBP ServiceChain CLI - Initial implementation Implements GroupPolicy ServiceChain CLI. Provides CRUD commands for ServiceChainNode, ServiceChainSpec and ServiceChainInstance APIs. Change-Id: I19d4cea8516e07a78121edec5707d7f8bd38b5d0 --- gbpclient/gbp/v2_0/servicechain.py | 314 ++++++++++++++++++ gbpclient/gbpshell.py | 26 ++ .../unit/test_cli20_servicechain_instance.py | 145 ++++++++ .../unit/test_cli20_servicechain_node.py | 156 +++++++++ .../unit/test_cli20_servicechain_spec.py | 144 ++++++++ gbpclient/v2_0/client.py | 98 ++++++ requirements.txt | 2 +- 7 files changed, 884 insertions(+), 1 deletion(-) create mode 100644 gbpclient/gbp/v2_0/servicechain.py create mode 100644 gbpclient/tests/unit/test_cli20_servicechain_instance.py create mode 100644 gbpclient/tests/unit/test_cli20_servicechain_node.py create mode 100644 gbpclient/tests/unit/test_cli20_servicechain_spec.py diff --git a/gbpclient/gbp/v2_0/servicechain.py b/gbpclient/gbp/v2_0/servicechain.py new file mode 100644 index 0000000..5986ad7 --- /dev/null +++ b/gbpclient/gbp/v2_0/servicechain.py @@ -0,0 +1,314 @@ +# Copyright 2012 OpenStack Foundation. +# All Rights Reserved +# +# 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 json +import logging +import os +import string + +from heatclient.common import template_utils + +from neutronclient.neutron import v2_0 as neutronV20 +from neutronclient.openstack.common.gettextutils import _ + + +class ListServiceChainInstance(neutronV20.ListCommand): + """List service chain instances that belong to a given tenant.""" + + resource = 'servicechain_instance' + log = logging.getLogger(__name__ + '.ListServiceChainInstance') + _formatters = {} + list_columns = ['id', 'name', 'description', 'servicechain_spec', 'port'] + pagination_support = True + sorting_support = True + + +class ShowServiceChainInstance(neutronV20.ShowCommand): + """Show information of a given service chain instance.""" + + resource = 'servicechain_instance' + log = logging.getLogger(__name__ + '.ShowServiceChainInstance') + + +class CreateServiceChainInstance(neutronV20.CreateCommand): + """Create a service chain instance.""" + + resource = 'servicechain_instance' + log = logging.getLogger(__name__ + '.CreateServiceChainInstance') + + def add_known_arguments(self, parser): + parser.add_argument( + 'name', + help=_('Name for the Service Chain Instance.')) + parser.add_argument( + '--description', + help=_('Description of the Service Chain Instance.')) + parser.add_argument( + '--service-chain-spec', dest='servicechain_spec', + help=_('Service Chain Spec ID or the Service Chain Spec name')) + parser.add_argument( + '--provider-epg', dest='provider_epg', + help=_('Destination Policy Target Group ID of the Provider.')) + parser.add_argument( + '--consumer-epg', dest='consumer_epg', + help=_('Source Policy Target Group ID of the Consumer.')) + parser.add_argument( + '--param-values', dest='param_values', + help=_('Name,Value pairs of Service Configuration Parameters for ' + 'Service Chain Node.')) + + def args2body(self, parsed_args): + body = {self.resource: {}, } + if parsed_args.servicechain_spec: + body[self.resource]['servicechain_spec'] = \ + neutronV20.find_resourceid_by_name_or_id( + self.get_client(), 'servicechain_spec', + parsed_args.servicechain_spec) + if parsed_args.provider_epg: + body[self.resource]['provider_epg'] = \ + neutronV20.find_resourceid_by_name_or_id( + self.get_client(), 'endpoint_group', + parsed_args.provider_epg) + if parsed_args.consumer_epg: + body[self.resource]['consumer_epg'] = \ + neutronV20.find_resourceid_by_name_or_id( + self.get_client(), 'endpoint_group', + parsed_args.consumer_epg) + neutronV20.update_dict(parsed_args, body[self.resource], + ['name', 'tenant_id', 'description', + 'servicechain_spec', 'provider_epg', + 'consumer_epg', 'param_values']) + return body + + +class UpdateServiceChainInstance(neutronV20.UpdateCommand): + """Update a given service chain instance.""" + + resource = 'servicechain_instance' + log = logging.getLogger(__name__ + '.UpdateServiceChainInstance') + + def add_known_arguments(self, parser): + parser.add_argument( + '--service-chain-spec', dest='servicechain_spec', + help=_('Service Chain Spec ID or the Service Chain Spec name')) + parser.add_argument( + '--provider-epg', dest='provider_epg', + help=_('Destination Policy Target Group ID of the Provider.')) + parser.add_argument( + '--consumer-epg', dest='consumer_epg', + help=_('Source Policy Target Group ID of the Consumer.')) + parser.add_argument( + '--param-values', dest='param_values', + help=_('Name,Value pairs of Service Configuration Parameters for ' + 'Service Chain Node.')) + + def args2body(self, parsed_args): + body = {self.resource: {}, } + if parsed_args.servicechain_spec: + body[self.resource]['servicechain_spec'] = \ + neutronV20.find_resourceid_by_name_or_id( + self.get_client(), 'servicechain_spec', + parsed_args.servicechain_spec) + if parsed_args.provider_epg: + body[self.resource]['provider_epg'] = \ + neutronV20.find_resourceid_by_name_or_id( + self.get_client(), 'endpoint_group', + parsed_args.provider_epg) + if parsed_args.consumer_epg: + body[self.resource]['consumer_epg'] = \ + neutronV20.find_resourceid_by_name_or_id( + self.get_client(), 'endpoint_group', + parsed_args.consumer_epg) + neutronV20.update_dict(parsed_args, body[self.resource], + ['name', 'description', + 'servicechain_spec', 'provider_epg', + 'consumer_epg', 'param_values']) + return body + + +class DeleteServiceChainInstance(neutronV20.DeleteCommand): + """Delete a given service chain instance.""" + + resource = 'servicechain_instance' + log = logging.getLogger(__name__ + '.DeleteServiceChainInstance') + + +class ListServiceChainNode(neutronV20.ListCommand): + """List service chain nodes that belong to a given tenant.""" + + resource = 'servicechain_node' + log = logging.getLogger(__name__ + '.ListServiceChainNode') + _formatters = {} + list_columns = ['id', 'name', 'description', 'service_type'] + pagination_support = True + sorting_support = True + + +class ShowServiceChainNode(neutronV20.ShowCommand): + """Show information of a given service chain node.""" + + resource = 'servicechain_node' + log = logging.getLogger(__name__ + '.ShowServiceChainNode') + + +class CreateServiceChainNode(neutronV20.CreateCommand): + """Create a service chain node.""" + + resource = 'servicechain_node' + log = logging.getLogger(__name__ + '.CreateServiceChainNode') + + def add_known_arguments(self, parser): + parser.add_argument( + 'name', + help=_('Name for the Service Chain Node.')) + parser.add_argument( + '--description', + help=_('Description of the Service Chain Node.')) + parser.add_argument( + '--servicetype', dest='service_type', + help=_('Service type ID or the Service Type name')) + parser.add_argument( + '--config', + help=_('Service Configuration for the Service Chain Node.')) + parser.add_argument( + '--template-file', + help=_('Service Configuration Template for the Service Chain ' + 'Node.')) + parser.add_argument( + '--param-names', dest='param_names', + help=_('List of Configuration Parameter Names for Service ' + 'Chain Node.')) + + def args2body(self, parsed_args): + body = {self.resource: {}, } + if parsed_args.template_file: + if os.path.isfile(parsed_args.template_file): + tpl_files, template = template_utils.get_template_contents( + parsed_args.template_file) + parsed_args.config = json.dumps(template) + neutronV20.update_dict(parsed_args, body[self.resource], + ['name', 'service_type', 'config', + 'tenant_id', 'param_names', 'description']) + return body + + +class UpdateServiceChainNode(neutronV20.UpdateCommand): + """Update a given service chain node.""" + + resource = 'servicechain_node' + log = logging.getLogger(__name__ + '.UpdateServiceChainNode') + + def add_known_arguments(self, parser): + parser.add_argument( + '--servicetype', dest='service_type', + help=_('Service type ID or the Service Type name')) + parser.add_argument( + '--config', + help=_('Service Configuration for the Service Chain Node.')) + + def args2body(self, parsed_args): + body = {self.resource: {}, } + neutronV20.update_dict(parsed_args, body[self.resource], + ['name', 'service_type', 'config', + 'tenant_id', 'description']) + return body + + +class DeleteServiceChainNode(neutronV20.DeleteCommand): + """Delete a given service chain node.""" + + resource = 'servicechain_node' + log = logging.getLogger(__name__ + '.DeleteServiceChainNode') + + +class ListServiceChainSpec(neutronV20.ListCommand): + """List service chain specs that belong to a given tenant.""" + + resource = 'servicechain_spec' + log = logging.getLogger(__name__ + '.ListServiceChainSpec') + _formatters = {} + list_columns = ['id', 'name', 'description', 'nodes'] + pagination_support = True + sorting_support = True + + +class ShowServiceChainSpec(neutronV20.ShowCommand): + """Show information of a given service chain spec.""" + + resource = 'servicechain_spec' + log = logging.getLogger(__name__ + '.ShowServiceChainSpec') + + +class CreateServiceChainSpec(neutronV20.CreateCommand): + """Create a service chain spec.""" + + resource = 'servicechain_spec' + log = logging.getLogger(__name__ + '.CreateServiceChainSpec') + + def add_known_arguments(self, parser): + parser.add_argument( + 'name', + help=_('Name for the Service Chain Spec.')) + parser.add_argument( + '--description', + help=_('Description of the Service Chain Specification.')) + parser.add_argument( + '--nodes', metavar='NODES', type=string.split, + help=_('Service Chain Node ID or name of the Service Chain Node')) + + def args2body(self, parsed_args): + body = {self.resource: {}, } + + if parsed_args.nodes: + body[self.resource]['nodes'] = [ + neutronV20.find_resourceid_by_name_or_id( + self.get_client(), + 'servicechain_node', + elem) for elem in parsed_args.nodes] + + neutronV20.update_dict(parsed_args, body[self.resource], + ['name', 'tenant_id', 'description']) + return body + + +class UpdateServiceChainSpec(neutronV20.UpdateCommand): + """Update a given service chain spec.""" + + resource = 'servicechain_spec' + log = logging.getLogger(__name__ + '.UpdateServiceChainSpec') + + def add_known_arguments(self, parser): + parser.add_argument( + '--nodes', type=string.split, + help=_('List of Service Chain Node IDs or names of the Service ' + 'Chain Nodes')) + + def args2body(self, parsed_args): + body = {self.resource: {}, } + if parsed_args.nodes: + body[self.resource]['nodes'] = [ + neutronV20.find_resourceid_by_name_or_id( + self.get_client(), + 'servicechain_node', + elem) for elem in parsed_args.nodes] + return body + + +class DeleteServiceChainSpec(neutronV20.DeleteCommand): + """Delete a given service chain spec.""" + + resource = 'servicechain_spec' + log = logging.getLogger(__name__ + '.DeleteServiceChainSpec') diff --git a/gbpclient/gbpshell.py b/gbpclient/gbpshell.py index 64c733e..755c060 100644 --- a/gbpclient/gbpshell.py +++ b/gbpclient/gbpshell.py @@ -39,6 +39,7 @@ from neutronclient.openstack.common import strutils from neutronclient.version import __version__ from gbpclient.gbp.v2_0 import groupbasedpolicy as gbp +from gbpclient.gbp.v2_0 import servicechain VERSION = '2.0' NEUTRON_API_VERSION = '2.0' @@ -131,6 +132,31 @@ COMMAND_V2 = { 'policy-rule-set-update': gbp.UpdateContract, 'policy-rule-set-list': gbp.ListContract, 'policy-rule-set-show': gbp.ShowContract, + 'servicechain-node-list': servicechain.ListServiceChainNode, + 'servicechain-node-show': servicechain.ShowServiceChainNode, + 'servicechain-node-create': servicechain.CreateServiceChainNode, + 'servicechain-node-delete': servicechain.DeleteServiceChainNode, + 'servicechain-node-update': servicechain.UpdateServiceChainNode, + 'servicechain-spec-list': servicechain.ListServiceChainSpec, + 'servicechain-spec-show': servicechain.ShowServiceChainSpec, + 'servicechain-spec-create': servicechain.CreateServiceChainSpec, + 'servicechain-spec-delete': servicechain.DeleteServiceChainSpec, + 'servicechain-spec-update': servicechain.UpdateServiceChainSpec, + 'servicechain-instance-list': ( + servicechain.ListServiceChainInstance + ), + 'servicechain-instance-show': ( + servicechain.ShowServiceChainInstance + ), + 'servicechain-instance-create': ( + servicechain.CreateServiceChainInstance + ), + 'servicechain-instance-delete': ( + servicechain.DeleteServiceChainInstance + ), + 'servicechain-instance-update': ( + servicechain.UpdateServiceChainInstance + ), } COMMANDS = {'2.0': COMMAND_V2} diff --git a/gbpclient/tests/unit/test_cli20_servicechain_instance.py b/gbpclient/tests/unit/test_cli20_servicechain_instance.py new file mode 100644 index 0000000..8448047 --- /dev/null +++ b/gbpclient/tests/unit/test_cli20_servicechain_instance.py @@ -0,0 +1,145 @@ +# Copyright 2012 OpenStack Foundation. +# All Rights Reserved +# +# 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 sys + +from gbpclient.gbp.v2_0 import servicechain as sc +from gbpclient.tests.unit import test_cli20 + + +class CLITestV20ServiceChainInstanceJSON(test_cli20.CLITestV20Base): + def setUp(self): + super(CLITestV20ServiceChainInstanceJSON, self).setUp() + + def test_create_servicechain_instance_with_mandatory_params(self): + """service-chain-instance-create with all mandatory params.""" + resource = 'servicechain_instance' + cmd = sc.CreateServiceChainInstance(test_cli20.MyApp(sys.stdout), None) + name = 'my-name' + tenant_id = 'my-tenant' + my_id = 'my-id' + args = ['--tenant-id', tenant_id, name] + position_names = ['name', ] + position_values = [name, ] + self._test_create_resource(resource, cmd, name, my_id, args, + position_names, position_values, + tenant_id=tenant_id) + + def test_create_servicechain_instance_with_all_params(self): + """service-chain-instance-create with all params.""" + resource = 'servicechain_instance' + cmd = sc.CreateServiceChainInstance(test_cli20.MyApp(sys.stdout), None) + name = 'my-name' + servicechain_spec_id = 'service-chain-spec-id' + tenant_id = 'my-tenant' + description = 'My Service Chain Instance' + my_id = 'my-id' + config_params = 'config' + args = ['--service-chain-spec', servicechain_spec_id, + '--tenant-id', tenant_id, + '--param-values', config_params, + '--description', description, + name] + position_names = ['name', ] + position_values = [name, ] + self._test_create_resource(resource, cmd, name, my_id, args, + position_names, position_values, + servicechain_spec=servicechain_spec_id, + tenant_id=tenant_id, + param_values=config_params, + description=description) + + def test_list_servicechain_instances(self): + """service-chain-instance-list.""" + resources = 'servicechain_instances' + cmd = sc.ListServiceChainInstance(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, True) + + def test_list_servicechain_instances_pagination(self): + """service-chain-instance-list.""" + resources = 'servicechain_instances' + cmd = sc.ListServiceChainInstance(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources_with_pagination(resources, cmd) + + def test_list_servicechain_instances_sort(self): + """service-chain-instance-list --sort-key name --sort-key id + --sort-key asc --sort-key desc + """ + resources = 'servicechain_instances' + cmd = sc.ListServiceChainInstance(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, + sort_key=["name", "id"], + sort_dir=["asc", "desc"]) + + def test_list_servicechain_instances_limit(self): + """service-chain-instance-list -P.""" + resources = 'servicechain_instances' + cmd = sc.ListServiceChainInstance(test_cli20.MyApp(sys.stdout), None) + self._test_list_resources(resources, cmd, page_size=1000) + + def test_show_servicechain_instance_id(self): + """service-chain-instance-show test_id.""" + resource = 'servicechain_instance' + cmd = sc.ShowServiceChainInstance(test_cli20.MyApp(sys.stdout), None) + args = ['--fields', 'id', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, args, ['id']) + + def test_show_servicechain_instance_id_name(self): + """service-chain-instance-show.""" + resource = 'servicechain_instance' + cmd = sc.ShowServiceChainInstance(test_cli20.MyApp(sys.stdout), None) + args = ['--fields', 'id', '--fields', 'name', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, + args, ['id', 'name']) + + def test_update_servicechain_instance(self): + """service-chain-instance-update myid --name myname --tags a b.""" + resource = 'servicechain_instance' + cmd = sc.UpdateServiceChainInstance(test_cli20.MyApp(sys.stdout), None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--name', 'myname', + '--tags', 'a', 'b'], + {'name': 'myname', 'tags': ['a', 'b'], }) + + def test_update_servicechain_instance_with_chainspec(self): + resource = 'servicechain_instance' + cmd = sc.UpdateServiceChainInstance(test_cli20.MyApp(sys.stdout), None) + body = { + 'servicechain_spec': 'my-spec-id' + } + args = ['myid', '--service-chain-spec', 'my-spec-id'] + self._test_update_resource(resource, cmd, 'myid', args, body) + + def test_update_servicechain_instance_with_chainspec_and_port(self): + resource = 'servicechain_instance' + cmd = sc.UpdateServiceChainInstance(test_cli20.MyApp(sys.stdout), None) + body = { + 'name': 'newname', + 'servicechain_spec': 'my-spec-id', + 'port': 'my-port-id' + } + args = ['myid', '--name', 'newname', + '--service-chain-spec', 'my-spec-id', + '--port', 'my-port-id'] + self._test_update_resource(resource, cmd, 'myid', args, body) + + def test_delete_servicechain_instance(self): + """service-chain-instance-delete my-id.""" + resource = 'servicechain_instance' + cmd = sc.DeleteServiceChainInstance(test_cli20.MyApp(sys.stdout), None) + my_id = 'my-id' + args = [my_id] + self._test_delete_resource(resource, cmd, my_id, args) diff --git a/gbpclient/tests/unit/test_cli20_servicechain_node.py b/gbpclient/tests/unit/test_cli20_servicechain_node.py new file mode 100644 index 0000000..b10dda6 --- /dev/null +++ b/gbpclient/tests/unit/test_cli20_servicechain_node.py @@ -0,0 +1,156 @@ +# Copyright 2012 OpenStack Foundation. +# All Rights Reserved +# +# 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 sys + +from gbpclient.gbp.v2_0 import servicechain +from gbpclient.tests.unit import test_cli20 + + +class CLITestV20ServiceChainNodeJSON(test_cli20.CLITestV20Base): + def setUp(self): + super(CLITestV20ServiceChainNodeJSON, self).setUp() + + def test_create_servicechain_node_with_mandatory_params(self): + """service-chain-node-create with all mandatory params.""" + resource = 'servicechain_node' + cmd = servicechain.CreateServiceChainNode(test_cli20.MyApp(sys.stdout), + None) + name = 'my-name' + tenant_id = 'my-tenant' + my_id = 'my-id' + args = ['--tenant-id', tenant_id, name] + position_names = ['name', ] + position_values = [name, ] + self._test_create_resource(resource, cmd, name, my_id, args, + position_names, position_values, + tenant_id=tenant_id) + + def test_create_servicechain_node_with_all_params(self): + """service-chain-node-create with all params.""" + resource = 'servicechain_node' + cmd = servicechain.CreateServiceChainNode(test_cli20.MyApp(sys.stdout), + None) + name = 'my-name' + service_type = 'servicetype1' + config = 'config1' + tenant_id = 'my-tenant' + description = 'My Service Chain Node' + my_id = 'my-id' + args = ['--servicetype', service_type, + '--config', config, + '--tenant-id', tenant_id, + '--description', description, + name] + position_names = ['name', ] + position_values = [name, ] + self._test_create_resource(resource, cmd, name, my_id, args, + position_names, position_values, + service_type=service_type, config=config, + tenant_id=tenant_id, + description=description) + + def test_list_servicechain_nodes(self): + """service-chain-node-list.""" + resources = 'servicechain_nodes' + cmd = servicechain.ListServiceChainNode(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(resources, cmd, True) + + def test_list_servicechain_nodes_pagination(self): + """service-chain-node-list.""" + resources = 'servicechain_nodes' + cmd = servicechain.ListServiceChainNode(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources_with_pagination(resources, cmd) + + def test_list_servicechain_nodes_sort(self): + """service-chain-node-list --sort-key name --sort-key id --sort-key asc + --sort-key desc + """ + resources = 'servicechain_nodes' + cmd = servicechain.ListServiceChainNode(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(resources, cmd, + sort_key=["name", "id"], + sort_dir=["asc", "desc"]) + + def test_list_servicechain_nodes_limit(self): + """service-chain-node-list -P.""" + resources = 'servicechain_nodes' + cmd = servicechain.ListServiceChainNode(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(resources, cmd, page_size=1000) + + def test_show_servicechain_node_id(self): + """service-chain-node-show test_id.""" + resource = 'servicechain_node' + cmd = servicechain.ShowServiceChainNode(test_cli20.MyApp(sys.stdout), + None) + args = ['--fields', 'id', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, args, ['id']) + + def test_show_servicechain_node_id_name(self): + """service-chain-node-show.""" + resource = 'servicechain_node' + cmd = servicechain.ShowServiceChainNode(test_cli20.MyApp(sys.stdout), + None) + args = ['--fields', 'id', '--fields', 'name', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, + args, ['id', 'name']) + + def test_update_servicechain_node(self): + """service-chain-node-update myid --name myname --tags a b.""" + resource = 'servicechain_node' + cmd = servicechain.UpdateServiceChainNode(test_cli20.MyApp(sys.stdout), + None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--name', 'myname', + '--tags', 'a', 'b'], + {'name': 'myname', 'tags': ['a', 'b'], }) + + def test_update_servicechain_node_with_servicetype(self): + resource = 'servicechain_node' + cmd = servicechain.UpdateServiceChainNode(test_cli20.MyApp(sys.stdout), + None) + body = { + 'service_type': 'service_type1' + } + args = ['myid', '--servicetype', 'service_type1'] + self._test_update_resource(resource, cmd, 'myid', args, body) + + def test_update_servicechain_node_with_type_and_config(self): + resource = 'servicechain_node' + cmd = servicechain.UpdateServiceChainNode(test_cli20.MyApp(sys.stdout), + None) + body = { + 'name': 'newname', + 'service_type': 'service_type1', + 'config': 'config1', + } + args = ['myid', '--name', 'newname', + '--servicetype', 'service_type1', + '--config', 'config1'] + self._test_update_resource(resource, cmd, 'myid', args, body) + + def test_delete_servicechain_node(self): + """service-chain-node-delete my-id.""" + resource = 'servicechain_node' + cmd = servicechain.DeleteServiceChainNode(test_cli20.MyApp(sys.stdout), + None) + my_id = 'my-id' + args = [my_id] + self._test_delete_resource(resource, cmd, my_id, args) diff --git a/gbpclient/tests/unit/test_cli20_servicechain_spec.py b/gbpclient/tests/unit/test_cli20_servicechain_spec.py new file mode 100644 index 0000000..f08f05a --- /dev/null +++ b/gbpclient/tests/unit/test_cli20_servicechain_spec.py @@ -0,0 +1,144 @@ +# Copyright 2012 OpenStack Foundation. +# All Rights Reserved +# +# 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 sys + +from gbpclient.gbp.v2_0 import servicechain +from gbpclient.tests.unit import test_cli20 + + +class CLITestV20ServiceChainSpecJSON(test_cli20.CLITestV20Base): + def setUp(self): + super(CLITestV20ServiceChainSpecJSON, self).setUp() + + def test_create_servicechain_spec_with_mandatory_params(self): + """service-chain-spec-create with all mandatory params.""" + resource = 'servicechain_spec' + cmd = servicechain.CreateServiceChainSpec(test_cli20.MyApp(sys.stdout), + None) + name = 'my-name' + tenant_id = 'my-tenant' + my_id = 'my-id' + args = ['--tenant-id', tenant_id, name] + position_names = ['name', ] + position_values = [name, ] + self._test_create_resource(resource, cmd, name, my_id, args, + position_names, position_values, + tenant_id=tenant_id) + + def test_create_servicechain_spec_with_all_params(self): + """service-chain-spec-create with all params.""" + resource = 'servicechain_spec' + cmd = servicechain.CreateServiceChainSpec(test_cli20.MyApp(sys.stdout), + None) + name = 'my-name' + nodes_arg = 'node1 node2' + nodes_res = ['node1', 'node2'] + tenant_id = 'my-tenant' + description = 'My Service Chain Spec' + my_id = 'my-id' + args = ['--nodes', nodes_arg, + '--tenant-id', tenant_id, + '--description', description, + name] + position_names = ['name', ] + position_values = [name, ] + self._test_create_resource(resource, cmd, name, my_id, args, + position_names, position_values, + nodes=nodes_res, tenant_id=tenant_id, + description=description) + + def test_list_servicechain_specs(self): + """service-chain-spec-list.""" + resources = 'servicechain_specs' + cmd = servicechain.ListServiceChainSpec(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(resources, cmd, True) + + def test_list_servicechain_specs_pagination(self): + """service-chain-spec-list.""" + resources = 'servicechain_specs' + cmd = servicechain.ListServiceChainSpec(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources_with_pagination(resources, cmd) + + def test_list_servicechain_specs_sort(self): + """service-chain-spec-list --sort-key name --sort-key id --sort-key asc + --sort-key desc + """ + resources = 'servicechain_specs' + cmd = servicechain.ListServiceChainSpec(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(resources, cmd, + sort_key=["name", "id"], + sort_dir=["asc", "desc"]) + + def test_list_servicechain_specs_limit(self): + """service-chain-spec-list -P.""" + resources = 'servicechain_specs' + cmd = servicechain.ListServiceChainSpec(test_cli20.MyApp(sys.stdout), + None) + self._test_list_resources(resources, cmd, page_size=1000) + + def test_show_servicechain_spec_id(self): + """service-chain-spec-show test_id.""" + resource = 'servicechain_spec' + cmd = servicechain.ShowServiceChainSpec(test_cli20.MyApp(sys.stdout), + None) + args = ['--fields', 'id', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, args, ['id']) + + def test_show_servicechain_spec_id_name(self): + """service-chain-spec-show.""" + resource = 'servicechain_spec' + cmd = servicechain.ShowServiceChainSpec(test_cli20.MyApp(sys.stdout), + None) + args = ['--fields', 'id', '--fields', 'name', self.test_id] + self._test_show_resource(resource, cmd, self.test_id, + args, ['id', 'name']) + + def test_update_servicechain_spec(self): + """service-chain-spec-update myid --name myname --tags a b.""" + resource = 'servicechain_spec' + cmd = servicechain.UpdateServiceChainSpec(test_cli20.MyApp(sys.stdout), + None) + self._test_update_resource(resource, cmd, 'myid', + ['myid', '--name', 'myname', + '--tags', 'a', 'b'], + {'name': 'myname', 'tags': ['a', 'b'], }) + + def test_update_servicechain_spec_with_nodes(self): + resource = 'servicechain_spec' + cmd = servicechain.UpdateServiceChainSpec(test_cli20.MyApp(sys.stdout), + None) + nodes_arg = 'node1 node2' + nodes_res = ['node1', 'node2'] + description = 'My Service Chain Spec' + body = { + 'nodes': nodes_res, + 'description': description + } + args = ['myid', '--nodes', nodes_arg, '--description', description] + self._test_update_resource(resource, cmd, 'myid', args, body) + + def test_delete_servicechain_spec(self): + """service-chain-spec-delete my-id.""" + resource = 'servicechain_spec' + cmd = servicechain.DeleteServiceChainSpec(test_cli20.MyApp(sys.stdout), + None) + my_id = 'my-id' + args = [my_id] + self._test_delete_resource(resource, cmd, my_id, args) diff --git a/gbpclient/v2_0/client.py b/gbpclient/v2_0/client.py index 9836bf7..f9b6f1e 100644 --- a/gbpclient/v2_0/client.py +++ b/gbpclient/v2_0/client.py @@ -165,6 +165,12 @@ class Client(object): policy_rule_path = "/grouppolicy/policy_rules/%s" contracts_path = "/grouppolicy/contracts" contract_path = "/grouppolicy/contracts/%s" + servicechain_nodes_path = "/servicechain/servicechain_nodes" + servicechain_node_path = "/servicechain/servicechain_nodes/%s" + servicechain_specs_path = "/servicechain/servicechain_specs" + servicechain_spec_path = "/servicechain/servicechain_specs/%s" + servicechain_instances_path = "/servicechain/servicechain_instances" + servicechain_instance_path = "/servicechain/servicechain_instances/%s" # API has no way to report plurals, so we have to hard code them EXTED_PLURALS = {'endpoints': 'endpoint', @@ -460,6 +466,98 @@ class Client(object): """Deletes the specified contract.""" return self.delete(self.contract_path % (contract)) + def list_servicechain_nodes(self, retrieve_all=True, **_params): + + """Fetches a list of all service chain nodes for a tenant.""" + # Pass filters in "params" argument to do_request + + return self.list('servicechain_nodes', self.servicechain_nodes_path, + retrieve_all, **_params) + + @APIParamsCall + def show_servicechain_node(self, servicechain_node, **_params): + """Fetches information of a certain service chain node.""" + return self.get(self.servicechain_node_path % (servicechain_node), + params=_params) + + @APIParamsCall + def create_servicechain_node(self, body=None): + """Creates a new service chain node.""" + return self.post(self.servicechain_nodes_path, body=body) + + @APIParamsCall + def update_servicechain_node(self, servicechain_node, body=None): + """Updates a service chain node.""" + return self.put(self.servicechain_node_path % (servicechain_node), + body=body) + + @APIParamsCall + def delete_servicechain_node(self, servicechain_node): + """Deletes the specified service chain node.""" + return self.delete(self.servicechain_node_path % (servicechain_node)) + + @APIParamsCall + def list_servicechain_specs(self, retrieve_all=True, **_params): + """Fetches a list of all service chain specs for a tenant.""" + # Pass filters in "params" argument to do_request + + return self.list('servicechain_specs', self.servicechain_specs_path, + retrieve_all, **_params) + + @APIParamsCall + def show_servicechain_spec(self, servicechain_spec, **_params): + """Fetches information of a certain service chain spec.""" + return self.get(self.servicechain_spec_path % (servicechain_spec), + params=_params) + + @APIParamsCall + def create_servicechain_spec(self, body=None): + """Creates a new service chain spec.""" + return self.post(self.servicechain_specs_path, body=body) + + @APIParamsCall + def update_servicechain_spec(self, servicechain_spec, body=None): + """Updates a service chain spec.""" + return self.put(self.servicechain_spec_path % (servicechain_spec), + body=body) + + @APIParamsCall + def delete_servicechain_spec(self, servicechain_spec): + """Deletes the specified service chain spec.""" + return self.delete(self.servicechain_spec_path % (servicechain_spec)) + + @APIParamsCall + def list_servicechain_instances(self, retrieve_all=True, **_params): + """Fetches a list of all service chain instances for a tenant.""" + # Pass filters in "params" argument to do_request + + return self.list('servicechain_instances', + self.servicechain_instances_path, + retrieve_all, **_params) + + @APIParamsCall + def show_servicechain_instance(self, servicechain_instance, **_params): + """Fetches information of a certain service chain instance.""" + return self.get(self.servicechain_instance_path % + (servicechain_instance), params=_params) + + @APIParamsCall + def create_servicechain_instance(self, body=None): + """Creates a new service chain instance.""" + return self.post(self.servicechain_instances_path, body=body) + + @APIParamsCall + def update_servicechain_instance(self, servicechain_instance, body=None): + """Updates a service chain instance.""" + return self.put(self.servicechain_instance_path % + (servicechain_instance), body=body) + + @APIParamsCall + def delete_servicechain_instance(self, servicechain_instance): + """Deletes the specified service chain instance.""" + return self.delete(self.servicechain_instance_path % + (servicechain_instance)) + def __init__(self, **kwargs): """Initialize a new client for the GBP v2.0 API.""" super(Client, self).__init__() diff --git a/requirements.txt b/requirements.txt index 2683dd4..52a155d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ # The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. - +python-heatclient>=0.2.9