Skip to content

Commit bf35f00

Browse files
committed
Enable expanding root disk root_disk_size_gb
Applications such as Nvidia's libraries, required for many AI / LLM applications, aren't easily installed on anywhere but the root disk; however, the amount of free space in the root disk of a Jammy stemcell is too small to accommodate these libraries. To address this, we introduce a new property, `root_disk_size_gb`. Setting this property will expand the root disk during VM creation (in the `create_vm` CPI method, before the BOSH packages are installed). Typical use (in a Cloud Config): ```yaml vm_extensions: - name: 20G_root cloud_properties: root_disk_size_gb: 20 ``` Setting this property disables the VM's "linked clone" feature [0], which is incompatible with extending the size of the root disk. The VM will therefore require more disk space than expected, typically the size of the root disk (in the above example, 20 GiB) Adding this feature brings the vSphere CPI to parity with the AWS, Azure, and GCP CPIs. Root disk size is measured in GiB (1,073,741,824 bytes), not in GB (1,000,000,000 bytes). Drive-by: Removed unnecessary initialization, `config_spec.device_change = []`: `ConfigSpec` class initializes `device_change` to an empty array; we don't need to do it. [0] A linked clone is a copy of a virtual machine that shares virtual disks with the parent virtual machine. Only changes from the parent disk are recorded, saving on disk space. Signed-off-by: Brian Cunnie <brian.cunnie@broadcom.com>
1 parent dcb5f87 commit bf35f00

5 files changed

Lines changed: 93 additions & 13 deletions

File tree

src/vsphere_cpi/lib/cloud/vsphere/vm_config.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ def pci_passthroughs
7676
vm_type.pci_passthroughs || []
7777
end
7878

79+
def root_disk_size_gb
80+
vm_type.root_disk_size_gb.to_i
81+
end
82+
7983
def storage_policy_name
8084
@manifest_params[:storage_policy]
8185
end

src/vsphere_cpi/lib/cloud/vsphere/vm_creator.rb

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@ def create(vm_config)
183183

