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")
74 self.created_object = []
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()
127 self.__logger.info("Additional pre-configuration steps")
129 self.util.set_credentials(self.snaps_creds)
132 self.__logger.info("Upload some OS images if it doesn't exist")
133 for image_name, image_file in self.images.iteritems():
134 self.__logger.info("image: %s, file: %s", image_name, image_file)
135 if image_file and image_name:
136 image_creator = OpenStackImage(
139 name=image_name, image_user='cloud',
140 img_format='qcow2', image_file=image_file))
141 image_creator.create()
142 self.created_object.append(image_creator)
144 def deploy_orchestrator(self):
145 # pylint: disable=too-many-locals,too-many-statements
147 Deploy Cloudify Manager.
148 network, security group, fip, VM creation
152 start_time = time.time()
153 self.__logger.info("Creating keypair ...")
154 kp_file = os.path.join(self.data_dir, "cloudify_vrouter.pem")
155 keypair_settings = KeypairConfig(name='cloudify_vrouter_kp',
156 private_filepath=kp_file)
157 keypair_creator = OpenStackKeypair(self.snaps_creds, keypair_settings)
158 keypair_creator.create()
159 self.created_object.append(keypair_creator)
161 self.__logger.info("Creating full network ...")
162 subnet_settings = SubnetConfig(
163 name='cloudify_vrouter_subnet-{}'.format(self.uuid),
164 cidr='10.67.79.0/24')
165 network_settings = NetworkConfig(
166 name='cloudify_vrouter_network-{}'.format(self.uuid),
167 subnet_settings=[subnet_settings])
168 network_creator = OpenStackNetwork(self.snaps_creds, network_settings)
169 network_creator.create()
170 self.created_object.append(network_creator)
171 ext_net_name = snaps_utils.get_ext_net_name(self.snaps_creds)
172 router_creator = OpenStackRouter(
175 name='cloudify_vrouter_router-{}'.format(self.uuid),
176 external_gateway=ext_net_name,
177 internal_subnets=[subnet_settings.name]))
178 router_creator.create()
179 self.created_object.append(router_creator)
181 # security group creation
182 self.__logger.info("Creating security group for cloudify manager vm")
185 SecurityGroupRuleConfig(
186 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
187 direction=Direction.ingress,
188 protocol=Protocol.tcp, port_range_min=1,
189 port_range_max=65535))
191 SecurityGroupRuleConfig(
192 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
193 direction=Direction.ingress,
194 protocol=Protocol.udp, port_range_min=1,
195 port_range_max=65535))
197 security_group_creator = OpenStackSecurityGroup(
200 name="sg-cloudify-manager-{}".format(self.uuid),
201 rule_settings=sg_rules))
203 security_group_creator.create()
204 self.created_object.append(security_group_creator)
206 # orchestrator VM flavor
207 self.__logger.info("Get or create flavor for cloudify manager vm ...")
209 flavor_settings = FlavorConfig(
210 name=self.orchestrator['requirements']['flavor']['name'],
211 ram=self.orchestrator['requirements']['flavor']['ram_min'],
214 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
215 flavor_creator.create()
216 self.created_object.append(flavor_creator)
217 image_settings = ImageConfig(
218 name=self.orchestrator['requirements']['os_image'],
222 port_settings = PortConfig(
223 name='cloudify_manager_port-{}'.format(self.uuid),
224 network_name=network_settings.name)
226 manager_settings = VmInstanceConfig(
227 name='cloudify_manager-{}'.format(self.uuid),
228 flavor=flavor_settings.name,
229 port_settings=[port_settings],
230 security_group_names=[
231 security_group_creator.sec_grp_settings.name],
232 floating_ip_settings=[FloatingIpConfig(
233 name='cloudify_manager_fip-{}'.format(self.uuid),
234 port_name=port_settings.name,
235 router_name=router_creator.router_settings.name)])
237 manager_creator = OpenStackVmInstance(
238 self.snaps_creds, manager_settings, image_settings,
241 self.__logger.info("Creating cloudify manager VM")
242 manager_creator.create()
243 self.created_object.append(manager_creator)
245 public_auth_url = keystone_utils.get_endpoint(
246 self.snaps_creds, 'identity')
248 self.__logger.info("Set creds for cloudify manager")
249 cfy_creds = dict(keystone_username=self.tenant_name,
250 keystone_password=self.tenant_name,
251 keystone_tenant_name=self.tenant_name,
252 keystone_url=public_auth_url)
254 cfy_client = CloudifyClient(host=manager_creator.get_floating_ip().ip,
257 tenant='default_tenant')
259 self.orchestrator['object'] = cfy_client
261 self.cfy_manager_ip = manager_creator.get_floating_ip().ip
263 self.__logger.info("Attemps running status of the Manager")
266 while str(cfy_status) != 'running' and retry:
268 cfy_status = cfy_client.manager.get_status()['status']
269 self.__logger.debug("The current manager status is %s",
271 except Exception: # pylint: disable=broad-except
272 self.__logger.warning("Cloudify Manager isn't " +
273 "up and running. Retrying ...")
277 if str(cfy_status) == 'running':
278 self.__logger.info("Cloudify Manager is up and running")
280 raise Exception("Cloudify Manager isn't up and running")
282 self.__logger.info("Put OpenStack creds in manager")
283 secrets_list = cfy_client.secrets.list()
284 for k, val in cfy_creds.iteritems():
285 if not any(d.get('key', None) == k for d in secrets_list):
286 cfy_client.secrets.create(k, val)
288 cfy_client.secrets.update(k, val)
290 duration = time.time() - start_time
292 self.__logger.info("Put private keypair in manager")
293 if manager_creator.vm_ssh_active(block=True):
294 ssh = manager_creator.ssh_client()
295 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
296 scp.put(kp_file, '~/')
297 cmd = "sudo cp ~/cloudify_vrouter.pem /etc/cloudify/"
298 run_blocking_ssh_command(ssh, cmd)
299 cmd = "sudo chmod 444 /etc/cloudify/cloudify_vrouter.pem"
300 run_blocking_ssh_command(ssh, cmd)
301 cmd = "sudo yum install -y gcc python-devel"
302 run_blocking_ssh_command(
303 ssh, cmd, "Unable to install packages on manager")
305 self.details['orchestrator'].update(status='PASS', duration=duration)
307 self.vnf['inputs'].update(dict(external_network_name=ext_net_name))
311 def deploy_vnf(self):
312 start_time = time.time()
314 self.__logger.info("Upload VNFD")
315 cfy_client = self.orchestrator['object']
316 descriptor = self.vnf['descriptor']
317 self.deployment_name = descriptor.get('name')
319 vrouter_blueprint_dir = os.path.join(self.data_dir,
320 self.util.blueprint_dir)
321 if not os.path.exists(vrouter_blueprint_dir):
322 Repo.clone_from(descriptor.get('url'),
323 vrouter_blueprint_dir,
324 branch=descriptor.get('version'))
326 cfy_client.blueprints.upload(vrouter_blueprint_dir +
327 self.util.blueprint_file_name,
328 descriptor.get('name'))
330 self.__logger.info("Get or create flavor for vrouter")
331 flavor_settings = FlavorConfig(
332 name=self.vnf['requirements']['flavor']['name'],
333 ram=self.vnf['requirements']['flavor']['ram_min'],
336 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
337 flavor = flavor_creator.create()
338 self.created_object.append(flavor_creator)
341 glance = glance_utils.glance_client(self.snaps_creds)
342 image = glance_utils.get_image(glance,
344 self.vnf['inputs'].update(dict(target_vnf_image_id=image.id))
345 self.vnf['inputs'].update(dict(reference_vnf_image_id=image.id))
348 self.vnf['inputs'].update(dict(target_vnf_flavor_id=flavor.id))
349 self.vnf['inputs'].update(dict(reference_vnf_flavor_id=flavor.id))
351 self.vnf['inputs'].update(dict(keystone_username=self.tenant_name))
352 self.vnf['inputs'].update(dict(keystone_password=self.tenant_name))
353 self.vnf['inputs'].update(dict(keystone_tenant_name=self.tenant_name))
354 self.vnf['inputs'].update(
355 dict(keystone_url=keystone_utils.get_endpoint(
356 self.snaps_creds, 'identity')))
358 self.__logger.info("Create VNF Instance")
359 cfy_client.deployments.create(descriptor.get('name'),
360 descriptor.get('name'),
361 self.vnf.get('inputs'))
363 wait_for_execution(cfy_client,
365 cfy_client, descriptor.get('name')),
369 self.__logger.info("Start the VNF Instance deployment")
370 execution = cfy_client.executions.start(descriptor.get('name'),
373 execution = wait_for_execution(cfy_client, execution, self.__logger)
375 duration = time.time() - start_time
377 self.__logger.info(execution)
378 if execution.status == 'terminated':
379 self.details['vnf'].update(status='PASS', duration=duration)
382 self.details['vnf'].update(status='FAIL', duration=duration)
387 cfy_client = self.orchestrator['object']
388 credentials = {"snaps_creds": self.snaps_creds,
389 "username": self.snaps_creds.username,
390 "password": self.snaps_creds.password,
391 "auth_url": self.snaps_creds.auth_url,
392 "tenant_name": self.snaps_creds.project_name}
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(status='PASS',
407 full_result=test_result_data,
410 self.details['test_vnf'].update(status='FAIL',
412 full_result=test_result_data,
419 cfy_client = self.orchestrator['object']
420 dep_name = self.vnf['descriptor'].get('name')
421 # kill existing execution
422 self.__logger.info('Deleting the current deployment')
423 exec_list = cfy_client.executions.list(dep_name)
424 for execution in exec_list:
425 if execution['status'] == "started":
427 cfy_client.executions.cancel(execution['id'],
429 except Exception: # pylint: disable=broad-except
430 self.__logger.warn("Can't cancel the current exec")
432 execution = cfy_client.executions.start(
435 parameters=dict(ignore_failure=True))
437 wait_for_execution(cfy_client, execution, self.__logger)
438 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
439 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
440 except Exception: # pylint: disable=broad-except
441 self.__logger.warn("Some issue during the undeployment ..")
442 self.__logger.warn("Tenant clean continue ..")
443 super(CloudifyVrouter, self).clean()
445 def get_vnf_info_list(self, target_vnf_name):
446 return self.util.get_vnf_info_list(
447 self.cfy_manager_ip, self.deployment_name, target_vnf_name)
450 def wait_for_execution(client, execution, logger, timeout=7200, ):
451 """Wait for a workflow execution on Cloudify Manager."""
452 # if execution already ended - return without waiting
453 if execution.status in Execution.END_STATES:
456 if timeout is not None:
457 deadline = time.time() + timeout
459 # Poll for execution status and execution logs, until execution ends
460 # and we receive an event of type in WORKFLOW_END_TYPES
464 execution_ended = False
466 event_list = client.events.list(
467 execution_id=execution.id,
471 sort='@timestamp').items
473 offset = offset + len(event_list)
474 for event in event_list:
475 logger.debug(event.get('message'))
477 if timeout is not None:
478 if time.time() > deadline:
480 'execution of operation {0} for deployment {1} '
481 'timed out'.format(execution.workflow_id,
482 execution.deployment_id))
484 # update the remaining timeout
485 timeout = deadline - time.time()
487 if not execution_ended:
488 execution = client.executions.get(execution.id)
489 execution_ended = execution.status in Execution.END_STATES
499 def get_execution_id(client, deployment_id):
501 Get the execution id of a env preparation.
502 network, security group, fip, VM creation
504 executions = client.executions.list(deployment_id=deployment_id)
505 for execution in executions:
506 if execution.workflow_id == 'create_deployment_environment':
508 raise RuntimeError('Failed to get create_deployment_environment '
509 'workflow execution.'
510 'Available executions: {0}'.format(executions))
513 def run_blocking_ssh_command(ssh, cmd, error_msg="Unable to run this command"):
514 """Command to run ssh command with the exit status."""
515 (_, stdout, _) = ssh.exec_command(cmd)
516 if stdout.channel.recv_exit_status() != 0:
517 raise Exception(error_msg)