Skip to content

Commit

Permalink
Support CPU/MEM/HDD reconfiguration
Browse files Browse the repository at this point in the history
With this commit we implement whatever support for virtual hardware
reconfiguration. Namely, we support following operations for VM:

- increase/decrease memory
- increase/decrease number of cpu cores and sockets
- increase disk size
- add/remove disk

We also update refresh parser to fetch correct data for disks.

Signed-off-by: Miha Pleško <miha.plesko@xlab.si>
  • Loading branch information
miha-plesko committed May 15, 2018
1 parent 153f893 commit b29d82e
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 0 deletions.
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
3 changes: 3 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,15 @@
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

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,63 @@
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 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
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,140 @@
describe ManageIQ::Providers::Vmware::CloudManager::Vm::Reconfigure do
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 do
vm.raw_power_state = 'on'
end

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 do
vm.memory_hot_add_enabled = false
end

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 do
vm.cpu_hot_add_enabled = false
end

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 do
vm.cpu_hot_remove_enabled = false
end

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
end
end
26 changes: 26 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,30 @@
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

it 'supports reconfigure_disksize' do
expect(vm.supports_reconfigure_disksize?).to be_truthy
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

0 comments on commit b29d82e

Please sign in to comment.