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(name='cloudify_ims_subnet',
148 cidr='10.67.79.0/24')
149 network_settings = NetworkConfig(name='cloudify_ims_network',
150 subnet_settings=[subnet_settings])
151 network_creator = OpenStackNetwork(self.snaps_creds, network_settings)
152 network_creator.create()
153 self.created_object.append(network_creator)
154 ext_net_name = snaps_utils.get_ext_net_name(self.snaps_creds)
155 router_creator = OpenStackRouter(
158 name='cloudify_ims_router',
159 external_gateway=ext_net_name,
160 internal_subnets=[subnet_settings.name]))
161 router_creator.create()
162 self.created_object.append(router_creator)
164 # security group creation
165 self.__logger.info("Creating security group for cloudify manager vm")
168 SecurityGroupRuleConfig(
169 sec_grp_name="sg-cloudify-manager",
170 direction=Direction.ingress, protocol=Protocol.tcp,
171 port_range_min=1, port_range_max=65535))
173 SecurityGroupRuleConfig(
174 sec_grp_name="sg-cloudify-manager",
175 direction=Direction.ingress, protocol=Protocol.udp,
176 port_range_min=1, port_range_max=65535))
178 security_group_creator = OpenStackSecurityGroup(
181 name="sg-cloudify-manager",
182 rule_settings=sg_rules))
184 security_group_creator.create()
185 self.created_object.append(security_group_creator)
187 # orchestrator VM flavor
188 self.__logger.info("Get or create flavor for cloudify manager vm ...")
190 flavor_settings = FlavorConfig(
191 name=self.orchestrator['requirements']['flavor']['name'],
192 ram=self.orchestrator['requirements']['flavor']['ram_min'],
195 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
196 flavor_creator.create()
197 self.created_object.append(flavor_creator)
198 image_settings = ImageConfig(
199 name=self.orchestrator['requirements']['os_image'],
203 port_settings = PortConfig(name='cloudify_manager_port',
204 network_name=network_settings.name)
206 manager_settings = VmInstanceConfig(
207 name='cloudify_manager',
208 flavor=flavor_settings.name,
209 port_settings=[port_settings],
210 security_group_names=[
211 security_group_creator.sec_grp_settings.name],
212 floating_ip_settings=[FloatingIpConfig(
213 name='cloudify_manager_fip',
214 port_name=port_settings.name,
215 router_name=router_creator.router_settings.name)])
217 manager_creator = OpenStackVmInstance(self.snaps_creds,
222 self.__logger.info("Creating cloudify manager VM")
223 manager_creator.create()
224 self.created_object.append(manager_creator)
226 public_auth_url = keystone_utils.get_endpoint(
227 self.snaps_creds, 'identity')
229 self.__logger.info("Set creds for cloudify manager")
230 cfy_creds = dict(keystone_username=self.snaps_creds.username,
231 keystone_password=self.snaps_creds.password,
232 keystone_tenant_name=self.snaps_creds.project_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=1500, ):
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)