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.opnfv_tests.vnf.router.utilvnf import Utilvnf
24 from functest.utils.constants import CONST
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
46 from snaps.openstack.utils import keystone_utils
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)
118 self.__logger.info("Upload some OS images if it doesn't exist")
119 for image_name, image_file in self.images.iteritems():
120 self.__logger.info("image: %s, file: %s", image_name, image_file)
121 if image_file and image_name:
122 image_creator = OpenStackImage(
124 ImageConfig(name=image_name,
127 image_file=image_file))
128 image_creator.create()
129 self.created_object.append(image_creator)
131 def deploy_orchestrator(self):
133 Deploy Cloudify Manager.
134 network, security group, fip, VM creation
138 start_time = time.time()
139 self.__logger.info("Creating keypair ...")
140 kp_file = os.path.join(self.data_dir, "cloudify_vrouter.pem")
141 keypair_settings = KeypairConfig(name='cloudify_vrouter_kp',
142 private_filepath=kp_file)
143 keypair_creator = OpenStackKeypair(self.snaps_creds, keypair_settings)
144 keypair_creator.create()
145 self.created_object.append(keypair_creator)
147 self.__logger.info("Creating full network ...")
148 subnet_settings = SubnetConfig(name='cloudify_vrouter_subnet',
149 cidr='10.67.79.0/24')
150 network_settings = NetworkConfig(name='cloudify_vrouter_network',
151 subnet_settings=[subnet_settings])
152 network_creator = OpenStackNetwork(self.snaps_creds, network_settings)
153 network_creator.create()
154 self.created_object.append(network_creator)
155 ext_net_name = snaps_utils.get_ext_net_name(self.snaps_creds)
156 router_creator = OpenStackRouter(
159 name='cloudify_vrouter_router',
160 external_gateway=ext_net_name,
161 internal_subnets=[subnet_settings.name]))
162 router_creator.create()
163 self.created_object.append(router_creator)
165 # security group creation
166 self.__logger.info("Creating security group for cloudify manager vm")
169 SecurityGroupRuleConfig(sec_grp_name="sg-cloudify-manager",
170 direction=Direction.ingress,
171 protocol=Protocol.tcp, port_range_min=1,
172 port_range_max=65535))
174 SecurityGroupRuleConfig(sec_grp_name="sg-cloudify-manager",
175 direction=Direction.ingress,
176 protocol=Protocol.udp, port_range_min=1,
177 port_range_max=65535))
179 security_group_creator = OpenStackSecurityGroup(
182 name="sg-cloudify-manager",
183 rule_settings=sg_rules))
185 security_group_creator.create()
186 self.created_object.append(security_group_creator)
188 # orchestrator VM flavor
189 self.__logger.info("Get or create flavor for cloudify manager vm ...")
191 flavor_settings = FlavorConfig(
192 name=self.orchestrator['requirements']['flavor']['name'],
193 ram=self.orchestrator['requirements']['flavor']['ram_min'],
196 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
197 flavor_creator.create()
198 self.created_object.append(flavor_creator)
199 image_settings = ImageConfig(
200 name=self.orchestrator['requirements']['os_image'],
204 port_settings = PortConfig(name='cloudify_manager_port',
205 network_name=network_settings.name)
207 manager_settings = VmInstanceConfig(
208 name='cloudify_manager',
209 flavor=flavor_settings.name,
210 port_settings=[port_settings],
211 security_group_names=[
212 security_group_creator.sec_grp_settings.name],
213 floating_ip_settings=[FloatingIpConfig(
214 name='cloudify_manager_fip',
215 port_name=port_settings.name,
216 router_name=router_creator.router_settings.name)])
218 manager_creator = OpenStackVmInstance(self.snaps_creds,
223 self.__logger.info("Creating cloudify manager VM")
224 manager_creator.create()
225 self.created_object.append(manager_creator)
227 public_auth_url = keystone_utils.get_endpoint(
228 self.snaps_creds, 'identity')
230 self.__logger.info("Set creds for cloudify manager")
231 cfy_creds = dict(keystone_username=self.tenant_name,
232 keystone_password=self.tenant_name,
233 keystone_tenant_name=self.tenant_name,
234 keystone_url=public_auth_url)
236 cfy_client = CloudifyClient(host=manager_creator.get_floating_ip().ip,
239 tenant='default_tenant')
241 self.orchestrator['object'] = cfy_client
243 self.cfy_manager_ip = manager_creator.get_floating_ip().ip
245 self.__logger.info("Attemps running status of the Manager")
248 while str(cfy_status) != 'running' and retry:
250 cfy_status = cfy_client.manager.get_status()['status']
251 self.__logger.debug("The current manager status is %s",
253 except Exception: # pylint: disable=broad-except
254 self.__logger.warning("Cloudify Manager isn't " +
255 "up and running. Retrying ...")
259 if str(cfy_status) == 'running':
260 self.__logger.info("Cloudify Manager is up and running")
262 raise Exception("Cloudify Manager isn't up and running")
264 self.__logger.info("Put OpenStack creds in manager")
265 secrets_list = cfy_client.secrets.list()
266 for k, val in cfy_creds.iteritems():
267 if not any(d.get('key', None) == k for d in secrets_list):
268 cfy_client.secrets.create(k, val)
270 cfy_client.secrets.update(k, val)
272 duration = time.time() - start_time
274 self.__logger.info("Put private keypair in manager")
275 if manager_creator.vm_ssh_active(block=True):
276 ssh = manager_creator.ssh_client()
277 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
278 scp.put(kp_file, '~/')
279 cmd = "sudo cp ~/cloudify_vrouter.pem /etc/cloudify/"
280 run_blocking_ssh_command(ssh, cmd)
281 cmd = "sudo chmod 444 /etc/cloudify/cloudify_vrouter.pem"
282 run_blocking_ssh_command(ssh, cmd)
283 cmd = "sudo yum install -y gcc python-devel"
284 run_blocking_ssh_command(
285 ssh, cmd, "Unable to install packages on manager")
287 self.details['orchestrator'].update(status='PASS', duration=duration)
289 self.vnf['inputs'].update(dict(external_network_name=ext_net_name))
293 def deploy_vnf(self):
294 start_time = time.time()
296 self.__logger.info("Upload VNFD")
297 cfy_client = self.orchestrator['object']
298 descriptor = self.vnf['descriptor']
299 self.deployment_name = descriptor.get('name')
301 vrouter_blueprint_dir = os.path.join(self.data_dir,
302 self.util.blueprint_dir)
303 if not os.path.exists(vrouter_blueprint_dir):
304 Repo.clone_from(descriptor.get('url'),
305 vrouter_blueprint_dir,
306 branch=descriptor.get('version'))
308 cfy_client.blueprints.upload(vrouter_blueprint_dir +
309 self.util.blueprint_file_name,
310 descriptor.get('name'))
312 self.__logger.info("Get or create flavor for vrouter")
313 flavor_settings = FlavorConfig(
314 name=self.vnf['requirements']['flavor']['name'],
315 ram=self.vnf['requirements']['flavor']['ram_min'],
318 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
319 flavor = flavor_creator.create()
320 self.created_object.append(flavor_creator)
323 glance = glance_utils.glance_client(self.snaps_creds)
324 image = glance_utils.get_image(glance,
326 self.vnf['inputs'].update(dict(target_vnf_image_id=image.id))
327 self.vnf['inputs'].update(dict(reference_vnf_image_id=image.id))
330 self.vnf['inputs'].update(dict(target_vnf_flavor_id=flavor.id))
331 self.vnf['inputs'].update(dict(reference_vnf_flavor_id=flavor.id))
333 self.vnf['inputs'].update(dict(keystone_username=self.tenant_name))
334 self.vnf['inputs'].update(dict(keystone_password=self.tenant_name))
335 self.vnf['inputs'].update(dict(keystone_tenant_name=self.tenant_name))
336 self.vnf['inputs'].update(
337 dict(keystone_url=keystone_utils.get_endpoint(
338 self.snaps_creds, 'identity')))
340 self.__logger.info("Create VNF Instance")
341 cfy_client.deployments.create(descriptor.get('name'),
342 descriptor.get('name'),
343 self.vnf.get('inputs'))
345 wait_for_execution(cfy_client,
347 cfy_client, descriptor.get('name')),
351 self.__logger.info("Start the VNF Instance deployment")
352 execution = cfy_client.executions.start(descriptor.get('name'),
355 execution = wait_for_execution(cfy_client, execution, self.__logger)
357 duration = time.time() - start_time
359 self.__logger.info(execution)
360 if execution.status == 'terminated':
361 self.details['vnf'].update(status='PASS', duration=duration)
364 self.details['vnf'].update(status='FAIL', duration=duration)
369 cfy_client = self.orchestrator['object']
370 credentials = {"snaps_creds": self.snaps_creds,
371 "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)