diff options
68 files changed, 5913 insertions, 0 deletions
@@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2019 Orange-OpenSource / lfn / onap + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/mock-aai/Dockerfile b/mock-aai/Dockerfile new file mode 100644 index 0000000..d558a0d --- /dev/null +++ b/mock-aai/Dockerfile @@ -0,0 +1,11 @@ +FROM python:3.8-alpine + +COPY . /app + +WORKDIR /app + +RUN pip install -r requirements.txt + +ENTRYPOINT ["python"] + +CMD ["app.py"] diff --git a/mock-aai/README.md b/mock-aai/README.md new file mode 100644 index 0000000..5217b76 --- /dev/null +++ b/mock-aai/README.md @@ -0,0 +1,2 @@ +# mock-aai + diff --git a/mock-aai/app.py b/mock-aai/app.py new file mode 100644 index 0000000..408d3e4 --- /dev/null +++ b/mock-aai/app.py @@ -0,0 +1,154 @@ +"""A&AI mock application.""" +""" + Copyright 2023 Deutsche Telekom AG, Orange + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +from flask import Flask +from flask_restful import Resource, Api + +from resources.cloud_region import ( + CloudRegion, + CloudRegionList, + CloudRegionRelationshipList, + CloudRegionRelationship, + Tenant, + TenantList, +) +from resources.complex import Complex, ComplexList +from resources.customer import ( + Customer, + CustomerList, + ServiceSubscription, + ServiceSubscriptionInstance, + ServiceSubscriptionList, + ServiceSubscriptionRelationship, + ServiceSubscriptionRelationshipList, + ServiceSubscriptionInstanceList, + ServiceSubscriptionInstanceRelationshipList, +) +from resources.network import Network +from resources.vnf import Vnf, VfModule, VfModuleList + +app = Flask(__name__) +api = Api(app) + +API_VERSIONS_SUPPORTED = ["v16", "v20", "v23", "v26"] + +def generate_urls_for_all_versions(endpoint_pattern: str) -> list: + """Helper function. + + Generates list of endpoints by replacing <version> pattern for each supported API version. + + Args: + endpoint_pattern (str): url pattern with <version> tag to be replaced + + Returns: + list: List of string values reporesenting endpoints that are currently supported + + """ + return [endpoint_pattern.replace("<version>", api_version) for api_version in API_VERSIONS_SUPPORTED] + +@app.route("/reset") +def reset() -> str: + """Reset endpoint. + + Reset all resources. + + Returns: + str: Empty string, it has to returns anything + + """ + CloudRegion.reset() + Complex.reset() + Customer.reset() + Network.reset() + Vnf.reset() + return "" + + +# Cloud region resource +api.add_resource(CloudRegionList, *generate_urls_for_all_versions("/aai/<version>/cloud-infrastructure/cloud-regions")) +api.add_resource( + CloudRegion, + *generate_urls_for_all_versions("/aai/<version>/cloud-infrastructure/cloud-regions/cloud-region/<cloud_owner>/<cloud_region_id>"), +) +api.add_resource( + CloudRegionRelationshipList, + *generate_urls_for_all_versions("/aai/<version>/cloud-infrastructure/cloud-regions/cloud-region/<cloud_owner>/<cloud_region_id>/relationship-list"), +) +api.add_resource( + CloudRegionRelationship, + *generate_urls_for_all_versions("/aai/<version>/cloud-infrastructure/cloud-regions/cloud-region/<cloud_owner>/<cloud_region_id>/relationship-list/relationship"), +) +api.add_resource( + TenantList, + *generate_urls_for_all_versions("/aai/<version>/cloud-infrastructure/cloud-regions/cloud-region/<cloud_owner>/<cloud_region_id>/tenants"), +) +api.add_resource( + Tenant, + *generate_urls_for_all_versions("/aai/<version>/cloud-infrastructure/cloud-regions/cloud-region/<cloud_owner>/<cloud_region_id>/tenants/tenant/<tenant_id>"), +) +# Complex resource +api.add_resource(ComplexList, *generate_urls_for_all_versions("/aai/<version>/cloud-infrastructure/complexes")) +api.add_resource( + Complex, *generate_urls_for_all_versions("/aai/<version>/cloud-infrastructure/complexes/complex/<physical_location_id>") +) +# Customer resource +api.add_resource(CustomerList, *generate_urls_for_all_versions("/aai/<version>/business/customers")) +api.add_resource(Customer, *generate_urls_for_all_versions("/aai/<version>/business/customers/customer/<global_customer_id>")) +api.add_resource( + ServiceSubscriptionList, + *generate_urls_for_all_versions("/aai/<version>/business/customers/customer/<global_customer_id>/service-subscriptions"), +) +api.add_resource( + ServiceSubscription, + *generate_urls_for_all_versions("/aai/<version>/business/customers/customer/<global_customer_id>/service-subscriptions/service-subscription/<service_type>"), +) +api.add_resource( + ServiceSubscriptionRelationshipList, + *generate_urls_for_all_versions("/aai/<version>/business/customers/customer/<global_customer_id>/service-subscriptions/service-subscription/<service_type>/relationship-list"), +) +api.add_resource( + ServiceSubscriptionRelationship, + *generate_urls_for_all_versions("/aai/<version>/business/customers/customer/<global_customer_id>/service-subscriptions/service-subscription/<service_type>/relationship-list/relationship"), +) +api.add_resource( + ServiceSubscriptionInstance, + *generate_urls_for_all_versions("/aai/<version>/business/customers/customer/<global_customer_id>/service-subscriptions/service-subscription/<service_type>/service-instances/service-instance/<service_instance_id>"), +) +api.add_resource( + ServiceSubscriptionInstanceList, + *generate_urls_for_all_versions("/aai/<version>/business/customers/customer/<global_customer_id>/service-subscriptions/service-subscription/<service_type>/service-instances"), +) +api.add_resource( + ServiceSubscriptionInstanceRelationshipList, + *generate_urls_for_all_versions("/aai/<version>/business/customers/customer/<global_customer_id>/service-subscriptions/service-subscription/<service_type>/service-instances/service-instance/<service_instance_id>/relationship-list"), +) +# VNF resource +api.add_resource(Vnf, *generate_urls_for_all_versions("/aai/<version>/network/generic-vnfs/generic-vnf/<vnf_instance_id>")) +api.add_resource( + VfModule, + *generate_urls_for_all_versions("/aai/<version>/network/generic-vnfs/generic-vnf/<vnf_instance_id>/vf-modules/<vf_module_instance_id>"), +) +api.add_resource( + VfModuleList, + *generate_urls_for_all_versions("/aai/<version>/network/generic-vnfs/generic-vnf/<vnf_instance_id>/vf-modules"), +) +api.add_resource( + Network, *generate_urls_for_all_versions("/aai/<version>/network/l3-networks/l3-network/<network_instance_id>") +) + + +if __name__ == "__main__": + app.run(host="0.0.0.0", debug=True) diff --git a/mock-aai/requirements.txt b/mock-aai/requirements.txt new file mode 100644 index 0000000..04f7256 --- /dev/null +++ b/mock-aai/requirements.txt @@ -0,0 +1 @@ +Flask-RESTful==0.3.9
\ No newline at end of file diff --git a/mock-aai/resources/__init__.py b/mock-aai/resources/__init__.py new file mode 100644 index 0000000..bbc4e6a --- /dev/null +++ b/mock-aai/resources/__init__.py @@ -0,0 +1,16 @@ +"""A&AI mock resources package.""" +""" + Copyright 2023 Deutsche Telekom AG, Orange + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" diff --git a/mock-aai/resources/cloud_region.py b/mock-aai/resources/cloud_region.py new file mode 100644 index 0000000..aa5098d --- /dev/null +++ b/mock-aai/resources/cloud_region.py @@ -0,0 +1,193 @@ +"""A&AI CloudRegion mock module.""" +""" + Copyright 2023 Deutsche Telekom AG, Orange + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +from typing import Dict, List, Tuple + +from flask_restful import Resource, request + +CLOUD_REGIONS = {} + + +class CloudRegion(Resource): + """Cloud region resource.""" + + def get(self, cloud_owner: str, cloud_region_id: str) -> Dict[str, str]: + """Get cloud region. + + Get cloud region from CLOUD_REGIONS dictionary. + + Args: + cloud_owner (str): cloud owner key value + cloud_region_id (str): cloud region id key value + + Returns: + Dict[str, str]: Cloud region dictionary + + """ + return CLOUD_REGIONS[cloud_owner][cloud_region_id] + + def put(self, cloud_owner: str, cloud_region_id: str) -> Tuple[str, int]: + """Cloud region resource put method. + + Add cloud region data into CLOUD_REGIONS dictionary. + + Args: + cloud_owner (str): Cloud owner key value + cloud_region_id (str): Cloud region id key value + + Returns: + Tuple[str, int]: Response tuple. First element is a response body, + the second one is HTTP response code. + """ + CLOUD_REGIONS.update({cloud_owner: {cloud_region_id: request.get_json()}}) + return "", 201 + + @staticmethod + def reset() -> None: + """Reset Cloud region resource. + + Clean CLOUD_REGIONS dictionary + + """ + global CLOUD_REGIONS + CLOUD_REGIONS = {} + + +class CloudRegionList(Resource): + """List of cloud regions resource.""" + + def get(self): + """Get the list of cloud regions. + + Return data from CLOUD_REGIONS dictionary. + + Returns: + Dict[str, List]: Cloud regions dictionary + + """ + return { + "cloud-region": [ + data + for cloud_owner, cloud_owner_dict in CLOUD_REGIONS.items() + for cloud_owner, data in cloud_owner_dict.items() + ] + } + + +class CloudRegionRelationship(Resource): + """Cloud region relationship resource.""" + + def put(self, cloud_owner: str, cloud_region_id: str): + """Cloud region relationship resource put method. + + Add cloud region relationship data into CLOUD_REGIONS dictionary. + + Args: + cloud_owner (str): Cloud owner key value + cloud_region_id (str): Cloud region id key value + + """ + try: + CLOUD_REGIONS[cloud_owner][cloud_region_id]["relationships"].apped(request.get_json()) + except KeyError: + CLOUD_REGIONS[cloud_owner][cloud_region_id]["relationships"] = [request.get_json()] + + +class CloudRegionRelationshipList(Resource): + """List of cloud region relationships resource.""" + + def get(self, cloud_owner: str, cloud_region_id: str) -> Dict[str, List]: + """Get the list of cloud region relationships. + + Return data from CLOUD_REGIONS dictionary. + + Args: + cloud_owner (str): Cloud owner key value + cloud_region_id (str): Cloud region id key value + + Returns: + Dict[str, List]: Cloud region relationships dictionary + + """ + try: + return {"relationship": CLOUD_REGIONS[cloud_owner][cloud_region_id]["relationships"]} + except KeyError: + return {"relationship": []} + + +class Tenant(Resource): + """Cloud region tenant resource.""" + + def put(self, cloud_owner: str, cloud_region_id: str, tenant_id: str) -> None: + """Cloud region tenant resource put method. + + Add cloud region tenant data into CLOUD_REGIONS dictionary. + + Args: + cloud_owner (str): Cloud owner key value + cloud_region_id (str): Cloud region id key value + + """ + try: + CLOUD_REGIONS[cloud_owner][cloud_region_id]["tenants"].update( + {tenant_id: request.get_json()} + ) + except KeyError: + CLOUD_REGIONS[cloud_owner][cloud_region_id]["tenants"] = {tenant_id: request.get_json()} + + def get(self, cloud_owner: str, cloud_region_id: str, tenant_id: str) -> Dict[str, str]: + """Get cloud region tenant. + + Get cloud region tenant from CLOUD_REGIONS dictionary. + + Args: + cloud_owner (str): cloud owner key value + cloud_region_id (str): cloud region id key value + + Returns: + Dict[str, str]: Cloud region tenant dictionary + + """ + try: + return CLOUD_REGIONS[cloud_owner][cloud_region_id]["tenants"][tenant_id] + except KeyError: + return "", 404 + + +class TenantList(Resource): + """List of tenants resource.""" + + def get(self, cloud_owner: str, cloud_region_id: str) -> Dict[str, List]: + """Get the list of cloud region tenants. + + Return data from CLOUD_REGIONS dictionary. + + Args: + cloud_owner (str): Cloud owner key value + cloud_region_id (str): Cloud region id key value + + Returns: + Dict[str, List]: Cloud region tenants dictionary + + """ + return { + "tenant": [ + data + for tenant_id, data in CLOUD_REGIONS[cloud_owner][cloud_region_id] + .get("tenants", {}) + .items() + ] + } diff --git a/mock-aai/resources/complex.py b/mock-aai/resources/complex.py new file mode 100644 index 0000000..9185221 --- /dev/null +++ b/mock-aai/resources/complex.py @@ -0,0 +1,65 @@ +"""A&AI Complex mock module.""" +""" + Copyright 2023 Deutsche Telekom AG, Orange + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +from typing import Dict, List + +from flask_restful import reqparse, Resource, request + +COMPLEXES = {} + +parser = reqparse.RequestParser() + + +class Complex(Resource): + """Complex resource class.""" + + def put(self, physical_location_id: str): + """Complex resource put method. + + Add complex data sent in JSON to COMPLEXES dictionary. + + Args: + physical_location_id (str): Complex physical location id + + """ + COMPLEXES.update({physical_location_id: request.get_json()}) + + @staticmethod + def reset(): + """Reset Complex resource. + + Clean COMPLEXES dictionary + + """ + global COMPLEXES + COMPLEXES = {} + + +class ComplexList(Resource): + """List of complexes resource.""" + + def get(self) -> Dict[str, List]: + """Get the list of complexes. + + Return data from COMPLEXES dictionary. + + Returns: + Dict[str, List]: Complexes dictionary + + """ + return { + "complex": [complex_data for physical_location_id, complex_data in COMPLEXES.items()] + } diff --git a/mock-aai/resources/customer.py b/mock-aai/resources/customer.py new file mode 100644 index 0000000..d53eb0d --- /dev/null +++ b/mock-aai/resources/customer.py @@ -0,0 +1,325 @@ +"""A&AI Customer mock module.""" +""" + Copyright 2023 Deutsche Telekom AG, Orange + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +from typing import Dict, List +from uuid import uuid4 + +from flask_restful import reqparse, Resource, request + + +CUSTOMERS = {} + + +def random_resource_version() -> str: + """Generate random resource version string value. + + Returns: + str: UUID value + + """ + return str(uuid4()) + + +class Customer(Resource): + """Customer resource.""" + + @staticmethod + def reset(): + """Reset Cloud region resource. + + Clean CUSTOMERS dictionary + + """ + global CUSTOMERS + CUSTOMERS = {} + + def get(self, global_customer_id: str): + """Get customer. + + Get customer from CUSTOMERS dictionary. + + Args: + global_customer_id (str): global customer id key value + + Returns: + Dict[str, str]: Customer dictionary + + """ + return CUSTOMERS[global_customer_id] + + def put(self, global_customer_id: str): + """Resource put method. + + Add customer data into CUSTOMERS dictionary. + + Args: + global_customer_id (str): global customer id key value + + Returns: + Tuple[str, int]: Response tuple. First element is a response body, + the second one is HTTP response code. + + """ + CUSTOMERS[global_customer_id] = request.get_json() + CUSTOMERS[global_customer_id]["resource-version"] = random_resource_version() + return "", 201 + + +class CustomerList(Resource): + """List of customers resource.""" + + def get(self) -> Dict[str, List]: + """Get the list of customers. + + Return data from CUSTOMERS dictionary. + + Returns: + Dict[str, List]: Customer dictionary + + """ + return {"customer": [data for global_customer_id, data in CUSTOMERS.items()]} + + +class ServiceSubscription(Resource): + """Service subscription resource.""" + + def put(self, global_customer_id: str, service_type: str) -> None: + """Service subscription resource put method. + + Add service subscription data into CUSTOMERS dictionary. + + Args: + global_customer_id (str): Global customer id key value + service_type (str): Service type key value + + """ + try: + CUSTOMERS[global_customer_id]["service_subscriptions"][ + service_type + ] = {"service-type": service_type} + except KeyError: + CUSTOMERS[global_customer_id]["service_subscriptions"] = { + service_type: {"service-type": service_type} + } + CUSTOMERS[global_customer_id]["service_subscriptions"][service_type].update( + {"service-type": service_type} + ) + + +class ServiceSubscriptionList(Resource): + """List of service subscriptions resource.""" + + def get(self, global_customer_id: str) -> Dict[str, List]: + """Get the list of service subscriptions. + + Return data from CUSTOMERS dictionary. + + Args: + global_customer_id (str): Global customer id key value + + Returns: + Dict[str, List]: Service subscriptions dictionary + + """ + service_type: str = request.args.get("service-type") + if not service_type: + return { + "service-subscription": [ + data + for service_type, data in CUSTOMERS[global_customer_id][ + "service_subscriptions" + ].items() + ] + } + try: + return { + "service-subscription": [ + CUSTOMERS[global_customer_id]["service_subscriptions"][service_type] + ] + } + except KeyError: + return "", 404 + + +class ServiceSubscriptionRelationship(Resource): + """Service subscription relationship resource.""" + + def put(self, global_customer_id: str, service_type: str) -> None: + """Service subscription relationship resource put method. + + Add service subscription relationship data into CUSTOMERS dictionary. + + Args: + global_customer_id (str): Global customer id key value + service_type (str): Service type key value + + """ + try: + CUSTOMERS[global_customer_id]["service_subscriptions"][service_type][ + "relationships" + ].append(request.get_json()) + except KeyError: + CUSTOMERS[global_customer_id]["service_subscriptions"][service_type][ + "relationships" + ] = [request.get_json()] + + +class ServiceSubscriptionRelationshipList(Resource): + """Service subscription relationships list resource.""" + + def get(self, global_customer_id: str, service_type: str) -> Dict[str, List]: + """Get the list of service subscription relationships. + + Return data from CUSTOMERS dictionary. + + Args: + global_customer_id (str): Global customer id key value + service_type (str): Service type key value + + Returns: + Dict[str, List]: Service subscription relationships dictionary + + """ + return { + "relationship": CUSTOMERS[global_customer_id]["service_subscriptions"][ + service_type + ].get("relationships", []) + } + + +class ServiceSubscriptionInstance(Resource): + """Service subscription instance resource.""" + + def delete(self, global_customer_id: str, service_type: str, service_instance_id: str) -> None: + """Delete service subscription instance. + + Removes data from CUSTOMERS dictionary. + + Args: + global_customer_id (str): Global customer id key value + service_type (str): Service type key value + service_instance_id (str): Service instance id key value + + """ + del CUSTOMERS[global_customer_id]["service_subscriptions"][service_type][ + "service_instances" + ][service_instance_id] + + +class ServiceSubscriptionInstanceList(Resource): + """Service subscription instances list resource.""" + + def get(self, global_customer_id: str, service_type: str) -> Dict[str, List]: + """Get service subscription's service instances. + + Returns data from CUSTOMERS dictionary + + Args: + global_customer_id (str): Global customer id key value + service_type (str): Service type key value + + Returns: + Dict[str, List]: Service instances dictionary + """ + return { + "service-instance": [ + data + for instance_id, data in CUSTOMERS[global_customer_id]["service_subscriptions"][ + service_type + ] + .get("service_instances", dict()) + .items() + ] + } + + def post(self, global_customer_id: str, service_type: str) -> None: + """Add service instance to service subscription. + + Add service instance data dictionary to service subscription's + service instances dictionary. + + Args: + global_customer_id (str): Global customer id key value + service_type (str): Service type key value + + """ + request_data = request.get_json() + instance_id = request_data["service-instance-id"] + try: + CUSTOMERS[global_customer_id]["service_subscriptions"][service_type][ + "service_instances" + ][instance_id] = request_data + except KeyError: + CUSTOMERS[global_customer_id]["service_subscriptions"][service_type][ + "service_instances" + ] = {instance_id: request_data} + + +class ServiceSubscriptionInstanceRelationshipList(Resource): + """Service subscription instance relationships list resource.""" + + def post(self, global_customer_id: str, service_type: str, service_instance_id) -> None: + """Add relationship into service instance relationships list. + + Args: + global_customer_id (str): Global customer id key value + service_type (str): Service type key value + service_instance_id (str): Service instance id key value + + """ + try: + CUSTOMERS[global_customer_id]["service_subscriptions"][service_type][ + "service_instances" + ][service_instance_id]["relationships"].append(request.get_json()) + except KeyError: + CUSTOMERS[global_customer_id]["service_subscriptions"][service_type][ + "service_instances" + ][service_instance_id]["relationships"] = [request.get_json()] + + def get( + self, global_customer_id: str, service_type: str, service_instance_id: str + ) -> Dict[str, List]: + """Get the service instance relationships list. + + Args: + global_customer_id (str): Global customer id key value + service_type (str): Service type key value + service_instance_id (str): Service instance id key value + + Returns: + Dict[str, List]: Service instance relationships dictionary + + """ + return { + "relationship": CUSTOMERS[global_customer_id]["service_subscriptions"][service_type][ + "service_instances" + ][service_instance_id].get("relationships", []) + } + + def delete(self, global_customer_id: str, service_type: str, service_instance_id: str) -> None: + """Delete service subscription instance relationships. + + Make relationships list clea. + + Args: + global_customer_id (str): Global customer id key value + service_type (str): Service type key value + service_instance_id (str): Service instance id key value + + """ + CUSTOMERS[global_customer_id]["service_subscriptions"][service_type]["service_instances"][ + service_instance_id + ]["relationships"] = [] diff --git a/mock-aai/resources/network.py b/mock-aai/resources/network.py new file mode 100644 index 0000000..3fddd41 --- /dev/null +++ b/mock-aai/resources/network.py @@ -0,0 +1,61 @@ +"""Vnf resources module.""" +""" + Copyright 2023 Deutsche Telekom AG, Orange + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +from typing import Dict, List +from uuid import uuid4 + +from flask_restful import Resource, request + + +NETWORKS = {} + + +class Network(Resource): + """Network resource.""" + + @staticmethod + def reset(): + """Reset resource for tests. + + Create new, empty NETWORKS dictionary + + """ + global NETWORKS + NETWORKS = {} + + def get(self, network_instance_id: str) -> Dict[str, List]: + """Get network instance data. + + Get data from NETWORKS dictionary + + Args: + network_instance_id (str): Network instance id key value + + Returns: + Dict[str, List]: Network instance data dictionary + + """ + try: + return NETWORKS[network_instance_id] + except KeyError: + NETWORKS[network_instance_id] = { + "network-id": network_instance_id, + "is-bound-to-vpn": False, + "is-provider-network": False, + "is-shared-network": False, + "is-external-network": False, + } + return NETWORKS[network_instance_id] diff --git a/mock-aai/resources/vnf.py b/mock-aai/resources/vnf.py new file mode 100644 index 0000000..3a49f01 --- /dev/null +++ b/mock-aai/resources/vnf.py @@ -0,0 +1,108 @@ +"""Vnf resources module.""" +""" + Copyright 2023 Deutsche Telekom AG, Orange + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +from typing import Dict, List +from uuid import uuid4 + +from flask_restful import Resource, request + + +VNFS = {} + + +class Vnf(Resource): + """Vnf resource.""" + + @staticmethod + def reset(): + """Reset resource for tests. + + Create new, empty VNFS dictionary + + """ + global VNFS + VNFS = {} + + def get(self, vnf_instance_id: str) -> Dict[str, List]: + """Get vnf instance data. + + Get data from VNFS dictionary + + Args: + vnf_instance_id (str): Vnf instance id key value + + Returns: + Dict[str, List]: Vnf instance data dictionary + + """ + try: + return VNFS[vnf_instance_id] + except KeyError: + VNFS[vnf_instance_id] = {"vnf-id": vnf_instance_id, "model-version-id": str(uuid4())} + return VNFS[vnf_instance_id] + + +class VfModule(Resource): + """Vf module resource.""" + + def delete(self, vnf_instance_id: str, vf_module_instance_id: str) -> None: + """Delete vf module. + + Removes vf module data from VNFS dictionary. + + Args: + vnf_instance_id (str): Vnf instance id key value + vf_module_instance_id (str): Vf module instance id key value + + """ + del VNFS[vnf_instance_id]["vf_modules"][vf_module_instance_id] + + +class VfModuleList(Resource): + """Vf module list resource.""" + + def post(self, vnf_instance_id: str) -> None: + """Create vf module. + + Add vf module data into VNFS dictionary. + + Args: + vnf_instance_id (str): Vnf instance id key value + + """ + vf_module_data = request.get_json() + vf_module_dict = {vf_module_data["vf-module-id"]: vf_module_data} + try: + VNFS[vnf_instance_id]["vf_modules"].update(vf_module_dict) + except KeyError: + VNFS[vnf_instance_id]["vf_modules"] = vf_module_dict + + def get(self, vnf_instance_id: str) -> Dict[str, List]: + """Get Vnf instance Vf modules list. + + Get data from VNFS dictionary + + Args: + vnf_instance_id (str): Vnf instance id key value + + Returns: + Dict[str, List]: Vnf instance vf modules dictionary + """ + return { + "vf-module": [ + data for vf_module_id, data in VNFS[vnf_instance_id].get("vf_modules", {}).items() + ] + } diff --git a/mock-cds/Dockerfile b/mock-cds/Dockerfile new file mode 100644 index 0000000..4a396bb --- /dev/null +++ b/mock-cds/Dockerfile @@ -0,0 +1,17 @@ +FROM python:3.7.8-alpine + +COPY . /app +WORKDIR /app + +# GCC for Alpine Linux in Docker +RUN apk add build-base + + +# Dependencies +RUN pip install pipenv && \ + pipenv requirements > requirements.txt && \ + pip install -r requirements.txt + + +ENTRYPOINT ["python"] +CMD ["app/app.py"]
\ No newline at end of file diff --git a/mock-cds/Pipfile b/mock-cds/Pipfile new file mode 100644 index 0000000..857def1 --- /dev/null +++ b/mock-cds/Pipfile @@ -0,0 +1,12 @@ +[[source]] +name = "pypi" +url = "https://pypi.org/simple" +verify_ssl = true + +[dev-packages] + +[packages] +aiohttp = "*" + +[requires] +python_version = "3.7" diff --git a/mock-cds/Pipfile.lock b/mock-cds/Pipfile.lock new file mode 100644 index 0000000..9ac068c --- /dev/null +++ b/mock-cds/Pipfile.lock @@ -0,0 +1,125 @@ +{ + "_meta": { + "hash": { + "sha256": "2c5c75ab0fc628a232239706591e922fec111ed0bb34cc8c62d3352e2d21a02d" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.7" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "aiohttp": { + "hashes": [ + "sha256:1e984191d1ec186881ffaed4581092ba04f7c61582a177b187d3a2f07ed9719e", + "sha256:259ab809ff0727d0e834ac5e8a283dc5e3e0ecc30c4d80b3cd17a4139ce1f326", + "sha256:2f4d1a4fdce595c947162333353d4a44952a724fba9ca3205a3df99a33d1307a", + "sha256:32e5f3b7e511aa850829fbe5aa32eb455e5534eaa4b1ce93231d00e2f76e5654", + "sha256:344c780466b73095a72c616fac5ea9c4665add7fc129f285fbdbca3cccf4612a", + "sha256:460bd4237d2dbecc3b5ed57e122992f60188afe46e7319116da5eb8a9dfedba4", + "sha256:4c6efd824d44ae697814a2a85604d8e992b875462c6655da161ff18fd4f29f17", + "sha256:50aaad128e6ac62e7bf7bd1f0c0a24bc968a0c0590a726d5a955af193544bcec", + "sha256:6206a135d072f88da3e71cc501c59d5abffa9d0bb43269a6dcd28d66bfafdbdd", + "sha256:65f31b622af739a802ca6fd1a3076fd0ae523f8485c52924a89561ba10c49b48", + "sha256:ae55bac364c405caa23a4f2d6cfecc6a0daada500274ffca4a9230e7129eac59", + "sha256:b778ce0c909a2653741cb4b1ac7015b5c130ab9c897611df43ae6a58523cb965" + ], + "index": "pypi", + "version": "==3.6.2" + }, + "async-timeout": { + "hashes": [ + "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f", + "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3" + ], + "markers": "python_full_version >= '3.5.3'", + "version": "==3.0.1" + }, + "attrs": { + "hashes": [ + "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", + "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==19.3.0" + }, + "chardet": { + "hashes": [ + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + ], + "version": "==3.0.4" + }, + "idna": { + "hashes": [ + "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", + "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.10" + }, + "multidict": { + "hashes": [ + "sha256:1ece5a3369835c20ed57adadc663400b5525904e53bae59ec854a5d36b39b21a", + "sha256:275ca32383bc5d1894b6975bb4ca6a7ff16ab76fa622967625baeebcf8079000", + "sha256:3750f2205b800aac4bb03b5ae48025a64e474d2c6cc79547988ba1d4122a09e2", + "sha256:4538273208e7294b2659b1602490f4ed3ab1c8cf9dbdd817e0e9db8e64be2507", + "sha256:5141c13374e6b25fe6bf092052ab55c0c03d21bd66c94a0e3ae371d3e4d865a5", + "sha256:51a4d210404ac61d32dada00a50ea7ba412e6ea945bbe992e4d7a595276d2ec7", + "sha256:5cf311a0f5ef80fe73e4f4c0f0998ec08f954a6ec72b746f3c179e37de1d210d", + "sha256:6513728873f4326999429a8b00fc7ceddb2509b01d5fd3f3be7881a257b8d463", + "sha256:7388d2ef3c55a8ba80da62ecfafa06a1c097c18032a501ffd4cabbc52d7f2b19", + "sha256:9456e90649005ad40558f4cf51dbb842e32807df75146c6d940b6f5abb4a78f3", + "sha256:c026fe9a05130e44157b98fea3ab12969e5b60691a276150db9eda71710cd10b", + "sha256:d14842362ed4cf63751648e7672f7174c9818459d169231d03c56e84daf90b7c", + "sha256:e0d072ae0f2a179c375f67e3da300b47e1a83293c554450b29c900e50afaae87", + "sha256:f07acae137b71af3bb548bd8da720956a3bc9f9a0b87733e0899226a2317aeb7", + "sha256:fbb77a75e529021e7c4a8d4e823d88ef4d23674a202be4f5addffc72cbb91430", + "sha256:fcfbb44c59af3f8ea984de67ec7c306f618a3ec771c2843804069917a8f2e255", + "sha256:feed85993dbdb1dbc29102f50bca65bdc68f2c0c8d352468c25b54874f23c39d" + ], + "markers": "python_version >= '3.5'", + "version": "==4.7.6" + }, + "typing-extensions": { + "hashes": [ + "sha256:6e95524d8a547a91e08f404ae485bbb71962de46967e1b71a0cb89af24e761c5", + "sha256:79ee589a3caca649a9bfd2a8de4709837400dfa00b6cc81962a1e6a1815969ae", + "sha256:f8d2bd89d25bc39dabe7d23df520442fa1d8969b82544370e03d88b5a591c392" + ], + "markers": "python_version < '3.8'", + "version": "==3.7.4.2" + }, + "yarl": { + "hashes": [ + "sha256:040b237f58ff7d800e6e0fd89c8439b841f777dd99b4a9cca04d6935564b9409", + "sha256:17668ec6722b1b7a3a05cc0167659f6c95b436d25a36c2d52db0eca7d3f72593", + "sha256:3a584b28086bc93c888a6c2aa5c92ed1ae20932f078c46509a66dce9ea5533f2", + "sha256:4439be27e4eee76c7632c2427ca5e73703151b22cae23e64adb243a9c2f565d8", + "sha256:48e918b05850fffb070a496d2b5f97fc31d15d94ca33d3d08a4f86e26d4e7c5d", + "sha256:9102b59e8337f9874638fcfc9ac3734a0cfadb100e47d55c20d0dc6087fb4692", + "sha256:9b930776c0ae0c691776f4d2891ebc5362af86f152dd0da463a6614074cb1b02", + "sha256:b3b9ad80f8b68519cc3372a6ca85ae02cc5a8807723ac366b53c0f089db19e4a", + "sha256:bc2f976c0e918659f723401c4f834deb8a8e7798a71be4382e024bcc3f7e23a8", + "sha256:c22c75b5f394f3d47105045ea551e08a3e804dc7e01b37800ca35b58f856c3d6", + "sha256:c52ce2883dc193824989a9b97a76ca86ecd1fa7955b14f87bf367a61b6232511", + "sha256:ce584af5de8830d8701b8979b18fcf450cef9a382b1a3c8ef189bedc408faf1e", + "sha256:da456eeec17fa8aa4594d9a9f27c0b1060b6a75f2419fe0c00609587b2695f4a", + "sha256:db6db0f45d2c63ddb1a9d18d1b9b22f308e52c83638c26b422d520a815c4b3fb", + "sha256:df89642981b94e7db5596818499c4b2219028f2a528c9c37cc1de45bf2fd3a3f", + "sha256:f18d68f2be6bf0e89f1521af2b1bb46e66ab0018faafa81d70f358153170a317", + "sha256:f379b7f83f23fe12823085cd6b906edc49df969eb99757f58ff382349a3303c6" + ], + "markers": "python_version >= '3.5'", + "version": "==1.5.1" + } + }, + "develop": {} +} diff --git a/mock-cds/README.md b/mock-cds/README.md new file mode 100644 index 0000000..d74d171 --- /dev/null +++ b/mock-cds/README.md @@ -0,0 +1,2 @@ +# mock-cds + diff --git a/mock-cds/app/app.py b/mock-cds/app/app.py new file mode 100644 index 0000000..a15dbea --- /dev/null +++ b/mock-cds/app/app.py @@ -0,0 +1,22 @@ +""" + Copyright 2023 Deutsche Telekom AG, Orange + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +from aiohttp import web +from routes import setup_routes + +app = web.Application() + +setup_routes(app) +web.run_app(app)
\ No newline at end of file diff --git a/mock-cds/app/routes.py b/mock-cds/app/routes.py new file mode 100644 index 0000000..10da668 --- /dev/null +++ b/mock-cds/app/routes.py @@ -0,0 +1,22 @@ +""" + Copyright 2023 Deutsche Telekom AG, Orange + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +from views import blueprint_enrich, blueprint_publish, dictionary, dictionary_get + +def setup_routes(app): + app.router.add_post('/api/v1/blueprint-model/enrich', blueprint_enrich) + app.router.add_post('/api/v1/blueprint-model/publish', blueprint_publish) + app.router.add_post('/api/v1/dictionary', dictionary) + app.router.add_get('/api/v1/dictionary/{name}', dictionary_get)
\ No newline at end of file diff --git a/mock-cds/app/views.py b/mock-cds/app/views.py new file mode 100644 index 0000000..b6d9f37 --- /dev/null +++ b/mock-cds/app/views.py @@ -0,0 +1,51 @@ +""" + Copyright 2023 Deutsche Telekom AG, Orange + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +import json + +from aiohttp import web + +BYTES = b'Response in bytes' +DICTIONARY = { + "message": "Response in JSON", + "success": True + } +DICTIONARIES = {} + +async def blueprint_enrich(request): + """ Blueprint enrichment """ + return web.Response(body=BYTES, status=200) + +async def blueprint_publish(request): + """ Blueprint publishing """ + return web.Response(body=BYTES, status=200) + +async def dictionary(request): + """ Data dictionary """ + try: + body = await request.json() + DICTIONARIES[body["name"]] = body + except ValueError: + print("No JSON sent! Leave it because we used that endpoint during " + "the availability and we won't break the integration tests") + return web.json_response(data=DICTIONARY, status=200) + +async def dictionary_get(request): + """ Data dictionary get """ + name = request.match_info["name"] + try: + return web.json_response(data=DICTIONARIES[name], status=200) + except KeyError: + return web.Response(status=404)
\ No newline at end of file diff --git a/mock-cds/cleanup-dev.sh b/mock-cds/cleanup-dev.sh new file mode 100644 index 0000000..fb4a1bc --- /dev/null +++ b/mock-cds/cleanup-dev.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +export APP_NAME=mock-cds-app + +docker rm $(docker stop $(docker ps -a -q --filter ancestor="${APP_NAME}" --format="{{.ID}}")) +docker rmi ${APP_NAME}
\ No newline at end of file diff --git a/mock-cds/run-dev.sh b/mock-cds/run-dev.sh new file mode 100644 index 0000000..392bfaa --- /dev/null +++ b/mock-cds/run-dev.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +export APP_NAME=mock-cds-app +export DOCKER_PORT=8080 +export APP_PORT=8080 + +docker build -t $APP_NAME . +docker run -d -p $DOCKER_PORT:$APP_PORT $APP_NAME
\ No newline at end of file diff --git a/mock-clamp/.dockerignore b/mock-clamp/.dockerignore new file mode 100644 index 0000000..393fc3b --- /dev/null +++ b/mock-clamp/.dockerignore @@ -0,0 +1,13 @@ +# Binaries for programs and plugins +mock-clamp +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out
\ No newline at end of file diff --git a/mock-clamp/Dockerfile b/mock-clamp/Dockerfile new file mode 100644 index 0000000..12a3a07 --- /dev/null +++ b/mock-clamp/Dockerfile @@ -0,0 +1,32 @@ +FROM golang:1.17-alpine AS builder + +# Add all the source code (except what's ignored +# under `.dockerignore`) to the build context. +ADD ./ /go/src/ + +WORKDIR /go/src + +RUN apk add --no-cache git +RUN go mod init onap.com/mock-clamp +RUN go get github.com/satori/go.uuid +RUN go get github.com/labstack/echo +RUN go get github.com/dgrijalva/jwt-go + +RUN set -ex && \ + CGO_ENABLED=0 GOOS=linux go build \ + -tags netgo \ + -installsuffix cgo \ + -v -a \ + -ldflags '-extldflags "-static"' \ + -o mock-clamp . + +RUN ls -la + +FROM scratch + +# Retrieve the binary from the previous stage +COPY --from=builder /go/src/mock-clamp /app/mock-clamp +WORKDIR /app + +# Set the binary as the entrypoint of the container +ENTRYPOINT [ "./mock-clamp" ] diff --git a/mock-clamp/README.md b/mock-clamp/README.md new file mode 100644 index 0000000..53540d1 --- /dev/null +++ b/mock-clamp/README.md @@ -0,0 +1 @@ +# mock-clamp
\ No newline at end of file diff --git a/mock-clamp/clamp_handlers.go b/mock-clamp/clamp_handlers.go new file mode 100644 index 0000000..edccf22 --- /dev/null +++ b/mock-clamp/clamp_handlers.go @@ -0,0 +1,130 @@ +// Copyright 2023 Deutsche Telekom AG, Orange +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "net/http" + + "github.com/labstack/echo" + "github.com/satori/go.uuid" +) + +//describes loop template in Clamp database +type LoopTemplate struct { + Name string `json:"name"` + DcaeBlueprintId string `json:"dcaeBlueprintId"` + ModelService struct { + ServiceDetails struct { + Name string `json:"name"` + } `json:"serviceDetails"` + } `json:"modelService"` +} + +//describes policy in Clamp database +type Policy struct { + PolicyModelType string `json:"policyModelType"` + Version string `json:"version"` + PolicyAcronym string `json:"policyAcronym"` + CreatedDate string `json:"createdDate"` + UpdatedDate string `json:"updatedDate"` + UpdatedBy string `json:"updatedBy"` + CreatedBy string `json:"createdBy"` +} + +//ClampError is the way to return Error in CLAMP +type ClampError struct { + Message string `json:"message"` + Error string `json:"error"` + Status string `json:"status"` +} + +var templateList []LoopTemplate + +//must modify this function to generate template with service model name +func generateInitialTemplateList() { + templateList = nil + u1 := uuid.NewV4().String() + loop1 := new(LoopTemplate) + loop1.Name = "template_service01" + loop1.DcaeBlueprintId = u1 + loop1.ModelService.ServiceDetails.Name = "service01" + templateList = append(templateList, *loop1) + u2 := uuid.NewV4().String() + loop2 := new(LoopTemplate) + loop2.Name = "template_service02" + loop2.DcaeBlueprintId = u2 + loop2.ModelService.ServiceDetails.Name = "service02" + templateList = append(templateList, *loop2) +} + +var policyList []Policy + +func generateInitialPolicyList() { + policyList = nil + policyList = append(policyList, Policy{ + PolicyModelType: "onap.policies.controlloop.MinMax", + Version: "1.0.0", + PolicyAcronym: "MinMax", + CreatedDate: "2020-04-30T09:03:30.362897Z", + UpdatedDate: "2020-04-30T09:03:30.362897Z", + UpdatedBy: "Not found", + CreatedBy: "Not found", + }) + policyList = append(policyList, Policy{ + PolicyModelType: "onap.policies.controlloop.Guard", + Version: "1.0.0", + PolicyAcronym: "Guard", + CreatedDate: "2020-04-30T09:03:30.362897Z", + UpdatedDate: "2020-04-30T09:03:30.362897Z", + UpdatedBy: "Not found", + CreatedBy: "Not found", + }) + policyList = append(policyList, Policy{ + PolicyModelType: "onap.policies.controlloop.guard.common.FrequencyLimiter", + Version: "1.0.0", + PolicyAcronym: "FrequencyLimiter", + CreatedDate: "2020-04-30T09:03:30.362897Z", + UpdatedDate: "2020-04-30T09:03:30.362897Z", + UpdatedBy: "Not found", + CreatedBy: "Not found", + }) +} + +func getTemplates(c echo.Context) error { + var templates []LoopTemplate + for _, t := range templateList { + //service must be distributed from sdc + if t.DcaeBlueprintId != "" { + templates = append(templates, t) + } + } + if len(templates) != 0 { + return c.JSON(http.StatusOK, templates) + } + return c.JSON(http.StatusNotFound, ClampError{ + Message: "No Templates found", + Error: "Not Found", + Status: "404"}) +} + +func getPolicies(c echo.Context) error { + if len(policyList) != 0 { + return c.JSON(http.StatusOK, policyList) + } + return c.JSON(http.StatusNotFound, ClampError{ + Message: "No Policies found", + Error: "Not Found", + Status: "404"}) +} diff --git a/mock-clamp/generic_handlers.go b/mock-clamp/generic_handlers.go new file mode 100644 index 0000000..a636ea1 --- /dev/null +++ b/mock-clamp/generic_handlers.go @@ -0,0 +1,32 @@ +// Copyright 2023 Deutsche Telekom AG, Orange +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "net/http" + + "github.com/labstack/echo" +) + +func index(c echo.Context) error { + return c.String(http.StatusOK, "Hello, World!") +} + +func reset(c echo.Context) error { + generateInitialTemplateList() + generateInitialPolicyList() + generateInitialLoopInstances() + return c.String(http.StatusCreated, "reset done!") +} diff --git a/mock-clamp/loop_handlers.go b/mock-clamp/loop_handlers.go new file mode 100644 index 0000000..ff8a552 --- /dev/null +++ b/mock-clamp/loop_handlers.go @@ -0,0 +1,404 @@ +// Copyright 2023 Deutsche Telekom AG, Orange +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "net/http" + "time" + + "github.com/labstack/echo" + uuid "github.com/satori/go.uuid" +) + +//describes the loop state in term of policy and DCAE +type State struct { + ComponentState struct { + StateName string `json:"stateName"` + } `json:"componentState"` +} + +//describes entity id in CLAMP got from SDC +type Resource struct { + ResourceID struct { + VfModuleModelName string `json:"vfModuleModelName"` + VfModuleModelInvariantUUID string `json:"vfModuleModelInvariantUUID"` + VfModuleModelUUID string `json:"vfModuleModelUUID"` + VfModuleModelVersion string `json:"vfModuleModelVersion"` + VfModuleModelCustomizationUUID string `json:"vfModuleModelCustomizationUUID"` + } `json:"resourceID"` +} + +//frequency limiter configuration +type Frequency_payload struct { + ID string `json:"id"` + Actor string `json:"actor"` + Operation string `json:"operation"` + Limit int `json:"limit"` + TimeWindow int `json:"timeWindow"` + TimeUnits string `json:"timeUnits"` +} + +/* +//describes operational policy configuration in CLAMP +type OperationalPolicy struct { + Name string `json:"name"` + PolicyModel Policy `json:"policyModel"` + ConfigurationsJson struct{ Frequency_payload } `json:"configurationsJson"` //depends on operational policy model +} +*/ +//describes TCA POLICY in CLAMP +type Tca_policy struct { + Domain string `json:"Domain"` + MetricsPerEventName []struct { + PolicyScope string `json:"policyScope"` + Thresholds []struct { + Version string `json:"version"` + Severity string `json:"severity"` + ThresholdValue int `json:"thresholdValue"` + ClosedLoopEventStatus string `json:"closedLoopEventStatus"` + ClosedLoopControlName string `json:"closedLoopControlName"` + Direction string `json:"direction"` + FieldPath string `json:"fieldPath"` + } `json:"thresholds"` + EventName string `json:"eventName"` + PolicyVersion string `json:"policyVersion"` + ControlLoopSchemaType string `json:"controlLoopSchemaType"` + PolicyName string `json:"policyName"` + } `json:"metricsPerEventName"` +} + +//describes TCA POLICY payload in CLAMP +type Tca_policy_struct struct { + TcaPolicy Tca_policy `json:"tca.policy"` +} + +//describes microservice policy configuration in CLAMP +type MicroServicePolicy struct { + Name string `json:"name"` + ConfigurationsJson Tca_policy_struct `json:"configurationsJson"` + PdpGroup string `json:"pdpGroup"` + PdpSubgroup string `json:"pdpSubgroup"` +} + +//LoopDetails describes loop innstance in CLAMP +type LoopDetails struct { + Name string `json:"name"` + Template LoopTemplate `json:"loopTemplate"` + GlobalPropertiesJSON struct { + DcaeDeployParameters struct { + UniqueBlueprintParameters struct { + PolicyID string `json:"policy_id"` + } `json:"uniqueBlueprintParameters"` + } `json:"dcaeDeployParameters"` + } `json:"globalPropertiesJson"` + LoopElementModelsUsed []string `json:"loopElementModelsUsed"` //microservices from sdc + Components struct { + POLICY State `json:"POLICY"` + DCAE State `json:"DCAE"` + } `json:"components"` + ModelService struct { + ResourceDetails struct { + VFModule Resource `json:"VFModule"` + } `json:"resourceDetails"` + } `json:"modelService"` + OperationalPolicies []OperationalPolicy_payload `json:"operationalPolicies"` + MicroServicePolicies []MicroServicePolicy `json:"microServicePolicies"` +} + +var loopInstanceList []LoopDetails + +func generateInitialLoopInstances() { + loopInstanceList = nil + loop1 := new(LoopDetails) + loop1.Template = templateList[0] + loop1.Name = "intance_template01" + loopInstanceList = append(loopInstanceList, *loop1) + loop2 := new(LoopDetails) + loop2.Name = "intance_template02" + loop2.Template = templateList[1] + loopInstanceList = append(loopInstanceList, *loop2) +} + +func updateLoopDetails(c echo.Context) error { + loopID := c.Param("loopID") + for _, loop := range loopInstanceList { + if loop.Name == loopID { + return c.JSON(http.StatusOK, loop) + } + } + return c.JSON(http.StatusNotFound, ClampError{ + Message: "No ClosedLoop found", + Error: "Not Found", + Status: "404"}) +} + +func createLoopInstance(c echo.Context) error { + loopID := c.Param("loopID") + templateName := c.QueryParam("templateName") + loop := new(LoopDetails) + + //must add the constraint of limit number of instances for a template + for _, l := range loopInstanceList { + if l.Name == loopID && + l.Template.Name == templateName { + //in reality it's overwritten + return c.JSON(http.StatusBadRequest, ClampError{ + Message: "loop of same Name and for same template exists", + Error: "Exists", + Status: "500"}) + } + } + loop.Name = loopID + loop.Template.Name = templateName + //For drools configuration + resource := new(Resource) + resource.ResourceID.VfModuleModelUUID = uuid.NewV4().String() + resource.ResourceID.VfModuleModelName = uuid.NewV4().String() + resource.ResourceID.VfModuleModelInvariantUUID = uuid.NewV4().String() + resource.ResourceID.VfModuleModelVersion = uuid.NewV4().String() + resource.ResourceID.VfModuleModelCustomizationUUID = uuid.NewV4().String() + loop.ModelService.ResourceDetails.VFModule = *resource + //must generate as much microservices as tca blueprints count + loop.LoopElementModelsUsed = append(loop.LoopElementModelsUsed, "microservice01") + nb_microservices := len(loop.LoopElementModelsUsed) + for i := 0; i < nb_microservices; i++ { + loop.MicroServicePolicies = append(loop.MicroServicePolicies, MicroServicePolicy{ + Name: "Microservice" + uuid.NewV4().String(), + }) + } + loop.GlobalPropertiesJSON.DcaeDeployParameters.UniqueBlueprintParameters.PolicyID = "Microservice" + uuid.NewV4().String() + state := new(State) + state.ComponentState.StateName = "NOT_SENT" + loop.Components.POLICY = *state + state.ComponentState.StateName = "BLUEPRINT_DEPLOYED" + loop.Components.DCAE = *state + + loopInstanceList = append(loopInstanceList, *loop) + return c.JSON(http.StatusCreated, *loop) +} + +func addOperationaPolicy(c echo.Context) error { + loopID := c.Param("loopID") + policyType := c.Param("policyType") + policyVersion := c.Param("policyVersion") + op_policy := new(struct { + PolicyModelType string `json:"policyModelType"` + Version string `json:"version"` + PolicyAcronym string `json:"policyAcronym"` + PolicyPdpGroup struct { + SupportedPdpGroups []struct { + DefaultGroup []string `json:"defaultGroup"` + } `json:"supportedPdpGroups"` + } `json:"policyPdpGroup"` + CreatedDate time.Time `json:"createdDate"` + UpdatedDate time.Time `json:"updatedDate"` + UpdatedBy string `json:"updatedBy"` + CreatedBy string `json:"createdBy"` + }) + for _, p := range policyList { + if p.PolicyModelType == policyType && + p.Version == policyVersion { + for j, l := range loopInstanceList { + if l.Name == loopID { + op_policy.PolicyAcronym = p.PolicyAcronym + loopInstanceList[j].OperationalPolicies = append(loopInstanceList[j].OperationalPolicies, + OperationalPolicy_payload{ + Name: "OPERATIONAL" + uuid.NewV4().String(), + PolicyModel: *op_policy, + }) + return c.JSON(http.StatusOK, loopInstanceList[j]) + } + } + return c.JSON(http.StatusBadRequest, ClampError{ + Message: "loop not found", + Error: "Not Found", + Status: "404"}) + } + } + return c.JSON(http.StatusBadRequest, ClampError{ + Message: "Policy not found", + Error: "Not Found", + Status: "404"}) +} + +//remove operation is not working in the real CLAMP +func removeOperationaPolicy(c echo.Context) error { + loopID := c.Param("loopID") + policyType := c.Param("policyType") + policyVersion := c.Param("policyVersion") + + for j, l := range loopInstanceList { + if l.Name == loopID { + for i, pp := range l.OperationalPolicies { + if pp.PolicyModel.PolicyModelType == policyType && + pp.PolicyModel.Version == policyVersion { + loopInstanceList[j].OperationalPolicies = append(loopInstanceList[j].OperationalPolicies[:i], + loopInstanceList[j].OperationalPolicies[i+1:]...) + return c.JSON(http.StatusOK, l) + } + } + return c.JSON(http.StatusBadRequest, ClampError{ + Message: "Policy not found", + Error: "Not Found", + Status: "404"}) + } + } + return c.JSON(http.StatusBadRequest, ClampError{ + Message: "loop not found", + Error: "Not Found", + Status: "404"}) +} + +//must review tca_policy struct +func addTcaConfig(c echo.Context) error { + loopID := c.Param("loopID") + data := new(MicroServicePolicy) + if err := c.Bind(data); err != nil { + return err + } + for j, l := range loopInstanceList { + if l.Name == loopID { + if l.MicroServicePolicies != nil { + for i, _ := range l.MicroServicePolicies { + loopInstanceList[j].MicroServicePolicies[i] = *data + } + return c.JSON(http.StatusOK, loopInstanceList[j].MicroServicePolicies[0]) + } + return c.JSON(http.StatusBadRequest, ClampError{ + Message: "Microservice policy not found", + Error: "Not Found", + Status: "404"}) + } + } + return c.JSON(http.StatusBadRequest, ClampError{ + Message: "Loop not found", + Error: "Not Found", + Status: "404"}) +} + +func addOperationalPolicyConfig(c echo.Context) error { + loopID := c.Param("loopID") + for j, l := range loopInstanceList { + if l.Name == loopID { + /* + //cannot bind a list as said in labstack echo bind + data := new([]OperationalPolicy_payload) + if err := c.Bind(data); err != nil { + return err + } + */ + if l.OperationalPolicies != nil { + //loopInstanceList[j].OperationalPolicies = *data + loopInstanceList[j].OperationalPolicies[len(l.OperationalPolicies)-1].ConfigurationsJSON.Actor = "Test" + return c.JSON(http.StatusOK, loopInstanceList[j]) + } + return c.JSON(http.StatusBadRequest, ClampError{ + Message: "Operational Policy not found", + Error: "Not Found", + Status: "404"}) + } + } + return c.JSON(http.StatusBadRequest, ClampError{ + Message: "Loop not found", + Error: "Not Found", + Status: "404"}) +} + +//util function +func checkPoliciesConfiguration(l LoopDetails) bool { + var empty struct { + Actor string `json:"actor"` + Operation string `json:"operation"` + Limit int `json:"limit"` + TimeWindow int `json:"timeWindow"` + TimeUnits string `json:"timeUnits"` + } + + for i, _ := range l.MicroServicePolicies { + if l.MicroServicePolicies[i].ConfigurationsJson.TcaPolicy.Domain == "" { + return false + } + } + for i, _ := range l.OperationalPolicies { + if l.OperationalPolicies[i].ConfigurationsJSON == empty { + return false + } + } + return true +} + +func putLoopAction(c echo.Context) error { + action := c.Param("action") + loopID := c.Param("loopID") + state := new(State) + for j, l := range loopInstanceList { + if l.Name == loopID { + //POLICY actions + if action == "submit" { + if checkPoliciesConfiguration(loopInstanceList[j]) { + state.ComponentState.StateName = "SENT_AND_DEPLOYED" + loopInstanceList[j].Components.POLICY = *state + return c.JSON(http.StatusOK, loopInstanceList[j].Components.POLICY.ComponentState) + } + return c.JSON(http.StatusBadRequest, ClampError{ + Message: "Policies are not well Configured", + Error: "Bad Action", + Status: "401"}) + } + if action == "stop" { + if l.Components.POLICY.ComponentState.StateName == "NOT_SENT" { + return c.JSON(http.StatusBadRequest, ClampError{ + Message: "Cannot perform this action", + Error: "Bad Action", + Status: "400"}) + } + state.ComponentState.StateName = "SENT" + loopInstanceList[j].Components.POLICY = *state + return c.JSON(http.StatusOK, "{}") + } + if action == "restart" { + state.ComponentState.StateName = "SENT_AND_DEPLOYED" + loopInstanceList[j].Components.POLICY = *state + return c.JSON(http.StatusOK, "{}") + } + //DCAE actions + if action == "deploy" { + //must add deploy failure + state.ComponentState.StateName = "MICROSERVICE_INSTALLED_SUCCESSFULLY" + loopInstanceList[j].Components.DCAE = *state + return c.JSON(http.StatusOK, "{}") + } + if action == "undeploy" { + state.ComponentState.StateName = "MICROSERVICE_UNINSTALLED_SUCCESSFULLY" + loopInstanceList[j].Components.DCAE = *state + return c.JSON(http.StatusOK, "{}") + } + //LOOP action + if action == "delete" { + loopInstanceList = append(loopInstanceList[:j], loopInstanceList[j+1:]...) + return c.JSON(http.StatusOK, "{}") + } + //action failure + return c.JSON(http.StatusBadRequest, ClampError{ + Message: "Cannot perform this action", + Error: "Bad Action", + Status: "400"}) + } + } + return c.JSON(http.StatusBadRequest, ClampError{ + Message: "Loop not found", + Error: "Not Found", + Status: "404"}) +} diff --git a/mock-clamp/mock-clamp.go b/mock-clamp/mock-clamp.go new file mode 100644 index 0000000..6e82d7f --- /dev/null +++ b/mock-clamp/mock-clamp.go @@ -0,0 +1,44 @@ +// Copyright 2023 Deutsche Telekom AG, Orange +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "github.com/labstack/echo" + "github.com/labstack/echo/middleware" + "github.com/labstack/gommon/log" +) + +func main() { + e := echo.New() + e.Use(middleware.Logger()) + e.Logger.SetLevel(log.DEBUG) + e.GET("/", index) + e.GET("/restservices/clds/v2/templates/", getTemplates) + e.GET("/restservices/clds/v2/policyToscaModels/", getPolicies) + + e.GET("/restservices/clds/v2/loop/:loopID", updateLoopDetails) + e.GET("/restservices/clds/v2/loop/getstatus/:loopID", updateLoopDetails) //refresh status + e.POST("/restservices/clds/v2/loop/create/:loopID", createLoopInstance) + e.PUT("/restservices/clds/v2/loop/addOperationaPolicy/:loopID/policyModel/:policyType/:policyVersion", addOperationaPolicy) + e.PUT("/restservices/clds/v2/loop/removeOperationaPolicy/:loopID/policyModel/:policyType/:policyVersion", removeOperationaPolicy) + e.POST("/restservices/clds/v2/loop/updateMicroservicePolicy/:loopID", addTcaConfig) //modify + e.POST("/restservices/clds/v2/loop/updateOperationalPolicies/:loopID", addOperationalPolicyConfig) //modify + e.PUT("/restservices/clds/v2/loop/:action/:loopID", putLoopAction) + e.POST("/reset", reset) + generateInitialTemplateList() + generateInitialPolicyList() + generateInitialLoopInstances() + e.Logger.Fatal(e.Start(":30258")) +} diff --git a/mock-clamp/policies_handlers.go b/mock-clamp/policies_handlers.go new file mode 100644 index 0000000..52cc04a --- /dev/null +++ b/mock-clamp/policies_handlers.go @@ -0,0 +1,98 @@ +// Copyright 2023 Deutsche Telekom AG, Orange +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import "time" + +//describes operational policy configuration in CLAMP +type OperationalPolicy_payload struct { + Name string `json:"name"` + JSONRepresentation struct { + Title string `json:"title"` + Type string `json:"type"` + Description string `json:"description"` + Required []string `json:"required"` + Properties struct { + ID struct { + Type string `json:"type"` + Description string `json:"description"` + } `json:"id"` + Actor struct { + Type string `json:"type"` + Description string `json:"description"` + } `json:"actor"` + Operation struct { + Type string `json:"type"` + Description string `json:"description"` + } `json:"operation"` + TimeRange struct { + Title string `json:"title"` + Type string `json:"type"` + Required []string `json:"required"` + Properties struct { + StartTime struct { + Type string `json:"type"` + Format string `json:"format"` + } `json:"start_time"` + EndTime struct { + Type string `json:"type"` + Format string `json:"format"` + } `json:"end_time"` + } `json:"properties"` + } `json:"timeRange"` + Limit struct { + Type string `json:"type"` + Description string `json:"description"` + ExclusiveMinimum string `json:"exclusiveMinimum"` + } `json:"limit"` + TimeWindow struct { + Type string `json:"type"` + Description string `json:"description"` + } `json:"timeWindow"` + TimeUnits struct { + Type string `json:"type"` + Description string `json:"description"` + Enum []string `json:"enum"` + } `json:"timeUnits"` + } `json:"properties"` + } `json:"jsonRepresentation"` + ConfigurationsJSON struct { + Actor string `json:"actor"` + Operation string `json:"operation"` + Limit int `json:"limit"` + TimeWindow int `json:"timeWindow"` + TimeUnits string `json:"timeUnits"` + } `json:"configurationsJson"` + PolicyModel struct { + PolicyModelType string `json:"policyModelType"` + Version string `json:"version"` + PolicyAcronym string `json:"policyAcronym"` + PolicyPdpGroup struct { + SupportedPdpGroups []struct { + DefaultGroup []string `json:"defaultGroup"` + } `json:"supportedPdpGroups"` + } `json:"policyPdpGroup"` + CreatedDate time.Time `json:"createdDate"` + UpdatedDate time.Time `json:"updatedDate"` + UpdatedBy string `json:"updatedBy"` + CreatedBy string `json:"createdBy"` + } `json:"policyModel"` + CreatedDate time.Time `json:"createdDate"` + UpdatedDate time.Time `json:"updatedDate"` + UpdatedBy string `json:"updatedBy"` + CreatedBy string `json:"createdBy"` + PdpGroup string `json:"pdpGroup"` + PdpSubgroup string `json:"pdpSubgroup"` +} diff --git a/mock-dmaap/Dockerfile b/mock-dmaap/Dockerfile new file mode 100644 index 0000000..d17e075 --- /dev/null +++ b/mock-dmaap/Dockerfile @@ -0,0 +1,7 @@ +FROM python:3 +COPY . /app +WORKDIR /app +COPY ./requirements.txt ./ +RUN pip install -r ./requirements.txt +ENV FLASK_APP=app/app.py +CMD ["python", "app/app.py"]
\ No newline at end of file diff --git a/mock-dmaap/Makefile b/mock-dmaap/Makefile new file mode 100644 index 0000000..af8f162 --- /dev/null +++ b/mock-dmaap/Makefile @@ -0,0 +1,23 @@ +all: build + +.PHONY: build + +build: + @echo "##### Build dmaap simulator image #####" + docker build . -t dmaap-simulator + @echo "##### DONE #####" + +start: + @echo "##### Start dmaap simulator #####" + docker run -d -p 3904:3904 --name dmaap-simulator dmaap-simulator + @echo "##### DONE #####" + +stop: + @echo "##### Stop dmaap simulator #####" + docker rm -f dmaap-simulator + @echo "##### DONE #####" + +get-data: + @echo "##### Get data fetched by dmaap-simulator #####\n" + curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://localhost:3904/events + @echo "\n\n##### DONE #####" diff --git a/mock-dmaap/README.md b/mock-dmaap/README.md new file mode 100644 index 0000000..6759e79 --- /dev/null +++ b/mock-dmaap/README.md @@ -0,0 +1,23 @@ +DMaaP Mock +--------------- + + +### Build an image +``` +make build +``` + +### Start +``` +make start +``` + +### Stop +``` +make stop +``` + +### Get fetched events +``` +make get-data +``` diff --git a/mock-dmaap/app/app.py b/mock-dmaap/app/app.py new file mode 100644 index 0000000..ffaaa63 --- /dev/null +++ b/mock-dmaap/app/app.py @@ -0,0 +1,98 @@ +""" + Copyright 2023 Deutsche Telekom AG, Nokia, Orange + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +import json +import logging as sys_logging +from event_storage import EventStorage +from sdc import Sdc +from ves import Ves + +from flask import Flask, request, logging, Response + +app = Flask(__name__) + +sys_logging.basicConfig(level=sys_logging.DEBUG) +logger = logging.create_logger(app) +apiKey = {"key": "test_key", "secret": "test_secret"} +event_storage = EventStorage() +sdc = Sdc(logger, event_storage) +ves = Ves(logger, event_storage) + + +@app.route("/apiKeys/create", methods=['POST']) +def create_api_key(): + resp = Response(json.dumps(apiKey)) + return resp + + +@app.route("/apiKeys/<path:key>", methods=['DELETE']) +def delete_api_key(key): + if key == apiKey["key"]: + return {}, 200 + return {"no such key"}, 404 + + +@app.route("/reset", methods=['GET']) +def reset_events(): + event_storage.clear() + return event_storage + + +@app.route("/events", methods=['GET']) +def get_events(): + resp = Response(json.dumps(event_storage)) + resp.headers['Content-Type'] = 'application/json' + return resp + + +@app.route("/events/<path:topic>/<path:consumer_group>/<path:consumer_id>", methods=['GET']) +def get_events_from_topic(topic, consumer_group, consumer_id): + events = event_storage.get_events_from_topic(topic) + resp = Response(json.dumps(events)) + event_storage.delete_events(topic) + resp.headers['Content-Type'] = 'application/json' + return resp + + +@app.route("/events/<path:topic>", methods=['POST']) +def post_msg_to_topic(topic): + receive_msg = request.data.decode("utf-8") + if sdc.is_event_from_sdc(receive_msg): + return sdc.post_msg_to_topic(topic, receive_msg) + else: + return ves.handle_new_event(topic, receive_msg) + + +@app.route("/events/<path:topic>/add", methods=['POST']) +def add_msg_to_topic(topic): + receive_msg = request.data.decode("utf-8") + return sdc.add_msg_to_topic(topic, receive_msg) + + +@app.route("/topics", methods=['GET']) +def get_topics(): + topics = { + 'topics': ['org.onap.dmaap.mr.PNF_REGISTRATION', 'SDC-DISTR-STATUS-TOPIC-AUTO', 'SDC-DISTR-NOTIF-TOPIC-AUTO', + 'org.onap.dmaap.mr.PNF_READY', 'POLICY-PDP-PAP', 'POLICY-NOTIFICATION', + 'unauthenticated.SEC_3GPP_FAULTSUPERVISION_OUTPUT', '__consumer_offsets', + 'org.onap.dmaap.mr.mirrormakeragent']} + resp = Response(json.dumps(topics)) + resp.headers['Content-Type'] = 'application/json' + return resp + + +if __name__ == "__main__": + app.run(host='0.0.0.0', port=3904) diff --git a/mock-dmaap/app/event_storage.py b/mock-dmaap/app/event_storage.py new file mode 100644 index 0000000..a520166 --- /dev/null +++ b/mock-dmaap/app/event_storage.py @@ -0,0 +1,40 @@ +""" + Copyright 2023 Deutsche Telekom AG, Nokia, Orange + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +class EventStorage: + __events = {} + + def get_events(self): + return self.__events + + def get_events_from_topic(self, topic): + if self.__events.__contains__(topic): + return self.__events[topic] + else: + return [] + + def add(self, topic, event): + if self.__events.__contains__(topic): + self.__events[topic].append(event) + else: + self.__events[topic] = [event] + + def delete_events(self, topic): + if self.__events.__contains__(topic): + self.__events[topic].clear() + + def clear(self): + self.__events.clear() diff --git a/mock-dmaap/app/sdc.py b/mock-dmaap/app/sdc.py new file mode 100644 index 0000000..5317ad2 --- /dev/null +++ b/mock-dmaap/app/sdc.py @@ -0,0 +1,38 @@ +""" + Copyright 2023 Deutsche Telekom AG, Nokia, Orange + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +class Sdc: + __jsonArtifact = {} + + def __init__(self, logger, events): + self.__logger = logger + self.__events = events + + def post_msg_to_topic(self, topic, receive_msg): + self.__logger.info("received events: " + str(receive_msg)) + return self.__jsonArtifact + + def add_msg_to_topic(self, topic, receive_msg): + self.__jsonArtifact = receive_msg + self.__events.add(topic, receive_msg) + self.__logger.info("received events: " + str(receive_msg)) + return receive_msg + + def is_event_from_sdc(self, event): + if event[:3] == '14.': + return True + else: + return False diff --git a/mock-dmaap/app/ves.py b/mock-dmaap/app/ves.py new file mode 100644 index 0000000..6c25730 --- /dev/null +++ b/mock-dmaap/app/ves.py @@ -0,0 +1,46 @@ +""" + Copyright 2023 Deutsche Telekom AG, Nokia, Orange + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +import json + + +class Ves: + + def __init__(self, logger, events): + self.__logger = logger + self.__events = events + + def handle_new_event(self, topic, http_request): + receive_events = self.__decode_request_data(http_request) + for event in receive_events: + self.__events.add(topic, json.loads(event)) + return {}, 200 + + def __decode_request_data(self, data): + receive_events = data.split("\n") + receive_events = receive_events[:-1] + self.__logger.info("received events: " + str(receive_events)) + correct_events = [] + for event in receive_events: + self.__logger.info("received event: " + str(event)) + correct_events.append(self.__get_correct_json(event)) + return correct_events + + def __get_correct_json(self, incorrect_json): + json_start_position = incorrect_json.find("{") + correct_json = incorrect_json[json_start_position:] + correct_json = correct_json.replace("\r", "").replace("\t", "").replace(" ", "") + return correct_json diff --git a/mock-dmaap/requirements.txt b/mock-dmaap/requirements.txt new file mode 100644 index 0000000..a6d2d3a --- /dev/null +++ b/mock-dmaap/requirements.txt @@ -0,0 +1,6 @@ +Click==7.0 +Flask==1.1.1 +itsdangerous==1.1.0 +Jinja2==2.10.3 +MarkupSafe==1.1.1 +Werkzeug==0.16.0 diff --git a/mock-msb-k8s/Dockerfile b/mock-msb-k8s/Dockerfile new file mode 100644 index 0000000..65efac9 --- /dev/null +++ b/mock-msb-k8s/Dockerfile @@ -0,0 +1,11 @@ +FROM python:3.8-alpine + +COPY . . + +WORKDIR /app + +RUN pip3 install -r ../requirements.txt + +EXPOSE 5003 + +CMD ["python","app.py"]
\ No newline at end of file diff --git a/mock-msb-k8s/README.md b/mock-msb-k8s/README.md new file mode 100644 index 0000000..f446fa0 --- /dev/null +++ b/mock-msb-k8s/README.md @@ -0,0 +1,2 @@ +# mock-msb-k8s + diff --git a/mock-msb-k8s/app/app.py b/mock-msb-k8s/app/app.py new file mode 100644 index 0000000..5da64d7 --- /dev/null +++ b/mock-msb-k8s/app/app.py @@ -0,0 +1,284 @@ +"""MSB k8s mock application""" +""" + Copyright 2023 Deutsche Telekom AG, Orange + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" + +import http +import json +import uuid + +from flask import Flask, request +from flask_restful import Api + +app = Flask(__name__) +api = Api(app) + +CONNECTIVITY_INFOS = [] +DEFINITIONS = [] +CONFIGURATIONS_TEMPLATES = [] +PROFILES = [] +INSTANCES = [] +INSTANCE_EXAMPLE = { + "id": "ID_GENERATED_BY_K8SPLUGIN", + "namespace": "NAMESPACE_WHERE_INSTANCE_HAS_BEEN_DEPLOYED_AS_DERIVED_FROM_PROFILE", + "release-name": "RELEASE_NAME_AS_COMPUTED_BASED_ON_INSTANTIATION_REQUEST_AND_PROFILE_DEFAULT", + "request": { + "rb-name": "test-rbdef", + "rb-version": "v1", + "profile-name": "p1", + "cloud-region": "krd", + "override-values": { + "optionalDictOfParameters": "andTheirValues, like", + "global.name": "dummy-name" + }, + "labels": { + "optionalLabelForInternalK8spluginInstancesMetadata": "dummy-value" + }, + }, + "resources": [ + { + "GVK": { + "Group": "", + "Kind": "ConfigMap", + "Version": "v1" + }, + "Name": "test-cm" + }, + { + "GVK": { + "Group": "", + "Kind": "Service", + "Version": "v1" + }, + "Name": "test-svc" + }, + { + "GVK": { + "Group": "apps", + "Kind": "Deployment", + "Version": "v1" + }, + "Name": "test-dep" + } + ] +} + + +@app.route('/api/multicloud-k8s/v1/v1/connectivity-info/<string:region_id>', methods=['GET', 'DELETE']) +def connectivity_info_get_delete(region_id): + if request.method == 'GET': + for conninfo in CONNECTIVITY_INFOS: + if conninfo["cloud-region"] == region_id: + return conninfo, http.HTTPStatus.OK + else: + return '', http.HTTPStatus.NOT_FOUND + if request.method == 'DELETE': + for conninfo in CONNECTIVITY_INFOS: + if conninfo["cloud-region"] == region_id: + CONNECTIVITY_INFOS.remove(conninfo) + return '', http.HTTPStatus.OK + else: + return '', http.HTTPStatus.NOT_FOUND + return '', http.HTTPStatus.METHOD_NOT_ALLOWED + + +@app.route('/api/multicloud-k8s/v1/v1/connectivity-info', methods=['POST']) +def connectivity_info_create(): + if request.method == 'POST': + kubeconfig = request.files['file'] + metadata = json.loads(request.values['metadata']) + CONNECTIVITY_INFOS.append({ + "cloud-region": metadata['cloud-region'], + "cloud-owner": metadata['cloud-region'], + "kubeconfig": kubeconfig.read().decode("utf-8") + }) + return '', http.HTTPStatus.OK + return '', http.HTTPStatus.METHOD_NOT_ALLOWED + + +@app.route('/api/multicloud-k8s/v1/v1/rb/definition', methods=['POST']) +def definition_create(): + if request.method == 'POST': + data = json.loads(request.data) + DEFINITIONS.append({ + "rb-name": data['rb-name'], + "rb-version": data['rb-version'], + "chart-name": data['chart-name'], + "description": data['description'], + "labels": data['labels'] + }) + return '', http.HTTPStatus.OK + return '', http.HTTPStatus.METHOD_NOT_ALLOWED + + +@app.route('/api/multicloud-k8s/v1/v1/rb/definition/<string:rb_name>/<string:rb_version>/content', methods=['POST']) +def definition_upload_artifact(rb_name, rb_version): + if request.method == 'POST': + data = request.data + return '', http.HTTPStatus.OK + return '', http.HTTPStatus.METHOD_NOT_ALLOWED + + +@app.route('/api/multicloud-k8s/v1/v1/rb/definition/<string:rb_name>/<string:rb_version>', methods=['GET', 'DELETE']) +def definition_get_delete(rb_name, rb_version): + if request.method == 'GET': + for rb in DEFINITIONS: + if rb['rb-name'] == rb_name and rb['rb-version'] == rb_version: + return rb, http.HTTPStatus.OK + else: + return '', http.HTTPStatus.NOT_FOUND + if request.method == 'DELETE': + for rb in DEFINITIONS: + if rb['rb-name'] == rb_name and rb['rb-version'] == rb_version: + DEFINITIONS.remove(rb) + return '', http.HTTPStatus.OK + else: + return '', http.HTTPStatus.NOT_FOUND + return '', http.HTTPStatus.METHOD_NOT_ALLOWED + + +@app.route('/api/multicloud-k8s/v1/v1/rb/definition', methods=['GET']) +def definition_get_all(): + if request.method == 'GET': + return json.dumps(DEFINITIONS), http.HTTPStatus.OK + return '', http.HTTPStatus.METHOD_NOT_ALLOWED + + +@app.route('/api/multicloud-k8s/v1/v1/rb/definition/<string:rb_name>/<string:rb_version>/profile', methods=['POST']) +def profile_create(**kwargs): + if request.method == 'POST': + data = json.loads(request.data) + PROFILES.append({ + "rb-name": data['rb-name'], + "rb-version": data['rb-version'], + "profile-name": data['profile-name'], + "release-name": data['release-name'], + "namespace": data['namespace'], + "kubernetes-version": data['kubernetes-version'] + }) + return '', http.HTTPStatus.OK + return '', http.HTTPStatus.METHOD_NOT_ALLOWED + + +@app.route( + '/api/multicloud-k8s/v1/v1/rb/definition/<string:rb_name>/<string:rb_version>/profile/<string:profile_name>/content' + , methods=['POST']) +def profile_upload_artifact(rb_name, rb_version, profile_name): + if request.method == 'POST': + data = request.data + return '', http.HTTPStatus.OK + return '', http.HTTPStatus.METHOD_NOT_ALLOWED + + +@app.route('/api/multicloud-k8s/v1/v1/rb/definition/<string:rb_name>/<string:rb_version>/profile/<string:profile_name>', + methods=['GET', 'DELETE']) +def profile_get_delete(rb_name, rb_version, profile_name): + if request.method == 'GET': + for profile in PROFILES: + if profile['rb-name'] == rb_name and profile['rb-version'] == rb_version \ + and profile["profile-name"] == profile_name: + return profile, http.HTTPStatus.OK + else: + return '', http.HTTPStatus.NOT_FOUND + if request.method == 'DELETE': + for profile in PROFILES: + if profile['rb-name'] == rb_name and profile['rb-version'] == rb_version \ + and profile["profile-name"] == profile_name: + PROFILES.remove(profile) + return '', http.HTTPStatus.OK + else: + return '', http.HTTPStatus.NOT_FOUND + return '', http.HTTPStatus.METHOD_NOT_ALLOWED + + +@app.route('/api/multicloud-k8s/v1/v1/rb/definition/<string:rb_name>/<string:rb_version>/profile', methods=['GET']) +def profile_get_all(rb_name, rb_version): + if request.method == 'GET': + profiles = [] + for profile in PROFILES: + if profile['rb-name'] == rb_name and profile['rb-version'] == rb_version: + profiles.append(profile) + return json.dumps(PROFILES), http.HTTPStatus.OK + return '', http.HTTPStatus.METHOD_NOT_ALLOWED + +@app.route('/api/multicloud-k8s/v1/v1/instance', methods=['POST']) +def instance_create(): + if request.method == 'POST': + data = json.loads(request.data) + instance_details = INSTANCE_EXAMPLE + instance_details["id"] = str(uuid.uuid4()) + instance_details["request"] = data + INSTANCES.append(instance_details) + return instance_details, http.HTTPStatus.OK + return '', http.HTTPStatus.METHOD_NOT_ALLOWED + +@app.route('/api/multicloud-k8s/v1/v1/instance/<string:instance_id>', methods=['GET', 'DELETE']) +def instance_get_delete(instance_id): + if request.method == 'GET': + for instance in INSTANCES: + if instance['id'] == instance_id: + return instance, http.HTTPStatus.OK + else: + return '', http.HTTPStatus.NOT_FOUND + if request.method == 'DELETE': + for instance in INSTANCES: + if instance['id'] == instance_id: + INSTANCES.remove(instance) + return '', http.HTTPStatus.OK + else: + return '', http.HTTPStatus.NOT_FOUND + return '', http.HTTPStatus.METHOD_NOT_ALLOWED + + +@app.route('/api/multicloud-k8s/v1/v1/instance', methods=['GET']) +def instance_get_all(): + if request.method == 'GET': + return json.dumps(INSTANCES), http.HTTPStatus.OK + return '', http.HTTPStatus.METHOD_NOT_ALLOWED + + +@app.route('/api/multicloud-k8s/v1/v1/rb/definition/<string:rb_name>/<string:rb_version>/config-template', + methods=["POST"]) +def configuration_template_create(rb_name, rb_version): + if request.method == "POST": + data = json.loads(request.data) + configuration_template = data + CONFIGURATIONS_TEMPLATES.append(configuration_template) + return '', http.HTTPStatus.OK + return '', http.HTTPStatus.METHOD_NOT_ALLOWED + + +@app.route('/api/multicloud-k8s/v1/v1/rb/definition/<string:rb_name>/<string:rb_version>/config-template/<string:name>', + methods=["GET"]) +def configuration_template_get(rb_name, rb_version, name): + if request.method == "GET": + for template in CONFIGURATIONS_TEMPLATES: + if template['template-name'] == name: + return json.dumps(template), http.HTTPStatus.OK + else: + return '', http.HTTPStatus.NOT_FOUND + return '', http.HTTPStatus.METHOD_NOT_ALLOWED + + +@app.route('/api/multicloud-k8s/v1/v1/rb/definition/<string:rb_name>/<string:rb_version>/config-template', + methods=["GET"]) +def configuration_template_get_all(rb_name, rb_version): + if request.method == 'GET': + return json.dumps(CONFIGURATIONS_TEMPLATES), http.HTTPStatus.OK + return '', http.HTTPStatus.METHOD_NOT_ALLOWED + + +if __name__ == "__main__": + app.run(debug=True, host='0.0.0.0', port=5003) diff --git a/mock-msb-k8s/requirements.txt b/mock-msb-k8s/requirements.txt new file mode 100644 index 0000000..07e3f1f --- /dev/null +++ b/mock-msb-k8s/requirements.txt @@ -0,0 +1,2 @@ +Flask==1.1.2 +Flask-RESTful==0.3.8 diff --git a/mock-sdc/.dockerignore b/mock-sdc/.dockerignore new file mode 100644 index 0000000..53b2e85 --- /dev/null +++ b/mock-sdc/.dockerignore @@ -0,0 +1,16 @@ +# Binaries for programs and plugins +mock-sdc +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ diff --git a/mock-sdc/Dockerfile b/mock-sdc/Dockerfile new file mode 100644 index 0000000..524c178 --- /dev/null +++ b/mock-sdc/Dockerfile @@ -0,0 +1,33 @@ +FROM golang:alpine AS builder + +# Add all the source code (except what's ignored +# under `.dockerignore`) to the build context. +ADD ./ /go/src/ + +WORKDIR /go/src + +RUN apk add --no-cache git +RUN go env -w GO111MODULE=auto +RUN go get github.com/satori/go.uuid +RUN go get github.com/labstack/echo +RUN go get github.com/golang-jwt/jwt +RUN go get golang.org/x/time/rate + +RUN set -ex && \ + CGO_ENABLED=0 GOOS=linux go build \ + -tags netgo \ + -installsuffix cgo \ + -v -a \ + -ldflags '-extldflags "-static"' \ + -o mock-sdc . + +RUN ls -la + +FROM scratch + +# Retrieve the binary from the previous stage +COPY --from=builder /go/src/mock-sdc /app/mock-sdc +WORKDIR /app + +# Set the binary as the entrypoint of the container +ENTRYPOINT [ "./mock-sdc" ] diff --git a/mock-sdc/README.md b/mock-sdc/README.md new file mode 100644 index 0000000..ae7ae66 --- /dev/null +++ b/mock-sdc/README.md @@ -0,0 +1,2 @@ +# mock-sdc + diff --git a/mock-sdc/generic_handlers.go b/mock-sdc/generic_handlers.go new file mode 100644 index 0000000..a24cb45 --- /dev/null +++ b/mock-sdc/generic_handlers.go @@ -0,0 +1,32 @@ +// Copyright 2023 Deutsche Telekom AG, Orange +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "net/http" + + "github.com/labstack/echo" +) + +func index(c echo.Context) error { + return c.String(http.StatusOK, "Hello, World!") +} + +func reset(c echo.Context) error { + generateInitialVendorList() + generateInitialVspList() + generateInitialResourceList() + return c.String(http.StatusCreated, "reset done!") +} diff --git a/mock-sdc/mock-sdc.go b/mock-sdc/mock-sdc.go new file mode 100644 index 0000000..0b53afe --- /dev/null +++ b/mock-sdc/mock-sdc.go @@ -0,0 +1,71 @@ +// Copyright 2023 Deutsche Telekom AG, Orange +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "github.com/labstack/echo" + "github.com/labstack/echo/middleware" + "github.com/labstack/gommon/log" +) + +func main() { + e := echo.New() + e.Use(middleware.Logger()) + e.Logger.SetLevel(log.DEBUG) + e.GET("/", index) + e.GET("/sdc1/feProxy/onboarding-api/v1.0/items/:itemID/versions", getItemVersions) + e.GET("/sdc1/feProxy/onboarding-api/v1.0/items/:itemID/versions/:versionID", getItemVersion) + e.PUT("/sdc1/feProxy/onboarding-api/v1.0/items/:itemID/versions/:versionID/actions", updateItemVersion) + e.GET("/sdc1/feProxy/onboarding-api/v1.0/vendor-license-models", getVendorServiceModels) + e.POST("/sdc1/feProxy/onboarding-api/v1.0/vendor-license-models", postVendorServiceModels) + e.PUT("/sdc1/feProxy/onboarding-api/v1.0/vendor-license-models/:vendorID/versions/:versionID/actions", updateVendorVersion) + e.GET("/sdc1/feProxy/onboarding-api/v1.0/vendor-software-products", getVendorSoftwareProducts) + e.POST("/sdc1/feProxy/onboarding-api/v1.0/vendor-software-products", postVendorSoftwareProducts) + e.GET("/sdc1/feProxy/onboarding-api/v1.0/vendor-software-products/:vspID/versions/:versionID", getVspVersion) + e.PUT("/sdc1/feProxy/onboarding-api/v1.0/vendor-software-products/:vspID/versions/:versionID/actions", updateVspVersion) + e.POST("/sdc1/feProxy/onboarding-api/v1.0/vendor-software-products/:vspID/versions/:versionID/orchestration-template-candidate", uploadArtifacts) + e.PUT("/sdc1/feProxy/onboarding-api/v1.0/vendor-software-products/:vspID/versions/:versionID/orchestration-template-candidate/process", validateArtifacts) + e.GET("/sdc1/feProxy/rest/v1/followed", getAllResources) + e.GET("/sdc1/feProxy/rest/v1/screen", getAllResources) + e.GET("/sdc/v1/catalog/resources", getResources) + e.GET("/sdc/v1/catalog/services", getServices) + e.GET("/sdc/v1/artifactTypes", getArtifactTypes) + e.GET("/sdc/v1/distributionKafkaData", distributionKafkaData) + e.POST("/sdc/v1/registerForDistribution", registerForDistribution) + e.POST("/sdc/v1/unRegisterForDistribution", unRegisterForDistribution) + e.POST("/sdc1/feProxy/rest/v1/catalog/resources", postResources) + e.POST("/sdc1/feProxy/rest/v1/catalog/resources/:resourceID/lifecycleState/:action", postResourceAction) + e.POST("/sdc1/feProxy/rest/v1/catalog/services", postResources) + e.POST("/sdc1/feProxy/rest/v1/catalog/services/:resourceID/resourceInstance", postAddResourceToService) + e.POST("/sdc1/feProxy/rest/v1/catalog/services/:resourceID/lifecycleState/:action", postResourceAction) + e.POST("/sdc1/feProxy/rest/v1/catalog/services/:resourceID/distribution-state/:action", postResourceAction) + e.POST("/sdc1/feProxy/rest/v1/catalog/services/:resourceID/distribution/PROD/:action", postResourceAction) + e.GET("/sdc1/feProxy/rest/v1/catalog/services/:resourceID/distribution", getDistribution) + e.GET("/sdc1/feProxy/rest/v1/catalog/services/distribution/:distributionID", getDistributionList) + e.GET("/sdc1/feProxy/rest/v1/catalog/services/:resourceID", getServiceUniqueIdentifier) + e.POST("/sdc1/feProxy/rest/v1/catalog/services/:resourceID/resourceInstance/:vfID/artifacts", uploadTcaArtifact) + e.POST("/sdc1/feProxy/rest/v1/catalog/services/:resourceID/properties", postResourceProperties) + e.POST("/sdc1/feProxy/rest/v1/catalog/services/:resourceID/create/inputs", postResourceInputs) + e.POST("/sdc1/feProxy/rest/v1/catalog/resources/:resourceID/create/inputs", postResourceInputs) + e.GET("/sdc1/feProxy/rest/v1/catalog/resources/:resourceID/filteredDataByParams", getResourcefilteredData) + e.GET("/sdc1/feProxy/rest/v1/catalog/services/:resourceID/filteredDataByParams", getResourcefilteredData) + e.GET("/sdc1/feProxy/rest/v1/setup/ui", getCategories) + e.POST("/reset", reset) + generateInitialVendorList() + generateInitialVspList() + generateInitialResourceList() + generateDistributionStatusList() + e.Logger.Fatal(e.Start(":30206")) +} diff --git a/mock-sdc/resource_handlers.go b/mock-sdc/resource_handlers.go new file mode 100644 index 0000000..53aa98f --- /dev/null +++ b/mock-sdc/resource_handlers.go @@ -0,0 +1,1379 @@ +// Copyright 2023 Deutsche Telekom AG, Orange +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "container/list" + "encoding/json" + "net/http" + "errors" + "io/ioutil" + + "github.com/labstack/echo" + uuid "github.com/satori/go.uuid" +) + +// ResourceLight describes license model in SDC +type ResourceLight struct { + ID string `json:"uuid"` + InvariantID string `json:"invariantUUID"` + ResourceType string `json:"resourceType"` + Name string `json:"name"` + Category string `json:"category"` + SubCategory string `json:"subCategory"` + LastUpdaterUserID string `json:"lastUpdaterUserId"` + LifecycleState string `json:"lifecycleState"` + Version string `json:"version"` + ToscaModelURL string `json:"toscaModelURL"` + DistributionStatus string `json:"distributionStatus"` +} + +// SubCategory describes SubCategory model in SDC +type SubCategory struct { + Name string `json:"name"` + NormalizedName string `json:"normalizedName"` + UniqueID string `json:"uniqueId"` + Icons []string `json:"icons"` + Groupings string `json:"groupings"` + OwnerID string `json:"ownerId"` + Empty bool `json:"empty"` + Version string `json:"version"` + Type string `json:"type"` +} + +// Category describes Category model in SDC +type Category struct { + Name string `json:"name"` + NormalizedName string `json:"normalizedName"` + UniqueID string `json:"uniqueId"` + Icons []string `json:"icons"` + Subcategories []SubCategory `json:"subcategories"` + OwnerID string `json:"ownerId"` + Empty bool `json:"empty"` + Type string `json:"type"` + Version string `json:"version"` +} + +//ArtifactAdd Describes ressource component Instances artifacts in SDC +type ArtifactAdd struct { + ArtifactName string `json:"artifactName"` + ArtifactLabel string `json:"artifactLabel"` + ArtifactType string `json:"artifactType"` + Description string `json:"description"` +} + +//ComponentInstance Describes ressource component Instances in SDC +type ComponentInstance struct { + UniqueID string `json:"uniqueId"` + Name string `json:"name"` + ComponentName string `json:"componentName"` + OriginType string `json:"originType"` + ComponentVersion string `json:"componentVersion"` + DeploymentArtifacts []ArtifactAdd `json:"deploymentArtifacts"` +} + +// Resource describes Resource model in SDC +type Resource struct { + ID string `json:"uuid"` + InvariantID string `json:"invariantUUID"` + UniqueID string `json:"uniqueId"` + ResourceType string `json:"resourceType"` + Name string `json:"name"` + Category string `json:"category"` + SubCategory string `json:"subCategory"` + LastUpdaterUserID string `json:"lastUpdaterUserId"` + LifecycleState string `json:"lifecycleState"` + Version string `json:"version"` + ToscaModelURL string `json:"toscaModelURL"` + Artifacts struct{} `json:"artifacts"` + Attributes []string `json:"attributes"` + Capabilities struct{} `json:"capabilities"` + Categories []Category `json:"categories"` + ComponentInstances []ComponentInstance `json:"componentInstances"` + ComponentInstancesAttributes struct{} `json:"componentInstancesAttributes"` + ComponentInstancesProperties struct{} `json:"componentInstancesProperties"` + ComponentType string `json:"componentType"` + ContactID string `json:"contactId"` + CsarUUID string `json:"csarUUID"` + CsarVersion string `json:"csarVersion"` + DeploymentArtifacts struct{} `json:"deploymentArtifacts"` + Description string `json:"description"` + Icon string `json:"icon"` + Properties []Property `json:"properties"` + Requirements struct{} `json:"requirements"` + Tags []string `json:"tags"` + ToscaArtifacts struct{} `json:"toscaArtifacts"` + VendorName string `json:"vendorName"` + VendorRelease string `json:"vendorRelease"` + DistributionStatus string `json:"distributionStatus"` + DistributionID string `json:"distributionID"` + Inputs []Input +} + +// ResourceList is the way to return Resources in SDC via DeepLoad +type ResourceList struct { + Resources []Resource `json:"resources"` + Services []Resource `json:"services"` +} + +// ActionBody yolo +type ActionBody struct { + UserRemarks string `json:"userRemarks"` +} + +// ResourceAdd to a Service +type ResourceAdd struct { + Name string `json:"name"` + ComponentVersion string `json:"componentVersion"` + PosY int `json:"posY"` + PosX int `json:"posX"` + UniqueID string `json:"uniqueId"` + OriginType string `json:"originType"` + ComponentUID string `json:"componentUid"` + Icon string `json:"icon"` +} + +// DistributionIDResult format +type DistributionIDResult struct { + DistributionID string `json:"distributionID"` + UserID string `json:"userId"` + DeployementStatus string `json:"deployementStatus"` +} + +// DistributionIDList format +type DistributionIDList struct { + DistributionStatusOfServiceList []DistributionIDResult `json:"distributionStatusOfServiceList"` +} + +//DistributionStatus format +type DistributionStatus struct { + OmfComponentID string `json:"omfComponentID"` + Timestamp string `json:"timestamp"` + URL string `json:"url"` + Status string `json:"status"` + ErrorReason string `json:"errorReason"` +} + +// DistributionStatusList format +type DistributionStatusList struct { + DistributionStatusList []DistributionStatus `json:"distributionStatusList"` +} + +// NewUploadResult format +type NewUploadResult struct { + Description string `json:"description"` + ArtifactType string `json:"artifactType"` + ArtifactName string `json:"artifactName"` +} + +//Property format +type Property struct { + Name string `json:"name"` + Value string `json:"value"` + Type string `json:"type"` + UniqueID string `json:"uniqueId"` + ParentUniqueID string `json:"parentUniqueId"` +} + +//Input format +type Input struct { + Name string `json:"name"` + Value string `json:"value"` + Type string `json:"type"` + UniqueID string `json:"uniqueId"` +} + +var resourceList []Resource +var distributionList []DistributionStatus + +func generateInitialResourceList() { + resourceList = nil + resourceList = append(resourceList, Resource{ + ID: "6c4952d2-0ecc-4697-a039-d9766565feae", + InvariantID: "803cbaf5-deea-4022-a731-709d285435d6", + UniqueID: "1e6e90ec-632a-492f-9511-f2787a2befaf", + ResourceType: "Configuration", + Name: "VLAN Network Receptor Configuration", + Category: "Configuration", + SubCategory: "Configuration", + LastUpdaterUserID: "jh0003", + LifecycleState: "CERTIFIED", + Version: "1.0", + ToscaModelURL: "/sdc/v1/catalog/resources/6c4952d2-0ecc-4697-a039-d9766565feae/toscaModel", + }) + resourceList = append(resourceList, Resource{ + ID: "85a9a912-b0ca-4cc9-9dc4-a480546ef93b", + InvariantID: "2df7615c-38f5-45e2-ac40-f9a8f97baec2", + UniqueID: "1e6e90ec-632a-492f-9511-f2787a2befaf", + ResourceType: "CP", + Name: "contrailV2VLANSubInterfaceV2", + Category: "Generic", + SubCategory: "Network Elements", + LastUpdaterUserID: "jh0003", + LifecycleState: "CERTIFIED", + Version: "1.0", + ToscaModelURL: "/sdc/v1/catalog/resources/85a9a912-b0ca-4cc9-9dc4-a480546ef93b/toscaModel", + }) + resourceList = append(resourceList, Resource{ + ID: "7c6b6644-590d-4e60-84d7-0dfba3ad4694", + InvariantID: "1e6e90ec-632a-492f-9511-f2787a2bef9f", + UniqueID: "1e6e90ec-632a-492f-9511-f2787a2befaf", + ResourceType: "VFC", + Name: "VDU Compute", + Category: "Generic", + SubCategory: "Infrastructure", + LastUpdaterUserID: "jh0003", + LifecycleState: "CERTIFIED", + Version: "1.0", + ToscaModelURL: "/sdc/v1/catalog/resources/7c6b6644-590d-4e60-84d7-0dfba3ad4694/toscaModel", + }) + resourceList = append(resourceList, Resource{ + ID: "9391354f-8f25-462d-b331-841e6cc5c851", + InvariantID: "85cd3f14-cb9c-4a28-811b-d076e9a48303", + UniqueID: "1e6e90ec-632a-492f-9511-f2787a2befaf", + ResourceType: "VFC", + Name: "Cp", + Category: "Generic", + SubCategory: "Infrastructure", + LastUpdaterUserID: "jh0003", + LifecycleState: "CERTIFIED", + Version: "1.0", + ToscaModelURL: "/sdc/v1/catalog/resources/9391354f-8f25-462d-b331-841e6cc5c851/toscaModel", + }) +} + +func getResources(c echo.Context) error { + resourceType := c.QueryParam("resourceType") + resources := []ResourceLight{} + for _, r := range resourceList { + if (r.ComponentType != "SERVICE") && + ((resourceType == "") || (r.ResourceType == resourceType)) { + resources = append(resources, ResourceLight{ + ID: r.ID, + InvariantID: r.InvariantID, + ResourceType: r.ResourceType, + Name: r.Name, + Category: r.Category, + SubCategory: r.SubCategory, + LastUpdaterUserID: r.LastUpdaterUserID, + LifecycleState: r.LifecycleState, + Version: r.Version, + ToscaModelURL: r.ToscaModelURL, + }) + } + } + if len(resources) != 0 { + return c.JSON(http.StatusOK, resources) + } + return c.JSON(http.StatusNotFound, SdcError{ + Message: "No Resources found", + ErrorCode: "SVC4642", + Status: "Not Found"}) +} + +func getServices(c echo.Context) error { + resources := []ResourceLight{} + for _, r := range resourceList { + if r.ComponentType == "SERVICE" { + resources = append(resources, ResourceLight{ + ID: r.ID, + InvariantID: r.InvariantID, + ResourceType: r.ResourceType, + Name: r.Name, + Category: r.Category, + SubCategory: r.SubCategory, + LastUpdaterUserID: r.LastUpdaterUserID, + LifecycleState: r.LifecycleState, + Version: r.Version, + ToscaModelURL: r.ToscaModelURL, + DistributionStatus: r.DistributionStatus, + }) + } + } + if len(resources) != 0 { + return c.JSON(http.StatusOK, resources) + } + return c.JSON(http.StatusNotFound, SdcError{ + Message: "No Resources found", + ErrorCode: "SVC4642", + Status: "Not Found"}) +} + +func postResources(c echo.Context) error { + resource := new(Resource) + if err := c.Bind(resource); err != nil { + return err + } + for _, r := range resourceList { + if resource.Name == r.Name && r.ResourceType == resource.ResourceType { + return c.JSON(http.StatusBadRequest, SdcError{ + Message: "Resource of same Name and ResourceType exists", + ErrorCode: "SVC3642", + Status: "Exists"}) + } + } + resource.ID = uuid.Must(uuid.NewV4()).String() + resource.InvariantID = uuid.Must(uuid.NewV4()).String() + resource.UniqueID = uuid.Must(uuid.NewV4()).String() + resource.Version = "0.1" + resource.LifecycleState = "NOT_CERTIFIED_CHECKOUT" + resource.DistributionStatus = "DISTRIBUTION_NOT_APPROVED" + + resourceList = append(resourceList, *resource) + + return c.JSON(http.StatusCreated, resource) +} + +func postResourceAction(c echo.Context) error { + resourceID := c.Param("resourceID") + action := c.Param("action") + actionBody := new(ActionBody) + if err := c.Bind(actionBody); err != nil { + return err + } + for i, r := range resourceList { + if r.UniqueID == resourceID { + if r.LifecycleState == "NOT_CERTIFIED_CHECKOUT" && action == "Certify" { + resourceList[i].Version = "1.0" + resourceList[i].LifecycleState = "CERTIFIED" + return c.JSON(http.StatusCreated, resourceList[i]) + } + if r.LifecycleState == "NOT_CERTIFIED_CHECKOUT" && action == "checkin" { + resourceList[i].LifecycleState = "NOT_CERTIFIED_CHECKIN" + return c.JSON(http.StatusOK, resourceList[i]) + } + if r.LifecycleState == "NOT_CERTIFIED_CHECKIN" && action == "Certify" { + resourceList[i].LifecycleState = "CERTIFIED" + resourceList[i].Version = "1.0" + resourceList[i].DistributionStatus = "DISTRIBUTION_APPROVED" + return c.JSON(http.StatusOK, resourceList[i]) + } + if r.LifecycleState == "CERTIFIED" && + r.DistributionStatus == "DISTRIBUTION_APPROVED" && + action == "activate" { + resourceList[i].DistributionStatus = "DISTRIBUTED" + return c.JSON(http.StatusOK, resourceList[i]) + } + return c.JSON(http.StatusBadRequest, SdcError{ + Message: "Cannot perform this action", + ErrorCode: "SVC3642", + Status: "Bad Action"}) + } + } + return c.JSON(http.StatusNotFound, SdcError{ + Message: "Resource not found", + ErrorCode: "SVC4642", + Status: "Not Found"}) +} + +func postAddResourceToService(c echo.Context) error { + resourceID := c.Param("resourceID") + resourceAdd := new(ResourceAdd) + if err := c.Bind(resourceAdd); err != nil { + return err + } + for i, r := range resourceList { + if r.UniqueID == resourceID { + if r.LifecycleState == "NOT_CERTIFIED_CHECKOUT" { + for _, rr := range resourceList { + if rr.UniqueID == resourceAdd.UniqueID && + rr.UniqueID == resourceAdd.ComponentUID && + rr.Name == resourceAdd.Name && + rr.Version == resourceAdd.ComponentVersion && + rr.ResourceType == resourceAdd.OriginType { + if rr.ResourceType == "VF" { + ci := ComponentInstance{ + UniqueID: uuid.Must(uuid.NewV4()).String(), + Name: resourceAdd.Name, + ComponentName: resourceAdd.Name, + OriginType: "VF", + ComponentVersion: "1.0", + } + resourceList[i].ComponentInstances = append(r.ComponentInstances, ci) + } + return c.JSON(http.StatusCreated, r) + } + } + } + + return c.JSON(http.StatusBadRequest, SdcError{ + Message: "Cannot perform this action", + ErrorCode: "SVC3642", + Status: "Bad Action"}) + } + } + + return c.JSON(http.StatusNotFound, SdcError{ + Message: "Resource not found", + ErrorCode: "SVC4642", + Status: "Not Found"}) +} + +func getDistribution(c echo.Context) error { + resourceID := c.Param("resourceID") + for i, r := range resourceList { + if r.ID == resourceID { + distributionIDResult := new(DistributionIDResult) + if r.DistributionStatus == "DISTRIBUTED" { + if len(r.DistributionID) < 1 { + resourceList[i].DistributionID = uuid.Must(uuid.NewV4()).String() + } + distributionIDResult.DeployementStatus = "Distributed" + distributionIDResult.UserID = "Oper P(op0001)" + distributionIDResult.DistributionID = resourceList[i].DistributionID + } + distributionIDList := new(DistributionIDList) + distributionIDList.DistributionStatusOfServiceList = append(distributionIDList.DistributionStatusOfServiceList, *distributionIDResult) + return c.JSON(http.StatusOK, distributionIDList) + } + } + return c.JSON(http.StatusNotFound, SdcError{ + Message: "Resource not found", + ErrorCode: "SVC4642", + Status: "Not Found"}) +} + +func getDistributionList(c echo.Context) error { + distributionID := c.Param("distributionID") + for _, r := range resourceList { + if r.DistributionID == distributionID { + d := new(DistributionStatusList) + d.DistributionStatusList = distributionList + return c.JSON(http.StatusOK, d) + } + } + return c.JSON(http.StatusNotFound, SdcError{ + Message: "Resource not found", + ErrorCode: "SVC4642", + Status: "Not Found"}) +} + +func getAllResources(c echo.Context) error { + + var listResources []Resource + var listServices []Resource + + for _, r := range resourceList { + if r.ComponentType != "SERVICE" { + listResources = append(listResources, r) + } else { + listServices = append(listServices, r) + } + } + list := &ResourceList{listResources, listServices} + return c.JSON(http.StatusOK, list) +} + +func getServiceUniqueIdentifier(c echo.Context) error { + resourceID := c.Param("resourceID") + for _, r := range resourceList { + if r.UniqueID == resourceID { + return c.JSON(http.StatusOK, r) + } + } + + return c.JSON(http.StatusNotFound, SdcError{ + Message: "Resource not found", + ErrorCode: "SVC4642", + Status: "Not Found"}) +} + +func uploadTcaArtifact(c echo.Context) error { + resourceID := c.Param("resourceID") + vfID := c.Param("vfID") + for _, r := range resourceList { + if r.UniqueID == resourceID { + if r.LifecycleState == "NOT_CERTIFIED_CHECKOUT" { + for _, cc := range r.ComponentInstances { + if cc.UniqueID == vfID { + newArtifact := new(ArtifactAdd) + if err := c.Bind(newArtifact); err != nil { + return err + } + cc.DeploymentArtifacts = append(cc.DeploymentArtifacts, *newArtifact) + NewUploadResult := NewUploadResult{ + Description: newArtifact.Description, + ArtifactType: newArtifact.ArtifactType, + ArtifactName: newArtifact.ArtifactName, + } + return c.JSON(http.StatusCreated, NewUploadResult) + } + } + } + return c.JSON(http.StatusBadRequest, SdcError{ + Message: "Cannot perform this action", + ErrorCode: "SVC3642", + Status: "Bad Action"}) + } + } + return c.JSON(http.StatusNotFound, SdcError{ + Message: "Resource not found", + ErrorCode: "SVC4642", + Status: "Not Found"}) +} + +func postResourceProperties(c echo.Context) error { + resourceID := c.Param("resourceID") + var bodyBytes []byte + if c.Request().Body != nil { + bodyBytes, _ = ioutil.ReadAll(c.Request().Body) + } + var dat map[string]interface{} + if err := json.Unmarshal(bodyBytes, &dat); err != nil { + return err + } + for i, r := range resourceList { + if r.UniqueID == resourceID { + for key := range dat { + propertyBody := new(Property) + propertyBody.Name = dat[key].(map[string]interface{})["name"].(string) + propertyBody.Type = dat[key].(map[string]interface{})["type"].(string) + resourceList[i].Properties = append(r.Properties, *propertyBody) + } + return c.JSON(http.StatusOK, "") + } + } + return c.JSON(http.StatusNotFound, "") +} + +func getResourceProperties(c echo.Context) error { + resourceID := c.Param("resourceID") + for _, r := range resourceList { + if r.UniqueID == resourceID { + return c.JSON(http.StatusOK, map[string][]Property{ + "properties": r.Properties, + }) + } + } + return c.JSON(http.StatusNotFound, "") +} + +func postResourceInputs(c echo.Context) error { + resourceID := c.Param("resourceID") + inputBody := new(Input) + if err := c.Bind(inputBody); err != nil { + return err + } + for i, r := range resourceList { + if r.UniqueID == resourceID { + resourceList[i].Inputs = append(r.Inputs, *inputBody) + return c.JSON(http.StatusOK, r.Inputs) + } + } + return c.JSON(http.StatusNotFound, "") +} + +func getResourcefilteredData(c echo.Context) error { + paramType := c.QueryParam("include") + switch paramType { + case "inputs": + return getResourceInputs(c) + case "properties": + return getResourceProperties(c) + } + return errors.New("Invalid query param") +} + +func getResourceInputs(c echo.Context) error { + resourceID := c.Param("resourceID") + for _, r := range resourceList { + if r.UniqueID == resourceID { + return c.JSON(http.StatusOK, map[string][]Input{ + "inputs": r.Inputs, + }) + } + } + return c.JSON(http.StatusNotFound, "") +} + +func generateDistributionStatusList() { + distributionList = nil + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "SO-COpenSource-Env11", + Timestamp: "1574774740421", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vf-license-model.xml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "aai-ml", + Timestamp: "1574774737842", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vf-license-model.xml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "SO-COpenSource-Env11", + Timestamp: "1574774740421", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-template.yml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "cds", + Timestamp: "1574774726254", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-csar.csar", + Status: "NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-windriver-id", + Timestamp: "1574774731805", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-csar.csar", + Status: "NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "sdc-COpenSource-Env11-sdnc-dockero", + Timestamp: "1574774720318", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vsrx0_modules.json", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "sdc-COpenSource-Env11-sdnc-dockero", + Timestamp: "1574774737396", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-csar.csar", + Status: "DEPLOY_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "clamp", + Timestamp: "1574774737925", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vendor-license-model.xml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-k8s-id", + Timestamp: "1574774750490", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vsrx0_modules.json", + Status: "DEPLOY_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-k8s-id", + Timestamp: "1574774736174", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vsrx0_modules.json", + Status: "NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-windriver-id", + Timestamp: "1574774731805", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.env", + Status: "NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "SO-COpenSource-Env11", + Timestamp: "1574774740421", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-csar.csar", + Status: "NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "clamp", + Timestamp: "1574774737925", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vf-license-model.xml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "policy-id", + Timestamp: "1574774728667", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.yaml", + Status: "NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-starlingx-id", + Timestamp: "1574774737784", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.env", + Status: "NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "clamp", + Timestamp: "1574774737925", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-template.yml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-k8s-id", + Timestamp: "1574774744883", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vsrx0_modules.json", + Status: "DOWNLOAD_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-windriver-id", + Timestamp: "1574774731805", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vsrx0_modules.json", + Status: "NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-windriver-id", + Timestamp: "1574774731805", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.yaml", + Status: "NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "SO-COpenSource-Env11", + Timestamp: "1574774740421", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.yaml", + Status: "NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "SO-COpenSource-Env11", + Timestamp: "1574774756945", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.env", + Status: "DEPLOY_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "SO-COpenSource-Env11", + Timestamp: "1574774752508", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.yaml", + Status: "DOWNLOAD_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "cds", + Timestamp: "1574774726254", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vsrx0_modules.json", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-k8s-id", + Timestamp: "1574774736174", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vendor-license-model.xml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "aai-ml", + Timestamp: "1574774745892", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-csar.csar", + Status: "DOWNLOAD_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "cds", + Timestamp: "1574774726254", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vf-license-model.xml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "aai-ml", + Timestamp: "1574774737842", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-csar.csar", + Status: "NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "SO-COpenSource-Env11", + Timestamp: "1574774757951", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-csar.csar", + Status: "DEPLOY_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "aai-ml", + Timestamp: "1574774737842", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.yaml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-starlingx-id", + Timestamp: "1574774737784", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vf-license-model.xml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "clamp", + Timestamp: "1574774737925", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-csar.csar", + Status: "NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "cds", + Timestamp: "1574774726254", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-template.yml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "dcae-sch", + Timestamp: "1574774724083", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vf-license-model.xml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-k8s-id", + Timestamp: "1574774749381", + URL: "", + Status: "COMPONENT_DONE_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-k8s-id", + Timestamp: "1574774736174", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vf-license-model.xml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-k8s-id", + Timestamp: "1574774746542", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.yaml", + Status: "DOWNLOAD_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "policy-id", + Timestamp: "1574774728667", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-template.yml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-k8s-id", + Timestamp: "1574774736174", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.env", + Status: "NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "cds", + Timestamp: "1574774726254", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.yaml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "cds", + Timestamp: "1574774735595", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-csar.csar", + Status: "DOWNLOAD_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-starlingx-id", + Timestamp: "1574774737784", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vsrx0_modules.json", + Status: "NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-k8s-id", + Timestamp: "1574774736174", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-template.yml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "dcae-sch", + Timestamp: "1574774724083", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vsrx0_modules.json", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "SO-COpenSource-Env11", + Timestamp: "1574774748752", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-csar.csar", + Status: "DOWNLOAD_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "cds", + Timestamp: "1574774736609", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-csar.csar", + Status: "COMPONENT_DONE_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "SO-COpenSource-Env11", + Timestamp: "1574774969197", + URL: "", + Status: "DISTRIBUTION_COMPLETE_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "dcae-sch", + Timestamp: "1574774724083", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-csar.csar", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "aai-ml", + Timestamp: "1574774737842", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-template.yml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "aai-ml", + Timestamp: "1574774750517", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-csar.csar", + Status: "DEPLOY_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-windriver-id", + Timestamp: "1574774744623", + URL: "", + Status: "COMPONENT_DONE_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "dcae-sch", + Timestamp: "1574774724083", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.yaml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "dcae-sch", + Timestamp: "1574774724083", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.env", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-windriver-id", + Timestamp: "1574774731805", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-template.yml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-starlingx-id", + Timestamp: "1574774750947", + URL: "", + Status: "COMPONENT_DONE_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "sdc-COpenSource-Env11-sdnc-dockero", + Timestamp: "1574774735764", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-csar.csar", + Status: "DOWNLOAD_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-starlingx-id", + Timestamp: "1574774737784", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-csar.csar", + Status: "NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "cds", + Timestamp: "1574774726254", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vendor-license-model.xml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "clamp", + Timestamp: "1574774750026", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-csar.csar", + Status: "ALREADY_DOWNLOADED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "SO-COpenSource-Env11", + Timestamp: "1574774753902", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.env", + Status: "DOWNLOAD_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-k8s-id", + Timestamp: "1574774736174", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-csar.csar", + Status: "NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "policy-id", + Timestamp: "1574774728667", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-csar.csar", + Status: "NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "policy-id", + Timestamp: "1574774737376", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-csar.csar", + Status: "DOWNLOAD_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "policy-id", + Timestamp: "1574774728667", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vsrx0_modules.json", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "sdc-COpenSource-Env11-sdnc-dockero", + Timestamp: "1574774720318", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-template.yml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "SO-COpenSource-Env11", + Timestamp: "1574774754939", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vsrx0_modules.json", + Status: "DEPLOY_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-windriver-id", + Timestamp: "1574774731805", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vf-license-model.xml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "sdc-COpenSource-Env11-sdnc-dockero", + Timestamp: "1574774720318", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-csar.csar", + Status: "NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "sdc-COpenSource-Env11-sdnc-dockero", + Timestamp: "1574774720318", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.yaml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-k8s-id", + Timestamp: "1574774748190", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.env", + Status: "DOWNLOAD_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "policy-id", + Timestamp: "1574774728667", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vendor-license-model.xml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-windriver-id", + Timestamp: "1574774742014", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.yaml", + Status: "DOWNLOAD_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "sdc-COpenSource-Env11-sdnc-dockero", + Timestamp: "1574774720318", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.env", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "policy-id", + Timestamp: "1574774728667", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.env", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-windriver-id", + Timestamp: "1574774745715", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vsrx0_modules.json", + Status: "DEPLOY_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "SO-COpenSource-Env11", + Timestamp: "1574774740421", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vsrx0_modules.json", + Status: "NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "dcae-sch", + Timestamp: "1574774724083", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vendor-license-model.xml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-k8s-id", + Timestamp: "1574774736174", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.yaml", + Status: "NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "sdc-COpenSource-Env11-sdnc-dockero", + Timestamp: "1574774738399", + URL: "", + Status: "COMPONENT_DONE_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-windriver-id", + Timestamp: "1574774740487", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vsrx0_modules.json", + Status: "DOWNLOAD_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-windriver-id", + Timestamp: "1574774731805", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vendor-license-model.xml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "policy-id", + Timestamp: "1574774740598", + URL: "", + Status: "COMPONENT_DONE_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "clamp", + Timestamp: "1574774737925", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vsrx0_modules.json", + Status: "NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "aai-ml", + Timestamp: "1574774737842", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.env", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "clamp", + Timestamp: "1574774737925", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.env", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-starlingx-id", + Timestamp: "1574774737784", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-template.yml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-starlingx-id", + Timestamp: "1574774749858", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.env", + Status: "DOWNLOAD_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-starlingx-id", + Timestamp: "1574774748343", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.yaml", + Status: "DOWNLOAD_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "aai-ml", + Timestamp: "1574774751522", + URL: "", + Status: "COMPONENT_DONE_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "cds", + Timestamp: "1574774726254", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.env", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "sdc-COpenSource-Env11-sdnc-dockero", + Timestamp: "1574774720318", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vendor-license-model.xml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "SO-COpenSource-Env11", + Timestamp: "1574774740421", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.env", + Status: "NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "policy-id", + Timestamp: "1574774739512", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-csar.csar", + Status: "DEPLOY_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-starlingx-id", + Timestamp: "1574774737784", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vendor-license-model.xml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-starlingx-id", + Timestamp: "1574774746621", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vsrx0_modules.json", + Status: "DOWNLOAD_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "clamp", + Timestamp: "1574774751028", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-csar.csar", + Status: "ALREADY_DEPLOYED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "SO-COpenSource-Env11", + Timestamp: "1574774740421", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vendor-license-model.xml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-starlingx-id", + Timestamp: "1574774737784", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.yaml", + Status: "NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "sdc-COpenSource-Env11-sdnc-dockero", + Timestamp: "1574774720318", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vf-license-model.xml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "policy-id", + Timestamp: "1574774728667", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vf-license-model.xml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "aai-ml", + Timestamp: "1574774737842", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vsrx0_modules.json", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "SO-COpenSource-Env11", + Timestamp: "1574774751123", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vsrx0_modules.json", + Status: "DOWNLOAD_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "aai-ml", + Timestamp: "1574774737842", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vendor-license-model.xml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "dcae-sch", + Timestamp: "1574774724083", + URL: "/sdc/v1/catalog/services/Test12/1.0/artifacts/service-Test12-template.yml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-starlingx-id", + Timestamp: "1574774752037", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/vsrx0_modules.json", + Status: "DEPLOY_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "SO-COpenSource-Env11", + Timestamp: "1574774755942", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.yaml", + Status: "DEPLOY_OK", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "clamp", + Timestamp: "1574774737925", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.yaml", + Status: "NOT_NOTIFIED", + ErrorReason: "null", + }) + distributionList = append(distributionList, DistributionStatus{ + OmfComponentID: "multicloud-windriver-id", + Timestamp: "1574774743466", + URL: "/sdc/v1/catalog/services/Test12/1.0/resourceInstances/vsrx0/artifacts/base_ubuntu16.env", + Status: "DOWNLOAD_OK", + ErrorReason: "null", + }) +} + +func getArtifactTypes(c echo.Context) error { + list := []string{"HEAT"} + return c.JSON(http.StatusOK, list) +} + +func registerForDistribution(c echo.Context) error { + distributionRegistration := map[string]string{ + "distrNotificationTopicName":"testName", + "distrStatusTopicName":"testTopic", + } + return c.JSON(http.StatusOK, distributionRegistration) +} + +func unRegisterForDistribution(c echo.Context) error { + return c.JSON(http.StatusOK, list.New()) +} + +func distributionKafkaData(c echo.Context) error { + kafkaData := map[string]string{ + "kafkaBootStrapServer":"localhost:43219", + "distrNotificationTopicName":"SDC-DIST-NOTIF-TOPIC", + "distrStatusTopicName":"SDC-DIST-STATUS-TOPIC", + } + return c.JSON(http.StatusOK, kafkaData) +} + diff --git a/mock-sdc/sdc_handlers.go b/mock-sdc/sdc_handlers.go new file mode 100644 index 0000000..02f8a11 --- /dev/null +++ b/mock-sdc/sdc_handlers.go @@ -0,0 +1,210 @@ +// Copyright 2023 Deutsche Telekom AG, Orange +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "net/http" + + "github.com/labstack/echo" +) + +// VersionList describe the list return in SDC +type VersionList struct { + ListCount int `json:"listCount"` + Results []VersionLight `json:"results"` +} + +// Version describe version model in SDC +type Version struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + BaseID string `json:"baseId"` + Status string `json:"status"` + RealStatus string + CreationTime int64 `json:"creationTime"` + ModificationTime int64 `json:"modificationTime"` + AdditionalInfo struct { + OptionalCreationMethods []string `json:"OptionalCreationMethods"` + } `json:"additionalInfo"` + State struct { + SynchronizationState string `json:"synchronizationState"` + Dirty bool `json:"dirty"` + } `json:"state"` +} + +// VersionLight describe version model in SDC +type VersionLight struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + BaseID string `json:"baseId"` + Status string `json:"status"` + CreationTime int64 `json:"creationTime"` + ModificationTime int64 `json:"modificationTime"` + AdditionalInfo struct { + OptionalCreationMethods []string `json:"OptionalCreationMethods"` + } `json:"additionalInfo"` +} + +// VersionDetails show details of a version +type VersionDetails struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Status string `json:"status"` + CreationTime int64 `json:"creationTime"` + ModificationTime int64 `json:"modificationTime"` + State struct { + SynchronizationState string `json:"synchronizationState"` + Dirty bool `json:"dirty"` + } `json:"state"` +} + +// CreatedVersion of a CreatedVendor in SDC +type CreatedVersion struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Status string `json:"status"` +} + +// CreatedItem model in SDC +type CreatedItem struct { + ItemID string `json:"itemId"` + Version CreatedVersion `json:"version"` +} + +// Action describe the action on items in SDC +type Action struct { + Action string `json:"action"` +} + +// SdcError is the way to return Error in SDC +type SdcError struct { + Status string `json:"status"` + ErrorCode string `json:"errorCode"` + Message string `json:"message"` +} + +func getItemVersions(c echo.Context) error { + itemID := c.Param("itemID") + for _, v := range vendorList { + if v.ID == itemID { + versionList := generateVersionList(v.Versions) + list := &VersionList{len(versionList), versionList} + return c.JSON(http.StatusOK, list) + } + } + for _, v := range vspList { + if v.ID == itemID { + versionList := generateVersionList(v.Versions) + list := &VersionList{len(versionList), versionList} + return c.JSON(http.StatusOK, list) + } + } + return echo.NewHTTPError(http.StatusNotFound, "Item Not Found") +} + +func generateVersionList(versions []Version) []VersionLight { + versionList := []VersionLight{} + for _, version := range versions { + versionList = append(versionList, VersionLight{ + ID: version.ID, + Name: version.Name, + Description: version.Description, + BaseID: version.BaseID, + Status: version.Status, + CreationTime: version.CreationTime, + ModificationTime: version.ModificationTime, + AdditionalInfo: version.AdditionalInfo, + }) + + } + return versionList +} + +func getItemVersion(c echo.Context) error { + itemID := c.Param("itemID") + versionID := c.Param("versionID") + for _, v := range vendorList { + if v.ID == itemID { + for _, version := range v.Versions { + if version.ID == versionID { + versionDetails := VersionDetails{ + ID: version.ID, + Name: version.Name, + Description: version.Description, + Status: version.Status, + CreationTime: version.CreationTime, + ModificationTime: version.ModificationTime, + State: version.State, + } + return c.JSON(http.StatusOK, versionDetails) + } + } + } + } + for _, v := range vspList { + if v.ID == itemID { + for _, version := range v.Versions { + if version.ID == versionID { + versionDetails := VersionDetails{ + ID: version.ID, + Name: version.Name, + Description: version.Description, + Status: version.Status, + CreationTime: version.CreationTime, + ModificationTime: version.ModificationTime, + State: version.State, + } + return c.JSON(http.StatusOK, versionDetails) + } + } + } + } + return echo.NewHTTPError(http.StatusNotFound, "Item Not Found") +} + +func updateItemVersion(c echo.Context) error { + action := new(Action) + if err := c.Bind(action); err != nil { + return err + } + itemID := c.Param("itemID") + versionID := c.Param("versionID") + for i, v := range vspList { + if v.ID == itemID { + for j, version := range v.Versions { + if version.ID == versionID { + if action.Action == "Commit" { + if version.RealStatus == "Validated" { + vspList[i].Versions[j].RealStatus = "Commited" + vspList[i].Versions[j].State.Dirty = false + return c.String(http.StatusOK, "{}") + } + return echo.NewHTTPError(http.StatusNotFound, "Item not in good state") + } + return echo.NewHTTPError(http.StatusNotFound, "Unknown Action") + } + } + } + } + return echo.NewHTTPError(http.StatusNotFound, "Item Not Found") +} + +func getCategories(c echo.Context) error { + return c.String(http.StatusOK, `{"categories":{"resourceCategories":[{"name":"Application L4+","normalizedName":"application l4+","uniqueId":"resourceNewCategory.application l4+","icons":null,"subcategories":[{"name":"Media Servers","normalizedName":"media servers","uniqueId":"resourceNewCategory.application l4+.media servers","icons":["applicationServer"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Database","normalizedName":"database","uniqueId":"resourceNewCategory.application l4+.database","icons":["database"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Border Element","normalizedName":"border element","uniqueId":"resourceNewCategory.application l4+.border element","icons":["borderElement"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Application Server","normalizedName":"application server","uniqueId":"resourceNewCategory.application l4+.application server","icons":["applicationServer"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Firewall","normalizedName":"firewall","uniqueId":"resourceNewCategory.application l4+.firewall","icons":["firewall"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Call Control","normalizedName":"call control","uniqueId":"resourceNewCategory.application l4+.call control","icons":["call_controll"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Web Server","normalizedName":"web server","uniqueId":"resourceNewCategory.application l4+.web server","icons":["applicationServer"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Load Balancer","normalizedName":"load balancer","uniqueId":"resourceNewCategory.application l4+.load balancer","icons":["loadBalancer"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null}],"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Network Connectivity","normalizedName":"network connectivity","uniqueId":"resourceNewCategory.network connectivity","icons":null,"subcategories":[{"name":"Connection Points","normalizedName":"connection points","uniqueId":"resourceNewCategory.network connectivity.connection points","icons":["cp"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Virtual Links","normalizedName":"virtual links","uniqueId":"resourceNewCategory.network connectivity.virtual links","icons":["vl"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null}],"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Allotted Resource","normalizedName":"allotted resource","uniqueId":"resourceNewCategory.allotted resource","icons":null,"subcategories":[{"name":"BRG","normalizedName":"brg","uniqueId":"resourceNewCategory.allotted resource.brg","icons":["brg"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"TunnelXConn","normalizedName":"tunnelxconn","uniqueId":"resourceNewCategory.allotted resource.tunnelxconn","icons":["tunnel_x_connect"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"IP Mux Demux","normalizedName":"ip mux demux","uniqueId":"resourceNewCategory.allotted resource.ip mux demux","icons":["ip_mux_demux"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Security Zone","normalizedName":"security zone","uniqueId":"resourceNewCategory.allotted resource.security zone","icons":["security_zone"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Service Admin","normalizedName":"service admin","uniqueId":"resourceNewCategory.allotted resource.service admin","icons":["service_admin"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Allotted Resource","normalizedName":"allotted resource","uniqueId":"resourceNewCategory.allotted resource.allotted resource","icons":["allotted_resource"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Contrail Route","normalizedName":"contrail route","uniqueId":"resourceNewCategory.allotted resource.contrail route","icons":["contrail_route"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null}],"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Configuration","normalizedName":"configuration","uniqueId":"resourceNewCategory.configuration","icons":null,"subcategories":[{"name":"Configuration","normalizedName":"configuration","uniqueId":"resourceNewCategory.configuration.configuration","icons":["pmc"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null}],"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Network L4+","normalizedName":"network l4+","uniqueId":"resourceNewCategory.network l4+","icons":null,"subcategories":[{"name":"Common Network Resources","normalizedName":"common network resources","uniqueId":"resourceNewCategory.network l4+.common network resources","icons":["network"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null}],"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Generic","normalizedName":"generic","uniqueId":"resourceNewCategory.generic","icons":null,"subcategories":[{"name":"Abstract","normalizedName":"abstract","uniqueId":"resourceNewCategory.generic.abstract","icons":["objectStorage","compute"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Network Service","normalizedName":"network service","uniqueId":"resourceNewCategory.generic.network service","icons":["network"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Rules","normalizedName":"rules","uniqueId":"resourceNewCategory.generic.rules","icons":["networkrules","securityrules"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Infrastructure","normalizedName":"infrastructure","uniqueId":"resourceNewCategory.generic.infrastructure","icons":["connector"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Network Elements","normalizedName":"network elements","uniqueId":"resourceNewCategory.generic.network elements","icons":["network","connector"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Database","normalizedName":"database","uniqueId":"resourceNewCategory.generic.database","icons":["database"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null}],"version":null,"ownerId":null,"empty":false,"type":null},{"name":"DCAE Component","normalizedName":"dcae component","uniqueId":"resourceNewCategory.dcae component","icons":null,"subcategories":[{"name":"Analytics","normalizedName":"analytics","uniqueId":"resourceNewCategory.dcae component.analytics","icons":["dcae_analytics"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Database","normalizedName":"database","uniqueId":"resourceNewCategory.dcae component.database","icons":["dcae_database"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Policy","normalizedName":"policy","uniqueId":"resourceNewCategory.dcae component.policy","icons":["dcae_policy"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Machine Learning","normalizedName":"machine learning","uniqueId":"resourceNewCategory.dcae component.machine learning","icons":["dcae_machineLearning"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Microservice","normalizedName":"microservice","uniqueId":"resourceNewCategory.dcae component.microservice","icons":["dcae_microservice"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Source","normalizedName":"source","uniqueId":"resourceNewCategory.dcae component.source","icons":["dcae_source"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Collector","normalizedName":"collector","uniqueId":"resourceNewCategory.dcae component.collector","icons":["dcae_collector"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Utility","normalizedName":"utility","uniqueId":"resourceNewCategory.dcae component.utility","icons":["dcae_utilty"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null}],"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Network L2-3","normalizedName":"network l2-3","uniqueId":"resourceNewCategory.network l2-3","icons":null,"subcategories":[{"name":"Gateway","normalizedName":"gateway","uniqueId":"resourceNewCategory.network l2-3.gateway","icons":["gateway"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"WAN Connectors","normalizedName":"wan connectors","uniqueId":"resourceNewCategory.network l2-3.wan connectors","icons":["network","connector","port"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Infrastructure","normalizedName":"infrastructure","uniqueId":"resourceNewCategory.network l2-3.infrastructure","icons":["ucpe"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Router","normalizedName":"router","uniqueId":"resourceNewCategory.network l2-3.router","icons":["router","vRouter"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"LAN Connectors","normalizedName":"lan connectors","uniqueId":"resourceNewCategory.network l2-3.lan connectors","icons":["network","connector","port"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null}],"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Template","normalizedName":"template","uniqueId":"resourceNewCategory.template","icons":null,"subcategories":[{"name":"Base Monitoring Template","normalizedName":"base monitoring template","uniqueId":"resourceNewCategory.template.base monitoring template","icons":["monitoring_template"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Monitoring Template","normalizedName":"monitoring template","uniqueId":"resourceNewCategory.template.monitoring template","icons":["monitoring_template"],"groupings":null,"version":null,"ownerId":null,"empty":false,"type":null}],"version":null,"ownerId":null,"empty":false,"type":null}],"serviceCategories":[{"name":"Mobility","normalizedName":"mobility","uniqueId":"serviceNewCategory.mobility","icons":["mobility"],"subcategories":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Network L4+","normalizedName":"network l4+","uniqueId":"serviceNewCategory.network l4+","icons":["network_l_4"],"subcategories":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"E2E Service","normalizedName":"e2e service","uniqueId":"serviceNewCategory.e2e service","icons":["network_l_1-3"],"subcategories":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"VoIP Call Control","normalizedName":"voip call control","uniqueId":"serviceNewCategory.voip call control","icons":["call_controll"],"subcategories":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Network Service","normalizedName":"network service","uniqueId":"serviceNewCategory.network service","icons":["network_l_1-3"],"subcategories":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Network L1-3","normalizedName":"network l1-3","uniqueId":"serviceNewCategory.network l1-3","icons":["network_l_1-3"],"subcategories":null,"version":null,"ownerId":null,"empty":false,"type":null},{"name":"Partner Domain Service","normalizedName":"partner domain service","uniqueId":"serviceNewCategory.partner domain service","icons":["partner_domain_service"],"subcategories":null,"version":null,"ownerId":null,"empty":false,"type":null}],"productCategories":[]},"version":"1.6.7"}`) +} diff --git a/mock-sdc/vendor_handlers.go b/mock-sdc/vendor_handlers.go new file mode 100644 index 0000000..2bdde87 --- /dev/null +++ b/mock-sdc/vendor_handlers.go @@ -0,0 +1,185 @@ +// Copyright 2023 Deutsche Telekom AG, Orange +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "net/http" + "time" + + "github.com/labstack/echo" + uuid "github.com/satori/go.uuid" +) + +// Vendor describes license model in SDC +type Vendor struct { + ID string `json:"id"` + Type string `json:"type"` + Name string `json:"name"` + Description string `json:"description"` + Owner string `json:"owner"` + Status string `json:"status"` + Properties struct { + } `json:"properties"` + Versions []Version `json:"-"` +} + +// NewVendor describe the vendor creation model in SDC +type NewVendor struct { + IconRef string `json:"iconRef"` + VendorName string `json:"vendorName"` + Description string `json:"description"` +} + +// VendorList is the way to return Vendors in SDC +type VendorList struct { + ListCount int `json:"listCount"` + Results []Vendor `json:"results"` +} + +var vendorList []Vendor + +func generateInitialVendorList() { + vendorList = nil + var empty struct{} + version1 := Version{ + ID: "61c134e128f54119934b3960c77a3f33", + Name: "1.0", + Description: "Initial version", + BaseID: "", + Status: "Certified", + RealStatus: "Certified", + CreationTime: 1559565688604, + ModificationTime: 1559565787436, + AdditionalInfo: struct { + OptionalCreationMethods []string `json:"OptionalCreationMethods"` + }{ + OptionalCreationMethods: []string{"major"}}, + } + version2 := Version{ + ID: "2e3ba48c748d47e3bd4afdd8348bdfb9", + Name: "1.0", + Description: "Initial version", + BaseID: "", + Status: "Certified", + RealStatus: "Certified", + CreationTime: 1559562354868, + ModificationTime: 1559562421476, + AdditionalInfo: struct { + OptionalCreationMethods []string `json:"OptionalCreationMethods"` + }{ + OptionalCreationMethods: []string{"major"}}, + } + vendorList = append(vendorList, + Vendor{ + ID: "212a52b2630749388a7693086ac1467e", + Type: "vlm", + Name: "wvfw", + Description: "wvfw", + Owner: "cs0008", + Status: "ACTIVE", + Properties: empty, + Versions: []Version{version1}}) + vendorList = append(vendorList, Vendor{ + ID: "e78eb0b1c73e43138f705cd92c0e4ace", + Type: "vlm", + Name: "vfw_test", + Description: "test vfw", + Owner: "cs0008", + Status: "ACTIVE", + Properties: empty, + Versions: []Version{version2}}) +} + +func getVendorServiceModels(c echo.Context) error { + list := &VendorList{len(vendorList), vendorList} + return c.JSON(http.StatusOK, list) +} + +func postVendorServiceModels(c echo.Context) error { + newVendor := new(NewVendor) + if err := c.Bind(newVendor); err != nil { + return err + } + + u1 := uuid.Must(uuid.NewV4()).String() + version := Version{ + ID: u1, + Name: "1.0", + Description: "Initial version", + BaseID: "", + Status: "Draft", + RealStatus: "Draft", + CreationTime: (time.Now().UnixNano() / 1000000), + ModificationTime: (time.Now().UnixNano() / 1000000), + AdditionalInfo: struct { + OptionalCreationMethods []string `json:"OptionalCreationMethods"` + }{ + OptionalCreationMethods: []string{"major"}}, + State: struct { + SynchronizationState string `json:"synchronizationState"` + Dirty bool `json:"dirty"` + }{ + SynchronizationState: "UpToDate", + Dirty: false, + }, + } + u2 := uuid.Must(uuid.NewV4()).String() + var empty struct{} + vendorList = append(vendorList, Vendor{ + ID: u2, + Type: "vlm", + Name: newVendor.VendorName, + Description: newVendor.Description, + Owner: "cs0008", + Status: "ACTIVE", + Properties: empty, + Versions: []Version{version}}) + + createdVendor := CreatedItem{ + ItemID: u2, + Version: CreatedVersion{ + ID: u1, + Name: "1.0", + Description: "Initial version", + Status: "Draft", + }, + } + return c.JSON(http.StatusCreated, createdVendor) +} + +func updateVendorVersion(c echo.Context) error { + action := new(Action) + if err := c.Bind(action); err != nil { + return err + } + if action.Action == "Submit" { + vendorID := c.Param("vendorID") + versionID := c.Param("versionID") + for i, vendor := range vendorList { + if vendor.ID == vendorID { + for j, version := range vendor.Versions { + if version.ID == versionID { + vendorList[i].Versions[j].Status = "Certified" + vendorList[i].Versions[j].RealStatus = "Certified" + } + return c.String(http.StatusOK, "{}") + } + return echo.NewHTTPError(http.StatusNotFound, "Version Not Found") + } + } + return echo.NewHTTPError(http.StatusNotFound, "Vendor Not Found") + } + return echo.NewHTTPError(http.StatusNotFound, "Unknown Action") +} diff --git a/mock-sdc/vsp_handlers.go b/mock-sdc/vsp_handlers.go new file mode 100644 index 0000000..6b45774 --- /dev/null +++ b/mock-sdc/vsp_handlers.go @@ -0,0 +1,418 @@ +// Copyright 2023 Deutsche Telekom AG, Orange +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "net/http" + "strings" + "time" + + "github.com/labstack/echo" + uuid "github.com/satori/go.uuid" +) + +// Vsp describes software product in SDC +type Vsp struct { + ID string `json:"id"` + Icon string `json:"icon"` + OnboardingMethod string `json:"onboardingMethod"` + Name string `json:"name"` + Description string `json:"description"` + Owner string `json:"owner"` + Status string `json:"status"` + VendorName string `json:"vendorName"` + VendorID string `json:"vendorId"` + Category string `json:"category"` + SubCategory string `json:"subCategory"` + CandidateOnboardingOrigin string `json:"candidateOnboardingOrigin"` + OnboardingOrigin string `json:"onboardingOrigin"` + NetworkPackageName string `json:"networkPackageName"` + ValidationData struct { + ImportStructure struct { + Heat string `json:"heat"` + } `json:"importStructure"` + } `json:"validationData"` + Versions []Version `json:"-"` +} + +// VspLight describes software product in SDC lists +type VspLight struct { + ID string `json:"id"` + OnboardingMethod string `json:"onboardingMethod"` + Name string `json:"name"` + Description string `json:"description"` + Owner string `json:"owner"` + Status string `json:"status"` + VendorName string `json:"vendorName"` + VendorID string `json:"vendorId"` +} + +// VspList is the way to return Vsps in SDC +type VspList struct { + ListCount int `json:"listCount"` + Results []VspLight `json:"results"` +} + +// VspDetailsDraft describes software product in SDC +type VspDetailsDraft struct { + ID string `json:"id"` + Icon string `json:"icon"` + OnboardingMethod string `json:"onboardingMethod"` + Name string `json:"name"` + Description string `json:"description"` + VendorName string `json:"vendorName"` + VendorID string `json:"vendorId"` + Version string `json:"version"` + Category string `json:"category"` + SubCategory string `json:"subCategory"` +} + +// VspDetailsUploaded describes software product in SDC +type VspDetailsUploaded struct { + ID string `json:"id"` + Icon string `json:"icon"` + OnboardingMethod string `json:"onboardingMethod"` + Name string `json:"name"` + Description string `json:"description"` + VendorName string `json:"vendorName"` + VendorID string `json:"vendorId"` + Version string `json:"version"` + Category string `json:"category"` + SubCategory string `json:"subCategory"` + CandidateOnboardingOrigin string `json:"candidateOnboardingOrigin"` + NetworkPackageName string `json:"networkPackageName"` +} + +// VspDetailsValidated describes software product in SDC +type VspDetailsValidated struct { + ID string `json:"id"` + Icon string `json:"icon"` + OnboardingMethod string `json:"onboardingMethod"` + Name string `json:"name"` + Description string `json:"description"` + VendorName string `json:"vendorName"` + VendorID string `json:"vendorId"` + Version string `json:"version"` + Category string `json:"category"` + SubCategory string `json:"subCategory"` + OnboardingOrigin string `json:"onboardingOrigin"` + NetworkPackageName string `json:"networkPackageName"` + ValidationData struct { + ImportStructure struct { + Heat string `json:"heat"` + } `json:"importStructure"` + } `json:"validationData"` +} + +// NewVsp describe the vsp creation model in SDC +type NewVsp struct { + Icon string `json:"iconRef"` + Name string `json:"name"` + VendorName string `json:"vendorName"` + VendorID string `json:"vendorId"` + Description string `json:"description"` + Category string `json:"category"` + SubCategory string `json:"subCategory"` + LicensingData struct{} `json:"licensingData"` + OnboardingMethod string `json:"onboardingMethod"` +} + +// ArtifactUploadResult bla +type ArtifactUploadResult struct { + Errors struct{} `json:"errors"` + Status string `json:"status"` + OnboardingOrigin string `json:"onboardingOrigin"` + NetworkPackageName string `json:"networkPackageName"` +} + +// ArtifactValidationResult bla +type ArtifactValidationResult struct { + Errors struct{} `json:"errors"` + Status string `json:"status"` + FileNames []string `json:"fileNames"` +} + +// CsarCreateResult bla +type CsarCreateResult struct { + Description string `json:"description"` + VspName string `json:"vspName"` + Version string `json:"version"` + PackageID string `json:"packageId"` + Category string `json:"category"` + SubCategory string `json:"subCategory"` + VendorName string `json:"vendorName"` + VendorRelease string `json:"vendorRelease"` + PackageType string `json:"packageType"` + ResourceType string `json:"resourceType"` +} + +var vspList []Vsp + +func generateInitialVspList() { + vspList = []Vsp{} +} + +func getVendorSoftwareProducts(c echo.Context) error { + vspLights := []VspLight{} + for _, v := range vspList { + vspLights = append(vspLights, VspLight{ + ID: v.ID, + OnboardingMethod: v.OnboardingMethod, + Name: v.Name, + Description: v.Description, + Owner: v.Owner, + Status: v.Status, + VendorName: v.VendorName, + VendorID: v.VendorID, + }) + } + list := &VspList{len(vspLights), vspLights} + return c.JSON(http.StatusOK, list) +} + +func postVendorSoftwareProducts(c echo.Context) error { + newVsp := new(NewVsp) + if err := c.Bind(newVsp); err != nil { + return err + } + + u1 := uuid.Must(uuid.NewV4()).String() + version := Version{ + ID: u1, + Name: "1.0", + Description: "Initial version", + BaseID: "", + Status: "Draft", + RealStatus: "Draft", + CreationTime: (time.Now().UnixNano() / 1000000), + ModificationTime: (time.Now().UnixNano() / 1000000), + AdditionalInfo: struct { + OptionalCreationMethods []string `json:"OptionalCreationMethods"` + }{ + OptionalCreationMethods: []string{"major"}}, + State: struct { + SynchronizationState string `json:"synchronizationState"` + Dirty bool `json:"dirty"` + }{ + SynchronizationState: "UpToDate", + Dirty: false, + }, + } + u2 := uuid.Must(uuid.NewV4()).String() + vspList = append(vspList, Vsp{ + ID: u2, + OnboardingMethod: "NetworkPackage", + Name: newVsp.Name, + Description: newVsp.Description, + Owner: "cs0008", + Status: "ACTIVE", + VendorName: newVsp.VendorName, + VendorID: newVsp.VendorID, + Category: "resourceNewCategory.generic", + SubCategory: "resourceNewCategory.generic.abstract", + Icon: "icon", + Versions: []Version{version}}) + + createdVsp := CreatedItem{ + ItemID: u2, + Version: CreatedVersion{ + ID: u1, + Name: "1.0", + Description: "Initial version", + Status: "Draft", + }, + } + return c.JSON(http.StatusCreated, createdVsp) +} + +func uploadArtifacts(c echo.Context) error { + vspID := c.Param("vspID") + versionID := c.Param("versionID") + for i, v := range vspList { + if v.ID == vspID { + for j, version := range v.Versions { + if version.ID == versionID { + if version.RealStatus == "Draft" { + file, err := c.FormFile("upload") + if err != nil { + return err + } + fileName := strings.Split(file.Filename, ".")[0] + fileExtension := strings.Split(file.Filename, ".")[1] + vspList[i].NetworkPackageName = fileName + vspList[i].CandidateOnboardingOrigin = fileExtension + vspList[i].Versions[j].RealStatus = "Uploaded" + var empty struct{} + artifactUploadResult := ArtifactUploadResult{ + Errors: empty, + Status: "Success", + OnboardingOrigin: fileExtension, + NetworkPackageName: fileName, + } + return c.JSON(http.StatusCreated, artifactUploadResult) + } + } + } + } + } + return echo.NewHTTPError(http.StatusNotFound, "Item Not Found") +} + +func validateArtifacts(c echo.Context) error { + vspID := c.Param("vspID") + versionID := c.Param("versionID") + for i, v := range vspList { + if v.ID == vspID { + for j, version := range v.Versions { + if version.ID == versionID { + if version.RealStatus == "Uploaded" { + vspList[i].OnboardingOrigin = vspList[i].CandidateOnboardingOrigin + vspList[i].Versions[j].State.Dirty = true + vspList[i].ValidationData = struct { + ImportStructure struct { + Heat string `json:"heat"` + } `json:"importStructure"` + }{ + ImportStructure: struct { + Heat string `json:"heat"` + }{ + Heat: "Yes", + }, + } + vspList[i].Versions[j].RealStatus = "Validated" + var empty struct{} + artifactValidationResult := ArtifactValidationResult{ + Errors: empty, + FileNames: []string{ + "base_ubuntu16.env", + "base_ubuntu16.yaml", + }, + Status: "Success", + } + return c.JSON(http.StatusOK, artifactValidationResult) + } + } + } + } + } + return echo.NewHTTPError(http.StatusNotFound, "Item Not Found") +} + +func updateVspVersion(c echo.Context) error { + action := new(Action) + if err := c.Bind(action); err != nil { + return err + } + vspID := c.Param("vspID") + versionID := c.Param("versionID") + for i, v := range vspList { + if v.ID == vspID { + for j, version := range v.Versions { + if version.ID == versionID { + if action.Action == "Submit" { + if version.RealStatus == "Commited" { + vspList[i].Versions[j].RealStatus = "Certified" + vspList[i].Versions[j].Status = "Certified" + return c.String(http.StatusOK, "{}") + } + return echo.NewHTTPError(http.StatusNotFound, "Item not in good state") + } + if action.Action == "Create_Package" { + if version.RealStatus == "Certified" { + csarCreateResult := CsarCreateResult{ + Description: v.Description, + VspName: v.Name, + Version: version.Name, + PackageID: v.ID, + Category: v.Category, + SubCategory: v.SubCategory, + VendorName: v.VendorName, + VendorRelease: "1.0", + PackageType: "CSAR", + ResourceType: "VF", + } + return c.JSON(http.StatusOK, csarCreateResult) + } + return echo.NewHTTPError(http.StatusNotFound, "Item not in good state") + } + return echo.NewHTTPError(http.StatusNotFound, "Unknown Action") + } + } + } + } + return echo.NewHTTPError(http.StatusNotFound, "Item Not Found") +} + +func getVspVersion(c echo.Context) error { + vspID := c.Param("vspID") + versionID := c.Param("versionID") + for _, v := range vspList { + if v.ID == vspID { + for _, version := range v.Versions { + if version.ID == versionID { + if version.RealStatus == "Draft" { + vspDetails := VspDetailsDraft{ + ID: v.ID, + Icon: v.Icon, + OnboardingMethod: v.OnboardingMethod, + Name: v.Name, + Description: v.Description, + VendorName: v.VendorName, + VendorID: v.VendorID, + Version: version.ID, + Category: v.Category, + SubCategory: v.SubCategory, + } + return c.JSON(http.StatusOK, vspDetails) + } + if version.RealStatus == "Uploaded" { + vspDetails := VspDetailsUploaded{ + ID: v.ID, + Icon: v.Icon, + OnboardingMethod: v.OnboardingMethod, + Name: v.Name, + Description: v.Description, + VendorName: v.VendorName, + VendorID: v.VendorID, + Version: version.ID, + Category: v.Category, + SubCategory: v.SubCategory, + CandidateOnboardingOrigin: v.CandidateOnboardingOrigin, + NetworkPackageName: v.NetworkPackageName, + } + return c.JSON(http.StatusOK, vspDetails) + } + vspDetails := VspDetailsValidated{ + ID: v.ID, + Icon: v.Icon, + OnboardingMethod: v.OnboardingMethod, + Name: v.Name, + Description: v.Description, + VendorName: v.VendorName, + VendorID: v.VendorID, + Version: version.ID, + Category: v.Category, + SubCategory: v.SubCategory, + OnboardingOrigin: v.OnboardingOrigin, + NetworkPackageName: v.NetworkPackageName, + ValidationData: v.ValidationData, + } + return c.JSON(http.StatusOK, vspDetails) + } + } + } + } + return echo.NewHTTPError(http.StatusNotFound, "Item Not Found") +} diff --git a/mock-sdnc/Dockerfile b/mock-sdnc/Dockerfile new file mode 100644 index 0000000..d558a0d --- /dev/null +++ b/mock-sdnc/Dockerfile @@ -0,0 +1,11 @@ +FROM python:3.8-alpine + +COPY . /app + +WORKDIR /app + +RUN pip install -r requirements.txt + +ENTRYPOINT ["python"] + +CMD ["app.py"] diff --git a/mock-sdnc/README.md b/mock-sdnc/README.md new file mode 100644 index 0000000..a2e32f0 --- /dev/null +++ b/mock-sdnc/README.md @@ -0,0 +1,2 @@ +# mock-sdnc + diff --git a/mock-sdnc/app.py b/mock-sdnc/app.py new file mode 100644 index 0000000..f5899e1 --- /dev/null +++ b/mock-sdnc/app.py @@ -0,0 +1,35 @@ +"""SDNC mock application.""" +""" + Copyright 2023 Deutsche Telekom AG, Orange + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +from flask import Flask +from flask_restful import Api + +from resources.preload import Preload + + +app = Flask(__name__) +api = Api(app) + + +api.add_resource( + Preload, + "/restconf/operations/GENERIC-RESOURCE-API:preload-vf-module-topology-operation", + "/restconf/operations/GENERIC-RESOURCE-API:preload-network-topology-operation", +) + + +if __name__ == "__main__": + app.run(host="0.0.0.0", debug=True) diff --git a/mock-sdnc/requirements.txt b/mock-sdnc/requirements.txt new file mode 100644 index 0000000..8c695e4 --- /dev/null +++ b/mock-sdnc/requirements.txt @@ -0,0 +1 @@ +Flask-RESTful==0.3.8 diff --git a/mock-sdnc/resources/__init__.py b/mock-sdnc/resources/__init__.py new file mode 100644 index 0000000..56a522c --- /dev/null +++ b/mock-sdnc/resources/__init__.py @@ -0,0 +1,16 @@ +"""SDNC mock resources package.""" +""" + Copyright 2023 Deutsche Telekom AG, Orange + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" diff --git a/mock-sdnc/resources/preload.py b/mock-sdnc/resources/preload.py new file mode 100644 index 0000000..9795e16 --- /dev/null +++ b/mock-sdnc/resources/preload.py @@ -0,0 +1,28 @@ +"""SDNC preload resource module.""" +""" + Copyright 2023 Deutsche Telekom AG, Orange + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +from flask_restful import Resource + + +class Preload(Resource): + """Preload resource class.""" + + def post(self) -> None: + """Preload resource mock method. + + Do nothing. + + """ diff --git a/mock-so/Dockerfile b/mock-so/Dockerfile new file mode 100644 index 0000000..d558a0d --- /dev/null +++ b/mock-so/Dockerfile @@ -0,0 +1,11 @@ +FROM python:3.8-alpine + +COPY . /app + +WORKDIR /app + +RUN pip install -r requirements.txt + +ENTRYPOINT ["python"] + +CMD ["app.py"] diff --git a/mock-so/README.md b/mock-so/README.md new file mode 100644 index 0000000..539b890 --- /dev/null +++ b/mock-so/README.md @@ -0,0 +1,2 @@ +# mock-so + diff --git a/mock-so/app.py b/mock-so/app.py new file mode 100644 index 0000000..b1d6db2 --- /dev/null +++ b/mock-so/app.py @@ -0,0 +1,126 @@ +"""SO mock application.""" +""" + Copyright 2023 Deutsche Telekom AG, Orange + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +import json + +from flask import Flask, request +from flask_restful import Api + +from resources.orchestration_request import OrchestrationRequest +from resources.service_instance import ( + NetworkInstance, + NetworkInstanceList, + ServiceInstance, + ServiceInstanceList, + VnfInstance, + VnfInstanceList, + VfModuleInstance, + VfModuleInstanceList, +) + + +app = Flask(__name__) +api = Api(app) + + +@app.route("/reset") +def reset(): + """Reset endpoint. + + Reset all resources. + + Returns: + str: Empty string, it has to returns anything + + """ + ServiceInstanceList.reset() + OrchestrationRequest.reset() + return "" + + +@app.route("/set_aai_mock", methods=["POST"]) +def set_aai_mock(): + """Set A&AI mock url address. + + Set it for all resources which connects with A&AI mock. + + Returns: + str: Empty string, it has to returns anything + + """ + aai_mock_url = json.loads(request.data)["AAI_MOCK"] + ServiceInstance.set_aai_mock(aai_mock_url) + ServiceInstanceList.set_aai_mock(aai_mock_url) + VnfInstance.set_aai_mock(aai_mock_url) + VfModuleInstance.set_aai_mock(aai_mock_url) + VnfInstanceList.set_aai_mock(aai_mock_url) + VfModuleInstanceList.set_aai_mock(aai_mock_url) + NetworkInstance.set_aai_mock(aai_mock_url) + NetworkInstanceList.set_aai_mock(aai_mock_url) + return "" + + +api.add_resource( + ServiceInstance, + "/onap/so/infra/serviceInstantiation/v7/serviceInstances/<service_instance_id>", +) +api.add_resource( + ServiceInstanceList, "/onap/so/infra/serviceInstantiation/v7/serviceInstances" +) +api.add_resource( + VnfInstance, + ( + "/onap/so/infra/serviceInstantiation/v7/serviceInstances/<service_instance_id>/" + "vnfs/<vnf_instance_id>" + ), +) +api.add_resource( + VnfInstanceList, + "/onap/so/infra/serviceInstantiation/v7/serviceInstances/<service_instance_id>/vnfs", +) +api.add_resource( + VfModuleInstance, + ( + "/onap/so/infra/serviceInstantiation/v7/serviceInstances/<service_instance_id>/vnfs/" + "<vnf_instance_id>/vfModules/<vf_module_instance_id>" + ), +) +api.add_resource( + VfModuleInstanceList, + ( + "/onap/so/infra/serviceInstantiation/v7/serviceInstances/<service_instance_id>/vnfs/" + "<vnf_instance_id>/vfModules" + ), +) +api.add_resource( + OrchestrationRequest, + "/onap/so/infra/orchestrationRequests/v7/<orchestration_request_id>", +) +api.add_resource( + NetworkInstanceList, + "/onap/so/infra/serviceInstantiation/v7/serviceInstances/<service_instance_id>/networks", +) +api.add_resource( + NetworkInstance, + ( + "/onap/so/infra/serviceInstantiation/v7/serviceInstances/<service_instance_id>/" + "networks/<network_instance_id>" + ), +) + + +if __name__ == "__main__": + app.run(host="0.0.0.0", debug=True) diff --git a/mock-so/requirements.txt b/mock-so/requirements.txt new file mode 100644 index 0000000..f733aba --- /dev/null +++ b/mock-so/requirements.txt @@ -0,0 +1,2 @@ +Flask-RESTful==0.3.8 +requests==2.23.0
\ No newline at end of file diff --git a/mock-so/resources/__init__.py b/mock-so/resources/__init__.py new file mode 100644 index 0000000..e189131 --- /dev/null +++ b/mock-so/resources/__init__.py @@ -0,0 +1,16 @@ +"""SO mock resources.""" +""" + Copyright 2023 Deutsche Telekom AG, Orange + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" diff --git a/mock-so/resources/orchestration_request.py b/mock-so/resources/orchestration_request.py new file mode 100644 index 0000000..e7df8e9 --- /dev/null +++ b/mock-so/resources/orchestration_request.py @@ -0,0 +1,93 @@ +"""Mock SO orchestration request resource.""" +""" + Copyright 2023 Deutsche Telekom AG, Orange + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +from dataclasses import dataclass, field +from datetime import datetime +from typing import Dict + +from flask_restful import Resource + + +@dataclass +class OrchestrationRequestData: + """Orchestration request dataclass.""" + + request_id: str + created_at: datetime = field(default_factory=datetime.now) + + +ORCHESTRATION_REQUESTS = {} + + +def time_diff(dt: datetime, diff: int = 1) -> bool: + """Check if given datetime has older (in seconds) than current datetime. + + Args: + dt (datetime): Datetime to check + diff (int, optional): Number of seconds to check. Defaults to 1. + + Returns: + bool: True if datetime is older, False otherwise + + """ + return (datetime.now() - dt).seconds > diff + + +class OrchestrationRequest(Resource): + """Orchestration request resource.""" + + @staticmethod + def reset() -> None: + """Reset orchestration request resource. + + Clean ORCHESTRATION_REQUESTS dictionary + + """ + global ORCHESTRATION_REQUESTS + ORCHESTRATION_REQUESTS = {} + + def get(self, orchestration_request_id: str) -> Dict[str, Dict[str, str]]: + """Get orchestration request data. + + Return orchestration request data from ORCHESTRATION_REQUESTS dictionary. + If it doesn't exist it creates that. + + Args: + orchestration_request_id (str): Orchestration request id key value + + Returns: + Dict[str, Dict[str, str]]: Orchestration request data + """ + try: + orchestration_request_data = ORCHESTRATION_REQUESTS[ + orchestration_request_id + ] + except KeyError: + orchestration_request_data = OrchestrationRequestData( + request_id=orchestration_request_id + ) + ORCHESTRATION_REQUESTS[ + orchestration_request_id + ] = orchestration_request_data + return { + "request": { + "requestStatus": { + "requestState": "COMPLETE" + if time_diff(orchestration_request_data.created_at) + else "IN_PROGRESS" + } + } + } diff --git a/mock-so/resources/service_instance.py b/mock-so/resources/service_instance.py new file mode 100644 index 0000000..60880c2 --- /dev/null +++ b/mock-so/resources/service_instance.py @@ -0,0 +1,344 @@ +"""SO mock instance resources.""" +""" + Copyright 2023 Deutsche Telekom AG, Orange + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +from typing import Callable, Dict, List +from uuid import uuid4 + +import requests +from flask_restful import Resource, request + + +SERVICE_INSTANCES = {} + + +class SoResource(Resource): + """Base SO resource class.""" + + AAI_MOCK_URL = "" + + @classmethod + def set_aai_mock(cls, aai_mock_url: str) -> None: + """Set A&AI mock address. + + Instance resources needs to communicate with A&AI mock + to update it's state. + + Args: + aai_mock_url (str): A&AI mock url + + """ + cls.AAI_MOCK_URL = aai_mock_url + + @classmethod + def reset(cls) -> None: + """Reset SO resource. + + Clean SERVICE_INSTANCES dictionary + and A&AI mock url address. + + """ + global SERVICE_INSTANCES + SERVICE_INSTANCES = {} + cls.AAI_MOCK_URL = "" + + def check_aai_mock_address(method: Callable) -> Callable: + """Decorate method to check if A&AI address is set. + + If A&AI mock address is not set it returns 500 HTTP + response for resource's method + + Args: + method (Callable): method to decorate + + """ + + def decorator(self, *args, **kwargs): + if not self.AAI_MOCK_URL: + return "A&AI mock address not set", 500 + return method(self, *args, **kwargs) + + return decorator + + +class ServiceInstance(SoResource): + """Service instance resource class.""" + + def delete(self, service_instance_id: str) -> Dict[str, Dict[str, str]]: + """Delete service instance. + + Remove service instance data from SERVICE_INSTANCES + dictionary. + + Args: + service_instance_id (str): Service instance id key value + + Returns: + Dict[str, Dict[str, str]]: Deletion request dictionary + + """ + service_instance = SERVICE_INSTANCES[service_instance_id] + requests.delete( + ( + f"{self.AAI_MOCK_URL}/aai/v16/business/customers/customer/" + f"{service_instance['customerId']}/service-subscriptions/service-subscription/" + f"{service_instance['serviceSubscriptionId']}/service-instances/service-instance/" + f"{service_instance_id}" + ) + ) + del SERVICE_INSTANCES[service_instance_id] + return {"requestReferences": {"requestId": str(uuid4())}} + + +class ServiceInstanceList(SoResource): + """Service instances list resource.""" + + @SoResource.check_aai_mock_address + def post(self) -> Dict[str, Dict[str, str]]: + """Create service instance. + + Create service instance data dictionary. + Call request to A&AI mock to create service instance there. + + Returns: + Dict[str, Dict[str, str]]: Creation request dictionary + + """ + instance_id = str(uuid4()) + request_data = request.get_json() + customer_id = request_data["requestDetails"]["subscriberInfo"][ + "globalSubscriberId" + ] + service_subscription_id = request_data["requestDetails"]["requestParameters"][ + "subscriptionServiceType" + ] + instance_name = request_data["requestDetails"]["requestInfo"]["instanceName"] + service_instance = { + "requestId": str(uuid4()), + "instanceId": instance_id, + "customerId": customer_id, + "serviceSubscriptionId": service_subscription_id, + "instanceName": instance_name, + } + requests.post( + ( + f"{self.AAI_MOCK_URL}/aai/v16/business/customers/customer/{customer_id}/" + f"service-subscriptions/service-subscription/{service_subscription_id}/" + "service-instances" + ), + json={ + "service-instance-name": instance_name, + "service-instance-id": instance_id, + }, + ) + SERVICE_INSTANCES[service_instance["instanceId"]] = service_instance + return {"requestReferences": service_instance} + + +class VnfInstance(SoResource): + """Vnf instance resource.""" + + @SoResource.check_aai_mock_address + def delete( + self, service_instance_id: str, vnf_instance_id: str + ) -> Dict[str, Dict[str, str]]: + """Delete vnf instance. + + Remove vnf instanca data from SERVICE_INSTANCES dictionary. + Call DELETE request to A&AI mock. + + Args: + service_instance_id (str): Service instance id key value + vnf_instance_id (str): Vnf instance id key value + + Returns: + Dict[str, Dict[str, str]]: Deletion request dictionary. + + """ + related_service = SERVICE_INSTANCES[service_instance_id] + requests.delete( + ( + f"{self.AAI_MOCK_URL}/aai/v16/business/customers/customer/" + f"{related_service['customerId']}/service-subscriptions/service-subscription/" + f"{related_service['serviceSubscriptionId']}/service-instances/service-instance/" + f"{service_instance_id}/relationship-list" + ) + ) + return {"requestReferences": {"requestId": str(uuid4())}} + + +class VnfInstanceList(SoResource): + """Vnf instances list resource.""" + + @SoResource.check_aai_mock_address + def post(self, service_instance_id: str) -> Dict[str, Dict[str, str]]: + """Create vnf instance. + + Create vnf instance data dictionary. + Call request to A&AI mock to create vnf instance there. + + Returns: + Dict[str, Dict[str, str]]: Creation request dictionary + + """ + instance_id = str(uuid4()) + request_data = request.get_json() + related_instance_id = request_data["requestDetails"]["relatedInstanceList"][0][ + "relatedInstance" + ]["instanceId"] + related_service = SERVICE_INSTANCES[related_instance_id] + requests.post( + ( + f"{self.AAI_MOCK_URL}/aai/v16/business/customers/customer/{related_service['customerId']}/" + f"service-subscriptions/service-subscription/{related_service['serviceSubscriptionId']}/" + f"service-instances/service-instance/{related_instance_id}/relationship-list" + ), + json={ + "related-to": "generic-vnf", + "related-link": f"/aai/v16/network/generic-vnfs/generic-vnf/{instance_id}", + }, + ) + return { + "requestReferences": {"requestId": str(uuid4()), "instanceId": instance_id} + } + + +class VfModuleInstance(SoResource): + """Vf module instance resource class.""" + + @SoResource.check_aai_mock_address + def delete( + self, service_instance_id: str, vnf_instance_id: str, vf_module_instance_id: str + ) -> Dict[str, Dict[str, str]]: + """Delete vf module instance. + + Call DELETE request to A&AI mock to delete vf module instance. + + Args: + service_instance_id (str): Service instance id key value. + vnf_instance_id (str): Vnf instance id key value. + vf_module_instance_id (str): Vf module instance id key value. + + Returns: + Dict[str, Dict[str, str]]: Deletion request dictionary + + """ + requests.delete( + ( + f"{self.AAI_MOCK_URL}/aai/v16/network/generic-vnfs/generic-vnf/" + f"{vnf_instance_id}/vf-modules/{vf_module_instance_id}" + ) + ) + return {"requestReferences": {"requestId": str(uuid4())}} + + +class VfModuleInstanceList(SoResource): + """Vf module instances list resource.""" + + @SoResource.check_aai_mock_address + def post( + self, service_instance_id: str, vnf_instance_id: str + ) -> Dict[str, Dict[str, str]]: + """Create vf module instance. + + Call POST request to A&AI mock to create vf module instance. + + Args: + service_instance_id (str): Service instance id key value + vnf_instance_id (str): Vnf instance id key value + + Returns: + Dict[str, Dict[str, str]]: Creation request dictionary + + """ + instance_id = str(uuid4()) + requests.post( + ( + f"{self.AAI_MOCK_URL}/aai/v16/network/generic-vnfs/generic-vnf/" + f"{vnf_instance_id}/vf-modules" + ), + json={"vf-module-id": instance_id}, + ) + return { + "requestReferences": {"requestId": str(uuid4()), "instanceId": instance_id} + } + + +class NetworkInstance(SoResource): + """Network instance resource.""" + + @SoResource.check_aai_mock_address + def delete( + self, service_instance_id: str, network_instance_id: str + ) -> Dict[str, Dict[str, str]]: + """Delete network instance. + + Remove network instanca data from SERVICE_INSTANCES dictionary. + Call DELETE request to A&AI mock. + + Args: + service_instance_id (str): Service instance id key value + network_instance_id (str): Network instance id key value + + Returns: + Dict[str, Dict[str, str]]: Deletion request dictionary. + + """ + related_service = SERVICE_INSTANCES[service_instance_id] + requests.delete( + ( + f"{self.AAI_MOCK_URL}/aai/v16/business/customers/customer/" + f"{related_service['customerId']}/service-subscriptions/service-subscription/" + f"{related_service['serviceSubscriptionId']}/service-instances/service-instance/" + f"{service_instance_id}/relationship-list" + ) + ) + return {"requestReferences": {"requestId": str(uuid4())}} + + +class NetworkInstanceList(SoResource): + """Network instances list resource.""" + + @SoResource.check_aai_mock_address + def post(self, service_instance_id: str) -> Dict[str, Dict[str, str]]: + """Create network instance. + + Create network instance data dictionary. + Call request to A&AI mock to create network instance there. + + Returns: + Dict[str, Dict[str, str]]: Creation request dictionary + + """ + instance_id = str(uuid4()) + request_data = request.get_json() + related_instance_id = request_data["requestDetails"]["relatedInstanceList"][0][ + "relatedInstance" + ]["instanceId"] + related_service = SERVICE_INSTANCES[related_instance_id] + requests.post( + ( + f"{self.AAI_MOCK_URL}/aai/v16/business/customers/customer/{related_service['customerId']}/" + f"service-subscriptions/service-subscription/{related_service['serviceSubscriptionId']}/" + f"service-instances/service-instance/{related_instance_id}/relationship-list" + ), + json={ + "related-to": "l3-network", + "related-link": f"/aai/v16/network/l3-networks/l3-network/{instance_id}", + }, + ) + return { + "requestReferences": {"requestId": str(uuid4()), "instanceId": instance_id} + } diff --git a/mock-ves/Dockerfile b/mock-ves/Dockerfile new file mode 100644 index 0000000..32c530a --- /dev/null +++ b/mock-ves/Dockerfile @@ -0,0 +1,17 @@ +FROM python:3.8-alpine + +COPY . /app +WORKDIR /app + +# GCC for Alpine Linux in Docker +RUN apk add build-base + + +# Dependencies +RUN pip install -r requirements.txt + +EXPOSE 30417 + +ENTRYPOINT ["python"] + +CMD ["app/app.py"]
\ No newline at end of file diff --git a/mock-ves/README.md b/mock-ves/README.md new file mode 100644 index 0000000..b75ea97 --- /dev/null +++ b/mock-ves/README.md @@ -0,0 +1,26 @@ +# mock-ves +This is a simple ves mock created for ONAP integration tests. + +Build image +=========== +To build ves-mock image you can simply run command: + + ``` +docker build . -t mock-ves +``` + +Run image +========= + +To run ves-mock image use below command: +``` +docker run -d --net=host --name mock-ves mock-ves +``` + +Stop container +============== +To stop the container use: +``` +docker rm -f mock-ves +``` + diff --git a/mock-ves/app/app.py b/mock-ves/app/app.py new file mode 100644 index 0000000..acf1caa --- /dev/null +++ b/mock-ves/app/app.py @@ -0,0 +1,84 @@ +""" + Copyright 2023 Deutsche Telekom AG, Nokia, Orange + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +""" +import json +import requests +import logging as sys_logging +from flask import Flask, logging, request + +FAULT_TOPIC = "fault" +dmaap_url = "" + +app = Flask(__name__) +sys_logging.basicConfig(level=sys_logging.DEBUG) +logger = logging.create_logger(app) + + +@app.route("/set_dmaap_address", methods=['POST']) +def set_dmaap_address(): + logger.debug(request.json) + global dmaap_url + dmaap_url = get_dmaap_mock_url() + return {}, 202 + + +@app.route("/eventListener/<version>", methods=['POST']) +def event_sec_fault_output(version): + logger.debug(request.json) + event = json.dumps(request.json["event"]) \ + .replace('\n', ' ') \ + .__add__("\n") + send_event_to_dmaap(dmaap_url, event, FAULT_TOPIC) + return handle_new_event(version) + + +@app.route("/eventListener/<version>/eventBatch", methods=['POST']) +def event_sec_fault_output_batch(version): + logger.debug(request.json) + dmaap_mock_url = dmaap_url + event = prepare_event_list_for_dmaap() + send_event_to_dmaap(dmaap_mock_url, event, FAULT_TOPIC) + return handle_new_event(version) + + +def send_event_to_dmaap(dmaap_mock_url, event, topic): + byte_event = change_from_str_to_byte_array(event) + requests.post("{}/events/{}".format(dmaap_mock_url, topic), data=byte_event) + + +def handle_new_event(version): + return {}, 202 + + +def change_from_str_to_byte_array(event): + b = bytearray() + b.extend(event.encode()) + return b + + +def prepare_event_list_for_dmaap(): + event_list = [] + for event in request.json["eventList"]: + event_list.append(json.dumps(event).replace('\n', ' ')) + event = "\n".join(event_list).__add__("\n") + return event + + +def get_dmaap_mock_url(): + return request.json["DMAAP_MOCK"] + + +if __name__ == "__main__": + app.run(host='0.0.0.0', port=30417, debug=True) diff --git a/mock-ves/cleanup-dev.sh b/mock-ves/cleanup-dev.sh new file mode 100644 index 0000000..0cf27b3 --- /dev/null +++ b/mock-ves/cleanup-dev.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +export APP_NAME=mock-ves + +docker rm -f ${APP_NAME} +docker rmi ${APP_NAME} + diff --git a/mock-ves/requirements.txt b/mock-ves/requirements.txt new file mode 100644 index 0000000..07da031 --- /dev/null +++ b/mock-ves/requirements.txt @@ -0,0 +1,2 @@ +Flask-RESTful==0.3.8 +requests[socks]==2.24.0
\ No newline at end of file diff --git a/mock-ves/run-dev.sh b/mock-ves/run-dev.sh new file mode 100644 index 0000000..5b50d95 --- /dev/null +++ b/mock-ves/run-dev.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +export APP_NAME=mock-ves +export DOCKER_PORT=30417 +export APP_PORT=30417 + +docker build . -t $APP_NAME +docker run -p $DOCKER_PORT:$APP_PORT --name $APP_NAME $APP_NAME |