ManageIQ предоставляет разработчикам удобные методы для быстрой настройки виртуальных машин. Например, для добавления памяти достаточно вызвать метод set_memory на VM. Однако, мы обнаружили несколько проблем при работе на платформе VMware: метод add_disk не позволяет указать адрес подключения диска и не умеет создавать контроллер, а метод resize_disk и вовсе не работает.
Методов быстрой настройки VM немного, и они имеют как свои преимущества, так и недостатки.
Изменение размера диска
С нерабочим методом resize_disk оказалось всё просто. При вызове этого метода в логах появляются сообщения об ошибках:
ERROR -- evm: Q-task_id([r1000000000784_service_reconfigure_task_1000000000784]) [NameError]: undefined local variable or method 'device' for #<MiqVimVm:...>
...
ERROR -- evm: Q-task_id([r1000000000784_service_reconfigure_task_1000000000784]) /opt/manageiq/manageiq-gemset/gems/vmware_web_service-3.2.0/lib/VMwareWebService/MiqVimVm.rb:873:in 'resizeDisk'
Т.е. в методе resizeDisk синтаксическая ошибка (MiqVimVm.rb, строка 873): неопределённая переменная device (недостаточное покрытие кода тестами). Судя по всему, имелась в виду переменная disk, нужно просто исправить:
# в методе resizeDisk нужно закомментировать строку или заменить в строке
logger.debug "MiqVimVm::resizeDisk: backingFile = #{backingFile} current size = #{device.capacityInKB} newSize = #{newSizeInKb} KB"
# device.capacityInKB на disk.capacityInKB
logger.debug "MiqVimVm::resizeDisk: backingFile = #{backingFile} current size = #{disk.capacityInKB} newSize = #{newSizeInKb} KB"
Добавление диска
Новый диск создаётся успешно, но подключить его на определённый SCSI-адрес не получится, такого функционала нет. Выбор места подключения диска производится в строке 757 того же файла MiqVimVm.rb. Метод available_scsi_units возвращает список доступных SCSI-адресов и из данного списка берётся самый первый. Требуется доработка данного скрипта.
# замените строку выбора первого свободного юнита
ck, un = available_scsi_units.first
# строкой
ck, un = options[:location].present? ? getScsiUnits(options[:location]) : available_scsi_units.first
И добавьте новые методы:
#
# Find a SCSI controller by disk location and
# return its key and disk unit_number.
#
def getScsiUnits(location)
scsi_bus, disk_unit_number = location.split(':')
scsi_controller = getOrCreateScsiController(scsi_bus)
[scsi_controller["key"].to_i, disk_unit_number.to_i]
end
#
# Find a SCSI controller by SCSI bus number and return it.
#
def getScsiControllerByBusNumber(scsi_bus)
hardware ||= getHardware
getScsiControllers(hardware).find { |sk| sk["busNumber"] == scsi_bus }
end
#
# Find or create SCSI controller by SCSI bus number and return it.
#
def getOrCreateScsiController(scsi_bus)
scsi_controller = getScsiControllerByBusNumber(scsi_bus)
if scsi_controller.nil?
addScsiController(scsi_bus)
scsi_controller = getScsiControllerByBusNumber(scsi_bus)
end
raise "getOrCreateScsiController: no SCSI controller found" if scsi_controller.nil?
scsi_controller
end
#
# Create a new SCSI controller.
#
def addScsiController(bus_number = 1, device_type = 'ParaVirtualSCSIController')
logger.info "MiqVimVm::addScsiController: device_type = #{device_type} bus_number = #{bus_number}"
vmConfigSpec = VimHash.new("VirtualMachineConfigSpec") do |vmcs|
vmcs.deviceChange = VimArray.new("ArrayOfVirtualDeviceConfigSpec") do |vmcs_vca|
vmcs_vca << VimHash.new("VirtualDeviceConfigSpec") do |vdcs|
vdcs.operation = VirtualDeviceConfigSpecOperation::Add
vdcs.device = VimHash.new(device_type) do |dev|
dev.sharedBus = VimString.new('noSharing', 'VirtualSCSISharing')
dev.busNumber = bus_number
dev.key = -101
end
end
end
end
logger.info "MiqVimVm(#{@invObj.server}, #{@invObj.username}).addScsiController: calling reconfigVM_Task"
taskMor = @invObj.reconfigVM_Task(@vmMor, vmConfigSpec)
logger.info "MiqVimVm(#{@invObj.server}, #{@invObj.username}).addScsiController: returned from reconfigVM_Task"
waitForTask(taskMor)
end
Осталось только обеспечить передачу параметра location в метод addDisk. Проблема в том, что список передаваемых параметров специально ограничен, зачем это сделано – не совсем понятно, но будем придерживаться единого с разработчиками подхода:
В файле /opt/manageiq/manageiq-gemset/bundler/gems/manageiq-providers-vmware-*/app/models/manageiq/providers/vmware/infra_manager/vm_or_template_shared/operations/configuration.rb нужно добавить передачу параметра location в вызове метода run_command_via_parent в raw_add_disk:
def raw_add_disk(disk_name, disk_size_mb, options = {})
...
run_command_via_parent(:vm_add_disk, :diskName => disk_name, :diskSize => disk_size_mb,
:thinProvisioned => options[:thin_provisioned], :dependent => options[:dependent],
:persistent => options[:persistent], :bootable => options[:bootable], :datastore => datastore,
:interface => options[:interface], :location => options[:location])
end
И в файле /opt/manageiq/manageiq-gemset/bundler/gems/manageiq-providers-vmware-*/app/models/manageiq/providers/vmware/infra_manager.rb добавить передачу параметра location в вызове invoke_vim_ws в методе vm_add_disk:
def vm_add_disk(vm, options = {})
invoke_vim_ws(:addDisk, vm, options[:user_event], options[:diskName], options[:diskSize], nil, nil,
:thin_provisioned => options[:thinProvisioned], :dependent => options[:dependent],
:persistent => options[:persistent], :location => options[:location])
end
Внимание! Внесение изменений в исходном коде потребует перезапуска сервера для их применения, также потребуется вносить эти изменения в код заново после каждого обновления MIQ.
Файлы к статье
manageiq-vm-disks2.zip – полный код всех изменённых скриптов.