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 cmd = "sudo yum install -y gcc python-devel"
300 self.run_blocking_ssh_command(
301 ssh, cmd, "Unable to install packages on manager")
302 self.run_blocking_ssh_command(ssh, 'cfy status')
304 self.__logger.error("Cannot connect to manager")
307 self.details['orchestrator'].update(status='PASS', duration=duration)
309 self.vnf['inputs'].update(dict(
310 external_network_name=ext_net_name,
311 network_name=network_settings.name,
312 key_pair_name=keypair_settings.name
314 self.result = 1/3 * 100
317 def deploy_vnf(self):
318 """Deploy Clearwater IMS."""
319 start_time = time.time()
321 self.__logger.info("Upload VNFD")
322 cfy_client = self.orchestrator['object']
323 descriptor = self.vnf['descriptor']
324 cfy_client.blueprints.upload(
325 descriptor.get('file_name'), descriptor.get('name'))
326 self.__logger.info("Get or create flavor for all clearwater vm")
327 flavor_settings = FlavorConfig(
329 self.vnf['requirements']['flavor']['name'],
331 ram=self.vnf['requirements']['flavor']['ram_min'],
334 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
335 flavor_creator.create()
336 self.created_object.append(flavor_creator)
338 self.vnf['inputs'].update(dict(
339 flavor_id=flavor_settings.name,
342 self.__logger.info("Create VNF Instance")
343 cfy_client.deployments.create(descriptor.get('name'),
344 descriptor.get('name'),
345 self.vnf.get('inputs'))
347 wait_for_execution(cfy_client,
348 _get_deployment_environment_creation_execution(
349 cfy_client, descriptor.get('name')),
353 self.__logger.info("Start the VNF Instance deployment")
354 execution = cfy_client.executions.start(descriptor.get('name'),
357 execution = wait_for_execution(
358 cfy_client, execution, self.__logger, timeout=3600)
360 duration = time.time() - start_time
362 self.__logger.info(execution)
363 if execution.status == 'terminated':
364 self.details['vnf'].update(status='PASS', duration=duration)
365 self.result += 1/3 * 100
368 self.details['vnf'].update(status='FAIL', duration=duration)
373 """Run test on clearwater ims instance."""
374 start_time = time.time()
376 cfy_client = self.orchestrator['object']
378 outputs = cfy_client.deployments.outputs.get(
379 self.vnf['descriptor'].get('name'))['outputs']
380 dns_ip = outputs['dns_ip']
381 ellis_ip = outputs['ellis_ip']
382 self.config_ellis(ellis_ip)
387 vims_test_result = self.run_clearwater_live_test(
389 public_domain=self.vnf['inputs']["public_domain"])
390 duration = time.time() - start_time
391 short_result, nb_test = sig_test_format(vims_test_result)
392 self.__logger.info(short_result)
393 self.details['test_vnf'].update(result=short_result,
394 full_result=vims_test_result,
397 vnf_test_rate = short_result['passed'] / nb_test
398 # orchestrator + vnf + test_vnf
399 self.result += vnf_test_rate / 3 * 100
400 except ZeroDivisionError:
401 self.__logger.error("No test has been executed")
402 self.details['test_vnf'].update(status='FAIL')
408 """Clean created objects/functions."""
410 cfy_client = self.orchestrator['object']
411 dep_name = self.vnf['descriptor'].get('name')
412 # kill existing execution
413 self.__logger.info('Deleting the current deployment')
414 exec_list = cfy_client.executions.list(dep_name)
415 for execution in exec_list:
416 if execution['status'] == "started":
418 cfy_client.executions.cancel(execution['id'],
420 except Exception: # pylint: disable=broad-except
421 self.__logger.warn("Can't cancel the current exec")
423 execution = cfy_client.executions.start(
426 parameters=dict(ignore_failure=True),
429 wait_for_execution(cfy_client, execution, self.__logger)
430 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
431 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
432 except Exception: # pylint: disable=broad-except
433 self.__logger.exception("Some issue during the undeployment ..")
435 super(CloudifyIms, self).clean()
438 def run_blocking_ssh_command(ssh, cmd,
439 error_msg="Unable to run this command"):
440 """Command to run ssh command with the exit status."""
441 _, stdout, stderr = ssh.exec_command(cmd)
442 CloudifyIms.__logger.debug("SSH %s stdout: %s", cmd, stdout.read())
443 if stdout.channel.recv_exit_status() != 0:
444 CloudifyIms.__logger.error("SSH %s stderr: %s", cmd, stderr.read())
445 raise Exception(error_msg)
447 @energy.enable_recording
448 def run(self, **kwargs):
449 """Execute CloudifyIms test case."""
450 return super(CloudifyIms, self).run(**kwargs)
453 # ----------------------------------------------------------
457 # -----------------------------------------------------------
458 def get_config(parameter, file_path):
460 Get config parameter.
462 Returns the value of a given parameter in file.yaml
463 parameter must be given in string format with dots
464 Example: general.openstack.image_name
466 with open(file_path) as config_file:
467 file_yaml = yaml.safe_load(config_file)
470 for element in parameter.split("."):
471 value = value.get(element)
473 raise ValueError("The parameter %s is not defined in"
474 " reporting.yaml" % parameter)
478 def wait_for_execution(client, execution, logger, timeout=3600, ):
479 """Wait for a workflow execution on Cloudify Manager."""
480 # if execution already ended - return without waiting
481 if execution.status in Execution.END_STATES:
484 if timeout is not None:
485 deadline = time.time() + timeout
487 # Poll for execution status and execution logs, until execution ends
488 # and we receive an event of type in WORKFLOW_END_TYPES
492 execution_ended = False
494 event_list = client.events.list(
495 execution_id=execution.id,
499 sort='@timestamp').items
501 offset = offset + len(event_list)
502 for event in event_list:
503 logger.debug(event.get('message'))
505 if timeout is not None:
506 if time.time() > deadline:
508 'execution of operation {0} for deployment {1} '
509 'timed out'.format(execution.workflow_id,
510 execution.deployment_id))
512 # update the remaining timeout
513 timeout = deadline - time.time()
515 if not execution_ended:
516 execution = client.executions.get(execution.id)
517 execution_ended = execution.status in Execution.END_STATES
527 def _get_deployment_environment_creation_execution(client, deployment_id):
529 Get the execution id of a env preparation.
531 network, security group, fip, VM creation
533 executions = client.executions.list(deployment_id=deployment_id)
534 for execution in executions:
535 if execution.workflow_id == 'create_deployment_environment':
537 raise RuntimeError('Failed to get create_deployment_environment '
538 'workflow execution.'
539 'Available executions: {0}'.format(executions))
542 def sig_test_format(sig_test):
543 """Process the signaling result to have a short result."""
547 for data_test in sig_test:
548 if data_test['result'] == "Passed":
550 elif data_test['result'] == "Failed":
552 elif data_test['result'] == "Skipped":
554 short_sig_test_result = {}
555 short_sig_test_result['passed'] = nb_passed
556 short_sig_test_result['failures'] = nb_failures
557 short_sig_test_result['skipped'] = nb_skipped
558 nb_test = nb_passed + nb_skipped
559 return (short_sig_test_result, nb_test)