Module: Pvectl::Commands::MigrateCommand
- Included in:
- MigrateContainer, MigrateVm
- Defined in:
- lib/pvectl/commands/migrate_command.rb,
sig/pvectl/commands/migrate_command.rbs
Overview
Shared functionality for migrate commands (migrate vm, migrate container).
This module extracts common code used by MigrateVm and MigrateContainer. Pattern: identical to DeleteCommand module but with migrate-specific behavior:
- Requires --target flag (no default)
- Validates --restart only for containers
- Async is default (no --async flag), --wait for sync
- partition_by_target logic is in Service layer
Defined Under Namespace
Modules: ClassMethods
Constant Summary collapse
- NODE_NAME_FORMAT =
Valid Proxmox node name format: lowercase alphanumeric, starting with letter, hyphens allowed.
/\A[a-z][a-z0-9-]*\z/
Class Method Summary collapse
-
.included(base) ⇒ void
Hook called when module is included.
Instance Method Summary collapse
-
#apply_selectors(resources) ⇒ Array
Applies selectors to resource collection.
-
#confirm_operation(resources, target) ⇒ Boolean
Confirms migrate operation with user.
-
#determine_exit_code(results) ⇒ Integer
Determines exit code based on results.
-
#execute ⇒ Integer
Executes the migrate command.
-
#initialize(args, options, global_options) ⇒ MigrateCommand
Initializes a migrate command.
-
#load_config ⇒ void
Loads configuration from file or environment.
-
#no_resources_found ⇒ Integer
Outputs no resources found error and returns exit code.
-
#output_results(results) ⇒ void
Outputs operation results using the configured formatter.
-
#perform_operation ⇒ Integer
Performs the migrate operation.
-
#resolve_resources(connection) ⇒ Array<Models::Vm, Models::Container>
Resolves resources based on IDs, --all flag, or selectors.
-
#resource_type_symbol ⇒ Symbol
Returns the resource type symbol (:vm or :container).
-
#restart_not_allowed? ⇒ Boolean
Checks if --restart is used for a non-container resource.
-
#selector_strings ⇒ Array<String>
Returns selector strings from options.
-
#service_options ⇒ Hash
Builds service options from command options.
-
#usage_error(message) ⇒ Integer
Outputs usage error and returns exit code.
Class Method Details
.included(base) ⇒ void
This method returns an undefined value.
Hook called when module is included.
41 42 43 |
# File 'lib/pvectl/commands/migrate_command.rb', line 41 def self.included(base) base.extend(ClassMethods) end |
Instance Method Details
#apply_selectors(resources) ⇒ Array
Applies selectors to resource collection.
171 172 173 174 175 176 177 |
# File 'lib/pvectl/commands/migrate_command.rb', line 171 def apply_selectors(resources) return resources if selector_strings.empty? selector_class = resource_type_symbol == :vm ? Selectors::Vm : Selectors::Container selector = selector_class.parse_all(selector_strings) selector.apply(resources) end |
#confirm_operation(resources, target) ⇒ Boolean
Confirms migrate operation with user.
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/pvectl/commands/migrate_command.rb', line 184 def confirm_operation(resources, target) return true if [:yes] type_name = resource_type_symbol == :vm ? "VM" : "container" type_plural = resource_type_symbol == :vm ? "VMs" : "containers" if resources.size == 1 r = resources.first $stdout.puts "You are about to migrate #{type_name} #{r.vmid} (#{r.name || 'unnamed'}) from #{r.node} to #{target}." else $stdout.puts "You are about to migrate #{resources.size} #{type_plural} to #{target}:" resources.each { |r| $stdout.puts " - #{r.vmid} (#{r.name || 'unnamed'}) on #{r.node}" } end $stdout.puts "" $stdout.puts "This will migrate the #{type_plural} to node #{target}." $stdout.print "Proceed? [y/N]: " response = $stdin.gets&.strip&.downcase %w[y yes].include?(response) end |
#determine_exit_code(results) ⇒ Integer
Determines exit code based on results.
251 252 253 254 255 256 |
# File 'lib/pvectl/commands/migrate_command.rb', line 251 def determine_exit_code(results) return ExitCodes::SUCCESS if results.all?(&:successful?) return ExitCodes::SUCCESS if results.all?(&:pending?) ExitCodes::GENERAL_ERROR end |
#execute ⇒ Integer
Executes the migrate command.
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/pvectl/commands/migrate_command.rb', line 59 def execute target = [:target] return usage_error("--target is required") if target.nil? || target.empty? unless target.match?(NODE_NAME_FORMAT) return usage_error("Invalid target node name: #{target}") end return usage_error("--restart is only supported for containers") if restart_not_allowed? if @resource_ids.empty? && ![:all] && selector_strings.empty? return usage_error("VMID, --all, or -l selector required") end perform_operation end |
#initialize(args, options, global_options) ⇒ MigrateCommand
Initializes a migrate command.
50 51 52 53 54 |
# File 'lib/pvectl/commands/migrate_command.rb', line 50 def initialize(args, , ) @resource_ids = Array(args).compact = = end |
#load_config ⇒ void
This method returns an undefined value.
Loads configuration from file or environment.
209 210 211 212 213 |
# File 'lib/pvectl/commands/migrate_command.rb', line 209 def load_config service = Pvectl::Config::Service.new service.load(config: [:config]) @config = service.current_config end |
#no_resources_found ⇒ Integer
Outputs no resources found error and returns exit code.
270 271 272 273 274 275 276 277 278 279 |
# File 'lib/pvectl/commands/migrate_command.rb', line 270 def no_resources_found type_plural = resource_type_symbol == :vm ? "VMs" : "containers" msg = if [:all] || selector_strings.any? [:node] ? "No #{type_plural} found on node #{@options[:node]}" : "No #{type_plural} found matching criteria" else "No #{type_plural} found for given IDs" end $stderr.puts "Error: #{msg}" ExitCodes::NOT_FOUND end |
#output_results(results) ⇒ void
This method returns an undefined value.
Outputs operation results using the configured formatter.
233 234 235 236 237 238 239 240 241 242 243 244 245 |
# File 'lib/pvectl/commands/migrate_command.rb', line 233 def output_results(results) presenter = if resource_type_symbol == :vm Pvectl::Presenters::VmOperationResult.new else Pvectl::Presenters::ContainerOperationResult.new end format = [:output] || "table" color_flag = [:color] formatter = Pvectl::Formatters::Registry.for(format) output = formatter.format(results, presenter, color: color_flag) puts output end |
#perform_operation ⇒ Integer
Performs the migrate operation.
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/pvectl/commands/migrate_command.rb', line 95 def perform_operation load_config connection = Pvectl::Connection.new(@config) resources = resolve_resources(connection) return no_resources_found if resources.empty? target = [:target] return ExitCodes::SUCCESS unless confirm_operation(resources, target) vm_repo = Pvectl::Repositories::Vm.new(connection) container_repo = Pvectl::Repositories::Container.new(connection) task_repo = Pvectl::Repositories::Task.new(connection) service = Pvectl::Services::ResourceMigration.new( vm_repository: vm_repo, container_repository: container_repo, task_repository: task_repo, options: ) results = service.execute(resource_type_symbol, resources, target: target) return ExitCodes::SUCCESS if results.empty? 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 |
#resolve_resources(connection) ⇒ Array<Models::Vm, Models::Container>
Resolves resources based on IDs, --all flag, or selectors.
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'lib/pvectl/commands/migrate_command.rb', line 136 def resolve_resources(connection) repo = resource_type_symbol == :vm ? Pvectl::Repositories::Vm.new(connection) : Pvectl::Repositories::Container.new(connection) resources = if [:all] repo.list(node: [:node]) elsif @resource_ids.any? @resource_ids.each do |id| unless id.match?(/\A\d+\z/) raise ArgumentError, "Invalid VMID/CTID: #{id}" end end resolved = @resource_ids.map { |id| repo.get(id.to_i) }.compact resolved = resolved.select { |r| r.node == [:node] } if [:node] resolved else return [] if selector_strings.empty? repo.list(node: [:node]) end apply_selectors(resources) end |
#resource_type_symbol ⇒ Symbol
Returns the resource type symbol (:vm or :container).
81 82 83 |
# File 'lib/pvectl/commands/migrate_command.rb', line 81 def resource_type_symbol self.class::RESOURCE_TYPE end |
#restart_not_allowed? ⇒ Boolean
Checks if --restart is used for a non-container resource.
88 89 90 |
# File 'lib/pvectl/commands/migrate_command.rb', line 88 def restart_not_allowed? [:restart] && resource_type_symbol == :vm end |
#selector_strings ⇒ Array<String>
Returns selector strings from options.
163 164 165 |
# File 'lib/pvectl/commands/migrate_command.rb', line 163 def selector_strings Array([:selector] || [:l]) end |
#service_options ⇒ Hash
Builds service options from command options.
218 219 220 221 222 223 224 225 226 227 |
# File 'lib/pvectl/commands/migrate_command.rb', line 218 def opts = {} opts[:timeout] = [:timeout] if [:timeout] opts[:wait] = true if [:wait] opts[:online] = true if [:online] opts[:restart] = true if [:restart] opts[:target_storage] = [:"target-storage"] if [:"target-storage"] opts[:fail_fast] = true if [:"fail-fast"] opts end |
#usage_error(message) ⇒ Integer
Outputs usage error and returns exit code.
262 263 264 265 |
# File 'lib/pvectl/commands/migrate_command.rb', line 262 def usage_error() $stderr.puts "Error: #{message}" ExitCodes::USAGE_ERROR end |