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 = CONST.__getattribute__(
69 '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 super(CloudifyVrouter, self).prepare()
123 self.__logger.info("Additional pre-configuration steps")
124 self.util.set_credentials(self.snaps_creds)
125 self.__logger.info("Upload some OS images if it doesn't exist")
126 for image_name, image_file in self.images.iteritems():
127 self.__logger.info("image: %s, file: %s", image_name, image_file)
128 if image_file and image_name:
129 image_creator = OpenStackImage(
132 name=image_name, image_user='cloud',
133 img_format='qcow2', image_file=image_file))
134 image_creator.create()
135 self.created_object.append(image_creator)
137 def deploy_orchestrator(self):
138 # pylint: disable=too-many-locals,too-many-statements
140 Deploy Cloudify Manager.
141 network, security group, fip, VM creation
144 start_time = time.time()
145 self.__logger.info("Creating keypair ...")
146 kp_file = os.path.join(self.data_dir, "cloudify_vrouter.pem")
147 keypair_settings = KeypairConfig(
148 name='cloudify_vrouter_kp-{}'.format(self.uuid),
149 private_filepath=kp_file)
150 keypair_creator = OpenStackKeypair(self.snaps_creds, keypair_settings)
151 keypair_creator.create()
152 self.created_object.append(keypair_creator)
154 self.__logger.info("Creating full network ...")
155 subnet_settings = SubnetConfig(
156 name='cloudify_vrouter_subnet-{}'.format(self.uuid),
157 cidr='10.67.79.0/24')
158 network_settings = NetworkConfig(
159 name='cloudify_vrouter_network-{}'.format(self.uuid),
160 subnet_settings=[subnet_settings])
161 network_creator = OpenStackNetwork(self.snaps_creds, network_settings)
162 network_creator.create()
163 self.created_object.append(network_creator)
164 ext_net_name = snaps_utils.get_ext_net_name(self.snaps_creds)
165 router_creator = OpenStackRouter(
168 name='cloudify_vrouter_router-{}'.format(self.uuid),
169 external_gateway=ext_net_name,
170 internal_subnets=[subnet_settings.name]))
171 router_creator.create()
172 self.created_object.append(router_creator)
174 # security group creation
175 self.__logger.info("Creating security group for cloudify manager vm")
178 SecurityGroupRuleConfig(
179 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
180 direction=Direction.ingress,
181 protocol=Protocol.tcp, port_range_min=1,
182 port_range_max=65535))
184 SecurityGroupRuleConfig(
185 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
186 direction=Direction.ingress,
187 protocol=Protocol.udp, port_range_min=1,
188 port_range_max=65535))
190 security_group_creator = OpenStackSecurityGroup(
193 name="sg-cloudify-manager-{}".format(self.uuid),
194 rule_settings=sg_rules))
196 security_group_creator.create()
197 self.created_object.append(security_group_creator)
199 # orchestrator VM flavor
200 self.__logger.info("Get or create flavor for cloudify manager vm ...")
202 flavor_settings = FlavorConfig(
203 name=self.orchestrator['requirements']['flavor']['name'],
204 ram=self.orchestrator['requirements']['flavor']['ram_min'],
206 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
207 flavor_creator.create()
208 self.created_object.append(flavor_creator)
209 image_settings = ImageConfig(
210 name=self.orchestrator['requirements']['os_image'],
211 image_user='centos', exists=True)
213 port_settings = PortConfig(
214 name='cloudify_manager_port-{}'.format(self.uuid),
215 network_name=network_settings.name)
217 manager_settings = VmInstanceConfig(
218 name='cloudify_manager-{}'.format(self.uuid),
219 flavor=flavor_settings.name,
220 port_settings=[port_settings],
221 security_group_names=[
222 security_group_creator.sec_grp_settings.name],
223 floating_ip_settings=[FloatingIpConfig(
224 name='cloudify_manager_fip-{}'.format(self.uuid),
225 port_name=port_settings.name,
226 router_name=router_creator.router_settings.name)])
228 manager_creator = OpenStackVmInstance(
229 self.snaps_creds, manager_settings, image_settings,
232 self.__logger.info("Creating cloudify manager VM")
233 manager_creator.create()
234 self.created_object.append(manager_creator)
236 cfy_client = CloudifyClient(
237 host=manager_creator.get_floating_ip().ip,
238 username='admin', password='admin', tenant='default_tenant')
240 self.orchestrator['object'] = cfy_client
242 self.cfy_manager_ip = manager_creator.get_floating_ip().ip
244 self.__logger.info("Attemps running status of the Manager")
247 while str(cfy_status) != 'running' and retry:
249 cfy_status = cfy_client.manager.get_status()['status']
250 self.__logger.debug("The current manager status is %s",
252 except Exception: # pylint: disable=broad-except
253 self.__logger.exception(
254 "Cloudify Manager isn't up and running. Retrying ...")
258 if str(cfy_status) == 'running':
259 self.__logger.info("Cloudify Manager is up and running")
261 raise Exception("Cloudify Manager isn't up and running")
263 duration = time.time() - start_time
265 self.__logger.info("Put private keypair in manager")
266 if manager_creator.vm_ssh_active(block=True):
267 ssh = manager_creator.ssh_client()
268 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
269 scp.put(kp_file, '~/')
270 cmd = "sudo cp ~/cloudify_vrouter.pem /etc/cloudify/"
271 run_blocking_ssh_command(ssh, cmd)
272 cmd = "sudo chmod 444 /etc/cloudify/cloudify_vrouter.pem"
273 run_blocking_ssh_command(ssh, cmd)
274 cmd = "sudo yum install -y gcc python-devel"
275 run_blocking_ssh_command(
276 ssh, cmd, "Unable to install packages on manager")
278 self.details['orchestrator'].update(status='PASS', duration=duration)
280 self.vnf['inputs'].update(dict(external_network_name=ext_net_name))
284 def deploy_vnf(self):
285 start_time = time.time()
287 self.__logger.info("Upload VNFD")
288 cfy_client = self.orchestrator['object']
289 descriptor = self.vnf['descriptor']
290 self.deployment_name = descriptor.get('name')
292 vrouter_blueprint_dir = os.path.join(
293 self.data_dir, self.util.blueprint_dir)
294 if not os.path.exists(vrouter_blueprint_dir):
296 descriptor.get('url'), vrouter_blueprint_dir,
297 branch=descriptor.get('version'))
299 cfy_client.blueprints.upload(
300 vrouter_blueprint_dir + self.util.blueprint_file_name,
301 descriptor.get('name'))
303 self.__logger.info("Get or create flavor for vrouter")
304 flavor_settings = FlavorConfig(
305 name=self.vnf['requirements']['flavor']['name'],
306 ram=self.vnf['requirements']['flavor']['ram_min'],
308 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
309 flavor = flavor_creator.create()
310 self.created_object.append(flavor_creator)
313 glance = glance_utils.glance_client(self.snaps_creds)
314 image = glance_utils.get_image(glance, "vyos1.1.7")
316 self.vnf['inputs'].update(dict(target_vnf_image_id=image.id))
317 self.vnf['inputs'].update(dict(reference_vnf_image_id=image.id))
318 self.vnf['inputs'].update(dict(target_vnf_flavor_id=flavor.id))
319 self.vnf['inputs'].update(dict(reference_vnf_flavor_id=flavor.id))
320 self.vnf['inputs'].update(dict(
321 keystone_username=self.snaps_creds.username))
322 self.vnf['inputs'].update(dict(
323 keystone_password=self.snaps_creds.password))
324 self.vnf['inputs'].update(dict(
325 keystone_tenant_name=self.snaps_creds.project_name))
326 self.vnf['inputs'].update(dict(
327 region=self.snaps_creds.region_name))
328 self.vnf['inputs'].update(dict(
329 keystone_url=keystone_utils.get_endpoint(
330 self.snaps_creds, 'identity')))
332 self.__logger.info("Create VNF Instance")
333 cfy_client.deployments.create(
334 descriptor.get('name'), descriptor.get('name'),
335 self.vnf.get('inputs'))
338 cfy_client, get_execution_id(cfy_client, descriptor.get('name')),
339 self.__logger, timeout=7200)
341 self.__logger.info("Start the VNF Instance deployment")
342 execution = cfy_client.executions.start(descriptor.get('name'),
345 execution = wait_for_execution(cfy_client, execution, self.__logger)
347 duration = time.time() - start_time
349 self.__logger.info(execution)
350 if execution.status == 'terminated':
351 self.details['vnf'].update(status='PASS', duration=duration)
354 self.details['vnf'].update(status='FAIL', duration=duration)
359 cfy_client = self.orchestrator['object']
360 credentials = {"snaps_creds": self.snaps_creds,
361 "username": self.snaps_creds.username,
362 "password": self.snaps_creds.password,
363 "auth_url": self.snaps_creds.auth_url,
364 "tenant_name": self.snaps_creds.project_name}
366 self.util_info = {"credentials": credentials,
368 "vnf_data_dir": self.util.vnf_data_dir}
370 start_time = time.time()
372 result, test_result_data = super(CloudifyVrouter, self).test_vnf()
374 duration = time.time() - start_time
377 self.details['test_vnf'].update(
378 status='PASS', result='OK', full_result=test_result_data,
381 self.details['test_vnf'].update(
382 status='FAIL', result='NG', full_result=test_result_data,
389 cfy_client = self.orchestrator['object']
390 dep_name = self.vnf['descriptor'].get('name')
391 # kill existing execution
392 self.__logger.info('Deleting the current deployment')
393 exec_list = cfy_client.executions.list(dep_name)
394 for execution in exec_list:
395 if execution['status'] == "started":
397 cfy_client.executions.cancel(
398 execution['id'], force=True)
399 except Exception: # pylint: disable=broad-except
400 self.__logger.warn("Can't cancel the current exec")
402 execution = cfy_client.executions.start(
403 dep_name, 'uninstall', parameters=dict(ignore_failure=True))
405 wait_for_execution(cfy_client, execution, self.__logger)
406 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
407 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
408 except Exception: # pylint: disable=broad-except
409 self.__logger.warn("Some issue during the undeployment ..")
410 self.__logger.warn("Tenant clean continue ..")
411 super(CloudifyVrouter, self).clean()
413 def get_vnf_info_list(self, target_vnf_name):
414 return self.util.get_vnf_info_list(
415 self.cfy_manager_ip, self.deployment_name, target_vnf_name)
418 def wait_for_execution(client, execution, logger, timeout=7200, ):
419 """Wait for a workflow execution on Cloudify Manager."""
420 # if execution already ended - return without waiting
421 if execution.status in Execution.END_STATES:
424 if timeout is not None:
425 deadline = time.time() + timeout
427 # Poll for execution status and execution logs, until execution ends
428 # and we receive an event of type in WORKFLOW_END_TYPES
432 execution_ended = False
434 event_list = client.events.list(
435 execution_id=execution.id, _offset=offset, _size=batch_size,
436 include_logs=False, sort='@timestamp').items
438 offset = offset + len(event_list)
439 for event in event_list:
440 logger.debug(event.get('message'))
442 if timeout is not None:
443 if time.time() > deadline:
445 'execution of operation {0} for deployment {1} '
446 'timed out'.format(execution.workflow_id,
447 execution.deployment_id))
449 # update the remaining timeout
450 timeout = deadline - time.time()
452 if not execution_ended:
453 execution = client.executions.get(execution.id)
454 execution_ended = execution.status in Execution.END_STATES
464 def get_execution_id(client, deployment_id):
466 Get the execution id of a env preparation.
467 network, security group, fip, VM creation
469 executions = client.executions.list(deployment_id=deployment_id)
470 for execution in executions:
471 if execution.workflow_id == 'create_deployment_environment':
473 raise RuntimeError('Failed to get create_deployment_environment '
474 'workflow execution.'
475 'Available executions: {0}'.format(executions))
478 def run_blocking_ssh_command(ssh, cmd, error_msg="Unable to run this command"):
479 """Command to run ssh command with the exit status."""
480 (_, stdout, _) = ssh.exec_command(cmd)
481 if stdout.channel.recv_exit_status() != 0:
482 raise Exception(error_msg)