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
23 from snaps.config.flavor import FlavorConfig
24 from snaps.config.image import ImageConfig
25 from snaps.config.keypair import KeypairConfig
26 from snaps.config.network import NetworkConfig, PortConfig, SubnetConfig
27 from snaps.config.router import RouterConfig
28 from snaps.config.security_group import (
29 Direction, Protocol, SecurityGroupConfig, SecurityGroupRuleConfig)
30 from snaps.config.user import UserConfig
31 from snaps.config.vm_inst import FloatingIpConfig, VmInstanceConfig
32 from snaps.openstack.create_flavor import OpenStackFlavor
33 from snaps.openstack.create_image import OpenStackImage
34 from snaps.openstack.create_instance import OpenStackVmInstance
35 from snaps.openstack.create_keypairs import OpenStackKeypair
36 from snaps.openstack.create_network import OpenStackNetwork
37 from snaps.openstack.create_router import OpenStackRouter
38 from snaps.openstack.create_security_group import OpenStackSecurityGroup
39 from snaps.openstack.create_user import OpenStackUser
40 from snaps.openstack.utils import keystone_utils
41 from xtesting.energy import energy
44 from functest.opnfv_tests.openstack.snaps import snaps_utils
45 from functest.opnfv_tests.vnf.ims import 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 six.iteritems(self.images):
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 if snaps_creds.region_name else (
255 user_domain_name=snaps_creds.user_domain_name,
256 project_domain_name=snaps_creds.project_domain_name)
257 self.__logger.info("Set creds for cloudify manager %s", cfy_creds)
259 cfy_client = CloudifyClient(
260 host=manager_creator.get_floating_ip().ip,
261 username='admin', password='admin', tenant='default_tenant',
264 self.orchestrator['object'] = cfy_client
266 self.__logger.info("Attemps running status of the Manager")
267 for loop in range(10):
270 "status %s", cfy_client.manager.get_status())
271 cfy_status = cfy_client.manager.get_status()['status']
273 "The current manager status is %s", cfy_status)
274 if str(cfy_status) != 'running':
275 raise Exception("Cloudify Manager isn't up and running")
276 self.__logger.info("Put OpenStack creds in manager")
277 secrets_list = cfy_client.secrets.list()
278 for k, val in six.iteritems(cfy_creds):
279 if not any(d.get('key', None) == k for d in secrets_list):
280 cfy_client.secrets.create(k, val)
282 cfy_client.secrets.update(k, val)
284 except Exception: # pylint: disable=broad-except
286 "try %s: Cloudify Manager isn't up and running", loop + 1)
289 self.logger.error("Cloudify Manager isn't up and running")
292 duration = time.time() - start_time
294 if manager_creator.vm_ssh_active(block=True):
295 self.__logger.info("Put private keypair in manager")
296 ssh = manager_creator.ssh_client()
297 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
298 scp.put(kp_file, '~/')
299 cmd = "sudo cp ~/cloudify_ims.pem /etc/cloudify/"
300 self.run_blocking_ssh_command(ssh, cmd)
301 cmd = "sudo chmod 444 /etc/cloudify/cloudify_ims.pem"
302 self.run_blocking_ssh_command(ssh, cmd)
303 # cmd2 is badly unpinned by Cloudify
304 cmd = "sudo yum install -y gcc python-devel python-cmd2"
305 self.run_blocking_ssh_command(
306 ssh, cmd, "Unable to install packages on manager")
307 self.run_blocking_ssh_command(ssh, 'cfy status')
309 self.__logger.error("Cannot connect to manager")
312 self.details['orchestrator'].update(status='PASS', duration=duration)
314 self.vnf['inputs'].update(dict(
315 external_network_name=ext_net_name,
316 network_name=network_settings.name,
317 key_pair_name=keypair_settings.name
319 self.result = 1/3 * 100
322 def deploy_vnf(self):
323 """Deploy Clearwater IMS."""
324 start_time = time.time()
326 self.__logger.info("Upload VNFD")
327 cfy_client = self.orchestrator['object']
328 descriptor = self.vnf['descriptor']
329 cfy_client.blueprints.upload(
330 descriptor.get('file_name'), descriptor.get('name'))
331 self.__logger.info("Get or create flavor for all clearwater vm")
332 flavor_settings = FlavorConfig(
334 self.vnf['requirements']['flavor']['name'],
336 ram=self.vnf['requirements']['flavor']['ram_min'],
339 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
340 flavor_creator.create()
341 self.created_object.append(flavor_creator)
343 self.vnf['inputs'].update(dict(
344 flavor_id=flavor_settings.name,
347 self.__logger.info("Create VNF Instance")
348 cfy_client.deployments.create(descriptor.get('name'),
349 descriptor.get('name'),
350 self.vnf.get('inputs'))
354 get_execution_id(cfy_client, descriptor.get('name')),
355 self.__logger, timeout=300)
357 self.__logger.info("Start the VNF Instance deployment")
358 execution = cfy_client.executions.start(descriptor.get('name'),
361 execution = wait_for_execution(
362 cfy_client, execution, self.__logger, timeout=3600)
364 duration = time.time() - start_time
366 self.__logger.info(execution)
367 if execution.status == 'terminated':
368 self.details['vnf'].update(status='PASS', duration=duration)
369 self.result += 1/3 * 100
372 self.details['vnf'].update(status='FAIL', duration=duration)
377 """Run test on clearwater ims instance."""
378 start_time = time.time()
380 cfy_client = self.orchestrator['object']
382 outputs = cfy_client.deployments.outputs.get(
383 self.vnf['descriptor'].get('name'))['outputs']
384 dns_ip = outputs['dns_ip']
385 ellis_ip = outputs['ellis_ip']
386 self.config_ellis(ellis_ip)
391 short_result = self.run_clearwater_live_test(
393 public_domain=self.vnf['inputs']["public_domain"])
394 duration = time.time() - start_time
395 self.__logger.info(short_result)
396 self.details['test_vnf'].update(result=short_result,
399 vnf_test_rate = short_result['passed'] / (
400 short_result['total'] - short_result['skipped'])
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')
407 except Exception: # pylint: disable=broad-except
408 self.__logger.exception("Cannot calculate results")
409 self.details['test_vnf'].update(status='FAIL')
411 return True if vnf_test_rate > 0 else False
414 """Clean created objects/functions."""
416 cfy_client = self.orchestrator['object']
417 dep_name = self.vnf['descriptor'].get('name')
418 # kill existing execution
419 self.__logger.info('Deleting the current deployment')
420 exec_list = cfy_client.executions.list(dep_name)
421 for execution in exec_list:
422 if execution['status'] == "started":
424 cfy_client.executions.cancel(execution['id'],
426 except Exception: # pylint: disable=broad-except
427 self.__logger.warn("Can't cancel the current exec")
429 execution = cfy_client.executions.start(
432 parameters=dict(ignore_failure=True),
435 wait_for_execution(cfy_client, execution, self.__logger)
436 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
437 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
438 except Exception: # pylint: disable=broad-except
439 self.__logger.exception("Some issue during the undeployment ..")
441 super(CloudifyIms, self).clean()
444 def run_blocking_ssh_command(ssh, cmd,
445 error_msg="Unable to run this command"):
446 """Command to run ssh command with the exit status."""
447 _, stdout, stderr = ssh.exec_command(cmd)
448 CloudifyIms.__logger.debug("SSH %s stdout: %s", cmd, stdout.read())
449 if stdout.channel.recv_exit_status() != 0:
450 CloudifyIms.__logger.error("SSH %s stderr: %s", cmd, stderr.read())
451 raise Exception(error_msg)
453 @energy.enable_recording
454 def run(self, **kwargs):
455 """Execute CloudifyIms test case."""
456 return super(CloudifyIms, self).run(**kwargs)
459 # ----------------------------------------------------------
463 # -----------------------------------------------------------
464 def get_config(parameter, file_path):
466 Get config parameter.
468 Returns the value of a given parameter in file.yaml
469 parameter must be given in string format with dots
470 Example: general.openstack.image_name
472 with open(file_path) as config_file:
473 file_yaml = yaml.safe_load(config_file)
476 for element in parameter.split("."):
477 value = value.get(element)
479 raise ValueError("The parameter %s is not defined in"
480 " reporting.yaml" % parameter)
484 def wait_for_execution(client, execution, logger, timeout=3600, ):
485 """Wait for a workflow execution on Cloudify Manager."""
486 # if execution already ended - return without waiting
487 if execution.status in Execution.END_STATES:
490 if timeout is not None:
491 deadline = time.time() + timeout
493 # Poll for execution status and execution logs, until execution ends
494 # and we receive an event of type in WORKFLOW_END_TYPES
498 execution_ended = False
500 event_list = client.events.list(
501 execution_id=execution.id,
505 sort='@timestamp').items
507 offset = offset + len(event_list)
508 for event in event_list:
509 logger.debug(event.get('message'))
511 if timeout is not None:
512 if time.time() > deadline:
514 'execution of operation {0} for deployment {1} '
515 'timed out'.format(execution.workflow_id,
516 execution.deployment_id))
518 # update the remaining timeout
519 timeout = deadline - time.time()
521 if not execution_ended:
522 execution = client.executions.get(execution.id)
523 execution_ended = execution.status in Execution.END_STATES
533 def get_execution_id(client, deployment_id):
535 Get the execution id of a env preparation.
537 network, security group, fip, VM creation
539 executions = client.executions.list(deployment_id=deployment_id)
540 for execution in executions:
541 if execution.workflow_id == 'create_deployment_environment':
543 raise RuntimeError('Failed to get create_deployment_environment '
544 'workflow execution.'
545 'Available executions: {0}'.format(executions))