Class: Pvectl::Services::ResourceMigration

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

Overview

Orchestrates migration of VMs and containers between cluster nodes.

Single service for both resource types, parameterized by resource_type. Async mode (default): returns UPID immediately, no blocking. Sync mode (--wait): polls Task until completion or timeout.

Examples:

Migrate VMs async (default)

service = ResourceMigration.new(vm_repository: vm_repo, container_repository: ct_repo, task_repository: task_repo)
results = service.execute(:vm, [vm1, vm2], target: "pve2")

Migrate with sync wait

service = ResourceMigration.new(..., options: { wait: true })
results = service.execute(:vm, [vm], target: "pve2")

Constant Summary collapse

DEFAULT_TIMEOUT =
600

Instance Method Summary collapse

Constructor Details

#initialize(vm_repository:, container_repository:, task_repository:, options: {}) ⇒ ResourceMigration

Creates a new ResourceMigration service.



28
29
30
31
32
33
# File 'lib/pvectl/services/resource_migration.rb', line 28

def initialize(vm_repository:, container_repository:, task_repository:, options: {})
  @vm_repository = vm_repository
  @container_repository = container_repository
  @task_repository = task_repository
  @options = options
end

Instance Method Details

#all_on_target_results(target) ⇒ Array

Handles case when all resources are already on target.



86
87
88
89
# File 'lib/pvectl/services/resource_migration.rb', line 86

def all_on_target_results(target)
  $stderr.puts "All resources are already on target node #{target}"
  []
end

#build_migrate_params(target) ⇒ Hash

Builds migration parameters for the API call.



127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/pvectl/services/resource_migration.rb', line 127

def build_migrate_params(target)
  params = { target: target }

  if @options[:online]
    params[:online] = 1
    params[:"with-local-disks"] = 1 if @resource_type == :vm
  end

  params[:restart] = 1 if @options[:restart] && @resource_type == :container
  params[:targetstorage] = @options[:target_storage] if @options[:target_storage]

  params
end

#build_result(resource, **attrs) ⇒ Models::VmOperationResult, Models::ContainerOperationResult

Builds typed OperationResult for the current resource type.



146
147
148
149
150
151
152
# File 'lib/pvectl/services/resource_migration.rb', line 146

def build_result(resource, **attrs)
  if @resource_type == :vm
    Models::VmOperationResult.new(vm: resource, **attrs)
  else
    Models::ContainerOperationResult.new(container: resource, **attrs)
  end
end

#execute(resource_type, resources, target:) ⇒ Array<Models::OperationResult>

Executes migration operation on resources.



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/pvectl/services/resource_migration.rb', line 41

def execute(resource_type, resources, target:)
  @resource_type = resource_type

  migratable, skipped = partition_by_target(resources, target)
  report_skipped(skipped, target)
  return all_on_target_results(target) if migratable.empty?

  results = []
  migratable.each do |resource|
    result = migrate_single(resource, target)
    results << result

    break if @options[:fail_fast] && result.failed?
  end

  results
end

#migrate_single(resource, target) ⇒ Models::OperationResult

Migrates a single resource.



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
# File 'lib/pvectl/services/resource_migration.rb', line 96

def migrate_single(resource, target)
  repo = repository_for(@resource_type)
  params = build_migrate_params(target)
  upid = repo.migrate(resource.vmid, resource.node, params)

  if @options[:wait]
    task = @task_repository.wait(upid, timeout: timeout)
    build_result(resource,
      operation: :migrate,
      task: task,
      success: task.successful?
    )
  else
    build_result(resource,
      operation: :migrate,
      task_upid: upid,
      success: :pending
    )
  end
rescue StandardError => e
  build_result(resource,
    operation: :migrate,
    success: false,
    error: e.message
  )
end

#partition_by_target(resources, target) ⇒ Array<Array, Array>

Partitions resources into migratable and already-on-target groups.



66
67
68
# File 'lib/pvectl/services/resource_migration.rb', line 66

def partition_by_target(resources, target)
  resources.partition { |r| r.node != target }
end

#report_skipped(skipped, target) ⇒ void

This method returns an undefined value.

Reports skipped resources to stderr.



75
76
77
78
79
80
# File 'lib/pvectl/services/resource_migration.rb', line 75

def report_skipped(skipped, target)
  type_name = @resource_type == :vm ? "VM" : "container"
  skipped.each do |r|
    $stderr.puts "Skipping #{type_name} #{r.vmid} (already on #{target})"
  end
end

#repository_for(type) ⇒ Repositories::Vm, Repositories::Container

Returns the appropriate repository for resource type.



158
159
160
# File 'lib/pvectl/services/resource_migration.rb', line 158

def repository_for(type)
  type == :vm ? @vm_repository : @container_repository
end

#timeoutInteger

Returns configured timeout.



165
166
167
# File 'lib/pvectl/services/resource_migration.rb', line 165

def timeout
  @options[:timeout] || DEFAULT_TIMEOUT
end