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."""
17 from cloudify_rest_client import CloudifyClient
18 from cloudify_rest_client.executions import Execution
19 from scp import SCPClient
22 from snaps.config.flavor import FlavorConfig
23 from snaps.config.image import ImageConfig
24 from snaps.config.keypair import KeypairConfig
25 from snaps.config.network import NetworkConfig, PortConfig, SubnetConfig
26 from snaps.config.router import RouterConfig
27 from snaps.config.security_group import (
28 Direction, Protocol, SecurityGroupConfig, SecurityGroupRuleConfig)
29 from snaps.config.user import UserConfig
30 from snaps.config.vm_inst import FloatingIpConfig, VmInstanceConfig
31 from snaps.openstack.create_flavor import OpenStackFlavor
32 from snaps.openstack.create_image import OpenStackImage
33 from snaps.openstack.create_instance import OpenStackVmInstance
34 from snaps.openstack.create_keypairs import OpenStackKeypair
35 from snaps.openstack.create_network import OpenStackNetwork
36 from snaps.openstack.create_router import OpenStackRouter
37 from snaps.openstack.create_security_group import OpenStackSecurityGroup
38 from snaps.openstack.create_user import OpenStackUser
39 from snaps.openstack.utils import keystone_utils
40 from xtesting.energy import energy
42 from functest.opnfv_tests.openstack.snaps import snaps_utils
43 import functest.opnfv_tests.vnf.ims.clearwater_ims_base as clearwater_ims_base
44 from functest.utils import config
45 from functest.utils import env
47 __author__ = "Valentin Boucher <valentin.boucher@orange.com>"
50 class CloudifyIms(clearwater_ims_base.ClearwaterOnBoardingBase):
51 """Clearwater vIMS deployed with Cloudify Orchestrator Case."""
53 __logger = logging.getLogger(__name__)
55 def __init__(self, **kwargs):
56 """Initialize CloudifyIms testcase object."""
57 if "case_name" not in kwargs:
58 kwargs["case_name"] = "cloudify_ims"
59 super(CloudifyIms, self).__init__(**kwargs)
61 # Retrieve the configuration
63 self.config = getattr(
64 config.CONF, 'vnf_{}_config'.format(self.case_name))
66 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)
118 def deploy_orchestrator(self):
119 # pylint: disable=too-many-locals,too-many-statements
121 Deploy Cloudify Manager.
123 network, security group, fip, VM creation
125 start_time = time.time()
127 # orchestrator VM flavor
128 self.__logger.info("Get or create flavor for cloudify manager vm ...")
129 flavor_settings = FlavorConfig(
131 self.orchestrator['requirements']['flavor']['name'],
133 ram=self.orchestrator['requirements']['flavor']['ram_min'],
136 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
137 flavor_creator.create()
138 self.created_object.append(flavor_creator)
140 self.__logger.info("Creating a second user to bypass issues ...")
141 user_creator = OpenStackUser(
144 name='cloudify_network_bug-{}'.format(self.uuid),
145 password=str(uuid.uuid4()),
146 project_name=self.tenant_name,
147 domain_name=self.snaps_creds.user_domain_name,
148 roles={'_member_': self.tenant_name}))
149 user_creator.create()
150 self.created_object.append(user_creator)
152 snaps_creds = user_creator.get_os_creds(self.snaps_creds.project_name)
153 self.__logger.debug("snaps creds: %s", snaps_creds)
155 self.__logger.info("Creating keypair ...")
156 kp_file = os.path.join(self.data_dir, "cloudify_ims.pem")
157 keypair_settings = KeypairConfig(
158 name='cloudify_ims_kp-{}'.format(self.uuid),
159 private_filepath=kp_file)
160 keypair_creator = OpenStackKeypair(snaps_creds, keypair_settings)
161 keypair_creator.create()
162 self.created_object.append(keypair_creator)
165 self.__logger.info("Upload some OS images if it doesn't exist")
166 for image_name, image_file in self.images.iteritems():
167 self.__logger.info("image: %s, file: %s", image_name, image_file)
168 if image_file and image_name:
169 image_creator = OpenStackImage(
172 name=image_name, image_user='cloud',
173 img_format='qcow2', image_file=image_file))
174 image_creator.create()
175 self.created_object.append(image_creator)
178 self.__logger.info("Creating full network ...")
179 subnet_settings = SubnetConfig(
180 name='cloudify_ims_subnet-{}'.format(self.uuid),
181 cidr='10.67.79.0/24',
182 dns_nameservers=[env.get('NAMESERVER')])
183 network_settings = NetworkConfig(
184 name='cloudify_ims_network-{}'.format(self.uuid),
185 subnet_settings=[subnet_settings])
186 network_creator = OpenStackNetwork(snaps_creds, network_settings)
187 network_creator.create()
188 self.created_object.append(network_creator)
189 ext_net_name = snaps_utils.get_ext_net_name(snaps_creds)
190 router_creator = OpenStackRouter(
193 name='cloudify_ims_router-{}'.format(self.uuid),
194 external_gateway=ext_net_name,
195 internal_subnets=[subnet_settings.name]))
196 router_creator.create()
197 self.created_object.append(router_creator)
199 # security group creation
200 self.__logger.info("Creating security group for cloudify manager vm")
203 SecurityGroupRuleConfig(
204 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
205 direction=Direction.ingress, protocol=Protocol.tcp,
206 port_range_min=1, port_range_max=65535))
208 SecurityGroupRuleConfig(
209 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
210 direction=Direction.ingress, protocol=Protocol.udp,
211 port_range_min=1, port_range_max=65535))
212 security_group_creator = OpenStackSecurityGroup(
215 name="sg-cloudify-manager-{}".format(self.uuid),
216 rule_settings=sg_rules))
217 security_group_creator.create()
218 self.created_object.append(security_group_creator)
220 image_settings = ImageConfig(
221 name=self.orchestrator['requirements']['os_image'],
224 port_settings = PortConfig(
225 name='cloudify_manager_port-{}'.format(self.uuid),
226 network_name=network_settings.name)
227 manager_settings = VmInstanceConfig(
228 name='cloudify_manager-{}'.format(self.uuid),
229 flavor=flavor_settings.name,
230 port_settings=[port_settings],
231 security_group_names=[
232 security_group_creator.sec_grp_settings.name],
233 floating_ip_settings=[FloatingIpConfig(
234 name='cloudify_manager_fip-{}'.format(self.uuid),
235 port_name=port_settings.name,
236 router_name=router_creator.router_settings.name)])
237 manager_creator = OpenStackVmInstance(
238 snaps_creds, manager_settings, image_settings,
240 self.__logger.info("Creating cloudify manager VM")
241 manager_creator.create()
242 self.created_object.append(manager_creator)
244 public_auth_url = keystone_utils.get_endpoint(snaps_creds, 'identity')
247 keystone_username=snaps_creds.username,
248 keystone_password=snaps_creds.password,
249 keystone_tenant_name=snaps_creds.project_name,
250 keystone_url=public_auth_url,
251 region=snaps_creds.region_name,
252 user_domain_name=snaps_creds.user_domain_name,
253 project_domain_name=snaps_creds.project_domain_name)
254 self.__logger.info("Set creds for cloudify manager %s", cfy_creds)
256 cfy_client = CloudifyClient(
257 host=manager_creator.get_floating_ip().ip,
258 username='admin', password='admin', tenant='default_tenant')
260 self.orchestrator['object'] = cfy_client
262 self.__logger.info("Attemps running status of the Manager")
263 for loop in range(10):
266 "status %s", cfy_client.manager.get_status())
267 cfy_status = cfy_client.manager.get_status()['status']
269 "The current manager status is %s", cfy_status)
270 if str(cfy_status) != 'running':
271 raise Exception("Cloudify Manager isn't up and running")
272 self.__logger.info("Put OpenStack creds in manager")
273 secrets_list = cfy_client.secrets.list()
274 for k, val in cfy_creds.iteritems():
275 if not any(d.get('key', None) == k for d in secrets_list):
276 cfy_client.secrets.create(k, val)
278 cfy_client.secrets.update(k, val)
280 except Exception: # pylint: disable=broad-except
282 "try %s: Cloudify Manager isn't up and running", loop + 1)
285 self.logger.error("Cloudify Manager isn't up and running")
288 duration = time.time() - start_time
290 if manager_creator.vm_ssh_active(block=True):
291 self.__logger.info("Put private keypair in manager")
292 ssh = manager_creator.ssh_client()
293 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
294 scp.put(kp_file, '~/')
295 cmd = "sudo cp ~/cloudify_ims.pem /etc/cloudify/"
296 self.run_blocking_ssh_command(ssh, cmd)
297 cmd = "sudo chmod 444 /etc/cloudify/cloudify_ims.pem"
298 self.run_blocking_ssh_command(ssh, cmd)
299 # cmd2 is badly unpinned by Cloudify
300 cmd = "sudo yum install -y gcc python-devel python-cmd2"
301 self.run_blocking_ssh_command(
302 ssh, cmd, "Unable to install packages on manager")
303 self.run_blocking_ssh_command(ssh, 'cfy status')
305 self.__logger.error("Cannot connect to manager")
308 self.details['orchestrator'].update(status='PASS', duration=duration)
310 self.vnf['inputs'].update(dict(
311 external_network_name=ext_net_name,
312 network_name=network_settings.name,
313 key_pair_name=keypair_settings.name
315 self.result = 1/3 * 100
318 def deploy_vnf(self):
319 """Deploy Clearwater IMS."""
320 start_time = time.time()
322 self.__logger.info("Upload VNFD")
323 cfy_client = self.orchestrator['object']
324 descriptor = self.vnf['descriptor']
325 cfy_client.blueprints.upload(
326 descriptor.get('file_name'), descriptor.get('name'))
327 self.__logger.info("Get or create flavor for all clearwater vm")
328 flavor_settings = FlavorConfig(
330 self.vnf['requirements']['flavor']['name'],
332 ram=self.vnf['requirements']['flavor']['ram_min'],
335 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
336 flavor_creator.create()
337 self.created_object.append(flavor_creator)
339 self.vnf['inputs'].update(dict(
340 flavor_id=flavor_settings.name,
343 self.__logger.info("Create VNF Instance")
344 cfy_client.deployments.create(descriptor.get('name'),
345 descriptor.get('name'),
346 self.vnf.get('inputs'))
348 wait_for_execution(cfy_client,
349 _get_deployment_environment_creation_execution(
350 cfy_client, descriptor.get('name')),
354 self.__logger.info("Start the VNF Instance deployment")
355 execution = cfy_client.executions.start(descriptor.get('name'),
358 execution = wait_for_execution(
359 cfy_client, execution, self.__logger, timeout=3600)
361 duration = time.time() - start_time
363 self.__logger.info(execution)
364 if execution.status == 'terminated':
365 self.details['vnf'].update(status='PASS', duration=duration)
366 self.result += 1/3 * 100
369 self.details['vnf'].update(status='FAIL', duration=duration)
374 """Run test on clearwater ims instance."""
375 start_time = time.time()
377 cfy_client = self.orchestrator['object']
379 outputs = cfy_client.deployments.outputs.get(
380 self.vnf['descriptor'].get('name'))['outputs']
381 dns_ip = outputs['dns_ip']
382 ellis_ip = outputs['ellis_ip']
383 self.config_ellis(ellis_ip)
388 vims_test_result = self.run_clearwater_live_test(
390 public_domain=self.vnf['inputs']["public_domain"])
391 duration = time.time() - start_time
392 short_result, nb_test = sig_test_format(vims_test_result)
393 self.__logger.info(short_result)
394 self.details['test_vnf'].update(result=short_result,
395 full_result=vims_test_result,
398 vnf_test_rate = short_result['passed'] / nb_test
399 # orchestrator + vnf + test_vnf
400 self.result += vnf_test_rate / 3 * 100
401 except ZeroDivisionError:
402 self.__logger.error("No test has been executed")
403 self.details['test_vnf'].update(status='FAIL')
409 """Clean created objects/functions."""
411 cfy_client = self.orchestrator['object']
412 dep_name = self.vnf['descriptor'].get('name')
413 # kill existing execution
414 self.__logger.info('Deleting the current deployment')
415 exec_list = cfy_client.executions.list(dep_name)
416 for execution in exec_list:
417 if execution['status'] == "started":
419 cfy_client.executions.cancel(execution['id'],
421 except Exception: # pylint: disable=broad-except
422 self.__logger.warn("Can't cancel the current exec")
424 execution = cfy_client.executions.start(
427 parameters=dict(ignore_failure=True),
430 wait_for_execution(cfy_client, execution, self.__logger)
431 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
432 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
433 except Exception: # pylint: disable=broad-except
434 self.__logger.exception("Some issue during the undeployment ..")
436 super(CloudifyIms, self).clean()
439 def run_blocking_ssh_command(ssh, cmd,
440 error_msg="Unable to run this command"):
441 """Command to run ssh command with the exit status."""
442 _, stdout, stderr = ssh.exec_command(cmd)
443 CloudifyIms.__logger.debug("SSH %s stdout: %s", cmd, stdout.read())
444 if stdout.channel.recv_exit_status() != 0:
445 CloudifyIms.__logger.error("SSH %s stderr: %s", cmd, stderr.read())
446 raise Exception(error_msg)
448 @energy.enable_recording
449 def run(self, **kwargs):
450 """Execute CloudifyIms test case."""
451 return super(CloudifyIms, self).run(**kwargs)
454 # ----------------------------------------------------------
458 # -----------------------------------------------------------
459 def get_config(parameter, file_path):
461 Get config parameter.
463 Returns the value of a given parameter in file.yaml
464 parameter must be given in string format with dots
465 Example: general.openstack.image_name
467 with open(file_path) as config_file:
468 file_yaml = yaml.safe_load(config_file)
471 for element in parameter.split("."):
472 value = value.get(element)
474 raise ValueError("The parameter %s is not defined in"
475 " reporting.yaml" % parameter)
479 def wait_for_execution(client, execution, logger, timeout=3600, ):
480 """Wait for a workflow execution on Cloudify Manager."""
481 # if execution already ended - return without waiting
482 if execution.status in Execution.END_STATES:
485 if timeout is not None:
486 deadline = time.time() + timeout
488 # Poll for execution status and execution logs, until execution ends
489 # and we receive an event of type in WORKFLOW_END_TYPES
493 execution_ended = False
495 event_list = client.events.list(
496 execution_id=execution.id,
500 sort='@timestamp').items
502 offset = offset + len(event_list)
503 for event in event_list:
504 logger.debug(event.get('message'))
506 if timeout is not None:
507 if time.time() > deadline:
509 'execution of operation {0} for deployment {1} '
510 'timed out'.format(execution.workflow_id,
511 execution.deployment_id))
513 # update the remaining timeout
514 timeout = deadline - time.time()
516 if not execution_ended:
517 execution = client.executions.get(execution.id)
518 execution_ended = execution.status in Execution.END_STATES
528 def _get_deployment_environment_creation_execution(client, deployment_id):
530 Get the execution id of a env preparation.
532 network, security group, fip, VM creation
534 executions = client.executions.list(deployment_id=deployment_id)
535 for execution in executions:
536 if execution.workflow_id == 'create_deployment_environment':
538 raise RuntimeError('Failed to get create_deployment_environment '
539 'workflow execution.'
540 'Available executions: {0}'.format(executions))
543 def sig_test_format(sig_test):
544 """Process the signaling result to have a short result."""
548 for data_test in sig_test:
549 if data_test['result'] == "Passed":
551 elif data_test['result'] == "Failed":
553 elif data_test['result'] == "Skipped":
555 short_sig_test_result = {}
556 short_sig_test_result['passed'] = nb_passed
557 short_sig_test_result['failures'] = nb_failures
558 short_sig_test_result['skipped'] = nb_skipped
559 nb_test = nb_passed + nb_skipped
560 return (short_sig_test_result, nb_test)