Fix last Pylint error in Functest
[functest.git] / functest / opnfv_tests / vnf / router / cloudify_vrouter.py
1 #!/usr/bin/env python
2
3 # Copyright (c) 2017 Okinawa Open Laboratory and others.
4 #
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
9
10 # pylint: disable=missing-docstring
11
12 """vrouter testcase implementation."""
13
14 import logging
15 import os
16 import time
17
18 from cloudify_rest_client import CloudifyClient
19 from cloudify_rest_client.executions import Execution
20 from scp import SCPClient
21 import yaml
22
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
28 from git import Repo
29
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
38
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
46
47 import snaps.openstack.utils.glance_utils as glance_utils
48 from snaps.openstack.utils import keystone_utils
49
50
51 __author__ = "Shuya Nakama <shuya.nakama@okinawaopenlabs.org>"
52
53
54 class CloudifyVrouter(vrouter_base.VrouterOnBoardingBase):
55     # pylint: disable=too-many-instance-attributes
56     """vrouter testcase deployed with Cloudify Orchestrator."""
57
58     __logger = logging.getLogger(__name__)
59     name = __name__
60
61     def __init__(self, **kwargs):
62         if "case_name" not in kwargs:
63             kwargs["case_name"] = "vyos_vrouter"
64         super(CloudifyVrouter, self).__init__(**kwargs)
65
66         # Retrieve the configuration
67         try:
68             self.config = CONST.__getattribute__(
69                 'vnf_{}_config'.format(self.case_name))
70         except Exception:
71             raise Exception("VNF config file not found")
72
73         self.snaps_creds = ''
74         self.created_object = []
75
76         self.cfy_manager_ip = ''
77         self.util_info = {}
78         self.deployment_name = ''
79
80         config_file = os.path.join(self.case_dir, self.config)
81         self.orchestrator = dict(
82             requirements=get_config("orchestrator.requirements", config_file),
83         )
84         self.details['orchestrator'] = dict(
85             name=get_config("orchestrator.name", config_file),
86             version=get_config("orchestrator.version", config_file),
87             status='ERROR',
88             result=''
89         )
90         self.__logger.debug("Orchestrator configuration %s", self.orchestrator)
91         self.__logger.debug("name = %s", self.name)
92         self.vnf = dict(
93             descriptor=get_config("vnf.descriptor", config_file),
94             inputs=get_config("vnf.inputs", config_file),
95             requirements=get_config("vnf.requirements", config_file)
96         )
97         self.details['vnf'] = dict(
98             descriptor_version=self.vnf['descriptor']['version'],
99             name=get_config("vnf.name", config_file),
100             version=get_config("vnf.version", config_file),
101         )
102         self.__logger.debug("VNF configuration: %s", self.vnf)
103
104         self.util = Utilvnf()
105
106         self.details['test_vnf'] = dict(
107             name=get_config("vnf_test_suite.name", config_file),
108             version=get_config("vnf_test_suite.version", config_file)
109         )
110         self.images = get_config("tenant_images", config_file)
111         self.__logger.info("Images needed for vrouter: %s", self.images)
112
113     def prepare(self):
114         super(CloudifyVrouter, self).prepare()
115
116         self.__logger.info("Additional pre-configuration steps")
117
118         self.util.set_credentials(self.snaps_creds)
119
120         # needs some images
121         self.__logger.info("Upload some OS images if it doesn't exist")
122         for image_name, image_file in self.images.iteritems():
123             self.__logger.info("image: %s, file: %s", image_name, image_file)
124             if image_file and image_name:
125                 image_creator = OpenStackImage(
126                     self.snaps_creds,
127                     ImageConfig(name=image_name,
128                                 image_user='cloud',
129                                 img_format='qcow2',
130                                 image_file=image_file))
131                 image_creator.create()
132                 self.created_object.append(image_creator)
133
134     def deploy_orchestrator(self):
135         # pylint: disable=too-many-locals,too-many-statements
136         """
137         Deploy Cloudify Manager.
138         network, security group, fip, VM creation
139         """
140         # network creation
141
142         start_time = time.time()
143         self.__logger.info("Creating keypair ...")
144         kp_file = os.path.join(self.data_dir, "cloudify_vrouter.pem")
145         keypair_settings = KeypairConfig(name='cloudify_vrouter_kp',
146                                          private_filepath=kp_file)
147         keypair_creator = OpenStackKeypair(self.snaps_creds, keypair_settings)
148         keypair_creator.create()
149         self.created_object.append(keypair_creator)
150
151         self.__logger.info("Creating full network ...")
152         subnet_settings = SubnetConfig(name='cloudify_vrouter_subnet',
153                                        cidr='10.67.79.0/24')
154         network_settings = NetworkConfig(name='cloudify_vrouter_network',
155                                          subnet_settings=[subnet_settings])
156         network_creator = OpenStackNetwork(self.snaps_creds, network_settings)
157         network_creator.create()
158         self.created_object.append(network_creator)
159         ext_net_name = snaps_utils.get_ext_net_name(self.snaps_creds)
160         router_creator = OpenStackRouter(
161             self.snaps_creds,
162             RouterConfig(
163                 name='cloudify_vrouter_router',
164                 external_gateway=ext_net_name,
165                 internal_subnets=[subnet_settings.name]))
166         router_creator.create()
167         self.created_object.append(router_creator)
168
169         # security group creation
170         self.__logger.info("Creating security group for cloudify manager vm")
171         sg_rules = list()
172         sg_rules.append(
173             SecurityGroupRuleConfig(sec_grp_name="sg-cloudify-manager",
174                                     direction=Direction.ingress,
175                                     protocol=Protocol.tcp, port_range_min=1,
176                                     port_range_max=65535))
177         sg_rules.append(
178             SecurityGroupRuleConfig(sec_grp_name="sg-cloudify-manager",
179                                     direction=Direction.ingress,
180                                     protocol=Protocol.udp, port_range_min=1,
181                                     port_range_max=65535))
182
183         security_group_creator = OpenStackSecurityGroup(
184             self.snaps_creds,
185             SecurityGroupConfig(
186                 name="sg-cloudify-manager",
187                 rule_settings=sg_rules))
188
189         security_group_creator.create()
190         self.created_object.append(security_group_creator)
191
192         # orchestrator VM flavor
193         self.__logger.info("Get or create flavor for cloudify manager vm ...")
194
195         flavor_settings = FlavorConfig(
196             name=self.orchestrator['requirements']['flavor']['name'],
197             ram=self.orchestrator['requirements']['flavor']['ram_min'],
198             disk=50,
199             vcpus=2)
200         flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
201         flavor_creator.create()
202         self.created_object.append(flavor_creator)
203         image_settings = ImageConfig(
204             name=self.orchestrator['requirements']['os_image'],
205             image_user='centos',
206             exists=True)
207
208         port_settings = PortConfig(name='cloudify_manager_port',
209                                    network_name=network_settings.name)
210
211         manager_settings = VmInstanceConfig(
212             name='cloudify_manager',
213             flavor=flavor_settings.name,
214             port_settings=[port_settings],
215             security_group_names=[
216                 security_group_creator.sec_grp_settings.name],
217             floating_ip_settings=[FloatingIpConfig(
218                 name='cloudify_manager_fip',
219                 port_name=port_settings.name,
220                 router_name=router_creator.router_settings.name)])
221
222         manager_creator = OpenStackVmInstance(self.snaps_creds,
223                                               manager_settings,
224                                               image_settings,
225                                               keypair_settings)
226
227         self.__logger.info("Creating cloudify manager VM")
228         manager_creator.create()
229         self.created_object.append(manager_creator)
230
231         public_auth_url = keystone_utils.get_endpoint(
232             self.snaps_creds, 'identity')
233
234         self.__logger.info("Set creds for cloudify manager")
235         cfy_creds = dict(keystone_username=self.tenant_name,
236                          keystone_password=self.tenant_name,
237                          keystone_tenant_name=self.tenant_name,
238                          keystone_url=public_auth_url)
239
240         cfy_client = CloudifyClient(host=manager_creator.get_floating_ip().ip,
241                                     username='admin',
242                                     password='admin',
243                                     tenant='default_tenant')
244
245         self.orchestrator['object'] = cfy_client
246
247         self.cfy_manager_ip = manager_creator.get_floating_ip().ip
248
249         self.__logger.info("Attemps running status of the Manager")
250         cfy_status = None
251         retry = 10
252         while str(cfy_status) != 'running' and retry:
253             try:
254                 cfy_status = cfy_client.manager.get_status()['status']
255                 self.__logger.debug("The current manager status is %s",
256                                     cfy_status)
257             except Exception:  # pylint: disable=broad-except
258                 self.__logger.warning("Cloudify Manager isn't " +
259                                       "up and running. Retrying ...")
260             retry = retry - 1
261             time.sleep(30)
262
263         if str(cfy_status) == 'running':
264             self.__logger.info("Cloudify Manager is up and running")
265         else:
266             raise Exception("Cloudify Manager isn't up and running")
267
268         self.__logger.info("Put OpenStack creds in manager")
269         secrets_list = cfy_client.secrets.list()
270         for k, val in cfy_creds.iteritems():
271             if not any(d.get('key', None) == k for d in secrets_list):
272                 cfy_client.secrets.create(k, val)
273             else:
274                 cfy_client.secrets.update(k, val)
275
276         duration = time.time() - start_time
277
278         self.__logger.info("Put private keypair in manager")
279         if manager_creator.vm_ssh_active(block=True):
280             ssh = manager_creator.ssh_client()
281             scp = SCPClient(ssh.get_transport(), socket_timeout=15.0)
282             scp.put(kp_file, '~/')
283             cmd = "sudo cp ~/cloudify_vrouter.pem /etc/cloudify/"
284             run_blocking_ssh_command(ssh, cmd)
285             cmd = "sudo chmod 444 /etc/cloudify/cloudify_vrouter.pem"
286             run_blocking_ssh_command(ssh, cmd)
287             cmd = "sudo yum install -y gcc python-devel"
288             run_blocking_ssh_command(
289                 ssh, cmd, "Unable to install packages on manager")
290
291         self.details['orchestrator'].update(status='PASS', duration=duration)
292
293         self.vnf['inputs'].update(dict(external_network_name=ext_net_name))
294
295         return True
296
297     def deploy_vnf(self):
298         start_time = time.time()
299
300         self.__logger.info("Upload VNFD")
301         cfy_client = self.orchestrator['object']
302         descriptor = self.vnf['descriptor']
303         self.deployment_name = descriptor.get('name')
304
305         vrouter_blueprint_dir = os.path.join(self.data_dir,
306                                              self.util.blueprint_dir)
307         if not os.path.exists(vrouter_blueprint_dir):
308             Repo.clone_from(descriptor.get('url'),
309                             vrouter_blueprint_dir,
310                             branch=descriptor.get('version'))
311
312         cfy_client.blueprints.upload(vrouter_blueprint_dir +
313                                      self.util.blueprint_file_name,
314                                      descriptor.get('name'))
315
316         self.__logger.info("Get or create flavor for vrouter")
317         flavor_settings = FlavorConfig(
318             name=self.vnf['requirements']['flavor']['name'],
319             ram=self.vnf['requirements']['flavor']['ram_min'],
320             disk=25,
321             vcpus=1)
322         flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
323         flavor = flavor_creator.create()
324         self.created_object.append(flavor_creator)
325
326         # set image name
327         glance = glance_utils.glance_client(self.snaps_creds)
328         image = glance_utils.get_image(glance,
329                                        "vyos1.1.7")
330         self.vnf['inputs'].update(dict(target_vnf_image_id=image.id))
331         self.vnf['inputs'].update(dict(reference_vnf_image_id=image.id))
332
333         # set flavor id
334         self.vnf['inputs'].update(dict(target_vnf_flavor_id=flavor.id))
335         self.vnf['inputs'].update(dict(reference_vnf_flavor_id=flavor.id))
336
337         self.vnf['inputs'].update(dict(keystone_username=self.tenant_name))
338         self.vnf['inputs'].update(dict(keystone_password=self.tenant_name))
339         self.vnf['inputs'].update(dict(keystone_tenant_name=self.tenant_name))
340         self.vnf['inputs'].update(
341             dict(keystone_url=keystone_utils.get_endpoint(
342                 self.snaps_creds, 'identity')))
343
344         self.__logger.info("Create VNF Instance")
345         cfy_client.deployments.create(descriptor.get('name'),
346                                       descriptor.get('name'),
347                                       self.vnf.get('inputs'))
348
349         wait_for_execution(cfy_client,
350                            get_execution_id(
351                                cfy_client, descriptor.get('name')),
352                            self.__logger,
353                            timeout=7200)
354
355         self.__logger.info("Start the VNF Instance deployment")
356         execution = cfy_client.executions.start(descriptor.get('name'),
357                                                 'install')
358         # Show execution log
359         execution = wait_for_execution(cfy_client, execution, self.__logger)
360
361         duration = time.time() - start_time
362
363         self.__logger.info(execution)
364         if execution.status == 'terminated':
365             self.details['vnf'].update(status='PASS', duration=duration)
366             result = True
367         else:
368             self.details['vnf'].update(status='FAIL', duration=duration)
369             result = False
370         return result
371
372     def test_vnf(self):
373         cfy_client = self.orchestrator['object']
374         credentials = {"snaps_creds": self.snaps_creds,
375                        "username": self.snaps_creds.username,
376                        "password": self.snaps_creds.password,
377                        "auth_url": self.snaps_creds.auth_url,
378                        "tenant_name": self.snaps_creds.project_name}
379
380         self.util_info = {"credentials": credentials,
381                           "cfy": cfy_client,
382                           "vnf_data_dir": self.util.vnf_data_dir}
383
384         start_time = time.time()
385
386         result, test_result_data = super(CloudifyVrouter, self).test_vnf()
387
388         duration = time.time() - start_time
389
390         if result:
391             self.details['test_vnf'].update(status='PASS',
392                                             result='OK',
393                                             full_result=test_result_data,
394                                             duration=duration)
395         else:
396             self.details['test_vnf'].update(status='FAIL',
397                                             result='NG',
398                                             full_result=test_result_data,
399                                             duration=duration)
400
401         return True
402
403     def clean(self):
404         try:
405             cfy_client = self.orchestrator['object']
406             dep_name = self.vnf['descriptor'].get('name')
407             # kill existing execution
408             self.__logger.info('Deleting the current deployment')
409             exec_list = cfy_client.executions.list(dep_name)
410             for execution in exec_list:
411                 if execution['status'] == "started":
412                     try:
413                         cfy_client.executions.cancel(execution['id'],
414                                                      force=True)
415                     except Exception:  # pylint: disable=broad-except
416                         self.__logger.warn("Can't cancel the current exec")
417
418             execution = cfy_client.executions.start(
419                 dep_name,
420                 'uninstall',
421                 parameters=dict(ignore_failure=True))
422
423             wait_for_execution(cfy_client, execution, self.__logger)
424             cfy_client.deployments.delete(self.vnf['descriptor'].get('name'))
425             cfy_client.blueprints.delete(self.vnf['descriptor'].get('name'))
426         except Exception:  # pylint: disable=broad-except
427             self.__logger.warn("Some issue during the undeployment ..")
428             self.__logger.warn("Tenant clean continue ..")
429         super(CloudifyVrouter, self).clean()
430
431     def get_vnf_info_list(self, target_vnf_name):
432         return self.util.get_vnf_info_list(
433             self.cfy_manager_ip, self.deployment_name, target_vnf_name)
434
435
436 # ----------------------------------------------------------
437 #
438 #               YAML UTILS
439 #
440 # -----------------------------------------------------------
441 def get_config(parameter, file_path):
442     """
443     Get config parameter.
444     Returns the value of a given parameter in file.yaml
445     parameter must be given in string format with dots
446     Example: general.openstack.image_name
447     """
448     with open(file_path) as config_file:
449         file_yaml = yaml.safe_load(config_file)
450     config_file.close()
451     value = file_yaml
452     for element in parameter.split("."):
453         value = value.get(element)
454         if value is None:
455             raise ValueError("The parameter %s is not defined in"
456                              " reporting.yaml" % parameter)
457     return value
458
459
460 def wait_for_execution(client, execution, logger, timeout=7200, ):
461     """Wait for a workflow execution on Cloudify Manager."""
462     # if execution already ended - return without waiting
463     if execution.status in Execution.END_STATES:
464         return execution
465
466     if timeout is not None:
467         deadline = time.time() + timeout
468
469     # Poll for execution status and execution logs, until execution ends
470     # and we receive an event of type in WORKFLOW_END_TYPES
471     offset = 0
472     batch_size = 50
473     event_list = []
474     execution_ended = False
475     while True:
476         event_list = client.events.list(
477             execution_id=execution.id,
478             _offset=offset,
479             _size=batch_size,
480             include_logs=False,
481             sort='@timestamp').items
482
483         offset = offset + len(event_list)
484         for event in event_list:
485             logger.debug(event.get('message'))
486
487         if timeout is not None:
488             if time.time() > deadline:
489                 raise RuntimeError(
490                     'execution of operation {0} for deployment {1} '
491                     'timed out'.format(execution.workflow_id,
492                                        execution.deployment_id))
493             else:
494                 # update the remaining timeout
495                 timeout = deadline - time.time()
496
497         if not execution_ended:
498             execution = client.executions.get(execution.id)
499             execution_ended = execution.status in Execution.END_STATES
500
501         if execution_ended:
502             break
503
504         time.sleep(5)
505
506     return execution
507
508
509 def get_execution_id(client, deployment_id):
510     """
511     Get the execution id of a env preparation.
512     network, security group, fip, VM creation
513     """
514     executions = client.executions.list(deployment_id=deployment_id)
515     for execution in executions:
516         if execution.workflow_id == 'create_deployment_environment':
517             return execution
518     raise RuntimeError('Failed to get create_deployment_environment '
519                        'workflow execution.'
520                        'Available executions: {0}'.format(executions))
521
522
523 def run_blocking_ssh_command(ssh, cmd, error_msg="Unable to run this command"):
524     """Command to run ssh command with the exit status."""
525     (_, stdout, _) = ssh.exec_command(cmd)
526     if stdout.channel.recv_exit_status() != 0:
527         raise Exception(error_msg)