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 Clearwater IMS testcase implementation."""
21 from functest.core import vnf
22 from functest.opnfv_tests.openstack.snaps import snaps_utils
23 from functest.utils import config
25 from org.openbaton.cli.errors.errors import NfvoException
26 from org.openbaton.cli.agents.agents import MainAgent
27 from snaps.config.flavor import FlavorConfig
28 from snaps.config.image import ImageConfig
29 from snaps.config.network import NetworkConfig, PortConfig, SubnetConfig
30 from snaps.config.router import RouterConfig
31 from snaps.config.security_group import (
32 Direction, Protocol, SecurityGroupConfig, SecurityGroupRuleConfig)
33 from snaps.config.vm_inst import FloatingIpConfig
34 from snaps.config.vm_inst import VmInstanceConfig
35 from snaps.openstack.utils import keystone_utils
36 from snaps.openstack.create_flavor import OpenStackFlavor
37 from snaps.openstack.create_image import OpenStackImage
38 from snaps.openstack.create_instance import OpenStackVmInstance
39 from snaps.openstack.create_network import OpenStackNetwork
40 from snaps.openstack.create_router import OpenStackRouter
41 from snaps.openstack.create_security_group import OpenStackSecurityGroup
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, _, 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 \"Install curl...\"\n"
93 userdata += "apt-get install curl\n"
94 userdata += "echo \"Inject public key...\"\n"
95 userdata += ("echo \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCuPXrV3"
96 "geeHc6QUdyUr/1Z+yQiqLcOskiEGBiXr4z76MK4abiFmDZ18OMQlc"
97 "fl0p3kS0WynVgyaOHwZkgy/DIoIplONVr2CKBKHtPK+Qcme2PVnCtv"
98 "EqItl/FcD+1h5XSQGoa+A1TSGgCod/DPo+pes0piLVXP8Ph6QS1k7S"
99 "ic7JDeRQ4oT1bXYpJ2eWBDMfxIWKZqcZRiGPgMIbJ1iEkxbpeaAd9O"
100 "4MiM9nGCPESmed+p54uYFjwEDlAJZShcAZziiZYAvMZhvAhe6USljc"
101 "7YAdalAnyD/jwCHuwIrUw/lxo7UdNCmaUxeobEYyyFA1YVXzpNFZya"
102 "XPGAAYIJwEq/ openbaton@opnfv\" >> /home/ubuntu/.ssh/aut"
104 userdata += "echo \"Download bootstrap...\"\n"
105 userdata += ("curl -s %s "
106 "> ./bootstrap\n" % orchestrator['bootstrap']['url'])
107 userdata += ("curl -s %s" "> ./config_file\n" %
108 orchestrator['bootstrap']['config']['url'])
109 userdata += ("echo \"Disable usage of mysql...\"\n")
110 userdata += "sed -i s/mysql=.*/mysql=no/g /config_file\n"
111 userdata += "echo \"Set autostart of components to 'false'\"\n"
112 userdata += "export OPENBATON_COMPONENT_AUTOSTART=false\n"
113 userdata += "echo \"Execute bootstrap...\"\n"
114 bootstrap = "sh ./bootstrap release -configFile=./config_file"
115 userdata += bootstrap + "\n"
116 userdata += "echo \"Setting 'nfvo.plugin.timeout' to '300000'\"\n"
117 userdata += ("echo \"nfvo.plugin.timeout=600000\" >> "
118 "/etc/openbaton/openbaton-nfvo.properties\n")
120 "wget %s -O /etc/openbaton/openbaton-vnfm-generic-user-data.sh\n" %
121 orchestrator['gvnfm']['userdata']['url'])
122 userdata += "sed -i '113i"'\ \ \ \ '"sleep 60' " \
123 "/etc/openbaton/openbaton-vnfm-generic-user-data.sh\n"
124 userdata += ("sed -i s/nfvo.marketplace.port=8082/nfvo.marketplace."
125 "port=8080/g /etc/openbaton/openbaton-nfvo.properties\n")
126 userdata += "echo \"Starting NFVO\"\n"
127 userdata += "service openbaton-nfvo restart\n"
128 userdata += "echo \"Starting Generic VNFM\"\n"
129 userdata += "service openbaton-vnfm-generic restart\n"
130 userdata += "echo \"...end of userdata...\"\n"
134 class ClearwaterImsVnf(vnf.VnfOnBoarding):
135 """Clearwater IMS VNF deployed with openBaton orchestrator"""
137 # logger = logging.getLogger(__name__)
139 def __init__(self, **kwargs):
140 if "case_name" not in kwargs:
141 kwargs["case_name"] = "orchestra_clearwaterims"
142 super(ClearwaterImsVnf, self).__init__(**kwargs)
143 self.logger = logging.getLogger("functest.ci.run_tests.orchestra")
144 self.logger.info("kwargs %s", (kwargs))
146 self.case_dir = pkg_resources.resource_filename(
147 'functest', 'opnfv_tests/vnf/ims/')
148 self.data_dir = getattr(config.CONF, 'dir_ims_data')
149 self.test_dir = getattr(config.CONF, 'dir_repo_vims_test')
150 self.created_resources = []
151 self.logger.info("%s VNF onboarding test starting", self.case_name)
154 self.config = getattr(
155 config.CONF, 'vnf_{}_config'.format(self.case_name))
156 except BaseException:
157 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)
198 self.orchestra_router = None
201 """Prepare testscase (Additional pre-configuration steps)."""
202 super(ClearwaterImsVnf, self).prepare()
204 self.logger.info("Additional pre-configuration steps")
206 public_auth_url = keystone_utils.get_endpoint(
207 self.snaps_creds, 'identity')
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()
218 def prepare_images(self):
219 """Upload images if they doen't exist yet"""
220 self.logger.info("Upload images if they doen't exist yet")
221 for image_name, image_file in self.images.iteritems():
222 self.logger.info("image: %s, file: %s", image_name, image_file)
223 if image_file and image_name:
224 image = OpenStackImage(
226 ImageConfig(name=image_name,
229 image_file=image_file,
232 self.created_resources.append(image)
234 def prepare_security_groups(self):
235 """Create Open Baton security group if it doesn't exist yet"""
237 "Creating security group for Open Baton if not yet existing...")
240 SecurityGroupRuleConfig(
241 sec_grp_name="orchestra-sec-group-allowall-{}".format(
243 direction=Direction.ingress,
244 protocol=Protocol.tcp,
246 port_range_max=65535))
248 SecurityGroupRuleConfig(
249 sec_grp_name="orchestra-sec-group-allowall-{}".format(
251 direction=Direction.egress,
252 protocol=Protocol.tcp,
254 port_range_max=65535))
256 SecurityGroupRuleConfig(
257 sec_grp_name="orchestra-sec-group-allowall-{}".format(
259 direction=Direction.ingress,
260 protocol=Protocol.udp,
262 port_range_max=65535))
264 SecurityGroupRuleConfig(
265 sec_grp_name="orchestra-sec-group-allowall-{}".format(
267 direction=Direction.egress,
268 protocol=Protocol.udp,
270 port_range_max=65535))
272 SecurityGroupRuleConfig(
273 sec_grp_name="orchestra-sec-group-allowall-{}".format(
275 direction=Direction.ingress,
276 protocol=Protocol.icmp))
278 SecurityGroupRuleConfig(
279 sec_grp_name="orchestra-sec-group-allowall-{}".format(
281 direction=Direction.egress,
282 protocol=Protocol.icmp))
283 security_group = OpenStackSecurityGroup(
286 name="orchestra-sec-group-allowall-{}".format(
288 rule_settings=sg_rules))
289 security_group_info = security_group.create()
290 self.created_resources.append(security_group)
291 self.mano['details']['sec_group'] = security_group_info.name
293 "Security group orchestra-sec-group-allowall prepared")
295 def prepare_flavor(self):
296 """Create Open Baton flavor if it doesn't exist yet"""
298 "Create Flavor for Open Baton NFVO if not yet existing")
300 flavor_settings = FlavorConfig(
301 name=self.mano['requirements']['flavor']['name'],
302 ram=self.mano['requirements']['flavor']['ram_min'],
303 disk=self.mano['requirements']['flavor']['disk'],
304 vcpus=self.mano['requirements']['flavor']['vcpus'])
305 flavor = OpenStackFlavor(self.snaps_creds, flavor_settings)
306 flavor_info = flavor.create()
307 self.created_resources.append(flavor)
308 self.mano['details']['flavor'] = {}
309 self.mano['details']['flavor']['name'] = flavor_settings.name
310 self.mano['details']['flavor']['id'] = flavor_info.id
312 def prepare_network(self):
313 """Create network/subnet/router if they doen't exist yet"""
315 "Creating network/subnet/router if they doen't exist yet...")
316 subnet_settings = SubnetConfig(
317 name='{}_subnet-{}'.format(self.case_name, self.uuid),
318 cidr="192.168.100.0/24")
319 network_settings = NetworkConfig(
320 name='{}_net-{}'.format(self.case_name, self.uuid),
321 subnet_settings=[subnet_settings])
322 orchestra_network = OpenStackNetwork(
323 self.snaps_creds, network_settings)
324 orchestra_network_info = orchestra_network.create()
325 self.mano['details']['network'] = {}
326 self.mano['details']['network']['id'] = orchestra_network_info.id
327 self.mano['details']['network']['name'] = orchestra_network_info.name
328 self.mano['details']['external_net_name'] = snaps_utils.\
329 get_ext_net_name(self.snaps_creds)
330 self.created_resources.append(orchestra_network)
331 self.orchestra_router = OpenStackRouter(
334 name='{}_router-{}'.format(self.case_name, self.uuid),
335 external_gateway=self.mano['details']['external_net_name'],
337 subnet_settings.name]))
338 self.orchestra_router.create()
339 self.created_resources.append(self.orchestra_router)
340 self.logger.info("Created network and router for Open Baton NFVO...")
342 def get_vim_descriptor(self):
343 """"Create VIM descriptor to be used for onboarding"""
345 "Building VIM descriptor with PoP creds: %s",
347 self.logger.debug("VIM project/tenant id: %s",
348 self.snaps_creds.project_name)
349 keystone = keystone_utils.keystone_client(self.snaps_creds)
350 project_id = keystone_utils.get_project(
351 keystone=keystone, project_name=self.snaps_creds.project_name).id
353 "name": "vim-instance",
354 "authUrl": self.creds.get("auth_url"),
355 "tenant": project_id,
356 "username": self.creds.get("username"),
357 "password": self.creds.get("password"),
359 self.mano['details']['sec_group']
364 "latitude": "52.525876",
365 "longitude": "13.314400"
368 self.logger.info("Built VIM descriptor: %s", vim_json)
371 def deploy_orchestrator(self):
372 self.logger.info("Deploying Open Baton...")
373 self.logger.info("Details: %s", self.mano['details'])
374 start_time = time.time()
376 self.logger.info("Creating orchestra instance...")
377 userdata = get_userdata(self.mano)
378 self.logger.info("flavor: %s\n"
381 self.mano['details']['flavor']['name'],
382 self.mano['requirements']['image'],
383 self.mano['details']['network']['id'])
384 self.logger.debug("userdata: %s\n", userdata)
386 image_settings = ImageConfig(
387 name=self.mano['requirements']['image'],
391 port_settings = PortConfig(
392 name='{}_port-{}'.format(self.case_name, self.uuid),
393 network_name=self.mano['details']['network']['name'])
395 # build configuration of vm
396 orchestra_settings = VmInstanceConfig(
398 flavor=self.mano['details']['flavor']['name'],
399 port_settings=[port_settings],
400 security_group_names=[self.mano['details']['sec_group']],
401 floating_ip_settings=[FloatingIpConfig(
402 name='orchestra_fip-{}'.format(self.uuid),
403 port_name=port_settings.name,
404 router_name=self.orchestra_router.router_settings.name)],
405 userdata=str(userdata))
406 orchestra_vm = OpenStackVmInstance(
407 self.snaps_creds, orchestra_settings, image_settings)
408 orchestra_vm.create()
409 self.mano['details']['fip'] = orchestra_vm.get_floating_ip()
410 self.created_resources.append(orchestra_vm)
411 self.mano['details']['id'] = orchestra_vm.get_vm_info()['id']
413 "Created orchestra instance: %s", self.mano['details']['id'])
414 self.logger.info("Waiting for Open Baton NFVO to be up and running...")
418 self.mano['details']['fip'].ip,
423 "Open Baton NFVO is not started yet (%ss)",
429 duration = time.time() - start_time
430 self.details["orchestrator"].update(
431 status='FAIL', duration=duration)
432 self.logger.error("Open Baton is not started correctly")
435 self.logger.info("Waiting for all components to be up and running...")
437 duration = time.time() - start_time
438 self.details["orchestrator"].update(status='PASS', duration=duration)
439 self.logger.info("Deploy Open Baton NFVO: OK")
442 def deploy_vnf(self):
443 start_time = time.time()
444 self.logger.info("Deploying %s...", self.vnf['name'])
446 main_agent = MainAgent(
447 nfvo_ip=self.mano['details']['fip'].ip,
451 username=self.mano['credentials']['username'],
452 password=self.mano['credentials']['password'])
455 "Create %s Flavor if not existing", self.vnf['name'])
456 flavor_settings = FlavorConfig(
457 name=self.vnf['requirements']['flavor']['name'],
458 ram=self.vnf['requirements']['flavor']['ram_min'],
459 disk=self.vnf['requirements']['flavor']['disk'],
460 vcpus=self.vnf['requirements']['flavor']['vcpus'])
461 flavor = OpenStackFlavor(self.snaps_creds, flavor_settings)
462 flavor_info = flavor.create()
463 self.logger.debug("Flavor id: %s", flavor_info.id)
465 self.logger.info("Getting project 'default'...")
466 project_agent = main_agent.get_agent("project", "")
467 for project in json.loads(project_agent.find()):
468 if project.get("name") == "default":
469 self.mano['details']['project_id'] = project.get("id")
470 self.logger.info("Found project 'default': %s", project)
473 vim_json = self.get_vim_descriptor()
474 self.logger.info("Registering VIM: %s", vim_json)
476 main_agent.get_agent(
477 "vim", project_id=self.mano['details']['project_id']).create(
478 entity=json.dumps(vim_json))
480 market_agent = main_agent.get_agent(
481 "market", project_id=self.mano['details']['project_id'])
484 self.logger.info("sending: %s", self.vnf['descriptor']['url'])
485 nsd = market_agent.create(entity=self.vnf['descriptor']['url'])
486 if nsd.get('id') is None:
487 self.logger.error("NSD not onboarded correctly")
488 duration = time.time() - start_time
489 self.details["vnf"].update(status='FAIL', duration=duration)
491 self.mano['details']['nsd_id'] = nsd.get('id')
492 self.logger.info("Onboarded NSD: " + nsd.get("name"))
494 nsr_agent = main_agent.get_agent(
495 "nsr", project_id=self.mano['details']['project_id'])
497 self.mano['details']['nsr'] = nsr_agent.create(
498 self.mano['details']['nsd_id'])
499 except NfvoException:
500 self.logger.exception("failed")
501 duration = time.time() - start_time
502 self.details["vnf"].update(status='FAIL', duration=duration)
505 if self.mano['details']['nsr'].get('code') is not None:
507 "%s cannot be deployed: %s -> %s",
509 self.mano['details']['nsr'].get('code'),
510 self.mano['details']['nsr'].get('message'))
511 self.logger.error("%s cannot be deployed", self.vnf['name'])
512 duration = time.time() - start_time
513 self.details["vnf"].update(status='FAIL', duration=duration)
517 self.logger.info("Waiting for NSR to go to ACTIVE...")
518 while self.mano['details']['nsr'].get("status") != 'ACTIVE' \
519 and self.mano['details']['nsr'].get("status") != 'ERROR':
521 self.logger.info("NSR is not yet ACTIVE... (%ss)", 60 * timeout)
523 self.logger.error("INACTIVE NSR after %s sec..", 60 * timeout)
524 duration = time.time() - start_time
525 self.details["vnf"].update(status='FAIL', duration=duration)
528 self.mano['details']['nsr'] = json.loads(
529 nsr_agent.find(self.mano['details']['nsr'].get('id')))
531 duration = time.time() - start_time
532 if self.mano['details']['nsr'].get("status") == 'ACTIVE':
533 self.details["vnf"].update(status='PASS', duration=duration)
534 self.logger.info("Sleep for 60s to ensure that all "
535 "services are up and running...")
539 self.details["vnf"].update(status='FAIL', duration=duration)
540 self.logger.error("NSR: %s", self.mano['details'].get('nsr'))
546 "Testing VNF Clearwater IMS is not yet implemented...")
547 start_time = time.time()
549 duration = time.time() - start_time
550 self.details["test_vnf"].update(status='PASS', duration=duration)
551 self.logger.info("Test VNF: OK")
555 self.logger.info("Cleaning %s...", self.case_name)
557 main_agent = MainAgent(
558 nfvo_ip=self.mano['details']['fip'].ip,
559 nfvo_port=8080, https=False, version=1,
560 username=self.mano['credentials']['username'],
561 password=self.mano['credentials']['password'])
562 self.logger.info("Terminating %s...", self.vnf['name'])
563 if self.mano['details'].get('nsr'):
564 main_agent.get_agent(
566 project_id=self.mano['details']['project_id']).delete(
567 self.mano['details']['nsr'].get('id'))
568 self.logger.info("Sleeping 60 seconds...")
571 self.logger.info("No need to terminate the VNF...")
572 except (NfvoException, KeyError) as exc:
573 self.logger.error('Unexpected error cleaning - %s', exc)
574 super(ClearwaterImsVnf, self).clean()