184184
# Clone VM
185185
logger.info("Cloning vm: #{replicated_stemcell_vm} to #{vm_config.name}")
186+
# Don't link clone if expanding root disk, otherwise "Invalid operation for device '0'. Disks with parents cannot be expanded."
187+
linked = vm_config.root_disk_size_gb > 0 ? false : true
186188
created_vm_mob = @client.wait_for_task do
187189
@cpi.clone_vm(
188190
replicated_stemcell_vm.mob,
@@ -191,7 +193,7 @@ def create(vm_config)
191193
cluster.resource_pool.mob,
192194
datastore: datastore.mob,
193195
host:,
194-
linked: true,
196+
linked: linked,
195197
snapshot: snapshot.current_snapshot,
196198
config: config_spec,
197199
datastore_cluster:
@@ -251,7 +253,6 @@ def create(vm_config)
251253
# Jammy stemcell at hardware version 13 only allows 1 vGPU; we want to be able to add more
252254
unless vm_config.vgpus.empty?
253255
config_spec = VimSdk::Vim::Vm::ConfigSpec.new
254-
config_spec.device_change = []
255256
vm_config.vgpus.each do |vgpu|
256257
vgpu = Resources::PCIPassthrough.create_vgpu(vgpu)
257258
vgpu_config = Resources::VM.create_add_device_spec(vgpu)
@@ -261,7 +262,6 @@ def create(vm_config)
261262
end
262263
unless vm_config.pci_passthroughs.empty?
263264
config_spec = VimSdk::Vim::Vm::ConfigSpec.new
264-
config_spec.device_change = []
265265
vm_config.pci_passthroughs.each do |pci_passthrough|
266266
virtual_pci_passthrough = Resources::PCIPassthrough.create_pci_passthrough(
267267
vendor_id: pci_passthrough['vendor_id'],
@@ -271,6 +271,15 @@ def create(vm_config)
271271
end
272272
@client.reconfig_vm(created_vm_mob, config_spec)
273273
end
274+
275+
if vm_config.root_disk_size_gb > 0
276+
device_spec = Resources::VM.create_edit_device_spec(created_vm.system_disk)
277+
device_spec.device.capacity_in_kb = vm_config.root_disk_size_gb * 2 ** 20 # GiB → kiB
278+
config_spec = VimSdk::Vim::Vm::ConfigSpec.new
279+
config_spec.device_change << device_spec
280+
@client.reconfig_vm(created_vm_mob, config_spec)
281+
end
282+
274283
# DRS Rules
275284
create_drs_rules(vm_config, created_vm.mob, cluster)
276285

src/vsphere_cpi/lib/cloud/vsphere/vm_type.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ def initialize(datacenter, cloud_properties, pbm)
2727
nsxt
2828
pci_passthroughs
2929
ram
30+
root_disk_size_gb
3031
storage_policy
3132
tags
3233
upgrade_hw_version
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
require 'integration/spec_helper'
2+
3+
describe 'root_disk_size_gb property' do
4+
5+
let(:vm_type) do
6+
{
7+
'ram' => 512,
8+
'disk' => 2048,
9+
'cpu' => 1,
10+
}
11+
end
12+
13+
context 'when "root_disk_size_gb" is not set' do
14+
it 'creates a VM whose system disk is a linked-clone to the stemcell' do
15+
simple_vm_lifecycle(@cpi, @vlan, vm_type) do |vm_id|
16+
vm = @cpi.vm_provider.find(vm_id)
17+
stemcell = @cpi.vm_provider.find(@stemcell_id)
18+
system_disk = vm.system_disk
19+
stemcell_disk = stemcell.system_disk
20+
expect(system_disk.backing.parent.uuid).to eq(stemcell_disk.backing.parent.uuid)
21+
end
22+
end
23+
end
24+
25+
context 'when "root_disk_size_gb" is set' do
26+
let(:root_disk_size_gb) { 15 }
27+
it 'creates a VM whose system disk is a linked-clone to the stemcell' do
28+
vm_type['root_disk_size_gb'] = root_disk_size_gb
29+
simple_vm_lifecycle(@cpi, @vlan, vm_type) do |vm_id|
30+
vm = @cpi.vm_provider.find(vm_id)
31+
system_disk = vm.system_disk
32+
expect(system_disk.backing.parent).to be_nil # no parent disk, not a linked-clone
33+
expect(system_disk.capacity_in_kb / 1024 / 1024).to eq(root_disk_size_gb) # convert kiB → GiB
34+
end
35+
end
36+
end
37+
end

src/vsphere_cpi/spec/unit/cloud/vsphere/vm_creator_spec.rb

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,15 @@ module VSphereCloud
2121
)
2222
}
2323
let(:agent_env) { instance_double('VSphereCloud::AgentEnv') }
24-
let(:config) { [ 'default_disk_type'=> default_disk_type, 'resource_pool' => 'test', 'datacenters'=> [ datacenter ],'host' => 'localhost', 'user' => 'admin', 'password' => 'password' ] }
24+
let(:config) {
25+
[
26+
default_disk_type: default_disk_type,
27+
resource_pool: 'test',
28+
datacenters: [datacenter],
29+
host: 'localhost',
30+
user: 'admin',
31+
password: 'password'
32+
] }
2533
let(:cpi_config) do
2634
instance_double(
2735
'VSphereCloud::Config',
@@ -36,6 +44,13 @@ module VSphereCloud
3644
let(:custom_fields_manager) { instance_double('VimSdk::Vim::CustomFieldsManager') }
3745
let(:datacenter) { { mob: datacenter_mob , name: 'dc-1', 'name' => 'dc-1', 'persistent_datastore_pattern' => 'ds-ps-*', 'datastore_pattern'=> 'ds-*', 'vm_folder'=> 'bosh_vms', 'template_folder'=> 'stemcells', 'disk_path' => 'disks', 'clusters' => [] } }
3846
let(:datacenter_mob) { instance_double('VimSdk::Vim::Datacenter', name: 'dc-1') }
47+
let(:cluster) { instance_double(Resources::Cluster, mob: cluster_mob,
48+
resource_pool: resource_pool, host_group: nil, accessible_datastores: { "ds-1": {}, "ds-2": {} }) }
49+
let(:cluster_mob) { instance_double('VimSdk::Vim::ClusterComputeResource') }
50+
let(:resource_pool) { instance_double(Resources::ResourcePool, mob: resource_pool_mob) }
51+
let(:resource_pool_mob) { instance_double(VimSdk::Vim::ResourcePool) }
52+
let(:datastore) { instance_double(Resources::Datastore, name: 'ds-1', mob: datastore_mob) }
53+
let(:datastore_mob) { instance_double(VimSdk::Vim::Datastore) }
3954
let(:default_disk_type) { 'preallocated' }
4055
let(:ip_conflict_detector) { instance_double(IPConflictDetector, ensure_no_conflicts: nil) }
4156
let(:ensure_no_ip_conflicts) { true }
@@ -162,13 +177,13 @@ module VSphereCloud
162177

163178
cluster_placements: [
164179
instance_double(VmPlacement,
165-
cluster: instance_double(Resources::Cluster, host_group: nil, mob: nil, accessible_datastores: {"ds-1": {}, "ds-2": {}}),
166-
fallback_disk_placements: [instance_double(Resources::Datastore, name: "ds-2")],
167-
disk_placement: instance_double(Resources::Datastore, name: "ds-1")
180+
cluster: cluster,
181+
disk_placement: datastore,
182+
fallback_disk_placements: [instance_double(Resources::Datastore, name: "ds-2")]
168183
)
169184
],
170-
stemcell_cid: 'here-stemcell-cid'
171-
185+
stemcell_cid: 'here-stemcell-cid',
186+
root_disk_size_gb: 0
172187
)
173188
}
174189

