Class: Pvectl::Commands::CreateSnapshot

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

Overview

Handler for the pvectl create snapshot sub-command.

Creates snapshots for VMs/containers. Snapshot name is the first positional argument, VMIDs are specified via --vmid flag. Without --vmid, operates on ALL VMs/CTs in the cluster.

Examples:

Create single snapshot

pvectl create snapshot before-upgrade --vmid 100

Create for multiple VMs

pvectl create snapshot before-upgrade --vmid 100 --vmid 101

Create cluster-wide

pvectl create snapshot before-upgrade --yes

Constant Summary collapse

VMID_PATTERN =

Returns:

/\A[1-9]\d{0,8}\z/

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(args, options, global_options) ⇒ CreateSnapshot

Initializes a create snapshot command.

Parameters:

  • positional args (snapshot name)

  • command options

  • global CLI options



86
87
88
89
90
91
92
93
# File 'lib/pvectl/commands/create_snapshot.rb', line 86

def initialize(args, options, global_options)
  @args = Array(args)
  @options = options
  @global_options = global_options
  @snapshot_name = @args.first
  @vmids = parse_vmids(options[:vmid])
  @node = options[:node]
end

Class Method Details

.execute(args, options, global_options) ⇒ Integer

Executes the create snapshot command.

Parameters:

  • positional args (snapshot name)

  • command options

  • global CLI options

Returns:

  • exit code



77
78
79
# File 'lib/pvectl/commands/create_snapshot.rb', line 77

def self.execute(args, options, global_options)
  new(args, options, global_options).execute
end

.register_subcommand(parent) ⇒ void

This method returns an undefined value.

Registers as a sub-command under the parent create command.

Parameters:

  • the parent create command



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/pvectl/commands/create_snapshot.rb', line 27

def self.register_subcommand(parent)
  parent.desc "Create a snapshot of VMs or containers"
  parent.long_desc "    Create a snapshot of one or more VMs or containers. Snapshots capture\n    the current state of disks (and optionally RAM) for later rollback.\n\n    EXAMPLES\n      Snapshot a single VM:\n        $ pvectl create snapshot before-upgrade --vmid 100\n\n      Snapshot multiple VMs:\n        $ pvectl create snapshot before-upgrade --vmid 100 --vmid 101\n\n      Snapshot with description and VM memory state:\n        $ pvectl create snapshot before-upgrade --vmid 100 --description \"Pre-upgrade\" --vmstate\n\n      Snapshot all VMs/containers cluster-wide:\n        $ pvectl create snapshot before-upgrade --yes\n\n    NOTES\n      --vmstate saves the VM's RAM state (QEMU only, not for containers).\n      This increases snapshot size but allows resuming from exact state.\n\n      Without --vmid, operates cluster-wide (requires --yes confirmation).\n\n    SEE ALSO\n      pvectl help rollback        Rollback to a snapshot\n      pvectl help delete snapshot Delete snapshots\n      pvectl help get snapshots   List existing snapshots\n  HELP\n  parent.command :snapshot do |s|\n    s.desc \"VM/CT ID (repeatable)\"\n    s.flag [:vmid], arg_name: \"VMID\", multiple: true\n\n    s.desc \"Save VM memory state (QEMU only)\"\n    s.switch [:vmstate], negatable: false\n\n    s.action do |global_options, options, args|\n      exit_code = execute(args, options, global_options)\n      exit exit_code if exit_code != 0\n    end\n  end\nend\n"

Instance Method Details

#confirm_operationBoolean

Confirms operation with user prompt.

Returns:

  • true if operation should proceed



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

def confirm_operation
  return true if @vmids.size == 1
  return true if @options[:yes]

  if @vmids.empty?
    $stdout.puts "You are about to create snapshot '#{@snapshot_name}' for ALL VMs/CTs in the cluster."
  else
    $stdout.puts "You are about to create snapshot '#{@snapshot_name}' for #{@vmids.size} VMs:"
    @vmids.each { |vmid| $stdout.puts "  - #{vmid}" }
  end
  $stdout.puts ""
  $stdout.print "Proceed? [y/N]: "

  response = $stdin.gets&.strip&.downcase
  %w[y yes].include?(response)
end

#determine_exit_code(results) ⇒ Integer

Determines exit code.

Parameters:

  • results

Returns:

  • exit code



