Пример #1
0
def test__deep_load_response_OK(mock_send, mock_created):
    mock_created.return_value = True
    vf = Vf()
    vf.identifier = "5689"
    vf._version = "4567"
    vf._status = const.CHECKED_IN
    mock_send.return_value = {
        'resources': [{
            'uuid':
            '5689',
            'name':
            'test',
            'uniqueId':
            '71011',
            'categories': [{
                'name': 'test',
                'subcategories': [{
                    'name': 'test_subcategory'
                }]
            }]
        }]
    }
    vf.deep_load()
    assert vf.unique_identifier == "71011"
    assert vf._category_name == "test"
    assert vf._subcategory_name == "test_subcategory"
    mock_send.assert_called_once_with(
        'GET',
        'Deep Load Vf',
        "{}/sdc1/feProxy/rest/v1/screen?excludeTypes=VFCMT&excludeTypes=Configuration"
        .format(vf.base_front_url),
        headers=headers_sdc_creator(vf.headers))
Пример #2
0
    def deep_load(self) -> None:
        """Deep load Object informations from SDC."""
        url = (f"{self.base_front_url}/sdc1/feProxy/rest/v1/"
               "screen?excludeTypes=VFCMT&excludeTypes=Configuration")
        headers = headers_sdc_creator(SdcResource.headers)
        if self.status == const.UNDER_CERTIFICATION:
            headers = headers_sdc_tester(SdcResource.headers)

        response = self.send_message_json("GET",
                                          "Deep Load {}".format(
                                              type(self).__name__),
                                          url,
                                          headers=headers)

        for resource in response[self._sdc_path()]:
            if resource["invariantUUID"] == self.unique_uuid:
                if resource["uuid"] == self.identifier:
                    self._logger.debug("Resource %s found in %s list",
                                       resource["name"], self._sdc_path())
                    self.unique_identifier = resource["uniqueId"]
                    self._category_name = resource["categories"][0]["name"]
                    subcategories = resource["categories"][0].get(
                        "subcategories", [{}])
                    self._subcategory_name = None if subcategories is None else \
                        subcategories[0].get("name")
                    return
                if self._sdc_path() == "services":
                    for dependency in self.send_message_json(
                            "GET", "Get service dependecies",
                            f"{self._base_create_url()}/services/"
                            f"{resource['uniqueId']}/"
                            "dependencies"):
                        if dependency["version"] == self.version:
                            self.unique_identifier = dependency["uniqueId"]
                            return
Пример #3
0
    def _check_distributed(self) -> bool:
        """Check if service is distributed and update status accordingly."""
        url = "{}/services/distribution/{}".format(self._base_create_url(),
                                                   self.distribution_id)
        headers = headers_sdc_creator(SdcResource.headers)

        status = {}
        for component in components_needing_distribution():
            status[component] = False

        try:
            result = self.send_message_json("GET",
                                            "Check distribution for {}".format(
                                                self.name),
                                            url,
                                            headers=headers)
        except ResourceNotFound:
            msg = f"No distributions found for {self.name} of {self.__class__.__name__}."
            self._logger.debug(msg)
        else:
            status = self._update_components_status(status, result)

        for state in status.values():
            if not state:
                self._distributed = False
                return
        self._distributed = True
Пример #4
0
class Vf(SdcResource):
    """
    ONAP Vf Object used for SDC operations.

    Attributes:
        name (str): the name of the vendor. Defaults to "Generic-Vendor".
        identifier (str): the unique ID of the vendor from SDC.
        status (str): the status of the vendor from SDC.
        version (str): the version ID of the vendor from SDC.
        vsp (Vsp): the Vsp used for VF creation
        uuid (str): the UUID of the VF (which is different from identifier,
                    don't ask why...)
        unique_identifier (str): Yet Another ID, just to puzzle us...

    """

    headers = headers_sdc_creator(SdcResource.headers)

    def __init__(self,
                 name: str = None,
                 sdc_values: Dict[str, str] = None,
                 vsp: Vsp = None):
        """
        Initialize vendor object.

        Args:
            name (optional): the name of the vendor

        """
        super().__init__(sdc_values=sdc_values)
        self.name: str = name or "ONAP-test-VF"
        self.vsp: Vsp = vsp or None
        self._time_wait: int = 10

    def onboard(self) -> None:
        """Onboard the VF in SDC."""
        if not self.status:
            if not self.vsp:
                raise ValueError("No Vsp was given")
            self.create()
            time.sleep(self._time_wait)
            self.onboard()
        elif self.status == const.DRAFT:
            self.submit()
            time.sleep(self._time_wait)
            self.onboard()
        elif self.status == const.CERTIFIED:
            self.load()

    def create(self) -> None:
        """Create the Vf in SDC if not already existing."""
        if self.vsp:
            self._create("vf_create.json.j2", name=self.name, vsp=self.vsp)

    def _really_submit(self) -> None:
        """Really submit the SDC Vf in order to enable it."""
        result = self._action_to_sdc(const.CERTIFY, "lifecycleState")
        if result:
            self.load()
Пример #5
0
def test_distribute(mock_verify):
    svc = Service()
    svc.distribute()
    mock_verify.assert_called_once_with(const.CERTIFIED,
                                        const.DISTRIBUTE,
                                        'distribution',
                                        headers=headers_sdc_creator(
                                            svc.headers))
Пример #6
0
def test_certify(mock_verify):
    svc = Service()
    svc.certify()
    mock_verify.assert_called_once_with(const.CHECKED_IN,
                                        const.CERTIFY,
                                        'lifecycleState',
                                        headers=headers_sdc_creator(
                                            svc.headers))
Пример #7
0
def test_load_metadata_bad_json(mock_send):
    mock_send.return_value = {'yolo': 'in the wood'}
    svc = Service()
    svc.identifier = "1"
    svc.load_metadata()
    assert svc._distribution_id is None
    mock_send.assert_called_once_with(
        'GET',
        'Get Metadata for ONAP-test-Service',
        'https://sdc.api.fe.simpledemo.onap.org:30207/sdc1/feProxy/rest/v1/catalog/services/1/distribution',
        headers=headers_sdc_creator(svc.headers))
Пример #8
0
def test_get_tosca_no_result(mock_send):
    if path.exists('/tmp/tosca_files'):
        shutil.rmtree('/tmp/tosca_files')
    mock_send.return_value = {}
    svc = Service()
    svc.identifier = "12"
    svc.get_tosca()
    headers = headers_sdc_creator(svc.headers)
    headers['Accept'] = 'application/octet-stream'
    mock_send.assert_called_once_with(
        'GET',
        'Download Tosca Model for ONAP-test-Service',
        'https://sdc.api.be.simpledemo.onap.org:30204/sdc/v1/catalog/services/12/toscaModel',
        headers=headers)
    assert not path.exists('/tmp/tosca_files')
def test__deep_load_no_response(mock_send, mock_created):
    mock_created.return_value = True
    vf = Vf()
    vf.identifier = "1234"
    vf._version = "4567"
    vf._status = const.CHECKED_IN
    mock_send.return_value = {}
    vf.deep_load()
    assert vf._unique_identifier is None
    mock_send.assert_called_once_with(
        'GET',
        'Deep Load Vf',
        "{}/sdc1/feProxy/rest/v1/screen?excludeTypes=VFCMT&excludeTypes=Configuration"
        .format(vf.base_front_url),
        headers=headers_sdc_creator(vf.headers))
