Merge from OSM SO master
[osm/SO.git] / rwlaunchpad / test / launchpad.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 logging
21 import os
22 import resource
23 import socket
24 import sys
25 import subprocess
26 import shlex
27 import shutil
28 import netifaces
29
30 from rift.rwlib.util import certs
31 import rift.rwcal.cloudsim
32 import rift.rwcal.cloudsim.net
33 import rift.vcs
34 import rift.vcs.core as core
35 import rift.vcs.demo
36 import rift.vcs.vms
37
38 import rift.rwcal.cloudsim
39 import rift.rwcal.cloudsim.net
40
41 from rift.vcs.ext import ClassProperty
42
43
44 logger = logging.getLogger(__name__)
45
46
47 class NsmTasklet(rift.vcs.core.Tasklet):
48 """
49 This class represents a network services manager tasklet.
50 """
51
52 def __init__(self, name='network-services-manager', uid=None,
53 config_ready=True,
54 recovery_action=core.RecoveryType.FAILCRITICAL.value,
55 data_storetype=core.DataStore.NOSTORE.value,
56 ):
57 """
58 Creates a NsmTasklet object.
59
60 Arguments:
61 name - the name of the tasklet
62 uid - a unique identifier
63 """
64 super(NsmTasklet, self).__init__(name=name, uid=uid,
65 config_ready=config_ready,
66 recovery_action=recovery_action,
67 data_storetype=data_storetype,
68 )
69
70 plugin_directory = ClassProperty('./usr/lib/rift/plugins/rwnsmtasklet')
71 plugin_name = ClassProperty('rwnsmtasklet')
72
73
74 class VnsTasklet(rift.vcs.core.Tasklet):
75 """
76 This class represents a network services manager tasklet.
77 """
78
79 def __init__(self, name='virtual-network-service', uid=None,
80 config_ready=True,
81 recovery_action=core.RecoveryType.FAILCRITICAL.value,
82 data_storetype=core.DataStore.NOSTORE.value,
83 ):
84 """
85 Creates a VnsTasklet object.
86
87 Arguments:
88 name - the name of the tasklet
89 uid - a unique identifier
90 """
91 super(VnsTasklet, self).__init__(name=name, uid=uid,
92 config_ready=config_ready,
93 recovery_action=recovery_action,
94 data_storetype=data_storetype,
95 )
96
97 plugin_directory = ClassProperty('./usr/lib/rift/plugins/rwvnstasklet')
98 plugin_name = ClassProperty('rwvnstasklet')
99
100
101 class VnfmTasklet(rift.vcs.core.Tasklet):
102 """
103 This class represents a virtual network function manager tasklet.
104 """
105
106 def __init__(self, name='virtual-network-function-manager', uid=None,
107 config_ready=True,
108 recovery_action=core.RecoveryType.FAILCRITICAL.value,
109 data_storetype=core.DataStore.NOSTORE.value,
110 ):
111 """
112 Creates a VnfmTasklet object.
113
114 Arguments:
115 name - the name of the tasklet
116 uid - a unique identifier
117 """
118 super(VnfmTasklet, self).__init__(name=name, uid=uid,
119 config_ready=config_ready,
120 recovery_action=recovery_action,
121 data_storetype=data_storetype,
122 )
123
124 plugin_directory = ClassProperty('./usr/lib/rift/plugins/rwvnfmtasklet')
125 plugin_name = ClassProperty('rwvnfmtasklet')
126
127
128 class ResMgrTasklet(rift.vcs.core.Tasklet):
129 """
130 This class represents a Resource Manager tasklet.
131 """
132
133 def __init__(self, name='Resource-Manager', uid=None,
134 config_ready=True,
135 recovery_action=core.RecoveryType.FAILCRITICAL.value,
136 data_storetype=core.DataStore.NOSTORE.value,
137 ):
138 """
139 Creates a ResMgrTasklet object.
140
141 Arguments:
142 name - the name of the tasklet
143 uid - a unique identifier
144 """
145 super(ResMgrTasklet, self).__init__(name=name, uid=uid,
146 config_ready=config_ready,
147 recovery_action=recovery_action,
148 data_storetype=data_storetype,
149 )
150
151 plugin_directory = ClassProperty('./usr/lib/rift/plugins/rwresmgrtasklet')
152 plugin_name = ClassProperty('rwresmgrtasklet')
153
154
155 class ImageMgrTasklet(rift.vcs.core.Tasklet):
156 """
157 This class represents a Image Manager tasklet.
158 """
159
160 def __init__(self, name='Image-Manager', uid=None,
161 config_ready=True,
162 recovery_action=core.RecoveryType.FAILCRITICAL.value,
163 data_storetype=core.DataStore.NOSTORE.value,
164 ):
165 """
166 Creates a Image Manager Tasklet object.
167
168 Arguments:
169 name - the name of the tasklet
170 uid - a unique identifier
171 """
172 super(ImageMgrTasklet, self).__init__(
173 name=name, uid=uid,
174 config_ready=config_ready,
175 recovery_action=recovery_action,
176 data_storetype=data_storetype,
177 )
178
179 plugin_directory = ClassProperty('./usr/lib/rift/plugins/rwimagemgrtasklet')
180 plugin_name = ClassProperty('rwimagemgrtasklet')
181
182
183 class MonitorTasklet(rift.vcs.core.Tasklet):
184 """
185 This class represents a tasklet that is used to monitor NFVI metrics.
186 """
187
188 def __init__(self, name='nfvi-metrics-monitor', uid=None,
189 config_ready=True,
190 recovery_action=core.RecoveryType.FAILCRITICAL.value,
191 data_storetype=core.DataStore.NOSTORE.value,
192 ):
193 """
194 Creates a MonitorTasklet object.
195
196 Arguments:
197 name - the name of the tasklet
198 uid - a unique identifier
199
200 """
201 super(MonitorTasklet, self).__init__(name=name, uid=uid,
202 config_ready=config_ready,
203 recovery_action=recovery_action,
204 data_storetype=data_storetype,
205 )
206
207 plugin_directory = ClassProperty('./usr/lib/rift/plugins/rwmonitor')
208 plugin_name = ClassProperty('rwmonitor')
209
210 class RedisServer(rift.vcs.NativeProcess):
211 def __init__(self, name="RW.Redis.Server",
212 config_ready=True,
213 recovery_action=core.RecoveryType.FAILCRITICAL.value,
214 data_storetype=core.DataStore.NOSTORE.value,
215 ):
216 super(RedisServer, self).__init__(
217 name=name,
218 exe="/usr/bin/redis-server",
219 config_ready=config_ready,
220 recovery_action=recovery_action,
221 data_storetype=data_storetype,
222 )
223
224 @property
225 def args(self):
226 return "./usr/bin/active_redis.conf --port 9999"
227
228
229 class MonitoringParameterTasklet(rift.vcs.core.Tasklet):
230 """
231 This class represents a tasklet that is used to generate monitoring
232 parameters.
233 """
234
235 def __init__(self, name='Monitoring-Parameter', uid=None,
236 config_ready=True,
237 recovery_action=core.RecoveryType.FAILCRITICAL.value,
238 data_storetype=core.DataStore.NOSTORE.value,
239 ):
240 """
241 Creates a MonitoringParameterTasklet object.
242
243 Arguments:
244 name - the name of the tasklet
245 uid - a unique identifier
246
247 """
248 super(MonitoringParameterTasklet, self).__init__(name=name, uid=uid,
249 config_ready=config_ready,
250 recovery_action=recovery_action,
251 data_storetype=data_storetype,
252 )
253
254 plugin_directory = ClassProperty('./usr/lib/rift/plugins/rwmonparam')
255 plugin_name = ClassProperty('rwmonparam')
256
257
258 class AutoscalerTasklet(rift.vcs.core.Tasklet):
259 """
260 This class represents a tasklet that is used to generate monitoring
261 parameters.
262 """
263
264 def __init__(self, name='Autoscaler', uid=None,
265 config_ready=True,
266 recovery_action=core.RecoveryType.FAILCRITICAL.value,
267 data_storetype=core.DataStore.NOSTORE.value,
268 ):
269 """
270 Creates a MonitoringParameterTasklet object.
271
272 Arguments:
273 name - the name of the tasklet
274 uid - a unique identifier
275
276 """
277 super(AutoscalerTasklet, self).__init__(name=name, uid=uid,
278 config_ready=config_ready,
279 recovery_action=recovery_action,
280 data_storetype=data_storetype,
281 )
282
283 plugin_directory = ClassProperty('./usr/lib/rift/plugins/rwautoscaler')
284 plugin_name = ClassProperty('rwautoscaler')
285
286 class StagingManagerTasklet(rift.vcs.core.Tasklet):
287 """
288 A class that provide a simple staging area for all tasklets
289 """
290
291 def __init__(self, name='StagingManager', uid=None,
292 config_ready=True,
293 recovery_action=core.RecoveryType.FAILCRITICAL.value,
294 data_storetype=core.DataStore.NOSTORE.value,
295 ):
296 """
297 Creates a StagingMangerTasklet object.
298
299 Arguments:
300 name - the name of the tasklet
301 uid - a unique identifier
302
303 """
304 super(StagingManagerTasklet, self).__init__(name=name, uid=uid,
305 config_ready=config_ready,
306 recovery_action=recovery_action,
307 data_storetype=data_storetype,
308 )
309
310 plugin_directory = ClassProperty('./usr/lib/rift/plugins/rwstagingmgr')
311 plugin_name = ClassProperty('rwstagingmgr')
312
313 def get_ui_ssl_args():
314 """Returns the SSL parameter string for launchpad UI processes"""
315
316 try:
317 use_ssl, certfile_path, keyfile_path = certs.get_bootstrap_cert_and_key()
318 except certs.BootstrapSslMissingException:
319 logger.error('No bootstrap certificates found. Disabling UI SSL')
320 use_ssl = False
321
322 # If we're not using SSL, no SSL arguments are necessary
323 if not use_ssl:
324 return ""
325
326 return "--enable-https --keyfile-path=%s --certfile-path=%s" % (keyfile_path, certfile_path)
327
328
329 class UIServer(rift.vcs.NativeProcess):
330 def __init__(self, name="RW.MC.UI",
331 config_ready=True,
332 recovery_action=core.RecoveryType.FAILCRITICAL.value,
333 data_storetype=core.DataStore.NOSTORE.value,
334 ):
335 super(UIServer, self).__init__(
336 name=name,
337 exe="./usr/share/rw.ui/skyquake/scripts/launch_ui.sh",
338 config_ready=config_ready,
339 recovery_action=recovery_action,
340 data_storetype=data_storetype,
341 )
342
343 @property
344 def args(self):
345 return get_ui_ssl_args()
346
347 class ConfigManagerTasklet(rift.vcs.core.Tasklet):
348 """
349 This class represents a Resource Manager tasklet.
350 """
351
352 def __init__(self, name='Configuration-Manager', uid=None,
353 config_ready=True,
354 recovery_action=core.RecoveryType.FAILCRITICAL.value,
355 data_storetype=core.DataStore.NOSTORE.value,
356 ):
357 """
358 Creates a ConfigManagerTasklet object.
359
360 Arguments:
361 name - the name of the tasklet
362 uid - a unique identifier
363 """
364 super(ConfigManagerTasklet, self).__init__(name=name, uid=uid,
365 config_ready=config_ready,
366 recovery_action=recovery_action,
367 data_storetype=data_storetype,
368 )
369
370 plugin_directory = ClassProperty('./usr/lib/rift/plugins/rwconmantasklet')
371 plugin_name = ClassProperty('rwconmantasklet')
372
373
374 class ProjectMgrManoTasklet(rift.vcs.core.Tasklet):
375 """
376 This class represents a Resource Manager tasklet.
377 """
378
379 def __init__(self, name='Project-Manager-Mano', uid=None,
380 config_ready=True,
381 recovery_action=core.RecoveryType.FAILCRITICAL.value,
382 data_storetype=core.DataStore.NOSTORE.value,
383 ):
384 """
385 Creates a ProjectMgrManoTasklet object.
386
387 Arguments:
388 name - the name of the tasklet
389 uid - a unique identifier
390 """
391 super(ProjectMgrManoTasklet, self).__init__(name=name, uid=uid,
392 config_ready=config_ready,
393 recovery_action=recovery_action,
394 data_storetype=data_storetype,
395 )
396
397 plugin_directory = ClassProperty('./usr/lib/rift/plugins/rwprojectmano')
398 plugin_name = ClassProperty('rwprojectmano')
399
400
401 class PackageManagerTasklet(rift.vcs.core.Tasklet):
402 """
403 This class represents a Resource Manager tasklet.
404 """
405
406 def __init__(self, name='Package-Manager', uid=None,
407 config_ready=True,
408 recovery_action=core.RecoveryType.FAILCRITICAL.value,
409 data_storetype=core.DataStore.NOSTORE.value,
410 ):
411 """
412 Creates a PackageManager object.
413
414 Arguments:
415 name - the name of the tasklet
416 uid - a unique identifier
417 """
418 super(PackageManagerTasklet, self).__init__(name=name, uid=uid,
419 config_ready=config_ready,
420 recovery_action=recovery_action,
421 data_storetype=data_storetype,
422 )
423
424 plugin_directory = ClassProperty('./usr/lib/rift/plugins/rwpkgmgr')
425 plugin_name = ClassProperty('rwpkgmgr')
426
427 class GlanceServer(rift.vcs.NativeProcess):
428 def __init__(self, name="glance-image-catalog",
429 config_ready=True,
430 recovery_action=core.RecoveryType.FAILCRITICAL.value,
431 data_storetype=core.DataStore.NOSTORE.value,
432 ):
433 super(GlanceServer, self).__init__(
434 name=name,
435 exe="./usr/bin/glance_start_wrapper",
436 config_ready=config_ready,
437 recovery_action=recovery_action,
438 data_storetype=data_storetype,
439 )
440
441 @property
442 def args(self):
443 return "./etc/glance"
444
445
446 class Demo(rift.vcs.demo.Demo):
447 def __init__(self, no_ui=False, ha_mode=None, mgmt_ip_list=[], test_name=None):
448 procs = [
449 ConfigManagerTasklet(),
450 GlanceServer(),
451 rift.vcs.DtsRouterTasklet(),
452 rift.vcs.MsgBrokerTasklet(),
453 rift.vcs.RestconfTasklet(),
454 rift.vcs.RiftCli(),
455 rift.vcs.uAgentTasklet(),
456 rift.vcs.IdentityManagerTasklet(),
457 rift.vcs.ProjectManagerTasklet(),
458 ProjectMgrManoTasklet(),
459 rift.vcs.Launchpad(),
460 ]
461
462 standby_procs = [
463 RedisServer(),
464 rift.vcs.DtsRouterTasklet(),
465 rift.vcs.MsgBrokerTasklet(),
466 ]
467
468 datastore = core.DataStore.BDB.value
469 if ha_mode:
470 procs.append(RedisServer())
471 datastore = core.DataStore.REDIS.value
472
473 if not no_ui:
474 procs.append(UIServer())
475
476 restart_procs = [
477 VnfmTasklet(recovery_action=core.RecoveryType.RESTART.value, data_storetype=datastore),
478 VnsTasklet(recovery_action=core.RecoveryType.RESTART.value, data_storetype=datastore),
479 # MonitorTasklet(recovery_action=core.RecoveryType.RESTART.value, data_storetype=datastore),
480 MonitoringParameterTasklet(recovery_action=core.RecoveryType.RESTART.value, data_storetype=datastore),
481 NsmTasklet(recovery_action=core.RecoveryType.RESTART.value, data_storetype=datastore),
482 ResMgrTasklet(recovery_action=core.RecoveryType.RESTART.value, data_storetype=datastore),
483 ImageMgrTasklet(recovery_action=core.RecoveryType.RESTART.value, data_storetype=datastore),
484 AutoscalerTasklet(recovery_action=core.RecoveryType.RESTART.value, data_storetype=datastore),
485 PackageManagerTasklet(recovery_action=core.RecoveryType.RESTART.value, data_storetype=datastore),
486 StagingManagerTasklet(recovery_action=core.RecoveryType.RESTART.value, data_storetype=datastore),
487 ProjectMgrManoTasklet(recovery_action=core.RecoveryType.RESTART.value, data_storetype=datastore),
488 ]
489
490 if not mgmt_ip_list or len(mgmt_ip_list) == 0:
491 mgmt_ip_list.append("127.0.0.1")
492
493 colony = rift.vcs.core.Colony(name='top', uid=1)
494
495 lead_lp_vm = rift.vcs.VirtualMachine(
496 name='vm-launchpad-1',
497 ip=mgmt_ip_list[0],
498 procs=procs,
499 restart_procs=restart_procs,
500 )
501 lead_lp_vm.leader = True
502 colony.append(lead_lp_vm)
503
504 if ha_mode:
505 stby_lp_vm = rift.vcs.VirtualMachine(
506 name='launchpad-vm-2',
507 ip=mgmt_ip_list[1],
508 procs=standby_procs,
509 start=False,
510 )
511 # WA to Agent mode_active flag reset
512 stby_lp_vm.add_tasklet(rift.vcs.uAgentTasklet(), mode_active=False)
513 colony.append(stby_lp_vm)
514
515 if ha_mode == "LSS":
516 stby_lp_vm_2 = rift.vcs.VirtualMachine(
517 name='launchpad-vm-3',
518 ip=mgmt_ip_list[2],
519 procs=standby_procs,
520 start=False,
521 )
522 stby_lp_vm_2.add_tasklet(rift.vcs.uAgentTasklet(), mode_active=False)
523 colony.append(stby_lp_vm_2)
524
525 sysinfo = rift.vcs.SystemInfo(
526 mode='ethsim',
527 zookeeper=rift.vcs.manifest.RaZookeeper(master_ip=mgmt_ip_list[0]),
528 colonies=[colony],
529 multi_broker=True,
530 multi_dtsrouter=True,
531 mgmt_ip_list=mgmt_ip_list,
532 test_name=test_name,
533 )
534
535 super(Demo, self).__init__(
536 # Construct the system. This system consists of 1 cluster in 1
537 # colony. The master cluster houses CLI and management VMs
538 sysinfo = sysinfo,
539
540 # Define the generic portmap.
541 port_map = {},
542
543 # Define a mapping from the placeholder logical names to the real
544 # port names for each of the different modes supported by this demo.
545 port_names = {
546 'ethsim': {
547 },
548 'pci': {
549 }
550 },
551
552 # Define the connectivity between logical port names.
553 port_groups = {},
554 )
555
556
557 def main(argv=sys.argv[1:]):
558 logging.basicConfig(format='%(asctime)-15s %(levelname)s %(message)s')
559
560 # Create a parser which includes all generic demo arguments
561 parser = rift.vcs.demo.DemoArgParser()
562 parser.add_argument("--no-ui", action='store_true')
563 args = parser.parse_args(argv)
564
565 # Disable loading any kernel modules for the launchpad VM
566 # since it doesn't need it and it will fail within containers
567 os.environ["NO_KERNEL_MODS"] = "1"
568
569 cleanup_dir_name = None
570 if os.environ["INSTALLDIR"] in ["/", "/home/rift", "/home/rift/.install",
571 "/usr/rift/build/fc20_debug/install/usr/rift", "/usr/rift"]:
572 cleanup_dir_name = os.environ["INSTALLDIR"] + "/var/rift/"
573
574 if args.test_name and not cleanup_dir_name:
575 cleanup_dir_name = "find {rift_install}/var/rift -name '*{pattern}*' -type d".format( \
576 rift_install=os.environ['RIFT_INSTALL'],
577 pattern = args.test_name)
578 try:
579 cleanup_dir_name = subprocess.check_output(cleanup_dir_name, shell=True)
580 cleanup_dir_name = cleanup_dir_name[:-1].decode("utf-8") + "/"
581 except Exception as e:
582 print ("Directory not found exception occurred. Probably running for first time")
583 print ("Zookeper cleanup cmd = {}".format(cleanup_dir_name))
584 else:
585 if not cleanup_dir_name:
586 cleanup_dir_name = os.environ["INSTALLDIR"] + "/"
587
588 # Remove the persistent Redis data
589 try:
590 for f in os.listdir(cleanup_dir_name):
591 if f.endswith(".aof") or f.endswith(".rdb"):
592 os.remove(os.path.join(cleanup_dir_name, f))
593
594 # Remove the persistant DTS recovery files
595 for f in os.listdir(cleanup_dir_name):
596 if f.endswith(".db"):
597 os.remove(os.path.join(cleanup_dir_name, f))
598
599 shutil.rmtree(os.path.join(cleanup_dir_name, "zk/server-1"))
600 shutil.rmtree(os.path.join(os.environ["INSTALLDIR"], "var/rift/tmp*"))
601 except FileNotFoundError as e:
602 pass
603 except Exception as e:
604 print ("Error while cleanup: {}".format(str(e)))
605
606 ha_mode = args.ha_mode
607 mgmt_ip_list = [] if not args.mgmt_ip_list else args.mgmt_ip_list
608
609 #load demo info and create Demo object
610 demo = Demo(args.no_ui, ha_mode, mgmt_ip_list, args.test_name)
611
612 system = rift.vcs.demo.prepared_system_from_demo_and_args(
613 demo, args,
614 northbound_listing=["platform_schema_listing.txt",
615 "platform_mgmt_schema_listing.txt",
616 "cli_launchpad_schema_listing.txt"],
617 netconf_trace_override=True)
618
619 # Search for externally accessible IP address with netifaces
620 gateways = netifaces.gateways()
621 # Check for default route facing interface and then get its ip address
622 if 'default' in gateways:
623 interface = gateways['default'][netifaces.AF_INET][1]
624 confd_ip = netifaces.ifaddresses(interface)[netifaces.AF_INET][0]['addr']
625 else:
626 # no default gateway. Revert to 127.0.0.1
627 confd_ip = "127.0.0.1"
628 # TODO: This need to be changed when launchpad starts running on multiple VMs
629 rift.vcs.logger.configure_sink(config_file=None, confd_ip=confd_ip)
630
631 # Start the prepared system
632 system.start()
633
634
635 if __name__ == "__main__":
636 resource.setrlimit(resource.RLIMIT_CORE, (resource.RLIM_INFINITY, resource.RLIM_INFINITY) )
637 os.system('/usr/rift/bin/UpdateHostsFile')
638 try:
639 main()
640 except rift.vcs.demo.ReservationError:
641 print("ERROR: unable to retrieve a list of IP addresses from the reservation system")
642 sys.exit(1)
643 except rift.vcs.demo.MissingModeError:
644 print("ERROR: you need to provide a mode to run the script")
645 sys.exit(1)
646 finally:
647 os.system("stty sane")