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