4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
18 from .bmctools import BMC
20 RAW_CHECK_NFS_SERVICE_STATUS = '0x32 0xd8 0x06 0x01 0x01 0x00'
22 RAW_GET_VMEDIA_DEVICE_COUNT = '0x32 0xca %s' # (type)
23 RAW_SET_VMEDIA_DEVICE_COUNT = '0x32 0xcb %s %s' # (type, count)
24 ( VMEDIA_DEVICE_TYPE_CD,
25 VMEDIA_DEVICE_TYPE_FD,
26 VMEDIA_DEVICE_TYPE_HD ) = ('0x04', '0x05', '0x06')
28 RAW_GET_VMEDIA_MOUNT_STATUS = '0x32 0xca 0x00'
29 RAW_SET_VMEDIA_MOUNT_STATUS = '0x32 0xcb 0x00 %s'
31 RAW_GET_VMEDIA_STATUS = '0x32 0xca 0x08'
32 RAW_SET_VMEDIA_STATUS = '0x32 0xcb 0x08 %s'
33 RAW_RESTART_VMEDIA = '0x32 0xcb 0x0a 0x01'
35 # Remote Image Service commands
36 RAW_RESTART_RIS_CD = '0x32 0x9f 0x01 0x0b 0x01'
37 RAW_SET_RIS_NFS = '0x32 0x9f 0x01 0x05 0x00 0x6e 0x66 0x73 0x00 0x00 0x00'
38 RAW_SET_RIS_NFS_IP = '0x32 0x9f 0x01 0x02 0x00 %s'
39 RAW_SET_RIS_NFS_PATH = '0x32 0x9f 0x01 0x01 0x01 %s'
40 RAW_SET_RIS_PROGRESS = '0x32 0x9f 0x01 0x01 0x00 %s'
41 RAW_CLEAR_RIS_CONFIG = '0x32 0x9f 0x01 0x0d'
42 RAW_RESTART_RIS = '0x32 0x9f 0x08 0x0b'
44 RAW_GET_MOUNTED_IMG_COUNT = '0x32 0xd8 0x00 0x01'
45 RAW_SET_IMG_NAME = '0x32 0xd7 0x01 0x01 0x01 0x01 %s'
46 RAW_STOP_REDIRECT = '0x32 0xd7 0x01 0x01 0x01 0x00 %s'
48 class BMCException(Exception):
52 def __init__(self, host, user, passwd, priv_level='ADMINISTRATOR', log_path=None):
53 super(FALCON, self).__init__(host, user, passwd, priv_level, log_path)
55 def _clear_ris_configuration(self):
56 # Clear Remote Image Service configuration
58 logging.debug('Clear RIS configuration.')
59 self._run_ipmitool_raw_command(RAW_CLEAR_RIS_CONFIG)
60 except Exception as err:
61 logging.warning('Exception when clearing RIS NFS configuration: %s', str(err))
65 def _check_virtual_media_started(self):
66 # Check virtmedia service status
68 out = self._run_ipmitool_raw_command(RAW_GET_VMEDIA_STATUS)
69 service_status = out[0]
70 logging.debug('Virtual media service status: %s', service_status)
71 except Exception as err:
72 logging.warning('Exception when checking virtual media service: %s', str(err))
74 return service_status == '01'
76 def _start_virtual_media(self):
77 # Enable "Remote Media Support" in GUI (p145)
79 logging.debug('Start virtual media service')
80 self._run_ipmitool_raw_command(RAW_SET_VMEDIA_STATUS % '0x01')
81 except Exception as err:
82 logging.warning('Exception when starting virtual media service: %s', str(err))
84 def _set_setup_nfs(self, nfs_host, mount_path):
88 logging.debug('Virtual media share type to NFS.')
89 self._run_ipmitool_raw_command(RAW_SET_RIS_NFS)
90 except Exception as err:
91 logging.warning('Exception when setting virtual media service type NFS: %s', str(err))
96 cmd = RAW_SET_RIS_NFS_IP % (self._convert_to_hex(nfs_host, True, 63))
97 logging.debug('Virtual media server "%s"', nfs_host)
98 self._run_ipmitool_raw_command(cmd)
99 except Exception as err:
100 logging.warning('Exception when setting virtual media server: %s', str(err))
103 # Set NFS Mount Root path
105 logging.debug('Virtual media path to "%s"', mount_path)
107 self._run_ipmitool_raw_command(RAW_SET_RIS_PROGRESS % '0x00')
109 self._run_ipmitool_raw_command(RAW_SET_RIS_PROGRESS % '0x01')
111 self._run_ipmitool_raw_command(RAW_SET_RIS_NFS_PATH % (self._convert_to_hex(mount_path, True, 64)))
113 self._run_ipmitool_raw_command(RAW_SET_RIS_PROGRESS % '0x00')
115 except Exception as err:
116 logging.warning('Exception when setting virtual media path: %s', str(err))
120 def _enable_virtual_media(self):
121 # Speed up things if it service is already running
122 if self._check_virtual_media_started():
123 logging.debug('Virtual media service already running.')
126 # Just enabling the service does not seem to start it (in all HW)
127 # Resetting it after enabling helps
128 self._start_virtual_media()
129 self._restart_virtual_media_service()
133 if self._check_virtual_media_started():
138 logging.warning('Ensure virtual media service start failed: attempts exceeded.')
141 def _get_virtual_media_device_count(self, devicetype):
144 # Get num of enabled devices
145 if devicetype == 'CD':
146 _devparam = VMEDIA_DEVICE_TYPE_CD
147 logging.debug('Get virtual CD count')
148 elif devicetype == 'FD':
149 _devparam = VMEDIA_DEVICE_TYPE_FD
150 logging.debug('Get virtual FD count')
151 elif devicetype == 'HD':
152 _devparam = VMEDIA_DEVICE_TYPE_HD
153 logging.debug('Get virtual HD count')
155 logging.warning('Unknown device type "%s"', devicetype)
158 cmd = RAW_GET_VMEDIA_DEVICE_COUNT % _devparam
159 out = self._run_ipmitool_raw_command(cmd)
160 _num_inst = int(out[0], 16)
161 logging.debug('Number of enabled %s devices is %d', devicetype, _num_inst)
163 except Exception as err:
164 raise BMCException('Exception when getting number of enabled %s devices. error: %s' % (devicetype, str(err)))
166 def _set_virtual_media_device_count(self, devicetype, devicecount):
167 if not 0 <= devicecount <= 4:
168 logging.warning('Number of devices must be in range 0 to 4')
171 if devicetype == 'CD':
172 _devparam = VMEDIA_DEVICE_TYPE_CD
173 logging.debug('Setting virtual CD count to %d', devicecount)
174 elif devicetype == 'HD':
175 _devparam = VMEDIA_DEVICE_TYPE_HD
176 logging.debug('Setting virtual HD count to %d', devicecount)
178 logging.warning('Unknown device type "%s"', devicetype)
182 cmd = RAW_SET_VMEDIA_DEVICE_COUNT % (_devparam, hex(devicecount))
183 self._run_ipmitool_raw_command(cmd)
185 _conf_device_num = self._get_virtual_media_device_count(devicetype)
187 while _conf_device_num != devicecount and _tries > 0:
188 logging.debug('Virtual %s count is %d expecting %d', devicetype, _conf_device_num, devicecount)
190 _conf_device_num = self._get_virtual_media_device_count(devicetype)
193 except Exception as err:
194 raise BMCException('Exception when setting virtual media device count : %s' % str(err))
197 def _restart_virtual_media_service(self):
199 cmd = RAW_RESTART_VMEDIA
200 logging.debug('Restart virtual media service')
201 self._run_ipmitool_raw_command(cmd)
202 except Exception as err:
203 raise BMCException('Exception when restarting virtual media service: %s' % str(err))
205 def _restart_ris(self):
207 cmd = RAW_RESTART_RIS
208 logging.debug('Restart RIS')
209 self._run_ipmitool_raw_command(cmd)
210 except Exception as err:
211 raise BMCException('Exception when restarting RIS: %s' % str(err))
215 def _restart_ris_cd(self):
217 cmd = RAW_RESTART_RIS_CD
218 logging.debug('Restart RIS CD media')
219 self._run_ipmitool_raw_command(cmd)
220 except Exception as err:
221 raise BMCException('Exception when restarting RIS CD media: %s' % str(err))
225 def _check_vmedia_mount_state(self, enabled):
226 expected_state = 'enabled' if enabled else 'disabled'
227 logging.debug('Check if CD/DVD device is %s', expected_state)
232 out = self._run_ipmitool_raw_command(RAW_GET_VMEDIA_MOUNT_STATUS)
234 logging.debug('Virtual media mount status: %s', status)
235 except Exception as err:
237 logging.warning('Exception when checking VMedia mount status: %s', str(err))
239 matched_state = (status == '01') if enabled else (status == '00')
241 # Virtual media mount found in expected state
247 logging.warning('Failed: CD/DVD mount is not %s (attempts exceeded).'
248 'Ignoring and trying to continue.',
252 def _toggle_virtual_device(self, enabled):
253 state_raw = '0x01' if enabled else '0x00'
254 state_str = 'enable' if enabled else 'disable'
256 logging.debug('Try to %s VMedia mount.', state_str)
258 self._run_ipmitool_raw_command(RAW_SET_VMEDIA_MOUNT_STATUS % state_raw)
260 return self._check_vmedia_mount_state(enabled)
261 except Exception as err:
262 logging.warning('Exception when tying to %s VMedia mount: %s. Ignoring... ',
266 def _mount_virtual_device(self):
267 return self._toggle_virtual_device(True)
269 def _demount_virtual_device(self):
270 return self._toggle_virtual_device(False)
272 def _get_mounted_image_count(self):
275 out = self._run_ipmitool_raw_command(RAW_GET_MOUNTED_IMG_COUNT)
276 count = int(out[0], 16)
277 logging.warning('Available image count: %d', count)
278 except Exception as err:
279 logging.warning('Exception when trying to get the image count: %s', str(err))
282 def _wait_for_mount_count(self):
283 # Poll until we got some images from server
286 if self._get_mounted_image_count() > 0:
289 logging.debug('Check available images count tries left: %d', tries)
292 logging.warning('Available images count 0, attempts exceeded.')
295 def _set_image_name(self, image_filename):
297 logging.debug('Setting virtual media image: %s', image_filename)
298 self._run_ipmitool_raw_command(RAW_SET_IMG_NAME % self._convert_to_hex(image_filename, True, 64))
299 except Exception as err:
300 logging.debug('Exception when setting virtual media image: %s', str(err))
304 def _get_bmc_nfs_service_status(self):
306 out = self._run_ipmitool_raw_command(RAW_CHECK_NFS_SERVICE_STATUS)
307 _image_name = str(bytearray.fromhex(''.join(out)))
308 logging.debug('Found mounted image: %s', _image_name)
313 def _stop_remote_redirection(self):
314 _num_inst = self._get_virtual_media_device_count('CD')
315 for driveindex in range(0, _num_inst):
316 cmd = RAW_STOP_REDIRECT % hex(driveindex)
317 logging.debug('Stop redirection CD/DVD drive index %d', driveindex)
319 out = self._run_ipmitool_raw_command(cmd)
320 logging.debug('ipmitool out = "%s"', out)
321 except Exception as err:
322 # Drive might not be mounted to start with
323 logging.debug('Ignoring exception when stopping redirection CD/DVD drive index %d error: %s',
324 driveindex, str(err))
326 def _set_boot_from_virtual_media(self):
327 logging.debug('Set boot from cd (%s), and boot after that', self._host)
329 self._run_ipmitool_command('chassis bootdev floppy options=persistent')
330 except Exception as err:
331 raise BMCException('Set Boot to CD failed: %s' % str(err))
333 def _detach_virtual_media(self):
334 logging.debug('Detach virtual media')
336 #Enable virtual media
337 if not self._enable_virtual_media():
338 raise BMCException("detach_virtual_cd: Failed to enable virtual media")
340 # Restart Remote Image Service
341 if not self._restart_ris():
342 raise BMCException("Failed to restart RIS")
345 self._stop_remote_redirection()
347 #Clear RIS configuration
348 if not self._clear_ris_configuration():
349 raise BMCException("detach_virtual_cd: Failed to clear RIS configuration")
351 #Demount virtual device
352 if not self._demount_virtual_device():
353 raise BMCException('detach_virtual_cd: Exception when disabling CD/DVD virtual media')
355 # Reduce the number of virtual devices (both HD and CD default to 4 devices each)
356 if not self._set_virtual_media_device_count('HD', 0):
357 BMCException('Failed to set virtual media device count for HD')
358 if not self._set_virtual_media_device_count('CD', 1):
359 BMCException('Failed to set virtual media device count for CD')
361 def attach_virtual_cd(self, nfs_host, nfs_mount, boot_iso_filename):
363 self._detach_virtual_media()
365 logging.debug('Attach virtual media')
367 #Enable virtual media
368 if not self._enable_virtual_media():
369 raise BMCException("Failed to enable virtual media")
371 #Enable CD/DVD device
372 if not self._toggle_virtual_device(True):
373 raise BMCException("Failed to enable virtual device")
375 #Clear RIS configuration
376 if not self._clear_ris_configuration():
377 raise BMCException("Failed to clear RIS configuration")
380 if not self._set_setup_nfs(nfs_host, nfs_mount):
381 raise BMCException("Failed to setup nfs")
383 # Restart Remote Image CD
384 if not self._restart_ris_cd():
385 raise BMCException("Failed to restart RIS CD")
387 #Wait for device to be mounted
388 if not self._wait_for_mount_count():
389 raise BMCException("Failed when waiting for the device to appear")
393 if not self._set_image_name(boot_iso_filename):
394 raise BMCException("Failed to set image name")
396 success = self._wait_for_bmc_nfs_service(90, 'mounted')
400 raise BMCException('NFS service setup failed')