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 functest_utils
31 from snaps.config.flavor import FlavorConfig
32 from snaps.config.image import ImageConfig
33 from snaps.config.keypair import KeypairConfig
34 from snaps.config.network import NetworkConfig, PortConfig, SubnetConfig
35 from snaps.config.router import RouterConfig
36 from snaps.config.security_group import (
37 Direction, Protocol, SecurityGroupConfig, SecurityGroupRuleConfig)
38 from snaps.config.user import UserConfig
39 from snaps.config.vm_inst import FloatingIpConfig, VmInstanceConfig
41 from snaps.openstack.create_flavor import OpenStackFlavor
42 from snaps.openstack.create_image import OpenStackImage
43 from snaps.openstack.create_instance import OpenStackVmInstance
44 from snaps.openstack.create_keypairs import OpenStackKeypair
45 from snaps.openstack.create_network import OpenStackNetwork
46 from snaps.openstack.create_security_group import OpenStackSecurityGroup
47 from snaps.openstack.create_router import OpenStackRouter
48 from snaps.openstack.create_user import OpenStackUser
50 import snaps.openstack.utils.glance_utils as glance_utils
51 from snaps.openstack.utils import keystone_utils
54 __author__ = "Shuya Nakama <shuya.nakama@okinawaopenlabs.org>"
57 class CloudifyVrouter(vrouter_base.VrouterOnBoardingBase):
58 # pylint: disable=too-many-instance-attributes
59 """vrouter testcase deployed with Cloudify Orchestrator."""
61 __logger = logging.getLogger(__name__)
64 def __init__(self, **kwargs):
65 if "case_name" not in kwargs:
66 kwargs["case_name"] = "vyos_vrouter"
67 super(CloudifyVrouter, self).__init__(**kwargs)
69 # Retrieve the configuration
71 self.config = getattr(
72 config.CONF, 'vnf_{}_config'.format(self.case_name))
74 raise Exception("VNF config file not found")
76 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 network_settings = NetworkConfig(
173 name='cloudify_vrouter_network-{}'.format(self.uuid),
174 subnet_settings=[subnet_settings])
175 network_creator = OpenStackNetwork(self.snaps_creds, network_settings)
176 network_creator.create()
177 self.created_object.append(network_creator)
178 ext_net_name = snaps_utils.get_ext_net_name(self.snaps_creds)
179 router_creator = OpenStackRouter(
182 name='cloudify_vrouter_router-{}'.format(self.uuid),
183 external_gateway=ext_net_name,
184 internal_subnets=[subnet_settings.name]))
185 router_creator.create()
186 self.created_object.append(router_creator)
188 # security group creation
189 self.__logger.info("Creating security group for cloudify manager vm")
192 SecurityGroupRuleConfig(
193 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
194 direction=Direction.ingress,
195 protocol=Protocol.tcp, port_range_min=1,
196 port_range_max=65535))
198 SecurityGroupRuleConfig(
199 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
200 direction=Direction.ingress,
201 protocol=Protocol.udp, port_range_min=1,
202 port_range_max=65535))
204 security_group_creator = OpenStackSecurityGroup(
207 name="sg-cloudify-manager-{}".format(self.uuid),
208 rule_settings=sg_rules))
210 security_group_creator.create()
211 self.created_object.append(security_group_creator)
213 # orchestrator VM flavor
214 self.__logger.info("Get or create flavor for cloudify manager vm ...")
216 flavor_settings = FlavorConfig(
217 name=self.orchestrator['requirements']['flavor']['name'],
218 ram=self.orchestrator['requirements']['flavor']['ram_min'],
220 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
221 flavor_creator.create()
222 self.created_object.append(flavor_creator)
223 image_settings = ImageConfig(
224 name=self.orchestrator['requirements']['os_image'],
225 image_user='centos', exists=True)
227 port_settings = PortConfig(
228 name='cloudify_manager_port-{}'.format(self.uuid),
229 network_name=network_settings.name)
231 manager_settings = VmInstanceConfig(
232 name='cloudify_manager-{}'.format(self.uuid),
233 flavor=flavor_settings.name,
234 port_settings=[port_settings],
235 security_group_names=[
236 security_group_creator.sec_grp_settings.name],
237 floating_ip_settings=[FloatingIpConfig(
238 name='cloudify_manager_fip-{}'.format(self.uuid),
239 port_name=port_settings.name,
240 router_name=router_creator.router_settings.name)])
242 manager_creator = OpenStackVmInstance(
243 self.snaps_creds, manager_settings, image_settings,
246 self.__logger.info("Creating cloudify manager VM")
247 manager_creator.create()
248 self.created_object.append(manager_creator)
250 cfy_client = CloudifyClient(
251 host=manager_creator.get_floating_ip().ip,
252 username='admin', password='admin', tenant='default_tenant')
254 self.orchestrator['object'] = cfy_client
256 self.cfy_manager_ip = manager_creator.get_floating_ip().ip
258 self.__logger.info("Attemps running status of the Manager")
261 while str(cfy_status) != 'running' and retry:
263 cfy_status = cfy_client.manager.get_status()['status']
264 self.__logger.debug("The current manager status is %s",
266 except Exception: # pylint: disable=broad-except
267 self.__logger.exception(
268 "Cloudify Manager isn't up and running. Retrying ...")
272 if str(cfy_status) == 'running':
273 self.__logger.info("Cloudify Manager is up and running")
275 raise Exception("Cloudify Manager isn't up and running")
277 duration = time.time() - start_time
279 self.__logger.info("Put private keypair in manager")
280 if manager_creator.vm_ssh_active(block=True):
281 ssh = manager_creator.ssh_client()
282 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
283 scp.put(kp_file, '~/')
284 cmd = "sudo cp ~/cloudify_vrouter.pem /etc/cloudify/"
285 self.run_blocking_ssh_command(ssh, cmd)
286 cmd = "sudo chmod 444 /etc/cloudify/cloudify_vrouter.pem"
287 self.run_blocking_ssh_command(ssh, cmd)
288 cmd = "sudo yum install -y gcc python-devel"
289 self.run_blocking_ssh_command(
290 ssh, cmd, "Unable to install packages on manager")
292 self.details['orchestrator'].update(status='PASS', duration=duration)
294 self.vnf['inputs'].update(dict(external_network_name=ext_net_name))
298 def deploy_vnf(self):
299 start_time = time.time()
301 self.__logger.info("Upload VNFD")
302 cfy_client = self.orchestrator['object']
303 descriptor = self.vnf['descriptor']
304 self.deployment_name = descriptor.get('name')
306 vrouter_blueprint_dir = os.path.join(
307 self.data_dir, self.util.blueprint_dir)
308 if not os.path.exists(vrouter_blueprint_dir):
310 descriptor.get('url'), vrouter_blueprint_dir,
311 branch=descriptor.get('version'))
313 cfy_client.blueprints.upload(
314 vrouter_blueprint_dir + self.util.blueprint_file_name,
315 descriptor.get('name'))
317 self.__logger.info("Get or create flavor for vrouter")
318 flavor_settings = FlavorConfig(
319 name=self.vnf['requirements']['flavor']['name'],
320 ram=self.vnf['requirements']['flavor']['ram_min'],
322 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
323 flavor = flavor_creator.create()
324 self.created_object.append(flavor_creator)
327 glance = glance_utils.glance_client(self.snaps_creds)
328 image = glance_utils.get_image(glance, "vyos1.1.7")
330 user_creator = OpenStackUser(
333 name='cloudify_network_bug-{}'.format(self.uuid),
334 password=str(uuid.uuid4()),
335 roles={'_member_': self.tenant_name}))
336 user_creator.create()
337 self.created_object.append(user_creator)
338 snaps_creds = user_creator.get_os_creds(self.snaps_creds.project_name)
340 self.vnf['inputs'].update(dict(target_vnf_image_id=image.id))
341 self.vnf['inputs'].update(dict(reference_vnf_image_id=image.id))
342 self.vnf['inputs'].update(dict(target_vnf_flavor_id=flavor.id))
343 self.vnf['inputs'].update(dict(reference_vnf_flavor_id=flavor.id))
344 self.vnf['inputs'].update(dict(
345 keystone_username=snaps_creds.username))
346 self.vnf['inputs'].update(dict(
347 keystone_password=snaps_creds.password))
348 self.vnf['inputs'].update(dict(
349 keystone_tenant_name=snaps_creds.project_name))
350 self.vnf['inputs'].update(dict(
351 region=snaps_creds.region_name))
352 self.vnf['inputs'].update(dict(
353 keystone_url=keystone_utils.get_endpoint(
354 snaps_creds, 'identity')))
356 self.__logger.info("Create VNF Instance")
357 cfy_client.deployments.create(
358 descriptor.get('name'), descriptor.get('name'),
359 self.vnf.get('inputs'))
362 cfy_client, get_execution_id(cfy_client, descriptor.get('name')),
363 self.__logger, timeout=7200)
365 self.__logger.info("Start the VNF Instance deployment")
366 execution = cfy_client.executions.start(descriptor.get('name'),
369 execution = wait_for_execution(cfy_client, execution, self.__logger)
371 duration = time.time() - start_time
373 self.__logger.info(execution)
374 if execution.status == 'terminated':
375 self.details['vnf'].update(status='PASS', duration=duration)
378 self.details['vnf'].update(status='FAIL', duration=duration)
383 cfy_client = self.orchestrator['object']
384 credentials = {"snaps_creds": self.snaps_creds,
385 "username": self.snaps_creds.username,
386 "password": self.snaps_creds.password,
387 "auth_url": self.snaps_creds.auth_url,
388 "tenant_name": self.snaps_creds.project_name}
390 self.util_info = {"credentials": credentials,
392 "vnf_data_dir": self.util.vnf_data_dir}
394 start_time = time.time()
396 result, test_result_data = super(CloudifyVrouter, self).test_vnf()
398 duration = time.time() - start_time
401 self.details['test_vnf'].update(
402 status='PASS', result='OK', full_result=test_result_data,
405 self.details['test_vnf'].update(
406 status='FAIL', result='NG', full_result=test_result_data,
413 cfy_client = self.orchestrator['object']
414 dep_name = self.vnf['descriptor'].get('name')
415 # kill existing execution
416 self.__logger.info('Deleting the current deployment')
417 exec_list = cfy_client.executions.list(dep_name)
418 for execution in exec_list:
419 if execution['status'] == "started":
421 cfy_client.executions.cancel(
422 execution['id'], force=True)
423 except Exception: # pylint: disable=broad-except
424 self.__logger.warn("Can't cancel the current exec")
426 execution = cfy_client.executions.start(
427 dep_name, 'uninstall', parameters=dict(ignore_failure=True))
429 wait_for_execution(cfy_client, execution, self.__logger)
430 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
431 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
432 except Exception: # pylint: disable=broad-except
433 self.__logger.warn("Some issue during the undeployment ..")
434 self.__logger.warn("Tenant clean continue ..")
435 super(CloudifyVrouter, self).clean()
437 def get_vnf_info_list(self, target_vnf_name):
438 return self.util.get_vnf_info_list(
439 self.cfy_manager_ip, self.deployment_name, target_vnf_name)
442 def wait_for_execution(client, execution, logger, timeout=7200, ):
443 """Wait for a workflow execution on Cloudify Manager."""
444 # if execution already ended - return without waiting
445 if execution.status in Execution.END_STATES:
448 if timeout is not None:
449 deadline = time.time() + timeout
451 # Poll for execution status and execution logs, until execution ends
452 # and we receive an event of type in WORKFLOW_END_TYPES
456 execution_ended = False
458 event_list = client.events.list(
459 execution_id=execution.id, _offset=offset, _size=batch_size,
460 include_logs=False, sort='@timestamp').items
462 offset = offset + len(event_list)
463 for event in event_list:
464 logger.debug(event.get('message'))
466 if timeout is not None:
467 if time.time() > deadline:
469 'execution of operation {0} for deployment {1} '
470 'timed out'.format(execution.workflow_id,
471 execution.deployment_id))
473 # update the remaining timeout
474 timeout = deadline - time.time()
476 if not execution_ended:
477 execution = client.executions.get(execution.id)
478 execution_ended = execution.status in Execution.END_STATES
488 def get_execution_id(client, deployment_id):
490 Get the execution id of a env preparation.
491 network, security group, fip, VM creation
493 executions = client.executions.list(deployment_id=deployment_id)
494 for execution in executions:
495 if execution.workflow_id == 'create_deployment_environment':
497 raise RuntimeError('Failed to get create_deployment_environment '
498 'workflow execution.'
499 'Available executions: {0}'.format(executions))