Class: Pvectl::Repositories::Container

Inherits:
Base
  • Object
show all
Defined in:
lib/pvectl/repositories/container.rb

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).

Examples:

Listing all containers

repo = Container.new(connection)
containers = repo.list
containers.each { |ct| puts "#{ct.vmid}: #{ct.name}" }

Listing containers on a specific node

containers = repo.list(node: "pve-node1")

Getting a single container

ct = repo.get(100)
puts ct.name if ct

See Also:

Instance Method Summary collapse

Methods inherited from Base

#initialize

Constructor Details

This class inherits a constructor from Pvectl::Repositories::Base

Instance Method Details

#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).

Parameters:

  • ctid (Integer, String)

    source container identifier

  • node (String)

    source node name

  • new_ctid (Integer)

    CTID for the new cloned container

  • options (Hash) (defaults to: {})

    optional clone parameters

Options Hash (options):

  • :hostname (String)

    hostname for the new container

  • :target (String)

    target node for the clone

  • :storage (String)

    target storage for the clone

  • :full (Boolean)

    full clone (true) or linked clone (false)

  • :description (String)

    description for the new container

  • :pool (String)

    resource pool for the new container

Returns:

  • (String)

    Task UPID



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, options = {})
  params = { newid: new_ctid }
  params[:hostname] = options[:hostname] if options[:hostname]
  params[:target] = options[:target] if options[:target]
  params[:storage] = options[:storage] if options[:storage]
  params[:full] = options[:full] ? 1 : 0 if options.key?(:full)
  params[:description] = options[:description] if options[:description]
  params[:pool] = options[:pool] if options[: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.

Parameters:

  • ctid (Integer, String)

    Container identifier

  • node (String)

    Node name



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.

Examples:

Create a basic container

repo.create("pve1", 200, { hostname: "web-ct", ostemplate: "local:vztmpl/debian-12.tar.zst" })
#=> "UPID:pve1:..."

Parameters:

  • node (String)

    target node name

  • ctid (Integer)

    container identifier

  • params (Hash) (defaults to: {})

    container configuration parameters (hostname, ostemplate, etc.)

Returns:

  • (String)

    Task UPID



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.

Parameters:

  • ctid (Integer, String)

    Container identifier

  • node (String)

    Node name

  • destroy_disks (Boolean) (defaults to: true)

    destroy unreferenced disks (default: true)

  • purge (Boolean) (defaults to: false)

    remove from HA, replication, backups (default: false)

  • force (Boolean) (defaults to: false)

    force removal (default: false)

Returns:

  • (String)

    Task UPID



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.

Parameters:

  • ctid (Integer, String)

    container identifier

Returns:

  • (Models::Container, nil)

    Container model with full details, or nil if not found



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

#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.

Examples:

Check whether container 200 can be cloned

repo.feature_available?(200, "pve1", "clone")
#=> { available: true, nodes: [] }

Parameters:

  • ctid (Integer, String)

    container identifier

  • node (String)

    node currently hosting the container

  • feature (String)

    feature name (one of: clone, snapshot, copy)

  • snapname (String, nil) (defaults to: nil)

    snapshot name (required for some checks)

Returns:

  • (Hash)

    result with :available (Boolean) and :nodes (Array<String>)



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.

Parameters:

  • node (String)

    node name

  • ctid (Integer)

    container identifier

Returns:

  • (Hash)

    config data



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

#get(ctid) ⇒ Models::Container?

Gets a single container by CTID.

Parameters:

  • ctid (Integer, String)

    container identifier

Returns:



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”).

Parameters:

  • node (String, nil) (defaults to: nil)

    filter by node name

Returns:



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.

Parameters:

  • ctid (Integer, String)

    container identifier

  • node (String)

    current node name

  • params (Hash) (defaults to: {})

    migration parameters (:target, :online, :restart, :targetstorage)

Returns:

  • (String)

    Task UPID

Raises:

  • (ArgumentError)

    if node name or ctid format is invalid



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.

Examples:

Move rootfs to a different storage

repo.move_volume(200, "pve1", "rootfs", "local-lvm")
#=> "UPID:pve1:..."

Move and delete source volume

repo.move_volume(200, "pve1", "mp0", "ceph-pool", delete: true)

Parameters:

  • ctid (Integer, String)

    container identifier

  • node (String)

    node name where the container currently resides

  • volume (String)

    volume identifier (e.g., “rootfs”, “mp0”)

  • target_storage (String)

    destination storage ID

  • delete (Boolean) (defaults to: false)

    delete the source volume after copy (default: false)

  • bwlimit (Integer, nil) (defaults to: nil)

    I/O bandwidth limit in KiB/s

Returns:

  • (String)

    Task UPID



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_ctidInteger

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.

Returns:

  • (Integer)

    next available CTID



237
238
239
# File 'lib/pvectl/repositories/container.rb', line 237

def next_available_ctid
  connection.client["cluster/nextid"].get.to_i
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.

Parameters:

  • ctid (Integer, String)

    container identifier

  • node (String)

    node name

  • disk (String)

    disk identifier (e.g., “rootfs”, “mp0”)

  • size (String)

    new size, absolute (“50G”) or relative (“+10G”)

Returns:

  • (nil)


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).

Parameters:

  • ctid (Integer, String)

    Container identifier

  • node (String)

    Node name

Returns:

  • (String)

    Task UPID



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.

Parameters:

  • ctid (Integer, String)

    Container identifier

  • node (String)

    Node name

Returns:

  • (String)

    Task UPID



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.

Parameters:

  • ctid (Integer, String)

    Container identifier

  • node (String)

    Node name

Returns:

  • (String)

    Task UPID



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).

Parameters:

  • ctid (Integer, String)

    Container identifier

  • node (String)

    Node name

Returns:

  • (String)

    Task UPID



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.

Parameters:

  • ctid (Integer, String)

    Container identifier

  • node (String)

    Node name

Returns:

  • (Hash)

    termproxy data with :port, :ticket, :user keys



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.

Parameters:

  • ctid (Integer, String)

    container identifier

  • node (String)

    node name

  • params (Hash) (defaults to: {})

    container configuration parameters to update

Returns:

  • (nil)


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