foreman_hyperv 0.0.4 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE.txt +1451 -12
- data/README.md +2 -0
- data/app/assets/javascripts/foreman_hyperv/compute_resource_base.js +78 -0
- data/app/helpers/foreman_hyperv/compute_resources_vms_helper.rb +23 -0
- data/app/models/concerns/fog_extensions/hyperv/compute.rb +8 -0
- data/app/models/concerns/fog_extensions/hyperv/hard_drive.rb +39 -0
- data/app/models/concerns/fog_extensions/hyperv/network_adapter.rb +115 -16
- data/app/models/concerns/fog_extensions/hyperv/server.rb +95 -39
- data/app/models/concerns/foreman_hyperv/host_managed_extensions.rb +65 -0
- data/app/models/foreman_hyperv/hyperv.rb +374 -143
- data/app/views/compute_resources/form/_hyperv.html.erb +6 -2
- data/app/views/compute_resources_vms/form/hyperv/_base.html.erb +36 -25
- data/app/views/compute_resources_vms/form/hyperv/_network.html.erb +34 -11
- data/app/views/compute_resources_vms/form/hyperv/_volume.html.erb +5 -2
- data/app/views/compute_resources_vms/index/_hyperv.html.erb +2 -2
- data/app/views/compute_resources_vms/show/_hyperv.html.erb +27 -9
- data/lib/foreman_hyperv/engine.rb +38 -17
- data/lib/foreman_hyperv/version.rb +3 -1
- data/lib/foreman_hyperv.rb +2 -0
- data/lib/tasks/foreman_hyperv_tasks.rake +33 -0
- metadata +54 -38
- data/.gitignore +0 -9
- data/.rubocop.yml +0 -22
- data/.rubocop_todo.yml +0 -20
- data/.travis.yml +0 -5
- data/CHANGELOG.md +0 -25
- data/Gemfile +0 -6
- data/app/assets/javascripts/compute_resources/hyperv/base.js +0 -32
- data/app/models/concerns/fog_extensions/hyperv/vhd.rb +0 -12
- data/foreman_hyperv.gemspec +0 -27
- data/test/foreman_hyperv_test.rb +0 -11
- data/test/test_helper.rb +0 -4
|
@@ -1,24 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ForemanHyperv
|
|
2
4
|
class Hyperv < ::ComputeResource
|
|
3
|
-
|
|
5
|
+
include ComputeResourceCaching
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
end
|
|
7
|
+
validates :url, :user, :password, presence: true
|
|
8
|
+
after_validation :validate_connectivity unless Rails.env.test?
|
|
8
9
|
|
|
9
10
|
def self.provider_friendly_name
|
|
10
11
|
'Hyper-V'
|
|
11
12
|
end
|
|
12
13
|
|
|
14
|
+
def to_label
|
|
15
|
+
"#{name} (#{provider_friendly_name})"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.available?
|
|
19
|
+
Fog::Compute.providers.include?(:hyperv)
|
|
20
|
+
end
|
|
21
|
+
|
|
13
22
|
def self.model_name
|
|
14
23
|
ComputeResource.model_name
|
|
15
24
|
end
|
|
16
25
|
|
|
26
|
+
def supports_update?
|
|
27
|
+
true
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def editable_network_interfaces?
|
|
31
|
+
true
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def capabilities
|
|
35
|
+
[:build]
|
|
36
|
+
end
|
|
37
|
+
|
|
17
38
|
def test_connection(options = {})
|
|
18
|
-
|
|
39
|
+
validate_connectivity(options)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def validate_connectivity(_options = {})
|
|
43
|
+
return unless connection_properties_valid?
|
|
44
|
+
return false if errors.any?
|
|
45
|
+
|
|
19
46
|
client.valid?
|
|
20
47
|
rescue Fog::Hyperv::Errors::ServiceError, ArgumentError, WinRM::WinRMAuthorizationError => e
|
|
21
|
-
errors
|
|
48
|
+
errors.add(:base, e.message)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def connection_valid?
|
|
52
|
+
return false if url.blank? || user.blank? || password.blank?
|
|
53
|
+
|
|
54
|
+
client&.valid?
|
|
55
|
+
rescue StandardError
|
|
56
|
+
false
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def connection_properties_valid?
|
|
60
|
+
errors[:url].empty? && errors[:user].empty? && errors[:password].empty?
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def caching_enabled
|
|
64
|
+
true
|
|
22
65
|
end
|
|
23
66
|
|
|
24
67
|
def provided_attributes
|
|
@@ -26,94 +69,152 @@ module ForemanHyperv
|
|
|
26
69
|
end
|
|
27
70
|
|
|
28
71
|
# TODO
|
|
29
|
-
def max_cpu_count
|
|
30
|
-
hypervisor.logical_processor_count
|
|
72
|
+
def max_cpu_count(host = nil)
|
|
73
|
+
(host || hypervisor).logical_processor_count
|
|
31
74
|
end
|
|
32
75
|
|
|
33
|
-
def max_memory
|
|
34
|
-
hypervisor.memory_capacity
|
|
76
|
+
def max_memory(host = nil)
|
|
77
|
+
(host || hypervisor).memory_capacity
|
|
35
78
|
end
|
|
36
79
|
|
|
37
|
-
def
|
|
38
|
-
|
|
80
|
+
def available_hypervisors
|
|
81
|
+
client.hosts.load(
|
|
82
|
+
cache.cache(:available_hypervisor) do
|
|
83
|
+
client.hosts.all.map(&:attributes)
|
|
84
|
+
end
|
|
85
|
+
)
|
|
39
86
|
end
|
|
87
|
+
alias hosts available_hypervisors
|
|
40
88
|
|
|
41
|
-
def
|
|
42
|
-
|
|
43
|
-
interfaces = nested_attributes_for :interfaces, attr[:interfaces_attributes]
|
|
44
|
-
interfaces.map { |i| vm.interfaces << new_interface(i) }
|
|
45
|
-
volumes = nested_attributes_for :volumes, attr[:volumes_attributes]
|
|
46
|
-
volumes.map { |v| vm.volumes << new_volume(v) }
|
|
47
|
-
vm
|
|
48
|
-
end
|
|
89
|
+
def cluster(name)
|
|
90
|
+
return nil unless name
|
|
49
91
|
|
|
50
|
-
|
|
51
|
-
find_vm_by_uuid(uuid).stop force: true
|
|
92
|
+
client.clusters.get name
|
|
52
93
|
end
|
|
53
94
|
|
|
54
|
-
def
|
|
55
|
-
|
|
56
|
-
|
|
95
|
+
def clusters
|
|
96
|
+
client.clusters.load(
|
|
97
|
+
cache.cache(:clusters) do
|
|
98
|
+
_clusters.map(&:attributes)
|
|
99
|
+
end
|
|
100
|
+
)
|
|
101
|
+
end
|
|
57
102
|
|
|
58
|
-
|
|
103
|
+
def hypervisor
|
|
104
|
+
client.hosts.new(
|
|
105
|
+
cache.cache(:hypervisor) do
|
|
106
|
+
client.hosts.first.attributes
|
|
107
|
+
end
|
|
108
|
+
)
|
|
109
|
+
end
|
|
59
110
|
|
|
60
|
-
|
|
61
|
-
boot_device: 'NetworkAdapter',
|
|
62
|
-
computer_name: args[:computer_name],
|
|
63
|
-
generation: args[:generation].to_i,
|
|
64
|
-
memory_startup: args[:memory_startup].presence.to_i,
|
|
65
|
-
name: args[:name],
|
|
66
|
-
no_vhd: true
|
|
67
|
-
}
|
|
111
|
+
delegate :servers, to: :client
|
|
68
112
|
|
|
69
|
-
|
|
113
|
+
def switches(host)
|
|
114
|
+
host ||= hosts.first
|
|
115
|
+
client.switches.load(
|
|
116
|
+
cache.cache(:"#{host.name}-switches") do
|
|
117
|
+
host.switches.all.map(&:attributes)
|
|
118
|
+
end
|
|
119
|
+
)
|
|
120
|
+
end
|
|
70
121
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
122
|
+
def new_vm(attr = {})
|
|
123
|
+
attr.delete :id
|
|
124
|
+
vm = super
|
|
125
|
+
iface_nested_attrs = nested_attributes_for :interfaces, attr[:interfaces_attributes]
|
|
126
|
+
vm.network_adapters = iface_nested_attrs.map do |attr|
|
|
127
|
+
attr.delete :id
|
|
128
|
+
Fog::Hyperv::Compute::NetworkAdapter.new(service: vm.service, vm: vm).tap do |nic|
|
|
129
|
+
attr.select { |_, v| v.present? }.each do |k, v|
|
|
130
|
+
nic.send(:"#{k}=", v)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
80
133
|
end
|
|
134
|
+
volume_nested_attrs = nested_attributes_for :volumes, attr[:volumes_attributes]
|
|
135
|
+
vm.hard_drives = volume_nested_attrs.map do |attr|
|
|
136
|
+
attr.delete :id
|
|
137
|
+
Fog::Hyperv::Compute::HardDrive.new(service: vm.service, vm: vm).tap do |hdd|
|
|
138
|
+
attr.select { |_, v| v.present? }.each do |k, v|
|
|
139
|
+
hdd.send(:"#{k}=", v)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
vm.id = nil
|
|
144
|
+
vm
|
|
145
|
+
end
|
|
81
146
|
|
|
82
|
-
|
|
147
|
+
def create_vm(attr = {})
|
|
148
|
+
attr = vm_instance_defaults.merge(attr.to_hash.deep_symbolize_keys)
|
|
149
|
+
attr.delete :computer_name if attr[:computer_name].blank?
|
|
150
|
+
attr.delete :start
|
|
151
|
+
|
|
152
|
+
validate_vm(attr, new: true)
|
|
153
|
+
validate_interfaces(attr)
|
|
154
|
+
validate_volumes(attr)
|
|
155
|
+
|
|
156
|
+
vm = client.servers.new(
|
|
157
|
+
name: attr[:name],
|
|
158
|
+
computer_name: attr[:computer_name],
|
|
159
|
+
generation: attr[:generation],
|
|
160
|
+
dynamic_memory_enabled: Foreman::Cast.to_bool(attr[:dynamic_memory_enabled]),
|
|
161
|
+
memory_startup: attr[:memory_startup].to_i,
|
|
162
|
+
memory_minimum: attr[:memory_minimum].to_i,
|
|
163
|
+
memory_maximum: attr[:memory_maximum].to_i,
|
|
164
|
+
processor_count: attr[:processor_count].to_i,
|
|
165
|
+
notes: attr[:notes]
|
|
166
|
+
)
|
|
167
|
+
# TODO: Allow configuring boot device?
|
|
168
|
+
vm.create boot_device: :NetworkAdapter
|
|
83
169
|
|
|
84
|
-
if vm.generation ==
|
|
170
|
+
if vm.generation == :UEFI && attr[:secure_boot_enabled].present?
|
|
85
171
|
f = vm.firmware
|
|
86
|
-
f.secure_boot = Foreman::Cast.to_bool(
|
|
172
|
+
f.secure_boot = Foreman::Cast.to_bool(attr[:secure_boot_enabled]) ? :On : :Off
|
|
87
173
|
f.save if f.dirty?
|
|
88
174
|
end
|
|
89
175
|
|
|
90
|
-
create_interfaces(vm,
|
|
91
|
-
create_volumes(vm,
|
|
92
|
-
|
|
93
|
-
vm.set_vlan(args[:vlan].to_i) if args[:vlan].presence && vm.respond_to?(:set_vlan)
|
|
176
|
+
create_interfaces(vm, attr)
|
|
177
|
+
create_volumes(vm, attr)
|
|
94
178
|
|
|
179
|
+
vm.start if attr[:start] == '1'
|
|
95
180
|
vm
|
|
96
181
|
rescue StandardError => e
|
|
97
|
-
vm
|
|
182
|
+
if vm
|
|
183
|
+
vm.stop
|
|
184
|
+
vm.hard_drives.each { |hdd| hdd.destroy(underlying: true) }
|
|
185
|
+
vm.destroy
|
|
186
|
+
end
|
|
98
187
|
|
|
99
188
|
raise e
|
|
100
189
|
end
|
|
101
190
|
|
|
102
|
-
def save_vm(uuid,
|
|
191
|
+
def save_vm(uuid, web_attr)
|
|
192
|
+
attr = web_attr.deep_symbolize_keys
|
|
193
|
+
|
|
194
|
+
validate_vm(attr)
|
|
195
|
+
validate_interfaces(attr)
|
|
196
|
+
validate_volumes(attr)
|
|
197
|
+
|
|
198
|
+
attr.delete :start
|
|
199
|
+
|
|
103
200
|
vm = find_vm_by_uuid(uuid)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
201
|
+
logger.debug "Updating VM #{vm} with arguments; #{attr}"
|
|
202
|
+
|
|
203
|
+
vm.processor_count = attr[:processor_count].to_i
|
|
204
|
+
vm.notes = attr[:notes].presence
|
|
205
|
+
vm.dynamic_memory_enabled = Foreman::Cast.to_bool(attr[:dynamic_memory_enabled])
|
|
206
|
+
if vm.dynamic_memory_enabled
|
|
207
|
+
vm.memory_minimum = attr[:memory_minimum].to_i
|
|
208
|
+
vm.memory_maximum = attr[:memory_maximum].to_i
|
|
107
209
|
end
|
|
108
|
-
|
|
109
|
-
if vm.generation == 2 && attr[:secure_boot_enabled].present?
|
|
210
|
+
if vm.generation == :UEFI && attr[:secure_boot_enabled].present?
|
|
110
211
|
f = vm.firmware
|
|
111
212
|
f.secure_boot = Foreman::Cast.to_bool(attr[:secure_boot_enabled]) ? :On : :Off
|
|
112
213
|
f.save if f.dirty?
|
|
113
214
|
end
|
|
114
215
|
|
|
115
|
-
update_interfaces(vm, attr
|
|
116
|
-
update_volumes(vm, attr
|
|
216
|
+
update_interfaces(vm, attr)
|
|
217
|
+
update_volumes(vm, attr)
|
|
117
218
|
|
|
118
219
|
vm.save if vm.dirty?
|
|
119
220
|
vm
|
|
@@ -121,10 +222,8 @@ module ForemanHyperv
|
|
|
121
222
|
|
|
122
223
|
def destroy_vm(uuid)
|
|
123
224
|
vm = find_vm_by_uuid(uuid)
|
|
124
|
-
vm.stop
|
|
125
|
-
vm.hard_drives.each
|
|
126
|
-
hd.vhd.destroy if hd.path
|
|
127
|
-
end
|
|
225
|
+
vm.stop turn_off: true
|
|
226
|
+
vm.hard_drives.each { |hdd| hdd.destroy(underlying: true) }
|
|
128
227
|
# TODO: Remove the empty VM folder
|
|
129
228
|
vm.destroy
|
|
130
229
|
rescue ActiveRecord::RecordNotFound, Fog::Errors::NotFound
|
|
@@ -132,51 +231,105 @@ module ForemanHyperv
|
|
|
132
231
|
true
|
|
133
232
|
end
|
|
134
233
|
|
|
135
|
-
def
|
|
136
|
-
|
|
137
|
-
|
|
234
|
+
def update_required?(old_attrs, new_attrs)
|
|
235
|
+
new_attrs.deep_symbolize_keys[:volumes_attributes]&.each_value do |hdd|
|
|
236
|
+
if hdd[:id].present? && hdd[:_delete] == '1'
|
|
237
|
+
Rails.logger.debug 'Scheduling compute instance update because a volume was removed'
|
|
238
|
+
return true
|
|
239
|
+
elsif hdd[:id].blank? && hdd[:_delete] != '1'
|
|
240
|
+
Rails.logger.debug 'Scheduling compute instance update because a new volume was added'
|
|
241
|
+
return true
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
new_attrs.deep_symbolize_keys[:interfaces_attributes]&.each_value do |iface|
|
|
245
|
+
if iface[:id].present? && iface[:_destroy] == '1'
|
|
246
|
+
Rails.logger.debug 'Scheduling compute instance update because an interface was removed'
|
|
247
|
+
return true
|
|
248
|
+
elsif iface[:id].blank? && iface[:_destroy] != '1'
|
|
249
|
+
Rails.logger.debug 'Scheduling compute instance update because a new interface was added'
|
|
250
|
+
return true
|
|
251
|
+
end
|
|
252
|
+
end
|
|
138
253
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
254
|
+
deep_update_required = proc do |old, new|
|
|
255
|
+
old.merge(new) do |k, old_v, new_v|
|
|
256
|
+
if %i[allowed_vlan_ids secondary_vlan_ids].include?(k)
|
|
257
|
+
tmp = Fog::Hyperv::Compute::NetworkAdapter.new
|
|
142
258
|
|
|
143
|
-
|
|
144
|
-
|
|
259
|
+
old_v = Fog::Hyperv::Compute::NetworkAdapterVlan.render_vlan_list(tmp.send(:parse_vlan_list, old_v.to_s))
|
|
260
|
+
new_v = Fog::Hyperv::Compute::NetworkAdapterVlan.render_vlan_list(tmp.send(:parse_vlan_list, new_v.to_s))
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
if old_v.is_a?(Hash) && new_v.is_a?(Hash)
|
|
264
|
+
deep_update_required.call(old_v, new_v)
|
|
265
|
+
elsif old_v.to_s != new_v.to_s
|
|
266
|
+
Rails.logger.debug do
|
|
267
|
+
"Scheduling compute instance update because #{k} changed it's value from '#{old_v}' (#{old_v.class}) to '#{new_v}' (#{new_v.class})"
|
|
268
|
+
end
|
|
269
|
+
return true
|
|
270
|
+
end
|
|
271
|
+
new_v
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
deep_update_required.call(old_attrs.deep_symbolize_keys, new_attrs.deep_symbolize_keys)
|
|
275
|
+
|
|
276
|
+
false
|
|
145
277
|
end
|
|
146
278
|
|
|
147
|
-
def
|
|
148
|
-
|
|
279
|
+
# def console(uuid)
|
|
280
|
+
# vm = find_vm_by_uuid(uuid)
|
|
281
|
+
#
|
|
282
|
+
# {
|
|
283
|
+
# type: 'rdp',
|
|
284
|
+
# host: vm.computer.fully_qualified_domain_name,
|
|
285
|
+
# }
|
|
286
|
+
# end
|
|
287
|
+
|
|
288
|
+
def new_interface(attr = {})
|
|
289
|
+
Fog::Hyperv::Compute::NetworkAdapter.new attr
|
|
149
290
|
end
|
|
150
291
|
|
|
151
|
-
def
|
|
152
|
-
|
|
292
|
+
def new_volume(attr = {})
|
|
293
|
+
basename = attr.delete(:basename) { 'Disk' }
|
|
294
|
+
size = attr.delete(:size)
|
|
295
|
+
|
|
296
|
+
vhd = client.vhds.new({ basename: basename, size: size }.compact)
|
|
297
|
+
Fog::Hyperv::Compute::HardDrive.new vhd: vhd, **attr
|
|
153
298
|
end
|
|
154
299
|
|
|
155
|
-
def
|
|
156
|
-
|
|
300
|
+
def new_cdrom(attr = {})
|
|
301
|
+
Fog::Hyperv::Compute::DvdDrive.new attr
|
|
157
302
|
end
|
|
158
303
|
|
|
159
|
-
def
|
|
160
|
-
|
|
304
|
+
def vm_instance_defaults
|
|
305
|
+
super.merge(
|
|
306
|
+
generation: 2,
|
|
307
|
+
memory_startup: 1024.megabytes,
|
|
308
|
+
processor_count: 1,
|
|
309
|
+
interfaces: [new_interface]
|
|
310
|
+
)
|
|
161
311
|
end
|
|
162
|
-
alias hosts available_hypervisors
|
|
163
312
|
|
|
164
|
-
def
|
|
165
|
-
|
|
166
|
-
|
|
313
|
+
def set_vm_volumes_attributes(vm, vm_attrs)
|
|
314
|
+
volumes = vm.hard_drives || []
|
|
315
|
+
vm_attrs[:volumes_attributes] = volumes.each_with_index.to_h do |volume, index|
|
|
316
|
+
[index.to_s, volume.compute_attributes]
|
|
167
317
|
end
|
|
168
|
-
|
|
169
|
-
client.clusters
|
|
170
|
-
rescue
|
|
171
|
-
[]
|
|
318
|
+
vm_attrs
|
|
172
319
|
end
|
|
173
320
|
|
|
174
|
-
def
|
|
175
|
-
|
|
321
|
+
def set_vm_interfaces_attributes(vm, vm_attrs)
|
|
322
|
+
interfaces = vm.interfaces || []
|
|
323
|
+
vm_attrs[:interfaces_attributes] = interfaces.each_with_index.to_h do |interface, index|
|
|
324
|
+
interface_attrs = {
|
|
325
|
+
mac: interface.mac,
|
|
326
|
+
compute_attributes: interface.compute_attributes
|
|
327
|
+
}
|
|
328
|
+
[index.to_s, interface_attrs]
|
|
329
|
+
end
|
|
330
|
+
vm_attrs
|
|
176
331
|
end
|
|
177
332
|
|
|
178
|
-
delegate :servers, to: :client
|
|
179
|
-
|
|
180
333
|
protected
|
|
181
334
|
|
|
182
335
|
def client
|
|
@@ -188,85 +341,163 @@ module ForemanHyperv
|
|
|
188
341
|
)
|
|
189
342
|
end
|
|
190
343
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
344
|
+
private
|
|
345
|
+
|
|
346
|
+
def _clusters
|
|
347
|
+
return [] if client.respond_to?(:supports_clusters?) && !client.supports_clusters?
|
|
348
|
+
|
|
349
|
+
client.clusters.all
|
|
350
|
+
rescue StandardError
|
|
351
|
+
[]
|
|
198
352
|
end
|
|
199
353
|
|
|
200
|
-
def
|
|
201
|
-
|
|
354
|
+
def validate_vm(attr, new: false)
|
|
355
|
+
# logger.debug "Validate VM #{attr.inspect}"
|
|
356
|
+
raise Foreman::Exception, 'VM lacks generation' if new && attr[:generation].blank?
|
|
357
|
+
raise Foreman::Exception, 'VM lacks memory' unless attr[:memory_startup].to_i.positive?
|
|
358
|
+
raise Foreman::Exception, 'VM lacks CPUs' unless attr[:processor_count].to_i.positive?
|
|
202
359
|
|
|
203
|
-
|
|
204
|
-
|
|
360
|
+
return unless Foreman::Cast.to_bool(attr[:dynamic_memory_enabled])
|
|
361
|
+
raise Foreman::Exception, 'VM lacks memory minimum' unless attr[:memory_minimum].to_i.positive?
|
|
362
|
+
raise Foreman::Exception, 'VM lacks memory maximum' unless attr[:memory_maximum].to_i.positive?
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
def validate_interfaces(attr)
|
|
366
|
+
interfaces = nested_attributes_for :interfaces, attr[:interfaces_attributes]
|
|
367
|
+
interfaces.reject! { |iface| iface[:_destroy] == '1' }
|
|
368
|
+
# logger.debug "Validate NIC #{interfaces.inspect}"
|
|
205
369
|
interfaces.each do |iface|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
370
|
+
compute = iface[:compute_attributes] || iface
|
|
371
|
+
case compute[:vlan_operation_mode].to_s
|
|
372
|
+
when 'Untagged'
|
|
373
|
+
# No VLAN settings to verify
|
|
374
|
+
when 'Access'
|
|
375
|
+
raise Foreman::Exception, 'Interface is missing access VLAN' unless compute[:access_vlan_id].to_i.positive?
|
|
376
|
+
when 'Trunk'
|
|
377
|
+
raise Foreman::Exception, 'Interface is missing native VLAN' unless compute[:native_vlan_id].to_i.positive?
|
|
378
|
+
raise Foreman::Exception, 'Interface is missing allowed VLANs' if compute[:allowed_vlan_ids].blank?
|
|
379
|
+
when 'Private'
|
|
380
|
+
raise Foreman::Exception, 'Interface is missing primary VLAN' unless compute[:primary_vlan_id].to_i.positive?
|
|
381
|
+
|
|
382
|
+
case compute[:vlan_private_mode].to_s
|
|
383
|
+
when 'Promiscuous'
|
|
384
|
+
if compute[:secondary_vlan_ids].blank?
|
|
385
|
+
raise Foreman::Exception,
|
|
386
|
+
'Interface is missing secondary VLANs'
|
|
387
|
+
end
|
|
388
|
+
else
|
|
389
|
+
unless compute[:secondary_vlan_id].to_i.positive?
|
|
390
|
+
raise Foreman::Exception,
|
|
391
|
+
'Interface is missing secondary VLAN'
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
else
|
|
395
|
+
raise Foreman::Exception, 'Interface has unknown VLAN mode'
|
|
210
396
|
end
|
|
211
|
-
|
|
212
|
-
|
|
397
|
+
end
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
def create_interfaces(vm, attr)
|
|
401
|
+
interfaces = nested_attributes_for :interfaces, attr[:interfaces_attributes]
|
|
402
|
+
logger.debug "Creating interfaces with: #{interfaces}"
|
|
403
|
+
|
|
404
|
+
first_provisioned = false
|
|
405
|
+
interfaces.each do |iface|
|
|
406
|
+
# The VM is pre-created with one NIC regardless of given creation options, so configure that one first
|
|
407
|
+
nic = vm.network_adapters.first unless first_provisioned
|
|
408
|
+
first_provisioned = true
|
|
409
|
+
|
|
410
|
+
nic ||= vm.network_adapters.new
|
|
411
|
+
iface.except(:identity, :ip, :ip6).each do |k, v|
|
|
412
|
+
nic.send(:"#{k}=", v.presence)
|
|
213
413
|
end
|
|
414
|
+
# nic.is_legacy = Foreman::Cast.to_bool(iface[:is_legacy]) if vm.generation == :BIOS && iface[:is_legacy].present?
|
|
415
|
+
# logger.debug "Creating interface with #{iface.inspect} - #{nic.inspect}\n#{nic.vlan_setting.inspect}"
|
|
214
416
|
nic.save
|
|
215
417
|
end
|
|
216
418
|
|
|
217
|
-
|
|
419
|
+
unless vm.network_adapters.all(_return_fields: %i[dynamic_mac_address_enabled]).any?(&:dynamic_mac_address_enabled)
|
|
420
|
+
return
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
# Populate all non-populated MAC addresses
|
|
218
424
|
vm.start
|
|
219
425
|
vm.stop turn_off: true
|
|
220
426
|
|
|
221
|
-
vm.network_adapters.reload
|
|
222
|
-
vm.network_adapters.each do |nic|
|
|
427
|
+
vm.network_adapters.reload.each do |nic|
|
|
223
428
|
nic.dynamic_mac_address_enabled = false
|
|
224
429
|
nic.save if nic.dirty?
|
|
225
430
|
end
|
|
226
431
|
end
|
|
227
432
|
|
|
228
|
-
def
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
volumes.each do |vol|
|
|
232
|
-
vhd = vm.vhds.create path: vm.folder_name + '\\' + vol[:path], size: vol[:size]
|
|
233
|
-
vm.hard_drives.create path: vhd.path
|
|
234
|
-
end
|
|
235
|
-
vm.hard_drives.reload
|
|
236
|
-
end
|
|
433
|
+
def update_interfaces(vm, attr)
|
|
434
|
+
interfaces = nested_attributes_for :interfaces, attr[:interfaces_attributes]
|
|
435
|
+
logger.debug "Updating interfaces with: #{interfaces}"
|
|
237
436
|
|
|
238
|
-
def update_interfaces(vm, attrs)
|
|
239
|
-
interfaces = nested_attributes_for :interfaces, attrs
|
|
240
|
-
client.logger.debug "Updating interfaces with: #{interfaces}"
|
|
241
437
|
interfaces.each do |interface|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
nic
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
nic = vm.network_adapters.find { |n| n.id == interface[:id] }
|
|
248
|
-
if interface[:_delete] == '1'
|
|
249
|
-
nic.delete
|
|
438
|
+
compute = interface[:compute_attributes]
|
|
439
|
+
if compute[:identity].present?
|
|
440
|
+
nic = vm.network_adapters.get compute[:identity]
|
|
441
|
+
if interface[:_destroy] == '1'
|
|
442
|
+
nic.destroy
|
|
250
443
|
else
|
|
251
|
-
|
|
252
|
-
nic.send("#{k}="
|
|
444
|
+
compute.each do |k, v|
|
|
445
|
+
nic.send(:"#{k}=", v.presence)
|
|
253
446
|
end
|
|
254
|
-
nic.
|
|
447
|
+
nic.mac ||= interface[:mac].presence
|
|
448
|
+
nic.save
|
|
255
449
|
end
|
|
450
|
+
elsif interface[:_destroy] != '1'
|
|
451
|
+
nic = vm.network_adapters.new
|
|
452
|
+
compute.each do |k, v|
|
|
453
|
+
nic.send(:"#{k}=", v.presence)
|
|
454
|
+
end
|
|
455
|
+
nic.mac ||= interface[:mac].presence
|
|
456
|
+
nic.save
|
|
256
457
|
end
|
|
257
458
|
end
|
|
258
459
|
end
|
|
259
460
|
|
|
260
|
-
def
|
|
261
|
-
volumes = nested_attributes_for :volumes,
|
|
262
|
-
|
|
461
|
+
def validate_volumes(attr)
|
|
462
|
+
volumes = nested_attributes_for :volumes, attr[:volumes_attributes]
|
|
463
|
+
volumes.reject! { |vol| vol[:_delete] == '1' }
|
|
464
|
+
# logger.debug "Validate Volume #{volumes.inspect}"
|
|
465
|
+
volumes.each do |vol|
|
|
466
|
+
raise Foreman::Exception, 'Volume lacks name' if vol[:basename].blank?
|
|
467
|
+
raise Foreman::Exception, 'Volume name should not include a file extension' if vol[:basename] =~ /\.vhd[sx]?$/
|
|
468
|
+
raise Foreman::Exception, 'Volume lacks size' unless vol[:size_bytes].to_i.positive?
|
|
469
|
+
end
|
|
470
|
+
return unless volumes.group_by { |vol| vol[:basename].downcase }.any? { |_k, v| v.many? }
|
|
471
|
+
|
|
472
|
+
raise Foreman::Exception, 'Volume names need to be unique'
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
def create_volumes(vm, attr)
|
|
476
|
+
volumes = nested_attributes_for :volumes, attr[:volumes_attributes]
|
|
477
|
+
logger.debug "Creating volumes with: #{volumes}"
|
|
478
|
+
volumes.each do |vol|
|
|
479
|
+
vhd = vm.vhds.create basename: vol[:basename], size: vol[:size_bytes].to_i
|
|
480
|
+
vm.hard_drives.create vhd: vhd
|
|
481
|
+
end
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
def update_volumes(vm, attr)
|
|
485
|
+
volumes = nested_attributes_for :volumes, attr[:volumes_attributes]
|
|
486
|
+
logger.debug "Updating volumes with: #{volumes}"
|
|
263
487
|
volumes.each do |volume|
|
|
264
|
-
if volume[:
|
|
265
|
-
hd = vm.hard_drives.
|
|
266
|
-
|
|
267
|
-
|
|
488
|
+
if volume[:id].present?
|
|
489
|
+
hd = vm.hard_drives.get volume[:id]
|
|
490
|
+
if volume[:_delete] == '1'
|
|
491
|
+
hd.destroy(underlying: true)
|
|
492
|
+
else
|
|
493
|
+
vhd = hd.vhd
|
|
494
|
+
vhd.size = volume[:size_bytes].to_i
|
|
495
|
+
vhd.save if vhd.dirty?
|
|
496
|
+
end
|
|
497
|
+
elsif volume[:_delete] != '1'
|
|
498
|
+
vhd = vm.vhds.create basename: volume[:basename], size: volume[:size_bytes].to_i
|
|
499
|
+
vm.hard_drives.create path: vhd.path
|
|
268
500
|
end
|
|
269
|
-
vm.hard_drives.create(path: volume[:path], size: volume[:size]) if volume[:id].blank? && volume[:_delete] != '1'
|
|
270
501
|
end
|
|
271
502
|
end
|
|
272
503
|
end
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
<%= text_f f, :user, label: _('User') %>
|
|
3
3
|
<%= password_f f, :password, label: _('Password') %>
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
<%# Currently not working due to marshaling errors %>
|
|
6
|
+
<%#= checkbox_f f, :caching_enabled, :label => _("Enable Caching"),
|
|
7
|
+
:help_inline => _("Cache slow calls to Hyper-V to speed up page rendering") %>
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
<div class="col-md-offset-2">
|
|
10
|
+
<%= test_connection_button_f(f, f.object.connection_valid?) %>
|
|
11
|
+
</div>
|