b18deb7c9c65f41ed9363ee6cfa203c38b110862
[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('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')
37
38
39 from gi.repository import (
40 RwNsdYang,
41 NsdYang,
42 RwVnfdYang,
43 VnfdYang,
44 RwYang,
45 RwProjectYang,
46 )
47
48
49 try:
50 import rift.mano.config_data.config as config_data
51 except ImportError:
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")
54 sys.path.append(path)
55
56 import config_data.config as config_data
57
58
59 NUM_PING_INSTANCES = 1
60 MAX_VNF_INSTANCES_PER_NS = 10
61 use_epa = False
62 aws = False
63 pingcount = NUM_PING_INSTANCES
64 use_ping_cloud_init_file = ""
65 use_pong_cloud_init_file = ""
66
67 PING_USERDATA_FILE = '''#cloud-config
68 password: fedora
69 chpasswd: { expire: False }
70 ssh_pwauth: True
71 runcmd:
72 - [ systemctl, daemon-reload ]
73 - [ systemctl, enable, ping.service ]
74 - [ systemctl, start, --no-block, ping.service ]
75 - [ ifup, eth1 ]
76 '''
77
78 PONG_USERDATA_FILE = '''#cloud-config
79 password: fedora
80 chpasswd: { expire: False }
81 ssh_pwauth: True
82 runcmd:
83 - [ systemctl, daemon-reload ]
84 - [ systemctl, enable, pong.service ]
85 - [ systemctl, start, --no-block, pong.service ]
86 - [ ifup, eth1 ]
87 '''
88
89
90 class UnknownVNFError(Exception):
91 pass
92
93
94 class ManoDescriptor(object):
95 def __init__(self, name):
96 self.name = name
97 self.descriptor = None
98
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)
103
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)
109 proj = yaml.load(ya)
110 desc = proj['rw-project:project'][0]
111
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.strip() == '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.strip() == '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")
126 if not desc:
127 desc = dom.getElementsByTagName("nsd:nsd-catalog")
128 fh.write(desc[0].toprettyxml())
129 else:
130 raise Exception("Invalid output format for the descriptor")
131
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))
137
138
139 class VirtualNetworkFunction(ManoDescriptor):
140 def __init__(self, name, instance_count=1):
141 self.vnfd_catalog = None
142 self.vnfd = None
143 self.instance_count = instance_count
144 self._placement_groups = []
145 self.use_vnf_init_conf = False
146 super(VirtualNetworkFunction, self).__init__(name)
147
148 def add_placement_group(self, group):
149 self._placement_groups.append(group)
150
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()
157 vnfd.id = self.id
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'
163 vnfd.version = '1.0'
164
165 self.vnfd = vnfd
166
167 if mano_ut is True:
168 internal_vlds = []
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)
177
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)
182
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
186 )
187 vnfd.http_endpoint.append(endp)
188
189 # Monitoring params
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)
194
195
196 for i in range(num_vms):
197 # VDU Specification
198 vdu = vnfd.vdu.add()
199 vdu.id = 'iovdu_%s' % i
200 vdu.name = 'iovdu_%s' % i
201 vdu.count = 1
202 # vdu.mgmt_vpci = '0000:00:20.0'
203
204 # specify the VM flavor
205 if use_epa:
206 vdu.vm_flavor.vcpu_count = 4
207 vdu.vm_flavor.memory_mb = 1024
208 vdu.vm_flavor.storage_gb = 4
209 else:
210 vdu.vm_flavor.vcpu_count = 1
211 vdu.vm_flavor.memory_mb = 512
212 vdu.vm_flavor.storage_gb = 4
213
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
220
221 if cloud_init_file and len(cloud_init_file):
222 vdu.cloud_init_file = cloud_init_file
223 else:
224 vdu.cloud_init = cloud_init
225 if aws:
226 vdu.cloud_init += " - [ systemctl, restart, --no-block, elastic-network-interfaces.service ]\n"
227
228 # sepcify the guest EPA
229 if use_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'
236
237 node = vdu.guest_epa.numa_node_policy.node.add()
238 node.id = 0
239 node.memory_mb = 512
240 vcpu = node.vcpu.add()
241 vcpu.id = 0
242 vcpu = node.vcpu.add()
243 vcpu.id = 1
244
245 node = vdu.guest_epa.numa_node_policy.node.add()
246 node.id = 1
247 node.memory_mb = 512
248 vcpu = node.vcpu.add()
249 vcpu.id = 2
250 vcpu = node.vcpu.add()
251 vcpu.id = 3
252
253 # specify the vswitch EPA
254 vdu.vswitch_epa.ovs_acceleration = 'DISABLED'
255 vdu.vswitch_epa.ovs_offload = 'DISABLED'
256
257 # Specify the hypervisor EPA
258 vdu.hypervisor_epa.type_yang = 'PREFER_KVM'
259
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']
268
269 if aws:
270 vdu.image = 'rift-ping-pong'
271 else:
272 vdu.image = image_name
273 if image_md5sum is not None:
274 vdu.image_checksum = image_md5sum
275
276 if mano_ut is True:
277 for i in range(num_ivlr_count):
278 internal_cp = vdu.internal_connection_point.add()
279 if vnfd.name.find("ping") >= 0:
280 cp_name = "ping"
281 else:
282 cp_name = "pong"
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_ref.add()
287 ivld_cp.id_ref = internal_cp.id
288
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'
293
294 # internal_interface.virtual_interface.vpci = '0000:00:1%d.0'%i
295
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)
300 if use_epa:
301 external_interface.virtual_interface.type_yang = 'VIRTIO'
302 else:
303 external_interface.virtual_interface.type_yang = 'VIRTIO'
304 # external_interface.virtual_interface.vpci = '0000:00:2%d.0'%i
305
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
311 if group.vdu_list:
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
316 else:
317 ### Add all VDUs to placement group
318 for vdu in vnfd.vdu:
319 member_vdu = placement_group.member_vdus.add()
320 member_vdu.member_vdu_ref = vdu.id
321
322
323 def write_to_file(self, outdir, output_format):
324 dirpath = "%s/%s" % (outdir, self.name)
325 if not os.path.exists(dirpath):
326 os.makedirs(dirpath)
327 super(VirtualNetworkFunction, self).write_to_file(['rw-project', 'vnfd', 'rw-vnfd'],
328 dirpath,
329 output_format)
330 self.add_scripts(outdir)
331
332 def add_scripts(self, outdir):
333 script_dir = os.path.join(outdir, self.name, 'cloud_init')
334 try:
335 os.makedirs(script_dir)
336 except OSError:
337 if not os.path.isdir(script_dir):
338 raise
339
340 if 'ping' in self.name:
341 script_file = os.path.join(script_dir, 'ping_cloud_init.cfg')
342 cfg = PING_USERDATA_FILE
343 else:
344 script_file = os.path.join(script_dir, 'pong_cloud_init.cfg')
345 cfg = PONG_USERDATA_FILE
346
347 with open(script_file, "w") as f:
348 f.write("{}".format(cfg))
349
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'
353
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)
360
361 dest_path = os.path.join(outdir, self.name, 'scripts')
362 os.makedirs(dest_path, exist_ok=True)
363
364 shutil.copy2(script_src, dest_path)
365
366
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 = []
373
374 def ping_config(self, mano_ut, use_ns_init_conf, use_vnf_init_conf):
375 suffix = ''
376 if mano_ut:
377 ping_cfg = r'''
378 #!/bin/bash
379
380 echo "!!!!!!!! Executed ping Configuration !!!!!!!!!"
381 '''
382 else:
383 ping_cfg = r'''
384 #!/bin/bash
385
386 # Rest API config
387 ping_mgmt_ip='<rw_mgmt_ip>'
388 ping_mgmt_port=18888
389
390 # VNF specific configuration
391 pong_server_ip='<rw_connection_point_name pong_vnfd%s/cp0>'
392 ping_rate=5
393 server_port=5555
394
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" \
399 -X POST \
400 -d "{\"ip\":\"$pong_server_ip\", \"port\":$server_port}" \
401 http://${ping_mgmt_ip}:${ping_mgmt_port}/api/v1/ping/server
402 rc=$?
403 if [ $rc -ne 0 ]
404 then
405 echo "Failed to set server info for ping!"
406 exit $rc
407 fi
408 ''' % suffix
409
410 if use_vnf_init_conf is False:
411 ping_cfg +='''
412 curl -D /dev/stdout \
413 -H "Accept: application/vnd.yang.data+xml" \
414 -H "Content-Type: application/vnd.yang.data+json" \
415 -X POST \
416 -d "{\"rate\":$ping_rate}" \
417 http://${ping_mgmt_ip}:${ping_mgmt_port}/api/v1/ping/rate
418 rc=$?
419 if [ $rc -ne 0 ]
420 then
421 echo "Failed to set ping rate!"
422 exit $rc
423 fi
424
425 '''
426 if use_ns_init_conf:
427 ping_cfg += "exit 0\n"
428 else:
429 ping_cfg +='''
430 output=$(curl -D /dev/stdout \
431 -H "Accept: application/vnd.yang.data+xml" \
432 -H "Content-Type: application/vnd.yang.data+json" \
433 -X POST \
434 -d "{\"enable\":true}" \
435 http://${ping_mgmt_ip}:${ping_mgmt_port}/api/v1/ping/adminstatus/state)
436 if [[ $output == *"Internal Server Error"* ]]
437 then
438 echo $output
439 exit 3
440 else
441 echo $output
442 fi
443
444 exit 0
445 '''
446 return ping_cfg
447
448 def pong_config(self, mano_ut, use_ns_init_conf):
449 suffix = ''
450 if mano_ut:
451 pong_cfg = r'''
452 #!/bin/bash
453
454 echo "!!!!!!!! Executed pong Configuration !!!!!!!!!"
455 '''
456 else:
457 pong_cfg = r'''
458 #!/bin/bash
459
460 # Rest API configuration
461 pong_mgmt_ip='<rw_mgmt_ip>'
462 pong_mgmt_port=18889
463 # username=<rw_username>
464 # password=<rw_password>
465
466 # VNF specific configuration
467 pong_server_ip='<rw_connection_point_name pong_vnfd%s/cp0>'
468 server_port=5555
469
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" \
474 -X POST \
475 -d "{\"ip\":\"$pong_server_ip\", \"port\":$server_port}" \
476 http://${pong_mgmt_ip}:${pong_mgmt_port}/api/v1/pong/server
477 rc=$?
478 if [ $rc -ne 0 ]
479 then
480 echo "Failed to set server(own) info for pong!"
481 exit $rc
482 fi
483
484 ''' % suffix
485
486 if use_ns_init_conf:
487 pong_cfg += "exit 0\n"
488 else:
489 pong_cfg +='''
490 curl -D /dev/stdout \
491 -H "Accept: application/vnd.yang.data+xml" \
492 -H "Content-Type: application/vnd.yang.data+json" \
493 -X POST \
494 -d "{\"enable\":true}" \
495 http://${pong_mgmt_ip}:${pong_mgmt_port}/api/v1/pong/adminstatus/state
496 rc=$?
497 if [ $rc -ne 0 ]
498 then
499 echo "Failed to enable pong service!"
500 exit $rc
501 fi
502
503 exit 0
504 '''
505 return pong_cfg
506
507 def pong_fake_juju_config(self, vnf_config):
508
509 if vnf_config:
510 # Select "script" configuration
511 vnf_config.juju.charm = 'clearwater-aio-proxy'
512
513 # Set the initital-config
514 vnf_config.create_initial_config_primitive()
515 init_config = VnfdYang.InitialConfigPrimitive.from_dict({
516 "seq": 1,
517 "name": "config",
518 "parameter": [
519 {"name": "proxied_ip", "value": "<rw_mgmt_ip>"},
520 ]
521 })
522 vnf_config.initial_config_primitive.append(init_config)
523
524 init_config_action = VnfdYang.InitialConfigPrimitive.from_dict({
525 "seq": 2,
526 "name": "action1",
527 "parameter": [
528 {"name": "Pong Connection Point", "value": "pong_vnfd/cp0"},
529 ]
530 })
531 vnf_config.initial_config_primitive.append(init_config_action)
532 init_config_action = VnfdYang.InitialConfigPrimitive.from_dict({
533 "seq": 3,
534 "name": "action2",
535 "parameter": [
536 {"name": "Ping Connection Point", "value": "ping_vnfd/cp0"},
537 ]
538 })
539 vnf_config.initial_config_primitive.append(init_config_action)
540
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({
546 "name": "config",
547 "parameter": [
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"},
552 ]
553 })
554 vnf_config.service_primitive.append(config)
555
556 config = VnfdYang.ServicePrimitive.from_dict({
557 "name": "create-update-user",
558 # "user-defined-script":"/tmp/test.py",
559 "parameter": [
560 {"name": "number", "data_type": "STRING", "mandatory": True},
561 {"name": "password", "data_type": "STRING", "mandatory": True},
562 ]
563 })
564 vnf_config.service_primitive.append(config)
565
566 config = VnfdYang.ServicePrimitive.from_dict({
567 "name": "delete-user",
568 "parameter": [
569 {"name": "number", "data_type": "STRING", "mandatory": True},
570 ]
571 })
572 vnf_config.service_primitive.append(config)
573
574 def default_config(self, const_vnfd, vnfd, mano_ut,
575 use_ns_init_conf,
576 use_vnf_init_conf):
577 vnf_config = vnfd.vnfd.vnf_configuration
578
579 vnf_config.config_attributes.config_priority = 0
580 vnf_config.config_attributes.config_delay = 0
581
582 # Select "script" configuration
583 vnf_config.script.script_type = 'bash'
584
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
589 if mano_ut is False:
590 vnf_config.config_attributes.config_delay = 60
591 else:
592 # This is PONG and inside mano_ut
593 # This is test only
594 vnf_config.config_attributes.config_delay = 10
595 # vnf_config.config_template = self.pong_config(vnf_config, use_ns_init_conf)
596
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,
600 use_ns_init_conf,
601 use_vnf_init_conf)
602 if use_vnf_init_conf:
603 vnf_config.initial_config_primitive.add().from_dict(
604 {
605 "seq": 1,
606 "name": "set ping rate",
607 "user_defined_script": "ping_set_rate.py",
608 "parameter": [
609 {
610 'name': 'rate',
611 'value': '5',
612 },
613 ],
614 }
615 )
616
617 def ns_config(self, nsd, vnfd_list, mano_ut):
618 # Used by scale group
619 if mano_ut:
620 nsd.service_primitive.add().from_dict(
621 {
622 "name": "ping config",
623 "user_defined_script": "{}".format(os.path.join(
624 os.environ['RIFT_ROOT'],
625 'modules/core/mano',
626 'examples/ping_pong_ns/rift/mano/examples',
627 'ping_config_ut.sh'))
628 })
629 else:
630 nsd.service_primitive.add().from_dict(
631 {
632 "name": "ping config",
633 "user_defined_script": "ping_config.py"
634 })
635
636 def ns_initial_config(self, nsd):
637 nsd.initial_config_primitive.add().from_dict(
638 {
639 "seq": 1,
640 "name": "start traffic",
641 "user_defined_script": "start_traffic.py",
642 "parameter": [
643 {
644 'name': 'userid',
645 'value': 'rift',
646 },
647 ],
648 }
649 )
650
651 def add_scale_group(self, scale_group):
652 self._scale_groups.append(scale_group)
653
654 def add_placement_group(self, placement_group):
655 self._placement_groups.append(placement_group)
656
657 def create_mon_params(self, vnfds):
658 NsdMonParam = NsdYang.YangData_RwProject_Project_NsdCatalog_Nsd_MonitoringParam
659 param_id = 1
660 for vnfd_obj in vnfds:
661 for mon_param in vnfd_obj.vnfd.monitoring_param:
662 nsd_monp = NsdMonParam.from_dict({
663 'id': str(param_id),
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}]
670 })
671
672 self.nsd.monitoring_param.append(nsd_monp)
673 param_id += 1
674
675
676
677
678 def compose(self, vnfd_list, cpgroup_list, mano_ut,
679 use_ns_init_conf=True,
680 use_vnf_init_conf=True,):
681
682 if mano_ut:
683 # Disable NS initial config primitive
684 use_ns_init_conf = False
685 use_vnf_init_conf = False
686
687 self.descriptor = RwNsdYang.YangData_RwProject_Project_NsdCatalog()
688 self.id = str(uuid.uuid1())
689 nsd = self.descriptor.nsd.add()
690 self.nsd = nsd
691 nsd.id = self.id
692 nsd.name = self.name
693 nsd.short_name = self.name
694 nsd.vendor = 'RIFT.io'
695 nsd.logo = 'rift_logo.png'
696 nsd.description = 'Toy NS'
697 nsd.version = '1.0'
698 nsd.input_parameter_xpath.append(
699 NsdYang.YangData_RwProject_Project_NsdCatalog_Nsd_InputParameterXpath(
700 xpath="/nsd:nsd-catalog/nsd:nsd/nsd:vendor",
701 )
702 )
703
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"
710
711 vld_id = 1
712 for cpgroup in cpgroup_list:
713 vld = nsd.vld.add()
714 vld.id = 'ping_pong_vld%s' % vld_id
715 vld_id += 1
716 vld.name = 'ping_pong_vld' # hard coded
717 vld.short_name = vld.name
718 vld.vendor = 'RIFT.io'
719 vld.description = 'Toy VL'
720 vld.version = '1.0'
721 vld.type_yang = 'ELAN'
722 vld.ip_profile_ref = 'InterVNFLink'
723 for cp in cpgroup:
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]
728
729 vnfd_index_map = {}
730 member_vnf_index = 1
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
736
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
742
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
747
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)
752
753 # Add NS initial config to start traffic
754 if use_ns_init_conf:
755 self.ns_initial_config(nsd)
756
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 = vnfd_index_map[vnfd]
765 member.count = count
766
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']
772
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])
782
783 # self.create_mon_params(vnfd_list)
784
785 def write_config(self, outdir, vnfds):
786
787 converter = config_data.ConfigPrimitiveConvertor()
788 yaml_data = converter.extract_nsd_config(self.nsd)
789
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)
794
795 if len(yaml_data):
796 with open('%s/%s.yaml' % (ns_config_dir, self.id), "w") as fh:
797 fh.write(yaml_data)
798
799 for i, vnfd in enumerate(vnfds, start=1):
800 yaml_data = converter.extract_vnfd_config(vnfd)
801
802 if len(yaml_data):
803 with open('%s/%s__%s.yaml' % (vnf_config_dir, vnfd.id, i), "w") as fh:
804 fh.write(yaml_data)
805
806 def write_initial_config_script(self, outdir):
807 script_name = 'start_traffic.py'
808
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)
815
816 dest_path = os.path.join(outdir, 'scripts')
817 os.makedirs(dest_path, exist_ok=True)
818
819 shutil.copy2(script_src, dest_path)
820
821 def write_to_file(self, outdir, output_format):
822 dirpath = os.path.join(outdir, self.name)
823 if not os.path.exists(dirpath):
824 os.makedirs(dirpath)
825
826 super(NetworkService, self).write_to_file(["rw-project", "nsd", "rw-nsd"],
827 dirpath,
828 output_format)
829
830 # Write the initial config script
831 self.write_initial_config_script(dirpath)
832
833
834 def get_ping_mon_params(path):
835 return [
836 {
837 'id': '1',
838 'name': 'ping-request-tx-count',
839 'http_endpoint_ref': path,
840 'json_query_method': "NAMEKEY",
841 'value_type': "INT",
842 'description': 'no of ping requests',
843 'group_tag': 'Group1',
844 'widget_type': 'COUNTER',
845 'units': 'packets'
846 },
847
848 {
849 'id': '2',
850 'name': 'ping-response-rx-count',
851 'http_endpoint_ref': path,
852 'json_query_method': "NAMEKEY",
853 'value_type': "INT",
854 'description': 'no of ping responses',
855 'group_tag': 'Group1',
856 'widget_type': 'COUNTER',
857 'units': 'packets'
858 },
859 ]
860
861
862 def get_pong_mon_params(path):
863 return [
864 {
865 'id': '1',
866 'name': 'ping-request-rx-count',
867 'http_endpoint_ref': path,
868 'json_query_method': "NAMEKEY",
869 'value_type': "INT",
870 'description': 'no of ping requests',
871 'group_tag': 'Group1',
872 'widget_type': 'COUNTER',
873 'units': 'packets'
874 },
875
876 {
877 'id': '2',
878 'name': 'ping-response-tx-count',
879 'http_endpoint_ref': path,
880 'json_query_method': "NAMEKEY",
881 'value_type': "INT",
882 'description': 'no of ping responses',
883 'group_tag': 'Group1',
884 'widget_type': 'COUNTER',
885 'units': 'packets'
886 },
887 ]
888
889
890 class ScaleGroup(object):
891 def __init__(self, name, min_count=1, max_count=1):
892 self.name = name
893 self.min_count = min_count
894 self.max_count = max_count
895 self.vnfd_count_map = {}
896 self.config_action = {}
897
898 def add_vnfd(self, vnfd, vnfd_count):
899 self.vnfd_count_map[vnfd] = vnfd_count
900
901 def add_config(self):
902 self.config_action['post_scale_out']= {'ns-config-primitive-name-ref':
903 'ping config'}
904
905 class PlacementGroup(object):
906 def __init__(self, name):
907 self.name = name
908 self.strategy = ''
909 self.requirement = ''
910
911 def add_strategy(self, strategy):
912 self.strategy = strategy
913
914 def add_requirement(self, requirement):
915 self.requirement = requirement
916
917 class NsdPlacementGroup(PlacementGroup):
918 def __init__(self, name):
919 self.vnfd_list = []
920 super(NsdPlacementGroup, self).__init__(name)
921
922 def add_member(self, vnfd):
923 self.vnfd_list.append(vnfd)
924
925
926 class VnfdPlacementGroup(PlacementGroup):
927 def __init__(self, name):
928 self.vdu_list = []
929 super(VnfdPlacementGroup, self).__init__(name)
930
931 def add_member(self, vdu):
932 self.vdu_list.append(vdu)
933
934
935
936
937 def generate_ping_pong_descriptors(fmt="json",
938 write_to_file=False,
939 out_dir="./",
940 pingcount=NUM_PING_INSTANCES,
941 external_vlr_count=1,
942 internal_vlr_count=1,
943 num_vnf_vms=1,
944 ping_md5sum=None,
945 pong_md5sum=None,
946 mano_ut=False,
947 use_scale_group=False,
948 ping_fmt=None,
949 pong_fmt=None,
950 nsd_fmt=None,
951 use_mon_params=True,
952 ping_userdata=None,
953 pong_userdata=None,
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,
959 ):
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
963 cpgroup_list = []
964 for i in range(external_vlr_count):
965 cpgroup_list.append([])
966
967 suffix = ''
968 ping = VirtualNetworkFunction("ping_vnfd%s" % (suffix), pingcount)
969 ping.use_vnf_init_conf = use_vnf_init_conf
970
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)
977
978 # ping = VirtualNetworkFunction("ping_vnfd", pingcount)
979 if not ping_userdata:
980 ping_userdata = PING_USERDATA_FILE
981
982 if ex_ping_userdata:
983 ping_userdata = '''\
984 {ping_userdata}
985 {ex_ping_userdata}
986 '''.format(
987 ping_userdata=ping_userdata,
988 ex_ping_userdata=ex_ping_userdata
989 )
990
991 ping.compose(
992 "Fedora-x86_64-20-20131211.1-sda-ping.qcow2",
993 ping_userdata,
994 use_ping_cloud_init_file,
995 "api/v1/ping/stats",
996 get_ping_mon_params("api/v1/ping/stats") if use_mon_params else [],
997 mon_port=18888,
998 mgmt_port=18888,
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,
1003 mano_ut=mano_ut,
1004 )
1005
1006 pong = VirtualNetworkFunction("pong_vnfd%s" % (suffix))
1007
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)
1014
1015
1016 # pong = VirtualNetworkFunction("pong_vnfd")
1017
1018 if not pong_userdata:
1019 pong_userdata = PONG_USERDATA_FILE
1020
1021 if ex_pong_userdata:
1022 pong_userdata = '''\
1023 {pong_userdata}
1024 {ex_pong_userdata}
1025 '''.format(
1026 pong_userdata=pong_userdata,
1027 ex_pong_userdata=ex_pong_userdata
1028 )
1029
1030
1031 pong.compose(
1032 "Fedora-x86_64-20-20131211.1-sda-pong.qcow2",
1033 pong_userdata,
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 [],
1037 mon_port=18889,
1038 mgmt_port=18889,
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,
1043 mano_ut=mano_ut,
1044 )
1045
1046 # Initialize the member VNF index
1047 member_vnf_index = 1
1048
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)
1053
1054 for idx in range(pingcount):
1055 cp_group.append((
1056 member_vnf_index,
1057 desc_id,
1058 filename,
1059 ))
1060
1061 member_vnf_index += 1
1062
1063 desc_id = pong.descriptor.vnfd[0].id
1064 filename = 'pong_vnfd{}/cp{}'.format(suffix, index)
1065
1066 cp_group.append((
1067 member_vnf_index,
1068 desc_id,
1069 filename,
1070 ))
1071
1072 member_vnf_index += 1
1073
1074 vnfd_list = [ping, pong]
1075
1076 nsd_catalog = NetworkService("ping_pong_nsd%s" % (suffix))
1077
1078 if use_scale_group:
1079 group = ScaleGroup("ping_group", max_count=10)
1080 group.add_vnfd(ping, 1)
1081 group.add_config()
1082 nsd_catalog.add_scale_group(group)
1083
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''')
1089
1090 for member_vnfd in vnfd_list:
1091 group.add_member(member_vnfd)
1092
1093 nsd_catalog.add_placement_group(group)
1094
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''')
1099
1100 for member_vnfd in vnfd_list:
1101 group.add_member(member_vnfd)
1102
1103 nsd_catalog.add_placement_group(group)
1104
1105
1106 nsd_catalog.compose(vnfd_list,
1107 cpgroup_list,
1108 mano_ut,
1109 use_ns_init_conf=use_ns_init_conf,
1110 use_vnf_init_conf=use_vnf_init_conf,)
1111
1112 if write_to_file:
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)
1117
1118 return (ping, pong, nsd_catalog)
1119
1120
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
1136 use_epa = args.epa
1137 aws = args.aws
1138 pingcount = args.pingcount
1139 use_ping_cloud_init_file = args.ping_cloud_init
1140 use_pong_cloud_init_file = args.pong_cloud_init
1141
1142 generate_ping_pong_descriptors(args.format, True, args.outdir, pingcount,
1143 ping_md5sum=args.ping_image_md5, pong_md5sum=args.pong_image_md5,
1144 mano_ut=False,
1145 use_scale_group=False,)
1146
1147 if __name__ == "__main__":
1148 main()