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,
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',
263 self.orchestrator['object'] = cfy_client
265 self.__logger.info("Attemps running status of the Manager")
266 for loop in range(10):
269 "status %s", cfy_client.manager.get_status())
270 cfy_status = cfy_client.manager.get_status()['status']
272 "The current manager status is %s", cfy_status)
273 if str(cfy_status) != 'running':
274 raise Exception("Cloudify Manager isn't up and running")
275 self.__logger.info("Put OpenStack creds in manager")
276 secrets_list = cfy_client.secrets.list()
277 for k, val in six.iteritems(cfy_creds):
278 if not any(d.get('key', None) == k for d in secrets_list):
279 cfy_client.secrets.create(k, val)
281 cfy_client.secrets.update(k, val)
283 except Exception: # pylint: disable=broad-except
285 "try %s: Cloudify Manager isn't up and running", loop + 1)
288 self.logger.error("Cloudify Manager isn't up and running")
291 duration = time.time() - start_time
293 if manager_creator.vm_ssh_active(block=True):
294 self.__logger.info("Put private keypair in manager")
295 ssh = manager_creator.ssh_client()
296 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
297 scp.put(kp_file, '~/')
298 cmd = "sudo cp ~/cloudify_ims.pem /etc/cloudify/"
299 self.run_blocking_ssh_command(ssh, cmd)
300 cmd = "sudo chmod 444 /etc/cloudify/cloudify_ims.pem"
301 self.run_blocking_ssh_command(ssh, cmd)
302 # cmd2 is badly unpinned by Cloudify
303 cmd = "sudo yum install -y gcc python-devel python-cmd2"
304 self.run_blocking_ssh_command(
305 ssh, cmd, "Unable to install packages on manager")
306 self.run_blocking_ssh_command(ssh, 'cfy status')
308 self.__logger.error("Cannot connect to manager")
311 self.details['orchestrator'].update(status='PASS', duration=duration)
313 self.vnf['inputs'].update(dict(
314 external_network_name=ext_net_name,
315 network_name=network_settings.name,
316 key_pair_name=keypair_settings.name
318 self.result = 1/3 * 100
321 def deploy_vnf(self):
322 """Deploy Clearwater IMS."""
323 start_time = time.time()
325 self.__logger.info("Upload VNFD")
326 cfy_client = self.orchestrator['object']
327 descriptor = self.vnf['descriptor']
328 cfy_client.blueprints.upload(
329 descriptor.get('file_name'), descriptor.get('name'))
330 self.__logger.info("Get or create flavor for all clearwater vm")
331 flavor_settings = FlavorConfig(
333 self.vnf['requirements']['flavor']['name'],
335 ram=self.vnf['requirements']['flavor']['ram_min'],
338 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
339 flavor_creator.create()
340 self.created_object.append(flavor_creator)
342 self.vnf['inputs'].update(dict(
343 flavor_id=flavor_settings.name,
346 self.__logger.info("Create VNF Instance")
347 cfy_client.deployments.create(descriptor.get('name'),
348 descriptor.get('name'),
349 self.vnf.get('inputs'))
353 get_execution_id(cfy_client, descriptor.get('name')),
354 self.__logger, timeout=300)
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 short_result = self.run_clearwater_live_test(
392 public_domain=self.vnf['inputs']["public_domain"])
393 duration = time.time() - start_time
394 self.__logger.info(short_result)
395 self.details['test_vnf'].update(result=short_result,
398 vnf_test_rate = short_result['passed'] / (
399 short_result['total'] - short_result['skipped'])
400 # orchestrator + vnf + test_vnf
401 self.result += vnf_test_rate / 3 * 100
402 except ZeroDivisionError:
403 self.__logger.error("No test has been executed")
404 self.details['test_vnf'].update(status='FAIL')
406 except Exception: # pylint: disable=broad-except
407 self.__logger.exception("Cannot calculate results")
408 self.details['test_vnf'].update(status='FAIL')
410 return True if vnf_test_rate > 0 else False
413 """Clean created objects/functions."""
415 cfy_client = self.orchestrator['object']
416 dep_name = self.vnf['descriptor'].get('name')
417 # kill existing execution
418 self.__logger.info('Deleting the current deployment')
419 exec_list = cfy_client.executions.list(dep_name)
420 for execution in exec_list:
421 if execution['status'] == "started":
423 cfy_client.executions.cancel(execution['id'],
425 except Exception: # pylint: disable=broad-except
426 self.__logger.warn("Can't cancel the current exec")
428 execution = cfy_client.executions.start(
431 parameters=dict(ignore_failure=True),
434 wait_for_execution(cfy_client, execution, self.__logger)
435 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
436 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
437 except Exception: # pylint: disable=broad-except
438 self.__logger.exception("Some issue during the undeployment ..")
440 super(CloudifyIms, self).clean()
443 def run_blocking_ssh_command(ssh, cmd,
444 error_msg="Unable to run this command"):
445 """Command to run ssh command with the exit status."""
446 _, stdout, stderr = ssh.exec_command(cmd)
447 CloudifyIms.__logger.debug("SSH %s stdout: %s", cmd, stdout.read())
448 if stdout.channel.recv_exit_status() != 0:
449 CloudifyIms.__logger.error("SSH %s stderr: %s", cmd, stderr.read())
450 raise Exception(error_msg)
452 @energy.enable_recording
453 def run(self, **kwargs):
454 """Execute CloudifyIms test case."""
455 return super(CloudifyIms, self).run(**kwargs)
458 # ----------------------------------------------------------
462 # -----------------------------------------------------------
463 def get_config(parameter, file_path):
465 Get config parameter.
467 Returns the value of a given parameter in file.yaml
468 parameter must be given in string format with dots
469 Example: general.openstack.image_name
471 with open(file_path) as config_file:
472 file_yaml = yaml.safe_load(config_file)
475 for element in parameter.split("."):
476 value = value.get(element)
478 raise ValueError("The parameter %s is not defined in"
479 " reporting.yaml" % parameter)
483 def wait_for_execution(client, execution, logger, timeout=3600, ):
484 """Wait for a workflow execution on Cloudify Manager."""
485 # if execution already ended - return without waiting
486 if execution.status in Execution.END_STATES:
489 if timeout is not None:
490 deadline = time.time() + timeout
492 # Poll for execution status and execution logs, until execution ends
493 # and we receive an event of type in WORKFLOW_END_TYPES
497 execution_ended = False
499 event_list = client.events.list(
500 execution_id=execution.id,
504 sort='@timestamp').items
506 offset = offset + len(event_list)
507 for event in event_list:
508 logger.debug(event.get('message'))
510 if timeout is not None:
511 if time.time() > deadline:
513 'execution of operation {0} for deployment {1} '
514 'timed out'.format(execution.workflow_id,
515 execution.deployment_id))
517 # update the remaining timeout
518 timeout = deadline - time.time()
520 if not execution_ended:
521 execution = client.executions.get(execution.id)
522 execution_ended = execution.status in Execution.END_STATES
532 def get_execution_id(client, deployment_id):
534 Get the execution id of a env preparation.
536 network, security group, fip, VM creation
538 executions = client.executions.list(deployment_id=deployment_id)
539 for execution in executions:
540 if execution.workflow_id == 'create_deployment_environment':
542 raise RuntimeError('Failed to get create_deployment_environment '
543 'workflow execution.'
544 'Available executions: {0}'.format(executions))