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.constants import CONST
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 CONST, '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 super(CloudifyVrouter, self).prepare()
126 self.__logger.info("Additional pre-configuration steps")
127 self.util.set_credentials(self.snaps_creds)
128 self.__logger.info("Upload some OS images if it doesn't exist")
129 for image_name, image_file in self.images.iteritems():
130 self.__logger.info("image: %s, file: %s", image_name, image_file)
131 if image_file and image_name:
132 image_creator = OpenStackImage(
135 name=image_name, image_user='cloud',
136 img_format='qcow2', image_file=image_file))
137 image_creator.create()
138 self.created_object.append(image_creator)
140 def deploy_orchestrator(self):
141 # pylint: disable=too-many-locals,too-many-statements
143 Deploy Cloudify Manager.
144 network, security group, fip, VM creation
147 start_time = time.time()
148 self.__logger.info("Creating keypair ...")
149 kp_file = os.path.join(self.data_dir, "cloudify_vrouter.pem")
150 keypair_settings = KeypairConfig(
151 name='cloudify_vrouter_kp-{}'.format(self.uuid),
152 private_filepath=kp_file)
153 keypair_creator = OpenStackKeypair(self.snaps_creds, keypair_settings)
154 keypair_creator.create()
155 self.created_object.append(keypair_creator)
157 self.__logger.info("Creating full network ...")
158 subnet_settings = SubnetConfig(
159 name='cloudify_vrouter_subnet-{}'.format(self.uuid),
160 cidr='10.67.79.0/24')
161 network_settings = NetworkConfig(
162 name='cloudify_vrouter_network-{}'.format(self.uuid),
163 subnet_settings=[subnet_settings])
164 network_creator = OpenStackNetwork(self.snaps_creds, network_settings)
165 network_creator.create()
166 self.created_object.append(network_creator)
167 ext_net_name = snaps_utils.get_ext_net_name(self.snaps_creds)
168 router_creator = OpenStackRouter(
171 name='cloudify_vrouter_router-{}'.format(self.uuid),
172 external_gateway=ext_net_name,
173 internal_subnets=[subnet_settings.name]))
174 router_creator.create()
175 self.created_object.append(router_creator)
177 # security group creation
178 self.__logger.info("Creating security group for cloudify manager vm")
181 SecurityGroupRuleConfig(
182 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
183 direction=Direction.ingress,
184 protocol=Protocol.tcp, port_range_min=1,
185 port_range_max=65535))
187 SecurityGroupRuleConfig(
188 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
189 direction=Direction.ingress,
190 protocol=Protocol.udp, port_range_min=1,
191 port_range_max=65535))
193 security_group_creator = OpenStackSecurityGroup(
196 name="sg-cloudify-manager-{}".format(self.uuid),
197 rule_settings=sg_rules))
199 security_group_creator.create()
200 self.created_object.append(security_group_creator)
202 # orchestrator VM flavor
203 self.__logger.info("Get or create flavor for cloudify manager vm ...")
205 flavor_settings = FlavorConfig(
206 name=self.orchestrator['requirements']['flavor']['name'],
207 ram=self.orchestrator['requirements']['flavor']['ram_min'],
209 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
210 flavor_creator.create()
211 self.created_object.append(flavor_creator)
212 image_settings = ImageConfig(
213 name=self.orchestrator['requirements']['os_image'],
214 image_user='centos', exists=True)
216 port_settings = PortConfig(
217 name='cloudify_manager_port-{}'.format(self.uuid),
218 network_name=network_settings.name)
220 manager_settings = VmInstanceConfig(
221 name='cloudify_manager-{}'.format(self.uuid),
222 flavor=flavor_settings.name,
223 port_settings=[port_settings],
224 security_group_names=[
225 security_group_creator.sec_grp_settings.name],
226 floating_ip_settings=[FloatingIpConfig(
227 name='cloudify_manager_fip-{}'.format(self.uuid),
228 port_name=port_settings.name,
229 router_name=router_creator.router_settings.name)])
231 manager_creator = OpenStackVmInstance(
232 self.snaps_creds, manager_settings, image_settings,
235 self.__logger.info("Creating cloudify manager VM")
236 manager_creator.create()
237 self.created_object.append(manager_creator)
239 cfy_client = CloudifyClient(
240 host=manager_creator.get_floating_ip().ip,
241 username='admin', password='admin', tenant='default_tenant')
243 self.orchestrator['object'] = cfy_client
245 self.cfy_manager_ip = manager_creator.get_floating_ip().ip
247 self.__logger.info("Attemps running status of the Manager")
250 while str(cfy_status) != 'running' and retry:
252 cfy_status = cfy_client.manager.get_status()['status']
253 self.__logger.debug("The current manager status is %s",
255 except Exception: # pylint: disable=broad-except
256 self.__logger.exception(
257 "Cloudify Manager isn't up and running. Retrying ...")
261 if str(cfy_status) == 'running':
262 self.__logger.info("Cloudify Manager is up and running")
264 raise Exception("Cloudify Manager isn't up and running")
266 duration = time.time() - start_time
268 self.__logger.info("Put private keypair in manager")
269 if manager_creator.vm_ssh_active(block=True):
270 ssh = manager_creator.ssh_client()
271 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
272 scp.put(kp_file, '~/')
273 cmd = "sudo cp ~/cloudify_vrouter.pem /etc/cloudify/"
274 run_blocking_ssh_command(ssh, cmd)
275 cmd = "sudo chmod 444 /etc/cloudify/cloudify_vrouter.pem"
276 run_blocking_ssh_command(ssh, cmd)
277 cmd = "sudo yum install -y gcc python-devel"
278 run_blocking_ssh_command(
279 ssh, cmd, "Unable to install packages on manager")
281 self.details['orchestrator'].update(status='PASS', duration=duration)
283 self.vnf['inputs'].update(dict(external_network_name=ext_net_name))
287 def deploy_vnf(self):
288 start_time = time.time()
290 self.__logger.info("Upload VNFD")
291 cfy_client = self.orchestrator['object']
292 descriptor = self.vnf['descriptor']
293 self.deployment_name = descriptor.get('name')
295 vrouter_blueprint_dir = os.path.join(
296 self.data_dir, self.util.blueprint_dir)
297 if not os.path.exists(vrouter_blueprint_dir):
299 descriptor.get('url'), vrouter_blueprint_dir,
300 branch=descriptor.get('version'))
302 cfy_client.blueprints.upload(
303 vrouter_blueprint_dir + self.util.blueprint_file_name,
304 descriptor.get('name'))
306 self.__logger.info("Get or create flavor for vrouter")
307 flavor_settings = FlavorConfig(
308 name=self.vnf['requirements']['flavor']['name'],
309 ram=self.vnf['requirements']['flavor']['ram_min'],
311 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
312 flavor = flavor_creator.create()
313 self.created_object.append(flavor_creator)
316 glance = glance_utils.glance_client(self.snaps_creds)
317 image = glance_utils.get_image(glance, "vyos1.1.7")
319 user_creator = OpenStackUser(
322 name='cloudify_network_bug-{}'.format(self.uuid),
323 password=str(uuid.uuid4()),
324 roles={'_member_': self.tenant_name}))
325 user_creator.create()
326 self.created_object.append(user_creator)
327 snaps_creds = user_creator.get_os_creds(self.snaps_creds.project_name)
329 self.vnf['inputs'].update(dict(target_vnf_image_id=image.id))
330 self.vnf['inputs'].update(dict(reference_vnf_image_id=image.id))
331 self.vnf['inputs'].update(dict(target_vnf_flavor_id=flavor.id))
332 self.vnf['inputs'].update(dict(reference_vnf_flavor_id=flavor.id))
333 self.vnf['inputs'].update(dict(
334 keystone_username=snaps_creds.username))
335 self.vnf['inputs'].update(dict(
336 keystone_password=snaps_creds.password))
337 self.vnf['inputs'].update(dict(
338 keystone_tenant_name=snaps_creds.project_name))
339 self.vnf['inputs'].update(dict(
340 region=snaps_creds.region_name))
341 self.vnf['inputs'].update(dict(
342 keystone_url=keystone_utils.get_endpoint(
343 snaps_creds, 'identity')))
345 self.__logger.info("Create VNF Instance")
346 cfy_client.deployments.create(
347 descriptor.get('name'), descriptor.get('name'),
348 self.vnf.get('inputs'))
351 cfy_client, get_execution_id(cfy_client, descriptor.get('name')),
352 self.__logger, timeout=7200)
354 self.__logger.info("Start the VNF Instance deployment")
355 execution = cfy_client.executions.start(descriptor.get('name'),
358 execution = wait_for_execution(cfy_client, execution, self.__logger)
360 duration = time.time() - start_time
362 self.__logger.info(execution)
363 if execution.status == 'terminated':
364 self.details['vnf'].update(status='PASS', duration=duration)
367 self.details['vnf'].update(status='FAIL', duration=duration)
372 cfy_client = self.orchestrator['object']
373 credentials = {"snaps_creds": self.snaps_creds,
374 "username": self.snaps_creds.username,
375 "password": self.snaps_creds.password,
376 "auth_url": self.snaps_creds.auth_url,
377 "tenant_name": self.snaps_creds.project_name}
379 self.util_info = {"credentials": credentials,
381 "vnf_data_dir": self.util.vnf_data_dir}
383 start_time = time.time()
385 result, test_result_data = super(CloudifyVrouter, self).test_vnf()
387 duration = time.time() - start_time
390 self.details['test_vnf'].update(
391 status='PASS', result='OK', full_result=test_result_data,
394 self.details['test_vnf'].update(
395 status='FAIL', result='NG', full_result=test_result_data,
402 cfy_client = self.orchestrator['object']
403 dep_name = self.vnf['descriptor'].get('name')
404 # kill existing execution
405 self.__logger.info('Deleting the current deployment')
406 exec_list = cfy_client.executions.list(dep_name)
407 for execution in exec_list:
408 if execution['status'] == "started":
410 cfy_client.executions.cancel(
411 execution['id'], force=True)
412 except Exception: # pylint: disable=broad-except
413 self.__logger.warn("Can't cancel the current exec")
415 execution = cfy_client.executions.start(
416 dep_name, 'uninstall', parameters=dict(ignore_failure=True))
418 wait_for_execution(cfy_client, execution, self.__logger)
419 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
420 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
421 except Exception: # pylint: disable=broad-except
422 self.__logger.warn("Some issue during the undeployment ..")
423 self.__logger.warn("Tenant clean continue ..")
424 super(CloudifyVrouter, self).clean()
426 def get_vnf_info_list(self, target_vnf_name):
427 return self.util.get_vnf_info_list(
428 self.cfy_manager_ip, self.deployment_name, target_vnf_name)
431 def wait_for_execution(client, execution, logger, timeout=7200, ):
432 """Wait for a workflow execution on Cloudify Manager."""
433 # if execution already ended - return without waiting
434 if execution.status in Execution.END_STATES:
437 if timeout is not None:
438 deadline = time.time() + timeout
440 # Poll for execution status and execution logs, until execution ends
441 # and we receive an event of type in WORKFLOW_END_TYPES
445 execution_ended = False
447 event_list = client.events.list(
448 execution_id=execution.id, _offset=offset, _size=batch_size,
449 include_logs=False, sort='@timestamp').items
451 offset = offset + len(event_list)
452 for event in event_list:
453 logger.debug(event.get('message'))
455 if timeout is not None:
456 if time.time() > deadline:
458 'execution of operation {0} for deployment {1} '
459 'timed out'.format(execution.workflow_id,
460 execution.deployment_id))
462 # update the remaining timeout
463 timeout = deadline - time.time()
465 if not execution_ended:
466 execution = client.executions.get(execution.id)
467 execution_ended = execution.status in Execution.END_STATES
477 def get_execution_id(client, deployment_id):
479 Get the execution id of a env preparation.
480 network, security group, fip, VM creation
482 executions = client.executions.list(deployment_id=deployment_id)
483 for execution in executions:
484 if execution.workflow_id == 'create_deployment_environment':
486 raise RuntimeError('Failed to get create_deployment_environment '
487 'workflow execution.'
488 'Available executions: {0}'.format(executions))
491 def run_blocking_ssh_command(ssh, cmd, error_msg="Unable to run this command"):
492 """Command to run ssh command with the exit status."""
493 (_, stdout, _) = ssh.exec_command(cmd)
494 if stdout.channel.recv_exit_status() != 0:
495 raise Exception(error_msg)