From 882f6b6c936e0788daebf5943806f572a5082c31 Mon Sep 17 00:00:00 2001 From: xu-haiwei Date: Wed, 13 Jul 2016 09:30:34 +0000 Subject: [PATCH] Add client support for VNFFG in NFVO Co-Authored-By: Tim Rozet implements blueprint: tacker-vnffg Change-Id: Ic5273319a01b8003e9497fbf7f984972602c7698 --- .../notes/vnffg-client-abd7d7f06860b91d.yaml | 3 + requirements.txt | 1 + tackerclient/shell.py | 22 +++ tackerclient/tacker/v1_0/nfvo/vnffg.py | 168 ++++++++++++++++++ tackerclient/tacker/v1_0/nfvo/vnffgd.py | 87 +++++++++ tackerclient/tests/unit/test_cli10.py | 2 +- .../tests/unit/vm/test_cli10_vnffg.py | 104 +++++++++++ .../tests/unit/vm/test_cli10_vnffgd.py | 93 ++++++++++ tackerclient/v1_0/client.py | 120 +++++++++++++ 9 files changed, 599 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/vnffg-client-abd7d7f06860b91d.yaml create mode 100644 tackerclient/tacker/v1_0/nfvo/vnffg.py create mode 100644 tackerclient/tacker/v1_0/nfvo/vnffgd.py create mode 100644 tackerclient/tests/unit/vm/test_cli10_vnffg.py create mode 100644 tackerclient/tests/unit/vm/test_cli10_vnffgd.py diff --git a/releasenotes/notes/vnffg-client-abd7d7f06860b91d.yaml b/releasenotes/notes/vnffg-client-abd7d7f06860b91d.yaml new file mode 100644 index 00000000..67b926ea --- /dev/null +++ b/releasenotes/notes/vnffg-client-abd7d7f06860b91d.yaml @@ -0,0 +1,3 @@ +--- +features: + - Add client support for VNFFG. diff --git a/requirements.txt b/requirements.txt index 36545a0f..fcc1bf4d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,3 +14,4 @@ Babel>=2.3.4 # BSD oslo.i18n>=2.1.0 # Apache-2.0 oslo.utils>=3.16.0 # Apache-2.0 oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0 +oslo.serialization>=1.10.0 # Apache-2.0 diff --git a/tackerclient/shell.py b/tackerclient/shell.py index 5f57d092..ee8b9bef 100644 --- a/tackerclient/shell.py +++ b/tackerclient/shell.py @@ -48,6 +48,8 @@ from tackerclient.i18n import _ from tackerclient.tacker.v1_0.events import events from tackerclient.tacker.v1_0 import extension from tackerclient.tacker.v1_0.nfvo import vim +from tackerclient.tacker.v1_0.nfvo import vnffg +from tackerclient.tacker.v1_0.nfvo import vnffgd from tackerclient.tacker.v1_0.vm import vnf from tackerclient.tacker.v1_0.vm import vnfd from tackerclient.version import __version__ @@ -132,6 +134,26 @@ COMMAND_V1 = { 'vim-events-list': events.ListVIMEvents, 'vnfd-events-list': events.ListVNFDEvents, + 'vnffgd-create': vnffgd.CreateVNFFGD, + 'vnffgd-delete': vnffgd.DeleteVNFFGD, + 'vnffgd-list': vnffgd.ListVNFFGD, + 'vnffgd-show': vnffgd.ShowVNFFGD, + 'vnffgd-template-show': vnffgd.ShowTemplateVNFFGD, + + 'vnffg-create': vnffg.CreateVNFFG, + 'vnffg-delete': vnffg.DeleteVNFFG, + 'vnffg-list': vnffg.ListVNFFG, + 'vnffg-show': vnffg.ShowVNFFG, + 'vnffg-update': vnffg.UpdateVNFFG, + + 'nfp-list': vnffg.ListNFP, + 'nfp-show': vnffg.ShowNFP, + + 'chain-list': vnffg.ListSFC, + 'chain-show': vnffg.ShowSFC, + + 'classifier-list': vnffg.ListFC, + 'classifier-show': vnffg.ShowFC, } COMMANDS = {'1.0': COMMAND_V1} diff --git a/tackerclient/tacker/v1_0/nfvo/vnffg.py b/tackerclient/tacker/v1_0/nfvo/vnffg.py new file mode 100644 index 00000000..50477829 --- /dev/null +++ b/tackerclient/tacker/v1_0/nfvo/vnffg.py @@ -0,0 +1,168 @@ +# 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. + +from tackerclient.tacker import v1_0 as tackerV10 + + +_VNFFG = 'vnffg' +_NFP = 'nfp' +_SFC = 'sfc' +_FC = 'classifier' + + +class ListFC(tackerV10.ListCommand): + """List FCs that belong to a given tenant.""" + + resource = _FC + list_columns = ['id', 'status', 'nfp_id', 'chain_id'] + + +class ShowFC(tackerV10.ShowCommand): + """Show information of a given FC.""" + + resource = _FC + + +class ListSFC(tackerV10.ListCommand): + """List SFCs that belong to a given tenant.""" + + resource = _SFC + list_columns = ['id', 'status', 'nfp_id'] + + +class ShowSFC(tackerV10.ShowCommand): + """Show information of a given SFC.""" + + resource = _SFC + + +class ListNFP(tackerV10.ListCommand): + """List NFPs that belong to a given tenant.""" + + resource = _NFP + list_columns = ['id', 'name', 'status', 'vnffg_id', 'path_id'] + + +class ShowNFP(tackerV10.ShowCommand): + """Show information of a given NFP.""" + + resource = _NFP + + +class ListVNFFG(tackerV10.ListCommand): + """List VNFFGs that belong to a given tenant.""" + + resource = _VNFFG + list_columns = ['id', 'name', 'description', 'status', 'vnffgd_id'] + + +class ShowVNFFG(tackerV10.ShowCommand): + """Show information of a given VNFFG.""" + + resource = _VNFFG + + +class CreateVNFFG(tackerV10.CreateCommand): + """Create a VNFFG.""" + + resource = _VNFFG + remove_output_fields = ["attributes"] + + def add_known_arguments(self, parser): + parser.add_argument( + 'name', metavar='NAME', + help='Set a name for the VNFFG') + vnffgd_group = parser.add_mutually_exclusive_group(required=True) + vnffgd_group.add_argument( + '--vnffgd-id', + help='VNFFGD ID to use as template to create VNFFG') + vnffgd_group.add_argument( + '--vnffgd-name', + help='VNFFGD Name to use as template to create VNFFG') + parser.add_argument( + '--vnf-mapping', + help='List of logical VNFD name to VNF instance name mapping. ' + 'Example: VNF1:my_vnf1,VNF2:my_vnf2') + parser.add_argument( + '--symmetrical', metavar='{True,False}', + help='Should a reverse path be created for the NFP') + + def args2body(self, parsed_args): + body = {self.resource: {}} + + tacker_client = self.get_client() + tacker_client.format = parsed_args.request_format + + if parsed_args.vnf_mapping: + _vnf_mapping = dict() + _vnf_mappings = parsed_args.vnf_mapping.split(",") + for mapping in _vnf_mappings: + vnfd_name, vnf = mapping.split(":", 1) + _vnf_mapping[vnfd_name] = \ + tackerV10.find_resourceid_by_name_or_id( + tacker_client, 'vnf', vnf) + + parsed_args.vnf_mapping = _vnf_mapping + + if parsed_args.vnffgd_name: + _id = tackerV10.find_resourceid_by_name_or_id(tacker_client, + 'vnffgd', + parsed_args. + vnffgd_name) + parsed_args.vnffgd_id = _id + + tackerV10.update_dict(parsed_args, body[self.resource], + ['tenant_id', 'name', 'vnffgd_id', + 'symmetrical', 'vnf_mapping']) + return body + + +class UpdateVNFFG(tackerV10.UpdateCommand): + """Update a given VNFFG.""" + + resource = _VNFFG + + def add_known_arguments(self, parser): + parser.add_argument( + '--vnf-mapping', + help='List of logical VNFD name to VNF instance name mapping. ' + 'Example: VNF1:my_vnf1,VNF2:my_vnf2') + parser.add_argument( + '--symmetrical', metavar='{True,False}', + help='Should a reverse path be created for the NFP') + + def args2body(self, parsed_args): + body = {self.resource: {}} + + tacker_client = self.get_client() + tacker_client.format = parsed_args.request_format + + if parsed_args.vnf_mapping: + _vnf_mapping = dict() + _vnf_mappings = parsed_args.vnf_mapping.split(",") + for mapping in _vnf_mappings: + vnfd_name, vnf = mapping.split(":", 1) + _vnf_mapping[vnfd_name] = \ + tackerV10.find_resourceid_by_name_or_id( + tacker_client, 'vnf', vnf) + + parsed_args.vnf_mapping = _vnf_mapping + + tackerV10.update_dict(parsed_args, body[self.resource], + ['tenant_id', 'vnf_mapping', 'symmetrical']) + return body + + +class DeleteVNFFG(tackerV10.DeleteCommand): + """Delete a given VNFFG.""" + + resource = _VNFFG diff --git a/tackerclient/tacker/v1_0/nfvo/vnffgd.py b/tackerclient/tacker/v1_0/nfvo/vnffgd.py new file mode 100644 index 00000000..604b9c64 --- /dev/null +++ b/tackerclient/tacker/v1_0/nfvo/vnffgd.py @@ -0,0 +1,87 @@ +# 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. + +from __future__ import print_function + +import yaml + +from oslo_serialization import jsonutils + +from tackerclient.i18n import _ +from tackerclient.tacker import v1_0 as tackerV10 + +_VNFFGD = "vnffgd" + + +class ListVNFFGD(tackerV10.ListCommand): + """List VNFFGDs that belong to a given tenant.""" + + resource = _VNFFGD + list_columns = ['id', 'name', 'description'] + + +class ShowVNFFGD(tackerV10.ShowCommand): + """Show information of a given VNFFGD.""" + + resource = _VNFFGD + + +class CreateVNFFGD(tackerV10.CreateCommand): + """Create a VNFFGD.""" + resource = _VNFFGD + remove_output_fields = ["attributes"] + + def add_known_arguments(self, parser): + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument('--vnffgd-file', help='Specify VNFFGD file') + group.add_argument('--vnffgd', help='Specify VNFFGD') + parser.add_argument( + 'name', metavar='NAME', + help='Set a name for the VNFFGD') + parser.add_argument( + '--description', + help='Set a description for the VNFFGD') + + def args2body(self, parsed_args): + body = {self.resource: {}} + if parsed_args.vnffgd_file: + with open(parsed_args.vnffgd_file) as f: + vnffgd = yaml.safe_load(f.read()) + body[self.resource]['template'] = {'vnffgd': vnffgd} + if parsed_args.vnffgd: + body[self.resource]['template'] = { + 'vnffgd': yaml.safe_load(parsed_args.vnffgd)} + tackerV10.update_dict(parsed_args, body[self.resource], + ['tenant_id', 'name', 'description']) + return body + + +class DeleteVNFFGD(tackerV10.DeleteCommand): + """Delete a given VNFFGD.""" + resource = _VNFFGD + + +class ShowTemplateVNFFGD(tackerV10.ShowCommand): + """Show template of a given VNFFGD.""" + resource = _VNFFGD + + def run(self, parsed_args): + self.log.debug('run(%s)', parsed_args) + template = None + data = self.get_data(parsed_args) + try: + attributes_index = data[0].index('attributes') + attributes_json = data[1][attributes_index] + template = jsonutils.loads(attributes_json).get('vnffgd', None) + except (IndexError, TypeError, ValueError) as e: + self.log.debug('Data handling error: %s', str(e)) + print(template or _('Unable to display VNFFGD template!')) diff --git a/tackerclient/tests/unit/test_cli10.py b/tackerclient/tests/unit/test_cli10.py index 9bf99aa1..28c580aa 100644 --- a/tackerclient/tests/unit/test_cli10.py +++ b/tackerclient/tests/unit/test_cli10.py @@ -205,7 +205,7 @@ class CLITestV10Base(testtools.TestCase): self.mox.StubOutWithMock(cmd, "get_client") self.mox.StubOutWithMock(self.client.httpclient, "request") cmd.get_client().MultipleTimes().AndReturn(self.client) - non_admin_status_resources = ['vnfd', 'vnf', 'vim'] + non_admin_status_resources = ['vnfd', 'vnf', 'vim', 'vnffgd', 'vnffg'] if (resource in non_admin_status_resources): body = {resource: {}, } else: diff --git a/tackerclient/tests/unit/vm/test_cli10_vnffg.py b/tackerclient/tests/unit/vm/test_cli10_vnffg.py new file mode 100644 index 00000000..4fd2f1df --- /dev/null +++ b/tackerclient/tests/unit/vm/test_cli10_vnffg.py @@ -0,0 +1,104 @@ +# Copyright 2014 Intel Corporation +# 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 tackerclient.tacker.v1_0.nfvo import vnffg +from tackerclient.tests.unit import test_cli10 + +API_VERSION = "1.0" +FORMAT = 'json' +TOKEN = 'testtoken' +ENDURL = 'localurl' + + +class CLITestV10VmVNFFGJSON(test_cli10.CLITestV10Base): + _RESOURCE = 'vnffg' + _RESOURCES = 'vnffgs' + + def setUp(self): + plurals = {'vnffgs': 'vnffg'} + super(CLITestV10VmVNFFGJSON, self).setUp(plurals=plurals) + + def test_create_vnffg_all_params(self): + cmd = vnffg.CreateVNFFG(test_cli10.MyApp(sys.stdout), None) + my_id = 'my-id' + vnffgd_id = 'vnffgd' + vnffg_name = 'fake-vnffg' + vnf_mapping = 'VNFD1:VNF1' + + args = [ + vnffg_name, + '--vnffgd-id', vnffgd_id, + '--vnf-mapping', vnf_mapping, + '--symmetrical', 'True'] + position_names = ['vnffgd_id', 'vnf_mapping', 'symmetrical'] + position_values = [vnffgd_id, {"VNFD1": "VNF1"}, 'True'] + extra_body = {'name': vnffg_name} + self._test_create_resource(self._RESOURCE, cmd, None, my_id, + args, position_names, position_values, + extra_body=extra_body) + + def test_create_vnffg_with_mandatory_params(self): + cmd = vnffg.CreateVNFFG(test_cli10.MyApp(sys.stdout), None) + my_id = 'my-id' + vnffg_name = 'fake-vnffg' + vnffgd_id = 'vnffgd' + args = [ + vnffg_name, + '--vnffgd-id', vnffgd_id, + ] + position_names = ['vnffgd_id'] + position_values = [vnffgd_id] + extra_body = {'name': vnffg_name} + self._test_create_resource(self._RESOURCE, cmd, vnffg_name, my_id, + args, position_names, position_values, + extra_body=extra_body) + + def test_list_vnffgs(self): + cmd = vnffg.ListVNFFG(test_cli10.MyApp(sys.stdout), None) + self._test_list_resources(self._RESOURCES, cmd, True) + + def test_list_vnffgs_pagenation(self): + cmd = vnffg.ListVNFFG(test_cli10.MyApp(sys.stdout), None) + self._test_list_resources(self._RESOURCES, cmd, True) + + def test_show_vnffg_id(self): + cmd = vnffg.ShowVNFFG(test_cli10.MyApp(sys.stdout), None) + args = ['--fields', 'id', self.test_id] + self._test_show_resource(self._RESOURCE, cmd, self.test_id, args, + ['id']) + + def test_show_vnffg_id_name(self): + cmd = vnffg.ShowVNFFG(test_cli10.MyApp(sys.stdout), None) + args = ['--fields', 'id', '--fields', 'name', self.test_id] + self._test_show_resource(self._RESOURCE, cmd, self.test_id, + args, ['id', 'name']) + + def test_update_vnffg(self): + cmd = vnffg.UpdateVNFFG(test_cli10.MyApp(sys.stdout), None) + my_id = 'my-id' + key = 'new_key' + value = 'new-value' + self._test_update_resource(self._RESOURCE, cmd, my_id, + [my_id, '--%s' % key, value], + {key: value}) + + def test_delete_vnffg(self): + cmd = vnffg.DeleteVNFFG(test_cli10.MyApp(sys.stdout), None) + my_id = 'my-id' + args = [my_id] + self._test_delete_resource(self._RESOURCE, cmd, my_id, args) diff --git a/tackerclient/tests/unit/vm/test_cli10_vnffgd.py b/tackerclient/tests/unit/vm/test_cli10_vnffgd.py new file mode 100644 index 00000000..ff80d426 --- /dev/null +++ b/tackerclient/tests/unit/vm/test_cli10_vnffgd.py @@ -0,0 +1,93 @@ +# 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 tackerclient.tacker.v1_0.nfvo import vnffgd +from tackerclient.tests.unit import test_cli10 + + +class CLITestV10VmVNFFGDJSON(test_cli10.CLITestV10Base): + _RESOURCE = 'vnffgd' + _RESOURCES = 'vnffgds' + + def setUp(self): + plurals = {'vnffgds': 'vnffgd'} + super(CLITestV10VmVNFFGDJSON, self).setUp(plurals=plurals) + + def test_create_vnffgd_all_params(self): + cmd = vnffgd.CreateVNFFGD(test_cli10.MyApp(sys.stdout), None) + my_id = 'my-id' + name = 'my-name' + attr_key = 'vnffgd' + attr_val = 'vnffgd' + description = 'vnffgd description' + args = [ + name, + '--vnffgd', 'vnffgd', + '--description', description, + ] + position_names = ['name', 'description'] + position_values = [name, description] + extra_body = { + 'template': {attr_key: attr_val}, + } + + self._test_create_resource(self._RESOURCE, cmd, name, my_id, + args, position_names, position_values, + extra_body=extra_body) + + def test_create_vnffgd_with_mandatory_params(self): + cmd = vnffgd.CreateVNFFGD(test_cli10.MyApp(sys.stdout), None) + my_id = 'my-id' + name = 'my-name' + attr_key = 'vnffgd' + attr_val = 'vnffgd' + args = [ + name, + '--vnffgd', 'vnffgd', + ] + position_names = ['name'] + position_values = [name] + extra_body = { + 'template': {attr_key: attr_val}, + } + + self._test_create_resource(self._RESOURCE, cmd, name, my_id, + args, position_names, position_values, + extra_body=extra_body) + + def test_list_vnffgds(self): + cmd = vnffgd.ListVNFFGD(test_cli10.MyApp(sys.stdout), None) + self._test_list_resources(self._RESOURCES, cmd, True) + + def test_list_vnffgds_pagenation(self): + cmd = vnffgd.ListVNFFGD(test_cli10.MyApp(sys.stdout), None) + self._test_list_resources(self._RESOURCES, cmd, True) + + def test_show_vnffgd_id(self): + cmd = vnffgd.ShowVNFFGD(test_cli10.MyApp(sys.stdout), None) + args = ['--fields', 'id', self.test_id] + self._test_show_resource(self._RESOURCE, cmd, self.test_id, args, + ['id']) + + def test_show_vnffgd_id_name(self): + cmd = vnffgd.ShowVNFFGD(test_cli10.MyApp(sys.stdout), None) + args = ['--fields', 'id', '--fields', 'name', self.test_id] + self._test_show_resource(self._RESOURCE, cmd, self.test_id, + args, ['id', 'name']) + + def test_delete_vnffgd(self): + cmd = vnffgd.DeleteVNFFGD(test_cli10.MyApp(sys.stdout), None) + my_id = 'my-id' + args = [my_id] + self._test_delete_resource(self._RESOURCE, cmd, my_id, args) diff --git a/tackerclient/v1_0/client.py b/tackerclient/v1_0/client.py index bc193258..d2fbd74e 100644 --- a/tackerclient/v1_0/client.py +++ b/tackerclient/v1_0/client.py @@ -346,6 +346,21 @@ class Client(ClientBase): events_path = '/events' event_path = '/events/%s' + vnffgds_path = '/vnffgds' + vnffgd_path = '/vnffgds/%s' + + vnffgs_path = '/vnffgs' + vnffg_path = '/vnffgs/%s' + + nfps_path = '/nfps' + nfp_path = '/nfps/%s' + + sfcs_path = '/sfcs' + sfc_path = '/sfcs/%s' + + fcs_path = '/classifiers' + fc_path = '/classifiers/%s' + # API has no way to report plurals, so we have to hard code them # EXTED_PLURALS = {} @@ -487,3 +502,108 @@ class Client(ClientBase): @APIParamsCall def show_event(self, event_id, **_params): return self.get(self.event_path % event_id, params=_params) + + _VNFFGD = "vnffgd" + + @APIParamsCall + def create_vnffgd(self, body=None): + return self.post(self.vnffgds_path, body) + + @APIParamsCall + def list_vnffgds(self, retrieve_all=True, **_params): + vnffgds_dict = self.list(self._VNFFGD + 's', + self.vnffgds_path, + retrieve_all, + **_params) + for vnffgd in vnffgds_dict['vnffgds']: + if 'description' in vnffgd.keys() and \ + len(vnffgd['description']) > DEFAULT_DESC_LENGTH: + vnffgd['description'] = vnffgd['description'][ + :DEFAULT_DESC_LENGTH] + vnffgd['description'] += '...' + return vnffgds_dict + + @APIParamsCall + def show_vnffgd(self, vnffgd, **_params): + return self.get(self.vnffgd_path % vnffgd, params=_params) + + @APIParamsCall + def delete_vnffgd(self, vnffgd): + return self.delete(self.vnffgd_path % vnffgd) + + @APIParamsCall + def list_vnffgs(self, retrieve_all=True, **_params): + vnffgs = self.list('vnffgs', self.vnffgs_path, retrieve_all, **_params) + for vnffg in vnffgs['vnffgs']: + error_reason = vnffg.get('error_reason', None) + if error_reason and \ + len(error_reason) > DEFAULT_ERROR_REASON_LENGTH: + vnffg['error_reason'] = error_reason[ + :DEFAULT_ERROR_REASON_LENGTH] + vnffg['error_reason'] += '...' + return vnffgs + + @APIParamsCall + def show_vnffg(self, vnffg, **_params): + return self.get(self.vnffg_path % vnffg, params=_params) + + @APIParamsCall + def create_vnffg(self, body=None): + return self.post(self.vnffgs_path, body=body) + + @APIParamsCall + def delete_vnffg(self, vnffg): + return self.delete(self.vnffg_path % vnffg) + + @APIParamsCall + def update_vnffg(self, vnffg, body=None): + return self.put(self.vnffg_path % vnffg, body=body) + + @APIParamsCall + def list_sfcs(self, retrieve_all=True, **_params): + sfcs = self.list('sfcs', self.sfcs_path, retrieve_all, **_params) + for chain in sfcs['sfcs']: + error_reason = chain.get('error_reason', None) + if error_reason and \ + len(error_reason) > DEFAULT_ERROR_REASON_LENGTH: + chain['error_reason'] = error_reason[ + :DEFAULT_ERROR_REASON_LENGTH] + chain['error_reason'] += '...' + return sfcs + + @APIParamsCall + def show_sfc(self, chain, **_params): + return self.get(self.sfc_path % chain, params=_params) + + @APIParamsCall + def list_nfps(self, retrieve_all=True, **_params): + nfps = self.list('nfps', self.nfps_path, retrieve_all, **_params) + for nfp in nfps['nfps']: + error_reason = nfp.get('error_reason', None) + if error_reason and \ + len(error_reason) > DEFAULT_ERROR_REASON_LENGTH: + nfp['error_reason'] = error_reason[ + :DEFAULT_ERROR_REASON_LENGTH] + nfp['error_reason'] += '...' + return nfps + + @APIParamsCall + def show_nfp(self, nfp, **_params): + return self.get(self.nfp_path % nfp, params=_params) + + @APIParamsCall + def list_classifiers(self, retrieve_all=True, **_params): + classifiers = self.list('classifiers', self.fcs_path, retrieve_all, + **_params) + for classifier in classifiers['classifiers']: + error_reason = classifier.get('error_reason', None) + if error_reason and \ + len(error_reason) > DEFAULT_ERROR_REASON_LENGTH: + classifier['error_reason'] = error_reason[ + :DEFAULT_ERROR_REASON_LENGTH] + classifier['error_reason'] += '...' + return classifiers + + @APIParamsCall + def show_classifier(self, classifier, **_params): + return self.get(self.fc_path % classifier, params=_params)