forked from vagrant-libvirt/vagrant-libvirt
-
Notifications
You must be signed in to change notification settings - Fork 0
/
destroy_domain.rb
185 lines (158 loc) · 7.5 KB
/
destroy_domain.rb
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
127
128
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# frozen_string_literal: true
require 'log4r'
require 'rexml/document'
require 'rexml/xpath'
require 'vagrant-libvirt/util/domain_flags'
module VagrantPlugins
module ProviderLibvirt
module Action
class DestroyDomain
def initialize(app, _env)
@logger = Log4r::Logger.new('vagrant_libvirt::action::destroy_domain')
@app = app
end
def call(env)
# Destroy the server, remove the tracking ID
env[:ui].info(I18n.t('vagrant_libvirt.destroy_domain'))
# Must delete any snapshots before domain can be destroyed
# Fog Libvirt currently doesn't support snapshots. Use
# ruby-libvirt client directly. Note this is racy, see
# http://www.libvirt.org/html/libvirt-libvirt.html#virDomainSnapshotListNames
libvirt_domain = env[:machine].provider.driver.connection.client.lookup_domain_by_uuid(
env[:machine].id
)
begin
libvirt_domain.list_snapshots.each do |name|
@logger.info("Deleting snapshot '#{name}'")
begin
libvirt_domain.lookup_snapshot_by_name(name).delete
rescue => e
raise Errors::SnapshotDeletionError, error_message: e.message
end
end
rescue
# Some drivers (xen) don't support getting list of snapshots,
# not much can be done here about it
@logger.warn("Failed to get list of snapshots")
end
# must remove managed saves
libvirt_domain.managed_save_remove if libvirt_domain.has_managed_save?
domain = env[:machine].provider.driver.connection.servers.get(env[:machine].id.to_s)
undefine_flags = 0
undefine_flags |= ProviderLibvirt::Util::DomainFlags::VIR_DOMAIN_UNDEFINE_KEEP_NVRAM if env[:machine].provider_config.nvram
if env[:machine].provider_config.disks.empty? &&
env[:machine].provider_config.cdroms.empty?
# if using default configuration of disks and cdroms
# cdroms are consider volumes, but cannot be destroyed
destroy_domain(domain, destroy_volumes: true, flags: undefine_flags)
else
domain_xml = libvirt_domain.xml_desc(1)
xml_descr = REXML::Document.new(domain_xml)
disks_xml = REXML::XPath.match(xml_descr, '/domain/devices/disk[@device="disk"]')
have_aliases = !(REXML::XPath.match(disks_xml, './alias[@name="ua-box-volume-0"]').first).nil?
if !have_aliases
env[:ui].warn(I18n.t('vagrant_libvirt.domain_xml.obsolete_method'))
end
destroy_domain(domain, destroy_volumes: false, flags: undefine_flags)
volumes = domain.volumes
# Remove root storage. If no aliases available, perform the removal by name and keep track
# of how many matches there are in the volumes. This will provide a fallback offset to where
# the additional storage devices are.
detected_box_volumes = 0
if have_aliases
REXML::XPath.match(disks_xml, './alias[contains(@name, "ua-box-volume-")]').each do |box_disk|
diskname = box_disk.parent.elements['source'].attributes['file'].rpartition('/').last
detected_box_volumes += 1
destroy_volume(volumes, diskname, env)
end
else
# fallback to try and infer which boxes are box images, as they are listed first
# as soon as there is no match, can exit
disks_xml.each_with_index do |box_disk, idx|
name = libvirt_domain.name + (idx == 0 ? '.img' : "_#{idx}.img")
diskname = box_disk.elements['source'].attributes['file'].rpartition('/').last
break if name != diskname
detected_box_volumes += 1
root_disk = volumes.select do |x|
x.name == name if x
end.first
if root_disk
root_disk.destroy
end
end
end
# work out if there are any custom disks attached that wasn't done by vagrant-libvirt,
# and warn there might be unexpected behaviour
total_disks = disks_xml.length
offset = total_disks - env[:machine].provider_config.disks.length
if offset != detected_box_volumes
env[:ui].warn(I18n.t('vagrant_libvirt.destroy.unexpected_volumes'))
end
if !have_aliases
# if no aliases found, see if it's possible to check the number of box disks
# otherwise the destroy could remove the wrong disk by accident.
if env[:machine].box != nil
box_disks = env[:machine].box.metadata.fetch('disks', [1])
offset = box_disks.length
if offset != detected_box_volumes
env[:ui].warn(I18n.t('vagrant_libvirt.destroy.expected_removal_mismatch'))
end
else
env[:ui].warn(I18n.t('vagrant_libvirt.destroy.box_metadata_unavailable'))
end
# offset only used when no aliases available
offset = detected_box_volumes
end
env[:machine].provider_config.disks.each_with_index.each do |disk, index|
# shared disks remove only manually or ???
next if disk[:allow_existing]
# look for exact match using aliases which will be used
# for subsequent domain creations
if have_aliases
domain_disk = REXML::XPath.match(disks_xml, './alias[@name="ua-disk-volume-' + index.to_s + '"]').first
domain_disk = domain_disk.parent if !domain_disk.nil?
else
# otherwise fallback to find the disk by device if specified by user
# and finally index counting with offset and hope the match is correct
if !disk[:device].nil?
domain_disk = REXML::XPath.match(disks_xml, './target[@dev="' + disk[:device] + '"]').first
domain_disk = domain_disk.parent if !domain_disk.nil?
else
domain_disk = disks_xml[offset + index]
end
end
next if domain_disk.nil?
diskname = domain_disk.elements['source'].attributes['file'].rpartition('/').last
destroy_volume(volumes, diskname, env)
end
end
@app.call(env)
end
protected
def destroy_volume(volumes, diskname, env)
# diskname is unique
libvirt_disk = volumes.select do |x|
x.name == diskname if x
end.first
if libvirt_disk
libvirt_disk.destroy
elsif disk[:path]
poolname = env[:machine].provider_config.storage_pool_name
libvirt_disk = volumes.select do |x|
# FIXME: can remove pool/target.img and pool/123/target.img
x.path =~ /\/#{disk[:path]}$/ && x.pool_name == poolname
end.first
libvirt_disk.destroy if libvirt_disk
end
end
def destroy_domain(domain, destroy_volumes:, flags:)
if domain.method(:destroy).parameters.first.include?(:flags)
domain.destroy(destroy_volumes: destroy_volumes, flags: flags)
else
domain.destroy(destroy_volumes: destroy_volumes)
end
end
end
end
end
end