Example #1
0
    def _create_pks_context_for_all_accounts_in_org(self):
        """Create PKS context for accounts in a given Org.

        If user is Sysadmin
            Creates PKS contexts for all PKS accounts defined in the entire
            system.
        else
            Creates PKS contexts for all PKS accounts assigned to the org.
            However if separate service accounts for each org hasn't been
            configued by admin via pks.yaml, then PKS accounts of the PKS
            server corresponding to the vCenters powering the individual
            orgVDC of the org will be picked up for creating the PKS contexts.

        :return: list of dict, where each dictionary is a PKS context

        :rtype: list
        """
        if not self.pks_cache:
            return []

        if self.vcd_client.is_sysadmin():
            all_pks_account_info = \
                self.pks_cache.get_all_pks_account_info_in_system()
            pks_ctx_list = [
                OvdcCache.construct_pks_context(pks_account_info,
                                                credentials_required=True)
                for pks_account_info in all_pks_account_info
            ]
            return pks_ctx_list

        org_name = self.session.get('org')
        if self.pks_cache.do_orgs_have_exclusive_pks_account():
            pks_account_infos = \
                self.pks_cache.get_exclusive_pks_accounts_info_for_org(
                    org_name)
            pks_ctx_list = [
                OvdcCache.construct_pks_context(pks_account_info,
                                                credentials_required=True)
                for pks_account_info in pks_account_infos
            ]
        else:
            org_resource = self.vcd_client.get_org()
            org = Org(self.vcd_client, resource=org_resource)
            vdc_names = [vdc['name'] for vdc in org.list_vdcs()]
            # Constructing dict instead of list to avoid duplicates
            # TODO() figure out a way to add pks contexts to a set directly
            pks_ctx_dict = {}
            for vdc_name in vdc_names:
                # this is a full blown pks_account_info + pvdc_info +
                # compute_profile_name dictionary
                ctr_prov_ctx = \
                    self.ovdc_cache.get_ovdc_container_provider_metadata(
                        ovdc_name=vdc_name, org_name=org_name,
                        credentials_required=True)
                if ctr_prov_ctx[K8S_PROVIDER_KEY] == K8sProviders.PKS:
                    pks_ctx_dict[ctr_prov_ctx['vc']] = ctr_prov_ctx

            pks_ctx_list = list(pks_ctx_dict.values())

        return pks_ctx_list
    def ovdc_info_for_kubernetes(self):
        """Info on ovdc for k8s deployment on the given container provider.

        :return: result object

        :rtype: dict

        :raises CseServerError: if the user is not system administrator.
        """
        result = dict()
        self._connect_tenant()
        if self.tenant_client.is_sysadmin():
            ovdc_cache = OvdcCache(self.tenant_client)
            metadata = ovdc_cache.get_ovdc_container_provider_metadata(
                self.body.get('ovdc_name', None),
                ovdc_id=self.body.get('ovdc_id', None),
                org_name=self.body.get('org_name', None))
            # remove username, secret from sending to client
            metadata.pop('username', None)
            metadata.pop('secret', None)
            result = dict()
            result['status_code'] = OK
            result['body'] = metadata
            return result
        else:
            raise CseServerError("Unauthorized Operation")
    def enable_ovdc_for_kubernetes(self):
        """Enable ovdc for k8-cluster deployment on given container provider.

        :return: result object

        :rtype: dict

        :raises CseServerError: if the user is not system administrator.
        """
        result = dict()
        self._connect_tenant()
        if self.tenant_client.is_sysadmin():
            ovdc_cache = OvdcCache(self.tenant_client)
            task = ovdc_cache.set_ovdc_container_provider_metadata(
                self.body['ovdc_name'],
                ovdc_id=self.body.get('ovdc_id', None),
                container_provider=self.body.get('container_provider', None),
                pks_plans=self.body['pks_plans'],
                org_name=self.body.get('org_name', None))
            response_body = dict()
            response_body['ovdc_name'] = self.body['ovdc_name']
            response_body['task_href'] = task.get('href')
            result['body'] = response_body
            result['status_code'] = ACCEPTED
            return result
        else:
            raise CseServerError("Unauthorized Operation")
Example #4
0
 def __init__(self, request_headers, request_query_params, request_spec):
     self.req_headers = request_headers
     self.req_qparams = request_query_params
     self.req_spec = request_spec
     self.pks_cache = get_pks_cache()
     self.ovdc_cache = OvdcCache(get_vcd_sys_admin_client())
     self.is_ovdc_present_in_request = False
     config = get_server_runtime_config()
     self.vcd_client, self.session = connect_vcd_user_via_token(
         vcd_uri=config['vcd']['host'],
         headers=self.req_headers,
         verify_ssl_certs=config['vcd']['verify'])