225
226
227
228
229
230
# File 'lib/pvectl/commands/create_snapshot.rb', line 225

def determine_exit_code(results)
  return ExitCodes::SUCCESS if results.all?(&:successful?)
  return ExitCodes::SUCCESS if results.all?(&:pending?)

  ExitCodes::GENERAL_ERROR
end

#executeInteger

Executes the create snapshot command.

Returns:

  • exit code



98
99
100
101
102
103
# File 'lib/pvectl/commands/create_snapshot.rb', line 98

def execute
  return usage_error("Snapshot name required") unless @snapshot_name
  return usage_error("Invalid VMID: #{invalid_vmid}") if invalid_vmid

  perform_operation
end

#invalid_vmidString?

Finds first invalid VMID in options.

Returns:

  • invalid VMID value or nil



120
121
122
123
124
# File 'lib/pvectl/commands/create_snapshot.rb', line 120

def invalid_vmid
  return nil if @options[:vmid].nil? || @options[:vmid].empty?

  Array(@options[:vmid]).find { |v| !VMID_PATTERN.match?(v.to_s) }
end

#load_configvoid

This method returns an undefined value.

Loads configuration.



190
191
192
193
194
# File 'lib/pvectl/commands/create_snapshot.rb', line 190

def load_config
  service = Pvectl::Config::Service.new
  service.load(config: @global_options[:config])
  @config = service.current_config
end

#output_results(results) ⇒ void

This method returns an undefined value.

Outputs operation results.

Parameters:

  • results



211
212
213
214
215
216
217
218
219
# File 'lib/pvectl/commands/create_snapshot.rb', line 211

def output_results(results)
  presenter = Pvectl::Presenters::SnapshotOperationResult.new
  format = @global_options[:output] || "table"
  color_flag = @global_options[:color]

  formatter = Pvectl::Formatters::Registry.for(format)
  output = formatter.format(results, presenter, color: color_flag)
  puts output
end

#parse_vmids(vmid_values) ⇒ Array<Integer>

Parses --vmid flag values to integer array.

Parameters:

  • raw vmid values

Returns:

  • parsed VMIDs



111
112
113
114
115
# File 'lib/pvectl/commands/create_snapshot.rb', line 111

def parse_vmids(vmid_values)
  return [] if vmid_values.nil? || vmid_values.empty?

  Array(vmid_values).map(&:to_i)
end

#perform_operationInteger

Performs the snapshot creation operation.

Returns:

  • exit code



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/pvectl/commands/create_snapshot.rb', line 129

def perform_operation
  load_config
  connection = Pvectl::Connection.new(@config)

  snapshot_repo = Pvectl::Repositories::Snapshot.new(connection)
  resolver = Pvectl::Utils::ResourceResolver.new(connection)
  task_repo = Pvectl::Repositories::Task.new(connection)

  service = Pvectl::Services::Snapshot.new(
    snapshot_repo: snapshot_repo,
    resource_resolver: resolver,
    task_repo: task_repo,
    options: service_options
  )

  return ExitCodes::SUCCESS unless confirm_operation

  results = service.create(
    @vmids,
    name: @snapshot_name,
    description: @options[:description],
    vmstate: @options[:vmstate] || false,
    node: @node
  )

  output_results(results)
  determine_exit_code(results)
rescue Pvectl::Config::ConfigNotFoundError,
       Pvectl::Config::InvalidConfigError,
       Pvectl::Config::ContextNotFoundError,
       Pvectl::Config::ClusterNotFoundError,
       Pvectl::Config::UserNotFoundError
  raise
rescue StandardError => e
  $stderr.puts "Error: #{e.message}"
  ExitCodes::GENERAL_ERROR
end

#service_optionsHash

Builds service options.

Returns:

  • service options



199
200
201
202
203
204
205
# File 'lib/pvectl/commands/create_snapshot.rb', line 199

def service_options
  opts = {}
  opts[:timeout] = @options[:timeout] if @options[:timeout]
  opts[:async] = true if @options[:async]
  opts[:fail_fast] = true if @options[:"fail-fast"]
  opts
end

#usage_error(message) ⇒ Integer

Outputs usage error.

Parameters:

  • error message

Returns:

  • exit code



236
237
238
239
# File 'lib/pvectl/commands/create_snapshot.rb', line 236

def usage_error(message)
  $stderr.puts "Error: #{message}"
  ExitCodes::USAGE_ERROR
end