From 303fe3b4343838ae4c7b7a2511cb065f3abe7699 Mon Sep 17 00:00:00 2001 From: Michal Chabiera Date: Mon, 7 Dec 2020 18:18:27 +0100 Subject: Automation scripts for vFW_CNF_CDS usecase Automation scripts for vFW_CNF_CDS usecase Issue-ID: INT-1658 Signed-off-by: Michal Chabiera Change-Id: I32ba9afe0ca95c5db66c6789756508a76a13f948 (cherry picked from commit 6a7d598d08aa0e145a5aff71c4ed6deafb098e39) --- heat/vFW_CNF_CDS/automation/Pipfile | 13 ++ heat/vFW_CNF_CDS/automation/README.md | 18 ++ heat/vFW_CNF_CDS/automation/__init__.py | 20 ++ .../automation/artifacts/cluster_kubeconfig | 0 .../automation/artifacts/onap_kubeconfig | 0 heat/vFW_CNF_CDS/automation/config.py | 72 +++++++ heat/vFW_CNF_CDS/automation/crds/crd1 | 117 ++++++++++++ heat/vFW_CNF_CDS/automation/crds/crd2 | 34 ++++ heat/vFW_CNF_CDS/automation/create_k8s_region.py | 127 +++++++++++++ heat/vFW_CNF_CDS/automation/delete.py | 61 ++++++ heat/vFW_CNF_CDS/automation/instantiate.py | 209 +++++++++++++++++++++ heat/vFW_CNF_CDS/automation/k8s_client.py | 59 ++++++ heat/vFW_CNF_CDS/automation/onboard.py | 98 ++++++++++ heat/vFW_CNF_CDS/automation/so_db_adapter.py | 92 +++++++++ .../automation/update_connectivity_info.py | 49 +++++ 15 files changed, 969 insertions(+) create mode 100755 heat/vFW_CNF_CDS/automation/Pipfile create mode 100755 heat/vFW_CNF_CDS/automation/README.md create mode 100755 heat/vFW_CNF_CDS/automation/__init__.py create mode 100644 heat/vFW_CNF_CDS/automation/artifacts/cluster_kubeconfig create mode 100644 heat/vFW_CNF_CDS/automation/artifacts/onap_kubeconfig create mode 100755 heat/vFW_CNF_CDS/automation/config.py create mode 100755 heat/vFW_CNF_CDS/automation/crds/crd1 create mode 100755 heat/vFW_CNF_CDS/automation/crds/crd2 create mode 100755 heat/vFW_CNF_CDS/automation/create_k8s_region.py create mode 100755 heat/vFW_CNF_CDS/automation/delete.py create mode 100755 heat/vFW_CNF_CDS/automation/instantiate.py create mode 100755 heat/vFW_CNF_CDS/automation/k8s_client.py create mode 100755 heat/vFW_CNF_CDS/automation/onboard.py create mode 100755 heat/vFW_CNF_CDS/automation/so_db_adapter.py create mode 100755 heat/vFW_CNF_CDS/automation/update_connectivity_info.py (limited to 'heat/vFW_CNF_CDS/automation') diff --git a/heat/vFW_CNF_CDS/automation/Pipfile b/heat/vFW_CNF_CDS/automation/Pipfile new file mode 100755 index 00000000..7c21af18 --- /dev/null +++ b/heat/vFW_CNF_CDS/automation/Pipfile @@ -0,0 +1,13 @@ +[[source]] +name = "pypi" +url = "https://pypi.org/simple" +verify_ssl = true + +[dev-packages] + +[packages] +onapsdk = "<=7.3.0" +kubernetes = "<=12.0.1" + +[requires] +python_version = "3.8" diff --git a/heat/vFW_CNF_CDS/automation/README.md b/heat/vFW_CNF_CDS/automation/README.md new file mode 100755 index 00000000..bbc9cdeb --- /dev/null +++ b/heat/vFW_CNF_CDS/automation/README.md @@ -0,0 +1,18 @@ +# vFW_CNF_CDS use case automation + +1. Install required packages with pipenv `pipenv install` +2. Run virtual environment `pipenv shell --fancy` +3. Add kubeconfig files, one for ONAP cluster, and one for k8s cluster that will host vFW: + - `artifacts/cluster_kubeconfig` + - `artifacts/onap_kubeconfig` +4. Prepare onboarding packages `cd ../templates/ && make && cd ../automation/` +5. Modify `config.py`: + - NATIVE - enables native helm orchestration path in SO + - CLOUD_REGION + - GLOBAL_CUSTOMER_ID + - VENDOR + - SERVICENAME + - CUSTOMER_RESOURCE_DEFINITIONS - add list of CRDs to be installed on non KUD k8s cluster +6. Run script `python create_k8s_region.py` in order to create **k8s cloud region** +7. Onboard **vFW** `python onboard.py` +8. Instantiate **vFW** `python instantiate.py` diff --git a/heat/vFW_CNF_CDS/automation/__init__.py b/heat/vFW_CNF_CDS/automation/__init__.py new file mode 100755 index 00000000..c9ad83e5 --- /dev/null +++ b/heat/vFW_CNF_CDS/automation/__init__.py @@ -0,0 +1,20 @@ +# ============LICENSE_START======================================================= +# Copyright (C) 2020 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. +# +# ============LICENSE_END========================================================= + +from .config import Config +from .so_db_adapter import SoDBAdapter +from .k8s_client import K8sClient diff --git a/heat/vFW_CNF_CDS/automation/artifacts/cluster_kubeconfig b/heat/vFW_CNF_CDS/automation/artifacts/cluster_kubeconfig new file mode 100644 index 00000000..e69de29b diff --git a/heat/vFW_CNF_CDS/automation/artifacts/onap_kubeconfig b/heat/vFW_CNF_CDS/automation/artifacts/onap_kubeconfig new file mode 100644 index 00000000..e69de29b diff --git a/heat/vFW_CNF_CDS/automation/config.py b/heat/vFW_CNF_CDS/automation/config.py new file mode 100755 index 00000000..f5d39c16 --- /dev/null +++ b/heat/vFW_CNF_CDS/automation/config.py @@ -0,0 +1,72 @@ +# ============LICENSE_START======================================================= +# Copyright (C) 2020 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. +# +# ============LICENSE_END========================================================= + +class Config: + #### REGION DETAILS #### + NATIVE = False + COMPLEX_ID = "complex" + CLOUD_OWNER = "k8sCloudOwner" + CLOUD_REGION = "k8s-region-1" + AVAILABILITY_ZONE_NAME = "k8s-availability-zone" + HYPERVISOR_TYPE = "k8s" + TENANT_NAME = "k8s-tenant-1" + K8S_NAMESPACE = "vfirewall" + CUSTOMER_RESOURCE_DEFINITIONS = [] +# Uncomment, if you want to run on non KUD k8s cluster +# CUSTOMER_RESOURCE_DEFINITIONS = ["crds/crd1", +# "crds/crd2"] + + CLUSTER_KUBECONFIG_PATH = "artifacts/cluster_kubeconfig" + ONAP_KUBECONFIG_PATH = "artifacts/onap_kubeconfig" + + #### SERVICE DETAILS #### + GLOBAL_CUSTOMER_ID = "customer_cnf" + VSPFILE = "vsp/vfw_k8s_demo.zip" + if NATIVE: + VSPFILE = "vsp/native_vfw_k8s_demo.zip" + VENDOR = "vendor_cnf" + SERVICENAME = "vfw_k8s_demo_CNF" + VSPNAME = "VSP_" + SERVICENAME + VFNAME = "VF_" + SERVICENAME + SERVICE_INSTANCE_NAME = "INSTANCE_" + SERVICENAME + SDNC_ARTIFACT_NAME = "vnf" + VF_MODULE_PREFIX = "" + if NATIVE: + VF_MODULE_PREFIX = "helm_" + + VF_MODULE_LIST = {VF_MODULE_PREFIX + "base_template": + {"name": VF_MODULE_PREFIX + "base_template", + "k8s-rb-profile-name": "vfw-cnf-cds-base-profile", + "k8s-rb-profile-namespace": K8S_NAMESPACE}, + VF_MODULE_PREFIX + "vfw": + {"name": VF_MODULE_PREFIX + "vfw", + "k8s-rb-profile-name": "vfw-cnf-cds-base-profile", + "k8s-rb-profile-namespace": K8S_NAMESPACE}, + VF_MODULE_PREFIX + "vpkg": + {"name": VF_MODULE_PREFIX + "vpkg", + "k8s-rb-profile-name": "vfw-cnf-cds-base-profile", + "k8s-rb-profile-namespace": K8S_NAMESPACE}, + VF_MODULE_PREFIX + "vsn": + {"name": VF_MODULE_PREFIX + "vsn", + "k8s-rb-profile-name": "vfw-cnf-cds-base-profile", + "k8s-rb-profile-namespace": K8S_NAMESPACE}} + + ######## DEFAULT VALUES ######## + OWNING_ENTITY = "OE-Demonstration" + PROJECT = "Project-Demonstration" + PLATFORM = "test" + LINE_OF_BUSINESS = "LOB-Demonstration" diff --git a/heat/vFW_CNF_CDS/automation/crds/crd1 b/heat/vFW_CNF_CDS/automation/crds/crd1 new file mode 100755 index 00000000..793261e0 --- /dev/null +++ b/heat/vFW_CNF_CDS/automation/crds/crd1 @@ -0,0 +1,117 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: networks.k8s.plugin.opnfv.org +spec: + group: k8s.plugin.opnfv.org + names: + kind: Network + listKind: NetworkList + plural: networks + singular: network + scope: Namespaced + subresources: + status: {} + validation: + openAPIV3Schema: + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + properties: + cniType: + description: 'INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + Important: Run "operator-sdk generate k8s" to regenerate code after + modifying this file Add custom validation using kubebuilder tags: + https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html' + type: string + dns: + properties: + domain: + type: string + nameservers: + items: + type: string + type: array + options: + items: + type: string + type: array + search: + items: + type: string + type: array + type: object + ipv4Subnets: + items: + properties: + excludeIps: + type: string + gateway: + type: string + name: + type: string + subnet: + type: string + required: + - name + - subnet + type: object + type: array + ipv6Subnets: + items: + properties: + excludeIps: + type: string + gateway: + type: string + name: + type: string + subnet: + type: string + required: + - name + - subnet + type: object + type: array + routes: + items: + properties: + dst: + type: string + gw: + type: string + required: + - dst + type: object + type: array + required: + - cniType + - ipv4Subnets + type: object + status: + properties: + state: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "operator-sdk generate k8s" to regenerate + code after modifying this file Add custom validation using kubebuilder + tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html' + type: string + required: + - state + type: object + version: v1alpha1 + versions: + - name: v1alpha1 + served: true + storage: true diff --git a/heat/vFW_CNF_CDS/automation/crds/crd2 b/heat/vFW_CNF_CDS/automation/crds/crd2 new file mode 100755 index 00000000..dcf52577 --- /dev/null +++ b/heat/vFW_CNF_CDS/automation/crds/crd2 @@ -0,0 +1,34 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: network-attachment-definitions.k8s.cni.cncf.io +spec: + group: k8s.cni.cncf.io + scope: Namespaced + names: + plural: network-attachment-definitions + singular: network-attachment-definition + kind: NetworkAttachmentDefinition + shortNames: + - net-attach-def + validation: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + type: object + required: + - config + properties: + config: + type: string + version: v1 + versions: + - name: v1 + served: true + storage: true diff --git a/heat/vFW_CNF_CDS/automation/create_k8s_region.py b/heat/vFW_CNF_CDS/automation/create_k8s_region.py new file mode 100755 index 00000000..cfbec8cc --- /dev/null +++ b/heat/vFW_CNF_CDS/automation/create_k8s_region.py @@ -0,0 +1,127 @@ +# ============LICENSE_START======================================================= +# Copyright (C) 2020 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. +# +# ============LICENSE_END========================================================= + +import logging +import os +from uuid import uuid4 + +from config import Config +from k8s_client import K8sClient +from so_db_adapter import SoDBAdapter +from onapsdk.aai.business import Customer +from onapsdk.aai.cloud_infrastructure import Complex, CloudRegion +from onapsdk.msb.k8s import ConnectivityInfo + +logger = logging.getLogger("") +logger.setLevel(logging.DEBUG) +fh = logging.StreamHandler() +fh_formatter = logging.Formatter('%(asctime)s %(levelname)s %(lineno)d:%(filename)s(%(process)d) - %(message)s') +fh.setFormatter(fh_formatter) +logger.addHandler(fh) + +MYPATH = os.path.dirname(os.path.realpath(__file__)) + +#### Create complex if not exists #### +logger.info("******** Complex *******") +try: + complex = list(Complex.get_all(physical_location_id=Config.COMPLEX_ID))[0] + logger.info("Complex exists") +except IndexError: + logger.info("Complex does not exists") + complex = Complex.create(physical_location_id=Config.COMPLEX_ID, + name=Config.COMPLEX_ID, + physical_location_type="office", + street1="DummyStreet 1", + city="DummyCity", + postal_code="00-000", + country="DummyCountry", + region="DummyRegion") + logger.info("Complex created") + +#### Create cloud region if not exists #### +logger.info("******** Cloud Region *******") +try: + cloud_region = list(CloudRegion.get_all(cloud_owner=Config.CLOUD_OWNER, cloud_region_id=Config.CLOUD_REGION))[0] + logger.info("Cloud region exists") +except IndexError: + logger.info("Cloud region does not exists") + cloud_region = CloudRegion.create(cloud_owner=Config.CLOUD_OWNER, + cloud_region_id=Config.CLOUD_REGION, + cloud_type="k8s", + owner_defined_type="t1", + cloud_region_version="1.0", + complex_name=complex.physical_location_id, + cloud_zone="CloudZone", + sriov_automation="false", + orchestration_disabled=False, + in_maint=False) + logger.info("Cloud region created") + +logger.info("******** Cloud regiongion <-> Complex *******") +cloud_region.link_to_complex(complex) + +logger.info("******** Availability zone *******") +cloud_region.add_availability_zone(availability_zone_name=Config.AVAILABILITY_ZONE_NAME, + availability_zone_hypervisor_type=Config.HYPERVISOR_TYPE) + +logger.info("******** Tenant *******") +cloud_region.add_tenant(str(uuid4()), Config.TENANT_NAME) + +#### Update or create connectivity info #### +logger.info("******** Connectivity Info *******") +with open(os.path.join(MYPATH, Config.CLUSTER_KUBECONFIG_PATH), 'rb') as kubeconfig_file: + kubeconfig = kubeconfig_file.read() +try: + connectivity_info = ConnectivityInfo.get_connectivity_info_by_region_id(cloud_region_id=Config.CLOUD_REGION) + logger.info("Connectivity Info exists ") + logger.info("Delete Connectivity Info ") + connectivity_info.delete() + connectivity_info = ConnectivityInfo.create(cloud_region_id=Config.CLOUD_REGION, + cloud_owner=Config.CLOUD_OWNER, + kubeconfig=kubeconfig) + logger.info("Connectivity Info created ") +except: + logger.info("Connectivity Info does not exists ") + connectivity_info = ConnectivityInfo.create(cloud_region_id=Config.CLOUD_REGION, + cloud_owner=Config.CLOUD_OWNER, + kubeconfig=kubeconfig) + logger.info("Connectivity Info created ") + +#### Add Custom Resource Definitions #### +k8s_client = K8sClient(kubeconfig_path=Config.CLUSTER_KUBECONFIG_PATH) +for crd in Config.CUSTOMER_RESOURCE_DEFINITIONS: + k8s_client.create_custom_object(crd) + +#### Create customer if not exists #### +logger.info("******** Customer *******") +try: + customer = Customer.get_by_global_customer_id(Config.GLOBAL_CUSTOMER_ID) + logger.info("Customer exists") +except: + logger.info("Customer exists") + customer = Customer.create(Config.GLOBAL_CUSTOMER_ID, Config.GLOBAL_CUSTOMER_ID, "INFRA") + logger.info("Customer created") + +#### Add region to SO db #### +logger.info("******** SO Database *******") +so_db_adapter = SoDBAdapter(cloud_region_id=Config.CLOUD_REGION, + complex_id=Config.COMPLEX_ID, + onap_kubeconfig_path=Config.ONAP_KUBECONFIG_PATH) +is_region_in_so = so_db_adapter.check_region_in_db() + +if not is_region_in_so: + so_db_adapter.add_region_to_so_db() diff --git a/heat/vFW_CNF_CDS/automation/delete.py b/heat/vFW_CNF_CDS/automation/delete.py new file mode 100755 index 00000000..435d04ae --- /dev/null +++ b/heat/vFW_CNF_CDS/automation/delete.py @@ -0,0 +1,61 @@ +# ============LICENSE_START======================================================= +# Copyright (C) 2020 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. +# +# ============LICENSE_END========================================================= + +import logging + +from onapsdk.aai.business import Customer + +from config import Config + +logger = logging.getLogger("") +logger.setLevel(logging.DEBUG) +fh = logging.StreamHandler() +fh_formatter = logging.Formatter('%(asctime)s %(levelname)s %(lineno)d:%(filename)s(%(process)d) - %(message)s') +fh.setFormatter(fh_formatter) +logger.addHandler(fh) + +logger.info("******** Get Customer *******") +customer = None +try: + customer = Customer.get_by_global_customer_id(Config.GLOBAL_CUSTOMER_ID) +except: + logger.error("Customer not found") + exit(1) + +logger.info("******** Check Service Subscription *******") +service_subscription = None +for service_sub in customer.service_subscriptions: + if service_sub.service_type == Config.SERVICENAME: + logger.info("Service %s subscribed", Config.SERVICENAME) + service_subscription = service_sub + break +if not service_subscription: + logger.error("Service Subscription not found") + exit(1) + +logger.info("******** Get Service Instance details *******") +service_instance = None +for service in service_subscription.service_instances: + if service.instance_name == Config.SERVICE_INSTANCE_NAME: + service_instance = service + break +if not service_instance: + logger.error("Service Instance not found") + exit(1) + +logger.info("******** Delete Service %s *******", service_instance.instance_name) +service_deletion = service_instance.delete() diff --git a/heat/vFW_CNF_CDS/automation/instantiate.py b/heat/vFW_CNF_CDS/automation/instantiate.py new file mode 100755 index 00000000..0316f113 --- /dev/null +++ b/heat/vFW_CNF_CDS/automation/instantiate.py @@ -0,0 +1,209 @@ +# ============LICENSE_START======================================================= +# Copyright (C) 2020 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. +# +# ============LICENSE_END========================================================= + +import logging +import zipfile +from io import BytesIO +from uuid import uuid4 + +import oyaml as yaml + +from config import Config +from onapsdk.aai.cloud_infrastructure import ( + CloudRegion, +) +from onapsdk.aai.business import ( + Customer, + OwningEntity as AaiOwningEntity +) +from onapsdk.msb.k8s import Definition + +from onapsdk.so.instantiation import ( + ServiceInstantiation, + InstantiationParameter, VnfParameters, VfmoduleParameters) +from onapsdk.sdc.service import Service +from onapsdk.vid import LineOfBusiness, OwningEntity, Platform, Project + +logger = logging.getLogger("") +logger.setLevel(logging.DEBUG) +fh = logging.StreamHandler() +fh_formatter = logging.Formatter('%(asctime)s %(levelname)s %(lineno)d:%(filename)s(%(process)d) - %(message)s') +fh.setFormatter(fh_formatter) +logger.addHandler(fh) + +logger.info("*******************************") +logger.info("**** SERVICE INSTANTIATION ****") +logger.info("*******************************") + +logger.info("******** Create Customer *******") +customer = None +for found_customer in list(Customer.get_all()): + logger.debug("Customer %s found", found_customer.subscriber_name) + if found_customer.subscriber_name == Config.GLOBAL_CUSTOMER_ID: + logger.info("Customer %s found", found_customer.subscriber_name) + customer = found_customer + break +if not customer: + customer = Customer.create(Config.GLOBAL_CUSTOMER_ID, Config.GLOBAL_CUSTOMER_ID, "INFRA") + +logger.info("******** Find Service in SDC *******") +service = None +services = Service.get_all() +for found_service in services: + logger.debug("Service %s is found, distribution %s", found_service.name, found_service.distribution_status) + if found_service.name == Config.SERVICENAME: + logger.info("Found Service %s in SDC", found_service.name) + service = found_service + break + +if not service: + logger.error("Service %s not found in SDC", Config.SERVICENAME) + exit(1) + +logger.info("******** Check Service Subscription *******") +service_subscription = None +for service_sub in customer.service_subscriptions: + logger.debug("Service subscription %s is found", service_sub.service_type) + if service_sub.service_type == Config.SERVICENAME: + logger.info("Service %s subscribed", Config.SERVICENAME) + service_subscription = service_sub + break + +if not service_subscription: + logger.info("******** Subscribe Service *******") + customer.subscribe_service(service) + +logger.info("******** Get Tenant *******") +cloud_region = CloudRegion(cloud_owner=Config.CLOUD_OWNER, cloud_region_id=Config.CLOUD_REGION, + orchestration_disabled=True, in_maint=False) +tenant = None +for found_tenant in cloud_region.tenants: + logger.debug("Tenant %s found in %s_%s", found_tenant.name, cloud_region.cloud_owner, cloud_region.cloud_region_id) + if found_tenant.name == Config.TENANT_NAME: + logger.info("Found my Tenant %s", found_tenant.name) + tenant = found_tenant + break + +if not tenant: + logger.error("tenant %s not found", Config.TENANT_NAME) + exit(1) + +logger.info("******** Connect Service to Tenant *******") +service_subscription = None +for service_sub in customer.service_subscriptions: + logger.debug("Service subscription %s is found", service_sub.service_type) + if service_sub.service_type == Config.SERVICENAME: + logger.info("Service %s subscribed", Config.SERVICENAME) + service_subscription = service_sub + break + +if not service_subscription: + logger.error("Service subscription %s is not found", Config.SERVICENAME) + exit(1) + +service_subscription.link_to_cloud_region_and_tenant(cloud_region, tenant) + +logger.info("******** Add Business Objects (OE, P, Pl, LoB) in VID *******") +vid_owning_entity = OwningEntity.create(Config.OWNING_ENTITY) +vid_project = Project.create(Config.PROJECT) +vid_platform = Platform.create(Config.PLATFORM) +vid_line_of_business = LineOfBusiness.create(Config.LINE_OF_BUSINESS) + +logger.info("******** Add Owning Entity in AAI *******") +owning_entity = None +for oe in AaiOwningEntity.get_all(): + if oe.name == vid_owning_entity.name: + owning_entity = oe + break +if not owning_entity: + logger.info("******** Owning Entity not existing: create *******") + owning_entity = AaiOwningEntity.create(vid_owning_entity.name, str(uuid4())) + +logger.info("******** Delete old profiles ********") +for vnf in service.vnfs: + for vf_module in vnf.vf_modules: + definition = Definition.get_definition_by_name_version(vf_module.metadata["vfModuleModelInvariantUUID"], + vf_module.metadata["vfModuleModelUUID"]) + vf_module_label = vf_module.properties["vf_module_label"] + if vf_module_label == "base_template_dummy_ignore": + continue + profile_name = Config.VF_MODULE_LIST[vf_module_label]["k8s-rb-profile-name"] + try: + profile = definition.get_profile_by_name(profile_name) + if profile.namespace != Config.VF_MODULE_LIST[vf_module_label]["k8s-rb-profile-namespace"]: + profile.delete() + logger.info("Profile: " + profile_name + " for " + vf_module.name + " deleted") + else: + logger.info("No need to delete Profile " + profile_name + + " for " + vf_module.name + ". Namespace is fine") + except ValueError: + logger.info("Profile: " + profile_name + " for " + vf_module.name + " not found") + +# Read SDNC MODEL NAME and VERSION from CBA.zip +logger.info("*******************************") +logger.info("Retrieving SDNC MODEL NAME and VERSION") +logger.info("*******************************") +with zipfile.ZipFile(Config.VSPFILE, 'r') as package: + cba_io = BytesIO(package.read("CBA.zip")) + with zipfile.ZipFile(cba_io) as cba: + with cba.open('TOSCA-Metadata/TOSCA.meta') as meta_file: + tosca_meta = yaml.load(meta_file, Loader=yaml.FullLoader) + SDNC_MODEL_NAME = tosca_meta.get("Template-Name") + SDNC_MODEL_VERSION = tosca_meta.get("Template-Version") + +logger.info("******** Instantiate Service *******") +service_instance = None +service_instantiation = None +for se in service_subscription.service_instances: + if se.instance_name == Config.SERVICE_INSTANCE_NAME: + service_instance = se + break +if not service_instance: + logger.info("******** Service Instance not existing: Instantiate *******") + # Instantiate service + vfmodules_list = Config.VF_MODULE_LIST + + vnf_param = [ + InstantiationParameter(name="sdnc_model_name", value=SDNC_MODEL_NAME), + InstantiationParameter(name="sdnc_model_version", value=SDNC_MODEL_VERSION), + InstantiationParameter(name="sdnc_artifact_name", value=Config.SDNC_ARTIFACT_NAME)] + + vfmodules_param = [] + for vfmodule in vfmodules_list: + params = [ + InstantiationParameter(name="k8s-rb-profile-name", value=vfmodules_list[vfmodule]["k8s-rb-profile-name"]), + InstantiationParameter(name="k8s-rb-profile-namespace", value=vfmodules_list[vfmodule]["k8s-rb-profile-namespace"]), + InstantiationParameter(name="sdnc_model_name", value=SDNC_MODEL_NAME), + InstantiationParameter(name="sdnc_model_version", value=SDNC_MODEL_VERSION), + InstantiationParameter(name="vf_module_label", value=vfmodules_list[vfmodule]["name"])] + + vfmodules_param.append(VfmoduleParameters(vfmodules_list[vfmodule]["name"], params)) + + vnf_params = VnfParameters(name=Config.VFNAME, vnf_parameters=vnf_param, vfmodule_parameters=vfmodules_param) + + service_instantiation = ServiceInstantiation.instantiate_macro( + sdc_service=service, + cloud_region=cloud_region, + tenant=tenant, + customer=customer, + owning_entity=owning_entity, + project=vid_project, + line_of_business=vid_line_of_business, + platform=vid_platform, + service_instance_name=Config.SERVICE_INSTANCE_NAME, + vnf_parameters=[vnf_params] + ) diff --git a/heat/vFW_CNF_CDS/automation/k8s_client.py b/heat/vFW_CNF_CDS/automation/k8s_client.py new file mode 100755 index 00000000..7b0b634f --- /dev/null +++ b/heat/vFW_CNF_CDS/automation/k8s_client.py @@ -0,0 +1,59 @@ +# ============LICENSE_START======================================================= +# Copyright (C) 2020 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. +# +# ============LICENSE_END========================================================= + +import os +from pprint import pprint + +import oyaml as yaml +from kubernetes import config, client +from kubernetes.client import OpenApiException + + +class K8sClient: + def __init__(self, kubeconfig_path): + self.mypath = os.path.dirname(os.path.realpath(__file__)) + config.load_kube_config(config_file=os.path.join(self.mypath, kubeconfig_path)) + self.api_instance = client.CustomObjectsApi() + + def read_custom_object_file(self, file_path): + with open(file_path) as crd_file: + crd_body = yaml.load(crd_file, Loader=yaml.FullLoader) + return crd_body + + def get_custom_object_details(self, crd_body): + group = crd_body["apiVersion"].split("/")[0] + version = crd_body["apiVersion"].split("/")[1] + plural = crd_body["kind"].lower() + "s" + #name = crd_body["metadata"]["name"] + + return group, version, plural #, name + + def create_custom_object(self, file_path): + crd_body = self.read_custom_object_file(file_path) + #group, version, plural, name = self.get_custom_object_details(crd_body) + group, version, plural = self.get_custom_object_details(crd_body) + api_response = None + try: + api_response = self.api_instance.create_cluster_custom_object(group=group, + version=version, + plural=plural, + body=crd_body, + pretty="true") + except OpenApiException as error: + print(str(error.status) + " " + error.reason) + pprint(error.body) + return api_response diff --git a/heat/vFW_CNF_CDS/automation/onboard.py b/heat/vFW_CNF_CDS/automation/onboard.py new file mode 100755 index 00000000..c97b3510 --- /dev/null +++ b/heat/vFW_CNF_CDS/automation/onboard.py @@ -0,0 +1,98 @@ +# ============LICENSE_START======================================================= +# Copyright (C) 2020 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. +# +# ============LICENSE_END========================================================= + +import logging + +import time +import zipfile +from io import BytesIO + +import oyaml as yaml + +from config import Config +from onapsdk.sdc.properties import Property + +from onapsdk.sdc.vendor import Vendor +from onapsdk.sdc.vsp import Vsp +from onapsdk.sdc.vf import Vf +from onapsdk.sdc.service import Service, ServiceInstantiationType + +import os + +logger = logging.getLogger("") +logger.setLevel(logging.DEBUG) +fh = logging.StreamHandler() +fh_formatter = logging.Formatter('%(asctime)s %(levelname)s %(lineno)d:%(filename)s(%(process)d) - %(message)s') +fh.setFormatter(fh_formatter) +logger.addHandler(fh) + +# Read SDNC MODEL NAME and VERSION from CBA.zip +logger.info("*******************************") +logger.info("Retrieving SDNC MODEL NAME and VERSION") +logger.info("*******************************") +with zipfile.ZipFile(Config.VSPFILE, 'r') as package: + cba_io = BytesIO(package.read("CBA.zip")) + with zipfile.ZipFile(cba_io) as cba: + with cba.open('TOSCA-Metadata/TOSCA.meta') as meta_file: + tosca_meta = yaml.load(meta_file, Loader=yaml.FullLoader) + SDNC_MODEL_NAME = tosca_meta.get("Template-Name") + SDNC_MODEL_VERSION = tosca_meta.get("Template-Version") + +logger.info("*******************************") +logger.info("******** SERVICE DESIGN *******") +logger.info("*******************************") + +logger.info("******** Onboard Vendor *******") +vendor = Vendor(name=Config.VENDOR) +vendor.onboard() + +logger.info("******** Onboard VSP *******") +mypath = os.path.dirname(os.path.realpath(__file__)) +myvspfile = os.path.join(mypath, Config.VSPFILE) +vsp = Vsp(name=Config.VSPNAME, vendor=vendor, package=open(myvspfile, 'rb')) +vsp.onboard() + +logger.info("******** Onboard VF *******") +vf = Vf(name=Config.VFNAME, properties=[ + Property(name="sdnc_model_name", property_type="string", value=SDNC_MODEL_NAME), + Property(name="sdnc_model_version", property_type="string", value=SDNC_MODEL_VERSION), + Property(name="sdnc_artifact_name", property_type="string", value=Config.SDNC_ARTIFACT_NAME) +] + ) +vf.vsp = vsp +vf.onboard() + +logger.info("******** Onboard Service *******") +svc = Service(name=Config.SERVICENAME, resources=[vf], instantiation_type=ServiceInstantiationType.MACRO) +svc.onboard() + +logger.info("******** Check Service Distribution *******") +distribution_completed = False +nb_try = 0 +nb_try_max = 10 +while distribution_completed is False and nb_try < nb_try_max: + distribution_completed = svc.distributed + if distribution_completed is True: + logger.info("Service Distribution for %s is successfully finished", svc.name) + break + logger.info("Service Distribution for %s ongoing, Wait for 60 s", svc.name) + time.sleep(60) + nb_try += 1 + +if distribution_completed is False: + logger.error("Service Distribution for %s failed !!", svc.name) + exit(1) diff --git a/heat/vFW_CNF_CDS/automation/so_db_adapter.py b/heat/vFW_CNF_CDS/automation/so_db_adapter.py new file mode 100755 index 00000000..a829fbda --- /dev/null +++ b/heat/vFW_CNF_CDS/automation/so_db_adapter.py @@ -0,0 +1,92 @@ +# ============LICENSE_START======================================================= +# Copyright (C) 2020 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. +# +# ============LICENSE_END========================================================= + +import base64 +import os + +from kubernetes import config, client +from kubernetes.stream import stream + + +class SoDBAdapter: + + def __init__(self, cloud_region_id, complex_id, onap_kubeconfig_path): + self.CLOUD_REGION_ID = cloud_region_id + self.COMPLEX_ID = complex_id + self.ONAP_KUBECONFIG_PATH = onap_kubeconfig_path + self.MYPATH = os.path.dirname(os.path.realpath(__file__)) + + config.load_kube_config(config_file=os.path.join(self.MYPATH, self.ONAP_KUBECONFIG_PATH)) + self.api_instance = client.CoreV1Api() + self.pod_name = self.get_mariadb_pod_name() + self.password = self.get_mariadb_root_username_password() + + def get_mariadb_pod_name(self): + pods = self.api_instance.list_namespaced_pod(namespace="onap") + for pod in pods.items: + if pod.metadata.name.find("mariadb-galera-0") != -1: + return pod.metadata.name + + def get_mariadb_root_username_password(self): + secrets = self.api_instance.list_namespaced_secret(namespace="onap") + for secret in secrets.items: + if secret.metadata.name.find("mariadb-galera-db-root-password") != -1: + base64_password = secret.data["password"] + base64_bytes = base64_password.encode('ascii') + password_bytes = base64.b64decode(base64_bytes) + + return password_bytes.decode('ascii') + + def run_exec_request(self, exec_command): + response = stream(self.api_instance.connect_get_namespaced_pod_exec, + name=self.pod_name, + # container="container-name", + namespace="onap", + command=exec_command, + stdin=False, + tty=False, + stderr=True, + stdout=True) + return response + + def check_region_in_db(self): + exec_command = [ + "/bin/sh", + "-c", + f"mysql -uroot -p{self.password} catalogdb -e 'SELECT * FROM cloud_sites;'"] + response = self.run_exec_request(exec_command) + + is_region_found = False + for line in response.split("\n"): + if line.split("\t")[0] == self.CLOUD_REGION_ID: + print(line) + is_region_found = True + return is_region_found + return is_region_found + + def add_region_to_so_db(self): + exec_command = [ + "/bin/sh", + "-c", + f"mysql -uroot -p{self.password} catalogdb -e " + f"'insert into cloud_sites(ID, REGION_ID, IDENTITY_SERVICE_ID, CLOUD_VERSION, CLLI, ORCHESTRATOR ) " + f"values (\"{self.CLOUD_REGION_ID}\", \"{self.CLOUD_REGION_ID}\", \"DEFAULT_KEYSTONE\", \"2.5\", " + f"\"{self.COMPLEX_ID}\", \"multicloud\");'"] + + response = self.run_exec_request(exec_command) + + return response diff --git a/heat/vFW_CNF_CDS/automation/update_connectivity_info.py b/heat/vFW_CNF_CDS/automation/update_connectivity_info.py new file mode 100755 index 00000000..2a341f21 --- /dev/null +++ b/heat/vFW_CNF_CDS/automation/update_connectivity_info.py @@ -0,0 +1,49 @@ +# ============LICENSE_START======================================================= +# Copyright (C) 2020 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. +# +# ============LICENSE_END========================================================= + +import logging +import os + +from config import Config +from onapsdk.msb.k8s import ConnectivityInfo + +logger = logging.getLogger("") +logger.setLevel(logging.DEBUG) +fh = logging.StreamHandler() +fh_formatter = logging.Formatter('%(asctime)s %(levelname)s %(lineno)d:%(filename)s(%(process)d) - %(message)s') +fh.setFormatter(fh_formatter) +logger.addHandler(fh) + +MYPATH = os.path.dirname(os.path.realpath(__file__)) + +logger.info("******** Connectivity Info *******") +with open(os.path.join(MYPATH, Config.CLUSTER_KUBECONFIG_PATH), 'rb') as kubeconfig_file: + kubeconfig = kubeconfig_file.read() +try: + connectivity_info = ConnectivityInfo.get_connectivity_info_by_region_id(cloud_region_id=Config.CLOUD_REGION) + logger.info("Connectivity Info exists ") + logger.info("Delete Connectivity Info exists ") + connectivity_info.delete() + connectivity_info = ConnectivityInfo.create(cloud_region_id=Config.CLOUD_REGION, + cloud_owner=Config.CLOUD_OWNER, + kubeconfig=kubeconfig) +except: + logger.info("Connectivity Info does not exists ") + connectivity_info = ConnectivityInfo.create(cloud_region_id=Config.CLOUD_REGION, + cloud_owner=Config.CLOUD_OWNER, + kubeconfig=kubeconfig) + logger.info("Connectivity Info created ") -- cgit 1.2.3-korg