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
32 from snaps.config.flavor import FlavorConfig
33 from snaps.config.image import ImageConfig
34 from snaps.config.keypair import KeypairConfig
35 from snaps.config.network import NetworkConfig, PortConfig, SubnetConfig
36 from snaps.config.router import RouterConfig
37 from snaps.config.security_group import (
38 Direction, Protocol, SecurityGroupConfig, SecurityGroupRuleConfig)
39 from snaps.config.user import UserConfig
40 from snaps.config.vm_inst import FloatingIpConfig, VmInstanceConfig
42 from snaps.openstack.create_flavor import OpenStackFlavor
43 from snaps.openstack.create_image import OpenStackImage
44 from snaps.openstack.create_instance import OpenStackVmInstance
45 from snaps.openstack.create_keypairs import OpenStackKeypair
46 from snaps.openstack.create_network import OpenStackNetwork
47 from snaps.openstack.create_security_group import OpenStackSecurityGroup
48 from snaps.openstack.create_router import OpenStackRouter
49 from snaps.openstack.create_user import OpenStackUser
51 import snaps.openstack.utils.glance_utils as glance_utils
52 from snaps.openstack.utils import keystone_utils
55 __author__ = "Shuya Nakama <shuya.nakama@okinawaopenlabs.org>"
58 class CloudifyVrouter(vrouter_base.VrouterOnBoardingBase):
59 # pylint: disable=too-many-instance-attributes
60 """vrouter testcase deployed with Cloudify Orchestrator."""
62 __logger = logging.getLogger(__name__)
65 def __init__(self, **kwargs):
66 if "case_name" not in kwargs:
67 kwargs["case_name"] = "vyos_vrouter"
68 super(CloudifyVrouter, self).__init__(**kwargs)
70 # Retrieve the configuration
72 self.config = getattr(
73 config.CONF, 'vnf_{}_config'.format(self.case_name))
75 raise Exception("VNF config file not found")
77 self.cfy_manager_ip = ''
79 self.deployment_name = ''
81 config_file = os.path.join(self.case_dir, self.config)
82 self.orchestrator = dict(
83 requirements=functest_utils.get_parameter_from_yaml(
84 "orchestrator.requirements", config_file),
86 self.details['orchestrator'] = dict(
87 name=functest_utils.get_parameter_from_yaml(
88 "orchestrator.name", config_file),
89 version=functest_utils.get_parameter_from_yaml(
90 "orchestrator.version", config_file),
94 self.__logger.debug("Orchestrator configuration %s", self.orchestrator)
95 self.__logger.debug("name = %s", self.name)
97 descriptor=functest_utils.get_parameter_from_yaml(
98 "vnf.descriptor", config_file),
99 inputs=functest_utils.get_parameter_from_yaml(
100 "vnf.inputs", config_file),
101 requirements=functest_utils.get_parameter_from_yaml(
102 "vnf.requirements", config_file)
104 self.details['vnf'] = dict(
105 descriptor_version=self.vnf['descriptor']['version'],
106 name=functest_utils.get_parameter_from_yaml(
107 "vnf.name", config_file),
108 version=functest_utils.get_parameter_from_yaml(
109 "vnf.version", config_file),
111 self.__logger.debug("VNF configuration: %s", self.vnf)
113 self.util = Utilvnf()
115 self.details['test_vnf'] = dict(
116 name=functest_utils.get_parameter_from_yaml(
117 "vnf_test_suite.name", config_file),
118 version=functest_utils.get_parameter_from_yaml(
119 "vnf_test_suite.version", config_file)
121 self.images = functest_utils.get_parameter_from_yaml(
122 "tenant_images", config_file)
123 self.__logger.info("Images needed for vrouter: %s", self.images)
126 def run_blocking_ssh_command(ssh, cmd,
127 error_msg="Unable to run this command"):
128 """Command to run ssh command with the exit status."""
129 (_, stdout, stderr) = ssh.exec_command(cmd)
130 CloudifyVrouter.__logger.debug("SSH %s stdout: %s", cmd, stdout.read())
131 if stdout.channel.recv_exit_status() != 0:
132 CloudifyVrouter.__logger.error(
133 "SSH %s stderr: %s", cmd, stderr.read())
134 raise Exception(error_msg)
137 super(CloudifyVrouter, self).prepare()
138 self.__logger.info("Additional pre-configuration steps")
139 self.util.set_credentials(self.snaps_creds)
140 self.__logger.info("Upload some OS images if it doesn't exist")
141 for image_name, image_file in self.images.iteritems():
142 self.__logger.info("image: %s, file: %s", image_name, image_file)
143 if image_file and image_name:
144 image_creator = OpenStackImage(
147 name=image_name, image_user='cloud',
148 img_format='qcow2', image_file=image_file))
149 image_creator.create()
150 self.created_object.append(image_creator)
152 def deploy_orchestrator(self):
153 # pylint: disable=too-many-locals,too-many-statements
155 Deploy Cloudify Manager.
156 network, security group, fip, VM creation
159 start_time = time.time()
160 self.__logger.info("Creating keypair ...")
161 kp_file = os.path.join(self.data_dir, "cloudify_vrouter.pem")
162 keypair_settings = KeypairConfig(
163 name='cloudify_vrouter_kp-{}'.format(self.uuid),
164 private_filepath=kp_file)
165 keypair_creator = OpenStackKeypair(self.snaps_creds, keypair_settings)
166 keypair_creator.create()
167 self.created_object.append(keypair_creator)
169 self.__logger.info("Creating full network ...")
170 subnet_settings = SubnetConfig(
171 name='cloudify_vrouter_subnet-{}'.format(self.uuid),
172 cidr='10.67.79.0/24',
173 dns_nameservers=[env.get('NAMESERVER')])
174 network_settings = NetworkConfig(
175 name='cloudify_vrouter_network-{}'.format(self.uuid),
176 subnet_settings=[subnet_settings])
177 network_creator = OpenStackNetwork(self.snaps_creds, network_settings)
178 network_creator.create()
179 self.created_object.append(network_creator)
180 ext_net_name = snaps_utils.get_ext_net_name(self.snaps_creds)
181 router_creator = OpenStackRouter(
184 name='cloudify_vrouter_router-{}'.format(self.uuid),
185 external_gateway=ext_net_name,
186 internal_subnets=[subnet_settings.name]))
187 router_creator.create()
188 self.created_object.append(router_creator)
190 # security group creation
191 self.__logger.info("Creating security group for cloudify manager vm")
194 SecurityGroupRuleConfig(
195 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
196 direction=Direction.ingress,
197 protocol=Protocol.tcp, port_range_min=1,
198 port_range_max=65535))
200 SecurityGroupRuleConfig(
201 sec_grp_name="sg-cloudify-manager-{}".format(self.uuid),
202 direction=Direction.ingress,
203 protocol=Protocol.udp, port_range_min=1,
204 port_range_max=65535))
206 security_group_creator = OpenStackSecurityGroup(
209 name="sg-cloudify-manager-{}".format(self.uuid),
210 rule_settings=sg_rules))
212 security_group_creator.create()
213 self.created_object.append(security_group_creator)
215 # orchestrator VM flavor
216 self.__logger.info("Get or create flavor for cloudify manager vm ...")
218 flavor_settings = FlavorConfig(
219 name=self.orchestrator['requirements']['flavor']['name'],
220 ram=self.orchestrator['requirements']['flavor']['ram_min'],
222 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
223 flavor_creator.create()
224 self.created_object.append(flavor_creator)
225 image_settings = ImageConfig(
226 name=self.orchestrator['requirements']['os_image'],
227 image_user='centos', exists=True)
229 port_settings = PortConfig(
230 name='cloudify_manager_port-{}'.format(self.uuid),
231 network_name=network_settings.name)
233 manager_settings = VmInstanceConfig(
234 name='cloudify_manager-{}'.format(self.uuid),
235 flavor=flavor_settings.name,
236 port_settings=[port_settings],
237 security_group_names=[
238 security_group_creator.sec_grp_settings.name],
239 floating_ip_settings=[FloatingIpConfig(
240 name='cloudify_manager_fip-{}'.format(self.uuid),
241 port_name=port_settings.name,
242 router_name=router_creator.router_settings.name)])
244 manager_creator = OpenStackVmInstance(
245 self.snaps_creds, manager_settings, image_settings,
248 self.__logger.info("Creating cloudify manager VM")
249 manager_creator.create()
250 self.created_object.append(manager_creator)
252 cfy_client = CloudifyClient(
253 host=manager_creator.get_floating_ip().ip,
254 username='admin', password='admin', tenant='default_tenant')
256 self.orchestrator['object'] = cfy_client
258 self.cfy_manager_ip = manager_creator.get_floating_ip().ip
260 self.__logger.info("Attemps running status of the Manager")
263 while str(cfy_status) != 'running' and retry:
265 cfy_status = cfy_client.manager.get_status()['status']
266 self.__logger.debug("The current manager status is %s",
268 except Exception: # pylint: disable=broad-except
269 self.__logger.exception(
270 "Cloudify Manager isn't up and running. Retrying ...")
274 if str(cfy_status) == 'running':
275 self.__logger.info("Cloudify Manager is up and running")
277 raise Exception("Cloudify Manager isn't up and running")
279 duration = time.time() - start_time
281 self.__logger.info("Put private keypair in manager")
282 if manager_creator.vm_ssh_active(block=True):
283 ssh = manager_creator.ssh_client()
284 scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
285 scp.put(kp_file, '~/')
286 cmd = "sudo cp ~/cloudify_vrouter.pem /etc/cloudify/"
287 self.run_blocking_ssh_command(ssh, cmd)
288 cmd = "sudo chmod 444 /etc/cloudify/cloudify_vrouter.pem"
289 self.run_blocking_ssh_command(ssh, cmd)
290 cmd = "sudo yum install -y gcc python-devel"
291 self.run_blocking_ssh_command(
292 ssh, cmd, "Unable to install packages on manager")
294 self.details['orchestrator'].update(status='PASS', duration=duration)
296 self.vnf['inputs'].update(dict(external_network_name=ext_net_name))
300 def deploy_vnf(self):
301 start_time = time.time()
303 self.__logger.info("Upload VNFD")
304 cfy_client = self.orchestrator['object']
305 descriptor = self.vnf['descriptor']
306 self.deployment_name = descriptor.get('name')
308 vrouter_blueprint_dir = os.path.join(
309 self.data_dir, self.util.blueprint_dir)
310 if not os.path.exists(vrouter_blueprint_dir):
312 descriptor.get('url'), vrouter_blueprint_dir,
313 branch=descriptor.get('version'))
315 cfy_client.blueprints.upload(
316 vrouter_blueprint_dir + self.util.blueprint_file_name,
317 descriptor.get('name'))
319 self.__logger.info("Get or create flavor for vrouter")
320 flavor_settings = FlavorConfig(
321 name=self.vnf['requirements']['flavor']['name'],
322 ram=self.vnf['requirements']['flavor']['ram_min'],
324 flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
325 flavor = flavor_creator.create()
326 self.created_object.append(flavor_creator)
329 glance = glance_utils.glance_client(self.snaps_creds)
330 image = glance_utils.get_image(glance, "vyos1.1.7")
332 user_creator = OpenStackUser(
335 name='cloudify_network_bug-{}'.format(self.uuid),
336 password=str(uuid.uuid4()),
337 project_name=self.tenant_name,
338 domain=self.snaps_creds.user_domain_name,
339 roles={'_member_': self.tenant_name}))
340 user_creator.create()
341 self.created_object.append(user_creator)
342 snaps_creds = user_creator.get_os_creds(self.snaps_creds.project_name)
343 self.__logger.debug("snaps creds: %s", snaps_creds)
345 self.vnf['inputs'].update(dict(target_vnf_image_id=image.id))
346 self.vnf['inputs'].update(dict(reference_vnf_image_id=image.id))
347 self.vnf['inputs'].update(dict(target_vnf_flavor_id=flavor.id))
348 self.vnf['inputs'].update(dict(reference_vnf_flavor_id=flavor.id))
349 self.vnf['inputs'].update(dict(
350 keystone_username=snaps_creds.username))
351 self.vnf['inputs'].update(dict(
352 keystone_password=snaps_creds.password))
353 self.vnf['inputs'].update(dict(
354 keystone_tenant_name=snaps_creds.project_name))
355 self.vnf['inputs'].update(dict(
356 region=snaps_creds.region_name))
357 self.vnf['inputs'].update(dict(
358 keystone_url=keystone_utils.get_endpoint(
359 snaps_creds, 'identity')))
361 self.__logger.info("Create VNF Instance")
362 cfy_client.deployments.create(
363 descriptor.get('name'), descriptor.get('name'),
364 self.vnf.get('inputs'))
367 cfy_client, get_execution_id(cfy_client, descriptor.get('name')),
368 self.__logger, timeout=7200)
370 self.__logger.info("Start the VNF Instance deployment")
371 execution = cfy_client.executions.start(descriptor.get('name'),
374 execution = wait_for_execution(cfy_client, execution, self.__logger)
376 duration = time.time() - start_time
378 self.__logger.info(execution)
379 if execution.status == 'terminated':
380 self.details['vnf'].update(status='PASS', duration=duration)
383 self.details['vnf'].update(status='FAIL', duration=duration)
388 cfy_client = self.orchestrator['object']
389 credentials = {"snaps_creds": self.snaps_creds,
390 "username": self.snaps_creds.username,
391 "password": self.snaps_creds.password,
392 "auth_url": self.snaps_creds.auth_url,
393 "tenant_name": self.snaps_creds.project_name}
395 self.util_info = {"credentials": credentials,
397 "vnf_data_dir": self.util.vnf_data_dir}
399 start_time = time.time()
401 result, test_result_data = super(CloudifyVrouter, self).test_vnf()
403 duration = time.time() - start_time
406 self.details['test_vnf'].update(
407 status='PASS', result='OK', full_result=test_result_data,
410 self.details['test_vnf'].update(
411 status='FAIL', result='NG', full_result=test_result_data,
418 cfy_client = self.orchestrator['object']
419 dep_name = self.vnf['descriptor'].get('name')
420 # kill existing execution
421 self.__logger.info('Deleting the current deployment')
422 exec_list = cfy_client.executions.list(dep_name)
423 for execution in exec_list:
424 if execution['status'] == "started":
426 cfy_client.executions.cancel(
427 execution['id'], force=True)
428 except Exception: # pylint: disable=broad-except
429 self.__logger.warn("Can't cancel the current exec")
431 execution = cfy_client.executions.start(
432 dep_name, 'uninstall', parameters=dict(ignore_failure=True))
434 wait_for_execution(cfy_client, execution, self.__logger)
435 cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
436 cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
437 except Exception: # pylint: disable=broad-except
438 self.__logger.warn("Some issue during the undeployment ..")
439 self.__logger.warn("Tenant clean continue ..")
440 super(CloudifyVrouter, self).clean()
442 def get_vnf_info_list(self, target_vnf_name):
443 return self.util.get_vnf_info_list(
444 self.cfy_manager_ip, self.deployment_name, target_vnf_name)
447 def wait_for_execution(client, execution, logger, timeout=7200, ):
448 """Wait for a workflow execution on Cloudify Manager."""
449 # if execution already ended - return without waiting
450 if execution.status in Execution.END_STATES:
453 if timeout is not None:
454 deadline = time.time() + timeout
456 # Poll for execution status and execution logs, until execution ends
457 # and we receive an event of type in WORKFLOW_END_TYPES
461 execution_ended = False
463 event_list = client.events.list(
464 execution_id=execution.id, _offset=offset, _size=batch_size,
465 include_logs=False, sort='@timestamp').items
467 offset = offset + len(event_list)
468 for event in event_list:
469 logger.debug(event.get('message'))
471 if timeout is not None:
472 if time.time() > deadline:
474 'execution of operation {0} for deployment {1} '
475 'timed out'.format(execution.workflow_id,
476 execution.deployment_id))
478 # update the remaining timeout
479 timeout = deadline - time.time()
481 if not execution_ended:
482 execution = client.executions.get(execution.id)
483 execution_ended = execution.status in Execution.END_STATES
493 def get_execution_id(client, deployment_id):
495 Get the execution id of a env preparation.
496 network, security group, fip, VM creation
498 executions = client.executions.list(deployment_id=deployment_id)
499 for execution in executions:
500 if execution.workflow_id == 'create_deployment_environment':
502 raise RuntimeError('Failed to get create_deployment_environment '
503 'workflow execution.'
504 'Available executions: {0}'.format(executions))