Пример #10
0
 def load_metadata(self) -> None:
     """Load Metada of Service and retrieve informations."""
     url = "{}/services/{}/distribution".format(self._base_create_url(),
                                                self.identifier)
     headers = headers_sdc_creator(SdcResource.headers)
     result = self.send_message_json("GET",
                                     "Get Metadata for {}".format(
                                         self.name),
                                     url,
                                     headers=headers)
     if (result and 'distributionStatusOfServiceList' in result
             and len(result['distributionStatusOfServiceList']) > 0):
         # API changed and the latest distribution is not added to the end
         # of distributions list but inserted as the first one.
         dist_status = result['distributionStatusOfServiceList'][0]
         self._distribution_id = dist_status['distributionID']
Пример #11
0
def test_load_metadata_OK(mock_send):
    mock_send.return_value = {
        'distributionStatusOfServiceList': [{
            'distributionID': "11"
        }, {
            'distributionID': "12"
        }]
    }
    svc = Service()
    svc.identifier = "1"
    svc.load_metadata()
    assert svc._distribution_id == "11"
    mock_send.assert_called_once_with(
        'GET',
        'Get Metadata for ONAP-test-Service',
        'https://sdc.api.fe.simpledemo.onap.org:30207/sdc1/feProxy/rest/v1/catalog/services/1/distribution',
        headers=headers_sdc_creator(svc.headers))
Пример #12
0
def test_distributed_not_distributed(mock_send):
    mock_send.return_value = {
        'distributionStatusList': [{
            'omfComponentID': "SO",
            'status': "DOWNLOAD_OK"
        }, {
            'omfComponentID': "aai",
            'status': "DOWNLOAD_OK"
        }]
    }
    svc = Service()
    svc.distribution_id = "12"
    assert not svc.distributed
    mock_send.assert_called_once_with(
        'GET',
        'Check distribution for ONAP-test-Service',
        'https://sdc.api.fe.simpledemo.onap.org:30207/sdc1/feProxy/rest/v1/catalog/services/distribution/12',
        headers=headers_sdc_creator(svc.headers))
Пример #13
0
 def deep_load(self) -> None:
     """Deep load Object informations from SDC."""
     url = (
         f"{self.base_front_url}/sdc1/feProxy/rest/v1/"
         "screen?excludeTypes=VFCMT&excludeTypes=Configuration"
     )
     headers = headers_sdc_creator(SdcResource.headers)
     if self.status == const.UNDER_CERTIFICATION:
         headers = headers_sdc_tester(SdcResource.headers)
     response = self.send_message_json("GET",
                                       "Deep Load {}".format(
                                           type(self).__name__),
                                       url,
                                       headers=headers)
     if response:
         for resource in response[self._sdc_path()]:
             if resource["uuid"] == self.identifier:
                 self._logger.debug("Resource %s found in %s list",
                                    resource["name"], self._sdc_path())
                 self.unique_identifier = resource["uniqueId"]
Пример #14
0
    def _check_distributed(self) -> bool:
        """Check if service is distributed and update status accordingly."""
        url = "{}/services/distribution/{}".format(self._base_create_url(),
                                                   self.distribution_id)
        headers = headers_sdc_creator(SdcResource.headers)
        result = self.send_message_json("GET",
                                        "Check distribution for {}".format(
                                            self.name),
                                        url,
                                        headers=headers)
        status = {}
        for component in components_needing_distribution():
            status[component] = False

        if result:
            status = self._update_components_status(status, result)

        for state in status.values():
            if not state:
                self._distributed = False
                return
        self._distributed = True
Пример #15
0
class Vsp(SdcElement):  # pylint: disable=too-many-instance-attributes
    """
    ONAP VSP Object used for SDC operations.

    Attributes:
        name (str): the name of the vsp. Defaults to "ONAP-test-VSP".
        identifier (str): the unique ID of the VSP from SDC.
        status (str): the status of the VSP from SDC.
        version (str): the version ID of the VSP from SDC.
        csar_uuid (str): the CSAR ID of the VSP from SDC.
        vendor (Vendor): The VSP Vendor

    """

    VSP_PATH = "vendor-software-products"
    headers = headers_sdc_creator(SdcElement.headers)

    def __init__(self,
                 name: str = None,
                 package: BinaryIO = None,
                 vendor: Vendor = None):
        """
        Initialize vsp object.

        Args:
            name (optional): the name of the vsp

        """
        super().__init__()
        self._csar_uuid: str = None
        self._vendor: Vendor = vendor or None
        self.name: str = name or "ONAP-test-VSP"
        self.package = package or None

    @property
    def status(self):
        """Return and load the status."""
        self.load_status()
        return self._status

    def onboard(self) -> None:
        """Onboard the VSP in SDC."""
        if not self.status:
            if not self.vendor:
                raise ValueError("No Vendor was given")
            self.create()
            self.onboard()
        elif self.status == const.DRAFT:
            if not self.package:
                raise ValueError("No file were given for upload")
            self.upload_package(self.package)
            self.onboard()
        elif self.status == const.UPLOADED:
            self.validate()
            self.onboard()
        elif self.status == const.VALIDATED:
            self.commit()
            self.onboard()
        elif self.status == const.COMMITED:
            self.submit()
            self.onboard()
        elif self.status == const.CERTIFIED:
            self.create_csar()

    def create(self) -> None:
        """Create the Vsp in SDC if not already existing."""
        if self.vendor:
            self._create("vsp_create.json.j2",
                         name=self.name,
                         vendor=self.vendor)

    def upload_package(self, package_to_upload: BinaryIO) -> None:
        """
        Upload given zip file into SDC as artifacts for this Vsp.

        Args:
            package_to_upload (file): the zip file to upload

        """
        self._action("upload package",
                     const.DRAFT,
                     self._upload_action,
                     package_to_upload=package_to_upload)

    def validate(self) -> None:
        """Validate the artifacts uploaded."""
        self._action("validate", const.UPLOADED, self._validate_action)

    def commit(self) -> None:
        """Commit the SDC Vsp."""
        self._action("commit",
                     const.VALIDATED,
                     self._generic_action,
                     action=const.COMMIT)

    def submit(self) -> None:
        """Submit the SDC Vsp in order to enable it."""
        self._action("certify/sumbit",
                     const.COMMITED,
                     self._generic_action,
                     action=const.SUBMIT)

    def create_csar(self) -> None:
        """Create the CSAR package in the SDC Vsp."""
        self._action("create CSAR package", const.CERTIFIED,
                     self._create_csar_action)

    @property
    def vendor(self) -> Vendor:
        """Return and lazy load the vendor."""
        if self.created() and not self._vendor:
            details = self._get_vsp_details()
            if details:
                self._vendor = Vendor(name=details['vendorName'])
        return self._vendor

    @vendor.setter
    def vendor(self, vendor: Vendor) -> None:
        """Set value for Vendor."""
        self._vendor = vendor

    @property
    def csar_uuid(self) -> str:
        """Return and lazy load the CSAR UUID."""
        if self.created() and not self._csar_uuid:
            self.create_csar()
        return self._csar_uuid

    @csar_uuid.setter
    def csar_uuid(self, csar_uuid: str) -> None:
        """Set value for csar uuid."""
        self._csar_uuid = csar_uuid

    def _upload_action(self, package_to_upload: BinaryIO = None):
        """Do upload for real."""
        if package_to_upload:
            url = "{}/{}/{}/orchestration-template-candidate".format(
                self._base_url(), Vsp._sdc_path(), self._version_path())
            headers = self.headers.copy()
            headers.pop("Content-Type")
            headers["Accept-Encoding"] = "gzip, deflate"
            data = {'upload': package_to_upload}
            upload_result = self.send_message('POST',
                                              'upload ZIP for Vsp',
                                              url,
                                              headers=headers,
                                              files=data)
            if upload_result:
                self._logger.info("Files for Vsp %s have been uploaded",
                                  self.name)
            else:
                self._logger.error(
                    "an error occured during file upload for Vsp %s",
                    self.name)

    def _validate_action(self):
        """Do validate for real."""
        url = "{}/{}/{}/orchestration-template-candidate/process".format(
            self._base_url(), Vsp._sdc_path(), self._version_path())
        validate_result = self.send_message_json('PUT',
                                                 'Validate artifacts for Vsp',
                                                 url)
        if validate_result and validate_result['status'] == 'Success':
            self._logger.info("Artifacts for Vsp %s have been validated",
                              self.name)
        else:
            self._logger.error(
                "an error occured during artifacts validation for Vsp %s",
                self.name)

    def _generic_action(self, action=None):
        """Do a generic action for real."""
        if action:
            self._action_to_sdc(action, action_type="lifecycleState")

    def _create_csar_action(self):
        """Create CSAR package for real."""
        result = self._action_to_sdc(const.CREATE_PACKAGE,
                                     action_type="lifecycleState")
        if result:
            self._logger.info("result: %s", result.text)
            data = result.json()
            self.csar_uuid = data['packageId']

    def _action(self, action_name: str, right_status: str,
                action_function: Callable[['Vsp'], None], **kwargs) -> None:
        """
        Generate an action on the instance in order to send it to SDC.

        Args:
            action_name (str): The name of the action (for the logs)
            right_status (str): The status that the object must be
            action_function (function): the function to perform if OK

        """
        self._logger.info("attempting to %s for %s in SDC", action_name,
                          self.name)
        if self.status == right_status:
            action_function(**kwargs)
        else:
            self._logger.warning(
                "vsp %s in SDC is not created or not in %s status", self.name,
                right_status)

    # VSP: DRAFT --> UPLOADED --> VALIDATED --> COMMITED --> CERTIFIED
    def load_status(self) -> None:
        """
        Load Vsp status from SDC.

        rules are following:

        * DRAFT: status == DRAFT and networkPackageName not present

        * UPLOADED: status == DRAFT and networkPackageName present and
          validationData not present

        * VALIDATED: status == DRAFT and networkPackageName present and
          validationData present and state.dirty = true

        * COMMITED: status == DRAFT and networkPackageName present and
          validationData present and state.dirty = false

        * CERTIFIED: status == CERTIFIED

        status is found in sdc items
        state is found in sdc version from items
        networkPackageName and validationData is found in SDC vsp show

        """
        item_details = self._get_item_details()
        if (item_details
                and item_details['results'][-1]['status'] == const.CERTIFIED):
            self._status = const.CERTIFIED
        else:
            self._check_status_not_certified()

    def _check_status_not_certified(self) -> None:
        """Check a status when it's not certified."""
        vsp_version_details = self._get_item_version_details()
        vsp_details = self._get_vsp_details()
        if (vsp_version_details and 'state' in vsp_version_details
                and not vsp_version_details['state']['dirty'] and vsp_details
                and 'validationData' in vsp_details):
            self._status = const.COMMITED
        else:
            self._check_status_not_commited()

    def _check_status_not_commited(self) -> None:
        """Check a status when it's not certified or commited."""
        vsp_details = self._get_vsp_details()
        if (vsp_details and 'validationData' in vsp_details):
            self._status = const.VALIDATED
        elif (vsp_details and 'validationData' not in vsp_details
              and 'networkPackageName' in vsp_details):
            self._status = const.UPLOADED
        elif vsp_details:
            self._status = const.DRAFT

    def _get_vsp_details(self) -> Dict[Any, Any]:
        """Get vsp details."""
        if self.created() and self.version:
            url = "{}/vendor-software-products/{}/versions/{}".format(
                self._base_url(), self.identifier, self.version)

            return self.send_message_json('GET', 'get vsp version', url)
        return {}

    @classmethod
    def import_from_sdc(cls, values: Dict[str, Any]) -> 'Vsp':
        """
        Import Vsp from SDC.

        Args:
            values (Dict[str, Any]): dict to parse returned from SDC.

        Returns:
            a Vsp instance with right values

        """
        cls._logger.debug("importing VSP %s from SDC", values['name'])
        vsp = Vsp(values['name'])
        vsp.identifier = values['id']
        vsp.vendor = Vendor(name=values['vendorName'])
        vsp.load_status()
        cls._logger.info("status of VSP %s: %s", vsp.name, vsp.status)
        return vsp

    def _really_submit(self) -> None:
        """Really submit the SDC Vf in order to enable it."""
        raise NotImplementedError("VSP don't need _really_submit")

    @classmethod
    def _sdc_path(cls) -> None:
        """Give back the end of SDC path."""
        return cls.VSP_PATH
