DevStack support
[doctor.git] / doctor_tests / installer / base.py
1 ##############################################################################
2 # Copyright (c) 2017 ZTE Corporation and others.
3 #
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
9 import abc
10 import getpass
11 import grp
12 import os
13 import pwd
14 import six
15 import stat
16 import subprocess
17 import time
18
19 from doctor_tests.common import utils
20 from doctor_tests.identity_auth import get_session
21 from doctor_tests.os_clients import nova_client
22
23
24 @six.add_metaclass(abc.ABCMeta)
25 class BaseInstaller(object):
26     def __init__(self, conf, log):
27         self.conf = conf
28         self.log = log
29         self.servers = list()
30         self.use_containers = False
31
32     @abc.abstractproperty
33     def node_user_name(self):
34         """user name for login to cloud node"""
35
36     @abc.abstractmethod
37     def get_ssh_key_from_installer(self):
38         pass
39
40     @abc.abstractmethod
41     def get_host_ip_from_hostname(self, hostname):
42         pass
43
44     @abc.abstractmethod
45     def setup(self):
46         pass
47
48     @abc.abstractmethod
49     def cleanup(self):
50         pass
51
52     def create_flavor(self):
53         self.nova = \
54             nova_client(self.conf.nova_version,
55                         get_session())
56         flavors = {flavor.name: flavor for flavor in self.nova.flavors.list()}
57         if self.conf.flavor not in flavors:
58             self.nova.flavors.create(self.conf.flavor, 512, 1, 1)
59
60     def setup_stunnel(self):
61         self.log.info('Setup ssh stunnel in %s installer......'
62                       % self.conf.installer.type)
63         tunnels = [self.conf.consumer.port]
64         if self.conf.test_case == 'maintenance':
65             tunnel_uptime = 1200
66             tunnels += [self.conf.app_manager.port, self.conf.inspector.port]
67         elif self.conf.test_case == 'all':
68             tunnel_uptime = 1800
69             tunnels += [self.conf.app_manager.port, self.conf.inspector.port]
70         else:
71             tunnel_uptime = 600
72
73         for node_ip in self.controllers:
74             for port in tunnels:
75                 self.log.info('tunnel for port %s' % port)
76                 cmd = ("ssh -o UserKnownHostsFile=/dev/null"
77                        " -o StrictHostKeyChecking=no"
78                        " -i %s %s@%s -R %s:localhost:%s"
79                        " sleep %s > ssh_tunnel.%s.%s"
80                        " 2>&1 < /dev/null "
81                        % (self.key_file,
82                           self.node_user_name,
83                           node_ip,
84                           port,
85                           port,
86                           tunnel_uptime,
87                           node_ip,
88                           port))
89                 server = subprocess.Popen('exec ' + cmd, shell=True)
90                 self.servers.append(server)
91         if self.conf.admin_tool.type == 'fenix':
92             port = self.conf.admin_tool.port
93             self.log.info('tunnel for port %s' % port)
94             cmd = ("ssh -o UserKnownHostsFile=/dev/null"
95                    " -o StrictHostKeyChecking=no"
96                    " -i %s %s@%s -L %s:localhost:%s"
97                    " sleep %s > ssh_tunnel.%s.%s"
98                    " 2>&1 < /dev/null "
99                    % (self.key_file,
100                       self.node_user_name,
101                       node_ip,
102                       port,
103                       port,
104                       tunnel_uptime,
105                       node_ip,
106                       port))
107             server = subprocess.Popen('exec ' + cmd, shell=True)
108             self.servers.append(server)
109
110     def _get_ssh_key(self, client, key_path):
111         self.log.info('Get SSH keys from %s installer......'
112                       % self.conf.installer.type)
113
114         if self.key_file is not None:
115             self.log.info('Already have SSH keys from %s installer......'
116                           % self.conf.installer.type)
117             return self.key_file
118
119         ssh_key = '{0}/{1}'.format(utils.get_doctor_test_root_dir(),
120                                    'instack_key')
121         client.scp(key_path, ssh_key, method='get')
122         user = getpass.getuser()
123         uid = pwd.getpwnam(user).pw_uid
124         gid = grp.getgrnam(user).gr_gid
125         os.chown(ssh_key, uid, gid)
126         os.chmod(ssh_key, stat.S_IREAD)
127         return ssh_key
128
129     @abc.abstractmethod
130     def get_transport_url(self):
131         pass
132
133     def _run_cmd_remote(self, client, command):
134         self.log.info('Run command=%s in %s installer......'
135                       % (command, self.conf.installer.type))
136
137         ret, output = client.ssh(command)
138         if ret:
139             raise Exception('Exec command in %s installer failed,'
140                             'ret=%s, output=%s'
141                             % (self.conf.installer.type,
142                                ret, output))
143         self.log.info('Output=%s command=%s in %s installer'
144                       % (output, command, self.conf.installer.type))
145         return output
146
147     def _check_cmd_remote(self, client, command):
148         self.log.info('Check command=%s return in %s installer......'
149                       % (command, self.conf.installer.type))
150
151         ret, output = client.ssh(command, raise_enabled=False)
152         self.log.info('return %s' % ret)
153         if ret == 0:
154             ret = True
155         else:
156             ret = False
157         return ret
158
159     @utils.run_async
160     def _run_apply_patches(self, client, restart_cmd, script_names,
161                            python='python3'):
162         installer_dir = os.path.dirname(os.path.realpath(__file__))
163         if isinstance(script_names, list):
164             for script_name in script_names:
165                 script_abs_path = '{0}/{1}/{2}'.format(installer_dir,
166                                                        'common', script_name)
167                 if self.conf.installer.type == "devstack":
168                     script_name = "/opt/stack/%s" % script_name
169                 try:
170                     client.scp(script_abs_path, script_name)
171                 except Exception:
172                     client.scp(script_abs_path, script_name)
173                 try:
174                     if ".py" in script_name:
175                         cmd = 'sudo %s %s' % (python, script_name)
176                     else:
177                         cmd = 'sudo chmod 700 %s;sudo ./%s' % (script_name,
178                                                                script_name)
179                     ret, output = client.ssh(cmd)
180                     self.log.info('Command %s output %s' % (cmd, output))
181                 except Exception:
182                     ret, output = client.ssh(cmd)
183                     self.log.info('Command %s output %s' % (cmd, output))
184                 if ret:
185                     raise Exception('Do the command in remote'
186                                     ' node failed, ret=%s, cmd=%s, output=%s'
187                                     % (ret, cmd, output))
188             if 'nova' in restart_cmd or 'devstack@n-' in restart_cmd:
189                 # Make sure scheduler has proper cpu_allocation_ratio
190                 time.sleep(5)
191             client.ssh(restart_cmd)