aboutsummaryrefslogtreecommitdiffstats
path: root/tutorials/ApacheCNF/automation
diff options
context:
space:
mode:
Diffstat (limited to 'tutorials/ApacheCNF/automation')
-rw-r--r--tutorials/ApacheCNF/automation/Pipfile14
-rw-r--r--tutorials/ApacheCNF/automation/README.md32
-rw-r--r--tutorials/ApacheCNF/automation/__init__.py19
-rw-r--r--tutorials/ApacheCNF/automation/artifacts/cluster_kubeconfig0
-rw-r--r--tutorials/ApacheCNF/automation/config.py142
-rw-r--r--tutorials/ApacheCNF/automation/create_cloud_regions.py202
-rw-r--r--tutorials/ApacheCNF/automation/delete.py131
-rw-r--r--tutorials/ApacheCNF/automation/healthcheck.py92
-rw-r--r--tutorials/ApacheCNF/automation/instantiate.py474
-rw-r--r--tutorials/ApacheCNF/automation/k8s_client.py59
-rw-r--r--tutorials/ApacheCNF/automation/onap_settings.py73
-rw-r--r--tutorials/ApacheCNF/automation/onboard.py213
-rw-r--r--tutorials/ApacheCNF/automation/update_cba.py57
-rw-r--r--tutorials/ApacheCNF/automation/update_connectivity_info.py40
-rw-r--r--tutorials/ApacheCNF/automation/vsp/.gitkeep0
-rw-r--r--tutorials/ApacheCNF/automation/vsp/.keep0
16 files changed, 1548 insertions, 0 deletions
diff --git a/tutorials/ApacheCNF/automation/Pipfile b/tutorials/ApacheCNF/automation/Pipfile
new file mode 100644
index 00000000..a8e79269
--- /dev/null
+++ b/tutorials/ApacheCNF/automation/Pipfile
@@ -0,0 +1,14 @@
+[[source]]
+name = "pypi"
+url = "https://pypi.org/simple"
+verify_ssl = true
+
+[dev-packages]
+
+[packages]
+onapsdk = "==9.3.0"
+markupsafe = "==2.0.1"
+kubernetes = "*"
+
+[requires]
+python_version = "3.8"
diff --git a/tutorials/ApacheCNF/automation/README.md b/tutorials/ApacheCNF/automation/README.md
new file mode 100644
index 00000000..5b2b80b3
--- /dev/null
+++ b/tutorials/ApacheCNF/automation/README.md
@@ -0,0 +1,32 @@
+# CNF automation
+
+0. Make sure you have python 3.8.x installed and default interpreter, what is required by onap-pythonsdk
+1. Install required packages with pipenv `pipenv install`
+2. Run virtual environment `pipenv shell`. In case of problems you maye try also `--fancy` option
+
+ **In case of problems with pipenv:** `venv` can be used as well. In that case, please install all required python packages in correct version according the list in `Pipfile`
+3. Add kubeconfig file for k8s cluster that will host your CNF. Apache can be deployed on any standard cluster but with defult values it need LoadBlanacer
+ - `artifacts/kubeconfig`
+4. Prepare onboarding packages `cd ../templates/ && make && cd ../automation/`
+5. Modify `service_config.yaml`. Please note that the configuration file has yaml syntax but also is jinja templated
+ and values defined in the configuration file may be used also in the jinja templaring process. Templating is an iterative process unless all the values are
+ not resolved. Please not that in most cases you don't have to modify this file at all, despite the configuration of your k8s cluster.
+ We recommend to modify only values from the 'UserParams' section:
+ - cnf_name - name of CNF
+ - k8s_namespace - k8s namespace to use for deployment of CNF
+ - k8s_version - version of the k8s cluster (important for proper helm templating)
+ - k8s_region - name of the k8s region that we want to create in ONAP
+ - release_name - name of the rleease of the helm application (user for naming of k8s resources)
+ - profile_source - source of the k8s profile with values - in our case it may be used to change from LoandBalanser to NodePort service type
+ - skip_day_2 - it defined the SKIP_POST_INSTANTIATION flag in SDC models. The value is used in the SDC service model name
+6. Verify service_config.yaml by running `python config.py`
+7. __Important:__ Before running python scripts, some settings for `onapsdk` with information about ONAP endpoints (and socks) have to be exported.
+ All settings for ONAP instance are located in `automation/onap_settings.py` file. To export that settings please run command inside `pipenv` or `venv` shell
+ ```shell
+ (automation) ubuntu@onap:~/automation$ export ONAP_PYTHON_SDK_SETTINGS="onap_settings"
+ ```
+8. Run script `python create_cloud_regions.py` in order to create **k8s or openstack cloud region**
+9. Onboard CNF `python onboard.py`
+10. Instantiate CNF `python instantiate.py`
+11. Once test is done, CNF service instance can be deleted with `python delete.py` command
+
diff --git a/tutorials/ApacheCNF/automation/__init__.py b/tutorials/ApacheCNF/automation/__init__.py
new file mode 100644
index 00000000..9525040e
--- /dev/null
+++ b/tutorials/ApacheCNF/automation/__init__.py
@@ -0,0 +1,19 @@
+# ============LICENSE_START=======================================================
+# Copyright (C) 2021 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 .k8s_client import K8sClient
diff --git a/tutorials/ApacheCNF/automation/artifacts/cluster_kubeconfig b/tutorials/ApacheCNF/automation/artifacts/cluster_kubeconfig
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tutorials/ApacheCNF/automation/artifacts/cluster_kubeconfig
diff --git a/tutorials/ApacheCNF/automation/config.py b/tutorials/ApacheCNF/automation/config.py
new file mode 100644
index 00000000..24983671
--- /dev/null
+++ b/tutorials/ApacheCNF/automation/config.py
@@ -0,0 +1,142 @@
+# ============LICENSE_START=======================================================
+# Copyright (C) 2021 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 typing import Dict, Union, List
+import oyaml as yaml
+import os
+from jinja2 import Template
+
+
+class VariablesDict:
+ env_variable = dict(os.environ)
+
+
+class Config:
+ def __init__(self, filename: str = "service_config.yaml", env_dict=None, print_final_file=False):
+ if env_dict is None:
+ env_dict = {}
+ self.filepath = os.path.join(os.path.dirname(os.path.dirname(
+ os.path.realpath(__file__))), filename)
+ self.content_env: Dict = env_dict
+ self.service_instance: Union[None, Dict] = None
+ self.service_model: Union[None, Dict] = None
+ self.user_params: Union[None, Dict] = None
+ self.cloud_regions: Union[None, List] = None
+ self.so_input: Union[None, Dict] = None
+ self.render(print_final_file)
+ self.so_input = self.create_so_input()
+
+ def _load_file(self) -> dict:
+ with open(self.filepath) as file:
+ file_content = yaml.safe_load(file)
+ return file_content
+
+ @staticmethod
+ def templating(rend_dict: dict, render_keys: dict):
+ for k, v in rend_dict.items():
+ if isinstance(v, str):
+ t = Template(v)
+ rend_dict[k] = t.render(**render_keys).strip()
+ elif isinstance(v, dict):
+ Config.templating(rend_dict=v, render_keys=render_keys)
+ elif isinstance(v, list):
+ for i in v:
+ Config.templating(rend_dict=i, render_keys=render_keys)
+ else:
+ pass
+ return rend_dict
+
+ def render(self, print_final_file=False):
+ raw_file = self._load_file()
+ config_dict = self._render(templated_file=raw_file)
+
+ while not self._completed(templated_file=config_dict):
+ config_dict = self._render(templated_file=config_dict)
+
+ self.__dict__.update(**config_dict)
+ if print_final_file:
+ print(yaml.dump(config_dict, sort_keys=False))
+
+ def _render(self, templated_file: dict) -> dict:
+ config_dict = self.templating(
+ rend_dict=templated_file,
+ render_keys={**self.content_env, **templated_file})
+
+ return config_dict
+
+ def _completed(self, templated_file: dict) -> bool:
+ for v in templated_file.values():
+ if isinstance(v, str):
+ if "{{" in v and "}}" in v:
+ return False
+ elif isinstance(v, dict):
+ return self._completed(templated_file=v)
+ elif isinstance(v, list):
+ for i in v:
+ return self._completed(templated_file=i)
+ else:
+ pass
+ return True
+
+ def create_so_input(self, other_cluster=False) -> dict:
+ so_input_dict = dict()
+ so_input_dict["subscription_service_type"] = self.service_instance.get("model_name")
+ _vnfs = self.service_instance.get("vnfs")
+ vnfs = list()
+
+ for vnf in _vnfs:
+ _vnf_raw = dict()
+ # filter vnfs with cloud_region, code block not required with ONAP Jakrta+
+ if vnf.get("cloud_region") and vnf.get("tenant_name"):
+ if not other_cluster:
+ continue
+ _vnf_raw["cloud_region"] = vnf.get("cloud_region")
+ _vnf_raw["tenant_name"] = vnf.get("tenant_name")
+ else:
+ if other_cluster:
+ continue
+ # end of filter, end of code block
+ _vnf_raw["model_name"] = vnf.get("model_name")
+ if vnf.get("vnf_name_suffix"):
+ _vnf_raw["instance_name"] = "Instance_" + vnf.get("model_name") + "_" + vnf.get("vnf_name_suffix")
+ else:
+ _vnf_raw["instance_name"] = "Instance_" + vnf.get("model_name") + "_" + str(_vnfs.index(vnf))
+ if vnf.get("processing_priority"):
+ _vnf_raw["processing_priority"] = vnf.get("processing_priority")
+ _vnf_raw["parameters"] = vnf.get("parameters")
+ _vnf_raw["vf_modules"] = list()
+ _vf_modules = vnf.get("vf_modules")
+ for vf_module in _vf_modules:
+ _vf_module_raw = dict()
+ _vf_module_raw["model_name"] = vf_module.get("model_name")
+ if vf_module.get("vf_module_name_suffix"):
+ _vf_module_raw["instance_name"] = \
+ "Instance_" + vf_module.get("model_name") + "_" + vf_module.get("vf_module_name_suffix")
+ else:
+ _vf_module_raw["instance_name"] = \
+ "Instance_" + vf_module.get("model_name") + "_" + str(_vnfs.index(vnf)) + \
+ "_" + str(_vf_modules.index(vf_module))
+ if vf_module.get("processing_priority"):
+ _vf_module_raw["processing_priority"] = vf_module["processing_priority"]
+ _vf_module_raw["parameters"] = vf_module.get("parameters")
+ _vnf_raw["vf_modules"].append(_vf_module_raw)
+ vnfs.append(_vnf_raw)
+ so_input_dict["vnfs"] = vnfs
+
+ return so_input_dict
+
+if __name__ == "__main__":
+ config = Config(env_dict=VariablesDict.env_variable, print_final_file=True)
diff --git a/tutorials/ApacheCNF/automation/create_cloud_regions.py b/tutorials/ApacheCNF/automation/create_cloud_regions.py
new file mode 100644
index 00000000..9cf94f12
--- /dev/null
+++ b/tutorials/ApacheCNF/automation/create_cloud_regions.py
@@ -0,0 +1,202 @@
+# ============LICENSE_START=======================================================
+# Copyright (C) 2021 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 onapsdk.so.so_db_adapter import SoDbAdapter, IdentityService
+
+from config import Config, VariablesDict
+from onapsdk.exceptions import ResourceNotFound, APIError
+from onapsdk.aai.cloud_infrastructure import Complex, CloudRegion
+from onapsdk.msb.k8s import ConnectivityInfo
+
+logger = logging.getLogger()
+logger.setLevel(logging.DEBUG)
+
+
+def create_complex(complex_id):
+ logger.info("******** Complex *******")
+ try:
+ region_complex = next(Complex.get_all(physical_location_id=complex_id))
+ logger.info("Complex exists")
+ return region_complex
+ except ResourceNotFound:
+ logger.info("Complex does not exist")
+ region_complex = Complex.create(physical_location_id=complex_id,
+ name=complex_id,
+ physical_location_type="office",
+ street1="DummyStreet 1",
+ city="DummyCity",
+ postal_code="00-000",
+ country="DummyCountry",
+ region="DummyRegion")
+ logger.info("Complex created")
+ return region_complex
+
+
+def create_cloud_region(cloud_region):
+ logger.info("******** Cloud Region *******")
+ region_id = cloud_region["name"]
+ cloud_owner = cloud_region["cloud_owner"]
+ cloud_type = cloud_region["cloud_type"]
+ complex_id = cloud_region["complex_id"]
+ cloud_region_version = "1.0" if cloud_type == "k8s" else "v2.5"
+ try:
+ region = next(CloudRegion.get_all(cloud_owner=cloud_owner, cloud_region_id=region_id))
+ logger.info("Cloud region exists")
+ return region
+ except ResourceNotFound:
+ logger.info("Cloud region does not exist")
+ region = CloudRegion.create(cloud_owner=cloud_owner,
+ cloud_region_id=region_id,
+ cloud_type=cloud_type,
+ owner_defined_type="t1",
+ cloud_region_version=cloud_region_version,
+ complex_name=complex_id,
+ cloud_zone="CloudZone",
+ sriov_automation="false",
+ orchestration_disabled=False,
+ in_maint=False)
+ logger.info("Cloud region created")
+ return region
+
+
+def link_region_to_complex(cloud_region, complx):
+ logger.info("******** Cloud region <-> Complex *******")
+ cloud_region.link_to_complex(complex_object=complx)
+
+
+def add_tenant(cloud_region, tenant_id, tenant_name):
+ logger.info("Tenant does not exist")
+ cloud_region.add_tenant(tenant_id=tenant_id,
+ tenant_name=tenant_name)
+ logger.info(f"Tenant {tenant_name} added to region")
+
+
+def add_tenants(cloud_region, k8s_region, tenants):
+ logger.info("******** Tenants *******")
+ for tenant in tenants:
+ tenant_name = tenant["name"]
+ if k8s_region:
+ try:
+ next(_tenant for _tenant in cloud_region.tenants if _tenant.name == tenant_name)
+ logger.info("Tenant exists")
+ except (StopIteration, ResourceNotFound):
+ tenant_id = str(uuid4())
+ add_tenant(cloud_region=cloud_region, tenant_id=tenant_id, tenant_name=tenant_name)
+ else:
+ tenant_id = tenant["id"]
+ try:
+ cloud_region.get_tenant(tenant_id)
+ logger.info("Tenant exists")
+ except ResourceNotFound:
+ add_tenant(cloud_region=cloud_region, tenant_id=tenant_id, tenant_name=tenant_name)
+
+
+def update_connectivity_info(region):
+ logger.info("******** Connectivity Info *******")
+ kubeconfig_path = region["kubeconfig_file"]
+ cloud_owner = region["cloud_owner"]
+ region_id = region["name"]
+ try:
+ with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), kubeconfig_path), 'rb') as kubeconfig_file:
+ kubeconfig = kubeconfig_file.read()
+
+ connectivity_info = ConnectivityInfo.get_connectivity_info_by_region_id(cloud_region_id=region_id)
+ logger.info("Connectivity Info exists ")
+ logger.info("Delete Connectivity Info ")
+ connectivity_info.delete()
+ ConnectivityInfo.create(cloud_region_id=region_id,
+ cloud_owner=cloud_owner,
+ kubeconfig=kubeconfig)
+ logger.info("Connectivity Info created ")
+ except (APIError, ResourceNotFound):
+ logger.info("Connectivity Info does not exists ")
+ ConnectivityInfo.create(cloud_region_id=region_id,
+ cloud_owner=cloud_owner,
+ kubeconfig=kubeconfig)
+ logger.info("Connectivity Info created ")
+ except FileNotFoundError:
+ logger.error("Error - File Not Found")
+ logger.info("Please check if kubeconfig file exists")
+ exit(1)
+
+
+def add_region_to_so_db(region):
+ logger.info("******** SO Database *******")
+ if is_k8s_region(region):
+ identity_service = IdentityService(identity_id="Keystone_K8s",
+ url="http://test:5000/v3",
+ mso_id="onapsdk_user",
+ mso_pass="mso_pass_onapsdk",
+ project_domain_name="NULL",
+ user_domain_name="NULL",
+ identity_server_type="KEYSTONE")
+
+ SoDbAdapter.add_cloud_site(cloud_region_id=region["name"],
+ complex_id=region["complex_id"],
+ identity_service=identity_service,
+ orchestrator="multicloud")
+ else:
+ identity_url = region["identity_url"]
+ mso_id = region["mso_id"]
+ mso_pass = region["mso_pass"]
+ identity_server_type = region["identity_server_type"]
+ identity_service = IdentityService(identity_id=region["name"] + "_KEYSTONE",
+ url=identity_url,
+ mso_id=mso_id,
+ mso_pass=mso_pass,
+ project_domain_name="Default",
+ user_domain_name="Default",
+ identity_server_type=identity_server_type)
+
+ SoDbAdapter.add_cloud_site(cloud_region_id=region["name"],
+ complex_id=region["complex_id"],
+ identity_service=identity_service,
+ orchestrator="NULL")
+
+
+def is_k8s_region(region):
+ is_k8s = False
+ if region["cloud_type"] == "k8s":
+ is_k8s = True
+ return is_k8s
+
+
+########################################################################################################################
+def main():
+ config = Config(env_dict=VariablesDict.env_variable)
+
+ for region in config.cloud_regions:
+ complx = create_complex(region["complex_id"])
+ cloud_region = create_cloud_region(region)
+ link_region_to_complex(cloud_region, complx)
+ add_tenants(cloud_region, is_k8s_region(region), region.get("tenants"))
+ if is_k8s_region(region):
+ update_connectivity_info(region)
+ add_region_to_so_db(region)
+
+
+if __name__ == "__main__":
+ sh = logging.StreamHandler()
+ sh_formatter = logging.Formatter('%(asctime)s %(levelname)s %(lineno)d:%(filename)s(%(process)d) - %(message)s')
+ sh.setFormatter(sh_formatter)
+ logger.addHandler(sh)
+
+ main()
diff --git a/tutorials/ApacheCNF/automation/delete.py b/tutorials/ApacheCNF/automation/delete.py
new file mode 100644
index 00000000..00d0418b
--- /dev/null
+++ b/tutorials/ApacheCNF/automation/delete.py
@@ -0,0 +1,131 @@
+# ============LICENSE_START=======================================================
+# Copyright (C) 2021 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 config import Config, VariablesDict
+from instantiate import get_customer, check_orchestration_status
+
+from onapsdk.exceptions import ResourceNotFound, APIError
+from onapsdk.aai.business import ServiceInstance
+
+logger = logging.getLogger()
+logger.setLevel(logging.DEBUG)
+
+
+def get_service_subscription(customer, service_type):
+ try:
+ service_subscription = customer.get_service_subscription_by_service_type(
+ service_type=service_type)
+ return service_subscription
+ except ResourceNotFound:
+ logger.error("Service Subscription not found")
+ exit(1)
+
+
+def get_service_instance(service_subscription, service_instance_name):
+ try:
+ service_instance = service_subscription.get_service_instance_by_name(
+ service_instance_name=service_instance_name)
+ return service_instance
+ except ResourceNotFound:
+ logger.error("Service Instance not found")
+ exit(1)
+
+
+def delete_service_macro(service_instance: ServiceInstance, service_instance_info):
+ vnf_infos_list = []
+ for index, vnf_info in enumerate(service_instance_info["vnfs"]):
+ if not vnf_info.get("vnf_name_suffix"):
+ vnf_info["vnf_name_suffix"] = str(index)
+ vnf_info["instance_name"] = f"Instance_{vnf_info['model_name']}_{vnf_info['vnf_name_suffix']}"
+ vnf_infos_list.append(vnf_info)
+
+ ordered_vnf_instances = sorted(vnf_infos_list,
+ key=lambda _vnf: _vnf.get("processing_priority", 100),
+ reverse=True)
+
+ delete_vnfs_instances(service_instance, ordered_vnf_instances)
+
+ return check_orchestration_status(service_instance.delete(a_la_carte=False))
+
+
+def delete_vnfs_instances(service_instance: ServiceInstance, ordered_vnf_instances):
+ for vnf in ordered_vnf_instances:
+ vnf_name = vnf.get("instance_name")
+ vnf_instance = next((vnf_instance
+ for vnf_instance in service_instance.vnf_instances
+ if vnf_instance.vnf_name == vnf_name), None)
+ if not vnf_instance:
+ continue
+
+ try:
+ vnf_deletion = vnf_instance.delete(a_la_carte=False)
+ except APIError:
+ logger.error("Operation not supported, whole service instance will be deleted with random order")
+ break
+ check_orchestration_status(vnf_deletion)
+ return
+
+
+def delete_service_alacarte(service_instance):
+ for vnf in service_instance.vnf_instances:
+ for vf_module in vnf.vf_modules:
+ vf_module_deletion = vf_module.delete()
+ check_orchestration_status(vf_module_deletion)
+ vnf_deletion = vnf.delete()
+ check_orchestration_status(vnf_deletion)
+ service_deletion = service_instance.delete(a_la_carte=True)
+ check_orchestration_status(service_deletion)
+
+
+def main():
+ logger.info("*******************************")
+ logger.info("**** SERVICE DELETION ****")
+ logger.info("*******************************")
+
+ config = Config(env_dict=VariablesDict.env_variable)
+ logger.info("******** GET Customer *******")
+ customer = get_customer(config.service_instance["customer_id"])
+
+ logger.info("******** Check Service Subscription *******")
+ service_subscription = get_service_subscription(customer, config.service_instance["model_name"])
+
+ logger.info("******** Get Service Instance details *******")
+ service_instance = get_service_instance(service_subscription, config.service_instance["instance_name"])
+
+ logger.info("******** Delete Service %s *******", service_instance.instance_name)
+ if config.service_model["macro_orchestration"]:
+ # if config.service_instance.get("deletion_policy") in ('InstantiationOrder', 'ReverseInstantiationOrder'):
+ # delete_service_macro_delete_policy(service_instance, config.service_instance)
+ # else:
+ delete_service_macro(service_instance, config.service_instance)
+ else:
+ logger.error("A_la_carte orchestration method not updated")
+ if config.service_model["pnfs"] is not None:
+ raise NotImplementedError
+ else:
+ delete_service_alacarte(service_instance)
+
+
+if __name__ == "__main__":
+ sh = logging.StreamHandler()
+ sh_formatter = logging.Formatter('%(asctime)s %(levelname)s %(lineno)d:%(filename)s(%(process)d) - %(message)s')
+ sh.setFormatter(sh_formatter)
+ logger.addHandler(sh)
+
+ main()
diff --git a/tutorials/ApacheCNF/automation/healthcheck.py b/tutorials/ApacheCNF/automation/healthcheck.py
new file mode 100644
index 00000000..aee101dd
--- /dev/null
+++ b/tutorials/ApacheCNF/automation/healthcheck.py
@@ -0,0 +1,92 @@
+# ============LICENSE_START=======================================================
+# Copyright (C) 2021 Samsung
+# ================================================================================
+# 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=========================================================
+#todo update when onapsdk > 9.3.0
+import logging
+import zipfile
+
+from onapsdk.aai.business import Customer
+from onapsdk.cds.blueprint import Workflow, Blueprint
+
+from config import Config
+
+#FIXME remove from global scope
+logger = logging.getLogger("")
+logger.setLevel(logging.INFO)
+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)
+
+
+def resolve_hc_inputs():
+ logger.info("******** Check 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 customer is None:
+ raise Exception("Customer %s wasn't found in ONAP" % Config.GLOBAL_CUSTOMER_ID)
+ 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
+ logger.info("******** Retrieve Service Metadata *******")
+ service_instance = None
+ for single_service in service_subscription.service_instances:
+ if single_service.instance_name == Config.SERVICE_INSTANCE_NAME:
+ service_instance = single_service
+ break
+ service_id = service_instance.instance_id
+ vnfs = list(service_instance.vnf_instances)
+ if len(vnfs) > 1:
+ raise NotImplementedError("Service %s is composed of more than one vnf!" % service_id)
+ if not vnfs:
+ raise Exception("Service %s doesn't contain any vnfs" % service_id)
+ vnf_id = vnfs[0].vnf_id
+ return service_id, vnf_id
+
+def main():
+ blueprint = None
+ with zipfile.ZipFile(Config.VSPFILE, 'r') as package:
+ with package.open("CBA.zip", 'r') as cba:
+ blueprint = Blueprint(cba.read())
+
+ healthcheck = Workflow('health-check', None, blueprint)
+ serv_id, vnf_id = resolve_hc_inputs()
+ cds_input = {"health-check-properties":
+ {
+ "service-instance-id": serv_id,
+ "vnf-id": vnf_id
+ }
+ }
+ logger.info("Requesting Healthcheck for CBA %s:%s with inputs:\n%s",
+ blueprint.metadata.template_name,
+ blueprint.metadata.template_version,
+ cds_input)
+ result = healthcheck.execute(cds_input)
+ logger.info("Healthcheck process completed with result: %s", result)
+ logger.info("Please check cds-blueprints-processor logs to see exact status")
+
+if __name__ == "__main__":
+ main()
diff --git a/tutorials/ApacheCNF/automation/instantiate.py b/tutorials/ApacheCNF/automation/instantiate.py
new file mode 100644
index 00000000..0d9dc5ef
--- /dev/null
+++ b/tutorials/ApacheCNF/automation/instantiate.py
@@ -0,0 +1,474 @@
+# ============LICENSE_START=======================================================
+# Copyright (C) 2021 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 time import sleep
+
+from config import Config, VariablesDict
+from onapsdk.aai.cloud_infrastructure import (
+ CloudRegion
+)
+from onapsdk.aai.business import (
+ Customer,
+ OwningEntity as AaiOwningEntity
+)
+from onapsdk.exceptions import ResourceNotFound, APIError
+from onapsdk.msb.k8s import Definition
+from onapsdk.so.instantiation import (
+ ServiceInstantiation,
+ InstantiationParameter, SoService, SoServiceVnf)
+from onapsdk.sdc.service import Service
+# from onapsdk.vid import LineOfBusiness, OwningEntity, Platform, Project
+from onapsdk.so.so_element import OrchestrationRequest
+from onapsdk.aai.service_design_and_creation import Service as AaiService
+
+logger = logging.getLogger()
+logger.setLevel(logging.DEBUG)
+
+
+def get_customer(global_customer_id: str = "customer_cnf"):
+ logger.info("******** Customer *******")
+ try:
+ customer = Customer.get_by_global_customer_id(global_customer_id)
+ logger.info("Customer exists")
+ except ResourceNotFound:
+ logger.info("Customer does not exist")
+ customer = Customer.create(global_customer_id, global_customer_id, "INFRA")
+ logger.info("Customer created")
+ return customer
+
+
+def get_service_model(model_name):
+ try:
+ service_model = next(model for model in Service.get_all() if model.name == model_name)
+ logger.info(
+ f"Found Service {service_model.name} in SDC, distribution status: {service_model.distribution_status}")
+ return service_model
+ except StopIteration:
+ logger.error(f"Service model {model_name} not found in SDC")
+ exit(1)
+
+
+def check_service_customer_subscription(customer, service):
+ try:
+ customer.get_service_subscription_by_service_type(
+ service_type=service.name)
+ except ResourceNotFound:
+ return False
+
+ logger.info(f"Customer {customer.subscriber_name} subscribed for {service.name}")
+ return True
+
+
+def subscribe_service_customer(customer, service):
+ if not check_service_customer_subscription(customer, service):
+ logger.info("******** Subscribe Service *******")
+ customer.subscribe_service(service.name)
+
+
+def get_cloud_region(cloud_owner, cloud_region):
+ try:
+ cloud_region_object = next(cr for cr in CloudRegion.get_all() if
+ cr.cloud_region_id == cloud_region and cr.cloud_owner == cloud_owner)
+ return cloud_region_object
+ except (StopIteration, ResourceNotFound):
+ logging.error("Cloud region not found!!!")
+ exit(1)
+
+
+def get_tenant(cloud_region, tenant_name):
+ try:
+ tenant = next(tenant for tenant in cloud_region.tenants if tenant.name == tenant_name)
+ return tenant
+ except (StopIteration, ResourceNotFound):
+ logger.error(f"Tenant {tenant_name} not found")
+ exit(1)
+
+
+def add_owning_entity(owning_entity):
+ logger.info("******** Add Owning Entity to AAI *******")
+ try:
+ aai_owning_entity = AaiOwningEntity.get_by_owning_entity_name(owning_entity)
+ except ResourceNotFound:
+ logger.info("******** Owning Entity not existing: create *******")
+ aai_owning_entity = AaiOwningEntity.create(owning_entity)
+
+ return aai_owning_entity
+
+
+def delete_old_profiles(service, service_config):
+ for vnf in service.vnfs:
+ vnf_config_details = next(
+ (_vnf for _vnf in service_config["vnfs"] if _vnf["model_name"] == vnf.model_name), None)
+ if not vnf_config_details:
+ continue
+ for vf_module in vnf.vf_modules:
+ vf_module_label = next(vfm_prop.value for vfm_prop in vf_module.properties if
+ vfm_prop.name == "vf_module_label")
+ if vf_module_label == "base_template_dummy_ignore":
+ continue
+ vf_module_config_details = next((_vf_module for _vf_module in vnf_config_details["vf_modules"] if
+ _vf_module["model_name"] == vf_module_label), None)
+ if not vf_module_config_details:
+ continue
+ if "k8s-rb-profile-name" not in vf_module_config_details["parameters"]:
+ continue
+ try:
+ definition = Definition.get_definition_by_name_version(
+ rb_name=vf_module.model_invariant_uuid,
+ rb_version=vf_module.model_customization_id)
+ except APIError:
+ definition = Definition.get_definition_by_name_version(
+ rb_name=vf_module.model_invariant_uuid,
+ rb_version=vf_module.model_version_id)
+ profile_name = vf_module_config_details["parameters"]["k8s-rb-profile-name"]
+ try:
+ profile = definition.get_profile_by_name(profile_name)
+ namespace = None
+ if "k8s-rb-profile-namespace" in vnf_config_details["parameters"]:
+ namespace = vnf_config_details["parameters"]["k8s-rb-profile-namespace"]
+ if "k8s-rb-profile-namespace" in vf_module_config_details["parameters"]:
+ namespace = vf_module_config_details["parameters"]["k8s-rb-profile-namespace"]
+ if namespace is not None and profile.namespace != 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 ResourceNotFound:
+ logger.info("Profile: " + profile_name + " for " + vf_module.name + " not found")
+
+
+def check_service_instance_exists(service_subscription, service_instance_name):
+ try:
+ service_instance = next((instance for instance in service_subscription.service_instances
+ if instance.instance_name == service_instance_name), None)
+ return service_instance
+ except ResourceNotFound:
+ return None
+
+
+def get_instantiation_parameters(properties, vnf_vf_module_config):
+ instantiation_parameters = []
+ for property_name, property_value in properties:
+ instantiation_parameters.append(InstantiationParameter(name=property_name, value=property_value))
+
+ for instantiation_parameter_key, instantiation_parameter_value in vnf_vf_module_config["parameters"]:
+ instantiation_parameters.append(InstantiationParameter(name=instantiation_parameter_key,
+ value=instantiation_parameter_value))
+
+ return instantiation_parameters
+
+
+def check_orchestration_status(instantiation):
+ status = None
+ while not (status == OrchestrationRequest.StatusEnum.COMPLETED
+ or status == OrchestrationRequest.StatusEnum.FAILED):
+ sleep(10)
+ status = instantiation.status
+ logger.info(f"Orchestration status is: {status.value}")
+
+ if status == OrchestrationRequest.StatusEnum.FAILED:
+ logger.error("Orchestration was failed!")
+ exit(1)
+ else:
+ logger.info("Orchestration was succeed")
+ return
+
+
+def get_aai_service(service_type):
+ logging.info("******** Retrieve product family for service *******")
+ try:
+ aai_service = next(service for service in AaiService.get_all() if service.service_id == service_type)
+ except (ResourceNotFound, StopIteration):
+ logging.info("******** Service design and creation in AAI not existing: create *******")
+ AaiService.create(service_id=service_type, service_description=service_type)
+ aai_service = next(service for service in AaiService.get_all() if service.service_id == service_type)
+
+ return aai_service
+
+
+def instantiate_service_macro(config, service, cloud_region, tenant, customer, owning_entity,
+ vid_project, vid_line_of_business, vid_platform):
+ service_instance_name = config.service_instance["instance_name"]
+ so_input = config.so_input
+ for vnf in so_input["vnfs"]:
+ _vnf = next(nf for nf in service.vnfs if nf.model_name == vnf["model_name"])
+ sdnc_model_name = next(prop.value for prop in _vnf.properties if prop.name == "sdnc_model_name")
+ sdnc_model_version = next(prop.value for prop in _vnf.properties if prop.name == "sdnc_model_version")
+ sdnc_artifact_name = next(prop.value for prop in _vnf.properties if prop.name == "sdnc_artifact_name")
+
+ vnf["parameters"]["sdnc_model_name"] = sdnc_model_name
+ vnf["parameters"]["sdnc_model_version"] = sdnc_model_version
+ vnf["parameters"]["sdnc_artifact_name"] = sdnc_artifact_name
+ for vf_module in vnf["vf_modules"]:
+ vf_module_label = vf_module["model_name"]
+ vf_module["parameters"]["sdnc_model_name"] = sdnc_model_name
+ vf_module["parameters"]["sdnc_model_version"] = sdnc_model_version
+ vf_module["parameters"]["vf_module_label"] = vf_module_label
+
+ # TODO: PNF support in so_input -> first ONAPSDK
+
+ so_service = SoService.load(so_input)
+
+ aai_service = get_aai_service(service.name)
+ 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=service_instance_name,
+ aai_service=aai_service,
+ so_service=so_service
+ )
+ check_orchestration_status(service_instantiation)
+
+
+def instantiate_vnfs_macro(config, service_subscription, line_of_business, platform):
+
+ service_instance = check_service_instance_exists(service_subscription, config.service_instance["instance_name"])
+
+ so_input_vnfs_locations = config.create_so_input(other_cluster=True)
+
+ if not so_input_vnfs_locations["vnfs"]:
+ return
+
+ so_input_vnfs_by_priority = sorted(so_input_vnfs_locations["vnfs"],
+ key=lambda _vnf: _vnf.get("processing_priority", 100))
+
+ for vnf in so_input_vnfs_by_priority:
+
+ cloud_owner_name = next(
+ region["cloud_owner"] for region in config.cloud_regions if region["name"] == vnf["cloud_region"])
+ cloud_region = get_cloud_region(cloud_owner=cloud_owner_name, cloud_region=vnf["cloud_region"])
+
+ vnf_model = next(nf for nf in service_instance.sdc_service.vnfs
+ if nf.model_name == vnf["model_name"])
+
+ sdnc_model_name = next(prop.value for prop in vnf_model.properties if prop.name == "sdnc_model_name")
+ sdnc_model_version = next(prop.value for prop in vnf_model.properties if prop.name == "sdnc_model_version")
+ sdnc_artifact_name = next(prop.value for prop in vnf_model.properties if prop.name == "sdnc_artifact_name")
+
+ vnf["parameters"]["sdnc_model_name"] = sdnc_model_name
+ vnf["parameters"]["sdnc_model_version"] = sdnc_model_version
+ vnf["parameters"]["sdnc_artifact_name"] = sdnc_artifact_name
+ for vf_module in vnf["vf_modules"]:
+ vf_module_label = vf_module["model_name"]
+ vf_module["parameters"]["sdnc_model_name"] = sdnc_model_name
+ vf_module["parameters"]["sdnc_model_version"] = sdnc_model_version
+ vf_module["parameters"]["vf_module_label"] = vf_module_label
+
+ so_vnf = SoServiceVnf.load(vnf)
+
+ vnf_instantiation = service_instance.add_vnf(
+ vnf=vnf_model,
+ line_of_business=line_of_business,
+ platform=platform,
+ cloud_region=cloud_region,
+ tenant=get_tenant(cloud_region=cloud_region, tenant_name=vnf["tenant_name"]),
+ vnf_instance_name=vnf["instance_name"],
+ so_vnf=so_vnf,
+ a_la_carte=False
+ )
+
+ check_orchestration_status(vnf_instantiation)
+
+
+def instantiate_vf_module(vf_module, vf_module_param_list, vnf_instance, sdnc_model_name, sdnc_model_version):
+ vf_module_label = next(vfm_prop.value for vfm_prop in vf_module.properties if vfm_prop.name == "vf_module_label")
+ region_id = vf_module_param_list[vf_module_label]["cloud_configuration"]
+ cloud_region = get_cloud_region(
+ Config.CLOUD_REGIONS[region_id]["cloud_owner"],
+ region_id)
+ tenant = get_tenant(cloud_region, Config.CLOUD_REGIONS[region_id]["tenant"]["name"])
+
+ vfmodule_instantiation_parameters = vf_module_param_list[vf_module_label]["instantiation_parameters"].items()
+
+ base_parameters = [
+ InstantiationParameter(name="sdnc_model_name", value=sdnc_model_name),
+ InstantiationParameter(name="sdnc_model_version", value=sdnc_model_version),
+ InstantiationParameter(name="vf_module_label", value=vf_module_label)]
+
+ for instantiation_parameter_key, instantiation_parameter_value in vfmodule_instantiation_parameters:
+ base_parameters.append(InstantiationParameter(name=instantiation_parameter_key,
+ value=instantiation_parameter_value))
+
+ vf_module_instantiation = vnf_instance.add_vf_module(
+ vf_module=vf_module,
+ cloud_region=cloud_region,
+ tenant=tenant,
+ vnf_parameters=base_parameters,
+ use_preload=False
+ )
+ check_orchestration_status(vf_module_instantiation)
+
+
+def check_vf_module_list_correct(vf_modules, vf_modules_config_list):
+ model_labels = set()
+ config_labels = set()
+ for vf_module in vf_modules:
+ _model_label = next(_prop.value for _prop in vf_module.properties if _prop.name == "vf_module_label")
+ model_labels.add(_model_label)
+ for vf_module in vf_modules_config_list:
+ config_labels.add(vf_module.model_name)
+ if model_labels == config_labels:
+ return True
+ else:
+ return False
+
+
+def get_properties(vnf):
+ properties = dict()
+
+ properties["sdnc_model_name"] = next(prop.value for prop in vnf.properties if prop.name == "sdnc_model_name")
+ properties["sdnc_model_version"] = next(prop.value for prop in vnf.properties if prop.name == "sdnc_model_version")
+ properties["sdnc_artifact_name"] = next(prop.value for prop in vnf.properties if prop.name == "sdnc_artifact_name")
+
+ return properties
+
+
+def instantiate_service_alacarte(config, service_subscription, service_model, cloud_region, tenant, customer,
+ owning_entity,
+ vid_project, vid_line_of_business, vid_platform):
+ raise NotImplementedError("Not supported since 2022")
+
+ service_instance_name = config.service_instance["instance_name"]
+ # Service creation
+ service_instantiation = ServiceInstantiation.instantiate_ala_carte(
+ sdc_service=service_model,
+ cloud_region=cloud_region,
+ tenant=tenant,
+ customer=customer,
+ owning_entity=owning_entity,
+ project=vid_project,
+ service_instance_name=service_instance_name
+ )
+ check_orchestration_status(service_instantiation)
+ # End of service creation
+
+ service_instance = service_subscription.get_service_instance_by_name(service_instance_name)
+ # Add VNFs
+ for vnf in service_model.vnfs:
+ # TODO: priority
+ properties = get_properties(vnf)
+ vnf_config = next(_vnf for _vnf in config.service_instance["vnfs"]
+ if config.service_instance["vnfs"]["model_name"] == vnf.name)
+ vnf_parameters = get_instantiation_parameters(properties, vnf_config)
+ # TODO: instance name
+ vnf_instantiation = service_instance.add_vnf(
+ vnf=vnf,
+ line_of_business=vid_line_of_business,
+ platform=vid_platform,
+ vnf_parameters=vnf_parameters
+ )
+ check_orchestration_status(vnf_instantiation)
+
+ # Add vf_modules
+ vnf_type = service_model.name + "/" + vnf.name
+ vnf_instance = next((vnf for vnf in service_instance.vnf_instances if vnf.vnf_type == vnf_type), None)
+
+ if check_vf_module_list_correct(vnf.vf_modules, vnf_config["vf_modules"]):
+ for vf_module in vnf.vf_modules:
+ vf_module_config = next(_vf for _vf in vnf_config["vf_modules"]
+ if _vf["model_name"] == vf_module.properties["vf_module_label"])
+ vf_module_parameters = get_instantiation_parameters(properties, vf_module_config)
+ vf_module_instantiation = vnf_instance.add_vf_module(
+ vf_module=vf_module,
+ cloud_region=cloud_region,
+ tenant=tenant,
+ vnf_parameters=vf_module_parameters,
+ use_preload=False
+ )
+ check_orchestration_status(vf_module_instantiation)
+ else:
+ logger.error("VF_MODULE_PARAM_LIST error. ")
+ # End of vf_modules
+ # End of VNFs
+
+
+def main():
+ logger.info("*******************************")
+ logger.info("**** SERVICE INSTANTIATION ****")
+ logger.info("*******************************")
+
+ config = Config(env_dict=VariablesDict.env_variable)
+
+ logger.info("******** GET Customer *******")
+ customer = get_customer(config.service_instance["customer_id"])
+
+ logger.info("******** GET Service Model from SDC *******")
+ service = get_service_model(config.service_instance["model_name"])
+
+ logger.info("******** Subscribe Customer for Service *******")
+ subscribe_service_customer(customer, service)
+
+ logger.info("******** Get Tenant *******")
+ region_details = next(
+ region for region in config.cloud_regions if region["name"] == config.service_instance["cloud_region"])
+ cloud_region = get_cloud_region(region_details["cloud_owner"], region_details["name"])
+ tenant = get_tenant(cloud_region,
+ config.service_instance["tenant_name"])
+
+ ######
+ logger.info("******** Connect Service to Tenant *******")
+ service_subscription = None
+ try:
+ service_subscription = customer.get_service_subscription_by_service_type(
+ service_type=config.service_instance["model_name"])
+ except ResourceNotFound:
+ logger.error("Service subscription %s is not found", config.service_instance["model_name"])
+ exit(1)
+
+ service_subscription.link_to_cloud_region_and_tenant(cloud_region, tenant)
+ ####
+
+ logger.info("******** Business Objects (OE, P, Pl, LoB) *******")
+ project = "Project-Demonstration"
+ platform = "Platform-test"
+ line_of_business = "Orange-LOB"
+ owning_entity = add_owning_entity("Orange")
+
+ logger.info("******** Delete old profiles ********")
+ delete_old_profiles(service, config.service_instance)
+
+ logger.info("******** Instantiate Service *******")
+ service_instance = check_service_instance_exists(service_subscription, config.service_instance["instance_name"])
+ if service_instance:
+ logger.info("******** Service Instance exists, do not instantiate *******")
+ else:
+ logger.info("******** Service Instance not existing: Instantiate *******")
+ if config.service_model["macro_orchestration"]:
+ instantiate_service_macro(config, service, cloud_region, tenant, customer, owning_entity,
+ project, line_of_business, platform)
+ instantiate_vnfs_macro(config=config, service_subscription=service_subscription,
+ line_of_business=line_of_business, platform=platform)
+ else:
+ instantiate_service_alacarte(config, service_subscription, service, cloud_region, tenant, customer,
+ owning_entity, project, line_of_business, platform)
+
+
+if __name__ == "__main__":
+ sh = logging.StreamHandler()
+ sh_formatter = logging.Formatter('%(asctime)s %(levelname)s %(lineno)d:%(filename)s(%(process)d) - %(message)s')
+ sh.setFormatter(sh_formatter)
+ logger.addHandler(sh)
+
+ main()
diff --git a/tutorials/ApacheCNF/automation/k8s_client.py b/tutorials/ApacheCNF/automation/k8s_client.py
new file mode 100644
index 00000000..98b451bc
--- /dev/null
+++ b/tutorials/ApacheCNF/automation/k8s_client.py
@@ -0,0 +1,59 @@
+# ============LICENSE_START=======================================================
+# Copyright (C) 2021 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/tutorials/ApacheCNF/automation/onap_settings.py b/tutorials/ApacheCNF/automation/onap_settings.py
new file mode 100644
index 00000000..e0bad35d
--- /dev/null
+++ b/tutorials/ApacheCNF/automation/onap_settings.py
@@ -0,0 +1,73 @@
+# ============LICENSE_START=======================================================
+# Copyright (C) 2021 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=========================================================
+
+"""Global settings module.""" # pylint: disable=bad-whitespace
+# uncomment if socks is used
+#from onapsdk.onap_service import OnapService
+
+######################
+# #
+# ONAP SERVICES URLS #
+# #
+######################
+
+AAI_URL = "https://aai.api.sparky.simpledemo.onap.org:30233"
+AAI_API_VERSION = "v23"
+AAI_AUTH = "Basic QUFJOkFBSQ=="
+CDS_URL = "http://portal.api.simpledemo.onap.org:30449"
+CDS_AUTH = ("ccsdkapps", "ccsdkapps")
+MSB_URL = "https://msb.api.simpledemo.onap.org:30283"
+SDC_BE_URL = "https://sdc.api.be.simpledemo.onap.org:30204"
+SDC_FE_URL = "https://sdc.api.fe.simpledemo.onap.org:30207"
+SDC_AUTH = "Basic YWFpOktwOGJKNFNYc3pNMFdYbGhhazNlSGxjc2UyZ0F3ODR2YW9HR21KdlV5MlU="
+SDNC_URL = "https://sdnc.api.simpledemo.onap.org:30267"
+SDNC_AUTH = "Basic YWRtaW46S3A4Yko0U1hzek0wV1hsaGFrM2VIbGNzZTJnQXc4NHZhb0dHbUp2VXkyVQ=="
+SO_URL = "http://so.api.simpledemo.onap.org:30277"
+SO_API_VERSION = "v7"
+SO_AUTH = "Basic SW5mcmFQb3J0YWxDbGllbnQ6cGFzc3dvcmQxJA=="
+SO_CAT_DB_AUTH = "Basic YnBlbDpwYXNzd29yZDEk"
+VID_URL = "https://vid.api.simpledemo.onap.org:30200"
+VID_API_VERSION = "/vid"
+CLAMP_URL = "https://clamp.api.simpledemo.onap.org:30258"
+CLAMP_AUTH = "Basic ZGVtb0BwZW9wbGUub3NhYWYub3JnOmRlbW8xMjM0NTYh"
+VES_URL = "http://ves.api.simpledemo.onap.org:30417"
+DMAAP_URL = "http://dmaap.api.simpledemo.onap.org:3904"
+NBI_URL = "https://nbi.api.simpledemo.onap.org:30274"
+NBI_API_VERSION = "/nbi/api/v4"
+DCAEMOD_URL = ""
+HOLMES_URL = "https://aai.api.sparky.simpledemo.onap.org:30293"
+POLICY_URL = ""
+
+## GUI
+AAI_GUI_URL = "https://aai.api.sparky.simpledemo.onap.org:30220"
+AAI_GUI_SERVICE = f"{AAI_GUI_URL}/services/aai/webapp/index.html#/browse"
+CDS_GUI_SERVICE = f"{CDS_URL}/"
+SO_MONITOR_GUI_SERVICE = f"{SO_URL}/"
+SDC_GUI_SERVICE = f"{SDC_FE_URL}/sdc1/portal"
+SDNC_DG_GUI_SERVICE = f"{SDNC_URL}/nifi/"
+SDNC_ODL_GUI_SERVICE = f"{SDNC_URL}/odlux/index.html"
+
+DCAEMOD_GUI_SERVICE = f"{DCAEMOD_URL}/"
+HOLMES_GUI_SERVICE = f"{HOLMES_URL}/iui/holmes/default.html"
+POLICY_GUI_SERVICE = f"{POLICY_URL}/onap/login.html"
+POLICY_CLAMP_GUI_SERVICE = f"{CLAMP_URL}/"
+
+# uncomment if socks is used
+#OnapService.set_proxy({'http': 'socks5h://127.0.0.1:8081', 'https': 'socks5h://127.0.0.1:8081'})
+
+# execute in the shell to apply these settings
+# export ONAP_PYTHON_SDK_SETTINGS="onap_settings"
diff --git a/tutorials/ApacheCNF/automation/onboard.py b/tutorials/ApacheCNF/automation/onboard.py
new file mode 100644
index 00000000..7e27419c
--- /dev/null
+++ b/tutorials/ApacheCNF/automation/onboard.py
@@ -0,0 +1,213 @@
+# ============LICENSE_START=======================================================
+# Copyright (C) 2021 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
+import time
+import zipfile
+from io import BytesIO
+
+import yaml
+
+from config import Config, VariablesDict
+import onapsdk.constants as const
+
+from onapsdk.sdc.vendor import Vendor
+from onapsdk.sdc.vsp import Vsp
+from onapsdk.sdc.vf import Vf
+from onapsdk.sdc.pnf import Pnf
+from onapsdk.sdc.service import Service, ServiceInstantiationType
+from onapsdk.exceptions import ResourceNotFound
+
+logger = logging.getLogger()
+logger.setLevel(logging.DEBUG)
+
+
+def retrieve_service(service_name: str):
+ logger.info("Retrieve service from SDC before onboarding")
+ services = Service.get_all()
+
+ for found_service in services:
+ if found_service.name == service_name:
+ logging.info(f"Service {found_service.name} found in SDC, onboarding will not be executed")
+ exit(0)
+ return
+
+
+def onboard_vendor(vendor_name: str = "demo_vendor"):
+ logger.info("******** Onboard Vendor *******")
+ try:
+ vendor = next(vendor for vendor in Vendor.get_all() if vendor.name.lower() == vendor_name.lower())
+ except (StopIteration, ResourceNotFound):
+ vendor = Vendor(vendor_name)
+ vendor.onboard()
+ return vendor
+
+
+def onboard_vsp(vsp_name, vsp_file, vendor):
+ logger.info(f"******** Onboard VSP - {vsp_name} *******")
+ mypath = os.path.dirname(os.path.realpath(__file__))
+ vsp_path = os.path.join(mypath, vsp_file)
+ vsp = None
+ try:
+ vsp = Vsp(name=vsp_name, vendor=vendor, package=open(vsp_path, 'rb'))
+ except FileNotFoundError:
+ logger.error(f"No vsp file was found for {vsp_name}!")
+ exit(1)
+ vsp.onboard()
+ return vsp
+
+
+def onboard_pnf(pnf_name, vsp_name, vsp_file, vendor_name):
+ logger.info(f"******** Onboard PNF - {pnf_name} *******")
+ vendor = onboard_vendor(vendor_name=vendor_name)
+ try:
+ pnf = next(_pnf for _pnf in Pnf.get_all() if _pnf.name == pnf_name)
+ logger.info("PNF with provided name exists in ONAP SDC, onboarding will not be executed")
+ except (StopIteration, ResourceNotFound):
+ pnf_vsp = onboard_vsp(vsp_name=vsp_name, vsp_file=vsp_file, vendor=vendor)
+ pnf = Pnf(name=pnf_name, vsp=pnf_vsp)
+ pnf.onboard()
+ return pnf
+
+
+def onboard_vnf(vnf_name, vsp_name, vsp_file, vendor_name):
+ logger.info(f"******** Onboard VNF - {vnf_name} *******")
+ vendor = onboard_vendor(vendor_name=vendor_name)
+ try:
+ vnf = next(_vf for _vf in Vf.get_all() if _vf.name == vnf_name)
+ if vnf.status != "Certified":
+ logger.error("Selected VNF is not certified. Try Onboard VF with new name.")
+ exit(1)
+ logger.info("VNF with provided name exists in ONAP SDC, onboarding will not be executed")
+ except (StopIteration, ResourceNotFound):
+ vnf_vsp = onboard_vsp(vsp_name=vsp_name, vsp_file=vsp_file, vendor=vendor)
+ vnf = Vf(name=vnf_name, vsp=vnf_vsp)
+ vnf.create()
+ vnf.onboard()
+ return vnf
+
+
+def create_service(service_name, is_macro: bool = True):
+ logger.info("******** Create Service *******")
+ if is_macro:
+ service = Service(name=service_name,
+ instantiation_type=ServiceInstantiationType.MACRO)
+ else:
+ service = Service(name=service_name,
+ instantiation_type=ServiceInstantiationType.A_LA_CARTE)
+ service.create()
+ return service
+
+
+def read_sdnc_model_details(file):
+ mypath = os.path.dirname(os.path.realpath(__file__))
+ file_path = os.path.join(mypath, file)
+ try:
+ with zipfile.ZipFile(file_path, 'r') as package:
+ try:
+ 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.SafeLoader)
+ sdnc_model_name = tosca_meta.get("Template-Name")
+ sdnc_model_version = tosca_meta.get("Template-Version")
+ return sdnc_model_name, sdnc_model_version
+ except KeyError:
+ logger.info("No CBA file was found")
+ return None, None
+ except FileNotFoundError:
+ logger.error("No vsp file was found!")
+ exit(1)
+
+
+def set_properties(service, xnf, vsp_details):
+ sdnc_model_name, sdnc_model_version = read_sdnc_model_details(vsp_details["vsp_file"])
+ if sdnc_model_name and sdnc_model_version:
+ if service.status == const.DRAFT:
+ logger.info("******** Set SDNC properties for VF ********")
+ component = service.get_component(xnf)
+ prop = component.get_property("sdnc_model_name")
+ prop.value = sdnc_model_name
+ prop = component.get_property("sdnc_model_version")
+ prop.value = sdnc_model_version
+ prop = component.get_property("controller_actor")
+ prop.value = "CDS"
+ prop = component.get_property("sdnc_artifact_name")
+ prop.value = vsp_details["sdnc_artifact_name"]
+ prop = component.get_property("skip_post_instantiation_configuration")
+ prop.value = vsp_details["skip_post_instantiation_configuration"]
+
+
+def check_distribution_status(service):
+ 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 = service.distributed
+ if distribution_completed is True:
+ logger.info(f"Service Distribution for {service.name} is successfully finished")
+ break
+ logger.info(f"Service Distribution for {service.name} ongoing, Wait for 60 s")
+ time.sleep(60)
+ nb_try += 1
+
+ if distribution_completed is False:
+ logger.error(f"Service Distribution for {service.name} failed !!", )
+ exit(1)
+
+
+def main():
+ config = Config(env_dict=VariablesDict.env_variable)
+ retrieve_service(service_name=config.service_model["model_name"])
+
+ logger.info("******** SERVICE DESIGN *******")
+ service = create_service(service_name=config.service_model["model_name"],
+ is_macro=config.service_model["macro_orchestration"])
+ vnfs = config.service_model.get("vnfs")
+ if vnfs:
+ for vnf in vnfs:
+ new_vnf = onboard_vnf(vnf_name=vnf["model_name"],
+ vsp_name="VSP" + "_" + vnf["model_name"],
+ vsp_file=vnf["vsp"]["vsp_file"],
+ vendor_name=vnf["vsp"]["vendor"])
+ service.add_resource(new_vnf)
+ set_properties(service=service, xnf=new_vnf, vsp_details=vnf["vsp"])
+
+ pnfs = config.service_model.get("pnfs")
+ if pnfs:
+ for pnf in pnfs:
+ new_pnf = onboard_pnf(pnf_name=pnf["model_name"],
+ vsp_name="VSP" + "_" + pnf["model_name"],
+ vsp_file=pnf["vsp"]["vsp_file"],
+ vendor_name=pnf["vsp"]["vendor"])
+ service.add_resource(new_pnf)
+ set_properties(service=service, xnf=new_pnf, vsp_details=pnf["vsp"])
+
+ service.checkin()
+ service.onboard()
+ check_distribution_status(service)
+
+
+if __name__ == "__main__":
+ sh = logging.StreamHandler()
+ sh_formatter = logging.Formatter('%(asctime)s %(levelname)s %(lineno)d:%(filename)s(%(process)d) - %(message)s')
+ sh.setFormatter(sh_formatter)
+ logger.addHandler(sh)
+
+ main()
diff --git a/tutorials/ApacheCNF/automation/update_cba.py b/tutorials/ApacheCNF/automation/update_cba.py
new file mode 100644
index 00000000..226029f5
--- /dev/null
+++ b/tutorials/ApacheCNF/automation/update_cba.py
@@ -0,0 +1,57 @@
+# ============LICENSE_START=======================================================
+# Copyright (C) 2021 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
+import zipfile
+from io import BytesIO
+
+from onapsdk.cds import Blueprint
+
+from config import Config, VariablesDict
+
+logger = logging.getLogger()
+logger.setLevel(logging.DEBUG)
+
+
+def update_cba(file):
+ mypath = os.path.dirname(os.path.realpath(__file__))
+ file_path = os.path.join(mypath, file)
+ try:
+ with zipfile.ZipFile(file_path, 'r') as package:
+ cba_io = BytesIO(package.read("CBA.zip"))
+
+ blueprint = Blueprint(cba_io)
+ blueprint.deploy()
+ except FileNotFoundError:
+ logger.error("Error - File Not Found")
+ exit(1)
+
+
+def main():
+ config = Config(env_dict=VariablesDict.env_variable)
+ for vnf in config.service_model["vnfs"]:
+ update_cba(vnf["vsp"]["vsp_file"])
+
+
+if __name__ == "__main__":
+ sh = logging.StreamHandler()
+ sh_formatter = logging.Formatter('%(asctime)s %(levelname)s %(lineno)d:%(filename)s(%(process)d) - %(message)s')
+ sh.setFormatter(sh_formatter)
+ logger.addHandler(sh)
+
+ main()
diff --git a/tutorials/ApacheCNF/automation/update_connectivity_info.py b/tutorials/ApacheCNF/automation/update_connectivity_info.py
new file mode 100644
index 00000000..d5138bc9
--- /dev/null
+++ b/tutorials/ApacheCNF/automation/update_connectivity_info.py
@@ -0,0 +1,40 @@
+# ============LICENSE_START=======================================================
+# Copyright (C) 2021 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 config import Config, VariablesDict
+from create_cloud_regions import update_connectivity_info, is_k8s_region
+
+logger = logging.getLogger()
+logger.setLevel(logging.DEBUG)
+
+
+def main():
+ config = Config(env_dict=VariablesDict.env_variable)
+ for region in config.cloud_regions:
+ if is_k8s_region(region):
+ update_connectivity_info(region)
+
+
+if __name__ == "__main__":
+ sh = logging.StreamHandler()
+ sh_formatter = logging.Formatter('%(asctime)s %(levelname)s %(lineno)d:%(filename)s(%(process)d) - %(message)s')
+ sh.setFormatter(sh_formatter)
+ logger.addHandler(sh)
+
+ main()
diff --git a/tutorials/ApacheCNF/automation/vsp/.gitkeep b/tutorials/ApacheCNF/automation/vsp/.gitkeep
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tutorials/ApacheCNF/automation/vsp/.gitkeep
diff --git a/tutorials/ApacheCNF/automation/vsp/.keep b/tutorials/ApacheCNF/automation/vsp/.keep
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tutorials/ApacheCNF/automation/vsp/.keep