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
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.user import UserConfig
38 from snaps.config.vm_inst import FloatingIpConfig, VmInstanceConfig
40 from snaps.openstack.create_flavor import OpenStackFlavor
41 from snaps.openstack.create_image import OpenStackImage
42 from snaps.openstack.create_instance import OpenStackVmInstance
43 from snaps.openstack.create_keypairs import OpenStackKeypair
44 from snaps.openstack.create_network import OpenStackNetwork
45 from snaps.openstack.create_security_group import OpenStackSecurityGroup
46 from snaps.openstack.create_router import OpenStackRouter
47 from snaps.openstack.create_user import OpenStackUser
49 import snaps.openstack.utils.glance_utils as glance_utils
50 from snaps.openstack.utils import keystone_utils
53 __author__ = "Shuya Nakama <shuya.nakama@okinawaopenlabs.org>"
56 class CloudifyVrouter(vrouter_base.VrouterOnBoardingBase):
57 # pylint: disable=too-many-instance-attributes
58 """vrouter testcase deployed with Cloudify Orchestrator."""
60 __logger = logging.getLogger(__name__)
63 def __init__(self, **kwargs):
64 if "case_name" not in kwargs:
65 kwargs["case_name"] = "vyos_vrouter"
66 super(CloudifyVrouter, self).__init__(**kwargs)
68 # Retrieve the configuration
70 self.config = getattr(
71 config.CONF, 'vnf_{}_config'.format(self.case_name))
73 raise Exception("VNF config file not found")
75 self.cfy_manager_ip = ''
76 self.deployment_name = ''
78 config_file = os.path.join(self.case_dir, self.config)
79 self.orchestrator = dict(
80 requirements=functest_utils.get_parameter_from_yaml(
81 "orchestrator.requirements", config_file),
83 self.details['orchestrator'] = dict(
84 name=functest_utils.get_parameter_from_yaml(
85 "orchestrator.name", config_file),
86 version=functest_utils.get_parameter_from_yaml(
87 "orchestrator.version", config_file),
91 self.__logger.debug("Orchestrator configuration %s", self.orchestrator)
92 self.__logger.debug("name = %s", self.name)
94 descriptor=functest_utils.get_parameter_from_yaml(
95 "vnf.descriptor", config_file),
96 inputs=functest_utils.get_parameter_from_yaml(
97 "vnf.inputs", config_file),
98 requirements=functest_utils.get_parameter_from_yaml(
99 "vnf.requirements", config_file)
101 self.details['vnf'] = dict(
102 descriptor_version=self.vnf['descriptor']['version'],
103 name=functest_utils.get_parameter_from_yaml(
104 "vnf.name", config_file),
105 version=functest_utils.get_parameter_from_yaml(
106 "vnf.version", config_file),
108 self.__logger.debug("VNF configuration: %s", self.vnf)
110 self.util = Utilvnf()
112 self.details['test_vnf'] = dict(
113 name=functest_utils.get_parameter_from_yaml(
114 "vnf_test_suite.name", config_file),
115 version=functest_utils.get_parameter_from_yaml(
116 "vnf_test_suite.version", config_file)
118 self.images = functest_utils.get_parameter_from_yaml(
119 "tenant_images", config_file)
120 self.__logger.info("Images needed for vrouter: %s", self.images)
123 def run_blocking_ssh_command(ssh, cmd,
124 error_msg="Unable to run this command"):
125 """Command to run ssh command with the exit status."""
126 (_, stdout, stderr) = ssh.exec_command(cmd)
127 CloudifyVrouter.__logger.debug("SSH %s stdout: %s", cmd, stdout.read())
128 if stdout.channel.recv_exit_status() != 0:
129 CloudifyVrouter.__logger.error(
130 "SSH %s stderr: %s", cmd, stderr.read())
131 raise Exception(error_msg)
134 super(CloudifyVrouter, self).prepare()
135 self.__logger.info("Additional pre-configuration steps")
136 self.util.set_credentials(self.snaps_creds)
138 def deploy_orchestrator(self):
139 # pylint: disable=too-many-locals,too-many-statements
141 Deploy Cloudify Manager.
142 network, security group, fip, VM creation
145 start_time = time.time()
147 # orchestrator VM flavor
148 self.__logger.info("Get or create flavor for cloudify manager vm ...")
149 flavor_settings = FlavorConfig(
151 self.orchestrator['requirements']['flavor']['name'],
153 ram=self.orchestrator['requirements']['flavor']['ram_min'],
155 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
156 flavor_creator.create()
157 self.created_object.append(flavor_creator)
159 user_creator = OpenStackUser(
162 name='cloudify_network_bug-{}'.format(self.uuid),
163 password=str(uuid.uuid4()),
164 project_name=self.tenant_name,
165 domain_name=self.snaps_creds.user_domain_name,
166 roles={'_member_': self.tenant_name}))
167 user_creator.create()
168 self.created_object.append(user_creator)
170 snaps_creds = user_creator.get_os_creds(self.snaps_creds.project_name)
171 self.__logger.debug("snaps creds: %s", snaps_creds)
173 self.__logger.info("Creating keypair ...")
174 kp_file = os.path.join(self.data_dir, "cloudify_vrouter.pem")
175 keypair_settings = KeypairConfig(
176 name='cloudify_vrouter_kp-{}'.format(self.uuid),
177 private_filepath=kp_file)
178 keypair_creator = OpenStackKeypair(snaps_creds, keypair_settings)
179 keypair_creator.create()
180 self.created_object.append(keypair_creator)
182 self.__logger.info("Upload some OS images if it doesn't exist")
183 for image_name, image_file in self.images.iteritems():
184 self.__logger.info("image: %s, file: %s", image_name, image_file)
185 if image_file and image_name:
186 image_creator = OpenStackImage(
189 name=image_name, image_user='cloud',
190 img_format='qcow2', image_file=image_file))
191 image_creator.create()
192 self.created_object.append(image_creator)
194 self.__logger.info("Creating full network ...")
195 subnet_settings = SubnetConfig(
196 name='cloudify_vrouter_subnet-{}'.format(self.uuid),
197 cidr='10.67.79.0/24',
198 dns_nameservers=[env.get('NAMESERVER')])
199 network_settings = NetworkConfig(
200 name='cloudify_vrouter_network-{}'.format(self.uuid),
201 subnet_settings=[subnet_settings])
202 network_creator = OpenStackNetwork(snaps_creds, network_settings)
203 network_creator.create()
204 self.created_object.append(network_creator)
205 ext_net_name = snaps_utils.get_ext_net_name(snaps_creds)
206 router_creator = OpenStackRouter(
209 name='cloudify_vrouter_router-{}'.format(self.uuid),
210 external_gateway=ext_net_name,
211 internal_subnets=[subnet_settings.name]))
212 router_creator.create()
213 self.created_object.append(router_creator)
215 # security group creation
216 self.__logger.info("Creating security group for cloudify manager vm")
219 SecurityGroupRuleConfig(
220 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
221 direction=Direction.ingress,
222 protocol=Protocol.tcp, port_range_min=1,
223 port_range_max=65535))
225 SecurityGroupRuleConfig(
226 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
227 direction=Direction.ingress,
228 protocol=Protocol.udp, port_range_min=1,
229 port_range_max=65535))
230 security_group_creator = OpenStackSecurityGroup(
233 name="sg-cloudify-manager-{}".format(self.uuid),
234 rule_settings=sg_rules))
235 security_group_creator.create()
236 self.created_object.append(security_group_creator)
238 image_settings = ImageConfig(
239 name=self.orchestrator['requirements']['os_image'],
240 image_user='centos', exists=True)
241 port_settings = PortConfig(
242 name='cloudify_manager_port-{}'.format(self.uuid),
243 network_name=network_settings.name)
244 manager_settings = VmInstanceConfig(
245 name='cloudify_manager-{}'.format(self.uuid),
246 flavor=flavor_settings.name,
247 port_settings=[port_settings],
248 security_group_names=[
249 security_group_creator.sec_grp_settings.name],
250 floating_ip_settings=[FloatingIpConfig(
251 name='cloudify_manager_fip-{}'.format(self.uuid),
252 port_name=port_settings.name,
253 router_name=router_creator.router_settings.name)])
254 manager_creator = OpenStackVmInstance(
255 snaps_creds, manager_settings, image_settings,
258 self.__logger.info("Creating cloudify manager VM")
259 manager_creator.create()
260 self.created_object.append(manager_creator)
262 cfy_client = CloudifyClient(
263 host=manager_creator.get_floating_ip().ip,
264 username='admin', password='admin', tenant='default_tenant')
266 self.orchestrator['object'] = cfy_client
268 self.cfy_manager_ip = manager_creator.get_floating_ip().ip
270 self.__logger.info("Attemps running status of the Manager")
271 for loop in range(10):
274 "status %s", cfy_client.manager.get_status())
275 cfy_status = cfy_client.manager.get_status()['status']
277 "The current manager status is %s", cfy_status)
278 if str(cfy_status) != 'running':
279 raise Exception("Cloudify Manager isn't up and running")
281 except Exception: # pylint: disable=broad-except
283 "try %s: Cloudify Manager isn't up and running", loop + 1)
286 self.logger.error("Cloudify Manager isn't up and running")
289 duration = time.time() - start_time
291 self.__logger.info("Put private keypair in manager")
292 if manager_creator.vm_ssh_active(block=True):
293 ssh = manager_creator.ssh_client()
294 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
295 scp.put(kp_file, '~/')
296 cmd = "sudo cp ~/cloudify_vrouter.pem /etc/cloudify/"
297 self.run_blocking_ssh_command(ssh, cmd)
298 cmd = "sudo chmod 444 /etc/cloudify/cloudify_vrouter.pem"
299 self.run_blocking_ssh_command(ssh, cmd)
300 cmd = "sudo yum install -y gcc python-devel"
301 self.run_blocking_ssh_command(
302 ssh, cmd, "Unable to install packages on manager")
304 self.__logger.error("Cannot connect to manager")
307 self.details['orchestrator'].update(status='PASS', duration=duration)
309 self.__logger.info("Get or create flavor for vrouter")
310 flavor_settings = FlavorConfig(
312 self.vnf['requirements']['flavor']['name'],
314 ram=self.vnf['requirements']['flavor']['ram_min'],
316 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
317 flavor = flavor_creator.create()
318 self.created_object.append(flavor_creator)
321 glance = glance_utils.glance_client(snaps_creds)
322 image = glance_utils.get_image(glance, "vyos1.1.7")
323 self.vnf['inputs'].update(dict(external_network_name=ext_net_name))
324 self.vnf['inputs'].update(dict(target_vnf_image_id=image.id))
325 self.vnf['inputs'].update(dict(reference_vnf_image_id=image.id))
326 self.vnf['inputs'].update(dict(target_vnf_flavor_id=flavor.id))
327 self.vnf['inputs'].update(dict(reference_vnf_flavor_id=flavor.id))
328 self.vnf['inputs'].update(dict(
329 keystone_username=snaps_creds.username))
330 self.vnf['inputs'].update(dict(
331 keystone_password=snaps_creds.password))
332 self.vnf['inputs'].update(dict(
333 keystone_tenant_name=snaps_creds.project_name))
334 self.vnf['inputs'].update(dict(
335 keystone_user_domain_name=snaps_creds.user_domain_name))
336 self.vnf['inputs'].update(dict(
337 keystone_project_domain_name=snaps_creds.project_domain_name))
338 self.vnf['inputs'].update(dict(
339 region=snaps_creds.region_name))
340 self.vnf['inputs'].update(dict(
341 keystone_url=keystone_utils.get_endpoint(
342 snaps_creds, 'identity')))
344 credentials = {"snaps_creds": snaps_creds}
345 self.util_info = {"credentials": credentials,
347 "vnf_data_dir": self.util.vnf_data_dir}
351 def deploy_vnf(self):
352 start_time = time.time()
354 self.__logger.info("Upload VNFD")
355 cfy_client = self.orchestrator['object']
356 descriptor = self.vnf['descriptor']
357 self.deployment_name = descriptor.get('name')
359 cfy_client.blueprints.upload(
360 descriptor.get('file_name'), descriptor.get('name'))
362 self.__logger.info("Create VNF Instance")
363 cfy_client.deployments.create(
364 descriptor.get('name'), descriptor.get('name'),
365 self.vnf.get('inputs'))
368 cfy_client, get_execution_id(cfy_client, descriptor.get('name')),
369 self.__logger, timeout=7200)
371 self.__logger.info("Start the VNF Instance deployment")
372 execution = cfy_client.executions.start(descriptor.get('name'),
375 execution = wait_for_execution(cfy_client, execution, self.__logger)
377 duration = time.time() - start_time
379 self.__logger.info(execution)
380 if execution.status == 'terminated':
381 self.details['vnf'].update(status='PASS', duration=duration)
384 self.details['vnf'].update(status='FAIL', duration=duration)
389 start_time = time.time()
390 result, test_result_data = super(CloudifyVrouter, self).test_vnf()
391 duration = time.time() - start_time
393 self.details['test_vnf'].update(
394 status='PASS', result='OK', full_result=test_result_data,
397 self.details['test_vnf'].update(
398 status='FAIL', result='NG', full_result=test_result_data,
404 cfy_client = self.orchestrator['object']
405 dep_name = self.vnf['descriptor'].get('name')
406 # kill existing execution
407 self.__logger.info('Deleting the current deployment')
408 exec_list = cfy_client.executions.list(dep_name)
409 for execution in exec_list:
410 if execution['status'] == "started":
412 cfy_client.executions.cancel(
413 execution['id'], force=True)
414 except Exception: # pylint: disable=broad-except
415 self.__logger.warn("Can't cancel the current exec")
417 execution = cfy_client.executions.start(
418 dep_name, 'uninstall', parameters=dict(ignore_failure=True))
420 wait_for_execution(cfy_client, execution, self.__logger)
421 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
422 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
423 except Exception: # pylint: disable=broad-except
424 self.__logger.exception("Some issue during the undeployment ..")
426 super(CloudifyVrouter, self).clean()
428 def get_vnf_info_list(self, target_vnf_name):
429 return self.util.get_vnf_info_list(
430 self.cfy_manager_ip, self.deployment_name, target_vnf_name)
433 def wait_for_execution(client, execution, logger, timeout=7200, ):
434 """Wait for a workflow execution on Cloudify Manager."""
435 # if execution already ended - return without waiting
436 if execution.status in Execution.END_STATES:
439 if timeout is not None:
440 deadline = time.time() + timeout
442 # Poll for execution status and execution logs, until execution ends
443 # and we receive an event of type in WORKFLOW_END_TYPES
447 execution_ended = False
449 event_list = client.events.list(
450 execution_id=execution.id, _offset=offset, _size=batch_size,
451 include_logs=True, sort='@timestamp').items
453 offset = offset + len(event_list)
454 for event in event_list:
455 logger.debug(event.get('message'))
457 if timeout is not None:
458 if time.time() > deadline:
460 'execution of operation {0} for deployment {1} '
461 'timed out'.format(execution.workflow_id,
462 execution.deployment_id))
464 # update the remaining timeout
465 timeout = deadline - time.time()
467 if not execution_ended:
468 execution = client.executions.get(execution.id)
469 execution_ended = execution.status in Execution.END_STATES
479 def get_execution_id(client, deployment_id):
481 Get the execution id of a env preparation.
482 network, security group, fip, VM creation
484 executions = client.executions.list(deployment_id=deployment_id)
485 for execution in executions:
486 if execution.workflow_id == 'create_deployment_environment':
488 raise RuntimeError('Failed to get create_deployment_environment '
489 'workflow execution.'
490 'Available executions: {0}'.format(executions))