diff options
author | Michal Chabiera <michal.chabiera@orange.com> | 2020-12-07 18:18:27 +0100 |
---|---|---|
committer | Morgan Richomme <morgan.richomme@orange.com> | 2020-12-07 21:27:43 +0000 |
commit | 303fe3b4343838ae4c7b7a2511cb065f3abe7699 (patch) | |
tree | b440bd4803ec5d8a19d3d4398573701456c5621b | |
parent | 28107b9b3116d05eead52f9b88bfa385f4762f0a (diff) |
Automation scripts for vFW_CNF_CDS usecase
Automation scripts for vFW_CNF_CDS usecase
Issue-ID: INT-1658
Signed-off-by: Michal Chabiera <michal.chabiera@orange.com>
Change-Id: I32ba9afe0ca95c5db66c6789756508a76a13f948
(cherry picked from commit 6a7d598d08aa0e145a5aff71c4ed6deafb098e39)
-rw-r--r-- | heat/vFW_CNF_CDS/README.txt | 3 | ||||
-rwxr-xr-x | heat/vFW_CNF_CDS/automation/Pipfile | 13 | ||||
-rwxr-xr-x | heat/vFW_CNF_CDS/automation/README.md | 18 | ||||
-rwxr-xr-x | heat/vFW_CNF_CDS/automation/__init__.py | 20 | ||||
-rw-r--r-- | heat/vFW_CNF_CDS/automation/artifacts/cluster_kubeconfig | 0 | ||||
-rw-r--r-- | heat/vFW_CNF_CDS/automation/artifacts/onap_kubeconfig | 0 | ||||
-rwxr-xr-x | heat/vFW_CNF_CDS/automation/config.py | 72 | ||||
-rwxr-xr-x | heat/vFW_CNF_CDS/automation/crds/crd1 | 117 | ||||
-rwxr-xr-x | heat/vFW_CNF_CDS/automation/crds/crd2 | 34 | ||||
-rwxr-xr-x | heat/vFW_CNF_CDS/automation/create_k8s_region.py | 127 | ||||
-rwxr-xr-x | heat/vFW_CNF_CDS/automation/delete.py | 61 | ||||
-rwxr-xr-x | heat/vFW_CNF_CDS/automation/instantiate.py | 209 | ||||
-rwxr-xr-x | heat/vFW_CNF_CDS/automation/k8s_client.py | 59 | ||||
-rwxr-xr-x | heat/vFW_CNF_CDS/automation/onboard.py | 98 | ||||
-rwxr-xr-x | heat/vFW_CNF_CDS/automation/so_db_adapter.py | 92 | ||||
-rwxr-xr-x | heat/vFW_CNF_CDS/automation/update_connectivity_info.py | 49 | ||||
-rw-r--r-- | heat/vFW_CNF_CDS/templates/Makefile | 2 |
17 files changed, 973 insertions, 1 deletions
diff --git a/heat/vFW_CNF_CDS/README.txt b/heat/vFW_CNF_CDS/README.txt index 651d9598..d1130908 100644 --- a/heat/vFW_CNF_CDS/README.txt +++ b/heat/vFW_CNF_CDS/README.txt @@ -6,7 +6,8 @@ Demo directory structure: |- /helm #Directory containing helm charts that need to be packaged and attached to VSP package \- /cba #Directory containing CBA content to be included to csar package. It is prepared for /examples/ #Directory with context-specific overrides over general resources +/automation/ #Directory with automation scripts. For more details read README file inside. -Note: Scripts generate two VSP packages, one in Frankfurt format with helm associated with dummy heat templates and second +Note: Makefile script generates two VSP packages, one in Frankfurt format with helm associated with dummy heat templates and second with native Helm VSP format where helm packages are standalone. CBA folder contains CBA Definition for native VSP format but make is coverting the definition into Frankfurt format for Frankfurt VSP. Frankfurt VSP is still supported in Guilin. 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 --- /dev/null +++ b/heat/vFW_CNF_CDS/automation/artifacts/cluster_kubeconfig 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 --- /dev/null +++ b/heat/vFW_CNF_CDS/automation/artifacts/onap_kubeconfig 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 ") diff --git a/heat/vFW_CNF_CDS/templates/Makefile b/heat/vFW_CNF_CDS/templates/Makefile index 7ca4ac04..3f0a39a6 100644 --- a/heat/vFW_CNF_CDS/templates/Makefile +++ b/heat/vFW_CNF_CDS/templates/Makefile @@ -13,6 +13,8 @@ all: package/ helm base cba cd package_native/ && zip -r $(NATIVE_ONBOARDING_PACKAGE_NAME).zip . mv package_dummy/$(DUMMY_ONBOARDING_PACKAGE_NAME).zip . mv package_native/$(NATIVE_ONBOARDING_PACKAGE_NAME).zip . + cp $(DUMMY_ONBOARDING_PACKAGE_NAME).zip ../automation/vsp/ + cp $(NATIVE_ONBOARDING_PACKAGE_NAME).zip ../automation/vsp/ helm: package/ make -C helm |