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 from functest.core import vnf
21 from functest.utils import config
22 from functest.utils import env
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 FloatingIpConfig
33 from snaps.config.vm_inst import VmInstanceConfig
34 from snaps.openstack.utils import keystone_utils
35 from snaps.openstack.create_image import OpenStackImage
36 from snaps.openstack.create_flavor import OpenStackFlavor
37 from snaps.openstack.create_security_group import OpenStackSecurityGroup
38 from snaps.openstack.create_network import OpenStackNetwork
39 from snaps.openstack.create_router import OpenStackRouter
40 from snaps.openstack.create_instance import OpenStackVmInstance
42 from functest.opnfv_tests.openstack.snaps import snaps_utils
45 __author__ = "Pauls, Michael <michael.pauls@fokus.fraunhofer.de>"
46 # ----------------------------------------------------------
50 # -----------------------------------------------------------
53 def get_config(parameter, file_path):
57 Returns the value of a given parameter in file.yaml
58 parameter must be given in string format with dots
59 Example: general.openstack.image_name
61 with open(file_path) as config_file:
62 file_yaml = yaml.safe_load(config_file)
65 for element in parameter.split("."):
66 value = value.get(element)
68 raise ValueError("The parameter %s is not defined in"
69 " reporting.yaml", parameter)
73 def servertest(host, port):
74 """Method to test that a server is reachable at IP:port"""
75 args = socket.getaddrinfo(host, port, socket.AF_INET, socket.SOCK_STREAM)
76 for family, socktype, proto, _, sockaddr in args:
77 sock = socket.socket(family, socktype, proto)
79 sock.connect(sockaddr)
87 def get_userdata(orchestrator=dict):
88 """Build userdata for Open Baton machine"""
89 userdata = "#!/bin/bash\n"
90 userdata += "echo \"Executing userdata...\"\n"
91 userdata += "set -x\n"
92 userdata += "set -e\n"
93 userdata += "echo \"Install curl...\"\n"
94 userdata += "apt-get install curl\n"
95 userdata += "echo \"Inject public key...\"\n"
96 userdata += ("echo \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCuPXrV3"
97 "geeHc6QUdyUr/1Z+yQiqLcOskiEGBiXr4z76MK4abiFmDZ18OMQlc"
98 "fl0p3kS0WynVgyaOHwZkgy/DIoIplONVr2CKBKHtPK+Qcme2PVnCtv"
99 "EqItl/FcD+1h5XSQGoa+A1TSGgCod/DPo+pes0piLVXP8Ph6QS1k7S"
100 "ic7JDeRQ4oT1bXYpJ2eWBDMfxIWKZqcZRiGPgMIbJ1iEkxbpeaAd9O"
101 "4MiM9nGCPESmed+p54uYFjwEDlAJZShcAZziiZYAvMZhvAhe6USljc"
102 "7YAdalAnyD/jwCHuwIrUw/lxo7UdNCmaUxeobEYyyFA1YVXzpNFZya"
103 "XPGAAYIJwEq/ openbaton@opnfv\" >> /home/ubuntu/.ssh/aut"
105 userdata += "echo \"Download bootstrap...\"\n"
106 userdata += ("curl -s %s "
107 "> ./bootstrap\n" % orchestrator['bootstrap']['url'])
108 userdata += ("curl -s %s" "> ./config_file\n" %
109 orchestrator['bootstrap']['config']['url'])
110 userdata += ("echo \"Disable usage of mysql...\"\n")
111 userdata += "sed -i s/mysql=.*/mysql=no/g /config_file\n"
112 userdata += "echo \"Set autostart of components to 'false'\"\n"
113 userdata += "export OPENBATON_COMPONENT_AUTOSTART=false\n"
114 userdata += "echo \"Execute bootstrap...\"\n"
115 bootstrap = "sh ./bootstrap release -configFile=./config_file"
116 userdata += bootstrap + "\n"
117 userdata += "echo \"Setting 'nfvo.plugin.timeout' to '300000'\"\n"
118 userdata += ("echo \"nfvo.plugin.timeout=600000\" >> "
119 "/etc/openbaton/openbaton-nfvo.properties\n")
121 "wget %s -O /etc/openbaton/openbaton-vnfm-generic-user-data.sh\n" %
122 orchestrator['gvnfm']['userdata']['url'])
123 userdata += "sed -i '113i"'\ \ \ \ '"sleep 60' " \
124 "/etc/openbaton/openbaton-vnfm-generic-user-data.sh\n"
125 userdata += ("sed -i s/nfvo.marketplace.port=8082/nfvo.marketplace."
126 "port=8080/g /etc/openbaton/openbaton-nfvo.properties\n")
127 userdata += "echo \"Starting NFVO\"\n"
128 userdata += "service openbaton-nfvo restart\n"
129 userdata += "echo \"Starting Generic VNFM\"\n"
130 userdata += "service openbaton-vnfm-generic restart\n"
131 userdata += "echo \"...end of userdata...\"\n"
135 class OpenImsVnf(vnf.VnfOnBoarding):
136 """OpenIMS VNF deployed with openBaton orchestrator"""
138 # logger = logging.getLogger(__name__)
140 def __init__(self, **kwargs):
141 if "case_name" not in kwargs:
142 kwargs["case_name"] = "orchestra_openims"
143 super(OpenImsVnf, self).__init__(**kwargs)
144 self.logger = logging.getLogger("functest.ci.run_tests.orchestra")
145 self.logger.info("kwargs %s", (kwargs))
147 self.case_dir = pkg_resources.resource_filename(
148 'functest', 'opnfv_tests/vnf/ims/')
149 self.data_dir = getattr(config.CONF, 'dir_ims_data')
150 self.test_dir = getattr(config.CONF, 'dir_repo_vims_test')
151 self.created_resources = []
152 self.logger.info("%s VNF onboarding test starting", self.case_name)
155 self.config = getattr(
156 config.CONF, 'vnf_{}_config'.format(self.case_name))
157 except BaseException:
158 raise Exception("Orchestra VNF config file not found")
159 config_file = self.case_dir + self.config
162 get_config("mano", config_file),
165 self.logger.debug("Orchestrator configuration %s", self.mano)
167 self.details['orchestrator'] = dict(
168 name=self.mano['name'],
169 version=self.mano['version'],
175 get_config(self.case_name, config_file),
177 self.logger.debug("VNF configuration: %s", self.vnf)
179 self.details['vnf'] = dict(
180 name=self.vnf['name'],
183 self.details['test_vnf'] = dict(
187 # Orchestra base Data directory creation
188 if not os.path.exists(self.data_dir):
189 os.makedirs(self.data_dir)
191 self.images = get_config("tenant_images.orchestrator", config_file)
192 self.images.update(get_config("tenant_images.%s" %
193 self.case_name, config_file))
195 self.orchestra_router = None
198 """Prepare testscase (Additional pre-configuration steps)."""
199 super(OpenImsVnf, self).prepare()
201 public_auth_url = keystone_utils.get_endpoint(
202 self.snaps_creds, 'identity')
204 self.logger.info("Additional pre-configuration steps")
206 "tenant": self.snaps_creds.project_name,
207 "username": self.snaps_creds.username,
208 "password": self.snaps_creds.password,
209 "auth_url": public_auth_url}
210 self.prepare_images()
211 self.prepare_flavor()
212 self.prepare_security_groups()
213 self.prepare_network()
215 def prepare_images(self):
216 """Upload images if they doen't exist yet"""
217 self.logger.info("Upload images if they doen't exist yet")
218 for image_name, image_file in self.images.iteritems():
219 self.logger.info("image: %s, file: %s", image_name, image_file)
220 if image_file and image_name:
221 image = OpenStackImage(
223 ImageConfig(name=image_name,
226 image_file=image_file,
229 self.created_resources.append(image)
231 def prepare_security_groups(self):
232 """Create Open Baton security group if it doesn't exist yet"""
234 "Creating security group for Open Baton if not yet existing...")
237 SecurityGroupRuleConfig(
238 sec_grp_name="orchestra-sec-group-allowall-{}".format(
240 direction=Direction.ingress,
241 protocol=Protocol.tcp,
243 port_range_max=65535))
245 SecurityGroupRuleConfig(
246 sec_grp_name="orchestra-sec-group-allowall-{}".format(
248 direction=Direction.egress,
249 protocol=Protocol.tcp,
251 port_range_max=65535))
253 SecurityGroupRuleConfig(
254 sec_grp_name="orchestra-sec-group-allowall-{}".format(
256 direction=Direction.ingress,
257 protocol=Protocol.udp,
259 port_range_max=65535))
261 SecurityGroupRuleConfig(
262 sec_grp_name="orchestra-sec-group-allowall-{}".format(
264 direction=Direction.egress,
265 protocol=Protocol.udp,
267 port_range_max=65535))
268 security_group = OpenStackSecurityGroup(
271 name="orchestra-sec-group-allowall-{}".format(
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(
303 name='{}_subnet-{}'.format(self.case_name, self.uuid),
304 cidr="192.168.100.0/24",
305 dns_nameservers=[env.get('NAMESERVER')])
306 network_settings = NetworkConfig(
307 name='{}_net-{}'.format(self.case_name, self.uuid),
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 self.orchestra_router = OpenStackRouter(
321 name='{}_router-{}'.format(self.case_name, self.uuid),
322 external_gateway=self.mano['details']['external_net_name'],
324 subnet_settings.name]))
325 self.orchestra_router.create()
326 self.created_resources.append(self.orchestra_router)
327 self.logger.info("Created network and router for Open Baton NFVO...")
329 def get_vim_descriptor(self):
330 """"Create VIM descriptor to be used for onboarding"""
332 "Building VIM descriptor with PoP creds: %s",
334 self.logger.debug("VIM project/tenant id: %s",
335 self.snaps_creds.project_name)
336 keystone = keystone_utils.keystone_client(self.snaps_creds)
337 project_id = keystone_utils.get_project(
338 keystone=keystone, project_name=self.snaps_creds.project_name).id
340 "name": "vim-instance",
341 "authUrl": self.creds.get("auth_url"),
342 "tenant": project_id,
343 "username": self.creds.get("username"),
344 "password": self.creds.get("password"),
346 self.mano['details']['sec_group']
351 "latitude": "52.525876",
352 "longitude": "13.314400"
355 self.logger.info("Built VIM descriptor: %s", vim_json)
358 def deploy_orchestrator(self):
359 self.logger.info("Deploying Open Baton...")
360 self.logger.info("Details: %s", self.mano['details'])
361 start_time = time.time()
363 self.logger.info("Creating orchestra instance...")
364 userdata = get_userdata(self.mano)
365 self.logger.info("flavor: %s\n"
368 self.mano['details']['flavor']['name'],
369 self.mano['requirements']['image'],
370 self.mano['details']['network']['id'])
371 self.logger.debug("userdata: %s\n", userdata)
373 image_settings = ImageConfig(
374 name=self.mano['requirements']['image'],
378 port_settings = PortConfig(
379 name='{}_port-{}'.format(self.case_name, self.uuid),
380 network_name=self.mano['details']['network']['name'])
381 # build configuration of vm
382 orchestra_settings = VmInstanceConfig(
383 name='{}-{}'.format(self.case_name, self.uuid),
384 flavor=self.mano['details']['flavor']['name'],
385 port_settings=[port_settings],
386 security_group_names=[self.mano['details']['sec_group']],
387 floating_ip_settings=[FloatingIpConfig(
388 name='orchestra_fip-{}'.format(self.uuid),
389 port_name=port_settings.name,
390 router_name=self.orchestra_router.router_settings.name)],
391 userdata=str(userdata))
392 orchestra_vm = OpenStackVmInstance(
393 self.snaps_creds, orchestra_settings, image_settings)
394 orchestra_vm.create()
395 self.mano['details']['fip'] = orchestra_vm.get_floating_ip()
396 self.created_resources.append(orchestra_vm)
397 self.mano['details']['id'] = orchestra_vm.get_vm_info()['id']
399 "Created orchestra instance: %s", self.mano['details']['id'])
400 self.logger.info("Waiting for Open Baton NFVO to be up and running...")
404 self.mano['details']['fip'].ip,
408 self.logger.info("Open Baton NFVO is not started yet (%ss)",
414 duration = time.time() - start_time
415 self.details["orchestrator"].update(
416 status='FAIL', duration=duration)
417 self.logger.error("Open Baton is not started correctly")
420 self.logger.info("Waiting for all components to be up and running...")
422 duration = time.time() - start_time
423 self.details["orchestrator"].update(status='PASS', duration=duration)
424 self.logger.info("Deploy Open Baton NFVO: OK")
427 def deploy_vnf(self):
428 start_time = time.time()
429 self.logger.info("Deploying %s...", self.vnf['name'])
431 main_agent = MainAgent(
432 nfvo_ip=self.mano['details']['fip'].ip,
436 username=self.mano['credentials']['username'],
437 password=self.mano['credentials']['password'])
440 "Create %s Flavor if not existing", self.vnf['name'])
441 flavor_settings = FlavorConfig(
442 name=self.vnf['requirements']['flavor']['name'],
443 ram=self.vnf['requirements']['flavor']['ram_min'],
444 disk=self.vnf['requirements']['flavor']['disk'],
445 vcpus=self.vnf['requirements']['flavor']['vcpus'])
446 flavor = OpenStackFlavor(self.snaps_creds, flavor_settings)
447 flavor_info = flavor.create()
448 self.logger.debug("Flavor id: %s", flavor_info.id)
450 self.logger.info("Getting project 'default'...")
451 project_agent = main_agent.get_agent("project", "")
452 for project in json.loads(project_agent.find()):
453 if project.get("name") == "default":
454 self.mano['details']['project_id'] = project.get("id")
455 self.logger.info("Found project 'default': %s", project)
458 vim_json = self.get_vim_descriptor()
459 self.logger.info("Registering VIM: %s", vim_json)
461 main_agent.get_agent(
462 "vim", project_id=self.mano['details']['project_id']).create(
463 entity=json.dumps(vim_json))
465 market_agent = main_agent.get_agent(
466 "market", project_id=self.mano['details']['project_id'])
469 self.logger.info("sending: %s", self.vnf['descriptor']['url'])
470 nsd = market_agent.create(entity=self.vnf['descriptor']['url'])
471 if nsd.get('id') is None:
472 self.logger.error("NSD not onboarded correctly")
473 duration = time.time() - start_time
474 self.details["vnf"].update(status='FAIL', duration=duration)
476 self.mano['details']['nsd_id'] = nsd.get('id')
477 self.logger.info("Onboarded NSD: " + nsd.get("name"))
479 nsr_agent = main_agent.get_agent(
480 "nsr", project_id=self.mano['details']['project_id'])
482 self.mano['details']['nsr'] = nsr_agent.create(
483 self.mano['details']['nsd_id'])
484 except NfvoException:
485 self.logger.exception("failed")
486 duration = time.time() - start_time
487 self.details["vnf"].update(status='FAIL', duration=duration)
490 if self.mano['details']['nsr'].get('code') is not None:
492 "%s cannot be deployed: %s -> %s",
494 self.mano['details']['nsr'].get('code'),
495 self.mano['details']['nsr'].get('message'))
496 self.logger.error("%s cannot be deployed", self.vnf['name'])
497 duration = time.time() - start_time
498 self.details["vnf"].update(status='FAIL', duration=duration)
502 self.logger.info("Waiting for NSR to go to ACTIVE...")
503 while self.mano['details']['nsr'].get("status") != 'ACTIVE' \
504 and self.mano['details']['nsr'].get("status") != 'ERROR':
506 self.logger.info("NSR is not yet ACTIVE... (%ss)", 60 * timeout)
508 self.logger.error("INACTIVE NSR after %s sec..", 60 * timeout)
509 duration = time.time() - start_time
510 self.details["vnf"].update(status='FAIL', duration=duration)
513 self.mano['details']['nsr'] = json.loads(
514 nsr_agent.find(self.mano['details']['nsr'].get('id')))
516 duration = time.time() - start_time
517 if self.mano['details']['nsr'].get("status") == 'ACTIVE':
518 self.details["vnf"].update(status='PASS', duration=duration)
519 self.logger.info("Sleep for 60s to ensure that all "
520 "services are up and running...")
524 self.details["vnf"].update(status='FAIL', duration=duration)
525 self.logger.error("NSR: %s", self.mano['details'].get('nsr'))
530 self.logger.info("Testing VNF OpenIMS...")
531 start_time = time.time()
533 "Testing if %s works properly...",
534 self.mano['details']['nsr'].get('name'))
535 for vnfr in self.mano['details']['nsr'].get('vnfr'):
537 "Checking ports %s of VNF %s",
538 self.vnf['test'][vnfr.get('name')]['ports'],
540 for vdu in vnfr.get('vdu'):
541 for vnfci in vdu.get('vnfc_instance'):
543 "Checking ports of VNFC instance %s",
544 vnfci.get('hostname'))
545 for floating_ip in vnfci.get('floatingIps'):
548 vnfci.get('hostname'),
549 floating_ip.get('ip'))
550 for port in self.vnf['test'][vnfr.get(
552 if servertest(floating_ip.get('ip'), port):
554 "VNFC instance %s is reachable at %s:%s",
555 vnfci.get('hostname'),
556 floating_ip.get('ip'),
560 "VNFC instance %s is not reachable "
562 vnfci.get('hostname'),
563 floating_ip.get('ip'),
565 duration = time.time() - start_time
566 self.details["test_vnf"].update(
567 status='FAIL', duration=duration, esult=(
568 "Port %s of server %s -> %s is "
571 vnfci.get('hostname'),
572 floating_ip.get('ip')))
573 self.logger.error("Test VNF: ERROR")
575 duration = time.time() - start_time
576 self.details["test_vnf"].update(status='PASS', duration=duration)
577 self.logger.info("Test VNF: OK")
581 self.logger.info("Cleaning %s...", self.case_name)
583 main_agent = MainAgent(
584 nfvo_ip=self.mano['details']['fip'].ip,
585 nfvo_port=8080, https=False, version=1,
586 username=self.mano['credentials']['username'],
587 password=self.mano['credentials']['password'])
588 self.logger.info("Terminating %s...", self.vnf['name'])
589 if self.mano['details'].get('nsr'):
590 main_agent.get_agent(
592 project_id=self.mano['details']['project_id']).\
593 delete(self.mano['details']['nsr'].get('id'))
594 self.logger.info("Sleeping 60 seconds...")
597 self.logger.info("No need to terminate the VNF...")
598 except (NfvoException, KeyError) as exc:
599 self.logger.error('Unexpected error cleaning - %s', exc)
600 super(OpenImsVnf, self).clean()