diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e115d26505..aa5e639010e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -86,6 +86,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - consolidate: disable vfull duplicate job check [PR #1739] - dird: disallow running always incremental virtual full jobs with empty jobid list [PR #1738] - msgchan: fix deadlock [PR #1858] +- VMware Plugin: Add option restore_allow_disks_mismatch [PR #1876] ### Removed - plugins: remove old deprecated postgres plugin [PR #1606] @@ -228,6 +229,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [PR #1871]: https://github.com/bareos/bareos/pull/1871 [PR #1872]: https://github.com/bareos/bareos/pull/1872 [PR #1875]: https://github.com/bareos/bareos/pull/1875 +[PR #1876]: https://github.com/bareos/bareos/pull/1876 [PR #1878]: https://github.com/bareos/bareos/pull/1878 [PR #1881]: https://github.com/bareos/bareos/pull/1881 [PR #1883]: https://github.com/bareos/bareos/pull/1883 diff --git a/core/src/plugins/filed/python/vmware/bareos-fd-vmware.py b/core/src/plugins/filed/python/vmware/bareos-fd-vmware.py index 36347f1eca6..683f9a93688 100644 --- a/core/src/plugins/filed/python/vmware/bareos-fd-vmware.py +++ b/core/src/plugins/filed/python/vmware/bareos-fd-vmware.py @@ -124,6 +124,7 @@ def __init__(self, plugindef): "vadp_dumper_sectors_per_call", "vadp_dumper_query_allocated_blocks_chunk_size", "fallback_to_full_cbt", + "restore_allow_disks_mismatch", ] self.allowed_options = ( self.mandatory_options_default @@ -392,6 +393,9 @@ def check_plugin_options(self, mandatory_options=None): if self.options.get("fallback_to_full_cbt") == "no": self.vadp.fallback_to_full_cbt = False + if self.options.get("restore_allow_disks_mismatch") == "yes": + self.vadp.restore_allow_disks_mismatch = True + for option in self.options: bareosfd.DebugMessage( 100, @@ -1247,6 +1251,7 @@ def __init__(self): self.vm_nvram_content = None self.restore_vm_created = False self.fallback_to_full_cbt = True + self.restore_allow_disks_mismatch = False def connect_vmware(self): # this prevents from repeating on second call @@ -2404,6 +2409,15 @@ def check_vm_disks_match(self): bareosfd.M_WARNING, "Backed up Disks: %s\n" % (StringCodec.encode(", ".join(backed_up_disks))), ) + + # currently ignoring disks mismatch is only allowed for recreated VMs + if self.restore_allow_disks_mismatch and self.restore_vm_created: + bareosfd.JobMessage( + bareosfd.M_WARNING, + "Ignoring disks mismatch as restore_allow_disks_mismatch=yes was given\n", + ) + return True + bareosfd.JobMessage( bareosfd.M_FATAL, "ERROR: VM disks not matching backed up disks\n", @@ -3157,40 +3171,87 @@ def add_disk_devices_to_vm(self, device_changes): """ Add devices to VM """ + retry_limit = 3 disk_index = 0 for device_spec in device_changes: config_spec = vim.vm.ConfigSpec() device_spec.device.controllerKey = abs(device_spec.device.controllerKey) config_spec.deviceChange = [device_spec] device_created = False - backing_path_changed = False - while not device_created: + + # 2024-07-17 sduehr: Always create disk with datastore only in + # the backing file name. + expected_disk_backing_filename = device_spec.device.backing.fileName + device_spec.device.backing.fileName = ( + "[%s] " % device_spec.device.backing.datastore.name + ) + retry_count = 1 + + while not device_created and retry_count <= retry_limit: reconfig_task = self.vm.ReconfigVM_Task(spec=config_spec) + bareosfd.DebugMessage( + 100, + "add_disk_devices_to_vm(): Trying to add disk %s\n" + % (device_spec.device.backing.fileName), + ) try: pyVim.task.WaitForTask(reconfig_task) device_created = True - except vim.fault.FileAlreadyExists: - device_spec.device.backing.fileName = ( - "[%s] " % device_spec.device.backing.datastore.name + except Exception as create_disk_exc: + bareosfd.JobMessage( + bareosfd.M_INFO, + "add_disk_devices_to_vm(): Failed to create disk %s: %s, retrying\n" + % (device_spec.device.backing.fileName, str(create_disk_exc)), ) - backing_path_changed = True - - # When handling the FileAlreadyExists exception, we only pass the datastore name - # and the backing path to the disk will be created by the API, then mapping in self.restore_disk_paths_map - # must be corrected. This is only possible by walking the VMs devices and detect the added disks path, - # because the task result does not contain it. - if backing_path_changed: - self.restore_disk_paths_map[ - list(self.restore_disk_paths_map)[disk_index] - ] = [ - device.backing.fileName - for device in self.vm.config.hardware.device - if type(device) == vim.vm.device.VirtualDisk - ][ - disk_index - ] + retry_count += 1 + + if not device_created: + bareosfd.JobMessage( + bareosfd.M_FATAL, + "add_disk_devices_to_vm(): Failed to create disk %s: giving up.\n" + % (device_spec.device.backing.fileName), + ) + return False + + # When only passing the datastore name in device.backing.fileName when creating a disk, + # the complete backing path to the disk will be created by the API, and the new backing + # path could differ from the old one so that mapping in self.restore_disk_paths_map + # must be corrected. This is only possible by walking the VMs devices and detect the + # added disks path, because the task result does not contain it. + created_disk_backing_filename = [ + device.backing.fileName + for device in self.vm.config.hardware.device + if type(device) == vim.vm.device.VirtualDisk + ][disk_index] + + if expected_disk_backing_filename != created_disk_backing_filename: + bareosfd.JobMessage( + bareosfd.M_INFO, + "Disk %s was recreated as %s\n" + % (expected_disk_backing_filename, created_disk_backing_filename), + ) + + bareosfd.DebugMessage( + 100, + "add_disk_devices_to_vm(): Adapting restore_disk_paths_maps" + "[%s] from %s to %s\n" + % ( + list(self.restore_disk_paths_map)[disk_index], + self.restore_disk_paths_map[ + list(self.restore_disk_paths_map)[disk_index] + ], + created_disk_backing_filename, + ), + ) + + self.restore_disk_paths_map[ + list(self.restore_disk_paths_map)[disk_index] + ] = created_disk_backing_filename + disk_index += 1 + return True + def adapt_vm_config(self, config_spec): """ Adapt VM config diff --git a/docs/manuals/source/TasksAndConcepts/Plugins/FileDaemonPlugins/VMwarePlugin.rst.inc b/docs/manuals/source/TasksAndConcepts/Plugins/FileDaemonPlugins/VMwarePlugin.rst.inc index 9bde79f905a..dac5cf8b55c 100644 --- a/docs/manuals/source/TasksAndConcepts/Plugins/FileDaemonPlugins/VMwarePlugin.rst.inc +++ b/docs/manuals/source/TasksAndConcepts/Plugins/FileDaemonPlugins/VMwarePlugin.rst.inc @@ -710,5 +710,15 @@ fallback_to_full_cbt (optional) Since :sinceVersion:`23.0.3: VMware Plugin` +restore_allow_disks_mismatch (optional) + When using VSAN, restoring with recreating the VM can fail because the plugin + detects a disk mismatch, as when using VSAN obviously recreated disks get a + generated backing disk path. When passing the plugin option + ``restore_allow_disks_mismatch=yes``, this disk match check will allow a + mismatch and continue the restore. + This option will only be used when recreating the VM to be restored. + + Since :sinceVersion:`23.0.4: VMware Plugin` + uuid (deprecated) The uuid option could be used instead of *dc*, *folder* and *vmname* to uniquely address a VM for backup. As the plugin since :sinceVersion:`22.0.0: VMware Plugin` is able recreate VMs in a different datacenter, folder or datastore, this option has become useless. When using uuid, restoring is only possible to the same still existing VM. It is recommended to change the configuration, as the uuid option will be dropped in the next version.