From 93f10aabdba5a1c745d002654c0bb6345a682a47 Mon Sep 17 00:00:00 2001 From: Hashir Mohammed Date: Wed, 1 Mar 2017 07:42:47 -0500 Subject: [PATCH] * YANG to TOSCA translator * Bug fixes - TOSCA to YANG Signed-off-by: Hashir Mohammed --- common/python/CMakeLists.txt | 18 + .../mano/tosca_translator/dummy_vnf_node.yaml | 1496 +++++++++++++++++ .../rwmano/tosca/tosca_compute.py | 47 +- .../rwmano/tosca/tosca_network_network.py | 15 +- .../rwmano/tosca/tosca_vnf_configuration.py | 33 +- .../rwmano/translate_node_templates.py | 37 +- .../data/ping_pong_csar_tosca_new_spec.zip | Bin 0 -> 113058 bytes .../mano/yang_translator/riftiotypes.yaml | 1493 ++++++++++++++++ .../rwmano/syntax/tosca_resource.py | 36 +- .../rwmano/syntax/tosca_template.py | 42 +- .../rwmano/translate_descriptors.py | 48 +- .../yang_translator/rwmano/yang/yang_nsd.py | 191 ++- .../yang_translator/rwmano/yang/yang_vdu.py | 172 +- .../yang_translator/rwmano/yang/yang_vnfd.py | 324 ++-- .../yang_translator/rwmano/yang_translator.py | 194 ++- .../rift/tasklets/rwlaunchpad/export.py | 58 +- .../rift/tasklets/rwlaunchpad/tosca.py | 41 +- 17 files changed, 3880 insertions(+), 365 deletions(-) create mode 100644 common/python/rift/mano/tosca_translator/dummy_vnf_node.yaml create mode 100644 common/python/rift/mano/tosca_translator/test/data/ping_pong_csar_tosca_new_spec.zip create mode 100644 common/python/rift/mano/yang_translator/riftiotypes.yaml diff --git a/common/python/CMakeLists.txt b/common/python/CMakeLists.txt index 22018c39..50ed1de7 100644 --- a/common/python/CMakeLists.txt +++ b/common/python/CMakeLists.txt @@ -112,6 +112,10 @@ rift_python_install_tree( rift/mano/tosca_translator/rwmano/tosca/tosca_compute.py rift/mano/tosca_translator/rwmano/tosca/tosca_scaling_group.py rift/mano/tosca_translator/rwmano/tosca/tosca_initial_config.py + rift/mano/tosca_translator/rwmano/tosca/tosca_placement_group.py + rift/mano/tosca_translator/rwmano/tosca/tosca_vnf_configuration.py + rift/mano/tosca_translator/rwmano/tosca/tosca_forwarding_graph.py + rift/mano/tosca_translator/rwmano/tosca/tosca_forwarding_path.py rift/mano/tosca_translator/common/__init__.py rift/mano/tosca_translator/common/utils.py rift/mano/tosca_translator/common/exception.py @@ -167,6 +171,20 @@ set(TRANSLATOR_SCRIPTS ${CMAKE_CURRENT_SOURCE_DIR}/rift/mano/tosca_translator/tosca-translator ${CMAKE_CURRENT_SOURCE_DIR}/rift/mano/yang_translator/yang-translator) +install( + FILES rift/mano/yang_translator/riftiotypes.yaml + DESTINATION + usr/rift/mano/common + COMPONENT ${PKG_LONG_NAME} + ) + +install( + FILES rift/mano/tosca_translator/dummy_vnf_node.yaml + DESTINATION + usr/rift/mano/common + COMPONENT ${PKG_LONG_NAME} + ) + install( FILES ${TRANSLATOR_SCRIPTS} DESTINATION diff --git a/common/python/rift/mano/tosca_translator/dummy_vnf_node.yaml b/common/python/rift/mano/tosca_translator/dummy_vnf_node.yaml new file mode 100644 index 00000000..6798e2ae --- /dev/null +++ b/common/python/rift/mano/tosca_translator/dummy_vnf_node.yaml @@ -0,0 +1,1496 @@ +tosca_definitions_version: tosca_simple_profile_for_nfv_1_0 +description: Toy NS +data_types: + tosca.datatypes.nfv.riftio.dashboard_params: + properties: + path: + type: string + description: >- + The HTTP path for the dashboard + port: + type: tosca.datatypes.network.PortDef + description: >- + The HTTP port for the dashboard + default: 80 + https: + type: boolean + description: >- + Pick HTTPS instead of HTTP , Default is false + default: false + required: false + tosca.datatypes.nfv.riftio.monitoring_param_ui: + properties: + description: + type: string + required: false + group_tag: + type: string + description: >- + A simple tag to group monitoring parameters + required: false + widget_type: + type: string + description: >- + Type of the widget + default: counter + constraints: + - valid_values: + - histogram + - bar + - gauge + - slider + - counter + - textbox + units: + type: string + required: false + tosca.datatypes.nfv.riftio.monitoring_param_value: + properties: + value_type: + type: string + default: integer + constraints: + - valid_values: + - integer + - float + - string + numeric_min: + type: integer + description: >- + Minimum value for the parameter + required: false + numeric_max: + type: integer + description: >- + Maxium value for the parameter + required: false + string_min: + type: integer + description: >- + Minimum string length for the parameter + required: false + constraints: + - greater_or_equal: 0 + string_max: + type: integer + description: >- + Maximum string length for the parameter + required: false + constraints: + - greater_or_equal: 0 + tosca.datatypes.compute.Container.Architecture.CPUAllocation: + derived_from: tosca.datatypes.Root + properties: + cpu_affinity: + type: string + required: false + constraints: + - valid_values: [shared, dedicated, any] + thread_allocation: + type: string + required: false + constraints: + - valid_values: [avoid, separate, isolate, prefer] + socket_count: + type: integer + required: false + core_count: + type: integer + required: false + thread_count: + type: integer + required: false + + tosca.datatypes.compute.Container.Architecture.NUMA: + derived_from: tosca.datatypes.Root + properties: + id: + type: integer + constraints: + - greater_or_equal: 0 + vcpus: + type: list + entry_schema: + type: integer + constraints: + - greater_or_equal: 0 + mem_size: + type: scalar-unit.size + constraints: + - greater_or_equal: 0 MB + tosca.datatypes.nfv.riftio.paired_thread_map: + properties: + thread_a: + type: integer + required: true + constraints: + - greater_or_equal: 0 + thread_b: + type: integer + required: true + constraints: + - greater_or_equal: 0 + + tosca.datatypes.nfv.riftio.paired_threads: + properties: + num_paired_threads: + type: integer + constraints: + - greater_or_equal: 1 + paired_thread_ids: + type: list + entry_schema: + type: tosca.datatypes.nfv.riftio.paired_thread_map + constraints: + - max_length: 16 + required: false + + tosca.datatypes.compute.riftio.numa: + properties: + id: + type: integer + constraints: + - greater_or_equal: 0 + vcpus: + type: list + entry_schema: + type: integer + constraints: + - greater_or_equal: 0 + required: false + mem_size: + type: scalar-unit.size + constraints: + - greater_or_equal: 0 MB + required: false + om_numa_type: + type: string + description: Openmano Numa type selection + constraints: + - valid_values: [cores, paired-threads, threads] + required: false + num_cores: + type: integer + description: Use when om_numa_type is cores + constraints: + - greater_or_equal: 1 + required: false + paired_threads: + type: tosca.datatypes.nfv.riftio.paired_threads + description: Use when om_numa_type is paired-threads + required: false + num_threads: + type: integer + description: Use when om_numa_type is threads + constraints: + - greater_or_equal: 1 + required: false + + tosca.nfv.datatypes.pathType: + properties: + forwarder: + type: string + required: true + capability: + type: string + required: true + + tosca.nfv.datatypes.aclType: + properties: + eth_type: + type: string + required: false + eth_src: + type: string + required: false + eth_dst: + type: string + required: false + vlan_id: + type: integer + constraints: + - in_range: [ 1, 4094 ] + required: false + vlan_pcp: + type: integer + constraints: + - in_range: [ 0, 7 ] + required: false + mpls_label: + type: integer + constraints: + - in_range: [ 16, 1048575] + required: false + mpls_tc: + type: integer + constraints: + - in_range: [ 0, 7 ] + required: false + ip_dscp: + type: integer + constraints: + - in_range: [ 0, 63 ] + required: false + ip_ecn: + type: integer + constraints: + - in_range: [ 0, 3 ] + required: false + ip_src_prefix: + type: string + required: false + ip_dst_prefix: + type: string + required: false + ip_proto: + type: integer + constraints: + - in_range: [ 1, 254 ] + required: false + destination_port_range: + type: string + required: false + source_port_range: + type: string + required: false + network_src_port_id: + type: string + required: false + network_dst_port_id: + type: string + required: false + network_id: + type: string + required: false + network_name: + type: string + required: false + tenant_id: + type: string + required: false + icmpv4_type: + type: integer + constraints: + - in_range: [ 0, 254 ] + required: false + icmpv4_code: + type: integer + constraints: + - in_range: [ 0, 15 ] + required: false + arp_op: + type: integer + constraints: + - in_range: [ 1, 25 ] + required: false + arp_spa: + type: string + required: false + arp_tpa: + type: string + required: false + arp_sha: + type: string + required: false + arp_tha: + type: string + required: false + ipv6_src: + type: string + required: false + ipv6_dst: + type: string + required: false + ipv6_flabel: + type: integer + constraints: + - in_range: [ 0, 1048575] + required: false + icmpv6_type: + type: integer + constraints: + - in_range: [ 0, 255] + required: false + icmpv6_code: + type: integer + constraints: + - in_range: [ 0, 7] + required: false + ipv6_nd_target: + type: string + required: false + ipv6_nd_sll: + type: string + required: false + ipv6_nd_tll: + type: string + required: false + + + tosca.datatypes.nfv.riftio.vnf_configuration: + properties: + config_type: + type: string + description: >- + Type of the configuration agent to use + constraints: + - valid_values: [script, netconf, rest, juju] + config_details: + type: map + description: >- + Specify the details for the config agent, like + script type, juju charm to use, etc. + config_template: + required: false + type: string + config_delay: + type: integer + constraints: + - greater_or_equal: 0 + default: 0 + required: false + config_priority: + type: integer + constraints: + - greater_than: 0 + + tosca.datatypes.nfv.riftio.parameter_value: + properties: + name: + type: string + description: Name of the parameter + value: + type: string + description: Value of the parameter + + tosca.datatypes.nfv.riftio.config_primitive: + properties: + name: + type: string + seq: + type: integer + description: >- + Order in which to apply, when multiple ones are defined + default: 0 + constraints: + - greater_or_equal: 0 + parameter: + type: list + entry_schema: + type: tosca.datatypes.nfv.riftio.parameter_value + user_defined_script: + type: string + tosca.datatypes.nfv.riftio.primitive_parameter: + properties: + data_type: + type: string + description: >- + Data type associated with the name + constraints: + - valid_values: [string, integer, boolean] + mandatory: + type: boolean + description: >- + If this field is mandatory + default: false + required: false + default_value: + type: string + description: >- + The default value for this field + required: false + parameter_pool: + type: string + description: >- + Parameter pool name to use for this parameter + required: false + read_only: + type: boolean + description: >- + The value should be greyed out by the UI. + Only applies to parameters with default values. + required: false + default: false + hidden: + type: boolean + description: >- + The field should be hidden by the UI. + Only applies to parameters with default values. + required: false + default: false + tosca.datatypes.nfv.riftio.primitive_parameter_group: + properties: + name: + type: string + description: >- + Name of the parameter group + mandatory: + type: boolean + description: >- + If this group is mandatory + default: false + required: false + parameter: + type: map + description: >- + List of parameters for the service primitive + entry_schema: osca.datatypes.riftio.primitive_parameter + + tosca.datatypes.nfv.riftio.vnf_primitive_group: + properties: + vnf_name: + type: string + description: >- + Name of the VNF in the NS + primitive: + type: map + entry_schema: + type: string + description: >- + Index and name of the primitive + + +capability_types: + tosca.capabilities.nfv.riftio.mgmt_interface: + derived_from: tosca.capabilities.Endpoint + properties: + static_ip: + type: string + required: false + description: >- + Specifies the static IP address for managing the VNF + connection_point: + type: string + required: false + description: >- + Use the ip address associated with this connection point + dashboard_params: + type: tosca.datatypes.nfv.riftio.dashboard_params + required: false + description: >- + Parameters for the VNF dashboard + tosca.capabilities.nfv.riftio.monitoring_param: + derived_from: tosca.capabilities.nfv.Metric + properties: + name: + type: string + required: false + description: + type: string + required: false + protocol: + type: string + default: http + constraints: + - equal: http + polling_interval: + type: scalar-unit.time + description: >- + The HTTP polling interval in seconds + default: 2 s + username: + type: string + description: >- + The HTTP basic auth username + required: false + password: + type: string + description: >- + The HTTP basic auth password + required: false + method: + type: string + description: >- + This is the method to be performed at the uri. + GET by default for action + default: get + constraints: + - valid_values: [post, put, get, delete, options, patch] + headers: + type: map + entry_schema: + type: string + description: >- + Custom HTTP headers to put on HTTP request + required: false + json_query_method: + type: string + description: >- + The method to extract a value from a JSON response + namekey - Use the name as the key for a non-nested value. + jsonpath - Use jsonpath-rw implemenation to extract a value. + objectpath - Use objectpath implemenation to extract a value. + constraints: + - valid_values: [namekey, jsonpath, objectpath] + default: namekey + json_query_path: + type: string + description: >- + The json path to use to extract value from JSON structure + required: false + json_object_path: + type: string + description: >- + The object path to use to extract value from JSON structure + required: false + ui_data: + type: tosca.datatypes.nfv.riftio.monitoring_param_ui + required: false + constraints: + type: tosca.datatypes.nfv.riftio.monitoring_param_value + required: false + tosca.capabilities.nfv.riftio.numa_extension: + derived_from: tosca.capabilities.Root + properties: + node_cnt: + type: integer + description: >- + The number of numa nodes to expose to the VM + constraints: + - greater_or_equal: 0 + mem_policy: + type: string + description: >- + This policy specifies how the memory should + be allocated in a multi-node scenario. + STRICT - The memory must be allocated + strictly from the memory attached + to the NUMA node. + PREFERRED - The memory should be allocated + preferentially from the memory + attached to the NUMA node + constraints: + - valid_values: [strict, preferred, STRICT, PREFERRED] + node: + type: list + entry_schema: + type: tosca.datatypes.compute.riftio.numa + tosca.capabilities.nfv.riftio.vswitch_epa: + derived_from: tosca.capabilities.Root + properties: + ovs_acceleration: + type: string + description: |- + Specifies Open vSwitch acceleration mode. + MANDATORY - OVS acceleration is required + PREFERRED - OVS acceleration is preferred + constraints: + - valid_values: [mandatory, preferred, disabled, MANDATORY, PREFERRED, DISABLED] + ovs_offload: + type: string + description: |- + Specifies Open vSwitch hardware offload mode. + MANDATORY - OVS offload is required + PREFERRED - OVS offload is preferred + constraints: + - valid_values: [mandatory, preferred, disabled, MANDATORY, PREFERRED, DISABLED] + + tosca.capabilities.nfv.riftio.hypervisor_epa: + derived_from: tosca.capabilities.Root + properties: + type: + type: string + description: |- + Specifies the type of hypervisor. + constraints: + - valid_values: [prefer_kvm, require_kvm, PREFER_KVM, REQUIRE_KVM] + version: + type: string + + tosca.capabilities.nfv.riftio.host_epa: + derived_from: tosca.capabilities.Root + properties: + cpu_model: + type: string + description: >- + Host CPU model. Examples include SandyBridge, + IvyBridge, etc. + required: false + constraints: + - valid_values: + - prefer_westmere + - require_westmere + - prefer_sandbridge + - require_sandybridge + - prefer_ivybridge + - require_ivybridge + - prefer_haswell + - require_haswell + - prefer_broadwell + - require_broadwell + - prefer_nehalem + - require_nehalem + - prefer_penryn + - require_penryn + - prefer_conroe + - require_conroe + - prefer_core2duo + - require_core2duo + - PREFER_WESTMERE + - REQUIRE_WESTMERE + - PREFER_SANDBRIDGE + - REQUIRE_SANDYBRIDGE + - PREFER_IVYBRIDGE + - REQUIRE_IVYBRIDGE + - PREFER_HASWELL + - REQUIRE_HASWELL + - PREFER_BROADWELL + - REQUIRE_BROADWELL + - PREFER_NEHALEM + - REQUIRE_NEHALEM + - PREFER_PENRYN + - REQUIRE_PENRYN + - PREFER_CONROE + - REQUIRE_CONROE + - PREFER_CORE2DUO + - REQUIRE_CORE2DUO + cpu_arch: + type: string + description: >- + Host CPU architecture + required: false + constraints: + - valid_values: + - prefer_x86 + - require_x86 + - prefer_x86_64 + - require_x86_64 + - prefer_i686 + - require_i686 + - prefer_ia64 + - require_ia64 + - prefer_armv7 + - require_armv7 + - prefer_armv8 + - require_armv8 + - PREFER_X86 + - REQUIRE_X86 + - PREFER_X86_64 + - REQUIRE_X86_64 + - PREFER_I686 + - REQUIRE_I686 + - PREFER_IA64 + - REQUIRE_IA64 + - PREFER_ARMV7 + - REQUIRE_ARMV7 + - PREFER_ARMV8 + - REQUIRE_ARMV8 + cpu_vendor: + type: string + description: >- + Host CPU vendor + required: false + constraints: + - valid_values: + - prefer_intel + - require_intel + - prefer_amd + - requie_amd + - PREFER_INTEL + - REQUIRE_INTEL + - PREFER_AMD + - REQUIE_AMD + cpu_socket_count: + type: integer + description: >- + Number of sockets on the host + required: false + constraints: + - greater_than : 0 + cpu_core_count: + type: integer + description: >- + Number of cores on the host + required: false + constraints: + - greater_than : 0 + cpu_core_thread_count: + type: integer + description: >- + Number of threads per core on the host + required: false + constraints: + - greater_than : 0 + cpu_feature: + type: list + entry_schema: + type: string + description: |- + Enumeration for CPU features. + + AES- CPU supports advanced instruction set for + AES (Advanced Encryption Standard). + + CAT- Cache Allocation Technology (CAT) allows + an Operating System, Hypervisor, or similar + system management agent to specify the amount + of L3 cache (currently the last-level cache + in most server and client platforms) space an + application can fill (as a hint to hardware + functionality, certain features such as power + management may override CAT settings). + + CMT- Cache Monitoring Technology (CMT) allows + an Operating System, Hypervisor, or similar + system management agent to determine the + usage of cache based on applications running + on the platform. The implementation is + directed at L3 cache monitoring (currently + the last-level cache in most server and + client platforms). + + DDIO- Intel Data Direct I/O (DDIO) enables + Ethernet server NICs and controllers talk + directly to the processor cache without a + detour via system memory. This enumeration + specifies if the VM requires a DDIO + capable host. + required: false + constraints: + -valid_values: + - prefer_aes + - require_aes + - prefer_cat + - require_cat + - prefer_cmt + - require_cmt + - prefer_ddio + - require_ddio + - prefer_vme + - require_vme + - prefer_de + - require_de + - prefer_pse + - require_pse + - prefer_tsc + - require_tsc + - prefer_msr + - require_msr + - prefer_pae + - require_pae + - prefer_mce + - require_mce + - prefer_cx8 + - require_cx8 + - prefer_apic + - require_apic + - prefer_sep + - require_sep + - prefer_mtrr + - require_mtrr + - prefer_pge + - require_pge + - prefer_mca + - require_mca + - prefer_cmov + - require_cmov + - prefer_pat + - require_pat + - prefer_pse36 + - require_pse36 + - prefer_clflush + - require_clflush + - prefer_dts + - require_dts + - prefer_acpi + - require_acpi + - prefer_mmx + - require_mmx + - prefer_fxsr + - require_fxsr + - prefer_sse + - require_sse + - prefer_sse2 + - require_sse2 + - prefer_ss + - require_ss + - prefer_ht + - require_ht + - prefer_tm + - require_tm + - prefer_ia64 + - require_ia64 + - prefer_pbe + - require_pbe + - prefer_rdtscp + - require_rdtscp + - prefer_pni + - require_pni + - prefer_pclmulqdq + - require_pclmulqdq + - prefer_dtes64 + - require_dtes64 + - prefer_monitor + - require_monitor + - prefer_ds_cpl + - require_ds_cpl + - prefer_vmx + - require_vmx + - prefer_smx + - require_smx + - prefer_est + - require_est + - prefer_tm2 + - require_tm2 + - prefer_ssse3 + - require_ssse3 + - prefer_cid + - require_cid + - prefer_fma + - require_fma + - prefer_cx16 + - require_cx16 + - prefer_xtpr + - require_xtpr + - prefer_pdcm + - require_pdcm + - prefer_pcid + - require_pcid + - prefer_dca + - require_dca + - prefer_sse4_1 + - require_sse4_1 + - prefer_sse4_2 + - require_sse4_2 + - prefer_x2apic + - require_x2apic + - prefer_movbe + - require_movbe + - prefer_popcnt + - require_popcnt + - prefer_tsc_deadline_timer + - require_tsc_deadline_timer + - prefer_xsave + - require_xsave + - prefer_avx + - require_avx + - prefer_f16c + - require_f16c + - prefer_rdrand + - require_rdrand + - prefer_fsgsbase + - require_fsgsbase + - prefer_bmi1 + - require_bmi1 + - prefer_hle + - require_hle + - prefer_avx2 + - require_avx2 + - prefer_smep + - require_smep + - prefer_bmi2 + - require_bmi2 + - prefer_erms + - require_erms + - prefer_invpcid + - require_invpcid + - prefer_rtm + - require_rtm + - prefer_mpx + - require_mpx + - prefer_rdseed + - require_rdseed + - prefer_adx + - require_adx + - prefer_smap + - require_smap + - PREFER_AES + - REQUIRE_AES + - PREFER_CAT + - REQUIRE_CAT + - PREFER_CMT + - REQUIRE_CMT + - PREFER_DDIO + - REQUIRE_DDIO + - PREFER_VME + - REQUIRE_VME + - PREFER_DE + - REQUIRE_DE + - PREFER_PSE + - REQUIRE_PSE + - PREFER_TSC + - REQUIRE_TSC + - PREFER_MSR + - REQUIRE_MSR + - PREFER_PAE + - REQUIRE_PAE + - PREFER_MCE + - REQUIRE_MCE + - PREFER_CX8 + - REQUIRE_CX8 + - PREFER_APIC + - REQUIRE_APIC + - PREFER_SEP + - REQUIRE_SEP + - PREFER_MTRR + - REQUIRE_MTRR + - PREFER_PGE + - REQUIRE_PGE + - PREFER_MCA + - REQUIRE_MCA + - PREFER_CMOV + - REQUIRE_CMOV + - PREFER_PAT + - REQUIRE_PAT + - PREFER_PSE36 + - REQUIRE_PSE36 + - PREFER_CLFLUSH + - REQUIRE_CLFLUSH + - PREFER_DTS + - REQUIRE_DTS + - PREFER_ACPI + - REQUIRE_ACPI + - PREFER_MMX + - REQUIRE_MMX + - PREFER_FXSR + - REQUIRE_FXSR + - PREFER_SSE + - REQUIRE_SSE + - PREFER_SSE2 + - REQUIRE_SSE2 + - PREFER_SS + - REQUIRE_SS + - PREFER_HT + - REQUIRE_HT + - PREFER_TM + - REQUIRE_TM + - PREFER_IA64 + - REQUIRE_IA64 + - PREFER_PBE + - REQUIRE_PBE + - PREFER_RDTSCP + - REQUIRE_RDTSCP + - PREFER_PNI + - REQUIRE_PNI + - PREFER_PCLMULQDQ + - REQUIRE_PCLMULQDQ + - PREFER_DTES64 + - REQUIRE_DTES64 + - PREFER_MONITOR + - REQUIRE_MONITOR + - PREFER_DS_CPL + - REQUIRE_DS_CPL + - PREFER_VMX + - REQUIRE_VMX + - PREFER_SMX + - REQUIRE_SMX + - PREFER_EST + - REQUIRE_EST + - PREFER_TM2 + - REQUIRE_TM2 + - PREFER_SSSE3 + - REQUIRE_SSSE3 + - PREFER_CID + - REQUIRE_CID + - PREFER_FMA + - REQUIRE_FMA + - PREFER_CX16 + - REQUIRE_CX16 + - PREFER_XTPR + - REQUIRE_XTPR + - PREFER_PDCM + - REQUIRE_PDCM + - PREFER_PCID + - REQUIRE_PCID + - PREFER_DCA + - REQUIRE_DCA + - PREFER_SSE4_1 + - REQUIRE_SSE4_1 + - PREFER_SSE4_2 + - REQUIRE_SSE4_2 + - PREFER_X2APIC + - REQUIRE_X2APIC + - PREFER_MOVBE + - REQUIRE_MOVBE + - PREFER_POPCNT + - REQUIRE_POPCNT + - PREFER_TSC_DEADLINE_TIMER + - REQUIRE_TSC_DEADLINE_TIMER + - PREFER_XSAVE + - REQUIRE_XSAVE + - PREFER_AVX + - REQUIRE_AVX + - PREFER_F16C + - REQUIRE_F16C + - PREFER_RDRAND + - REQUIRE_RDRAND + - PREFER_FSGSBASE + - REQUIRE_FSGSBASE + - PREFER_BMI1 + - REQUIRE_BMI1 + - PREFER_HLE + - REQUIRE_HLE + - PREFER_AVX2 + - REQUIRE_AVX2 + - PREFER_SMEP + - REQUIRE_SMEP + - PREFER_BMI2 + - REQUIRE_BMI2 + - PREFER_ERMS + - REQUIRE_ERMS + - PREFER_INVPCID + - REQUIRE_INVPCID + - PREFER_RTM + - REQUIRE_RTM + - PREFER_MPX + - REQUIRE_MPX + - PREFER_RDSEED + - REQUIRE_RDSEED + - PREFER_ADX + - REQUIRE_ADX + - PREFER_SMAP + - REQUIRE_SMAP + om_cpu_model_string: + type: string + description: >- + Openmano CPU model string + required: false + om_cpu_feature: + type: list + entry_schema: + type: string + description: >- + List of openmano CPU features + required: false + + tosca.capabilities.nfv.riftio.sfc: + derived_from: tosca.capabilities.Root + description: >- + Service Function Chaining support on this VDU + properties: + sfc_type: + type: string + description: >- + Type of node in Service Function Chaining Architecture + constraints: + - valid_values: [unaware, classifier, sf, sff, UNAWARE, CLASSIFIER, SF, SFF] + default: unaware + sf_type: + type: string + description: >- + Type of Service Function. + NOTE- This needs to map with Service Function Type in ODL to + support VNFFG. Service Function Type is manadatory param in ODL + SFC. + required: false + tosca.capabilities.Compute.Container.Architecture: + derived_from: tosca.capabilities.Container + properties: + mem_page_size: + type: string + description: >- + Memory page allocation size. If a VM requires + hugepages, it should choose huge or size_2MB + or size_1GB. If the VM prefers hugepages, it + should chose prefer_huge. + huge - Require hugepages (either 2MB or 1GB) + normal - Does not require hugepages + size_2MB - Requires 2MB hugepages + size_1GB - Requires 1GB hugepages + prefer_huge - Application perfers hugepages + NOTE - huge and normal is only defined in standards as of + now. + required: false + constraints: + - valid_values: [normal, huge, size_2MB, size_1GB, prefer_huge, NORMAL, HUGE, SIZE_2MB, SIZE_1GB, PREFER_HUGE] + cpu_allocation: + type: tosca.datatypes.compute.Container.Architecture.CPUAllocation + required: false + numa_nodes: + type: map + required: false + entry_schema: + type: tosca.datatypes.compute.Container.Architecture.NUMA + + +node_types: + tosca.nodes.nfv.riftio.VDU1: + derived_from: tosca.nodes.nfv.VDU + properties: + description: + type: string + required: false + image: + description: >- + If an image is specified here, it is assumed that the image + is already present in the RO or VIM and not in the package. + type: string + required: false + image_checksum: + type: string + description: >- + Image checksum for the image in RO or VIM. + required: false + cloud_init: + description: >- + Inline cloud-init specification + required: false + type: string + count: + default: 1 + type: integer + capabilities: + virtualLink: + type: tosca.capabilities.nfv.VirtualLinkable + monitoring_param_1: + type: tosca.capabilities.nfv.riftio.monitoring_param + mgmt_interface: + type: tosca.capabilities.nfv.riftio.mgmt_interface + monitoring_param: + type: tosca.capabilities.nfv.riftio.monitoring_param + numa_extension: + type: tosca.capabilities.nfv.riftio.numa_extension + vswitch_epa: + type: tosca.capabilities.nfv.riftio.vswitch_epa + hypervisor_epa: + type: tosca.capabilities.nfv.riftio.hypervisor_epa + host_epa: + type: tosca.capabilities.nfv.riftio.host_epa + tosca.nodes.nfv.riftio.CP1: + derived_from: tosca.nodes.nfv.CP + properties: + cp_type: + description: Type of the connection point + type: string + default: VPORT + constraints: + - valid_values: [VPORT] + name: + description: Name of the connection point + type: string + required: false + vdu_intf_name: + description: Name of the interface on VDU + type: string + vdu_intf_type: + description: >- + Specifies the type of virtual interface + between VM and host. + VIRTIO - Use the traditional VIRTIO interface. + PCI-PASSTHROUGH - Use PCI-PASSTHROUGH interface. + SR-IOV - Use SR-IOV interface. + E1000 - Emulate E1000 interface. + RTL8139 - Emulate RTL8139 interface. + PCNET - Emulate PCNET interface. + OM-MGMT - Used to specify openmano mgmt external-connection type + type: string + constraints: + - valid_values: [OM-MGMT, VIRTIO, E1000, SR-IOV] + bandwidth: + type: integer + description: Aggregate bandwidth of the NIC + constraints: + - greater_or_equal: 0 + required: false + vpci: + type: string + description: >- + Specifies the virtual PCI address. Expressed in + the following format dddd:dd:dd.d. For example + 0000:00:12.0. This information can be used to + pass as metadata during the VM creation. + required: false + capabilities: + sfc: + type: tosca.capabilities.nfv.riftio.sfc + tosca.nodes.nfv.riftio.VNF1: + derived_from: tosca.nodes.nfv.VNF + properties: + member_index: + type: integer + constraints: + - greater_or_equal: 1 + description: Index of the VNF in the NS + required: false + start_by_default: + type: boolean + default: true + description: Start this VNF on NS instantiate + logo: + type: string + description: >- + Logo to display with the VNF in the orchestrator + required: false + capabilities: + mgmt_interface: + type: tosca.capabilities.nfv.riftio.mgmt_interface + monitoring_param: + type: tosca.capabilities.nfv.riftio.monitoring_param + sfc: + type: tosca.capabilities.nfv.riftio.sfc + tosca.nodes.nfv.riftio.ELAN: + derived_from: tosca.nodes.nfv.VL.ELAN + properties: + description: + type: string + required: false + network_name: + type: string + description: >- + Name of network in VIM account. This is used to indicate + pre-provisioned network name in cloud account. + required: false + root_bandwidth: + type: integer + description: >- + This is the aggregate bandwidth + constraints: + - greater_or_equal: 0 + required: false + leaf_bandwidth: + type: integer + description: >- + This is the bandwidth of branches + constraints: + - greater_or_equal: 0 + required: false + tosca.nodes.nfv.riftio.FP1: + derived_from: tosca.nodes.nfv.FP + properties: + id: + type: integer + required: false + policy: + type: tosca.nfv.datatypes.policyType + required: true + description: policy to use to match traffic for this FP + path: + type: list + required: true + entry_schema: + type: tosca.nfv.datatypes.pathType + cp: + type: tosca.nfv.datatypes.pathType + required: true + + + +artifact_types: + tosca.artifacts.Deployment.riftio.cloud_init_file: + derived_from: tosca.artifacts.Deployment + file: + type: string + + tosca.artifacts.Deployment.Image.riftio.QCOW2: + derived_from: tosca.artifacts.Deployment.Image.VM.QCOW2 + image_checksum: + required: false + type: string + +group_types: + tosca.groups.nfv.VNFFG: + derived_from: tosca.groups.Root + properties: + vendor: + type: string + required: true + description: name of the vendor who generate this VNFFG + version: + type: string + required: true + description: version of this VNFFG + number_of_endpoints: + type: integer + required: true + description: count of the external endpoints included in this VNFFG + dependent_virtual_link: + type: list + entry_schema: + type: string + required: true + description: Reference to a VLD used in this Forwarding Graph + connection_point: + type: list + entry_schema: + type: string + required: true + description: Reference to Connection Points forming the VNFFG + constituent_vnfs: + type: list + entry_schema: + type: string + required: true + description: Reference to a list of VNFD used in this VNF Forwarding Graph + members: [ tosca.nodes.nfv.FP ] + + tosca.groups.nfv.riftio.scaling: + derived_from: tosca.groups.Root + properties: + name: + type: string + min_instances: + type: integer + description: >- + Minimum instances of the scaling group which are allowed. + These instances are created by default when the network service + is instantiated. + max_instances: + type: integer + description: >- + Maximum instances of this scaling group that are allowed + in a single network service. The network service scaling + will fail, when the number of service group instances + exceed the max-instance-count specified. + cooldown_time: + type: integer + description: >- + The duration after a scaling-in/scaling-out action has been + triggered, for which there will be no further optional + ratio: + type: map + entry_schema: + type: integer + description: >- + Specify the number of instances of each VNF to instantiate + for a scaling action + members: [tosca.nodes.nfv.VNF] + interfaces: + action: + type: tosca.interfaces.nfv.riftio.scaling.action + +interface_types: + tosca.interfaces.nfv.riftio.scaling.action: + pre_scale_in: + description: Operation to execute before a scale in + post_scale_in: + description: Operation to execute after a scale in + pre_scale_out: + description: Operation to execute before a scale out + post_scale_out: + description: Operation to execute after a scale out + +policy_types: + tosca.policies.nfv.riftio.placement: + derived_from: tosca.policies.Placement + properties: + name: + type: string + description: >- + Place group construct to define the compute resource placement strategy + in cloud environment + requirement: + type: string + description: >- + This is free text space used to describe the intent/rationale + behind this placement group. This is for human consumption only + strategy: + type: string + description: >- + Strategy associated with this placement group + Following values are possible + COLOCATION - Colocation strategy imply intent to share the physical + infrastructure (hypervisor/network) among all members + of this group. + ISOLATION - Isolation strategy imply intent to not share the physical + infrastructure (hypervisor/network) among the members + of this group. + constraints: + valid_values: + - COLOCATION + - ISOLATION + tosca.policies.nfv.riftio.vnf_configuration: + derived_from: tosca.policies.Root + properties: + config: + type: tosca.datatypes.nfv.riftio.vnf_configuration + initial_config: + type: list + entry_schema: + type: tosca.datatypes.nfv.riftio.config_primitive + tosca.policies.nfv.riftio.vnf_service_primitives: + derived_from: tosca.policies.Root + properties: + parameter: + type: map + entry_schema: + type: primitive_parameter + tosca.policies.nfv.riftio.ns_service_primitives: + derived_from: tosca.policies.Root + properties: + parameter: + type: map + entry_schema: + type: primitive_parameter + parameter_group: + type: tosca.datatypes.nfv.riftio.primitive_parameter_group + description: >- + Grouping of parameters which are logically grouped in UI + required: false + vnf_primitive_group: + type: tosca.datatypes.nfv.riftio.vnf_primitive_group + description: >- + List of service primitives grouped by VNF + required: false + user_defined_script: + type: string + description: >- + A user defined script + required: false + tosca.policies.nfv.riftio.initial_config_primitive: + derived_from: tosca.policies.Root + properties: + name: + type: string + seq: + type: integer + description: >- + Order in which to apply, when multiple ones are defined + default: 0 + constraints: + - greater_or_equal: 0 + parameter: + type: map + entry_schema: + type: string + user_defined_script: + type: string + tosca.policies.nfv.riftio.users: + derived_from: tosca.policies.Root + description: >- + Specify list of public keys to be injected as + part of NS instantitation. Use default as entry, + to specify the key pairs for default user. + properties: + user_info: + type: string + description: >- + The user\'s real name + required: false + key_pairs: + type: map + description: >- + List of public keys for the user + entry_schema: + type: string + required: true + tosca.policies.nfv.riftio.dependency: + derived_from: tosca.policies.Root + description: >- + Map dependency between VDUs or VNFs + properties: + parameter: + type: map + entry_schema: + type: string + description: >- + Parameter and value for the config + tosca.nfv.datatypes.policyType: + properties: + type: + type: string + required: false + constraints: + - valid_values: [ ACL ] + criteria: + type: list + required: true + entry_schema: + type: tosca.nfv.datatypes.aclType +topology_template: + node_templates: + new_vnfd: + type: tosca.nodes.nfv.riftio.VNF1 + properties: + id: 2 + vendor: RIFT.io + version: 1.0 diff --git a/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_compute.py b/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_compute.py index 7bc0bf3b..79384857 100755 --- a/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_compute.py +++ b/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_compute.py @@ -47,6 +47,16 @@ class ToscaCompute(ManoResource): 'float':'DECIMAL' } + + TOSCA_MEM_SIZE = { + 'huge': 'LARGE', + 'normal': 'SMALL', + 'size_2MB': 'SIZE_2MB', + 'size_1GB': 'SIZE_1GB', + 'prefer_huge': 'PREFER_LARGE' + + } + def __init__(self, log, nodetemplate, metadata=None): super(ToscaCompute, self).__init__(log, nodetemplate, @@ -62,6 +72,7 @@ class ToscaCompute(ManoResource): self._id = self.name self._monitor_param = [] self._mgmt_interface = {} + self._http_endpoint = None @property def image(self): @@ -207,11 +218,11 @@ class ToscaCompute(ManoResource): if 'type' in specs: hypervisor_epa['type'] = specs['type'].upper() if 'version' in specs: - hypervisor_epa['version'] = specs['version'] + hypervisor_epa['version'] = str(specs['version']) return hypervisor_epa - def get_guest_epa(specs): + def get_guest_epa(specs, nfv_comput_specs): guest_epa = {} guest_epa['numa-node-policy'] = {} guest_epa['numa-node-policy']['node'] = [] @@ -230,6 +241,11 @@ class ToscaCompute(ManoResource): else: err_msg = "Specify mem_size of NUMA extension should be in MB" raise ValidationError(message=err_msg) + if 'vcpus' in node: + vcpu_lis =[] + for vcpu in node['vcpus']: + vcpu_lis.append({'id': vcpu}) + node_prop['vcpu'] = vcpu_lis if 'om_numa_type' in node: numa_type = node['om_numa_type'] if 'paired-threads' == numa_type: @@ -245,6 +261,16 @@ class ToscaCompute(ManoResource): err_msg = "om_numa_type should be among cores, paired-threads or threads" raise ValidationError(message=err_msg) guest_epa['numa-node-policy']['node'].append(node_prop) + + if 'mem_page_size' in nfv_comput_specs: + guest_epa['mempage-size'] = self.TOSCA_MEM_SIZE[nfv_comput_specs['mem_page_size']] + if 'cpu_allocation' in nfv_comput_specs: + if 'cpu_affinity' in nfv_comput_specs['cpu_allocation']: + guest_epa['cpu-pinning-policy'] = nfv_comput_specs['cpu_allocation']['cpu_affinity'].upper() + guest_epa['trusted-execution'] = False + if 'thread_allocation' in nfv_comput_specs['cpu_allocation']: + guest_epa['cpu-thread-pinning-policy'] = nfv_comput_specs['cpu_allocation']['thread_allocation'].upper() + return guest_epa tosca_caps = self.get_tosca_caps() @@ -261,13 +287,23 @@ class ToscaCompute(ManoResource): if 'vswitch_epa' in tosca_caps: self.properties['vswitch-epa'] = get_vswitch_epa(tosca_caps['vswitch_epa']) if 'numa_extension' in tosca_caps: - self.properties['guest-epa'] = get_guest_epa(tosca_caps['numa_extension']) + self.properties['guest-epa'] = get_guest_epa(tosca_caps['numa_extension'], tosca_caps['nfv_compute']) if 'monitoring_param' in tosca_caps: self._monitor_param.append(get_monitor_param(tosca_caps['monitoring_param'], '1')) if 'monitoring_param_1' in tosca_caps: self._monitor_param.append(get_monitor_param(tosca_caps['monitoring_param_1'], '2')) if 'mgmt_interface' in tosca_caps: self._mgmt_interface = get_mgmt_interface(tosca_caps['mgmt_interface']) + if len(self._mgmt_interface) > 0: + prop = {} + if 'dashboard-params' in self._mgmt_interface: + if 'path' in self._mgmt_interface['dashboard-params']: + prop['path'] = self._mgmt_interface['dashboard-params']['path'] + if 'port' in self._mgmt_interface['dashboard-params']: + prop['port'] = self._mgmt_interface['dashboard-params']['port'] + self._http_endpoint = prop + + def handle_artifacts(self): if self.artifacts is None: @@ -284,9 +320,11 @@ class ToscaCompute(ManoResource): prefix, type_ = value.rsplit('.', 1) if type_ == 'QCOW2': details['type'] = 'qcow2' + self._image = props['file'] self.properties['image'] = os.path.basename(props['file']) elif name == 'type' and value == 'tosca.artifacts.Deployment.riftio.cloud_init_file': details['cloud_init_file'] = os.path.basename(props['file']) + self._cloud_init = props['file'] self.properties['cloud_init_file'] = os.path.basename(props['file']) elif name == 'file': details['file'] = value @@ -325,6 +363,7 @@ class ToscaCompute(ManoResource): return None def update_image_checksum(self, in_file): + # Create image checksum # in_file is the TOSCA yaml file location if self._image is None: @@ -389,6 +428,8 @@ class ToscaCompute(ManoResource): try: if len(self._mgmt_interface) > 0: vnfd.mgmt_interface.from_dict(convert_keys_to_python(self._mgmt_interface)) + if self._http_endpoint: + vnfd.http_endpoint.add().from_dict(convert_keys_to_python(self._http_endpoint)) vnfd.vdu.add().from_dict(props) except Exception as e: err_msg = _("{0} Exception vdu from dict {1}: {2}"). \ diff --git a/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_network_network.py b/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_network_network.py index a2dd92ba..a94624de 100644 --- a/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_network_network.py +++ b/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_network_network.py @@ -49,6 +49,7 @@ class ToscaNetwork(ManoResource): vld_prop['name'] = self.name vld_prop['short-name'] = self.name vld_prop['type'] = self.get_type() + vld_prop['ip_profile_ref'] = "{0}_{1}".format(self.nodetemplate.name, "ip") if 'description' in specs: vld_prop['description'] = specs['description'] if 'vendor' in specs: @@ -63,13 +64,13 @@ class ToscaNetwork(ManoResource): for mapping in substitution_mapping_list: if req_key in mapping: # link the VLD to the connection point - node = self.get_node_with_name(mapping[req_key][0], nodes) + node_vld = self.get_node_with_name(mapping[req_key][0], nodes) if node: #print() prop = {} - prop['member-vnf-index-ref'] = index_count - prop['vnfd-connection-point-ref'] = node.cp_name - prop['vnfd-id-ref'] = node.vnf._id + prop['member-vnf-index-ref'] = node.get_member_vnf_index() + prop['vnfd-connection-point-ref'] = node_vld.cp_name + prop['vnfd-id-ref'] = node_vld.vnf._id vld_connection_point_list.append(prop) index_count += 1 if len(vld_connection_point_list) > 1: @@ -79,10 +80,8 @@ class ToscaNetwork(ManoResource): def get_ip_profile_props(specs): ip_profile_prop = {} ip_profile_param = {} - if 'name' in specs: - ip_profile_prop['name'] = specs['name'] - elif 'description' in specs: - ip_profile_prop['name'] = specs['description'] + if 'ip_profile_ref' in self._vld: + ip_profile_prop['name'] = self._vld['ip_profile_ref'] if 'description' in specs: ip_profile_prop['description'] = specs['description'] diff --git a/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_vnf_configuration.py b/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_vnf_configuration.py index 0a54bcc0..f90c1874 100644 --- a/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_vnf_configuration.py +++ b/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_vnf_configuration.py @@ -44,12 +44,17 @@ class ToscaVnfConfiguration(ManoResource): self.properties = {} self.linked_to_vnf = True self._vnf_name = vnf_name + self._vnf_id = None + self.scripts = [] def __str__(self): return "%s(%s)" % (self.name, self.type) def handle_properties(self, nodes, groups): tosca_props = self.get_policy_props() + if self._vnf_name: + vnf_node = self.get_node_with_name(self._vnf_name, nodes) + self._vnf_id = vnf_node.id self.properties["vnf-configuration"] = {} prop = {} prop["config-attributes"] = {} @@ -64,6 +69,20 @@ class ToscaVnfConfiguration(ManoResource): if 'config_details' in tosca_props['config']: if 'script_type' in tosca_props['config']['config_details']: prop["script"]["script-type"] = tosca_props['config']['config_details']['script_type'] + if 'initial_config' in tosca_props: + prop['initial-config-primitive'] = [] + #print("Weleek " + str(tosca_props['initial_config'])) + for init_config in tosca_props['initial_config']: + if 'parameter' in init_config: + parameters = init_config.pop('parameter') + init_config['parameter'] = [] + for key, value in parameters.items(): + init_config['parameter'].append({'name': key, 'value': str(value)}) + if 'user_defined_script' in init_config: + self.scripts.append('../scripts/{}'. \ + format(init_config['user_defined_script'])) + prop['initial-config-primitive'].append(init_config) + self.properties = prop def generate_yang_submodel_gi(self, vnfd): @@ -86,4 +105,16 @@ class ToscaVnfConfiguration(ManoResource): tosca_props[prop.name] = {'get_param': prop.value.input_name} else: tosca_props[prop.name] = prop.value - return tosca_props \ No newline at end of file + return tosca_props + def get_supporting_files(self, files, desc_id=None): + if not len(self.scripts): + return + + if self._vnf_id not in files: + files[desc_id] = [] + + for script in self.scripts: + files[self._vnf_id].append({ + 'type': 'script', + 'name': script, + },) \ No newline at end of file diff --git a/common/python/rift/mano/tosca_translator/rwmano/translate_node_templates.py b/common/python/rift/mano/tosca_translator/rwmano/translate_node_templates.py index 3f74336c..2d6c3e1a 100644 --- a/common/python/rift/mano/tosca_translator/rwmano/translate_node_templates.py +++ b/common/python/rift/mano/tosca_translator/rwmano/translate_node_templates.py @@ -23,6 +23,7 @@ from rift.mano.tosca_translator.common.exception import ToscaClassImportError from rift.mano.tosca_translator.common.exception import ToscaModImportError from rift.mano.tosca_translator.conf.config import ConfigProvider as translatorConfig from rift.mano.tosca_translator.rwmano.syntax.mano_resource import ManoResource +from toscaparser.tosca_template import ToscaTemplate class TranslateNodeTemplates(object): @@ -197,6 +198,7 @@ class TranslateNodeTemplates(object): vnf_type_substitution_mapping = {} vnf_type_to_capability_substitution_mapping = {} tpl = self.tosca.tpl['topology_template']['node_templates'] + associated_vnfd_flag = False for node in self.nodetemplates: all_node_templates.append(node) @@ -214,6 +216,7 @@ class TranslateNodeTemplates(object): vnf_type_to_capability_substitution_mapping[vnf_type] = [] vnf_type_to_capability_substitution_mapping[vnf_type] = [] policies = [] + for node in template.nodetemplates: all_node_templates.append(node) for node_key in tpl_node: @@ -226,10 +229,11 @@ class TranslateNodeTemplates(object): policies.append(policy.name) for req in template.substitution_mappings.requirements: vnf_type_substitution_mapping[template.substitution_mappings.node_type].append(req) - for capability in template.substitution_mappings.capabilities: - sub_list = template.substitution_mappings.capabilities[capability] - if len(sub_list) > 0: - vnf_type_to_capability_substitution_mapping[vnf_type].append({capability: sub_list[0]}) + if template.substitution_mappings.capabilities: + for capability in template.substitution_mappings.capabilities: + sub_list = template.substitution_mappings.capabilities[capability] + if len(sub_list) > 0: + vnf_type_to_capability_substitution_mapping[vnf_type].append({capability: sub_list[0]}) for node in all_node_templates: base_type = ManoResource.get_base_type(node.type_definition) @@ -245,11 +249,36 @@ class TranslateNodeTemplates(object): metadata=self.metadata) # Currently tosca-parser does not add the artifacts # to the node + if mano_node.type == 'vnfd': + associated_vnfd_flag = True if mano_node.name in node_to_artifact_map: mano_node.artifacts = node_to_artifact_map[mano_node.name] self.mano_resources.append(mano_node) self.mano_lookup[node] = mano_node + if not associated_vnfd_flag: + dummy_file = "{0}{1}".format(os.getenv('RIFT_INSTALL'), "/usr/rift/mano/common/dummy_vnf_node.yaml") + tosca_vnf = ToscaTemplate(dummy_file, {}, True) + vnf_type = self.tosca.topology_template.substitution_mappings.node_type + vnf_type_to_vdus_map[vnf_type] = [] + + for node in tosca_vnf.nodetemplates: + all_node_templates.append(node) + base_type = ManoResource.get_base_type(node.type_definition) + vnf_type_to_vnf_node[vnf_type] = node.name + mano_node = TranslateNodeTemplates. \ + TOSCA_TO_MANO_TYPE[base_type.type]( + self.log, + node, + metadata=self.metadata) + mano_node.vnf_type = vnf_type + self.mano_resources.append(mano_node) + print("Adding a new node") + + for node in self.tosca.nodetemplates: + if 'VDU' in node.type: + vnf_type_to_vdus_map[vnf_type].append(node.name) + # The parser currently do not generate the objects for groups for group in self.tosca.topology_template.groups: group_type = group.type diff --git a/common/python/rift/mano/tosca_translator/test/data/ping_pong_csar_tosca_new_spec.zip b/common/python/rift/mano/tosca_translator/test/data/ping_pong_csar_tosca_new_spec.zip new file mode 100644 index 0000000000000000000000000000000000000000..a7550403c9d0e3a8a2265a188af4f7884ab4af15 GIT binary patch literal 113058 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

lf^X*cG}bPzGJFjNp8Plz0@P5 z-kp_&FxF9;a@`x%9@So#vxCwdE(eoiIqPR5MX{$7A}(j_q88pohx@aK>BUvc60lZV zOw-QPOYaUbPItaj8Ka!sWWhb9C-vnUAJ{|9ag&>N!fb`6rfsuTWN)XLDUhWj`gJ|e zQn}-*FleuoY^g8}nE|ThmXmnK{j;r}49^=jkQ7RWCd5xZi^vxl7H`atXT}RN-h(KP zAlGxevs>Dw?pZQoaJ(9nR~J&MuFMgO<>9fLnBiT4p zna-rO?G!V`LfdY4ZaZXA6ISPPa&L8lA>&*+^C*N4HS?Vo1OfT@KlC9nU5 zTYJT&A&}nbRy8*bH3y5BBGH*7bV1J2YM}xr4ytC!oVB>fZVr*P_w=3a=j0>rGs&XV z<<_<7$(94w?z)&56Wqt@(Wt1n7hA;K9Yn`)HtxYu6BVf^r!GNv!QsZyAaMs6g$ZugR4j)*J=3;P?B>^|i695q zS)QT_uD9B0I=vH~>s(=)eGKz~jW?qE%fK*`ml^xAaEcAej-LlnBzop|8yz3F$KTxT zHja1qJKVHdEflzLS&Q@rBExXO9N%VV%0QcPGk~}YSFXt?ddqIcG8NblLh0URPYOd% z%fowzmxM-SUw_QB8dSOZ=%>kPG!+|ZY z60Bd&(zKBD>>7F&*V0yn%1{){-+02X8GBOhu0?~K?0Eu;9c}opHA#{^(rZyNkE{(sleIyLb9r`usfl!aI(g zkBu2~yem|VetLK$>j|8^zDB+_24c&zJd)yv-%ttJTWyD6SqJwPoUFtn2nquR9M@2K zXK_Lj=u%h-x3DJuEIgVdMgGH<2{)dThk39&xXJZgIzZd3~d(jOFWoiyL7Oic>}kqj_$ z0O8#i7_)X4plZ@SA1H*gBE))yv^FGYqxdslYt%WFMXsp?@Yv}RfJbj4{?n3Yh(WyY zy88Rd$Kq;|#388DOIR2_(#HF{`g@SFr6)v9Ufqz=QDro#V8E5P2p>`rbb$s9idejW zMggAE1IroNaq+7VoL~oXwT2$$6hzS=q%c=s10)sY%(#5_o1o~R< z_R*&!wJI_qc4g?WYdbz@zvs!_qr+Nr7J_LScjzi0o_`4l^&{&d)& zheCXFm^hdoDL@|TZE`Wre&8vINk52MadeOTN_7sfZ@so5lV7SazI2GzTgeeOITH>9 zdo$sA#6Qw1$V56H@0bA!dRT0|0+e0ICJ&l4S<)CE z#C5RH;qx6ULOB8jm$~m)lN(<(>N-C=oem^?iysQKnB+0;Ddu0C)HbiJ z0GLRJL7b_oYc`BCqf&3DjJv-oCIl2nw8L)_{Vf1i0G}VZ0GHvys5zKF9)JtKB1x=g zFKBr{W>QsEu#+QyhKTO|=;q*E(j8oR7SR5 zU%HtWrz8?|4H!YmipJcBo<7UKSgw2yle?&ddSj9dzPI)!&2&ZB#mT#$3lrk52hKv9 zr*7nlP7PUOm;HxD;_-FM=IN&@=Wo$?l$Xd3WS%dx$hh~!Wt=9WQ~vbd7phCVE2e%P zfX_dQf4RUbO0cm!w?(~@hc;gy+&|EMUTMIZzjKLbKj|C%jre2wkLg5qL;O%c3^2ay zt}=+0T&PRe04Z(B0k8cWY3viCs=^3YmfMRPo^DqcZ-=;*NA;wl^Ya?YK){JW(U^JM z41LMQnn?-3orfe&84J$T&<|-i*~Q3+nr}JSfwk5l?#DzbEgU^Om~F}hzB07!aiw|1 zNdYU+PAZ9;_fM9-!;-*iTj;osn%8`hnwUGdU)V6uX8T>{>^s5fIXd6!pUw0;FZUnJ z^q)|i{y)rgN+G8>YAGsLGnw@Zt4Fo(5lLn1^Vh8M*{kaAWeJPlsa$IENr=VYZUj66-NF+VjvpU4u8*!2DDq-}SB$9}+|*L$Ah( zN}PiM=;da4Jc>H|XaTUD6ra9-le;NyCv0e3Mk76|7A3!T5_U_qd1OM9D>)PWkyF$% z>YFM>Mz!CER_qtB578ICT=hQUm~E{O5Qp77K#4&fxk3K2Sp;!uv)DZx(5JayF4wu^ zBy=g7tla_qS(UT<_MgVV7WF}iO`(l)(q*y7eY|11 z%DZe-wb)3MYo=hXFpRg^Rasa(_Ff3|FN>>3{8MgDPuCcc>DZzp(NBiouh?2!vnHm( zlIy>u>&+Ss#Q^p5q6_G}k5kp+@Z-{wloA-i#gA?y?3c@7lIZnDQOT0SQ>dAMmpTVl zCjSzv^KloAtDx_bej-)`9yO@RTmSwgui4Ez-4zR!Ywe1IK0GjOY=fAbw2GUT?2ZCKg~a1JSJ8bEBD9eb**wANmg5btDUnG&4!O zyU;O77|}^Vnn^KKp?pXWO{;4^#*BN4aGhl4w$U4l3`pv{N!c9J2pCLC$i7FG@9l%dDsp7@1Q zaI{`1(>4v4iw@%Q+m0Eab0pCpF)NBHj&Z`qR=10gCTaFT+Ue>nJUDG)2Oo`iJmaWC#?knm95uq(yDrJ6fuAvwM+C?Bzk zEEp}%UVea^SYs+s^#)wgQro28k_e|h+!&NXWQ5MM6^)(vy7e5?eF>LOfGC=dPEv74 zU`pIW&{`q(BpXrrwS&g#$Zd%J9eX=jTEs^ulS$y=)S>O*Gh2!B8dfQ%ydClKiO|gm zh|8IL%UhzAfB_2l-ReM(M5OD$%(b#iaV2C<<^iV#$Ksr~pBL)MON~@1Z=@w1&Y(2L z+sYf*mb0+z=^88=Z3<^QavnO=8A*Ul{iqN*Y|+Z?6mUXdQ!PHfEnrPeX>M_wbXJlw zR3{0qOG&7vtoR0fGW0lR6G^cK>m|-V&mB;1m=cp|0dwZs^o%uwY{O8pz(EUde8#HG zW}0A<-USqBnfV%&CWjN`3%nOi3kQNW8fn&P` zeZ15C$rQ2#aS2UGT%j$*U)y$oUnGE}H~HXBRKu-RL%X^m{6d9rxlQ(? znR|2Fhi{Lj9^)9AB(ZD@BCym7+6EIL3Zd2yTvzKj;QXuRRJ2(v!cLm9HE2Wn3|Dhs z1-(Pc^lo{oomOkF*>g^AINgDN&jE-0N)n_%eu0p$MKQat+PgpI0Gn41uPYNQ~uv`0Oz9Gl~RF4XM05>S z1v;Q7#o6uBS&Dz};0U}%Neo*|pnpG7Ma;*n{_x_N5on%b9CoqFn5YY1PqxwU~nktpIGmQ=$Tr^*}kuw{ct)XA9q2Oz~m zyJkw7fE|zE9GeE(QZpA#i;JosQ0}bdS#C1yloz;gaIr&sMAb*;TQTN^kwFbFH)*-)XFw+rNlMolg;Ps!<}#1~^l z@CX6TXu)*8(#63y*FV?d&iAfwZ5`?W;eqe$c#rFTW`5oH+1ash;K({F5u0&3wQM0< zVMYBLJ+ffV{T;q;JjsqlDSD@?dW0fkl@CqWnR0gWjp*`mNpIK36Aew06!@@0yeUvN zMytpX>on7i49P~!iNDoJJeL3Jmf{gtgjtMs#Gwf0lVBiVRJ4+5%aet!P$VXaq)ucvdLr&JbcJM%;9C8~YtU~TeToXi zGDN9pH?US3^)n*R*6j8OFOaA0E6td4mZGH|xu zUSQnoT7TicVxw#VJu>J`#<)jw)AT^ed+7_QytBHASGAmLwfniNgIz}YOx|z2G~l)X z2Yiw)-f5%=s;pJJqMAWHcmUQaVHD?J7tTAW5FVkP+t9?sq0`QwQoqnRZou@kA6^KB zBL)Yx1XM)MU7(%@=csL5CCC>}^6NNDR+UlAUfKZqhB^T&t{}++RbuNLTM09UNp4QN z{uZ7{?~dimSSIfv&7`XL;Z%)O9Iq&>HQ0pZG<61HVJPVE+~as|c>3@T9*)lB^B4h=%Qtw42tADif?dz2$uT@ zL~Qvd=gGWc8L63CXxS)MFCiS2iq`fw_aPW;G#Z^X5lBpgNIu{2Zi_#|rfhxqyzr9y zc|!tAesgQ=s{C+Lcz7CjW^b@83N${K8i@vFBLl?OP zb|vAE(Pdnm%?tq3vamg(4~o4?_B&W&%cE6G`}X)Ci8!;5bA zuU&q*Fr)E3H%!jm8k$;)B(9q+wmc2vBs}^8$0;oyAxF(aAZ<#sUqSO;l>g%myaqvK z1}SsN&heACWGog%9DU=rGe+Yc;9g_RnoavT9-~&1@Zxu!&<=3N3WjgS?AW4h{F^Jh z9uy&LuQrh~-Sk{OUtVv_j${IhSSelDFz0sHQugYVx+^@M^1=xK&t&BgUo<7>cwg=Y z+zaEk$b@RqIL*-O&9N&!8IrRrPw8M{ae|ydUg66LHg=)E_VM~QMNF<-H#M7dzMX8q z8a{qQjTsg;iWx(aW(X5|QcWg}FvBO&M;^)TGcsnqaYEYYB^N2eqgTL8QbTL_p>5yinXYo_Uovo514BBe1Wj#`%QLa+@mxw8`4P+2B z5+CL!T7d<#FDP-07&8G&jtaZ};v%7I-`19m&R+CSIP+imt0hMHKx77Qv*d+Ou^J7` zql=^cRH%yX&CWwh?!Qfl1zo$6F5aZeba&p=?>@9t_fVJvSl^KSD5k%_`STS8=D)Sg z|EZXMqSr5}VRQM1Wtl%F9B2sd@b|M*diF4*pY&m}$J~ z7XQ65)2p%nk(hqT@5cUbYPYR-VSlK{{`ZFcu^Jopr*FsS_hJ8DL;bH! z{<)a`-mA&~buoQLtzV4(Z;I)!yqf=KYv51B^ndRNA}Flz)9)~I5ae~MfctaE-@`aC_0fjv^~A&o{0i_VFOz%!XCVJ{ z68jh7{Ku2n&*6OM^PfTdV>tgmo$%~_a`gL#{|xN^%ah}8E3u4KK*HFFZvgG}Xx{RA zVAuWudX}f^^j`=4`^%BvL%9*>pERWUZAa#RJwq>U!f5lo=C|*U;eWyWZJ&c#`s-lY z8tR!F7;68Pq@9d@i--GvBvJJH_V#O=px-(ZeZOF3c^-=c{$;0~KQs+`-6Os?4f^Hf z)$dS$?QilU)HUh94(dM+*YDj<{vq7o;^2=L9nJragYTDN6vMv`?*HHd75LvEzjq@m z{_8>h%Nx@7rT1SgZU6Ok=8r!Ac{-Lxf4R^9<#hf>7e2iocDyQ2CgZ<+I{(f^^0&?T zSFx=h7w73;2=$NBTdz^_4`jD~yqEl{tnwSy?>!%n_)Dz-f7V%fHTaz_@Fz?CSCy5& z8~nbBUjA(GA84-p*$BV6_g6y4kM2E)`3dshZfbudJ-m+bo!;>eXYtz@zfviF9Ai56 z=P~{fvEt9>@!O{OD~aLnp8D?k);Pc)C=IXHzEc|h1o5wgh940F#{X{ZS9-&%_3!kC tKe7JntFj-hgQom${nr;~uhzf6IQzx=Z$10S0muQUQC}}_S>My_{{cCHKyCm4 literal 0 HcmV?d00001 diff --git a/common/python/rift/mano/yang_translator/riftiotypes.yaml b/common/python/rift/mano/yang_translator/riftiotypes.yaml new file mode 100644 index 00000000..2fea0e33 --- /dev/null +++ b/common/python/rift/mano/yang_translator/riftiotypes.yaml @@ -0,0 +1,1493 @@ +tosca_definitions_version: tosca_simple_profile_for_nfv_1_0 +description: Extended types + + +data_types: + tosca.datatypes.nfv.riftio.dashboard_params: + properties: + path: + type: string + description: >- + The HTTP path for the dashboard + port: + type: tosca.datatypes.network.PortDef + description: >- + The HTTP port for the dashboard + default: 80 + https: + type: boolean + description: >- + Pick HTTPS instead of HTTP , Default is false + default: false + required: false + tosca.datatypes.nfv.riftio.monitoring_param_ui: + properties: + description: + type: string + required: false + group_tag: + type: string + description: >- + A simple tag to group monitoring parameters + required: false + widget_type: + type: string + description: >- + Type of the widget + default: counter + constraints: + - valid_values: + - histogram + - bar + - gauge + - slider + - counter + - textbox + units: + type: string + required: false + tosca.datatypes.nfv.riftio.monitoring_param_value: + properties: + value_type: + type: string + default: integer + constraints: + - valid_values: + - integer + - float + - string + numeric_min: + type: integer + description: >- + Minimum value for the parameter + required: false + numeric_max: + type: integer + description: >- + Maxium value for the parameter + required: false + string_min: + type: integer + description: >- + Minimum string length for the parameter + required: false + constraints: + - greater_or_equal: 0 + string_max: + type: integer + description: >- + Maximum string length for the parameter + required: false + constraints: + - greater_or_equal: 0 + tosca.datatypes.compute.Container.Architecture.CPUAllocation: + derived_from: tosca.datatypes.Root + properties: + cpu_affinity: + type: string + required: false + constraints: + - valid_values: [shared, dedicated, any] + thread_allocation: + type: string + required: false + constraints: + - valid_values: [avoid, separate, isolate, prefer] + socket_count: + type: integer + required: false + core_count: + type: integer + required: false + thread_count: + type: integer + required: false + + tosca.datatypes.compute.Container.Architecture.NUMA: + derived_from: tosca.datatypes.Root + properties: + id: + type: integer + constraints: + - greater_or_equal: 0 + vcpus: + type: list + entry_schema: + type: integer + constraints: + - greater_or_equal: 0 + mem_size: + type: scalar-unit.size + constraints: + - greater_or_equal: 0 MB + tosca.datatypes.nfv.riftio.paired_thread_map: + properties: + thread_a: + type: integer + required: true + constraints: + - greater_or_equal: 0 + thread_b: + type: integer + required: true + constraints: + - greater_or_equal: 0 + + tosca.datatypes.nfv.riftio.paired_threads: + properties: + num_paired_threads: + type: integer + constraints: + - greater_or_equal: 1 + paired_thread_ids: + type: list + entry_schema: + type: tosca.datatypes.nfv.riftio.paired_thread_map + constraints: + - max_length: 16 + required: false + + tosca.datatypes.compute.riftio.numa: + properties: + id: + type: integer + constraints: + - greater_or_equal: 0 + vcpus: + type: list + entry_schema: + type: integer + constraints: + - greater_or_equal: 0 + required: false + mem_size: + type: scalar-unit.size + constraints: + - greater_or_equal: 0 MB + required: false + om_numa_type: + type: string + description: Openmano Numa type selection + constraints: + - valid_values: [cores, paired-threads, threads] + required: false + num_cores: + type: integer + description: Use when om_numa_type is cores + constraints: + - greater_or_equal: 1 + required: false + paired_threads: + type: tosca.datatypes.nfv.riftio.paired_threads + description: Use when om_numa_type is paired-threads + required: false + num_threads: + type: integer + description: Use when om_numa_type is threads + constraints: + - greater_or_equal: 1 + required: false + + tosca.nfv.datatypes.pathType: + properties: + forwarder: + type: string + required: true + capability: + type: string + required: true + + tosca.nfv.datatypes.aclType: + properties: + eth_type: + type: string + required: false + eth_src: + type: string + required: false + eth_dst: + type: string + required: false + vlan_id: + type: integer + constraints: + - in_range: [ 1, 4094 ] + required: false + vlan_pcp: + type: integer + constraints: + - in_range: [ 0, 7 ] + required: false + mpls_label: + type: integer + constraints: + - in_range: [ 16, 1048575] + required: false + mpls_tc: + type: integer + constraints: + - in_range: [ 0, 7 ] + required: false + ip_dscp: + type: integer + constraints: + - in_range: [ 0, 63 ] + required: false + ip_ecn: + type: integer + constraints: + - in_range: [ 0, 3 ] + required: false + ip_src_prefix: + type: string + required: false + ip_dst_prefix: + type: string + required: false + ip_proto: + type: integer + constraints: + - in_range: [ 1, 254 ] + required: false + destination_port_range: + type: string + required: false + source_port_range: + type: string + required: false + network_src_port_id: + type: string + required: false + network_dst_port_id: + type: string + required: false + network_id: + type: string + required: false + network_name: + type: string + required: false + tenant_id: + type: string + required: false + icmpv4_type: + type: integer + constraints: + - in_range: [ 0, 254 ] + required: false + icmpv4_code: + type: integer + constraints: + - in_range: [ 0, 15 ] + required: false + arp_op: + type: integer + constraints: + - in_range: [ 1, 25 ] + required: false + arp_spa: + type: string + required: false + arp_tpa: + type: string + required: false + arp_sha: + type: string + required: false + arp_tha: + type: string + required: false + ipv6_src: + type: string + required: false + ipv6_dst: + type: string + required: false + ipv6_flabel: + type: integer + constraints: + - in_range: [ 0, 1048575] + required: false + icmpv6_type: + type: integer + constraints: + - in_range: [ 0, 255] + required: false + icmpv6_code: + type: integer + constraints: + - in_range: [ 0, 7] + required: false + ipv6_nd_target: + type: string + required: false + ipv6_nd_sll: + type: string + required: false + ipv6_nd_tll: + type: string + required: false + + + tosca.datatypes.nfv.riftio.vnf_configuration: + properties: + config_type: + type: string + description: >- + Type of the configuration agent to use + constraints: + - valid_values: [script, netconf, rest, juju] + config_details: + type: map + description: >- + Specify the details for the config agent, like + script type, juju charm to use, etc. + config_template: + required: false + type: string + config_delay: + type: integer + constraints: + - greater_or_equal: 0 + default: 0 + required: false + config_priority: + type: integer + constraints: + - greater_than: 0 + + tosca.datatypes.nfv.riftio.parameter_value: + properties: + name: + type: string + description: Name of the parameter + value: + type: string + description: Value of the parameter + + tosca.datatypes.nfv.riftio.config_primitive: + properties: + name: + type: string + seq: + type: integer + description: >- + Order in which to apply, when multiple ones are defined + default: 0 + constraints: + - greater_or_equal: 0 + parameter: + type: list + entry_schema: + type: tosca.datatypes.nfv.riftio.parameter_value + user_defined_script: + type: string + tosca.datatypes.nfv.riftio.primitive_parameter: + properties: + data_type: + type: string + description: >- + Data type associated with the name + constraints: + - valid_values: [string, integer, boolean] + mandatory: + type: boolean + description: >- + If this field is mandatory + default: false + required: false + default_value: + type: string + description: >- + The default value for this field + required: false + parameter_pool: + type: string + description: >- + Parameter pool name to use for this parameter + required: false + read_only: + type: boolean + description: >- + The value should be greyed out by the UI. + Only applies to parameters with default values. + required: false + default: false + hidden: + type: boolean + description: >- + The field should be hidden by the UI. + Only applies to parameters with default values. + required: false + default: false + tosca.datatypes.nfv.riftio.primitive_parameter_group: + properties: + name: + type: string + description: >- + Name of the parameter group + mandatory: + type: boolean + description: >- + If this group is mandatory + default: false + required: false + parameter: + type: map + description: >- + List of parameters for the service primitive + entry_schema: osca.datatypes.riftio.primitive_parameter + + tosca.datatypes.nfv.riftio.vnf_primitive_group: + properties: + vnf_name: + type: string + description: >- + Name of the VNF in the NS + primitive: + type: map + entry_schema: + type: string + description: >- + Index and name of the primitive + + +capability_types: + tosca.capabilities.nfv.riftio.mgmt_interface: + derived_from: tosca.capabilities.Endpoint + properties: + static_ip: + type: string + required: false + description: >- + Specifies the static IP address for managing the VNF + connection_point: + type: string + required: false + description: >- + Use the ip address associated with this connection point + dashboard_params: + type: tosca.datatypes.nfv.riftio.dashboard_params + required: false + description: >- + Parameters for the VNF dashboard + tosca.capabilities.nfv.riftio.monitoring_param: + derived_from: tosca.capabilities.nfv.Metric + properties: + name: + type: string + required: false + description: + type: string + required: false + protocol: + type: string + default: http + constraints: + - equal: http + polling_interval: + type: scalar-unit.time + description: >- + The HTTP polling interval in seconds + default: 2 s + username: + type: string + description: >- + The HTTP basic auth username + required: false + password: + type: string + description: >- + The HTTP basic auth password + required: false + method: + type: string + description: >- + This is the method to be performed at the uri. + GET by default for action + default: get + constraints: + - valid_values: [post, put, get, delete, options, patch] + headers: + type: map + entry_schema: + type: string + description: >- + Custom HTTP headers to put on HTTP request + required: false + json_query_method: + type: string + description: >- + The method to extract a value from a JSON response + namekey - Use the name as the key for a non-nested value. + jsonpath - Use jsonpath-rw implemenation to extract a value. + objectpath - Use objectpath implemenation to extract a value. + constraints: + - valid_values: [namekey, jsonpath, objectpath] + default: namekey + json_query_path: + type: string + description: >- + The json path to use to extract value from JSON structure + required: false + json_object_path: + type: string + description: >- + The object path to use to extract value from JSON structure + required: false + ui_data: + type: tosca.datatypes.nfv.riftio.monitoring_param_ui + required: false + constraints: + type: tosca.datatypes.nfv.riftio.monitoring_param_value + required: false + tosca.capabilities.nfv.riftio.numa_extension: + derived_from: tosca.capabilities.Root + properties: + node_cnt: + type: integer + description: >- + The number of numa nodes to expose to the VM + constraints: + - greater_or_equal: 0 + mem_policy: + type: string + description: >- + This policy specifies how the memory should + be allocated in a multi-node scenario. + STRICT - The memory must be allocated + strictly from the memory attached + to the NUMA node. + PREFERRED - The memory should be allocated + preferentially from the memory + attached to the NUMA node + constraints: + - valid_values: [strict, preferred, STRICT, PREFERRED] + node: + type: list + entry_schema: + type: tosca.datatypes.compute.riftio.numa + tosca.capabilities.nfv.riftio.vswitch_epa: + derived_from: tosca.capabilities.Root + properties: + ovs_acceleration: + type: string + description: |- + Specifies Open vSwitch acceleration mode. + MANDATORY - OVS acceleration is required + PREFERRED - OVS acceleration is preferred + constraints: + - valid_values: [mandatory, preferred, disabled, MANDATORY, PREFERRED, DISABLED] + ovs_offload: + type: string + description: |- + Specifies Open vSwitch hardware offload mode. + MANDATORY - OVS offload is required + PREFERRED - OVS offload is preferred + constraints: + - valid_values: [mandatory, preferred, disabled, MANDATORY, PREFERRED, DISABLED] + + tosca.capabilities.nfv.riftio.hypervisor_epa: + derived_from: tosca.capabilities.Root + properties: + type: + type: string + description: |- + Specifies the type of hypervisor. + constraints: + - valid_values: [prefer_kvm, require_kvm, PREFER_KVM, REQUIRE_KVM] + version: + type: string + + tosca.capabilities.nfv.riftio.host_epa: + derived_from: tosca.capabilities.Root + properties: + cpu_model: + type: string + description: >- + Host CPU model. Examples include SandyBridge, + IvyBridge, etc. + required: false + constraints: + - valid_values: + - prefer_westmere + - require_westmere + - prefer_sandbridge + - require_sandybridge + - prefer_ivybridge + - require_ivybridge + - prefer_haswell + - require_haswell + - prefer_broadwell + - require_broadwell + - prefer_nehalem + - require_nehalem + - prefer_penryn + - require_penryn + - prefer_conroe + - require_conroe + - prefer_core2duo + - require_core2duo + - PREFER_WESTMERE + - REQUIRE_WESTMERE + - PREFER_SANDBRIDGE + - REQUIRE_SANDYBRIDGE + - PREFER_IVYBRIDGE + - REQUIRE_IVYBRIDGE + - PREFER_HASWELL + - REQUIRE_HASWELL + - PREFER_BROADWELL + - REQUIRE_BROADWELL + - PREFER_NEHALEM + - REQUIRE_NEHALEM + - PREFER_PENRYN + - REQUIRE_PENRYN + - PREFER_CONROE + - REQUIRE_CONROE + - PREFER_CORE2DUO + - REQUIRE_CORE2DUO + cpu_arch: + type: string + description: >- + Host CPU architecture + required: false + constraints: + - valid_values: + - prefer_x86 + - require_x86 + - prefer_x86_64 + - require_x86_64 + - prefer_i686 + - require_i686 + - prefer_ia64 + - require_ia64 + - prefer_armv7 + - require_armv7 + - prefer_armv8 + - require_armv8 + - PREFER_X86 + - REQUIRE_X86 + - PREFER_X86_64 + - REQUIRE_X86_64 + - PREFER_I686 + - REQUIRE_I686 + - PREFER_IA64 + - REQUIRE_IA64 + - PREFER_ARMV7 + - REQUIRE_ARMV7 + - PREFER_ARMV8 + - REQUIRE_ARMV8 + cpu_vendor: + type: string + description: >- + Host CPU vendor + required: false + constraints: + - valid_values: + - prefer_intel + - require_intel + - prefer_amd + - requie_amd + - PREFER_INTEL + - REQUIRE_INTEL + - PREFER_AMD + - REQUIE_AMD + cpu_socket_count: + type: integer + description: >- + Number of sockets on the host + required: false + constraints: + - greater_than : 0 + cpu_core_count: + type: integer + description: >- + Number of cores on the host + required: false + constraints: + - greater_than : 0 + cpu_core_thread_count: + type: integer + description: >- + Number of threads per core on the host + required: false + constraints: + - greater_than : 0 + cpu_feature: + type: list + entry_schema: + type: string + description: |- + Enumeration for CPU features. + + AES- CPU supports advanced instruction set for + AES (Advanced Encryption Standard). + + CAT- Cache Allocation Technology (CAT) allows + an Operating System, Hypervisor, or similar + system management agent to specify the amount + of L3 cache (currently the last-level cache + in most server and client platforms) space an + application can fill (as a hint to hardware + functionality, certain features such as power + management may override CAT settings). + + CMT- Cache Monitoring Technology (CMT) allows + an Operating System, Hypervisor, or similar + system management agent to determine the + usage of cache based on applications running + on the platform. The implementation is + directed at L3 cache monitoring (currently + the last-level cache in most server and + client platforms). + + DDIO- Intel Data Direct I/O (DDIO) enables + Ethernet server NICs and controllers talk + directly to the processor cache without a + detour via system memory. This enumeration + specifies if the VM requires a DDIO + capable host. + required: false + constraints: + -valid_values: + - prefer_aes + - require_aes + - prefer_cat + - require_cat + - prefer_cmt + - require_cmt + - prefer_ddio + - require_ddio + - prefer_vme + - require_vme + - prefer_de + - require_de + - prefer_pse + - require_pse + - prefer_tsc + - require_tsc + - prefer_msr + - require_msr + - prefer_pae + - require_pae + - prefer_mce + - require_mce + - prefer_cx8 + - require_cx8 + - prefer_apic + - require_apic + - prefer_sep + - require_sep + - prefer_mtrr + - require_mtrr + - prefer_pge + - require_pge + - prefer_mca + - require_mca + - prefer_cmov + - require_cmov + - prefer_pat + - require_pat + - prefer_pse36 + - require_pse36 + - prefer_clflush + - require_clflush + - prefer_dts + - require_dts + - prefer_acpi + - require_acpi + - prefer_mmx + - require_mmx + - prefer_fxsr + - require_fxsr + - prefer_sse + - require_sse + - prefer_sse2 + - require_sse2 + - prefer_ss + - require_ss + - prefer_ht + - require_ht + - prefer_tm + - require_tm + - prefer_ia64 + - require_ia64 + - prefer_pbe + - require_pbe + - prefer_rdtscp + - require_rdtscp + - prefer_pni + - require_pni + - prefer_pclmulqdq + - require_pclmulqdq + - prefer_dtes64 + - require_dtes64 + - prefer_monitor + - require_monitor + - prefer_ds_cpl + - require_ds_cpl + - prefer_vmx + - require_vmx + - prefer_smx + - require_smx + - prefer_est + - require_est + - prefer_tm2 + - require_tm2 + - prefer_ssse3 + - require_ssse3 + - prefer_cid + - require_cid + - prefer_fma + - require_fma + - prefer_cx16 + - require_cx16 + - prefer_xtpr + - require_xtpr + - prefer_pdcm + - require_pdcm + - prefer_pcid + - require_pcid + - prefer_dca + - require_dca + - prefer_sse4_1 + - require_sse4_1 + - prefer_sse4_2 + - require_sse4_2 + - prefer_x2apic + - require_x2apic + - prefer_movbe + - require_movbe + - prefer_popcnt + - require_popcnt + - prefer_tsc_deadline_timer + - require_tsc_deadline_timer + - prefer_xsave + - require_xsave + - prefer_avx + - require_avx + - prefer_f16c + - require_f16c + - prefer_rdrand + - require_rdrand + - prefer_fsgsbase + - require_fsgsbase + - prefer_bmi1 + - require_bmi1 + - prefer_hle + - require_hle + - prefer_avx2 + - require_avx2 + - prefer_smep + - require_smep + - prefer_bmi2 + - require_bmi2 + - prefer_erms + - require_erms + - prefer_invpcid + - require_invpcid + - prefer_rtm + - require_rtm + - prefer_mpx + - require_mpx + - prefer_rdseed + - require_rdseed + - prefer_adx + - require_adx + - prefer_smap + - require_smap + - PREFER_AES + - REQUIRE_AES + - PREFER_CAT + - REQUIRE_CAT + - PREFER_CMT + - REQUIRE_CMT + - PREFER_DDIO + - REQUIRE_DDIO + - PREFER_VME + - REQUIRE_VME + - PREFER_DE + - REQUIRE_DE + - PREFER_PSE + - REQUIRE_PSE + - PREFER_TSC + - REQUIRE_TSC + - PREFER_MSR + - REQUIRE_MSR + - PREFER_PAE + - REQUIRE_PAE + - PREFER_MCE + - REQUIRE_MCE + - PREFER_CX8 + - REQUIRE_CX8 + - PREFER_APIC + - REQUIRE_APIC + - PREFER_SEP + - REQUIRE_SEP + - PREFER_MTRR + - REQUIRE_MTRR + - PREFER_PGE + - REQUIRE_PGE + - PREFER_MCA + - REQUIRE_MCA + - PREFER_CMOV + - REQUIRE_CMOV + - PREFER_PAT + - REQUIRE_PAT + - PREFER_PSE36 + - REQUIRE_PSE36 + - PREFER_CLFLUSH + - REQUIRE_CLFLUSH + - PREFER_DTS + - REQUIRE_DTS + - PREFER_ACPI + - REQUIRE_ACPI + - PREFER_MMX + - REQUIRE_MMX + - PREFER_FXSR + - REQUIRE_FXSR + - PREFER_SSE + - REQUIRE_SSE + - PREFER_SSE2 + - REQUIRE_SSE2 + - PREFER_SS + - REQUIRE_SS + - PREFER_HT + - REQUIRE_HT + - PREFER_TM + - REQUIRE_TM + - PREFER_IA64 + - REQUIRE_IA64 + - PREFER_PBE + - REQUIRE_PBE + - PREFER_RDTSCP + - REQUIRE_RDTSCP + - PREFER_PNI + - REQUIRE_PNI + - PREFER_PCLMULQDQ + - REQUIRE_PCLMULQDQ + - PREFER_DTES64 + - REQUIRE_DTES64 + - PREFER_MONITOR + - REQUIRE_MONITOR + - PREFER_DS_CPL + - REQUIRE_DS_CPL + - PREFER_VMX + - REQUIRE_VMX + - PREFER_SMX + - REQUIRE_SMX + - PREFER_EST + - REQUIRE_EST + - PREFER_TM2 + - REQUIRE_TM2 + - PREFER_SSSE3 + - REQUIRE_SSSE3 + - PREFER_CID + - REQUIRE_CID + - PREFER_FMA + - REQUIRE_FMA + - PREFER_CX16 + - REQUIRE_CX16 + - PREFER_XTPR + - REQUIRE_XTPR + - PREFER_PDCM + - REQUIRE_PDCM + - PREFER_PCID + - REQUIRE_PCID + - PREFER_DCA + - REQUIRE_DCA + - PREFER_SSE4_1 + - REQUIRE_SSE4_1 + - PREFER_SSE4_2 + - REQUIRE_SSE4_2 + - PREFER_X2APIC + - REQUIRE_X2APIC + - PREFER_MOVBE + - REQUIRE_MOVBE + - PREFER_POPCNT + - REQUIRE_POPCNT + - PREFER_TSC_DEADLINE_TIMER + - REQUIRE_TSC_DEADLINE_TIMER + - PREFER_XSAVE + - REQUIRE_XSAVE + - PREFER_AVX + - REQUIRE_AVX + - PREFER_F16C + - REQUIRE_F16C + - PREFER_RDRAND + - REQUIRE_RDRAND + - PREFER_FSGSBASE + - REQUIRE_FSGSBASE + - PREFER_BMI1 + - REQUIRE_BMI1 + - PREFER_HLE + - REQUIRE_HLE + - PREFER_AVX2 + - REQUIRE_AVX2 + - PREFER_SMEP + - REQUIRE_SMEP + - PREFER_BMI2 + - REQUIRE_BMI2 + - PREFER_ERMS + - REQUIRE_ERMS + - PREFER_INVPCID + - REQUIRE_INVPCID + - PREFER_RTM + - REQUIRE_RTM + - PREFER_MPX + - REQUIRE_MPX + - PREFER_RDSEED + - REQUIRE_RDSEED + - PREFER_ADX + - REQUIRE_ADX + - PREFER_SMAP + - REQUIRE_SMAP + om_cpu_model_string: + type: string + description: >- + Openmano CPU model string + required: false + om_cpu_feature: + type: list + entry_schema: + type: string + description: >- + List of openmano CPU features + required: false + + tosca.capabilities.nfv.riftio.sfc: + derived_from: tosca.capabilities.Root + description: >- + Service Function Chaining support on this VDU + properties: + sfc_type: + type: string + description: >- + Type of node in Service Function Chaining Architecture + constraints: + - valid_values: [unaware, classifier, sf, sff, UNAWARE, CLASSIFIER, SF, SFF] + default: unaware + sf_type: + type: string + description: >- + Type of Service Function. + NOTE- This needs to map with Service Function Type in ODL to + support VNFFG. Service Function Type is manadatory param in ODL + SFC. + required: false + tosca.capabilities.Compute.Container.Architecture: + derived_from: tosca.capabilities.Container + properties: + mem_page_size: + type: string + description: >- + Memory page allocation size. If a VM requires + hugepages, it should choose huge or size_2MB + or size_1GB. If the VM prefers hugepages, it + should chose prefer_huge. + huge/large - Require hugepages (either 2MB or 1GB) + normal - Does not require hugepages + size_2MB - Requires 2MB hugepages + size_1GB - Requires 1GB hugepages + prefer_huge - Application perfers hugepages + NOTE - huge and normal is only defined in standards as of + now. + required: false + constraints: + - valid_values: [normal, large, huge, size_2MB, size_1GB, prefer_huge, NORMAL,LARGE, HUGE, SIZE_2MB, SIZE_1GB, PREFER_HUGE] + cpu_allocation: + type: tosca.datatypes.compute.Container.Architecture.CPUAllocation + required: false + numa_nodes: + type: map + required: false + entry_schema: + type: tosca.datatypes.compute.Container.Architecture.NUMA + + +node_types: + tosca.nodes.nfv.riftio.VDU1: + derived_from: tosca.nodes.nfv.VDU + properties: + description: + type: string + required: false + image: + description: >- + If an image is specified here, it is assumed that the image + is already present in the RO or VIM and not in the package. + type: string + required: false + image_checksum: + type: string + description: >- + Image checksum for the image in RO or VIM. + required: false + cloud_init: + description: >- + Inline cloud-init specification + required: false + type: string + count: + default: 1 + type: integer + capabilities: + virtualLink: + type: tosca.capabilities.nfv.VirtualLinkable + monitoring_param_1: + type: tosca.capabilities.nfv.riftio.monitoring_param + mgmt_interface: + type: tosca.capabilities.nfv.riftio.mgmt_interface + monitoring_param: + type: tosca.capabilities.nfv.riftio.monitoring_param + numa_extension: + type: tosca.capabilities.nfv.riftio.numa_extension + vswitch_epa: + type: tosca.capabilities.nfv.riftio.vswitch_epa + hypervisor_epa: + type: tosca.capabilities.nfv.riftio.hypervisor_epa + host_epa: + type: tosca.capabilities.nfv.riftio.host_epa + tosca.nodes.nfv.riftio.CP1: + derived_from: tosca.nodes.nfv.CP + properties: + cp_type: + description: Type of the connection point + type: string + default: VPORT + constraints: + - valid_values: [VPORT] + name: + description: Name of the connection point + type: string + required: false + vdu_intf_name: + description: Name of the interface on VDU + type: string + vdu_intf_type: + description: >- + Specifies the type of virtual interface + between VM and host. + VIRTIO - Use the traditional VIRTIO interface. + PCI-PASSTHROUGH - Use PCI-PASSTHROUGH interface. + SR-IOV - Use SR-IOV interface. + E1000 - Emulate E1000 interface. + RTL8139 - Emulate RTL8139 interface. + PCNET - Emulate PCNET interface. + OM-MGMT - Used to specify openmano mgmt external-connection type + type: string + constraints: + - valid_values: [OM-MGMT, VIRTIO, E1000, SR-IOV] + bandwidth: + type: integer + description: Aggregate bandwidth of the NIC + constraints: + - greater_or_equal: 0 + required: false + vpci: + type: string + description: >- + Specifies the virtual PCI address. Expressed in + the following format dddd:dd:dd.d. For example + 0000:00:12.0. This information can be used to + pass as metadata during the VM creation. + required: false + capabilities: + sfc: + type: tosca.capabilities.nfv.riftio.sfc + tosca.nodes.nfv.riftio.VNF1: + derived_from: tosca.nodes.nfv.VNF + properties: + member_index: + type: integer + constraints: + - greater_or_equal: 1 + description: Index of the VNF in the NS + required: false + start_by_default: + type: boolean + default: true + description: Start this VNF on NS instantiate + logo: + type: string + description: >- + Logo to display with the VNF in the orchestrator + required: false + capabilities: + mgmt_interface: + type: tosca.capabilities.nfv.riftio.mgmt_interface + monitoring_param: + type: tosca.capabilities.nfv.riftio.monitoring_param + sfc: + type: tosca.capabilities.nfv.riftio.sfc + tosca.nodes.nfv.riftio.ELAN: + derived_from: tosca.nodes.nfv.VL.ELAN + properties: + description: + type: string + required: false + network_name: + type: string + description: >- + Name of network in VIM account. This is used to indicate + pre-provisioned network name in cloud account. + required: false + root_bandwidth: + type: integer + description: >- + This is the aggregate bandwidth + constraints: + - greater_or_equal: 0 + required: false + leaf_bandwidth: + type: integer + description: >- + This is the bandwidth of branches + constraints: + - greater_or_equal: 0 + required: false + tosca.nodes.nfv.riftio.FP1: + derived_from: tosca.nodes.nfv.FP + properties: + id: + type: integer + required: false + policy: + type: tosca.nfv.datatypes.policyType + required: true + description: policy to use to match traffic for this FP + path: + type: list + required: true + entry_schema: + type: tosca.nfv.datatypes.pathType + cp: + type: tosca.nfv.datatypes.pathType + required: true + + + +artifact_types: + tosca.artifacts.Deployment.riftio.cloud_init_file: + derived_from: tosca.artifacts.Deployment + file: + type: string + + tosca.artifacts.Deployment.Image.riftio.QCOW2: + derived_from: tosca.artifacts.Deployment.Image.VM.QCOW2 + image_checksum: + required: false + type: string + +group_types: + tosca.groups.nfv.VNFFG: + derived_from: tosca.groups.Root + properties: + vendor: + type: string + required: true + description: name of the vendor who generate this VNFFG + version: + type: string + required: true + description: version of this VNFFG + number_of_endpoints: + type: integer + required: true + description: count of the external endpoints included in this VNFFG + dependent_virtual_link: + type: list + entry_schema: + type: string + required: true + description: Reference to a VLD used in this Forwarding Graph + connection_point: + type: list + entry_schema: + type: string + required: true + description: Reference to Connection Points forming the VNFFG + constituent_vnfs: + type: list + entry_schema: + type: string + required: true + description: Reference to a list of VNFD used in this VNF Forwarding Graph + members: [ tosca.nodes.nfv.FP ] + + tosca.groups.nfv.riftio.scaling: + derived_from: tosca.groups.Root + properties: + name: + type: string + min_instances: + type: integer + description: >- + Minimum instances of the scaling group which are allowed. + These instances are created by default when the network service + is instantiated. + max_instances: + type: integer + description: >- + Maximum instances of this scaling group that are allowed + in a single network service. The network service scaling + will fail, when the number of service group instances + exceed the max-instance-count specified. + cooldown_time: + type: integer + description: >- + The duration after a scaling-in/scaling-out action has been + triggered, for which there will be no further optional + ratio: + type: map + entry_schema: + type: integer + description: >- + Specify the number of instances of each VNF to instantiate + for a scaling action + members: [tosca.nodes.nfv.VNF] + interfaces: + action: + type: tosca.interfaces.nfv.riftio.scaling.action + +interface_types: + tosca.interfaces.nfv.riftio.scaling.action: + pre_scale_in: + description: Operation to execute before a scale in + post_scale_in: + description: Operation to execute after a scale in + pre_scale_out: + description: Operation to execute before a scale out + post_scale_out: + description: Operation to execute after a scale out + +policy_types: + tosca.policies.nfv.riftio.placement: + derived_from: tosca.policies.Placement + properties: + name: + type: string + description: >- + Place group construct to define the compute resource placement strategy + in cloud environment + requirement: + type: string + description: >- + This is free text space used to describe the intent/rationale + behind this placement group. This is for human consumption only + strategy: + type: string + description: >- + Strategy associated with this placement group + Following values are possible + COLOCATION - Colocation strategy imply intent to share the physical + infrastructure (hypervisor/network) among all members + of this group. + ISOLATION - Isolation strategy imply intent to not share the physical + infrastructure (hypervisor/network) among the members + of this group. + constraints: + valid_values: + - COLOCATION + - ISOLATION + tosca.policies.nfv.riftio.vnf_configuration: + derived_from: tosca.policies.Root + properties: + config: + type: tosca.datatypes.nfv.riftio.vnf_configuration + initial_config: + type: list + entry_schema: + type: tosca.datatypes.nfv.riftio.config_primitive + tosca.policies.nfv.riftio.vnf_service_primitives: + derived_from: tosca.policies.Root + properties: + parameter: + type: map + entry_schema: + type: primitive_parameter + tosca.policies.nfv.riftio.ns_service_primitives: + derived_from: tosca.policies.Root + properties: + parameter: + type: map + entry_schema: + type: primitive_parameter + parameter_group: + type: tosca.datatypes.nfv.riftio.primitive_parameter_group + description: >- + Grouping of parameters which are logically grouped in UI + required: false + vnf_primitive_group: + type: tosca.datatypes.nfv.riftio.vnf_primitive_group + description: >- + List of service primitives grouped by VNF + required: false + user_defined_script: + type: string + description: >- + A user defined script + required: false + tosca.policies.nfv.riftio.initial_config_primitive: + derived_from: tosca.policies.Root + properties: + name: + type: string + seq: + type: integer + description: >- + Order in which to apply, when multiple ones are defined + default: 0 + constraints: + - greater_or_equal: 0 + parameter: + type: map + entry_schema: + type: string + user_defined_script: + type: string + tosca.policies.nfv.riftio.users: + derived_from: tosca.policies.Root + description: >- + Specify list of public keys to be injected as + part of NS instantitation. Use default as entry, + to specify the key pairs for default user. + properties: + user_info: + type: string + description: >- + The user\'s real name + required: false + key_pairs: + type: map + description: >- + List of public keys for the user + entry_schema: + type: string + required: true + tosca.policies.nfv.riftio.dependency: + derived_from: tosca.policies.Root + description: >- + Map dependency between VDUs or VNFs + properties: + parameter: + type: map + entry_schema: + type: string + description: >- + Parameter and value for the config + tosca.nfv.datatypes.policyType: + properties: + type: + type: string + required: false + constraints: + - valid_values: [ ACL ] + criteria: + type: list + required: true + entry_schema: + type: tosca.nfv.datatypes.aclType + + + diff --git a/common/python/rift/mano/yang_translator/rwmano/syntax/tosca_resource.py b/common/python/rift/mano/yang_translator/rwmano/syntax/tosca_resource.py index f05933b0..7143e675 100644 --- a/common/python/rift/mano/yang_translator/rwmano/syntax/tosca_resource.py +++ b/common/python/rift/mano/yang_translator/rwmano/syntax/tosca_resource.py @@ -17,7 +17,7 @@ from rift.mano.yang_translator.common.utils import _ class ToscaResource(object): - '''Base class for YANG node type translation to RIFT.io TOSCA type.''' + '''Base class for YANG node type translation to RIFT.io SUBSTITUTION_MAPPINGtype.''' # Used when creating the resource, so keeping separate # from REQUIRED_FIELDS below @@ -26,8 +26,10 @@ class ToscaResource(object): REQUIRED_FIELDS = (DESC, VERSION, VENDOR, ID) = \ ('description', 'version', 'vendor', 'id') - COMMON_FIELDS = (PATH, PORT, HOST, XPATH, TYPE, COUNT, FILE) = \ - ('path', 'port', 'host', 'xpath', 'type', 'count', 'file') + COMMON_FIELDS = (PATH, PORT, HOST, XPATH, TYPE, COUNT, FILE, + NFV_COMPUTE, HOST_EPA, VSWITCH_EPA, HYPERVISOR_EPA, GUEST_EPA) = \ + ('path', 'port', 'host', 'xpath', 'type', 'count', 'file', 'nfv_compute', + 'host_epa', 'vswitch_epa', 'hypervisor_epa', 'guest_epa') IGNORE_FIELDS = ['short_name'] @@ -66,17 +68,17 @@ class ToscaResource(object): GROUP_TYPES, POLICY_TYPES, REQUIREMENTS, ARTIFACTS, PROPERTIES, INTERFACES, CAPABILITIES, RELATIONSHIP, - ARTIFACT_TYPES) = \ + ARTIFACT_TYPES, TARGETS) = \ ('data_types', 'capability_types', 'node_types', 'group_types', 'policy_types', 'requirements', 'artifacts', 'properties', 'interfaces', 'capabilities', 'relationship', - 'artifact_types') + 'artifact_types', 'targets') TOSCA_TMPL = (INPUTS, NODE_TMPL, GROUPS, POLICIES, - METADATA, TOPOLOGY_TMPL, OUTPUTS) = \ + METADATA, TOPOLOGY_TMPL, OUTPUTS, SUBSTITUTION_MAPPING, IMPORT) = \ ('inputs', 'node_templates', 'groups', 'policies', - 'metadata', 'topology_template', 'outputs') + 'metadata', 'topology_template', 'outputs', 'substitution_mappings', 'imports') TOSCA_DERIVED = ( T_VNF_CONFIG, @@ -91,26 +93,32 @@ class ToscaResource(object): T_SCALE_GRP, T_ARTF_QCOW2, T_INITIAL_CFG, + T_ARTF_CLOUD_INIT, + T_PLACEMENT, + T_ELAN ) = \ - ('tosca.datatypes.network.riftio.vnf_configuration', + ('tosca.policies.nfv.riftio.vnf_configuration', 'tosca.capabilities.riftio.http_endpoint_type', 'tosca.capabilities.riftio.mgmt_interface_type', 'tosca.capabilities.riftio.monitoring_param', - 'tosca.nodes.riftio.VNF1', - 'tosca.nodes.riftio.VDU1', - 'tosca.nodes.riftio.CP1', + 'tosca.nodes.nfv.riftio.VNF1', + 'tosca.nodes.nfv.riftio.VDU1', + 'tosca.nodes.nfv.riftio.CP1', 'tosca.nodes.riftio.VL1', 'tosca.groups.riftio.ConfigPrimitives', 'tosca.policies.riftio.ScalingGroup', 'tosca.artifacts.Deployment.Image.riftio.QCOW2', - 'tosca.policies.riftio.InitialConfigPrimitive' + 'tosca.policies.nfv.riftio.initial_config_primitive', + 'tosca.artifacts.Deployment.riftio.cloud_init_file', + 'tosca.policies.nfv.riftio.placement', + 'tosca.nodes.nfv.riftio.ELAN' ) SUPPORT_FILES = ( SRC, DEST, EXISTING) = \ ('source', 'destination', 'existing') - SUPPORT_DIRS = (IMAGE_DIR, SCRIPT_DIR,) = \ - ('images', 'scripts',) + SUPPORT_DIRS = (IMAGE_DIR, SCRIPT_DIR, CLOUD_INIT_DIR) = \ + ('images', 'scripts','cloud_init') def __init__(self, log, diff --git a/common/python/rift/mano/yang_translator/rwmano/syntax/tosca_template.py b/common/python/rift/mano/yang_translator/rwmano/syntax/tosca_template.py index 7c31df59..95f2cb26 100644 --- a/common/python/rift/mano/yang_translator/rwmano/syntax/tosca_template.py +++ b/common/python/rift/mano/yang_translator/rwmano/syntax/tosca_template.py @@ -36,6 +36,15 @@ class ToscaTemplate(object): self.log.debug(_('Converting translated output to tosca template.')) templates = {} + vnfd_templates = {} + + for resource in self.resources: + if resource.type == 'vnfd': + tmpl = resource.generate_tosca() + tmpl = resource.generate_tosca_template(tmpl) + self.log.debug(_("TOSCA template generated for {0}:\n{1}"). + format(resource.name, tmpl)) + vnfd_templates[resource.name] = tmpl for resource in self.resources: # Each NSD should generate separate templates @@ -49,6 +58,14 @@ class ToscaTemplate(object): if len(files): templates[resource.name][self.FILES] = files + for resource in self.resources: + if resource.type == 'vnfd': + tmpl = vnfd_templates[resource.name] + templates[resource.name] = {self.TOSCA: self.output_to_yaml(tmpl)} + files = resource.get_supporting_files() + if len(files): + templates[resource.name][self.FILES] = files + return templates def represent_ordereddict(self, dumper, data): @@ -66,6 +83,7 @@ class ToscaTemplate(object): ToscaResource.REQUIREMENTS,ToscaResource.ARTIFACTS, ToscaResource.INTERFACES] new_node = OrderedDict() + self.log.debug("Node to oder: {}".format(node)) for ent in order: if ent in node: new_node.update({ent: node.pop(ent)}) @@ -87,10 +105,18 @@ class ToscaTemplate(object): else: return nodes + def ordered_nodes_sub_mapping(self, nodes): + new_nodes = OrderedDict() + if isinstance(nodes, dict): + for name, node in nodes.items(): + new_nodes.update({name: node}) + return new_nodes + else: + return nodes + def output_to_yaml(self, tosca): self.log.debug(_('Converting translated output to yaml format.')) dict_output = OrderedDict() - dict_output.update({'tosca_definitions_version': tosca['tosca_definitions_version']}) # Description @@ -106,6 +132,9 @@ class ToscaTemplate(object): if ToscaResource.METADATA in tosca: dict_output.update({ToscaResource.METADATA: tosca[ToscaResource.METADATA]}) + if ToscaResource.IMPORT in tosca: + dict_output.update({ToscaResource.IMPORT: + tosca[ToscaResource.IMPORT]}) # Add all types types_list = [ToscaResource.DATA_TYPES, ToscaResource.CAPABILITY_TYPES, @@ -122,9 +151,14 @@ class ToscaTemplate(object): if ToscaResource.TOPOLOGY_TMPL in tosca: tmpl = OrderedDict() for typ in tosca[ToscaResource.TOPOLOGY_TMPL]: - tmpl.update({typ: - self.ordered_nodes( - tosca[ToscaResource.TOPOLOGY_TMPL][typ])}) + if typ != ToscaResource.SUBSTITUTION_MAPPING: + tmpl.update({typ: + self.ordered_nodes( + tosca[ToscaResource.TOPOLOGY_TMPL][typ])}) + else: + tmpl.update({typ: + self.ordered_nodes_sub_mapping( + tosca[ToscaResource.TOPOLOGY_TMPL][typ])}) dict_output.update({ToscaResource.TOPOLOGY_TMPL: tmpl}) yaml.add_representer(OrderedDict, self.represent_ordereddict) diff --git a/common/python/rift/mano/yang_translator/rwmano/translate_descriptors.py b/common/python/rift/mano/yang_translator/rwmano/translate_descriptors.py index f0a68665..707ab7fb 100644 --- a/common/python/rift/mano/yang_translator/rwmano/translate_descriptors.py +++ b/common/python/rift/mano/yang_translator/rwmano/translate_descriptors.py @@ -104,10 +104,11 @@ class TranslateDescriptors(object): return types_map - def __init__(self, log, yangs, tosca_template): + def __init__(self, log, yangs, tosca_template, vnfd_files=None): self.log = log self.yangs = yangs self.tosca_template = tosca_template + self.vnfd_files = vnfd_files # list of all TOSCA resources generated self.tosca_resources = [] self.metadata = {} @@ -143,27 +144,30 @@ class TranslateDescriptors(object): def _translate_yang(self): self.log.debug(_('Translating the descriptors.')) - for nsd in self.yangs[self.NSD]: - self.log.debug(_("Translate descriptor of type nsd: {}"). - format(nsd)) - tosca_node = TranslateDescriptors. \ - YANG_TO_TOSCA_TYPE[self.NSD]( - self.log, - nsd.pop(ToscaResource.NAME), - self.NSD, - nsd) - self.tosca_resources.append(tosca_node) - - for vnfd in self.yangs[self.VNFD]: - self.log.debug(_("Translate descriptor of type vnfd: {}"). - format(vnfd)) - tosca_node = TranslateDescriptors. \ - YANG_TO_TOSCA_TYPE[self.VNFD]( - self.log, - vnfd.pop(ToscaResource.NAME), - self.VNFD, - vnfd) - self.tosca_resources.append(tosca_node) + if self.NSD in self.yangs: + for nsd in self.yangs[self.NSD]: + self.log.debug(_("Translate descriptor of type nsd: {}"). + format(nsd)) + tosca_node = TranslateDescriptors. \ + YANG_TO_TOSCA_TYPE[self.NSD]( + self.log, + nsd.pop(ToscaResource.NAME), + self.NSD, + nsd, + self.vnfd_files) + self.tosca_resources.append(tosca_node) + + if self.VNFD in self.yangs: + for vnfd in self.yangs[self.VNFD]: + self.log.debug(_("Translate descriptor of type vnfd: {}"). + format(vnfd)) + tosca_node = TranslateDescriptors. \ + YANG_TO_TOSCA_TYPE[self.VNFD]( + self.log, + vnfd.pop(ToscaResource.NAME), + self.VNFD, + vnfd) + self.tosca_resources.append(tosca_node) # First translate VNFDs for node in self.tosca_resources: diff --git a/common/python/rift/mano/yang_translator/rwmano/yang/yang_nsd.py b/common/python/rift/mano/yang_translator/rwmano/yang/yang_nsd.py index 491bd86d..4e421d1f 100644 --- a/common/python/rift/mano/yang_translator/rwmano/yang/yang_nsd.py +++ b/common/python/rift/mano/yang_translator/rwmano/yang/yang_nsd.py @@ -49,7 +49,8 @@ class YangNsd(ToscaResource): log, name, type_, - yang): + yang, + vnfd_files): super(YangNsd, self).__init__(log, name, type_, @@ -57,10 +58,16 @@ class YangNsd(ToscaResource): self.props = {} self.inputs = [] self.vnfds = {} - self.vlds = [] + self.vlds = {} self.conf_prims = [] self.scale_grps = [] self.initial_cfg = [] + self.placement_groups = [] + self.vnf_id_to_vnf_map = {} + self.vnfd_files = vnfd_files + self.vld_to_vnf_map = {} + self.vnf_to_vld_map = {} + self._vnf_vld_conn_point_map = {} def handle_yang(self, vnfds): self.log.debug(_("Process NSD desc {0}: {1}"). @@ -85,6 +92,7 @@ class YangNsd(ToscaResource): vnfd_id = cvnfd.pop(self.VNFD_ID_REF) for vnfd in vnfds: if vnfd.type == self.VNFD and vnfd.id == vnfd_id: + self.vnf_id_to_vnf_map[vnfd_id] = vnfd.name self.vnfds[cvnfd.pop(self.MEM_VNF_INDEX)] = vnfd if self.START_BY_DFLT in cvnfd: vnfd.props[self.START_BY_DFLT] = \ @@ -160,7 +168,82 @@ class YangNsd(ToscaResource): self.log.warn(_("{0}, Did not process all fields for {1}"). format(self, dic)) self.log.debug(_("{0}, Initial config {1}").format(self, icp)) - self.initial_cfg.append(icp) + self.initial_cfg.append({self.PROPERTIES : icp}) + + def process_vld(vld, dic): + vld_conf = {} + vld_prop = {} + ip_profile_vld = None + vld_name = None + if 'ip_profile_ref' in vld: + ip_profile_name = vld['ip_profile_ref'] + if 'ip_profiles' in dic: + for ip_prof in dic['ip_profiles']: + if ip_profile_name == ip_prof['name']: + ip_profile_vld = ip_prof + if 'name' in vld: + vld_name = vld['name'] + if 'description' in vld: + vld_conf['description'] = vld['description'] + if 'vendor' in vld: + vld_conf['vendor'] = vld['vendor'] + if ip_profile_vld: + if 'ip_profile_params' in ip_profile_vld: + ip_param = ip_profile_vld['ip_profile_params'] + if 'gateway_address' in ip_param: + vld_conf['gateway_ip'] = ip_param['gateway_address'] + if 'subnet_address' in ip_param: + vld_conf['cidr'] = ip_param['subnet_address'] + if 'ip_version' in ip_param: + vld_conf['ip_version'] = ip_param['ip_version'].replace('ipv','') + + if vld_name: + vld_prop = {vld_name : + { + 'type': self.T_ELAN, + self.PROPERTIES : vld_conf + }} + self.vlds[vld_name] = { 'type': self.T_ELAN, + self.PROPERTIES : vld_conf + } + + self.vld_to_vnf_map[vld_name] = [] + if 'vnfd_connection_point_ref' in vld: + for vnfd_ref in vld['vnfd_connection_point_ref']: + vnf_name = self.vnf_id_to_vnf_map[vnfd_ref['vnfd_id_ref']] + if vnf_name in self.vnf_to_vld_map: + self.vnf_to_vld_map[vnf_name].append(vld_name) + self._vnf_vld_conn_point_map[vnf_name].\ + append((vld_name ,vnfd_ref['vnfd_connection_point_ref'])) + else: + self.vnf_to_vld_map[vnf_name] = [] + self._vnf_vld_conn_point_map[vnf_name] = [] + self.vnf_to_vld_map[vnf_name].append(vld_name) + self._vnf_vld_conn_point_map[vnf_name].\ + append((vld_name ,vnfd_ref['vnfd_connection_point_ref'])) + + def process_placement_group(placement_groups): + for i in range(0, len(placement_groups)): + placement_group = placement_groups[i] + pg_name = "placement_{0}".format(i) + pg_config = {} + targets = [] + if 'name' in placement_group: + pg_config['name'] = placement_group['name'] + if 'requirement' in placement_group: + pg_config['requirement'] = placement_group['requirement'] + if 'strategy' in placement_group: + pg_config['strategy'] = placement_group['strategy'] + if 'member_vnfd' in placement_group: + for member_vnfd in placement_group['member_vnfd']: + targets.append(self.vnf_id_to_vnf_map[member_vnfd['vnfd_id_ref']]) + placement = { pg_name : { + 'type': self.T_PLACEMENT, + self.PROPERTIES: pg_config, + self.TARGETS : str(targets) + } + } + self.placement_groups.append(placement) dic = deepcopy(self.yang) try: @@ -177,10 +260,8 @@ class YangNsd(ToscaResource): # Process VLDs if self.VLD in dic: for vld_dic in dic.pop(self.VLD): - vld = YangVld(self.log, vld_dic.pop(self.NAME), - self.VLD, vld_dic) - vld.process_vld(self.vnfds) - self.vlds.append(vld) + process_vld(vld_dic, dic) + #self.vlds.append(vld) # Process config primitives if self.CONF_PRIM in dic: @@ -212,6 +293,10 @@ class YangNsd(ToscaResource): for param in dic.pop(self.INPUT_PARAM_XPATH): process_input_param(param) + if 'placement_groups' in dic: + process_placement_group(dic['placement_groups']) + + self.remove_ignored_fields(dic) if len(dic): self.log.warn(_("{0}, Did not process the following for " @@ -226,13 +311,14 @@ class YangNsd(ToscaResource): raise ValidationError(message=err_msg) def generate_tosca_type(self): + self.log.debug(_("{0} Generate tosa types"). format(self)) tosca = {} - tosca[self.DATA_TYPES] = {} - tosca[self.NODE_TYPES] = {} - + #tosca[self.DATA_TYPES] = {} + #tosca[self.NODE_TYPES] = {} + return tosca for idx, vnfd in self.vnfds.items(): tosca = vnfd.generate_tosca_type(tosca) @@ -287,20 +373,25 @@ class YangNsd(ToscaResource): def generate_tosca_template(self, tosca): self.log.debug(_("{0}, Generate tosca template"). format(self, tosca)) - # Add the standard entries tosca['tosca_definitions_version'] = \ - 'tosca_simple_profile_for_nfv_1_0_0' + 'tosca_simple_profile_for_nfv_1_0' tosca[self.DESC] = self.props[self.DESC] tosca[self.METADATA] = { 'ID': self.name, self.VENDOR: self.props[self.VENDOR], self.VERSION: self.props[self.VERSION], } + if len(self.vnfd_files) > 0: + tosca[self.IMPORT] = [] + imports = [] + for vnfd_file in self.vnfd_files: + tosca[self.IMPORT].append('"{0}.yaml"'.format(vnfd_file)) tosca[self.TOPOLOGY_TMPL] = {} # Add input params + ''' if len(self.inputs): if self.INPUTS not in tosca[self.TOPOLOGY_TMPL]: tosca[self.TOPOLOGY_TMPL][self.INPUTS] = {} @@ -309,15 +400,38 @@ class YangNsd(ToscaResource): self.DESC: 'Translated from YANG'}} tosca[self.TOPOLOGY_TMPL][self.INPUTS] = entry - + ''' tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL] = {} # Add the VNFDs and VLDs for idx, vnfd in self.vnfds.items(): - vnfd.generate_vnf_template(tosca, idx) + #vnfd.generate_vnf_template(tosca, idx) + node = { + 'type' : vnfd.vnf_type, + self.PROPERTIES : { + self.ID : idx, + self.VENDOR : self.props[self.VENDOR], + self.VERSION : self.props[self.VERSION] + } + } + if vnfd.name in self.vnf_to_vld_map: + vld_list = self.vnf_to_vld_map[vnfd.name] + node[self.REQUIREMENTS] = [] + for vld_idx in range(0, len(vld_list)): + vld_link_name = "{0}{1}".format("virtualLink", vld_idx + 1) + vld_prop = {} + vld_prop[vld_link_name] = vld_list[vld_idx] + node[self.REQUIREMENTS].append(vld_prop) + if vnfd.name in self._vnf_vld_conn_point_map: + vnf_vld_list = self._vnf_vld_conn_point_map[vnfd.name] + for vnf_vld in vnf_vld_list: + vnfd.generate_vld_link(vld_link_name, vnf_vld[1]) - for vld in self.vlds: - vld.generate_tosca_template(tosca) + + tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][vnfd.name] = node + + for vld_node_name in self.vlds: + tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][vld_node_name] = self.vlds[vld_node_name] # add the config primitives if len(self.conf_prims): @@ -361,33 +475,38 @@ class YangNsd(ToscaResource): tosca[self.TOPOLOGY_TMPL][self.POLICIES] = [] for icp in self.initial_cfg: - icpt = { - self.TYPE: self.T_INITIAL_CFG, - } - icpt.update(icp) - tosca[self.TOPOLOGY_TMPL][self.POLICIES].append({ - self.INITIAL_CFG: icpt - }) + if len(tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL]) > 0: + node_name = list(tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL].keys())[0] + icpt = { + self.TYPE: self.T_INITIAL_CFG, + self.TARGETS : "[{0}]".format(node_name) + } + icpt.update(icp) + tosca[self.TOPOLOGY_TMPL][self.POLICIES].append({ + self.INITIAL_CFG: icpt + }) + + if len(self.placement_groups) > 0: + if self.POLICIES not in tosca[self.TOPOLOGY_TMPL]: + tosca[self.TOPOLOGY_TMPL][self.POLICIES] = [] + + for placment_group in self.placement_groups: + tosca[self.TOPOLOGY_TMPL][self.POLICIES].append(placment_group) return tosca def get_supporting_files(self): files = [] - - for vnfd in self.vnfds.values(): - f = vnfd.get_supporting_files() - if f and len(f): - files.extend(f) - # Get the config files for initial config for icp in self.initial_cfg: - if self.USER_DEF_SCRIPT in icp: - script = os.path.basename(icp[self.USER_DEF_SCRIPT]) - files.append({ - self.TYPE: 'script', - self.NAME: script, - self.DEST: "{}/{}".format(self.SCRIPT_DIR, script), - }) + if 'properties' in icp: + if 'user_defined_script' in icp['properties']: + script = os.path.basename(icp['properties']['user_defined_script']) + files.append({ + self.TYPE: 'script', + self.NAME: script, + self.DEST: "{}/{}".format(self.SCRIPT_DIR, script), + }) # TODO (pjoseph): Add support for config scripts, # charms, etc diff --git a/common/python/rift/mano/yang_translator/rwmano/yang/yang_vdu.py b/common/python/rift/mano/yang_translator/rwmano/yang/yang_vdu.py index 7d095c1e..3786d86c 100644 --- a/common/python/rift/mano/yang_translator/rwmano/yang/yang_vdu.py +++ b/common/python/rift/mano/yang_translator/rwmano/yang/yang_vdu.py @@ -56,6 +56,15 @@ class YangVdu(ToscaResource): 'storage_gb': ' GB', } + TOSCA_MEM_SIZE = { + 'LARGE': 'huge', + 'SMALL': 'normal', + 'SIZE_2MB': 'size_2MB', + 'SIZE_1GB': 'size_1GB', + 'PREFER_LARGE': 'prefer_huge' + + } + def __init__(self, log, name, @@ -69,8 +78,15 @@ class YangVdu(ToscaResource): self.props = {} self.ext_cp = [] self.int_cp = [] - self.image = None + self.image = None self.cloud_init_file = None + self.host_epa = None + self.vswitch_epa = None + self.hypervisor_epa = None + self.guest_epa = None + self.cp_name_to_cp_node = {} + self.pinning_epa_prop = {} + self.mem_page_guest_epa = None def process_vdu(self): self.log.debug(_("Process VDU desc {0}: {1}").format(self.name, @@ -88,9 +104,9 @@ class YangVdu(ToscaResource): self.id = vdu[self.ID] if self.VM_FLAVOR in vdu_dic: - vdu[self.HOST] = {} + vdu[self.NFV_COMPUTE] = {} for key, value in vdu_dic.pop(self.VM_FLAVOR).items(): - vdu[self.HOST][self.VM_FLAVOR_MAP[key]] = "{}{}". \ + vdu[self.NFV_COMPUTE][self.VM_FLAVOR_MAP[key]] = "{}{}". \ format(value, self.VM_SIZE_UNITS_MAP[key]) if self.EXT_INTF in vdu_dic: @@ -103,7 +119,110 @@ class YangVdu(ToscaResource): format(self, cp, ext_intf)) self.ext_cp.append(cp) + if self.HOST_EPA in vdu_dic: + host_epa = vdu_dic.pop(self.HOST_EPA) + host_epa_prop = {} + self.host_epa = host_epa + ''' + if 'cpu_model' in host_epa: + host_epa_prop['cpu_model'] = host_epa['cpu_model'].lower() + if 'cpu_arch' in host_epa: + host_epa_prop['cpu_arch'] = host_epa['cpu_arch'].lower() + if 'cpu_vendor' in host_epa: + host_epa_prop['cpu_vendor'] = host_epa['cpu_vendor'].lower() + if 'cpu_socket_count' in host_epa: + host_epa_prop['cpu_socket_count'] = host_epa['cpu_socket_count'] + if 'cpu_core_count' in host_epa: + host_epa_prop['cpu_core_count'] = host_epa['cpu_core_count'] + if 'cpu_core_thread_count' in host_epa: + host_epa_prop['cpu_core_thread_count'] = host_epa['cpu_core_thread_count'] + if 'om_cpu_model_string' in host_epa: + host_epa_prop['om_cpu_model_string'] = host_epa['om_cpu_model_string'] + if 'cpu_feature' in host_epa: + host_epa_prop['cpu_feature'] = [] + for cpu_feature in host_epa['cpu_feature']: + cpu_feature_prop = {} + cpu_feature_prop['feature'] = cpu_feature['feature'].lower() + host_epa_prop['cpu_feature'] .append(cpu_feature_prop) + + if 'om_cpu_feature' in host_epa: + host_epa_prop['om_cpu_feature'] = [] + for cpu_feature in host_epa['om_cpu_feature']: + om_cpu_feature_prop = {} + om_cpu_feature_prop['feature'] = cpu_feature + host_epa_prop['om_cpu_feature'].append(om_cpu_feature_prop) + self.host_epa = host_epa + ''' + # We might have to re write this piece of code, there are mismatch in + # enum names. Its all capital in RIFT yang and TOSCA + if self.VSWITCH_EPA in vdu_dic: + vswitch_epa = vdu_dic.pop(self.VSWITCH_EPA) + self.vswitch_epa = vswitch_epa + if self.HYPERVISOR_EPA in vdu_dic: + hypervisor_epa = vdu_dic.pop(self.HYPERVISOR_EPA) + hypervisor_epa_prop = {} + + if 'type_yang' in hypervisor_epa: + hypervisor_epa_prop['type'] = hypervisor_epa['type_yang'] + if 'version' in hypervisor_epa: + hypervisor_epa_prop['version'] = str(hypervisor_epa['version']) + else: + hypervisor_epa_prop['version'] = '1' + self.hypervisor_epa = hypervisor_epa_prop + + if self.GUEST_EPA in vdu_dic: + guest_epa = vdu_dic[self.GUEST_EPA] + guest_epa_prop = {} + + # This is a hack. I have to rewrite this. I have got this quick to working + # 'ANY' check should be added in riftio common file. Its not working for some reason. Will fix. + + if 'cpu_pinning_policy' in guest_epa and guest_epa['cpu_pinning_policy'] != 'ANY': + self.pinning_epa_prop['cpu_affinity'] = guest_epa['cpu_pinning_policy'].lower() + if 'cpu_thread_pinning_policy' in guest_epa: + self.pinning_epa_prop['thread_allocation'] = guest_epa['cpu_thread_pinning_policy'].lower() + if 'mempage_size' in guest_epa: + self.mem_page_guest_epa = self.TOSCA_MEM_SIZE[guest_epa['mempage_size']] + + if 'numa_node_policy' in guest_epa: + num_node_policy = guest_epa['numa_node_policy'] + if 'node_cnt' in num_node_policy: + guest_epa_prop['node_cnt'] = num_node_policy['node_cnt'] + if 'mem_policy' in num_node_policy: + guest_epa_prop['mem_policy'] = num_node_policy['mem_policy'] + if 'node' in num_node_policy: + nodes = [] + for node in num_node_policy['node']: + node_prop = {} + if 'id' in node: + node_prop['id'] = node['id'] + if 'vcpu' in node: + vc =[] + for vcp in node['vcpu']: + vc.append(vcp['id']) + + node_prop['vcpus'] = vc + if 'memory_mb' in node: + node_prop['mem_size'] = "{} MB".format(node['memory_mb']) + # om_numa_type generation + + if 'num_cores' in node: + node_prop['om_numa_type'] = 'num_cores' + node_prop['num_cores'] = node['num_cores'] + elif 'paired_threads' in node: + node_prop['om_numa_type'] = 'paired-threads' + node_prop['paired_threads'] = node['paired_threads'] + elif 'threads]' in node: + node_prop['om_numa_type'] = 'threads]' + node_prop['num_thread]'] = node['threads]'] + + nodes.append(node_prop) + guest_epa_prop['node'] = nodes + + self.guest_epa = guest_epa_prop + self.remove_ignored_fields(vdu_dic) + if len(vdu_dic): self.log.warn(_("{0}, Did not process the following in " "VDU: {1}"). @@ -151,6 +270,7 @@ class YangVdu(ToscaResource): # Create a unique name incase multiple VNFs use same # name for the vdu return "{}_{}".format(vnf_name, self.name) + #return self.name def generate_tosca_type(self, tosca): self.log.debug(_("{0} Generate tosa types"). @@ -218,14 +338,35 @@ class YangVdu(ToscaResource): node = {} node[self.TYPE] = self.T_VDU1 + node[self.CAPABILITIES] = {} - if self.HOST in self.props: - node[self.CAPABILITIES] = { - self.HOST: {self.PROPERTIES: self.props.pop(self.HOST)} - } + if self.NFV_COMPUTE in self.props: + node[self.CAPABILITIES][self.NFV_COMPUTE] = {self.PROPERTIES: self.props.pop(self.NFV_COMPUTE)} else: self.log.warn(_("{0}, Does not have host requirements defined"). format(self)) + if self.host_epa: + node[self.CAPABILITIES][self.HOST_EPA] = { + self.PROPERTIES: self.host_epa + } + if self.vswitch_epa: + node[self.CAPABILITIES][self.VSWITCH_EPA] = { + self.PROPERTIES: self.vswitch_epa + } + if self.hypervisor_epa: + node[self.CAPABILITIES][self.HYPERVISOR_EPA] = { + self.PROPERTIES: self.hypervisor_epa + } + if self.guest_epa: + node[self.CAPABILITIES]['numa_extension'] = { + self.PROPERTIES: self.guest_epa + } + if len(self.pinning_epa_prop) > 0: + if node[self.CAPABILITIES][self.NFV_COMPUTE] and node[self.CAPABILITIES][self.NFV_COMPUTE][self.PROPERTIES]: + node[self.CAPABILITIES][self.NFV_COMPUTE][self.PROPERTIES]['cpu_allocation'] = self.pinning_epa_prop + if self.mem_page_guest_epa: + if node[self.CAPABILITIES][self.NFV_COMPUTE] and node[self.CAPABILITIES][self.NFV_COMPUTE][self.PROPERTIES]: + node[self.CAPABILITIES][self.NFV_COMPUTE][self.PROPERTIES]['mem_page_size'] = self.mem_page_guest_epa if self.IMAGE in self.props: img_name = "{}_{}_vm_image".format(vnf_name, self.name) @@ -241,10 +382,22 @@ class YangVdu(ToscaResource): node[self.INTERFACES] = {'Standard': { 'create': img_name }} - # Add cloud init script if available if self.CLOUD_INIT_FILE in self.props: + cloud_name = "{}_{}_cloud_init".format(vnf_name, self.name) self.cloud_init_file = self.props[self.CLOUD_INIT_FILE] + cloud_init_file = "../{}/{}".format(self.CLOUD_INIT_DIR, self.props.pop(self.CLOUD_INIT_FILE)) + if self.ARTIFACTS in node: + node[self.ARTIFACTS][cloud_name] = { + self.FILE: cloud_init_file, + self.TYPE: self.T_ARTF_CLOUD_INIT, + } + else: + node[self.ARTIFACTS] = { + cloud_name: { + self.FILE: cloud_init_file, + self.TYPE: self.T_ARTF_CLOUD_INIT, + }} # Remove self.props.pop(self.ID) @@ -269,6 +422,7 @@ class YangVdu(ToscaResource): cpt[self.PROPERTIES] = cp cp_name = cp[self.NAME].replace('/', '_') + self.cp_name_to_cp_node[cp[self.NAME]] = cp_name self.log.debug(_("{0}, CP node {1}: {2}"). format(self, cp_name, cpt)) @@ -296,7 +450,5 @@ class YangVdu(ToscaResource): }) self.log.debug(_("Supporting files for {} : {}").format(self, files)) - if not len(files): - shutil.rmtree(out_dir) return files diff --git a/common/python/rift/mano/yang_translator/rwmano/yang/yang_vnfd.py b/common/python/rift/mano/yang_translator/rwmano/yang/yang_vnfd.py index 9ff53e2f..1094c141 100644 --- a/common/python/rift/mano/yang_translator/rwmano/yang/yang_vnfd.py +++ b/common/python/rift/mano/yang_translator/rwmano/yang/yang_vnfd.py @@ -33,6 +33,7 @@ class YangVnfd(ToscaResource): OTHER_KEYS = (MGMT_INTF, HTTP_EP, MON_PARAM) = \ ('mgmt_interface', 'http_endpoint', 'monitoring_param') + vnf_prefix_type = 'tosca.nodes.nfv.riftio.' def __init__(self, @@ -49,6 +50,12 @@ class YangVnfd(ToscaResource): self.mgmt_intf = {} self.mon_param = [] self.http_ep = [] + self.vnf_configuration = None + self.monitor_param = {} + self.monitor_param_1 = {} + self.vnf_type = None + self.tosca = None + self.script_files = [] def handle_yang(self): self.log.debug(_("Process VNFD desc {0}: {1}").format(self.name, @@ -56,28 +63,45 @@ class YangVnfd(ToscaResource): def process_vnf_config(conf): vnf_conf = {} - if self.CONFIG_ATTR in conf: - for key, value in conf.pop(self.CONFIG_ATTR).items(): - vnf_conf[key] = value - - if self.CONFIG_TMPL in conf: - vnf_conf[self.CONFIG_TMPL] = conf.pop(self.CONFIG_TMPL) - - def copy_config_details(conf_type, conf_details): - vnf_conf[self.CONFIG_TYPE] = conf_type - vnf_conf[self.CONFIG_DETAILS] = conf_details - - for key in self.CONFIG_TYPES: - if key in conf: - copy_config_details(key, conf.pop(key)) - break - - if len(conf): - self.log.warn(_("{0}, Did not process all in VNF " - "configuration {1}"). - format(self, conf)) - self.log.debug(_("{0}, vnf config: {1}").format(self, vnf_conf)) - self.props[self.VNF_CONFIG] = vnf_conf + config = {} + + init_primitive_config = {} + if 'config_template' in conf: + config['config_template'] = conf['config_template'] + if 'config_attributes' in conf: + if 'config_delay' in conf['config_attributes']: + config['config_delay'] = conf['config_attributes']['config_delay'] + if 'config_priority' in conf['config_attributes']: + config['config_priority'] = conf['config_attributes']['config_priority'] + if 'config_type' in conf: + config['config_type'] = conf['config_type'] + if 'script' in conf: + config['config_details'] = conf['script'] + for conf_type in self.CONFIG_TYPES: + if conf_type in conf: + config['config_type'] = conf_type + if len(config) > 0: + vnf_conf['config'] = config + + if 'initial_config_primitive' in conf: + init_config_prims = [] + for init_conf_prim in conf['initial_config_primitive']: + init_conf = {} + if 'name' in init_conf_prim: + init_conf['name'] = init_conf_prim['name'] + if 'seq' in init_conf_prim: + init_conf['seq'] = init_conf_prim['seq'] + if 'user_defined_script' in init_conf_prim: + init_conf['user_defined_script'] = init_conf_prim['user_defined_script'] + self.script_files.append(init_conf_prim['user_defined_script']) + if 'parameter' in init_conf_prim: + init_conf['parameter'] = [] + for parameter in init_conf_prim['parameter']: + init_conf['parameter'].append({parameter['name']: parameter['value']}) + init_config_prims.append(init_conf) + vnf_conf['initial_config_primitive'] = init_config_prims + + self.vnf_configuration = vnf_conf def process_mgmt_intf(intf): if len(self.mgmt_intf) > 0: @@ -131,16 +155,34 @@ class YangVnfd(ToscaResource): fields = [self.NAME, self.ID, 'value_type', 'units', 'group_tag', 'json_query_method', 'http_endpoint_ref', 'widget_type', self.DESC] - for key in fields: - if key in param: - monp[key] = param.pop(key) + mon_param = {} + ui_param = {} + if 'name' in param: + mon_param['name'] = param['name'] + if 'description' in param: + mon_param['description'] = param['description'] + if 'polling_interval' in param: + mon_param['polling_interval'] = param['polling_interval'] + if 'http_endpoint_ref' in param: + mon_param['url_path'] = param['http_endpoint_ref'] + if 'json_query_method' in param: + mon_param['json_query_method'] = param['json_query_method'].lower() + if 'group_tag' in param: + ui_param['group_tag'] = param['group_tag'] + if 'widget_type' in param: + ui_param['widget_type'] = param['widget_type'].lower() + if 'units' in param: + ui_param['units'] = param['units'] + mon_param['ui_data'] = ui_param + + self.mon_param.append(mon_param) if len(param): self.log.warn(_("{0}, Did not process the following for " "monitporing-param {1}"). format(self, param)) self.log.debug(_("{0}, Monitoring param: {1}").format(self, monp)) - self.mon_param.append(monp) + #self.mon_param.append(monp) def process_cp(cps): for cp_dic in cps: @@ -161,7 +203,6 @@ class YangVnfd(ToscaResource): self.MON_PARAM: process_mon_param, 'connection_point': process_cp } - dic = deepcopy(self.yang) try: for key in self.REQUIRED_FIELDS: @@ -177,11 +218,9 @@ class YangVnfd(ToscaResource): self.VDU, vdu_dic) vdu.process_vdu() self.vdus.append(vdu) - for key in ENDPOINTS_MAP.keys(): if key in dic: ENDPOINTS_MAP[key](dic.pop(key)) - if self.VNF_CONFIG in dic: process_vnf_config(dic.pop(self.VNF_CONFIG)) @@ -203,135 +242,53 @@ class YangVnfd(ToscaResource): if cp: vdu.set_vld(cp_name, vld_name) break - - def generate_tosca_type(self, tosca): - self.log.debug(_("{0} Generate tosa types"). - format(self)) - - for vdu in self.vdus: - tosca = vdu.generate_tosca_type(tosca) - - # Add data_types - if self.T_VNF_CONFIG not in tosca[self.DATA_TYPES]: - tosca[self.DATA_TYPES][self.T_VNF_CONFIG] = { - self.PROPERTIES: - {self.CONFIG_TYPE: - {self.TYPE: self.STRING}, - 'config_delay': - {self.TYPE: self.INTEGER, - self.DEFAULT: 0, - self.REQUIRED: self.NO, - self.CONSTRAINTS: - [{'greater_or_equal': 0}]}, - 'config_priority': - {self.TYPE: self.INTEGER, - self.CONSTRAINTS: - [{'greater_than': 0}]}, - self.CONFIG_DETAILS: - {self.TYPE: self.MAP}, - self.CONFIG_TMPL: - {self.TYPE: self.STRING, - self.REQUIRED: self.NO}, - } - } - - # Add capability types - if self.CAPABILITY_TYPES not in tosca: - tosca[self.CAPABILITY_TYPES] = {} - if self.T_HTTP_EP not in tosca[self.CAPABILITY_TYPES]: - tosca[self.CAPABILITY_TYPES][self.T_HTTP_EP] = { - self.DERIVED_FROM: 'tosca.capabilities.Endpoint', - self.PROPERTIES: { - 'polling_interval': - {self.TYPE: self.INTEGER}, - 'path': - {self.TYPE: self.STRING}, - }, - } - - if self.T_MGMT_INTF not in tosca[self.CAPABILITY_TYPES]: - tosca[self.CAPABILITY_TYPES][self.T_MGMT_INTF] = { - self.DERIVED_FROM: 'tosca.capabilities.Endpoint', - self.PROPERTIES: { - self.DASHBOARD_PARAMS: - {self.TYPE: self.MAP}, - self.VDU: - {self.TYPE: self.STRING}, - }, - } - - if self.T_MON_PARAM not in tosca[self.CAPABILITY_TYPES]: - tosca[self.CAPABILITY_TYPES][self.T_MON_PARAM] = { - self.DERIVED_FROM: 'tosca.capabilities.nfv.Metric', - self.PROPERTIES: { - 'id': - {self.TYPE: self.INTEGER}, - 'name': - {self.TYPE: self.STRING}, - 'value_type': - {self.TYPE: self.STRING, - self.DEFAULT: 'INT'}, - 'group_tag': - {self.TYPE: self.STRING, - self.DEFAULT: 'Group1'}, - 'units': - {self.TYPE: self.STRING}, - 'description': - {self.TYPE: self.STRING}, - 'json_query_method': - {self.TYPE: self.STRING, - self.DEFAULT: 'NAMEKEY'}, - 'http_endpoint_ref': - {self.TYPE: self.STRING}, - 'widget_type': - {self.TYPE: self.STRING, - self.DEFAULT: 'COUNTER'}, - } + def _generate_vnf_type(self, tosca): + name = self.name.split('_', 1)[0] + self.vnf_type = "{0}{1}{2}".format(self.vnf_prefix_type, name, 'VNF') + if self.NODE_TYPES not in tosca and self.vnf_type: + tosca[self.NODE_TYPES] = {} + tosca[self.NODE_TYPES][self.vnf_type] = { + self.DERIVED_FROM : self.T_VNF1 } - # Define the VNF type - if self.T_VNF1 not in tosca[self.NODE_TYPES]: - tosca[self.NODE_TYPES][self.T_VNF1] = { - self.DERIVED_FROM: 'tosca.nodes.nfv.VNF', - self.PROPERTIES: { - 'vnf_configuration': - {self.TYPE: self.T_VNF_CONFIG}, - 'port': - {self.TYPE: self.INTEGER, - self.CONSTRAINTS: - [{'in_range': '[1, 65535]'}]}, - self.START_BY_DFLT: - {self.TYPE: self.BOOL, - self.DEFAULT: self.TRUE}, - }, - self.CAPABILITIES: { - 'mgmt_interface': - {self.TYPE: self.T_MGMT_INTF}, - 'http_endpoint': - {self.TYPE: self.T_HTTP_EP}, - 'monitoring_param_0': - {self.TYPE: self.T_MON_PARAM}, - 'monitoring_param_1': - {self.TYPE: self.T_MON_PARAM}, - }, - self.REQUIREMENTS: [ - {'vdus': - {self.TYPE: 'tosca.capabilities.nfv.VirtualLinkable', - self.RELATIONSHIP: - 'tosca.relationships.nfv.VirtualLinksTo', - self.NODE: self.T_VDU1, - self.OCCURENCES: '[1, UNBOUND]'}} - ], - } + def generate_tosca_template(self, tosca): + self.tosca = tosca + tosca['tosca_definitions_version'] = 'tosca_simple_profile_for_nfv_1_0' + tosca[self.IMPORT] = [] + tosca[self.IMPORT].append("riftiotypes.yaml") + tosca[self.DESC] = self.props[self.DESC] + tosca[self.METADATA] = { + 'ID': self.name, + self.VENDOR: self.props[self.VENDOR], + self.VERSION: self.props[self.VERSION], + } + if self.name: + self._generate_vnf_type(tosca); - return tosca - def generate_vnf_template(self, tosca, index): - self.log.debug(_("{0}, Generate tosca template for VNF {1}"). - format(self, index, tosca)) + tosca[self.TOPOLOGY_TMPL] = {} + tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL] = {} + tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING] = {} + tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING]['node_type'] = self.vnf_type for vdu in self.vdus: - tosca = vdu.generate_vdu_template(tosca, self.name) + vdu.generate_vdu_template(tosca, self.name) + if 'vdu' in self.mgmt_intf and self.mgmt_intf['vdu'] == vdu.get_name(self.name): #TEST + mgmt_interface = {} + mgmt_interface[self.PROPERTIES] = self.mgmt_intf + self.mgmt_intf.pop('vdu') + caps = [] + tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][vdu.get_name(self.name)][self.CAPABILITIES]['mgmt_interface'] = mgmt_interface #TEST + if len(self.mon_param) > 0: + mon_param = {} + mon_param = {} + mon_param['properties'] = self.mon_param[0] + tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][vdu.get_name(self.name)][self.CAPABILITIES]['monitoring_param'] = mon_param #TEST + if len(self.mon_param) == 2: + mon_param = {} + mon_param = {} + mon_param['properties'] = self.mon_param[1] + tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][vdu.get_name(self.name)][self.CAPABILITIES]['monitoring_param_1'] = mon_param node = {} node[self.TYPE] = self.T_VNF1 @@ -340,7 +297,9 @@ class YangVnfd(ToscaResource): self.props.pop(self.DESC) # Update index to the member-vnf-index - self.props[self.ID] = index + + # For now I am putting index as 1. This needs to be revisted + self.props[self.ID] = 1 node[self.PROPERTIES] = self.props caps = {} @@ -379,16 +338,67 @@ class YangVnfd(ToscaResource): self.log.debug(_("{0}, VNF node: {1}").format(self, node)) - tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][self.name] = node + #tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL][self.name] = node + self.get_vnf_configuration_policy(tosca) + + return tosca + + def generate_vld_link(self, virtualLink, conn_point): + if self.REQUIREMENTS not in self.tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING]: + self.tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING] = {} + self.tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING]['node_type'] = self.vnf_type + #self.tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING]['node_type'] = [] + #self.tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING]['node_type'].\ + #append(['node_type', self.vnf_type]) + self.tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING][self.REQUIREMENTS] = [] + + for vdu in self.vdus: + if conn_point in vdu.cp_name_to_cp_node: + conn_point_node_name = vdu.cp_name_to_cp_node[conn_point] + self.tosca[self.TOPOLOGY_TMPL][self.SUBSTITUTION_MAPPING][self.REQUIREMENTS].\ + append({virtualLink : "[{0}, {1}]".format(conn_point_node_name, "virtualLink")}) + + if self.REQUIREMENTS not in self.tosca[self.NODE_TYPES][self.vnf_type]: + self.tosca[self.NODE_TYPES][self.vnf_type][self.REQUIREMENTS] = [] + self.tosca[self.NODE_TYPES][self.vnf_type][self.REQUIREMENTS].append({virtualLink : { + "type": "tosca.nodes.nfv.VL"}}) + + def generate_tosca(self): + tosca = {} return tosca + def get_vnf_configuration_policy(self, tosca): + if self.vnf_configuration: + if self.POLICIES in tosca: + tosca[self.TOPOLOGY_TMPL][self.POLICIES]['configuration'] ={ + 'type' : self.T_VNF_CONFIG, + self.PROPERTIES: self.vnf_configuration + } + else: + tosca[self.TOPOLOGY_TMPL][self.POLICIES] = [] + # This is bad hack. TOSCA Openstack does not return policies without target + if len(tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL]) > 0: + node_name = list(tosca[self.TOPOLOGY_TMPL][self.NODE_TMPL].keys())[0] + tosca[self.TOPOLOGY_TMPL][self.POLICIES].append({'configuration' :{ + 'type' : self.T_VNF_CONFIG, + self.PROPERTIES: self.vnf_configuration, + self.TARGETS : "[{0}]".format(node_name) + }}) + def get_supporting_files(self): files = [] + for file in self.script_files: + files.append({ + self.TYPE: 'script', + self.NAME: file, + self.DEST: "{}/{}".format(self.SCRIPT_DIR, file), + }) + for vdu in self.vdus: - f = vdu.get_supporting_files() - if f and len(f): - files.extend(f) + vdu_files = vdu.get_supporting_files() + for vdu_file in vdu_files: + files.append(vdu_file) return files diff --git a/common/python/rift/mano/yang_translator/rwmano/yang_translator.py b/common/python/rift/mano/yang_translator/rwmano/yang_translator.py index 907a4a0e..09194943 100644 --- a/common/python/rift/mano/yang_translator/rwmano/yang_translator.py +++ b/common/python/rift/mano/yang_translator/rwmano/yang_translator.py @@ -48,24 +48,36 @@ class YangTranslator(object): self.tosca_template = ToscaTemplate(log) self.node_translator = None self.pkgs = packages + self.output_files = {} + self.output_files['nsd'] = [] + self.output_files['vnfd'] = [] + log.info(_('Initialized parameters for translation.')) def translate(self): if self.files: self.get_yangs() + else: + if 'nsd' in self.yangs: + self.output_files['nsd'].append(self.yangs['nsd'][0]['short_name']) + if 'vnfd' in self.yangs: + for yang_vnfd in self.yangs['vnfd']: + self.output_files['vnfd'].append(yang_vnfd['short_name']) self.node_translator = TranslateDescriptors(self.log, self.yangs, - self.tosca_template) - + self.tosca_template, + self.output_files['vnfd']) self.tosca_template.resources = self.node_translator.translate() + return self.tosca_template.output_to_tosca() def get_yangs(self): '''Get the descriptors and convert to yang instances''' for filename in self.files: self.log.debug(_("Load file {0}").format(filename)) + # Only one descriptor per file if tarfile.is_tarfile(filename): tar = open(filename, "r+b") @@ -78,36 +90,70 @@ class YangTranslator(object): self.yangs[TranslateDescriptors.NSD] = [] self.yangs[TranslateDescriptors.NSD]. \ append(pkg.descriptor_msg.as_dict()) + if 'name' in pkg.descriptor_msg.as_dict() is not None: + self.output_files['nsd'].append(pkg.descriptor_msg.as_dict()['name']) + else: + raise ValidationError(message="NSD Descriptor name attribute is not populated ") elif desc_type == TranslateDescriptors.VNFD: if TranslateDescriptors.VNFD not in self.yangs: self.yangs[TranslateDescriptors.VNFD] = [] self.yangs[TranslateDescriptors.VNFD]. \ append(pkg.descriptor_msg.as_dict()) + if 'name' in pkg.descriptor_msg.as_dict() is not None: + self.output_files['vnfd'].append(pkg.descriptor_msg.as_dict()['name']) + else: + raise ValidationError(message="VNFD Descriptor name attribute is not populated ") else: raise ValidationError("Unknown descriptor type: {}". format(desc_type)) - def _create_csar_files(self, output_dir, name, tmpl, + def _create_csar_files(self, output_dir, tmpl_out, archive=False): - if ToscaTemplate.TOSCA not in tmpl: - self.log.error(_("Did not find TOSCA template for {0}"). - format(name)) - return - + ''' + for tmpl in tmpl_out: + if ToscaTemplate.TOSCA not in tmpl: + self.log.error(_("Did not find TOSCA template for {0}"). + format(tmpl)) + return + ''' # Create sub for each NS template - subdir = os.path.join(output_dir, name) + sub_folder_name = None + if self.files: + if len(self.output_files['nsd']) > 0: + if len(self.output_files['nsd']) == 1: + sub_folder_name = self.output_files['nsd'][0] + else: + raise ValidationError(message="Multiple NSD Descriptor uploaded ") + elif len(self.output_files['vnfd']) > 0: + if len(self.output_files['vnfd']) == 1: + sub_folder_name = self.output_files['vnfd'][0] + else: + raise ValidationError(message="Multiple VNFDs Descriptors uploaded without NSD") + else: + raise ValidationError(message="No NSD or VNFD uploaded") + else: + if 'nsd' in self.yangs: + sub_folder_name = self.yangs['nsd'][0]['short_name'] + elif 'vnfd' in self.yangs: + sub_folder_name = self.yangs['vnfd'][0]['short_name'] + + + subdir = os.path.join(output_dir, sub_folder_name) if os.path.exists(subdir): shutil.rmtree(subdir) os.makedirs(subdir) - + riftio_src_file = "{0}{1}".format(os.getenv('RIFT_INSTALL'), "/usr/rift/mano/common/riftiotypes.yaml") # Create the definitions dir def_dir = os.path.join(subdir, 'Definitions') os.makedirs(def_dir) - entry_file = os.path.join(def_dir, name+'.yaml') - self.log.debug(_("Writing file {0}"). - format(entry_file)) - with open(entry_file, 'w+') as f: - f.write(tmpl[ToscaTemplate.TOSCA]) + shutil.copy2(riftio_src_file, def_dir + "/riftiotypes.yaml") + for tmpl_key in tmpl_out: + tmpl = tmpl_out[tmpl_key] + entry_file = os.path.join(def_dir, tmpl_key+'.yaml') + self.log.debug(_("Writing file {0}"). + format(entry_file)) + with open(entry_file, 'w+') as f: + f.write(tmpl[ToscaTemplate.TOSCA]) # Create the Tosca meta meta_dir = os.path.join(subdir, 'TOSCA-Metadata') @@ -116,7 +162,7 @@ class YangTranslator(object): CSAR-Version: 1.1 Created-By: RIFT.io Entry-Definitions: Definitions/''' - meta_data = "{}{}".format(meta, name+'.yaml') + meta_data = "{}{}".format(meta, sub_folder_name+'.yaml') meta_file = os.path.join(meta_dir, 'TOSCA.meta') self.log.debug(_("Writing file {0}:\n{1}"). format(meta_file, meta_data)) @@ -124,53 +170,55 @@ Entry-Definitions: Definitions/''' f.write(meta_data) # Copy other supporting files - if ToscaTemplate.FILES in tmpl: - for f in tmpl[ToscaTemplate.FILES]: - self.log.debug(_("Copy supporting file {0}").format(f)) - - # Search in source packages - if len(self.pkgs): - for pkg in self.pkgs: - # TODO(pjoseph): Need to add support for other file types - fname = f[ToscaResource.NAME] - dest_path = os.path.join(subdir, f[ToscaResource.DEST]) - ftype = f[ToscaResource.TYPE] - - if ftype == 'image': - image_file_map = rift.package.image.get_package_image_files(pkg) - - if fname in image_file_map: - self.log.debug(_("Extracting image {0} to {1}"). - format(fname, dest_path)) - pkg.extract_file(image_file_map[fname], - dest_path) - break - - elif ftype == 'script': - script_file_map = \ - rift.package.script.PackageScriptExtractor.package_script_files(pkg) - if fname in script_file_map: - self.log.debug(_("Extracting script {0} to {1}"). - format(fname, dest_path)) - pkg.extract_file(script_file_map[fname], - dest_path) - break - - elif ftype == 'cloud_init': - script_file_map = \ - rift.package.cloud_init.PackageCloudInitExtractor.package_script_files(pkg) - if fname in script_file_map: - self.log.debug(_("Extracting script {0} to {1}"). - format(fname, dest_path)) - pkg.extract_file(script_file_map[fname], - dest_path) - break - - else: - self.log.warn(_("Unknown file type {0}: {1}"). - format(ftype, f)) - - #TODO(pjoseph): Search in other locations + for key in tmpl_out: + tmpl = tmpl_out[key] + if ToscaTemplate.FILES in tmpl: + for f in tmpl[ToscaTemplate.FILES]: + self.log.debug(_("Copy supporting file {0}").format(f)) + + # Search in source packages + if len(self.pkgs): + for pkg in self.pkgs: + # TODO(pjoseph): Need to add support for other file types + fname = f[ToscaResource.NAME] + dest_path = os.path.join(subdir, f[ToscaResource.DEST]) + ftype = f[ToscaResource.TYPE] + + if ftype == 'image': + image_file_map = rift.package.image.get_package_image_files(pkg) + + if fname in image_file_map: + self.log.debug(_("Extracting image {0} to {1}"). + format(fname, dest_path)) + pkg.extract_file(image_file_map[fname], + dest_path) + break + + elif ftype == 'script': + script_file_map = \ + rift.package.script.PackageScriptExtractor.package_script_files(pkg) + if fname in script_file_map: + self.log.debug(_("Extracting script {0} to {1}"). + format(fname, dest_path)) + pkg.extract_file(script_file_map[fname], + dest_path) + break + + elif ftype == 'cloud_init': + script_file_map = \ + rift.package.cloud_init.PackageCloudInitExtractor.package_script_files(pkg) + if fname in script_file_map: + self.log.debug(_("Extracting script {0} to {1}"). + format(fname, dest_path)) + pkg.extract_file(script_file_map[fname], + dest_path) + break + + else: + self.log.warn(_("Unknown file type {0}: {1}"). + format(ftype, f)) + + #TODO(pjoseph): Search in other locations # Create the ZIP archive if archive: @@ -178,7 +226,7 @@ Entry-Definitions: Definitions/''' os.chdir(subdir) try: - zip_file = name + '.zip' + zip_file = key + '.zip' zip_path = os.path.join(output_dir, zip_file) self.log.debug(_("Creating zip file {0}").format(zip_path)) zip_cmd = "zip -r {}.partial ." @@ -207,14 +255,12 @@ Entry-Definitions: Definitions/''' archive=False,): if output: zip_files = [] - for key in output.keys(): - if output_dir: - zf = self._create_csar_files(output_dir, - key, - output[key], - archive=archive,) - zip_files.append(zf) - else: - print(_("TOSCA Template {0}:\n{1}"). - format(key, output[key])) + #for key in output.keys(): + if output_dir: + zf = self._create_csar_files(output_dir, + output, + archive=archive,) + zip_files.append(zf) + else: + print(_("There is an issue with TOSCA Template")) return zip_files diff --git a/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/export.py b/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/export.py index 7fa61301..ff6a3730 100644 --- a/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/export.py +++ b/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/export.py @@ -326,11 +326,6 @@ class ExportRpcHandler(mano_dts.AbstractRpcHandler): if format_ != "yaml": log.warn("Only yaml format supported for TOSCA export") - if desc_type != "nsd": - raise tornado.web.HTTPError( - 400, - "NSD need to passed to generate TOSCA: {}".format(desc_type)) - def get_pkg_from_store(id_, type_): package = None # Attempt to get the package from the package store @@ -345,27 +340,38 @@ class ExportRpcHandler(mano_dts.AbstractRpcHandler): return package - pkg = tosca.ExportTosca() - - # Add NSD and related descriptors for exporting - nsd_id = pkg.add_nsd(desc_msg, get_pkg_from_store(desc_id, "nsd")) - - catalog = self.catalog_map["vnfd"] - for const_vnfd in desc_msg.constituent_vnfd: - vnfd_id = const_vnfd.vnfd_id_ref - if vnfd_id in catalog: - pkg.add_vnfd(nsd_id, - catalog[vnfd_id], - get_pkg_from_store(vnfd_id, "vnfd")) - else: - raise tornado.web.HTTPError( - 400, - "Unknown VNFD descriptor {} for NSD {}". - format(vnfd_id, nsd_id)) - - # Create the archive. - pkg.create_archive(transaction_id, - dest=self.application.export_dir) + if desc_type == "nsd": + pkg = tosca.ExportTosca() + + # Add NSD and related descriptors for exporting + nsd_id = pkg.add_nsd(desc_msg, get_pkg_from_store(desc_id, "nsd")) + + catalog = self.catalog_map["vnfd"] + for const_vnfd in desc_msg.constituent_vnfd: + vnfd_id = const_vnfd.vnfd_id_ref + if vnfd_id in catalog: + pkg.add_vnfd(nsd_id, + catalog[vnfd_id], + get_pkg_from_store(vnfd_id, "vnfd")) + else: + raise tornado.web.HTTPError( + 400, + "Unknown VNFD descriptor {} for NSD {}". + format(vnfd_id, nsd_id)) + + # Create the archive. + pkg.create_archive(transaction_id, + dest=self.application.export_dir) + if desc_type == "vnfd": + pkg = tosca.ExportTosca() + vnfd_id = desc_msg.id + pkg.add_single_vnfd(vnfd_id, + desc_msg, + get_pkg_from_store(vnfd_id, "vnfd")) + + # Create the archive. + pkg.create_archive(transaction_id, + dest=self.application.export_dir) class ExportStateHandler(state.StateHandler): diff --git a/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/tosca.py b/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/tosca.py index 8ccc899f..d61e47e8 100644 --- a/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/tosca.py +++ b/rwlaunchpad/plugins/rwlaunchpadtasklet/rift/tasklets/rwlaunchpad/tosca.py @@ -61,6 +61,7 @@ class ExportTosca(object): self.log = log self.nsds = {} self.csars = list() + self.vnfds = {} def add_image(self, nsd_id, image, chksum=None): if image.name not in self.images: @@ -73,6 +74,14 @@ class ExportTosca(object): if pkg: self.nsds[nsd_id]['pkgs'].append(pkg) + def add_single_vnfd(self, vnfd_id, vnfd, pkg=None): + if vnfd is not None: + self.vnfds['vnfds'] = [] + self.vnfds['pkgs'] = [] + self.vnfds['vnfds'].append(vnfd) + if pkg: + self.vnfds['pkgs'].append(pkg) + def add_vnfd(self, nsd_id, vnfd, pkg=None): if not 'vnfds' in self.nsds[nsd_id]: self.nsds[nsd_id]['vnfds'] = [] @@ -112,8 +121,25 @@ class ExportTosca(object): archive=True)) self.log.debug("Created CSAR archive {}".format(self.csars[-1])) + def create_vnfd_csar(self, dest=None): + if dest is None: + dest = tempfile.mkdtemp() + yangs = {} + yangs['vnfd'] = [] + for vnfd in self.vnfds['vnfds']: + yangs['vnfd'].append(vnfd.as_dict()) + translator = YangTranslator(self.log, + yangs=yangs, + packages=self.vnfds['pkgs']) + output = translator.translate() + self.csars.extend(translator.write_output(output, + output_dir=dest, + archive=True)) + self.log.debug("Created CSAR archive {}".format(self.csars[-1])) + + def create_archive(self, archive_name, dest=None): - if not len(self.nsds): + if not len(self.nsds) and len(self.vnfds) == 0: self.log.error("Did not find any NSDs to export") return @@ -127,13 +153,16 @@ class ExportTosca(object): try: # Convert each NSD to a TOSCA template - for nsd_id in self.nsds: - # Not passing the dest dir to prevent clash in case - # multiple export of the same desc happens - self.create_csar(nsd_id) + if len(self.nsds) > 0: + for nsd_id in self.nsds: + # Not passing the dest dir to prevent clash in case + # multiple export of the same desc happens + self.create_csar(nsd_id) + elif len(self.vnfds) > 0: + self.create_vnfd_csar() except Exception as e: - msg = "Exception converting NSD {}: {}".format(nsd_id, e) + msg = "Exception converting NSD/VNFD {}".format(e) self.log.exception(e) raise YangTranslateNsdError(msg) -- 2.25.1