@@ -48,6 +48,63 @@ def update_persistent_disk(instance_plan)
4848 end
4949 end
5050
51+ # Called by RecreateHandler after disks are detached but before VM deletion.
52+ # Attempts to update disk types via CPI while disks are already in a detached state.
53+ # If the CPI doesn't support update_disk, changes are left for the post-recreation
54+ # update_persistent_disk call to handle via the standard copy path.
55+ def update_detached_disks ( instance_plan )
56+ return unless Config . enable_cpi_update_disk
57+ return unless instance_plan . persistent_disk_changed?
58+
59+ instance_model = instance_plan . instance . model
60+ new_disks = instance_plan . desired_instance . instance_group . persistent_disk_collection
61+ old_disks = instance_model . active_persistent_disks
62+
63+ changed_disk_pairs = DeploymentPlan ::PersistentDiskCollection . changed_disk_pairs (
64+ old_disks ,
65+ instance_plan . instance . previous_variable_set ,
66+ new_disks ,
67+ instance_plan . instance . desired_variable_set ,
68+ )
69+
70+ changed_disk_pairs . each do |disk_pair |
71+ new_disk = disk_pair [ :new ]
72+ old_disk = disk_pair [ :old ]
73+
74+ next unless new_disk && old_disk && new_disk . managed? && old_disk . managed?
75+
76+ old_disk_model = old_disk . model
77+ next if old_disk_model . nil?
78+
79+ active_vm = old_disk_model . instance . active_vm
80+ if active_vm . nil?
81+ @logger . warn ( "No active VM for disk '#{ old_disk_model . disk_cid } ', skipping CPI update" )
82+ next
83+ end
84+
85+ begin
86+ cloud = cloud_for_cpi ( active_vm . cpi )
87+ @logger . info ( "Updating detached disk '#{ old_disk_model . disk_cid } ' via CPI" )
88+ new_disk_cid = cloud . update_disk ( old_disk_model . disk_cid , new_disk . size , new_disk . cloud_properties )
89+
90+ updates = { size : new_disk . size , cloud_properties : new_disk . cloud_properties }
91+ if new_disk_cid && new_disk_cid != old_disk_model . disk_cid
92+ @logger . info ( "Disk CID changed from '#{ old_disk_model . disk_cid } ' to '#{ new_disk_cid } '" )
93+ updates [ :disk_cid ] = new_disk_cid
94+ end
95+
96+ old_disk_model . update ( updates )
97+ @logger . info ( "Successfully updated detached disk '#{ old_disk_model . disk_cid } '" )
98+ rescue Bosh ::Clouds ::NotImplemented , Bosh ::Clouds ::NotSupported => e
99+ # Only catch "not available" errors. Other CPI errors (e.g. partial failures during
100+ # snapshot-based migration) must propagate — the CPI may have replaced the disk and
101+ # swallowing the error would lose the new disk CID.
102+ @logger . info ( "CPI does not support update_disk for '#{ old_disk_model . disk_cid } ': #{ e . message } . " \
103+ "Will use copy path after VM recreation." )
104+ end
105+ end
106+ end
107+
51108 def attach_disks_if_needed ( instance_plan )
52109 unless instance_plan . needs_disk?
53110 @logger . warn ( 'Skipping disk attachment, instance no longer needs disk' )
@@ -249,24 +306,33 @@ def update_disk_cpi(instance_plan, new_disk, old_disk)
249306 return
250307 end
251308
309+ if old_disk_model . size == new_disk . size && old_disk_model . cloud_properties == new_disk . cloud_properties
310+ @logger . info ( "Disk '#{ old_disk_model . disk_cid } ' already matches desired state, skipping CPI update" )
311+ return
312+ end
313+
252314 @logger . info ( "Starting IaaS native update of disk '#{ old_disk_model . disk_cid } ' with new size '#{ new_disk . size } ' and cloud properties '#{ new_disk . cloud_properties } '" )
253315 detach_disk ( old_disk_model )
254316
255317 begin
256318 cloud = cloud_for_cpi ( old_disk_model . instance . active_vm . cpi )
257- cloud . update_disk ( old_disk_model . disk_cid , new_disk . size , new_disk . cloud_properties )
319+ new_disk_cid = cloud . update_disk ( old_disk_model . disk_cid , new_disk . size , new_disk . cloud_properties )
258320 rescue Bosh ::Clouds ::NotImplemented , Bosh ::Clouds ::NotSupported => e
259321 @logger . info ( "IaaS native update not possible for disk #{ old_disk_model . disk_cid } . Falling back to creating new disk.\n #{ e . message } " )
260322 attach_disk ( old_disk_model , instance_plan . tags )
261323 update_disk ( instance_plan , new_disk , old_disk )
262-
263324 return
264325 end
265326
327+ updates = { size : new_disk . size , cloud_properties : new_disk . cloud_properties }
328+ if new_disk_cid && new_disk_cid != old_disk_model . disk_cid
329+ @logger . info ( "Disk CID changed from '#{ old_disk_model . disk_cid } ' to '#{ new_disk_cid } ' after IaaS update" )
330+ updates [ :disk_cid ] = new_disk_cid
331+ end
332+
266333 attach_disk ( old_disk_model , instance_plan . tags )
267334
268- old_disk_model . update ( size : new_disk . size )
269- old_disk_model . update ( cloud_properties : new_disk . cloud_properties )
335+ old_disk_model . update ( updates )
270336 @logger . info ( "Finished IaaS native update of disk '#{ old_disk_model . disk_cid } '" )
271337 end
272338
0 commit comments