Fix BMCException handling
[ta/remote-installer.git] / src / remoteinstaller / installer / bmc_management / or18.py
1 # Copyright 2019 Nokia
2
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 import logging
16 import time
17 from .bmctools import BMC, BMCException
18
19 class OR18(BMC):
20     def __init__(self, host, user, passwd, priv_level='ADMINISTRATOR', log_path=None):
21         super(OR18, self).__init__(host, user, passwd, priv_level, log_path)
22
23     def _clear_ris_configuration(self):
24         # Clear RIS configuration
25         try:
26             logging.debug('Clear RIS configuration.')
27             self._run_ipmitool_raw_command('0x32 0x9f 0x01 0x0d')
28         except Exception as err:
29             logging.warning('Exception when clearing RIS NFS configuration: %s', str(err))
30             return False
31         return True
32
33     def _check_virtual_media_started(self):
34         # check virtmedia service status
35         try:
36             out = self._run_ipmitool_raw_command('0x32 0xca 0x08')
37             logging.debug('Virtual media service status: %s', str(out[0]))
38         except Exception as err:
39             logging.warning('Exception when checking virtual media service: %s', str(err))
40         if out[0] == '01':
41             return True
42         return False
43
44     def _start_virtual_media(self):
45         # Enable "Remote Media Support" in GUI (p145)
46         try:
47             logging.debug('Start virtual media service')
48             self._run_ipmitool_raw_command('0x32 0xcb 0x08 0x01')
49         except Exception as err:
50             logging.warning('Exception when starting virtual media service: %s', str(err))
51
52     def _set_setup_nfs(self, nfs_host, mount_path):
53
54         # Set share type NFS
55         try:
56             logging.debug('Virtual media share type to NFS.')
57             self._run_ipmitool_raw_command('0x32 0x9f 0x01 0x05 0x00 0x6e 0x66 0x73 0x00 0x00 0x00')
58         except Exception as err:
59             logging.warning('Exception when setting virtual media service type NFS: %s', str(err))
60             return False
61
62         # NFS server IP
63         try:
64             cmd = '0x32 0x9f 0x01 0x02 0x00 %s' % (self._convert_to_hex(nfs_host, True, 63))
65             logging.debug('Virtual media server "%s"', nfs_host)
66             self._run_ipmitool_raw_command(cmd)
67         except Exception as err:
68             logging.warning('Exception when setting virtual media server: %s', str(err))
69             return False
70
71         # Set NFS Mount Root path
72         try:
73             logging.debug('Virtual media path to "%s"', mount_path)
74             # set progress bit (hmm. seems to return error if it is already set.. So should check..)
75             time.sleep(2)
76             cmd = '0x32 0x9f 0x01 0x01 0x00 0x00'
77             self._run_ipmitool_raw_command(cmd)
78             time.sleep(2)
79             cmd = '0x32 0x9f 0x01 0x01 0x00 0x01'
80             self._run_ipmitool_raw_command(cmd)
81             time.sleep(2)
82             cmd = '0x32 0x9f 0x01 0x01 0x01 %s' % (self._convert_to_hex(mount_path, True, 64))
83             self._run_ipmitool_raw_command(cmd)
84             time.sleep(2)
85             # clear progress bit
86             cmd = '0x32 0x9f 0x01 0x01 0x00 0x00'
87             self._run_ipmitool_raw_command(cmd)
88         except Exception as err:
89             logging.warning('Exception when setting virtual media path: %s', str(err))
90             return False
91         return True
92
93     def _enable_virtual_media(self):
94         logging.debug('Enable Virtual Media')
95
96         # 0x32 0xcb command will cause vmedia service restart automatically after 2 seconds according doc.
97         # restart is delayed by 2 seconds if new command is received during 2 second delay
98         # In other words, do all config in batch and it will only be restarted once.
99
100         # Speed up things if it service is already running
101         if self._check_virtual_media_started():
102             logging.debug('Virtual media service already running.')
103             # Service is already started
104             return True
105
106         self._start_virtual_media()
107
108         _max_tries = 6
109         _try = 1
110         # Just enabling the service doe not seem to start it (in all HW)
111         # Resetting it after enabling helps
112         self._restart_virtual_media_service()
113         while not self._check_virtual_media_started():
114             if _try > _max_tries:
115                 logging.warning('Ensure virtual media service start failed, attempts exceeded.')
116                 return False
117             time.sleep(5)
118             _try = _try + 1
119         return True
120
121     def _get_virtual_media_device_count(self, devicetype):
122         try:
123             _num_inst = 0
124             # Get num of enabled devices
125             if devicetype == 'CD':
126                 _devparam = '0x04'
127                 logging.debug('Get virtual CD count')
128             elif devicetype == 'FD':
129                 _devparam = '0x05'
130                 logging.debug('Get virtual FD count')
131             elif devicetype == 'HD':
132                 _devparam = '0x06'
133                 logging.debug('Get virtual HD count')
134             else:
135                 logging.warning('Unknown device type "%s"', devicetype)
136                 return _num_inst
137
138             cmd = '0x32 0xca %s' % _devparam
139             out = self._run_ipmitool_raw_command(cmd)
140             _num_inst = int(out[0], 16)
141
142             logging.debug('Number of enabled %s devices is %d', devicetype, _num_inst)
143
144             return _num_inst
145         except Exception as err:
146             raise BMCException('Exception when getting number of enabled %s devices. error: %s' % (devicetype, str(err)))
147
148     def _set_virtual_media_device_count(self, devicetype, devicecount):
149         # Chapter 46.2 page 181
150         if not 0 <= devicecount <= 4:
151             logging.warning('Number of devices must be in range 0 to 4')
152             return False
153
154         if devicetype == 'CD':
155             _devparam = '0x04'
156             logging.debug('Setting virtual CD count to %d', devicecount)
157         elif devicetype == 'HD':
158             _devparam = '0x06'
159             logging.debug('Setting virtual HD count to %d', devicecount)
160         else:
161             logging.warning('_set_virtual_media_device_count: Unknown device type "%s"', devicetype)
162             return False
163
164         try:
165             cmd = '0x32 0xcb %s 0x%s' % (_devparam, str(devicecount))
166             self._run_ipmitool_raw_command(cmd)
167
168             _conf_device_num = self._get_virtual_media_device_count(devicetype)
169             _tries = 4
170             while _conf_device_num != devicecount and _tries > 0:
171                 logging.debug('Virtual %s count is %d expecting %d', devicetype, _conf_device_num, devicecount)
172                 time.sleep(5)
173                 _conf_device_num = self._get_virtual_media_device_count(devicetype)
174                 _tries = _tries -1
175         except Exception as err:
176             raise BMCException('Exception when setting virtual media device count : %s' % str(err))
177         return True
178
179     def _restart_virtual_media_service(self):
180         try:
181             cmd = '0x32 0xcb 0x0a 0x01'
182             logging.debug('Restart virtual media service')
183             self._run_ipmitool_raw_command(cmd)
184         except Exception as err:
185             raise BMCException('Exception when restarting virtual media service: %s' % str(err))
186
187     def _restart_ris(self):
188         try:
189             logging.debug('Restart RIS')
190             cmd = '0x32 0x9f 0x08 0x0b'
191             self._run_ipmitool_raw_command(cmd)
192         except Exception as err:
193             raise BMCException('Exception when restarting RIS: %s'% str(err))
194
195         return True
196
197     def _restart_ris_cd(self):
198         try:
199             logging.debug('Restart RIS CD media')
200             cmd = '0x32 0x9f 0x01 0x0b 0x01'
201             self._run_ipmitool_raw_command(cmd)
202         except Exception as err:
203             raise BMCException('Exception when restarting RIS CD media: %s' % str(err))
204
205         return True
206
207     def _check_cd_dvd_enabled(self, enabled):
208         try:
209             out = self._run_ipmitool_raw_command('0x32 0xca 0x0')
210             logging.debug('Virtual cd_dvd status: %s', str(out[0]))
211         except Exception as err:
212             logging.warning('Exception when checking cd_dvd status: %s', str(err))
213         if (out[0] == '01' and enabled) or (out[0] == '00' and not enabled):
214             return True
215         return False
216
217     def _enable_disable_cd_dvd(self, enabled):
218         _max_tries = 6
219         _try = 1
220         logging.debug('Enable/Disable cd_dvd')
221         while not self._check_cd_dvd_enabled(enabled):
222             if _try > _max_tries:
223                 logging.warning('Ensure cd_dvd enable/disable failed, attempts exceeded. Ignoring and trying to continue.')
224                 return True
225             time.sleep(5)
226             _try = _try + 1
227         return True
228
229     def _toggle_virtual_device(self, enabled):
230         # Enable "Mount CD/DVD" in GUI (p144) should cause vmedia restart withing 2 seconds.
231         # Seems "Mount CD/DVD" need to be enabled (or toggled) after config. refresh/vmedia restart
232         # is not enough(?)
233         try:
234             logging.debug('Enable/Disable mount CD/DVD.')
235             time.sleep(1)
236             #This will fail with new firmware on OR18
237             self._run_ipmitool_raw_command('0x32 0xcb 0x00 0x0%s' %(str(int(enabled))))
238             return self._enable_disable_cd_dvd(enabled)
239         except Exception as err:
240             logging.warning('Exception when CD/DVD virtual media new firmware? ignoring... Error: %s', str(err))
241         return True
242
243     def _mount_virtual_device(self):
244         return self._toggle_virtual_device(True)
245
246     def _demount_virtual_device(self):
247         return self._toggle_virtual_device(False)
248
249     def _get_mounted_image_count(self):
250         count = 0
251         try:
252             out = self._run_ipmitool_raw_command('0x32 0xd8 0x00 0x01')
253             count = int(out[1], 16)
254             logging.warning('Available image count: %d', count)
255         except Exception as err:
256             logging.warning('Exception when trying to get the image count: %s', str(err))
257         return count
258
259     def _wait_for_mount_count(self):
260         # Poll until we got some images from server
261         _max_tries = 12
262         _try = 1
263         while self._get_mounted_image_count() == 0:
264             logging.debug('Check available images count try %d/%d', _try, _max_tries)
265             if _try > _max_tries:
266                 logging.warning('Available images count 0, attempts exceeded.')
267                 return False
268             time.sleep(10)
269             _try = _try + 1
270         return True
271
272     def _set_image_name(self, image_filename):
273         try:
274             logging.debug('Setting virtual media image: %s', image_filename)
275             self._run_ipmitool_raw_command('0x32 0xd7 0x01 0x01 0x01 0x01 %s' % (self._convert_to_hex(image_filename, True, 64)))
276         except Exception as err:
277             logging.debug('Exception when setting virtual media image: %s', str(err))
278             return False
279         return True
280
281     def _get_bmc_nfs_service_status(self):
282         # Check NFS Service Status
283         try:
284             out = self._run_ipmitool_raw_command('0x32 0xd8 0x06 0x01 0x01 0x00')
285             _image_name = str(bytearray.fromhex(''.join(out)))
286             return 'mounted'
287         except Exception:
288             return 'nfserror'
289
290     def _stop_remote_redirection(self):
291         # Get num of enabled devices
292         _num_inst = self._get_virtual_media_device_count('CD')
293         for driveindex in range(0, _num_inst):
294             cmd = '0x32 0xd7 0x00 0x01 0x01 0x00 %s' % hex(driveindex)
295             logging.debug('Stop redirection CD/DVD drive index %d', driveindex)
296             try:
297                 out = self._run_ipmitool_raw_command(cmd)
298                 logging.debug('ipmitool out = %s', (out))
299             except Exception as err:
300                 # Drive might not be mounted to start with
301                 logging.debug('_stop_remote_redirection: Ignoring exception when stopping redirection CD/DVD drive index %d error: %s', driveindex, str(err))
302
303     def _set_boot_from_virtual_media(self):
304         logging.debug('Set boot from cd (%s), and boot after that', self._host)
305         self._run_ipmitool_command('chassis bootdev cdrom options=persistent')
306
307         #logging.debug('Set boot from cd (%s), and boot after that', self._host)
308         #try:
309         #    self._run_ipmitool_raw_command('0x00 0x08 0x05 0xC0 0x20 0x00 0x00 0x00')
310         #except Exception as err:
311         #    logging.warning('Set Boot to CD failed: %s' % str(err))
312         #    raise BMCException('Set Boot to CD failed')
313
314     def _detach_virtual_media(self):
315         logging.debug('Detach virtual media')
316
317         #Enable virtual media
318         if not self._enable_virtual_media():
319             raise BMCException("detach_virtual_cd: Failed to enable virtual media")
320
321         # Restart Remote Image Service
322         if not self._restart_ris():
323             raise BMCException("Failed to restart RIS")
324
325         # Stop redirection
326         self._stop_remote_redirection()
327
328         #Clear RIS configuration
329         if not self._clear_ris_configuration():
330             raise BMCException("detach_virtual_cd: Failed to clear RIS configuration")
331
332         #Demount virtual device
333         if not self._demount_virtual_device():
334             raise BMCException('detach_virtual_cd: Exception when disabling CD/DVD virtual media')
335
336         # Reduce the number of virtual devices (both HD and CD default to 4 devices each)
337         if not self._set_virtual_media_device_count('HD', 0):
338             BMCException('Failed to set virtual media device count for HD')
339         if not self._set_virtual_media_device_count('CD', 1):
340             BMCException('Failed to set virtual media device count for CD')
341
342     def attach_virtual_cd(self, nfs_host, nfs_mount, boot_iso_filename):
343         # Detach first
344         self._detach_virtual_media()
345
346         logging.debug('Attach virtual media')
347
348         #Enable virtual media
349         if not self._enable_virtual_media():
350             raise BMCException("Failed to enable virtual media")
351
352         #Enable CD/DVD device
353         if not self._toggle_virtual_device(True):
354             raise BMCException("Failed to enable virtual device")
355
356         #Clear RIS configuration
357         if not self._clear_ris_configuration():
358             raise BMCException("Failed to clear RIS configuration")
359
360         #Setup nfs
361         if not self._set_setup_nfs(nfs_host, nfs_mount):
362             raise BMCException("Failed to setup nfs")
363
364         # Restart Remote Image CD
365         if not self._restart_ris_cd():
366             raise BMCException("Failed to restart RIS CD")
367
368         #Wait for device to be mounted
369         if not self._wait_for_mount_count():
370             raise BMCException("Failed when waiting for the device to appear")
371
372         # Set Image Name
373         time.sleep(2)
374         if not self._set_image_name(boot_iso_filename):
375             raise BMCException("Failed to set image name")
376
377         success = self._wait_for_bmc_nfs_service(90, 'mounted')
378         if success:
379             return True
380         else:
381             raise BMCException('NFS service setup failed')