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 # pylint: disable=missing-docstring
12 """vrouter testcase implementation."""
18 from cloudify_rest_client import CloudifyClient
19 from cloudify_rest_client.executions import Execution
20 from scp import SCPClient
22 from functest.opnfv_tests.openstack.snaps import snaps_utils
23 import functest.opnfv_tests.vnf.router.vrouter_base as vrouter_base
24 from functest.opnfv_tests.vnf.router.utilvnf import Utilvnf
25 from functest.utils.constants import CONST
26 from functest.utils import functest_utils
30 from snaps.config.flavor import FlavorConfig
31 from snaps.config.image import ImageConfig
32 from snaps.config.keypair import KeypairConfig
33 from snaps.config.network import NetworkConfig, PortConfig, SubnetConfig
34 from snaps.config.router import RouterConfig
35 from snaps.config.security_group import (
36 Direction, Protocol, SecurityGroupConfig, SecurityGroupRuleConfig)
37 from snaps.config.vm_inst import FloatingIpConfig, VmInstanceConfig
39 from snaps.openstack.create_flavor import OpenStackFlavor
40 from snaps.openstack.create_image import OpenStackImage
41 from snaps.openstack.create_instance import OpenStackVmInstance
42 from snaps.openstack.create_keypairs import OpenStackKeypair
43 from snaps.openstack.create_network import OpenStackNetwork
44 from snaps.openstack.create_security_group import OpenStackSecurityGroup
45 from snaps.openstack.create_router import OpenStackRouter
47 import snaps.openstack.utils.glance_utils as glance_utils
48 from snaps.openstack.utils import keystone_utils
51 __author__ = "Shuya Nakama <shuya.nakama@okinawaopenlabs.org>"
54 class CloudifyVrouter(vrouter_base.VrouterOnBoardingBase):
55 # pylint: disable=too-many-instance-attributes
56 """vrouter testcase deployed with Cloudify Orchestrator."""
58 __logger = logging.getLogger(__name__)
61 def __init__(self, **kwargs):
62 if "case_name" not in kwargs:
63 kwargs["case_name"] = "vyos_vrouter"
64 super(CloudifyVrouter, self).__init__(**kwargs)
66 # Retrieve the configuration
68 self.config = getattr(
69 CONST, 'vnf_{}_config'.format(self.case_name))
71 raise Exception("VNF config file not found")
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=functest_utils.get_parameter_from_yaml(
80 "orchestrator.requirements", config_file),
82 self.details['orchestrator'] = dict(
83 name=functest_utils.get_parameter_from_yaml(
84 "orchestrator.name", config_file),
85 version=functest_utils.get_parameter_from_yaml(
86 "orchestrator.version", config_file),
90 self.__logger.debug("Orchestrator configuration %s", self.orchestrator)
91 self.__logger.debug("name = %s", self.name)
93 descriptor=functest_utils.get_parameter_from_yaml(
94 "vnf.descriptor", config_file),
95 inputs=functest_utils.get_parameter_from_yaml(
96 "vnf.inputs", config_file),
97 requirements=functest_utils.get_parameter_from_yaml(
98 "vnf.requirements", config_file)
100 self.details['vnf'] = dict(
101 descriptor_version=self.vnf['descriptor']['version'],
102 name=functest_utils.get_parameter_from_yaml(
103 "vnf.name", config_file),
104 version=functest_utils.get_parameter_from_yaml(
105 "vnf.version", config_file),
107 self.__logger.debug("VNF configuration: %s", self.vnf)
109 self.util = Utilvnf()
111 self.details['test_vnf'] = dict(
112 name=functest_utils.get_parameter_from_yaml(
113 "vnf_test_suite.name", config_file),
114 version=functest_utils.get_parameter_from_yaml(
115 "vnf_test_suite.version", config_file)
117 self.images = functest_utils.get_parameter_from_yaml(
118 "tenant_images", config_file)
119 self.__logger.info("Images needed for vrouter: %s", self.images)
122 def run_blocking_ssh_command(ssh, cmd,
123 error_msg="Unable to run this command"):
124 """Command to run ssh command with the exit status."""
125 (_, stdout, stderr) = ssh.exec_command(cmd)
126 CloudifyVrouter.__logger.debug("SSH %s stdout: %s", cmd, stdout.read())
127 if stdout.channel.recv_exit_status() != 0:
128 CloudifyVrouter.__logger.error(
129 "SSH %s stderr: %s", cmd, stderr.read())
130 raise Exception(error_msg)
133 super(CloudifyVrouter, self).prepare()
134 self.__logger.info("Additional pre-configuration steps")
135 self.util.set_credentials(self.snaps_creds)
136 self.__logger.info("Upload some OS images if it doesn't exist")
137 for image_name, image_file in self.images.iteritems():
138 self.__logger.info("image: %s, file: %s", image_name, image_file)
139 if image_file and image_name:
140 image_creator = OpenStackImage(
143 name=image_name, image_user='cloud',
144 img_format='qcow2', image_file=image_file))
145 image_creator.create()
146 self.created_object.append(image_creator)
148 def deploy_orchestrator(self):
149 # pylint: disable=too-many-locals,too-many-statements
151 Deploy Cloudify Manager.
152 network, security group, fip, VM creation
155 start_time = time.time()
156 self.__logger.info("Creating keypair ...")
157 kp_file = os.path.join(self.data_dir, "cloudify_vrouter.pem")
158 keypair_settings = KeypairConfig(
159 name='cloudify_vrouter_kp-{}'.format(self.uuid),
160 private_filepath=kp_file)
161 keypair_creator = OpenStackKeypair(self.snaps_creds, keypair_settings)
162 keypair_creator.create()
163 self.created_object.append(keypair_creator)
165 self.__logger.info("Creating full network ...")
166 subnet_settings = SubnetConfig(
167 name='cloudify_vrouter_subnet-{}'.format(self.uuid),
168 cidr='10.67.79.0/24')
169 network_settings = NetworkConfig(
170 name='cloudify_vrouter_network-{}'.format(self.uuid),
171 subnet_settings=[subnet_settings])
172 network_creator = OpenStackNetwork(self.snaps_creds, network_settings)
173 network_creator.create()
174 self.created_object.append(network_creator)
175 ext_net_name = snaps_utils.get_ext_net_name(self.snaps_creds)
176 router_creator = OpenStackRouter(
179 name='cloudify_vrouter_router-{}'.format(self.uuid),
180 external_gateway=ext_net_name,
181 internal_subnets=[subnet_settings.name]))
182 router_creator.create()
183 self.created_object.append(router_creator)
185 # security group creation
186 self.__logger.info("Creating security group for cloudify manager vm")
189 SecurityGroupRuleConfig(
190 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
191 direction=Direction.ingress,
192 protocol=Protocol.tcp, port_range_min=1,
193 port_range_max=65535))
195 SecurityGroupRuleConfig(
196 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
197 direction=Direction.ingress,
198 protocol=Protocol.udp, port_range_min=1,
199 port_range_max=65535))
201 security_group_creator = OpenStackSecurityGroup(
204 name="sg-cloudify-manager-{}".format(self.uuid),
205 rule_settings=sg_rules))
207 security_group_creator.create()
208 self.created_object.append(security_group_creator)
210 # orchestrator VM flavor
211 self.__logger.info("Get or create flavor for cloudify manager vm ...")
213 flavor_settings = FlavorConfig(
214 name=self.orchestrator['requirements']['flavor']['name'],
215 ram=self.orchestrator['requirements']['flavor']['ram_min'],
217 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
218 flavor_creator.create()
219 self.created_object.append(flavor_creator)
220 image_settings = ImageConfig(
221 name=self.orchestrator['requirements']['os_image'],
222 image_user='centos', exists=True)
224 port_settings = PortConfig(
225 name='cloudify_manager_port-{}'.format(self.uuid),
226 network_name=network_settings.name)
228 manager_settings = VmInstanceConfig(
229 name='cloudify_manager-{}'.format(self.uuid),
230 flavor=flavor_settings.name,
231 port_settings=[port_settings],
232 security_group_names=[
233 security_group_creator.sec_grp_settings.name],
234 floating_ip_settings=[FloatingIpConfig(
235 name='cloudify_manager_fip-{}'.format(self.uuid),
236 port_name=port_settings.name,
237 router_name=router_creator.router_settings.name)])
239 manager_creator = OpenStackVmInstance(
240 self.snaps_creds, manager_settings, image_settings,
243 self.__logger.info("Creating cloudify manager VM")
244 manager_creator.create()
245 self.created_object.append(manager_creator)
247 cfy_client = CloudifyClient(
248 host=manager_creator.get_floating_ip().ip,
249 username='admin', password='admin', tenant='default_tenant')
251 self.orchestrator['object'] = cfy_client
253 self.cfy_manager_ip = manager_creator.get_floating_ip().ip
255 self.__logger.info("Attemps running status of the Manager")
258 while str(cfy_status) != 'running' and retry:
260 cfy_status = cfy_client.manager.get_status()['status']
261 self.__logger.debug("The current manager status is %s",
263 except Exception: # pylint: disable=broad-except
264 self.__logger.exception(
265 "Cloudify Manager isn't up and running. Retrying ...")
269 if str(cfy_status) == 'running':
270 self.__logger.info("Cloudify Manager is up and running")
272 raise Exception("Cloudify Manager isn't up and running")
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 self.run_blocking_ssh_command(ssh, cmd)
283 cmd = "sudo chmod 444 /etc/cloudify/cloudify_vrouter.pem"
284 self.run_blocking_ssh_command(ssh, cmd)
285 cmd = "sudo yum install -y gcc python-devel"
286 self.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(
304 self.data_dir, self.util.blueprint_dir)
305 if not os.path.exists(vrouter_blueprint_dir):
307 descriptor.get('url'), vrouter_blueprint_dir,
308 branch=descriptor.get('version'))
310 cfy_client.blueprints.upload(
311 vrouter_blueprint_dir + 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'],
319 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
320 flavor = flavor_creator.create()
321 self.created_object.append(flavor_creator)
324 glance = glance_utils.glance_client(self.snaps_creds)
325 image = glance_utils.get_image(glance, "vyos1.1.7")
327 self.vnf['inputs'].update(dict(target_vnf_image_id=image.id))
328 self.vnf['inputs'].update(dict(reference_vnf_image_id=image.id))
329 self.vnf['inputs'].update(dict(target_vnf_flavor_id=flavor.id))
330 self.vnf['inputs'].update(dict(reference_vnf_flavor_id=flavor.id))
331 self.vnf['inputs'].update(dict(
332 keystone_username=self.snaps_creds.username))
333 self.vnf['inputs'].update(dict(
334 keystone_password=self.snaps_creds.password))
335 self.vnf['inputs'].update(dict(
336 keystone_tenant_name=self.snaps_creds.project_name))
337 self.vnf['inputs'].update(dict(
338 region=self.snaps_creds.region_name))
339 self.vnf['inputs'].update(dict(
340 keystone_url=keystone_utils.get_endpoint(
341 self.snaps_creds, 'identity')))
343 self.__logger.info("Create VNF Instance")
344 cfy_client.deployments.create(
345 descriptor.get('name'), descriptor.get('name'),
346 self.vnf.get('inputs'))
349 cfy_client, get_execution_id(cfy_client, descriptor.get('name')),
350 self.__logger, timeout=7200)
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 = {"snaps_creds": self.snaps_creds,
372 "username": self.snaps_creds.username,
373 "password": self.snaps_creds.password,
374 "auth_url": self.snaps_creds.auth_url,
375 "tenant_name": self.snaps_creds.project_name}
377 self.util_info = {"credentials": credentials,
379 "vnf_data_dir": self.util.vnf_data_dir}
381 start_time = time.time()
383 result, test_result_data = super(CloudifyVrouter, self).test_vnf()
385 duration = time.time() - start_time
388 self.details['test_vnf'].update(
389 status='PASS', result='OK', full_result=test_result_data,
392 self.details['test_vnf'].update(
393 status='FAIL', result='NG', full_result=test_result_data,
400 cfy_client = self.orchestrator['object']
401 dep_name = self.vnf['descriptor'].get('name')
402 # kill existing execution
403 self.__logger.info('Deleting the current deployment')
404 exec_list = cfy_client.executions.list(dep_name)
405 for execution in exec_list:
406 if execution['status'] == "started":
408 cfy_client.executions.cancel(
409 execution['id'], force=True)
410 except Exception: # pylint: disable=broad-except
411 self.__logger.warn("Can't cancel the current exec")
413 execution = cfy_client.executions.start(
414 dep_name, 'uninstall', parameters=dict(ignore_failure=True))
416 wait_for_execution(cfy_client, execution, self.__logger)
417 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
418 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
419 except Exception: # pylint: disable=broad-except
420 self.__logger.warn("Some issue during the undeployment ..")
421 self.__logger.warn("Tenant clean continue ..")
422 super(CloudifyVrouter, self).clean()
424 def get_vnf_info_list(self, target_vnf_name):
425 return self.util.get_vnf_info_list(
426 self.cfy_manager_ip, self.deployment_name, target_vnf_name)
429 def wait_for_execution(client, execution, logger, timeout=7200, ):
430 """Wait for a workflow execution on Cloudify Manager."""
431 # if execution already ended - return without waiting
432 if execution.status in Execution.END_STATES:
435 if timeout is not None:
436 deadline = time.time() + timeout
438 # Poll for execution status and execution logs, until execution ends
439 # and we receive an event of type in WORKFLOW_END_TYPES
443 execution_ended = False
445 event_list = client.events.list(
446 execution_id=execution.id, _offset=offset, _size=batch_size,
447 include_logs=False, sort='@timestamp').items
449 offset = offset + len(event_list)
450 for event in event_list:
451 logger.debug(event.get('message'))
453 if timeout is not None:
454 if time.time() > deadline:
456 'execution of operation {0} for deployment {1} '
457 'timed out'.format(execution.workflow_id,
458 execution.deployment_id))
460 # update the remaining timeout
461 timeout = deadline - time.time()
463 if not execution_ended:
464 execution = client.executions.get(execution.id)
465 execution_ended = execution.status in Execution.END_STATES
475 def get_execution_id(client, deployment_id):
477 Get the execution id of a env preparation.
478 network, security group, fip, VM creation
480 executions = client.executions.list(deployment_id=deployment_id)
481 for execution in executions:
482 if execution.workflow_id == 'create_deployment_environment':
484 raise RuntimeError('Failed to get create_deployment_environment '
485 'workflow execution.'
486 'Available executions: {0}'.format(executions))