mcp/deploy/scripts: Move to git submodule
[fuel.git] / deploy / install_fuel_master.py
1 ###############################################################################
2 # Copyright (c) 2015 Ericsson AB and others.
3 # szilard.cserey@ericsson.com
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
10 import time
11 import os
12 import glob
13 from ssh_client import SSHClient
14 from dha_adapters.libvirt_adapter import LibvirtAdapter
15
16 from common import (
17     log,
18     err,
19     delete,
20 )
21
22 TRANSPLANT_FUEL_SETTINGS = 'transplant_fuel_settings.py'
23 BOOTSTRAP_ADMIN = 'bootstrap_admin_node'
24 FUEL_CLIENT_CONFIG = '/etc/fuel/client/config.yaml'
25 PLUGINS_DIR = '~/plugins'
26 LOCAL_PLUGIN_FOLDER = '/opt/opnfv'
27 IGNORABLE_FUEL_ERRORS = ['does not update installed package',
28                          'Couldn\'t resolve host']
29
30
31 class InstallFuelMaster(object):
32
33     def __init__(self, dea_file, dha_file, fuel_ip, fuel_username,
34                  fuel_password, fuel_node_id, iso_file, work_dir,
35                  fuel_plugins_dir, no_plugins):
36         self.dea_file = dea_file
37         self.dha = LibvirtAdapter(dha_file)
38         self.fuel_ip = fuel_ip
39         self.fuel_username = fuel_username
40         self.fuel_password = fuel_password
41         self.fuel_node_id = fuel_node_id
42         self.iso_file = iso_file
43         self.iso_dir = os.path.dirname(self.iso_file)
44         self.work_dir = work_dir
45         self.fuel_plugins_dir = fuel_plugins_dir
46         self.no_plugins = no_plugins
47         self.file_dir = os.path.dirname(os.path.realpath(__file__))
48         self.ssh = SSHClient(self.fuel_ip, self.fuel_username,
49                              self.fuel_password)
50
51     def install(self):
52         log('Start Fuel Installation')
53
54         self.dha.node_power_off(self.fuel_node_id)
55
56         if os.environ.get('LIBVIRT_DEFAULT_URI'):
57             log('Upload ISO to pool')
58             self.iso_file = self.dha.upload_iso(self.iso_file)
59         else:
60             log('Zero the MBR')
61             self.dha.node_zero_mbr(self.fuel_node_id)
62
63         self.dha.node_set_boot_order(self.fuel_node_id, ['disk', 'iso'])
64
65         try:
66             self.proceed_with_installation()
67         except Exception as e:
68             self.post_install_cleanup()
69             err(e)
70
71     def proceed_with_installation(self):
72         log('Eject ISO')
73         self.dha.node_eject_iso(self.fuel_node_id)
74
75         log('Insert ISO %s' % self.iso_file)
76         self.dha.node_insert_iso(self.fuel_node_id, self.iso_file)
77
78         self.dha.node_power_on(self.fuel_node_id)
79
80         log('Waiting for Fuel master to accept SSH')
81         self.wait_for_node_up()
82
83         log('Wait until Fuel menu is up')
84         fuel_menu_pid = self.wait_until_fuel_menu_up()
85
86         log('Inject our own astute.yaml and fuel_bootstrap_cli.yaml settings')
87         self.inject_own_astute_and_bootstrap_yaml()
88
89         log('Let the Fuel deployment continue')
90         log('Found FUEL menu as PID %s, now killing it' % fuel_menu_pid)
91         self.ssh_exec_cmd('kill %s' % fuel_menu_pid, False)
92
93         log('Wait until installation is complete')
94         self.wait_until_installation_completed()
95
96         log('Waiting for one minute for Fuel to stabilize')
97         time.sleep(60)
98
99         self.delete_deprecated_fuel_client_config()
100
101         if not self.no_plugins:
102
103             self.collect_plugin_files()
104
105             self.install_plugins()
106
107         self.post_install_cleanup()
108
109         log('Fuel Master installed successfully !')
110
111     def collect_plugin_files(self):
112         with self.ssh as s:
113             s.exec_cmd('mkdir %s' % PLUGINS_DIR)
114             if self.fuel_plugins_dir:
115                 for f in glob.glob('%s/*.rpm' % self.fuel_plugins_dir):
116                     s.scp_put(f, PLUGINS_DIR)
117
118     def install_plugins(self):
119         log('Installing Fuel Plugins')
120         plugin_files = []
121         with self.ssh as s:
122             for plugin_location in [PLUGINS_DIR, LOCAL_PLUGIN_FOLDER]:
123                 s.exec_cmd('mkdir -p %s' % plugin_location)
124                 r = s.exec_cmd('find %s -type f -name \'*.rpm\''
125                                % plugin_location)
126                 plugin_files.extend(r.splitlines())
127             for f in plugin_files:
128                 log('Found plugin %s, installing ...' % f)
129                 r, e = s.exec_cmd('fuel plugins --install %s' % f, False)
130                 printout = r + e if e else r
131                 if e and all([err not in printout
132                               for err in IGNORABLE_FUEL_ERRORS]):
133                     raise Exception('Installation of Fuel Plugin %s '
134                                     'failed: %s' % (f, e))
135
136     def wait_for_node_up(self):
137         WAIT_LOOP = 240
138         SLEEP_TIME = 10
139         success = False
140         for i in range(WAIT_LOOP):
141             try:
142                 self.ssh.open()
143                 success = True
144                 break
145             except Exception:
146                 log('Trying to SSH into Fuel VM %s ... sleeping %s seconds'
147                     % (self.fuel_ip, SLEEP_TIME))
148                 time.sleep(SLEEP_TIME)
149             finally:
150                 self.ssh.close()
151
152         if not success:
153             raise Exception('Could not SSH into Fuel VM %s' % self.fuel_ip)
154
155     def wait_until_fuel_menu_up(self):
156         WAIT_LOOP = 60
157         SLEEP_TIME = 10
158         CMD = 'pgrep -f fuelmenu'
159         fuel_menu_pid = None
160         with self.ssh:
161             for i in range(WAIT_LOOP):
162                 ret = self.ssh.exec_cmd(CMD)
163                 fuel_menu_pid = ret.strip()
164                 if not fuel_menu_pid:
165                     time.sleep(SLEEP_TIME)
166                 else:
167                     break
168         if not fuel_menu_pid:
169             raise Exception('Could not find the Fuel Menu Process ID')
170         return fuel_menu_pid
171
172     def ssh_exec_cmd(self, cmd, check=True):
173         with self.ssh:
174             ret = self.ssh.exec_cmd(cmd, check=check)
175         return ret
176
177     def inject_own_astute_and_bootstrap_yaml(self):
178         with self.ssh as s:
179             s.exec_cmd('rm -rf %s' % self.work_dir, False)
180             s.exec_cmd('mkdir %s' % self.work_dir)
181             s.scp_put(self.dea_file, self.work_dir)
182             s.scp_put('%s/common.py' % self.file_dir, self.work_dir)
183             s.scp_put('%s/dea.py' % self.file_dir, self.work_dir)
184             s.scp_put('%s/transplant_fuel_settings.py'
185                       % self.file_dir, self.work_dir)
186             log('Modifying Fuel astute')
187             s.run('python %s/%s %s/%s'
188                   % (self.work_dir, TRANSPLANT_FUEL_SETTINGS,
189                      self.work_dir, os.path.basename(self.dea_file)))
190
191     def wait_until_installation_completed(self):
192         WAIT_LOOP = 360
193         SLEEP_TIME = 10
194         CMD = 'pgrep -f %s' % BOOTSTRAP_ADMIN
195
196         install_completed = False
197         with self.ssh:
198             for i in range(WAIT_LOOP):
199                 ret = self.ssh.exec_cmd(CMD)
200                 if not ret:
201                     install_completed = True
202                     break
203                 else:
204                     time.sleep(SLEEP_TIME)
205
206         if not install_completed:
207             raise Exception('Fuel installation did not complete')
208
209     def post_install_cleanup(self):
210         log('Eject ISO file %s' % self.iso_file)
211         self.dha.node_eject_iso(self.fuel_node_id)
212         delete(self.iso_dir)
213
214     def delete_deprecated_fuel_client_config(self):
215         with self.ssh as s:
216             response, error = s.exec_cmd('fuel -v', False)
217         if (error and
218             'DEPRECATION WARNING' in error and FUEL_CLIENT_CONFIG in error):
219             log('Delete deprecated fuel client config %s' % FUEL_CLIENT_CONFIG)
220             with self.ssh as s:
221                 s.exec_cmd('rm %s' % FUEL_CLIENT_CONFIG, False)