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 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_deployment_environment_creation_execution(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))