# All Rights Reserved.
#
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

import os
import re
import toscaparser.utils.yamlparser
from urllib.parse import urlparse
import urllib.request as urllib2
import yaml

from kubernetes import client
from oslo_config import cfg
from oslo_log import log as logging
from tacker.common import exceptions


LOG = logging.getLogger(__name__)
CONF = cfg.CONF

YAML_LOADER = toscaparser.utils.yamlparser.load_yaml
NEWLINE_CHARACTER = "\n"
COLON_CHARACTER = ':'
WHITE_SPACE_CHARACTER = ' '
NON_WHITE_SPACE_CHARACTER = ''
HYPHEN_CHARACTER = '-'
DASH_CHARACTER = '_'
# Due to the dependency of k8s resource creation, according to the design,
# other resources (resources not mentioned in self.RESOURCE_CREATION_SORT)
# will be created after the NetworkPolicy resource. This number is a flag
# to ensure that when multiple resources are to be created, the order of
# other resources is after NetworkPolicy and before Service.
OTHER_RESOURCE_SORT_POSITION = 8


class Transformer(object):
    """Transform TOSCA template to Kubernetes resources"""

    def __init__(self, core_v1_api_client, app_v1_api_client,
                 scaling_api_client, k8s_client_dict):
        # the old param used when creating vnf with TOSCA template
        self.core_v1_api_client = core_v1_api_client
        self.app_v1_api_client = app_v1_api_client
        self.scaling_api_client = scaling_api_client
        # the new param used when instantiating vnf with addtionalParams
        self.k8s_client_dict = k8s_client_dict
        self.RESOURCE_CREATION_ORDER = [
            'StorageClass',
            'PersistentVolume',
            'PriorityClass',
            'Namespace',
            'LimitRange',
            'ResourceQuota',
            'HorizontalPodAutoscaler',
            'NetworkPolicy',
            'Service',
            'Endpoints',
            'PersistentVolumeClaim',
            'ConfigMap',
            'Secret',
            'StatefulSet',
            'Job',
            'Deployment',
            'DaemonSet',
            'Pod'
        ]
        self.method_value = {
            "Pod": 'create_namespaced_pod',
            "Service": 'create_namespaced_service',
            "ConfigMap": 'create_namespaced_config_map',
            "Secret": 'create_namespaced_secret',
            "PersistentVolumeClaim":
                'create_namespaced_persistent_volume_claim',
            "LimitRange": 'create_namespaced_limit_range',
            "PodTemplate": 'create_namespaced_pod_template',
            "Binding": 'create_namespaced_binding',
            "Namespace": 'create_namespace',
            "Node": 'create_node',
            "PersistentVolume": 'create_persistent_volume',
            "ResourceQuota": 'create_namespaced_resource_quota',
            "ServiceAccount": 'create_namespaced_service_account',
            "APIService": 'create_api_service',
            "DaemonSet": 'create_namespaced_daemon_set',
            "Deployment": 'create_namespaced_deployment',
            "ReplicaSet": 'create_namespaced_replica_set',
            "StatefulSet": 'create_namespaced_stateful_set',
            "ControllerRevision": 'create_namespaced_controller_revision',
            "TokenReview": 'create_token_review',
            "LocalSubjectAccessReview": 'create_namespaced_local_'
                                        'subject_access_review',
            "SelfSubjectAccessReview": 'create_self_subject_access_review',
            "SelfSubjectRulesReview": 'create_self_subject_rules_review',
            "SubjectAccessReview": 'create_subject_access_review',
            "HorizontalPodAutoscaler": 'create_namespaced_horizontal_'
                                       'pod_autoscaler',
            "Job": 'create_namespaced_job',
            "Lease": 'create_namespaced_lease',
            "NetworkPolicy": 'create_namespaced_network_policy',
            "ClusterRole": 'create_cluster_role',
            "ClusterRoleBinding": 'create_cluster_role_binding',
            "Role": 'create_namespaced_role',
            "RoleBinding": 'create_namespaced_role_binding',
            "PriorityClass": 'create_priority_class',
            "StorageClass": 'create_storage_class',
            "VolumeAttachment": 'create_volume_attachment',
        }

    def transform(self, tosca_kube_objects):
        """transform function translates from tosca_kube_object to

        kubernetes_object (ConfigMap, Deployment, Service, HPA)
        """

        # kubernetes_objects store all kubernetes objects that are transformed
        # from TOSCA VNF template
        kubernetes_objects = dict()
        for tosca_kube_obj in tosca_kube_objects:
            namespace = tosca_kube_obj.namespace
            kubernetes_objects['namespace'] = namespace
            kubernetes_objects['objects'] = list()
            kube_obj_name = tosca_kube_obj.name
            new_kube_obj_name = self.pre_process_name(kube_obj_name)

            # translate environments to ConfigMap objects
            for container in tosca_kube_obj.containers:
                config_map_object = \
                    self.init_configmap(container_props=container,
                                        kube_obj_name=new_kube_obj_name)
                if config_map_object:
                    kubernetes_objects['objects'].append(config_map_object)

            # translate Deployment object
            deployment_object = \
                self.init_deployment(tosca_kube_obj=tosca_kube_obj,
                                     kube_obj_name=new_kube_obj_name)
            kubernetes_objects['objects'].append(deployment_object)

            # translate to Horizontal Pod Autoscaler object
            hpa_object = self.init_hpa(tosca_kube_obj=tosca_kube_obj,
                                       kube_obj_name=new_kube_obj_name)
            if hpa_object:
                kubernetes_objects['objects'].append(hpa_object)

            # translate to Service object
            service_object = self.init_service(
                tosca_kube_obj=tosca_kube_obj,
                kube_obj_name=new_kube_obj_name)
            kubernetes_objects['objects'].append(service_object)

        return kubernetes_objects

    def _create_k8s_object(self, kind, file_content_dict):
        # must_param referring K8s official object page
        # *e.g:https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1Service.md
        # initiating k8s object, you need to
        # give the must param an empty value.
        must_param = {
            'V1LocalSubjectAccessReview': '(spec="")',
            'V1HTTPGetAction': '(port="")',
            'V1DeploymentSpec': '(selector="", template="")',
            'V1PodSpec': '(containers=[])',
            'V1ConfigMapKeySelector': '(key="")',
            'V1Container': '(name="")',
            'V1EnvVar': '(name="")',
            'V1SecretKeySelector': '(key="")',
            'V1ContainerPort': '(container_port="")',
            'V1VolumeMount': '(mount_path="", name="")',
            'V1PodCondition': '(status="", type="")',
            'V1ContainerStatus': '('
                                 'image="", image_id="", '
                                 'name="", ready="", '
                                 'restart_count="")',
            'V1ServicePort': '(port=80)',
            'V1TypedLocalObjectReference': '(kind="", name="")',
            'V1LabelSelectorRequirement': '(key="", operator="")',
            'V1PersistentVolumeClaimCondition': '(status="", type="")',
            'V1AWSElasticBlockStoreVolumeSource': '(volume_id="")',
            'V1AzureDiskVolumeSource': '(disk_name="", disk_uri="")',
            'V1AzureFileVolumeSource': '(secret_name="", share_name="")',
            'V1CephFSVolumeSource': '(monitors=[])',
            'V1CinderVolumeSource': '(volume_id="")',
            'V1KeyToPath': '(key="", path="")',
            'V1CSIVolumeSource': '(driver="")',
            'V1DownwardAPIVolumeFile': '(path="")',
            'V1ObjectFieldSelector': '(field_path="")',
            'V1ResourceFieldSelector': '(resource="")',
            'V1FlexVolumeSource': '(driver="")',
            'V1GCEPersistentDiskVolumeSource': '(pd_name="")',
            'V1GitRepoVolumeSource': '(repository="")',
            'V1GlusterfsVolumeSource': '(endpoints="", path="")',
            'V1HostPathVolumeSource': '(path="")',
            'V1ISCSIVolumeSource': '(iqn="", lun=0, target_portal="")',
            'V1Volume': '(name="")',
            'V1NFSVolumeSource': '(path="", server="")',
            'V1PersistentVolumeClaimVolumeSource': '(claim_name="")',
            'V1PhotonPersistentDiskVolumeSource': '(pd_id="")',
            'V1PortworxVolumeSource': '(volume_id="")',
            'V1ProjectedVolumeSource': '(sources=[])',
            'V1ServiceAccountTokenProjection': '(path="")',
            'V1QuobyteVolumeSource': '(registry="", volume="")',
            'V1RBDVolumeSource': '(image="", monitors=[])',
            'V1ScaleIOVolumeSource': '('
                                     'gateway="", secret_ref="", '
                                     'system="")',
            'V1VsphereVirtualDiskVolumeSource': '(volume_path="")',
            'V1LimitRangeSpec': '(limits=[])',
            'V1Binding': '(target="")',
            'V1ComponentCondition': '(status="", type="")',
            'V1NamespaceCondition': '(status="", type="")',
            'V1ConfigMapNodeConfigSource': '(kubelet_config_key="", '
                                           'name="", namespace="")',
            'V1Taint': '(effect="", key="")',
            'V1NodeAddress': '(address="", type="")',
            'V1NodeCondition': '(status="", type="")',
            'V1DaemonEndpoint': '(port=0)',
            'V1ContainerImage': '(names=[])',
            'V1NodeSystemInfo': '(architecture="", boot_id="", '
                                'container_runtime_version="",'
                                'kernel_version="", '
                                'kube_proxy_version="", '
                                'kubelet_version="",'
                                'machine_id="", operating_system="", '
                                'os_image="", system_uuid="")',
            'V1AttachedVolume': '(device_path="", name="")',
            'V1ScopedResourceSelectorRequirement':
                '(operator="", scope_name="")',
            'V1APIServiceSpec': '(group_priority_minimum=0, '
                                'service="", '
                                'version_priority=0)',
            'V1APIServiceCondition': '(status="", type="")',
            'V1DaemonSetSpec': '(selector="", template="")',
            'V1ReplicaSetSpec': '(selector="")',
            'V1StatefulSetSpec': '(selector="", '
                                 'service_name="", template="")',
            'V1StatefulSetCondition': '(status="", type="")',
            'V1StatefulSetStatus': '(replicas=0)',
            'V1ControllerRevision': '(revision=0)',
            'V1TokenReview': '(spec="")',
            'V1SubjectAccessReviewStatus': '(allowed=True)',
            'V1SelfSubjectAccessReview': '(spec="")',
            'V1SelfSubjectRulesReview': '(spec="")',
            'V1SubjectRulesReviewStatus': '(incomplete=True, '
                                          'non_resource_rules=[], '
                                          'resource_rules=[])',
            'V1NonResourceRule': '(verbs=[])',
            'V1SubjectAccessReview': '(spec="")',
            'V1HorizontalPodAutoscalerSpec':
                '(max_replicas=0, scale_target_ref="")',
            'V1CrossVersionObjectReference': '(kind="", name="")',
            'V1HorizontalPodAutoscalerStatus':
                '(current_replicas=0, desired_replicas=0)',
            'V1JobSpec': '(template="")',
            'V1NetworkPolicySpec': '(pod_selector="")',
            'V1PolicyRule': '(verbs=[])',
            'V1ClusterRoleBinding': '(role_ref="")',
            'V1RoleRef': '(api_group="", kind="", name="")',
            'V1Subject': '(kind="", name="")',
            'V1RoleBinding': '(role_ref="")',
            'V1PriorityClass': '(value=0)',
            'V1StorageClass': '(provisioner="")',
            'V1TopologySelectorLabelRequirement': '(key="", values=[])',
            'V1VolumeAttachment': '(spec="")',
            'V1VolumeAttachmentSpec':
                '(attacher="", node_name="", source="")',
            'V1VolumeAttachmentStatus': '(attached=True)',
            'V1NodeSelector': '(node_selector_terms=[])',
            'V1NodeSelectorRequirement': '(key="", operator="")',
            'V1PreferredSchedulingTerm': '(preference="", weight=1)',
            'V1PodAffinityTerm': '(topology_key="")',
            'V1WeightedPodAffinityTerm': '(pod_affinity_term="", weight=1)',
            'V1OwnerReference': '(api_version="", kind="", name="", uid="")',
            'V1HTTPHeader': '(name="", value="")',
            'V1TCPSocketAction': '(port="")',
            'V1VolumeDevice': '(device_path="", name="")',
            'V1PodReadinessGate': '(condition_type="")',
            'V1Sysctl': '(name="", value="")',
            'V1ContainerStateTerminated': '(exit_code=0)',
            'V1AzureFilePersistentVolumeSource': '(secret_name="",'
                                                 ' share_name="")',
            'V1CephFSPersistentVolumeSource': '(monitors=[])',
            'V1CinderPersistentVolumeSource': '(volume_id="")',
            'V1CSIPersistentVolumeSource': '(driver="", volume_handle="")',
            'V1FlexPersistentVolumeSource': '(driver="")',
            'V1GlusterfsPersistentVolumeSource': '(endpoints="", path="")',
            'V1ISCSIPersistentVolumeSource': '(iqn="", lun=0,'
                                             ' target_portal="")',
            'V1LocalVolumeSource': '(path="")',
            'V1RBDPersistentVolumeSource': '(image="", monitors=[])',
            'V1ScaleIOPersistentVolumeSource': '('
                                               'gateway="",'
                                               ' secret_ref="",'
                                               ' system="")',
            'V1DaemonSetStatus': '(current_number_scheduled=0, '
                                 'desired_number_scheduled=0, '
                                 'number_misscheduled=0, '
                                 'number_ready=0)',
            'V1DaemonSetCondition': '(status="", type="")',
            'V1DeploymentCondition': '(status="", type="")',
            'V1ReplicaSetStatus': '(replicas=0)',
            'V1ReplicaSetCondition': '(status="", type="")',
            'V1ResourceRule': '(verbs=[])',
            'V1JobCondition': '(status="", type="")',
            'V1IPBlock': '(cidr="")',
            'V1EphemeralContainer': '(name="")',
            'V1TopologySpreadConstraint': '(max_skew=0, topology_key="",'
                                          ' when_unsatisfiable="")',
            'V1LimitRangeItem': '(type="")'
        }
        whole_kind = 'V1' + kind
        if whole_kind in must_param.keys():
            k8s_obj = eval('client.V1' + kind + must_param.get(whole_kind))
        else:
            k8s_obj = eval('client.V1' + kind + '()')
        self._init_k8s_obj(k8s_obj, file_content_dict, must_param)
        return k8s_obj

    def _get_k8s_obj_from_file_content_dict(self, file_content_dict,
                                            namespace=None):
        k8s_obj = {}
        kind = file_content_dict.get('kind', '')
        try:
            k8s_obj['object'] = self._create_k8s_object(
                kind, file_content_dict)
        except Exception as e:
            if isinstance(e, client.rest.ApiException):
                msg = '{kind} create failure. Reason={reason}'.format(
                    kind=file_content_dict.get('kind', ''), reason=e.body)
            else:
                msg = '{kind} create failure. Reason={reason}'.format(
                    kind=file_content_dict.get('kind', ''), reason=e)
            LOG.error(msg)
            raise exceptions.InitApiFalse(error=msg)

        k8s_obj['namespace'] = namespace
        if k8s_obj['object'].metadata:
            k8s_obj['object'].metadata.namespace = namespace

        return k8s_obj

    def get_k8s_objs_from_yaml(self, artifact_files, vnf_package_path,
                               namespace=None):
        k8s_objs = []
        for artifact_file in artifact_files:
            if ((urlparse(artifact_file).scheme == 'file') or
                    (bool(urlparse(artifact_file).scheme) and
                     bool(urlparse(artifact_file).netloc))):
                file_content = urllib2.urlopen(artifact_file).read()
            else:
                artifact_file_path = os.path.join(
                    vnf_package_path, artifact_file)
                with open(artifact_file_path, 'r') as f:
                    file_content = f.read()
            file_content_dicts = list(yaml.safe_load_all(file_content))
            for file_content_dict in file_content_dicts:
                k8s_obj = self._get_k8s_obj_from_file_content_dict(
                    file_content_dict, namespace)
                k8s_objs.append(k8s_obj)
        return k8s_objs

    def get_k8s_objs_from_manifest(self, mf_content, namespace):
        mkobj_kind_list = [
            "Pod",
            "Service",
            "PersistentVolumeClaim",
            "Namespace",
            "Node",
            "PersistentVolume",
            "DaemonSet",
            "Deployment",
            "ReplicaSet",
            "StatefulSet",
            "Job"
        ]
        k8s_objs = []
        mf_content_dicts = list(yaml.safe_load_all(mf_content))
        for mf_content_dict in mf_content_dicts:
            kind = mf_content_dict.get('kind', '')
            if kind in mkobj_kind_list:
                k8s_obj = self._get_k8s_obj_from_file_content_dict(
                    file_content_dict=mf_content_dict,
                    namespace=namespace)
                k8s_objs.append(k8s_obj)
        return k8s_objs

    def _select_k8s_client_and_api(
            self, kind, namespace, api_version, body):
        k8s_client_obj = self.k8s_client_dict[api_version]
        if 'namespaced' in self.method_value[kind]:
            response = getattr(k8s_client_obj, self.method_value.get(kind))(
                namespace=namespace, body=body
            )
        else:
            response = getattr(k8s_client_obj, self.method_value.get(kind))(
                body=body
            )
        return response

    def deploy(self, kubernetes_objects):
        """Deploy Kubernetes objects on Kubernetes VIM and return

        a list name of services
        """
        deployment_names = list()
        namespace = kubernetes_objects.get('namespace')
        k8s_objects = kubernetes_objects.get('objects')

        for k8s_object in k8s_objects:
            object_type = k8s_object.kind

            if object_type == 'ConfigMap':
                self.core_v1_api_client.create_namespaced_config_map(
                    namespace=namespace,
                    body=k8s_object)
                LOG.debug('Successfully created ConfigMap %s',
                          k8s_object.metadata.name)
            elif object_type == 'Deployment':
                self.app_v1_api_client.create_namespaced_deployment(
                    namespace=namespace,
                    body=k8s_object)
                LOG.debug('Successfully created Deployment %s',
                          k8s_object.metadata.name)
            elif object_type == 'HorizontalPodAutoscaler':
                self.scaling_api_client. \
                    create_namespaced_horizontal_pod_autoscaler(
                        namespace=namespace,
                        body=k8s_object)
                LOG.debug('Successfully created Horizontal Pod Autoscaler %s',
                          k8s_object.metadata.name)
            elif object_type == 'Service':
                self.core_v1_api_client.create_namespaced_service(
                    namespace=namespace,
                    body=k8s_object)
                LOG.debug('Successfully created Service %s',
                          k8s_object.metadata.name)
                deployment_names.append(namespace)
                deployment_names.append(k8s_object.metadata.name)

        # return a string that contains all deployment namespace and names
        # for tracking resources pattern:
        # namespace1,deployment1,namespace2,deployment2,namespace3,deployment3
        return ",".join(deployment_names)

    def deploy_k8s(self, kubernetes_objects):
        """Deploy kubernetes

        Deploy Kubernetes objects on Kubernetes VIM and
        return a list name of services
        """
        kubernetes_objects = self._sort_k8s_obj(kubernetes_objects)
        new_k8s_objs = list()
        for kubernetes_object in kubernetes_objects:
            namespace = kubernetes_object.get('namespace', '')
            kind = kubernetes_object.get('object', '').kind
            api_version = kubernetes_object.get('object', '').api_version
            body = kubernetes_object.get('object', '')
            if kubernetes_object.get('object', '').metadata:
                name = kubernetes_object.get('object', '').metadata.name
            else:
                name = ''
            try:
                LOG.debug("{kind} begin create.".format(kind=kind))
                self._select_k8s_client_and_api(
                    kind, namespace, api_version, body)
                kubernetes_object['status'] = 'Creating'
            except Exception as e:
                if isinstance(e, client.rest.ApiException):
                    kubernetes_object['status'] = 'creating_failed'
                    msg = '''The request to create a resource failed.
                    namespace: {namespace}, name: {name},kind: {kind},
                    Reason: {exception}'''.format(
                        namespace=namespace, name=name, kind=kind,
                        exception=e.body
                    )
                else:
                    kubernetes_object['status'] = 'creating_failed'
                    msg = '''The request to create a resource failed.
                    namespace: {namespace}, name: {name},kind: {kind},
                    Reason: {exception}'''.format(
                        namespace=namespace, name=name, kind=kind,
                        exception=e
                    )
                LOG.error(msg)
                raise exceptions.CreateApiFalse(error=msg)
            new_k8s_objs.append(kubernetes_object)
        return new_k8s_objs

    def _get_lower_case_name(self, name):
        name = name.strip()
        return re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower()

    def _init_k8s_obj(self, obj, content, must_param):
        for key, value in content.items():
            param_value = self._get_lower_case_name(key)
            if hasattr(obj, param_value) and \
                    not isinstance(value, dict) and \
                    not isinstance(value, list):
                setattr(obj, param_value, value)
            elif isinstance(value, dict):
                obj_name = obj.openapi_types.get(param_value)
                if obj_name == 'dict(str, str)':
                    setattr(obj, param_value, value)
                else:
                    if obj_name in must_param.keys():
                        rely_obj = eval('client.' + obj_name +
                                       must_param.get(obj_name))
                    else:
                        rely_obj = eval('client.' + obj_name + '()')
                    self._init_k8s_obj(rely_obj, value, must_param)
                    setattr(obj, param_value, rely_obj)
            elif isinstance(value, list):
                obj_name = obj.openapi_types.get(param_value)
                if obj_name == 'list[str]':
                    setattr(obj, param_value, value)
                else:
                    rely_objs = []
                    rely_obj_name = \
                        re.findall(r".*\[([^\[\]]*)\].*", obj_name)[0]
                    for v in value:
                        if rely_obj_name in must_param.keys():
                            rely_obj = eval('client.' + rely_obj_name +
                                           must_param.get(rely_obj_name))
                        else:
                            rely_obj = \
                                eval('client.' + rely_obj_name + '()')
                        self._init_k8s_obj(rely_obj, v, must_param)
                        rely_objs.append(rely_obj)
                    setattr(obj, param_value, rely_objs)

    def _sort_k8s_obj(self, k8s_objs):
        pos = 0
        objs = k8s_objs
        sorted_k8s_objs = list()
        for sort_index, kind in enumerate(self.RESOURCE_CREATION_ORDER):
            for obj_index, obj in enumerate(objs):
                if obj["object"].kind == kind:
                    sorted_k8s_objs.append(objs.pop(obj_index))
            if sort_index == OTHER_RESOURCE_SORT_POSITION:
                pos = len(sorted_k8s_objs)
        for obj in objs:
            sorted_k8s_objs.insert(pos, obj)

        return sorted_k8s_objs

    def get_object_meta(self, content):
        must_param = {}
        v1_object_meta = client.V1ObjectMeta()
        self._init_k8s_obj(v1_object_meta, content, must_param)
        return v1_object_meta

    # config_labels configures label
    def config_labels(self, deployment_name=None, scaling_name=None):
        label = dict()
        if deployment_name:
            label.update({"selector": deployment_name})
        if scaling_name:
            label.update({"scaling_name": scaling_name})
        return label

    # Init resource requirement for container
    def init_resource_requirements(self, container):
        limits = dict()
        requests = dict()
        if container.num_cpus:
            limits.update({'cpu': container.num_cpus})
            requests.update({'cpu': container.num_cpus})
        if container.mem_size:
            limits.update({'memory': container.mem_size})
            requests.update({'memory': container.mem_size})
        return client.V1ResourceRequirements(limits=limits,
                                             requests=requests)

    def init_envs(self, container_props, name):
        config = container_props.config
        config_dict = self.pre_process_config(config)
        configmap_name = name

        list_envs = []
        for key in config_dict:
            config_map_ref = client.V1ConfigMapKeySelector(
                key=key,
                name=configmap_name)
            env_var = client.V1EnvVarSource(
                config_map_key_ref=config_map_ref)
            env_object = client.V1EnvVar(
                name=key,
                value_from=env_var)
            list_envs.append(env_object)
        return list_envs

    # Init container object
    def init_containers(self, container_props, limit_resource, name):
        list_env_var = self.init_envs(container_props, name)
        container_name = self.pre_process_name(container_props.name)
        list_container_port = list()
        if container_props.ports:
            for container_port in container_props.ports:
                port = int(container_port)
                cport = client.V1ContainerPort(container_port=port)
                list_container_port.append(cport)
        container = client.V1Container(
            name=container_name,
            image=container_props.image,
            ports=list_container_port,
            resources=limit_resource,
            command=container_props.command,
            args=container_props.args,
            env=list_env_var,
            image_pull_policy="IfNotPresent")
        return container

    # init_deployment initializes Kubernetes Pod object
    def init_deployment(self, tosca_kube_obj, kube_obj_name):
        """Instantiate the deployment object"""

        deployment_name = kube_obj_name
        # Create a list of container, which made a Pod
        containers = list()
        for container_prop in tosca_kube_obj.containers:
            limit_resource = self.init_resource_requirements(container_prop)
            container = self.init_containers(
                container_props=container_prop,
                limit_resource=limit_resource,
                name=deployment_name)
            containers.append(container)

        # Make a label with pattern {"selector": "deployment_name"}
        if tosca_kube_obj.scaling_object:
            scaling_name = tosca_kube_obj.scaling_object.scaling_name
            update_label = self.config_labels(deployment_name=deployment_name,
                                              scaling_name=scaling_name)
        else:
            update_label = self.config_labels(deployment_name=deployment_name)
        if tosca_kube_obj.labels:
            if 'selector' in update_label:
                del update_label['selector']
            update_label.update(tosca_kube_obj.labels)
        labels = update_label

        # Create and configure a spec section
        pod_template = client.V1PodTemplateSpec(
            metadata=client.V1ObjectMeta(
                labels=labels, annotations=tosca_kube_obj.annotations),
            spec=client.V1PodSpec(containers=containers))
        # Create the specification of deployment
        label_selector = client.V1LabelSelector(match_labels=labels)
        deployment_spec = client.V1DeploymentSpec(
            template=pod_template, selector=label_selector)
        metadata = client.V1ObjectMeta(name=deployment_name, labels=labels)

        # Instantiate the deployment object
        deployment = client.V1Deployment(
            api_version="apps/v1",
            kind="Deployment",
            metadata=metadata,
            spec=deployment_spec)
        return deployment

    # init_hpa initializes Kubernetes Horizon Pod Auto-scaling object
    def init_hpa(self, tosca_kube_obj, kube_obj_name):
        scaling_props = tosca_kube_obj.scaling_object
        hpa = None
        if scaling_props:
            min_replicas = scaling_props.min_replicas
            max_replicas = scaling_props.max_replicas
            cpu_util = scaling_props.target_cpu_utilization_percentage
            deployment_name = kube_obj_name

            # Create target Deployment object
            target = client.V1CrossVersionObjectReference(
                api_version="apps/v1",
                kind="Deployment",
                name=deployment_name)
            # Create the specification of horizon pod auto-scaling
            hpa_spec = client.V1HorizontalPodAutoscalerSpec(
                min_replicas=min_replicas,
                max_replicas=max_replicas,
                target_cpu_utilization_percentage=cpu_util,
                scale_target_ref=target)
            metadata = client.V1ObjectMeta(name=deployment_name)
            # Create Horizon Pod Auto-Scaling
            hpa = client.V1HorizontalPodAutoscaler(
                api_version="autoscaling/v1",
                kind="HorizontalPodAutoscaler",
                spec=hpa_spec,
                metadata=metadata)
        return hpa

    # init_service initializes Kubernetes service object
    def init_service(self, tosca_kube_obj, kube_obj_name):
        list_service_port = list()
        service_label = tosca_kube_obj.labels
        for port in tosca_kube_obj.mapping_ports:
            if COLON_CHARACTER in port:
                ports = port.split(COLON_CHARACTER)
                published_port = int(ports[0])
                target_port = int(ports[1])
            else:
                target_port = published_port = int(port)
            service_port = client.V1ServicePort(
                name=str(published_port),
                port=published_port,
                target_port=target_port)
            list_service_port.append(service_port)

        deployment_name = kube_obj_name
        selector_by_name = self.config_labels(deployment_name)
        if tosca_kube_obj.labels:
            selectors = tosca_kube_obj.labels.copy()
        else:
            selectors = selector_by_name
        if tosca_kube_obj.mgmt_connection_point:
            service_label['management_connection'] = 'True'
        if tosca_kube_obj.network_name:
            service_label['network_name'] = tosca_kube_obj.network_name
        service_label['vdu_name'] = tosca_kube_obj.name

        metadata = client.V1ObjectMeta(name=deployment_name,
                                       labels=service_label)
        if tosca_kube_obj.service_type:
            service_type = tosca_kube_obj.service_type
        else:
            service_type = None
        service_spec = client.V1ServiceSpec(
            selector=selectors,
            ports=list_service_port,
            type=service_type)

        service = client.V1Service(
            api_version="v1",
            kind="Service",
            spec=service_spec,
            metadata=metadata)
        return service

    # init_config_map initializes Kubernetes ConfigMap object
    def init_configmap(self, container_props, kube_obj_name):
        config_map = None
        if container_props.config:
            configmap_name = kube_obj_name
            metadata = client.V1ObjectMeta(name=configmap_name)
            config_dict = self.pre_process_config(container_props.config)
            config_map = client.V1ConfigMap(
                api_version="v1",
                kind="ConfigMap",
                data=config_dict,
                metadata=metadata)
        return config_map

    def pre_process_name(self, name):
        # replace '_' by '-' to meet Kubernetes' requirement
        new_name = name.replace(DASH_CHARACTER, HYPHEN_CHARACTER).lower()
        return new_name

    def pre_process_config(self, config):
        # Split by separating lines
        config_dict = {}
        if config:
            configs = config.split(NEWLINE_CHARACTER)
            for config_item in configs:
                # Ignore if config_item is null
                if config_item:
                    # Strip all types of white-space characters
                    config_item = config_item.replace(
                        WHITE_SPACE_CHARACTER,
                        NON_WHITE_SPACE_CHARACTER)
                    config_prop = config_item.split(COLON_CHARACTER)
                    config_dict[config_prop[0]] = config_prop[1]
            # config_dict has the pattern such as
            # {'param1': 'key1', 'param0': 'key0'}
        return config_dict
