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 functest.energy import energy
22 from functest.opnfv_tests.openstack.snaps import snaps_utils
23 import functest.opnfv_tests.vnf.ims.clearwater_ims_base as clearwater_ims_base
24 from functest.utils.constants import CONST
25 import functest.utils.openstack_utils as os_utils
27 from snaps.config.flavor import FlavorConfig
28 from snaps.config.image import ImageConfig
29 from snaps.config.keypair import KeypairConfig
30 from snaps.config.network import NetworkConfig, PortConfig, SubnetConfig
31 from snaps.config.router import RouterConfig
32 from snaps.config.security_group import (
33 Direction, Protocol, SecurityGroupConfig, SecurityGroupRuleConfig)
34 from snaps.config.vm_inst import FloatingIpConfig, VmInstanceConfig
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_keypairs import OpenStackKeypair
40 from snaps.openstack.create_network import OpenStackNetwork
41 from snaps.openstack.create_router import OpenStackRouter
42 from snaps.openstack.create_security_group import OpenStackSecurityGroup
45 __author__ = "Valentin Boucher <valentin.boucher@orange.com>"
48 class CloudifyIms(clearwater_ims_base.ClearwaterOnBoardingBase):
49 """Clearwater vIMS deployed with Cloudify Orchestrator Case."""
51 __logger = logging.getLogger(__name__)
53 def __init__(self, **kwargs):
54 """Initialize CloudifyIms testcase object."""
55 if "case_name" not in kwargs:
56 kwargs["case_name"] = "cloudify_ims"
57 super(CloudifyIms, self).__init__(**kwargs)
59 # Retrieve the configuration
61 self.config = CONST.__getattribute__(
62 'vnf_{}_config'.format(self.case_name))
64 raise Exception("VNF config file not found")
68 config_file = os.path.join(self.case_dir, self.config)
69 self.orchestrator = dict(
70 requirements=get_config("orchestrator.requirements", config_file),
72 self.details['orchestrator'] = dict(
73 name=get_config("orchestrator.name", config_file),
74 version=get_config("orchestrator.version", config_file),
78 self.__logger.debug("Orchestrator configuration %s", self.orchestrator)
80 descriptor=get_config("vnf.descriptor", config_file),
81 inputs=get_config("vnf.inputs", config_file),
82 requirements=get_config("vnf.requirements", config_file)
84 self.details['vnf'] = dict(
85 descriptor_version=self.vnf['descriptor']['version'],
86 name=get_config("vnf.name", config_file),
87 version=get_config("vnf.version", config_file),
89 self.__logger.debug("VNF configuration: %s", self.vnf)
91 self.details['test_vnf'] = dict(
92 name=get_config("vnf_test_suite.name", config_file),
93 version=get_config("vnf_test_suite.version", config_file)
95 self.images = get_config("tenant_images", config_file)
96 self.__logger.info("Images needed for vIMS: %s", self.images)
99 """Prepare testscase (Additional pre-configuration steps)."""
100 super(CloudifyIms, self).prepare()
102 self.__logger.info("Additional pre-configuration steps")
104 compute_quotas = self.os_project.get_compute_quotas()
105 network_quotas = self.os_project.get_network_quotas()
108 self.vnf['requirements']['compute_quotas'].items()):
109 setattr(compute_quotas, key, value)
112 self.vnf['requirements']['network_quotas'].items()):
113 setattr(network_quotas, key, value)
115 compute_quotas = self.os_project.update_compute_quotas(compute_quotas)
116 network_quotas = self.os_project.update_network_quotas(network_quotas)
119 self.__logger.info("Upload some OS images if it doesn't exist")
120 for image_name, image_file in self.images.iteritems():
121 self.__logger.info("image: %s, file: %s", image_name, image_file)
122 if image_file and image_name:
123 image_creator = OpenStackImage(
126 name=image_name, image_user='cloud',
127 img_format='qcow2', image_file=image_file))
128 image_creator.create()
129 # self.created_object.append(image_creator)
131 def deploy_orchestrator(self):
133 Deploy Cloudify Manager.
135 network, security group, fip, VM creation
139 start_time = time.time()
140 self.__logger.info("Creating keypair ...")
141 kp_file = os.path.join(self.data_dir, "cloudify_ims.pem")
142 keypair_settings = KeypairConfig(name='cloudify_ims_kp',
143 private_filepath=kp_file)
144 keypair_creator = OpenStackKeypair(self.snaps_creds, keypair_settings)
145 keypair_creator.create()
146 self.created_object.append(keypair_creator)
148 self.__logger.info("Creating full network ...")
149 subnet_settings = SubnetConfig(name='cloudify_ims_subnet',
150 cidr='10.67.79.0/24')
151 network_settings = NetworkConfig(name='cloudify_ims_network',
152 subnet_settings=[subnet_settings])
153 network_creator = OpenStackNetwork(self.snaps_creds, network_settings)
154 network_creator.create()
155 self.created_object.append(network_creator)
156 ext_net_name = snaps_utils.get_ext_net_name(self.snaps_creds)
157 router_creator = OpenStackRouter(
160 name='cloudify_ims_router',
161 external_gateway=ext_net_name,
162 internal_subnets=[subnet_settings.name]))
163 router_creator.create()
164 self.created_object.append(router_creator)
166 # security group creation
167 self.__logger.info("Creating security group for cloudify manager vm")
170 SecurityGroupRuleConfig(
171 sec_grp_name="sg-cloudify-manager",
172 direction=Direction.ingress, protocol=Protocol.tcp,
173 port_range_min=1, port_range_max=65535))
175 SecurityGroupRuleConfig(
176 sec_grp_name="sg-cloudify-manager",
177 direction=Direction.ingress, protocol=Protocol.udp,
178 port_range_min=1, port_range_max=65535))
180 securit_group_creator = OpenStackSecurityGroup(
183 name="sg-cloudify-manager",
184 rule_settings=sg_rules))
186 securit_group_creator.create()
187 self.created_object.append(securit_group_creator)
189 # orchestrator VM flavor
190 self.__logger.info("Get or create flavor for cloudify manager vm ...")
192 flavor_settings = FlavorConfig(
193 name=self.orchestrator['requirements']['flavor']['name'],
194 ram=self.orchestrator['requirements']['flavor']['ram_min'],
197 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
198 flavor_creator.create()
199 self.created_object.append(flavor_creator)
200 image_settings = ImageConfig(
201 name=self.orchestrator['requirements']['os_image'],
205 port_settings = PortConfig(name='cloudify_manager_port',
206 network_name=network_settings.name)
208 manager_settings = VmInstanceConfig(
209 name='cloudify_manager',
210 flavor=flavor_settings.name,
211 port_settings=[port_settings],
212 security_group_names=[securit_group_creator.sec_grp_settings.name],
213 floating_ip_settings=[FloatingIpConfig(
214 name='cloudify_manager_fip',
215 port_name=port_settings.name,
216 router_name=router_creator.router_settings.name)])
218 manager_creator = OpenStackVmInstance(self.snaps_creds,
223 self.__logger.info("Creating cloudify manager VM")
224 manager_creator.create()
225 self.created_object.append(manager_creator)
227 public_auth_url = os_utils.get_endpoint('identity')
229 self.__logger.info("Set creds for cloudify manager")
230 cfy_creds = dict(keystone_username=self.tenant_name,
231 keystone_password=self.tenant_name,
232 keystone_tenant_name=self.tenant_name,
233 keystone_url=public_auth_url)
235 cfy_client = CloudifyClient(host=manager_creator.get_floating_ip().ip,
238 tenant='default_tenant')
240 self.orchestrator['object'] = cfy_client
242 self.__logger.info("Attemps running status of the Manager")
245 while str(cfy_status) != 'running' and retry:
247 cfy_status = cfy_client.manager.get_status()['status']
248 self.__logger.debug("The current manager status is %s",
250 except Exception: # pylint: disable=broad-except
251 self.__logger.warning("Cloudify Manager isn't " +
252 "up and running. Retrying ...")
256 if str(cfy_status) == 'running':
257 self.__logger.info("Cloudify Manager is up and running")
259 raise Exception("Cloudify Manager isn't up and running")
261 self.__logger.info("Put OpenStack creds in manager")
262 secrets_list = cfy_client.secrets.list()
263 for k, val in cfy_creds.iteritems():
264 if not any(d.get('key', None) == k for d in secrets_list):
265 cfy_client.secrets.create(k, val)
267 cfy_client.secrets.update(k, val)
269 duration = time.time() - start_time
271 self.__logger.info("Put private keypair in manager")
272 if manager_creator.vm_ssh_active(block=True):
273 ssh = manager_creator.ssh_client()
274 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
275 scp.put(kp_file, '~/')
276 cmd = "sudo cp ~/cloudify_ims.pem /etc/cloudify/"
277 run_blocking_ssh_command(ssh, cmd)
278 cmd = "sudo chmod 444 /etc/cloudify/cloudify_ims.pem"
279 run_blocking_ssh_command(ssh, cmd)
280 cmd = "sudo yum install -y gcc python-devel"
281 run_blocking_ssh_command(ssh, cmd, "Unable to install packages \
284 self.details['orchestrator'].update(status='PASS', duration=duration)
286 self.vnf['inputs'].update(dict(
287 external_network_name=ext_net_name,
288 network_name=network_settings.name
290 self.result = 1/3 * 100
293 def deploy_vnf(self):
294 """Deploy Clearwater IMS."""
295 start_time = time.time()
297 self.__logger.info("Upload VNFD")
298 cfy_client = self.orchestrator['object']
299 descriptor = self.vnf['descriptor']
300 cfy_client.blueprints.publish_archive(descriptor.get('url'),
301 descriptor.get('name'),
302 descriptor.get('file_name'))
304 self.__logger.info("Get or create flavor for all clearwater vm")
305 flavor_settings = FlavorConfig(
306 name=self.vnf['requirements']['flavor']['name'],
307 ram=self.vnf['requirements']['flavor']['ram_min'],
310 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
311 flavor_creator.create()
312 self.created_object.append(flavor_creator)
314 self.vnf['inputs'].update(dict(
315 flavor_id=self.vnf['requirements']['flavor']['name'],
318 self.__logger.info("Create VNF Instance")
319 cfy_client.deployments.create(descriptor.get('name'),
320 descriptor.get('name'),
321 self.vnf.get('inputs'))
323 wait_for_execution(cfy_client,
324 _get_deployment_environment_creation_execution(
325 cfy_client, descriptor.get('name')),
329 self.__logger.info("Start the VNF Instance deployment")
330 execution = cfy_client.executions.start(descriptor.get('name'),
333 execution = wait_for_execution(cfy_client, execution, self.__logger)
335 duration = time.time() - start_time
337 self.__logger.info(execution)
338 if execution.status == 'terminated':
339 self.details['vnf'].update(status='PASS', duration=duration)
340 self.result += 1/3 * 100
343 self.details['vnf'].update(status='FAIL', duration=duration)
348 """Run test on clearwater ims instance."""
349 start_time = time.time()
351 cfy_client = self.orchestrator['object']
353 outputs = cfy_client.deployments.outputs.get(
354 self.vnf['descriptor'].get('name'))['outputs']
355 dns_ip = outputs['dns_ip']
356 ellis_ip = outputs['ellis_ip']
357 self.config_ellis(ellis_ip)
362 vims_test_result = self.run_clearwater_live_test(
364 public_domain=self.vnf['inputs']["public_domain"])
365 duration = time.time() - start_time
366 short_result, nb_test = sig_test_format(vims_test_result)
367 self.__logger.info(short_result)
368 self.details['test_vnf'].update(result=short_result,
369 full_result=vims_test_result,
372 vnf_test_rate = short_result['passed'] / nb_test
373 # orchestrator + vnf + test_vnf
374 self.result += vnf_test_rate / 3 * 100
375 except ZeroDivisionError:
376 self.__logger.error("No test has been executed")
377 self.details['test_vnf'].update(status='FAIL')
383 """Clean created objects/functions."""
385 cfy_client = self.orchestrator['object']
386 dep_name = self.vnf['descriptor'].get('name')
387 # kill existing execution
388 self.__logger.info('Deleting the current deployment')
389 exec_list = cfy_client.executions.list(dep_name)
390 for execution in exec_list:
391 if execution['status'] == "started":
393 cfy_client.executions.cancel(execution['id'],
395 except: # pylint: disable=broad-except
396 self.__logger.warn("Can't cancel the current exec")
398 execution = cfy_client.executions.start(
401 parameters=dict(ignore_failure=True),
404 wait_for_execution(cfy_client, execution, self.__logger)
405 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
406 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
407 except: # pylint: disable=broad-except
408 self.__logger.warn("Some issue during the undeployment ..")
409 self.__logger.warn("Tenant clean continue ..")
411 super(CloudifyIms, self).clean()
413 @energy.enable_recording
414 def run(self, **kwargs):
415 """Execute CloudifyIms test case."""
416 return super(CloudifyIms, self).run(**kwargs)
419 # ----------------------------------------------------------
423 # -----------------------------------------------------------
424 def get_config(parameter, file_path):
426 Get config parameter.
428 Returns the value of a given parameter in file.yaml
429 parameter must be given in string format with dots
430 Example: general.openstack.image_name
432 with open(file_path) as config_file:
433 file_yaml = yaml.safe_load(config_file)
436 for element in parameter.split("."):
437 value = value.get(element)
439 raise ValueError("The parameter %s is not defined in"
440 " reporting.yaml" % parameter)
444 def wait_for_execution(client, execution, logger, timeout=2400, ):
445 """Wait for a workflow execution on Cloudify Manager."""
446 # if execution already ended - return without waiting
447 if execution.status in Execution.END_STATES:
450 if timeout is not None:
451 deadline = time.time() + timeout
453 # Poll for execution status and execution logs, until execution ends
454 # and we receive an event of type in WORKFLOW_END_TYPES
458 execution_ended = False
460 event_list = client.events.list(
461 execution_id=execution.id,
465 sort='@timestamp').items
467 offset = offset + len(event_list)
468 for event in event_list:
469 logger.debug(event.get('message'))
471 if timeout is not None:
472 if time.time() > deadline:
474 'execution of operation {0} for deployment {1} '
475 'timed out'.format(execution.workflow_id,
476 execution.deployment_id))
478 # update the remaining timeout
479 timeout = deadline - time.time()
481 if not execution_ended:
482 execution = client.executions.get(execution.id)
483 execution_ended = execution.status in Execution.END_STATES
493 def _get_deployment_environment_creation_execution(client, deployment_id):
495 Get the execution id of a env preparation.
497 network, security group, fip, VM creation
499 executions = client.executions.list(deployment_id=deployment_id)
500 for execution in executions:
501 if execution.workflow_id == 'create_deployment_environment':
503 raise RuntimeError('Failed to get create_deployment_environment '
504 'workflow execution.'
505 'Available executions: {0}'.format(executions))
508 def sig_test_format(sig_test):
509 """Process the signaling result to have a short result."""
513 for data_test in sig_test:
514 if data_test['result'] == "Passed":
516 elif data_test['result'] == "Failed":
518 elif data_test['result'] == "Skipped":
520 short_sig_test_result = {}
521 short_sig_test_result['passed'] = nb_passed
522 short_sig_test_result['failures'] = nb_failures
523 short_sig_test_result['skipped'] = nb_skipped
524 nb_test = nb_passed + nb_skipped
525 return (short_sig_test_result, nb_test)
528 def run_blocking_ssh_command(ssh, cmd, error_msg="Unable to run this command"):
529 """Command to run ssh command with the exit status."""
530 stdin, stdout, stderr = ssh.exec_command(cmd)
531 if stdout.channel.recv_exit_status() != 0:
532 raise Exception(error_msg)