Example #5
0
    def _get_ovdc_params(self):
        ovdc_id = self.req_spec.get('ovdc_id')
        org_name = self.req_spec.get('org_name')
        pks_plans = self.req_spec['pks_plans']
        ovdc = self.ovdc_cache.get_ovdc(ovdc_id=ovdc_id)
        pvdc_id = self.ovdc_cache.get_pvdc_id(ovdc)

        pks_context = None
        if self.req_spec[CONTAINER_PROVIDER_KEY] == CtrProvType.PKS.value:
            if not self.pks_cache:
                raise CseServerError('PKS config file does not exist')
            pvdc_info = self.pks_cache.get_pvdc_info(pvdc_id)
            pks_account_info = self.pks_cache.get_pks_account_info(
                org_name, pvdc_info.vc)
            nsxt_info = self.pks_cache.get_nsxt_info(pvdc_info.vc)

            pks_compute_profile_name = \
                self.ovdc_cache.get_compute_profile_name(
                    ovdc_id, ovdc.resource.get('name'))
            pks_context = OvdcCache.construct_pks_context(
                pks_account_info=pks_account_info,
                pvdc_info=pvdc_info,
                nsxt_info=nsxt_info,
                pks_compute_profile_name=pks_compute_profile_name,
                pks_plans=pks_plans,
                credentials_required=True)

        return pks_context, ovdc
    def _get_ovdc_params(self):
        ovdc_id = self.req_spec.get('ovdc_id')
        org_name = self.req_spec.get('org_name')
        pks_plans = self.req_spec['pks_plans']
        pks_cluster_domain = self.req_spec['pks_cluster_domain']
        ovdc = self.ovdc_cache.get_ovdc(ovdc_id=ovdc_id)
        pvdc_id = self.ovdc_cache.get_pvdc_id(ovdc)

        pks_context = None
        if self.req_spec[K8S_PROVIDER_KEY] == K8sProviders.PKS:
            if not self.pks_cache:
                raise CseServerError('PKS config file does not exist')
            pvdc_info = self.pks_cache.get_pvdc_info(pvdc_id)
            if not pvdc_info:
                LOGGER.debug(f"pvdc '{pvdc_id}' is not backed "
                             f"by PKS-managed-vSphere resources")
                raise CseServerError(f"'{ovdc.resource.get('name')}' is not "
                                     f"eligible to provide resources for "
                                     f"PKS clusters. Refer debug logs for more"
                                     f" details.")
            pks_account_info = self.pks_cache.get_pks_account_info(
                org_name, pvdc_info.vc)
            nsxt_info = self.pks_cache.get_nsxt_info(pvdc_info.vc)

            pks_compute_profile_name = \
                self.ovdc_cache.get_compute_profile_name(
                    ovdc_id, ovdc.resource.get('name'))
            pks_context = OvdcCache.construct_pks_context(
                pks_account_info=pks_account_info,
                pvdc_info=pvdc_info,
                nsxt_info=nsxt_info,
                pks_compute_profile_name=pks_compute_profile_name,
                pks_plans=pks_plans,
                pks_cluster_domain=pks_cluster_domain,
                credentials_required=True)

        return pks_context, ovdc
