Skip to content

Commit

Permalink
Add support for ipmi get/set boot device command
Browse files Browse the repository at this point in the history
  • Loading branch information
kurokobo committed Sep 8, 2020
1 parent 7fddb14 commit 2380859
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 47 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Added
- N/A

## [0.0.5] - 2020-09-09
### Added
- Add `chassis bootdev pxe|disk|cdrom` command support
- Add `chassis bootparam get 5` command support

## [0.0.4] - 2020-09-06
### Added
- Add docker support
Expand Down Expand Up @@ -37,7 +42,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Project starts based on the copy of [VirtualBMC 2.1.0.dev](https://github.com/openstack/virtualbmc/commit/c4c8edb66bc49fcb1b8fb41af77546e06d2e8bce)


[Unreleased]: https://github.com/kurokobo/virtualbmc-for-vsphere/compare/0.0.4...HEAD
[Unreleased]: https://github.com/kurokobo/virtualbmc-for-vsphere/compare/0.0.5...HEAD
[0.0.5]: https://github.com/kurokobo/virtualbmc-for-vsphere/compare/0.0.4...0.0.5
[0.0.4]: https://github.com/kurokobo/virtualbmc-for-vsphere/compare/0.0.3...0.0.4
[0.0.3]: https://github.com/kurokobo/virtualbmc-for-vsphere/compare/0.0.2...0.0.3
[0.0.2]: https://github.com/kurokobo/virtualbmc-for-vsphere/compare/0.0.1...0.0.2
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ ipmitool -I lanplus -U admin -P password -H 192.168.0.1 -p 6230 power on|off|sof
# Check the power status
ipmitool -I lanplus -U admin -P password -H 192.168.0.1 -p 6230 power status

# Set the boot device to network, dick or cdrom
ipmitool -I lanplus -U admin -P password -H 192.168.0.1 -p 6230 chassis bootdev pxe|disk|cdrom

# Get the current boot device
ipmitool -I lanplus -U admin -P password -H 192.168.0.1 -p 6230 chassis bootparam get 5

# Get the channel info. Note that its output is always a dummy, not actual information.
ipmitool -I lanplus -U admin -P password -H 192.168.0.1 -p 6230 channel info

Expand All @@ -53,8 +59,6 @@ ipmitool -I lanplus -U admin -P password -H 192.168.0.1 -p 6230 lan print 1
Not Implemented yet:

* Inject NMI: `power diag`
* Set the boot device to network, hd or cdrom: `chassis bootdev pxe|disk|cdrom`
* Get the current boot device: `chassis bootparam get 5`


## Architecture
Expand Down
2 changes: 1 addition & 1 deletion docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ version: '3'
services:
vbmc4vsphere:
container_name: vbmc4vsphere
image: ghcr.io/kurokobo/vbmc4vsphere:0.0.4
image: ghcr.io/kurokobo/vbmc4vsphere:0.0.5
networks:
- vbmc-network
ports:
Expand Down
72 changes: 72 additions & 0 deletions vbmc4vsphere/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,78 @@ def get_viserver_vm(conn, vm):
raise exception.VMNotFound(vm=vm)


def get_bootable_device_type(conn, boot_dev):
if isinstance(boot_dev, vim.vm.BootOptions.BootableFloppyDevice):
return "floppy"
elif isinstance(boot_dev, vim.vm.BootOptions.BootableDiskDevice):
return "disk"
elif isinstance(boot_dev, vim.vm.BootOptions.BootableCdromDevice):
return "cdrom"
elif isinstance(boot_dev, vim.vm.BootOptions.BootableEthernetDevice):
return "ethernet"


def set_boot_order(conn, vm, device):
"""Set boot device to specified device.
https://github.com/ansible-collections/vmware/blob/main/plugins/module_utils/vmware.py
"""

boot_order_list = []
if device == "cdrom":
bootable_cdroms = [
dev
for dev in vm.config.hardware.device
if isinstance(dev, vim.vm.device.VirtualCdrom)
]
if bootable_cdroms:
boot_order_list.append(vim.vm.BootOptions.BootableCdromDevice())
elif device == "disk":
bootable_disks = [
dev
for dev in vm.config.hardware.device
if isinstance(dev, vim.vm.device.VirtualDisk)
]
if bootable_disks:
boot_order_list.extend(
[
vim.vm.BootOptions.BootableDiskDevice(deviceKey=bootable_disk.key)
for bootable_disk in bootable_disks
]
)
elif device == "ethernet":
bootable_ethernets = [
dev
for dev in vm.config.hardware.device
if isinstance(dev, vim.vm.device.VirtualEthernetCard)
]
if bootable_ethernets:
boot_order_list.extend(
[
vim.vm.BootOptions.BootableEthernetDevice(
deviceKey=bootable_ethernet.key
)
for bootable_ethernet in bootable_ethernets
]
)
elif device == "floppy":
bootable_floppy = [
dev
for dev in vm.config.hardware.device
if isinstance(dev, vim.vm.device.VirtualFloppy)
]
if bootable_floppy:
boot_order_list.append(vim.vm.BootOptions.BootableFloppyDevice())

kwargs = dict()
kwargs.update({"bootOrder": boot_order_list})

vm_conf = vim.vm.ConfigSpec()
vm_conf.bootOptions = vim.vm.BootOptions(**kwargs)
vm.ReconfigVM_Task(vm_conf)
return


def check_viserver_connection_and_vm(vi, vm, vi_username=None, vi_password=None):
with viserver_open(
vi, readonly=True, vi_username=vi_username, vi_password=vi_password
Expand Down
83 changes: 40 additions & 43 deletions vbmc4vsphere/vbmc.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,17 @@

# Boot device maps
GET_BOOT_DEVICES_MAP = {
"network": 4,
"hd": 8,
"ethernet": 0x4,
"disk": 0x8,
"cdrom": 0x14,
"floppy": 0x3C,
}

SET_BOOT_DEVICES_MAP = {
"network": "network",
"hd": "hd",
"network": "ethernet",
"hd": "disk",
"optical": "cdrom",
"floppy": "floppy",
}


Expand Down Expand Up @@ -194,14 +196,23 @@ def __init__(

def get_boot_device(self):
LOG.debug("Get boot device called for %(vm)s", {"vm": self.vm_name})
return IPMI_COMMAND_NODE_BUSY
# with utils.libvirt_open(readonly=True, **self._conn_args) as conn:
# vm = utils.get_libvirt_vm(conn, self.vm_name)
# boot_element = ET.fromstring(vm.XMLDesc()).find('.//os/boot')
# boot_dev = None
# if boot_element is not None:
# boot_dev = boot_element.attrib.get('dev')
# return GET_BOOT_DEVICES_MAP.get(boot_dev, 0)
try:
with utils.viserver_open(**self._conn_args) as conn:
vm = utils.get_viserver_vm(conn, self.vm_name)
boot_element = vm.config.bootOptions.bootOrder
boot_dev = None
if boot_element:
boot_dev = utils.get_bootable_device_type(conn, boot_element[0])
LOG.debug("Boot device is: %s" % boot_dev)
return GET_BOOT_DEVICES_MAP.get(boot_dev, 0)
return IPMI_COMMAND_NODE_BUSY
except Exception as e:
msg = "Error getting boot device of vm %(vm)s. " "Error: %(error)s" % {
"vm": self.vm_name,
"error": e,
}
LOG.error(msg)
raise exception.VirtualBMCError(message=msg)

def _remove_boot_elements(self, parent_element):
for boot_element in parent_element.findall("boot"):
Expand All @@ -212,37 +223,23 @@ def set_boot_device(self, bootdevice):
"Set boot device called for %(vm)s with boot " 'device "%(bootdev)s"',
{"vm": self.vm_name, "bootdev": bootdevice},
)
return IPMI_COMMAND_NODE_BUSY
# device = SET_BOOT_DEVICES_MAP.get(bootdevice)
# if device is None:
# # Invalid data field in request
# return IPMI_INVALID_DATA
#
# try:
# with utils.libvirt_open(**self._conn_args) as conn:
# vm = utils.get_libvirt_vm(conn, self.vm_name)
# tree = ET.fromstring(vm.XMLDesc())
#
# # Remove all "boot" element under "devices"
# # They are mutually exclusive with "os/boot"
# for device_element in tree.findall('devices/*'):
# self._remove_boot_elements(device_element)
#
# for os_element in tree.findall('os'):
# # Remove all "boot" elements under "os"
# self._remove_boot_elements(os_element)
#
# # Add a new boot element with the request boot device
# boot_element = ET.SubElement(os_element, 'boot')
# boot_element.set('dev', device)
#
# conn.defineXML(ET.tostring(tree, encoding="unicode"))
# except libvirt.libvirtError:
# LOG.error('Failed setting the boot device %(bootdev)s for '
# 'vm %(vm)s', {'bootdev': device,
# 'vm': self.vm_name})
# # Command failed, but let client to retry
# return IPMI_COMMAND_NODE_BUSY
device = SET_BOOT_DEVICES_MAP.get(bootdevice)
if device is None:
# Invalid data field in request
return IPMI_INVALID_DATA
try:
with utils.viserver_open(**self._conn_args) as conn:
vm = utils.get_viserver_vm(conn, self.vm_name)
utils.set_boot_order(conn, vm, device)
return IPMI_COMMAND_NODE_BUSY
except Exception as e:
LOG.error(
"Failed setting the boot device %(bootdev)s for vm %(vm)s."
"Error: %(error)s",
{"bootdev": device, "vm": self.vm_name, "error": e},
)
# Command failed, but let client to retry
return IPMI_COMMAND_NODE_BUSY

def get_power_state(self):
LOG.debug("Get power state called for vm %(vm)s", {"vm": self.vm_name})
Expand Down

0 comments on commit 2380859

Please sign in to comment.