Set project_name arg when creating users
[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 import config
27 from functest.utils import env
28 from functest.utils import functest_utils
29
30 from git import Repo
31
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
41
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
50
51 import snaps.openstack.utils.glance_utils as glance_utils
52 from snaps.openstack.utils import keystone_utils
53
54
55 __author__ = "Shuya Nakama <shuya.nakama@okinawaopenlabs.org>"
56
57
58 class CloudifyVrouter(vrouter_base.VrouterOnBoardingBase):
59     # pylint: disable=too-many-instance-attributes
60     """vrouter testcase deployed with Cloudify Orchestrator."""
61
62     __logger = logging.getLogger(__name__)
63     name = __name__
64
65     def __init__(self, **kwargs):
66         if "case_name" not in kwargs:
67             kwargs["case_name"] = "vyos_vrouter"
68         super(CloudifyVrouter, self).__init__(**kwargs)
69
70         # Retrieve the configuration
71         try:
72             self.config = getattr(
73                 config.CONF, 'vnf_{}_config'.format(self.case_name))
74         except Exception:
75             raise Exception("VNF config file not found")
76
77         self.cfy_manager_ip = ''
78         self.util_info = {}
79         self.deployment_name = ''
80
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),
85         )
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),
91             status='ERROR',
92             result=''
93         )
94         self.__logger.debug("Orchestrator configuration %s", self.orchestrator)
95         self.__logger.debug("name = %s", self.name)
96         self.vnf = dict(
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)
103         )
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),
110         )
111         self.__logger.debug("VNF configuration: %s", self.vnf)
112
113         self.util = Utilvnf()
114
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)
120         )
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)
124
125     @staticmethod
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)
135
136     def prepare(self):
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(
145                     self.snaps_creds,
146                     ImageConfig(
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)
151
152     def deploy_orchestrator(self):
153         # pylint: disable=too-many-locals,too-many-statements
154         """
155         Deploy Cloudify Manager.
156         network, security group, fip, VM creation
157         """
158         # network 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)
168
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(
182             self.snaps_creds,
183             RouterConfig(
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)
189
190         # security group creation
191         self.__logger.info("Creating security group for cloudify manager vm")
192         sg_rules = list()
193         sg_rules.append(
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))
199         sg_rules.append(
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))
205
206         security_group_creator = OpenStackSecurityGroup(
207             self.snaps_creds,
208             SecurityGroupConfig(
209                 name="sg-cloudify-manager-{}".format(self.uuid),
210                 rule_settings=sg_rules))
211
212         security_group_creator.create()
213         self.created_object.append(security_group_creator)
214
215         # orchestrator VM flavor
216         self.__logger.info("Get or create flavor for cloudify manager vm ...")
217
218         flavor_settings = FlavorConfig(
219             name=self.orchestrator['requirements']['flavor']['name'],
220             ram=self.orchestrator['requirements']['flavor']['ram_min'],
221             disk=50, vcpus=2)
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)
228
229         port_settings = PortConfig(
230             name='cloudify_manager_port-{}'.format(self.uuid),
231             network_name=network_settings.name)
232
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)])
243
244         manager_creator = OpenStackVmInstance(
245             self.snaps_creds, manager_settings, image_settings,
246             keypair_settings)
247
248         self.__logger.info("Creating cloudify manager VM")
249         manager_creator.create()
250         self.created_object.append(manager_creator)
251
252         cfy_client = CloudifyClient(
253             host=manager_creator.get_floating_ip().ip,
254             username='admin', password='admin', tenant='default_tenant')
255
256         self.orchestrator['object'] = cfy_client
257
258         self.cfy_manager_ip = manager_creator.get_floating_ip().ip
259
260         self.__logger.info("Attemps running status of the Manager")
261         cfy_status = None
262         retry = 10
263         while str(cfy_status) != 'running' and retry:
264             try:
265                 cfy_status = cfy_client.manager.get_status()['status']
266                 self.__logger.debug("The current manager status is %s",
267                                     cfy_status)
268             except Exception:  # pylint: disable=broad-except
269                 self.__logger.exception(
270                     "Cloudify Manager isn't up and running. Retrying ...")
271             retry = retry - 1
272             time.sleep(30)
273
274         if str(cfy_status) == 'running':
275             self.__logger.info("Cloudify Manager is up and running")
276         else:
277             raise Exception("Cloudify Manager isn't up and running")
278
279         duration = time.time() - start_time
280
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")
293
294         self.details['orchestrator'].update(status='PASS', duration=duration)
295
296         self.vnf['inputs'].update(dict(external_network_name=ext_net_name))
297
298         return True
299
300     def deploy_vnf(self):
301         start_time = time.time()
302
303         self.__logger.info("Upload VNFD")
304         cfy_client = self.orchestrator['object']
305         descriptor = self.vnf['descriptor']
306         self.deployment_name = descriptor.get('name')
307
308         vrouter_blueprint_dir = os.path.join(
309             self.data_dir, self.util.blueprint_dir)
310         if not os.path.exists(vrouter_blueprint_dir):
311             Repo.clone_from(
312                 descriptor.get('url'), vrouter_blueprint_dir,
313                 branch=descriptor.get('version'))
314
315         cfy_client.blueprints.upload(
316             vrouter_blueprint_dir + self.util.blueprint_file_name,
317             descriptor.get('name'))
318
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'],
323             disk=25, vcpus=1)
324         flavor_creator = OpenStackFlavor(self.snaps_creds, flavor_settings)
325         flavor = flavor_creator.create()
326         self.created_object.append(flavor_creator)
327
328         # set image name
329         glance = glance_utils.glance_client(self.snaps_creds)
330         image = glance_utils.get_image(glance, "vyos1.1.7")
331
332         user_creator = OpenStackUser(
333             self.snaps_creds,
334             UserConfig(
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)
344
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')))
360
361         self.__logger.info("Create VNF Instance")
362         cfy_client.deployments.create(
363             descriptor.get('name'), descriptor.get('name'),
364             self.vnf.get('inputs'))
365
366         wait_for_execution(
367             cfy_client, get_execution_id(cfy_client, descriptor.get('name')),
368             self.__logger, timeout=7200)
369
370         self.__logger.info("Start the VNF Instance deployment")
371         execution = cfy_client.executions.start(descriptor.get('name'),
372                                                 'install')
373         # Show execution log
374         execution = wait_for_execution(cfy_client, execution, self.__logger)
375
376         duration = time.time() - start_time
377
378         self.__logger.info(execution)
379         if execution.status == 'terminated':
380             self.details['vnf'].update(status='PASS', duration=duration)
381             result = True
382         else:
383             self.details['vnf'].update(status='FAIL', duration=duration)
384             result = False
385         return result
386
387     def test_vnf(self):
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}
394
395         self.util_info = {"credentials": credentials,
396                           "cfy": cfy_client,
397                           "vnf_data_dir": self.util.vnf_data_dir}
398
399         start_time = time.time()
400
401         result, test_result_data = super(CloudifyVrouter, self).test_vnf()
402
403         duration = time.time() - start_time
404
405         if result:
406             self.details['test_vnf'].update(
407                 status='PASS', result='OK', full_result=test_result_data,
408                 duration=duration)
409         else:
410             self.details['test_vnf'].update(
411                 status='FAIL', result='NG', full_result=test_result_data,
412                 duration=duration)
413
414         return True
415
416     def clean(self):
417         try:
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":
425                     try:
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")
430
431             execution = cfy_client.executions.start(
432                 dep_name, 'uninstall', parameters=dict(ignore_failure=True))
433
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()
441
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)
445
446
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:
451         return execution
452
453     if timeout is not None:
454         deadline = time.time() + timeout
455
456     # Poll for execution status and execution logs, until execution ends
457     # and we receive an event of type in WORKFLOW_END_TYPES
458     offset = 0
459     batch_size = 50
460     event_list = []
461     execution_ended = False
462     while True:
463         event_list = client.events.list(
464             execution_id=execution.id, _offset=offset, _size=batch_size,
465             include_logs=False, sort='@timestamp').items
466
467         offset = offset + len(event_list)
468         for event in event_list:
469             logger.debug(event.get('message'))
470
471         if timeout is not None:
472             if time.time() > deadline:
473                 raise RuntimeError(
474                     'execution of operation {0} for deployment {1} '
475                     'timed out'.format(execution.workflow_id,
476                                        execution.deployment_id))
477             else:
478                 # update the remaining timeout
479                 timeout = deadline - time.time()
480
481         if not execution_ended:
482             execution = client.executions.get(execution.id)
483             execution_ended = execution.status in Execution.END_STATES
484
485         if execution_ended:
486             break
487
488         time.sleep(5)
489
490     return execution
491
492
493 def get_execution_id(client, deployment_id):
494     """
495     Get the execution id of a env preparation.
496     network, security group, fip, VM creation
497     """
498     executions = client.executions.list(deployment_id=deployment_id)
499     for execution in executions:
500         if execution.workflow_id == 'create_deployment_environment':
501             return execution
502     raise RuntimeError('Failed to get create_deployment_environment '
503                        'workflow execution.'
504                        'Available executions: {0}'.format(executions))