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):
129 # pylint: disable=too-many-locals,too-many-statements
131 Deploy Cloudify Manager.
133 network, security group, fip, VM creation
137 start_time = time.time()
138 self.__logger.info("Creating keypair ...")
139 kp_file = os.path.join(self.data_dir, "cloudify_ims.pem")
140 keypair_settings = KeypairConfig(
141 name='cloudify_ims_kp-{}'.format(self.uuid),
142 private_filepath=kp_file)
143 keypair_creator = OpenStackKeypair(self.snaps_creds, keypair_settings)
144 keypair_creator.create()
145 self.created_object.append(keypair_creator)
147 self.__logger.info("Creating full network ...")
148 subnet_settings = SubnetConfig(
149 name='cloudify_ims_subnet-{}'.format(self.uuid),
150 cidr='10.67.79.0/24',
151 dns_nameservers=[env.get('NAMESERVER')])
152 network_settings = NetworkConfig(
153 name='cloudify_ims_network-{}'.format(self.uuid),
154 subnet_settings=[subnet_settings])
155 network_creator = OpenStackNetwork(self.snaps_creds, network_settings)
156 network_creator.create()
157 self.created_object.append(network_creator)
158 ext_net_name = snaps_utils.get_ext_net_name(self.snaps_creds)
159 router_creator = OpenStackRouter(
162 name='cloudify_ims_router-{}'.format(self.uuid),
163 external_gateway=ext_net_name,
164 internal_subnets=[subnet_settings.name]))
165 router_creator.create()
166 self.created_object.append(router_creator)
168 # security group creation
169 self.__logger.info("Creating security group for cloudify manager vm")
172 SecurityGroupRuleConfig(
173 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
174 direction=Direction.ingress, protocol=Protocol.tcp,
175 port_range_min=1, port_range_max=65535))
177 SecurityGroupRuleConfig(
178 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
179 direction=Direction.ingress, protocol=Protocol.udp,
180 port_range_min=1, port_range_max=65535))
182 security_group_creator = OpenStackSecurityGroup(
185 name="sg-cloudify-manager-{}".format(self.uuid),
186 rule_settings=sg_rules))
188 security_group_creator.create()
189 self.created_object.append(security_group_creator)
191 # orchestrator VM flavor
192 self.__logger.info("Get or create flavor for cloudify manager vm ...")
194 flavor_settings = FlavorConfig(
195 name=self.orchestrator['requirements']['flavor']['name'],
196 ram=self.orchestrator['requirements']['flavor']['ram_min'],
199 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
200 flavor_creator.create()
201 self.created_object.append(flavor_creator)
202 image_settings = ImageConfig(
203 name=self.orchestrator['requirements']['os_image'],
207 port_settings = PortConfig(
208 name='cloudify_manager_port-{}'.format(self.uuid),
209 network_name=network_settings.name)
211 manager_settings = VmInstanceConfig(
212 name='cloudify_manager-{}'.format(self.uuid),
213 flavor=flavor_settings.name,
214 port_settings=[port_settings],
215 security_group_names=[
216 security_group_creator.sec_grp_settings.name],
217 floating_ip_settings=[FloatingIpConfig(
218 name='cloudify_manager_fip-{}'.format(self.uuid),
219 port_name=port_settings.name,
220 router_name=router_creator.router_settings.name)])
222 manager_creator = OpenStackVmInstance(
223 self.snaps_creds, manager_settings, image_settings,
226 self.__logger.info("Creating cloudify manager VM")
227 manager_creator.create()
228 self.created_object.append(manager_creator)
230 public_auth_url = keystone_utils.get_endpoint(
231 self.snaps_creds, 'identity')
234 keystone_username=self.snaps_creds.username,
235 keystone_password=self.snaps_creds.password,
236 keystone_tenant_name=self.snaps_creds.project_name,
237 keystone_url=public_auth_url,
238 region=self.snaps_creds.region_name,
239 user_domain_name=self.snaps_creds.user_domain_name,
240 project_domain_name=self.snaps_creds.project_domain_name)
241 self.__logger.info("Set creds for cloudify manager %s", cfy_creds)
243 cfy_client = CloudifyClient(
244 host=manager_creator.get_floating_ip().ip,
245 username='admin', password='admin', tenant='default_tenant')
247 self.orchestrator['object'] = cfy_client
249 self.__logger.info("Attemps running status of the Manager")
252 while str(cfy_status) != 'running' and retry:
255 "status %s", cfy_client.manager.get_status())
256 cfy_status = cfy_client.manager.get_status()['status']
258 "The current manager status is %s", cfy_status)
259 except Exception: # pylint: disable=broad-except
261 "Cloudify Manager isn't up and running. Retrying ...")
265 if str(cfy_status) == 'running':
266 self.__logger.info("Cloudify Manager is up and running")
268 raise Exception("Cloudify Manager isn't up and running")
270 self.__logger.info("Put OpenStack creds in manager")
271 secrets_list = cfy_client.secrets.list()
272 for k, val in cfy_creds.iteritems():
273 if not any(d.get('key', None) == k for d in secrets_list):
274 cfy_client.secrets.create(k, val)
276 cfy_client.secrets.update(k, val)
278 duration = time.time() - start_time
280 self.__logger.info("Put private keypair in manager")
281 if manager_creator.vm_ssh_active(block=True):
282 ssh = manager_creator.ssh_client()
283 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
284 scp.put(kp_file, '~/')
285 cmd = "sudo cp ~/cloudify_ims.pem /etc/cloudify/"
286 self.run_blocking_ssh_command(ssh, cmd)
287 cmd = "sudo chmod 444 /etc/cloudify/cloudify_ims.pem"
288 self.run_blocking_ssh_command(ssh, cmd)
289 cmd = "sudo yum install -y gcc python-devel"
290 self.run_blocking_ssh_command(
291 ssh, cmd, "Unable to install packages on manager")
292 self.run_blocking_ssh_command(ssh, 'cfy status')
294 self.details['orchestrator'].update(status='PASS', duration=duration)
296 self.vnf['inputs'].update(dict(
297 external_network_name=ext_net_name,
298 network_name=network_settings.name,
299 key_pair_name=keypair_settings.name
301 self.result = 1/3 * 100
304 def deploy_vnf(self):
305 """Deploy Clearwater IMS."""
306 start_time = time.time()
308 self.__logger.info("Upload VNFD")
309 cfy_client = self.orchestrator['object']
310 descriptor = self.vnf['descriptor']
311 cfy_client.blueprints.upload(
312 descriptor.get('file_name'), descriptor.get('name'))
313 self.__logger.info("Get or create flavor for all clearwater vm")
314 flavor_settings = FlavorConfig(
315 name=self.vnf['requirements']['flavor']['name'],
316 ram=self.vnf['requirements']['flavor']['ram_min'],
319 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
320 flavor_creator.create()
321 self.created_object.append(flavor_creator)
323 self.vnf['inputs'].update(dict(
324 flavor_id=self.vnf['requirements']['flavor']['name'],
327 self.__logger.info("Create VNF Instance")
328 cfy_client.deployments.create(descriptor.get('name'),
329 descriptor.get('name'),
330 self.vnf.get('inputs'))
332 wait_for_execution(cfy_client,
333 _get_deployment_environment_creation_execution(
334 cfy_client, descriptor.get('name')),
338 self.__logger.info("Start the VNF Instance deployment")
339 execution = cfy_client.executions.start(descriptor.get('name'),
342 execution = wait_for_execution(cfy_client, execution, self.__logger)
344 duration = time.time() - start_time
346 self.__logger.info(execution)
347 if execution.status == 'terminated':
348 self.details['vnf'].update(status='PASS', duration=duration)
349 self.result += 1/3 * 100
352 self.details['vnf'].update(status='FAIL', duration=duration)
357 """Run test on clearwater ims instance."""
358 start_time = time.time()
360 cfy_client = self.orchestrator['object']
362 outputs = cfy_client.deployments.outputs.get(
363 self.vnf['descriptor'].get('name'))['outputs']
364 dns_ip = outputs['dns_ip']
365 ellis_ip = outputs['ellis_ip']
366 self.config_ellis(ellis_ip)
371 vims_test_result = self.run_clearwater_live_test(
373 public_domain=self.vnf['inputs']["public_domain"])
374 duration = time.time() - start_time
375 short_result, nb_test = sig_test_format(vims_test_result)
376 self.__logger.info(short_result)
377 self.details['test_vnf'].update(result=short_result,
378 full_result=vims_test_result,
381 vnf_test_rate = short_result['passed'] / nb_test
382 # orchestrator + vnf + test_vnf
383 self.result += vnf_test_rate / 3 * 100
384 except ZeroDivisionError:
385 self.__logger.error("No test has been executed")
386 self.details['test_vnf'].update(status='FAIL')
392 """Clean created objects/functions."""
394 cfy_client = self.orchestrator['object']
395 dep_name = self.vnf['descriptor'].get('name')
396 # kill existing execution
397 self.__logger.info('Deleting the current deployment')
398 exec_list = cfy_client.executions.list(dep_name)
399 for execution in exec_list:
400 if execution['status'] == "started":
402 cfy_client.executions.cancel(execution['id'],
404 except Exception: # pylint: disable=broad-except
405 self.__logger.warn("Can't cancel the current exec")
407 execution = cfy_client.executions.start(
410 parameters=dict(ignore_failure=True),
413 wait_for_execution(cfy_client, execution, self.__logger)
414 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
415 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
416 except Exception: # pylint: disable=broad-except
417 self.__logger.exception("Some issue during the undeployment ..")
419 super(CloudifyIms, self).clean()
422 def run_blocking_ssh_command(ssh, cmd,
423 error_msg="Unable to run this command"):
424 """Command to run ssh command with the exit status."""
425 _, stdout, stderr = ssh.exec_command(cmd)
426 CloudifyIms.__logger.debug("SSH %s stdout: %s", cmd, stdout.read())
427 if stdout.channel.recv_exit_status() != 0:
428 CloudifyIms.__logger.error("SSH %s stderr: %s", cmd, stderr.read())
429 raise Exception(error_msg)
431 @energy.enable_recording
432 def run(self, **kwargs):
433 """Execute CloudifyIms test case."""
434 return super(CloudifyIms, self).run(**kwargs)
437 # ----------------------------------------------------------
441 # -----------------------------------------------------------
442 def get_config(parameter, file_path):
444 Get config parameter.
446 Returns the value of a given parameter in file.yaml
447 parameter must be given in string format with dots
448 Example: general.openstack.image_name
450 with open(file_path) as config_file:
451 file_yaml = yaml.safe_load(config_file)
454 for element in parameter.split("."):
455 value = value.get(element)
457 raise ValueError("The parameter %s is not defined in"
458 " reporting.yaml" % parameter)
462 def wait_for_execution(client, execution, logger, timeout=1500, ):
463 """Wait for a workflow execution on Cloudify Manager."""
464 # if execution already ended - return without waiting
465 if execution.status in Execution.END_STATES:
468 if timeout is not None:
469 deadline = time.time() + timeout
471 # Poll for execution status and execution logs, until execution ends
472 # and we receive an event of type in WORKFLOW_END_TYPES
476 execution_ended = False
478 event_list = client.events.list(
479 execution_id=execution.id,
483 sort='@timestamp').items
485 offset = offset + len(event_list)
486 for event in event_list:
487 logger.debug(event.get('message'))
489 if timeout is not None:
490 if time.time() > deadline:
492 'execution of operation {0} for deployment {1} '
493 'timed out'.format(execution.workflow_id,
494 execution.deployment_id))
496 # update the remaining timeout
497 timeout = deadline - time.time()
499 if not execution_ended:
500 execution = client.executions.get(execution.id)
501 execution_ended = execution.status in Execution.END_STATES
511 def _get_deployment_environment_creation_execution(client, deployment_id):
513 Get the execution id of a env preparation.
515 network, security group, fip, VM creation
517 executions = client.executions.list(deployment_id=deployment_id)
518 for execution in executions:
519 if execution.workflow_id == 'create_deployment_environment':
521 raise RuntimeError('Failed to get create_deployment_environment '
522 'workflow execution.'
523 'Available executions: {0}'.format(executions))
526 def sig_test_format(sig_test):
527 """Process the signaling result to have a short result."""
531 for data_test in sig_test:
532 if data_test['result'] == "Passed":
534 elif data_test['result'] == "Failed":
536 elif data_test['result'] == "Skipped":
538 short_sig_test_result = {}
539 short_sig_test_result['passed'] = nb_passed
540 short_sig_test_result['failures'] = nb_failures
541 short_sig_test_result['skipped'] = nb_skipped
542 nb_test = nb_passed + nb_skipped
543 return (short_sig_test_result, nb_test)