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