Class: Pvectl::Services::CloneContainer
- Inherits:
-
Object
- Object
- Pvectl::Services::CloneContainer
- Defined in:
- lib/pvectl/services/clone_container.rb,
sig/pvectl/services/clone_container.rbs
Overview
Orchestrates container clone operations.
Handles validation, auto-generation of CTID/hostname, 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_mountpoint_params(params, mountpoints) ⇒ void
Adds mountpoint parameters mapped to mp0, mp1, etc.
-
#add_net_params(params, nets) ⇒ void
Adds network parameters mapped to net0, net1, etc.
-
#apply_config_update(source_ct, new_ctid, node, config_params, resource_info) ⇒ Models::ContainerOperationResult
Applies config update to the cloned container.
-
#build_clone_options(hostname:, target_node:, storage:, linked:, pool:, description:) ⇒ Hash
Builds clone options hash for repository call.
-
#build_ct_config_api_params(config_params) ⇒ Hash
Builds Proxmox API parameters from user-friendly container config options.
-
#container_not_found_error(ctid) ⇒ Models::ContainerOperationResult
Returns error for container not found.
-
#execute(ctid:, node: nil, new_ctid: nil, hostname: nil, target_node: nil, storage: nil, linked: false, pool: nil, description: nil, config_params: {}) ⇒ Models::ContainerOperationResult
Executes clone operation.
-
#generate_hostname(source_ct) ⇒ String
Generates clone hostname from source container.
-
#initialize(container_repository:, task_repository:, options: {}) ⇒ CloneContainer
constructor
Creates a new CloneContainer service.
-
#linked_clone_error(source_ct) ⇒ Models::ContainerOperationResult
Returns error for linked clone of non-template container.
-
#start_container(ctid, node) ⇒ void
Starts a container after successful clone and config update.
-
#timeout ⇒ Integer
Returns configured timeout.
Constructor Details
#initialize(container_repository:, task_repository:, options: {}) ⇒ CloneContainer
Creates a new CloneContainer service.
33 34 35 36 37 |
# File 'lib/pvectl/services/clone_container.rb', line 33 def initialize(container_repository:, task_repository:, options: {}) @container_repository = container_repository @task_repository = task_repository @options = end |
Instance Method Details
#add_mountpoint_params(params, mountpoints) ⇒ void
This method returns an undefined value.
Adds mountpoint parameters mapped to mp0, mp1, etc.
201 202 203 204 205 |
# File 'lib/pvectl/services/clone_container.rb', line 201 def add_mountpoint_params(params, mountpoints) mountpoints.each_with_index do |mp, index| params[:"mp#{index}"] = Parsers::LxcMountConfig.to_proxmox(mp) end end |
#add_net_params(params, nets) ⇒ void
This method returns an undefined value.
Adds network parameters mapped to net0, net1, etc.
212 213 214 215 216 |
# File 'lib/pvectl/services/clone_container.rb', line 212 def add_net_params(params, nets) nets.each_with_index do |net, index| params[:"net#{index}"] = Parsers::LxcNetConfig.to_proxmox(net) end end |
#apply_config_update(source_ct, new_ctid, node, config_params, resource_info) ⇒ Models::ContainerOperationResult
Applies config update to the cloned container.
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_container.rb', line 155 def apply_config_update(source_ct, new_ctid, node, config_params, resource_info) api_params = build_ct_config_api_params(config_params) @container_repository.update(new_ctid, node, api_params) start_container(new_ctid, node) if @options[:start] Models::ContainerOperationResult.new( container: source_ct, operation: :clone, success: true, resource: resource_info ) rescue StandardError => e Models::ContainerOperationResult.new( container: source_ct, operation: :clone, success: :partial, resource: resource_info, error: "Cloned successfully, but config update failed: #{e.}" ) end |
#build_clone_options(hostname:, 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_container.rb', line 135 def (hostname:, target_node:, storage:, linked:, pool:, description:) opts = { hostname: hostname, 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_ct_config_api_params(config_params) ⇒ Hash
Builds Proxmox API parameters from user-friendly container config options.
Maps config keys to their Proxmox API equivalents. Does not include hostname, ostemplate, 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 |
# File 'lib/pvectl/services/clone_container.rb', line 178 def build_ct_config_api_params(config_params) params = {} params[:cores] = config_params[:cores] if config_params[:cores] params[:memory] = config_params[:memory] if config_params[:memory] params[:swap] = config_params[:swap] if config_params[:swap] params[:unprivileged] = config_params[:privileged] ? 0 : 1 unless config_params[:privileged].nil? params[:rootfs] = Parsers::LxcMountConfig.to_proxmox(config_params[:rootfs]) if config_params[:rootfs] add_mountpoint_params(params, config_params[:mountpoints]) if config_params[:mountpoints] add_net_params(params, config_params[:nets]) if config_params[:nets] params[:features] = config_params[:features] if config_params[:features] params[:password] = config_params[:password] if config_params[:password] params[:"ssh-public-keys"] = config_params[:ssh_public_keys] if config_params[:ssh_public_keys] params[:onboot] = config_params[:onboot] ? 1 : 0 unless config_params[:onboot].nil? params[:startup] = config_params[:startup] if config_params[:startup] params[:tags] = config_params[:tags] if config_params[:tags] params end |
#container_not_found_error(ctid) ⇒ Models::ContainerOperationResult
Returns error for container not found.
239 240 241 242 243 244 245 |
# File 'lib/pvectl/services/clone_container.rb', line 239 def container_not_found_error(ctid) Models::ContainerOperationResult.new( operation: :clone, success: false, error: "Container #{ctid} not found" ) end |
#execute(ctid:, node: nil, new_ctid: nil, hostname: nil, target_node: nil, storage: nil, linked: false, pool: nil, description: nil, config_params: {}) ⇒ Models::ContainerOperationResult
Executes clone operation.
Performs a two-step flow: clone the container first, then optionally apply config updates via PUT /nodes/node/lxc/ctid/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_container.rb', line 55 def execute(ctid:, node: nil, new_ctid: nil, hostname: nil, target_node: nil, storage: nil, linked: false, pool: nil, description: nil, config_params: {}) source_ct = @container_repository.get(ctid) return container_not_found_error(ctid) unless source_ct if linked && !source_ct.template? return linked_clone_error(source_ct) end node ||= source_ct.node new_ctid ||= @container_repository.next_available_ctid hostname ||= generate_hostname(source_ct) = ( hostname: hostname, target_node: target_node, storage: storage, linked: linked, pool: pool, description: description ) upid = @container_repository.clone(ctid, node, new_ctid, ) resource_info = { new_ctid: new_ctid, hostname: hostname, node: target_node || node } if @options[:async] Models::ContainerOperationResult.new( container: source_ct, operation: :clone, task_upid: upid, success: :pending, resource: resource_info ) else task = @task_repository.wait(upid, timeout: timeout) unless task.successful? return Models::ContainerOperationResult.new( container: source_ct, operation: :clone, task: task, success: task.successful?, resource: resource_info ) end if config_params.any? apply_config_update(source_ct, new_ctid, resource_info[:node], config_params, resource_info) else start_container(new_ctid, resource_info[:node]) if @options[:start] Models::ContainerOperationResult.new( container: source_ct, operation: :clone, task: task, success: true, resource: resource_info ) end end rescue StandardError => e Models::ContainerOperationResult.new( container: source_ct, operation: :clone, success: false, error: e. ) end |
#generate_hostname(source_ct) ⇒ String
Generates clone hostname from source container.
118 119 120 121 122 123 124 |
# File 'lib/pvectl/services/clone_container.rb', line 118 def generate_hostname(source_ct) if source_ct.name && !source_ct.name.empty? "#{source_ct.name}-clone" else "ct-#{source_ct.vmid}-clone" end end |
#linked_clone_error(source_ct) ⇒ Models::ContainerOperationResult
Returns error for linked clone of non-template container.
251 252 253 254 255 256 257 |
# File 'lib/pvectl/services/clone_container.rb', line 251 def linked_clone_error(source_ct) Models::ContainerOperationResult.new( container: source_ct, operation: :clone, success: false, error: "Linked clone requires container to be a template. Container #{source_ct.vmid} is not a template" ) end |
#start_container(ctid, node) ⇒ void
This method returns an undefined value.
Starts a container after successful clone and config update.
223 224 225 226 |
# File 'lib/pvectl/services/clone_container.rb', line 223 def start_container(ctid, node) upid = @container_repository.start(ctid, node) @task_repository.wait(upid, timeout: START_TIMEOUT) end |
#timeout ⇒ Integer
Returns configured timeout.
231 232 233 |
# File 'lib/pvectl/services/clone_container.rb', line 231 def timeout @options[:timeout] || DEFAULT_TIMEOUT end |