diff options
author | stark, steven <steven.stark@att.com> | 2020-03-11 14:30:58 -0700 |
---|---|---|
committer | stark, steven <steven.stark@att.com> | 2020-03-12 14:41:09 -0700 |
commit | c02d3ed39e53d4ce56406a43089fc4a336c43f17 (patch) | |
tree | 8354fe71b2b4d379f36042b76c0e29775692f03e /onap-client/onap_client | |
parent | d7d8722ce27e308defb6764d8d76f85ce7d63927 (diff) |
[VVP] Adding onap-client intial commit.
This is supplementary tooling to interact with various ONAP applications.
It will be used by the OVP VNF Testcase, it's not deployed
with the ONAP platform.
Issue-ID: VVP-381
Change-Id: I2ff3952ba8f4b9448acb5a24717ccc3b1f0a92fe
Signed-off-by: stark, steven <steven.stark@att.com>
Diffstat (limited to 'onap-client/onap_client')
64 files changed, 8512 insertions, 0 deletions
diff --git a/onap-client/onap_client/__init__.py b/onap-client/onap_client/__init__.py new file mode 100644 index 0000000..5519a84 --- /dev/null +++ b/onap-client/onap_client/__init__.py @@ -0,0 +1,36 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ diff --git a/onap-client/onap_client/aai/__init__.py b/onap-client/onap_client/aai/__init__.py new file mode 100644 index 0000000..a67037c --- /dev/null +++ b/onap-client/onap_client/aai/__init__.py @@ -0,0 +1,40 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +from onap_client.config import APP_CONFIG + +AAI_PROPERTIES = APP_CONFIG.aai diff --git a/onap-client/onap_client/aai/catalog/__init__.py b/onap-client/onap_client/aai/catalog/__init__.py new file mode 100644 index 0000000..5519a84 --- /dev/null +++ b/onap-client/onap_client/aai/catalog/__init__.py @@ -0,0 +1,36 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ diff --git a/onap-client/onap_client/aai/catalog/business_catalog.py b/onap-client/onap_client/aai/catalog/business_catalog.py new file mode 100644 index 0000000..2b9af21 --- /dev/null +++ b/onap-client/onap_client/aai/catalog/business_catalog.py @@ -0,0 +1,274 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import uuid +from functools import partial + +from onap_client import aai +from onap_client import config +from onap_client.aai.client import AAIClient + +PAYLOADS_DIR = config.PAYLOADS_DIR +aai_properties = aai.AAI_PROPERTIES +application_id = config.APPLICATION_ID + + +class BusinessClient(AAIClient): + @property + def catalog_resources(self): + return CATALOG_RESOURCES + + @property + def namespace(self): + return "business" + + +CATALOG_RESOURCES = { + "GET_OWNING_ENTITY": { + "verb": "GET", + "description": "Queries AAI for an owning entity", + "uri": partial( + "{endpoint}{service_path}/owning-entities?owning-entity-name={name}".format, + endpoint=aai_properties.AAI_BE_ENDPOINT, + service_path=aai_properties.AAI_BUSINESS_PATH, + ), + "uri-parameters": ["name"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (aai_properties.AAI_USERNAME, aai_properties.AAI_PASSWORD,), + }, + "GET_OWNING_ENTITIES": { + "verb": "GET", + "description": "Queries AAI for all owning entities", + "uri": partial( + "{endpoint}{service_path}/owning-entities".format, + endpoint=aai_properties.AAI_BE_ENDPOINT, + service_path=aai_properties.AAI_BUSINESS_PATH, + ), + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (aai_properties.AAI_USERNAME, aai_properties.AAI_PASSWORD,), + }, + "GET_CUSTOMER": { + "verb": "GET", + "description": "Queries AAI for a customer", + "uri": partial( + "{endpoint}{service_path}/customers?global-customer-id={name}".format, + endpoint=aai_properties.AAI_BE_ENDPOINT, + service_path=aai_properties.AAI_BUSINESS_PATH, + ), + "uri-parameters": ["name"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (aai_properties.AAI_USERNAME, aai_properties.AAI_PASSWORD,), + }, + "GET_CUSTOMERS": { + "verb": "GET", + "description": "Queries AAI for all customers", + "uri": partial( + "{endpoint}{service_path}/customers".format, + endpoint=aai_properties.AAI_BE_ENDPOINT, + service_path=aai_properties.AAI_BUSINESS_PATH, + ), + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (aai_properties.AAI_USERNAME, aai_properties.AAI_PASSWORD,), + }, + "GET_PLATFORM": { + "verb": "GET", + "description": "Queries AAI for a platform", + "uri": partial( + "{endpoint}{service_path}/platforms?platform-name={name}".format, + endpoint=aai_properties.AAI_BE_ENDPOINT, + service_path=aai_properties.AAI_BUSINESS_PATH, + ), + "uri-parameters": ["name"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (aai_properties.AAI_USERNAME, aai_properties.AAI_PASSWORD,), + }, + "GET_PLATFORMS": { + "verb": "GET", + "description": "Queries AAI for all platforms", + "uri": partial( + "{endpoint}{service_path}/platforms".format, + endpoint=aai_properties.AAI_BE_ENDPOINT, + service_path=aai_properties.AAI_BUSINESS_PATH, + ), + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (aai_properties.AAI_USERNAME, aai_properties.AAI_PASSWORD,), + }, + "GET_PROJECT": { + "verb": "GET", + "description": "Queries AAI for a project", + "uri": partial( + "{endpoint}{service_path}/projects/project/{name}".format, + endpoint=aai_properties.AAI_BE_ENDPOINT, + service_path=aai_properties.AAI_BUSINESS_PATH, + ), + "uri-parameters": ["name"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (aai_properties.AAI_USERNAME, aai_properties.AAI_PASSWORD,), + }, + "GET_PROJECTS": { + "verb": "GET", + "description": "Queries AAI for all projects", + "uri": partial( + "{endpoint}{service_path}/projects".format, + endpoint=aai_properties.AAI_BE_ENDPOINT, + service_path=aai_properties.AAI_BUSINESS_PATH, + ), + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (aai_properties.AAI_USERNAME, aai_properties.AAI_PASSWORD,), + }, + "GET_LINES_OF_BUSINESS": { + "verb": "GET", + "description": "Queries AAI for all lobs", + "uri": partial( + "{endpoint}{service_path}/lines-of-business".format, + endpoint=aai_properties.AAI_BE_ENDPOINT, + service_path=aai_properties.AAI_BUSINESS_PATH, + ), + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (aai_properties.AAI_USERNAME, aai_properties.AAI_PASSWORD,), + }, + "CREATE_CUSTOMER": { + "verb": "PUT", + "description": "Creates a customer in AAI", + "uri": partial( + "{endpoint}{service_path}/customers/customer/{customer_name}".format, + endpoint=aai_properties.AAI_BE_ENDPOINT, + service_path=aai_properties.AAI_BUSINESS_PATH, + ), + "uri-parameters": ["customer_name"], + "payload-parameters": ["customer_name", "subscriber_name"], + "payload": "{}/aai_create_customer.jinja".format(PAYLOADS_DIR), + "success_code": 201, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (aai_properties.AAI_USERNAME, aai_properties.AAI_PASSWORD,), + }, + "GET_CUSTOMER_SUBSCRIPTIONS": { + "verb": "GET", + "description": "Queries AAI the subscriptions for a customer", + "uri": partial( + "{endpoint}{service_path}/customers/customer/{customer_name}/service-subscriptions".format, + endpoint=aai_properties.AAI_BE_ENDPOINT, + service_path=aai_properties.AAI_BUSINESS_PATH, + ), + "uri-parameters": ["customer_name"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (aai_properties.AAI_USERNAME, aai_properties.AAI_PASSWORD,), + }, + "GET_SERVICE_INSTANCES": { + "verb": "GET", + "description": "Queries AAI the service instances for a customer subscription", + "uri": partial( + "{endpoint}{service_path}/customers/customer/{customer_name}/service-subscriptions/service-subscription/{subscription_name}/service-instances".format, + endpoint=aai_properties.AAI_BE_ENDPOINT, + service_path=aai_properties.AAI_BUSINESS_PATH, + ), + "uri-parameters": ["customer_name", "subscription_name"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (aai_properties.AAI_USERNAME, aai_properties.AAI_PASSWORD,), + }, +} diff --git a/onap-client/onap_client/aai/catalog/cloud_infrastructure_catalog.py b/onap-client/onap_client/aai/catalog/cloud_infrastructure_catalog.py new file mode 100644 index 0000000..b2570a0 --- /dev/null +++ b/onap-client/onap_client/aai/catalog/cloud_infrastructure_catalog.py @@ -0,0 +1,132 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import uuid +from functools import partial + +from onap_client import aai +from onap_client import config +from onap_client.aai.client import AAIClient + +PAYLOADS_DIR = config.PAYLOADS_DIR +aai_properties = aai.AAI_PROPERTIES +application_id = config.APPLICATION_ID + + +class CloudInfrastructureClient(AAIClient): + @property + def catalog_resources(self): + return CATALOG_RESOURCES + + @property + def namespace(self): + return "cloud_infrastructure" + + +CATALOG_RESOURCES = { + "GET_CLOUD_REGIONS": { + "verb": "GET", + "description": "Queries AAI for all cloud regions", + "uri": partial( + "{endpoint}{service_path}/cloud-regions".format, + endpoint=aai_properties.AAI_BE_ENDPOINT, + service_path=aai_properties.AAI_CLOUD_INFRASTRUCTURE_PATH, + ), + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (aai_properties.AAI_USERNAME, aai_properties.AAI_PASSWORD,), + }, + "GET_CLOUD_REGION": { + "verb": "GET", + "description": "Queries AAI for a cloud region", + "uri": partial( + "{endpoint}{service_path}/cloud-regions/cloud-region/{cloud_owner}/{cloud_region}".format, + endpoint=aai_properties.AAI_BE_ENDPOINT, + service_path=aai_properties.AAI_CLOUD_INFRASTRUCTURE_PATH, + ), + "uri-parameters": ["cloud_region", "cloud_owner"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (aai_properties.AAI_USERNAME, aai_properties.AAI_PASSWORD,), + }, + "GET_CLOUD_REGION_TENANTS": { + "verb": "GET", + "description": "Queries AAI for a cloud region's tenants", + "uri": partial( + "{endpoint}{service_path}/cloud-regions/cloud-region/{cloud_owner}/{cloud_region}/tenants".format, + endpoint=aai_properties.AAI_BE_ENDPOINT, + service_path=aai_properties.AAI_CLOUD_INFRASTRUCTURE_PATH, + ), + "uri-parameters": ["cloud_region", "cloud_owner"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (aai_properties.AAI_USERNAME, aai_properties.AAI_PASSWORD,), + }, + "GET_ESR_LIST": { + "verb": "GET", + "description": "Queries AAI for a esr info", + "uri": partial( + "{endpoint}{service_path}/cloud-regions/cloud-region/{cloud_owner}/{cloud_region}/esr-system-info-list".format, + endpoint=aai_properties.AAI_BE_ENDPOINT, + service_path=aai_properties.AAI_CLOUD_INFRASTRUCTURE_PATH, + ), + "uri-parameters": ["cloud_region", "cloud_owner"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (aai_properties.AAI_USERNAME, aai_properties.AAI_PASSWORD,), + }, +} diff --git a/onap-client/onap_client/aai/client.py b/onap-client/onap_client/aai/client.py new file mode 100644 index 0000000..5a2ddb1 --- /dev/null +++ b/onap-client/onap_client/aai/client.py @@ -0,0 +1,77 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import uuid + +from functools import partial +from onap_client import aai +from onap_client.client.clients import Client +from onap_client import config + +aai_properties = aai.AAI_PROPERTIES +application_id = config.APPLICATION_ID + + +class AAIClient(Client): + @property + def namespace(self): + return "aai" + + @property + def catalog_resources(self): + return CATALOG_RESOURCES + + +CATALOG_RESOURCES = { + "HEALTH_CHECK": { + "verb": "GET", + "description": "Queries AAI health check endpoint", + "uri": partial( + "{endpoint}{service_path}".format, + endpoint=aai_properties.AAI_BE_ENDPOINT, + service_path=aai_properties.AAI_HEALTH_CHECK_PATH, + ), + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (aai_properties.AAI_USERNAME, aai_properties.AAI_PASSWORD,), + }, +} diff --git a/onap-client/onap_client/aai/tests/__init__.py b/onap-client/onap_client/aai/tests/__init__.py new file mode 100644 index 0000000..5519a84 --- /dev/null +++ b/onap-client/onap_client/aai/tests/__init__.py @@ -0,0 +1,36 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ diff --git a/onap-client/onap_client/cli.py b/onap-client/onap_client/cli.py new file mode 100644 index 0000000..24f8b9b --- /dev/null +++ b/onap-client/onap_client/cli.py @@ -0,0 +1,215 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import json + +from prettytable import PrettyTable +from onap_client.client.clients import Client +from onap_client.client.catalog import Catalog +from onap_client.engine import spec_cli +from onap_client.util import utility_cli + + +def main(*args): + cli_arguments = list(args) + request_arguments = {} + search_key = None + keys = None + + oc = Client() + + if len(args) > 0 and args[0] == "spec-engine": + # use engine cli instead + spec_cli(cli_arguments[1:]) + elif len(args) > 0 and args[0] == "utility": + # use engine cli instead + utility_cli(oc, cli_arguments[1:]) + elif len(args) == 0 or args[0] == "--help": + print(help(oc, extra_clients=["spec-engine", "utility"])) + else: + while cli_arguments: + arg = cli_arguments.pop(0) + if arg == "--help": + print(help(oc)) + return + elif arg == "--search": + search_key = cli_arguments.pop(0) + continue + elif arg == "--keys": + keys = True + continue + + if is_argument(arg): + arg = convert_to_underscores(arg) + arg = sanitize_argument(arg) + try: + value = get_value(cli_arguments.pop(0)) + if is_argument(value): + print( + "No Value passed for argument: {}. Try --help".format(arg) + ) + return + except IndexError: + print("No Value passed for argument: {}. Try --help".format(arg)) + return + request_arguments[arg] = value + else: + arg = convert_to_underscores(arg) + oc = getattr(oc, arg, None) + if not oc: + print("Invalid Argument: {}. Try --help".format(arg)) + return + + if isinstance(oc, Catalog.CallHandle): + data = oc(**request_arguments) + + output_data = data.response_data + + if isinstance(output_data, dict): + if keys: + print("\n".join(x for x in output_data.keys())) + elif search_key: + print(output_data.get(search_key)) + else: + print(json.dumps(output_data, indent=4)) + else: + print(output_data) + else: + print("Command Invalid: {}. Try --help".format(args)) + + +def is_argument(argument): + return argument.startswith("--") + + +def sanitize_argument(argument): + return argument.replace("__", "") + + +def convert_to_underscores(argument): + return argument.replace("-", "_") + + +def parameterize(argument): + return "--{}".format(argument.replace("_", "-")) + + +def get_value(value): + if value in ["True", "true"]: + return True + elif value in ["False", "false"]: + return False + + return value + + +def help(client, extra_clients=[]): + namespaces = [] + actions = [] + + if isinstance(client, Catalog): + + for attr, item in client.__dict__.items(): + if isinstance(item, Catalog): + namespaces.append(attr) + + for item_name, catalog_item in client.catalog_items.items(): + actions.append(get_catalog_item_data(catalog_item)) + + elif isinstance(client, Catalog.CallHandle): + actions.append(get_catalog_item_data(client.resource)) + + data = {"clients": namespaces, "actions": actions} + data["clients"].extend(extra_clients) + + return help_table(data) + + +def help_table(data): + x = PrettyTable() + + x.field_names = [ + "name", + "description", + "required parameters", + "optional parameters", + ] + x.align["name"] = "l" + x.align["description"] = "l" + x.align["required parameters"] = "l" + x.align["optional parameters"] = "l" + + for item in data.get("actions"): + name = item.get("name").lower().replace("_", "-") + description = item.get("description") + parameters = [] + for param in item.get("parameters"): + if isinstance(param, str): + parameters.append(parameterize(param)) + elif isinstance(param, list): + for param2 in param: + parameters.append(parameterize(param2)) + x.add_row([name, description, "\n".join(parameters), "--keys, --search"]) + x.add_row(["", "", "", ""]) + + for item in data.get("clients"): + name = item + description = "Various actions available for {}".format(name) + parameters = ["--help"] + x.add_row([name, description, "\n".join(parameters), ""]) + x.add_row(["", "", "", ""]) + + return x + + +def get_catalog_item_data(catalog_item): + item = {} + item["parameters"] = [] + item["name"] = catalog_item.catalog_resource_name.lower() + item["parameters"].extend(x for x in catalog_item.file_parameters) + item["parameters"].extend( + x for x in catalog_item.payload_parameters if x not in item["parameters"] + ) + item["parameters"].extend( + x for x in catalog_item.uri_parameters if x not in item["parameters"] + ) + item["parameters"] += ( + [catalog_item.payload_path] if catalog_item.payload_path else [] + ) + item["description"] = catalog_item.description + + return item diff --git a/onap-client/onap_client/client/__init__.py b/onap-client/onap_client/client/__init__.py new file mode 100644 index 0000000..5519a84 --- /dev/null +++ b/onap-client/onap_client/client/__init__.py @@ -0,0 +1,36 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ diff --git a/onap-client/onap_client/client/catalog.py b/onap-client/onap_client/client/catalog.py new file mode 100644 index 0000000..419d00f --- /dev/null +++ b/onap-client/onap_client/client/catalog.py @@ -0,0 +1,156 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +from abc import ABC, abstractmethod +from onap_client.lib import make_request + + +class Catalog(ABC): + """Abstract class for an ONAP client, automatically loads + child classes as attributes.""" + + class CallHandle: + """Attached as an attribute for each catalog entry in a catalog. + Used to make a request to ONAP.""" + + def __init__(self, catalog_resource): + self.resource = catalog_resource + + def __call__(self, **kwargs): + return make_request(self.resource, **kwargs) + + def __init__(self): + """Iterates through all child classes and attaches them as attributes, named + after the namespace property. + + If the child Catalog class has items in the + catalog_resources property, they will be added as attributes to the child attribute + as a CallHandle object. + """ + self.catalog_items = {} + + for cls in self.__class__.__subclasses__(): + subclass = cls() + namespace = subclass.namespace + catalog_resources = subclass.catalog_resources + + for k, v in catalog_resources.items(): + subclass.load(k, v) + + setattr(self, namespace, subclass) + + def load(self, item_name, resource_data): + """Consume a catalog resource entry as an APICatalogResource, + and set it as an attribute on this.class as a CallHandle object""" + resource = APICatalogResource(item_name, resource_data) + + self.catalog_items[item_name] = resource + setattr(self, item_name.lower(), self.CallHandle(resource)) + + @property + @abstractmethod + def namespace(self): + raise NotImplementedError + + @property + @abstractmethod + def catalog_resources(self): + raise NotImplementedError + + +class APICatalogResource: + """Class representation of a single catalog entry""" + + def __init__(self, catalog_resource_name, resource_data): + """ + :catalog_resource_name: name of the catalog resource + :resource_data: dictionary containing catalog resource attributes + """ + self.catalog_resource_name = catalog_resource_name + self.catalog_resource_data = resource_data + + @property + def verb(self): + return self.catalog_resource_data.get("verb", None) + + @property + def description(self): + return self.catalog_resource_data.get("description", None) + + @property + def uri(self): + return self.catalog_resource_data.get("uri", None) + + @property + def payload(self): + return self.catalog_resource_data.get("payload", None) + + @property + def uri_parameters(self): + return self.catalog_resource_data.get("uri-parameters", []) + + @property + def payload_parameters(self): + return self.catalog_resource_data.get("payload-parameters", []) + + @property + def payload_path(self): + return self.catalog_resource_data.get("payload-path", []) + + @property + def file_parameters(self): + return self.catalog_resource_data.get("files-parameters", []) + + @property + def header_parameters(self): + return self.catalog_resource_data.get("header_parameters", []) + + @property + def success_code(self): + return self.catalog_resource_data.get("success_code", None) + + @property + def headers(self): + return self.catalog_resource_data.get("headers", None) + + @property + def return_data(self): + return self.catalog_resource_data.get("return_data", {}) + + @property + def auth(self): + return self.catalog_resource_data.get("auth", None) diff --git a/onap-client/onap_client/client/clients.py b/onap-client/onap_client/client/clients.py new file mode 100644 index 0000000..0c4605f --- /dev/null +++ b/onap-client/onap_client/client/clients.py @@ -0,0 +1,85 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import importlib +import onap_client +import pkgutil +import inspect + +from onap_client.client.catalog import Catalog + + +class Client(Catalog): + """Base class for the ONAP client. Subclasses are dynamically + loaded and added as attributes. Instantiate and use this class + to interact with ONAP.""" + def __init__(self): + self.modules = import_submodules(onap_client) + super().__init__() + + @property + def namespace(self): + return "onap" + + @property + def catalog_resources(self): + return {} + + @property + def utility_functions(self): + utility_functions = {} + for module_name, module in self.modules.items(): + all_functions = inspect.getmembers(module, inspect.isfunction) + for func in all_functions: + function = func[1] + if hasattr(function, "utility_function"): + utility_functions[func[0]] = func[1] + return utility_functions + + +def import_submodules(package, recursive=True): + """Import all the modules in onap-client, except for those starting + with tests*. This is needed so that the Client object can register child classes""" + if isinstance(package, str): + package = importlib.import_module(package) + results = {} + for loader, name, is_pkg in pkgutil.walk_packages(package.__path__): + full_name = package.__name__ + "." + name + results[full_name] = importlib.import_module(full_name) + if recursive and is_pkg and full_name.find("tests") == -1: + results.update(import_submodules(full_name)) + return results diff --git a/onap-client/onap_client/client/request.py b/onap-client/onap_client/client/request.py new file mode 100644 index 0000000..0e40591 --- /dev/null +++ b/onap-client/onap_client/client/request.py @@ -0,0 +1,203 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import jinja2 +import requests +import json +import os +import copy + +from onap_client.client.response import ResponseHandler +from onap_client.config import LOG as logger +from onap_client.exceptions import FilesRequestFailure +from jinja2 import exceptions as jinja_exceptions + + +class RequestHandler: + """Handles a APICatalogRequestObject to make a request + and returns a ResponseHandler object""" + + def __init__(self, request_object): + """ + :request_object: APICatalogRequestObject + """ + self.request_object = request_object + + def make_request(self): + r = Request(self.request_object) + + logger.warning("Submitting request: {}".format(self.request_object.description)) + # TODO + # Add verify to config file + return ResponseHandler(r.request(verify=False), self.request_object) + + +class Request: + """Parses a APICatalogRequestObject to fill out the + kwargs to send to the requests library""" + + def __init__(self, request_object): + """ + :request_object: APICatalogRequestObject + """ + self.request_object = request_object + self.kwargs = {} + self.response = None + + self.build_request() + + def build_request(self): + request_object = self.request_object + + if request_object.verb: + self.kwargs["method"] = request_object.verb + + if request_object.auth: + self.kwargs["auth"] = request_object.auth + + if request_object.uri: + self.kwargs["url"] = request_object.uri + + if request_object.headers: + self.kwargs["headers"] = request_object.headers + + if request_object.payload: + self.kwargs["data"] = request_object.payload + + if request_object.files: + self.kwargs["files"] = request_object.files + + debug_request = copy.deepcopy(self.kwargs) + if "auth" in debug_request: + debug_request["auth"] = "***********" + + try: + logger.info(json.dumps(debug_request, indent=4)) + except TypeError: + logger.info(debug_request) + + def request(self, verify=True): + return requests.request(**self.kwargs, verify=verify) + + +class APICatalogRequestObject: + """Fills a APICatalogResource object with request-specific data""" + + def __init__(self, api_catalog_resource, **kwargs): + """ + :api_catalog_resource: APICatalogResource object + :kwargs: key/value to fill in APICatalogResource parameters + """ + self.api_catalog_resource = api_catalog_resource + self.payload_parameters = kwargs.get("payload_parameters", {}) + self.uri_parameters = kwargs.get("uri_parameters", {}) + self.header_parameters = kwargs.get("header_parameters", {}) + self.file_parameters = kwargs.get("file_parameters", {}) + if api_catalog_resource.payload_path: + self.payload_path = kwargs.get("payload_path", {}).get( + api_catalog_resource.payload_path[0] + ) + + self.uri = "" + self.files = None + self.payload = None + self.verb = api_catalog_resource.verb + self.headers = api_catalog_resource.headers + self.success_code = api_catalog_resource.success_code + self.return_data = api_catalog_resource.return_data + self.auth = api_catalog_resource.auth + self.description = api_catalog_resource.description + + if api_catalog_resource.payload or api_catalog_resource.payload_path: + self.resolve_payload() + + if api_catalog_resource.file_parameters: + self.resolve_files() + + if isinstance(self.headers, dict): + for k, v in self.header_parameters.items(): + self.headers[k] = v + + self.resolve_uri() + + def resolve_files(self): + # TODO + # is there a better way to figure out waht params are needed? + # right now its hardcoded + file_type = self.file_parameters.get("file_type", "application/zip") + file_path = self.file_parameters.get("file_path") + if not file_path: + raise FilesRequestFailure("File path was not provided") + + try: + with open(file_path, "rb") as f: + data = f.read() + except IOError: + logger.error("file {} was not found".format(file_path)) + raise + + file_name = os.path.basename(file_path) + + self.files = {"upload": [file_name, data, file_type]} + + def resolve_payload(self): + try: + if self.api_catalog_resource.payload_path: + with open(self.payload_path, "r") as f: + self.payload = f.read() + else: + with open(self.api_catalog_resource.payload, "r") as f: + self.payload = jinja2.Template(f.read()).render( + **self.payload_parameters + ) + except jinja_exceptions.TemplateNotFound: + logger.error( + "{} file not found. Check payloads directory.".format(self.payload) + ) + raise + except FileNotFoundError: + logger.error( + "{} file not found. Check payloads directory.".format(self.payload) + ) + raise + + def resolve_uri(self): + try: + self.uri = self.api_catalog_resource.uri(**self.uri_parameters) + except KeyError: + logger.error("invalid uri keys {}.".format(self.uri_parameters)) + raise diff --git a/onap-client/onap_client/client/response.py b/onap-client/onap_client/client/response.py new file mode 100644 index 0000000..ef0aab5 --- /dev/null +++ b/onap-client/onap_client/client/response.py @@ -0,0 +1,115 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import simplejson + +from onap_client.config import LOG as logger + + +class ResponseHandler: + """Handles a response from the requests library, + and compares it to the APICatalogRequestObject that was used to make the request. + If the request object has return_data, then it will parse the response + object and add the return data as an attribute.""" + + def __init__(self, response, request_object): + """ + :response: requests.response + :request_object: APICatalogRequestObject + """ + self.response = response + self.request_object = request_object + self.response_data = {} + self.status_code = None + self.success = False + + self.validate_response() + + def validate_response(self): + response = self.response + if self.request_object.success_code != response.status_code: + response_data = response.text + logger.error( + "Request failed with code {} and data {}".format( + response.status_code, response_data + ) + ) + else: + logger.info("Request was successful") + self.success = True + try: + response_data = response.json() + for ( + response_key, + response_items, + ) in self.request_object.return_data.items(): + response_value = response_iterator(response_data, *response_items) + if not response_value: + logger.warning( + "Request was successful but value for {} was not present in response".format( + response_key + ) + ) + setattr(self, response_key, response_value) + except simplejson.errors.JSONDecodeError: + response_data = response.text + + logger.debug("{}\n".format(response_data)) + + self.response_data = response_data + self.status_code = response.status_code + + +def response_iterator(response_content, *keys): + """helper function to search a response for return_data keys""" + props = list(keys) + + key = props.pop(0) + prop = response_content.get(key, None) + + if isinstance(prop, str) or len(props) <= 0: + return prop + elif isinstance(prop, list): + if isinstance(key, int): + return response_iterator(prop[key], *props) + else: + for x in prop: + return response_iterator(x, *props) + elif isinstance(prop, dict): + return response_iterator(prop, *props) + else: + return None diff --git a/onap-client/onap_client/config.py b/onap-client/onap_client/config.py new file mode 100644 index 0000000..9e6bd35 --- /dev/null +++ b/onap-client/onap_client/config.py @@ -0,0 +1,99 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import distutils.sysconfig +import logging as logger +import os +import yaml + +PATH = "{}/onap_client".format(distutils.sysconfig.PREFIX) +PAYLOADS_DIR = "{}/payloads".format(PATH) +APPLICATION_ID = "robot-ete" +CONFIG_ENV = os.environ.get("OC_CONFIG") +CONFIG_FILE = CONFIG_ENV or "/etc/onap_client/config.yaml" + + +class Config: + class ConfigClient: + def __init__(self, config_dict): + self.config = config_dict + + def __getattr__(self, attr): + return self.config.get(attr, None) + + def __init__(self, config_file): + self.config = {} + self.config_file = config_file + + def __getattr__(self, attr): + item = self.config.get(attr, None) + if isinstance(item, str): + return item + elif isinstance(item, dict): + return self.ConfigClient(item) + else: + return None + + def load(self, *keys): + if self.config_file and self.config_file != "NONE": + try: + with open(self.config_file, "r") as f: + config_data = yaml.safe_load(f) + except FileNotFoundError: + logger.warn( + "Config file {} not found, using default".format(self.config_file) + ) + else: + with open("{}/config.example.yaml".format(PATH), "r") as f: + config_data = yaml.safe_load(f) + + self.config = config_data + for key in keys: + self.config = self.config.get(key, {}) + + +def load_config(config_file, *config_args): + config = Config(config_file) + config.load(*config_args) + + return config + + +APP_CONFIG = load_config(CONFIG_FILE, "onap_client") +LOG = logger +log_level = getattr(LOG, APP_CONFIG.LOG_LEVEL.upper()) +LOG.basicConfig(format="%(asctime)s %(message)s", level=log_level) diff --git a/onap-client/onap_client/engine.py b/onap-client/onap_client/engine.py new file mode 100644 index 0000000..7543783 --- /dev/null +++ b/onap-client/onap_client/engine.py @@ -0,0 +1,216 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import argparse +import json + +from onap_client.resource import Resource +from onap_client.config import LOG as logger +from onap_client.client.clients import import_submodules +from onap_client.exceptions import InvalidSpecException, ResourceTypeNotFoundException + + +def dumper(obj): + try: + return obj.toJSON() + except: # noqa: E722 + return str(obj) + + +def list_spec_resources(): + for subclass in Resource.__subclasses__(): + print(subclass.resource_name) + + +def show_resource_spec(resource_name): + for subclass in Resource.__subclasses__(): + if resource_name == subclass.resource_name: + print(json.dumps(subclass.spec, default=dumper, indent=4)) + return subclass.spec + + print( + "Resource {} not found. This is the list of available resources:".format( + resource_name + ) + ) + list_spec_resources() + + +def load_spec(input_spec, validate_only=False): + try: + with open(input_spec, "r") as f: + jdata = json.loads(f.read()) + except json.decoder.JSONDecodeError: + print("{} is not valid json, exiting...".format(input_spec)) + raise + + engine = SpecEngine() + return engine.load_spec(jdata, validate_only=validate_only) + + +def spec_cli(args): + parser = argparse.ArgumentParser(description="Spec Engine CLI") + + parser.add_argument( + "--load-spec", + required=False, + help="Load a local spec file into the ONAP client spec engine.", + ) + + parser.add_argument( + "--validate-spec", + required=False, + help="Validates a local spec file for the spec engine.", + ) + + parser.add_argument( + "--show-resource-spec", required=False, help="Show spec for a given resource." + ) + + parser.add_argument( + "--list-spec-resources", + action="store_true", + required=False, + help="List available spec resources.", + ) + + arguments = parser.parse_args(args) + + if arguments.list_spec_resources: + list_spec_resources() + elif arguments.show_resource_spec: + show_resource_spec(arguments.show_resource_spec) + elif arguments.validate_spec: + print(json.dumps(load_spec(arguments.validate_spec, validate_only=True), indent=4)) + elif arguments.load_spec: + load_spec(arguments.load_spec) + + +class SpecEngine: + def __init__(self): + self.initialize() + self.spec = {} + + def initialize(self): + import_submodules("onap_client") + + def load_spec(self, spec, distribute=True, validate_only=False): + # print("loading spec {}".format(spec)) + self.spec = resolve_spec(spec) + self.validate(self.spec.get("spec", {})) + + if not validate_only: + self._create(self.spec.get("spec", {}), distribute) + + return self.spec + + def validate(self, spec): + if not isinstance(spec, list): + raise InvalidSpecException( + "Input spec to spec engine must be a list, but is type {}".format( + type(spec) + ) + ) + + for item_spec in spec: + if not isinstance(item_spec, dict): + raise InvalidSpecException( + "Items in input spec to engine must be dict, but is type {}".format( + type(item_spec) + ) + ) + resource_type = item_spec.get("type") + if not resource_type: + raise InvalidSpecException( + "Items in input spec must contain key/value item for 'type:'" + ) + resource_spec = item_spec.get("resource_spec") + if not resource_spec: + raise InvalidSpecException( + "Items in input spec must contain key/value item for 'resource_spec:'" + ) + subclass = get_resource_subclass(resource_type) + if not subclass: + raise ResourceTypeNotFoundException( + "Resource type {} was not found".format(resource_type) + ) + subclass.validate(resource_spec) + + def _create(self, spec, distribute): + full_engine_spec = [] + for item_spec in spec: + resource_type = item_spec.get("type") + resource_spec = item_spec.get("resource_spec") + subclass = get_resource_subclass(resource_type) + if not subclass: + raise ResourceTypeNotFoundException( + "Resource type {} was not found".format(resource_type) + ) + full_spec = subclass.validate(resource_spec) + logger.debug(json.dumps(full_spec, indent=4)) + subclass.create_from_spec(full_spec, submit=distribute) + full_engine_spec.append({"type": resource_type, "resource_spec": full_spec}) + + logger.info(json.dumps(full_engine_spec, indent=4)) + + +def resolve_spec(spec_dict): + specs = spec_dict.get("spec") + parameters = spec_dict.get("parameters", {}) + + for param_name, param_val in parameters.items(): + specs = replace(specs, "{{{{{}}}}}".format(param_name), param_val) + + spec_dict["spec"] = specs + return spec_dict + + +def replace(data, match, repl): + if isinstance(data, dict): + return {k: replace(v, match, repl) for k, v in data.items()} + elif isinstance(data, list): + return [replace(i, match, repl) for i in data] + else: + return repl if data == match else data + + +def get_resource_subclass(subclass_name): + for subclass in Resource.__subclasses__(): + if subclass.resource_name == subclass_name: + return subclass + + return None diff --git a/onap-client/onap_client/exceptions.py b/onap-client/onap_client/exceptions.py new file mode 100644 index 0000000..76bb77e --- /dev/null +++ b/onap-client/onap_client/exceptions.py @@ -0,0 +1,144 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + + +class MissingAttributeException(Exception): + pass + + +class RequestFailure(Exception): + pass + + +class FilesRequestFailure(Exception): + pass + + +class CatalogItemNotFound(Exception): + pass + + +class InputNotFoundException(Exception): + pass + + +class PropertyNotFoundException(Exception): + pass + + +class MissingInputException(Exception): + pass + + +class ResourceNotFoundException(Exception): + pass + + +class UnknownGroupException(Exception): + pass + + +class UnknownPolicyException(Exception): + pass + + +class InvalidSpecException(Exception): + pass + + +class ResourceAlreadyExistsException(Exception): + pass + + +class ResourceTypeNotFoundException(Exception): + pass + + +class ResourceIDNotFoundException(Exception): + pass + + +class SORequestStatusUnavailable(Exception): + pass + + +class SORequestFailed(Exception): + pass + + +class SORequestTimeout(Exception): + pass + + +class ServiceInstanceNotFound(Exception): + pass + + +class VNFComponentNotFound(Exception): + pass + + +class VNFInstanceNotFound(Exception): + pass + + +class ModuleInstanceNotFound(Exception): + pass + + +class NoArtifactFoundInModel(Exception): + pass + + +class ModuleModelNameNotFound(Exception): + pass + + +class DistributionNotFound(Exception): + pass + + +class DistributionFailure(Exception): + pass + + +class DistributionTimeout(Exception): + pass + + +class TenantNotFound(Exception): + pass diff --git a/onap-client/onap_client/lib.py b/onap-client/onap_client/lib.py new file mode 100644 index 0000000..78da11b --- /dev/null +++ b/onap-client/onap_client/lib.py @@ -0,0 +1,124 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import uuid +from datetime import datetime, timedelta + +from onap_client import exceptions +from onap_client.client.request import RequestHandler, APICatalogRequestObject + + +def make_request(catalog_item, **kwargs): + """Makes a request using by merging an APICatalogResource and + kwargs to fill in the required parameters + + :catalog_item: APICatalogResource + :kwargs: key/value to fill in data for APICatalogResource parameters + + :return: ResponseHandler object with response data from request + """ + request_input = validate_request(catalog_item, kwargs) + + catalog_request = APICatalogRequestObject(catalog_item, **request_input,) + + request_handler = RequestHandler(catalog_request) + + response_handler = request_handler.make_request() + + if not response_handler.success: + raise exceptions.RequestFailure( + "Failed making request for catalog item {}: {}".format( + catalog_item.catalog_resource_name, + response_handler.response_data + ) + ) + + return response_handler + + +def validate_request(catalog_item, kwargs): + request_input = {} + + request_input["payload_parameters"] = validate_parameters( + catalog_item.payload_parameters, kwargs + ) + request_input["uri_parameters"] = validate_parameters( + catalog_item.uri_parameters, kwargs + ) + request_input["file_parameters"] = validate_parameters( + catalog_item.file_parameters, kwargs + ) + request_input["header_parameters"] = validate_parameters( + catalog_item.header_parameters, kwargs + ) + request_input["payload_path"] = validate_parameters( + catalog_item.payload_path, kwargs + ) + + return request_input + + +def validate_parameters(catalog_item_parameters, kwargs): + values = {} + + if not valid_input(catalog_item_parameters, kwargs.keys()): + raise exceptions.MissingAttributeException( + "Missing parameters for request {}".format( + set(catalog_item_parameters) - set(kwargs.keys()) + ) + ) + else: + for param in catalog_item_parameters: + values[param] = kwargs.get(param) + + return values + + +def valid_input(required_attributes, input_attributes): + return set(required_attributes).issubset(input_attributes) + + +def generate_dummy_string(start="", random_length=4): + rand_string = str(uuid.uuid4())[0:random_length] + return "{}{}".format(start, rand_string) + + +def generate_dummy_date(days=0): + tmpdate = datetime.now() + timedelta(days=days) + return "{}/{}/{}".format( + "{:02d}".format(tmpdate.month), "{:02d}".format(tmpdate.day), tmpdate.year + ) diff --git a/onap-client/onap_client/resource.py b/onap-client/onap_client/resource.py new file mode 100644 index 0000000..0af0fad --- /dev/null +++ b/onap-client/onap_client/resource.py @@ -0,0 +1,189 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import inspect + +from abc import ABC, abstractmethod +from onap_client.exceptions import InvalidSpecException + + +class Resource(ABC): + resource_name = "abstract" + spec = {} + + def __init__(self, input): + self.attributes = {} + + attributes = self._create(input) + self.resolve_attributes(attributes) + + self._post_create() + + def __getattr__(self, attr): + return self.attributes.get(attr, None) + + @abstractmethod + def _create(self, input): + pass + + @abstractmethod + def _post_create(self): + pass + + @abstractmethod + def _submit(self): + pass + + @classmethod + def validate(cls, input, spec=None): + """Validates that an input dictionary spec + is valid according to a provided class spec. + + Recursively walksdown and checks if all required attributes are present, and + attribute types match spec types. + + Returns complete spec with all attributes. + """ + valid_spec = {} + + if not isinstance(input, dict): + raise InvalidSpecException("input spec was not a dictionary") + + if not spec: + spec = cls.spec + + for k, v in input.items(): + if not spec.get(k): + raise InvalidSpecException("Unknown property found: {}".format(k)) + + for k, v in spec.items(): + property_name = k + property_type = v.get("type") + property_required = v.get("required") + property_default = v.get("default", default_empty_value(property_type)) + + input_property = validate_property( + input, property_name, property_required, property_default, property_type + ) + + if ( + property_type == dict + and input_property != property_default + and v.get("nested") + ): + property_value = cls.validate(input_property, v.get("nested")) + elif property_type == list: + list_property_type = v.get("list_item") + list_spec = [] + for item in input_property: + if type(item) != list_property_type: + raise InvalidSpecException( + "list item {} not match type {}".format( + item, list_property_type + ) + ) + if list_property_type == str: + list_spec.insert(0, item) + else: + list_spec.insert(0, cls.validate(item, v.get("nested", {}))) + + property_value = list_spec + else: + property_value = input_property + + valid_spec[property_name] = property_value + + return valid_spec + + @classmethod + def create_from_spec(cls, spec, submit=True): + input_args = [] + + arguments = inspect.getfullargspec(cls).args + arguments.pop(0) + + for argument in arguments: + input_args.append(spec.get(argument)) + + instance = cls(*input_args) + + if submit: + instance._submit() + + return instance + + def resolve_attributes(self, attributes): + for key, val in attributes.items(): + self.attributes[key] = val + + def print(self): + for k, v in self.attributes.items(): + val = str(v) + value = val[:50] + "..." if len(val) > 50 else val + print("{}: {}".format(k, value)) + + +def validate_property( + input_spec, property_name, property_required, property_default, property_type +): + input_property = input_spec.get(property_name) + if not input_property: + if property_required: + raise InvalidSpecException( + "required property {} not found in input spec".format(property_name) + ) + else: + input_property = property_default + elif type(input_property) != property_type: + raise InvalidSpecException( + "input property {} not match type {}".format(property_name, property_type) + ) + + return input_property + + +def default_empty_value(property_type): + if property_type == str: + return None + elif property_type == list: + return [] + elif property_type == dict: + return {} + elif property_type == bool: + return False + else: + return None diff --git a/onap-client/onap_client/sdc/__init__.py b/onap-client/onap_client/sdc/__init__.py new file mode 100644 index 0000000..d8028f6 --- /dev/null +++ b/onap-client/onap_client/sdc/__init__.py @@ -0,0 +1,40 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +from onap_client.config import APP_CONFIG + +SDC_PROPERTIES = APP_CONFIG.sdc diff --git a/onap-client/onap_client/sdc/catalog/__init__.py b/onap-client/onap_client/sdc/catalog/__init__.py new file mode 100644 index 0000000..d35e96a --- /dev/null +++ b/onap-client/onap_client/sdc/catalog/__init__.py @@ -0,0 +1,37 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + diff --git a/onap-client/onap_client/sdc/catalog/license_model_catalog.py b/onap-client/onap_client/sdc/catalog/license_model_catalog.py new file mode 100644 index 0000000..278645d --- /dev/null +++ b/onap-client/onap_client/sdc/catalog/license_model_catalog.py @@ -0,0 +1,318 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import uuid +from functools import partial + +from onap_client import sdc +from onap_client import config +from onap_client.sdc.client import SDCClient + +PAYLOADS_DIR = config.PAYLOADS_DIR +sdc_properties = sdc.SDC_PROPERTIES +application_id = config.APPLICATION_ID + + +class LicenseModelClient(SDCClient): + @property + def catalog_resources(self): + return CATALOG_RESOURCES + + @property + def namespace(self): + return "license_model" + + +CATALOG_RESOURCES = { + "ADD_LICENSE_MODEL": { + "verb": "POST", + "description": "creates a license model in the SDC catalog", + "uri": partial( + "{endpoint}{service_path}".format, + endpoint=sdc_properties.SDC_BE_ONBOARD_ENDPOINT, + service_path=sdc_properties.SDC_VENDOR_LICENSE_MODEL_PATH, + ), + "payload": "{}/license_model.jinja".format(PAYLOADS_DIR), + "payload-parameters": ["vendor_name"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "return_data": { + "license_model_id": ("itemId",), + "license_model_version_id": ("version", "id"), + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "ADD_KEY_GROUP": { + "verb": "POST", + "description": "Adds a key group to a license model", + "uri": partial( + "{endpoint}{service_path}/{license_model_id}/versions/{license_model_version_id}/license-key-groups".format, + endpoint=sdc_properties.SDC_BE_ONBOARD_ENDPOINT, + service_path=sdc_properties.SDC_VENDOR_LICENSE_MODEL_PATH, + ), + "uri-parameters": ["license_model_id", "license_model_version_id"], + "payload": "{}/key_group.jinja".format(PAYLOADS_DIR), + "payload-parameters": [ + "license_start_date", + "license_end_date", + "key_group_name", + ], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "return_data": {"key_group_id": ("value",)}, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "ADD_ENTITLEMENT_POOL": { + "verb": "POST", + "description": "Adds an entitlement pool to a license model", + "uri": partial( + "{endpoint}{service_path}/{license_model_id}/versions/{license_model_version_id}/entitlement-pools".format, + endpoint=sdc_properties.SDC_BE_ONBOARD_ENDPOINT, + service_path=sdc_properties.SDC_VENDOR_LICENSE_MODEL_PATH, + ), + "uri-parameters": ["license_model_id", "license_model_version_id"], + "payload": "{}/entitlement_pool.jinja".format(PAYLOADS_DIR), + "payload-parameters": [ + "license_start_date", + "license_end_date", + "entitlement_pool_name", + ], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "return_data": {"entitlement_pool_id": ("value",)}, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "ADD_FEATURE_GROUP": { + "verb": "POST", + "description": "Adds an feature group to a license model", + "uri": partial( + "{endpoint}{service_path}/{license_model_id}/versions/{license_model_version_id}/feature-groups".format, + endpoint=sdc_properties.SDC_BE_ONBOARD_ENDPOINT, + service_path=sdc_properties.SDC_VENDOR_LICENSE_MODEL_PATH, + ), + "uri-parameters": ["license_model_id", "license_model_version_id"], + "payload": "{}/feature_group.jinja".format(PAYLOADS_DIR), + "payload-parameters": [ + "feature_group_name", + "key_group_id", + "entitlement_pool_id", + "manufacturer_reference_number", + ], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "return_data": {"feature_group_id": ("value",)}, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "ADD_LICENSE_AGREEMENT": { + "verb": "POST", + "description": "Adds an license agreement to a license model", + "uri": partial( + "{endpoint}{service_path}/{license_model_id}/versions/{license_model_version_id}/license-agreements".format, + endpoint=sdc_properties.SDC_BE_ONBOARD_ENDPOINT, + service_path=sdc_properties.SDC_VENDOR_LICENSE_MODEL_PATH, + ), + "uri-parameters": ["license_model_id", "license_model_version_id"], + "payload": "{}/license_agreement.jinja".format(PAYLOADS_DIR), + "payload-parameters": ["feature_group_id", "license_agreement_name"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "return_data": {"license_agreement_id": ("value",)}, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "SUBMIT_LICENSE_MODEL": { + "verb": "PUT", + "description": "Submits a license model", + "uri": partial( + "{endpoint}{service_path}/{license_model_id}/versions/{license_model_version_id}/actions".format, + endpoint=sdc_properties.SDC_BE_ONBOARD_ENDPOINT, + service_path=sdc_properties.SDC_VENDOR_LICENSE_MODEL_PATH, + ), + "uri-parameters": ["license_model_id", "license_model_version_id"], + "payload": "{}/action.jinja".format(PAYLOADS_DIR), + "payload-parameters": ["action"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "GET_LICENSE_MODEL": { + "verb": "GET", + "description": "Returns a license model", + "uri": partial( + "{endpoint}{service_path}/{license_model_id}/versions/{license_model_version_id}".format, + endpoint=sdc_properties.SDC_BE_ONBOARD_ENDPOINT, + service_path=sdc_properties.SDC_VENDOR_LICENSE_MODEL_PATH, + ), + "uri-parameters": ["license_model_id", "license_model_version_id"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "return_data": { + "vendor_name": ("vendorName",), + "license_model_id": ("id",), + "description": ("description",), + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "GET_LICENSE_MODEL_VERSION_ATTRIBUTE": { + "verb": "GET", + "description": "Returns an attribute for a license model (license-agreements, features-groups, etc...)", + "uri": partial( + "{endpoint}{service_path}/{license_model_id}/versions/{license_model_version_id}/{attribute}".format, + endpoint=sdc_properties.SDC_BE_ONBOARD_ENDPOINT, + service_path=sdc_properties.SDC_VENDOR_LICENSE_MODEL_PATH, + ), + "uri-parameters": ["license_model_id", "license_model_version_id", "attribute"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "GET_LICENSE_MODEL_VERSIONS": { + "verb": "GET", + "description": "Returns the version list for a license model", + "uri": partial( + "{endpoint}{service_path}/{license_model_id}/versions".format, + endpoint=sdc_properties.SDC_BE_ONBOARD_ENDPOINT, + service_path=sdc_properties.SDC_VENDOR_ITEMS_PATH, + ), + "uri-parameters": ["license_model_id"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "GET_LICENSE_MODELS": { + "verb": "GET", + "description": "Returns the full list of license models from SDC", + "uri": partial( + "{endpoint}{service_path}?&itemType=vlm".format, + endpoint=sdc_properties.SDC_BE_ONBOARD_ENDPOINT, + service_path=sdc_properties.SDC_VENDOR_ITEMS_PATH, + ), + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "return_data": {"results": ("results",)}, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, +} diff --git a/onap-client/onap_client/sdc/catalog/service_catalog.py b/onap-client/onap_client/sdc/catalog/service_catalog.py new file mode 100644 index 0000000..1d30725 --- /dev/null +++ b/onap-client/onap_client/sdc/catalog/service_catalog.py @@ -0,0 +1,426 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import uuid +from functools import partial + +from onap_client import sdc +from onap_client import config +from onap_client.sdc.client import SDCClient + +PAYLOADS_DIR = config.PAYLOADS_DIR +sdc_properties = sdc.SDC_PROPERTIES +application_id = config.APPLICATION_ID + + +class ServiceCatalog(SDCClient): + @property + def catalog_resources(self): + return CATALOG_RESOURCES + + @property + def namespace(self): + return "service" + + +CATALOG_RESOURCES = { + "ADD_CATALOG_SERVICE": { + "verb": "POST", + "description": "Creates a Service in the SDC catalog", + "uri": partial( + "{endpoint}{service_path}".format, + endpoint=sdc_properties.SDC_BE_ENDPOINT, + service_path=sdc_properties.SDC_CATALOG_SERVICES_PATH, + ), + "payload": "{}/catalog_service.jinja".format(PAYLOADS_DIR), + "payload-parameters": [ + "service_name", + "instantiation_type", + "contact_id", + "category_name", + "category_id", + "category_name_lower", + "category_name_icon", + "tag", + "project_code", + "environment_context", + "ecomp_generated_naming", + "description", + "service_type", + "service_role", + "naming_policy", + ], + "success_code": 201, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "return_data": {"catalog_service_id": ("uniqueId",)}, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "ADD_RESOURCE_INSTANCE": { + "verb": "POST", + "description": "Attaches a Resource to a Service", + "uri": partial( + "{endpoint}{service_path}/{catalog_service_id}/resourceInstance".format, + endpoint=sdc_properties.SDC_BE_ENDPOINT, + service_path=sdc_properties.SDC_CATALOG_SERVICES_PATH, + ), + "uri-parameters": ["catalog_service_id"], + "payload": "{}/resource_instance.jinja".format(PAYLOADS_DIR), + "payload-parameters": [ + "milli_timestamp", + "catalog_resource_id", + "catalog_resource_name", + "originType", + "posX", + "posY", + ], + "success_code": 201, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "return_data": {"catalog_resource_instance_id": ("uniqueId",)}, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "CHECKIN_SERVICE": { + "verb": "POST", + "description": "Checks a service into the SDC Catalog", + "uri": partial( + "{endpoint}{service_path}/{catalog_service_id}/lifecycleState/checkin".format, + endpoint=sdc_properties.SDC_BE_ENDPOINT, + service_path=sdc_properties.SDC_CATALOG_SERVICES_PATH, + ), + "uri-parameters": ["catalog_service_id"], + "payload": "{}/user_remarks.jinja".format(PAYLOADS_DIR), + "payload-parameters": ["user_remarks"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "REQUEST_SERVICE_CERTIFICATION": { + "verb": "POST", + "description": "Requests certification of a service into the SDC Catalog", + "uri": partial( + "{endpoint}{service_path}/{catalog_service_id}/lifecycleState/certificationRequest".format, + endpoint=sdc_properties.SDC_BE_ENDPOINT, + service_path=sdc_properties.SDC_CATALOG_SERVICES_PATH, + ), + "uri-parameters": ["catalog_service_id"], + "payload": "{}/user_remarks.jinja".format(PAYLOADS_DIR), + "payload-parameters": ["user_remarks"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "START_SERVICE_CERTIFICATION": { + "verb": "POST", + "description": "Starts certification of a service into the SDC Catalog", + "uri": partial( + "{endpoint}{service_path}/{catalog_service_id}/lifecycleState/startCertification".format, + endpoint=sdc_properties.SDC_BE_ENDPOINT, + service_path=sdc_properties.SDC_CATALOG_SERVICES_PATH, + ), + "uri-parameters": ["catalog_service_id"], + "payload": "{}/user_remarks.jinja".format(PAYLOADS_DIR), + "payload-parameters": ["user_remarks"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_TESTER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "FINISH_SERVICE_CERTIFICATION": { + "verb": "POST", + "description": "Finishes certification of a service from the SDC Catalog", + "uri": partial( + "{endpoint}{service_path}/{catalog_service_id}/lifecycleState/certify".format, + endpoint=sdc_properties.SDC_BE_ENDPOINT, + service_path=sdc_properties.SDC_CATALOG_SERVICES_PATH, + ), + "uri-parameters": ["catalog_service_id"], + "payload": "{}/user_remarks.jinja".format(PAYLOADS_DIR), + "payload-parameters": ["user_remarks"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_TESTER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "return_data": {"catalog_service_id": ("uniqueId",)}, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "APPROVE_SERVICE_CERTIFICATION": { + "verb": "POST", + "description": "Approves a service from the SDC Catalog", + "uri": partial( + "{endpoint}{service_path}/{catalog_service_id}/distribution-state/approve".format, + endpoint=sdc_properties.SDC_BE_ENDPOINT, + service_path=sdc_properties.SDC_CATALOG_SERVICES_PATH, + ), + "uri-parameters": ["catalog_service_id"], + "payload": "{}/user_remarks.jinja".format(PAYLOADS_DIR), + "payload-parameters": ["user_remarks"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_GOVERNOR_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "DISTRIBUTE_SDC_SERVICE": { + "verb": "POST", + "description": "Distributes a service from the SDC Catalog", + "uri": partial( + "{endpoint}{service_path}/{catalog_service_id}/distribution/PROD/activate".format, + endpoint=sdc_properties.SDC_BE_ENDPOINT, + service_path=sdc_properties.SDC_CATALOG_SERVICES_PATH, + ), + "uri-parameters": ["catalog_service_id"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_OPS_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "ADD_CATALOG_SERVICE_PROPERTY": { + "verb": "POST", + "description": "Add a property value for a VF in a Service", + "uri": partial( + "{endpoint}{service_path}/{catalog_service_id}/resourceInstance/{catalog_resource_instance_id}/properties".format, + endpoint=sdc_properties.SDC_BE_ENDPOINT, + service_path=sdc_properties.SDC_CATALOG_RESOURCES_PATH, + ), + "uri-parameters": ["catalog_service_id", "catalog_resource_instance_id"], + "payload": "{}/catalog_service_property.jinja".format(PAYLOADS_DIR), + "payload-parameters": [ + "unique_id", + "parent_unique_id", + "owner_id", + "input_name", + "input_value", + "schema_type", + "property_type", + ], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "ADD_CATALOG_SERVICE_INPUT": { + "verb": "POST", + "description": "Add an input value for a VF in a Service", + "uri": partial( + "{endpoint}{service_path}/{catalog_service_id}/resourceInstance/{catalog_resource_instance_id}/inputs".format, + endpoint=sdc_properties.SDC_BE_ENDPOINT, + service_path=sdc_properties.SDC_CATALOG_RESOURCES_PATH, + ), + "uri-parameters": ["catalog_service_id", "catalog_resource_instance_id"], + "payload": "{}/catalog_service_property.jinja".format(PAYLOADS_DIR), + "payload-parameters": [ + "unique_id", + "parent_unique_id", + "owner_id", + "input_name", + "input_value", + ], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "GET_SDC_SERVICE": { + "verb": "GET", + "description": "Gets a service from the SDC Catalog", + "uri": partial( + "{endpoint}{service_path}/{catalog_service_id}".format, + endpoint=sdc_properties.SDC_BE_ENDPOINT, + service_path=sdc_properties.SDC_CATALOG_SERVICES_PATH, + ), + "uri-parameters": ["catalog_service_id"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "GET_SERVICES": { + "verb": "GET", + "description": "Get all services in the SDC catalog", + "uri": partial( + "{endpoint}{service_path}".format, + endpoint=sdc_properties.SDC_BE_ENDPOINT, + service_path=sdc_properties.SDC_SCREEN_PATH, + ), + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "return_data": {"services": ("services",)}, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "GET_SERVICE_DISTRIBUTION": { + "verb": "GET", + "description": "Gets the distribution for a service from the SDC Catalog", + "uri": partial( + "{endpoint}{service_path}/{distribution_service_id}/distribution".format, + endpoint=sdc_properties.SDC_BE_ENDPOINT, + service_path=sdc_properties.SDC_CATALOG_SERVICES_PATH, + ), + "uri-parameters": ["distribution_service_id"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "GET_SERVICE_DISTRIBUTION_DETAILS": { + "verb": "GET", + "description": "Gets the distribution details for a service from the SDC Catalog", + "uri": partial( + "{endpoint}{service_path}/distribution/{distribution_id}".format, + endpoint=sdc_properties.SDC_BE_ENDPOINT, + service_path=sdc_properties.SDC_CATALOG_SERVICES_PATH, + ), + "uri-parameters": ["distribution_id"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, +} diff --git a/onap-client/onap_client/sdc/catalog/vnf_catalog.py b/onap-client/onap_client/sdc/catalog/vnf_catalog.py new file mode 100644 index 0000000..c4fd3da --- /dev/null +++ b/onap-client/onap_client/sdc/catalog/vnf_catalog.py @@ -0,0 +1,410 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import uuid +from functools import partial + +from onap_client import sdc +from onap_client import config +from onap_client.sdc.client import SDCClient + +PAYLOADS_DIR = config.PAYLOADS_DIR +sdc_properties = sdc.SDC_PROPERTIES +application_id = config.APPLICATION_ID + + +class VNFCatalog(SDCClient): + @property + def catalog_resources(self): + return CATALOG_RESOURCES + + @property + def namespace(self): + return "vnf" + + +CATALOG_RESOURCES = { + "ADD_CATALOG_RESOURCE": { + "verb": "POST", + "description": "Adds a VNF to the SDC catalog", + "uri": partial( + "{endpoint}{service_path}".format, + endpoint=sdc_properties.SDC_BE_ENDPOINT, + service_path=sdc_properties.SDC_CATALOG_RESOURCES_PATH, + ), + "payload": "{}/catalog_resource.jinja".format(PAYLOADS_DIR), + "payload-parameters": [ + "software_product_id", + "vnf_name", + "vendor_name", + "resource_type", + ], + "success_code": 201, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "return_data": {"catalog_resource_id": ("uniqueId",)}, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "CERTIFY_CATALOG_RESOURCE": { + "verb": "POST", + "description": "Certifies a VNF in the SDC catalog", + "uri": partial( + "{endpoint}{service_path}/{catalog_resource_id}/lifecycleState/certify".format, + endpoint=sdc_properties.SDC_BE_ENDPOINT, + service_path=sdc_properties.SDC_CATALOG_RESOURCES_PATH, + ), + "uri-parameters": ["catalog_resource_id"], + "payload": "{}/user_remarks.jinja".format(PAYLOADS_DIR), + "payload-parameters": ["user_remarks"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "return_data": {"catalog_resource_id": ("uniqueId",)}, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "ADD_CATALOG_RESOURCE_INPUT": { + "verb": "POST", + "description": "Adds an input value for a VNF", + "uri": partial( + "{endpoint}{service_path}/{catalog_resource_id}/update/inputs".format, + endpoint=sdc_properties.SDC_BE_ENDPOINT, + service_path=sdc_properties.SDC_CATALOG_RESOURCES_PATH, + ), + "payload": "{}/catalog_vnf_input.jinja".format(PAYLOADS_DIR), + "payload-parameters": [ + "input_default_value", + "input_name", + "input_parent_unique_id", + "input_unique_id", + "input_owner_id", + ], + "uri-parameters": ["catalog_resource_id"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "ADD_CATALOG_RESOURCE_PROPERTY": { + "verb": "POST", + "description": "Adds an property value for a VNF", + "uri": partial( + "{endpoint}{service_path}/{catalog_resource_id}/resourceInstance/{catalog_resource_instance_id}/inputs".format, + endpoint=sdc_properties.SDC_BE_ENDPOINT, + service_path=sdc_properties.SDC_CATALOG_RESOURCES_PATH, + ), + "payload": "{}/catalog_vnf_property.jinja".format(PAYLOADS_DIR), + "payload-parameters": [ + "unique_id", + "parent_unique_id", + "owner_id", + "property_name", + "property_default_value", + "schema_type", + "property_type", + ], + "uri-parameters": ["catalog_resource_id", "catalog_resource_instance_id"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "ADD_CATALOG_RESOURCE_POLICY": { + "verb": "POST", + "description": "Adds an policy resource to a VNF", + "uri": partial( + "{endpoint}{service_path}/{catalog_resource_id}/policies/{catalog_policy_name}".format, + endpoint=sdc_properties.SDC_BE_ENDPOINT, + service_path=sdc_properties.SDC_CATALOG_RESOURCES_PATH, + ), + "uri-parameters": ["catalog_resource_id", "catalog_policy_name"], + "success_code": 201, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "return_data": {"catalog_resource_id": ("uniqueId",)}, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "ADD_CATALOG_POLICY_PROPERTY": { + "verb": "PUT", + "description": "Adds a property to a policy for a VNF", + "uri": partial( + "{endpoint}{service_path}/{catalog_resource_id}/policies/{catalog_policy_id}/properties".format, + endpoint=sdc_properties.SDC_BE_ENDPOINT, + service_path=sdc_properties.SDC_CATALOG_RESOURCES_PATH, + ), + "uri-parameters": ["catalog_resource_id", "catalog_policy_id"], + "payload": "{}/catalog_vnf_policy_property.jinja".format(PAYLOADS_DIR), + "payload-parameters": [ + "unique_id", + "property_name", + "property_default_value", + "description", + "property_type", + ], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "ADD_CATALOG_RESOURCE_GROUP": { + "verb": "POST", + "description": "Adds an group resource to a VNF", + "uri": partial( + "{endpoint}{service_path}/{catalog_resource_id}/groups/{catalog_group_name}".format, + endpoint=sdc_properties.SDC_BE_ENDPOINT, + service_path=sdc_properties.SDC_CATALOG_RESOURCES_PATH, + ), + "uri-parameters": ["catalog_resource_id", "catalog_group_name"], + "success_code": 201, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "return_data": {"catalog_resource_id": ("uniqueId",)}, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "ADD_CATALOG_GROUP_PROPERTY": { + "verb": "PUT", + "description": "Adds a property to a group for a VNF", + "uri": partial( + "{endpoint}{service_path}/{catalog_resource_id}/groups/{catalog_group_id}/properties".format, + endpoint=sdc_properties.SDC_BE_ENDPOINT, + service_path=sdc_properties.SDC_CATALOG_RESOURCES_PATH, + ), + "uri-parameters": ["catalog_resource_id", "catalog_group_id"], + "payload": "{}/catalog_vnf_group_property.jinja".format(PAYLOADS_DIR), + "payload-parameters": [ + "unique_id", + "property_name", + "property_default_value", + "description", + "property_type", + "owner_id", + "parent_unique_id", + ], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "ADD_GROUP_TO_INSTANCE": { + "verb": "POST", + "description": "Associate a group with a Catalog Instance", + "uri": partial( + "{endpoint}{service_path}/{catalog_resource_id}/groups/{catalog_group_id}/members".format, + endpoint=sdc_properties.SDC_BE_ENDPOINT, + service_path=sdc_properties.SDC_CATALOG_RESOURCES_PATH, + ), + "payload": "{}/catalog_vnf_group.jinja".format(PAYLOADS_DIR), + "payload-parameters": ["instance_id"], + "uri-parameters": ["catalog_resource_id", "catalog_group_id"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "ADD_POLICY_TO_INSTANCE": { + "verb": "POST", + "description": "Associate a policy with a Catalog Instance", + "uri": partial( + "{endpoint}{service_path}/{catalog_resource_id}/policies/{catalog_policy_id}/targets".format, + endpoint=sdc_properties.SDC_BE_ENDPOINT, + service_path=sdc_properties.SDC_CATALOG_RESOURCES_PATH, + ), + "payload": "{}/catalog_vnf_policy.jinja".format(PAYLOADS_DIR), + "payload-parameters": ["instance_ids"], + "uri-parameters": ["catalog_resource_id", "catalog_policy_id"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "ADD_RESOURCE_INSTANCE": { + "verb": "POST", + "description": "Attaches a Resource to a VNF", + "uri": partial( + "{endpoint}{service_path}/{catalog_resource_id}/resourceInstance".format, + endpoint=sdc_properties.SDC_BE_ENDPOINT, + service_path=sdc_properties.SDC_CATALOG_RESOURCES_PATH, + ), + "uri-parameters": ["catalog_resource_id"], + "payload": "{}/resource_instance_vnf.jinja".format(PAYLOADS_DIR), + "payload-parameters": [ + "milli_timestamp", + "new_catalog_resource_id", + "new_catalog_resource_name", + "originType", + "posX", + "posY", + ], + "success_code": 201, + "return_data": {"catalog_resource_instance_id": ("uniqueId",)}, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "GET_CATALOG_RESOURCE": { + "verb": "GET", + "description": "Gets a VNF in the SDC catalog", + "uri": partial( + "{endpoint}{service_path}/{catalog_resource_id}".format, + endpoint=sdc_properties.SDC_BE_ENDPOINT, + service_path=sdc_properties.SDC_CATALOG_RESOURCES_PATH, + ), + "uri-parameters": ["catalog_resource_id"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "return_data": {"catalog_resource_name": ("name",)}, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "GET_RESOURCES": { + "verb": "GET", + "description": "Get all resources in the SDC catalog", + "uri": partial( + "{endpoint}{service_path}".format, + endpoint=sdc_properties.SDC_BE_ENDPOINT, + service_path=sdc_properties.SDC_SCREEN_PATH, + ), + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "return_data": {"resources": ("resources",)}, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, +} diff --git a/onap-client/onap_client/sdc/catalog/vsp_catalog.py b/onap-client/onap_client/sdc/catalog/vsp_catalog.py new file mode 100644 index 0000000..65781a6 --- /dev/null +++ b/onap-client/onap_client/sdc/catalog/vsp_catalog.py @@ -0,0 +1,286 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import uuid +from functools import partial + +from onap_client import sdc +from onap_client import config +from onap_client.sdc.client import SDCClient + +PAYLOADS_DIR = config.PAYLOADS_DIR +sdc_properties = sdc.SDC_PROPERTIES +application_id = config.APPLICATION_ID + + +class VSPCatalog(SDCClient): + @property + def catalog_resources(self): + return CATALOG_RESOURCES + + @property + def namespace(self): + return "vsp" + + +CATALOG_RESOURCES = { + "ADD_SOFTWARE_PRODUCT": { + "verb": "POST", + "description": "Creates a VSP in the SDC catalog", + "uri": partial( + "{endpoint}{service_path}".format, + endpoint=sdc_properties.SDC_BE_ONBOARD_ENDPOINT, + service_path=sdc_properties.SDC_VENDOR_SOFTWARE_PRODUCT_PATH, + ), + "payload": "{}/software_product.jinja".format(PAYLOADS_DIR), + "payload-parameters": [ + "software_product_name", + "feature_group_id", + "license_agreement_id", + "vendor_name", + "license_model_id", + "license_model_version_id", + "description", + "category", + "sub_category", + ], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "return_data": { + "software_product_id": ("itemId",), + "software_product_version_id": ("version", "id"), + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "UPLOAD_HEAT_PACKAGE": { + "verb": "POST", + "description": "Uploads a heat zip to a VSP", + "uri": partial( + "{endpoint}{service_path}/{software_product_id}/versions/{software_product_version_id}/orchestration-template-candidate".format, + endpoint=sdc_properties.SDC_BE_ONBOARD_ENDPOINT, + service_path=sdc_properties.SDC_VENDOR_SOFTWARE_PRODUCT_PATH, + ), + "uri-parameters": ["software_product_id", "software_product_version_id"], + "files-parameters": ["file_path", "file_type"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "multipart/form-data", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "VALIDATE_SOFTWARE_PRODUCT": { + "verb": "PUT", + "description": "Validates VSP with Heat Zip", + "uri": partial( + "{endpoint}{service_path}/{software_product_id}/versions/{software_product_version_id}/orchestration-template-candidate/process".format, + endpoint=sdc_properties.SDC_BE_ONBOARD_ENDPOINT, + service_path=sdc_properties.SDC_VENDOR_SOFTWARE_PRODUCT_PATH, + ), + "uri-parameters": ["software_product_id", "software_product_version_id"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "SUBMIT_SOFTWARE_PRODUCT": { + "verb": "PUT", + "description": "Submits Heat Zip to VSP", + "uri": partial( + "{endpoint}{service_path}/{software_product_id}/versions/{software_product_version_id}/actions".format, + endpoint=sdc_properties.SDC_BE_ONBOARD_ENDPOINT, + service_path=sdc_properties.SDC_VENDOR_SOFTWARE_PRODUCT_PATH, + ), + "uri-parameters": ["software_product_id", "software_product_version_id"], + "payload": "{}/action.jinja".format(PAYLOADS_DIR), + "payload-parameters": ["action"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "PACKAGE_SOFTWARE_PRODUCT": { + "verb": "PUT", + "description": "Packages VSP (description needs to be better??)", + "uri": partial( + "{endpoint}{service_path}/{software_product_id}/versions/{software_product_version_id}/actions".format, + endpoint=sdc_properties.SDC_BE_ONBOARD_ENDPOINT, + service_path=sdc_properties.SDC_VENDOR_SOFTWARE_PRODUCT_PATH, + ), + "uri-parameters": ["software_product_id", "software_product_version_id"], + "payload": "{}/action.jinja".format(PAYLOADS_DIR), + "payload-parameters": ["action"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "GET_SOFTWARE_PRODUCT": { + "verb": "GET", + "description": "Gets VSP from Catalog", + "uri": partial( + "{endpoint}{service_path}/{software_product_id}/versions/{software_product_version_id}".format, + endpoint=sdc_properties.SDC_BE_ONBOARD_ENDPOINT, + service_path=sdc_properties.SDC_VENDOR_SOFTWARE_PRODUCT_PATH, + ), + "uri-parameters": ["software_product_id", "software_product_version_id"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "return_data": {"name": ("name",)}, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "GET_SOFTWARE_PRODUCT_VERSIONS": { + "verb": "GET", + "description": "Returns a list of vsp versions", + "uri": partial( + "{endpoint}{service_path}/{software_product_id}/versions".format, + endpoint=sdc_properties.SDC_BE_ONBOARD_ENDPOINT, + service_path=sdc_properties.SDC_VENDOR_ITEMS_PATH, + ), + "uri-parameters": ["software_product_id"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "return_data": { + "software_product_version_id": ("id",), + "description": ("description",), + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "GET_SOFTWARE_PRODUCTS": { + "verb": "GET", + "description": "Returns a list of vsps", + "uri": partial( + "{endpoint}{service_path}?&itemType=vsp".format, + endpoint=sdc_properties.SDC_BE_ONBOARD_ENDPOINT, + service_path=sdc_properties.SDC_VENDOR_ITEMS_PATH, + ), + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "return_data": {"results": ("results",)}, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, + "ADD_VSP_CONTRIBUTER": { + "verb": "PUT", + "description": "Adds a user to a VSP as a contributer", + "uri": partial( + "{endpoint}{service_path}/{software_product_id}/permissions/Contributor".format, + endpoint=sdc_properties.SDC_BE_ONBOARD_ENDPOINT, + service_path=sdc_properties.SDC_VENDOR_ITEMS_PATH, + ), + "uri-parameters": ["software_product_id"], + "payload": "{}/add_vsp_contributer.jinja".format(PAYLOADS_DIR), + "payload-parameters": ["user_id"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": ( + sdc_properties.GLOBAL_SDC_USERNAME, + sdc_properties.GLOBAL_SDC_PASSWORD, + ), + }, +} diff --git a/onap-client/onap_client/sdc/client.py b/onap-client/onap_client/sdc/client.py new file mode 100644 index 0000000..8863e07 --- /dev/null +++ b/onap-client/onap_client/sdc/client.py @@ -0,0 +1,81 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import uuid + +from functools import partial +from onap_client import sdc +from onap_client.client.clients import Client +from onap_client import config + +sdc_properties = sdc.SDC_PROPERTIES +application_id = config.APPLICATION_ID + + +class SDCClient(Client): + @property + def namespace(self): + return "sdc" + + @property + def catalog_resources(self): + return CATALOG_RESOURCES + + +CATALOG_RESOURCES = { + "HEALTH_CHECK": { + "verb": "GET", + "description": "Queries SDC health check endpoint", + "uri": partial( + "{endpoint}{service_path}".format, + endpoint=sdc_properties.SDC_HC_ENDPOINT, + service_path=sdc_properties.SDC_HEALTH_CHECK_PATH, + ), + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "USER_ID": sdc_properties.SDC_DESIGNER_USER_ID, + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": ( + sdc_properties.SDC_DESIGNER_USER_ID, + sdc_properties.SDC_DESIGNER_PASSWORD, + ), + }, +} diff --git a/onap-client/onap_client/sdc/license_model.py b/onap-client/onap_client/sdc/license_model.py new file mode 100644 index 0000000..e5ae740 --- /dev/null +++ b/onap-client/onap_client/sdc/license_model.py @@ -0,0 +1,218 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +from onap_client.lib import generate_dummy_string, generate_dummy_date +from onap_client.resource import Resource +from onap_client.client.clients import Client as SDCClient + +license_model_client = SDCClient().sdc.license_model + + +class LicenseModel(Resource): + resource_name = "LICENSE_MODEL" + spec = { + "vendor_name": { + "type": str, + "required": False, + "default": generate_dummy_string("test_vendor_"), + }, + "mfr_ref_number": { + "type": str, + "required": False, + "default": generate_dummy_string("mfref"), + }, + "entitlement_pool_name": { + "type": str, + "required": False, + "default": generate_dummy_string("test_kg_"), + }, + "key_group_name": { + "type": str, + "required": False, + "default": generate_dummy_string("test_ep_"), + }, + "feature_group_name": { + "type": str, + "required": False, + "default": generate_dummy_string("test_fg_"), + }, + "license_agreement_name": { + "type": str, + "required": False, + "default": generate_dummy_string("test_la_"), + }, + "license_start_date": { + "type": str, + "required": False, + "default": generate_dummy_date(days=1), + }, + "license_end_date": { + "type": str, + "required": False, + "default": generate_dummy_date(days=365), + }, + } + + def __init__( + self, + vendor_name, + mfr_ref_number, + entitlement_pool_name, + key_group_name, + feature_group_name, + license_agreement_name, + license_start_date, + license_end_date, + ): + + license_input = {} + license_input["vendor_name"] = vendor_name + license_input["manufacturer_reference_number"] = mfr_ref_number + license_input["entitlement_pool_name"] = entitlement_pool_name + license_input["key_group_name"] = key_group_name + license_input["feature_group_name"] = feature_group_name + license_input["license_agreement_name"] = license_agreement_name + license_input["license_start_date"] = license_start_date + license_input["license_end_date"] = license_end_date + + super().__init__(license_input) + + def _create(self, license_input): + """Creates a license model object in SDC""" + return create_license_model(license_input) + + def _post_create(self): + pass + + def _submit(self): + """Submits the license model in SDC""" + + license_model_client.submit_license_model(**self.attributes, action="Submit") + + license_model = license_model_client.get_license_model(**self.attributes) + self.attributes["tosca"] = license_model.response_data + + +# TODO +# Break this up into class funcs? +def create_license_model(license_input): + """Creates a license model object in SDC + + :license_input: dictionary with values to input for lm creation + + :return: dictionary of updated values for created lm + """ + kwargs = license_input + license_model = license_model_client.add_license_model(**kwargs) + + kwargs["license_model_id"] = license_model.license_model_id + kwargs["license_model_version_id"] = license_model.license_model_version_id + + key_group = license_model_client.add_key_group(**kwargs) + key_group_id = key_group.key_group_id + + entitlement_pool = license_model_client.add_entitlement_pool(**kwargs) + entitlement_pool_id = entitlement_pool.entitlement_pool_id + + kwargs["entitlement_pool_id"] = entitlement_pool_id + kwargs["key_group_id"] = key_group_id + + feature_group = license_model_client.add_feature_group(**kwargs) + feature_group_id = feature_group.feature_group_id + + kwargs["feature_group_id"] = feature_group_id + + license_agreement = license_model_client.add_license_agreement(**kwargs) + kwargs["license_agreement_id"] = license_agreement.license_agreement_id + + license_model = license_model_client.get_license_model(**kwargs) + kwargs["tosca"] = license_model.response_data + + return kwargs + + +def get_license_model_id(license_model_name): + """GETs license model UUID from SDC + + :license_model_name: name of license model in SDC + + :return: uuid of lm or None + """ + response = license_model_client.get_license_models() + results = response.response_data.get("results") + for license_model in results: + if license_model.get("name") == license_model_name: + return license_model.get("id") + return None + + +def get_license_model_version_id(license_model_id): + """GETs license model version UUID from SDC + + :license_model_id: uuid of license model in SDC + + :return: uuid of lm version id or None + """ + license_model_version_id = None + creation_time = -1 + response = license_model_client.get_license_model_versions( + license_model_id=license_model_id + ) + results = response.response_data.get("results") + for version in results: + if version.get("creationTime", 0) > creation_time: + creation_time = version.get("creationTime") + license_model_version_id = version.get("id") + + return license_model_version_id + + +def get_license_model_attribute(license_model_id, license_model_version_id, attribute): + """GETs license model attribute from SDC + + :license_model_id: uuid of license model in SDC + :license_model_version_id: uuid of license model version in SDC + :attribute: attribute to GET (license-agreements, feature-groups, entitlement-pools, license-key-groups) + + :return: uuid of attribute of license-model + """ + response = license_model_client.get_license_model_version_attribute( + license_model_id=license_model_id, + license_model_version_id=license_model_version_id, + attribute=attribute, + ) + return response.response_data.get("results")[0] diff --git a/onap-client/onap_client/sdc/service.py b/onap-client/onap_client/sdc/service.py new file mode 100644 index 0000000..4e3dd03 --- /dev/null +++ b/onap-client/onap_client/sdc/service.py @@ -0,0 +1,426 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +from onap_client.lib import generate_dummy_string +from onap_client.resource import Resource +from onap_client import exceptions +from onap_client.client.clients import Client as SDCClient +from onap_client.sdc.vnf import get_vnf_id +from onap_client import sdc +from onap_client.sdc import SDC_PROPERTIES +from onap_client.util import utility + +import time +import json +import random + +service_client = SDCClient().sdc.service +sdc_properties = sdc.SDC_PROPERTIES + + +def normalize_category_icon(category_name): + if category_name == "Network L1-3": + return "network_l_4" + elif category_name == "Mobility": + return "mobility" + elif category_name == "E2E Service": + return "network_l_1-3" + elif category_name == "Network L1-3": + return "network_l_1-3" + elif category_name == "Network Service": + return "network_l_1-3" + elif category_name == "Network 4+": + return "network_l_4" + elif category_name == "VoIP Call Control": + return "call_controll" + else: + return "network_l_1-3" + + +class Service(Resource): + resource_name = "SERVICE" + spec = { + "instantiation_type": { + "type": str, + "required": False, + "default": "A-la-carte", + }, + "service_name": { + "type": str, + "required": False, + "default": generate_dummy_string("test_service_"), + }, + "contact_id": {"type": str, "required": False, "default": "cs0008"}, + "category_name": {"type": str, "required": False, "default": "Network L1-3"}, + "tag": {"type": str, "required": False, "default": "robot-ete"}, + "project_code": {"type": str, "required": False, "default": "123456"}, + "environment_context": { + "type": str, + "required": False, + "default": "General_Revenue-Bearing", + }, + "ecomp_generated_naming": {"type": str, "required": False, "default": "true"}, + "description": { + "type": str, + "required": False, + "default": "Brand New Service", + }, + "service_type": {"type": str, "required": False, "default": ""}, + "service_role": {"type": str, "required": False, "default": ""}, + "naming_policy": {"type": str, "required": False, "default": ""}, + "resources": { + "type": list, + "list_item": dict, + "required": False, + "default": [], + "nested": { + "resource_name": {"type": str, "required": True}, + "resource_id": {"type": str, "required": False}, + "catalog_resource_name": {"type": str, "required": False}, + "origin_type": {"type": str, "required": False, "default": "VF"}, + "properties": {"type": dict, "required": False, "default": {}}, + }, + }, + "wait_for_distribution": {"type": bool, "required": False, "default": False}, + } + + def __init__( + self, + instantiation_type, + service_name, + contact_id, + category_name, + tag, + project_code, + environment_context, + ecomp_generated_naming, + description, + service_type, + service_role, + naming_policy, + resources=[], + wait_for_distribution=False, + ): + service_input = {} + + category_name_lower = category_name.lower() + category_name_icon = normalize_category_icon(category_name) + category_id = "serviceNewCategory.{}".format(category_name_lower) + + service_input["service_name"] = service_name + service_input["instantiation_type"] = instantiation_type + service_input["contact_id"] = contact_id + service_input["category_name"] = category_name + service_input["category_id"] = category_id + service_input["category_name_lower"] = category_name_lower + service_input["category_name_icon"] = category_name_icon + service_input["tag"] = tag + service_input["project_code"] = project_code + service_input["environment_context"] = environment_context + service_input["ecomp_generated_naming"] = ecomp_generated_naming + service_input["description"] = description + service_input["service_type"] = service_type + service_input["service_role"] = service_role + service_input["naming_policy"] = naming_policy + service_input["resources"] = resources + service_input["wait_for_distribution"] = wait_for_distribution + + super().__init__(service_input) + + def _create(self, service_input): + """Creates a service object in SDC""" + service = None + + if get_service_id(service_input.get("service_name")) is None: + service = create_service(service_input) + else: + raise exceptions.ResourceAlreadyExistsException( + "Service resource {} already exists".format( + service_input.get("service_name") + ) + ) + + return service + + def _post_create(self): + resources = self.resources + + for resource in resources: + resource_name = resource.get("resource_name") + catalog_resource_name = resource.get("catalog_resource_name") + resource_id = resource.get("resource_id") + resource_properties = resource.get("properties") + if not resource_id: + resource_id = get_vnf_id(catalog_resource_name) + if not resource_id: + raise exceptions.ResourceIDNotFoundException( + "resource ID was not passed, and resource lookup by name was not found {}".format( + resource_name + ) + ) + resource_origin = resource.get("origin_type") + self.add_resource(resource_id, resource_name, origin_type=resource_origin) + for k, v in resource_properties.items(): + if isinstance(v, dict): + v = json.dumps(v).replace('"', '\\"') + self.add_property_value(resource_name, k, v) + + def _submit(self): + """Submits the service in SDC and distributes the model""" + DISTRIBUTION_STEPS = sdc_properties.SERVICE_DISTRIBUTION or [] + + service_client.checkin_service(**self.attributes, user_remarks="checking in") + + if ( + not DISTRIBUTION_STEPS + or "request_service_certification" in DISTRIBUTION_STEPS + ): + service_client.request_service_certification( + **self.attributes, user_remarks="requesting certification" + ) + + if ( + not DISTRIBUTION_STEPS + or "start_service_certification" in DISTRIBUTION_STEPS + ): + service_client.start_service_certification( + **self.attributes, user_remarks="certifying" + ) + + if ( + not DISTRIBUTION_STEPS + or "finish_service_certification" in DISTRIBUTION_STEPS + ): + catalog_service = service_client.finish_service_certification( + **self.attributes, user_remarks="certified" + ) + self.attributes["catalog_service_id"] = catalog_service.catalog_service_id + + if ( + not DISTRIBUTION_STEPS + or "approve_service_certification" in DISTRIBUTION_STEPS + ): + service_client.approve_service_certification( + **self.attributes, user_remarks="approved" + ) + + service_client.distribute_sdc_service(**self.attributes) + + if self.wait_for_distribution: + poll_distribution(self.service_name) + + self._refresh() + + def add_resource( + self, catalog_resource_id, catalog_resource_name, origin_type="VF" + ): + """Attaches a resource to a Service in SDC + + :catalog_resource_id: ID of a resource in the SDC catalog + :catalog_resource_name: name to give to the resource when attaching to service + :origin_type: specifies the origin of the attached resource + + """ + milli_timestamp = int(time.time() * 1000) + + resource_instance = service_client.add_resource_instance( + **self.attributes, + posX=random.randrange(150, 550), # nosec + posY=random.randrange(150, 450), # nosec + milli_timestamp=milli_timestamp, + catalog_resource_id=catalog_resource_id, + catalog_resource_name=catalog_resource_name, + originType=origin_type, + ) + + response = { + "id": resource_instance.catalog_resource_instance_id, + "tosca": resource_instance.response_data, + } + self.attributes[catalog_resource_name] = response + + self._refresh() + + def add_property_value(self, resource_name, property_name, input_value): + """Updates an property value on a resource attached to a Service + + :resource_name: Name of a resource attached to a service + :property_name: property name to update + :input_value: value to update property with + + """ + resource = self.attributes.get(resource_name) + if not resource: + raise exceptions.ResourceNotFoundException( + "Resource {} was not found on Service {}".format( + resource_name, self.service_name + ) + ) + resource_id = resource["id"] + + instance_inputs = self.tosca.get("componentInstancesProperties", {}).get( + resource_id, [] + ) + for prop in instance_inputs: + if prop.get("name") == property_name: + unique_id = prop.get("uniqueId") + parent_unique_id = prop.get("parentUniqueId") + owner_id = prop.get("ownerId") + schemaType = prop.get("schemaType", "") + property_type = prop.get("type") + return service_client.add_catalog_service_property( + **self.attributes, + unique_id=unique_id, + parent_unique_id=parent_unique_id, + owner_id=owner_id, + catalog_resource_instance_id=resource_id, + input_name=property_name, + input_value=input_value, + schema_type=schemaType, + property_type=property_type, + ) + + raise exceptions.PropertyNotFoundException( + "Property {} was not found in VF Instance {}".format( + property_name, resource_id + ) + ) + + def _refresh(self): + self.tosca = service_client.get_sdc_service( + catalog_service_id=self.catalog_service_id + ).response_data + + +def create_service(service_input): + """Creates a service object in SDC + + :service_input: dictionary with values to input for service creation + + :return: dictionary of updated values for created service + """ + kwargs = service_input + + service = service_client.add_catalog_service(**kwargs) + + kwargs["catalog_service_id"] = service.catalog_service_id + kwargs["tosca"] = service.response_data + + return kwargs + + +@utility +def get_service(service_name): + """Queries SDC for the TOSCA model for a service""" + return service_client.get_sdc_service( + catalog_service_id=get_service_id(service_name) + ).response_data + + +@utility +def get_service_id(service_name): + """Queries SDC for the uniqueId of a service model""" + response = service_client.get_services() + results = response.response_data.get("services", []) + for service in results: + if service.get("name") == service_name: + return service["uniqueId"] + return None + + +def get_service_uuid(service_name): + return get_service(service_name).get("uuid") + + +def get_service_distribution(service_name): + distribution_id = get_distribution_id(service_name) + + if distribution_id: + return service_client.get_service_distribution_details( + distribution_id=distribution_id + ).response_data + + return None + + +def get_distribution_id(service_name): + distribution = service_client.get_service_distribution( + distribution_service_id=get_service_uuid(service_name) + ).response_data + if distribution: + details = distribution.get("distributionStatusOfServiceList", []) + for entry in details: + return entry.get("distributionID") + + return None + + +@utility +def poll_distribution(service_name): + """Polls a distributed service until distribution is complete""" + poll_interval = SDC_PROPERTIES.POLL_INTERVAL or 30 + x = 0 + while x < 30: + distribution = get_service_distribution(service_name) + if not distribution: + raise exceptions.DistributionNotFound( + "Could not determine distribution status for {}".format(service_name) + ) + distribution_list = distribution.get("distributionStatusList") + for component in distribution_list: + status = component.get("status") + component_name = component.get("omfComponentID") + if status == "DISTRIBUTION_COMPLETE_ERROR": + raise exceptions.DistributionFailure( + "Distribution failure for service {}, component details {}".format( + service_name, component + ) + ) + elif status == "COMPONENT_DONE_ERROR" and component_name == "aai-ml": + raise exceptions.DistributionFailure( + "Distribution failure for service {}, component details {}".format( + service_name, component + ) + ) + elif status == "DISTRIBUTION_COMPLETE_OK": + return "Distribution Successful" + x += 1 + time.sleep(poll_interval) + + raise exceptions.DistributionTimeout( + "Distribution polling timed out waiting for {}".format(service_name) + ) diff --git a/onap-client/onap_client/sdc/tests/__init__.py b/onap-client/onap_client/sdc/tests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/onap-client/onap_client/sdc/tests/__init__.py diff --git a/onap-client/onap_client/sdc/tests/test.zip b/onap-client/onap_client/sdc/tests/test.zip Binary files differnew file mode 100644 index 0000000..37b2ed1 --- /dev/null +++ b/onap-client/onap_client/sdc/tests/test.zip diff --git a/onap-client/onap_client/sdc/tests/test_license_model.py b/onap-client/onap_client/sdc/tests/test_license_model.py new file mode 100644 index 0000000..459c2e0 --- /dev/null +++ b/onap-client/onap_client/sdc/tests/test_license_model.py @@ -0,0 +1,125 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import responses +from onap_client.client.clients import Client +from onap_client.sdc.license_model import LicenseModel +from onap_client.tests.utils import mockup_client, mockup_catalog_item + +license_model_client = Client().sdc.license_model + + +@responses.activate +def test_license_model_create(): + LICENSE_MODEL_ID = "license_model_id" + LICENSE_MODEL_VERSION_ID = "license_model_version_id" + FEATURE_GROUP_ID = "feature_group_id" + KEYGROUP_ID = "key_group_id" + ENTITLEMENT_POOL_ID = "entitlement_pool_id" + LICENSE_AGREEMENT_ID = "license_agreement_id" + VENDOR_NAME = "vendor_name" + ID = "id" + DESCRIPTION = "description" + + mockup_catalog_item( + license_model_client.catalog_items["ADD_LICENSE_MODEL"], + override_return_data={ + "itemId": LICENSE_MODEL_ID, + "version": {"id": LICENSE_MODEL_VERSION_ID}, + }, + ) + + mockup_catalog_item( + license_model_client.catalog_items["ADD_KEY_GROUP"], + override_return_data={"value": KEYGROUP_ID}, + override_uri_params={ + "license_model_id": LICENSE_MODEL_ID, + "license_model_version_id": LICENSE_MODEL_VERSION_ID, + }, + ) + + mockup_catalog_item( + license_model_client.catalog_items["ADD_ENTITLEMENT_POOL"], + override_return_data={"value": ENTITLEMENT_POOL_ID}, + override_uri_params={ + "license_model_id": LICENSE_MODEL_ID, + "license_model_version_id": LICENSE_MODEL_VERSION_ID, + }, + ) + + mockup_catalog_item( + license_model_client.catalog_items["ADD_FEATURE_GROUP"], + override_return_data={"value": FEATURE_GROUP_ID}, + override_uri_params={ + "license_model_id": LICENSE_MODEL_ID, + "license_model_version_id": LICENSE_MODEL_VERSION_ID, + }, + ) + + mockup_catalog_item( + license_model_client.catalog_items["ADD_LICENSE_AGREEMENT"], + override_return_data={"value": LICENSE_AGREEMENT_ID}, + override_uri_params={ + "license_model_id": LICENSE_MODEL_ID, + "license_model_version_id": LICENSE_MODEL_VERSION_ID, + }, + ) + + return_data = {"vendorName": VENDOR_NAME, "id": ID, "description": DESCRIPTION} + mockup_catalog_item( + license_model_client.catalog_items["GET_LICENSE_MODEL"], + override_return_data=return_data, + override_uri_params={ + "license_model_id": LICENSE_MODEL_ID, + "license_model_version_id": LICENSE_MODEL_VERSION_ID, + }, + ) + + mockup_client(license_model_client) + + lm = LicenseModel( + VENDOR_NAME, + "abc123", + "entitlement_pool_name", + "key_group_name", + "feature_group_name", + "license_agreement_name", + "license_start_date", + "license_end_date", + ) + + assert lm.tosca == return_data diff --git a/onap-client/onap_client/sdc/tests/test_sdc_client.py b/onap-client/onap_client/sdc/tests/test_sdc_client.py new file mode 100644 index 0000000..c4188a9 --- /dev/null +++ b/onap-client/onap_client/sdc/tests/test_sdc_client.py @@ -0,0 +1,68 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +from onap_client.client.clients import Client + + +def test_sdc_client(): + c = Client() + + assert hasattr(c, "sdc") + + +def test_license_model_client(): + c = Client().sdc + + assert hasattr(c, "license_model") + + +def test_vsp_client(): + c = Client().sdc + + assert hasattr(c, "vsp") + + +def test_vnf_client(): + c = Client().sdc + + assert hasattr(c, "vnf") + + +def test_service_client(): + c = Client().sdc + + assert hasattr(c, "service") diff --git a/onap-client/onap_client/sdc/tests/test_vnf.py b/onap-client/onap_client/sdc/tests/test_vnf.py new file mode 100644 index 0000000..025a2d8 --- /dev/null +++ b/onap-client/onap_client/sdc/tests/test_vnf.py @@ -0,0 +1,172 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import responses +from onap_client.tests.utils import mockup_client, mockup_catalog_item +from onap_client.client.clients import Client +from onap_client.sdc.vnf import VNF +from onap_client.sdc.vnf import ( + instance_ids_for_property, + network_role_property_for_instance, +) + +vnf_client = Client().sdc.vnf +vsp_client = Client().sdc.vsp + + +@responses.activate +def test_vnf_create(): + SOFTWARE_PRODUCT_NAME = "software_product_name" + SOFTWARE_PRODUCT_ID = "software_product_id" + SOFTWARE_PRODUCT_VERSION_ID = "software_product_version_id" + VNF_NAME = "vnf_name" + RESOURCE_TYPE = "VF" + CATALOG_RESOURCE_ID = "catalog_resource_id" + + return_data = { + "uniqueId": CATALOG_RESOURCE_ID, + "componentInstancesInputs": { + "instance_id1": [ + {"name": "vm_type_tag", "value": "red"}, + {"name": "nf_role", "value": "dfankafd"}, + ] + }, + "name": VNF_NAME, + } + mockup_catalog_item( + vsp_client.catalog_items["GET_SOFTWARE_PRODUCTS"], + override_return_data={ + "results": [{"name": SOFTWARE_PRODUCT_NAME, "id": SOFTWARE_PRODUCT_ID}] + }, + ) + mockup_catalog_item( + vsp_client.catalog_items["GET_SOFTWARE_PRODUCT_VERSIONS"], + override_return_data={ + "results": [ + {"name": SOFTWARE_PRODUCT_NAME, "id": SOFTWARE_PRODUCT_VERSION_ID} + ] + }, + override_uri_params={"software_product_id": SOFTWARE_PRODUCT_ID}, + ) + mockup_catalog_item( + vsp_client.catalog_items["GET_SOFTWARE_PRODUCT"], + override_return_data={"vendorName": "vendor_name"}, + override_uri_params={ + "software_product_id": SOFTWARE_PRODUCT_ID, + "software_product_version_id": SOFTWARE_PRODUCT_VERSION_ID, + }, + ) + mockup_catalog_item( + vnf_client.catalog_items["GET_RESOURCES"], + override_return_data={"resources": []}, + ) + mockup_catalog_item( + vnf_client.catalog_items["ADD_CATALOG_RESOURCE"], + override_return_data=return_data, + ) + mockup_catalog_item( + vnf_client.catalog_items["GET_CATALOG_RESOURCE"], + override_return_data=return_data, + override_uri_params={"catalog_resource_id": CATALOG_RESOURCE_ID}, + ) + mockup_catalog_item( + vnf_client.catalog_items["CERTIFY_CATALOG_RESOURCE"], + override_return_data=return_data, + override_uri_params={"catalog_resource_id": CATALOG_RESOURCE_ID}, + ) + mockup_catalog_item( + vnf_client.catalog_items["ADD_CATALOG_RESOURCE_PROPERTY"], + override_uri_params={ + "catalog_resource_id": CATALOG_RESOURCE_ID, + "catalog_resource_instance_id": "instance_id1", + }, + ) + + mockup_client(vnf_client) + + vnf = VNF( + SOFTWARE_PRODUCT_NAME, + VNF_NAME, + RESOURCE_TYPE, + vm_types=[{"vm_type": "red", "properties": {"nf_role": "blue"}}], + ) + + vnf._submit() + + assert "catalog_resource_name" in vnf.tosca + + +def test_instance_ids_for_property(): + vnf_model = { + "componentInstancesInputs": { + "item1id": [ + {"name": "vm_type", "value": "db"}, + {"name": "otherprop", "value": "otherval"}, + ], + "item2id": [ + {"name": "vm_type", "value": "db"}, + {"name": "otherprop", "value": "otherval"}, + ], + } + } + + ids = instance_ids_for_property(vnf_model, "vm_type", "db") + + assert "item1id" in ids and "item2id" in ids + + +def test_network_role_property_for_instance(): + vnf_model = { + "componentInstancesInputs": { + "item1id": [ + {"name": "vm_type", "value": "db"}, + {"name": "item1id.port123.oam.network_role_tag", "value": "oam"}, + { + "name": "item1id.port123.oam.network_role", + "value": "ACTUALNETWORKROLE", + }, + ], + "item2id": [ + {"name": "vm_type", "value": "db"}, + {"name": "otherprop", "value": "otherval"}, + ], + } + } + + prop = network_role_property_for_instance("oam", vnf_model, "item1id") + + assert prop == "item1id.port123.oam.network_role" diff --git a/onap-client/onap_client/sdc/tests/test_vsp.py b/onap-client/onap_client/sdc/tests/test_vsp.py new file mode 100644 index 0000000..4d71a81 --- /dev/null +++ b/onap-client/onap_client/sdc/tests/test_vsp.py @@ -0,0 +1,118 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import responses +from onap_client.client.clients import Client +from onap_client import sdc +from onap_client.tests.utils import mockup_client, mockup_catalog_item +from os.path import dirname, abspath + +THIS_DIR = dirname(abspath(__file__)) + +license_model_client = Client().sdc.license_model +vsp_client = Client().sdc.vsp + + +@responses.activate +def test_vsp_create(): + LICENSE_MODEL_ID = "license_model_id" + LICENSE_MODEL_VERSION_ID = "license_model_version_id" + FEATURE_GROUP_ID = "feature_group_id" + LICENSE_AGREEMENT_ID = "license_agreement_id" + LICENSE_MODEL_NAME = "test" + VSP_MODEL_ID = "software_product_id" + VSP_MODEL_VERSION_ID = "software_product_version_id" + VSP_NAME = "software_product_name" + + mockup_catalog_item( + license_model_client.catalog_items["GET_LICENSE_MODELS"], + override_return_data={ + "results": [{"name": LICENSE_MODEL_NAME, "id": LICENSE_MODEL_ID}] + }, + ) + mockup_catalog_item( + license_model_client.catalog_items["GET_LICENSE_MODEL_VERSIONS"], + override_return_data={ + "results": [{"name": LICENSE_MODEL_NAME, "id": LICENSE_MODEL_VERSION_ID}] + }, + ) + mockup_catalog_item( + license_model_client.catalog_items["GET_LICENSE_MODEL_VERSION_ATTRIBUTE"], + override_return_data={ + "results": [{"name": LICENSE_MODEL_NAME, "id": FEATURE_GROUP_ID}] + }, + override_uri_params={"attribute": "feature-groups"}, + ) + mockup_catalog_item( + license_model_client.catalog_items["GET_LICENSE_MODEL_VERSION_ATTRIBUTE"], + override_return_data={ + "results": [{"name": LICENSE_MODEL_NAME, "id": LICENSE_AGREEMENT_ID}] + }, + override_uri_params={"attribute": "license-agreements"}, + ) + mockup_client(license_model_client) + + mockup_catalog_item( + vsp_client.catalog_items["GET_SOFTWARE_PRODUCTS"], + override_return_data={"results": []}, + ) + mockup_catalog_item( + vsp_client.catalog_items["ADD_SOFTWARE_PRODUCT"], + override_return_data={ + "itemId": VSP_MODEL_ID, + "version": {"id": VSP_MODEL_VERSION_ID}, + }, + ) + mockup_catalog_item( + vsp_client.catalog_items["GET_SOFTWARE_PRODUCT"], + override_return_data={"name": VSP_NAME}, + ) + mockup_client(vsp_client) + + vsp = sdc.vsp.VSP( + "vendor_name", + LICENSE_MODEL_NAME, + "{}/test.zip".format(THIS_DIR), + "application/zip", + VSP_NAME, + "description", + "category", + "sub_category", + contributers=["test123"], + ) + + assert vsp.tosca == {"name": VSP_NAME} diff --git a/onap-client/onap_client/sdc/vnf.py b/onap-client/onap_client/sdc/vnf.py new file mode 100644 index 0000000..da8f213 --- /dev/null +++ b/onap-client/onap_client/sdc/vnf.py @@ -0,0 +1,507 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +from onap_client.lib import generate_dummy_string +from onap_client.resource import Resource +from onap_client import exceptions, sdc +from onap_client.client.clients import Client as SDCClient +from onap_client.sdc import vsp +from onap_client.util import utility + +import time + +vnf_client = SDCClient().sdc.vnf + + +class VNF(Resource): + resource_name = "VNF" + spec = { + "software_product_name": {"type": str, "required": True}, + "vnf_name": { + "type": str, + "required": False, + "default": generate_dummy_string("test_vnf_"), + }, + "resource_type": {"type": str, "required": False, "default": "VF"}, + "inputs": {"type": dict, "required": False, "default": {}}, + "vm_types": { + "type": list, + "list_item": dict, + "required": False, + "default": [], + "nested": { + "vm_type": {"type": str, "required": True}, + "properties": {"type": dict, "required": True, "default": {}}, + }, + }, + "network_roles": { + "type": list, + "list_item": dict, + "required": False, + "default": [], + "nested": { + "network_role_tag": {"type": str, "required": True}, + "network_role": {"type": str, "required": True}, + "related_networks": { + "type": list, + "list_item": str, + "required": False, + "default": [], + }, + }, + }, + "policies": { + "type": list, + "list_item": dict, + "required": False, + "default": [], + "nested": { + "policy_name": {"type": str, "required": True}, + "properties": {"type": dict, "required": False, "default": {}}, + }, + }, + } + + def __init__( + self, + software_product_name, + vnf_name, + resource_type, + inputs={}, + vm_types=[], + network_roles=[], + policies=[], + ): + + vnf_input = {} + + software_product_id = vsp.get_vsp_id(software_product_name) + software_product_version_id = vsp.get_vsp_version_id(software_product_id) + vsp_model = vsp.get_vsp_model(software_product_id, software_product_version_id) + vsp_vendor = vsp_model.get("vendorName") + + vnf_input["software_product_id"] = software_product_id + vnf_input["vendor_name"] = vsp_vendor + vnf_input["vnf_name"] = vnf_name + vnf_input["resource_type"] = resource_type + vnf_input["inputs"] = inputs + vnf_input["vm_types"] = vm_types + vnf_input["network_roles"] = network_roles + vnf_input["policies"] = policies + + super().__init__(vnf_input) + + def _create(self, vnf_input): + """Creates a vnf object in SDC""" + vnf = None + + if get_vnf_id(vnf_input.get("vnf_name")) is None: + vnf = create_vnf(vnf_input) + else: + raise exceptions.ResourceAlreadyExistsException( + "VNF resource {} already exists".format(vnf_input.get("vnf_name")) + ) + + return vnf + + def _post_create(self): + inputs = self.inputs + vm_types = self.vm_types + network_roles = self.network_roles + policies = self.policies + model = self.tosca + vm_type_instances = [] + + for vm_type in vm_types: + vm_type_tag = vm_type.get("vm_type") + properties = vm_type.get("properties") + instance_ids = instance_ids_for_property(model, "vm_type_tag", vm_type_tag) + for instance_id in instance_ids: + for k, v in properties.items(): + # updating vm_type properties + self.add_instance_property(instance_id, k, v) + vm_type_instances.append(instance_id) + for network_role in network_roles: + # checking if abstract node has matching network role, + # and updating if found + nrt = network_role.get("network_role_tag") + nr = network_role.get("network_role") + related_networks = network_role.get("related_networks") + instance_property = network_role_property_for_instance( + nrt, model, instance_id + ) + if instance_property: + self.add_instance_property(instance_id, instance_property, nr) + if related_networks: + property_val = [ + {"related_network_role": related_network_role} + for related_network_role in related_networks + ] + rnr_instance_property = instance_property.replace( + "_network_role", "_related_networks" + ) + self.add_instance_property( + instance_id, + rnr_instance_property, + str(property_val).replace("'", '\\"'), + ) + + for policy in policies: + policy_name = policy.get("policy_name") + policy_model = self.add_policy_resource(policy_name) + self.associate_policy(policy_model.catalog_resource_id, vm_type_instances) + for k, v in policy.get("properties", {}).items(): + self.add_policy_property(policy_model.catalog_resource_id, k, v) + + for k, v in inputs.items(): + self.add_input_value(k, v) + + def _submit(self): + """Submits the vnf in SDC""" + certification = vnf_client.certify_catalog_resource( + **self.attributes, user_remarks="Ready!" + ) + self.attributes["catalog_resource_id"] = certification.catalog_resource_id + + vnf = vnf_client.get_catalog_resource(**self.attributes) + + self.attributes["catalog_resource_name"] = vnf.catalog_resource_name + self.attributes["tosca"] = vnf.response_data + + def add_resource( + self, catalog_resource_id, catalog_resource_name, origin_type="VF" + ): + """Attaches a resource to a VNF in SDC + + :catalog_resource_id: ID of a resource in the SDC catalog + :catalog_resource_name: name to give to the resource when attaching to vnf + :origin_type: specifies the origin of the attached resource + + """ + milli_timestamp = int(time.time() * 1000) + + resource_instance = vnf_client.add_resource_instance( + **self.attributes, + posX=306, + posY=248, + milli_timestamp=milli_timestamp, + new_catalog_resource_id=catalog_resource_id, + new_catalog_resource_name=catalog_resource_name, + originType=origin_type, + ) + + response = { + "id": resource_instance.catalog_resource_instance_id, + "tosca": resource_instance.response_data, + } + self.attributes[catalog_resource_name] = response + + def add_input_value(self, input_name, input_default_value): + """Updates an input value on a VNF + + :input_name: input name to update + :property_value: value to update input with + + """ + self._refresh() + + inputs = self.tosca.get("inputs", []) + for item in inputs: + if item["name"] == input_name: + unique_id = item["uniqueId"] + parent_unique_id = item["parentUniqueId"] + owner_id = item["ownerId"] + return vnf_client.add_catalog_resource_input( + **self.attributes, + input_default_value=input_default_value, + input_name=input_name, + input_parent_unique_id=parent_unique_id, + input_unique_id=unique_id, + input_owner_id=owner_id, + ) + + raise exceptions.InputNotFoundException( + "Input {} was not found in VF".format(input_name) + ) + + # TODO + # instance, policy, and group properties can probably be merged + # rn there is a lot of dup + + def add_instance_property(self, instance_id, property_name, property_value): + """Updates an instance property on a abstract instance attached to a VNF + + :instance_id: ID of a instance attached to a VNF + :property_name: property name to update + :property_value: value to update property with + + """ + self._refresh() + + instance_inputs = self.tosca.get("componentInstancesInputs", {}).get( + instance_id, {} + ) + + for prop in instance_inputs: + if prop.get("name") == property_name: + unique_id = prop.get("uniqueId") + parent_unique_id = prop.get("parentUniqueId") + owner_id = prop.get("ownerId") + schemaType = prop.get("schemaType", "") + property_type = prop.get("type") + return vnf_client.add_catalog_resource_property( + **self.attributes, + unique_id=unique_id, + parent_unique_id=parent_unique_id, + owner_id=owner_id, + catalog_resource_instance_id=instance_id, + property_name=property_name, + property_default_value=property_value, + schema_type=schemaType, + property_type=property_type, + ) + + raise exceptions.PropertyNotFoundException( + "Property {} was not found in Instance {}".format( + property_name, instance_id + ) + ) + + def add_policy_property(self, policy_id, property_name, property_value): + """Updates a policy property on a polic attached to a VNF + + :policy_id: ID of a policy attached to a VNF + :property_name: property name to update + :property_value: value to update property with + + """ + self._refresh() + + policies = ( + self.tosca.get("policies", {}).get(policy_id, {}).get("properties", {}) + ) + + for prop in policies: + if prop.get("name") == property_name: + unique_id = prop.get("uniqueId") + property_type = prop.get("type") + description = prop.get("description") + return vnf_client.add_catalog_policy_property( + **self.attributes, + unique_id=unique_id, + catalog_policy_id=policy_id, + property_name=property_name, + property_default_value=property_value, + description=description, + property_type=property_type, + ) + + raise exceptions.PropertyNotFoundException( + "Property {} was not found in policy {}".format(property_name, policy_id) + ) + + def add_group_property(self, group_id, property_name, property_value): + """Updates a group property on a group attached to a VNF + + :group_id: ID of a group attached to a VNF + :property_name: property name to update + :property_value: value to update property with + + """ + self._refresh() + + groups = self.tosca.get("groups", []) + + for group in groups: + if group.get("uniqueId") == group_id: + properties = group.get("properties", []) + for prop in properties: + unique_id = prop.get("uniqueId") + property_type = prop.get("type") + description = prop.get("description") + parent_unique_id = prop.get("parentUniqueId") + owner_id = prop.get("ownerId") + return vnf_client.add_catalog_group_property( + **self.attributes, + unique_id=unique_id, + catalog_group_id=group_id, + property_name=property_name, + property_default_value=property_value, + description=description, + property_type=property_type, + parent_unique_id=parent_unique_id, + owner_id=owner_id, + ) + + raise exceptions.PropertyNotFoundException( + "Property {} was not found in group {}".format(property_name, group_id) + ) + + def add_group_resource(self, group_name): + """Adds an SDC group resource to a VNF + + :group_name: name of the group, matching onap-client.conf + + """ + sdc_properties = sdc.SDC_PROPERTIES + group = sdc_properties.GROUPS.get(group_name) + if not group: + raise exceptions.UnknownGroupException( + "Group {} was not found in configuration file".format(group_name) + ) + + return vnf_client.add_catalog_resource_group( + **self.attributes, catalog_group_name=group + ) + + def add_policy_resource(self, policy_name): + """Adds an SDC policy resource to a VNF + + :group_name: name of the policy, matching onap-client.conf + + """ + sdc_properties = sdc.SDC_PROPERTIES + policy = sdc_properties.POLICIES.get(policy_name) + if not policy: + raise exceptions.UnknownPolicyException( + "Policy {} was not found in configuration file".format(policy_name) + ) + + return vnf_client.add_catalog_resource_policy( + **self.attributes, catalog_policy_name=policy + ) + + def associate_policy(self, policy_id, instance_ids): + """associates an SDC policy resource to an VNF instance resource + + :policy_id: ID of policy resource from catalog + :instance_ids: list of instance ids to associate policy with + + """ + + return vnf_client.add_policy_to_instance( + **self.attributes, catalog_policy_id=policy_id, instance_ids=instance_ids + ) + + def associate_group(self, group_id, instance_id): + """associates an SDC group resource to an VNF instance resource""" + return vnf_client.add_group_to_instance( + **self.attributes, catalog_group_id=group_id, instance_id=instance_id + ) + + def _refresh(self): + """GETs the VNF model from SDC and updates the VNF object""" + vnf = vnf_client.get_catalog_resource(**self.attributes) + self.attributes["tosca"] = vnf.response_data + + +def create_vnf(vnf_input): + """Creates a vnf object in SDC + + :vnf_input: dictionary with values to input for vnf creation + + :return: dictionary of updated values for created vnf + """ + kwargs = vnf_input + vnf = vnf_client.add_catalog_resource(**kwargs) + + kwargs["catalog_resource_id"] = vnf.catalog_resource_id + kwargs["tosca"] = vnf.response_data + + return kwargs + + +def instance_ids_for_property(vnf_model, property_name, property_value): + """Parses a VNF model dictionary for a property + property value, to find the + abstract node tosca uuid + + :vnf_model: dictionary for a VNF tosca model + :property_name: name of a property to look for in the vnf model + :property_value: value of a property to look for in the vnf model + + :return: matching [instance_ids] or [] + """ + instance_ids = [] + instances = vnf_model.get("componentInstancesInputs", {}) + for instance_id, properties in instances.items(): + for prop in properties: + if ( + prop.get("name") == property_name + and prop.get("value", "") == property_value + ): + instance_ids.append(instance_id) + break + + return instance_ids + + +def network_role_property_for_instance(network_role_tag, vnf_model, instance_id): + """Parses a VNF model dictionary for a network_role_tag property, to find the + corresponding network role property + + :network_role_tag: the network role tag to search for + :vnf_model: dictionary for a VNF tosca model + :instance_id: unique ID for an abstract node to look for the network_tag_property + + :return: network_role property ID or None + """ + instance_inputs = vnf_model.get("componentInstancesInputs", {}).get(instance_id, {}) + for prop in instance_inputs: + if prop.get("name").endswith( + "network_role_tag" + ) and network_role_tag == prop.get("value"): + network_role_property = prop.get("name").replace("_tag", "") + return network_role_property + + return None + + +@utility +def get_vnf(vnf_name): + """Queries SDC for the TOSCA model for a VNF""" + return vnf_client.get_catalog_resource( + catalog_resource_id=get_vnf_id(vnf_name) + ).response_data + + +def get_vnf_id(vnf_name): + response = vnf_client.get_resources() + results = response.response_data.get("resources", []) + for vnf in results: + if vnf.get("name") == vnf_name: + return vnf["uniqueId"] + return None diff --git a/onap-client/onap_client/sdc/vsp.py b/onap-client/onap_client/sdc/vsp.py new file mode 100644 index 0000000..fc9258b --- /dev/null +++ b/onap-client/onap_client/sdc/vsp.py @@ -0,0 +1,213 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +from onap_client.lib import generate_dummy_string +from onap_client.resource import Resource +from onap_client.client.clients import Client as SDCClient +from onap_client import sdc +from onap_client.util import utility +from onap_client.exceptions import ResourceAlreadyExistsException + +vsp_client = SDCClient().sdc.vsp + + +class VSP(Resource): + resource_name = "VSP" + spec = { + "vendor_name": {"type": str, "required": True}, + "license_model_name": {"type": str, "required": True}, + "file_path": {"type": str, "required": True}, + "file_type": {"type": str, "required": False, "default": "application/zip"}, + "software_product_name": { + "type": str, + "required": False, + "default": generate_dummy_string("test_vsp_"), + }, + "description": { + "type": str, + "required": False, + "default": "new software product", + }, + "category": {"type": str, "required": False, "default": "generic"}, + "sub_category": {"type": str, "required": False, "default": "abstract"}, + "contributers": { + "type": list, + "list_item": str, + "required": False, + "default": [], + }, + } + + def __init__( + self, + vendor_name, + license_model_name, + file_path, + file_type, + software_product_name, + description, + category, + sub_category, + contributers=[], + ): + + vsp_input = {} + + license_model_id = sdc.license_model.get_license_model_id(license_model_name) + license_model_version_id = sdc.license_model.get_license_model_version_id( + license_model_id + ) + feature_group = sdc.license_model.get_license_model_attribute( + license_model_id, license_model_version_id, "feature-groups" + ) + license_agreement = sdc.license_model.get_license_model_attribute( + license_model_id, license_model_version_id, "license-agreements" + ) + + vsp_input["software_product_name"] = software_product_name + vsp_input["feature_group_id"] = feature_group["id"] + vsp_input["license_agreement_id"] = license_agreement["id"] + vsp_input["vendor_name"] = vendor_name + vsp_input["license_model_id"] = license_model_id + vsp_input["license_model_version_id"] = license_model_version_id + vsp_input["file_path"] = file_path + vsp_input["file_type"] = file_type + vsp_input["description"] = description + vsp_input["category"] = category.lower() + vsp_input["sub_category"] = sub_category.lower() + vsp_input["contributers"] = contributers + + super().__init__(vsp_input) + + def _create(self, kwargs): + """Creates a vsp object in SDC""" + vsp = None + + if get_vsp_id(kwargs.get("software_product_name")) is None: + vsp = create_vsp(kwargs) + else: + raise ResourceAlreadyExistsException( + "VSP resource {} already exists".format( + kwargs.get("software_product_name") + ) + ) + + return vsp + + def _post_create(self): + for contributer in self.contributers: + vsp_client.add_vsp_contributer( + user_id=contributer, software_product_id=self.software_product_id + ) + + def _submit(self): + """Submits the vsp in SDC""" + vsp_client.submit_software_product(**self.attributes, action="Submit") + vsp_client.package_software_product(**self.attributes, action="Create_Package") + + vsp = vsp_client.get_software_product(**self.attributes) + self.attributes["tosca"] = vsp.response_data + + +def create_vsp(vsp_input): + """Creates a VSP object in SDC + + :vsp_input: dictionary with values to input for vsp creation + + :return: dictionary of updated values for created vsp + """ + kwargs = vsp_input + vsp = vsp_client.add_software_product(**kwargs) + + kwargs["software_product_id"] = vsp.software_product_id + kwargs["software_product_version_id"] = vsp.software_product_version_id + + vsp_client.upload_heat_package(**kwargs) + vsp_client.validate_software_product(**kwargs) + + vsp = vsp_client.get_software_product(**kwargs) + kwargs["tosca"] = vsp.response_data + + return kwargs + + +def get_vsp_id(vsp_name): + """GETs vsp model ID from SDC + + :vsp_name: name of vsp model in SDC + + :return: id of vsp or None + """ + response = vsp_client.get_software_products() + results = response.response_data.get("results", {}) + for vsp in results: + if vsp.get("name") == vsp_name: + return vsp["id"] + return None + + +def get_vsp_version_id(vsp_id): + """GETs vsp model version UUID from SDC + + :vsp_id: uuid of vsp model in SDC + + :return: uuid of vsp version id or None + """ + vsp_version_id = None + creation_time = -1 + response = vsp_client.get_software_product_versions(software_product_id=vsp_id) + results = response.response_data.get("results") + for version in results: + if version.get("creationTime", 0) > creation_time: + creation_time = version.get("creationTime") + vsp_version_id = version.get("id") + + return vsp_version_id + + +def get_vsp_model(vsp_id, vsp_version_id): + return vsp_client.get_software_product( + software_product_id=vsp_id, software_product_version_id=vsp_version_id, + ).response_data + + +@utility +def get_vsp(vsp_name): + """Queries SDC for the tosca model for a VSP""" + vsp_id = get_vsp_id(vsp_name) + vsp_version_id = get_vsp_version_id(vsp_id) + return get_vsp_model(vsp_id, vsp_version_id) diff --git a/onap-client/onap_client/sdnc/__init__.py b/onap-client/onap_client/sdnc/__init__.py new file mode 100644 index 0000000..eb3184f --- /dev/null +++ b/onap-client/onap_client/sdnc/__init__.py @@ -0,0 +1,40 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +from onap_client.config import APP_CONFIG + +SDNC_PROPERTIES = APP_CONFIG.sdnc diff --git a/onap-client/onap_client/sdnc/catalog/__init__.py b/onap-client/onap_client/sdnc/catalog/__init__.py new file mode 100644 index 0000000..5519a84 --- /dev/null +++ b/onap-client/onap_client/sdnc/catalog/__init__.py @@ -0,0 +1,36 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ diff --git a/onap-client/onap_client/sdnc/catalog/config_catalog.py b/onap-client/onap_client/sdnc/catalog/config_catalog.py new file mode 100644 index 0000000..da3c1cf --- /dev/null +++ b/onap-client/onap_client/sdnc/catalog/config_catalog.py @@ -0,0 +1,114 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import uuid +from functools import partial + +from onap_client import sdnc +from onap_client import config +from onap_client.sdnc.client import SDNCClient + +PAYLOADS_DIR = config.PAYLOADS_DIR +sdnc_properties = sdnc.SDNC_PROPERTIES +application_id = config.APPLICATION_ID + + +class ConfigClient(SDNCClient): + @property + def catalog_resources(self): + return CATALOG_RESOURCES + + @property + def namespace(self): + return "config" + + +CATALOG_RESOURCES = { + "GET_SERVICE_INSTANCES": { + "verb": "GET", + "description": "Get a list of all service instances", + "uri": partial( + "{endpoint}{service_path}/GENERIC-RESOURCE-API:services".format, + endpoint=sdnc_properties.SDNC_ENDPOINT, + service_path=sdnc_properties.SDNC_CONFIG_PATH, + ), + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (sdnc_properties.SDNC_USERNAME, sdnc_properties.SDNC_PASSWORD,), + }, + "GET_SERVICE_INSTANCE": { + "verb": "GET", + "description": "Get details for a service instance", + "uri": partial( + "{endpoint}{service_path}/GENERIC-RESOURCE-API:services/service/{service_instance_id}".format, + endpoint=sdnc_properties.SDNC_ENDPOINT, + service_path=sdnc_properties.SDNC_CONFIG_PATH, + ), + "uri-parameters": ["service_instance_id"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (sdnc_properties.SDNC_USERNAME, sdnc_properties.SDNC_PASSWORD,), + }, + "GET_VNF_INSTANCE": { + "verb": "GET", + "description": "Get details for a vnf instance", + "uri": partial( + "{endpoint}{service_path}/GENERIC-RESOURCE-API:services/service/{service_instance_id}/service-data/vnfs/vnf/{vnf_instance_id}".format, + endpoint=sdnc_properties.SDNC_ENDPOINT, + service_path=sdnc_properties.SDNC_CONFIG_PATH, + ), + "uri-parameters": ["service_instance_id", "vnf_instance_id"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (sdnc_properties.SDNC_USERNAME, sdnc_properties.SDNC_PASSWORD,), + }, +} diff --git a/onap-client/onap_client/sdnc/catalog/operations_catalog.py b/onap-client/onap_client/sdnc/catalog/operations_catalog.py new file mode 100644 index 0000000..12db3a3 --- /dev/null +++ b/onap-client/onap_client/sdnc/catalog/operations_catalog.py @@ -0,0 +1,97 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import uuid +from functools import partial + +from onap_client import sdnc +from onap_client import config +from onap_client.sdnc.client import SDNCClient + +PAYLOADS_DIR = config.PAYLOADS_DIR +sdnc_properties = sdnc.SDNC_PROPERTIES +application_id = config.APPLICATION_ID + + +class OperationsClient(SDNCClient): + @property + def catalog_resources(self): + return CATALOG_RESOURCES + + @property + def namespace(self): + return "operations" + + +CATALOG_RESOURCES = { + "GR_API_PRELOAD": { + "verb": "POST", + "description": "Upload a GR API preload to SDNC", + "uri": partial( + "{endpoint}{service_path}/GENERIC-RESOURCE-API:preload-vf-module-topology-operation".format, + endpoint=sdnc_properties.SDNC_ENDPOINT, + service_path=sdnc_properties.SDNC_OPERATIONS_PATH, + ), + "payload-path": ["preload_path"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (sdnc_properties.SDNC_USERNAME, sdnc_properties.SDNC_PASSWORD,), + }, + "VNF_API_PRELOAD": { + "verb": "POST", + "description": "Upload a VNF API preload to SDNC", + "uri": partial( + "{endpoint}{service_path}/VNF-API:preload-vnf-topology-operation".format, + endpoint=sdnc_properties.SDNC_ENDPOINT, + service_path=sdnc_properties.SDNC_OPERATIONS_PATH, + ), + "payload-path": ["preload_path"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (sdnc_properties.SDNC_USERNAME, sdnc_properties.SDNC_PASSWORD,), + }, +} diff --git a/onap-client/onap_client/sdnc/client.py b/onap-client/onap_client/sdnc/client.py new file mode 100644 index 0000000..c0a3d1b --- /dev/null +++ b/onap-client/onap_client/sdnc/client.py @@ -0,0 +1,53 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +from onap_client import sdnc +from onap_client.client.clients import Client +from onap_client import config + +sdnc_properties = sdnc.SDNC_PROPERTIES +application_id = config.APPLICATION_ID + + +class SDNCClient(Client): + @property + def namespace(self): + return "sdnc" + + @property + def catalog_resources(self): + return {} diff --git a/onap-client/onap_client/sdnc/preload.py b/onap-client/onap_client/sdnc/preload.py new file mode 100644 index 0000000..65c5e0c --- /dev/null +++ b/onap-client/onap_client/sdnc/preload.py @@ -0,0 +1,191 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import json +import tempfile + +from onap_client.resource import Resource +from onap_client.client.clients import Client as SDNCClient +from onap_client.exceptions import ServiceInstanceNotFound, VNFInstanceNotFound +from onap_client import so +from onap_client.config import LOG as logger + +oc = SDNCClient() +sdnc_client = oc.sdnc + + +class Preload(Resource): + resource_name = "PRELOAD" + spec = { + "api_type": {"type": str, "required": True}, + "preload_path": {"type": str, "required": True}, + "service_instance_name": {"type": str, "required": True}, + "vnf_instance_name": {"type": str, "required": True}, + "module_instance_name": {"type": str, "required": True}, + "heat_template_name": {"type": str, "required": True}, + } + + def __init__( + self, + preload_path, + vnf_instance_name, + service_instance_name, + module_instance_name, + heat_template_name, + api_type, + ): + instance_input = {} + + instance_input["preload_path"] = preload_path + instance_input["vnf_instance_name"] = vnf_instance_name + instance_input["service_instance_name"] = service_instance_name + instance_input["module_instance_name"] = module_instance_name + instance_input["heat_template_name"] = heat_template_name + instance_input["api_type"] = api_type + + super().__init__(instance_input) + + def _create(self, instance_input): + service_instance = so.vnf_instance.get_service_instance( + instance_input.get("service_instance_name") + ) + if not service_instance: + raise ServiceInstanceNotFound( + "No service instance found for {}".format( + instance_input.get("service_instance_name") + ) + ) + + vnf_instance = so.vnf_instance.get_vnf_instance( + service_instance, instance_input.get("vnf_instance_name") + ) + if not vnf_instance: + raise VNFInstanceNotFound( + "No vnf instance found for {}".format( + instance_input.get("vnf_instance_name") + ) + ) + + service_model_information = ( + service_instance.get("service-data") + .get("service-information") + .get("onap-model-information") + ) + service_model_name = service_model_information["model-name"] + vnf_model_information = ( + vnf_instance.get("vnf-data") + .get("vnf-information") + .get("onap-model-information") + ) + vnf_model_name = vnf_model_information["model-name"] + + vnf_component = so.vnf_instance.get_vnf_model_component( + service_model_name, vnf_model_name + ) + module_model = so.vnf_instance.get_module_model( + vnf_component, instance_input.get("heat_template_name") + ) + + preload_path = update_preload_with_instance( + instance_input.get("preload_path"), + instance_input.get("api_type"), + "{}/{} 0".format(service_model_name, vnf_model_name), + instance_input.get("vnf_instance_name"), + instance_input.get("module_instance_name"), + module_model.get("groupName"), + ) + + logger.info("Created preload {}".format(preload_path)) + + create_preload(preload_path, instance_input.get("api_type")) + + return instance_input + + def _post_create(self): + pass + + def _submit(self): + pass + + +def create_preload(preload_path, api_type): + if api_type == "GR_API": + sdnc_client.operations.gr_api_preload(preload_path=preload_path) + elif api_type == "VNF_API": + sdnc_client.operations.vnf_api_preload(preload_path=preload_path) + + +def update_preload_with_instance( + preload_path, + api_type, + vnf_type, + vnf_instance_name, + module_instance_name, + module_model_name, +): + with open(preload_path, "r") as f: + preload_data = json.loads(f.read()) + + if api_type == "GR_API": + data = {"vnf-name": vnf_instance_name, "vnf-type": vnf_type} + preload_data["input"]["preload-vf-module-topology-information"][ + "vnf-topology-identifier-structure" + ] = data + data = { + "vf-module-type": module_model_name, + "vf-module-name": module_instance_name, + } + preload_data["input"]["preload-vf-module-topology-information"][ + "vf-module-topology" + ]["vf-module-topology-identifier"] = data + elif api_type == "VNF_API": + data = { + "vnf-name": module_instance_name, + "vnf-type": module_model_name, + "generic-vnf-type": vnf_type, + "generic-vnf-name": vnf_instance_name, + } + preload_data["input"]["vnf-topology-information"][ + "vnf-topology-identifier" + ] = data + + filename = "" + with tempfile.NamedTemporaryFile(mode='w+t', delete=False) as f: + filename = f.name + json.dump(preload_data, f, indent=4) + + return filename diff --git a/onap-client/onap_client/sdnc/tests/__init__.py b/onap-client/onap_client/sdnc/tests/__init__.py new file mode 100644 index 0000000..5519a84 --- /dev/null +++ b/onap-client/onap_client/sdnc/tests/__init__.py @@ -0,0 +1,36 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ diff --git a/onap-client/onap_client/so/__init__.py b/onap-client/onap_client/so/__init__.py new file mode 100644 index 0000000..44cf6d7 --- /dev/null +++ b/onap-client/onap_client/so/__init__.py @@ -0,0 +1,40 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +from onap_client.config import APP_CONFIG + +SO_PROPERTIES = APP_CONFIG.so diff --git a/onap-client/onap_client/so/catalog/__init__.py b/onap-client/onap_client/so/catalog/__init__.py new file mode 100644 index 0000000..5519a84 --- /dev/null +++ b/onap-client/onap_client/so/catalog/__init__.py @@ -0,0 +1,36 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ diff --git a/onap-client/onap_client/so/catalog/service_instantiation.py b/onap-client/onap_client/so/catalog/service_instantiation.py new file mode 100644 index 0000000..665402f --- /dev/null +++ b/onap-client/onap_client/so/catalog/service_instantiation.py @@ -0,0 +1,316 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import uuid +from functools import partial + +from onap_client import so +from onap_client import config +from onap_client.so.client import SOClient + +PAYLOADS_DIR = config.PAYLOADS_DIR +so_properties = so.SO_PROPERTIES +application_id = config.APPLICATION_ID + + +class ServiceInstantiationClient(SOClient): + @property + def catalog_resources(self): + return CATALOG_RESOURCES + + @property + def namespace(self): + return "service_instantiation" + + +CATALOG_RESOURCES = { + "CREATE_SERVICE_INSTANCE": { + "verb": "POST", + "description": "Creates a Service Instance from the service catalog", + "uri": partial( + "{endpoint}{service_path}".format, + endpoint=so_properties.SO_ENDPOINT, + service_path=so_properties.SO_SERVICE_INSTANCE_PATH, + ), + "payload": "{}/so_service_instance.jinja".format(PAYLOADS_DIR), + "payload-parameters": [ + "service_instance_name", + "requestor_id", + "model_invariant_id", + "model_version_id", + "model_name", + "model_version", + "tenant_id", + "cloud_owner", + "cloud_region", + "api_type", + "service_type", + "customer_id", + "project_name", + "owning_entity_id", + ], + "header-parameters": ["X-TransactionId"], + "success_code": 202, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + # "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (so_properties.SO_USERNAME, so_properties.SO_PASSWORD), + }, + "DELETE_SERVICE_INSTANCE": { + "verb": "DELETE", + "description": "Deletes a VNF Instance.", + "uri": partial( + "{endpoint}{service_path}/{service_instance_id}".format, + endpoint=so_properties.SO_ENDPOINT, + service_path=so_properties.SO_SERVICE_INSTANCE_PATH, + ), + "uri-parameters": ["service_instance_id"], + "payload": "{}/so_delete_service.jinja".format(PAYLOADS_DIR), + "payload-parameters": [ + "service_invariant_id", + "service_name", + "service_version", + "api_type", + ], + "header-parameters": ["X-TransactionId"], + "success_code": 202, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + # "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (so_properties.SO_USERNAME, so_properties.SO_PASSWORD), + }, + "CREATE_VNF_INSTANCE": { + "verb": "POST", + "description": "Creates a VNF Instance.", + "uri": partial( + "{endpoint}{service_path}/{service_instance_id}/vnfs".format, + endpoint=so_properties.SO_ENDPOINT, + service_path=so_properties.SO_SERVICE_INSTANCE_PATH, + ), + "uri-parameters": ["service_instance_id"], + "payload": "{}/so_vnf_instance.jinja".format(PAYLOADS_DIR), + "payload-parameters": [ + "vnf_instance_name", + "requestor_id", + "model_invariant_id", + "model_version_id", + "model_name", + "model_version", + "model_customization_id", + "tenant_id", + "cloud_owner", + "cloud_region", + "api_type", + "platform", + "line_of_business", + "service_model_name", + "service_model_invariant_id", + "service_model_version", + "service_model_version_id", + "service_instance_id", + ], + "header-parameters": ["X-TransactionId"], + "success_code": 202, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + # "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (so_properties.SO_USERNAME, so_properties.SO_PASSWORD), + }, + "DELETE_VNF_INSTANCE": { + "verb": "DELETE", + "description": "Deletes a VNF Instance.", + "uri": partial( + "{endpoint}{service_path}/{service_instance_id}/vnfs/{vnf_instance_id}".format, + endpoint=so_properties.SO_ENDPOINT, + service_path=so_properties.SO_SERVICE_INSTANCE_PATH, + ), + "uri-parameters": ["service_instance_id", "vnf_instance_id"], + "payload": "{}/so_delete_vnf.jinja".format(PAYLOADS_DIR), + "payload-parameters": [ + "vnf_invariant_id", + "vnf_name", + "vnf_version", + "cloud_region", + "cloud_owner", + "tenant_id", + "api_type", + ], + "header-parameters": ["X-TransactionId"], + "success_code": 202, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + # "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (so_properties.SO_USERNAME, so_properties.SO_PASSWORD), + }, + "CREATE_MODULE_INSTANCE": { + "verb": "POST", + "description": "Creates a VNF Module Instance.", + "uri": partial( + "{endpoint}{service_path}/{service_instance_id}/vnfs/{vnf_instance_id}/vfModules".format, + endpoint=so_properties.SO_ENDPOINT, + service_path=so_properties.SO_SERVICE_INSTANCE_PATH, + ), + "uri-parameters": ["service_instance_id", "vnf_instance_id"], + "payload": "{}/so_create_module.jinja".format(PAYLOADS_DIR), + "payload-parameters": [ + "module_instance_name", + "model_invariant_id", + "model_version_id", + "model_name", + "model_version", + "model_customization_id", + "model_name", + "api_type", + "tenant_id", + "cloud_owner", + "cloud_region", + "service_instance_id", + "service_model_name", + "service_model_invariant_id", + "service_model_version", + "service_model_version_id", + "vnf_instance_id", + "vnf_model_name", + "vnf_model_invariant_id", + "vnf_model_version", + "vnf_model_version_id", + "vnf_model_customization_id", + ], + "header-parameters": ["X-TransactionId"], + "success_code": 202, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + # "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (so_properties.SO_USERNAME, so_properties.SO_PASSWORD), + }, + "DELETE_MODULE_INSTANCE": { + "verb": "DELETE", + "description": "Deletes a VNF Module Instance.", + "uri": partial( + "{endpoint}{service_path}/{service_instance_id}/vnfs/{vnf_instance_id}/vfModules/{vf_module_id}".format, + endpoint=so_properties.SO_ENDPOINT, + service_path=so_properties.SO_SERVICE_INSTANCE_PATH, + ), + "uri-parameters": ["service_instance_id", "vnf_instance_id", "vf_module_id"], + "payload": "{}/so_delete_module.jinja".format(PAYLOADS_DIR), + "payload-parameters": [ + "module_invariant_id", + "module_name", + "module_version", + "cloud_region", + "cloud_owner", + "tenant_id", + "api_type", + ], + "header-parameters": ["X-TransactionId"], + "success_code": 202, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + # "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (so_properties.SO_USERNAME, so_properties.SO_PASSWORD), + }, + "GET_REQUEST_STATUS": { + "verb": "GET", + "description": "Queries the status for a given request ID", + "uri": partial( + "{endpoint}{service_path}/{request_id}".format, + endpoint=so_properties.SO_ENDPOINT, + service_path=so_properties.SO_ORCHESTRATION_PATH, + ), + "uri-parameters": ["request_id"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (so_properties.SO_USERNAME, so_properties.SO_PASSWORD), + }, + "GET_SERVICE_MODEL": { + "verb": "GET", + "description": "Searches the SO catalog for a service model", + "uri": partial( + "{endpoint}/service/search/findOneByModelName?modelName={model_name}".format, + endpoint=so_properties.SO_ENDPOINT, + ), + "uri-parameters": ["model_name"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (so_properties.SO_USERNAME, so_properties.SO_PASSWORD), + }, + "GET_SERVICE_MODEL_DETAILS": { + "verb": "GET", + "description": "Searches the SO catalog for a service model", + "uri": partial( + "{endpoint}/service/search/findOneByModelName?modelName={model_name}".format, + endpoint=so_properties.SO_ENDPOINT, + ), + "uri-parameters": ["model_name"], + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (so_properties.SO_USERNAME, so_properties.SO_PASSWORD), + }, +} diff --git a/onap-client/onap_client/so/client.py b/onap-client/onap_client/so/client.py new file mode 100644 index 0000000..c38cf0c --- /dev/null +++ b/onap-client/onap_client/so/client.py @@ -0,0 +1,69 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +from functools import partial +from onap_client import so +from onap_client.client.clients import Client +from onap_client import config + +so_properties = so.SO_PROPERTIES +application_id = config.APPLICATION_ID + + +class SOClient(Client): + @property + def namespace(self): + return "so" + + @property + def catalog_resources(self): + return CATALOG_RESOURCES + + +CATALOG_RESOURCES = { + "HEALTH_CHECK": { + "verb": "GET", + "description": "Queries so health check endpoint", + "uri": partial( + "{endpoint}{service_path}".format, + endpoint=so_properties.SO_ENDPOINT, + service_path=so_properties.SO_HEALTH_CHECK_PATH, + ), + "success_code": 200, + "auth": (so_properties.SO_USERNAME, so_properties.SO_PASSWORD), + }, +} diff --git a/onap-client/onap_client/so/module_instance.py b/onap-client/onap_client/so/module_instance.py new file mode 100644 index 0000000..19af17a --- /dev/null +++ b/onap-client/onap_client/so/module_instance.py @@ -0,0 +1,236 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import uuid +from onap_client.resource import Resource +from onap_client.client.clients import Client as SOClient +from onap_client.exceptions import ServiceInstanceNotFound, VNFInstanceNotFound, ModuleInstanceNotFound +from onap_client import so +from onap_client import sdnc +from onap_client.util import utility + +oc = SOClient() +so_client = oc.so + + +class VNFInstance(Resource): + resource_name = "MODULE_INSTANCE" + spec = { + "module_instance_name": {"type": str, "required": True}, + "vnf_instance_name": {"type": str, "required": True}, + "service_instance_name": {"type": str, "required": True}, + "requestor_id": {"type": str, "required": False, "default": "cs0008"}, + "heat_template_name": {"type": str, "required": True}, + "preload_path": {"type": str, "required": True}, + "tenant_name": {"type": str, "required": True}, + "cloud_owner": {"type": str, "required": True}, + "cloud_region": {"type": str, "required": True}, + "api_type": {"type": str, "required": False, "default": "GR_API"}, + } + + def __init__( + self, + module_instance_name, + vnf_instance_name, + service_instance_name, + requestor_id, + heat_template_name, + preload_path, + tenant_name, + cloud_owner, + cloud_region, + api_type, + ): + instance_input = {} + + tenant_id = so.service_instance.get_tenant_id(cloud_region, cloud_owner, tenant_name) + + instance_input["module_instance_name"] = module_instance_name + instance_input["vnf_instance_name"] = vnf_instance_name + instance_input["service_instance_name"] = service_instance_name + instance_input["requestor_id"] = requestor_id + instance_input["heat_template_name"] = heat_template_name + instance_input["preload_path"] = preload_path + instance_input["tenant_id"] = tenant_id + instance_input["cloud_owner"] = cloud_owner + instance_input["cloud_region"] = cloud_region + instance_input["api_type"] = api_type + + super().__init__(instance_input) + + def _create(self, instance_input): + service_instance = so.vnf_instance.get_service_instance( + instance_input.get("service_instance_name") + ) + if not service_instance: + raise ServiceInstanceNotFound( + "No service instance found for {}".format( + instance_input.get("service_instance_name") + ) + ) + service_instance_id = service_instance.get("service-instance-id") + model_information = ( + service_instance.get("service-data") + .get("service-information") + .get("onap-model-information") + ) + service_model_invariant_id = model_information["model-invariant-uuid"] + service_model_version_id = model_information["model-uuid"] + service_model_version = model_information["model-version"] + service_model_name = model_information["model-name"] + + vnf_instance = so.vnf_instance.get_vnf_instance( + service_instance, instance_input.get("vnf_instance_name") + ) + if not vnf_instance: + raise VNFInstanceNotFound( + "No vnf instance found for {}".format( + instance_input.get("vnf_instance_name") + ) + ) + vnf_model_information = vnf_instance.get("vnf-data").get("vnf-information") + vnf_instance_id = vnf_model_information.get("vnf-id") + vnf_model_name = vnf_model_information.get("onap-model-information").get( + "model-name" + ) + vnf_model_invariant_id = vnf_model_information.get( + "onap-model-information" + ).get("model-invariant-uuid") + vnf_model_version_id = vnf_model_information.get("onap-model-information").get( + "model-uuid" + ) + vnf_model_customization_id = vnf_model_information.get( + "onap-model-information" + ).get("model-customization-uuid") + vnf_model_version = vnf_model_information.get("onap-model-information").get( + "model-version" + ) + + vnf_model = so.vnf_instance.get_vnf_model_component( + service_model_name, vnf_model_name + ) + + module_model = so.vnf_instance.get_module_model( + vnf_model, instance_input.get("heat_template_name") + ) + model_invariant_id = module_model.get("invariantUUID") + model_version_id = module_model.get("groupUUID") + model_customization_id = module_model.get("customizationUUID") + model_name = module_model.get("groupName") + model_version = module_model.get("version") + + instance_input["model_invariant_id"] = model_invariant_id + instance_input["model_version_id"] = model_version_id + instance_input["model_name"] = model_name + instance_input["model_version"] = model_version + instance_input["model_customization_id"] = model_customization_id + instance_input["service_instance_id"] = service_instance_id + instance_input["service_model_name"] = service_model_name + instance_input["service_model_invariant_id"] = service_model_invariant_id + instance_input["service_model_version"] = service_model_version + instance_input["service_model_version_id"] = service_model_version_id + instance_input["vnf_instance_id"] = vnf_instance_id + instance_input["vnf_model_name"] = vnf_model_name + instance_input["vnf_model_invariant_id"] = vnf_model_invariant_id + instance_input["vnf_model_version_id"] = vnf_model_version_id + instance_input["vnf_model_version"] = vnf_model_version + instance_input["vnf_model_customization_id"] = vnf_model_customization_id + + return create_module_instance(instance_input) + + def _post_create(self): + pass + + def _submit(self): + pass + + +def create_module_instance(instance_input): + sdnc.preload.Preload( + instance_input.get("preload_path"), + instance_input.get("vnf_instance_name"), + instance_input.get("service_instance_name"), + instance_input.get("module_instance_name"), + instance_input.get("heat_template_name"), + instance_input.get("api_type") + ) + + headers = {"X-TransactionId": str(uuid.uuid4())} + module_instance = so_client.service_instantiation.create_module_instance( + **instance_input, **headers + ) + + request_id = module_instance.response_data.get("requestReferences", {}).get( + "requestId" + ) + + instance_input["request_info"] = so.service_instance.poll_request(request_id) + + return instance_input + + +@utility +def delete_module_instance(service_instance_name, vnf_instance_name, module_instance_name, api_type="GR_API"): + """Delete a Module Instance from SO""" + si = so.service_instance.get_service_instance(service_instance_name) + si_id = si.get("service-instance-id") + for vnfi in si.get("service-data", {}).get("vnfs", {}).get("vnf", []): + vnfi_id = vnfi.get("vnf-id") + if vnfi.get("vnf-data", {}).get("vnf-request-input", {}).get("vnf-name") == vnf_instance_name: + for modulei in vnfi.get("vnf-data").get("vf-modules", {}).get("vf-module", []): + if modulei.get("vf-module-data", {}).get("vf-module-request-input", {}).get("vf-module-name") == module_instance_name: + module_id = modulei.get("vf-module-id") + module_invariant_id = modulei.get("vf-module-data").get("vf-module-topology").get("onap-model-information").get("model-invariant-uuid") + module_version = modulei.get("vf-module-data").get("vf-module-topology").get("onap-model-information").get("model-version") + module_name = modulei.get("vf-module-data").get("vf-module-topology").get("onap-model-information").get("model-name") + tenant_id = modulei.get("vf-module-data").get("vf-module-request-input").get("tenant") + cloud_owner = modulei.get("vf-module-data").get("vf-module-request-input").get("cloud-owner") + cloud_region = modulei.get("vf-module-data").get("vf-module-request-input").get("aic-cloud-region") + return so_client.service_instantiation.delete_module_instance( + module_invariant_id=module_invariant_id, + module_name=module_name, + module_version=module_version, + cloud_region=cloud_region, + cloud_owner=cloud_owner, + tenant_id=tenant_id, + vnf_instance_id=vnfi_id, + service_instance_id=si_id, + vf_module_id=module_id, + api_type=api_type + ).response_data + + raise ModuleInstanceNotFound("Module Instance was not found: {} {} {}".format(service_instance_name, vnf_instance_name, module_instance_name)) diff --git a/onap-client/onap_client/so/service_instance.py b/onap-client/onap_client/so/service_instance.py new file mode 100644 index 0000000..a5a2e8f --- /dev/null +++ b/onap-client/onap_client/so/service_instance.py @@ -0,0 +1,230 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import uuid + +from onap_client.lib import generate_dummy_string +from onap_client.resource import Resource +from onap_client.client.clients import Client as SOClient +from onap_client.so import SO_PROPERTIES +from onap_client.exceptions import ( + SORequestStatusUnavailable, + SORequestFailed, + SORequestTimeout, + TenantNotFound, + ServiceInstanceNotFound, +) +from onap_client import sdc +from onap_client.util import utility + +from time import sleep + +oc = SOClient() +so_client = oc.so +sdc_client = oc.sdc +aai_client = oc.aai +sdnc_client = oc.sdnc +vid_client = oc.vid + + +class ServiceInstance(Resource): + resource_name = "SERVICE_INSTANCE" + spec = { + "service_instance_name": { + "type": str, + "required": False, + "default": generate_dummy_string("SI_"), + }, + "requestor_id": {"type": str, "required": False, "default": "cs0008"}, + "model_name": {"type": str, "required": True}, + "model_version": {"type": str, "required": False, "default": "1.0"}, + "tenant_name": {"type": str, "required": True}, + "cloud_owner": {"type": str, "required": True}, + "cloud_region": {"type": str, "required": True}, + "api_type": {"type": str, "required": False, "default": "GR_API"}, + "service_type": {"type": str, "required": True}, + "customer_name": {"type": str, "required": True}, + "project_name": {"type": str, "required": True}, + "owning_entity_name": {"type": str, "required": True}, + } + + def __init__( + self, + service_instance_name, + requestor_id, + model_name, + model_version, + tenant_name, + cloud_owner, + cloud_region, + api_type, + service_type, + customer_name, + project_name, + owning_entity_name, + ): + instance_input = {} + + tenant_id = get_tenant_id(cloud_region, cloud_owner, tenant_name) + + instance_input["service_instance_name"] = service_instance_name + instance_input["requestor_id"] = requestor_id + instance_input["model_name"] = model_name + instance_input["model_version"] = model_version + instance_input["tenant_id"] = tenant_id + instance_input["cloud_owner"] = cloud_owner + instance_input["cloud_region"] = cloud_region + instance_input["api_type"] = api_type + instance_input["service_type"] = service_type + instance_input["customer_id"] = customer_name + instance_input["project_name"] = project_name + instance_input["owning_entity_name"] = owning_entity_name + + super().__init__(instance_input) + + def _create(self, instance_input): + service_model = sdc_client.service.get_sdc_service( + catalog_service_id=sdc.service.get_service_id( + instance_input.get("model_name") + ) + ).response_data + + instance_input["model_invariant_id"] = service_model["invariantUUID"] + instance_input["model_version_id"] = service_model["uniqueId"] + + category_parameters = vid_client.maintenance.get_category_parameters().response_data + for entity in category_parameters.get("categoryParameters", {}).get("owningEntity", []): + if entity.get("name") == instance_input.get("owning_entity_name"): + instance_input["owning_entity_id"] = entity.get("id") + break + + return create_service_instance(instance_input) + + def _post_create(self): + pass + + def _submit(self): + pass + + +@utility +def get_service_instance(instance_name): + """Queries SDNC for a list of all service instances and returns + The service instance that matches <instance name>""" + service_instances = sdnc_client.config.get_service_instances().response_data + for si in service_instances.get("services", {}).get("service", []): + if si.get("service-data", {}).get("service-request-input", {}).get("service-instance-name") == instance_name: + return si + + raise ServiceInstanceNotFound("Service Instance {} was not found".format(instance_name)) + + +def get_tenant_id(cloud_region, cloud_owner, tenant_name): + tenants = aai_client.cloud_infrastructure.get_cloud_region_tenants( + cloud_owner=cloud_owner, + cloud_region=cloud_region + ).response_data + + for tenant in tenants.get("tenant"): + if tenant.get("tenant-name") == tenant_name: + return tenant.get("tenant-id") + + raise TenantNotFound("Tenant {} was not found in AAI".format(tenant_name)) + + +def create_service_instance(instance_input): + headers = {"X-TransactionId": str(uuid.uuid4())} + service_instance = so_client.service_instantiation.create_service_instance( + **instance_input, **headers + ) + + request_id = service_instance.response_data.get("requestReferences", {}).get( + "requestId" + ) + + instance_input["request_info"] = poll_request(request_id) + + return instance_input + + +@utility +def poll_request(request_id): + """Poll an SO request until completion""" + poll_interval = SO_PROPERTIES.POLL_INTERVAL or 30 + request = None + x = 0 + while x < 30: + request = so_client.service_instantiation.get_request_status( + request_id=request_id + ).response_data + status = request.get("request", {}).get("requestStatus", {}).get("requestState") + if not status: + raise SORequestStatusUnavailable( + "Could not determine request for {}".format(request_id) + ) + if status == "FAILED": + failure_message = ( + request.get("request", {}).get("requestStatus", {}).get("statusMessage") + ) + raise SORequestFailed( + "Request {} failed with message {}".format(request_id, failure_message) + ) + elif status == "COMPLETE": + return request + + x += 1 + + sleep(poll_interval) + + raise SORequestTimeout("Request {} timed out polling for status".format(request_id)) + + +@utility +def delete_service_instance(service_instance_name, api_type="GR_API"): + """Delete a Service Instance from SO""" + si = get_service_instance(service_instance_name) + si_id = si.get("service-instance-id") + invariant_id = si.get("service-data").get("service-information").get("onap-model-information").get("model-invariant-uuid") + version = si.get("service-data").get("service-information").get("onap-model-information").get("model-version") + + return so_client.service_instantiation.delete_service_instance( + service_invariant_id=invariant_id, + service_name=service_instance_name, + service_version=version, + service_instance_id=si_id, + api_type=api_type, + ).response_data diff --git a/onap-client/onap_client/so/tests/__init__.py b/onap-client/onap_client/so/tests/__init__.py new file mode 100644 index 0000000..5519a84 --- /dev/null +++ b/onap-client/onap_client/so/tests/__init__.py @@ -0,0 +1,36 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ diff --git a/onap-client/onap_client/so/vnf_instance.py b/onap-client/onap_client/so/vnf_instance.py new file mode 100644 index 0000000..4d289a0 --- /dev/null +++ b/onap-client/onap_client/so/vnf_instance.py @@ -0,0 +1,267 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import uuid + +from onap_client.lib import generate_dummy_string +from onap_client.resource import Resource +from onap_client.client.clients import Client as SOClient +from onap_client.exceptions import ( + ServiceInstanceNotFound, + VNFComponentNotFound, + ModuleModelNameNotFound, + NoArtifactFoundInModel, + VNFInstanceNotFound, +) +from onap_client import sdc +from onap_client import so +from onap_client.util import utility + + +oc = SOClient() +so_client = oc.so +sdc_client = oc.sdc +sdnc_client = oc.sdnc + + +class VNFInstance(Resource): + resource_name = "VNF_INSTANCE" + spec = { + "vnf_instance_name": { + "type": str, + "required": False, + "default": generate_dummy_string("VNF_"), + }, + "service_instance_name": {"type": str, "required": True}, + "requestor_id": {"type": str, "required": False, "default": "cs0008"}, + "model_name": {"type": str, "required": True}, + "tenant_name": {"type": str, "required": True}, + "cloud_owner": {"type": str, "required": True}, + "cloud_region": {"type": str, "required": True}, + "api_type": {"type": str, "required": False, "default": "GR_API"}, + "platform": {"type": str, "required": True}, + "line_of_business": {"type": str, "required": True}, + } + + def __init__( + self, + vnf_instance_name, + service_instance_name, + requestor_id, + model_name, + tenant_name, + cloud_owner, + cloud_region, + api_type, + platform, + line_of_business, + ): + instance_input = {} + + tenant_id = so.service_instance.get_tenant_id(cloud_region, cloud_owner, tenant_name) + + instance_input["vnf_instance_name"] = vnf_instance_name + instance_input["service_instance_name"] = service_instance_name + instance_input["requestor_id"] = requestor_id + instance_input["model_name"] = model_name + instance_input["tenant_id"] = tenant_id + instance_input["cloud_owner"] = cloud_owner + instance_input["cloud_region"] = cloud_region + instance_input["api_type"] = api_type + instance_input["platform"] = platform + instance_input["line_of_business"] = line_of_business + + super().__init__(instance_input) + + def _create(self, instance_input): + service_instance = get_service_instance( + instance_input.get("service_instance_name") + ) + if not service_instance: + raise ServiceInstanceNotFound( + "No service instance found for {}".format( + instance_input.get("service_instance_name") + ) + ) + service_instance_id = service_instance.get("service-instance-id") + model_information = ( + service_instance.get("service-data") + .get("service-information") + .get("onap-model-information") + ) + service_invariant_id = model_information["model-invariant-uuid"] + service_model_id = model_information["model-uuid"] + service_model_version = model_information["model-version"] + service_model_name = model_information["model-name"] + + vnf_component = get_vnf_model_component( + service_model_name, instance_input.get("model_name") + ) + if not vnf_component: + raise VNFComponentNotFound( + "No component found for {}".format(instance_input.get("model_name")) + ) + vnf_model_customization_id = vnf_component["customizationUUID"] + vnf_model_version_id = vnf_component["actualComponentUid"] + vnf_model_version = vnf_component["componentVersion"] + + vnf_model = sdc_client.vnf.get_catalog_resource( + catalog_resource_id=vnf_model_version_id, + ).response_data + vnf_model_invariant_id = vnf_model["invariantUUID"] + + instance_input["model_invariant_id"] = vnf_model_invariant_id + instance_input["model_version_id"] = vnf_model_version_id + instance_input["model_customization_id"] = vnf_model_customization_id + instance_input["model_version"] = vnf_model_version + instance_input["service_model_name"] = service_model_name + instance_input["service_model_invariant_id"] = service_invariant_id + instance_input["service_model_version"] = service_model_version + instance_input["service_model_version_id"] = service_model_id + instance_input["service_instance_id"] = service_instance_id + + return create_vnf_instance(instance_input) + + def _post_create(self): + pass + + def _submit(self): + pass + + +def get_vnf_model_component(service_model_name, vnf_model_name): + service_model = sdc_client.service.get_sdc_service( + catalog_service_id=sdc.service.get_service_id(service_model_name) + ).response_data + + for component in service_model.get("componentInstances", []): + if component["componentName"] == vnf_model_name: + return component + return None + + +def get_service_instance(service_instance_name): + service_instances = sdnc_client.config.get_service_instances().response_data + for si in service_instances.get("services", {}).get("service", []): + si_name = ( + si.get("service-data", {}) + .get("service-request-input", {}) + .get("service-instance-name") + ) + if si_name == service_instance_name: + return si + return None + + +def get_module_model(vnf_model, heat_template_name): + artifact_uuid = None + deployment_artifacts = vnf_model.get("deploymentArtifacts", {}) + for artifact_name, artifact_data in deployment_artifacts.items(): + if artifact_data.get("artifactName") == heat_template_name: + artifact_uuid = artifact_data.get("artifactUUID") + + if not artifact_uuid: + raise NoArtifactFoundInModel( + "Heat Template {} was not found in service model".format(heat_template_name) + ) + + group_instances = vnf_model.get("groupInstances", []) + for instance in group_instances: + if artifact_uuid in instance.get("artifactsUuid", []): + # return instance.get("groupName") + return instance + + raise ModuleModelNameNotFound( + "Module Model Name for {} was not found in service model".format( + heat_template_name + ) + ) + + +def get_vnf_instance(service_instance, vnf_instance_name): + for vnf_instance in ( + service_instance.get("service-data", {}).get("vnfs", {}).get("vnf", []) + ): + vi_name = ( + vnf_instance.get("vnf-data", {}).get("vnf-information", {}).get("vnf-name") + ) + if vi_name == vnf_instance_name: + return vnf_instance + return None + + +def create_vnf_instance(instance_input): + headers = {"X-TransactionId": str(uuid.uuid4())} + vnf_instance = so_client.service_instantiation.create_vnf_instance( + **instance_input, **headers + ) + + request_id = vnf_instance.response_data.get("requestReferences", {}).get( + "requestId" + ) + + instance_input["request_info"] = so.service_instance.poll_request(request_id) + + return instance_input + + +@utility +def delete_vnf_instance(service_instance_name, vnf_instance_name, api_type="GR_API"): + """Delete a VNF Instance from SO""" + si = so.service_instance.get_service_instance(service_instance_name) + si_id = si.get("service-instance-id") + for vnfi in si.get("service-data", {}).get("vnfs", {}).get("vnf", []): + vnfi_id = vnfi.get("vnf-id") + if vnfi.get("vnf-data", {}).get("vnf-request-input", {}).get("vnf-name") == vnf_instance_name: + invariant_id = vnfi.get("vnf-data").get("vnf-information").get("onap-model-information").get("model-invariant-uuid") + vnf_version = vnfi.get("vnf-data").get("vnf-information").get("onap-model-information").get("model-version") + tenant_id = vnfi.get("vnf-data").get("vnf-request-input").get("tenant") + cloud_owner = vnfi.get("vnf-data").get("vnf-request-input").get("cloud-owner") + cloud_region = vnfi.get("vnf-data").get("vnf-request-input").get("aic-cloud-region") + return so_client.service_instantiation.delete_vnf_instance( + vnf_invariant_id=invariant_id, + vnf_version=vnf_version, + vnf_name=vnf_instance_name, + cloud_region=cloud_region, + cloud_owner=cloud_owner, + tenant_id=tenant_id, + vnf_instance_id=vnfi_id, + service_instance_id=si_id, + api_type=api_type, + ).response_data + + raise VNFInstanceNotFound("VNF Instance was not found: {} {}".format(service_instance_name, vnf_instance_name)) diff --git a/onap-client/onap_client/tests/__init__.py b/onap-client/onap_client/tests/__init__.py new file mode 100644 index 0000000..5519a84 --- /dev/null +++ b/onap-client/onap_client/tests/__init__.py @@ -0,0 +1,36 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ diff --git a/onap-client/onap_client/tests/test.zip b/onap-client/onap_client/tests/test.zip Binary files differnew file mode 100644 index 0000000..2bd8d04 --- /dev/null +++ b/onap-client/onap_client/tests/test.zip diff --git a/onap-client/onap_client/tests/test_catalog.py b/onap-client/onap_client/tests/test_catalog.py new file mode 100644 index 0000000..944c115 --- /dev/null +++ b/onap-client/onap_client/tests/test_catalog.py @@ -0,0 +1,125 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +from onap_client.client.clients import Client +from onap_client.tests.testdata import TestClient, CATALOG_RESOURCES, THIS_DIR # noqa: F401 + + +def test_catalog_items(): + c = Client() + + assert "MAKE_TEST_REQUEST" in c.test.catalog_items + + +def test_client_namespace(): + c = Client() + + assert hasattr(c, "test") + + +def test_client_catalog_resources(): + c = Client() + + assert c.test.catalog_resources == CATALOG_RESOURCES + + +def test_catalog_item_verb(): + c = Client() + + resource = c.test.catalog_items.get("MAKE_TEST_REQUEST") + + assert resource.verb == "POST" + + +def test_catalog_item_description(): + c = Client() + + resource = c.test.catalog_items.get("MAKE_TEST_REQUEST") + + assert resource.description == "Test Catalog Request" + + +def test_catalog_item_payload(): + c = Client() + + resource = c.test.catalog_items.get("MAKE_TEST_REQUEST") + + assert resource.payload == "{}/test_payload.jinja".format(THIS_DIR) + + +def test_catalog_item_uri_parameter(): + c = Client() + + resource = c.test.catalog_items.get("MAKE_TEST_REQUEST") + + assert "test_uri_parameter" in resource.uri_parameters + + +def test_catalog_item_payload_parameter(): + c = Client() + + resource = c.test.catalog_items.get("MAKE_TEST_REQUEST") + + assert "test_item_parameter" in resource.payload_parameters + + +def test_catalog_item_headers(): + c = Client() + + resource = c.test.catalog_items.get("MAKE_TEST_REQUEST") + + headers = {"Accept": "application/json", "Content-Type": "application/json"} + + assert resource.headers == headers + + +def test_catalog_item_auth(): + c = Client() + + resource = c.test.catalog_items.get("MAKE_TEST_REQUEST") + + auth = ("abc", "123") + + assert resource.auth == auth + + +def test_catalog_item_return_data(): + c = Client() + + resource = c.test.catalog_items.get("MAKE_TEST_REQUEST") + + assert "return_data_1" in resource.return_data diff --git a/onap-client/onap_client/tests/test_payload.jinja b/onap-client/onap_client/tests/test_payload.jinja new file mode 100644 index 0000000..0038703 --- /dev/null +++ b/onap-client/onap_client/tests/test_payload.jinja @@ -0,0 +1 @@ +{"test_item_parameter": "{{test_item_parameter}}"}
\ No newline at end of file diff --git a/onap-client/onap_client/tests/test_request.py b/onap-client/onap_client/tests/test_request.py new file mode 100644 index 0000000..5c7e019 --- /dev/null +++ b/onap-client/onap_client/tests/test_request.py @@ -0,0 +1,187 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import responses +import json +import os +import sys + +from io import StringIO + +from onap_client.client.clients import Client +from onap_client.client.request import APICatalogRequestObject + +from onap_client.tests.testdata import ( + THIS_DIR, + RETURN_DATA, +) +from onap_client.tests.testdata import TestClient # noqa: F401 +from onap_client.cli import main + +DUMMY_PARAM = "dummy_param" + + +def test_request_uri(): + c = Client() + + resource = c.test.catalog_items.get("MAKE_FILES_REQUEST") + + request_object = create_request_object(resource) + + assert request_object.uri == "http://this.is.a.test.com/{}".format(DUMMY_PARAM) + + +def test_payload(): + c = Client() + + resource = c.test.catalog_items.get("MAKE_FILES_REQUEST") + + request_object = create_request_object(resource) + + assert json.loads(request_object.payload) == {"test_item_parameter": DUMMY_PARAM} + + +def test_files(): + c = Client() + + resource = c.test.catalog_items.get("MAKE_FILES_REQUEST") + + request_object = create_request_object(resource) + + file_path = "{}/test.zip".format(THIS_DIR) + with open(file_path, "rb") as f: + data = f.read() + file_name = os.path.basename(file_path) + files = {"upload": [file_name, data, "application/zip"]} + assert request_object.files == files + + +@responses.activate +def test_make_request(): + responses.add( + responses.POST, + "http://this.is.a.test.com/{}".format(DUMMY_PARAM), + json=RETURN_DATA, + ) + + c = Client() + + params = {"test_item_parameter": DUMMY_PARAM, "test_uri_parameter": DUMMY_PARAM} + + resp = c.test.make_test_request(**params) + + assert resp.response_data == RETURN_DATA + + +def test_cli(): + cli_string = ["test", "--help"] + temp_out = StringIO() + sys.stdout = temp_out + + main(*cli_string) + + sys.stdout.seek(0) + output = sys.stdout.read() + + sys.stdout = sys.__stdout__ + + assert output.find("make-test-request") != -1 + + +def test_cli_request_help(): + cli_string = ["test", "make-test-request", "--help"] + temp_out = StringIO() + sys.stdout = temp_out + + main(*cli_string) + sys.stdout.seek(0) + output = sys.stdout.read() + + sys.stdout = sys.__stdout__ + + assert output.find("--test-item-parameter") != -1 + + +@responses.activate +def test_cli_request(): + temp_out = StringIO() + sys.stdout = temp_out + + responses.add( + responses.POST, + "http://this.is.a.test.com/{}".format(DUMMY_PARAM), + json=RETURN_DATA, + ) + + cli_string = [ + "test", + "make-test-request", + "--test-item-parameter", + DUMMY_PARAM, + "--test-uri-parameter", + DUMMY_PARAM, + ] + + main(*cli_string) + + sys.stdout.seek(0) + output = sys.stdout.read() + + sys.stdout = sys.__stdout__ + + assert json.loads(output) == RETURN_DATA + + +def create_request_object(catalog_item): + payload_input = {} + uri_input = {} + file_input = {} + + for param in catalog_item.payload_parameters: + payload_input[param] = DUMMY_PARAM + + for param in catalog_item.uri_parameters: + uri_input[param] = DUMMY_PARAM + + file_input["file_path"] = "{}/test.zip".format(THIS_DIR) + + return APICatalogRequestObject( + catalog_item, + payload_parameters=payload_input, + uri_parameters=uri_input, + file_parameters=file_input, + ) diff --git a/onap-client/onap_client/tests/test_resource.py b/onap-client/onap_client/tests/test_resource.py new file mode 100644 index 0000000..5b4124a --- /dev/null +++ b/onap-client/onap_client/tests/test_resource.py @@ -0,0 +1,68 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +from onap_client.resource import Resource +from onap_client.lib import generate_dummy_string, generate_dummy_date + + +class TestResource(Resource): + def __init__(self, test_param): + test_input = {} + + test_input["param1"] = test_param + test_input["param2"] = generate_dummy_string() + test_input["param3"] = generate_dummy_date() + + super().__init__(test_input) + + def _create(self, create_input): + return create_input + + def _post_create(self): + pass + + def _submit(self): + pass + + +def test_resource(): + test_param = "abc" + + r = TestResource(test_param) + r.print() + + assert hasattr(r, "param1") and r.param1 == test_param diff --git a/onap-client/onap_client/tests/testdata.py b/onap-client/onap_client/tests/testdata.py new file mode 100644 index 0000000..ade4713 --- /dev/null +++ b/onap-client/onap_client/tests/testdata.py @@ -0,0 +1,92 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +from functools import partial +from os.path import dirname, abspath + +from onap_client.client.clients import Client + +TEST_URI = "http://this.is.a.test.com" +THIS_DIR = dirname(abspath(__file__)) + + +class TestClient(Client): + @property + def namespace(self): + return "test" + + @property + def catalog_resources(self): + return CATALOG_RESOURCES + + +CATALOG_RESOURCES = { + "MAKE_TEST_REQUEST": { + "verb": "POST", + "description": "Test Catalog Request", + "uri": partial("{endpoint}/{test_uri_parameter}".format, endpoint=TEST_URI), + "uri-parameters": ["test_uri_parameter"], + "payload": "{}/test_payload.jinja".format(THIS_DIR), + "payload-parameters": ["test_item_parameter"], + "success_code": 200, + "headers": {"Accept": "application/json", "Content-Type": "application/json"}, + "return_data": { + "return_data_1": ("return_parameter_1",), + "return_data_2": ("return_parameter_2",), + }, + "auth": ("abc", "123"), + }, + "MAKE_FILES_REQUEST": { + "verb": "POST", + "description": "Test Catalog Request With Files", + "uri": partial("{endpoint}/{test_uri_parameter}".format, endpoint=TEST_URI), + "uri-parameters": ["test_uri_parameter"], + "files-parameters": ["file_path", "file_type"], + "payload": "{}/test_payload.jinja".format(THIS_DIR), + "payload-parameters": ["test_item_parameter"], + "success_code": 200, + "headers": {"Accept": "application/json", "Content-Type": "application/json"}, + "return_data": { + "return_data_1": ("return_parameter_1",), + "return_data_2": ("return_parameter_2",), + }, + "auth": ("abc", "123"), + }, +} + + +RETURN_DATA = {"return_parameter_1": "abc", "return_parameter_2": "123"} diff --git a/onap-client/onap_client/tests/utils.py b/onap-client/onap_client/tests/utils.py new file mode 100644 index 0000000..ddcca9e --- /dev/null +++ b/onap-client/onap_client/tests/utils.py @@ -0,0 +1,79 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import responses +import functools + + +def mockup_client(client): + for k, v in client.catalog_items.items(): + mockup_catalog_item(v) + + +def mockup_return_item(item, item_list): + items = list(item_list) + if not items: + return {item: item} + new_item = items.pop(0) + return {item: mockup_return_item(new_item, items)} + + +def mockup_catalog_item( + catalog_resource, override_return_data=None, override_uri_params={} +): + uri = catalog_resource.uri + if isinstance(uri, functools.partial): + params = {} + for param in catalog_resource.uri_parameters: + params[param] = param + params.update(override_uri_params) + uri = uri(**params) + + return_data = catalog_resource.return_data + return_items = {} + for k, v in return_data.items(): + return_items.update(mockup_return_item(k, v)) + + if override_return_data: + return_items = override_return_data + + responses.add( + getattr(responses, catalog_resource.verb), + uri, + json=return_items, + status=catalog_resource.success_code, + ) diff --git a/onap-client/onap_client/util.py b/onap-client/onap_client/util.py new file mode 100644 index 0000000..d3148d0 --- /dev/null +++ b/onap-client/onap_client/util.py @@ -0,0 +1,134 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import json +from prettytable import PrettyTable + + +def utility_cli(onap_client, cli_arguments): + functions = onap_client.utility_functions + + if len(cli_arguments) == 0 or cli_arguments[0] == "--help": + help(functions) + else: + while cli_arguments: + argument = cli_arguments.pop(0) + + if argument == "--help": + help(functions) + return + + argument = convert_to_underscores(argument) + functions = functions.get(argument) + if not functions: + print("Invalid argument {}. Try --help.".format(argument)) + return + + if callable(functions): + if cli_arguments[0] == "--help": + help(functions) + return + + if functions.__code__.co_argcount != len(cli_arguments): + print( + "Function requires {} arguments, but {} were passed. Try --help.".format( + functions.__code__.co_argcount, len(cli_arguments) + ) + ) + return + + return_data = functions(*cli_arguments[0:]) + if isinstance(return_data, str): + print(return_data) + elif isinstance(return_data, dict) or isinstance(return_data, list): + print(json.dumps(return_data, indent=4)) + + return + + +def convert_to_underscores(argument): + return argument.replace("-", "_") + + +def convert_to_dash(argument): + return argument.replace("_", "-") + + +def help(functions): + actions = {} + actions["--help"] = ("", "") + if isinstance(functions, dict): + for k, v in functions.items(): + actions[convert_to_dash(k)] = ( + v.__doc__, + list(v.__code__.co_varnames[: v.__code__.co_argcount]), + ) + elif callable(functions): + actions[convert_to_dash(functions.__name__)] = ( + functions.__doc__, + list(functions.__code__.co_varnames[: functions.__code__.co_argcount]), + ) + + print(help_table(actions)) + + +def help_table(actions): + x = PrettyTable() + + x.field_names = [ + "name", + "description", + "parameters", + ] + x.align["name"] = "l" + x.align["description"] = "l" + x.align["parameters"] = "l" + + for action, data in actions.items(): + name = action + description = data[0] + parameters = [] + parameters.extend("<{}>".format(x) for x in data[1]) + x.add_row([name, description, "\n".join(parameters)]) + x.add_row(["", "", ""]) + + return x + + +def utility(func): + func.utility_function = True + return func diff --git a/onap-client/onap_client/vid/__init__.py b/onap-client/onap_client/vid/__init__.py new file mode 100644 index 0000000..1cb6244 --- /dev/null +++ b/onap-client/onap_client/vid/__init__.py @@ -0,0 +1,40 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +from onap_client.config import APP_CONFIG + +VID_PROPERTIES = APP_CONFIG.vid diff --git a/onap-client/onap_client/vid/catalog/__init__.py b/onap-client/onap_client/vid/catalog/__init__.py new file mode 100644 index 0000000..5519a84 --- /dev/null +++ b/onap-client/onap_client/vid/catalog/__init__.py @@ -0,0 +1,36 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ diff --git a/onap-client/onap_client/vid/catalog/maintenance_catalog.py b/onap-client/onap_client/vid/catalog/maintenance_catalog.py new file mode 100644 index 0000000..2f01a79 --- /dev/null +++ b/onap-client/onap_client/vid/catalog/maintenance_catalog.py @@ -0,0 +1,154 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +import uuid +from functools import partial + +from onap_client import vid +from onap_client import config +from onap_client.vid.client import VIDClient + +PAYLOADS_DIR = config.PAYLOADS_DIR +vid_properties = vid.VID_PROPERTIES +application_id = config.APPLICATION_ID + + +class MaintenanceClient(VIDClient): + @property + def catalog_resources(self): + return CATALOG_RESOURCES + + @property + def namespace(self): + return "maintenance" + + +CATALOG_RESOURCES = { + "CREATE_OWNING_ENTITY": { + "verb": "POST", + "description": "Creates an owning entity in VID", + "uri": partial( + "{endpoint}{service_path}/category_parameter/owningEntity".format, + endpoint=vid_properties.VID_ENDPOINT, + service_path=vid_properties.VID_MAINTENANCE_PATH, + ), + "payload-parameters": ["name"], + "payload": "{}/vid_maintenance.jinja".format(PAYLOADS_DIR), + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (vid_properties.VID_USERNAME, vid_properties.VID_PASSWORD,), + }, + "CREATE_LINE_OF_BUSINESS": { + "verb": "POST", + "description": "Creates a line of business in VID", + "uri": partial( + "{endpoint}{service_path}/category_parameter/lineOfBusiness".format, + endpoint=vid_properties.VID_ENDPOINT, + service_path=vid_properties.VID_MAINTENANCE_PATH, + ), + "payload-parameters": ["name"], + "payload": "{}/vid_maintenance.jinja".format(PAYLOADS_DIR), + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (vid_properties.VID_USERNAME, vid_properties.VID_PASSWORD,), + }, + "CREATE_PLATFORM": { + "verb": "POST", + "description": "Creates a platform in VID", + "uri": partial( + "{endpoint}{service_path}/category_parameter/platform".format, + endpoint=vid_properties.VID_ENDPOINT, + service_path=vid_properties.VID_MAINTENANCE_PATH, + ), + "payload-parameters": ["name"], + "payload": "{}/vid_maintenance.jinja".format(PAYLOADS_DIR), + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (vid_properties.VID_USERNAME, vid_properties.VID_PASSWORD,), + }, + "CREATE_PROJECT": { + "verb": "POST", + "description": "Creates a project in VID", + "uri": partial( + "{endpoint}{service_path}/category_parameter/project".format, + endpoint=vid_properties.VID_ENDPOINT, + service_path=vid_properties.VID_MAINTENANCE_PATH, + ), + "payload-parameters": ["name"], + "payload": "{}/vid_maintenance.jinja".format(PAYLOADS_DIR), + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (vid_properties.VID_USERNAME, vid_properties.VID_PASSWORD,), + }, + "GET_CATEGORY_PARAMETERS": { + "verb": "GET", + "description": "Returns the category parameters currently stored in VID", + "uri": partial( + "{endpoint}{service_path}/category_parameter?familyName=PARAMETER_STANDARDIZATION".format, + endpoint=vid_properties.VID_ENDPOINT, + service_path=vid_properties.VID_MAINTENANCE_PATH, + ), + "success_code": 200, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json", + "X-TransactionId": str(uuid.uuid4()), + "X-FromAppId": application_id, + }, + "auth": (vid_properties.VID_USERNAME, vid_properties.VID_PASSWORD,), + }, +} diff --git a/onap-client/onap_client/vid/client.py b/onap-client/onap_client/vid/client.py new file mode 100644 index 0000000..3b95618 --- /dev/null +++ b/onap-client/onap_client/vid/client.py @@ -0,0 +1,69 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ + +from functools import partial +from onap_client import vid +from onap_client.client.clients import Client +from onap_client import config + +vid_properties = vid.VID_PROPERTIES +application_id = config.APPLICATION_ID + + +class VIDClient(Client): + @property + def namespace(self): + return "vid" + + @property + def catalog_resources(self): + return CATALOG_RESOURCES + + +CATALOG_RESOURCES = { + "HEALTH_CHECK": { + "verb": "GET", + "description": "Queries VID health check endpoint", + "uri": partial( + "{endpoint}{service_path}".format, + endpoint=vid_properties.VID_ENDPOINT, + service_path=vid_properties.VID_HEALTH_CHECK_PATH, + ), + "success_code": 200, + "auth": (vid_properties.VID_USERNAME, vid_properties.VID_PASSWORD,), + }, +} diff --git a/onap-client/onap_client/vid/tests/__init__.py b/onap-client/onap_client/vid/tests/__init__.py new file mode 100644 index 0000000..5519a84 --- /dev/null +++ b/onap-client/onap_client/vid/tests/__init__.py @@ -0,0 +1,36 @@ +# -*- coding: utf8 -*- +# ============LICENSE_START======================================================= +# org.onap.vvp/validation-scripts +# =================================================================== +# Copyright © 2020 AT&T Intellectual Property. All rights reserved. +# =================================================================== +# +# Unless otherwise specified, all software contained herein is licensed +# under the Apache License, Version 2.0 (the "License"); +# you may not use this software 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. +# +# +# +# Unless otherwise specified, all documentation contained herein is licensed +# under the Creative Commons License, Attribution 4.0 Intl. (the "License"); +# you may not use this documentation except in compliance with the License. +# You may obtain a copy of the License at +# +# https://creativecommons.org/licenses/by/4.0/ +# +# Unless required by applicable law or agreed to in writing, documentation +# 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. +# +# ============LICENSE_END============================================ |