4 # Copyright 2016-2017 RIFT.IO Inc
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
10 # http://www.apache.org/licenses/LICENSE-2.0
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
28 from xml
.dom
.minidom
import parseString
31 gi
.require_version('RwYang', '1.0')
32 gi
.require_version('RwProjectYang', '1.0')
33 gi
.require_version('RwVnfdYang', '1.0')
34 gi
.require_version('VnfdYang', '1.0')
35 gi
.require_version('RwNsdYang', '1.0')
36 gi
.require_version('NsdYang', '1.0')
39 from gi
.repository
import (
50 import rift
.mano
.config_data
.config
as config_data
52 # Load modules from common which are not yet installed
53 path
= os
.path
.abspath(os
.path
.dirname(os
.path
.abspath(__file__
)) + "../../../common/python/rift/mano")
56 import config_data
.config
as config_data
59 NUM_PING_INSTANCES
= 1
60 MAX_VNF_INSTANCES_PER_NS
= 10
63 pingcount
= NUM_PING_INSTANCES
64 use_ping_cloud_init_file
= ""
65 use_pong_cloud_init_file
= ""
67 PING_USERDATA_FILE
= '''#cloud-config
69 chpasswd: { expire: False }
72 - [ systemctl, daemon-reload ]
73 - [ systemctl, enable, ping.service ]
74 - [ systemctl, start, --no-block, ping.service ]
78 PONG_USERDATA_FILE
= '''#cloud-config
80 chpasswd: { expire: False }
83 - [ systemctl, daemon-reload ]
84 - [ systemctl, enable, pong.service ]
85 - [ systemctl, start, --no-block, pong.service ]
90 class UnknownVNFError(Exception):
94 class ManoDescriptor(object):
95 def __init__(self
, name
):
97 self
.descriptor
= None
99 def write_to_file(self
, module_list
, outdir
, output_format
):
100 model
= RwYang
.Model
.create_libncx()
101 for module
in module_list
:
102 model
.load_module(module
)
104 # Need to extract the catalog part to dump as descriptor
105 if output_format
in ['json', 'yaml']:
106 # Convert to yaml instead of directly using as_dict as
107 # this adds the namespaces correctly
108 ya
= self
.descriptor
.to_yaml(model
)
110 desc
= proj
['rw-project:project'][0]
112 if output_format
== 'json':
113 with
open('%s/%s.json' % (outdir
, self
.name
), "w") as fh
:
114 fh
.write(simplejson
.dumps(desc
, indent
=4))
115 elif output_format
== 'yaml':
116 with
open('%s/%s.yaml' % (outdir
, self
.name
), "w") as fh
:
117 fh
.write(yaml
.dump(desc
, default_flow_style
=False))
118 elif output_format
== 'xml':
119 # Converting from dict to xml does not provide the
120 # required output. So using the PBCM to_xml and then
121 # printing only from the catalog tag.
122 with
open('%s/%s.xml' % (outdir
, self
.name
), "w") as fh
:
123 proj
= self
.descriptor
.to_xml_v2(model
)
124 dom
= parseString(proj
)
125 desc
= dom
.getElementsByTagName("vnfd:vnfd-catalog")
127 desc
= dom
.getElementsByTagName("nsd:nsd-catalog")
128 fh
.write(desc
[0].toprettyxml())
130 raise Exception("Invalid output format for the descriptor")
132 def get_json(self
, module_list
):
133 model
= RwYang
.Model
.create_libncx()
134 for module
in module_list
:
135 model
.load_module(module
)
136 print(self
.descriptor
.to_json(model
))
139 class VirtualNetworkFunction(ManoDescriptor
):
140 def __init__(self
, name
, instance_count
=1):
141 self
.vnfd_catalog
= None
143 self
.instance_count
= instance_count
144 self
._placement
_groups
= []
145 self
.use_vnf_init_conf
= False
146 super(VirtualNetworkFunction
, self
).__init
__(name
)
148 def add_placement_group(self
, group
):
149 self
._placement
_groups
.append(group
)
151 def compose(self
, image_name
, cloud_init
="", cloud_init_file
="", endpoint
=None, mon_params
=[],
152 mon_port
=8888, mgmt_port
=8888, num_vlr_count
=1, num_ivlr_count
=1,
153 num_vms
=1, image_md5sum
=None, mano_ut
=False):
154 self
.descriptor
= RwVnfdYang
.YangData_RwProject_Project_VnfdCatalog()
155 self
.id = str(uuid
.uuid1())
156 vnfd
= self
.descriptor
.vnfd
.add()
158 vnfd
.name
= self
.name
159 vnfd
.short_name
= self
.name
160 vnfd
.vendor
= 'RIFT.io'
161 vnfd
.logo
= 'rift_logo.png'
162 vnfd
.description
= 'This is an example RIFT.ware VNF'
169 for i
in range(num_ivlr_count
):
170 internal_vld
= vnfd
.internal_vld
.add()
171 internal_vld
.id = 'ivld%s' % i
172 internal_vld
.name
= 'fabric%s' % i
173 internal_vld
.short_name
= 'fabric%s' % i
174 internal_vld
.description
= 'Virtual link for internal fabric%s' % i
175 internal_vld
.type_yang
= 'ELAN'
176 internal_vlds
.append(internal_vld
)
178 for i
in range(num_vlr_count
):
179 cp
= vnfd
.connection_point
.add()
180 cp
.type_yang
= 'VPORT'
181 cp
.name
= '%s/cp%d' % (self
.name
, i
)
183 if endpoint
is not None:
184 endp
= VnfdYang
.YangData_RwProject_Project_VnfdCatalog_Vnfd_HttpEndpoint(
185 path
=endpoint
, port
=mon_port
, polling_interval_secs
=2
187 vnfd
.http_endpoint
.append(endp
)
190 for monp_dict
in mon_params
:
191 monp
= VnfdYang
.YangData_RwProject_Project_VnfdCatalog_Vnfd_MonitoringParam
.from_dict(monp_dict
)
192 monp
.http_endpoint_ref
= endpoint
193 vnfd
.monitoring_param
.append(monp
)
196 for i
in range(num_vms
):
199 vdu
.id = 'iovdu_%s' % i
200 vdu
.name
= 'iovdu_%s' % i
202 # vdu.mgmt_vpci = '0000:00:20.0'
204 # specify the VM flavor
206 vdu
.vm_flavor
.vcpu_count
= 4
207 vdu
.vm_flavor
.memory_mb
= 1024
208 vdu
.vm_flavor
.storage_gb
= 4
210 vdu
.vm_flavor
.vcpu_count
= 1
211 vdu
.vm_flavor
.memory_mb
= 512
212 vdu
.vm_flavor
.storage_gb
= 4
214 # Management interface
215 mgmt_intf
= vnfd
.mgmt_interface
216 mgmt_intf
.vdu_id
= vdu
.id
217 mgmt_intf
.port
= mgmt_port
218 mgmt_intf
.dashboard_params
.path
= endpoint
219 mgmt_intf
.dashboard_params
.port
= mgmt_port
221 if cloud_init_file
and len(cloud_init_file
):
222 vdu
.cloud_init_file
= cloud_init_file
224 vdu
.cloud_init
= cloud_init
226 vdu
.cloud_init
+= " - [ systemctl, restart, --no-block, elastic-network-interfaces.service ]\n"
228 # sepcify the guest EPA
230 vdu
.guest_epa
.trusted_execution
= False
231 vdu
.guest_epa
.mempage_size
= 'LARGE'
232 vdu
.guest_epa
.cpu_pinning_policy
= 'DEDICATED'
233 vdu
.guest_epa
.cpu_thread_pinning_policy
= 'PREFER'
234 vdu
.guest_epa
.numa_node_policy
.node_cnt
= 2
235 vdu
.guest_epa
.numa_node_policy
.mem_policy
= 'STRICT'
237 node
= vdu
.guest_epa
.numa_node_policy
.node
.add()
240 vcpu
= node
.vcpu
.add()
242 vcpu
= node
.vcpu
.add()
245 node
= vdu
.guest_epa
.numa_node_policy
.node
.add()
248 vcpu
= node
.vcpu
.add()
250 vcpu
= node
.vcpu
.add()
253 # specify the vswitch EPA
254 vdu
.vswitch_epa
.ovs_acceleration
= 'DISABLED'
255 vdu
.vswitch_epa
.ovs_offload
= 'DISABLED'
257 # Specify the hypervisor EPA
258 vdu
.hypervisor_epa
.type_yang
= 'PREFER_KVM'
260 # Specify the host EPA
261 # vdu.host_epa.cpu_model = 'PREFER_SANDYBRIDGE'
262 # vdu.host_epa.cpu_arch = 'PREFER_X86_64'
263 # vdu.host_epa.cpu_vendor = 'PREFER_INTEL'
264 # vdu.host_epa.cpu_socket_count = 2
265 # vdu.host_epa.cpu_core_count = 8
266 # vdu.host_epa.cpu_core_thread_count = 2
267 # vdu.host_epa.cpu_feature = ['PREFER_AES', 'REQUIRE_VME', 'PREFER_MMX','REQUIRE_SSE2']
270 vdu
.image
= 'rift-ping-pong'
272 vdu
.image
= image_name
273 if image_md5sum
is not None:
274 vdu
.image_checksum
= image_md5sum
277 for i
in range(num_ivlr_count
):
278 internal_cp
= vdu
.internal_connection_point
.add()
279 if vnfd
.name
.find("ping") >= 0:
283 internal_cp
.name
= cp_name
+ "/icp{}".format(i
)
284 internal_cp
.id = cp_name
+ "/icp{}".format(i
)
285 internal_cp
.type_yang
= 'VPORT'
286 ivld_cp
= internal_vlds
[i
].internal_connection_point
.add()
287 ivld_cp
.id_ref
= internal_cp
.id
289 internal_interface
= vdu
.internal_interface
.add()
290 internal_interface
.name
= 'fab%d' % i
291 internal_interface
.vdu_internal_connection_point_ref
= internal_cp
.id
292 internal_interface
.virtual_interface
.type_yang
= 'VIRTIO'
294 # internal_interface.virtual_interface.vpci = '0000:00:1%d.0'%i
296 for i
in range(num_vlr_count
):
297 external_interface
= vdu
.external_interface
.add()
298 external_interface
.name
= 'eth%d' % i
299 external_interface
.vnfd_connection_point_ref
= '%s/cp%d' % (self
.name
, i
)
301 external_interface
.virtual_interface
.type_yang
= 'VIRTIO'
303 external_interface
.virtual_interface
.type_yang
= 'VIRTIO'
304 # external_interface.virtual_interface.vpci = '0000:00:2%d.0'%i
306 for group
in self
._placement
_groups
:
307 placement_group
= vnfd
.placement_groups
.add()
308 placement_group
.name
= group
.name
309 placement_group
.requirement
= group
.requirement
310 placement_group
.strategy
= group
.strategy
312 ### Add specific VDUs to placement group
313 for vdu
in group
.vdu_list
:
314 member_vdu
= placement_group
.member_vdus
.add()
315 member_vdu
.member_vdu_ref
= vdu
.id
317 ### Add all VDUs to placement group
319 member_vdu
= placement_group
.member_vdus
.add()
320 member_vdu
.member_vdu_ref
= vdu
.id
323 def write_to_file(self
, outdir
, output_format
):
324 dirpath
= "%s/%s" % (outdir
, self
.name
)
325 if not os
.path
.exists(dirpath
):
327 super(VirtualNetworkFunction
, self
).write_to_file(['rw-project', 'vnfd', 'rw-vnfd'],
330 self
.add_scripts(outdir
)
332 def add_scripts(self
, outdir
):
333 script_dir
= os
.path
.join(outdir
, self
.name
, 'cloud_init')
335 os
.makedirs(script_dir
)
337 if not os
.path
.isdir(script_dir
):
340 if 'ping' in self
.name
:
341 script_file
= os
.path
.join(script_dir
, 'ping_cloud_init.cfg')
342 cfg
= PING_USERDATA_FILE
344 script_file
= os
.path
.join(script_dir
, 'pong_cloud_init.cfg')
345 cfg
= PONG_USERDATA_FILE
347 with
open(script_file
, "w") as f
:
348 f
.write("{}".format(cfg
))
350 # Copy the vnf_init_config script
351 if self
.use_vnf_init_conf
and ('ping' in self
.name
):
352 script_name
= 'ping_set_rate.py'
354 src_path
= os
.path
.dirname(os
.path
.abspath(os
.path
.realpath(__file__
)))
355 script_src
= os
.path
.join(src_path
, script_name
)
356 if not os
.path
.exists(script_src
):
357 src_path
= os
.path
.join(os
.environ
['RIFT_ROOT'],
358 'modules/core/mano/examples/ping_pong_ns/rift/mano/examples')
359 script_src
= os
.path
.join(src_path
, script_name
)
361 dest_path
= os
.path
.join(outdir
, self
.name
, 'scripts')
362 os
.makedirs(dest_path
, exist_ok
=True)
364 shutil
.copy2(script_src
, dest_path
)
367 class NetworkService(ManoDescriptor
):
368 def __init__(self
, name
):
369 super(NetworkService
, self
).__init
__(name
)
370 self
._scale
_groups
= []
371 self
.vnfd_config
= {}
372 self
._placement
_groups
= []
374 def ping_config(self
, mano_ut
, use_ns_init_conf
, use_vnf_init_conf
):
380 echo "!!!!!!!! Executed ping Configuration !!!!!!!!!"
387 ping_mgmt_ip='<rw_mgmt_ip>'
390 # VNF specific configuration
391 pong_server_ip='<rw_connection_point_name pong_vnfd%s/cp0>'
395 # Make rest API calls to configure VNF
396 curl -D /dev/stdout \
397 -H "Accept: application/vnd.yang.data+xml" \
398 -H "Content-Type: application/vnd.yang.data+json" \
400 -d "{\"ip\":\"$pong_server_ip\", \"port\":$server_port}" \
401 http://${ping_mgmt_ip}:${ping_mgmt_port}/api/v1/ping/server
405 echo "Failed to set server info for ping!"
410 if use_vnf_init_conf
is False:
412 curl -D /dev/stdout \
413 -H "Accept: application/vnd.yang.data+xml" \
414 -H "Content-Type: application/vnd.yang.data+json" \
416 -d "{\"rate\":$ping_rate}" \
417 http://${ping_mgmt_ip}:${ping_mgmt_port}/api/v1/ping/rate
421 echo "Failed to set ping rate!"
427 ping_cfg
+= "exit 0\n"
430 output=$(curl -D /dev/stdout \
431 -H "Accept: application/vnd.yang.data+xml" \
432 -H "Content-Type: application/vnd.yang.data+json" \
434 -d "{\"enable\":true}" \
435 http://${ping_mgmt_ip}:${ping_mgmt_port}/api/v1/ping/adminstatus/state)
436 if [[ $output == *"Internal Server Error"* ]]
448 def pong_config(self
, mano_ut
, use_ns_init_conf
):
454 echo "!!!!!!!! Executed pong Configuration !!!!!!!!!"
460 # Rest API configuration
461 pong_mgmt_ip='<rw_mgmt_ip>'
463 # username=<rw_username>
464 # password=<rw_password>
466 # VNF specific configuration
467 pong_server_ip='<rw_connection_point_name pong_vnfd%s/cp0>'
470 # Make Rest API calls to configure VNF
471 curl -D /dev/stdout \
472 -H "Accept: application/vnd.yang.data+xml" \
473 -H "Content-Type: application/vnd.yang.data+json" \
475 -d "{\"ip\":\"$pong_server_ip\", \"port\":$server_port}" \
476 http://${pong_mgmt_ip}:${pong_mgmt_port}/api/v1/pong/server
480 echo "Failed to set server(own) info for pong!"
487 pong_cfg
+= "exit 0\n"
490 curl -D /dev/stdout \
491 -H "Accept: application/vnd.yang.data+xml" \
492 -H "Content-Type: application/vnd.yang.data+json" \
494 -d "{\"enable\":true}" \
495 http://${pong_mgmt_ip}:${pong_mgmt_port}/api/v1/pong/adminstatus/state
499 echo "Failed to enable pong service!"
507 def pong_fake_juju_config(self
, vnf_config
):
510 # Select "script" configuration
511 vnf_config
.juju
.charm
= 'clearwater-aio-proxy'
513 # Set the initital-config
514 vnf_config
.create_initial_config_primitive()
515 init_config
= VnfdYang
.InitialConfigPrimitive
.from_dict({
519 {"name": "proxied_ip", "value": "<rw_mgmt_ip>"},
522 vnf_config
.initial_config_primitive
.append(init_config
)
524 init_config_action
= VnfdYang
.InitialConfigPrimitive
.from_dict({
528 {"name": "Pong Connection Point", "value": "pong_vnfd/cp0"},
531 vnf_config
.initial_config_primitive
.append(init_config_action
)
532 init_config_action
= VnfdYang
.InitialConfigPrimitive
.from_dict({
536 {"name": "Ping Connection Point", "value": "ping_vnfd/cp0"},
539 vnf_config
.initial_config_primitive
.append(init_config_action
)
541 # Config parameters can be taken from config.yaml and
542 # actions from actions.yaml in the charm
543 # Config to set the home domain
544 vnf_config
.create_service_primitive()
545 config
= VnfdYang
.ServicePrimitive
.from_dict({
548 {"name": "home_domain", "data_type": "STRING"},
549 {"name": "base_number", "data_type": "STRING"},
550 {"name": "number_count", "data_type": "INTEGER"},
551 {"name": "password", "data_type": "STRING"},
554 vnf_config
.service_primitive
.append(config
)
556 config
= VnfdYang
.ServicePrimitive
.from_dict({
557 "name": "create-update-user",
558 # "user-defined-script":"/tmp/test.py",
560 {"name": "number", "data_type": "STRING", "mandatory": True},
561 {"name": "password", "data_type": "STRING", "mandatory": True},
564 vnf_config
.service_primitive
.append(config
)
566 config
= VnfdYang
.ServicePrimitive
.from_dict({
567 "name": "delete-user",
569 {"name": "number", "data_type": "STRING", "mandatory": True},
572 vnf_config
.service_primitive
.append(config
)
574 def default_config(self
, const_vnfd
, vnfd
, mano_ut
,
577 vnf_config
= vnfd
.vnfd
.vnf_configuration
579 vnf_config
.config_attributes
.config_priority
= 0
580 vnf_config
.config_attributes
.config_delay
= 0
582 # Select "script" configuration
583 vnf_config
.script
.script_type
= 'bash'
585 if vnfd
.name
== 'pong_vnfd' or vnfd
.name
== 'pong_vnfd_with_epa' or vnfd
.name
== 'pong_vnfd_aws':
586 vnf_config
.config_attributes
.config_priority
= 1
587 vnf_config
.config_template
= self
.pong_config(mano_ut
, use_ns_init_conf
)
588 # First priority config delay will delay the entire NS config delay
590 vnf_config
.config_attributes
.config_delay
= 60
592 # This is PONG and inside mano_ut
594 vnf_config
.config_attributes
.config_delay
= 10
595 # vnf_config.config_template = self.pong_config(vnf_config, use_ns_init_conf)
597 if vnfd
.name
== 'ping_vnfd' or vnfd
.name
== 'ping_vnfd_with_epa' or vnfd
.name
== 'ping_vnfd_aws':
598 vnf_config
.config_attributes
.config_priority
= 2
599 vnf_config
.config_template
= self
.ping_config(mano_ut
,
602 if use_vnf_init_conf
:
603 vnf_config
.initial_config_primitive
.add().from_dict(
606 "name": "set ping rate",
607 "user_defined_script": "ping_set_rate.py",
617 def ns_config(self
, nsd
, vnfd_list
, mano_ut
):
618 # Used by scale group
620 nsd
.service_primitive
.add().from_dict(
622 "name": "ping config",
623 "user_defined_script": "{}".format(os
.path
.join(
624 os
.environ
['RIFT_ROOT'],
626 'examples/ping_pong_ns/rift/mano/examples',
627 'ping_config_ut.sh'))
630 nsd
.service_primitive
.add().from_dict(
632 "name": "ping config",
633 "user_defined_script": "ping_config.py"
636 def ns_initial_config(self
, nsd
):
637 nsd
.initial_config_primitive
.add().from_dict(
640 "name": "start traffic",
641 "user_defined_script": "start_traffic.py",
651 def add_scale_group(self
, scale_group
):
652 self
._scale
_groups
.append(scale_group
)
654 def add_placement_group(self
, placement_group
):
655 self
._placement
_groups
.append(placement_group
)
657 def create_mon_params(self
, vnfds
):
658 NsdMonParam
= NsdYang
.YangData_RwProject_Project_NsdCatalog_Nsd_MonitoringParam
660 for vnfd_obj
in vnfds
:
661 for mon_param
in vnfd_obj
.vnfd
.monitoring_param
:
662 nsd_monp
= NsdMonParam
.from_dict({
664 'name': mon_param
.name
,
665 'aggregation_type': "AVERAGE",
666 'value_type': mon_param
.value_type
,
667 'vnfd_monitoring_param': [
668 {'vnfd_id_ref': vnfd_obj
.vnfd
.id,
669 'vnfd_monitoring_param_ref': mon_param
.id}]
672 self
.nsd
.monitoring_param
.append(nsd_monp
)
678 def compose(self
, vnfd_list
, cpgroup_list
, mano_ut
,
679 use_ns_init_conf
=True,
680 use_vnf_init_conf
=True,):
683 # Disable NS initial config primitive
684 use_ns_init_conf
= False
685 use_vnf_init_conf
= False
687 self
.descriptor
= RwNsdYang
.YangData_RwProject_Project_NsdCatalog()
688 self
.id = str(uuid
.uuid1())
689 nsd
= self
.descriptor
.nsd
.add()
693 nsd
.short_name
= self
.name
694 nsd
.vendor
= 'RIFT.io'
695 nsd
.logo
= 'rift_logo.png'
696 nsd
.description
= 'Toy NS'
698 nsd
.input_parameter_xpath
.append(
699 NsdYang
.YangData_RwProject_Project_NsdCatalog_Nsd_InputParameterXpath(
700 xpath
="/nsd:nsd-catalog/nsd:nsd/nsd:vendor",
704 ip_profile
= nsd
.ip_profiles
.add()
705 ip_profile
.name
= "InterVNFLink"
706 ip_profile
.description
= "Inter VNF Link"
707 ip_profile
.ip_profile_params
.ip_version
= "ipv4"
708 ip_profile
.ip_profile_params
.subnet_address
= "31.31.31.0/24"
709 ip_profile
.ip_profile_params
.gateway_address
= "31.31.31.210"
712 for cpgroup
in cpgroup_list
:
714 vld
.id = 'ping_pong_vld%s' % vld_id
716 vld
.name
= 'ping_pong_vld' # hard coded
717 vld
.short_name
= vld
.name
718 vld
.vendor
= 'RIFT.io'
719 vld
.description
= 'Toy VL'
721 vld
.type_yang
= 'ELAN'
722 vld
.ip_profile_ref
= 'InterVNFLink'
724 cpref
= vld
.vnfd_connection_point_ref
.add()
725 cpref
.member_vnf_index_ref
= str(cp
[0])
726 cpref
.vnfd_id_ref
= cp
[1]
727 cpref
.vnfd_connection_point_ref
= cp
[2]
731 for vnfd
in vnfd_list
:
732 for i
in range(vnfd
.instance_count
):
733 constituent_vnfd
= nsd
.constituent_vnfd
.add()
734 constituent_vnfd
.member_vnf_index
= member_vnf_index
735 vnfd_index_map
[vnfd
] = member_vnf_index
737 # Set the start by default to false for ping vnfd,
738 # if scaling is enabled
739 if (len(self
._scale
_groups
) and
740 vnfd
.descriptor
.vnfd
[0].name
== 'ping_vnfd'):
741 constituent_vnfd
.start_by_default
= False
743 constituent_vnfd
.vnfd_id_ref
= vnfd
.descriptor
.vnfd
[0].id
744 self
.default_config(constituent_vnfd
, vnfd
, mano_ut
,
745 use_ns_init_conf
, use_vnf_init_conf
)
746 member_vnf_index
+= 1
748 # Enable config primitives if either mano_ut or
749 # scale groups are enabled
750 if mano_ut
or len(self
._scale
_groups
):
751 self
.ns_config(nsd
, vnfd_list
, mano_ut
)
753 # Add NS initial config to start traffic
755 self
.ns_initial_config(nsd
)
757 for scale_group
in self
._scale
_groups
:
758 group_desc
= nsd
.scaling_group_descriptor
.add()
759 group_desc
.name
= scale_group
.name
760 group_desc
.max_instance_count
= scale_group
.max_count
761 group_desc
.min_instance_count
= scale_group
.min_count
762 for vnfd
, count
in scale_group
.vnfd_count_map
.items():
763 member
= group_desc
.vnfd_member
.add()
764 member
.member_vnf_index_ref
= str(vnfd_index_map
[vnfd
])
767 for trigger
in scale_group
.config_action
:
768 config_action
= group_desc
.scaling_config_action
.add()
769 config_action
.trigger
= trigger
770 config
= scale_group
.config_action
[trigger
]
771 config_action
.ns_config_primitive_name_ref
= config
['ns-config-primitive-name-ref']
773 for placement_group
in self
._placement
_groups
:
774 group
= nsd
.placement_groups
.add()
775 group
.name
= placement_group
.name
776 group
.strategy
= placement_group
.strategy
777 group
.requirement
= placement_group
.requirement
778 for member_vnfd
in placement_group
.vnfd_list
:
779 member
= group
.member_vnfd
.add()
780 member
.vnfd_id_ref
= member_vnfd
.descriptor
.vnfd
[0].id
781 member
.member_vnf_index_ref
= str(vnfd_index_map
[member_vnfd
])
783 # self.create_mon_params(vnfd_list)
785 def write_config(self
, outdir
, vnfds
):
787 converter
= config_data
.ConfigPrimitiveConvertor()
788 yaml_data
= converter
.extract_nsd_config(self
.nsd
)
790 ns_config_dir
= os
.path
.join(outdir
, self
.name
, "ns_config")
791 os
.makedirs(ns_config_dir
, exist_ok
=True)
792 vnf_config_dir
= os
.path
.join(outdir
, self
.name
, "vnf_config")
793 os
.makedirs(vnf_config_dir
, exist_ok
=True)
796 with
open('%s/%s.yaml' % (ns_config_dir
, self
.id), "w") as fh
:
799 for i
, vnfd
in enumerate(vnfds
, start
=1):
800 yaml_data
= converter
.extract_vnfd_config(vnfd
)
803 with
open('%s/%s__%s.yaml' % (vnf_config_dir
, vnfd
.id, i
), "w") as fh
:
806 def write_initial_config_script(self
, outdir
):
807 script_name
= 'start_traffic.py'
809 src_path
= os
.path
.dirname(os
.path
.abspath(os
.path
.realpath(__file__
)))
810 script_src
= os
.path
.join(src_path
, script_name
)
811 if not os
.path
.exists(script_src
):
812 src_path
= os
.path
.join(os
.environ
['RIFT_ROOT'],
813 'modules/core/mano/examples/ping_pong_ns/rift/mano/examples')
814 script_src
= os
.path
.join(src_path
, script_name
)
816 dest_path
= os
.path
.join(outdir
, 'scripts')
817 os
.makedirs(dest_path
, exist_ok
=True)
819 shutil
.copy2(script_src
, dest_path
)
821 def write_to_file(self
, outdir
, output_format
):
822 dirpath
= os
.path
.join(outdir
, self
.name
)
823 if not os
.path
.exists(dirpath
):
826 super(NetworkService
, self
).write_to_file(["rw-project", "nsd", "rw-nsd"],
830 # Write the initial config script
831 self
.write_initial_config_script(dirpath
)
834 def get_ping_mon_params(path
):
838 'name': 'ping-request-tx-count',
839 'http_endpoint_ref': path
,
840 'json_query_method': "NAMEKEY",
842 'description': 'no of ping requests',
843 'group_tag': 'Group1',
844 'widget_type': 'COUNTER',
850 'name': 'ping-response-rx-count',
851 'http_endpoint_ref': path
,
852 'json_query_method': "NAMEKEY",
854 'description': 'no of ping responses',
855 'group_tag': 'Group1',
856 'widget_type': 'COUNTER',
862 def get_pong_mon_params(path
):
866 'name': 'ping-request-rx-count',
867 'http_endpoint_ref': path
,
868 'json_query_method': "NAMEKEY",
870 'description': 'no of ping requests',
871 'group_tag': 'Group1',
872 'widget_type': 'COUNTER',
878 'name': 'ping-response-tx-count',
879 'http_endpoint_ref': path
,
880 'json_query_method': "NAMEKEY",
882 'description': 'no of ping responses',
883 'group_tag': 'Group1',
884 'widget_type': 'COUNTER',
890 class ScaleGroup(object):
891 def __init__(self
, name
, min_count
=1, max_count
=1):
893 self
.min_count
= min_count
894 self
.max_count
= max_count
895 self
.vnfd_count_map
= {}
896 self
.config_action
= {}
898 def add_vnfd(self
, vnfd
, vnfd_count
):
899 self
.vnfd_count_map
[vnfd
] = vnfd_count
901 def add_config(self
):
902 self
.config_action
['post_scale_out']= {'ns-config-primitive-name-ref':
905 class PlacementGroup(object):
906 def __init__(self
, name
):
909 self
.requirement
= ''
911 def add_strategy(self
, strategy
):
912 self
.strategy
= strategy
914 def add_requirement(self
, requirement
):
915 self
.requirement
= requirement
917 class NsdPlacementGroup(PlacementGroup
):
918 def __init__(self
, name
):
920 super(NsdPlacementGroup
, self
).__init
__(name
)
922 def add_member(self
, vnfd
):
923 self
.vnfd_list
.append(vnfd
)
926 class VnfdPlacementGroup(PlacementGroup
):
927 def __init__(self
, name
):
929 super(VnfdPlacementGroup
, self
).__init
__(name
)
931 def add_member(self
, vdu
):
932 self
.vdu_list
.append(vdu
)
937 def generate_ping_pong_descriptors(fmt
="json",
940 pingcount
=NUM_PING_INSTANCES
,
941 external_vlr_count
=1,
942 internal_vlr_count
=1,
947 use_scale_group
=False,
954 ex_ping_userdata
=None,
955 ex_pong_userdata
=None,
956 use_placement_group
=True,
957 use_ns_init_conf
=True,
958 use_vnf_init_conf
=True,
960 # List of connection point groups
961 # Each connection point group refers to a virtual link
962 # the CP group consists of tuples of connection points
964 for i
in range(external_vlr_count
):
965 cpgroup_list
.append([])
968 ping
= VirtualNetworkFunction("ping_vnfd%s" % (suffix
), pingcount
)
969 ping
.use_vnf_init_conf
= use_vnf_init_conf
971 if use_placement_group
:
972 ### Add group name Eris
973 group
= VnfdPlacementGroup('Eris')
974 group
.add_strategy('COLOCATION')
975 group
.add_requirement('''Place this VM on the Kuiper belt object Eris''')
976 ping
.add_placement_group(group
)
978 # ping = VirtualNetworkFunction("ping_vnfd", pingcount)
979 if not ping_userdata
:
980 ping_userdata
= PING_USERDATA_FILE
987 ping_userdata
=ping_userdata
,
988 ex_ping_userdata
=ex_ping_userdata
992 "Fedora-x86_64-20-20131211.1-sda-ping.qcow2",
994 use_ping_cloud_init_file
,
996 get_ping_mon_params("api/v1/ping/stats") if use_mon_params
else [],
999 num_vlr_count
=external_vlr_count
,
1000 num_ivlr_count
=internal_vlr_count
,
1001 num_vms
=num_vnf_vms
,
1002 image_md5sum
=ping_md5sum
,
1006 pong
= VirtualNetworkFunction("pong_vnfd%s" % (suffix
))
1008 if use_placement_group
:
1009 ### Add group name Weywot
1010 group
= VnfdPlacementGroup('Weywot')
1011 group
.add_strategy('COLOCATION')
1012 group
.add_requirement('''Place this VM on the Kuiper belt object Weywot''')
1013 pong
.add_placement_group(group
)
1016 # pong = VirtualNetworkFunction("pong_vnfd")
1018 if not pong_userdata
:
1019 pong_userdata
= PONG_USERDATA_FILE
1021 if ex_pong_userdata
:
1022 pong_userdata
= '''\
1026 pong_userdata
=pong_userdata
,
1027 ex_pong_userdata
=ex_pong_userdata
1032 "Fedora-x86_64-20-20131211.1-sda-pong.qcow2",
1034 use_pong_cloud_init_file
,
1035 "api/v1/pong/stats",
1036 get_pong_mon_params("api/v1/pong/stats") if use_mon_params
else [],
1039 num_vlr_count
=external_vlr_count
,
1040 num_ivlr_count
=internal_vlr_count
,
1041 num_vms
=num_vnf_vms
,
1042 image_md5sum
=pong_md5sum
,
1046 # Initialize the member VNF index
1047 member_vnf_index
= 1
1049 # define the connection point groups
1050 for index
, cp_group
in enumerate(cpgroup_list
):
1051 desc_id
= ping
.descriptor
.vnfd
[0].id
1052 filename
= 'ping_vnfd{}/cp{}'.format(suffix
, index
)
1054 for idx
in range(pingcount
):
1061 member_vnf_index
+= 1
1063 desc_id
= pong
.descriptor
.vnfd
[0].id
1064 filename
= 'pong_vnfd{}/cp{}'.format(suffix
, index
)
1072 member_vnf_index
+= 1
1074 vnfd_list
= [ping
, pong
]
1076 nsd_catalog
= NetworkService("ping_pong_nsd%s" % (suffix
))
1079 group
= ScaleGroup("ping_group", max_count
=10)
1080 group
.add_vnfd(ping
, 1)
1082 nsd_catalog
.add_scale_group(group
)
1084 if use_placement_group
:
1085 ### Add group name Orcus
1086 group
= NsdPlacementGroup('Orcus')
1087 group
.add_strategy('COLOCATION')
1088 group
.add_requirement('''Place this VM on the Kuiper belt object Orcus''')
1090 for member_vnfd
in vnfd_list
:
1091 group
.add_member(member_vnfd
)
1093 nsd_catalog
.add_placement_group(group
)
1095 ### Add group name Quaoar
1096 group
= NsdPlacementGroup('Quaoar')
1097 group
.add_strategy('COLOCATION')
1098 group
.add_requirement('''Place this VM on the Kuiper belt object Quaoar''')
1100 for member_vnfd
in vnfd_list
:
1101 group
.add_member(member_vnfd
)
1103 nsd_catalog
.add_placement_group(group
)
1106 nsd_catalog
.compose(vnfd_list
,
1109 use_ns_init_conf
=use_ns_init_conf
,
1110 use_vnf_init_conf
=use_vnf_init_conf
,)
1113 ping
.write_to_file(out_dir
, ping_fmt
if ping_fmt
is not None else fmt
)
1114 pong
.write_to_file(out_dir
, pong_fmt
if ping_fmt
is not None else fmt
)
1115 nsd_catalog
.write_config(out_dir
, vnfd_list
)
1116 nsd_catalog
.write_to_file(out_dir
, ping_fmt
if nsd_fmt
is not None else fmt
)
1118 return (ping
, pong
, nsd_catalog
)
1121 def main(argv
=sys
.argv
[1:]):
1122 global outdir
, output_format
, use_epa
, aws
, use_ping_cloud_init_file
, use_pong_cloud_init_file
1123 parser
= argparse
.ArgumentParser()
1124 parser
.add_argument('-o', '--outdir', default
='.')
1125 parser
.add_argument('-f', '--format', default
='json')
1126 parser
.add_argument('-e', '--epa', action
="store_true", default
=False)
1127 parser
.add_argument('-a', '--aws', action
="store_true", default
=False)
1128 parser
.add_argument('-n', '--pingcount', default
=NUM_PING_INSTANCES
)
1129 parser
.add_argument('--ping-image-md5')
1130 parser
.add_argument('--pong-image-md5')
1131 parser
.add_argument('--ping-cloud-init', default
=None)
1132 parser
.add_argument('--pong-cloud-init', default
=None)
1133 args
= parser
.parse_args()
1134 outdir
= args
.outdir
1135 output_format
= args
.format
1138 pingcount
= args
.pingcount
1139 use_ping_cloud_init_file
= args
.ping_cloud_init
1140 use_pong_cloud_init_file
= args
.pong_cloud_init
1142 generate_ping_pong_descriptors(args
.format
, True, args
.outdir
, pingcount
,
1143 ping_md5sum
=args
.ping_image_md5
, pong_md5sum
=args
.pong_image_md5
,
1145 use_scale_group
=False,)
1147 if __name__
== "__main__":