Class: Pvectl::Services::CloneVm
- Inherits:
-
Object
- Object
- Pvectl::Services::CloneVm
- Defined in:
- lib/pvectl/services/clone_vm.rb,
sig/pvectl/services/clone_vm.rbs
Overview
Orchestrates VM clone operations.
Handles validation, auto-generation of VMID/name, and sync/async modes. Supports both full clones and linked clones (templates only).
Constant Summary collapse
- DEFAULT_TIMEOUT =
300- START_TIMEOUT =
Returns Default timeout for start operations (seconds).
60
Instance Method Summary collapse
-
#add_disk_params(params, disks) ⇒ void
Adds disk parameters mapped to scsi0, scsi1, etc.
-
#add_net_params(params, nets) ⇒ void
Adds network parameters mapped to net0, net1, etc.
-
#apply_config_update(source_vm, new_vmid, node, config_params, resource_info) ⇒ Models::VmOperationResult
Applies config update to the cloned VM.
-
#build_clone_options(name:, target_node:, storage:, linked:, pool:, description:) ⇒ Hash
Builds clone options hash for repository call.
-
#build_config_api_params(config_params) ⇒ Hash
Builds Proxmox API parameters from user-friendly config options.
-
#execute(vmid:, node: nil, new_vmid: nil, name: nil, target_node: nil, storage: nil, linked: false, pool: nil, description: nil, config_params: {}) ⇒ Models::OperationResult
Executes clone operation.
-
#generate_name(source_vm) ⇒ String
Generates clone name from source VM.
-
#initialize(vm_repository:, task_repository:, options: {}) ⇒ CloneVm
constructor
Creates a new CloneVm service.
-
#linked_clone_error(source_vm) ⇒ Models::OperationResult
Returns error for linked clone of non-template VM.
-
#start_vm(vmid, node) ⇒ void
Starts a VM after successful clone and config update.
-
#timeout ⇒ Integer
Returns configured timeout.
-
#vm_not_found_error(vmid) ⇒ Models::OperationResult
Returns error for VM not found.
Constructor Details
#initialize(vm_repository:, task_repository:, options: {}) ⇒ CloneVm
Creates a new CloneVm service.
33 34 35 36 37 |
# File 'lib/pvectl/services/clone_vm.rb', line 33 def initialize(vm_repository:, task_repository:, options: {}) @vm_repository = vm_repository @task_repository = task_repository = end |
Instance Method Details
#add_disk_params(params, disks) ⇒ void
This method returns an undefined value.
Adds disk parameters mapped to scsi0, scsi1, etc.
206 207 208 209 210 |
# File 'lib/pvectl/services/clone_vm.rb', line 206 def add_disk_params(params, disks) disks.each_with_index do |disk, index| params[:"scsi#{index}"] = Parsers::DiskConfig.to_proxmox(disk) end end |
#add_net_params(params, nets) ⇒ void
This method returns an undefined value.
Adds network parameters mapped to net0, net1, etc.
217 218 219 220 221 |
# File 'lib/pvectl/services/clone_vm.rb', line 217 def add_net_params(params, nets) nets.each_with_index do |net, index| params[:"net#{index}"] = Parsers::NetConfig.to_proxmox(net) end end |
#apply_config_update(source_vm, new_vmid, node, config_params, resource_info) ⇒ Models::VmOperationResult
Applies config update to the cloned VM.
Converts user-friendly params to Proxmox API format and calls the repository update method. Returns partial result on failure.
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/pvectl/services/clone_vm.rb', line 155 def apply_config_update(source_vm, new_vmid, node, config_params, resource_info) api_params = build_config_api_params(config_params) @vm_repository.update(new_vmid, node, api_params) start_vm(new_vmid, node) if [:start] Models::VmOperationResult.new( vm: source_vm, operation: :clone, success: true, resource: resource_info ) rescue StandardError => e Models::VmOperationResult.new( vm: source_vm, operation: :clone, success: :partial, resource: resource_info, error: "Cloned successfully, but config update failed: #{e.message}" ) end |
#build_clone_options(name:, target_node:, storage:, linked:, pool:, description:) ⇒ Hash
Builds clone options hash for repository call.
135 136 137 138 139 140 141 142 |
# File 'lib/pvectl/services/clone_vm.rb', line 135 def (name:, target_node:, storage:, linked:, pool:, description:) opts = { name: name, full: !linked } opts[:target] = target_node if target_node opts[:storage] = storage if storage opts[:pool] = pool if pool opts[:description] = description if description opts end |
#build_config_api_params(config_params) ⇒ Hash
Builds Proxmox API parameters from user-friendly config options.
Maps config keys to their Proxmox API equivalents. Does not include name, description, or pool (those belong to the clone step).
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/pvectl/services/clone_vm.rb', line 178 def build_config_api_params(config_params) params = {} params[:cores] = config_params[:cores] if config_params[:cores] params[:sockets] = config_params[:sockets] if config_params[:sockets] params[:cpu] = config_params[:cpu_type] if config_params[:cpu_type] params[:numa] = config_params[:numa] ? 1 : 0 unless config_params[:numa].nil? params[:memory] = config_params[:memory] if config_params[:memory] params[:balloon] = config_params[:balloon] if config_params[:balloon] add_disk_params(params, config_params[:disks]) if config_params[:disks] params[:scsihw] = config_params[:scsihw] if config_params[:scsihw] params[:cdrom] = config_params[:cdrom] if config_params[:cdrom] add_net_params(params, config_params[:nets]) if config_params[:nets] params[:bios] = config_params[:bios] if config_params[:bios] params[:boot] = "order=#{config_params[:boot_order]}" if config_params[:boot_order] params[:machine] = config_params[:machine] if config_params[:machine] params[:efidisk0] = config_params[:efidisk] if config_params[:efidisk] params.merge!(config_params[:cloud_init]) if config_params[:cloud_init] params[:agent] = config_params[:agent] ? "1" : "0" unless config_params[:agent].nil? params[:ostype] = config_params[:ostype] if config_params[:ostype] params[:tags] = config_params[:tags] if config_params[:tags] params end |
#execute(vmid:, node: nil, new_vmid: nil, name: nil, target_node: nil, storage: nil, linked: false, pool: nil, description: nil, config_params: {}) ⇒ Models::OperationResult
Executes clone operation.
Performs a two-step flow: clone the VM first, then optionally apply config updates via PUT /nodes/node/qemu/vmid/config.
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/pvectl/services/clone_vm.rb', line 55 def execute(vmid:, node: nil, new_vmid: nil, name: nil, target_node: nil, storage: nil, linked: false, pool: nil, description: nil, config_params: {}) source_vm = @vm_repository.get(vmid) return vm_not_found_error(vmid) unless source_vm if linked && !source_vm.template? return linked_clone_error(source_vm) end node ||= source_vm.node new_vmid ||= @vm_repository.next_available_vmid name ||= generate_name(source_vm) = ( name: name, target_node: target_node, storage: storage, linked: linked, pool: pool, description: description ) upid = @vm_repository.clone(vmid, node, new_vmid, ) resource_info = { new_vmid: new_vmid, name: name, node: target_node || node } if [:async] Models::VmOperationResult.new( vm: source_vm, operation: :clone, task_upid: upid, success: :pending, resource: resource_info ) else task = @task_repository.wait(upid, timeout: timeout) unless task.successful? return Models::VmOperationResult.new( vm: source_vm, operation: :clone, task: task, success: task.successful?, resource: resource_info ) end if config_params.any? apply_config_update(source_vm, new_vmid, resource_info[:node], config_params, resource_info) else start_vm(new_vmid, resource_info[:node]) if [:start] Models::VmOperationResult.new( vm: source_vm, operation: :clone, task: task, success: true, resource: resource_info ) end end rescue StandardError => e Models::VmOperationResult.new( vm: source_vm, operation: :clone, success: false, error: e. ) end |
#generate_name(source_vm) ⇒ String
Generates clone name from source VM.
118 119 120 121 122 123 124 |
# File 'lib/pvectl/services/clone_vm.rb', line 118 def generate_name(source_vm) if source_vm.name && !source_vm.name.empty? "#{source_vm.name}-clone" else "vm-#{source_vm.vmid}-clone" end end |
#linked_clone_error(source_vm) ⇒ Models::OperationResult
Returns error for linked clone of non-template VM.
256 257 258 259 260 261 262 |
# File 'lib/pvectl/services/clone_vm.rb', line 256 def linked_clone_error(source_vm) Models::VmOperationResult.new( vm: source_vm, operation: :clone, success: false, error: "Linked clone requires VM to be a template. VM #{source_vm.vmid} is not a template" ) end |
#start_vm(vmid, node) ⇒ void
This method returns an undefined value.
Starts a VM after successful clone and config update.
228 229 230 231 |
# File 'lib/pvectl/services/clone_vm.rb', line 228 def start_vm(vmid, node) upid = @vm_repository.start(vmid, node) @task_repository.wait(upid, timeout: START_TIMEOUT) end |
#timeout ⇒ Integer
Returns configured timeout.
236 237 238 |
# File 'lib/pvectl/services/clone_vm.rb', line 236 def timeout [:timeout] || DEFAULT_TIMEOUT end |
#vm_not_found_error(vmid) ⇒ Models::OperationResult
Returns error for VM not found.
244 245 246 247 248 249 250 |
# File 'lib/pvectl/services/clone_vm.rb', line 244 def vm_not_found_error(vmid) Models::VmOperationResult.new( operation: :clone, success: false, error: "VM #{vmid} not found" ) end |