3 # Copyright (c) 2017 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 """CloudifyIms testcase implementation."""
16 from cloudify_rest_client import CloudifyClient
17 from cloudify_rest_client.executions import Execution
18 from scp import SCPClient
21 from snaps.config.flavor import FlavorConfig
22 from snaps.config.image import ImageConfig
23 from snaps.config.keypair import KeypairConfig
24 from snaps.config.network import NetworkConfig, PortConfig, SubnetConfig
25 from snaps.config.router import RouterConfig
26 from snaps.config.security_group import (
27 Direction, Protocol, SecurityGroupConfig, SecurityGroupRuleConfig)
28 from snaps.config.vm_inst import FloatingIpConfig, VmInstanceConfig
29 from snaps.openstack.create_flavor import OpenStackFlavor
30 from snaps.openstack.create_image import OpenStackImage
31 from snaps.openstack.create_instance import OpenStackVmInstance
32 from snaps.openstack.create_keypairs import OpenStackKeypair
33 from snaps.openstack.create_network import OpenStackNetwork
34 from snaps.openstack.create_router import OpenStackRouter
35 from snaps.openstack.create_security_group import OpenStackSecurityGroup
36 from snaps.openstack.utils import keystone_utils
37 from xtesting.energy import energy
39 from functest.opnfv_tests.openstack.snaps import snaps_utils
40 import functest.opnfv_tests.vnf.ims.clearwater_ims_base as clearwater_ims_base
41 from functest.utils import config
43 __author__ = "Valentin Boucher <valentin.boucher@orange.com>"
46 class CloudifyIms(clearwater_ims_base.ClearwaterOnBoardingBase):
47 """Clearwater vIMS deployed with Cloudify Orchestrator Case."""
49 __logger = logging.getLogger(__name__)
51 def __init__(self, **kwargs):
52 """Initialize CloudifyIms testcase object."""
53 if "case_name" not in kwargs:
54 kwargs["case_name"] = "cloudify_ims"
55 super(CloudifyIms, self).__init__(**kwargs)
57 # Retrieve the configuration
59 self.config = getattr(
60 config.CONF, 'vnf_{}_config'.format(self.case_name))
62 raise Exception("VNF config file not found")
64 config_file = os.path.join(self.case_dir, self.config)
65 self.orchestrator = dict(
66 requirements=get_config("orchestrator.requirements", config_file),
68 self.details['orchestrator'] = dict(
69 name=get_config("orchestrator.name", config_file),
70 version=get_config("orchestrator.version", config_file),
74 self.__logger.debug("Orchestrator configuration %s", self.orchestrator)
76 descriptor=get_config("vnf.descriptor", config_file),
77 inputs=get_config("vnf.inputs", config_file),
78 requirements=get_config("vnf.requirements", config_file)
80 self.details['vnf'] = dict(
81 descriptor_version=self.vnf['descriptor']['version'],
82 name=get_config("vnf.name", config_file),
83 version=get_config("vnf.version", config_file),
85 self.__logger.debug("VNF configuration: %s", self.vnf)
87 self.details['test_vnf'] = dict(
88 name=get_config("vnf_test_suite.name", config_file),
89 version=get_config("vnf_test_suite.version", config_file)
91 self.images = get_config("tenant_images", config_file)
92 self.__logger.info("Images needed for vIMS: %s", self.images)
95 """Prepare testscase (Additional pre-configuration steps)."""
96 super(CloudifyIms, self).prepare()
98 self.__logger.info("Additional pre-configuration steps")
100 compute_quotas = self.os_project.get_compute_quotas()
101 network_quotas = self.os_project.get_network_quotas()
104 self.vnf['requirements']['compute_quotas'].items()):
105 setattr(compute_quotas, key, value)
108 self.vnf['requirements']['network_quotas'].items()):
109 setattr(network_quotas, key, value)
111 compute_quotas = self.os_project.update_compute_quotas(compute_quotas)
112 network_quotas = self.os_project.update_network_quotas(network_quotas)
115 self.__logger.info("Upload some OS images if it doesn't exist")
116 for image_name, image_file in self.images.iteritems():
117 self.__logger.info("image: %s, file: %s", image_name, image_file)
118 if image_file and image_name:
119 image_creator = OpenStackImage(
122 name=image_name, image_user='cloud',
123 img_format='qcow2', image_file=image_file))
124 image_creator.create()
125 self.created_object.append(image_creator)
127 def deploy_orchestrator(self):
129 Deploy Cloudify Manager.
131 network, security group, fip, VM creation
135 start_time = time.time()
136 self.__logger.info("Creating keypair ...")
137 kp_file = os.path.join(self.data_dir, "cloudify_ims.pem")
138 keypair_settings = KeypairConfig(
139 name='cloudify_ims_kp-{}'.format(self.uuid),
140 private_filepath=kp_file)
141 keypair_creator = OpenStackKeypair(self.snaps_creds, keypair_settings)
142 keypair_creator.create()
143 self.created_object.append(keypair_creator)
145 self.__logger.info("Creating full network ...")
146 subnet_settings = SubnetConfig(
147 name='cloudify_ims_subnet-{}'.format(self.uuid),
148 cidr='10.67.79.0/24')
149 network_settings = NetworkConfig(
150 name='cloudify_ims_network-{}'.format(self.uuid),
151 subnet_settings=[subnet_settings])
152 network_creator = OpenStackNetwork(self.snaps_creds, network_settings)
153 network_creator.create()
154 self.created_object.append(network_creator)
155 ext_net_name = snaps_utils.get_ext_net_name(self.snaps_creds)
156 router_creator = OpenStackRouter(
159 name='cloudify_ims_router-{}'.format(self.uuid),
160 external_gateway=ext_net_name,
161 internal_subnets=[subnet_settings.name]))
162 router_creator.create()
163 self.created_object.append(router_creator)
165 # security group creation
166 self.__logger.info("Creating security group for cloudify manager vm")
169 SecurityGroupRuleConfig(
170 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
171 direction=Direction.ingress, protocol=Protocol.tcp,
172 port_range_min=1, port_range_max=65535))
174 SecurityGroupRuleConfig(
175 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
176 direction=Direction.ingress, protocol=Protocol.udp,
177 port_range_min=1, port_range_max=65535))
179 security_group_creator = OpenStackSecurityGroup(
182 name="sg-cloudify-manager-{}".format(self.uuid),
183 rule_settings=sg_rules))
185 security_group_creator.create()
186 self.created_object.append(security_group_creator)
188 # orchestrator VM flavor
189 self.__logger.info("Get or create flavor for cloudify manager vm ...")
191 flavor_settings = FlavorConfig(
192 name=self.orchestrator['requirements']['flavor']['name'],
193 ram=self.orchestrator['requirements']['flavor']['ram_min'],
196 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
197 flavor_creator.create()
198 self.created_object.append(flavor_creator)
199 image_settings = ImageConfig(
200 name=self.orchestrator['requirements']['os_image'],
204 port_settings = PortConfig(
205 name='cloudify_manager_port-{}'.format(self.uuid),
206 network_name=network_settings.name)
208 manager_settings = VmInstanceConfig(
209 name='cloudify_manager-{}'.format(self.uuid),
210 flavor=flavor_settings.name,
211 port_settings=[port_settings],
212 security_group_names=[
213 security_group_creator.sec_grp_settings.name],
214 floating_ip_settings=[FloatingIpConfig(
215 name='cloudify_manager_fip-{}'.format(self.uuid),
216 port_name=port_settings.name,
217 router_name=router_creator.router_settings.name)])
219 manager_creator = OpenStackVmInstance(
220 self.snaps_creds, manager_settings, image_settings,
223 self.__logger.info("Creating cloudify manager VM")
224 manager_creator.create()
225 self.created_object.append(manager_creator)
227 public_auth_url = keystone_utils.get_endpoint(
228 self.snaps_creds, 'identity')
231 keystone_username=self.snaps_creds.username,
232 keystone_password=self.snaps_creds.password,
233 keystone_tenant_name=self.snaps_creds.project_name,
234 keystone_url=public_auth_url,
235 region=self.snaps_creds.region_name,
236 user_domain_name=self.snaps_creds.user_domain_name,
237 project_domain_name=self.snaps_creds.project_domain_name)
238 self.__logger.info("Set creds for cloudify manager %s", cfy_creds)
240 cfy_client = CloudifyClient(
241 host=manager_creator.get_floating_ip().ip,
242 username='admin', password='admin', tenant='default_tenant')
244 self.orchestrator['object'] = cfy_client
246 self.__logger.info("Attemps running status of the Manager")
249 while str(cfy_status) != 'running' and retry:
251 cfy_status = cfy_client.manager.get_status()['status']
252 self.__logger.debug("The current manager status is %s",
254 except Exception: # pylint: disable=broad-except
255 self.__logger.exception(
256 "Cloudify Manager isn't up and running. Retrying ...")
260 if str(cfy_status) == 'running':
261 self.__logger.info("Cloudify Manager is up and running")
263 raise Exception("Cloudify Manager isn't up and running")
265 self.__logger.info("Put OpenStack creds in manager")
266 secrets_list = cfy_client.secrets.list()
267 for k, val in cfy_creds.iteritems():
268 if not any(d.get('key', None) == k for d in secrets_list):
269 cfy_client.secrets.create(k, val)
271 cfy_client.secrets.update(k, val)
273 duration = time.time() - start_time
275 self.__logger.info("Put private keypair in manager")
276 if manager_creator.vm_ssh_active(block=True):
277 ssh = manager_creator.ssh_client()
278 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
279 scp.put(kp_file, '~/')
280 cmd = "sudo cp ~/cloudify_ims.pem /etc/cloudify/"
281 self.run_blocking_ssh_command(ssh, cmd)
282 cmd = "sudo chmod 444 /etc/cloudify/cloudify_ims.pem"
283 self.run_blocking_ssh_command(ssh, cmd)
284 cmd = "sudo yum install -y gcc python-devel"
285 self.run_blocking_ssh_command(
286 ssh, cmd, "Unable to install packages on manager")
288 self.details['orchestrator'].update(status='PASS', duration=duration)
290 self.vnf['inputs'].update(dict(
291 external_network_name=ext_net_name,
292 network_name=network_settings.name,
293 key_pair_name=keypair_settings.name
295 self.result = 1/3 * 100
298 def deploy_vnf(self):
299 """Deploy Clearwater IMS."""
300 start_time = time.time()
302 self.__logger.info("Upload VNFD")
303 cfy_client = self.orchestrator['object']
304 descriptor = self.vnf['descriptor']
305 cfy_client.blueprints.publish_archive(descriptor.get('url'),
306 descriptor.get('name'),
307 descriptor.get('file_name'))
309 self.__logger.info("Get or create flavor for all clearwater vm")
310 flavor_settings = FlavorConfig(
311 name=self.vnf['requirements']['flavor']['name'],
312 ram=self.vnf['requirements']['flavor']['ram_min'],
315 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
316 flavor_creator.create()
317 self.created_object.append(flavor_creator)
319 self.vnf['inputs'].update(dict(
320 flavor_id=self.vnf['requirements']['flavor']['name'],
323 self.__logger.info("Create VNF Instance")
324 cfy_client.deployments.create(descriptor.get('name'),
325 descriptor.get('name'),
326 self.vnf.get('inputs'))
328 wait_for_execution(cfy_client,
329 _get_deployment_environment_creation_execution(
330 cfy_client, descriptor.get('name')),
334 self.__logger.info("Start the VNF Instance deployment")
335 execution = cfy_client.executions.start(descriptor.get('name'),
338 execution = wait_for_execution(cfy_client, execution, self.__logger)
340 duration = time.time() - start_time
342 self.__logger.info(execution)
343 if execution.status == 'terminated':
344 self.details['vnf'].update(status='PASS', duration=duration)
345 self.result += 1/3 * 100
348 self.details['vnf'].update(status='FAIL', duration=duration)
353 """Run test on clearwater ims instance."""
354 start_time = time.time()
356 cfy_client = self.orchestrator['object']
358 outputs = cfy_client.deployments.outputs.get(
359 self.vnf['descriptor'].get('name'))['outputs']
360 dns_ip = outputs['dns_ip']
361 ellis_ip = outputs['ellis_ip']
362 self.config_ellis(ellis_ip)
367 vims_test_result = self.run_clearwater_live_test(
369 public_domain=self.vnf['inputs']["public_domain"])
370 duration = time.time() - start_time
371 short_result, nb_test = sig_test_format(vims_test_result)
372 self.__logger.info(short_result)
373 self.details['test_vnf'].update(result=short_result,
374 full_result=vims_test_result,
377 vnf_test_rate = short_result['passed'] / nb_test
378 # orchestrator + vnf + test_vnf
379 self.result += vnf_test_rate / 3 * 100
380 except ZeroDivisionError:
381 self.__logger.error("No test has been executed")
382 self.details['test_vnf'].update(status='FAIL')
388 """Clean created objects/functions."""
390 cfy_client = self.orchestrator['object']
391 dep_name = self.vnf['descriptor'].get('name')
392 # kill existing execution
393 self.__logger.info('Deleting the current deployment')
394 exec_list = cfy_client.executions.list(dep_name)
395 for execution in exec_list:
396 if execution['status'] == "started":
398 cfy_client.executions.cancel(execution['id'],
400 except Exception: # pylint: disable=broad-except
401 self.__logger.warn("Can't cancel the current exec")
403 execution = cfy_client.executions.start(
406 parameters=dict(ignore_failure=True),
409 wait_for_execution(cfy_client, execution, self.__logger)
410 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
411 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
412 except Exception: # pylint: disable=broad-except
413 self.__logger.warn("Some issue during the undeployment ..")
414 self.__logger.warn("Tenant clean continue ..")
416 super(CloudifyIms, self).clean()
419 def run_blocking_ssh_command(ssh, cmd,
420 error_msg="Unable to run this command"):
421 """Command to run ssh command with the exit status."""
422 _, stdout, stderr = ssh.exec_command(cmd)
423 CloudifyIms.__logger.debug("SSH %s stdout: %s", cmd, stdout.read())
424 if stdout.channel.recv_exit_status() != 0:
425 CloudifyIms.__logger.error("SSH %s stderr: %s", cmd, stderr.read())
426 raise Exception(error_msg)
428 @energy.enable_recording
429 def run(self, **kwargs):
430 """Execute CloudifyIms test case."""
431 return super(CloudifyIms, self).run(**kwargs)
434 # ----------------------------------------------------------
438 # -----------------------------------------------------------
439 def get_config(parameter, file_path):
441 Get config parameter.
443 Returns the value of a given parameter in file.yaml
444 parameter must be given in string format with dots
445 Example: general.openstack.image_name
447 with open(file_path) as config_file:
448 file_yaml = yaml.safe_load(config_file)
451 for element in parameter.split("."):
452 value = value.get(element)
454 raise ValueError("The parameter %s is not defined in"
455 " reporting.yaml" % parameter)
459 def wait_for_execution(client, execution, logger, timeout=1500, ):
460 """Wait for a workflow execution on Cloudify Manager."""
461 # if execution already ended - return without waiting
462 if execution.status in Execution.END_STATES:
465 if timeout is not None:
466 deadline = time.time() + timeout
468 # Poll for execution status and execution logs, until execution ends
469 # and we receive an event of type in WORKFLOW_END_TYPES
473 execution_ended = False
475 event_list = client.events.list(
476 execution_id=execution.id,
480 sort='@timestamp').items
482 offset = offset + len(event_list)
483 for event in event_list:
484 logger.debug(event.get('message'))
486 if timeout is not None:
487 if time.time() > deadline:
489 'execution of operation {0} for deployment {1} '
490 'timed out'.format(execution.workflow_id,
491 execution.deployment_id))
493 # update the remaining timeout
494 timeout = deadline - time.time()
496 if not execution_ended:
497 execution = client.executions.get(execution.id)
498 execution_ended = execution.status in Execution.END_STATES
508 def _get_deployment_environment_creation_execution(client, deployment_id):
510 Get the execution id of a env preparation.
512 network, security group, fip, VM creation
514 executions = client.executions.list(deployment_id=deployment_id)
515 for execution in executions:
516 if execution.workflow_id == 'create_deployment_environment':
518 raise RuntimeError('Failed to get create_deployment_environment '
519 'workflow execution.'
520 'Available executions: {0}'.format(executions))
523 def sig_test_format(sig_test):
524 """Process the signaling result to have a short result."""
528 for data_test in sig_test:
529 if data_test['result'] == "Passed":
531 elif data_test['result'] == "Failed":
533 elif data_test['result'] == "Skipped":
535 short_sig_test_result = {}
536 short_sig_test_result['passed'] = nb_passed
537 short_sig_test_result['failures'] = nb_failures
538 short_sig_test_result['skipped'] = nb_skipped
539 nb_test = nb_passed + nb_skipped
540 return (short_sig_test_result, nb_test)