3 # Copyright (c) 2016 Orange and others.
5 # All rights reserved. This program and the accompanying materials
6 # are made available under the terms of the Apache License, Version 2.0
7 # which accompanies this distribution, and is available at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 """Orchestra OpenIMS testcase implementation."""
20 import functest.core.vnf as vnf
21 import functest.utils.openstack_utils as os_utils
22 from functest.utils.constants import CONST
24 from org.openbaton.cli.errors.errors import NfvoException
25 from org.openbaton.cli.agents.agents import MainAgent
26 from snaps.config.flavor import FlavorConfig
27 from snaps.config.image import ImageConfig
28 from snaps.config.network import NetworkConfig, PortConfig, SubnetConfig
29 from snaps.config.router import RouterConfig
30 from snaps.config.security_group import (
31 Direction, Protocol, SecurityGroupConfig, SecurityGroupRuleConfig)
32 from snaps.config.vm_inst import VmInstanceConfig
33 from snaps.openstack.utils import keystone_utils
34 from snaps.openstack.create_image import OpenStackImage
35 from snaps.openstack.create_flavor import OpenStackFlavor
36 from snaps.openstack.create_security_group import OpenStackSecurityGroup
37 from snaps.openstack.create_network import OpenStackNetwork
38 from snaps.openstack.create_router import OpenStackRouter
39 from snaps.openstack.create_instance import OpenStackVmInstance
41 from functest.opnfv_tests.openstack.snaps import snaps_utils
44 __author__ = "Pauls, Michael <michael.pauls@fokus.fraunhofer.de>"
45 # ----------------------------------------------------------
49 # -----------------------------------------------------------
52 def get_config(parameter, file_path):
56 Returns the value of a given parameter in file.yaml
57 parameter must be given in string format with dots
58 Example: general.openstack.image_name
60 with open(file_path) as config_file:
61 file_yaml = yaml.safe_load(config_file)
64 for element in parameter.split("."):
65 value = value.get(element)
67 raise ValueError("The parameter %s is not defined in"
68 " reporting.yaml", parameter)
72 def servertest(host, port):
73 """Method to test that a server is reachable at IP:port"""
74 args = socket.getaddrinfo(host, port, socket.AF_INET, socket.SOCK_STREAM)
75 for family, socktype, proto, canonname, sockaddr in args:
76 sock = socket.socket(family, socktype, proto)
78 sock.connect(sockaddr)
86 def get_userdata(orchestrator=dict):
87 """Build userdata for Open Baton machine"""
88 userdata = "#!/bin/bash\n"
89 userdata += "echo \"Executing userdata...\"\n"
90 userdata += "set -x\n"
91 userdata += "set -e\n"
92 userdata += "echo \"Set nameserver to '8.8.8.8'...\"\n"
93 userdata += "echo \"nameserver 8.8.8.8\" >> /etc/resolv.conf\n"
94 userdata += "echo \"Install curl...\"\n"
95 userdata += "apt-get install curl\n"
96 userdata += "echo \"Inject public key...\"\n"
97 userdata += ("echo \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCuPXrV3"
98 "geeHc6QUdyUr/1Z+yQiqLcOskiEGBiXr4z76MK4abiFmDZ18OMQlc"
99 "fl0p3kS0WynVgyaOHwZkgy/DIoIplONVr2CKBKHtPK+Qcme2PVnCtv"
100 "EqItl/FcD+1h5XSQGoa+A1TSGgCod/DPo+pes0piLVXP8Ph6QS1k7S"
101 "ic7JDeRQ4oT1bXYpJ2eWBDMfxIWKZqcZRiGPgMIbJ1iEkxbpeaAd9O"
102 "4MiM9nGCPESmed+p54uYFjwEDlAJZShcAZziiZYAvMZhvAhe6USljc"
103 "7YAdalAnyD/jwCHuwIrUw/lxo7UdNCmaUxeobEYyyFA1YVXzpNFZya"
104 "XPGAAYIJwEq/ openbaton@opnfv\" >> /home/ubuntu/.ssh/aut"
106 userdata += "echo \"Download bootstrap...\"\n"
107 userdata += ("curl -s %s "
108 "> ./bootstrap\n" % orchestrator['bootstrap']['url'])
109 userdata += ("curl -s %s" "> ./config_file\n" %
110 orchestrator['bootstrap']['config']['url'])
111 userdata += ("echo \"Disable usage of mysql...\"\n")
112 userdata += "sed -i s/mysql=.*/mysql=no/g /config_file\n"
113 userdata += ("echo \"Setting 'rabbitmq_broker_ip' to '%s'\"\n"
114 % orchestrator['details']['fip'].ip)
115 userdata += ("sed -i s/rabbitmq_broker_ip=localhost/rabbitmq_broker_ip"
116 "=%s/g /config_file\n" % orchestrator['details']['fip'].ip)
117 userdata += "echo \"Set autostart of components to 'false'\"\n"
118 userdata += "export OPENBATON_COMPONENT_AUTOSTART=false\n"
119 userdata += "echo \"Execute bootstrap...\"\n"
120 bootstrap = "sh ./bootstrap release -configFile=./config_file"
121 userdata += bootstrap + "\n"
122 userdata += "echo \"Setting 'nfvo.plugin.timeout' to '300000'\"\n"
123 userdata += ("echo \"nfvo.plugin.timeout=600000\" >> "
124 "/etc/openbaton/openbaton-nfvo.properties\n")
126 "wget %s -O /etc/openbaton/openbaton-vnfm-generic-user-data.sh\n" %
127 orchestrator['gvnfm']['userdata']['url'])
128 userdata += "sed -i '113i"'\ \ \ \ '"sleep 60' " \
129 "/etc/openbaton/openbaton-vnfm-generic-user-data.sh\n"
130 userdata += ("sed -i s/nfvo.marketplace.port=8082/nfvo.marketplace."
131 "port=8080/g /etc/openbaton/openbaton-nfvo.properties\n")
132 userdata += "echo \"Starting NFVO\"\n"
133 userdata += "service openbaton-nfvo restart\n"
134 userdata += "echo \"Starting Generic VNFM\"\n"
135 userdata += "service openbaton-vnfm-generic restart\n"
136 userdata += "echo \"...end of userdata...\"\n"
140 class OpenImsVnf(vnf.VnfOnBoarding):
141 """OpenIMS VNF deployed with openBaton orchestrator"""
143 # logger = logging.getLogger(__name__)
145 def __init__(self, **kwargs):
146 if "case_name" not in kwargs:
147 kwargs["case_name"] = "orchestra_openims"
148 super(OpenImsVnf, self).__init__(**kwargs)
149 self.logger = logging.getLogger("functest.ci.run_tests.orchestra")
150 self.logger.info("kwargs %s", (kwargs))
152 self.case_dir = pkg_resources.resource_filename(
153 'functest', 'opnfv_tests/vnf/ims/')
154 self.data_dir = CONST.__getattribute__('dir_ims_data')
155 self.test_dir = CONST.__getattribute__('dir_repo_vims_test')
156 self.created_resources = []
157 self.logger.info("%s VNF onboarding test starting", self.case_name)
160 self.config = CONST.__getattribute__(
161 'vnf_{}_config'.format(self.case_name))
162 except BaseException:
163 raise Exception("Orchestra VNF config file not found")
164 config_file = self.case_dir + self.config
167 get_config("mano", config_file),
170 self.logger.debug("Orchestrator configuration %s", self.mano)
172 self.details['orchestrator'] = dict(
173 name=self.mano['name'],
174 version=self.mano['version'],
180 get_config(self.case_name, config_file),
182 self.logger.debug("VNF configuration: %s", self.vnf)
184 self.details['vnf'] = dict(
185 name=self.vnf['name'],
188 self.details['test_vnf'] = dict(
192 # Orchestra base Data directory creation
193 if not os.path.exists(self.data_dir):
194 os.makedirs(self.data_dir)
196 self.images = get_config("tenant_images.orchestrator", config_file)
197 self.images.update(get_config("tenant_images.%s" %
198 self.case_name, config_file))
201 """Prepare testscase (Additional pre-configuration steps)."""
202 super(OpenImsVnf, self).prepare()
204 public_auth_url = keystone_utils.get_endpoint(
205 self.snaps_creds, 'identity')
207 self.logger.info("Additional pre-configuration steps")
209 "tenant": self.snaps_creds.project_name,
210 "username": self.snaps_creds.username,
211 "password": self.snaps_creds.password,
212 "auth_url": public_auth_url
214 self.prepare_images()
215 self.prepare_flavor()
216 self.prepare_security_groups()
217 self.prepare_network()
218 self.prepare_floating_ip()
220 def prepare_images(self):
221 """Upload images if they doen't exist yet"""
222 self.logger.info("Upload images if they doen't exist yet")
223 for image_name, image_file in self.images.iteritems():
224 self.logger.info("image: %s, file: %s", image_name, image_file)
225 if image_file and image_name:
226 image = OpenStackImage(
228 ImageConfig(name=image_name,
231 image_file=image_file,
234 # self.created_resources.append(image);
236 def prepare_security_groups(self):
237 """Create Open Baton security group if it doesn't exist yet"""
239 "Creating security group for Open Baton if not yet existing...")
242 SecurityGroupRuleConfig(
243 sec_grp_name="orchestra-sec-group-allowall",
244 direction=Direction.ingress,
245 protocol=Protocol.tcp,
247 port_range_max=65535))
249 SecurityGroupRuleConfig(
250 sec_grp_name="orchestra-sec-group-allowall",
251 direction=Direction.egress,
252 protocol=Protocol.tcp,
254 port_range_max=65535))
256 SecurityGroupRuleConfig(
257 sec_grp_name="orchestra-sec-group-allowall",
258 direction=Direction.ingress,
259 protocol=Protocol.udp,
261 port_range_max=65535))
263 SecurityGroupRuleConfig(
264 sec_grp_name="orchestra-sec-group-allowall",
265 direction=Direction.egress,
266 protocol=Protocol.udp,
268 port_range_max=65535))
269 security_group = OpenStackSecurityGroup(
272 name="orchestra-sec-group-allowall",
273 rule_settings=sg_rules))
275 security_group_info = security_group.create()
276 self.created_resources.append(security_group)
277 self.mano['details']['sec_group'] = security_group_info.name
279 "Security group orchestra-sec-group-allowall prepared")
281 def prepare_flavor(self):
282 """Create Open Baton flavor if it doesn't exist yet"""
284 "Create Flavor for Open Baton NFVO if not yet existing")
286 flavor_settings = FlavorConfig(
287 name=self.mano['requirements']['flavor']['name'],
288 ram=self.mano['requirements']['flavor']['ram_min'],
289 disk=self.mano['requirements']['flavor']['disk'],
290 vcpus=self.mano['requirements']['flavor']['vcpus'])
291 flavor = OpenStackFlavor(self.snaps_creds, flavor_settings)
292 flavor_info = flavor.create()
293 self.created_resources.append(flavor)
294 self.mano['details']['flavor'] = {}
295 self.mano['details']['flavor']['name'] = flavor_settings.name
296 self.mano['details']['flavor']['id'] = flavor_info.id
298 def prepare_network(self):
299 """Create network/subnet/router if they doen't exist yet"""
301 "Creating network/subnet/router if they doen't exist yet...")
302 subnet_settings = SubnetConfig(
305 cidr="192.168.100.0/24")
306 network_settings = NetworkConfig(
309 subnet_settings=[subnet_settings])
310 orchestra_network = OpenStackNetwork(
311 self.snaps_creds, network_settings)
312 orchestra_network_info = orchestra_network.create()
313 self.mano['details']['network'] = {}
314 self.mano['details']['network']['id'] = orchestra_network_info.id
315 self.mano['details']['network']['name'] = orchestra_network_info.name
316 self.mano['details']['external_net_name'] = \
317 snaps_utils.get_ext_net_name(self.snaps_creds)
318 self.created_resources.append(orchestra_network)
319 orchestra_router = OpenStackRouter(
324 external_gateway=self.mano['details']['external_net_name'],
326 subnet_settings.name]))
327 orchestra_router.create()
328 self.created_resources.append(orchestra_router)
329 self.logger.info("Created network and router for Open Baton NFVO...")
331 def prepare_floating_ip(self):
332 """Select/Create Floating IP if it doesn't exist yet"""
333 self.logger.info("Retrieving floating IP for Open Baton NFVO")
334 neutron_client = snaps_utils.neutron_utils.neutron_client(
336 # Finding Tenant ID to check to which tenant the Floating IP belongs
337 tenant_id = os_utils.get_tenant_id(
338 os_utils.get_keystone_client(self.creds),
340 # Use os_utils to retrieve complete information of Floating IPs
341 floating_ips = os_utils.get_floating_ips(neutron_client)
343 # Filter Floating IPs with tenant id
344 for floating_ip in floating_ips:
345 # self.logger.info("Floating IP: %s", floating_ip)
346 if floating_ip.get('tenant_id') == tenant_id:
347 my_floating_ips.append(floating_ip.get('floating_ip_address'))
348 # Select if Floating IP exist else create new one
349 if len(my_floating_ips) >= 1:
350 # Get Floating IP object from snaps for clean up
351 snaps_floating_ips = snaps_utils.neutron_utils.get_floating_ips(
353 for my_floating_ip in my_floating_ips:
354 for snaps_floating_ip in snaps_floating_ips:
355 if snaps_floating_ip.ip == my_floating_ip:
356 self.mano['details']['fip'] = snaps_floating_ip
358 "Selected floating IP for Open Baton NFVO %s",
359 (self.mano['details']['fip'].ip))
361 if self.mano['details']['fip'] is not None:
364 self.logger.info("Creating floating IP for Open Baton NFVO")
365 self.mano['details']['fip'] = (
366 snaps_utils.neutron_utils. create_floating_ip(
367 neutron_client, self.mano['details']['external_net_name']))
369 "Created floating IP for Open Baton NFVO %s",
370 (self.mano['details']['fip'].ip))
372 def get_vim_descriptor(self):
373 """"Create VIM descriptor to be used for onboarding"""
375 "Building VIM descriptor with PoP creds: %s",
377 # Depending on API version either tenant ID or project name must be
379 if os_utils.is_keystone_v3():
381 "Using v3 API of OpenStack... -> Using OS_PROJECT_ID")
382 project_id = os_utils.get_tenant_id(
383 os_utils.get_keystone_client(),
384 self.creds.get("project_name"))
387 "Using v2 API of OpenStack... -> Using OS_TENANT_NAME")
388 project_id = self.creds.get("tenant_name")
389 self.logger.debug("VIM project/tenant id: %s", project_id)
391 "name": "vim-instance",
392 "authUrl": self.creds.get("auth_url"),
393 "tenant": project_id,
394 "username": self.creds.get("username"),
395 "password": self.creds.get("password"),
397 self.mano['details']['sec_group']
402 "latitude": "52.525876",
403 "longitude": "13.314400"
406 self.logger.info("Built VIM descriptor: %s", vim_json)
409 def deploy_orchestrator(self):
410 self.logger.info("Deploying Open Baton...")
411 self.logger.info("Details: %s", self.mano['details'])
412 start_time = time.time()
414 self.logger.info("Creating orchestra instance...")
415 userdata = get_userdata(self.mano)
416 self.logger.info("flavor: %s\n"
419 self.mano['details']['flavor']['name'],
420 self.mano['requirements']['image'],
421 self.mano['details']['network']['id'])
422 self.logger.debug("userdata: %s\n", userdata)
424 image_settings = ImageConfig(
425 name=self.mano['requirements']['image'],
429 port_settings = PortConfig(
430 name='%s_port' % self.case_name,
431 network_name=self.mano['details']['network']['name'])
432 # build configuration of vm
433 orchestra_settings = VmInstanceConfig(
435 flavor=self.mano['details']['flavor']['name'],
436 port_settings=[port_settings],
437 security_group_names=[self.mano['details']['sec_group']],
438 userdata=str(userdata))
439 orchestra_vm = OpenStackVmInstance(self.snaps_creds,
443 orchestra_vm.create()
444 self.created_resources.append(orchestra_vm)
445 self.mano['details']['id'] = orchestra_vm.get_vm_info()['id']
447 "Created orchestra instance: %s",
448 self.mano['details']['id'])
450 self.logger.info("Associating floating ip: '%s' to VM '%s' ",
451 self.mano['details']['fip'].ip,
453 nova_client = os_utils.get_nova_client()
454 if not os_utils.add_floating_ip(
456 self.mano['details']['id'],
457 self.mano['details']['fip'].ip):
458 duration = time.time() - start_time
459 self.details["orchestrator"].update(
460 status='FAIL', duration=duration)
461 self.logger.error("Cannot associate floating IP to VM.")
464 self.logger.info("Waiting for Open Baton NFVO to be up and running...")
468 self.mano['details']['fip'].ip,
472 self.logger.info("Open Baton NFVO is not started yet (%ss)",
478 duration = time.time() - start_time
479 self.details["orchestrator"].update(
480 status='FAIL', duration=duration)
481 self.logger.error("Open Baton is not started correctly")
484 self.logger.info("Waiting for all components to be up and running...")
486 duration = time.time() - start_time
487 self.details["orchestrator"].update(status='PASS', duration=duration)
488 self.logger.info("Deploy Open Baton NFVO: OK")
491 def deploy_vnf(self):
492 start_time = time.time()
493 self.logger.info("Deploying %s...", self.vnf['name'])
495 main_agent = MainAgent(
496 nfvo_ip=self.mano['details']['fip'].ip,
500 username=self.mano['credentials']['username'],
501 password=self.mano['credentials']['password'])
504 "Create %s Flavor if not existing", self.vnf['name'])
505 flavor_settings = FlavorConfig(
506 name=self.vnf['requirements']['flavor']['name'],
507 ram=self.vnf['requirements']['flavor']['ram_min'],
508 disk=self.vnf['requirements']['flavor']['disk'],
509 vcpus=self.vnf['requirements']['flavor']['vcpus'])
510 flavor = OpenStackFlavor(self.snaps_creds, flavor_settings)
511 flavor_info = flavor.create()
512 self.logger.debug("Flavor id: %s", flavor_info.id)
514 self.logger.info("Getting project 'default'...")
515 project_agent = main_agent.get_agent("project", "")
516 for project in json.loads(project_agent.find()):
517 if project.get("name") == "default":
518 self.mano['details']['project_id'] = project.get("id")
519 self.logger.info("Found project 'default': %s", project)
522 vim_json = self.get_vim_descriptor()
523 self.logger.info("Registering VIM: %s", vim_json)
525 main_agent.get_agent(
526 "vim", project_id=self.mano['details']['project_id']).create(
527 entity=json.dumps(vim_json))
529 market_agent = main_agent.get_agent(
530 "market", project_id=self.mano['details']['project_id'])
533 self.logger.info("sending: %s", self.vnf['descriptor']['url'])
534 nsd = market_agent.create(entity=self.vnf['descriptor']['url'])
535 if nsd.get('id') is None:
536 self.logger.error("NSD not onboarded correctly")
537 duration = time.time() - start_time
538 self.details["vnf"].update(status='FAIL', duration=duration)
540 self.mano['details']['nsd_id'] = nsd.get('id')
541 self.logger.info("Onboarded NSD: " + nsd.get("name"))
543 nsr_agent = main_agent.get_agent(
544 "nsr", project_id=self.mano['details']['project_id'])
546 self.mano['details']['nsr'] = nsr_agent.create(
547 self.mano['details']['nsd_id'])
548 except NfvoException as exc:
549 self.logger.error(exc.message)
550 duration = time.time() - start_time
551 self.details["vnf"].update(status='FAIL', duration=duration)
554 if self.mano['details']['nsr'].get('code') is not None:
556 "%s cannot be deployed: %s -> %s",
558 self.mano['details']['nsr'].get('code'),
559 self.mano['details']['nsr'].get('message'))
560 self.logger.error("%s cannot be deployed", self.vnf['name'])
561 duration = time.time() - start_time
562 self.details["vnf"].update(status='FAIL', duration=duration)
566 self.logger.info("Waiting for NSR to go to ACTIVE...")
567 while self.mano['details']['nsr'].get("status") != 'ACTIVE' \
568 and self.mano['details']['nsr'].get("status") != 'ERROR':
570 self.logger.info("NSR is not yet ACTIVE... (%ss)", 60 * timeout)
572 self.logger.error("INACTIVE NSR after %s sec..", 60 * timeout)
573 duration = time.time() - start_time
574 self.details["vnf"].update(status='FAIL', duration=duration)
577 self.mano['details']['nsr'] = json.loads(
578 nsr_agent.find(self.mano['details']['nsr'].get('id')))
580 duration = time.time() - start_time
581 if self.mano['details']['nsr'].get("status") == 'ACTIVE':
582 self.details["vnf"].update(status='PASS', duration=duration)
583 self.logger.info("Sleep for 60s to ensure that all "
584 "services are up and running...")
588 self.details["vnf"].update(status='FAIL', duration=duration)
589 self.logger.error("NSR: %s", self.mano['details'].get('nsr'))
594 self.logger.info("Testing VNF OpenIMS...")
595 start_time = time.time()
597 "Testing if %s works properly...",
598 self.mano['details']['nsr'].get('name'))
599 for vnfr in self.mano['details']['nsr'].get('vnfr'):
601 "Checking ports %s of VNF %s",
602 self.vnf['test'][vnfr.get('name')]['ports'],
604 for vdu in vnfr.get('vdu'):
605 for vnfci in vdu.get('vnfc_instance'):
607 "Checking ports of VNFC instance %s",
608 vnfci.get('hostname'))
609 for floating_ip in vnfci.get('floatingIps'):
612 vnfci.get('hostname'),
613 floating_ip.get('ip'))
614 for port in self.vnf['test'][vnfr.get(
616 if servertest(floating_ip.get('ip'), port):
618 "VNFC instance %s is reachable at %s:%s",
619 vnfci.get('hostname'),
620 floating_ip.get('ip'),
624 "VNFC instance %s is not reachable "
626 vnfci.get('hostname'),
627 floating_ip.get('ip'),
629 duration = time.time() - start_time
630 self.details["test_vnf"].update(
631 status='FAIL', duration=duration, esult=(
632 "Port %s of server %s -> %s is "
635 vnfci.get('hostname'),
636 floating_ip.get('ip')))
637 self.logger.error("Test VNF: ERROR")
639 duration = time.time() - start_time
640 self.details["test_vnf"].update(status='PASS', duration=duration)
641 self.logger.info("Test VNF: OK")
645 self.logger.info("Cleaning %s...", self.case_name)
647 main_agent = MainAgent(
648 nfvo_ip=self.mano['details']['fip'].ip,
652 username=self.mano['credentials']['username'],
653 password=self.mano['credentials']['password'])
654 self.logger.info("Terminating %s...", self.vnf['name'])
655 if (self.mano['details'].get('nsr')):
656 main_agent.get_agent(
658 project_id=self.mano['details']['project_id']).\
659 delete(self.mano['details']['nsr'].get('id'))
660 self.logger.info("Sleeping 60 seconds...")
663 self.logger.info("No need to terminate the VNF...")
664 except (NfvoException, KeyError) as exc:
665 self.logger.error('Unexpected error cleaning - %s', exc)
668 neutron_client = os_utils.get_neutron_client(self.creds)
669 self.logger.info("Deleting Open Baton Port...")
670 port = snaps_utils.neutron_utils.get_port(
672 port_name='%s_port' % self.case_name)
673 snaps_utils.neutron_utils.delete_port(neutron_client, port)
675 except Exception as exc: # pylint: disable=broad-except
676 self.logger.error('Unexpected error cleaning - %s', exc)
678 self.logger.info("Deleting Open Baton Floating IP...")
679 snaps_utils.neutron_utils.delete_floating_ip(
680 neutron_client, self.mano['details']['fip'])
681 except Exception as exc: # pylint: disable=broad-except
682 self.logger.error('Unexpected error cleaning - %s', exc)
684 for resource in reversed(self.created_resources):
686 self.logger.info("Cleaning %s", str(resource))
688 except Exception as exc:
689 self.logger.error('Unexpected error cleaning - %s', exc)
690 super(OpenImsVnf, self).clean()