@@ -199,7 +214,21 @@ module VSphereCloud
199214
expect(client).to receive(:upgrade_vm_virtual_hardware).with(cloned_vm_mob)
200215
expect(client).to receive(:reconfig_vm).with(cloned_vm_mob, anything)
201216
subject.create(vm_config)
202-
# expect{subject.create(vm_config)}.not_to raise_exception
217+
end
218+
end
219+
220+
context 'with root_disk_size_gb set to 15 GiB' do
221+
let(:system_disk) { instance_double('VimSdk::Vim::Vm::Device::VirtualDisk') }
222+
let(:device_spec) { instance_double(VimSdk::Vim::Vm::Device::VirtualDeviceSpec, device: system_disk) }
223+
let(:new_disk_size_gb) { 15 }
224+
before do
225+
allow(vm_config).to receive(:root_disk_size_gb).and_return(new_disk_size_gb)
226+
allow(Resources::VM).to receive(:create_edit_device_spec).and_return(device_spec)
227+
end
228+
it "reconfigures the VM with a larger root disk and we don't check linked clones because the mocking is already too much" do
229+
expect(system_disk).to receive(:capacity_in_kb=).with(new_disk_size_gb * 2 ** 20) # convert kiB → GiB
230+
expect(client).to receive(:reconfig_vm).with(cloned_vm_mob, anything)
231+
subject.create(vm_config)
203232
end
204233
end
205234

@@ -243,13 +272,13 @@ module VSphereCloud
243272

244273
cluster_placements: [
245274
instance_double(VmPlacement,
246-
cluster: instance_double(Resources::Cluster, host_group: nil, mob: nil, accessible_datastores: {"ds-1": {}, "ds-2": {}}),
275+
cluster: cluster,
247276
fallback_disk_placements: [],
248277
disk_placement: instance_double(Resources::Datastore, name: "ds-1")
249278
)
250279
],
251-
stemcell_cid: 'here-stemcell-cid'
252-
280+
stemcell_cid: 'here-stemcell-cid',
281+
root_disk_size_gb: 0
253282
)
254283
}
255284
it 'still fails if there are no viable fallback_disk_placements' do

0 commit comments

Comments
 (0)