Class: Pvectl::Presenters::Base Abstract

Inherits:
Object
  • Object
show all
Defined in:
lib/pvectl/presenters/base.rb,
sig/pvectl/presenters/base.rbs

Overview

This class is abstract.

Subclass and implement #columns, #to_row, and #to_hash.

Abstract base class for resource presenters.

Presenters define how models are displayed in different formats. Each resource type (VM, Container, Node, etc.) has its own presenter.

Examples:

Implementing a resource presenter

class VmPresenter < Base
  def columns
    ["NAME", "STATUS", "NODE"]
  end

  def extra_columns
    ["MEMORY", "CPU"]
  end

  def to_row(model, **context)
    [model.name, model.status, model.node]
  end

  def extra_values(model, **context)
    [model.memory, model.cpu]
  end

  def to_hash(model)
    { "name" => model.name, "status" => model.status, "node" => model.node }
  end
end

See Also:

Instance Method Summary collapse

Instance Method Details

#columnsArray<String>

Returns column headers for table format.

Raises:

  • if not implemented by subclass

Returns:

  • column names (uppercase, e.g., ["NAME", "STATUS"])



42
43
44
# File 'lib/pvectl/presenters/base.rb', line 42

def columns
  raise NotImplementedError, "#{self.class}#columns must be implemented"
end

#extra_columnsArray<String>

Returns additional columns for wide format. Override in subclass to add extra columns.

Returns:

  • extra column names (empty by default)



58
59
60
# File 'lib/pvectl/presenters/base.rb', line 58

def extra_columns
  []
end

#extra_values(model, **context) ⇒ Array<String, nil>

Returns additional values for wide format. Override in subclass to add extra values.

Parameters:

  • domain model object

  • optional context

Returns:

  • extra values (empty by default)



88
89
90
# File 'lib/pvectl/presenters/base.rb', line 88

def extra_values(model, **context)
  []
end

#format_bytes(bytes) ⇒ String

Formats bytes to human readable string.

Parameters:

  • bytes

Returns:

  • formatted size



268
269
270
271
272
273
274
275
276
277
278
279
280
# File 'lib/pvectl/presenters/base.rb', line 268

def format_bytes(bytes)
  return "-" if bytes.nil? || bytes.zero?

  if bytes >= 1024 * 1024 * 1024
    "#{(bytes.to_f / 1024 / 1024 / 1024).round(1)} GiB"
  elsif bytes >= 1024 * 1024
    "#{(bytes.to_f / 1024 / 1024).round(1)} MiB"
  elsif bytes >= 1024
    "#{(bytes.to_f / 1024).round(1)} KiB"
  else
    "#{bytes} B"
  end
end

#format_firewall(firewall_data) ⇒ Hash, String

Formats Firewall section from dedicated API data.

Shows firewall options, rules, aliases, and IP sets from the /firewall/ API endpoints. Shared by VM and CT presenters.

Parameters:

  • firewall data with :options, :rules, :aliases, :ipset

Returns:

  • firewall info or "-" if no data



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/pvectl/presenters/base.rb', line 194

def format_firewall(firewall_data)
  return "-" if firewall_data.nil? || firewall_data.empty?

  options = firewall_data[:options]
  options = {} unless options.is_a?(Hash)
  aliases = firewall_data[:aliases]
  aliases = [] unless aliases.is_a?(Array)
  ipset = firewall_data[:ipset]
  ipset = [] unless ipset.is_a?(Array)

  result = {
    "Enable" => options[:enable] == 1 ? "Yes" : "No",
    "Input Policy" => (options[:policy_in] || "DROP").to_s,
    "Output Policy" => (options[:policy_out] || "ACCEPT").to_s
  }

  # Optional options
  result["DHCP"] = options[:dhcp] == 1 ? "Yes" : "No" if options.key?(:dhcp)
  result["MAC Filter"] = options[:macfilter] == 1 ? "Yes" : "No" if options.key?(:macfilter)
  result["IP Filter"] = options[:ipfilter] == 1 ? "Yes" : "No" if options.key?(:ipfilter)
  result["NDP"] = options[:ndp] == 1 ? "Yes" : "No" if options.key?(:ndp)
  result["Router Advertisement"] = options[:radv] == 1 ? "Yes" : "No" if options.key?(:radv)
  result["Log Level In"] = options[:log_level_in].to_s if options[:log_level_in] && options[:log_level_in] != "nolog"
  result["Log Level Out"] = options[:log_level_out].to_s if options[:log_level_out] && options[:log_level_out] != "nolog"

  # Aliases table
  if aliases.any?
    result["Aliases"] = aliases.map do |a|
      { "NAME" => a[:name] || "-", "CIDR" => a[:cidr] || "-", "COMMENT" => a[:comment] || "-" }
    end
  end

  # IP Sets table
  if ipset.any?
    result["IP Sets"] = ipset.map do |s|
      { "NAME" => s[:name] || "-", "COMMENT" => s[:comment] || "-" }
    end
  end

  result
end

#format_firewall_rules(firewall_data) ⇒ Array<Hash>, String

Formats Firewall Rules section as a standalone table.

Renders firewall rules with the columns: ENABLED, TYPE, ACTION, PROTO, SOURCE, DEST, COMMENT. Used as a top-level describe section for VMs and containers. Empty or missing rules render as "-".

Parameters:

  • firewall data with :rules key

Returns:

  • rules table or "-" when empty



