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 import config
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 = getattr(config.CONF, 'dir_ims_data')
155 self.test_dir = getattr(config.CONF, 'dir_repo_vims_test')
156 self.created_resources = []
157 self.logger.info("%s VNF onboarding test starting", self.case_name)
160 self.config = getattr(
161 config.CONF, '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}
213 self.prepare_images()
214 self.prepare_flavor()
215 self.prepare_security_groups()
216 self.prepare_network()
217 self.prepare_floating_ip()
219 def prepare_images(self):
220 """Upload images if they doen't exist yet"""
221 self.logger.info("Upload images if they doen't exist yet")
222 for image_name, image_file in self.images.iteritems():
223 self.logger.info("image: %s, file: %s", image_name, image_file)
224 if image_file and image_name:
225 image = OpenStackImage(
227 ImageConfig(name=image_name,
230 image_file=image_file,
233 # self.created_resources.append(image);
235 def prepare_security_groups(self):
236 """Create Open Baton security group if it doesn't exist yet"""
238 "Creating security group for Open Baton if not yet existing...")
241 SecurityGroupRuleConfig(
242 sec_grp_name="orchestra-sec-group-allowall",
243 direction=Direction.ingress,
244 protocol=Protocol.tcp,
246 port_range_max=65535))
248 SecurityGroupRuleConfig(
249 sec_grp_name="orchestra-sec-group-allowall",
250 direction=Direction.egress,
251 protocol=Protocol.tcp,
253 port_range_max=65535))
255 SecurityGroupRuleConfig(
256 sec_grp_name="orchestra-sec-group-allowall",
257 direction=Direction.ingress,
258 protocol=Protocol.udp,
260 port_range_max=65535))
262 SecurityGroupRuleConfig(
263 sec_grp_name="orchestra-sec-group-allowall",
264 direction=Direction.egress,
265 protocol=Protocol.udp,
267 port_range_max=65535))
268 security_group = OpenStackSecurityGroup(
271 name="orchestra-sec-group-allowall",
272 rule_settings=sg_rules))
274 security_group_info = security_group.create()
275 self.created_resources.append(security_group)
276 self.mano['details']['sec_group'] = security_group_info.name
278 "Security group orchestra-sec-group-allowall prepared")
280 def prepare_flavor(self):
281 """Create Open Baton flavor if it doesn't exist yet"""
283 "Create Flavor for Open Baton NFVO if not yet existing")
285 flavor_settings = FlavorConfig(
286 name=self.mano['requirements']['flavor']['name'],
287 ram=self.mano['requirements']['flavor']['ram_min'],
288 disk=self.mano['requirements']['flavor']['disk'],
289 vcpus=self.mano['requirements']['flavor']['vcpus'])
290 flavor = OpenStackFlavor(self.snaps_creds, flavor_settings)
291 flavor_info = flavor.create()
292 self.created_resources.append(flavor)
293 self.mano['details']['flavor'] = {}
294 self.mano['details']['flavor']['name'] = flavor_settings.name
295 self.mano['details']['flavor']['id'] = flavor_info.id
297 def prepare_network(self):
298 """Create network/subnet/router if they doen't exist yet"""
300 "Creating network/subnet/router if they doen't exist yet...")
301 subnet_settings = SubnetConfig(
304 cidr="192.168.100.0/24")
305 network_settings = NetworkConfig(
308 subnet_settings=[subnet_settings])
309 orchestra_network = OpenStackNetwork(
310 self.snaps_creds, network_settings)
311 orchestra_network_info = orchestra_network.create()
312 self.mano['details']['network'] = {}
313 self.mano['details']['network']['id'] = orchestra_network_info.id
314 self.mano['details']['network']['name'] = orchestra_network_info.name
315 self.mano['details']['external_net_name'] = \
316 snaps_utils.get_ext_net_name(self.snaps_creds)
317 self.created_resources.append(orchestra_network)
318 orchestra_router = OpenStackRouter(
323 external_gateway=self.mano['details']['external_net_name'],
325 subnet_settings.name]))
326 orchestra_router.create()
327 self.created_resources.append(orchestra_router)
328 self.logger.info("Created network and router for Open Baton NFVO...")
330 def prepare_floating_ip(self):
331 """Select/Create Floating IP if it doesn't exist yet"""
332 self.logger.info("Retrieving floating IP for Open Baton NFVO")
333 neutron_client = snaps_utils.neutron_utils.neutron_client(
335 # Finding Tenant ID to check to which tenant the Floating IP belongs
336 tenant_id = os_utils.get_tenant_id(
337 os_utils.get_keystone_client(self.creds),
339 # Use os_utils to retrieve complete information of Floating IPs
340 floating_ips = os_utils.get_floating_ips(neutron_client)
342 # Filter Floating IPs with tenant id
343 for floating_ip in floating_ips:
344 # self.logger.info("Floating IP: %s", floating_ip)
345 if floating_ip.get('tenant_id') == tenant_id:
346 my_floating_ips.append(floating_ip.get('floating_ip_address'))
347 # Select if Floating IP exist else create new one
348 if len(my_floating_ips) >= 1:
349 # Get Floating IP object from snaps for clean up
350 snaps_floating_ips = snaps_utils.neutron_utils.get_floating_ips(
352 for my_floating_ip in my_floating_ips:
353 for snaps_floating_ip in snaps_floating_ips:
354 if snaps_floating_ip.ip == my_floating_ip:
355 self.mano['details']['fip'] = snaps_floating_ip
357 "Selected floating IP for Open Baton NFVO %s",
358 (self.mano['details']['fip'].ip))
360 if self.mano['details']['fip'] is not None:
363 self.logger.info("Creating floating IP for Open Baton NFVO")
364 self.mano['details']['fip'] = (
365 snaps_utils.neutron_utils. create_floating_ip(
366 neutron_client, self.mano['details']['external_net_name']))
368 "Created floating IP for Open Baton NFVO %s",
369 (self.mano['details']['fip'].ip))
371 def get_vim_descriptor(self):
372 """"Create VIM descriptor to be used for onboarding"""
374 "Building VIM descriptor with PoP creds: %s",
376 # Depending on API version either tenant ID or project name must be
378 if os_utils.is_keystone_v3():
380 "Using v3 API of OpenStack... -> Using OS_PROJECT_ID")
381 project_id = os_utils.get_tenant_id(
382 os_utils.get_keystone_client(),
383 self.creds.get("project_name"))
386 "Using v2 API of OpenStack... -> Using OS_TENANT_NAME")
387 project_id = self.creds.get("tenant_name")
388 self.logger.debug("VIM project/tenant id: %s", project_id)
390 "name": "vim-instance",
391 "authUrl": self.creds.get("auth_url"),
392 "tenant": project_id,
393 "username": self.creds.get("username"),
394 "password": self.creds.get("password"),
396 self.mano['details']['sec_group']
401 "latitude": "52.525876",
402 "longitude": "13.314400"
405 self.logger.info("Built VIM descriptor: %s", vim_json)
408 def deploy_orchestrator(self):
409 self.logger.info("Deploying Open Baton...")
410 self.logger.info("Details: %s", self.mano['details'])
411 start_time = time.time()
413 self.logger.info("Creating orchestra instance...")
414 userdata = get_userdata(self.mano)
415 self.logger.info("flavor: %s\n"
418 self.mano['details']['flavor']['name'],
419 self.mano['requirements']['image'],
420 self.mano['details']['network']['id'])
421 self.logger.debug("userdata: %s\n", userdata)
423 image_settings = ImageConfig(
424 name=self.mano['requirements']['image'],
428 port_settings = PortConfig(
429 name='%s_port' % self.case_name,
430 network_name=self.mano['details']['network']['name'])
431 # build configuration of vm
432 orchestra_settings = VmInstanceConfig(
434 flavor=self.mano['details']['flavor']['name'],
435 port_settings=[port_settings],
436 security_group_names=[self.mano['details']['sec_group']],
437 userdata=str(userdata))
438 orchestra_vm = OpenStackVmInstance(self.snaps_creds,
442 orchestra_vm.create()
443 self.created_resources.append(orchestra_vm)
444 self.mano['details']['id'] = orchestra_vm.get_vm_info()['id']
446 "Created orchestra instance: %s",
447 self.mano['details']['id'])
449 self.logger.info("Associating floating ip: '%s' to VM '%s' ",
450 self.mano['details']['fip'].ip,
452 nova_client = os_utils.get_nova_client()
453 if not os_utils.add_floating_ip(
455 self.mano['details']['id'],
456 self.mano['details']['fip'].ip):
457 duration = time.time() - start_time
458 self.details["orchestrator"].update(
459 status='FAIL', duration=duration)
460 self.logger.error("Cannot associate floating IP to VM.")
463 self.logger.info("Waiting for Open Baton NFVO to be up and running...")
467 self.mano['details']['fip'].ip,
471 self.logger.info("Open Baton NFVO is not started yet (%ss)",
477 duration = time.time() - start_time
478 self.details["orchestrator"].update(
479 status='FAIL', duration=duration)
480 self.logger.error("Open Baton is not started correctly")
483 self.logger.info("Waiting for all components to be up and running...")
485 duration = time.time() - start_time
486 self.details["orchestrator"].update(status='PASS', duration=duration)
487 self.logger.info("Deploy Open Baton NFVO: OK")
490 def deploy_vnf(self):
491 start_time = time.time()
492 self.logger.info("Deploying %s...", self.vnf['name'])
494 main_agent = MainAgent(
495 nfvo_ip=self.mano['details']['fip'].ip,
499 username=self.mano['credentials']['username'],
500 password=self.mano['credentials']['password'])
503 "Create %s Flavor if not existing", self.vnf['name'])
504 flavor_settings = FlavorConfig(
505 name=self.vnf['requirements']['flavor']['name'],
506 ram=self.vnf['requirements']['flavor']['ram_min'],
507 disk=self.vnf['requirements']['flavor']['disk'],
508 vcpus=self.vnf['requirements']['flavor']['vcpus'])
509 flavor = OpenStackFlavor(self.snaps_creds, flavor_settings)
510 flavor_info = flavor.create()
511 self.logger.debug("Flavor id: %s", flavor_info.id)
513 self.logger.info("Getting project 'default'...")
514 project_agent = main_agent.get_agent("project", "")
515 for project in json.loads(project_agent.find()):
516 if project.get("name") == "default":
517 self.mano['details']['project_id'] = project.get("id")
518 self.logger.info("Found project 'default': %s", project)
521 vim_json = self.get_vim_descriptor()
522 self.logger.info("Registering VIM: %s", vim_json)
524 main_agent.get_agent(
525 "vim", project_id=self.mano['details']['project_id']).create(
526 entity=json.dumps(vim_json))
528 market_agent = main_agent.get_agent(
529 "market", project_id=self.mano['details']['project_id'])
532 self.logger.info("sending: %s", self.vnf['descriptor']['url'])
533 nsd = market_agent.create(entity=self.vnf['descriptor']['url'])
534 if nsd.get('id') is None:
535 self.logger.error("NSD not onboarded correctly")
536 duration = time.time() - start_time
537 self.details["vnf"].update(status='FAIL', duration=duration)
539 self.mano['details']['nsd_id'] = nsd.get('id')
540 self.logger.info("Onboarded NSD: " + nsd.get("name"))
542 nsr_agent = main_agent.get_agent(
543 "nsr", project_id=self.mano['details']['project_id'])
545 self.mano['details']['nsr'] = nsr_agent.create(
546 self.mano['details']['nsd_id'])
547 except NfvoException as exc:
548 self.logger.error(exc.message)
549 duration = time.time() - start_time
550 self.details["vnf"].update(status='FAIL', duration=duration)
553 if self.mano['details']['nsr'].get('code') is not None:
555 "%s cannot be deployed: %s -> %s",
557 self.mano['details']['nsr'].get('code'),
558 self.mano['details']['nsr'].get('message'))
559 self.logger.error("%s cannot be deployed", self.vnf['name'])
560 duration = time.time() - start_time
561 self.details["vnf"].update(status='FAIL', duration=duration)
565 self.logger.info("Waiting for NSR to go to ACTIVE...")
566 while self.mano['details']['nsr'].get("status") != 'ACTIVE' \
567 and self.mano['details']['nsr'].get("status") != 'ERROR':
569 self.logger.info("NSR is not yet ACTIVE... (%ss)", 60 * timeout)
571 self.logger.error("INACTIVE NSR after %s sec..", 60 * timeout)
572 duration = time.time() - start_time
573 self.details["vnf"].update(status='FAIL', duration=duration)
576 self.mano['details']['nsr'] = json.loads(
577 nsr_agent.find(self.mano['details']['nsr'].get('id')))
579 duration = time.time() - start_time
580 if self.mano['details']['nsr'].get("status") == 'ACTIVE':
581 self.details["vnf"].update(status='PASS', duration=duration)
582 self.logger.info("Sleep for 60s to ensure that all "
583 "services are up and running...")
587 self.details["vnf"].update(status='FAIL', duration=duration)
588 self.logger.error("NSR: %s", self.mano['details'].get('nsr'))
593 self.logger.info("Testing VNF OpenIMS...")
594 start_time = time.time()
596 "Testing if %s works properly...",
597 self.mano['details']['nsr'].get('name'))
598 for vnfr in self.mano['details']['nsr'].get('vnfr'):
600 "Checking ports %s of VNF %s",
601 self.vnf['test'][vnfr.get('name')]['ports'],
603 for vdu in vnfr.get('vdu'):
604 for vnfci in vdu.get('vnfc_instance'):
606 "Checking ports of VNFC instance %s",
607 vnfci.get('hostname'))
608 for floating_ip in vnfci.get('floatingIps'):
611 vnfci.get('hostname'),
612 floating_ip.get('ip'))
613 for port in self.vnf['test'][vnfr.get(
615 if servertest(floating_ip.get('ip'), port):
617 "VNFC instance %s is reachable at %s:%s",
618 vnfci.get('hostname'),
619 floating_ip.get('ip'),
623 "VNFC instance %s is not reachable "
625 vnfci.get('hostname'),
626 floating_ip.get('ip'),
628 duration = time.time() - start_time
629 self.details["test_vnf"].update(
630 status='FAIL', duration=duration, esult=(
631 "Port %s of server %s -> %s is "
634 vnfci.get('hostname'),
635 floating_ip.get('ip')))
636 self.logger.error("Test VNF: ERROR")
638 duration = time.time() - start_time
639 self.details["test_vnf"].update(status='PASS', duration=duration)
640 self.logger.info("Test VNF: OK")
644 self.logger.info("Cleaning %s...", self.case_name)
646 main_agent = MainAgent(
647 nfvo_ip=self.mano['details']['fip'].ip,
651 username=self.mano['credentials']['username'],
652 password=self.mano['credentials']['password'])
653 self.logger.info("Terminating %s...", self.vnf['name'])
654 if (self.mano['details'].get('nsr')):
655 main_agent.get_agent(
657 project_id=self.mano['details']['project_id']).\
658 delete(self.mano['details']['nsr'].get('id'))
659 self.logger.info("Sleeping 60 seconds...")
662 self.logger.info("No need to terminate the VNF...")
663 except (NfvoException, KeyError) as exc:
664 self.logger.error('Unexpected error cleaning - %s', exc)
667 neutron_client = os_utils.get_neutron_client(self.creds)
668 self.logger.info("Deleting Open Baton Port...")
669 port = snaps_utils.neutron_utils.get_port(
671 port_name='%s_port' % self.case_name)
672 snaps_utils.neutron_utils.delete_port(neutron_client, port)
674 except Exception as exc: # pylint: disable=broad-except
675 self.logger.error('Unexpected error cleaning - %s', exc)
677 self.logger.info("Deleting Open Baton Floating IP...")
678 snaps_utils.neutron_utils.delete_floating_ip(
679 neutron_client, self.mano['details']['fip'])
680 except Exception as exc: # pylint: disable=broad-except
681 self.logger.error('Unexpected error cleaning - %s', exc)
683 for resource in reversed(self.created_resources):
685 self.logger.info("Cleaning %s", str(resource))
687 except Exception as exc:
688 self.logger.error('Unexpected error cleaning - %s', exc)
689 super(OpenImsVnf, self).clean()