3 # Copyright (c) 2017 Okinawa Open Laboratory 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 """vrouter testcase implementation."""
16 from cloudify_rest_client import CloudifyClient
17 from cloudify_rest_client.executions import Execution
18 from scp import SCPClient
21 from functest.opnfv_tests.openstack.snaps import snaps_utils
22 import functest.opnfv_tests.vnf.router.vrouter_base as vrouter_base
23 from functest.utils.constants import CONST
24 import functest.utils.openstack_utils as os_utils
28 from snaps.config.flavor import FlavorConfig
29 from snaps.config.image import ImageConfig
30 from snaps.config.keypair import KeypairConfig
31 from snaps.config.network import NetworkConfig, PortConfig, SubnetConfig
32 from snaps.config.router import RouterConfig
33 from snaps.config.security_group import (
34 Direction, Protocol, SecurityGroupConfig, SecurityGroupRuleConfig)
35 from snaps.config.vm_inst import FloatingIpConfig, VmInstanceConfig
37 from snaps.openstack.create_flavor import OpenStackFlavor
38 from snaps.openstack.create_image import OpenStackImage
39 from snaps.openstack.create_instance import OpenStackVmInstance
40 from snaps.openstack.create_keypairs import OpenStackKeypair
41 from snaps.openstack.create_network import OpenStackNetwork
42 from snaps.openstack.create_security_group import OpenStackSecurityGroup
43 from snaps.openstack.create_router import OpenStackRouter
45 import snaps.openstack.utils.glance_utils as glance_utils
47 from functest.opnfv_tests.vnf.router.utilvnf import Utilvnf
49 __author__ = "Shuya Nakama <shuya.nakama@okinawaopenlabs.org>"
52 class CloudifyVrouter(vrouter_base.VrouterOnBoardingBase):
53 """vrouter testcase deployed with Cloudify Orchestrator."""
55 __logger = logging.getLogger(__name__)
58 def __init__(self, **kwargs):
59 if "case_name" not in kwargs:
60 kwargs["case_name"] = "vyos_vrouter"
61 super(CloudifyVrouter, self).__init__(**kwargs)
63 # Retrieve the configuration
65 self.config = CONST.__getattribute__(
66 'vnf_{}_config'.format(self.case_name))
68 raise Exception("VNF config file not found")
71 self.created_object = []
73 self.cfy_manager_ip = ''
75 self.deployment_name = ''
77 config_file = os.path.join(self.case_dir, self.config)
78 self.orchestrator = dict(
79 requirements=get_config("orchestrator.requirements", config_file),
81 self.details['orchestrator'] = dict(
82 name=get_config("orchestrator.name", config_file),
83 version=get_config("orchestrator.version", config_file),
87 self.__logger.debug("Orchestrator configuration %s", self.orchestrator)
88 self.__logger.debug("name = %s", self.name)
90 descriptor=get_config("vnf.descriptor", config_file),
91 inputs=get_config("vnf.inputs", config_file),
92 requirements=get_config("vnf.requirements", config_file)
94 self.details['vnf'] = dict(
95 descriptor_version=self.vnf['descriptor']['version'],
96 name=get_config("vnf.name", config_file),
97 version=get_config("vnf.version", config_file),
99 self.__logger.debug("VNF configuration: %s", self.vnf)
101 self.util = Utilvnf()
103 self.details['test_vnf'] = dict(
104 name=get_config("vnf_test_suite.name", config_file),
105 version=get_config("vnf_test_suite.version", config_file)
107 self.images = get_config("tenant_images", config_file)
108 self.__logger.info("Images needed for vrouter: %s", self.images)
111 super(CloudifyVrouter, self).prepare()
113 self.__logger.info("Additional pre-configuration steps")
115 self.util.set_credentials(self.snaps_creds.username,
116 self.snaps_creds.password,
117 self.snaps_creds.auth_url,
118 self.snaps_creds.project_name)
121 self.__logger.info("Upload some OS images if it doesn't exist")
122 for image_name, image_file in self.images.iteritems():
123 self.__logger.info("image: %s, file: %s", image_name, image_file)
124 if image_file and image_name:
125 image_creator = OpenStackImage(
127 ImageConfig(name=image_name,
130 image_file=image_file))
131 image_creator.create()
132 self.created_object.append(image_creator)
134 def deploy_orchestrator(self):
136 Deploy Cloudify Manager.
137 network, security group, fip, VM creation
141 start_time = time.time()
142 self.__logger.info("Creating keypair ...")
143 kp_file = os.path.join(self.data_dir, "cloudify_vrouter.pem")
144 keypair_settings = KeypairConfig(name='cloudify_vrouter_kp',
145 private_filepath=kp_file)
146 keypair_creator = OpenStackKeypair(self.snaps_creds, keypair_settings)
147 keypair_creator.create()
148 self.created_object.append(keypair_creator)
150 self.__logger.info("Creating full network ...")
151 subnet_settings = SubnetConfig(name='cloudify_vrouter_subnet',
152 cidr='10.67.79.0/24')
153 network_settings = NetworkConfig(name='cloudify_vrouter_network',
154 subnet_settings=[subnet_settings])
155 network_creator = OpenStackNetwork(self.snaps_creds, network_settings)
156 network_creator.create()
157 self.created_object.append(network_creator)
158 ext_net_name = snaps_utils.get_ext_net_name(self.snaps_creds)
159 router_creator = OpenStackRouter(
162 name='cloudify_vrouter_router',
163 external_gateway=ext_net_name,
164 internal_subnets=[subnet_settings.name]))
165 router_creator.create()
166 self.created_object.append(router_creator)
168 # security group creation
169 self.__logger.info("Creating security group for cloudify manager vm")
172 SecurityGroupRuleConfig(sec_grp_name="sg-cloudify-manager",
173 direction=Direction.ingress,
174 protocol=Protocol.tcp, port_range_min=1,
175 port_range_max=65535))
177 SecurityGroupRuleConfig(sec_grp_name="sg-cloudify-manager",
178 direction=Direction.ingress,
179 protocol=Protocol.udp, port_range_min=1,
180 port_range_max=65535))
182 security_group_creator = OpenStackSecurityGroup(
185 name="sg-cloudify-manager",
186 rule_settings=sg_rules))
188 security_group_creator.create()
189 self.created_object.append(security_group_creator)
191 # orchestrator VM flavor
192 self.__logger.info("Get or create flavor for cloudify manager vm ...")
194 flavor_settings = FlavorConfig(
195 name=self.orchestrator['requirements']['flavor']['name'],
196 ram=self.orchestrator['requirements']['flavor']['ram_min'],
199 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
200 flavor_creator.create()
201 self.created_object.append(flavor_creator)
202 image_settings = ImageConfig(
203 name=self.orchestrator['requirements']['os_image'],
207 port_settings = PortConfig(name='cloudify_manager_port',
208 network_name=network_settings.name)
210 manager_settings = VmInstanceConfig(
211 name='cloudify_manager',
212 flavor=flavor_settings.name,
213 port_settings=[port_settings],
214 security_group_names=[
215 security_group_creator.sec_grp_settings.name],
216 floating_ip_settings=[FloatingIpConfig(
217 name='cloudify_manager_fip',
218 port_name=port_settings.name,
219 router_name=router_creator.router_settings.name)])
221 manager_creator = OpenStackVmInstance(self.snaps_creds,
226 self.__logger.info("Creating cloudify manager VM")
227 manager_creator.create()
228 self.created_object.append(manager_creator)
230 public_auth_url = os_utils.get_endpoint('identity')
232 self.__logger.info("Set creds for cloudify manager")
233 cfy_creds = dict(keystone_username=self.tenant_name,
234 keystone_password=self.tenant_name,
235 keystone_tenant_name=self.tenant_name,
236 keystone_url=public_auth_url)
238 cfy_client = CloudifyClient(host=manager_creator.get_floating_ip().ip,
241 tenant='default_tenant')
243 self.orchestrator['object'] = cfy_client
245 self.cfy_manager_ip = manager_creator.get_floating_ip().ip
247 self.__logger.info("Attemps running status of the Manager")
250 while str(cfy_status) != 'running' and retry:
252 cfy_status = cfy_client.manager.get_status()['status']
253 self.__logger.debug("The current manager status is %s",
255 except Exception: # pylint: disable=broad-except
256 self.__logger.warning("Cloudify Manager isn't " +
257 "up and running. Retrying ...")
261 if str(cfy_status) == 'running':
262 self.__logger.info("Cloudify Manager is up and running")
264 raise Exception("Cloudify Manager isn't up and running")
266 self.__logger.info("Put OpenStack creds in manager")
267 secrets_list = cfy_client.secrets.list()
268 for k, val in cfy_creds.iteritems():
269 if not any(d.get('key', None) == k for d in secrets_list):
270 cfy_client.secrets.create(k, val)
272 cfy_client.secrets.update(k, val)
274 duration = time.time() - start_time
276 self.__logger.info("Put private keypair in manager")
277 if manager_creator.vm_ssh_active(block=True):
278 ssh = manager_creator.ssh_client()
279 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
280 scp.put(kp_file, '~/')
281 cmd = "sudo cp ~/cloudify_vrouter.pem /etc/cloudify/"
282 run_blocking_ssh_command(ssh, cmd)
283 cmd = "sudo chmod 444 /etc/cloudify/cloudify_vrouter.pem"
284 run_blocking_ssh_command(ssh, cmd)
285 cmd = "sudo yum install -y gcc python-devel"
286 run_blocking_ssh_command(
287 ssh, cmd, "Unable to install packages on manager")
289 self.details['orchestrator'].update(status='PASS', duration=duration)
291 self.vnf['inputs'].update(dict(external_network_name=ext_net_name))
295 def deploy_vnf(self):
296 start_time = time.time()
298 self.__logger.info("Upload VNFD")
299 cfy_client = self.orchestrator['object']
300 descriptor = self.vnf['descriptor']
301 self.deployment_name = descriptor.get('name')
303 vrouter_blueprint_dir = os.path.join(self.data_dir,
304 self.util.blueprint_dir)
305 if not os.path.exists(vrouter_blueprint_dir):
306 Repo.clone_from(descriptor.get('url'),
307 vrouter_blueprint_dir,
308 branch=descriptor.get('version'))
310 cfy_client.blueprints.upload(vrouter_blueprint_dir +
311 self.util.blueprint_file_name,
312 descriptor.get('name'))
314 self.__logger.info("Get or create flavor for vrouter")
315 flavor_settings = FlavorConfig(
316 name=self.vnf['requirements']['flavor']['name'],
317 ram=self.vnf['requirements']['flavor']['ram_min'],
320 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
321 flavor = flavor_creator.create()
322 self.created_object.append(flavor_creator)
325 glance = glance_utils.glance_client(self.snaps_creds)
326 image = glance_utils.get_image(glance,
328 self.vnf['inputs'].update(dict(target_vnf_image_id=image.id))
329 self.vnf['inputs'].update(dict(reference_vnf_image_id=image.id))
332 self.vnf['inputs'].update(dict(target_vnf_flavor_id=flavor.id))
333 self.vnf['inputs'].update(dict(reference_vnf_flavor_id=flavor.id))
335 self.vnf['inputs'].update(dict(keystone_username=self.tenant_name))
336 self.vnf['inputs'].update(dict(keystone_password=self.tenant_name))
337 self.vnf['inputs'].update(dict(keystone_tenant_name=self.tenant_name))
338 self.vnf['inputs'].update(
339 dict(keystone_url=os_utils.get_endpoint('identity')))
341 self.__logger.info("Create VNF Instance")
342 cfy_client.deployments.create(descriptor.get('name'),
343 descriptor.get('name'),
344 self.vnf.get('inputs'))
346 wait_for_execution(cfy_client,
348 cfy_client, descriptor.get('name')),
352 self.__logger.info("Start the VNF Instance deployment")
353 execution = cfy_client.executions.start(descriptor.get('name'),
356 execution = wait_for_execution(cfy_client, execution, self.__logger)
358 duration = time.time() - start_time
360 self.__logger.info(execution)
361 if execution.status == 'terminated':
362 self.details['vnf'].update(status='PASS', duration=duration)
365 self.details['vnf'].update(status='FAIL', duration=duration)
370 cfy_client = self.orchestrator['object']
371 credentials = {"username": self.snaps_creds.username,
372 "password": self.snaps_creds.password,
373 "auth_url": self.snaps_creds.auth_url,
374 "tenant_name": self.snaps_creds.project_name}
376 self.util_info = {"credentials": credentials,
378 "vnf_data_dir": self.util.vnf_data_dir}
380 start_time = time.time()
382 result, test_result_data = super(CloudifyVrouter, self).test_vnf()
384 duration = time.time() - start_time
387 self.details['test_vnf'].update(status='PASS',
389 full_result=test_result_data,
392 self.details['test_vnf'].update(status='FAIL',
394 full_result=test_result_data,
401 cfy_client = self.orchestrator['object']
402 dep_name = self.vnf['descriptor'].get('name')
403 # kill existing execution
404 self.__logger.info('Deleting the current deployment')
405 exec_list = cfy_client.executions.list(dep_name)
406 for execution in exec_list:
407 if execution['status'] == "started":
409 cfy_client.executions.cancel(execution['id'],
411 except: # pylint: disable=broad-except
412 self.__logger.warn("Can't cancel the current exec")
414 execution = cfy_client.executions.start(
417 parameters=dict(ignore_failure=True))
419 wait_for_execution(cfy_client, execution, self.__logger)
420 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
421 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
422 except: # pylint: disable=broad-except
423 self.__logger.warn("Some issue during the undeployment ..")
424 self.__logger.warn("Tenant clean continue ..")
426 self.__logger.info('Remove the cloudify manager OS object ..')
427 for creator in reversed(self.created_object):
430 except Exception as exc:
431 self.logger.error('Unexpected error cleaning - %s', exc)
433 super(CloudifyVrouter, self).clean()
435 def run(self, **kwargs):
436 """Execute CloudifyVrouter test case."""
437 return super(CloudifyVrouter, self).run(**kwargs)
439 def get_vnf_info_list(self, target_vnf_name):
440 return self.util.get_vnf_info_list(self.cfy_manager_ip,
441 self.deployment_name,
445 # ----------------------------------------------------------
449 # -----------------------------------------------------------
450 def get_config(parameter, file_path):
452 Get config parameter.
453 Returns the value of a given parameter in file.yaml
454 parameter must be given in string format with dots
455 Example: general.openstack.image_name
457 with open(file_path) as config_file:
458 file_yaml = yaml.safe_load(config_file)
461 for element in parameter.split("."):
462 value = value.get(element)
464 raise ValueError("The parameter %s is not defined in"
465 " reporting.yaml" % parameter)
469 def wait_for_execution(client, execution, logger, timeout=7200, ):
470 """Wait for a workflow execution on Cloudify Manager."""
471 # if execution already ended - return without waiting
472 if execution.status in Execution.END_STATES:
475 if timeout is not None:
476 deadline = time.time() + timeout
478 # Poll for execution status and execution logs, until execution ends
479 # and we receive an event of type in WORKFLOW_END_TYPES
483 execution_ended = False
485 event_list = client.events.list(
486 execution_id=execution.id,
490 sort='@timestamp').items
492 offset = offset + len(event_list)
493 for event in event_list:
494 logger.debug(event.get('message'))
496 if timeout is not None:
497 if time.time() > deadline:
499 'execution of operation {0} for deployment {1} '
500 'timed out'.format(execution.workflow_id,
501 execution.deployment_id))
503 # update the remaining timeout
504 timeout = deadline - time.time()
506 if not execution_ended:
507 execution = client.executions.get(execution.id)
508 execution_ended = execution.status in Execution.END_STATES
518 def get_execution_id(client, deployment_id):
520 Get the execution id of a env preparation.
521 network, security group, fip, VM creation
523 executions = client.executions.list(deployment_id=deployment_id)
524 for execution in executions:
525 if execution.workflow_id == 'create_deployment_environment':
527 raise RuntimeError('Failed to get create_deployment_environment '
528 'workflow execution.'
529 'Available executions: {0}'.format(executions))
532 def run_blocking_ssh_command(ssh, cmd, error_msg="Unable to run this command"):
533 """Command to run ssh command with the exit status."""
534 (_, stdout, _) = ssh.exec_command(cmd)
535 if stdout.channel.recv_exit_status() != 0:
536 raise Exception(error_msg)