Create a second user in cloudify_vrouter
[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 import uuid
18
19 from cloudify_rest_client import CloudifyClient
20 from cloudify_rest_client.executions import Execution
21 from scp import SCPClient
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 from functest.utils import functest_utils
28
29 from git import Repo
30
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
40
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
49
50 import snaps.openstack.utils.glance_utils as glance_utils
51 from snaps.openstack.utils import keystone_utils
52
53
54 __author__ = "Shuya Nakama <shuya.nakama@okinawaopenlabs.org>"
55
56
57 class CloudifyVrouter(vrouter_base.VrouterOnBoardingBase):
58     # pylint: disable=too-many-instance-attributes
59     """vrouter testcase deployed with Cloudify Orchestrator."""
60
61     __logger = logging.getLogger(__name__)
62     name = __name__
63
64     def __init__(self, **kwargs):
65         if "case_name" not in kwargs:
66             kwargs["case_name"] = "vyos_vrouter"
67         super(CloudifyVrouter, self).__init__(**kwargs)
68
69         # Retrieve the configuration
70         try:
71             self.config = getattr(
72                 CONST, 'vnf_{}_config'.format(self.case_name))
73         except Exception:
74             raise Exception("VNF config file not found")
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=functest_utils.get_parameter_from_yaml(
83                 "orchestrator.requirements", config_file),
84         )
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),
90             status='ERROR',
91             result=''
92         )
93         self.__logger.debug("Orchestrator configuration %s", self.orchestrator)
94         self.__logger.debug("name = %s", self.name)
95         self.vnf = dict(
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)
102         )
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),
109         )
110         self.__logger.debug("VNF configuration: %s", self.vnf)
111
112         self.util = Utilvnf()
113
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)
119         )
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)
123
124     def prepare(self):
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(
133                     self.snaps_creds,
134                     ImageConfig(
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)
139
140     def deploy_orchestrator(self):
141         # pylint: disable=too-many-locals,too-many-statements
142         """
143         Deploy Cloudify Manager.
144         network, security group, fip, VM creation
145         """
146         # network 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)
156
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(
169             self.snaps_creds,
170             RouterConfig(
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)
176
177         # security group creation
178         self.__logger.info("Creating security group for cloudify manager vm")
179         sg_rules = list()
180         sg_rules.append(
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))
186         sg_rules.append(
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))
192
193         security_group_creator = OpenStackSecurityGroup(
194             self.snaps_creds,
195             SecurityGroupConfig(
196                 name="sg-cloudify-manager-{}".format(self.uuid),
197                 rule_settings=sg_rules))
198
199         security_group_creator.create()
200         self.created_object.append(security_group_creator)
201
202         # orchestrator VM flavor
203         self.__logger.info("Get or create flavor for cloudify manager vm ...")
204
205         flavor_settings = FlavorConfig(
206             name=self.orchestrator['requirements']['flavor']['name'],
207             ram=self.orchestrator['requirements']['flavor']['ram_min'],
208             disk=50, vcpus=2)
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)
215
216         port_settings = PortConfig(
217             name='cloudify_manager_port-{}'.format(self.uuid),
218             network_name=network_settings.name)
219
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)])
230
231         manager_creator = OpenStackVmInstance(
232             self.snaps_creds, manager_settings, image_settings,
233             keypair_settings)
234
235         self.__logger.info("Creating cloudify manager VM")
236         manager_creator.create()
237         self.created_object.append(manager_creator)
238
239         cfy_client = CloudifyClient(
240             host=manager_creator.get_floating_ip().ip,
241             username='admin', password='admin', tenant='default_tenant')
242
243         self.orchestrator['object'] = cfy_client
244
245         self.cfy_manager_ip = manager_creator.get_floating_ip().ip
246
247         self.__logger.info("Attemps running status of the Manager")
248         cfy_status = None
249         retry = 10
250         while str(cfy_status) != 'running' and retry:
251             try:
252                 cfy_status = cfy_client.manager.get_status()['status']
253                 self.__logger.debug("The current manager status is %s",
254                                     cfy_status)
255             except Exception:  # pylint: disable=broad-except
256                 self.__logger.exception(
257                     "Cloudify Manager isn't up and running. Retrying ...")
258             retry = retry - 1
259             time.sleep(30)
260
261         if str(cfy_status) == 'running':
262             self.__logger.info("Cloudify Manager is up and running")
263         else:
264             raise Exception("Cloudify Manager isn't up and running")
265
266         duration = time.time() - start_time
267
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")
280
281         self.details['orchestrator'].update(status='PASS', duration=duration)
282
283         self.vnf['inputs'].update(dict(external_network_name=ext_net_name))
284
285         return True
286
287     def deploy_vnf(self):
288         start_time = time.time()
289
290         self.__logger.info("Upload VNFD")
291         cfy_client = self.orchestrator['object']
292         descriptor = self.vnf['descriptor']
293         self.deployment_name = descriptor.get('name')
294
295         vrouter_blueprint_dir = os.path.join(
296             self.data_dir, self.util.blueprint_dir)
297         if not os.path.exists(vrouter_blueprint_dir):
298             Repo.clone_from(
299                 descriptor.get('url'), vrouter_blueprint_dir,
300                 branch=descriptor.get('version'))
301
302         cfy_client.blueprints.upload(
303             vrouter_blueprint_dir + self.util.blueprint_file_name,
304             descriptor.get('name'))
305
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'],
310             disk=25, vcpus=1)
311         flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
312         flavor = flavor_creator.create()
313         self.created_object.append(flavor_creator)
314
315         # set image name
316         glance = glance_utils.glance_client(self.snaps_creds)
317         image = glance_utils.get_image(glance, "vyos1.1.7")
318
319         user_creator = OpenStackUser(
320             self.snaps_creds,
321             UserConfig(
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)
328
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')))
344
345         self.__logger.info("Create VNF Instance")
346         cfy_client.deployments.create(
347             descriptor.get('name'), descriptor.get('name'),
348             self.vnf.get('inputs'))
349
350         wait_for_execution(
351             cfy_client, get_execution_id(cfy_client, descriptor.get('name')),
352             self.__logger, timeout=7200)
353
354         self.__logger.info("Start the VNF Instance deployment")
355         execution = cfy_client.executions.start(descriptor.get('name'),
356                                                 'install')
357         # Show execution log
358         execution = wait_for_execution(cfy_client, execution, self.__logger)
359
360         duration = time.time() - start_time
361
362         self.__logger.info(execution)
363         if execution.status == 'terminated':
364             self.details['vnf'].update(status='PASS', duration=duration)
365             result = True
366         else:
367             self.details['vnf'].update(status='FAIL', duration=duration)
368             result = False
369         return result
370
371     def test_vnf(self):
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}
378
379         self.util_info = {"credentials": credentials,
380                           "cfy": cfy_client,
381                           "vnf_data_dir": self.util.vnf_data_dir}
382
383         start_time = time.time()
384
385         result, test_result_data = super(CloudifyVrouter, self).test_vnf()
386
387         duration = time.time() - start_time
388
389         if result:
390             self.details['test_vnf'].update(
391                 status='PASS', result='OK', full_result=test_result_data,
392                 duration=duration)
393         else:
394             self.details['test_vnf'].update(
395                 status='FAIL', result='NG', full_result=test_result_data,
396                 duration=duration)
397
398         return True
399
400     def clean(self):
401         try:
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":
409                     try:
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")
414
415             execution = cfy_client.executions.start(
416                 dep_name, 'uninstall', parameters=dict(ignore_failure=True))
417
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()
425
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)
429
430
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:
435         return execution
436
437     if timeout is not None:
438         deadline = time.time() + timeout
439
440     # Poll for execution status and execution logs, until execution ends
441     # and we receive an event of type in WORKFLOW_END_TYPES
442     offset = 0
443     batch_size = 50
444     event_list = []
445     execution_ended = False
446     while True:
447         event_list = client.events.list(
448             execution_id=execution.id, _offset=offset, _size=batch_size,
449             include_logs=False, sort='@timestamp').items
450
451         offset = offset + len(event_list)
452         for event in event_list:
453             logger.debug(event.get('message'))
454
455         if timeout is not None:
456             if time.time() > deadline:
457                 raise RuntimeError(
458                     'execution of operation {0} for deployment {1} '
459                     'timed out'.format(execution.workflow_id,
460                                        execution.deployment_id))
461             else:
462                 # update the remaining timeout
463                 timeout = deadline - time.time()
464
465         if not execution_ended:
466             execution = client.executions.get(execution.id)
467             execution_ended = execution.status in Execution.END_STATES
468
469         if execution_ended:
470             break
471
472         time.sleep(5)
473
474     return execution
475
476
477 def get_execution_id(client, deployment_id):
478     """
479     Get the execution id of a env preparation.
480     network, security group, fip, VM creation
481     """
482     executions = client.executions.list(deployment_id=deployment_id)
483     for execution in executions:
484         if execution.workflow_id == 'create_deployment_environment':
485             return execution
486     raise RuntimeError('Failed to get create_deployment_environment '
487                        'workflow execution.'
488                        'Available executions: {0}'.format(executions))
489
490
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)