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."""
19 from cloudify_rest_client import CloudifyClient
20 from cloudify_rest_client.executions import Execution
21 from scp import SCPClient
23 from functest.opnfv_tests.openstack.snaps import snaps_utils
24 import functest.opnfv_tests.vnf.router.vrouter_base as vrouter_base
25 from functest.opnfv_tests.vnf.router.utilvnf import Utilvnf
26 from functest.utils import config
27 from functest.utils import env
28 from functest.utils import functest_utils
32 from snaps.config.flavor import FlavorConfig
33 from snaps.config.image import ImageConfig
34 from snaps.config.keypair import KeypairConfig
35 from snaps.config.network import NetworkConfig, PortConfig, SubnetConfig
36 from snaps.config.router import RouterConfig
37 from snaps.config.security_group import (
38 Direction, Protocol, SecurityGroupConfig, SecurityGroupRuleConfig)
39 from snaps.config.user import UserConfig
40 from snaps.config.vm_inst import FloatingIpConfig, VmInstanceConfig
42 from snaps.openstack.create_flavor import OpenStackFlavor
43 from snaps.openstack.create_image import OpenStackImage
44 from snaps.openstack.create_instance import OpenStackVmInstance
45 from snaps.openstack.create_keypairs import OpenStackKeypair
46 from snaps.openstack.create_network import OpenStackNetwork
47 from snaps.openstack.create_security_group import OpenStackSecurityGroup
48 from snaps.openstack.create_router import OpenStackRouter
49 from snaps.openstack.create_user import OpenStackUser
51 import snaps.openstack.utils.glance_utils as glance_utils
52 from snaps.openstack.utils import keystone_utils
55 __author__ = "Shuya Nakama <shuya.nakama@okinawaopenlabs.org>"
58 class CloudifyVrouter(vrouter_base.VrouterOnBoardingBase):
59 # pylint: disable=too-many-instance-attributes
60 """vrouter testcase deployed with Cloudify Orchestrator."""
62 __logger = logging.getLogger(__name__)
65 def __init__(self, **kwargs):
66 if "case_name" not in kwargs:
67 kwargs["case_name"] = "vyos_vrouter"
68 super(CloudifyVrouter, self).__init__(**kwargs)
70 # Retrieve the configuration
72 self.config = getattr(
73 config.CONF, 'vnf_{}_config'.format(self.case_name))
75 raise Exception("VNF config file not found")
77 self.cfy_manager_ip = ''
78 self.deployment_name = ''
80 config_file = os.path.join(self.case_dir, self.config)
81 self.orchestrator = dict(
82 requirements=functest_utils.get_parameter_from_yaml(
83 "orchestrator.requirements", config_file),
85 self.details['orchestrator'] = dict(
86 name=functest_utils.get_parameter_from_yaml(
87 "orchestrator.name", config_file),
88 version=functest_utils.get_parameter_from_yaml(
89 "orchestrator.version", config_file),
93 self.__logger.debug("Orchestrator configuration %s", self.orchestrator)
94 self.__logger.debug("name = %s", self.name)
96 descriptor=functest_utils.get_parameter_from_yaml(
97 "vnf.descriptor", config_file),
98 inputs=functest_utils.get_parameter_from_yaml(
99 "vnf.inputs", config_file),
100 requirements=functest_utils.get_parameter_from_yaml(
101 "vnf.requirements", config_file)
103 self.details['vnf'] = dict(
104 descriptor_version=self.vnf['descriptor']['version'],
105 name=functest_utils.get_parameter_from_yaml(
106 "vnf.name", config_file),
107 version=functest_utils.get_parameter_from_yaml(
108 "vnf.version", config_file),
110 self.__logger.debug("VNF configuration: %s", self.vnf)
112 self.util = Utilvnf()
114 self.details['test_vnf'] = dict(
115 name=functest_utils.get_parameter_from_yaml(
116 "vnf_test_suite.name", config_file),
117 version=functest_utils.get_parameter_from_yaml(
118 "vnf_test_suite.version", config_file)
120 self.images = functest_utils.get_parameter_from_yaml(
121 "tenant_images", config_file)
122 self.__logger.info("Images needed for vrouter: %s", self.images)
125 def run_blocking_ssh_command(ssh, cmd,
126 error_msg="Unable to run this command"):
127 """Command to run ssh command with the exit status."""
128 (_, stdout, stderr) = ssh.exec_command(cmd)
129 CloudifyVrouter.__logger.debug("SSH %s stdout: %s", cmd, stdout.read())
130 if stdout.channel.recv_exit_status() != 0:
131 CloudifyVrouter.__logger.error(
132 "SSH %s stderr: %s", cmd, stderr.read())
133 raise Exception(error_msg)
136 super(CloudifyVrouter, self).prepare()
137 self.__logger.info("Additional pre-configuration steps")
138 self.util.set_credentials(self.snaps_creds)
139 self.__logger.info("Upload some OS images if it doesn't exist")
140 for image_name, image_file in self.images.iteritems():
141 self.__logger.info("image: %s, file: %s", image_name, image_file)
142 if image_file and image_name:
143 image_creator = OpenStackImage(
146 name=image_name, image_user='cloud',
147 img_format='qcow2', image_file=image_file))
148 image_creator.create()
149 self.created_object.append(image_creator)
151 def deploy_orchestrator(self):
152 # pylint: disable=too-many-locals,too-many-statements
154 Deploy Cloudify Manager.
155 network, security group, fip, VM creation
158 start_time = time.time()
159 self.__logger.info("Creating keypair ...")
160 kp_file = os.path.join(self.data_dir, "cloudify_vrouter.pem")
161 keypair_settings = KeypairConfig(
162 name='cloudify_vrouter_kp-{}'.format(self.uuid),
163 private_filepath=kp_file)
164 keypair_creator = OpenStackKeypair(self.snaps_creds, keypair_settings)
165 keypair_creator.create()
166 self.created_object.append(keypair_creator)
168 self.__logger.info("Creating full network ...")
169 subnet_settings = SubnetConfig(
170 name='cloudify_vrouter_subnet-{}'.format(self.uuid),
171 cidr='10.67.79.0/24',
172 dns_nameservers=[env.get('NAMESERVER')])
173 network_settings = NetworkConfig(
174 name='cloudify_vrouter_network-{}'.format(self.uuid),
175 subnet_settings=[subnet_settings])
176 network_creator = OpenStackNetwork(self.snaps_creds, network_settings)
177 network_creator.create()
178 self.created_object.append(network_creator)
179 ext_net_name = snaps_utils.get_ext_net_name(self.snaps_creds)
180 router_creator = OpenStackRouter(
183 name='cloudify_vrouter_router-{}'.format(self.uuid),
184 external_gateway=ext_net_name,
185 internal_subnets=[subnet_settings.name]))
186 router_creator.create()
187 self.created_object.append(router_creator)
189 # security group creation
190 self.__logger.info("Creating security group for cloudify manager vm")
193 SecurityGroupRuleConfig(
194 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
195 direction=Direction.ingress,
196 protocol=Protocol.tcp, port_range_min=1,
197 port_range_max=65535))
199 SecurityGroupRuleConfig(
200 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
201 direction=Direction.ingress,
202 protocol=Protocol.udp, port_range_min=1,
203 port_range_max=65535))
205 security_group_creator = OpenStackSecurityGroup(
208 name="sg-cloudify-manager-{}".format(self.uuid),
209 rule_settings=sg_rules))
211 security_group_creator.create()
212 self.created_object.append(security_group_creator)
214 # orchestrator VM flavor
215 self.__logger.info("Get or create flavor for cloudify manager vm ...")
217 flavor_settings = FlavorConfig(
218 name=self.orchestrator['requirements']['flavor']['name'],
219 ram=self.orchestrator['requirements']['flavor']['ram_min'],
221 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
222 flavor_creator.create()
223 self.created_object.append(flavor_creator)
224 image_settings = ImageConfig(
225 name=self.orchestrator['requirements']['os_image'],
226 image_user='centos', exists=True)
228 port_settings = PortConfig(
229 name='cloudify_manager_port-{}'.format(self.uuid),
230 network_name=network_settings.name)
232 manager_settings = VmInstanceConfig(
233 name='cloudify_manager-{}'.format(self.uuid),
234 flavor=flavor_settings.name,
235 port_settings=[port_settings],
236 security_group_names=[
237 security_group_creator.sec_grp_settings.name],
238 floating_ip_settings=[FloatingIpConfig(
239 name='cloudify_manager_fip-{}'.format(self.uuid),
240 port_name=port_settings.name,
241 router_name=router_creator.router_settings.name)])
243 manager_creator = OpenStackVmInstance(
244 self.snaps_creds, manager_settings, image_settings,
247 self.__logger.info("Creating cloudify manager VM")
248 manager_creator.create()
249 self.created_object.append(manager_creator)
251 cfy_client = CloudifyClient(
252 host=manager_creator.get_floating_ip().ip,
253 username='admin', password='admin', tenant='default_tenant')
255 self.orchestrator['object'] = cfy_client
257 self.cfy_manager_ip = manager_creator.get_floating_ip().ip
259 self.__logger.info("Attemps running status of the Manager")
262 while str(cfy_status) != 'running' and retry:
264 cfy_status = cfy_client.manager.get_status()['status']
266 "The current manager status is %s", cfy_status)
267 except Exception: # pylint: disable=broad-except
269 "Cloudify Manager isn't up and running. Retrying ...")
273 if str(cfy_status) == 'running':
274 self.__logger.info("Cloudify Manager is up and running")
276 raise Exception("Cloudify Manager isn't up and running")
278 duration = time.time() - start_time
280 self.__logger.info("Put private keypair in manager")
281 if manager_creator.vm_ssh_active(block=True):
282 ssh = manager_creator.ssh_client()
283 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
284 scp.put(kp_file, '~/')
285 cmd = "sudo cp ~/cloudify_vrouter.pem /etc/cloudify/"
286 self.run_blocking_ssh_command(ssh, cmd)
287 cmd = "sudo chmod 444 /etc/cloudify/cloudify_vrouter.pem"
288 self.run_blocking_ssh_command(ssh, cmd)
289 cmd = "sudo yum install -y gcc python-devel"
290 self.run_blocking_ssh_command(
291 ssh, cmd, "Unable to install packages on manager")
293 self.details['orchestrator'].update(status='PASS', duration=duration)
295 self.vnf['inputs'].update(dict(external_network_name=ext_net_name))
299 def deploy_vnf(self):
300 start_time = time.time()
302 self.__logger.info("Upload VNFD")
303 cfy_client = self.orchestrator['object']
304 descriptor = self.vnf['descriptor']
305 self.deployment_name = descriptor.get('name')
307 vrouter_blueprint_dir = os.path.join(
308 self.data_dir, self.util.blueprint_dir)
309 if not os.path.exists(vrouter_blueprint_dir):
311 descriptor.get('url'), vrouter_blueprint_dir,
312 branch=descriptor.get('version'))
314 cfy_client.blueprints.upload(
315 vrouter_blueprint_dir + self.util.blueprint_file_name,
316 descriptor.get('name'))
318 self.__logger.info("Get or create flavor for vrouter")
319 flavor_settings = FlavorConfig(
320 name=self.vnf['requirements']['flavor']['name'],
321 ram=self.vnf['requirements']['flavor']['ram_min'],
323 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
324 flavor = flavor_creator.create()
325 self.created_object.append(flavor_creator)
328 glance = glance_utils.glance_client(self.snaps_creds)
329 image = glance_utils.get_image(glance, "vyos1.1.7")
331 user_creator = OpenStackUser(
334 name='cloudify_network_bug-{}'.format(self.uuid),
335 password=str(uuid.uuid4()),
336 project_name=self.tenant_name,
337 domain=self.snaps_creds.user_domain_name,
338 roles={'_member_': self.tenant_name}))
339 user_creator.create()
340 self.created_object.append(user_creator)
341 snaps_creds = user_creator.get_os_creds(self.snaps_creds.project_name)
342 self.__logger.debug("snaps creds: %s", snaps_creds)
344 self.vnf['inputs'].update(dict(target_vnf_image_id=image.id))
345 self.vnf['inputs'].update(dict(reference_vnf_image_id=image.id))
346 self.vnf['inputs'].update(dict(target_vnf_flavor_id=flavor.id))
347 self.vnf['inputs'].update(dict(reference_vnf_flavor_id=flavor.id))
348 self.vnf['inputs'].update(dict(
349 keystone_username=snaps_creds.username))
350 self.vnf['inputs'].update(dict(
351 keystone_password=snaps_creds.password))
352 self.vnf['inputs'].update(dict(
353 keystone_tenant_name=snaps_creds.project_name))
354 self.vnf['inputs'].update(dict(
355 keystone_user_domain_name=snaps_creds.user_domain_name))
356 self.vnf['inputs'].update(dict(
357 keystone_project_domain_name=snaps_creds.project_domain_name))
358 self.vnf['inputs'].update(dict(
359 region=snaps_creds.region_name))
360 self.vnf['inputs'].update(dict(
361 keystone_url=keystone_utils.get_endpoint(
362 snaps_creds, 'identity')))
364 self.__logger.info("Create VNF Instance")
365 cfy_client.deployments.create(
366 descriptor.get('name'), descriptor.get('name'),
367 self.vnf.get('inputs'))
370 cfy_client, get_execution_id(cfy_client, descriptor.get('name')),
371 self.__logger, timeout=7200)
373 self.__logger.info("Start the VNF Instance deployment")
374 execution = cfy_client.executions.start(descriptor.get('name'),
377 execution = wait_for_execution(cfy_client, execution, self.__logger)
379 duration = time.time() - start_time
381 self.__logger.info(execution)
382 if execution.status == 'terminated':
383 self.details['vnf'].update(status='PASS', duration=duration)
386 self.details['vnf'].update(status='FAIL', duration=duration)
391 cfy_client = self.orchestrator['object']
392 credentials = {"snaps_creds": self.snaps_creds}
394 self.util_info = {"credentials": credentials,
396 "vnf_data_dir": self.util.vnf_data_dir}
398 start_time = time.time()
400 result, test_result_data = super(CloudifyVrouter, self).test_vnf()
402 duration = time.time() - start_time
405 self.details['test_vnf'].update(
406 status='PASS', result='OK', full_result=test_result_data,
409 self.details['test_vnf'].update(
410 status='FAIL', result='NG', full_result=test_result_data,
417 cfy_client = self.orchestrator['object']
418 dep_name = self.vnf['descriptor'].get('name')
419 # kill existing execution
420 self.__logger.info('Deleting the current deployment')
421 exec_list = cfy_client.executions.list(dep_name)
422 for execution in exec_list:
423 if execution['status'] == "started":
425 cfy_client.executions.cancel(
426 execution['id'], force=True)
427 except Exception: # pylint: disable=broad-except
428 self.__logger.warn("Can't cancel the current exec")
430 execution = cfy_client.executions.start(
431 dep_name, 'uninstall', parameters=dict(ignore_failure=True))
433 wait_for_execution(cfy_client, execution, self.__logger)
434 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
435 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
436 except Exception: # pylint: disable=broad-except
437 self.__logger.exception("Some issue during the undeployment ..")
439 super(CloudifyVrouter, self).clean()
441 def get_vnf_info_list(self, target_vnf_name):
442 return self.util.get_vnf_info_list(
443 self.cfy_manager_ip, self.deployment_name, target_vnf_name)
446 def wait_for_execution(client, execution, logger, timeout=7200, ):
447 """Wait for a workflow execution on Cloudify Manager."""
448 # if execution already ended - return without waiting
449 if execution.status in Execution.END_STATES:
452 if timeout is not None:
453 deadline = time.time() + timeout
455 # Poll for execution status and execution logs, until execution ends
456 # and we receive an event of type in WORKFLOW_END_TYPES
460 execution_ended = False
462 event_list = client.events.list(
463 execution_id=execution.id, _offset=offset, _size=batch_size,
464 include_logs=False, sort='@timestamp').items
466 offset = offset + len(event_list)
467 for event in event_list:
468 logger.debug(event.get('message'))
470 if timeout is not None:
471 if time.time() > deadline:
473 'execution of operation {0} for deployment {1} '
474 'timed out'.format(execution.workflow_id,
475 execution.deployment_id))
477 # update the remaining timeout
478 timeout = deadline - time.time()
480 if not execution_ended:
481 execution = client.executions.get(execution.id)
482 execution_ended = execution.status in Execution.END_STATES
492 def get_execution_id(client, deployment_id):
494 Get the execution id of a env preparation.
495 network, security group, fip, VM creation
497 executions = client.executions.list(deployment_id=deployment_id)
498 for execution in executions:
499 if execution.workflow_id == 'create_deployment_environment':
501 raise RuntimeError('Failed to get create_deployment_environment '
502 'workflow execution.'
503 'Available executions: {0}'.format(executions))