Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support CPU/MEM/HDD reconfiguration #231

Merged
merged 1 commit into from
May 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions app/models/manageiq/providers/vmware/cloud_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,12 @@ def vm_remove_all_snapshots(vm, _options = {})
service.process_task(response.body)
end
end

def vm_reconfigure(vm, options = {})
with_provider_connection do |service|
xml = service.get_vapp(vm.ems_ref, :parser => 'xml').body
response = service.post_reconfigure_vm(vm.ems_ref, xml, options[:spec])
service.process_task(response.body)
end
end
end
5 changes: 5 additions & 0 deletions app/models/manageiq/providers/vmware/cloud_manager/vm.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
class ManageIQ::Providers::Vmware::CloudManager::Vm < ManageIQ::Providers::CloudManager::Vm
include_concern 'Operations'
include_concern 'RemoteConsole'
include_concern 'Reconfigure'

supports :snapshots
supports :remove_all_snapshots
supports_not :remove_snapshot
supports :snapshot_create
supports :revert_to_snapshot
supports :reconfigure_disks
supports :reconfigure_disksize do
unsupported_reason_add(:reconfigure_disksize, 'Cannot resize disks of a VM with snapshots') unless snapshots.empty?
end

def provider_object(connection = nil)
connection ||= ext_management_system.connect
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
module ManageIQ::Providers::Vmware::CloudManager::Vm::Reconfigure
# Show Reconfigure VM task
def reconfigurable?
true
end

def max_cpu_cores_per_socket(_total_vcpus = nil)
128
end

def max_total_vcpus
128
end

def max_vcpus
128
end

def max_memory_mb
4.terabyte / 1.megabyte
end

def disk_types
['LSI Logic Parallel SCSI']
end

def disk_default_type
'LSI Logic Parallel SCSI'
end

def validate_config_spec(options)
if vm_powered_on?
if options[:number_of_cpus]
number_of_cpus = options[:number_of_cpus].to_i
cores_per_socket = options[:cores_per_socket].to_i
raise MiqException::MiqVmError, 'CPU Hot-Add not enabled' if number_of_cpus != cpu_total_cores && !cpu_hot_add_enabled
raise MiqException::MiqVmError, 'CPU Hot-Remove not enabled' if number_of_cpus < cpu_total_cores && !cpu_hot_remove_enabled
raise MiqException::MiqVmError, 'Cannot change CPU cores per socket on a running VM' if cores_per_socket != cpu_cores_per_socket
end

if options[:vm_memory]
vm_memory = options[:vm_memory].to_i
raise MiqException::MiqVmError, 'Memory Hot-Add not enabled' if vm_memory > ram_size && !memory_hot_add_enabled
raise MiqException::MiqVmError, 'Cannot remove memory from a running VM' if vm_memory < ram_size
end
end

raise MiqException::MiqVmError, 'Cannot resize disk of VM with shapshots' if options[:disk_resize] && !snapshots.empty?
end

def build_config_spec(options)
validate_config_spec(options)

# Virtual hardware modifications.
new_hw = {}
new_hw[:memory] = { :quantity_mb => options[:vm_memory] } if options[:vm_memory]
new_hw[:cpu] = { :num_cores => options[:number_of_cpus], :cores_per_socket => options[:cores_per_socket] } if options[:number_of_cpus]
if (%i(disk_add disk_resize disk_remove) & options.keys).any?
new_hw[:disk] = []
Array(options[:disk_add]) .each_with_object(new_hw[:disk]) { |d, res| res << { :capacity_mb => d[:disk_size_in_mb].to_i } }
Array(options[:disk_resize]).each_with_object(new_hw[:disk]) { |d, res| res << { :id => disk_id(d[:disk_name]), :capacity_mb => d[:disk_size_in_mb].to_i } }
Array(options[:disk_remove]).each_with_object(new_hw[:disk]) { |d, res| res << { :id => disk_id(d[:disk_name]), :capacity_mb => -1 } }
end

new_hw.empty? ? {} : { :hardware => new_hw }
end

def disk_id(disk_name)
disk = disks.detect { |d| d.filename == disk_name }
# Disk location is stored as "{addr}/{parent_addr}/{disk_id}" e.g. "0/3/2000"
disk.location.to_s.split('/').last
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
describe ManageIQ::Providers::Vmware::CloudManager::Vm::Reconfigure do
before { EvmSpecHelper.create_guid_miq_server_zone }
let(:vm) do
FactoryGirl.create(
:vm_vmware_cloud,
:name => 'test_vm',
:raw_power_state => 'off',
:cpu_hot_add_enabled => true,
:cpu_hot_remove_enabled => true,
:memory_hot_add_enabled => true,
:hardware => FactoryGirl.create(
:hardware,
:cpu4x2,
:ram1GB,
:disks => [
FactoryGirl.create(:disk, :size => 1024, :filename => 'Disk 0', :location => '0/1/2000'),
FactoryGirl.create(:disk, :size => 2048, :filename => 'Disk 1', :location => '0/2/2001'),
]
)
)
end