Пример #16
0
class Service(SdcResource):  # pylint: disable=too-many-instance-attributes
    """
    ONAP Service Object used for SDC operations.

    Attributes:
        name (str): the name of the service. Defaults to "ONAP-test-Service".
        identifier (str): the unique ID of the service from SDC.
        status (str): the status of the service from SDC.
        version (str): the version ID of the service from SDC.
        uuid (str): the UUID of the Service (which is different from
                    identifier, don't ask why...)
        distribution_status (str):  the status of distribution in the different
                                    ONAP parts.
        distribution_id (str): the ID of the distribution when service is
                               distributed.
        distributed (bool): True if the service is distributed
        unique_identifier (str): Yet Another ID, just to puzzle us...

    """

    SERVICE_PATH = "services"
    headers = headers_sdc_creator(SdcResource.headers)

    def __init__(self,
                 name: str = None,
                 sdc_values: Dict[str, str] = None,
                 resources: List[SdcResource] = None):
        """
        Initialize service object.

        Args:
            name (str, optional): the name of the service
            sdc_values (Dict[str, str], optional): dictionary of values
                returned by SDC
            resources (List[SdcResource], optional): list of SDC resources

        """
        super().__init__(sdc_values=sdc_values)
        self.name: str = name or "ONAP-test-Service"
        self.distribution_status = None
        if sdc_values:
            self.distribution_status = sdc_values['distributionStatus']
        self.resources = resources or []
        self._distribution_id: str = None
        self._distributed: bool = False
        self._resource_type: str = "services"
        self._tosca_model: bytes = None
        self._tosca_template: str = None
        self._vnfs: list = None
        self._networks: list = None
        self._vf_modules: list = None
        self._time_wait: int = 10

    def onboard(self) -> None:
        """Onboard the Service in SDC."""
        # first Lines are equivalent for all onboard functions but it's more
        # readable
        if not self.status:
            self.create()
            time.sleep(self._time_wait)
            self.onboard()
        elif self.status == const.DRAFT:
            if not self.resources:
                raise ValueError("No resources were given")
            for resource in self.resources:
                self.add_resource(resource)
            self.checkin()
            time.sleep(self._time_wait)
            self.onboard()
        elif self.status == const.CHECKED_IN:
            self.certify()
            time.sleep(self._time_wait)
            self.onboard()
            time.sleep(self._time_wait)
        elif self.status == const.CERTIFIED:
            self.distribute()
        elif self.status == const.DISTRIBUTED:
            self._logger.info("Service %s onboarded", self.name)
            return
        self._logger.error("Service has invalid status")

    @property
    def distribution_id(self) -> str:
        """Return and lazy load the distribution_id."""
        if not self._distribution_id:
            self.load_metadata()
        return self._distribution_id

    @distribution_id.setter
    def distribution_id(self, value: str) -> None:
        """Set value for distribution_id."""
        self._distribution_id = value

    @property
    def distributed(self) -> bool:
        """Return and lazy load the distributed state."""
        if not self._distributed:
            self._check_distributed()
        return self._distributed

    @property
    def tosca_template(self) -> str:
        """Service tosca template file.

        Get tosca template from service tosca model bytes.

        Raises:
            AttributeError: Tosca model can't be downloaded using HTTP API

        Returns:
            str: Tosca template file

        """
        if not self._tosca_template and self.tosca_model:
            self._unzip_csar_file(BytesIO(self.tosca_model),
                                  self._load_tosca_template)
        return self._tosca_template

    @property
    def tosca_model(self) -> bytes:
        """Service's tosca model file.

        Send request to get service TOSCA model,

        Raises:
            AttributeError: Tosca model can't be downloaded using HTTP API

        Returns:
            bytes: TOSCA model file bytes

        """
        if not self._tosca_model:
            url = "{}/services/{}/toscaModel".format(self._base_url(),
                                                     self.identifier)
            headers = self.headers.copy()
            headers["Accept"] = "application/octet-stream"
            self._tosca_model = self.send_message(
                "GET",
                "Download Tosca Model for {}".format(self.name),
                url,
                headers=headers,
                exception=AttributeError).content
        return self._tosca_model

    @property
    def vnfs(self) -> List[Vnf]:
        """Service Vnfs.

        Load VNFs from service's tosca file

        Raises:
            AttributeError: Service has no TOSCA template

        Returns:
            List[Vnf]: Vnf objects list

        """
        if not self.tosca_template:
            raise AttributeError("Service has no TOSCA template")
        if self._vnfs is None:
            self._vnfs = []
            for node_template_name, values in \
                self.tosca_template.get("topology_template", {}).get(
                        "node_templates", {}).items():
                if re.match("org.openecomp.resource.vf.*", values["type"]):
                    vnf: Vnf = Vnf(name=node_template_name,
                                   node_template_type=values["type"],
                                   metadata=values["metadata"],
                                   properties=values["properties"],
                                   capabilities=values.get("capabilities", {}))
                    vnf.associate_vf_module(self.vf_modules)
                    self._vnfs.append(vnf)
        return self._vnfs

    @property
    def networks(self) -> List[Network]:
        """Service networks.

        Load networks from service's tosca file

        Raises:
            AttributeError: Service has no TOSCA template

        Returns:
            List[Network]: Network objects list

        """
        if not self.tosca_template:
            raise AttributeError("Service has no TOSCA template")
        if self._networks is None:
            self._networks = []
            for node_template_name, values in \
                self.tosca_template.get("topology_template", {}).get(
                        "node_templates", {}).items():
                if re.match("org.openecomp.resource.vl.*", values["type"]):
                    self._networks.append(
                        Network(name=node_template_name,
                                node_template_type=values["type"],
                                metadata=values["metadata"],
                                properties=values["properties"],
                                capabilities=values["capabilities"]))
        return self._networks

    @property
    def vf_modules(self) -> List[VfModule]:
        """Service VF modules.

        Load VF modules from service's tosca file

        Returns:
            List[VfModule]: VfModule objects list

        """
        if self._vf_modules is None:
            self._vf_modules = []
            groups: dict = self.tosca_template.get("topology_template",
                                                   {}).get("groups", {})
            for group_name, values in groups.items():
                self._vf_modules.append(
                    VfModule(name=group_name,
                             group_type=values["type"],
                             metadata=values["metadata"],
                             properties=values["properties"]))
        return self._vf_modules

    def create(self) -> None:
        """Create the Service in SDC if not already existing."""
        self._create("service_create.json.j2", name=self.name)

    def add_resource(self, resource: SdcResource) -> None:
        """
        Add a Resource to the service.

        Args:
            resource (SdcResource): the resource to add

        """
        if self.status == const.DRAFT:
            url = "{}/{}/{}/resourceInstance".format(self._base_create_url(),
                                                     self._sdc_path(),
                                                     self.unique_identifier)

            template = jinja_env().get_template(
                "add_resource_to_service.json.j2")
            data = template.render(resource=resource,
                                   resource_type=type(resource).__name__)
            result = self.send_message("POST",
                                       "Add {} to service".format(
                                           type(resource).__name__),
                                       url,
                                       data=data)
            if result:
                self._logger.info("Resource %s %s has been added on serice %s",
                                  type(resource).__name__, resource.name,
                                  self.name)
                return result
            self._logger.error(("an error occured during adding resource %s %s"
                                " on service %s in SDC"),
                               type(resource).__name__, resource.name,
                               self.name)
            return None
        self._logger.error("Service is not in Draft mode")
        return None

    def checkin(self) -> None:
        """Checkin Service."""
        self._verify_lcm_to_sdc(const.DRAFT, const.CHECKIN)

    def submit(self) -> None:
        """Really submit the SDC Service."""
        self._verify_lcm_to_sdc(const.CHECKED_IN, const.SUBMIT_FOR_TESTING)

    def start_certification(self) -> None:
        """Start Certification on Service."""
        headers = headers_sdc_creator(SdcResource.headers)
        self._verify_lcm_to_sdc(const.CHECKED_IN,
                                const.START_CERTIFICATION,
                                headers=headers)

    def certify(self) -> None:
        """Certify Service in SDC."""
        headers = headers_sdc_creator(SdcResource.headers)
        self._verify_lcm_to_sdc(const.CHECKED_IN,
                                const.CERTIFY,
                                headers=headers)

    def approve(self) -> None:
        """Approve Service in SDC."""
        headers = headers_sdc_creator(SdcResource.headers)
        self._verify_approve_to_sdc(const.CERTIFIED,
                                    const.APPROVE,
                                    headers=headers)

    def distribute(self) -> None:
        """Apptove Service in SDC."""
        headers = headers_sdc_creator(SdcResource.headers)
        self._verify_distribute_to_sdc(const.CERTIFIED,
                                       const.DISTRIBUTE,
                                       headers=headers)

    def get_tosca(self) -> None:
        """Get Service tosca files and save it."""
        url = "{}/services/{}/toscaModel".format(self._base_url(),
                                                 self.identifier)
        headers = self.headers.copy()
        headers["Accept"] = "application/octet-stream"
        result = self.send_message("GET",
                                   "Download Tosca Model for {}".format(
                                       self.name),
                                   url,
                                   headers=headers)
        if result:
            self._create_tosca_file(result)

    def _create_tosca_file(self, result: Response) -> None:
        """Create Service Tosca files from HTTP response."""
        csar_filename = "service-{}-csar.csar".format(self.name)
        makedirs(tosca_path(), exist_ok=True)
        with open((tosca_path() + csar_filename), 'wb') as csar_file:
            for chunk in result.iter_content(chunk_size=128):
                csar_file.write(chunk)
        try:
            self._unzip_csar_file(tosca_path() + csar_filename,
                                  self._write_csar_file)
        except BadZipFile as exc:
            self._logger.exception(exc)

    def _check_distributed(self) -> bool:
        """Check if service is distributed and update status accordingly."""
        url = "{}/services/distribution/{}".format(self._base_create_url(),
                                                   self.distribution_id)
        headers = headers_sdc_creator(SdcResource.headers)
        result = self.send_message_json("GET",
                                        "Check distribution for {}".format(
                                            self.name),
                                        url,
                                        headers=headers)
        status = {}
        for component in components_needing_distribution():
            status[component] = False

        if result:
            status = self._update_components_status(status, result)

        for state in status.values():
            if not state:
                self._distributed = False
                return
        self._distributed = True

    def _update_components_status(self, status: Dict[str, bool],
                                  result: Response) -> Dict[str, bool]:
        """Update components distribution status."""
        distrib_list = result['distributionStatusList']
        self._logger.debug("[SDC][Get Distribution] distrib_list = %s",
                           distrib_list)
        for elt in distrib_list:
            status = self._parse_components_status(status, elt)
        return status

    def _parse_components_status(self, status: Dict[str, bool],
                                 element: Dict[str, Any]) -> Dict[str, bool]:
        """Parse components distribution status."""
        for key in status:
            if ((key in element['omfComponentID'])
                    and (const.DOWNLOAD_OK in element['status'])):
                status[key] = True
                self._logger.info(("[SDC][Get Distribution] Service "
                                   "distributed in %s"), key)
        return status

    def load_metadata(self) -> None:
        """Load Metada of Service and retrieve informations."""
        url = "{}/services/{}/distribution".format(self._base_create_url(),
                                                   self.identifier)
        headers = headers_sdc_creator(SdcResource.headers)
        result = self.send_message_json("GET",
                                        "Get Metadata for {}".format(
                                            self.name),
                                        url,
                                        headers=headers)
        if (result and 'distributionStatusOfServiceList' in result
                and len(result['distributionStatusOfServiceList']) > 0):
            # API changed and the latest distribution is not added to the end
            # of distributions list but inserted as the first one.
            dist_status = result['distributionStatusOfServiceList'][0]
            self._distribution_id = dist_status['distributionID']

    @classmethod
    def _get_all_url(cls) -> str:
        """
        Get URL for all elements in SDC.

        Returns:
            str: the url

        """
        return "{}/{}".format(cls._base_url(), cls._sdc_path())

    def _really_submit(self) -> None:
        """Really submit the SDC Service in order to enable it."""
        result = self._action_to_sdc(const.CERTIFY,
                                     action_type="lifecycleState")
        if result:
            self.load()

    def _specific_copy(self, obj: 'Service') -> None:
        """
        Copy specific properties from object.

        Args:
            obj (Service): the object to "copy"

        """

    def _verify_distribute_to_sdc(self, desired_status: str,
                                  desired_action: str, **kwargs) -> None:
        self._verify_action_to_sdc(desired_status, desired_action,
                                   "distribution", **kwargs)

    def _verify_approve_to_sdc(self, desired_status: str, desired_action: str,
                               **kwargs) -> None:
        self._verify_action_to_sdc(desired_status, desired_action,
                                   "distribution-state", **kwargs)

    def _verify_lcm_to_sdc(self, desired_status: str, desired_action: str,
                           **kwargs) -> None:
        self._verify_action_to_sdc(desired_status, desired_action,
                                   "lifecycleState", **kwargs)

    def _verify_action_to_sdc(self, desired_status: str, desired_action: str,
                              action_type: str, **kwargs) -> None:
        """
        Verify action to SDC.

        Verify that object is in right state before launching the action on
        SDC.

        Args:
            desired_status (str): the status the object should be
            desired_action (str): the action we want to perform
            action_type (str): the type of action ('distribution-state' or
                               'lifecycleState')
            **kwargs: any specific stuff to give to requests

        """
        self._logger.info("attempting to %s Service %s in SDC", desired_action,
                          self.name)
        if self.status == desired_status and self.created():
            result = self._action_to_sdc(desired_action,
                                         action_type=action_type,
                                         **kwargs)
            if result:
                self.load()
        elif not self.created():
            self._logger.warning("Service %s in SDC is not created", self.name)
        elif self.status != desired_status:
            self._logger.warning(("Service %s in SDC is in status %s and it "
                                  "should be in  status %s"), self.name,
                                 self.status, desired_status)

    @staticmethod
    def _unzip_csar_file(
            zip_file: Union[str, BytesIO],
            function: Callable[[str, TextIOWrapper], None]) -> None:
        """
        Unzip Csar File and perform an action on the file.

        Raises:
            AttributeError: CSAR file has no service template

        """
        with ZipFile(zip_file) as myzip:
            service_template = None
            for name in myzip.namelist():
                if (name[-13:] == "-template.yml"
                        and name[:20] == "Definitions/service-"):
                    service_template = name

            if not service_template:
                raise AttributeError("CSAR file has no service template")

            with myzip.open(service_template) as template_file:
                function(service_template, template_file)

    @staticmethod
    def _write_csar_file(service_template: str,
                         template_file: TextIOWrapper) -> None:
        """Write service temple into a file."""
        with open(tosca_path() + service_template[12:], 'wb') as file:
            file.write(template_file.read())

    # _service_template is not used but function generation is generic
    # pylint: disable-unused-argument
    def _load_tosca_template(self, _service_template: str,
                             template_file: TextIOWrapper) -> None:
        """Load Tosca template."""
        self._tosca_template = yaml.safe_load(template_file.read())

    @classmethod
    def _sdc_path(cls) -> None:
        """Give back the end of SDC path."""
        return cls.SERVICE_PATH
