From f64976e64325073e08dce191d99bb807efd98301 Mon Sep 17 00:00:00 2001 From: stasys10 Date: Thu, 24 Feb 2022 17:38:03 +0000 Subject: [PATCH] Add ASD node and data types to SDC AID model Issue-ID: SDC-3938 Signed-off-by: stasys10 Change-Id: Idac990c39f7d782eb029c72e5829b607de72dbb6 Signed-off-by: stasys10 --- .../import/tosca/data-types/dataTypes.yml | 307 +++++++++++++++++++++ .../resources/import/tosca/nfv-types/asd/asd.json | 15 + .../resources/import/tosca/nfv-types/asd/asd.yml | 55 ++++ .../import/tosca/nfv-types/asdInNsd/asdInNsd.json | 15 + .../import/tosca/nfv-types/asdInNsd/asdInNsd.yml | 54 ++++ .../sdcBePy/tosca/data/onapTypesToUpgrade.json | 4 +- .../sdcBePy/tosca/models/normativeTypesList.py | 4 +- .../src/test/resources/Files/ASDs/asd.csar | Bin 11330 -> 11308 bytes 8 files changed, 452 insertions(+), 2 deletions(-) create mode 100644 catalog-be/src/main/resources/import/tosca/nfv-types/asd/asd.json create mode 100644 catalog-be/src/main/resources/import/tosca/nfv-types/asd/asd.yml create mode 100644 catalog-be/src/main/resources/import/tosca/nfv-types/asdInNsd/asdInNsd.json create mode 100644 catalog-be/src/main/resources/import/tosca/nfv-types/asdInNsd/asdInNsd.yml diff --git a/catalog-be/src/main/resources/import/tosca/data-types/dataTypes.yml b/catalog-be/src/main/resources/import/tosca/data-types/dataTypes.yml index a6071e126f..5926d4b806 100644 --- a/catalog-be/src/main/resources/import/tosca/data-types/dataTypes.yml +++ b/catalog-be/src/main/resources/import/tosca/data-types/dataTypes.yml @@ -3165,3 +3165,310 @@ onap.datatypes.ToscaConceptIdentifier: version: type: string required: true + +tosca.datatypes.asd.networkInterfaceRequirements: + derived_from: tosca.datatypes.Root + version: 0.1 + description: "Describes the datatype for network interface requirements" + properties: + trunk_mode: + description: > + Information about whether the CP instantiated from this Cp is + in Trunk mode (802.1Q or other). When operating in "trunk mode", + the Cp is capable of carrying traffic for several VLANs. + Absence of this property implies that trunkMode is not configured + for the Cp i.e. It is equivalent to boolean value "false". + required: true + type: boolean + default: false + ipam: + description: > + Identifies whether application expects IP address assignment to be + managed by the cluster infrastructure (CNI IPAM plugin), or + configured by orchestrator via for example helm input parameter, + or if IP assignment is handled by the application itself. + required: true + type: string + constraints: + - valid_values: ["infraProvided", "orchestrated", "userManaged"] + default: "infraProvided" + interface_type: + description: > + Indicates what type of network interface the application expects. + Kernel based virtual netdev based on CNIs such as ovs | bridge | + macvlan | ipvlan, or PCIe dev directly visible in application + namespace with kernel or userspace driver or bonded with the Bond + CNI, or userspace-CNI based network interface + (requires DPDK-OVS/VPP vSwitch). + required: true + type: string + constraints: + - valid_values: ["kernel.netdev", "direct.userdriver", "direct.kerneldriver", "direct.bond", "userspace"] + default: "kernel.netdev" + interface_option: + description: > + This attribute describes verified realization options for the + network interface in question. Currently listed options + (virtio and memif) are applicable for the interfaceType “userspace”. + required: false + type: list + entry_schema: + type: string + constraints: + - valid_values: ["virtio", "memif"] + interface_redundancy: + description: > + Identifies switch-plane redundancy method the application uses, + and that node infrastructure is required to comply with. + "infraProvided", “left” and “right”: The container sees a + single vNIC that a) the infrastructure bonds over both switchplanes + or b) that is connected to the network via only left or + right the switchplane. + The other cases are for a mated pair of vnics connecting to + same network, but where one vNIC connects + via left switch plane and the other via right switch plane, + and where the application manages the redundancy. + "activePassiveBond": the application bonds with move of MAC address. + "activeActiveBond“: bonded left/right links must be part of a multi-chassis LAG + "activePassiveL3": application will move application IP address between the vNICs. + "activeActiveL3": the application uses anycast/ECMP. + required: true + type: string + constraints: + - valid_values: ["infraProvided", "actPassBond", "actActBond", "actPassL3", "actActL3", "Left", "Right"] + default: "infraProvided" + nic_options: + description: > + Identifies for the direct.userdriver interface type, the physical + nics the driver is verified to work with. + Allowed values for nic types must be handled via a registry or be standardized. + required: false + type: list + entry_schema: + type: string + +tosca.datatypes.asd.paramMappings: + version: 0.1 + derived_from: tosca.datatypes.Root + description: "Describes the datatype for parameter mapping" + properties: + loadbalancer_IP: + description: > + When present, this attribute specifies the name of the deployment + artifact input parameter through which the orchestrator can + configure the loadbalancerIP parameter of the K8s service + or ingress controller that the extCpdData represents. + Note: The format of the Content strings is specific for each different + orchestration templating technology used (Helm, Teraform, etc.). + Currently only a format for use with Helm charts is suggested: + ":[.]^(0..N)[.]^(0..N)". + Whether the optional parts of the format are present depends on how the + parameter is declared in the helm chart. An example is: + "chartName:subChart1.subChart2.subChart3.Parent1.Parent2.Parent3.LBIP". + required: false + type: string + external_IPs: + description: > + When present, this attribute specifies the name of the deployment + artifact input parameter through which the orchestrator can + configure the extermalIPs parameter of the K8s service or ingress + controller, or the pod network interface annotation, that the + extCpdData represents. + Note: The format of the Content strings is specific for each different + orchestration templating technology used (Helm, Teraform, etc.). + Currently only a format for use with Helm charts is suggested: + ":[.]^(0..N)[.]^(0..N)". + Whether the optional parts of the format are present depends on how the + parameter is declared in the helm chart. An example is: + "chartName:subChart1.subChart2.subChart3.Parent1.Parent2.Parent3.extIP". + required: false + type: list + entry_schema: + type: string + nad_names: + description: > + Specifies, for an extCpdData respesenting a secondary network interface, + the name(s) of the deployment artifact input parameter(s) through which + the orchestrator can provide the names of the network attachment + definitions (NADs) the orchestrator has created as base for the network + interface the extCpdData represents. + Note 1: When the extCpdData represent a networkRedundant/mated-pair of + sriov interfaces, there are references to 2 or 3 related NADs needed + to be passed, while for other interface types only one NAD reference + is needed to be passed. + Note 2: The format of the Content strings is specific for each different + orchestration templating technology used (Helm, Teraform, etc.). + Currently only a format for use with Helm charts is suggested: + ":[.]^(0..N)[.]^(0..N)". + Whether the optional parts of the format are present depends on how the + parameter is declared in the helm chart. An example is: + chartName:"subChart1.subChart2.subChart3.Parent1.Parent2.Parent3.nadName". + Note 3: A direct attached (passthrough) network interface, such as an sriov + interface, attaches to a network via only one of the two switch planes + in the infrastructure. + When using a direct attached network interface one therefore commonly in a + pod uses a mated pair of sriov network attachments, where each interface + attaches same network but via different switchplane. + The application uses the mated pair of network interfaces as a single + logical “swith-path-redundant” network interface – and this is represented + by a single extCpdData. + Also there is a case where a third “bond” attachment interface is used in + the pod, bonding the two direct interfaces so that the application do not + need to handle the redundancy issues – application just uses the bond interface. + In this case, all three attachments are together making up a logical + “switch-path-redundant” network interface represented by a single extCpdData. + When three NADs are used in the extCpdData the NAD implementing the bond attachment + interface is provided through the parameter indicated in the third place in + the nadNames attribute. + required: false + type: list + entry_schema: + type: string + nad_namespace: + description: > + Specifies, for an extCpdData respesenting a secondary network interface, + the name of the deployment artifact input parameter through which the orchestrator + can provide the namespace where the NetworkAttachmentDefinitions (NADs) are located. + Attribute may be omitted if the namespace is same as the application + namespace. + Note: The format of the Content strings is specific for each different + orchestration templating technology used (Helm, Teraform, etc.). + Currently only a format for use with Helm charts is suggested: + ":[.]^(0..N)[.]^(0..N)". + Whether the optional parts of the format are present depends on how the + parameter is declared in the helm chart. An example is: + "chartName:subChart1.subChart2.subChart3.Parent1.Parent2.Parent3.NameSpace". + required: false + type: string + +tosca.datatypes.asd.extCpdData: + version: 0.1 + derived_from: tosca.datatypes.Root + description: "Describes the datatype for external connection point definition data" + properties: + id: + description: "The identifier of this extCpdData" + required: true + type: string + description: + description: > + This property describes for a particular ExtCpd instance + what service it exposes. + required: true + type: string + virtual_link_requirement: + description: > + Refers in an abstract way to the network or multiple networks that + the ExtCpd shall be exposed on (ex: OAM, EndUser, backhaul, LI, etc) + required: true + type: string + network_interface_realization_requirements: + description: > + Details container implementation specific requirements on + the NetworkAttachmentDefinition + required: false + type: tosca.datatypes.asd.networkInterfaceRequirements + input_param_mappings: + description: > + Information on what helm chart input parameters that + are required to be configured for this extCpd + required: false + type: tosca.datatypes.asd.paramMappings + resource_mapping: + description: > + Kubernetes API resource name for the resource manifest for the service, + ingress controller or pod + required: false + type: string + +tosca.datatypes.asd.enhancedClusterCapabilities: + version: 0.1 + derived_from: tosca.datatypes.Root + description: "Describes the datatype for parameter mapping" + properties: + min_kernel_version: + description: > + Describes the minimal required Kernel version, e.g. 4.15.0. + Coded as displayed by linux command uname –r + required: true + type: string + required_kernel_modules: + description: > + Required kernel modules are coded as listed by linux lsmod command, + e.g. ip6_tables, cryptd, nf_nat etc. + required: false + type: list + entry_schema: + type: string + conflicting_kernel_modules: + description: > + Kernel modules, which must not be present in the target environment. + The kernel modules are coded as listed by linux lsmod command, + e.g., ip6_tables, cryptd, nf_nat etc. + Example: Linux kernel SCTP module, which would conflict with use of + proprietary user space SCTP stack provided by the application. + required: false + type: list + entry_schema: + type: string + required_custom_resources: + description: > + List the custom resource kinds required to be supported in the target + environment. The list shall include those custom resource kinds which + are not delivered with the application. + required: false + type: list + entry_schema: + type: tosca.datatypes.asd.customResourceRequirement + cluster_labels: + description: > + This attribute allows to associate arbitrary labels to clusters. + These can indicate special infrastructure capabilities (e.g., NW acceleration, + GPU compute, etc.). The intent of these labels is to serve as a set of + values that can help in application placement decisions. + clusterLabels follow the Kubernetes label key-value-nomenclature + (https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/). + It is recommended that labels follow a standardized meaning e.g. for node + features (https://kubernetes-sigs.github.io/node-feature-discovery/v0.9/get-started/features.html#table-of-contents). + Example: + ClusterLabels + - feature.node.kubernetes.io/cpu-cpuid.AESNI: true + required: false + type: list + entry_schema: + type: string + required_plugin: + description: a list of the name of the required K8s plugin + required: false + type: list + entry_schema: + type: tosca.datatypes.asd.requiredPlugin + +tosca.datatypes.asd.customResourceRequirement: + version: 0.1 + derived_from: tosca.datatypes.Root + description: > + kind: "Redis", apiVersion: "kubedb.com/v1alpha1" + properties: + kind: + description: "the name of the custom resource requirement" + type: string + required: true + api_version: + description: "the api version of the custom resource requirement" + type: string + required: true + +tosca.datatypes.asd.requiredPlugin: + version: 0.1 + derived_from: tosca.datatypes.Root + description: "the required K8s plugin" + properties: + name: + description: "the name of the required K8s plugin" + type: string + required: true + version: + description: "the version of the required K8s plugin" + type: string + required: true \ No newline at end of file diff --git a/catalog-be/src/main/resources/import/tosca/nfv-types/asd/asd.json b/catalog-be/src/main/resources/import/tosca/nfv-types/asd/asd.json new file mode 100644 index 0000000000..7da91b29be --- /dev/null +++ b/catalog-be/src/main/resources/import/tosca/nfv-types/asd/asd.json @@ -0,0 +1,15 @@ +{ + "payloadName": "asd.yml", + "contactId": "jh0003", + "name": "asd", + "description": "The ASD node type", + "resourceIconPath": "defaulticon", + "resourceType": "VFC", + "categories": [{ + "name": "Generic", + "subcategories": [{ + "name": "Network Elements" + }] + }], + "tags": ["asd"] +} diff --git a/catalog-be/src/main/resources/import/tosca/nfv-types/asd/asd.yml b/catalog-be/src/main/resources/import/tosca/nfv-types/asd/asd.yml new file mode 100644 index 0000000000..e204538f08 --- /dev/null +++ b/catalog-be/src/main/resources/import/tosca/nfv-types/asd/asd.yml @@ -0,0 +1,55 @@ +tosca_definitions_version: tosca_simple_yaml_1_3 +node_types: + tosca.nodes.asd: + derived_from: tosca.nodes.Root + description: "The ASD node type" + version: 0.1 + properties: + descriptor_id: + type: string # UUID + required: true + description: Identifier of this ASD. It is in UUID format as specified in RFC 4122 + descriptor_invariant_id: + type: string # UUID + required: true + description: > + Identifier of this descriptor in a version independent manner. This attribute + is invariant across versions of ASD. It is in UUID format as specified in RFC 4122 + descriptor_version: + type: string + required: true + description: Identifies the version of the ASD. + schema_version: + type: string + required: true + description: Identifies the Identifies the version of this ASD’s schema. + function_description: + type: string + required: false + description: Description of the application service described by this ASD. + provider: + type: string + required: true + description: Identifies the provider of the ASD. + application_name: + type: string + required: true + description: Name to identify the application service described by this ASD + application_version: + type: string + required: true + description: Identifies the version of the application service described by this ASD. + ext_cpds: + type: list + required: false + entry_schema: + type: tosca.datatypes.asd.extCpdData + description: > + Describes the externally exposed connection points of the application + service described by this ASD + enhanced_cluster_capabilities: + type: tosca.datatypes.asd.enhancedClusterCapabilities + required: false + description: > + A list of expected capabilities of the target Kubernetes cluster to aid + placement of the application service on a suitable cluster. diff --git a/catalog-be/src/main/resources/import/tosca/nfv-types/asdInNsd/asdInNsd.json b/catalog-be/src/main/resources/import/tosca/nfv-types/asdInNsd/asdInNsd.json new file mode 100644 index 0000000000..910a2c6c04 --- /dev/null +++ b/catalog-be/src/main/resources/import/tosca/nfv-types/asdInNsd/asdInNsd.json @@ -0,0 +1,15 @@ +{ + "payloadName": "asdInNsd.yml", + "contactId": "jh0003", + "name": "asdInNsd", + "description": "The ASD in NSD", + "resourceIconPath": "defaulticon", + "resourceType": "VFC", + "categories": [{ + "name": "Generic", + "subcategories": [{ + "name": "Network Elements" + }] + }], + "tags": ["asdInNsd"] +} diff --git a/catalog-be/src/main/resources/import/tosca/nfv-types/asdInNsd/asdInNsd.yml b/catalog-be/src/main/resources/import/tosca/nfv-types/asdInNsd/asdInNsd.yml new file mode 100644 index 0000000000..fa48ca5eda --- /dev/null +++ b/catalog-be/src/main/resources/import/tosca/nfv-types/asdInNsd/asdInNsd.yml @@ -0,0 +1,54 @@ +tosca_definitions_version: tosca_simple_yaml_1_3 +node_types: + tosca.nodes.asdInNsd: + derived_from: tosca.nodes.nfv.VNF + description: "The generic ASD node types for NS requirements. Optional properties are not list here." + version: 0.1 + properties: + descriptor_id: + type: string # UUID + description: Identifier of an ASD. It is in UUID format as specified in RFC 4122 + required: true + descriptor_version: + type: string + description: Identifies the version of the ASD. + required: true + default: "" + provider: + type: string + description: Identifies the provider of the ASD + required: true + default: "" + product_name: + type: string + description: Please use the application_name property + required: true + default: "" + software_version: + type: string + description: Please use the application_version property + required: true + default: "" + flavour_id: + type: string + required: true + constraints: [ equal: "simple" ] + default: "simple" + flavour_description: + type: string + required: true + default: "" + vnfm_info: + type: list + required: true + entry_schema: + type: string + requirements: + - virtual_link: + capability: tosca.capabilities.nfv.VirtualLinkable + relationship: tosca.relationships.nfv.VirtualLinkableTo + occurrences: [ 0,0 ] + - virtual_links: + capability: tosca.capabilities.nfv.VirtualLinkable + relationship: tosca.relationships.nfv.VirtualLinkableTo + occurrences: [ 0,UNBOUNDED ] diff --git a/catalog-be/src/main/resources/scripts/sdcBePy/tosca/data/onapTypesToUpgrade.json b/catalog-be/src/main/resources/scripts/sdcBePy/tosca/data/onapTypesToUpgrade.json index 96c30e25f0..18e9632639 100644 --- a/catalog-be/src/main/resources/scripts/sdcBePy/tosca/data/onapTypesToUpgrade.json +++ b/catalog-be/src/main/resources/scripts/sdcBePy/tosca/data/onapTypesToUpgrade.json @@ -3,7 +3,9 @@ "allottedResource", "NS", "NsVirtualLink", - "Sap" + "Sap", + "asd", + "asdInNsd" ], "nfv_2_7_1": [ "NfpPositionElement", diff --git a/catalog-be/src/main/resources/scripts/sdcBePy/tosca/models/normativeTypesList.py b/catalog-be/src/main/resources/scripts/sdcBePy/tosca/models/normativeTypesList.py index 0ee2d6ca99..d94a0b4b40 100644 --- a/catalog-be/src/main/resources/scripts/sdcBePy/tosca/models/normativeTypesList.py +++ b/catalog-be/src/main/resources/scripts/sdcBePy/tosca/models/normativeTypesList.py @@ -113,7 +113,9 @@ def get_nfv(base_file_location="/"): "PonUni", "OltNni", "OntNni", - "Sap"]) + "Sap", + "asd", + "asdInNsd"]) def get_nfv_2_7_1(base_file_location="/"): return NormativeTypeCandidate(base_file_location + "nfv-types/2.7.1/", diff --git a/integration-tests/src/test/resources/Files/ASDs/asd.csar b/integration-tests/src/test/resources/Files/ASDs/asd.csar index 3f928b5b52945d2ce3bf612a20907d55f150d911..2b4de0594226589008e2cae02b25156075f58631 100644 GIT binary patch delta 4217 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