it '#reconfigurable?' do
expect(vm.reconfigurable?).to be_truthy
end

describe '#build_config_spec' do
let(:fog_options) { vm.build_config_spec(options) }
let(:options) do
{
:vm_memory => 16_384,
:cores_per_socket => 2,
:number_of_cpus => 16,
:disk_add => [{ :disk_size_in_mb => '4096' }],
:disk_resize => [{ :disk_name => 'Disk 0', :disk_size_in_mb => '6144' }],
:disk_remove => [{ :disk_name => 'Disk 1' }],
}
end

describe 'no hardware changes' do
let(:options) { {} }

it 'fog request optimized' do
expect(fog_options).to eq({})
end
end

context 'VM off' do
it 'memory' do
expect(fog_options[:hardware][:memory][:quantity_mb]).to eq(16_384)
end

describe 'cpu' do
it 'num_cores' do
expect(fog_options[:hardware][:cpu][:num_cores]).to eq(16)
end

it 'cores_per_socket' do
expect(fog_options[:hardware][:cpu][:cores_per_socket]).to eq(2)
end
end

describe 'disks' do
it 'add' do
expect(fog_options[:hardware][:disk]).to include(:capacity_mb => 4096)
end

it 'resize' do
expect(fog_options[:hardware][:disk]).to include(:id => '2000', :capacity_mb => 6144)
end

it 'remove' do
expect(fog_options[:hardware][:disk]).to include(:id => '2001', :capacity_mb => -1)
end
end
end

context 'VM on' do
before { vm.raw_power_state = 'on' }

describe 'memory' do
it 'add memory' do
expect(fog_options[:hardware][:memory][:quantity_mb]).to eq(16_384)
end

it 'remove memory' do
options[:vm_memory] = 512
expect { fog_options }.to raise_error(MiqException::MiqVmError, 'Cannot remove memory from a running VM')
end
end

describe 'cpu' do
it 'add cores' do
expect(fog_options[:hardware][:cpu][:num_cores]).to eq(16)
end

it 'add cores per socket' do
expect(fog_options[:hardware][:cpu][:cores_per_socket]).to eq(2)
end

it 'remove cores per socket' do
options[:cores_per_socket] = 1
expect { fog_options }.to raise_error(MiqException::MiqVmError, 'Cannot change CPU cores per socket on a running VM')
end
end

describe 'hot memory disabled' do
before { vm.memory_hot_add_enabled = false }

it 'add memory' do
expect { fog_options }.to raise_error(MiqException::MiqVmError, 'Memory Hot-Add not enabled')
end
end

describe 'hot cpu add disabled' do
before { vm.cpu_hot_add_enabled = false }

it 'add cores' do
expect { fog_options }.to raise_error(MiqException::MiqVmError, 'CPU Hot-Add not enabled')
end
end

describe 'hot cpu remove disabled' do
before { vm.cpu_hot_remove_enabled = false }

it 'remove cores' do
options[:number_of_cpus] = 1
expect { fog_options }.to raise_error(MiqException::MiqVmError, 'CPU Hot-Remove not enabled')
end
end
end

context 'VM with snapshot' do
before { FactoryGirl.create(:snapshot, :vm_or_template => vm) }

describe 'disks' do
it 'resize' do
expect { fog_options }.to raise_error(MiqException::MiqVmError, 'Cannot resize disk of VM with shapshots')
end
end
end
end
end
36 changes: 36 additions & 0 deletions spec/models/manageiq/providers/vmware/cloud_manager_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -162,4 +162,40 @@
end
end
end

describe 'reconfigure operations' do
before do
allow(@ems).to receive(:with_provider_connection).and_yield(connection)
end

let(:vm) { FactoryGirl.create(:vm_vcloud, :ext_management_system => @ems) }
let(:connection) { double('connection') }
let(:vm_xml) { double('vm_xml') }
let(:options) { { :spec => 'fog-options' } }

it 'supports reconfigure_disks' do
expect(vm.supports_reconfigure_disks?).to be_truthy
end

describe 'supports reconfigure_disksize' do
it 'without snapshots' do
expect(vm.supports_reconfigure_disksize?).to be_truthy
end

context 'with snapshots' do
before { FactoryGirl.create(:snapshot, :vm_or_template => vm) }

it do
expect(vm.supports_reconfigure_disksize?).to be_falsey
end
end
end

it '.vm_reconfigure' do
expect(connection).to receive(:get_vapp).with(vm.ems_ref, :parser => 'xml').and_return(double(:body => vm_xml))
expect(connection).to receive(:post_reconfigure_vm).with(vm.ems_ref, vm_xml, 'fog-options').and_return(double(:body => nil))
expect(connection).to receive(:process_task)
@ems.vm_reconfigure(vm, options)
end
end
end