1 # Copyright 2019 Cachengo
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.
19 from ironic.conductor import utils as manager_utils
20 from ironic.common import boot_devices
21 from ironic.common import exception
22 from ironic.drivers.modules import ipmitool
24 from ..openbmc_hw import OpenBMCIronicVirtMediaHW
26 RAW_CHECK_NFS_SERVICE_STATUS = '0x32 0xd8 0x06 0x01 0x01 0x00'
28 RAW_GET_VMEDIA_DEVICE_COUNT = '0x32 0xca %s' # (type)
29 RAW_SET_VMEDIA_DEVICE_COUNT = '0x32 0xcb %s %s' # (type, count)
30 ( VMEDIA_DEVICE_TYPE_CD,
31 VMEDIA_DEVICE_TYPE_FD,
32 VMEDIA_DEVICE_TYPE_HD ) = ( '0x04', '0x05', '0x06' )
34 RAW_GET_VMEDIA_MOUNT_STATUS = '0x32 0xca 0x00'
35 RAW_SET_VMEDIA_MOUNT_STATUS = '0x32 0xcb 0x00 %s'
37 RAW_GET_VMEDIA_STATUS = '0x32 0xca 0x08'
38 RAW_SET_VMEDIA_STATUS = '0x32 0xcb 0x08 %s'
39 RAW_RESTART_VMEDIA = '0x32 0xcb 0x0a 0x01'
41 # Remote Image Service commands
42 RAW_RESTART_RIS_CD = '0x32 0x9f 0x01 0x0b 0x01'
43 RAW_SET_RIS_NFS = '0x32 0x9f 0x01 0x05 0x00 0x6e 0x66 0x73 0x00 0x00 0x00'
44 RAW_SET_RIS_NFS_IP = '0x32 0x9f 0x01 0x02 0x00 %s'
45 RAW_SET_RIS_NFS_PATH = '0x32 0x9f 0x01 0x01 0x01 %s'
46 RAW_SET_RIS_PROGRESS = '0x32 0x9f 0x01 0x01 0x00 %s'
47 RAW_CLEAR_RIS_CONFIG = '0x32 0x9f 0x01 0x0d'
48 RAW_RESTART_RIS = '0x32 0x9f 0x08 0x0b'
50 RAW_GET_MOUNTED_IMG_COUNT = '0x32 0xd8 0x00 0x01'
51 RAW_SET_IMG_NAME = '0x32 0xd7 0x01 0x01 0x01 0x01 %s'
52 RAW_STOP_REDIRECT = '0x32 0xd7 0x01 0x01 0x01 0x00 %s'
54 class FALCON(OpenBMCIronicVirtMediaHW):
55 def __init__(self, log):
56 super(FALCON, self).__init__(log)
58 def get_disk_attachment_status(self, task):
59 # Check NFS Service Status
61 cmd = RAW_CHECK_NFS_SERVICE_STATUS
62 out, err = ipmitool.send_raw(task, cmd)
63 _image_name = str(bytearray.fromhex(out.replace('\n', '').strip()))
68 def _get_virtual_media_device_count(self, task, devicetype):
71 # Get num of enabled devices
72 if devicetype == 'CD':
73 _devparam = VMEDIA_DEVICE_TYPE_CD
74 self.log.debug('Get virtual CD count')
75 elif devicetype == 'FD':
76 _devparam = VMEDIA_DEVICE_TYPE_FD
77 self.log.debug('Get virtual FD count')
78 elif devicetype == 'HD':
79 _devparam = VMEDIA_DEVICE_TYPE_HD
80 self.log.debug('Get virtual HD count')
82 self.log.warning('Unknown device type "%s"' % devicetype)
85 cmd = RAW_GET_VMEDIA_DEVICE_COUNT % _devparam
86 out, err = ipmitool.send_raw(task, cmd)
87 _num_inst = int(out.strip())
88 self.log.debug('Number of enabled %s devices is %d' % (devicetype, _num_inst))
90 except Exception as err:
91 # Drive might not be mounted to start with
92 self.log.debug('Exception when getting number of enabled %s devices. error: %s' % (devicetype, str(err)))
95 def _set_virtual_media_device_count(self, task, devicetype, devicecount):
96 if not 0 <= devicecount <= 4:
97 self.log.warning('Number of devices must be in range 0 to 4')
100 if devicetype == 'CD':
101 _devparam = VMEDIA_DEVICE_TYPE_CD
102 self.log.debug('Setting virtual CD count to %d' % devicecount)
103 elif devicetype == 'HD':
104 _devparam = VMEDIA_DEVICE_TYPE_HD
105 self.log.debug('Setting virtual HD count to %d' % devicecount)
107 self.log.warning('_set_virtual_media_device_count: Unknown device type "%s"' % devicetype)
111 cmd = RAW_SET_VMEDIA_DEVICE_COUNT % (_devparam, hex(devicecount))
112 ipmitool.send_raw(task, cmd)
114 _conf_device_num = self._get_virtual_media_device_count(task, devicetype)
116 while _conf_device_num != devicecount and _tries > 0:
117 self.log.debug('Virtual %s count is %d expecting %d' % (devicetype, _conf_device_num, devicecount))
119 _conf_device_num = self._get_virtual_media_device_count(task, devicetype)
122 except Exception as err:
123 self.log.warning('Exception when setting virtual media device count, error: %s' % str(err))
127 def _check_virtual_media_started(self, task):
128 service_status = None
129 # check virtmedia service status
131 cmd = RAW_GET_VMEDIA_STATUS
132 out, err = ipmitool.send_raw(task, cmd)
133 service_status = out.strip()
134 self.log.warning('Virtual media service status: %s' % service_status)
135 except Exception as err:
136 self.log.warning('Exception when checking virtual media service: %s' % str(err))
138 return service_status == '01'
140 def _start_virtual_media(self, task):
141 # Enable "Remote Media Support"
143 cmd = RAW_SET_VMEDIA_STATUS % '0x01'
144 self.log.debug('Start virtual media service')
145 ipmitool.send_raw(task, cmd)
146 except Exception as err:
147 self.log.warning('Exception when starting virtual media service: %s' % str(err))
149 def _restart_virtual_media_service(self, task):
151 cmd = RAW_RESTART_VMEDIA
152 self.log.debug('Restart virtual media service')
153 ipmitool.send_raw(task, cmd)
154 except Exception as err:
155 self.log.warning('Exception when restarting virtual media service: %s' % str(err))
157 def _restart_ris(self, task):
158 # Restart Remote Image Service
160 self.log.debug('Restart Remote Image Service')
161 cmd = RAW_RESTART_RIS
162 ipmitool.send_raw(task, cmd)
163 except Exception as err:
164 self.log.warning('Exception when restarting RIS: %s' % str(err))
168 def _restart_ris_cd(self, task):
169 # Restart Remote Image Service CD media
171 self.log.debug('Restart Remote Image Service CD media')
172 cmd = RAW_RESTART_RIS_CD
173 ipmitool.send_raw(task, cmd)
174 except Exception as err:
175 self.log.warning('Exception when restarting RIS CD media: %s' % str(err))
179 def _enable_virtual_media(self, task):
180 # Speed up things if it service is already running
181 if self._check_virtual_media_started(task):
182 self.log.debug('Virtual media service already running.')
185 # Just enabling the service doe not seem to start it (in all HW)
186 # Resetting it after enabling helps
187 self._start_virtual_media(task)
188 self._restart_virtual_media_service(task)
192 if self._check_virtual_media_started(task):
197 self.log.warning('Ensure virtual media service start failed: attempts exceeded.')
200 def _set_nfs_server_ip(self, driver_info, task):
202 cmd = RAW_SET_RIS_NFS_IP % (self.hex_convert(driver_info['provisioning_server'], True, 63))
203 self.log.debug('Virtual media server "%s"' % driver_info['provisioning_server'])
204 ipmitool.send_raw(task, cmd)
205 except Exception as err:
206 self.log.warning('Exception when setting virtual media server: %s' % str(err))
209 def _set_share_type(self, task):
211 cmd = RAW_SET_RIS_NFS
212 self.log.debug('Virtual media share type to NFS.')
213 ipmitool.send_raw(task, cmd)
214 except Exception as err:
215 self.log.warning('Exception when setting virtual media service type NFS: %s' % str(err))
218 def _set_nfs_root_path(self, driver_info, task):
220 self.log.debug('Virtual media path to "%s"' % self.remote_share)
221 # set progress bit (hmm. seems to return error if it is already set.. So should check..)
222 # Welp there is no way checking this. As workaround clearing it first ( does not seem to
223 # return error even if alreay cleared).
225 cmd = RAW_SET_RIS_PROGRESS % '0x00'
226 ipmitool.send_raw(task, cmd)
229 cmd = RAW_SET_RIS_PROGRESS % '0x01'
230 ipmitool.send_raw(task, cmd)
233 cmd = RAW_SET_RIS_NFS_PATH % (self.hex_convert(self.remote_share, True, 64))
234 ipmitool.send_raw(task, cmd)
238 cmd = RAW_SET_RIS_PROGRESS % '0x00'
239 ipmitool.send_raw(task, cmd)
240 except Exception as err:
241 self.log.warning('Exception when setting virtual media path: %s' % str(err))
244 def _set_setup_nfs(self, driver_info, task):
247 self._set_share_type(task)
249 self._set_nfs_server_ip(driver_info, task)
250 # Set NFS Mount Root path
251 self._set_nfs_root_path(driver_info, task)
257 def _toggle_virtual_device(self, enabled, task):
258 # Enable "Mount CD/DVD" in GUI should cause vmedia restart withing 2 seconds.
259 # Seems "Mount CD/DVD" need to be enabled (or toggled) after config.
260 # refresh/vmedia restart is not enough(?)
262 status = '01' if enabled else '00'
264 cmd = RAW_SET_VMEDIA_MOUNT_STATUS % ('0x' + status)
265 self.log.debug('Set mount CD/DVD enable status %s' % status)
266 ipmitool.send_raw(task, cmd)
268 self.log.debug('Ensure CD/DVD enable status is %s' % status)
271 out, err = ipmitool.send_raw(task, RAW_GET_VMEDIA_MOUNT_STATUS)
274 self.log.debug('CD/DVD enable status is %s' % res)
281 except Exception as err:
282 self.log.warning('Exception when CD/DVD virtual media new firmware? ignoring... Error: %s' % str(err))
284 self.log.warning('Ensure virtual media status failed, attempts exceeded.')
287 def _mount_virtual_device(self, task):
288 return self._toggle_virtual_device(True, task)
290 def _demount_virtual_device(self, task):
291 return self._toggle_virtual_device(False, task)
293 def _get_mounted_image_count(self, task):
296 cmd = RAW_GET_MOUNTED_IMG_COUNT
297 out, err = ipmitool.send_raw(task, cmd)
300 count = int(data, 16)
301 self.log.debug('Available image count: %d' % count)
302 except Exception as err:
303 self.log.debug('Exception when trying to get the image count: %s' % str(err))
306 def _set_image_name(self, image_filename, task):
308 cmd = RAW_SET_IMG_NAME % (self.hex_convert(image_filename, True, 64))
309 self.log.debug('Setting virtual media image: %s' % image_filename)
310 ipmitool.send_raw(task, cmd)
311 except Exception as err:
312 self.log.debug('Exception when setting virtual media image: %s' % str(err))
316 def _stop_remote_redirection(self, task):
318 # Get num of enabled devices
319 _num_inst = self._get_virtual_media_device_count(task, 'CD')
320 for driveindex in range(0, _num_inst):
321 cmd = RAW_STOP_REDIRECT % hex(driveindex)
322 self.log.debug('Stop redirection CD/DVD drive index %d' % driveindex)
323 out, err = ipmitool.send_raw(task, cmd)
324 self.log.debug('ipmitool out = %s' % (out))
325 except Exception as err:
326 # Drive might not be mounted to start with
327 self.log.debug('_stop_remote_redirection: Ignoring exception when stopping redirection CD/DVD drive index %d error: %s' % (driveindex, str(err)))
330 def _clear_ris_configuration(self, task):
331 # Clear Remote Image Service configuration
333 cmd = RAW_CLEAR_RIS_CONFIG
334 self.log.debug('Clear Remote Image Service configuration.')
335 ipmitool.send_raw(task, cmd)
336 except Exception as err:
337 self.log.warning('Exception when clearing RIS NFS configuration: %s' % str(err))
341 def _wait_for_mount_count(self, task):
342 # Poll until we got some images from server
345 if self._get_mounted_image_count(task) > 0:
348 self.log.debug('Check available images count. Tries left: %d' % tries)
351 self.log.warning('Available images count is 0. Attempts exceeded.')
354 def attach_virtual_cd(self, image_filename, driver_info, task):
356 # Enable virtual media
357 if not self._enable_virtual_media(task):
358 self.log.error("Failed to enable virtual media")
361 # Enable CD/DVD device
362 if not self._toggle_virtual_device(True, task):
363 self.log.error("Failed to enable virtual device")
366 # Clear Remote Image Service configuration
367 if not self._clear_ris_configuration(task):
368 self.log.error("Failed to clear Remote Image Service configuration")
372 if not self._set_setup_nfs(driver_info, task):
373 self.log.error("Failed to setup nfs")
376 # Restart Remote Image CD
377 if not self._restart_ris_cd(task):
378 self.log.error("Failed to restart Remote Image Service CD")
381 # Wait for device to be mounted
382 if not self._wait_for_mount_count(task):
383 self.log.error("Failed when waiting for the device to appear")
387 if not self._set_image_name(image_filename, task):
388 self.log.error("Failed to set image name")
391 return self.check_and_wait_for_cd_mounting(image_filename, task, driver_info)
393 def detach_virtual_cd(self, driver_info, task):
394 """Detaches virtual cdrom on the node.
396 :param task: an ironic task object
398 # Enable virtual media
399 if not self._enable_virtual_media(task):
400 self.log.error("detach_virtual_cd: Failed to enable virtual media")
403 # Restart Remote Image Service
404 if not self._restart_ris(task):
405 self.log.error("Failed to restart Remote Image Service")
409 self._stop_remote_redirection(task)
411 # Clear Remote Image Service configuration
412 if not self._clear_ris_configuration(task):
413 self.log.error("detach_virtual_cd: Failed to clear RIS configuration")
416 # Unmount virtual device
417 if not self._demount_virtual_device(task):
418 self.log.error('detach_virtual_cd: Exception when disabling CD/DVD virtual media')
421 # Reduce the number of virtual devices (both HD and CD default to 4 devices each)
422 if not self._set_virtual_media_device_count(task, 'HD', 0):
424 if not self._set_virtual_media_device_count(task, 'CD', 1):
429 def set_boot_device(self, task):
430 manager_utils.node_set_boot_device(task, boot_devices.FLOPPY, persistent=True)