Пример #17
0
 def distribute(self) -> None:
     """Apptove Service in SDC."""
     headers = headers_sdc_creator(SdcResource.headers)
     self._verify_distribute_to_sdc(const.CERTIFIED,
                                    const.DISTRIBUTE,
                                    headers=headers)
Пример #18
0
 def approve(self) -> None:
     """Approve Service in SDC."""
     headers = headers_sdc_creator(SdcResource.headers)
     self._verify_approve_to_sdc(const.CERTIFIED,
                                 const.APPROVE,
                                 headers=headers)
Пример #19
0
 def certify(self) -> None:
     """Certify Service in SDC."""
     headers = headers_sdc_creator(SdcResource.headers)
     self._verify_lcm_to_sdc(const.CHECKED_IN,
                             const.CERTIFY,
                             headers=headers)
Пример #20
0
def test_headers_sdc_creator():
    base_header = {}
    sdc_headers_creator = headers_sdc_creator(base_header)
    assert base_header != sdc_headers_creator
    assert sdc_headers_creator["USER_ID"] == "cs0008"
    assert sdc_headers_creator["Authorization"]
Пример #21
0
class Vendor(SdcElement):
    """
    ONAP Vendor Object used for SDC operations.

    Attributes:
        name (str): the name of the vendor. Defaults to "Generic-Vendor".
        identifier (str): the unique ID of the vendor from SDC.
        status (str): the status of the vendor from SDC.
        version (str): the version ID of the vendor from SDC.

    """

    VENDOR_PATH = "vendor-license-models"
    headers = headers_sdc_creator(SdcElement.headers)

    def __init__(self, name: str = None):
        """
        Initialize vendor object.

        Args:
            name (optional): the name of the vendor

        """
        super().__init__()
        self.name: str = name or "Generic-Vendor"

    def onboard(self) -> None:
        """Onboard the vendor in SDC."""
        if not self.status:
            self.create()
            self.onboard()
        elif self.status == const.DRAFT:
            self.submit()

    def create(self) -> None:
        """Create the vendor in SDC if not already existing."""
        self._create("vendor_create.json.j2", name=self.name)

    def submit(self) -> None:
        """Submit the SDC vendor in order to enable it."""
        self._logger.info("attempting to certify/sumbit vendor %s in SDC",
                          self.name)
        if self.status != const.CERTIFIED and self.created():
            self._really_submit()
        elif self.status == const.CERTIFIED:
            self._logger.warning(
                "vendor %s in SDC is already submitted/certified", self.name)
        elif not self.created():
            self._logger.warning("vendor %s in SDC is not created", self.name)

    def update_informations_from_sdc(self, details: Dict[str, Any]) -> None:
        """

        Update instance with details from SDC.

        Args:
            details (Dict[str, Any]): dict from SDC

        """
        self._status = details['results'][-1]['status']

    @classmethod
    def import_from_sdc(cls, values: Dict[str, Any]) -> 'Vendor':
        """
        Import Vendor from SDC.

        Args:
            values (Dict[str, Any]): dict to parse returned from SDC.

        Returns:
            a Vsp instance with right values

        """
        vendor = Vendor(values['name'])
        vendor.identifier = values['id']
        return vendor

    def _really_submit(self) -> None:
        """Really submit the SDC Vf in order to enable it."""
        result = self._action_to_sdc(const.SUBMIT)
        if result:
            self._status = const.CERTIFIED

    @classmethod
    def _sdc_path(cls) -> None:
        """Give back the end of SDC path."""
        return cls.VENDOR_PATH