244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/pvectl/presenters/base.rb', line 244

def format_firewall_rules(firewall_data)
  return "-" if firewall_data.nil? || firewall_data.empty?

  rules = firewall_data[:rules]
  rules = [] unless rules.is_a?(Array)
  return "-" if rules.empty?

  rules.sort_by { |r| r[:pos].to_i }.map do |rule|
    {
      "ENABLED" => rule[:enable] == 1 ? "yes" : "no",
      "TYPE" => rule[:type]&.to_s&.upcase || "-",
      "ACTION" => rule[:action] || "-",
      "PROTO" => rule[:proto] || "-",
      "SOURCE" => (rule[:source].nil? || rule[:source].to_s.empty?) ? "-" : rule[:source],
      "DEST" => (rule[:dest].nil? || rule[:dest].to_s.empty?) ? "-" : rule[:dest],
      "COMMENT" => (rule[:comment].nil? || rule[:comment].to_s.empty?) ? "-" : rule[:comment]
    }
  end
end

#format_task_history(tasks) ⇒ Array<Hash>, String

Formats task history for describe output.

Parameters:

  • recent tasks

Returns:

  • table data or "No task history"



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/pvectl/presenters/base.rb', line 171

def format_task_history(tasks)
  return "No task history" if tasks.nil? || tasks.empty?

  tasks.map do |task|
    start = task.starttime ? Time.at(task.starttime).strftime("%Y-%m-%d %H:%M:%S") : "-"
    dur = task.duration ? "#{task.duration}s" : "-"
    {
      "TYPE" => task.type || "-",
      "STATUS" => task.exitstatus || task.status || "-",
      "DATE" => start,
      "DURATION" => dur,
      "USER" => task.user || "-"
    }
  end
end

#resourceObject

Returns the current resource model. Must be implemented by subclasses.

Raises:

  • if not implemented

Returns:

  • current model



163
164
165
# File 'lib/pvectl/presenters/base.rb', line 163

def resource
  raise NotImplementedError, "#{self.class}#resource must be implemented"
end

#tags_arrayArray<String>

Returns tags as array.

Returns:

  • array of tags, or empty array if no tags



134
135
136
137
138
139
# File 'lib/pvectl/presenters/base.rb', line 134

def tags_array
  tags = resource.tags
  return [] if tags.nil? || tags.empty?

  tags.split(";").map(&:strip)
end

#tags_displayString

Returns tags as comma-separated string.

Returns:

  • formatted tags (e.g., "prod, web") or "-" if no tags



144
145
146
147
# File 'lib/pvectl/presenters/base.rb', line 144

def tags_display
  arr = tags_array
  arr.empty? ? "-" : arr.join(", ")
end

#template_displayString

Returns template display string.

Returns:

  • "yes" if template, "-" otherwise



152
153
154
# File 'lib/pvectl/presenters/base.rb', line 152

def template_display
  resource.template? ? "yes" : "-"
end

#to_description(model) ⇒ Hash

Converts model to description format (kubectl-style vertical layout). By default, delegates to to_hash. Override for custom describe output.

Parameters:

  • domain model object

Returns:

  • hash representation (keys become labels)



106
107
108
# File 'lib/pvectl/presenters/base.rb', line 106

def to_description(model)
  to_hash(model)
end

#to_hash(model) ⇒ Hash

Converts model to hash for JSON/YAML format.

Raises:

  • if not implemented by subclass

Parameters:

  • domain model object

Returns:

  • hash representation with string keys



97
98
99
# File 'lib/pvectl/presenters/base.rb', line 97

def to_hash(model)
  raise NotImplementedError, "#{self.class}#to_hash must be implemented"
end

#to_row(model, **context) ⇒ Array<String, nil>

Converts model to table row values.

Raises:

  • if not implemented by subclass

Parameters:

  • domain model object

  • optional context (e.g., current_context: "prod")

Returns:

  • row values matching columns order



68
69
70
# File 'lib/pvectl/presenters/base.rb', line 68

def to_row(model, **context)
  raise NotImplementedError, "#{self.class}#to_row must be implemented"
end

#to_wide_row(model, **context) ⇒ Array<String, nil>

Converts model to wide table row values. By default, appends extra_values to to_row.

Parameters:

  • domain model object

  • optional context

Returns:

  • row values (normal + extra)



78
79
80
# File 'lib/pvectl/presenters/base.rb', line 78

def to_wide_row(model, **context)
  to_row(model, **context) + extra_values(model, **context)
end

#uptime_humanString

Returns uptime in human-readable format. Delegates to resource.uptime. Override in subclasses with custom logic.

Returns:

  • formatted uptime (e.g., "15d 3h") or "-"



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/pvectl/presenters/base.rb', line 114

def uptime_human
  uptime = resource.uptime
  return "-" if uptime.nil? || uptime.zero?

  days = uptime / 86_400
  hours = (uptime % 86_400) / 3600
  minutes = (uptime % 3600) / 60

  if days.positive?
    "#{days}d #{hours}h"
  elsif hours.positive?
    "#{hours}h #{minutes}m"
  else
    "#{minutes}m"
  end
end

#wide_columnsArray<String>

Returns extended column headers for wide format. By default, appends extra_columns to columns.

Returns:

  • column names (normal + extra)



50
51
52
# File 'lib/pvectl/presenters/base.rb', line 50

def wide_columns
  columns + extra_columns
end