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")
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 = keystone_utils.get_endpoint(
228 self.snaps_creds, 'identity')
230 self.__logger.info("Set creds for cloudify manager")
231 cfy_creds = dict(keystone_username=self.snaps_creds.username,
232 keystone_password=self.snaps_creds.password,
233 keystone_tenant_name=self.snaps_creds.project_name,
234 keystone_url=public_auth_url)
236 cfy_client = CloudifyClient(host=manager_creator.get_floating_ip().ip,
239 tenant='default_tenant')
241 self.orchestrator['object'] = cfy_client
243 self.__logger.info("Attemps running status of the Manager")
246 while str(cfy_status) != 'running' and retry:
248 cfy_status = cfy_client.manager.get_status()['status']
249 self.__logger.debug("The current manager status is %s",
251 except Exception: # pylint: disable=broad-except
252 self.__logger.warning("Cloudify Manager isn't " +
253 "up and running. Retrying ...")
257 if str(cfy_status) == 'running':
258 self.__logger.info("Cloudify Manager is up and running")
260 raise Exception("Cloudify Manager isn't up and running")
262 self.__logger.info("Put OpenStack creds in manager")
263 secrets_list = cfy_client.secrets.list()
264 for k, val in cfy_creds.iteritems():
265 if not any(d.get('key', None) == k for d in secrets_list):
266 cfy_client.secrets.create(k, val)
268 cfy_client.secrets.update(k, val)
270 duration = time.time() - start_time
272 self.__logger.info("Put private keypair in manager")
273 if manager_creator.vm_ssh_active(block=True):
274 ssh = manager_creator.ssh_client()
275 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
276 scp.put(kp_file, '~/')
277 cmd = "sudo cp ~/cloudify_ims.pem /etc/cloudify/"
278 run_blocking_ssh_command(ssh, cmd)
279 cmd = "sudo chmod 444 /etc/cloudify/cloudify_ims.pem"
280 run_blocking_ssh_command(ssh, cmd)
281 cmd = "sudo yum install -y gcc python-devel"
282 run_blocking_ssh_command(ssh, cmd, "Unable to install packages \
285 self.details['orchestrator'].update(status='PASS', duration=duration)
287 self.vnf['inputs'].update(dict(
288 external_network_name=ext_net_name,
289 network_name=network_settings.name
291 self.result = 1/3 * 100
294 def deploy_vnf(self):
295 """Deploy Clearwater IMS."""
296 start_time = time.time()
298 self.__logger.info("Upload VNFD")
299 cfy_client = self.orchestrator['object']
300 descriptor = self.vnf['descriptor']
301 cfy_client.blueprints.publish_archive(descriptor.get('url'),
302 descriptor.get('name'),
303 descriptor.get('file_name'))
305 self.__logger.info("Get or create flavor for all clearwater vm")
306 flavor_settings = FlavorConfig(
307 name=self.vnf['requirements']['flavor']['name'],
308 ram=self.vnf['requirements']['flavor']['ram_min'],
311 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
312 flavor_creator.create()
313 self.created_object.append(flavor_creator)
315 self.vnf['inputs'].update(dict(
316 flavor_id=self.vnf['requirements']['flavor']['name'],
319 self.__logger.info("Create VNF Instance")
320 cfy_client.deployments.create(descriptor.get('name'),
321 descriptor.get('name'),
322 self.vnf.get('inputs'))
324 wait_for_execution(cfy_client,
325 _get_deployment_environment_creation_execution(
326 cfy_client, descriptor.get('name')),
330 self.__logger.info("Start the VNF Instance deployment")
331 execution = cfy_client.executions.start(descriptor.get('name'),
334 execution = wait_for_execution(cfy_client, execution, self.__logger)
336 duration = time.time() - start_time
338 self.__logger.info(execution)
339 if execution.status == 'terminated':
340 self.details['vnf'].update(status='PASS', duration=duration)
341 self.result += 1/3 * 100
344 self.details['vnf'].update(status='FAIL', duration=duration)
349 """Run test on clearwater ims instance."""
350 start_time = time.time()
352 cfy_client = self.orchestrator['object']
354 outputs = cfy_client.deployments.outputs.get(
355 self.vnf['descriptor'].get('name'))['outputs']
356 dns_ip = outputs['dns_ip']
357 ellis_ip = outputs['ellis_ip']
358 self.config_ellis(ellis_ip)
363 vims_test_result = self.run_clearwater_live_test(
365 public_domain=self.vnf['inputs']["public_domain"])
366 duration = time.time() - start_time
367 short_result, nb_test = sig_test_format(vims_test_result)
368 self.__logger.info(short_result)
369 self.details['test_vnf'].update(result=short_result,
370 full_result=vims_test_result,
373 vnf_test_rate = short_result['passed'] / nb_test
374 # orchestrator + vnf + test_vnf
375 self.result += vnf_test_rate / 3 * 100
376 except ZeroDivisionError:
377 self.__logger.error("No test has been executed")
378 self.details['test_vnf'].update(status='FAIL')
384 """Clean created objects/functions."""
386 cfy_client = self.orchestrator['object']
387 dep_name = self.vnf['descriptor'].get('name')
388 # kill existing execution
389 self.__logger.info('Deleting the current deployment')
390 exec_list = cfy_client.executions.list(dep_name)
391 for execution in exec_list:
392 if execution['status'] == "started":
394 cfy_client.executions.cancel(execution['id'],
396 except: # pylint: disable=broad-except
397 self.__logger.warn("Can't cancel the current exec")
399 execution = cfy_client.executions.start(
402 parameters=dict(ignore_failure=True),
405 wait_for_execution(cfy_client, execution, self.__logger)
406 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
407 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
408 except: # pylint: disable=broad-except
409 self.__logger.warn("Some issue during the undeployment ..")
410 self.__logger.warn("Tenant clean continue ..")
412 super(CloudifyIms, self).clean()
414 @energy.enable_recording
415 def run(self, **kwargs):
416 """Execute CloudifyIms test case."""
417 return super(CloudifyIms, self).run(**kwargs)
420 # ----------------------------------------------------------
424 # -----------------------------------------------------------
425 def get_config(parameter, file_path):
427 Get config parameter.
429 Returns the value of a given parameter in file.yaml
430 parameter must be given in string format with dots
431 Example: general.openstack.image_name
433 with open(file_path) as config_file:
434 file_yaml = yaml.safe_load(config_file)
437 for element in parameter.split("."):
438 value = value.get(element)
440 raise ValueError("The parameter %s is not defined in"
441 " reporting.yaml" % parameter)
445 def wait_for_execution(client, execution, logger, timeout=1500, ):
446 """Wait for a workflow execution on Cloudify Manager."""
447 # if execution already ended - return without waiting
448 if execution.status in Execution.END_STATES:
451 if timeout is not None:
452 deadline = time.time() + timeout
454 # Poll for execution status and execution logs, until execution ends
455 # and we receive an event of type in WORKFLOW_END_TYPES
459 execution_ended = False
461 event_list = client.events.list(
462 execution_id=execution.id,
466 sort='@timestamp').items
468 offset = offset + len(event_list)
469 for event in event_list:
470 logger.debug(event.get('message'))
472 if timeout is not None:
473 if time.time() > deadline:
475 'execution of operation {0} for deployment {1} '
476 'timed out'.format(execution.workflow_id,
477 execution.deployment_id))
479 # update the remaining timeout
480 timeout = deadline - time.time()
482 if not execution_ended:
483 execution = client.executions.get(execution.id)
484 execution_ended = execution.status in Execution.END_STATES
494 def _get_deployment_environment_creation_execution(client, deployment_id):
496 Get the execution id of a env preparation.
498 network, security group, fip, VM creation
500 executions = client.executions.list(deployment_id=deployment_id)
501 for execution in executions:
502 if execution.workflow_id == 'create_deployment_environment':
504 raise RuntimeError('Failed to get create_deployment_environment '
505 'workflow execution.'
506 'Available executions: {0}'.format(executions))
509 def sig_test_format(sig_test):
510 """Process the signaling result to have a short result."""
514 for data_test in sig_test:
515 if data_test['result'] == "Passed":
517 elif data_test['result'] == "Failed":
519 elif data_test['result'] == "Skipped":
521 short_sig_test_result = {}
522 short_sig_test_result['passed'] = nb_passed
523 short_sig_test_result['failures'] = nb_failures
524 short_sig_test_result['skipped'] = nb_skipped
525 nb_test = nb_passed + nb_skipped
526 return (short_sig_test_result, nb_test)
529 def run_blocking_ssh_command(ssh, cmd, error_msg="Unable to run this command"):
530 """Command to run ssh command with the exit status."""
531 stdin, stdout, stderr = ssh.exec_command(cmd)
532 if stdout.channel.recv_exit_status() != 0:
533 raise Exception(error_msg)