Пример #22
0
class SdcResource(SdcOnboardable, ABC):  # pylint: disable=too-many-instance-attributes, too-many-public-methods
    """Mother Class of all SDC resources."""

    RESOURCE_PATH = 'resources'
    ACTION_TEMPLATE = 'sdc_resource_action.json.j2'
    ACTION_METHOD = 'POST'
    headers = headers_sdc_creator(SdcOnboardable.headers)

    def __init__(
            self,
            name: str = None,
            version: str = None,  # pylint: disable=too-many-arguments
            sdc_values: Dict[str, str] = None,
            properties: List[Property] = None,
            inputs: Union[Property, NestedInput] = None,
            category: str = None,
            subcategory: str = None):
        """Initialize the object."""
        super().__init__(name)
        self.version_filter: str = version
        self._unique_uuid: str = None
        self._unique_identifier: str = None
        self._resource_type: str = "resources"
        self._properties_to_add: List[Property] = properties or []
        self._inputs_to_add: Union[Property, NestedInput] = inputs or []
        self._time_wait: int = 10
        self._category_name: str = category
        self._subcategory_name: str = subcategory
        if sdc_values:
            self._logger.debug("SDC values given, using them")
            self.identifier = sdc_values['uuid']
            self.version = sdc_values['version']
            self.unique_uuid = sdc_values['invariantUUID']
            distribitution_state = None
            if 'distributionStatus' in sdc_values:
                distribitution_state = sdc_values['distributionStatus']
            self.status = self._parse_sdc_status(sdc_values['lifecycleState'],
                                                 distribitution_state,
                                                 self._logger)
            self._logger.debug("SDC resource %s status: %s", self.name,
                               self.status)

    def __repr__(self) -> str:
        """SDC resource description.

        Returns:
            str: SDC resource object description

        """
        return f"{self.__class__.__name__.upper()}(name={self.name})"

    @property
    def unique_uuid(self) -> str:
        """Return and lazy load the unique_uuid."""
        if not self._unique_uuid:
            self.load()
        return self._unique_uuid

    @property
    def unique_identifier(self) -> str:
        """Return and lazy load the unique_identifier."""
        if not self._unique_identifier:
            self.deep_load()
        return self._unique_identifier

    @unique_uuid.setter
    def unique_uuid(self, value: str) -> None:
        """Set value for unique_uuid."""
        self._unique_uuid = value

    @unique_identifier.setter
    def unique_identifier(self, value: str) -> None:
        """Set value for unique_identifier."""
        self._unique_identifier = value

    def load(self) -> None:
        """Load Object information from SDC."""
        self.exists()

    def deep_load(self) -> None:
        """Deep load Object informations from SDC."""
        url = (f"{self.base_front_url}/sdc1/feProxy/rest/v1/"
               "screen?excludeTypes=VFCMT&excludeTypes=Configuration")
        headers = headers_sdc_creator(SdcResource.headers)
        if self.status == const.UNDER_CERTIFICATION:
            headers = headers_sdc_tester(SdcResource.headers)
        response = self.send_message_json("GET",
                                          "Deep Load {}".format(
                                              type(self).__name__),
                                          url,
                                          headers=headers)
        if response:
            for resource in response[self._sdc_path()]:
                if resource["uuid"] == self.identifier:
                    self._logger.debug("Resource %s found in %s list",
                                       resource["name"], self._sdc_path())
                    self.unique_identifier = resource["uniqueId"]
                    self._category_name = resource["categories"][0]["name"]
                    subcategories = resource["categories"][0].get(
                        "subcategories", [{}])
                    self._subcategory_name = None if subcategories is None else \
                        subcategories[0].get("name")

    def _generate_action_subpath(self, action: str) -> str:
        """

        Generate subpath part of SDC action url.

        Args:
            action (str): the action that will be done

        Returns:
            str: the subpath part

        """
        return action

    def _version_path(self) -> str:
        """
        Give the end of the path for a version.

        Returns:
            str: the end of the path

        """
        return self.unique_identifier

    def _action_url(self,
                    base: str,
                    subpath: str,
                    version_path: str,
                    action_type: str = None) -> str:
        """
        Generate action URL for SDC.

        Args:
            base (str): base part of url
            subpath (str): subpath of url
            version_path (str): version path of the url
            action_type (str, optional): the type of action
                                         ('distribution', 'distribution-state'
                                         or 'lifecycleState'). Default to
                                         'lifecycleState').

        Returns:
            str: the URL to use

        """
        if not action_type:
            action_type = "lifecycleState"
        return "{}/{}/{}/{}/{}".format(base, self._resource_type, version_path,
                                       action_type, subpath)

    @classmethod
    def _base_create_url(cls) -> str:
        """
        Give back the base url of Sdc.

        Returns:
            str: the base url

        """
        return "{}/sdc1/feProxy/rest/v1/catalog".format(cls.base_front_url)

    @classmethod
    def _base_url(cls) -> str:
        """
        Give back the base url of Sdc.

        Returns:
            str: the base url

        """
        return "{}/sdc/v1/catalog".format(cls.base_back_url)

    @classmethod
    def _get_all_url(cls) -> str:
        """
        Get URL for all elements in SDC.

        Returns:
            str: the url

        """
        return "{}/{}?resourceType={}".format(cls._base_url(), cls._sdc_path(),
                                              cls.__name__.upper())

    @classmethod
    def _get_objects_list(
            cls, result: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """
        Import objects created in SDC.

        Args:
            result (Dict[str, Any]): the result returned by SDC in a Dict

        Return:
            List[Dict[str, Any]]: the list of objects

        """
        return result

    def _get_version_from_sdc(self, sdc_infos: Dict[str, Any]) -> str:
        """
        Get version from SDC results.

        Args:
            sdc_infos (Dict[str, Any]): the result dict from SDC

        Returns:
            str: the version

        """
        return sdc_infos['version']

    def _get_identifier_from_sdc(self, sdc_infos: Dict[str, Any]) -> str:
        """
        Get identifier from SDC results.

        Args:
            sdc_infos (Dict[str, Any]): the result dict from SDC

        Returns:
            str: the identifier

        """
        return sdc_infos['uuid']

    @classmethod
    def import_from_sdc(cls, values: Dict[str, Any]) -> 'SdcResource':
        """
        Import SdcResource from SDC.

        Args:
            values (Dict[str, Any]): dict to parse returned from SDC.

        Return:
            SdcResource: the created resource

        """
        cls._logger.debug("importing SDC Resource %s from SDC", values['name'])
        return cls(name=values['name'], sdc_values=values)

    def _copy_object(self, obj: 'SdcResource') -> None:
        """
        Copy relevant properties from object.

        Args:
            obj (SdcResource): the object to "copy"

        """
        self.identifier = obj.identifier
        self.unique_uuid = obj.unique_uuid
        self.status = obj.status
        self.version = obj.version
        self.unique_identifier = obj.unique_identifier
        self._specific_copy(obj)

    def _specific_copy(self, obj: 'SdcResource') -> None:
        """
        Copy specific properties from object.

        Args:
            obj (SdcResource): the object to "copy"

        """

    def update_informations_from_sdc(self, details: Dict[str, Any]) -> None:
        """

        Update instance with details from SDC.

        Args:
            details ([type]): [description]

        """

    def update_informations_from_sdc_creation(self,
                                              details: Dict[str, Any]) -> None:
        """

        Update instance with details from SDC after creation.

        Args:
            details ([type]): the details from SDC

        """
        self.unique_uuid = details['invariantUUID']
        distribution_state = None

        if 'distributionStatus' in details:
            distribution_state = details['distributionStatus']
        self.status = self._parse_sdc_status(details['lifecycleState'],
                                             distribution_state, self._logger)
        self.version = details['version']
        self.unique_identifier = details['uniqueId']

    # Not my fault if SDC has so many states...
    # pylint: disable=too-many-return-statements
    @staticmethod
    def _parse_sdc_status(sdc_status: str, distribution_state: str,
                          logger: logging.Logger) -> str:
        """
        Parse  SDC status in order to normalize it.

        Args:
            sdc_status (str): the status found in SDC
            distribution_state (str): the distribution status found in SDC.
                                        Can be None.

        Returns:
            str: the normalized status

        """
        logger.debug("Parse status for SDC Resource")
        if sdc_status.capitalize() == const.CERTIFIED:
            if distribution_state and distribution_state == const.SDC_DISTRIBUTED:
                return const.DISTRIBUTED
            return const.CERTIFIED
        if sdc_status == const.NOT_CERTIFIED_CHECKOUT:
            return const.DRAFT
        if sdc_status == const.NOT_CERTIFIED_CHECKIN:
            return const.CHECKED_IN
        if sdc_status == const.READY_FOR_CERTIFICATION:
            return const.SUBMITTED
        if sdc_status == const.CERTIFICATION_IN_PROGRESS:
            return const.UNDER_CERTIFICATION
        if sdc_status != "":
            return sdc_status
        return None

    def _really_submit(self) -> None:
        """Really submit the SDC Vf in order to enable it."""
        raise NotImplementedError("SDC is an abstract class")

    def onboard(self) -> None:
        """Onboard resource in SDC."""
        if not self.status:
            self.create()
            time.sleep(self._time_wait)
            self.onboard()
        elif self.status == const.DRAFT:
            for property_to_add in self._properties_to_add:
                self.add_property(property_to_add)
            for input_to_add in self._inputs_to_add:
                self.declare_input(input_to_add)
            self.submit()
            time.sleep(self._time_wait)
            self.onboard()
        elif self.status == const.CERTIFIED:
            self.load()

    @classmethod
    def _sdc_path(cls) -> None:
        """Give back the end of SDC path."""
        return cls.RESOURCE_PATH

    @property
    def deployment_artifacts_url(self) -> str:
        """Deployment artifacts url.

        Returns:
            str: SdcResource Deployment artifacts url

        """
        return (
            f"{self._base_create_url()}/resources/"
            f"{self.unique_identifier}/filteredDataByParams?include=deploymentArtifacts"
        )

    @property
    def add_deployment_artifacts_url(self) -> str:
        """Add deployment artifacts url.

        Returns:
            str: Url used to add deployment artifacts

        """
        return (f"{self._base_create_url()}/resources/"
                f"{self.unique_identifier}/artifacts")

    @property
    def properties_url(self) -> str:
        """Properties url.

        Returns:
            str: SdcResource properties url

        """
        return (
            f"{self._base_create_url()}/resources/"
            f"{self.unique_identifier}/filteredDataByParams?include=properties"
        )

    @property
    def add_property_url(self) -> str:
        """Add property url.

        Returns:
            str: Url used to add property

        """
        return (f"{self._base_create_url()}/services/"
                f"{self.unique_identifier}/properties")

    @property
    def set_input_default_value_url(self) -> str:
        """Url to set input default value.

        Returns:
            str: SDC API url used to set input default value

        """
        return (f"{self._base_create_url()}/resources/"
                f"{self.unique_identifier}/update/inputs")

    @property
    def origin_type(self) -> str:
        """Resource origin type.

        Value needed for composition. It's used for adding SDC resource
            as an another SDC resource component.

        Returns:
            str: SDC resource origin type

        """
        return type(self).__name__

    @property
    def properties(self) -> Iterator[Property]:
        """SDC resource properties.

        Iterate resource properties.

        Yields:
            Property: Resource property

        """
        for property_data in self.send_message_json(\
                "GET",
                f"Get {self.name} resource properties",
                self.properties_url,
                exception=AttributeError).get("properties", []):
            yield Property(
                sdc_resource=self,
                unique_id=property_data["uniqueId"],
                name=property_data["name"],
                property_type=property_data["type"],
                parent_unique_id=property_data["parentUniqueId"],
                value=property_data.get("value"),
                description=property_data.get("description"),
                get_input_values=property_data.get("getInputValues"),
            )

    def get_property(self, property_name: str) -> Property:
        """Get resource property by it's name.

        Args:
            property_name (str): property name

        Raises:
            AttributeError: Resource has no property with given name

        Returns:
            Property: Resource's property object

        """
        for property_obj in self.properties:
            if property_obj.name == property_name:
                return property_obj
        raise AttributeError("Resource has no property with %s name" %
                             property_name)

    @property
    def resource_inputs_url(self) -> str:
        """Resource inputs url.

        Method which returns url which point to resource inputs.

        Returns:
            str: Resource inputs url

        """
        return (f"{self._base_create_url()}/resources/"
                f"{self.unique_identifier}")

    def create(self) -> None:
        """Create resource.

        Abstract method which should be implemented by subclasses and creates resource in SDC.

        Raises:
            NotImplementedError: Method not implemented by subclasses.

        """
        raise NotImplementedError

    @property
    def inputs(self) -> Iterator[Input]:
        """SDC resource inputs.

        Iterate resource inputs.

        Yields:
            Iterator[Input]: Resource input

        """
        for input_data in self.send_message_json(\
                "GET",
                f"Get {self.name} resource inputs",
                f"{self.resource_inputs_url}/filteredDataByParams?include=inputs",
                exception=AttributeError).get("inputs", []):

            yield Input(unique_id=input_data["uniqueId"],
                        input_type=input_data["type"],
                        name=input_data["name"],
                        sdc_resource=self,
                        _default_value=input_data.get("defaultValue"))

    def get_input(self, input_name: str) -> Input:
        """Get input by it's name.

        Args:
            input_name (str): Input name

        Raises:
            AttributeError: Resource doesn't have input with given name

        Returns:
            Input: Found input object

        """
        for input_obj in self.inputs:
            if input_obj.name == input_name:
                return input_obj
        raise AttributeError("SDC resource has no %s input" % input_name)

    def add_deployment_artifact(self, artifact_type: str, artifact_label: str,
                                artifact_name: str, artifact: str):
        """
        Add deployment artifact to resource.

        Add deployment artifact to resource using payload data.

        Args:
            artifact_type (str): all SDC artifact types are supported (DCAE_*, HEAT_*, ...)
            artifact_name (str): the artifact file name including its extension
            artifact (str): artifact file to upload
            artifact_label (str): Unique Identifier of the artifact within the VF / Service.

        Raises:
            AttributeError: Resource has not DRAFT status

        """
        data = open(artifact, 'rb').read()
        artifact_string = base64.b64encode(data).decode('utf-8')
        if self.status != const.DRAFT:
            raise AttributeError(
                "Can't add artifact to resource which is not in DRAFT status")
        self._logger.debug("Add deployment artifact to sdc resource")
        my_data = jinja_env().get_template(
            "sdc_resource_add_deployment_artifact.json.j2").\
                render(artifact_name=artifact_name,
                       artifact_label=artifact_label,
                       artifact_type=artifact_type,
                       b64_artifact=artifact_string)
        my_header = headers_sdc_artifact_upload(base_header=self.headers,
                                                data=my_data)

        self.send_message_json(
            "POST",
            f"Add deployment artifact for {self.name} sdc resource",
            self.add_deployment_artifacts_url,
            data=my_data,
            headers=my_header,
            exception=ValueError)

    @property
    def components(self) -> Iterator[Component]:
        """Resource components.

        Iterate resource components.

        Yields:
            Component: Resource component object

        """
        for component_instance in self.send_message_json(\
                "GET",
                f"Get {self.name} resource inputs",
                f"{self.resource_inputs_url}/filteredDataByParams?include=componentInstances",
                exception=AttributeError).get("componentInstances", []):
            sdc_resource: "SdcResource" = SdcResource.import_from_sdc(self.send_message_json(\
                "GET",
                f"Get {self.name} component's SDC resource metadata",
                (f"{self.base_front_url}/sdc1/feProxy/rest/v1/catalog/resources/"
                 f"{component_instance['actualComponentUid']}/"
                 "filteredDataByParams?include=metadata"),
                exception=AttributeError)["metadata"])
            yield Component.create_from_api_response(
                api_response=component_instance,
                sdc_resource=sdc_resource,
                parent_sdc_resource=self)

    @property
    def category(self) -> Union[ResourceCategory, ServiceCategory]:
        """Sdc resource category.

        Depends on the resource type returns ResourceCategory or ServiceCategory.

        Returns:
            Uniton[ResourceCategory, ServiceCategory]: resource category

        """
        if self.created():
            if not any([self._category_name, self._subcategory_name]):
                self.deep_load()
            if all([self._category_name, self._subcategory_name]):
                return ResourceCategory.get(name=self._category_name,
                                            subcategory=self._subcategory_name)
            return ServiceCategory.get(name=self._category_name)
        return self.get_category_for_new_resource()

    def get_category_for_new_resource(self) -> ResourceCategory:
        """Get category for resource not created in SDC yet.

        If no category values are provided default category is going to be used.

        Returns:
            ResourceCategory: Category of the new resource

        """
        if not all([self._category_name, self._subcategory_name]):
            return ResourceCategory.get(name="Generic", subcategory="Abstract")
        return ResourceCategory.get(name=self._category_name,
                                    subcategory=self._subcategory_name)

    def get_component_properties_url(self, component: "Component") -> str:
        """Url to get component's properties.

        This method is here because component can have different url when
            it's a component of another SDC resource type, eg. for service and
            for VF components have different urls.

        Args:
            component (Component): Component object to prepare url for

        Returns:
            str: Component's properties url

        """
        return (f"{self.resource_inputs_url}/"
                f"componentInstances/{component.unique_id}/properties")

    def get_component_properties_value_set_url(self,
                                               component: "Component") -> str:
        """Url to set component property value.

        This method is here because component can have different url when
            it's a component of another SDC resource type, eg. for service and
            for VF components have different urls.

        Args:
            component (Component): Component object to prepare url for

        Returns:
            str: Component's properties url

        """
        return (f"{self.resource_inputs_url}/"
                f"resourceInstance/{component.unique_id}/properties")

    def is_own_property(self, property_to_check: Property) -> bool:
        """Check if given property is one of the resource's properties.

        Args:
            property_to_check (Property): Property to check

        Returns:
            bool: True if resource has given property, False otherwise

        """
        return any((prop == property_to_check for prop in self.properties))

    def get_component(self, sdc_resource: "SdcResource") -> Component:
        """Get resource's component.

        Get component by SdcResource object.

        Args:
            sdc_resource (SdcResource): Component's SdcResource

        Raises:
            AttributeError: Component with given SdcResource does not exist

        Returns:
            Component: Component object

        """
        for component in self.components:
            if component.sdc_resource.name == sdc_resource.name:
                return component
        raise AttributeError("SDC resource %s is not a component" %
                             sdc_resource.name)

    def declare_input_for_own_property(self, property_obj: Property) -> None:
        """Declare input for resource's property.

        For each property input can be declared.

        Args:
            property_obj (Property): Property to declare input

        """
        self._logger.debug("Declare input for SDC resource property")
        self.send_message_json("POST",
                               f"Declare new input for {property_obj.name} property",
                               f"{self.resource_inputs_url}/create/inputs",
                               data=jinja_env().get_template(\
                                   "sdc_resource_add_input.json.j2").\
                                       render(\
                                           sdc_resource=self,
                                           property=property_obj),
                               exception=ValueError)

    def declare_nested_input(self, nested_input: NestedInput) -> None:
        """Declare nested input for SDC resource.

        Nested input is an input of one of the components.

        Args:
            nested_input (NestedInput): Nested input object

        """
        self._logger.debug(
            "Declare input for SDC resource's component property")
        component: Component = self.get_component(nested_input.sdc_resource)
        self.send_message_json("POST",
                               f"Declare new input for {nested_input.input_obj.name} input",
                               f"{self.resource_inputs_url}/create/inputs",
                               data=jinja_env().get_template(\
                                   "sdc_resource_add_nested_input.json.j2").\
                                       render(\
                                           sdc_resource=self,
                                           component=component,
                                           input=nested_input.input_obj),
                               exception=ValueError)

    def declare_input(self, input_to_declare: Union[Property,
                                                    NestedInput]) -> None:
        """Declare input for given property or nested input object.

        Call SDC FE API to declare input for given property.

        Args:
            input_declaration (Union[Property, NestedInput]): Property to declare input
                or NestedInput object

        """
        self._logger.debug("Declare input")
        if isinstance(input_to_declare, Property):
            if self.is_own_property(input_to_declare):
                self.declare_input_for_own_property(input_to_declare)
            else:
                raise ValueError("Given property is not SDC resource property")
        else:
            self.declare_nested_input(input_to_declare)

    def add_property(self, property_to_add: Property) -> None:
        """Add property to resource.

        Call SDC FE API to add property to resource.

        Args:
            property_to_add (Property): Property object to add to resource.

        Raises:
            AttributeError: Resource has not DRAFT status

        """
        if self.status != const.DRAFT:
            raise AttributeError(
                "Can't add property to resource which is not in DRAFT status")
        self._logger.debug("Add property to sdc resource")
        self.send_message_json("POST",
                               f"Declare new property for {self.name} sdc resource",
                               self.add_property_url,
                               data=jinja_env().get_template(
                                   "sdc_resource_add_property.json.j2").\
                                    render(
                                        property=property_to_add
                                    ),
                               exception=ValueError)

    def set_property_value(self, property_obj: Property, value: Any) -> None:
        """Set property value.

        Set given value to resource property

        Args:
            property_obj (Property): Property object
            value (Any): Property value to set

        """
        if not self.is_own_property(property_obj):
            raise ValueError("Given property is not a resource's property")
        self._logger.debug("Set %s property value", property_obj.name)
        self.send_message_json("PUT",
                               f"Set {property_obj.name} value to {value}",
                               self.add_property_url,
                               data=jinja_env().get_template(
                                   "sdc_resource_set_property_value.json.j2").\
                                    render(
                                        sdc_resource=self,
                                        property=property_obj,
                                        value=value
                                    ),
                               exception=ValueError
                               )

    def set_input_default_value(self, input_obj: Input,
                                default_value: Any) -> None:
        """Set input default value.

        Set given value as input default value

        Args:
            input_obj (Input): Input object
            value (Any): Default value to set

        """
        self._logger.debug("Set %s input default value", input_obj.name)
        self.send_message_json("POST",
                               f"Set {input_obj.name} default value to {default_value}",
                               self.set_input_default_value_url,
                               data=jinja_env().get_template(
                                   "sdc_resource_set_input_default_value.json.j2").\
                                    render(
                                        sdc_resource=self,
                                        input=input_obj,
                                        default_value=default_value
                                    ),
                               exception=ValueError
                               )
Пример #23
0
 def start_certification(self) -> None:
     """Start Certification on Service."""
     headers = headers_sdc_creator(SdcResource.headers)
     self._verify_lcm_to_sdc(const.CHECKED_IN,
                             const.START_CERTIFICATION,
                             headers=headers)