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(
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 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')
232 self.__logger.info("Set creds for cloudify manager")
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)
239 cfy_client = CloudifyClient(
240 host=manager_creator.get_floating_ip().ip,
241 username='admin', password='admin', tenant='default_tenant')
243 self.orchestrator['object'] = cfy_client
245 self.__logger.info("Attemps running status of the Manager")
248 while str(cfy_status) != 'running' and retry:
250 cfy_status = cfy_client.manager.get_status()['status']
251 self.__logger.debug("The current manager status is %s",
253 except Exception: # pylint: disable=broad-except
254 self.__logger.exception(
255 "Cloudify Manager isn't up and running. Retrying ...")
259 if str(cfy_status) == 'running':
260 self.__logger.info("Cloudify Manager is up and running")
262 raise Exception("Cloudify Manager isn't up and running")
264 self.__logger.info("Put OpenStack creds in manager")
265 secrets_list = cfy_client.secrets.list()
266 for k, val in cfy_creds.iteritems():
267 if not any(d.get('key', None) == k for d in secrets_list):
268 cfy_client.secrets.create(k, val)
270 cfy_client.secrets.update(k, val)
272 duration = time.time() - start_time
274 self.__logger.info("Put private keypair in manager")
275 if manager_creator.vm_ssh_active(block=True):
276 ssh = manager_creator.ssh_client()
277 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
278 scp.put(kp_file, '~/')
279 cmd = "sudo cp ~/cloudify_ims.pem /etc/cloudify/"
280 run_blocking_ssh_command(ssh, cmd)
281 cmd = "sudo chmod 444 /etc/cloudify/cloudify_ims.pem"
282 run_blocking_ssh_command(ssh, cmd)
283 cmd = "sudo yum install -y gcc python-devel"
284 run_blocking_ssh_command(ssh, cmd, "Unable to install packages \
287 self.details['orchestrator'].update(status='PASS', duration=duration)
289 self.vnf['inputs'].update(dict(
290 external_network_name=ext_net_name,
291 network_name=network_settings.name,
292 key_pair_name=keypair_settings.name
294 self.result = 1/3 * 100
297 def deploy_vnf(self):
298 """Deploy Clearwater IMS."""
299 start_time = time.time()
301 self.__logger.info("Upload VNFD")
302 cfy_client = self.orchestrator['object']
303 descriptor = self.vnf['descriptor']
304 cfy_client.blueprints.publish_archive(descriptor.get('url'),
305 descriptor.get('name'),
306 descriptor.get('file_name'))
308 self.__logger.info("Get or create flavor for all clearwater vm")
309 flavor_settings = FlavorConfig(
310 name=self.vnf['requirements']['flavor']['name'],
311 ram=self.vnf['requirements']['flavor']['ram_min'],
314 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
315 flavor_creator.create()
316 self.created_object.append(flavor_creator)
318 self.vnf['inputs'].update(dict(
319 flavor_id=self.vnf['requirements']['flavor']['name'],
322 self.__logger.info("Create VNF Instance")
323 cfy_client.deployments.create(descriptor.get('name'),
324 descriptor.get('name'),
325 self.vnf.get('inputs'))
327 wait_for_execution(cfy_client,
328 _get_deployment_environment_creation_execution(
329 cfy_client, descriptor.get('name')),
333 self.__logger.info("Start the VNF Instance deployment")
334 execution = cfy_client.executions.start(descriptor.get('name'),
337 execution = wait_for_execution(cfy_client, execution, self.__logger)
339 duration = time.time() - start_time
341 self.__logger.info(execution)
342 if execution.status == 'terminated':
343 self.details['vnf'].update(status='PASS', duration=duration)
344 self.result += 1/3 * 100
347 self.details['vnf'].update(status='FAIL', duration=duration)
352 """Run test on clearwater ims instance."""
353 start_time = time.time()
355 cfy_client = self.orchestrator['object']
357 outputs = cfy_client.deployments.outputs.get(
358 self.vnf['descriptor'].get('name'))['outputs']
359 dns_ip = outputs['dns_ip']
360 ellis_ip = outputs['ellis_ip']
361 self.config_ellis(ellis_ip)
366 vims_test_result = self.run_clearwater_live_test(
368 public_domain=self.vnf['inputs']["public_domain"])
369 duration = time.time() - start_time
370 short_result, nb_test = sig_test_format(vims_test_result)
371 self.__logger.info(short_result)
372 self.details['test_vnf'].update(result=short_result,
373 full_result=vims_test_result,
376 vnf_test_rate = short_result['passed'] / nb_test
377 # orchestrator + vnf + test_vnf
378 self.result += vnf_test_rate / 3 * 100
379 except ZeroDivisionError:
380 self.__logger.error("No test has been executed")
381 self.details['test_vnf'].update(status='FAIL')
387 """Clean created objects/functions."""
389 cfy_client = self.orchestrator['object']
390 dep_name = self.vnf['descriptor'].get('name')
391 # kill existing execution
392 self.__logger.info('Deleting the current deployment')
393 exec_list = cfy_client.executions.list(dep_name)
394 for execution in exec_list:
395 if execution['status'] == "started":
397 cfy_client.executions.cancel(execution['id'],
399 except: # pylint: disable=broad-except
400 self.__logger.warn("Can't cancel the current exec")
402 execution = cfy_client.executions.start(
405 parameters=dict(ignore_failure=True),
408 wait_for_execution(cfy_client, execution, self.__logger)
409 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
410 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
411 except: # pylint: disable=broad-except
412 self.__logger.warn("Some issue during the undeployment ..")
413 self.__logger.warn("Tenant clean continue ..")
415 super(CloudifyIms, self).clean()
417 @energy.enable_recording
418 def run(self, **kwargs):
419 """Execute CloudifyIms test case."""
420 return super(CloudifyIms, self).run(**kwargs)
423 # ----------------------------------------------------------
427 # -----------------------------------------------------------
428 def get_config(parameter, file_path):
430 Get config parameter.
432 Returns the value of a given parameter in file.yaml
433 parameter must be given in string format with dots
434 Example: general.openstack.image_name
436 with open(file_path) as config_file:
437 file_yaml = yaml.safe_load(config_file)
440 for element in parameter.split("."):
441 value = value.get(element)
443 raise ValueError("The parameter %s is not defined in"
444 " reporting.yaml" % parameter)
448 def wait_for_execution(client, execution, logger, timeout=1500, ):
449 """Wait for a workflow execution on Cloudify Manager."""
450 # if execution already ended - return without waiting
451 if execution.status in Execution.END_STATES:
454 if timeout is not None:
455 deadline = time.time() + timeout
457 # Poll for execution status and execution logs, until execution ends
458 # and we receive an event of type in WORKFLOW_END_TYPES
462 execution_ended = False
464 event_list = client.events.list(
465 execution_id=execution.id,
469 sort='@timestamp').items
471 offset = offset + len(event_list)
472 for event in event_list:
473 logger.debug(event.get('message'))
475 if timeout is not None:
476 if time.time() > deadline:
478 'execution of operation {0} for deployment {1} '
479 'timed out'.format(execution.workflow_id,
480 execution.deployment_id))
482 # update the remaining timeout
483 timeout = deadline - time.time()
485 if not execution_ended:
486 execution = client.executions.get(execution.id)
487 execution_ended = execution.status in Execution.END_STATES
497 def _get_deployment_environment_creation_execution(client, deployment_id):
499 Get the execution id of a env preparation.
501 network, security group, fip, VM creation
503 executions = client.executions.list(deployment_id=deployment_id)
504 for execution in executions:
505 if execution.workflow_id == 'create_deployment_environment':
507 raise RuntimeError('Failed to get create_deployment_environment '
508 'workflow execution.'
509 'Available executions: {0}'.format(executions))
512 def sig_test_format(sig_test):
513 """Process the signaling result to have a short result."""
517 for data_test in sig_test:
518 if data_test['result'] == "Passed":
520 elif data_test['result'] == "Failed":
522 elif data_test['result'] == "Skipped":
524 short_sig_test_result = {}
525 short_sig_test_result['passed'] = nb_passed
526 short_sig_test_result['failures'] = nb_failures
527 short_sig_test_result['skipped'] = nb_skipped
528 nb_test = nb_passed + nb_skipped
529 return (short_sig_test_result, nb_test)
532 def run_blocking_ssh_command(ssh, cmd, error_msg="Unable to run this command"):
533 """Command to run ssh command with the exit status."""
534 stdin, stdout, stderr = ssh.exec_command(cmd)
535 if stdout.channel.recv_exit_status() != 0:
536 raise Exception(error_msg)