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."""
12 from __future__ import division
19 from cloudify_rest_client import CloudifyClient
20 from cloudify_rest_client.executions import Execution
21 from scp import SCPClient
24 from snaps.config.flavor import FlavorConfig
25 from snaps.config.image import ImageConfig
26 from snaps.config.keypair import KeypairConfig
27 from snaps.config.network import NetworkConfig, PortConfig, SubnetConfig
28 from snaps.config.router import RouterConfig
29 from snaps.config.security_group import (
30 Direction, Protocol, SecurityGroupConfig, SecurityGroupRuleConfig)
31 from snaps.config.user import UserConfig
32 from snaps.config.vm_inst import FloatingIpConfig, VmInstanceConfig
33 from snaps.openstack.create_flavor import OpenStackFlavor
34 from snaps.openstack.create_image import OpenStackImage
35 from snaps.openstack.create_instance import OpenStackVmInstance
36 from snaps.openstack.create_keypairs import OpenStackKeypair
37 from snaps.openstack.create_network import OpenStackNetwork
38 from snaps.openstack.create_router import OpenStackRouter
39 from snaps.openstack.create_security_group import OpenStackSecurityGroup
40 from snaps.openstack.create_user import OpenStackUser
41 from snaps.openstack.utils import keystone_utils
42 from xtesting.energy import energy
44 from functest.opnfv_tests.openstack.snaps import snaps_utils
45 import functest.opnfv_tests.vnf.ims.clearwater_ims_base as clearwater_ims_base
46 from functest.utils import config
47 from functest.utils import env
49 __author__ = "Valentin Boucher <valentin.boucher@orange.com>"
52 class CloudifyIms(clearwater_ims_base.ClearwaterOnBoardingBase):
53 """Clearwater vIMS deployed with Cloudify Orchestrator Case."""
55 __logger = logging.getLogger(__name__)
57 def __init__(self, **kwargs):
58 """Initialize CloudifyIms testcase object."""
59 if "case_name" not in kwargs:
60 kwargs["case_name"] = "cloudify_ims"
61 super(CloudifyIms, self).__init__(**kwargs)
63 # Retrieve the configuration
65 self.config = getattr(
66 config.CONF, 'vnf_{}_config'.format(self.case_name))
68 raise Exception("VNF config file not found")
70 config_file = os.path.join(self.case_dir, self.config)
71 self.orchestrator = dict(
72 requirements=get_config("orchestrator.requirements", config_file),
74 self.details['orchestrator'] = dict(
75 name=get_config("orchestrator.name", config_file),
76 version=get_config("orchestrator.version", config_file),
80 self.__logger.debug("Orchestrator configuration %s", self.orchestrator)
82 descriptor=get_config("vnf.descriptor", config_file),
83 inputs=get_config("vnf.inputs", config_file),
84 requirements=get_config("vnf.requirements", config_file)
86 self.details['vnf'] = dict(
87 descriptor_version=self.vnf['descriptor']['version'],
88 name=get_config("vnf.name", config_file),
89 version=get_config("vnf.version", config_file),
91 self.__logger.debug("VNF configuration: %s", self.vnf)
93 self.details['test_vnf'] = dict(
94 name=get_config("vnf_test_suite.name", config_file),
95 version=get_config("vnf_test_suite.version", config_file)
97 self.images = get_config("tenant_images", config_file)
98 self.__logger.info("Images needed for vIMS: %s", self.images)
101 """Prepare testscase (Additional pre-configuration steps)."""
102 super(CloudifyIms, self).prepare()
104 self.__logger.info("Additional pre-configuration steps")
106 compute_quotas = self.os_project.get_compute_quotas()
107 network_quotas = self.os_project.get_network_quotas()
110 self.vnf['requirements']['compute_quotas'].items()):
111 setattr(compute_quotas, key, value)
114 self.vnf['requirements']['network_quotas'].items()):
115 setattr(network_quotas, key, value)
117 compute_quotas = self.os_project.update_compute_quotas(compute_quotas)
118 network_quotas = self.os_project.update_network_quotas(network_quotas)
120 def deploy_orchestrator(self):
121 # pylint: disable=too-many-locals,too-many-statements
123 Deploy Cloudify Manager.
125 network, security group, fip, VM creation
127 start_time = time.time()
129 # orchestrator VM flavor
130 self.__logger.info("Get or create flavor for cloudify manager vm ...")
131 flavor_settings = FlavorConfig(
133 self.orchestrator['requirements']['flavor']['name'],
135 ram=self.orchestrator['requirements']['flavor']['ram_min'],
138 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
139 flavor_creator.create()
140 self.created_object.append(flavor_creator)
142 self.__logger.info("Creating a second user to bypass issues ...")
143 user_creator = OpenStackUser(
146 name='cloudify_network_bug-{}'.format(self.uuid),
147 password=str(uuid.uuid4()),
148 project_name=self.tenant_name,
149 domain_name=self.snaps_creds.user_domain_name,
150 roles={'_member_': self.tenant_name}))
151 user_creator.create()
152 self.created_object.append(user_creator)
154 snaps_creds = user_creator.get_os_creds(self.snaps_creds.project_name)
155 self.__logger.debug("snaps creds: %s", snaps_creds)
157 self.__logger.info("Creating keypair ...")
158 kp_file = os.path.join(self.data_dir, "cloudify_ims.pem")
159 keypair_settings = KeypairConfig(
160 name='cloudify_ims_kp-{}'.format(self.uuid),
161 private_filepath=kp_file)
162 keypair_creator = OpenStackKeypair(snaps_creds, keypair_settings)
163 keypair_creator.create()
164 self.created_object.append(keypair_creator)
167 self.__logger.info("Upload some OS images if it doesn't exist")
168 for image_name, image_file in self.images.iteritems():
169 self.__logger.info("image: %s, file: %s", image_name, image_file)
170 if image_file and image_name:
171 image_creator = OpenStackImage(
174 name=image_name, image_user='cloud',
175 img_format='qcow2', image_file=image_file))
176 image_creator.create()
177 self.created_object.append(image_creator)
180 self.__logger.info("Creating full network ...")
181 subnet_settings = SubnetConfig(
182 name='cloudify_ims_subnet-{}'.format(self.uuid),
183 cidr='10.67.79.0/24',
184 dns_nameservers=[env.get('NAMESERVER')])
185 network_settings = NetworkConfig(
186 name='cloudify_ims_network-{}'.format(self.uuid),
187 subnet_settings=[subnet_settings])
188 network_creator = OpenStackNetwork(snaps_creds, network_settings)
189 network_creator.create()
190 self.created_object.append(network_creator)
191 ext_net_name = snaps_utils.get_ext_net_name(snaps_creds)
192 router_creator = OpenStackRouter(
195 name='cloudify_ims_router-{}'.format(self.uuid),
196 external_gateway=ext_net_name,
197 internal_subnets=[subnet_settings.name]))
198 router_creator.create()
199 self.created_object.append(router_creator)
201 # security group creation
202 self.__logger.info("Creating security group for cloudify manager vm")
205 SecurityGroupRuleConfig(
206 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
207 direction=Direction.ingress, protocol=Protocol.tcp,
208 port_range_min=1, port_range_max=65535))
210 SecurityGroupRuleConfig(
211 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
212 direction=Direction.ingress, protocol=Protocol.udp,
213 port_range_min=1, port_range_max=65535))
214 security_group_creator = OpenStackSecurityGroup(
217 name="sg-cloudify-manager-{}".format(self.uuid),
218 rule_settings=sg_rules))
219 security_group_creator.create()
220 self.created_object.append(security_group_creator)
222 image_settings = ImageConfig(
223 name=self.orchestrator['requirements']['os_image'],
226 port_settings = PortConfig(
227 name='cloudify_manager_port-{}'.format(self.uuid),
228 network_name=network_settings.name)
229 manager_settings = VmInstanceConfig(
230 name='cloudify_manager-{}'.format(self.uuid),
231 flavor=flavor_settings.name,
232 port_settings=[port_settings],
233 security_group_names=[
234 security_group_creator.sec_grp_settings.name],
235 floating_ip_settings=[FloatingIpConfig(
236 name='cloudify_manager_fip-{}'.format(self.uuid),
237 port_name=port_settings.name,
238 router_name=router_creator.router_settings.name)])
239 manager_creator = OpenStackVmInstance(
240 snaps_creds, manager_settings, image_settings,
242 self.__logger.info("Creating cloudify manager VM")
243 manager_creator.create()
244 self.created_object.append(manager_creator)
246 public_auth_url = keystone_utils.get_endpoint(snaps_creds, 'identity')
249 keystone_username=snaps_creds.username,
250 keystone_password=snaps_creds.password,
251 keystone_tenant_name=snaps_creds.project_name,
252 keystone_url=public_auth_url,
253 region=snaps_creds.region_name,
254 user_domain_name=snaps_creds.user_domain_name,
255 project_domain_name=snaps_creds.project_domain_name)
256 self.__logger.info("Set creds for cloudify manager %s", cfy_creds)
258 cfy_client = CloudifyClient(
259 host=manager_creator.get_floating_ip().ip,
260 username='admin', password='admin', tenant='default_tenant')
262 self.orchestrator['object'] = cfy_client
264 self.__logger.info("Attemps running status of the Manager")
265 for loop in range(10):
268 "status %s", cfy_client.manager.get_status())
269 cfy_status = cfy_client.manager.get_status()['status']
271 "The current manager status is %s", cfy_status)
272 if str(cfy_status) != 'running':
273 raise Exception("Cloudify Manager isn't up and running")
274 self.__logger.info("Put OpenStack creds in manager")
275 secrets_list = cfy_client.secrets.list()
276 for k, val in cfy_creds.iteritems():
277 if not any(d.get('key', None) == k for d in secrets_list):
278 cfy_client.secrets.create(k, val)
280 cfy_client.secrets.update(k, val)
282 except Exception: # pylint: disable=broad-except
284 "try %s: Cloudify Manager isn't up and running", loop + 1)
287 self.logger.error("Cloudify Manager isn't up and running")
290 duration = time.time() - start_time
292 if manager_creator.vm_ssh_active(block=True):
293 self.__logger.info("Put private keypair in manager")
294 ssh = manager_creator.ssh_client()
295 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
296 scp.put(kp_file, '~/')
297 cmd = "sudo cp ~/cloudify_ims.pem /etc/cloudify/"
298 self.run_blocking_ssh_command(ssh, cmd)
299 cmd = "sudo chmod 444 /etc/cloudify/cloudify_ims.pem"
300 self.run_blocking_ssh_command(ssh, cmd)
301 # cmd2 is badly unpinned by Cloudify
302 cmd = "sudo yum install -y gcc python-devel python-cmd2"
303 self.run_blocking_ssh_command(
304 ssh, cmd, "Unable to install packages on manager")
305 self.run_blocking_ssh_command(ssh, 'cfy status')
307 self.__logger.error("Cannot connect to manager")
310 self.details['orchestrator'].update(status='PASS', duration=duration)
312 self.vnf['inputs'].update(dict(
313 external_network_name=ext_net_name,
314 network_name=network_settings.name,
315 key_pair_name=keypair_settings.name
317 self.result = 1/3 * 100
320 def deploy_vnf(self):
321 """Deploy Clearwater IMS."""
322 start_time = time.time()
324 self.__logger.info("Upload VNFD")
325 cfy_client = self.orchestrator['object']
326 descriptor = self.vnf['descriptor']
327 cfy_client.blueprints.upload(
328 descriptor.get('file_name'), descriptor.get('name'))
329 self.__logger.info("Get or create flavor for all clearwater vm")
330 flavor_settings = FlavorConfig(
332 self.vnf['requirements']['flavor']['name'],
334 ram=self.vnf['requirements']['flavor']['ram_min'],
337 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
338 flavor_creator.create()
339 self.created_object.append(flavor_creator)
341 self.vnf['inputs'].update(dict(
342 flavor_id=flavor_settings.name,
345 self.__logger.info("Create VNF Instance")
346 cfy_client.deployments.create(descriptor.get('name'),
347 descriptor.get('name'),
348 self.vnf.get('inputs'))
350 wait_for_execution(cfy_client,
351 _get_deployment_environment_creation_execution(
352 cfy_client, descriptor.get('name')),
356 self.__logger.info("Start the VNF Instance deployment")
357 execution = cfy_client.executions.start(descriptor.get('name'),
360 execution = wait_for_execution(
361 cfy_client, execution, self.__logger, timeout=3600)
363 duration = time.time() - start_time
365 self.__logger.info(execution)
366 if execution.status == 'terminated':
367 self.details['vnf'].update(status='PASS', duration=duration)
368 self.result += 1/3 * 100
371 self.details['vnf'].update(status='FAIL', duration=duration)
376 """Run test on clearwater ims instance."""
377 start_time = time.time()
379 cfy_client = self.orchestrator['object']
381 outputs = cfy_client.deployments.outputs.get(
382 self.vnf['descriptor'].get('name'))['outputs']
383 dns_ip = outputs['dns_ip']
384 ellis_ip = outputs['ellis_ip']
385 self.config_ellis(ellis_ip)
390 vims_test_result = self.run_clearwater_live_test(
392 public_domain=self.vnf['inputs']["public_domain"])
393 duration = time.time() - start_time
394 short_result, nb_test = sig_test_format(vims_test_result)
395 self.__logger.info(short_result)
396 self.details['test_vnf'].update(result=short_result,
397 full_result=vims_test_result,
400 vnf_test_rate = short_result['passed'] / nb_test
401 # orchestrator + vnf + test_vnf
402 self.result += vnf_test_rate / 3 * 100
403 except ZeroDivisionError:
404 self.__logger.error("No test has been executed")
405 self.details['test_vnf'].update(status='FAIL')
408 return True if vnf_test_rate > 0 else False
411 """Clean created objects/functions."""
413 cfy_client = self.orchestrator['object']
414 dep_name = self.vnf['descriptor'].get('name')
415 # kill existing execution
416 self.__logger.info('Deleting the current deployment')
417 exec_list = cfy_client.executions.list(dep_name)
418 for execution in exec_list:
419 if execution['status'] == "started":
421 cfy_client.executions.cancel(execution['id'],
423 except Exception: # pylint: disable=broad-except
424 self.__logger.warn("Can't cancel the current exec")
426 execution = cfy_client.executions.start(
429 parameters=dict(ignore_failure=True),
432 wait_for_execution(cfy_client, execution, self.__logger)
433 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
434 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
435 except Exception: # pylint: disable=broad-except
436 self.__logger.exception("Some issue during the undeployment ..")
438 super(CloudifyIms, self).clean()
441 def run_blocking_ssh_command(ssh, cmd,
442 error_msg="Unable to run this command"):
443 """Command to run ssh command with the exit status."""
444 _, stdout, stderr = ssh.exec_command(cmd)
445 CloudifyIms.__logger.debug("SSH %s stdout: %s", cmd, stdout.read())
446 if stdout.channel.recv_exit_status() != 0:
447 CloudifyIms.__logger.error("SSH %s stderr: %s", cmd, stderr.read())
448 raise Exception(error_msg)
450 @energy.enable_recording
451 def run(self, **kwargs):
452 """Execute CloudifyIms test case."""
453 return super(CloudifyIms, self).run(**kwargs)
456 # ----------------------------------------------------------
460 # -----------------------------------------------------------
461 def get_config(parameter, file_path):
463 Get config parameter.
465 Returns the value of a given parameter in file.yaml
466 parameter must be given in string format with dots
467 Example: general.openstack.image_name
469 with open(file_path) as config_file:
470 file_yaml = yaml.safe_load(config_file)
473 for element in parameter.split("."):
474 value = value.get(element)
476 raise ValueError("The parameter %s is not defined in"
477 " reporting.yaml" % parameter)
481 def wait_for_execution(client, execution, logger, timeout=3600, ):
482 """Wait for a workflow execution on Cloudify Manager."""
483 # if execution already ended - return without waiting
484 if execution.status in Execution.END_STATES:
487 if timeout is not None:
488 deadline = time.time() + timeout
490 # Poll for execution status and execution logs, until execution ends
491 # and we receive an event of type in WORKFLOW_END_TYPES
495 execution_ended = False
497 event_list = client.events.list(
498 execution_id=execution.id,
502 sort='@timestamp').items
504 offset = offset + len(event_list)
505 for event in event_list:
506 logger.debug(event.get('message'))
508 if timeout is not None:
509 if time.time() > deadline:
511 'execution of operation {0} for deployment {1} '
512 'timed out'.format(execution.workflow_id,
513 execution.deployment_id))
515 # update the remaining timeout
516 timeout = deadline - time.time()
518 if not execution_ended:
519 execution = client.executions.get(execution.id)
520 execution_ended = execution.status in Execution.END_STATES
530 def _get_deployment_environment_creation_execution(client, deployment_id):
532 Get the execution id of a env preparation.
534 network, security group, fip, VM creation
536 executions = client.executions.list(deployment_id=deployment_id)
537 for execution in executions:
538 if execution.workflow_id == 'create_deployment_environment':
540 raise RuntimeError('Failed to get create_deployment_environment '
541 'workflow execution.'
542 'Available executions: {0}'.format(executions))
545 def sig_test_format(sig_test):
546 """Process the signaling result to have a short result."""
550 for data_test in sig_test:
551 if data_test['result'] == "Passed":
553 elif data_test['result'] == "Failed":
555 elif data_test['result'] == "Skipped":
557 short_sig_test_result = {}
558 short_sig_test_result['passed'] = nb_passed
559 short_sig_test_result['failures'] = nb_failures
560 short_sig_test_result['skipped'] = nb_skipped
561 nb_test = nb_passed + nb_skipped
562 return (short_sig_test_result, nb_test)