forked from ManageIQ/manageiq-providers-proxmox
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsnapshot.rb
More file actions
126 lines (109 loc) · 4.12 KB
/
snapshot.rb
File metadata and controls
126 lines (109 loc) · 4.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
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
module ManageIQ::Providers::Proxmox::InfraManager::Vm::Operations::Snapshot
extend ActiveSupport::Concern
included do
supports :snapshots
end
def params_for_create_snapshot
{
:fields => [
{
:component => 'text-field',
:name => 'name',
:id => 'name',
:label => _('Name'),
:isRequired => true,
:helperText => _('Must start with a letter and contain only letters, numbers, and underscores'),
:validate => [
{:type => 'required'},
{:type => 'pattern', :pattern => '^[a-zA-Z][a-zA-Z0-9_]*$', :message => _('Must start with a letter and contain only letters, numbers, and underscores')}
]
},
{
:component => 'textarea',
:name => 'description',
:id => 'description',
:label => _('Description')
},
{
:component => 'switch',
:name => 'memory',
:id => 'memory',
:label => _('Snapshot VM memory'),
:onText => _('Yes'),
:offText => _('No'),
:isDisabled => current_state != 'on',
:helperText => _('Snapshotting the memory is only available if the VM is powered on')
}
]
}
end
def raw_create_snapshot(name, desc = nil, memory = false) # rubocop:disable Style/OptionalBooleanParameter
raise MiqException::MiqVmSnapshotError, "Snapshot name is required" if name.blank?
$proxmox_log.info("Creating snapshot for VM #{self.name} with name=#{name}, desc=#{desc}, memory=#{memory}")
with_snapshot_error_handling("create") do
params = {:snapname => name}
params[:description] = desc if desc.present?
params[:vmstate] = 1 if memory && current_state == 'on'
run_task(:post, "snapshot?#{URI.encode_www_form(params)}")
end
end
def raw_remove_snapshot(snapshot_id)
snapshot = snapshots.find(snapshot_id)
$proxmox_log.info("Removing snapshot #{snapshot.name} from VM #{name}")
with_snapshot_error_handling("remove") { run_task(:delete, "snapshot/#{snapshot.name}") }
end
def raw_revert_to_snapshot(snapshot_id)
snapshot = snapshots.find(snapshot_id)
$proxmox_log.info("Reverting VM #{name} to snapshot #{snapshot.name}")
with_snapshot_error_handling("revert") { run_task(:post, "snapshot/#{snapshot.name}/rollback") }
end
def raw_remove_all_snapshots
$proxmox_log.info("Removing all snapshots from VM #{name}")
with_snapshot_error_handling("remove_all") do
snapshots.each { |s| run_task(:delete, "snapshot/#{s.name}") }
end
end
private
def run_task(method, path)
with_provider_connection do |connection|
upid = connection.request(method, "#{vm_path}/#{path}")
wait_for_task!(connection, upid)
end
end
def with_snapshot_error_handling(operation)
yield
rescue => err
error_message = parse_api_error(err)
create_notification(:vm_snapshot_failure, :error => error_message, :snapshot_op => operation)
raise MiqException::MiqVmSnapshotError, error_message
end
def wait_for_task!(connection, upid, timeout: 300, interval: 2)
return unless upid.kind_of?(String) && upid.start_with?("UPID:")
node = upid.split(":")[1]
deadline = Time.now.utc + timeout
loop do
status = connection.request(:get, "/nodes/#{node}/tasks/#{upid}/status")
case status["status"]
when "stopped"
return if status["exitstatus"] == "OK"
raise "Task failed: #{status["exitstatus"]}"
when "running"
raise "Task #{upid} timed out after #{timeout}s" if Time.now.utc > deadline
sleep(interval)
else
raise "Unknown task status: #{status["status"]}"
end
end
end
def parse_api_error(err)
msg = err.to_s
return msg unless msg.start_with?("ApiError:")
data = JSON.parse(msg.sub(/^ApiError:\s*/, ""))
parts = []
parts << data["message"].strip if data["message"].present?
data["errors"]&.each { |field, error| parts << "#{field}: #{error.strip}" }
parts.any? ? parts.join(" ") : msg
rescue JSON::ParserError
msg
end
end