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
26 from snaps.config.flavor import FlavorConfig
27 from snaps.config.image import ImageConfig
28 from snaps.config.keypair import KeypairConfig
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, VmInstanceConfig
35 from snaps.openstack.create_flavor import OpenStackFlavor
36 from snaps.openstack.create_image import OpenStackImage
37 from snaps.openstack.create_instance import OpenStackVmInstance
38 from snaps.openstack.create_keypairs import OpenStackKeypair
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
42 from snaps.openstack.utils import keystone_utils
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")
66 config_file = os.path.join(self.case_dir, self.config)
67 self.orchestrator = dict(
68 requirements=get_config("orchestrator.requirements", config_file),
70 self.details['orchestrator'] = dict(
71 name=get_config("orchestrator.name", config_file),
72 version=get_config("orchestrator.version", config_file),
76 self.__logger.debug("Orchestrator configuration %s", self.orchestrator)
78 descriptor=get_config("vnf.descriptor", config_file),
79 inputs=get_config("vnf.inputs", config_file),
80 requirements=get_config("vnf.requirements", config_file)
82 self.details['vnf'] = dict(
83 descriptor_version=self.vnf['descriptor']['version'],
84 name=get_config("vnf.name", config_file),
85 version=get_config("vnf.version", config_file),
87 self.__logger.debug("VNF configuration: %s", self.vnf)
89 self.details['test_vnf'] = dict(
90 name=get_config("vnf_test_suite.name", config_file),
91 version=get_config("vnf_test_suite.version", config_file)
93 self.images = get_config("tenant_images", config_file)
94 self.__logger.info("Images needed for vIMS: %s", self.images)
97 """Prepare testscase (Additional pre-configuration steps)."""
98 super(CloudifyIms, self).prepare()
100 self.__logger.info("Additional pre-configuration steps")
102 compute_quotas = self.os_project.get_compute_quotas()
103 network_quotas = self.os_project.get_network_quotas()
106 self.vnf['requirements']['compute_quotas'].items()):
107 setattr(compute_quotas, key, value)
110 self.vnf['requirements']['network_quotas'].items()):
111 setattr(network_quotas, key, value)
113 compute_quotas = self.os_project.update_compute_quotas(compute_quotas)
114 network_quotas = self.os_project.update_network_quotas(network_quotas)
117 self.__logger.info("Upload some OS images if it doesn't exist")
118 for image_name, image_file in self.images.iteritems():
119 self.__logger.info("image: %s, file: %s", image_name, image_file)
120 if image_file and image_name:
121 image_creator = OpenStackImage(
124 name=image_name, image_user='cloud',
125 img_format='qcow2', image_file=image_file))
126 image_creator.create()
127 self.created_object.append(image_creator)
129 def deploy_orchestrator(self):
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(name='cloudify_ims_kp',
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 network_settings = NetworkConfig(
151 name='cloudify_ims_network-{}'.format(self.uuid),
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-{}'.format(self.uuid),
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-{}".format(self.uuid),
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-{}".format(self.uuid),
177 direction=Direction.ingress, protocol=Protocol.udp,
178 port_range_min=1, port_range_max=65535))
180 security_group_creator = OpenStackSecurityGroup(
183 name="sg-cloudify-manager-{}".format(self.uuid),
184 rule_settings=sg_rules))
186 security_group_creator.create()
187 self.created_object.append(security_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(
206 name='cloudify_manager_port-{}'.format(self.uuid),
207 network_name=network_settings.name)
209 manager_settings = VmInstanceConfig(
210 name='cloudify_manager-{}'.format(self.uuid),
211 flavor=flavor_settings.name,
212 port_settings=[port_settings],
213 security_group_names=[
214 security_group_creator.sec_grp_settings.name],
215 floating_ip_settings=[FloatingIpConfig(
216 name='cloudify_manager_fip-{}'.format(self.uuid),
217 port_name=port_settings.name,
218 router_name=router_creator.router_settings.name)])
220 manager_creator = OpenStackVmInstance(
221 self.snaps_creds, manager_settings, image_settings,
224 self.__logger.info("Creating cloudify manager VM")
225 manager_creator.create()
226 self.created_object.append(manager_creator)
228 public_auth_url = keystone_utils.get_endpoint(
229 self.snaps_creds, 'identity')
231 self.__logger.info("Set creds for cloudify manager")
232 cfy_creds = dict(keystone_username=self.snaps_creds.username,
233 keystone_password=self.snaps_creds.password,
234 keystone_tenant_name=self.snaps_creds.project_name,
235 keystone_url=public_auth_url)
237 cfy_client = CloudifyClient(host=manager_creator.get_floating_ip().ip,
240 tenant='default_tenant')
242 self.orchestrator['object'] = cfy_client
244 self.__logger.info("Attemps running status of the Manager")
247 while str(cfy_status) != 'running' and retry:
249 cfy_status = cfy_client.manager.get_status()['status']
250 self.__logger.debug("The current manager status is %s",
252 except Exception: # pylint: disable=broad-except
253 self.__logger.warning("Cloudify Manager isn't " +
254 "up and running. Retrying ...")
258 if str(cfy_status) == 'running':
259 self.__logger.info("Cloudify Manager is up and running")
261 raise Exception("Cloudify Manager isn't up and running")
263 self.__logger.info("Put OpenStack creds in manager")
264 secrets_list = cfy_client.secrets.list()
265 for k, val in cfy_creds.iteritems():
266 if not any(d.get('key', None) == k for d in secrets_list):
267 cfy_client.secrets.create(k, val)
269 cfy_client.secrets.update(k, val)
271 duration = time.time() - start_time
273 self.__logger.info("Put private keypair in manager")
274 if manager_creator.vm_ssh_active(block=True):
275 ssh = manager_creator.ssh_client()
276 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
277 scp.put(kp_file, '~/')
278 cmd = "sudo cp ~/cloudify_ims.pem /etc/cloudify/"
279 run_blocking_ssh_command(ssh, cmd)
280 cmd = "sudo chmod 444 /etc/cloudify/cloudify_ims.pem"
281 run_blocking_ssh_command(ssh, cmd)
282 cmd = "sudo yum install -y gcc python-devel"
283 run_blocking_ssh_command(ssh, cmd, "Unable to install packages \
286 self.details['orchestrator'].update(status='PASS', duration=duration)
288 self.vnf['inputs'].update(dict(
289 external_network_name=ext_net_name,
290 network_name=network_settings.name
292 self.result = 1/3 * 100
295 def deploy_vnf(self):
296 """Deploy Clearwater IMS."""
297 start_time = time.time()
299 self.__logger.info("Upload VNFD")
300 cfy_client = self.orchestrator['object']
301 descriptor = self.vnf['descriptor']
302 cfy_client.blueprints.publish_archive(descriptor.get('url'),
303 descriptor.get('name'),
304 descriptor.get('file_name'))
306 self.__logger.info("Get or create flavor for all clearwater vm")
307 flavor_settings = FlavorConfig(
308 name=self.vnf['requirements']['flavor']['name'],
309 ram=self.vnf['requirements']['flavor']['ram_min'],
312 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
313 flavor_creator.create()
314 self.created_object.append(flavor_creator)
316 self.vnf['inputs'].update(dict(
317 flavor_id=self.vnf['requirements']['flavor']['name'],
320 self.__logger.info("Create VNF Instance")
321 cfy_client.deployments.create(descriptor.get('name'),
322 descriptor.get('name'),
323 self.vnf.get('inputs'))
325 wait_for_execution(cfy_client,
326 _get_deployment_environment_creation_execution(
327 cfy_client, descriptor.get('name')),
331 self.__logger.info("Start the VNF Instance deployment")
332 execution = cfy_client.executions.start(descriptor.get('name'),
335 execution = wait_for_execution(cfy_client, execution, self.__logger)
337 duration = time.time() - start_time
339 self.__logger.info(execution)
340 if execution.status == 'terminated':
341 self.details['vnf'].update(status='PASS', duration=duration)
342 self.result += 1/3 * 100
345 self.details['vnf'].update(status='FAIL', duration=duration)
350 """Run test on clearwater ims instance."""
351 start_time = time.time()
353 cfy_client = self.orchestrator['object']
355 outputs = cfy_client.deployments.outputs.get(
356 self.vnf['descriptor'].get('name'))['outputs']
357 dns_ip = outputs['dns_ip']
358 ellis_ip = outputs['ellis_ip']
359 self.config_ellis(ellis_ip)
364 vims_test_result = self.run_clearwater_live_test(
366 public_domain=self.vnf['inputs']["public_domain"])
367 duration = time.time() - start_time
368 short_result, nb_test = sig_test_format(vims_test_result)
369 self.__logger.info(short_result)
370 self.details['test_vnf'].update(result=short_result,
371 full_result=vims_test_result,
374 vnf_test_rate = short_result['passed'] / nb_test
375 # orchestrator + vnf + test_vnf
376 self.result += vnf_test_rate / 3 * 100
377 except ZeroDivisionError:
378 self.__logger.error("No test has been executed")
379 self.details['test_vnf'].update(status='FAIL')
385 """Clean created objects/functions."""
387 cfy_client = self.orchestrator['object']
388 dep_name = self.vnf['descriptor'].get('name')
389 # kill existing execution
390 self.__logger.info('Deleting the current deployment')
391 exec_list = cfy_client.executions.list(dep_name)
392 for execution in exec_list:
393 if execution['status'] == "started":
395 cfy_client.executions.cancel(execution['id'],
397 except: # pylint: disable=broad-except
398 self.__logger.warn("Can't cancel the current exec")
400 execution = cfy_client.executions.start(
403 parameters=dict(ignore_failure=True),
406 wait_for_execution(cfy_client, execution, self.__logger)
407 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
408 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
409 except: # pylint: disable=broad-except
410 self.__logger.warn("Some issue during the undeployment ..")
411 self.__logger.warn("Tenant clean continue ..")
413 super(CloudifyIms, self).clean()
415 @energy.enable_recording
416 def run(self, **kwargs):
417 """Execute CloudifyIms test case."""
418 return super(CloudifyIms, self).run(**kwargs)
421 # ----------------------------------------------------------
425 # -----------------------------------------------------------
426 def get_config(parameter, file_path):
428 Get config parameter.
430 Returns the value of a given parameter in file.yaml
431 parameter must be given in string format with dots
432 Example: general.openstack.image_name
434 with open(file_path) as config_file:
435 file_yaml = yaml.safe_load(config_file)
438 for element in parameter.split("."):
439 value = value.get(element)
441 raise ValueError("The parameter %s is not defined in"
442 " reporting.yaml" % parameter)
446 def wait_for_execution(client, execution, logger, timeout=1500, ):
447 """Wait for a workflow execution on Cloudify Manager."""
448 # if execution already ended - return without waiting
449 if execution.status in Execution.END_STATES:
452 if timeout is not None:
453 deadline = time.time() + timeout
455 # Poll for execution status and execution logs, until execution ends
456 # and we receive an event of type in WORKFLOW_END_TYPES
460 execution_ended = False
462 event_list = client.events.list(
463 execution_id=execution.id,
467 sort='@timestamp').items
469 offset = offset + len(event_list)
470 for event in event_list:
471 logger.debug(event.get('message'))
473 if timeout is not None:
474 if time.time() > deadline:
476 'execution of operation {0} for deployment {1} '
477 'timed out'.format(execution.workflow_id,
478 execution.deployment_id))
480 # update the remaining timeout
481 timeout = deadline - time.time()
483 if not execution_ended:
484 execution = client.executions.get(execution.id)
485 execution_ended = execution.status in Execution.END_STATES
495 def _get_deployment_environment_creation_execution(client, deployment_id):
497 Get the execution id of a env preparation.
499 network, security group, fip, VM creation
501 executions = client.executions.list(deployment_id=deployment_id)
502 for execution in executions:
503 if execution.workflow_id == 'create_deployment_environment':
505 raise RuntimeError('Failed to get create_deployment_environment '
506 'workflow execution.'
507 'Available executions: {0}'.format(executions))
510 def sig_test_format(sig_test):
511 """Process the signaling result to have a short result."""
515 for data_test in sig_test:
516 if data_test['result'] == "Passed":
518 elif data_test['result'] == "Failed":
520 elif data_test['result'] == "Skipped":
522 short_sig_test_result = {}
523 short_sig_test_result['passed'] = nb_passed
524 short_sig_test_result['failures'] = nb_failures
525 short_sig_test_result['skipped'] = nb_skipped
526 nb_test = nb_passed + nb_skipped
527 return (short_sig_test_result, nb_test)
530 def run_blocking_ssh_command(ssh, cmd, error_msg="Unable to run this command"):
531 """Command to run ssh command with the exit status."""
532 stdin, stdout, stderr = ssh.exec_command(cmd)
533 if stdout.channel.recv_exit_status() != 0:
534 raise Exception(error_msg)