Example #7
0
class BrokerManager(object):
    """Manage calls to vCD and PKS brokers.

    Handles:
    Pre-processing of requests to brokers
    Post-processing of results from brokers.
    """
    def __init__(self, request_headers, request_query_params, request_spec):
        self.req_headers = request_headers
        self.req_qparams = request_query_params
        self.req_spec = request_spec
        self.pks_cache = get_pks_cache()
        self.ovdc_cache = OvdcCache(get_vcd_sys_admin_client())
        self.is_ovdc_present_in_request = False
        config = get_server_runtime_config()
        self.vcd_client, self.session = connect_vcd_user_via_token(
            vcd_uri=config['vcd']['host'],
            headers=self.req_headers,
            verify_ssl_certs=config['vcd']['verify'])

    @exception_handler
    def invoke(self, op):
        """Invoke right broker(s) to perform the operation requested.

        Might result in further (pre/post)processing on the request/result(s).


        Depending on the operation requested, this method may do one or more
        of below mentioned points.
        1. Extract and construct the relevant params for the operation.
        2. Choose right broker to perform the operation/method requested.
        3. Scan through available brokers to aggregate (or) filter results.
        4. Construct and return the HTTP response

        :param Operation op: Operation to be performed by one of the brokers.

        :return result: HTTP response

        :rtype: dict
        """
        result = {}
        result['body'] = []
        result['status_code'] = OK

        self.is_ovdc_present_in_request = self.req_spec.get('vdc') or \
            self.req_qparams.get('vdc')
        if op == Operation.INFO_OVDC:
            ovdc_id = self.req_spec.get('ovdc_id')
            # TODO() Constructing response should be moved out of this layer
            result['body'] = self.ovdc_cache. \
                get_ovdc_container_provider_metadata(ovdc_id=ovdc_id)
            result['status_code'] = OK
        elif op == Operation.ENABLE_OVDC:
            pks_ctx, ovdc = self._get_ovdc_params()
            if self.req_spec[CONTAINER_PROVIDER_KEY] == CtrProvType.PKS.value:
                self._create_pks_compute_profile(pks_ctx)
            task = self.ovdc_cache. \
                set_ovdc_container_provider_metadata(
                    ovdc,
                    container_prov_data=pks_ctx,
                    container_provider=self.req_spec[CONTAINER_PROVIDER_KEY])
            # TODO() Constructing response should be moved out of this layer
            result['body'] = {'task_href': task.get('href')}
            result['status_code'] = ACCEPTED
        elif op == Operation.GET_CLUSTER:
            cluster_spec = \
                {'cluster_name': self.req_spec.get('cluster_name', None)}
            result['body'] = self._get_cluster_info(**cluster_spec)[0]
        elif op == Operation.LIST_CLUSTERS:
            result['body'] = self._list_clusters()
        elif op == Operation.DELETE_CLUSTER:
            cluster_spec = \
                {'cluster_name': self.req_spec.get('cluster_name', None)}
            result['body'] = self._delete_cluster(**cluster_spec)
            result['status_code'] = ACCEPTED
        elif op == Operation.RESIZE_CLUSTER:
            # TODO(resize_cluster) Once VcdBroker.create_nodes() is hooked to
            #  broker_manager, ensure broker.resize_cluster returns only
            #  response body. Construct the remainder of the response here.
            #  This cannot be done at the moment as @exception_handler cannot
            #  be removed on create_nodes() as of today (Mar 15, 2019).
            cluster_spec = \
                {'cluster_name': self.req_spec.get('cluster_name', None),
                 'node_count': self.req_spec.get('node_count', None)
                 }
            result['body'] = self._resize_cluster(**cluster_spec)
            result['status_code'] = ACCEPTED
        elif op == Operation.GET_CLUSTER_CONFIG:
            cluster_spec = \
                {'cluster_name': self.req_spec.get('cluster_name', None)}
            result['body'] = \
                self._get_cluster_config(**cluster_spec)
        elif op == Operation.CREATE_CLUSTER:
            # TODO(ClusterSpec) Create an inner class "ClusterSpec"
            #  in abstract_broker.py and have subclasses define and use it
            #  as instance variable.
            #  Method 'Create_cluster' in VcdBroker and PksBroker should take
            #  ClusterSpec either as a param (or)
            #  read from instance variable (if needed only).
            cluster_spec = {
                'cluster_name': self.req_spec.get('cluster_name', None),
                'vdc_name': self.req_spec.get('vdc', None),
                'node_count': self.req_spec.get('node_count', None),
                'storage_profile': self.req_spec.get('storage_profile', None),
                'network_name': self.req_spec.get('network', None),
                'template': self.req_spec.get('template', None),
                'pks_plan': self.req_spec.get('pks_plan', None),
                'pks_ext_host': self.req_spec.get('pks_ext_host', None)
            }
            result['body'] = self._create_cluster(**cluster_spec)
            result['status_code'] = ACCEPTED
        elif op == Operation.LIST_OVDCS:
            result['body'] = self._list_ovdcs()

        return result

    def _list_ovdcs(self):
        """Get list of ovdcs.

        If client is sysadmin,
            Gets all ovdcs of all organizations.
        Else
            Gets all ovdcs of the organization in context.
        """
        if self.vcd_client.is_sysadmin():
            org_resource_list = self.vcd_client.get_org_list()
        else:
            org_resource_list = list(self.vcd_client.get_org())

        ovdc_list = []
        for org_resource in org_resource_list:
            org = Org(self.vcd_client, resource=org_resource)
            vdc_list = org.list_vdcs()
            for vdc in vdc_list:
                ctr_prov_ctx = \
                    self.ovdc_cache.get_ovdc_container_provider_metadata(
                        ovdc_name=vdc['name'], org_name=org.get_name(),
                        credentials_required=False)
                vdc_dict = {
                    'org': org.get_name(),
                    'name': vdc['name'],
                    CONTAINER_PROVIDER_KEY:
                    ctr_prov_ctx[CONTAINER_PROVIDER_KEY]
                }
                ovdc_list.append(vdc_dict)
        return ovdc_list

    def _get_cluster_info(self, **cluster_spec):
        """Get cluster details directly from cloud provider.

        Logic of the method is as follows.

        If 'ovdc' is present in the cluster spec,
            choose the right broker (by identifying the container_provider
            (vcd|pks) defined for that ovdc) to do get_cluster operation.
        else
            Invoke set of all (vCD/PKS) brokers in the org to find the cluster

        :return: a tuple of cluster information as dictionary and the broker
            instance used to find the cluster information.

        :rtype: tuple
        """
        cluster_name = cluster_spec['cluster_name']
        if self.is_ovdc_present_in_request:
            broker = self.get_broker_based_on_vdc()
            return broker.get_cluster_info(cluster_name=cluster_name), broker
        else:
            cluster, broker = self._find_cluster_in_org(cluster_name)
            if cluster:
                return cluster, broker

        raise ClusterNotFoundError(f'cluster {cluster_name} not found '
                                   f'either in vCD or PKS')

    def _get_cluster_config(self, **cluster_spec):
        """Get the cluster configuration.

        :param str cluster_name: Name of cluster.

        :return: Cluster config.

        :rtype: str
        """
        cluster_name = cluster_spec['cluster_name']
        if self.is_ovdc_present_in_request:
            broker = self.get_broker_based_on_vdc()
            return broker.get_cluster_config(cluster_name=cluster_name)
        else:
            cluster, broker = self._find_cluster_in_org(cluster_name)
            if cluster:
                return broker.get_cluster_config(cluster_name=cluster['name'])

        raise ClusterNotFoundError(f'cluster {cluster_name} not found '
                                   f'either in vCD or PKS')

    def _list_clusters(self):
        """Logic of the method is as follows.

        If 'ovdc' is present in the body,
            choose the right broker (by identifying the container_provider
            (vcd|pks) defined for that ovdc) to do list_clusters operation.
        Else
            Invoke set of all (vCD/PKS)brokers in the org to do list_clusters.
            Post-process the result returned by each broker.
            Aggregate all the results into one.
        """
        if self.is_ovdc_present_in_request:
            broker = self.get_broker_based_on_vdc()
            return broker.list_clusters()
        else:
            common_cluster_properties = ('name', 'vdc', 'status')
            vcd_broker = VcdBroker(self.req_headers, self.req_spec)
            vcd_clusters = []
            for cluster in vcd_broker.list_clusters():
                vcd_cluster = {
                    k: cluster.get(k, None)
                    for k in common_cluster_properties
                }
                vcd_cluster[CONTAINER_PROVIDER_KEY] = CtrProvType.VCD.value
                vcd_clusters.append(vcd_cluster)

            pks_clusters = []
            pks_ctx_list = self._create_pks_context_for_all_accounts_in_org()
            for pks_ctx in pks_ctx_list:
                pks_broker = PKSBroker(self.req_headers, self.req_spec,
                                       pks_ctx)
                for cluster in pks_broker.list_clusters():
                    pks_cluster = self._get_truncated_cluster_info(
                        cluster, pks_broker, common_cluster_properties)
                    pks_cluster[CONTAINER_PROVIDER_KEY] = CtrProvType.PKS.value
                    pks_clusters.append(pks_cluster)
            return vcd_clusters + pks_clusters

    def _resize_cluster(self, **cluster_spec):
        cluster, broker = self._get_cluster_info(**cluster_spec)
        return broker.resize_cluster(curr_cluster_info=cluster, **cluster_spec)

    def _delete_cluster(self, **cluster_spec):
        cluster_name = cluster_spec['cluster_name']
        _, broker = self._get_cluster_info(**cluster_spec)
        return broker.delete_cluster(cluster_name=cluster_name)

    def _create_cluster(self, **cluster_spec):
        cluster_name = cluster_spec['cluster_name']
        cluster = self._find_cluster_in_org(cluster_name)[0]
        if not cluster:
            broker = self.get_broker_based_on_vdc()
            return broker.create_cluster(**cluster_spec)
        else:
            raise CseServerError(f'Cluster with name: {cluster_name} '
                                 f'already found')

    def _find_cluster_in_org(self, cluster_name):
        """Invoke set of all (vCD/PKS)brokers in the org to find the cluster.

        If cluster found:
            Return a tuple of (cluster and the broker instance used to find
            the cluster)
        Else:
            (None, None) if cluster not found.
        """
        vcd_broker = VcdBroker(self.req_headers, self.req_spec)
        try:
            return vcd_broker.get_cluster_info(cluster_name), vcd_broker
        except Exception as err:
            LOGGER.debug(f"Get cluster info on {cluster_name} failed "
                         f"on vCD with error: {err}")

        pks_ctx_list = self._create_pks_context_for_all_accounts_in_org()
        for pks_ctx in pks_ctx_list:
            pksbroker = PKSBroker(self.req_headers, self.req_spec, pks_ctx)
            try:
                return pksbroker.get_cluster_info(cluster_name=cluster_name),\
                    pksbroker
            except Exception as err:
                LOGGER.debug(f"Get cluster info on {cluster_name} failed "
                             f"on {pks_ctx['host']} with error: {err}")

        return None, None

    def _create_pks_context_for_all_accounts_in_org(self):
        """Create PKS context for accounts in a given Org.

        If user is Sysadmin
            Creates PKS contexts for all PKS accounts defined in the entire
            system.
        else
            Creates PKS contexts for all PKS accounts assigned to the org.
            However if separate service accounts for each org hasn't been
            configued by admin via pks.yaml, then PKS accounts of the PKS
            server corresponding to the vCenters powering the individual
            orgVDC of the org will be picked up for creating the PKS contexts.

        :return: list of dict, where each dictionary is a PKS context

        :rtype: list
        """
        if not self.pks_cache:
            return []

        if self.vcd_client.is_sysadmin():
            all_pks_account_info = \
                self.pks_cache.get_all_pks_account_info_in_system()
            pks_ctx_list = [
                OvdcCache.construct_pks_context(pks_account_info,
                                                credentials_required=True)
                for pks_account_info in all_pks_account_info
            ]
            return pks_ctx_list

        org_name = self.session.get('org')
        if self.pks_cache.do_orgs_have_exclusive_pks_account():
            pks_account_infos = \
                self.pks_cache.get_exclusive_pks_accounts_info_for_org(
                    org_name)
            pks_ctx_list = [
                OvdcCache.construct_pks_context(pks_account_info,
                                                credentials_required=True)
                for pks_account_info in pks_account_infos
            ]
        else:
            org_resource = self.vcd_client.get_org()
            org = Org(self.vcd_client, resource=org_resource)
            vdc_names = [vdc['name'] for vdc in org.list_vdcs()]
            # Constructing dict instead of list to avoid duplicates
            # TODO() figure out a way to add pks contexts to a set directly
            pks_ctx_dict = {}
            for vdc_name in vdc_names:
                # this is a full blown pks_account_info + pvdc_info +
                # compute_profile_name dictionary
                ctr_prov_ctx = \
                    self.ovdc_cache.get_ovdc_container_provider_metadata(
                        ovdc_name=vdc_name, org_name=org_name,
                        credentials_required=True)
                if ctr_prov_ctx[CONTAINER_PROVIDER_KEY] == \
                        CtrProvType.PKS.value:
                    pks_ctx_dict[ctr_prov_ctx['vc']] = ctr_prov_ctx

            pks_ctx_list = list(pks_ctx_dict.values())

        return pks_ctx_list

    def _get_truncated_cluster_info(self, cluster, pks_broker,
                                    cluster_property_keys):
        pks_cluster = {k: cluster.get(k) for k in cluster_property_keys}
        # Extract vdc name from compute-profile-name
        # Example: vdc name in the below compute profile is: vdc-PKS1
        # compute-profile: cp--f3272127-9b7f-4f90-8849-0ee70a28be56--vdc-PKS1
        compute_profile_name = cluster.get('compute-profile-name', '')
        pks_cluster['vdc'] = compute_profile_name.split('--')[-1] \
            if compute_profile_name else ''
        pks_cluster['status'] = \
            cluster.get('last-action', '').lower() + ' ' + \
            pks_cluster.get('status', '').lower()
        return pks_cluster

    def get_broker_based_on_vdc(self):
        """Get the broker based on ovdc.

        :return: broker

        :rtype: container_service_extension.abstract_broker.AbstractBroker
        """
        ovdc_name = self.req_spec.get('vdc') or \
            self.req_qparams.get('vdc')
        org_name = self.req_spec.get('org') or \
            self.req_qparams.get('org') or \
            self.session.get('org')

        LOGGER.debug(f"org_name={org_name};vdc_name=\'{ovdc_name}\'")

        # Get the ovdc metadata using org and ovdc.
        # Create the right broker based on value of 'container_provider'.
        # Fall back to DefaultBroker for missing ovdc or org.
        if ovdc_name and org_name:
            ctr_prov_ctx = \
                self.ovdc_cache.get_ovdc_container_provider_metadata(
                    ovdc_name=ovdc_name, org_name=org_name,
                    credentials_required=True, nsxt_info_required=True)
            LOGGER.debug(
                f"ovdc metadata for {ovdc_name}-{org_name}=>{ctr_prov_ctx}")
            if ctr_prov_ctx.get(CONTAINER_PROVIDER_KEY) == \
                    CtrProvType.PKS.value:
                return PKSBroker(self.req_headers,
                                 self.req_spec,
                                 pks_ctx=ctr_prov_ctx)
            elif ctr_prov_ctx.get(CONTAINER_PROVIDER_KEY) == \
                    CtrProvType.VCD.value:
                return VcdBroker(self.req_headers, self.req_spec)
            else:
                raise CseServerError(f"Vdc '{ovdc_name}' is not enabled for "
                                     "Kubernetes cluster deployment")
        else:
            # TODO() - This call should be based on a boolean flag
            # Specify flag in config file whether to have default
            # handling is required for missing ovdc or org.
            return VcdBroker(self.req_headers, self.req_spec)

    def _get_ovdc_params(self):
        ovdc_id = self.req_spec.get('ovdc_id')
        org_name = self.req_spec.get('org_name')
        pks_plans = self.req_spec['pks_plans']
        ovdc = self.ovdc_cache.get_ovdc(ovdc_id=ovdc_id)
        pvdc_id = self.ovdc_cache.get_pvdc_id(ovdc)

        pks_context = None
        if self.req_spec[CONTAINER_PROVIDER_KEY] == CtrProvType.PKS.value:
            if not self.pks_cache:
                raise CseServerError('PKS config file does not exist')
            pvdc_info = self.pks_cache.get_pvdc_info(pvdc_id)
            pks_account_info = self.pks_cache.get_pks_account_info(
                org_name, pvdc_info.vc)
            nsxt_info = self.pks_cache.get_nsxt_info(pvdc_info.vc)

            pks_compute_profile_name = \
                self.ovdc_cache.get_compute_profile_name(
                    ovdc_id, ovdc.resource.get('name'))
            pks_context = OvdcCache.construct_pks_context(
                pks_account_info=pks_account_info,
                pvdc_info=pvdc_info,
                nsxt_info=nsxt_info,
                pks_compute_profile_name=pks_compute_profile_name,
                pks_plans=pks_plans,
                credentials_required=True)

        return pks_context, ovdc

    def _create_pks_compute_profile(self, pks_ctx):
        ovdc_id = self.req_spec.get('ovdc_id')
        org_name = self.req_spec.get('org_name')
        ovdc_name = self.req_spec.get('ovdc_name')
        # Compute profile creation
        pks_compute_profile_name = self.\
            ovdc_cache.get_compute_profile_name(ovdc_id, ovdc_name)
        pks_compute_profile_description = f"{org_name}--{ovdc_name}" \
            f"--{ovdc_id}"
        pks_az_name = f"az-{ovdc_name}"
        ovdc_rp_name = f"{ovdc_name} ({ovdc_id})"

        compute_profile_params = PksComputeProfileParams(
            pks_compute_profile_name, pks_az_name,
            pks_compute_profile_description, pks_ctx.get('cpi'),
            pks_ctx.get('datacenter'), pks_ctx.get('cluster'),
            ovdc_rp_name).to_dict()

        LOGGER.debug(f"Creating PKS Compute Profile with name:"
                     f"{pks_compute_profile_name}")

        pksbroker = PKSBroker(self.req_headers, self.req_spec, pks_ctx)
        try:
            pksbroker.create_compute_profile(**compute_profile_params)
        except PksServerError as ex:
            if ex.status == HTTPStatus.CONFLICT.value:
                LOGGER.debug(f"Compute profile name {pks_compute_profile_name}"
                             f" already exists\n{str(ex)}")
            else:
                raise ex
