ef2dd90e507fcf624ef084c11d6b418dcf6f5779
[osm/SO.git] / examples / ping_pong_ns / rift / mano / examples / ping_pong_nsd.py
1 #!/usr/bin/env python3
2
3 #
4 # Copyright 2016 RIFT.IO Inc
5 #
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
9 #
10 # http://www.apache.org/licenses/LICENSE-2.0
11 #
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.
17 #
18
19
20 import argparse
21 import os
22 import shutil
23 import sys
24 import uuid
25
26 import gi
27 gi.require_version('RwYang', '1.0')
28 gi.require_version('RwVnfdYang', '1.0')
29 gi.require_version('VnfdYang', '1.0')
30 gi.require_version('RwNsdYang', '1.0')
31 gi.require_version('NsdYang', '1.0')
32
33
34 from gi.repository import (
35 RwNsdYang,
36 NsdYang,
37 RwVnfdYang,
38 VnfdYang,
39 RwYang,
40 )
41
42
43 try:
44 import rift.mano.config_data.config as config_data
45 except ImportError:
46 # Load modules from common which are not yet installed
47 path = os.path.abspath(os.path.dirname(os.path.abspath(__file__)) + "../../../common/python/rift/mano")
48 sys.path.append(path)
49
50 import config_data.config as config_data
51
52
53 NUM_PING_INSTANCES = 1
54 MAX_VNF_INSTANCES_PER_NS = 10
55 use_epa = False
56 aws = False
57 pingcount = NUM_PING_INSTANCES
58 use_ping_cloud_init_file = ""
59 use_pong_cloud_init_file = ""
60
61 PING_USERDATA_FILE = '''#cloud-config
62 password: fedora
63 chpasswd: { expire: False }
64 ssh_pwauth: True
65 runcmd:
66 - [ systemctl, daemon-reload ]
67 - [ systemctl, enable, ping.service ]
68 - [ systemctl, start, --no-block, ping.service ]
69 - [ ifup, eth1 ]
70 '''
71
72 PONG_USERDATA_FILE = '''#cloud-config
73 password: fedora
74 chpasswd: { expire: False }
75 ssh_pwauth: True
76 runcmd:
77 - [ systemctl, daemon-reload ]
78 - [ systemctl, enable, pong.service ]
79 - [ systemctl, start, --no-block, pong.service ]
80 - [ ifup, eth1 ]
81 '''
82
83
84 class UnknownVNFError(Exception):
85 pass
86
87
88 class ManoDescriptor(object):
89 def __init__(self, name):
90 self.name = name
91 self.descriptor = None
92
93 def write_to_file(self, module_list, outdir, output_format):
94 model = RwYang.Model.create_libncx()
95 for module in module_list:
96 model.load_module(module)
97
98 if output_format == 'json':
99 with open('%s/%s.json' % (outdir, self.name), "w") as fh:
100 fh.write(self.descriptor.to_json(model))
101 elif output_format.strip() == 'xml':
102 with open('%s/%s.xml' % (outdir, self.name), "w") as fh:
103 fh.write(self.descriptor.to_xml_v2(model))
104 elif output_format.strip() == 'yaml':
105 with open('%s/%s.yaml' % (outdir, self.name), "w") as fh:
106 fh.write(self.descriptor.to_yaml(model))
107 else:
108 raise Exception("Invalid output format for the descriptor")
109
110 def get_json(self, module_list):
111 model = RwYang.Model.create_libncx()
112 for module in module_list:
113 model.load_module(module)
114 print(self.descriptor.to_json(model))
115
116
117 class VirtualNetworkFunction(ManoDescriptor):
118 def __init__(self, name, instance_count=1):
119 self.vnfd_catalog = None
120 self.vnfd = None
121 self.instance_count = instance_count
122 self._placement_groups = []
123 self.use_vnf_init_conf = False
124 super(VirtualNetworkFunction, self).__init__(name)
125
126 def add_placement_group(self, group):
127 self._placement_groups.append(group)
128
129 def compose(self, image_name, cloud_init="", cloud_init_file="", endpoint=None, mon_params=[],
130 mon_port=8888, mgmt_port=8888, num_vlr_count=1, num_ivlr_count=1,
131 num_vms=1, image_md5sum=None, mano_ut=False):
132 self.descriptor = RwVnfdYang.YangData_Vnfd_VnfdCatalog()
133 self.id = str(uuid.uuid1())
134 vnfd = self.descriptor.vnfd.add()
135 vnfd.id = self.id
136 vnfd.name = self.name
137 vnfd.short_name = self.name
138 vnfd.vendor = 'RIFT.io'
139 vnfd.logo = 'rift_logo.png'
140 vnfd.description = 'This is an example RIFT.ware VNF'
141 vnfd.version = '1.0'
142
143 self.vnfd = vnfd
144
145 if mano_ut is True:
146 internal_vlds = []
147 for i in range(num_ivlr_count):
148 internal_vld = vnfd.internal_vld.add()
149 internal_vld.id = 'ivld%s' % i
150 internal_vld.name = 'fabric%s' % i
151 internal_vld.short_name = 'fabric%s' % i
152 internal_vld.description = 'Virtual link for internal fabric%s' % i
153 internal_vld.type_yang = 'ELAN'
154 internal_vlds.append(internal_vld)
155
156 for i in range(num_vlr_count):
157 cp = vnfd.connection_point.add()
158 cp.type_yang = 'VPORT'
159 cp.name = '%s/cp%d' % (self.name, i)
160
161 if endpoint is not None:
162 endp = VnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd_HttpEndpoint(
163 path=endpoint, port=mon_port, polling_interval_secs=2
164 )
165 vnfd.http_endpoint.append(endp)
166
167 # Monitoring params
168 for monp_dict in mon_params:
169 monp = VnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd_MonitoringParam.from_dict(monp_dict)
170 monp.http_endpoint_ref = endpoint
171 vnfd.monitoring_param.append(monp)
172
173
174 for i in range(num_vms):
175 # VDU Specification
176 vdu = vnfd.vdu.add()
177 vdu.id = 'iovdu_%s' % i
178 vdu.name = 'iovdu_%s' % i
179 vdu.count = 1
180 # vdu.mgmt_vpci = '0000:00:20.0'
181
182 # specify the VM flavor
183 if use_epa:
184 vdu.vm_flavor.vcpu_count = 4
185 vdu.vm_flavor.memory_mb = 1024
186 vdu.vm_flavor.storage_gb = 4
187 else:
188 vdu.vm_flavor.vcpu_count = 1
189 vdu.vm_flavor.memory_mb = 512
190 vdu.vm_flavor.storage_gb = 4
191
192 # Management interface
193 mgmt_intf = vnfd.mgmt_interface
194 mgmt_intf.vdu_id = vdu.id
195 mgmt_intf.port = mgmt_port
196 mgmt_intf.dashboard_params.path = endpoint
197 mgmt_intf.dashboard_params.port = mgmt_port
198
199 if cloud_init_file and len(cloud_init_file):
200 vdu.cloud_init_file = cloud_init_file
201 else:
202 vdu.cloud_init = cloud_init
203 if aws:
204 vdu.cloud_init += " - [ systemctl, restart, --no-block, elastic-network-interfaces.service ]\n"
205
206 # sepcify the guest EPA
207 if use_epa:
208 vdu.guest_epa.trusted_execution = False
209 vdu.guest_epa.mempage_size = 'LARGE'
210 vdu.guest_epa.cpu_pinning_policy = 'DEDICATED'
211 vdu.guest_epa.cpu_thread_pinning_policy = 'PREFER'
212 vdu.guest_epa.numa_node_policy.node_cnt = 2
213 vdu.guest_epa.numa_node_policy.mem_policy = 'STRICT'
214
215 node = vdu.guest_epa.numa_node_policy.node.add()
216 node.id = 0
217 node.memory_mb = 512
218 vcpu = node.vcpu.add()
219 vcpu.id = 0
220 vcpu = node.vcpu.add()
221 vcpu.id = 1
222
223 node = vdu.guest_epa.numa_node_policy.node.add()
224 node.id = 1
225 node.memory_mb = 512
226 vcpu = node.vcpu.add()
227 vcpu.id = 2
228 vcpu = node.vcpu.add()
229 vcpu.id = 3
230
231 # specify the vswitch EPA
232 vdu.vswitch_epa.ovs_acceleration = 'DISABLED'
233 vdu.vswitch_epa.ovs_offload = 'DISABLED'
234
235 # Specify the hypervisor EPA
236 vdu.hypervisor_epa.type_yang = 'PREFER_KVM'
237
238 # Specify the host EPA
239 # vdu.host_epa.cpu_model = 'PREFER_SANDYBRIDGE'
240 # vdu.host_epa.cpu_arch = 'PREFER_X86_64'
241 # vdu.host_epa.cpu_vendor = 'PREFER_INTEL'
242 # vdu.host_epa.cpu_socket_count = 2
243 # vdu.host_epa.cpu_core_count = 8
244 # vdu.host_epa.cpu_core_thread_count = 2
245 # vdu.host_epa.cpu_feature = ['PREFER_AES', 'REQUIRE_VME', 'PREFER_MMX','REQUIRE_SSE2']
246
247 if aws:
248 vdu.image = 'rift-ping-pong'
249 else:
250 vdu.image = image_name
251 if image_md5sum is not None:
252 vdu.image_checksum = image_md5sum
253
254 if mano_ut is True:
255 for i in range(num_ivlr_count):
256 internal_cp = vdu.internal_connection_point.add()
257 if vnfd.name.find("ping") >= 0:
258 cp_name = "ping"
259 else:
260 cp_name = "pong"
261 internal_cp.name = cp_name + "/icp{}".format(i)
262 internal_cp.id = cp_name + "/icp{}".format(i)
263 internal_cp.type_yang = 'VPORT'
264 ivld_cp = internal_vlds[i].internal_connection_point_ref.add()
265 ivld_cp.id_ref = internal_cp.id
266
267 internal_interface = vdu.internal_interface.add()
268 internal_interface.name = 'fab%d' % i
269 internal_interface.vdu_internal_connection_point_ref = internal_cp.id
270 internal_interface.virtual_interface.type_yang = 'VIRTIO'
271
272 # internal_interface.virtual_interface.vpci = '0000:00:1%d.0'%i
273
274 for i in range(num_vlr_count):
275 external_interface = vdu.external_interface.add()
276 external_interface.name = 'eth%d' % i
277 external_interface.vnfd_connection_point_ref = '%s/cp%d' % (self.name, i)
278 if use_epa:
279 external_interface.virtual_interface.type_yang = 'VIRTIO'
280 else:
281 external_interface.virtual_interface.type_yang = 'VIRTIO'
282 # external_interface.virtual_interface.vpci = '0000:00:2%d.0'%i
283
284 for group in self._placement_groups:
285 placement_group = vnfd.placement_groups.add()
286 placement_group.name = group.name
287 placement_group.requirement = group.requirement
288 placement_group.strategy = group.strategy
289 if group.vdu_list:
290 ### Add specific VDUs to placement group
291 for vdu in group.vdu_list:
292 member_vdu = placement_group.member_vdus.add()
293 member_vdu.member_vdu_ref = vdu.id
294 else:
295 ### Add all VDUs to placement group
296 for vdu in vnfd.vdu:
297 member_vdu = placement_group.member_vdus.add()
298 member_vdu.member_vdu_ref = vdu.id
299
300
301 def write_to_file(self, outdir, output_format):
302 dirpath = "%s/%s" % (outdir, self.name)
303 if not os.path.exists(dirpath):
304 os.makedirs(dirpath)
305 super(VirtualNetworkFunction, self).write_to_file(['vnfd', 'rw-vnfd'],
306 dirpath,
307 output_format)
308 self.add_scripts(outdir)
309
310 def add_scripts(self, outdir):
311 script_dir = os.path.join(outdir, self.name, 'cloud_init')
312 try:
313 os.makedirs(script_dir)
314 except OSError:
315 if not os.path.isdir(script_dir):
316 raise
317
318 if 'ping' in self.name:
319 script_file = os.path.join(script_dir, 'ping_cloud_init.cfg')
320 cfg = PING_USERDATA_FILE
321 else:
322 script_file = os.path.join(script_dir, 'pong_cloud_init.cfg')
323 cfg = PONG_USERDATA_FILE
324
325 with open(script_file, "w") as f:
326 f.write("{}".format(cfg))
327
328 # Copy the vnf_init_config script
329 if self.use_vnf_init_conf and ('ping' in self.name):
330 script_name = 'ping_set_rate.py'
331
332 src_path = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
333 script_src = os.path.join(src_path, script_name)
334 if not os.path.exists(script_src):
335 src_path = os.path.join(os.environ['RIFT_ROOT'],
336 'modules/core/mano/examples/ping_pong_ns/rift/mano/examples')
337 script_src = os.path.join(src_path, script_name)
338
339 dest_path = os.path.join(outdir, self.name, 'scripts')
340 os.makedirs(dest_path, exist_ok=True)
341
342 shutil.copy2(script_src, dest_path)
343
344
345 class NetworkService(ManoDescriptor):
346 def __init__(self, name):
347 super(NetworkService, self).__init__(name)
348 self._scale_groups = []
349 self.vnfd_config = {}
350 self._placement_groups = []
351
352 def ping_config(self, mano_ut, use_ns_init_conf, use_vnf_init_conf):
353 suffix = ''
354 if mano_ut:
355 ping_cfg = r'''
356 #!/bin/bash
357
358 echo "!!!!!!!! Executed ping Configuration !!!!!!!!!"
359 '''
360 else:
361 ping_cfg = r'''
362 #!/bin/bash
363
364 # Rest API config
365 ping_mgmt_ip='<rw_mgmt_ip>'
366 ping_mgmt_port=18888
367
368 # VNF specific configuration
369 pong_server_ip='<rw_connection_point_name pong_vnfd%s/cp0>'
370 ping_rate=5
371 server_port=5555
372
373 # Make rest API calls to configure VNF
374 curl -D /dev/stdout \
375 -H "Accept: application/vnd.yang.data+xml" \
376 -H "Content-Type: application/vnd.yang.data+json" \
377 -X POST \
378 -d "{\"ip\":\"$pong_server_ip\", \"port\":$server_port}" \
379 http://${ping_mgmt_ip}:${ping_mgmt_port}/api/v1/ping/server
380 rc=$?
381 if [ $rc -ne 0 ]
382 then
383 echo "Failed to set server info for ping!"
384 exit $rc
385 fi
386 ''' % suffix
387
388 if use_vnf_init_conf is False:
389 ping_cfg +='''
390 curl -D /dev/stdout \
391 -H "Accept: application/vnd.yang.data+xml" \
392 -H "Content-Type: application/vnd.yang.data+json" \
393 -X POST \
394 -d "{\"rate\":$ping_rate}" \
395 http://${ping_mgmt_ip}:${ping_mgmt_port}/api/v1/ping/rate
396 rc=$?
397 if [ $rc -ne 0 ]
398 then
399 echo "Failed to set ping rate!"
400 exit $rc
401 fi
402
403 '''
404 if use_ns_init_conf:
405 ping_cfg += "exit 0\n"
406 else:
407 ping_cfg +='''
408 output=$(curl -D /dev/stdout \
409 -H "Accept: application/vnd.yang.data+xml" \
410 -H "Content-Type: application/vnd.yang.data+json" \
411 -X POST \
412 -d "{\"enable\":true}" \
413 http://${ping_mgmt_ip}:${ping_mgmt_port}/api/v1/ping/adminstatus/state)
414 if [[ $output == *"Internal Server Error"* ]]
415 then
416 echo $output
417 exit 3
418 else
419 echo $output
420 fi
421
422 exit 0
423 '''
424 return ping_cfg
425
426 def pong_config(self, mano_ut, use_ns_init_conf):
427 suffix = ''
428 if mano_ut:
429 pong_cfg = r'''
430 #!/bin/bash
431
432 echo "!!!!!!!! Executed pong Configuration !!!!!!!!!"
433 '''
434 else:
435 pong_cfg = r'''
436 #!/bin/bash
437
438 # Rest API configuration
439 pong_mgmt_ip='<rw_mgmt_ip>'
440 pong_mgmt_port=18889
441 # username=<rw_username>
442 # password=<rw_password>
443
444 # VNF specific configuration
445 pong_server_ip='<rw_connection_point_name pong_vnfd%s/cp0>'
446 server_port=5555
447
448 # Make Rest API calls to configure VNF
449 curl -D /dev/stdout \
450 -H "Accept: application/vnd.yang.data+xml" \
451 -H "Content-Type: application/vnd.yang.data+json" \
452 -X POST \
453 -d "{\"ip\":\"$pong_server_ip\", \"port\":$server_port}" \
454 http://${pong_mgmt_ip}:${pong_mgmt_port}/api/v1/pong/server
455 rc=$?
456 if [ $rc -ne 0 ]
457 then
458 echo "Failed to set server(own) info for pong!"
459 exit $rc
460 fi
461
462 ''' % suffix
463
464 if use_ns_init_conf:
465 pong_cfg += "exit 0\n"
466 else:
467 pong_cfg +='''
468 curl -D /dev/stdout \
469 -H "Accept: application/vnd.yang.data+xml" \
470 -H "Content-Type: application/vnd.yang.data+json" \
471 -X POST \
472 -d "{\"enable\":true}" \
473 http://${pong_mgmt_ip}:${pong_mgmt_port}/api/v1/pong/adminstatus/state
474 rc=$?
475 if [ $rc -ne 0 ]
476 then
477 echo "Failed to enable pong service!"
478 exit $rc
479 fi
480
481 exit 0
482 '''
483 return pong_cfg
484
485 def pong_fake_juju_config(self, vnf_config):
486
487 if vnf_config:
488 # Select "script" configuration
489 vnf_config.juju.charm = 'clearwater-aio-proxy'
490
491 # Set the initital-config
492 vnf_config.create_initial_config_primitive()
493 init_config = VnfdYang.InitialConfigPrimitive.from_dict({
494 "seq": 1,
495 "name": "config",
496 "parameter": [
497 {"name": "proxied_ip", "value": "<rw_mgmt_ip>"},
498 ]
499 })
500 vnf_config.initial_config_primitive.append(init_config)
501
502 init_config_action = VnfdYang.InitialConfigPrimitive.from_dict({
503 "seq": 2,
504 "name": "action1",
505 "parameter": [
506 {"name": "Pong Connection Point", "value": "pong_vnfd/cp0"},
507 ]
508 })
509 vnf_config.initial_config_primitive.append(init_config_action)
510 init_config_action = VnfdYang.InitialConfigPrimitive.from_dict({
511 "seq": 3,
512 "name": "action2",
513 "parameter": [
514 {"name": "Ping Connection Point", "value": "ping_vnfd/cp0"},
515 ]
516 })
517 vnf_config.initial_config_primitive.append(init_config_action)
518
519 # Config parameters can be taken from config.yaml and
520 # actions from actions.yaml in the charm
521 # Config to set the home domain
522 vnf_config.create_service_primitive()
523 config = VnfdYang.ServicePrimitive.from_dict({
524 "name": "config",
525 "parameter": [
526 {"name": "home_domain", "data_type": "STRING"},
527 {"name": "base_number", "data_type": "STRING"},
528 {"name": "number_count", "data_type": "INTEGER"},
529 {"name": "password", "data_type": "STRING"},
530 ]
531 })
532 vnf_config.service_primitive.append(config)
533
534 config = VnfdYang.ServicePrimitive.from_dict({
535 "name": "create-update-user",
536 # "user-defined-script":"/tmp/test.py",
537 "parameter": [
538 {"name": "number", "data_type": "STRING", "mandatory": True},
539 {"name": "password", "data_type": "STRING", "mandatory": True},
540 ]
541 })
542 vnf_config.service_primitive.append(config)
543
544 config = VnfdYang.ServicePrimitive.from_dict({
545 "name": "delete-user",
546 "parameter": [
547 {"name": "number", "data_type": "STRING", "mandatory": True},
548 ]
549 })
550 vnf_config.service_primitive.append(config)
551
552 def default_config(self, const_vnfd, vnfd, mano_ut,
553 use_ns_init_conf,
554 use_vnf_init_conf):
555 vnf_config = vnfd.vnfd.vnf_configuration
556
557 vnf_config.config_attributes.config_priority = 0
558 vnf_config.config_attributes.config_delay = 0
559
560 # Select "script" configuration
561 vnf_config.script.script_type = 'bash'
562
563 if vnfd.name == 'pong_vnfd' or vnfd.name == 'pong_vnfd_with_epa' or vnfd.name == 'pong_vnfd_aws':
564 vnf_config.config_attributes.config_priority = 1
565 vnf_config.config_template = self.pong_config(mano_ut, use_ns_init_conf)
566 # First priority config delay will delay the entire NS config delay
567 if mano_ut is False:
568 vnf_config.config_attributes.config_delay = 60
569 else:
570 # This is PONG and inside mano_ut
571 # This is test only
572 vnf_config.config_attributes.config_delay = 10
573 # vnf_config.config_template = self.pong_config(vnf_config, use_ns_init_conf)
574
575 if vnfd.name == 'ping_vnfd' or vnfd.name == 'ping_vnfd_with_epa' or vnfd.name == 'ping_vnfd_aws':
576 vnf_config.config_attributes.config_priority = 2
577 vnf_config.config_template = self.ping_config(mano_ut,
578 use_ns_init_conf,
579 use_vnf_init_conf)
580 if use_vnf_init_conf:
581 vnf_config.initial_config_primitive.add().from_dict(
582 {
583 "seq": 1,
584 "name": "set ping rate",
585 "user_defined_script": "ping_set_rate.py",
586 "parameter": [
587 {
588 'name': 'rate',
589 'value': '5',
590 },
591 ],
592 }
593 )
594
595 def ns_config(self, nsd, vnfd_list, mano_ut):
596 # Used by scale group
597 if mano_ut:
598 nsd.service_primitive.add().from_dict(
599 {
600 "name": "ping config",
601 "user_defined_script": "{}".format(os.path.join(
602 os.environ['RIFT_ROOT'],
603 'modules/core/mano',
604 'examples/ping_pong_ns/rift/mano/examples',
605 'ping_config_ut.sh'))
606 })
607 else:
608 nsd.service_primitive.add().from_dict(
609 {
610 "name": "ping config",
611 "user_defined_script": "ping_config.py"
612 })
613
614 def ns_initial_config(self, nsd):
615 nsd.initial_config_primitive.add().from_dict(
616 {
617 "seq": 1,
618 "name": "start traffic",
619 "user_defined_script": "start_traffic.py",
620 "parameter": [
621 {
622 'name': 'userid',
623 'value': 'rift',
624 },
625 ],
626 }
627 )
628
629 def add_scale_group(self, scale_group):
630 self._scale_groups.append(scale_group)
631
632 def add_placement_group(self, placement_group):
633 self._placement_groups.append(placement_group)
634
635 def create_mon_params(self, vnfds):
636 NsdMonParam = NsdYang.YangData_Nsd_NsdCatalog_Nsd_MonitoringParam
637 param_id = 1
638 for vnfd_obj in vnfds:
639 for mon_param in vnfd_obj.vnfd.monitoring_param:
640 nsd_monp = NsdMonParam.from_dict({
641 'id': str(param_id),
642 'name': mon_param.name,
643 'aggregation_type': "AVERAGE",
644 'value_type': mon_param.value_type,
645 'vnfd_monitoring_param': [
646 {'vnfd_id_ref': vnfd_obj.vnfd.id,
647 'vnfd_monitoring_param_ref': mon_param.id}]
648 })
649
650 self.nsd.monitoring_param.append(nsd_monp)
651 param_id += 1
652
653
654
655
656 def compose(self, vnfd_list, cpgroup_list, mano_ut,
657 use_ns_init_conf=True,
658 use_vnf_init_conf=True,):
659
660 if mano_ut:
661 # Disable NS initial config primitive
662 use_ns_init_conf = False
663 use_vnf_init_conf = False
664
665 self.descriptor = RwNsdYang.YangData_Nsd_NsdCatalog()
666 self.id = str(uuid.uuid1())
667 nsd = self.descriptor.nsd.add()
668 self.nsd = nsd
669 nsd.id = self.id
670 nsd.name = self.name
671 nsd.short_name = self.name
672 nsd.vendor = 'RIFT.io'
673 nsd.logo = 'rift_logo.png'
674 nsd.description = 'Toy NS'
675 nsd.version = '1.0'
676 nsd.input_parameter_xpath.append(
677 NsdYang.YangData_Nsd_NsdCatalog_Nsd_InputParameterXpath(
678 xpath="/nsd:nsd-catalog/nsd:nsd/nsd:vendor",
679 )
680 )
681
682 ip_profile = nsd.ip_profiles.add()
683 ip_profile.name = "InterVNFLink"
684 ip_profile.description = "Inter VNF Link"
685 ip_profile.ip_profile_params.ip_version = "ipv4"
686 ip_profile.ip_profile_params.subnet_address = "31.31.31.0/24"
687 ip_profile.ip_profile_params.gateway_address = "31.31.31.210"
688
689 vld_id = 1
690 for cpgroup in cpgroup_list:
691 vld = nsd.vld.add()
692 vld.id = 'ping_pong_vld%s' % vld_id
693 vld_id += 1
694 vld.name = 'ping_pong_vld' # hard coded
695 vld.short_name = vld.name
696 vld.vendor = 'RIFT.io'
697 vld.description = 'Toy VL'
698 vld.version = '1.0'
699 vld.type_yang = 'ELAN'
700 vld.ip_profile_ref = 'InterVNFLink'
701 for cp in cpgroup:
702 cpref = vld.vnfd_connection_point_ref.add()
703 cpref.member_vnf_index_ref = cp[0]
704 cpref.vnfd_id_ref = cp[1]
705 cpref.vnfd_connection_point_ref = cp[2]
706
707 vnfd_index_map = {}
708 member_vnf_index = 1
709 for vnfd in vnfd_list:
710 for i in range(vnfd.instance_count):
711 constituent_vnfd = nsd.constituent_vnfd.add()
712 constituent_vnfd.member_vnf_index = member_vnf_index
713 vnfd_index_map[vnfd] = member_vnf_index
714
715 # Set the start by default to false for ping vnfd,
716 # if scaling is enabled
717 if (len(self._scale_groups) and
718 vnfd.descriptor.vnfd[0].name == 'ping_vnfd'):
719 constituent_vnfd.start_by_default = False
720
721 constituent_vnfd.vnfd_id_ref = vnfd.descriptor.vnfd[0].id
722 self.default_config(constituent_vnfd, vnfd, mano_ut,
723 use_ns_init_conf, use_vnf_init_conf)
724 member_vnf_index += 1
725
726 # Enable config primitives if either mano_ut or
727 # scale groups are enabled
728 if mano_ut or len(self._scale_groups):
729 self.ns_config(nsd, vnfd_list, mano_ut)
730
731 # Add NS initial config to start traffic
732 if use_ns_init_conf:
733 self.ns_initial_config(nsd)
734
735 for scale_group in self._scale_groups:
736 group_desc = nsd.scaling_group_descriptor.add()
737 group_desc.name = scale_group.name
738 group_desc.max_instance_count = scale_group.max_count
739 group_desc.min_instance_count = scale_group.min_count
740 for vnfd, count in scale_group.vnfd_count_map.items():
741 member = group_desc.vnfd_member.add()
742 member.member_vnf_index_ref = vnfd_index_map[vnfd]
743 member.count = count
744
745 for trigger in scale_group.config_action:
746 config_action = group_desc.scaling_config_action.add()
747 config_action.trigger = trigger
748 config = scale_group.config_action[trigger]
749 config_action.ns_config_primitive_name_ref = config['ns-config-primitive-name-ref']
750
751 for placement_group in self._placement_groups:
752 group = nsd.placement_groups.add()
753 group.name = placement_group.name
754 group.strategy = placement_group.strategy
755 group.requirement = placement_group.requirement
756 for member_vnfd in placement_group.vnfd_list:
757 member = group.member_vnfd.add()
758 member.vnfd_id_ref = member_vnfd.descriptor.vnfd[0].id
759 member.member_vnf_index_ref = vnfd_index_map[member_vnfd]
760
761 # self.create_mon_params(vnfd_list)
762
763 def write_config(self, outdir, vnfds):
764
765 converter = config_data.ConfigPrimitiveConvertor()
766 yaml_data = converter.extract_nsd_config(self.nsd)
767
768 ns_config_dir = os.path.join(outdir, self.name, "ns_config")
769 os.makedirs(ns_config_dir, exist_ok=True)
770 vnf_config_dir = os.path.join(outdir, self.name, "vnf_config")
771 os.makedirs(vnf_config_dir, exist_ok=True)
772
773 if len(yaml_data):
774 with open('%s/%s.yaml' % (ns_config_dir, self.id), "w") as fh:
775 fh.write(yaml_data)
776
777 for i, vnfd in enumerate(vnfds, start=1):
778 yaml_data = converter.extract_vnfd_config(vnfd)
779
780 if len(yaml_data):
781 with open('%s/%s__%s.yaml' % (vnf_config_dir, vnfd.id, i), "w") as fh:
782 fh.write(yaml_data)
783
784 def write_initial_config_script(self, outdir):
785 script_name = 'start_traffic.py'
786
787 src_path = os.path.dirname(os.path.abspath(os.path.realpath(__file__)))
788 script_src = os.path.join(src_path, script_name)
789 if not os.path.exists(script_src):
790 src_path = os.path.join(os.environ['RIFT_ROOT'],
791 'modules/core/mano/examples/ping_pong_ns/rift/mano/examples')
792 script_src = os.path.join(src_path, script_name)
793
794 dest_path = os.path.join(outdir, 'scripts')
795 os.makedirs(dest_path, exist_ok=True)
796
797 shutil.copy2(script_src, dest_path)
798
799 def write_to_file(self, outdir, output_format):
800 dirpath = os.path.join(outdir, self.name)
801 if not os.path.exists(dirpath):
802 os.makedirs(dirpath)
803
804 super(NetworkService, self).write_to_file(["nsd", "rw-nsd"],
805 dirpath,
806 output_format)
807
808 # Write the initial config script
809 self.write_initial_config_script(dirpath)
810
811
812 def get_ping_mon_params(path):
813 return [
814 {
815 'id': '1',
816 'name': 'ping-request-tx-count',
817 'http_endpoint_ref': path,
818 'json_query_method': "NAMEKEY",
819 'value_type': "INT",
820 'description': 'no of ping requests',
821 'group_tag': 'Group1',
822 'widget_type': 'COUNTER',
823 'units': 'packets'
824 },
825
826 {
827 'id': '2',
828 'name': 'ping-response-rx-count',
829 'http_endpoint_ref': path,
830 'json_query_method': "NAMEKEY",
831 'value_type': "INT",
832 'description': 'no of ping responses',
833 'group_tag': 'Group1',
834 'widget_type': 'COUNTER',
835 'units': 'packets'
836 },
837 ]
838
839
840 def get_pong_mon_params(path):
841 return [
842 {
843 'id': '1',
844 'name': 'ping-request-rx-count',
845 'http_endpoint_ref': path,
846 'json_query_method': "NAMEKEY",
847 'value_type': "INT",
848 'description': 'no of ping requests',
849 'group_tag': 'Group1',
850 'widget_type': 'COUNTER',
851 'units': 'packets'
852 },
853
854 {
855 'id': '2',
856 'name': 'ping-response-tx-count',
857 'http_endpoint_ref': path,
858 'json_query_method': "NAMEKEY",
859 'value_type': "INT",
860 'description': 'no of ping responses',
861 'group_tag': 'Group1',
862 'widget_type': 'COUNTER',
863 'units': 'packets'
864 },
865 ]
866
867
868 class ScaleGroup(object):
869 def __init__(self, name, min_count=1, max_count=1):
870 self.name = name
871 self.min_count = min_count
872 self.max_count = max_count
873 self.vnfd_count_map = {}
874 self.config_action = {}
875
876 def add_vnfd(self, vnfd, vnfd_count):
877 self.vnfd_count_map[vnfd] = vnfd_count
878
879 def add_config(self):
880 self.config_action['post_scale_out']= {'ns-config-primitive-name-ref':
881 'ping config'}
882
883 class PlacementGroup(object):
884 def __init__(self, name):
885 self.name = name
886 self.strategy = ''
887 self.requirement = ''
888
889 def add_strategy(self, strategy):
890 self.strategy = strategy
891
892 def add_requirement(self, requirement):
893 self.requirement = requirement
894
895 class NsdPlacementGroup(PlacementGroup):
896 def __init__(self, name):
897 self.vnfd_list = []
898 super(NsdPlacementGroup, self).__init__(name)
899
900 def add_member(self, vnfd):
901 self.vnfd_list.append(vnfd)
902
903
904 class VnfdPlacementGroup(PlacementGroup):
905 def __init__(self, name):
906 self.vdu_list = []
907 super(VnfdPlacementGroup, self).__init__(name)
908
909 def add_member(self, vdu):
910 self.vdu_list.append(vdu)
911
912
913
914
915 def generate_ping_pong_descriptors(fmt="json",
916 write_to_file=False,
917 out_dir="./",
918 pingcount=NUM_PING_INSTANCES,
919 external_vlr_count=1,
920 internal_vlr_count=1,
921 num_vnf_vms=1,
922 ping_md5sum=None,
923 pong_md5sum=None,
924 mano_ut=False,
925 use_scale_group=False,
926 ping_fmt=None,
927 pong_fmt=None,
928 nsd_fmt=None,
929 use_mon_params=True,
930 ping_userdata=None,
931 pong_userdata=None,
932 ex_ping_userdata=None,
933 ex_pong_userdata=None,
934 use_placement_group=True,
935 use_ns_init_conf=True,
936 use_vnf_init_conf=True,
937 ):
938 # List of connection point groups
939 # Each connection point group refers to a virtual link
940 # the CP group consists of tuples of connection points
941 cpgroup_list = []
942 for i in range(external_vlr_count):
943 cpgroup_list.append([])
944
945 suffix = ''
946 ping = VirtualNetworkFunction("ping_vnfd%s" % (suffix), pingcount)
947 ping.use_vnf_init_conf = use_vnf_init_conf
948
949 if use_placement_group:
950 ### Add group name Eris
951 group = VnfdPlacementGroup('Eris')
952 group.add_strategy('COLOCATION')
953 group.add_requirement('''Place this VM on the Kuiper belt object Eris''')
954 ping.add_placement_group(group)
955
956 # ping = VirtualNetworkFunction("ping_vnfd", pingcount)
957 if not ping_userdata:
958 ping_userdata = PING_USERDATA_FILE
959
960 if ex_ping_userdata:
961 ping_userdata = '''\
962 {ping_userdata}
963 {ex_ping_userdata}
964 '''.format(
965 ping_userdata=ping_userdata,
966 ex_ping_userdata=ex_ping_userdata
967 )
968
969 ping.compose(
970 "Fedora-x86_64-20-20131211.1-sda-ping.qcow2",
971 ping_userdata,
972 use_ping_cloud_init_file,
973 "api/v1/ping/stats",
974 get_ping_mon_params("api/v1/ping/stats") if use_mon_params else [],
975 mon_port=18888,
976 mgmt_port=18888,
977 num_vlr_count=external_vlr_count,
978 num_ivlr_count=internal_vlr_count,
979 num_vms=num_vnf_vms,
980 image_md5sum=ping_md5sum,
981 mano_ut=mano_ut,
982 )
983
984 pong = VirtualNetworkFunction("pong_vnfd%s" % (suffix))
985
986 if use_placement_group:
987 ### Add group name Weywot
988 group = VnfdPlacementGroup('Weywot')
989 group.add_strategy('COLOCATION')
990 group.add_requirement('''Place this VM on the Kuiper belt object Weywot''')
991 pong.add_placement_group(group)
992
993
994 # pong = VirtualNetworkFunction("pong_vnfd")
995
996 if not pong_userdata:
997 pong_userdata = PONG_USERDATA_FILE
998
999 if ex_pong_userdata:
1000 pong_userdata = '''\
1001 {pong_userdata}
1002 {ex_pong_userdata}
1003 '''.format(
1004 pong_userdata=pong_userdata,
1005 ex_pong_userdata=ex_pong_userdata
1006 )
1007
1008
1009 pong.compose(
1010 "Fedora-x86_64-20-20131211.1-sda-pong.qcow2",
1011 pong_userdata,
1012 use_pong_cloud_init_file,
1013 "api/v1/pong/stats",
1014 get_pong_mon_params("api/v1/pong/stats") if use_mon_params else [],
1015 mon_port=18889,
1016 mgmt_port=18889,
1017 num_vlr_count=external_vlr_count,
1018 num_ivlr_count=internal_vlr_count,
1019 num_vms=num_vnf_vms,
1020 image_md5sum=pong_md5sum,
1021 mano_ut=mano_ut,
1022 )
1023
1024 # Initialize the member VNF index
1025 member_vnf_index = 1
1026
1027 # define the connection point groups
1028 for index, cp_group in enumerate(cpgroup_list):
1029 desc_id = ping.descriptor.vnfd[0].id
1030 filename = 'ping_vnfd{}/cp{}'.format(suffix, index)
1031
1032 for idx in range(pingcount):
1033 cp_group.append((
1034 member_vnf_index,
1035 desc_id,
1036 filename,
1037 ))
1038
1039 member_vnf_index += 1
1040
1041 desc_id = pong.descriptor.vnfd[0].id
1042 filename = 'pong_vnfd{}/cp{}'.format(suffix, index)
1043
1044 cp_group.append((
1045 member_vnf_index,
1046 desc_id,
1047 filename,
1048 ))
1049
1050 member_vnf_index += 1
1051
1052 vnfd_list = [ping, pong]
1053
1054 nsd_catalog = NetworkService("ping_pong_nsd%s" % (suffix))
1055
1056 if use_scale_group:
1057 group = ScaleGroup("ping_group", max_count=10)
1058 group.add_vnfd(ping, 1)
1059 group.add_config()
1060 nsd_catalog.add_scale_group(group)
1061
1062 if use_placement_group:
1063 ### Add group name Orcus
1064 group = NsdPlacementGroup('Orcus')
1065 group.add_strategy('COLOCATION')
1066 group.add_requirement('''Place this VM on the Kuiper belt object Orcus''')
1067
1068 for member_vnfd in vnfd_list:
1069 group.add_member(member_vnfd)
1070
1071 nsd_catalog.add_placement_group(group)
1072
1073 ### Add group name Quaoar
1074 group = NsdPlacementGroup('Quaoar')
1075 group.add_strategy('COLOCATION')
1076 group.add_requirement('''Place this VM on the Kuiper belt object Quaoar''')
1077
1078 for member_vnfd in vnfd_list:
1079 group.add_member(member_vnfd)
1080
1081 nsd_catalog.add_placement_group(group)
1082
1083
1084 nsd_catalog.compose(vnfd_list,
1085 cpgroup_list,
1086 mano_ut,
1087 use_ns_init_conf=use_ns_init_conf,
1088 use_vnf_init_conf=use_vnf_init_conf,)
1089
1090 if write_to_file:
1091 ping.write_to_file(out_dir, ping_fmt if ping_fmt is not None else fmt)
1092 pong.write_to_file(out_dir, pong_fmt if ping_fmt is not None else fmt)
1093 nsd_catalog.write_config(out_dir, vnfd_list)
1094 nsd_catalog.write_to_file(out_dir, ping_fmt if nsd_fmt is not None else fmt)
1095
1096 return (ping, pong, nsd_catalog)
1097
1098
1099 def main(argv=sys.argv[1:]):
1100 global outdir, output_format, use_epa, aws, use_ping_cloud_init_file, use_pong_cloud_init_file
1101 parser = argparse.ArgumentParser()
1102 parser.add_argument('-o', '--outdir', default='.')
1103 parser.add_argument('-f', '--format', default='json')
1104 parser.add_argument('-e', '--epa', action="store_true", default=False)
1105 parser.add_argument('-a', '--aws', action="store_true", default=False)
1106 parser.add_argument('-n', '--pingcount', default=NUM_PING_INSTANCES)
1107 parser.add_argument('--ping-image-md5')
1108 parser.add_argument('--pong-image-md5')
1109 parser.add_argument('--ping-cloud-init', default=None)
1110 parser.add_argument('--pong-cloud-init', default=None)
1111 args = parser.parse_args()
1112 outdir = args.outdir
1113 output_format = args.format
1114 use_epa = args.epa
1115 aws = args.aws
1116 pingcount = args.pingcount
1117 use_ping_cloud_init_file = args.ping_cloud_init
1118 use_pong_cloud_init_file = args.pong_cloud_init
1119
1120 generate_ping_pong_descriptors(args.format, True, args.outdir, pingcount,
1121 ping_md5sum=args.ping_image_md5, pong_md5sum=args.pong_image_md5,
1122 mano_ut=False,
1123 use_scale_group=False,)
1124
1125 if __name__ == "__main__":
1126 main()