app/controllers/mixins/actions/vm_actions/reconfigure.rb
module Mixins
module Actions
module VmActions
module Reconfigure
def reconfigure(reconfigure_ids = [])
@sb[:explorer] = true if @explorer
@request_id = nil
@in_a_form = @reconfigure = true
drop_breadcrumb(:name => _("Reconfigure"), :url => "/vm_common/reconfigure")
reconfigure_ids = params[:rec_ids] if params[:rec_ids]
@request_id = params[:req_id] if params[:req_id]
@reconfigitems = find_records_with_rbac(Vm, reconfigure_ids)
build_targets_hash(@reconfigitems)
@force_no_grid_xml = true
@view, @pages = get_view(Vm, :view_suffix => "VmReconfigureRequest", :selected_ids => reconfigure_ids) # Get the records (into a view) and the paginator
get_reconfig_limits(reconfigure_ids)
if @reconfigitems.size == 1
vm = @reconfigitems.first
# NOTE: available_vlans is ovirt specific vlan lookup
@vlan_options = vm.try(:available_vlans) || get_vlan_options(vm.host)
@avail_adapter_names = vm.try(:available_adapter_names) || []
@iso_options = get_iso_options(vm)
end
unless @explorer
render :action => "show"
end
end
def reconfigure_update
case params[:button]
when "cancel" then reconfigure_handle_cancel_button
when "submit" then reconfigure_handle_submit_button
end
end
def reconfigure_form_fields
@request_id, request_data = params[:id].split(/\s*,\s*/, 2)
reconfigure_ids = request_data.split(/\s*,\s*/)
request_hash = build_reconfigure_hash(reconfigure_ids)
render :json => request_hash
end
# reconfiguration for VMs only
def vm_reconfigure
assert_privileges('vm_reconfigure_all', :any => true)
# if coming in to edit from miq_request list view
recs = checked_or_params
if !session[:checked_items].nil? && (@lastaction == "set_checked_items" || params[:pressed] == "miq_request_edit")
recs = session[:checked_items]
request_id = params[:id]
end
if recs.empty?
add_flash(_("One or more %{model} must be selected to Reconfigure") %
{:model => Dictionary.gettext(db.to_s, :type => :model, :notfound => :titleize, :plural => true)}, :error)
javascript_flash
return
else
if VmOrTemplate.includes_template?(recs)
add_flash(_("Reconfigure does not apply because you selected at least one VM Template"), :error)
javascript_flash
return
end
unless VmOrTemplate.reconfigurable?(recs)
add_flash(_("Reconfigure does not apply because you selected at least one un-reconfigurable VM"), :error)
javascript_flash
return
end
reconfigure_ids = recs.collect(&:to_i)
end
if @explorer
reconfigure(reconfigure_ids)
session[:changed] = true # need to enable submit button when screen loads
@refresh_partial = "vm_common/reconfigure"
else
# redirect to build the ownership screen
javascript_redirect(:controller => 'vm', :action => 'reconfigure', :req_id => request_id, :rec_ids => reconfigure_ids, :escape => false)
end
end
def get_reconfig_limits(reconfigure_ids)
@reconfig_limits = VmReconfigureRequest.request_limits(:src_ids => reconfigure_ids)
mem1, fmt1 = reconfigure_calculations(@reconfig_limits[:min__vm_memory])
mem2, fmt2 = reconfigure_calculations(@reconfig_limits[:max__vm_memory])
@reconfig_memory_note = _("Between %{min} and %{max}") % {:min => "#{mem1}#{fmt1}", :max => "#{mem2}#{fmt2}"}
@socket_options = []
@reconfig_limits[:max__number_of_sockets].times do |tidx|
idx = tidx + @reconfig_limits[:min__number_of_sockets]
@socket_options.push(idx) if idx <= @reconfig_limits[:max__number_of_sockets]
end
@cores_options = []
@reconfig_limits[:max__cores_per_socket].times do |tidx|
idx = tidx + @reconfig_limits[:min__cores_per_socket]
@cores_options.push(idx) if idx <= @reconfig_limits[:max__cores_per_socket]
end
end
# Build the reconfigure data hash
def build_reconfigure_hash(reconfigure_ids)
@req = nil
@reconfig_values = {}
if @request_id == 'new'
@reconfig_values = get_reconfig_info(reconfigure_ids)
else
@req = MiqRequest.find_by(:id => @request_id)
@reconfig_values[:src_ids] = @req.options[:src_ids]
@reconfig_values[:memory], @reconfig_values[:memory_type] = @req.options[:vm_memory] ? reconfigure_calculations(@req.options[:vm_memory]) : ['', '']
@reconfig_values[:cores_per_socket_count] = @req.options[:cores_per_socket] ? @req.options[:cores_per_socket].to_s : ''
@reconfig_values[:socket_count] = @req.options[:number_of_sockets] ? @req.options[:number_of_sockets].to_s : ''
# check if there is only one VM that supports disk reconfiguration
@reconfig_values[:disk_add] = @req.options[:disk_add]
@reconfig_values[:disk_resize] = @req.options[:disk_resize]
@reconfig_values[:cdrom_connect] = @req.options[:cdrom_connect]
@reconfig_values[:cdrom_disconnect] = @req.options[:cdrom_disconnect]
vmdisks = []
vmcdroms = []
@req.options[:disk_add]&.each do |disk|
adsize, adunit = reconfigure_calculations(disk[:disk_size_in_mb])
vmdisks << {:hdFilename => disk[:disk_name],
:hdType => disk[:thin_provisioned] ? 'thin' : 'thick',
:hdMode => disk[:persistent] ? 'persistent' : 'nonpersistent',
:hdSize => adsize.to_s,
:hdUnit => adunit,
:new_controller_type => disk[:new_controller_type].to_s,
:cb_dependent => disk[:dependent],
:cb_bootable => disk[:bootable],
:add_remove => 'add'}
end
reconfig_item = Vm.find(reconfigure_ids)
if reconfig_item
reconfig_item.first.hardware.disks.each do |disk|
next if disk.device_type != 'disk'
removing = ''
delbacking = false
if disk.filename && @req.options[:disk_remove]
@req.options[:disk_remove].each do |remdisk|
if remdisk[:disk_name] == disk.filename
removing = 'remove'
delbacking = remdisk[:delete_backing]
end
end
end
dsize, dunit = reconfigure_calculations(disk.size / (1024 * 1024))
vmdisk = {:hdFilename => disk.filename,
:hdType => disk.disk_type.to_s,
:hdMode => disk.mode.to_s,
:hdSize => dsize.to_s,
:hdUnit => dunit.to_s,
:delete_backing => delbacking,
:cb_bootable => disk.bootable,
:add_remove => removing}
vmdisks << vmdisk
end
cdroms = reconfig_item.first.hardware.cdroms
if cdroms.present?
vmcdroms = build_request_cdroms_list(cdroms)
end
end
@reconfig_values[:disks] = vmdisks
@reconfig_values[:cdroms] = vmcdroms
end
@reconfig_values[:cb_memory] = !!(@req && @req.options[:vm_memory]) # default for checkbox is false for new request
@reconfig_values[:cb_cpu] = !!(@req && (@req.options[:number_of_sockets] || @req.options[:cores_per_socket])) # default for checkbox is false for new request
@reconfig_values
end
def build_request_cdroms_list(cdroms)
vmcdroms = []
connect_disconnect = ''
cdroms.map do |cd|
id = cd.id
device_name = cd.device_name
type = cd.device_type
filename = cd.filename
storage_id = cd.storage_id
if cd.filename && @req.options[:cdrom_disconnect]&.find { |d_cd| d_cd[:device_name] == cd.device_name }
filename = ''
connect_disconnect = 'disconnect'
end
conn_cd = @req.options[:cdrom_connect]&.find { |c_cd| c_cd[:device_name] == cd.device_name }
if cd.filename && conn_cd
filename = conn_cd[:filename]
connect_disconnect = 'connect'
end
vmcdroms << {:id => id, :device_name => device_name, :filename => filename, :type => type, :storage_id => storage_id, :connect_disconnect => connect_disconnect}
end
vmcdroms
end
def reconfigure_calculations(mbsize)
humansize = mbsize
fmt = "MB"
if mbsize.to_i > 1024 && (mbsize.to_i % 1024).zero?
humansize = mbsize.to_i / 1024
fmt = "GB"
end
return humansize.to_s, fmt
end
# determine available switches for this host...
def get_vlan_options(host)
switch_ids = Rbac.filtered(host.switches).pluck(:id)
Rbac.filtered(Lan.where(:switch_id => switch_ids).order(:name)).pluck(:name)
end
def get_iso_options(vm)
iso_options = []
return iso_options unless vm&.host&.storages
datastore_ids = vm.host.storages.pluck(:id)
# determine available iso files for the datastores
Rbac.filtered(StorageFile.where("storage_id IN (?) and ext_name = 'iso'", datastore_ids)).each do |sf|
iso_options << [sf.name, sf.name + ',' + sf.storage_id.to_s]
end
iso_options
end
def get_reconfig_info(reconfigure_ids)
@reconfigureitems = Vm.find(reconfigure_ids).sort_by(&:name)
# set memory to nil if multiple items were selected with different mem_cpu values
memory = @reconfigureitems.first.mem_cpu
memory = nil unless @reconfigureitems.all? { |vm| vm.mem_cpu == memory }
socket_count = @reconfigureitems.first.num_cpu
socket_count = '' unless @reconfigureitems.all? { |vm| vm.num_cpu == socket_count }
cores_per_socket = @reconfigureitems.first.cpu_cores_per_socket
cores_per_socket = '' unless @reconfigureitems.all? { |vm| vm.cpu_cores_per_socket == cores_per_socket }
memory, memory_type = reconfigure_calculations(memory)
# if only one vm that supports disk reconfiguration is selected, get the disks information
vmdisks = []
@reconfigureitems.first.hardware.disks.order(:filename).each do |disk|
next if disk.device_type != 'disk'
dsize, dunit = reconfigure_calculations(disk.size / (1024 * 1024))
vmdisks << {:hdFilename => disk.filename,
:hdType => disk.disk_type,
:hdMode => disk.mode,
:hdSize => dsize,
:hdUnit => dunit,
:add_remove => '',
:cb_bootable => disk.bootable}
end
# reconfiguring network adapters is only supported when one vm was selected
network_adapters = []
vmcdroms = []
if @reconfigureitems.size == 1
vm = @reconfigureitems.first
if vm.supports?(:reconfigure_network_adapters)
network_adapters = build_network_adapters_list(vm)
end
if vm.supports?(:reconfigure_cdroms)
# CD-ROMS
vmcdroms = build_vmcdrom_list(vm)
end
end
{:objectIds => reconfigure_ids,
:memory => memory,
:memory_type => memory_type,
:socket_count => socket_count.to_s,
:cores_per_socket_count => cores_per_socket.to_s,
:disks => vmdisks,
:network_adapters => network_adapters,
:cdroms => vmcdroms,
:vm_vendor => @reconfigureitems.first.vendor,
:vm_type => @reconfigureitems.first.class.name,
:orchestration_stack_id => @reconfigureitems.first.try(:orchestration_stack_id),
:disk_default_type => @reconfigureitems.first.try(:disk_default_type) || 'thin'}
end
def build_network_adapters_list(vm)
network_adapters = []
vm.hardware.guest_devices.order(:device_name => 'asc').each do |guest_device|
lan = Lan.find_by(:id => guest_device.lan_id)
network_adapters << {:name => guest_device.device_name, :vlan => lan.name, :mac => guest_device.address, :add_remove => ''} unless lan.nil?
end
if vm.kind_of?(ManageIQ::Providers::Vmware::CloudManager::Vm)
vm.network_ports.order(:name).each do |port|
network_adapters << { :name => port.name, :network => port.cloud_subnets.try(:first).try(:name) || _('None'), :mac => port.mac_address, :add_remove => '' }
end
end
network_adapters
end
def filename_string(name)
# an empty cdrom filename can be in the form of a string containing a pair of square brackets
name.blank? || name == '[]' ? '' : name.to_s
end
def build_vmcdrom_list(vm)
vmcdroms = []
cdroms = vm.hardware.cdroms
if cdroms.present?
cdroms.map do |cd|
id = cd.id
device_name = cd.device_name
type = cd.device_type
filename = filename_string(cd.filename)
storage_id = cd.storage_id || ''
vmcdroms << {:id => id, :device_name => device_name, :filename => filename, :type => type, :storage_id => storage_id}
end
vmcdroms
end
end
private
def item_supports?(*features)
features << :reconfigure_disks if features.include?(:reconfigure_disksize) # special case
item = @reconfigitems.first if @reconfigitems&.size == 1
item && features.all? { |feature| item.supports?(feature) }
end
# 'true' => true
# 'false' => false
# 'rootofevil' => 'rootofevil'
# Example:
#
# {"a" => "true", "b" => "false", "c" => "42"}.transform_values! { |v| eval_if_bool_string(v) }
# => {"a" => true, "b" => false, "c" => "42"}
def eval_if_bool_string(str)
case str
when 'true' then true
when 'false' then false
else str
end
end
def reconfigure_handle_cancel_button
add_flash(_("VM Reconfigure Request was cancelled by the user"))
if @sb[:explorer]
@sb[:action] = nil
replace_right_cell
else
session[:flash_msgs] = @flash_array
javascript_redirect(previous_breadcrumb_url)
end
end
# parameter to accept for vm reconfiguration based on role permissions
def reconfigure_param_list
list = []
if role_allows?(:feature => 'vm_reconfigure_disks')
list += [
%i[vmAddDisks disk_add],
%i[vmResizeDisks disk_resize],
%i[vmRemoveDisks disk_remove]
]
end
if role_allows?(:feature => 'vm_reconfigure_networks')
list += [
%i[vmAddNetworkAdapters network_adapter_add],
%i[vmRemoveNetworkAdapters network_adapter_remove],
%i[vmEditNetworkAdapters network_adapter_edit],
]
end
if role_allows?(:feature => 'vm_reconfigure_drives')
list += [
%i[vmConnectCDRoms cdrom_connect],
%i[vmDisconnectCDRoms cdrom_disconnect]
]
end
list
end
def reconfigure_handle_submit_button
options = {:src_ids => params[:objectIds]}
if params[:cb_memory] == 'true' && role_allows?(:feature => 'vm_reconfigure_memory')
options[:vm_memory] = params[:memory_type] == "MB" ? params[:memory] : params[:memory].to_i * 1024
end
if params[:cb_cpu] == 'true' && role_allows?(:feature => 'vm_reconfigure_cpu')
options[:cores_per_socket] = params[:cores_per_socket_count].nil? ? 1 : params[:cores_per_socket_count].to_i
options[:number_of_sockets] = params[:socket_count].nil? ? 1 : params[:socket_count].to_i
vccores = params[:cores_per_socket_count].to_i.zero? ? 1 : params[:cores_per_socket_count].to_i
vsockets = params[:socket_count].to_i.zero? ? 1 : params[:socket_count].to_i
options[:number_of_cpus] = vccores * vsockets
end
reconfigure_param_list.each do |params_key, options_key|
next if params[params_key].blank?
params[params_key].each do |_key, p|
p.transform_values! { |v| eval_if_bool_string(v) }
end
options[options_key] = params[params_key].values.map(&:to_unsafe_h)
end
if params[:id] && params[:id] != 'new'
@request_id = params[:id]
end
VmReconfigureRequest.make_request(@request_id, options, current_user)
flash_to_session(_("VM Reconfigure Request was saved"))
if role_allows?(:feature => "miq_request_show_list", :any => true)
javascript_redirect(:controller => 'miq_request', :action => 'show_list')
else
url = previous_breadcrumb_url.split('/')
javascript_redirect(:controller => url[1], :action => url[2])
end
end
end
end
end
end