Class: Pvectl::Repositories::Storage
- Defined in:
- lib/pvectl/repositories/storage.rb,
sig/pvectl/repositories/storage.rbs
Overview
Repository for Proxmox cluster storage pools.
Uses the /cluster/resources?type=storage API endpoint for cluster-wide view.
Handles aggregation of shared storage (deduplication by name).
Instance Attribute Summary
Attributes inherited from Base
Instance Method Summary collapse
-
#aggregate_storage(storage_data) ⇒ Array<Hash>
Aggregates storage data by name.
-
#build_describe_model(storage, config, status, content) ⇒ Models::Storage
Builds Storage model with comprehensive describe data.
-
#build_model(data) ⇒ Models::Storage
Builds Storage model from API response data.
-
#build_model_from_node_api(data, node_name) ⇒ Models::Storage
Builds Storage model from /nodes/node/storage API response.
-
#describe(name, node: nil) ⇒ Models::Storage?
Describes a storage with comprehensive details from multiple API endpoints.
-
#fetch_storage_config(name) ⇒ Hash
Fetches storage configuration from /storage/name.
-
#fetch_storage_content(node, name) ⇒ Array<Hash>
Fetches storage content (volumes) from /nodes/node/storage/name/content.
-
#fetch_storage_status(node, name) ⇒ Hash
Fetches storage status from /nodes/node/storage/name/status.
-
#find_node_for_storage(name, storage) ⇒ String?
Finds an active node where this storage is accessible.
-
#get(name) ⇒ Models::Storage?
Gets a single storage pool by name.
-
#get_for_node(name, node) ⇒ Models::Storage?
Gets storage for a specific node.
-
#list(node: nil) ⇒ Array<Models::Storage>
Lists all storage pools in the cluster.
-
#list_for_node(node_name) ⇒ Array<Models::Storage>
Lists storage pools for a specific node.
-
#list_instances(name) ⇒ Array<Models::Storage>
Lists all instances of a storage by name.
-
#normalize_response(response) ⇒ Array<Hash>
Normalizes API response to array format.
Methods inherited from Base
#extract_data, #initialize, #models_from, #unwrap
Constructor Details
This class inherits a constructor from Pvectl::Repositories::Base
Instance Method Details
#aggregate_storage(storage_data) ⇒ Array<Hash>
Aggregates storage data by name. For shared storage, keeps first entry (data is identical across nodes). For local storage, keeps all entries.
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 |
# File 'lib/pvectl/repositories/storage.rb', line 283 def aggregate_storage(storage_data) seen = {} storage_data.each do |data| name = data[:storage] next if name.nil? # For shared storage, keep only first entry if data[:shared] == 1 seen[name] ||= data else # For local storage, include all (unique by name+node) key = "#{name}:#{data[:node]}" seen[key] ||= data end end seen.values end |
#build_describe_model(storage, config, status, content) ⇒ Models::Storage
Builds Storage model with comprehensive describe data.
Merges data from basic storage, config, status, and content endpoints.
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 |
# File 'lib/pvectl/repositories/storage.rb', line 230 def build_describe_model(storage, config, status, content) Models::Storage.new( # Basic attributes from list name: storage.name, plugintype: storage.plugintype, status: storage.status, node: storage.node, disk: storage.disk, maxdisk: storage.maxdisk, content: storage.content, shared: storage.shared, # Config attributes path: config[:path], server: config[:server], export: config[:export], pool: config[:pool], vgname: config[:vgname], thinpool: config[:thinpool], nodes: config[:nodes], "prune-backups": config[:"prune-backups"], maxfiles: config[:maxfiles], # Status attributes (override if available) avail: status[:avail] || storage.avail, enabled: status[:enabled] || storage.enabled, active: status[:active] || storage.active_flag, # Content (volumes) volumes: content ) end |
#build_model(data) ⇒ Models::Storage
Builds Storage model from API response data.
124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/pvectl/repositories/storage.rb', line 124 def build_model(data) Models::Storage.new( name: data[:storage], plugintype: data[:plugintype], status: data[:status], node: data[:node], disk: data[:disk], maxdisk: data[:maxdisk], content: data[:content], shared: data[:shared] ) end |
#build_model_from_node_api(data, node_name) ⇒ Models::Storage
Builds Storage model from /nodes/node/storage API response.
Maps node-specific API fields to model attributes:
- type -> plugintype
- used -> disk
- total -> maxdisk
- active -> status (derived)
148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/pvectl/repositories/storage.rb', line 148 def build_model_from_node_api(data, node_name) Models::Storage.new( name: data[:storage], plugintype: data[:type], node: node_name, disk: data[:used], maxdisk: data[:total], avail: data[:avail], content: data[:content], enabled: data[:enabled], active: data[:active], shared: 0 # /nodes/{node}/storage doesn't return shared flag ) end |
#describe(name, node: nil) ⇒ Models::Storage?
Describes a storage with comprehensive details from multiple API endpoints.
Fetches:
- Basic storage info from cluster resources (via get or get_for_node)
- Configuration from /storage/name
- Status from /nodes/node/storage/name/status
- Content (volumes) from /nodes/node/storage/name/content
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/pvectl/repositories/storage.rb', line 85 def describe(name, node: nil) storage = node ? get_for_node(name, node) : get(name) return nil unless storage # GET /storage/{name} - configuration config = fetch_storage_config(name) # Find active node for this storage node = find_node_for_storage(name, storage) # GET /nodes/{node}/storage/{name}/status (if node available) status = node ? fetch_storage_status(node, name) : {} # GET /nodes/{node}/storage/{name}/content (volumes) content = node ? fetch_storage_content(node, name) : [] build_describe_model(storage, config, status, content) end |
#fetch_storage_config(name) ⇒ Hash
Fetches storage configuration from /storage/name.
169 170 171 172 173 174 |
# File 'lib/pvectl/repositories/storage.rb', line 169 def fetch_storage_config(name) response = connection.client["storage/#{name}"].get extract_data(response) rescue StandardError {} end |
#fetch_storage_content(node, name) ⇒ Array<Hash>
Fetches storage content (volumes) from /nodes/node/storage/name/content.
193 194 195 196 197 198 |
# File 'lib/pvectl/repositories/storage.rb', line 193 def fetch_storage_content(node, name) response = connection.client["nodes/#{node}/storage/#{name}/content"].get unwrap(response) rescue StandardError [] end |
#fetch_storage_status(node, name) ⇒ Hash
Fetches storage status from /nodes/node/storage/name/status.
181 182 183 184 185 186 |
# File 'lib/pvectl/repositories/storage.rb', line 181 def fetch_storage_status(node, name) response = connection.client["nodes/#{node}/storage/#{name}/status"].get extract_data(response) rescue StandardError {} end |
#find_node_for_storage(name, storage) ⇒ String?
Finds an active node where this storage is accessible.
For local storage: uses the node it belongs to. For shared storage: finds first online node in the cluster.
208 209 210 211 212 213 214 215 216 217 218 219 |
# File 'lib/pvectl/repositories/storage.rb', line 208 def find_node_for_storage(name, storage) # For local storage, use the node it belongs to return storage.node unless storage.shared? # For shared storage, find first available online node nodes_response = connection.client["nodes"].get nodes = unwrap(nodes_response) online_node = nodes.find { |n| n[:status] == "online" } online_node&.dig(:node) rescue StandardError nil end |
#get(name) ⇒ Models::Storage?
Gets a single storage pool by name.
48 49 50 |
# File 'lib/pvectl/repositories/storage.rb', line 48 def get(name) list.find { |s| s.name == name } end |
#get_for_node(name, node) ⇒ Models::Storage?
Gets storage for a specific node.
70 71 72 |
# File 'lib/pvectl/repositories/storage.rb', line 70 def get_for_node(name, node) list_instances(name).find { |s| s.node == node } end |
#list(node: nil) ⇒ Array<Models::Storage>
Lists all storage pools in the cluster.
Uses /cluster/resources?type=storage endpoint for cluster-wide view.
Aggregates shared storage by keeping first entry per storage name.
29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/pvectl/repositories/storage.rb', line 29 def list(node: nil) response = connection.client["cluster/resources"].get(params: { type: "storage" }) storage_data = normalize_response(response) # Aggregate shared storage (deduplicate by name, keep first entry) aggregated = aggregate_storage(storage_data) # Filter by node if specified if node aggregated = aggregated.select { |data| data[:node] == node || data[:shared] == 1 } end aggregated.map { |data| build_model(data) } end |
#list_for_node(node_name) ⇒ Array<Models::Storage>
Lists storage pools for a specific node.
Uses /nodes/{node}/storage endpoint which returns detailed
per-node storage information including avail, enabled, active flags.
111 112 113 114 115 116 |
# File 'lib/pvectl/repositories/storage.rb', line 111 def list_for_node(node_name) response = connection.client["nodes/#{node_name}/storage"].get storage_data = normalize_response(response) storage_data.map { |data| build_model_from_node_api(data, node_name) } end |
#list_instances(name) ⇒ Array<Models::Storage>
Lists all instances of a storage by name.
For shared storage: returns single instance. For local storage: returns all instances (one per node).
59 60 61 62 63 |
# File 'lib/pvectl/repositories/storage.rb', line 59 def list_instances(name) response = connection.client["cluster/resources"].get(params: { type: "storage" }) storage_data = normalize_response(response) storage_data.select { |s| s[:storage] == name }.map { |data| build_model(data) } end |
#normalize_response(response) ⇒ Array<Hash>
Normalizes API response to array format.
267 268 269 270 271 272 273 274 275 |
# File 'lib/pvectl/repositories/storage.rb', line 267 def normalize_response(response) if response.is_a?(Array) response elsif response.is_a?(Hash) && response[:data] response[:data] else response.to_a end end |