Class: Pvectl::Repositories::Container
- Defined in:
- lib/pvectl/repositories/container.rb,
sig/pvectl/repositories/container.rbs
Overview
Repository for LXC containers.
Uses the /cluster/resources API endpoint to list containers across the cluster.
Filters to only include LXC containers (excludes QEMU VMs).
Instance Attribute Summary
Attributes inherited from Base
Instance Method Summary collapse
-
#build_describe_model(basic_data, config, status, snapshots = [], tasks = [], firewall = {}) ⇒ Models::Container
Builds Container model with describe-specific attributes.
-
#build_model(data) ⇒ Models::Container
Builds Container model from API response data.
-
#clone(ctid, node, new_ctid, options = {}) ⇒ String
Clones a container to create a new container.
-
#convert_to_template(ctid, node) ⇒ void
Converts a container to a template.
-
#create(node, ctid, params = {}) ⇒ String
Creates a new LXC container on the specified node.
-
#delete(ctid, node, destroy_disks: true, purge: false, force: false) ⇒ String
Deletes a container from the cluster.
-
#describe(ctid) ⇒ Models::Container?
Describes a container with comprehensive details from multiple API endpoints.
-
#extract_network_interfaces(config) ⇒ Array<Hash>
Extracts network interfaces from config.
-
#feature_available?(ctid, node, feature, snapname: nil) ⇒ Hash
Checks whether a feature (clone, snapshot, copy) is available for a container.
-
#fetch_config(node, ctid) ⇒ Hash
Fetches container configuration.
-
#fetch_firewall(node, ctid) ⇒ Hash
Fetches firewall configuration (options, rules, aliases, IP sets).
-
#fetch_snapshots(node, ctid) ⇒ Array<Hash>
Fetches container snapshots.
-
#fetch_status(node, ctid) ⇒ Hash
Fetches container runtime status.
-
#fetch_tasks(node, ctid, limit: 10) ⇒ Array<Models::TaskEntry>
Fetches recent task history for the container.
-
#find_container_basic_data(ctid) ⇒ Hash?
Finds container basic data from cluster resources.
-
#get(ctid) ⇒ Models::Container?
Gets a single container by CTID.
-
#list(node: nil) ⇒ Array<Models::Container>
Lists all containers in the cluster.
-
#migrate(ctid, node, params = {}) ⇒ String
Migrates a container to another node.
-
#move_volume(ctid, node, volume, target_storage, delete: false, bwlimit: nil) ⇒ String
Moves a container volume to a different storage on the same node.
-
#next_available_ctid ⇒ Integer
Returns the next available CTID from the Proxmox cluster.
-
#parse_network_interface(key, value) ⇒ Hash
Parses a single network interface string.
-
#resize(ctid, node, disk:, size:) ⇒ nil
Resizes a container disk.
-
#restart(ctid, node) ⇒ String
Restarts a container (reboot).
-
#shutdown(ctid, node) ⇒ String
Shuts down a container gracefully.
-
#start(ctid, node) ⇒ String
Starts a container.
-
#stop(ctid, node) ⇒ String
Stops a container (hard stop).
-
#termproxy(ctid, node) ⇒ Hash
Opens a terminal proxy session for a container.
-
#update(ctid, node, params = {}) ⇒ nil
Updates an existing LXC container configuration.
Methods inherited from Base
#extract_data, #initialize, #models_from, #unwrap
Constructor Details
This class inherits a constructor from Pvectl::Repositories::Base
Instance Method Details
#build_describe_model(basic_data, config, status, snapshots = [], tasks = [], firewall = {}) ⇒ Models::Container
Builds Container model with describe-specific attributes.
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 |
# File 'lib/pvectl/repositories/container.rb', line 455 def build_describe_model(basic_data, config, status, snapshots = [], tasks = [], firewall = {}) network_interfaces = extract_network_interfaces(config) Models::Container.new( # Basic attributes from cluster/resources vmid: basic_data[:vmid], name: basic_data[:name], status: basic_data[:status], node: basic_data[:node], type: basic_data[:type], cpu: basic_data[:cpu], maxcpu: basic_data[:maxcpu], mem: basic_data[:mem], maxmem: basic_data[:maxmem], swap: basic_data[:swap], maxswap: basic_data[:maxswap], disk: basic_data[:disk], maxdisk: basic_data[:maxdisk], uptime: basic_data[:uptime], template: basic_data[:template], tags: basic_data[:tags], pool: basic_data[:pool], lock: basic_data[:lock], netin: basic_data[:netin], netout: basic_data[:netout], # Config attributes (kept for backward compat) ostype: config[:ostype], arch: config[:arch], unprivileged: config[:unprivileged], features: config[:features], rootfs: config[:rootfs], description: config[:description], hostname: config[:hostname], # Status attributes pid: status[:pid], ha: status[:ha], # Parsed network interfaces network_interfaces: network_interfaces, # Raw API data for comprehensive describe describe_data: { config: config, status: status, snapshots: snapshots, tasks: tasks, firewall: firewall } ) end |
#build_model(data) ⇒ Models::Container
Builds Container model from API response data.
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 |
# File 'lib/pvectl/repositories/container.rb', line 324 def build_model(data) Models::Container.new( vmid: data[:vmid], name: data[:name], status: data[:status], node: data[:node], cpu: data[:cpu], maxcpu: data[:maxcpu], mem: data[:mem], maxmem: data[:maxmem], swap: data[:swap], maxswap: data[:maxswap], disk: data[:disk], maxdisk: data[:maxdisk], uptime: data[:uptime], template: data[:template], tags: data[:tags], pool: data[:pool], lock: data[:lock], netin: data[:netin], netout: data[:netout], type: data[:type] ) end |
#clone(ctid, node, new_ctid, options = {}) ⇒ String
Clones a container to create a new container.
Posts to /nodes/{node}/lxc/{ctid}/clone with the specified parameters.
Note: LXC API uses hostname parameter (not name like QEMU).
131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/pvectl/repositories/container.rb', line 131 def clone(ctid, node, new_ctid, = {}) params = { newid: new_ctid } params[:hostname] = [:hostname] if [:hostname] params[:target] = [:target] if [:target] params[:storage] = [:storage] if [:storage] params[:full] = [:full] ? 1 : 0 if .key?(:full) params[:description] = [:description] if [:description] params[:pool] = [:pool] if [:pool] connection.client["nodes/#{node}/lxc/#{ctid}/clone"].post(params) end |
#convert_to_template(ctid, node) ⇒ void
This method returns an undefined value.
Converts a container to a template.
This is an irreversible operation. The container will become read-only and can only be used as a source for cloning.
151 152 153 |
# File 'lib/pvectl/repositories/container.rb', line 151 def convert_to_template(ctid, node) connection.client["nodes/#{node}/lxc/#{ctid}/template"].post({}) end |
#create(node, ctid, params = {}) ⇒ String
Creates a new LXC container on the specified node.
Posts to /nodes/{node}/lxc with the container configuration parameters.
The ctid is merged into params automatically.
273 274 275 276 |
# File 'lib/pvectl/repositories/container.rb', line 273 def create(node, ctid, params = {}) api_params = params.merge(vmid: ctid) connection.client["nodes/#{node}/lxc"].post(api_params) end |
#delete(ctid, node, destroy_disks: true, purge: false, force: false) ⇒ String
Deletes a container from the cluster.
79 80 81 82 83 84 85 86 |
# File 'lib/pvectl/repositories/container.rb', line 79 def delete(ctid, node, destroy_disks: true, purge: false, force: false) params = {} params["destroy-unreferenced-disks"] = 1 if destroy_disks params[:purge] = 1 if purge params[:force] = 1 if force connection.client["nodes/#{node}/lxc/#{ctid}"].delete(params) end |
#describe(ctid) ⇒ Models::Container?
Describes a container with comprehensive details from multiple API endpoints.
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/pvectl/repositories/container.rb', line 54 def describe(ctid) ctid = ctid.to_i basic_data = find_container_basic_data(ctid) return nil if basic_data.nil? node = basic_data[:node] config = fetch_config(node, ctid) status = fetch_status(node, ctid) snapshots = fetch_snapshots(node, ctid) tasks = fetch_tasks(node, ctid) firewall = fetch_firewall(node, ctid) build_describe_model(basic_data, config, status, snapshots, tasks, firewall) end |
#extract_network_interfaces(config) ⇒ Array<Hash>
Extracts network interfaces from config. Network interfaces are stored as net0, net1, etc. keys.
418 419 420 421 422 423 424 425 426 427 |
# File 'lib/pvectl/repositories/container.rb', line 418 def extract_network_interfaces(config) interfaces = [] config.each do |key, value| next unless key.to_s.match?(/^net\d+$/) parsed = parse_network_interface(key.to_s, value) interfaces << parsed if parsed end interfaces end |
#feature_available?(ctid, node, feature, snapname: nil) ⇒ Hash
Checks whether a feature (clone, snapshot, copy) is available for a container.
Calls GET /nodes/{node}/lxc/{vmid}/feature with the feature and
optional snapshot name. The Proxmox LXC API returns hasFeature (0/1).
Unlike the QEMU variant, the LXC endpoint does not return a nodes array;
this method always returns nodes: [] for shape compatibility.
217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/pvectl/repositories/container.rb', line 217 def feature_available?(ctid, node, feature, snapname: nil) params = { feature: feature } params[:snapname] = snapname unless snapname.nil? response = connection.client["nodes/#{node}/lxc/#{ctid}/feature"].get(params: params) data = extract_data(response) { available: data[:hasFeature].to_i == 1, nodes: Array(data[:nodes]) } end |
#fetch_config(node, ctid) ⇒ Hash
Fetches container configuration.
311 312 313 314 315 316 |
# File 'lib/pvectl/repositories/container.rb', line 311 def fetch_config(node, ctid) resp = connection.client["nodes/#{node}/lxc/#{ctid}/config"].get extract_data(resp) rescue StandardError {} end |
#fetch_firewall(node, ctid) ⇒ Hash
Fetches firewall configuration (options, rules, aliases, IP sets).
389 390 391 392 393 394 395 396 397 398 |
# File 'lib/pvectl/repositories/container.rb', line 389 def fetch_firewall(node, ctid) base = "nodes/#{node}/lxc/#{ctid}/firewall" = (extract_data(connection.client["#{base}/options"].get) rescue {}) rules = (unwrap(connection.client["#{base}/rules"].get) rescue []) aliases_data = (unwrap(connection.client["#{base}/aliases"].get) rescue []) ipset = (unwrap(connection.client["#{base}/ipset"].get) rescue []) { options: , rules: rules, aliases: aliases_data, ipset: ipset } rescue StandardError {} end |
#fetch_snapshots(node, ctid) ⇒ Array<Hash>
Fetches container snapshots.
377 378 379 380 381 382 |
# File 'lib/pvectl/repositories/container.rb', line 377 def fetch_snapshots(node, ctid) resp = connection.client["nodes/#{node}/lxc/#{ctid}/snapshot"].get unwrap(resp).reject { |s| s[:name] == "current" } rescue StandardError [] end |
#fetch_status(node, ctid) ⇒ Hash
Fetches container runtime status.
365 366 367 368 369 370 |
# File 'lib/pvectl/repositories/container.rb', line 365 def fetch_status(node, ctid) resp = connection.client["nodes/#{node}/lxc/#{ctid}/status/current"].get extract_data(resp) rescue StandardError {} end |
#fetch_tasks(node, ctid, limit: 10) ⇒ Array<Models::TaskEntry>
Fetches recent task history for the container.
406 407 408 409 410 411 |
# File 'lib/pvectl/repositories/container.rb', line 406 def fetch_tasks(node, ctid, limit: 10) task_list_repo = TaskList.new(connection) task_list_repo.list(node: node, vmid: ctid, limit: limit) rescue StandardError [] end |
#find_container_basic_data(ctid) ⇒ Hash?
Finds container basic data from cluster resources.
355 356 357 358 |
# File 'lib/pvectl/repositories/container.rb', line 355 def find_container_basic_data(ctid) response = connection.client["cluster/resources"].get(params: { type: "vm" }) unwrap(response).find { |r| r[:type] == "lxc" && r[:vmid] == ctid } end |
#get(ctid) ⇒ Models::Container?
Gets a single container by CTID.
46 47 48 |
# File 'lib/pvectl/repositories/container.rb', line 46 def get(ctid) list.find { |ct| ct.vmid == ctid.to_i } end |
#list(node: nil) ⇒ Array<Models::Container>
Lists all containers in the cluster.
Uses /cluster/resources?type=lxc endpoint for efficient cluster-wide
listing. Filters to only include LXC containers (type == "lxc").
35 36 37 38 39 40 |
# File 'lib/pvectl/repositories/container.rb', line 35 def list(node: nil) response = connection.client["cluster/resources"].get(params: { type: "vm" }) containers = unwrap(response).select { |r| r[:type] == "lxc" } containers = containers.select { |r| r[:node] == node } if node containers.map { |data| build_model(data) } end |
#migrate(ctid, node, params = {}) ⇒ String
Migrates a container to another node.
162 163 164 165 166 167 168 169 170 171 |
# File 'lib/pvectl/repositories/container.rb', line 162 def migrate(ctid, node, params = {}) unless node.match?(/\A[a-z][a-z0-9-]*\z/) raise ArgumentError, "Invalid node name: #{node}" end unless ctid.is_a?(Integer) && ctid.positive? raise ArgumentError, "Invalid CTID: #{ctid}" end connection.client["nodes/#{node}/lxc/#{ctid}/migrate"].post(params) end |
#move_volume(ctid, node, volume, target_storage, delete: false, bwlimit: nil) ⇒ String
Moves a container volume to a different storage on the same node.
POSTs to /nodes/{node}/lxc/{ctid}/move_volume with the volume identifier
and target storage. The operation is asynchronous — the returned UPID
can be polled via Repositories::Task to track completion.
193 194 195 196 197 198 199 |
# File 'lib/pvectl/repositories/container.rb', line 193 def move_volume(ctid, node, volume, target_storage, delete: false, bwlimit: nil) params = { volume: volume, storage: target_storage } params[:delete] = 1 if delete params[:bwlimit] = bwlimit if bwlimit connection.client["nodes/#{node}/lxc/#{ctid}/move_volume"].post(params) end |
#next_available_ctid ⇒ Integer
Returns the next available CTID from the Proxmox cluster.
Uses the /cluster/nextid API endpoint which performs server-side allocation.
This is more reliable than client-side scanning because it detects stale
config files that don't appear in /cluster/resources.
237 238 239 |
# File 'lib/pvectl/repositories/container.rb', line 237 def next_available_ctid connection.client["cluster/nextid"].get.to_i end |
#parse_network_interface(key, value) ⇒ Hash
Parses a single network interface string.
434 435 436 437 438 439 440 441 442 443 444 445 446 |
# File 'lib/pvectl/repositories/container.rb', line 434 def parse_network_interface(key, value) return nil unless value.is_a?(String) interface = { id: key } # Parse key=value pairs from the interface string value.split(",").each do |part| k, v = part.split("=", 2) interface[k.to_sym] = v if k && v end interface end |
#resize(ctid, node, disk:, size:) ⇒ nil
Resizes a container disk.
PUTs to /nodes/{node}/lxc/{ctid}/resize with disk identifier
and new size. This is a synchronous, irreversible operation —
Proxmox does not support shrinking disks.
302 303 304 |
# File 'lib/pvectl/repositories/container.rb', line 302 def resize(ctid, node, disk:, size:) connection.client["nodes/#{node}/lxc/#{ctid}/resize"].put({ disk: disk, size: size }) end |
#restart(ctid, node) ⇒ String
Restarts a container (reboot).
246 247 248 |
# File 'lib/pvectl/repositories/container.rb', line 246 def restart(ctid, node) connection.client["nodes/#{node}/lxc/#{ctid}/status/reboot"].post end |
#shutdown(ctid, node) ⇒ String
Shuts down a container gracefully.
111 112 113 |
# File 'lib/pvectl/repositories/container.rb', line 111 def shutdown(ctid, node) connection.client["nodes/#{node}/lxc/#{ctid}/status/shutdown"].post end |
#start(ctid, node) ⇒ String
Starts a container.
93 94 95 |
# File 'lib/pvectl/repositories/container.rb', line 93 def start(ctid, node) connection.client["nodes/#{node}/lxc/#{ctid}/status/start"].post end |
#stop(ctid, node) ⇒ String
Stops a container (hard stop).
102 103 104 |
# File 'lib/pvectl/repositories/container.rb', line 102 def stop(ctid, node) connection.client["nodes/#{node}/lxc/#{ctid}/status/stop"].post end |
#termproxy(ctid, node) ⇒ Hash
Opens a terminal proxy session for a container.
255 256 257 258 |
# File 'lib/pvectl/repositories/container.rb', line 255 def termproxy(ctid, node) response = connection.client["nodes/#{node}/lxc/#{ctid}/termproxy"].post({}) extract_data(response) end |
#update(ctid, node, params = {}) ⇒ nil
Updates an existing LXC container configuration.
PUTs to /nodes/{node}/lxc/{ctid}/config with configuration parameters.
This is a synchronous operation — changes are applied immediately.
287 288 289 |
# File 'lib/pvectl/repositories/container.rb', line 287 def update(ctid, node, params = {}) connection.client["nodes/#{node}/lxc/#{ctid}/config"].put(params) end |