Class: Pvectl::Repositories::Volume
- Defined in:
- lib/pvectl/repositories/volume.rb,
sig/pvectl/repositories/volume.rbs
Overview
Repository for virtual disk volumes attached to VMs and containers.
Aggregates volume data from two sources:
- VM/CT config endpoints (parsed disk keys from configuration)
- Storage content API (+/nodes/node/storage/storage/content+)
Uses composition: delegates to VmRepository and ContainerRepository for config fetching and node resolution.
Constant Summary collapse
- VM_DISK_PATTERN =
Pattern matching VM disk keys (scsi0, virtio1, ide2, sata3, efidisk0, tpmstate0)
/\A(?:scsi|virtio|ide|sata|efidisk|tpmstate)\d+\z/- CT_DISK_PATTERN =
Pattern matching container disk keys (rootfs, mp0, mp1, ...)
/\A(?:rootfs|mp\d+)\z/
Instance Attribute Summary
Attributes inherited from Base
Instance Method Summary collapse
-
#build_storage_volume(data, node, storage) ⇒ Models::Volume
Builds a Volume model from storage content API data.
-
#container_repo ⇒ Repositories::Container
Returns container repository instance.
-
#extract_resource_from_volume_id(volume_id, content) ⇒ Array(String?, Integer?)
Extracts resource type and ID from a volume identifier.
-
#extract_volumes(config, resource_type, resource_id, node) ⇒ Array<Models::Volume>
Extracts volume models from a config hash.
-
#fetch_storage_volumes(node_name, storage) ⇒ Array<Models::Volume>
Fetches volumes from a storage on a specific node.
-
#find(resource_type:, id:, disk_name:, node: nil) ⇒ Models::Volume?
Finds a specific volume by disk name in a VM/CT config.
-
#format_bytes_to_size(bytes) ⇒ String?
Formats bytes to human-readable size string.
-
#initialize(connection, vm_repo: nil, container_repo: nil) ⇒ Volume
constructor
Creates a new Volume repository.
-
#list_from_config(resource_type:, ids:, node: nil) ⇒ Array<Models::Volume>
Lists volumes from VM/CT configuration for given resource IDs.
-
#list_from_storage(storage:, node: nil) ⇒ Array<Models::Volume>
Lists volumes from storage content API.
-
#normalize_resource_type(type) ⇒ String
Normalizes resource type string to canonical form.
-
#online_nodes ⇒ Array<String>
Fetches list of online node names.
-
#parse_config_value(name, value, resource_type, resource_id, node) ⇒ Models::Volume
Parses a config value string into a Volume model.
-
#parse_int(value) ⇒ Integer?
Parses a string value to integer.
-
#repo_for(type) ⇒ Repositories::Vm, ...
Returns the appropriate repository for the given resource type.
-
#vm_repo ⇒ Repositories::Vm
Returns VM repository instance.
Methods inherited from Base
#build_model, #extract_data, #get, #list, #models_from, #unwrap
Constructor Details
#initialize(connection, vm_repo: nil, container_repo: nil) ⇒ Volume
Creates a new Volume repository.
39 40 41 42 43 |
# File 'lib/pvectl/repositories/volume.rb', line 39 def initialize(connection, vm_repo: nil, container_repo: nil) super(connection) @vm_repo = vm_repo @container_repo = container_repo end |
Instance Method Details
#build_storage_volume(data, node, storage) ⇒ Models::Volume
Builds a Volume model from storage content API data.
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
# File 'lib/pvectl/repositories/volume.rb', line 221 def build_storage_volume(data, node, storage) resource_type, resource_id = extract_resource_from_volume_id(data[:volid], data[:content]) Models::Volume.new( volid: data[:volid], volume_id: data[:volid]&.split(":")&.last, storage: storage, size: format_bytes_to_size(data[:size]), format: data[:format], content: data[:content], resource_type: resource_type, resource_id: resource_id, node: node ) end |
#container_repo ⇒ Repositories::Container
Returns container repository instance. Uses injected repository if provided, otherwise creates new one.
121 122 123 |
# File 'lib/pvectl/repositories/volume.rb', line 121 def container_repo @container_repo ||= Repositories::Container.new(connection) end |
#extract_resource_from_volume_id(volume_id, content) ⇒ Array(String?, Integer?)
Extracts resource type and ID from a volume identifier.
Volume IDs follow patterns like:
- "vm-100-disk-0" => ["vm", 100]
- "subvol-200-disk-0" => ["ct", 200]
- "base-100-disk-0" => ["vm", 100]
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 |
# File 'lib/pvectl/repositories/volume.rb', line 247 def extract_resource_from_volume_id(volume_id, content) return [nil, nil] unless volume_id vol_part = volume_id.split(":").last return [nil, nil] unless vol_part case vol_part when /\Avm-(\d+)-/ ["vm", ::Regexp.last_match(1).to_i] when /\Asubvol-(\d+)-/ ["ct", ::Regexp.last_match(1).to_i] when /\Abase-(\d+)-/ type = content == "rootdir" ? "ct" : "vm" [type, ::Regexp.last_match(1).to_i] else [nil, nil] end end |
#extract_volumes(config, resource_type, resource_id, node) ⇒ Array<Models::Volume>
Extracts volume models from a config hash.
Iterates over config keys, selects disk-related entries, excludes CD-ROMs, and builds Volume models.
147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/pvectl/repositories/volume.rb', line 147 def extract_volumes(config, resource_type, resource_id, node) pattern = resource_type == "vm" ? VM_DISK_PATTERN : CT_DISK_PATTERN config.each_with_object([]) do |(key, value), volumes| key_str = key.to_s next unless key_str.match?(pattern) value_str = value.to_s next if value_str.include?("media=cdrom") volumes << parse_config_value(key_str, value_str, resource_type, resource_id, node) end end |
#fetch_storage_volumes(node_name, storage) ⇒ Array<Models::Volume>
Fetches volumes from a storage on a specific node.
207 208 209 210 211 212 213 |
# File 'lib/pvectl/repositories/volume.rb', line 207 def fetch_storage_volumes(node_name, storage) response = connection.client["nodes/#{node_name}/storage/#{storage}/content"].get data = unwrap(response) data.map { |item| build_storage_volume(item, node_name, storage) } rescue StandardError [] end |
#find(resource_type:, id:, disk_name:, node: nil) ⇒ Models::Volume?
Finds a specific volume by disk name in a VM/CT config.
91 92 93 94 |
# File 'lib/pvectl/repositories/volume.rb', line 91 def find(resource_type:, id:, disk_name:, node: nil) volumes = list_from_config(resource_type: resource_type, ids: [id], node: node) volumes.find { |v| v.name == disk_name } end |
#format_bytes_to_size(bytes) ⇒ String?
Formats bytes to human-readable size string.
270 271 272 273 274 275 276 277 278 279 280 |
# File 'lib/pvectl/repositories/volume.rb', line 270 def format_bytes_to_size(bytes) return nil unless bytes.is_a?(Integer) && bytes.positive? gb = bytes / (1024 * 1024 * 1024) return "#{gb}G" if gb.positive? mb = bytes / (1024 * 1024) return "#{mb}M" if mb.positive? "#{bytes}B" end |
#list_from_config(resource_type:, ids:, node: nil) ⇒ Array<Models::Volume>
Lists volumes from VM/CT configuration for given resource IDs.
Fetches config from each VM/CT and extracts disk entries.
Excludes CD-ROM entries (containing media=cdrom).
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/pvectl/repositories/volume.rb', line 54 def list_from_config(resource_type:, ids:, node: nil) type = normalize_resource_type(resource_type) repo = repo_for(type) return [] unless repo volumes = ids.flat_map do |id| resource = repo.get(id) next [] if resource.nil? next [] if node && resource.node != node config = repo.fetch_config(resource.node, id.to_i) extract_volumes(config, type, id.to_i, resource.node) end volumes end |
#list_from_storage(storage:, node: nil) ⇒ Array<Models::Volume>
Lists volumes from storage content API.
Queries /nodes/{node}/storage/{storage}/content to list
all volumes in the given storage.
79 80 81 82 |
# File 'lib/pvectl/repositories/volume.rb', line 79 def list_from_storage(storage:, node: nil) nodes = node ? [node] : online_nodes nodes.flat_map { |node_name| fetch_storage_volumes(node_name, storage) } end |
#normalize_resource_type(type) ⇒ String
Normalizes resource type string to canonical form.
129 130 131 132 133 134 135 |
# File 'lib/pvectl/repositories/volume.rb', line 129 def normalize_resource_type(type) case type.to_s.downcase when "vm", "qemu" then "vm" when "ct", "container", "lxc" then "ct" else type.to_s.downcase end end |
#online_nodes ⇒ Array<String>
Fetches list of online node names.
285 286 287 288 289 290 291 292 293 |
# File 'lib/pvectl/repositories/volume.rb', line 285 def online_nodes response = connection.client["nodes"].get nodes_data = unwrap(response) nodes_data .select { |n| n[:status] == "online" } .map { |n| n[:node] || n[:name] } rescue StandardError [] end |
#parse_config_value(name, value, resource_type, resource_id, node) ⇒ Models::Volume
Parses a config value string into a Volume model.
Config values have the format:
"storage:volume-id,key1=val1,key2=val2"
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/pvectl/repositories/volume.rb', line 172 def parse_config_value(name, value, resource_type, resource_id, node) # Split "storage:volume-id,key=val,..." into storage_part and options parts = value.split(",") storage_spec = parts.shift || "" storage, volume_id = storage_spec.split(":", 2) # Parse key=value options attrs = { name: name, storage: storage, volume_id: volume_id, resource_type: resource_type, resource_id: resource_id, node: node } parts.each do |part| k, v = part.split("=", 2) next unless k && v case k when "size" then attrs[:size] = v when "format" then attrs[:format] = v when "cache" then attrs[:cache] = v when "discard" then attrs[:discard] = v when "ssd" then attrs[:ssd] = parse_int(v) when "iothread" then attrs[:iothread] = parse_int(v) when "backup" then attrs[:backup] = parse_int(v) when "mp" then attrs[:mp] = v end end Models::Volume.new(attrs) end |
#parse_int(value) ⇒ Integer?
Parses a string value to integer.
299 300 301 302 303 |
# File 'lib/pvectl/repositories/volume.rb', line 299 def parse_int(value) return nil unless value value.to_i end |
#repo_for(type) ⇒ Repositories::Vm, ...
Returns the appropriate repository for the given resource type.
102 103 104 105 106 107 |
# File 'lib/pvectl/repositories/volume.rb', line 102 def repo_for(type) case type when "vm" then vm_repo when "ct" then container_repo end end |
#vm_repo ⇒ Repositories::Vm
Returns VM repository instance. Uses injected repository if provided, otherwise creates new one.
113 114 115 |
# File 'lib/pvectl/repositories/volume.rb', line 113 def vm_repo @vm_repo ||= Repositories::Vm.new(connection) end |