Example #8
0
class BrokerManager(object):
    """Manage calls to vCD and PKS brokers.

    Handles:
    Pre-processing of requests to brokers
    Post-processing of results from brokers.
    """
    def __init__(self, request_headers, request_query_params, request_spec):
        self.req_headers = request_headers
        self.req_qparams = request_query_params
        self.req_spec = request_spec
        self.pks_cache = get_pks_cache()
        self.ovdc_cache = OvdcCache(get_vcd_sys_admin_client())
        self.is_ovdc_present_in_request = False
        config = get_server_runtime_config()
        self.vcd_client, self.session = connect_vcd_user_via_token(
            vcd_uri=config['vcd']['host'],
            headers=self.req_headers,
            verify_ssl_certs=config['vcd']['verify'])

    @exception_handler
    def invoke(self, op):
        """Invoke right broker(s) to perform the operation requested.

        Might result in further (pre/post)processing on the request/result(s).


        Depending on the operation requested, this method may do one or more
        of below mentioned points.
        1. Extract and construct the relevant params for the operation.
        2. Choose right broker to perform the operation/method requested.
        3. Scan through available brokers to aggregate (or) filter results.
        4. Construct and return the HTTP response

        :param Operation op: Operation to be performed by one of the brokers.

        :return result: HTTP response

        :rtype: dict
        """
        result = {}
        result['body'] = []
        result['status_code'] = OK

        self.is_ovdc_present_in_request = self.req_spec.get('vdc') or \
            self.req_qparams.get('vdc')
        if op == Operation.INFO_OVDC:
            ovdc_id = self.req_spec.get('ovdc_id')
            # TODO() Constructing response should be moved out of this layer
            result['body'] = self.ovdc_cache. \
                get_ovdc_container_provider_metadata(ovdc_id=ovdc_id)
            result['status_code'] = OK
        elif op == Operation.ENABLE_OVDC:
            pks_ctx, ovdc = self._get_ovdc_params()
            if self.req_spec[K8S_PROVIDER_KEY] == K8sProviders.PKS:
                self._create_pks_compute_profile(pks_ctx)
            task = self.ovdc_cache. \
                set_ovdc_container_provider_metadata(
                    ovdc,
                    container_prov_data=pks_ctx,
                    container_provider=self.req_spec[K8S_PROVIDER_KEY])
            # TODO() Constructing response should be moved out of this layer
            result['body'] = {'task_href': task.get('href')}
            result['status_code'] = ACCEPTED
        elif op == Operation.GET_CLUSTER:
            cluster_spec = \
                {'cluster_name': self.req_spec.get('cluster_name', None)}
            result['body'] = self._get_cluster_info(**cluster_spec)[0]
        elif op == Operation.LIST_CLUSTERS:
            result['body'] = self._list_clusters()
        elif op == Operation.DELETE_CLUSTER:
            cluster_spec = \
                {'cluster_name': self.req_spec.get('cluster_name', None)}
            result['body'] = self._delete_cluster(**cluster_spec)
            result['status_code'] = ACCEPTED
        elif op == Operation.RESIZE_CLUSTER:
            # TODO(resize_cluster) Once VcdBroker.create_nodes() is hooked to
            #  broker_manager, ensure broker.resize_cluster returns only
            #  response body. Construct the remainder of the response here.
            #  This cannot be done at the moment as @exception_handler cannot
            #  be removed on create_nodes() as of today (Mar 15, 2019).
            cluster_spec = \
                {'cluster_name': self.req_spec.get('cluster_name', None),
                 'node_count': self.req_spec.get('node_count', None)
                 }
            result['body'] = self._resize_cluster(**cluster_spec)
            result['status_code'] = ACCEPTED
        elif op == Operation.GET_CLUSTER_CONFIG:
            cluster_spec = \
                {'cluster_name': self.req_spec.get('cluster_name', None)}
            result['body'] = \
                self._get_cluster_config(**cluster_spec)
        elif op == Operation.CREATE_CLUSTER:
            # TODO(ClusterSpec) Create an inner class "ClusterSpec"
            #  in abstract_broker.py and have subclasses define and use it
            #  as instance variable.
            #  Method 'Create_cluster' in VcdBroker and PksBroker should take
            #  ClusterSpec either as a param (or)
            #  read from instance variable (if needed only).
            cluster_spec = {
                'cluster_name': self.req_spec.get('cluster_name', None),
                'vdc_name': self.req_spec.get('vdc', None),
                'node_count': self.req_spec.get('node_count', None),
                'storage_profile': self.req_spec.get('storage_profile', None),
                'network_name': self.req_spec.get('network', None),
                'template': self.req_spec.get('template', None),
            }
            result['body'] = self._create_cluster(**cluster_spec)
            result['status_code'] = ACCEPTED
        elif op == Operation.LIST_OVDCS:
            list_pks_plans = self.req_spec.get('list_pks_plans', False)
            result['body'] = self._list_ovdcs(list_pks_plans=list_pks_plans)

        return result

    def _list_ovdcs(self, list_pks_plans=False):
        """Get list of ovdcs.

        If client is sysadmin,
            Gets all ovdcs of all organizations.
        Else
            Gets all ovdcs of the organization in context.
        """
        if self.vcd_client.is_sysadmin():
            org_resource_list = self.vcd_client.get_org_list()
        else:
            org_resource_list = list(self.vcd_client.get_org())

        ovdc_list = []
        vc_to_pks_plans_map = {}
        if list_pks_plans:
            if self.vcd_client.is_sysadmin():
                vc_to_pks_plans_map = self._construct_vc_to_pks_map()
            else:
                raise UnauthorizedActionError(
                    'Operation Denied. Plans available only for '
                    'System Administrator.')
        for org_resource in org_resource_list:
            org = Org(self.vcd_client, resource=org_resource)
            vdc_list = org.list_vdcs()
            for vdc in vdc_list:
                ctr_prov_ctx = \
                    self.ovdc_cache.get_ovdc_container_provider_metadata(
                        ovdc_name=vdc['name'], org_name=org.get_name(),
                        credentials_required=False)
                if list_pks_plans:
                    pks_plans, pks_server = self.\
                        _get_pks_plans_and_server_for_vdc(vdc,
                                                          org_resource,
                                                          vc_to_pks_plans_map)
                    vdc_dict = {
                        'org': org.get_name(),
                        'name': vdc['name'],
                        'pks_api_server': pks_server,
                        'available pks plans': pks_plans
                    }
                else:
                    vdc_dict = {
                        'name': vdc['name'],
                        'org': org.get_name(),
                        K8S_PROVIDER_KEY: ctr_prov_ctx[K8S_PROVIDER_KEY]
                    }
                ovdc_list.append(vdc_dict)
        return ovdc_list

    def _get_cluster_info(self, **cluster_spec):
        """Get cluster details directly from cloud provider.

        Logic of the method is as follows.

        If 'ovdc' is present in the cluster spec,
            choose the right broker (by identifying the container_provider
            (vcd|pks) defined for that ovdc) to do get_cluster operation.
        else
            Invoke set of all (vCD/PKS) brokers in the org to find the cluster

        :return: a tuple of cluster information as dictionary and the broker
            instance used to find the cluster information.

        :rtype: tuple
        """
        cluster_name = cluster_spec['cluster_name']
        if self.is_ovdc_present_in_request:
            broker = self.get_broker_based_on_vdc()
            return broker.get_cluster_info(cluster_name=cluster_name), broker
        else:
            cluster, broker = self._find_cluster_in_org(cluster_name)
            if cluster:
                return cluster, broker

        raise ClusterNotFoundError(f"Cluster {cluster_name} not found "
                                   f"either in vCD or PKS")

    def _get_cluster_config(self, **cluster_spec):
        """Get the cluster configuration.

        :param str cluster_name: Name of cluster.

        :return: Cluster config.

        :rtype: str
        """
        cluster_name = cluster_spec['cluster_name']
        if self.is_ovdc_present_in_request:
            broker = self.get_broker_based_on_vdc()
            return broker.get_cluster_config(cluster_name=cluster_name)
        else:
            cluster, broker = self._find_cluster_in_org(cluster_name)
            if cluster:
                return broker.get_cluster_config(cluster_name=cluster['name'])

        raise ClusterNotFoundError(f"Cluster {cluster_name} not found "
                                   f"either in vCD or PKS")

    def _list_clusters(self):
        """Logic of the method is as follows.

        If 'ovdc' is present in the body,
            choose the right broker (by identifying the container_provider
            (vcd|pks) defined for that ovdc) to do list_clusters operation.
        Else
            Invoke set of all (vCD/PKS)brokers in the org to do list_clusters.
            Post-process the result returned by each broker.
            Aggregate all the results into one.
        """
        if self.is_ovdc_present_in_request:
            broker = self.get_broker_based_on_vdc()
            return broker.list_clusters()
        else:
            common_cluster_properties = ('name', 'vdc', 'status', 'org_name')
            vcd_broker = VcdBroker(self.req_headers, self.req_spec)
            vcd_clusters = []
            for cluster in vcd_broker.list_clusters():
                vcd_cluster = {
                    k: cluster.get(k, None)
                    for k in common_cluster_properties
                }
                vcd_cluster[K8S_PROVIDER_KEY] = K8sProviders.NATIVE
                vcd_clusters.append(vcd_cluster)

            pks_clusters = []
            pks_ctx_list = self._create_pks_context_for_all_accounts_in_org()
            for pks_ctx in pks_ctx_list:
                pks_broker = PKSBroker(self.req_headers, self.req_spec,
                                       pks_ctx)
                # Get all cluster information to get vdc name from
                # compute-profile-name
                for cluster in pks_broker.list_clusters(is_admin_request=True):
                    pks_cluster = \
                        PKSBroker.generate_cluster_subset_with_given_keys(
                            cluster, common_cluster_properties)
                    pks_cluster[K8S_PROVIDER_KEY] = K8sProviders.PKS
                    pks_clusters.append(pks_cluster)
            return vcd_clusters + pks_clusters

    def _resize_cluster(self, **cluster_spec):
        cluster, broker = self._get_cluster_info(**cluster_spec)
        return broker.resize_cluster(curr_cluster_info=cluster, **cluster_spec)

    def _delete_cluster(self, **cluster_spec):
        cluster_name = cluster_spec['cluster_name']
        _, broker = self._get_cluster_info(**cluster_spec)
        return broker.delete_cluster(cluster_name=cluster_name)

    def _create_cluster(self, **cluster_spec):
        cluster_name = cluster_spec['cluster_name']
        # 'is_org_admin_search' is used here to prevent cluster creation with
        # same cluster-name by users within org.
        # If it is true, cluster list is filtered by the org name of the
        # logged-in user to check for duplicates.
        cluster, _ = self._find_cluster_in_org(cluster_name,
                                               is_org_admin_search=True)
        if not cluster:
            ctr_prov_ctx = self._get_ctr_prov_ctx_from_ovdc_metadata()
            if ctr_prov_ctx.get(K8S_PROVIDER_KEY) == K8sProviders.PKS:
                cluster_spec['pks_plan'] = ctr_prov_ctx[PKS_PLANS_KEY][0]
                cluster_spec['pks_ext_host'] = f"{cluster_name}." \
                    f"{ctr_prov_ctx[PKS_CLUSTER_DOMAIN_KEY]}"
            broker = self._get_broker_based_on_ctr_prov_ctx(ctr_prov_ctx)
            return broker.create_cluster(**cluster_spec)
        else:
            raise ClusterAlreadyExistsError(
                f"Cluster {cluster_name} already exists.")

    def _find_cluster_in_org(self, cluster_name, is_org_admin_search=False):
        """Invoke set of all (vCD/PKS)brokers in the org to find the cluster.

        'is_org_admin_search' is used here to prevent cluster creation with
        same cluster-name by users within org. If it is true,
        cluster list is filtered by the org name of the logged-in user.

        If cluster found:
            Return a tuple of (cluster and the broker instance used to find
            the cluster)
        Else:
            (None, None) if cluster not found.
        """
        vcd_broker = VcdBroker(self.req_headers, self.req_spec)
        try:
            return vcd_broker.get_cluster_info(cluster_name), vcd_broker
        except Exception as err:
            LOGGER.debug(f"Get cluster info on {cluster_name} failed "
                         f"on vCD with error: {err}")

        pks_ctx_list = self._create_pks_context_for_all_accounts_in_org()
        for pks_ctx in pks_ctx_list:
            pksbroker = PKSBroker(self.req_headers, self.req_spec, pks_ctx)
            try:
                return pksbroker.get_cluster_info(
                    cluster_name=cluster_name,
                    is_org_admin_search=is_org_admin_search), pksbroker
            except PksServerError as err:
                LOGGER.debug(f"Get cluster info on {cluster_name} failed "
                             f"on {pks_ctx['host']} with error: {err}")

        return None, None

    def _create_pks_context_for_all_accounts_in_org(self):
        """Create PKS context for accounts in a given Org.

        If user is Sysadmin
            Creates PKS contexts for all PKS accounts defined in the entire
            system.
        else
            Creates PKS contexts for all PKS accounts assigned to the org.
            However if separate service accounts for each org hasn't been
            configued by admin via pks.yaml, then PKS accounts of the PKS
            server corresponding to the vCenters powering the individual
            orgVDC of the org will be picked up for creating the PKS contexts.

        :return: list of dict, where each dictionary is a PKS context

        :rtype: list
        """
        if not self.pks_cache:
            return []

        if self.vcd_client.is_sysadmin():
            all_pks_account_info = \
                self.pks_cache.get_all_pks_account_info_in_system()
            pks_ctx_list = [
                OvdcCache.construct_pks_context(pks_account_info,
                                                credentials_required=True)
                for pks_account_info in all_pks_account_info
            ]
            return pks_ctx_list

        org_name = self.session.get('org')
        if self.pks_cache.do_orgs_have_exclusive_pks_account():
            pks_account_infos = \
                self.pks_cache.get_exclusive_pks_accounts_info_for_org(
                    org_name)
            pks_ctx_list = [
                OvdcCache.construct_pks_context(pks_account_info,
                                                credentials_required=True)
                for pks_account_info in pks_account_infos
            ]
        else:
            org_resource = self.vcd_client.get_org()
            org = Org(self.vcd_client, resource=org_resource)
            vdc_names = [vdc['name'] for vdc in org.list_vdcs()]
            # Constructing dict instead of list to avoid duplicates
            # TODO() figure out a way to add pks contexts to a set directly
            pks_ctx_dict = {}
            for vdc_name in vdc_names:
                # this is a full blown pks_account_info + pvdc_info +
                # compute_profile_name dictionary
                ctr_prov_ctx = \
                    self.ovdc_cache.get_ovdc_container_provider_metadata(
                        ovdc_name=vdc_name, org_name=org_name,
                        credentials_required=True)
                if ctr_prov_ctx[K8S_PROVIDER_KEY] == K8sProviders.PKS:
                    pks_ctx_dict[ctr_prov_ctx['vc']] = ctr_prov_ctx

            pks_ctx_list = list(pks_ctx_dict.values())

        return pks_ctx_list

    def _get_ctr_prov_ctx_from_ovdc_metadata(self,
                                             ovdc_name=None,
                                             org_name=None):
        ovdc_name = \
            ovdc_name or self.req_spec.get('vdc') or \
            self.req_qparams.get('vdc')
        org_name = \
            org_name or self.req_spec.get('org') or \
            self.req_qparams.get('org') or self.session.get('org')

        if ovdc_name and org_name:
            ctr_prov_ctx = \
                self.ovdc_cache.get_ovdc_container_provider_metadata(
                    ovdc_name=ovdc_name, org_name=org_name,
                    credentials_required=True, nsxt_info_required=True)
            return ctr_prov_ctx

    def _get_broker_based_on_ctr_prov_ctx(self, ctr_prov_ctx):
        # If system is equipped with PKS, use the metadata on ovdc to determine
        # the correct broker, otherwise fallback to vCD for cluster deployment.
        # However if the system is enabled for PKS and has no metadata on odvc
        # or isn't enabled for container deployment raise appropriate
        # exception.
        if self.pks_cache:
            if ctr_prov_ctx:
                if ctr_prov_ctx.get(K8S_PROVIDER_KEY) == K8sProviders.PKS:
                    return PKSBroker(self.req_headers,
                                     self.req_spec,
                                     pks_ctx=ctr_prov_ctx)
                elif ctr_prov_ctx.get(K8S_PROVIDER_KEY) == K8sProviders.NATIVE:
                    return VcdBroker(self.req_headers, self.req_spec)

        else:
            return VcdBroker(self.req_headers, self.req_spec)

        raise CseServerError("Org VDC is not enabled for Kubernetes cluster "
                             "deployment")

    def get_broker_based_on_vdc(self):
        """Get the broker based on ovdc.

        :return: broker

        :rtype: container_service_extension.abstract_broker.AbstractBroker
        """
        ovdc_name = \
            self.req_spec.get('vdc') or \
            self.req_qparams.get('vdc')
        org_name = \
            self.req_spec.get('org') or \
            self.req_qparams.get('org') or \
            self.session.get('org')

        ctr_prov_ctx = self._get_ctr_prov_ctx_from_ovdc_metadata(
            ovdc_name=ovdc_name, org_name=org_name)

        return self._get_broker_based_on_ctr_prov_ctx(ctr_prov_ctx)

    def _get_ovdc_params(self):
        ovdc_id = self.req_spec.get('ovdc_id')
        org_name = self.req_spec.get('org_name')
        pks_plans = self.req_spec['pks_plans']
        pks_cluster_domain = self.req_spec['pks_cluster_domain']
        ovdc = self.ovdc_cache.get_ovdc(ovdc_id=ovdc_id)
        pvdc_id = self.ovdc_cache.get_pvdc_id(ovdc)

        pks_context = None
        if self.req_spec[K8S_PROVIDER_KEY] == K8sProviders.PKS:
            if not self.pks_cache:
                raise CseServerError('PKS config file does not exist')
            pvdc_info = self.pks_cache.get_pvdc_info(pvdc_id)
            if not pvdc_info:
                LOGGER.debug(f"pvdc '{pvdc_id}' is not backed "
                             f"by PKS-managed-vSphere resources")
                raise CseServerError(f"'{ovdc.resource.get('name')}' is not "
                                     f"eligible to provide resources for "
                                     f"PKS clusters. Refer debug logs for more"
                                     f" details.")
            pks_account_info = self.pks_cache.get_pks_account_info(
                org_name, pvdc_info.vc)
            nsxt_info = self.pks_cache.get_nsxt_info(pvdc_info.vc)

            pks_compute_profile_name = \
                create_pks_compute_profile_name_from_vdc_id(ovdc_id)
            pks_context = OvdcCache.construct_pks_context(
                pks_account_info=pks_account_info,
                pvdc_info=pvdc_info,
                nsxt_info=nsxt_info,
                pks_compute_profile_name=pks_compute_profile_name,
                pks_plans=pks_plans,
                pks_cluster_domain=pks_cluster_domain,
                credentials_required=True)

        return pks_context, ovdc

    def _create_pks_compute_profile(self, pks_ctx):
        ovdc_id = self.req_spec.get('ovdc_id')
        org_name = self.req_spec.get('org_name')
        ovdc_name = self.req_spec.get('ovdc_name')
        # Compute profile creation
        pks_compute_profile_name = \
            create_pks_compute_profile_name_from_vdc_id(ovdc_id)
        pks_compute_profile_description = f"{org_name}--{ovdc_name}" \
            f"--{ovdc_id}"
        pks_az_name = f"az-{ovdc_name}"
        ovdc_rp_name = f"{ovdc_name} ({ovdc_id})"

        compute_profile_params = PksComputeProfileParams(
            pks_compute_profile_name, pks_az_name,
            pks_compute_profile_description, pks_ctx.get('cpi'),
            pks_ctx.get('datacenter'), pks_ctx.get('cluster'),
            ovdc_rp_name).to_dict()

        LOGGER.debug(f"Creating PKS Compute Profile with name:"
                     f"{pks_compute_profile_name}")

        pksbroker = PKSBroker(self.req_headers, self.req_spec, pks_ctx)
        try:
            pksbroker.create_compute_profile(**compute_profile_params)
        except PksServerError as ex:
            if ex.status == HTTPStatus.CONFLICT.value:
                LOGGER.debug(f"Compute profile name {pks_compute_profile_name}"
                             f" already exists\n{str(ex)}")
            else:
                raise ex

    def _get_pks_plans_and_server_for_vdc(self, vdc, org_resource,
                                          vc_to_pks_plans_map):
        pks_server = ''
        pks_plans = []
        vc_backing_vdc = self.ovdc_cache.get_ovdc(
            ovdc_name=vdc['name'],
            org_name=org_resource.get('name')).resource.ComputeProviderScope

        pks_plan_and_server_info = vc_to_pks_plans_map.get(vc_backing_vdc, [])
        if len(pks_plan_and_server_info) > 0:
            pks_plans = pks_plan_and_server_info[0]
            pks_server = pks_plan_and_server_info[1]
        return pks_plans, pks_server

    def _construct_vc_to_pks_map(self):
        pks_vc_plans_map = {}
        pks_ctx_list = self._create_pks_context_for_all_accounts_in_org()

        for pks_ctx in pks_ctx_list:
            if pks_ctx['vc'] in pks_vc_plans_map:
                continue
            pks_broker = PKSBroker(self.req_headers, self.req_spec, pks_ctx)
            plans = pks_broker.list_plans()
            plan_names = [plan.get('name') for plan in plans]
            pks_vc_plans_map[pks_ctx['vc']] = [plan_names, pks_ctx['host']]
        return pks_vc_plans_map