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
42 from functest.utils import env
44 __author__ = "Valentin Boucher <valentin.boucher@orange.com>"
47 class CloudifyIms(clearwater_ims_base.ClearwaterOnBoardingBase):
48 """Clearwater vIMS deployed with Cloudify Orchestrator Case."""
50 __logger = logging.getLogger(__name__)
52 def __init__(self, **kwargs):
53 """Initialize CloudifyIms testcase object."""
54 if "case_name" not in kwargs:
55 kwargs["case_name"] = "cloudify_ims"
56 super(CloudifyIms, self).__init__(**kwargs)
58 # Retrieve the configuration
60 self.config = getattr(
61 config.CONF, 'vnf_{}_config'.format(self.case_name))
63 raise Exception("VNF config file not found")
65 config_file = os.path.join(self.case_dir, self.config)
66 self.orchestrator = dict(
67 requirements=get_config("orchestrator.requirements", config_file),
69 self.details['orchestrator'] = dict(
70 name=get_config("orchestrator.name", config_file),
71 version=get_config("orchestrator.version", config_file),
75 self.__logger.debug("Orchestrator configuration %s", self.orchestrator)
77 descriptor=get_config("vnf.descriptor", config_file),
78 inputs=get_config("vnf.inputs", config_file),
79 requirements=get_config("vnf.requirements", config_file)
81 self.details['vnf'] = dict(
82 descriptor_version=self.vnf['descriptor']['version'],
83 name=get_config("vnf.name", config_file),
84 version=get_config("vnf.version", config_file),
86 self.__logger.debug("VNF configuration: %s", self.vnf)
88 self.details['test_vnf'] = dict(
89 name=get_config("vnf_test_suite.name", config_file),
90 version=get_config("vnf_test_suite.version", config_file)
92 self.images = get_config("tenant_images", config_file)
93 self.__logger.info("Images needed for vIMS: %s", self.images)
96 """Prepare testscase (Additional pre-configuration steps)."""
97 super(CloudifyIms, self).prepare()
99 self.__logger.info("Additional pre-configuration steps")
101 compute_quotas = self.os_project.get_compute_quotas()
102 network_quotas = self.os_project.get_network_quotas()
105 self.vnf['requirements']['compute_quotas'].items()):
106 setattr(compute_quotas, key, value)
109 self.vnf['requirements']['network_quotas'].items()):
110 setattr(network_quotas, key, value)
112 compute_quotas = self.os_project.update_compute_quotas(compute_quotas)
113 network_quotas = self.os_project.update_network_quotas(network_quotas)
116 self.__logger.info("Upload some OS images if it doesn't exist")
117 for image_name, image_file in self.images.iteritems():
118 self.__logger.info("image: %s, file: %s", image_name, image_file)
119 if image_file and image_name:
120 image_creator = OpenStackImage(
123 name=image_name, image_user='cloud',
124 img_format='qcow2', image_file=image_file))
125 image_creator.create()
126 self.created_object.append(image_creator)
128 def deploy_orchestrator(self):
130 Deploy Cloudify Manager.
132 network, security group, fip, VM creation
136 start_time = time.time()
137 self.__logger.info("Creating keypair ...")
138 kp_file = os.path.join(self.data_dir, "cloudify_ims.pem")
139 keypair_settings = KeypairConfig(
140 name='cloudify_ims_kp-{}'.format(self.uuid),
141 private_filepath=kp_file)
142 keypair_creator = OpenStackKeypair(self.snaps_creds, keypair_settings)
143 keypair_creator.create()
144 self.created_object.append(keypair_creator)
146 self.__logger.info("Creating full network ...")
147 subnet_settings = SubnetConfig(
148 name='cloudify_ims_subnet-{}'.format(self.uuid),
149 cidr='10.67.79.0/24',
150 dns_nameservers=[env.get('NAMESERVER')])
151 network_settings = NetworkConfig(
152 name='cloudify_ims_network-{}'.format(self.uuid),
153 subnet_settings=[subnet_settings])
154 network_creator = OpenStackNetwork(self.snaps_creds, network_settings)
155 network_creator.create()
156 self.created_object.append(network_creator)
157 ext_net_name = snaps_utils.get_ext_net_name(self.snaps_creds)
158 router_creator = OpenStackRouter(
161 name='cloudify_ims_router-{}'.format(self.uuid),
162 external_gateway=ext_net_name,
163 internal_subnets=[subnet_settings.name]))
164 router_creator.create()
165 self.created_object.append(router_creator)
167 # security group creation
168 self.__logger.info("Creating security group for cloudify manager vm")
171 SecurityGroupRuleConfig(
172 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
173 direction=Direction.ingress, protocol=Protocol.tcp,
174 port_range_min=1, port_range_max=65535))
176 SecurityGroupRuleConfig(
177 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
178 direction=Direction.ingress, protocol=Protocol.udp,
179 port_range_min=1, port_range_max=65535))
181 security_group_creator = OpenStackSecurityGroup(
184 name="sg-cloudify-manager-{}".format(self.uuid),
185 rule_settings=sg_rules))
187 security_group_creator.create()
188 self.created_object.append(security_group_creator)
190 # orchestrator VM flavor
191 self.__logger.info("Get or create flavor for cloudify manager vm ...")
193 flavor_settings = FlavorConfig(
194 name=self.orchestrator['requirements']['flavor']['name'],
195 ram=self.orchestrator['requirements']['flavor']['ram_min'],
198 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
199 flavor_creator.create()
200 self.created_object.append(flavor_creator)
201 image_settings = ImageConfig(
202 name=self.orchestrator['requirements']['os_image'],
206 port_settings = PortConfig(
207 name='cloudify_manager_port-{}'.format(self.uuid),
208 network_name=network_settings.name)
210 manager_settings = VmInstanceConfig(
211 name='cloudify_manager-{}'.format(self.uuid),
212 flavor=flavor_settings.name,
213 port_settings=[port_settings],
214 security_group_names=[
215 security_group_creator.sec_grp_settings.name],
216 floating_ip_settings=[FloatingIpConfig(
217 name='cloudify_manager_fip-{}'.format(self.uuid),
218 port_name=port_settings.name,
219 router_name=router_creator.router_settings.name)])
221 manager_creator = OpenStackVmInstance(
222 self.snaps_creds, manager_settings, image_settings,
225 self.__logger.info("Creating cloudify manager VM")
226 manager_creator.create()
227 self.created_object.append(manager_creator)
229 public_auth_url = keystone_utils.get_endpoint(
230 self.snaps_creds, 'identity')
233 keystone_username=self.snaps_creds.username,
234 keystone_password=self.snaps_creds.password,
235 keystone_tenant_name=self.snaps_creds.project_name,
236 keystone_url=public_auth_url,
237 region=self.snaps_creds.region_name,
238 user_domain_name=self.snaps_creds.user_domain_name,
239 project_domain_name=self.snaps_creds.project_domain_name)
240 self.__logger.info("Set creds for cloudify manager %s", cfy_creds)
242 cfy_client = CloudifyClient(
243 host=manager_creator.get_floating_ip().ip,
244 username='admin', password='admin', tenant='default_tenant')
246 self.orchestrator['object'] = cfy_client
248 self.__logger.info("Attemps running status of the Manager")
251 while str(cfy_status) != 'running' and retry:
253 cfy_status = cfy_client.manager.get_status()['status']
255 "The current manager status is %s", cfy_status)
256 except Exception: # pylint: disable=broad-except
258 "Cloudify Manager isn't up and running. Retrying ...")
262 if str(cfy_status) == 'running':
263 self.__logger.info("Cloudify Manager is up and running")
265 raise Exception("Cloudify Manager isn't up and running")
267 self.__logger.info("Put OpenStack creds in manager")
268 secrets_list = cfy_client.secrets.list()
269 for k, val in cfy_creds.iteritems():
270 if not any(d.get('key', None) == k for d in secrets_list):
271 cfy_client.secrets.create(k, val)
273 cfy_client.secrets.update(k, val)
275 duration = time.time() - start_time
277 self.__logger.info("Put private keypair in manager")
278 if manager_creator.vm_ssh_active(block=True):
279 ssh = manager_creator.ssh_client()
280 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
281 scp.put(kp_file, '~/')
282 cmd = "sudo cp ~/cloudify_ims.pem /etc/cloudify/"
283 self.run_blocking_ssh_command(ssh, cmd)
284 cmd = "sudo chmod 444 /etc/cloudify/cloudify_ims.pem"
285 self.run_blocking_ssh_command(ssh, cmd)
286 cmd = "sudo yum install -y gcc python-devel"
287 self.run_blocking_ssh_command(
288 ssh, cmd, "Unable to install packages on manager")
290 self.details['orchestrator'].update(status='PASS', duration=duration)
292 self.vnf['inputs'].update(dict(
293 external_network_name=ext_net_name,
294 network_name=network_settings.name,
295 key_pair_name=keypair_settings.name
297 self.result = 1/3 * 100
300 def deploy_vnf(self):
301 """Deploy Clearwater IMS."""
302 start_time = time.time()
304 self.__logger.info("Upload VNFD")
305 cfy_client = self.orchestrator['object']
306 descriptor = self.vnf['descriptor']
307 cfy_client.blueprints.publish_archive(descriptor.get('url'),
308 descriptor.get('name'),
309 descriptor.get('file_name'))
311 self.__logger.info("Get or create flavor for all clearwater vm")
312 flavor_settings = FlavorConfig(
313 name=self.vnf['requirements']['flavor']['name'],
314 ram=self.vnf['requirements']['flavor']['ram_min'],
317 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
318 flavor_creator.create()
319 self.created_object.append(flavor_creator)
321 self.vnf['inputs'].update(dict(
322 flavor_id=self.vnf['requirements']['flavor']['name'],
325 self.__logger.info("Create VNF Instance")
326 cfy_client.deployments.create(descriptor.get('name'),
327 descriptor.get('name'),
328 self.vnf.get('inputs'))
330 wait_for_execution(cfy_client,
331 _get_deployment_environment_creation_execution(
332 cfy_client, descriptor.get('name')),
336 self.__logger.info("Start the VNF Instance deployment")
337 execution = cfy_client.executions.start(descriptor.get('name'),
340 execution = wait_for_execution(cfy_client, execution, self.__logger)
342 duration = time.time() - start_time
344 self.__logger.info(execution)
345 if execution.status == 'terminated':
346 self.details['vnf'].update(status='PASS', duration=duration)
347 self.result += 1/3 * 100
350 self.details['vnf'].update(status='FAIL', duration=duration)
355 """Run test on clearwater ims instance."""
356 start_time = time.time()
358 cfy_client = self.orchestrator['object']
360 outputs = cfy_client.deployments.outputs.get(
361 self.vnf['descriptor'].get('name'))['outputs']
362 dns_ip = outputs['dns_ip']
363 ellis_ip = outputs['ellis_ip']
364 self.config_ellis(ellis_ip)
369 vims_test_result = self.run_clearwater_live_test(
371 public_domain=self.vnf['inputs']["public_domain"])
372 duration = time.time() - start_time
373 short_result, nb_test = sig_test_format(vims_test_result)
374 self.__logger.info(short_result)
375 self.details['test_vnf'].update(result=short_result,
376 full_result=vims_test_result,
379 vnf_test_rate = short_result['passed'] / nb_test
380 # orchestrator + vnf + test_vnf
381 self.result += vnf_test_rate / 3 * 100
382 except ZeroDivisionError:
383 self.__logger.error("No test has been executed")
384 self.details['test_vnf'].update(status='FAIL')
390 """Clean created objects/functions."""
392 cfy_client = self.orchestrator['object']
393 dep_name = self.vnf['descriptor'].get('name')
394 # kill existing execution
395 self.__logger.info('Deleting the current deployment')
396 exec_list = cfy_client.executions.list(dep_name)
397 for execution in exec_list:
398 if execution['status'] == "started":
400 cfy_client.executions.cancel(execution['id'],
402 except Exception: # pylint: disable=broad-except
403 self.__logger.warn("Can't cancel the current exec")
405 execution = cfy_client.executions.start(
408 parameters=dict(ignore_failure=True),
411 wait_for_execution(cfy_client, execution, self.__logger)
412 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
413 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
414 except Exception: # pylint: disable=broad-except
415 self.__logger.exception("Some issue during the undeployment ..")
417 super(CloudifyIms, self).clean()
420 def run_blocking_ssh_command(ssh, cmd,
421 error_msg="Unable to run this command"):
422 """Command to run ssh command with the exit status."""
423 _, stdout, stderr = ssh.exec_command(cmd)
424 CloudifyIms.__logger.debug("SSH %s stdout: %s", cmd, stdout.read())
425 if stdout.channel.recv_exit_status() != 0:
426 CloudifyIms.__logger.error("SSH %s stderr: %s", cmd, stderr.read())
427 raise Exception(error_msg)
429 @energy.enable_recording
430 def run(self, **kwargs):
431 """Execute CloudifyIms test case."""
432 return super(CloudifyIms, self).run(**kwargs)
435 # ----------------------------------------------------------
439 # -----------------------------------------------------------
440 def get_config(parameter, file_path):
442 Get config parameter.
444 Returns the value of a given parameter in file.yaml
445 parameter must be given in string format with dots
446 Example: general.openstack.image_name
448 with open(file_path) as config_file:
449 file_yaml = yaml.safe_load(config_file)
452 for element in parameter.split("."):
453 value = value.get(element)
455 raise ValueError("The parameter %s is not defined in"
456 " reporting.yaml" % parameter)
460 def wait_for_execution(client, execution, logger, timeout=1500, ):
461 """Wait for a workflow execution on Cloudify Manager."""
462 # if execution already ended - return without waiting
463 if execution.status in Execution.END_STATES:
466 if timeout is not None:
467 deadline = time.time() + timeout
469 # Poll for execution status and execution logs, until execution ends
470 # and we receive an event of type in WORKFLOW_END_TYPES
474 execution_ended = False
476 event_list = client.events.list(
477 execution_id=execution.id,
481 sort='@timestamp').items
483 offset = offset + len(event_list)
484 for event in event_list:
485 logger.debug(event.get('message'))
487 if timeout is not None:
488 if time.time() > deadline:
490 'execution of operation {0} for deployment {1} '
491 'timed out'.format(execution.workflow_id,
492 execution.deployment_id))
494 # update the remaining timeout
495 timeout = deadline - time.time()
497 if not execution_ended:
498 execution = client.executions.get(execution.id)
499 execution_ended = execution.status in Execution.END_STATES
509 def _get_deployment_environment_creation_execution(client, deployment_id):
511 Get the execution id of a env preparation.
513 network, security group, fip, VM creation
515 executions = client.executions.list(deployment_id=deployment_id)
516 for execution in executions:
517 if execution.workflow_id == 'create_deployment_environment':
519 raise RuntimeError('Failed to get create_deployment_environment '
520 'workflow execution.'
521 'Available executions: {0}'.format(executions))
524 def sig_test_format(sig_test):
525 """Process the signaling result to have a short result."""
529 for data_test in sig_test:
530 if data_test['result'] == "Passed":
532 elif data_test['result'] == "Failed":
534 elif data_test['result'] == "Skipped":
536 short_sig_test_result = {}
537 short_sig_test_result['passed'] = nb_passed
538 short_sig_test_result['failures'] = nb_failures
539 short_sig_test_result['skipped'] = nb_skipped
540 nb_test = nb_passed + nb_skipped
541 return (short_sig_test_result, nb_test)