foreman_hyperv 0.0.4 → 0.1.0
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 +19 -0
- data/app/models/concerns/fog_extensions/hyperv/compute.rb +6 -0
- data/app/models/concerns/fog_extensions/hyperv/hard_drive.rb +37 -0
- data/app/models/concerns/fog_extensions/hyperv/network_adapter.rb +111 -16
- data/app/models/concerns/fog_extensions/hyperv/server.rb +87 -38
- data/app/models/concerns/foreman_hyperv/host_managed_extensions.rb +66 -0
- data/app/models/foreman_hyperv/hyperv.rb +365 -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 +25 -37
- 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,119 +1,222 @@
|
|
|
1
1
|
module ForemanHyperv
|
|
2
2
|
class Hyperv < ::ComputeResource
|
|
3
|
-
|
|
3
|
+
include ComputeResourceCaching
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
end
|
|
5
|
+
validates :url, :user, :password, presence: true
|
|
6
|
+
after_validation :validate_connectivity unless Rails.env.test?
|
|
8
7
|
|
|
9
8
|
def self.provider_friendly_name
|
|
10
9
|
'Hyper-V'
|
|
11
10
|
end
|
|
12
11
|
|
|
12
|
+
def to_label
|
|
13
|
+
"#{name} (#{provider_friendly_name})"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.available?
|
|
17
|
+
Fog::Compute.providers.include?(:hyperv)
|
|
18
|
+
end
|
|
19
|
+
|
|
13
20
|
def self.model_name
|
|
14
21
|
ComputeResource.model_name
|
|
15
22
|
end
|
|
16
23
|
|
|
24
|
+
def supports_update?
|
|
25
|
+
true
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def editable_network_interfaces?
|
|
29
|
+
true
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def capabilities
|
|
33
|
+
[:build]
|
|
34
|
+
end
|
|
35
|
+
|
|
17
36
|
def test_connection(options = {})
|
|
18
|
-
|
|
37
|
+
validate_connectivity(options)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def validate_connectivity(options = {})
|
|
41
|
+
return unless connection_properties_valid?
|
|
42
|
+
return false if errors.any?
|
|
43
|
+
|
|
19
44
|
client.valid?
|
|
20
45
|
rescue Fog::Hyperv::Errors::ServiceError, ArgumentError, WinRM::WinRMAuthorizationError => e
|
|
21
|
-
errors
|
|
46
|
+
errors.add(:base, e.message)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def connection_valid?
|
|
50
|
+
return false if url.blank? || user.blank? || password.blank?
|
|
51
|
+
|
|
52
|
+
client&.valid?
|
|
53
|
+
rescue StandardError
|
|
54
|
+
false
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def connection_properties_valid?
|
|
58
|
+
errors[:url].empty? && errors[:user].empty? && errors[:password].empty?
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def caching_enabled
|
|
62
|
+
true
|
|
22
63
|
end
|
|
23
64
|
|
|
24
65
|
def provided_attributes
|
|
25
66
|
super.merge(mac: :mac)
|
|
26
67
|
end
|
|
27
68
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
hypervisor.logical_processor_count
|
|
69
|
+
def editable_network_interfaces?
|
|
70
|
+
true
|
|
31
71
|
end
|
|
32
72
|
|
|
33
|
-
|
|
34
|
-
|
|
73
|
+
# TODO
|
|
74
|
+
def max_cpu_count(host = nil)
|
|
75
|
+
(host || hypervisor).logical_processor_count
|
|
35
76
|
end
|
|
36
77
|
|
|
37
|
-
def
|
|
38
|
-
|
|
78
|
+
def max_memory(host = nil)
|
|
79
|
+
(host || hypervisor).memory_capacity
|
|
39
80
|
end
|
|
40
81
|
|
|
41
|
-
def
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
vm
|
|
82
|
+
def available_hypervisors
|
|
83
|
+
client.hosts.load(
|
|
84
|
+
cache.cache(:available_hypervisor) do
|
|
85
|
+
client.hosts.all.map(&:attributes)
|
|
86
|
+
end
|
|
87
|
+
)
|
|
48
88
|
end
|
|
89
|
+
alias hosts available_hypervisors
|
|
90
|
+
|
|
91
|
+
def cluster(name)
|
|
92
|
+
return nil unless name
|
|
49
93
|
|
|
50
|
-
|
|
51
|
-
find_vm_by_uuid(uuid).stop force: true
|
|
94
|
+
client.clusters.get name
|
|
52
95
|
end
|
|
53
96
|
|
|
54
|
-
def
|
|
55
|
-
|
|
56
|
-
|
|
97
|
+
def clusters
|
|
98
|
+
client.clusters.load(
|
|
99
|
+
cache.cache(:clusters) do
|
|
100
|
+
_clusters.map(&:attributes)
|
|
101
|
+
end
|
|
102
|
+
)
|
|
103
|
+
end
|
|
57
104
|
|
|
58
|
-
|
|
105
|
+
def hypervisor
|
|
106
|
+
client.hosts.new(
|
|
107
|
+
cache.cache(:hypervisor) do
|
|
108
|
+
client.hosts.first.attributes
|
|
109
|
+
end
|
|
110
|
+
)
|
|
111
|
+
end
|
|
59
112
|
|
|
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
|
-
}
|
|
113
|
+
delegate :servers, to: :client
|
|
68
114
|
|
|
69
|
-
|
|
115
|
+
def switches(host)
|
|
116
|
+
host ||= hosts.first
|
|
117
|
+
client.switches.load(
|
|
118
|
+
cache.cache(:"#{host.name}-switches") do
|
|
119
|
+
host.switches.all.map(&:attributes)
|
|
120
|
+
end
|
|
121
|
+
)
|
|
122
|
+
end
|
|
70
123
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
124
|
+
def new_vm(attr = {})
|
|
125
|
+
attr.delete :id
|
|
126
|
+
vm = super
|
|
127
|
+
iface_nested_attrs = nested_attributes_for :interfaces, attr[:interfaces_attributes]
|
|
128
|
+
vm.network_adapters = iface_nested_attrs.map do |attr|
|
|
129
|
+
attr.delete :id
|
|
130
|
+
Fog::Hyperv::Compute::NetworkAdapter.new(service: vm.service, vm:).tap do |nic|
|
|
131
|
+
attr.select { |_, v| v.present? }.each do |k, v|
|
|
132
|
+
nic.send(:"#{k}=", v)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
80
135
|
end
|
|
136
|
+
volume_nested_attrs = nested_attributes_for :volumes, attr[:volumes_attributes]
|
|
137
|
+
vm.hard_drives = volume_nested_attrs.map do |attr|
|
|
138
|
+
attr.delete :id
|
|
139
|
+
Fog::Hyperv::Compute::HardDrive.new(service: vm.service, vm:).tap do |hdd|
|
|
140
|
+
attr.select { |_, v| v.present? }.each do |k, v|
|
|
141
|
+
hdd.send(:"#{k}=", v)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
vm.id = nil
|
|
146
|
+
vm
|
|
147
|
+
end
|
|
81
148
|
|
|
82
|
-
|
|
149
|
+
def create_vm(attr = {})
|
|
150
|
+
attr = vm_instance_defaults.merge(attr.to_hash.deep_symbolize_keys)
|
|
151
|
+
attr.delete :computer_name if attr[:computer_name].blank?
|
|
152
|
+
attr.delete :start
|
|
153
|
+
|
|
154
|
+
validate_vm(attr, new: true)
|
|
155
|
+
validate_interfaces(attr)
|
|
156
|
+
validate_volumes(attr)
|
|
157
|
+
|
|
158
|
+
vm = client.servers.new(
|
|
159
|
+
name: attr[:name],
|
|
160
|
+
computer_name: attr[:computer_name],
|
|
161
|
+
generation: attr[:generation],
|
|
162
|
+
dynamic_memory_enabled: Foreman::Cast.to_bool(attr[:dynamic_memory_enabled]),
|
|
163
|
+
memory_startup: attr[:memory_startup].to_i,
|
|
164
|
+
memory_minimum: attr[:memory_minimum].to_i,
|
|
165
|
+
memory_maximum: attr[:memory_maximum].to_i,
|
|
166
|
+
processor_count: attr[:processor_count].to_i,
|
|
167
|
+
notes: attr[:notes]
|
|
168
|
+
)
|
|
169
|
+
# TODO: Allow configuring boot device?
|
|
170
|
+
vm.create boot_device: :NetworkAdapter
|
|
83
171
|
|
|
84
|
-
if vm.generation ==
|
|
172
|
+
if vm.generation == :UEFI && attr[:secure_boot_enabled].present?
|
|
85
173
|
f = vm.firmware
|
|
86
|
-
f.secure_boot = Foreman::Cast.to_bool(
|
|
174
|
+
f.secure_boot = Foreman::Cast.to_bool(attr[:secure_boot_enabled]) ? :On : :Off
|
|
87
175
|
f.save if f.dirty?
|
|
88
176
|
end
|
|
89
177
|
|
|
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)
|
|
178
|
+
create_interfaces(vm, attr)
|
|
179
|
+
create_volumes(vm, attr)
|
|
94
180
|
|
|
181
|
+
vm.start if attr[:start] == '1'
|
|
95
182
|
vm
|
|
96
183
|
rescue StandardError => e
|
|
97
|
-
vm
|
|
184
|
+
if vm
|
|
185
|
+
vm.stop
|
|
186
|
+
vm.hard_drives.each { |hdd| hdd.destroy(underlying: true) }
|
|
187
|
+
vm.destroy
|
|
188
|
+
end
|
|
98
189
|
|
|
99
190
|
raise e
|
|
100
191
|
end
|
|
101
192
|
|
|
102
|
-
def save_vm(uuid,
|
|
193
|
+
def save_vm(uuid, web_attr)
|
|
194
|
+
attr = web_attr.deep_symbolize_keys
|
|
195
|
+
|
|
196
|
+
validate_vm(attr)
|
|
197
|
+
validate_interfaces(attr)
|
|
198
|
+
validate_volumes(attr)
|
|
199
|
+
|
|
200
|
+
attr.delete :start
|
|
201
|
+
|
|
103
202
|
vm = find_vm_by_uuid(uuid)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
203
|
+
logger.debug "Updating VM #{vm} with arguments; #{attr}"
|
|
204
|
+
|
|
205
|
+
vm.processor_count = attr[:processor_count].to_i
|
|
206
|
+
vm.notes = attr[:notes].presence
|
|
207
|
+
vm.dynamic_memory_enabled = Foreman::Cast.to_bool(attr[:dynamic_memory_enabled])
|
|
208
|
+
if vm.dynamic_memory_enabled
|
|
209
|
+
vm.memory_minimum = attr[:memory_minimum].to_i
|
|
210
|
+
vm.memory_maximum = attr[:memory_maximum].to_i
|
|
107
211
|
end
|
|
108
|
-
|
|
109
|
-
if vm.generation == 2 && attr[:secure_boot_enabled].present?
|
|
212
|
+
if vm.generation == :UEFI && attr[:secure_boot_enabled].present?
|
|
110
213
|
f = vm.firmware
|
|
111
214
|
f.secure_boot = Foreman::Cast.to_bool(attr[:secure_boot_enabled]) ? :On : :Off
|
|
112
215
|
f.save if f.dirty?
|
|
113
216
|
end
|
|
114
217
|
|
|
115
|
-
update_interfaces(vm, attr
|
|
116
|
-
update_volumes(vm, attr
|
|
218
|
+
update_interfaces(vm, attr)
|
|
219
|
+
update_volumes(vm, attr)
|
|
117
220
|
|
|
118
221
|
vm.save if vm.dirty?
|
|
119
222
|
vm
|
|
@@ -121,10 +224,8 @@ module ForemanHyperv
|
|
|
121
224
|
|
|
122
225
|
def destroy_vm(uuid)
|
|
123
226
|
vm = find_vm_by_uuid(uuid)
|
|
124
|
-
vm.stop
|
|
125
|
-
vm.hard_drives.each
|
|
126
|
-
hd.vhd.destroy if hd.path
|
|
127
|
-
end
|
|
227
|
+
vm.stop turn_off: true
|
|
228
|
+
vm.hard_drives.each { |hdd| hdd.destroy(underlying: true) }
|
|
128
229
|
# TODO: Remove the empty VM folder
|
|
129
230
|
vm.destroy
|
|
130
231
|
rescue ActiveRecord::RecordNotFound, Fog::Errors::NotFound
|
|
@@ -132,51 +233,103 @@ module ForemanHyperv
|
|
|
132
233
|
true
|
|
133
234
|
end
|
|
134
235
|
|
|
135
|
-
def
|
|
136
|
-
|
|
137
|
-
|
|
236
|
+
def update_required?(old_attrs, new_attrs)
|
|
237
|
+
new_attrs.deep_symbolize_keys[:volumes_attributes]&.each do |_, hdd|
|
|
238
|
+
if hdd[:id].present? && hdd[:_delete] == '1'
|
|
239
|
+
Rails.logger.debug "Scheduling compute instance update because a volume was removed"
|
|
240
|
+
return true
|
|
241
|
+
elsif !hdd[:id].present? && hdd[:_delete] != '1'
|
|
242
|
+
Rails.logger.debug "Scheduling compute instance update because a new volume was added"
|
|
243
|
+
return true
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
new_attrs.deep_symbolize_keys[:interfaces_attributes]&.each do |_, iface|
|
|
247
|
+
if iface[:id].present? && iface[:_destroy] == '1'
|
|
248
|
+
Rails.logger.debug "Scheduling compute instance update because an interface was removed"
|
|
249
|
+
return true
|
|
250
|
+
elsif !iface[:id].present? && iface[:_destroy] != '1'
|
|
251
|
+
Rails.logger.debug "Scheduling compute instance update because a new interface was added"
|
|
252
|
+
return true
|
|
253
|
+
end
|
|
254
|
+
end
|
|
138
255
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
256
|
+
deep_update_required = proc do |old, new|
|
|
257
|
+
old.merge(new) do |k, old_v, new_v|
|
|
258
|
+
if k == :allowed_vlan_ids || k == :secondary_vlan_ids
|
|
259
|
+
tmp = Fog::Hyperv::Compute::NetworkAdapter.new
|
|
142
260
|
|
|
143
|
-
|
|
144
|
-
|
|
261
|
+
old_v = Fog::Hyperv::Compute::NetworkAdapterVlan.render_vlan_list(tmp.send(:parse_vlan_list, old_v.to_s))
|
|
262
|
+
new_v = Fog::Hyperv::Compute::NetworkAdapterVlan.render_vlan_list(tmp.send(:parse_vlan_list, new_v.to_s))
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
if old_v.is_a?(Hash) && new_v.is_a?(Hash)
|
|
266
|
+
deep_update_required.call(old_v, new_v)
|
|
267
|
+
elsif old_v.to_s != new_v.to_s
|
|
268
|
+
Rails.logger.debug "Scheduling compute instance update because #{k} changed it's value from '#{old_v}' (#{old_v.class}) to '#{new_v}' (#{new_v.class})"
|
|
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:, size: }.compact)
|
|
297
|
+
Fog::Hyperv::Compute::HardDrive.new 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,154 @@ module ForemanHyperv
|
|
|
188
341
|
)
|
|
189
342
|
end
|
|
190
343
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
344
|
+
private
|
|
345
|
+
|
|
346
|
+
def _clusters
|
|
347
|
+
if client.respond_to? :supports_clusters?
|
|
348
|
+
return [] unless client.supports_clusters?
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
client.clusters.all
|
|
352
|
+
rescue
|
|
353
|
+
[]
|
|
198
354
|
end
|
|
199
355
|
|
|
200
|
-
def
|
|
201
|
-
|
|
356
|
+
def validate_vm(attr, new: false)
|
|
357
|
+
# logger.debug "Validate VM #{attr.inspect}"
|
|
358
|
+
raise Foreman::Exception, 'VM lacks generation' if new && !attr[:generation].present?
|
|
359
|
+
raise Foreman::Exception, 'VM lacks memory' unless attr[:memory_startup].to_i > 0
|
|
360
|
+
raise Foreman::Exception, 'VM lacks CPUs' unless attr[:processor_count].to_i > 0
|
|
202
361
|
|
|
203
|
-
|
|
204
|
-
|
|
362
|
+
if Foreman::Cast.to_bool(attr[:dynamic_memory_enabled])
|
|
363
|
+
raise Foreman::Exception, 'VM lacks memory minimum' unless attr[:memory_minimum].to_i > 0
|
|
364
|
+
raise Foreman::Exception, 'VM lacks memory maximum' unless attr[:memory_maximum].to_i > 0
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
def validate_interfaces(attr)
|
|
369
|
+
interfaces = nested_attributes_for :interfaces, attr[:interfaces_attributes]
|
|
370
|
+
interfaces.reject! { |iface| iface[:_destroy] == '1' }
|
|
371
|
+
# logger.debug "Validate NIC #{interfaces.inspect}"
|
|
205
372
|
interfaces.each do |iface|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
373
|
+
compute = iface[:compute_attributes] || iface
|
|
374
|
+
case compute[:vlan_operation_mode].to_s
|
|
375
|
+
when 'Untagged'
|
|
376
|
+
when 'Access'
|
|
377
|
+
raise Foreman::Exception, 'Interface is missing access VLAN' unless compute[:access_vlan_id].to_i > 0
|
|
378
|
+
when 'Trunk'
|
|
379
|
+
raise Foreman::Exception, 'Interface is missing native VLAN' unless compute[:native_vlan_id].to_i > 0
|
|
380
|
+
raise Foreman::Exception, 'Interface is missing allowed VLANs' unless compute[:allowed_vlan_ids].present?
|
|
381
|
+
when 'Private'
|
|
382
|
+
raise Foreman::Exception, 'Interface is missing primary VLAN' unless compute[:primary_vlan_id].to_i > 0
|
|
383
|
+
case compute[:vlan_private_mode].to_s
|
|
384
|
+
when 'Promiscuous'
|
|
385
|
+
raise Foreman::Exception, 'Interface is missing secondary VLANs' unless compute[:secondary_vlan_ids].present?
|
|
386
|
+
else
|
|
387
|
+
raise Foreman::Exception, 'Interface is missing secondary VLAN' unless compute[:secondary_vlan_id].to_i > 0
|
|
388
|
+
end
|
|
389
|
+
else
|
|
390
|
+
raise Foreman::Exception, 'Interface has unknown VLAN mode'
|
|
210
391
|
end
|
|
211
|
-
|
|
212
|
-
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
def create_interfaces(vm, attr)
|
|
396
|
+
interfaces = nested_attributes_for :interfaces, attr[:interfaces_attributes]
|
|
397
|
+
logger.debug "Creating interfaces with: #{interfaces}"
|
|
398
|
+
|
|
399
|
+
first_provisioned = false
|
|
400
|
+
interfaces.each do |iface|
|
|
401
|
+
# The VM is pre-created with one NIC regardless of given creation options, so configure that one first
|
|
402
|
+
nic = vm.network_adapters.first unless first_provisioned
|
|
403
|
+
first_provisioned = true
|
|
404
|
+
|
|
405
|
+
nic ||= vm.network_adapters.new
|
|
406
|
+
iface.except(:identity, :ip, :ip6).each do |k, v|
|
|
407
|
+
nic.send(:"#{k}=", v.presence)
|
|
213
408
|
end
|
|
409
|
+
# nic.is_legacy = Foreman::Cast.to_bool(iface[:is_legacy]) if vm.generation == :BIOS && iface[:is_legacy].present?
|
|
410
|
+
# logger.debug "Creating interface with #{iface.inspect} - #{nic.inspect}\n#{nic.vlan_setting.inspect}"
|
|
214
411
|
nic.save
|
|
215
412
|
end
|
|
216
413
|
|
|
217
|
-
|
|
414
|
+
return unless vm.network_adapters.all(_return_fields: %i[dynamic_mac_address_enabled]).any?(&:dynamic_mac_address_enabled)
|
|
415
|
+
|
|
416
|
+
# Populate all non-populated MAC addresses
|
|
218
417
|
vm.start
|
|
219
418
|
vm.stop turn_off: true
|
|
220
419
|
|
|
221
|
-
vm.network_adapters.reload
|
|
222
|
-
vm.network_adapters.each do |nic|
|
|
420
|
+
vm.network_adapters.reload.each do |nic|
|
|
223
421
|
nic.dynamic_mac_address_enabled = false
|
|
224
422
|
nic.save if nic.dirty?
|
|
225
423
|
end
|
|
226
424
|
end
|
|
227
425
|
|
|
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
|
|
426
|
+
def update_interfaces(vm, attr)
|
|
427
|
+
interfaces = nested_attributes_for :interfaces, attr[:interfaces_attributes]
|
|
428
|
+
logger.debug "Updating interfaces with: #{interfaces}"
|
|
237
429
|
|
|
238
|
-
def update_interfaces(vm, attrs)
|
|
239
|
-
interfaces = nested_attributes_for :interfaces, attrs
|
|
240
|
-
client.logger.debug "Updating interfaces with: #{interfaces}"
|
|
241
430
|
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
|
|
431
|
+
compute = interface[:compute_attributes]
|
|
432
|
+
if compute[:identity].present?
|
|
433
|
+
nic = vm.network_adapters.get compute[:identity]
|
|
434
|
+
if interface[:_destroy] == '1'
|
|
435
|
+
nic.destroy
|
|
250
436
|
else
|
|
251
|
-
|
|
252
|
-
nic.send("#{k}="
|
|
437
|
+
compute.each do |k, v|
|
|
438
|
+
nic.send(:"#{k}=", v.presence)
|
|
253
439
|
end
|
|
254
|
-
nic.
|
|
440
|
+
nic.mac ||= interface[:mac].presence
|
|
441
|
+
nic.save
|
|
255
442
|
end
|
|
443
|
+
elsif interface[:_destroy] != '1'
|
|
444
|
+
nic = vm.network_adapters.new
|
|
445
|
+
compute.each do |k, v|
|
|
446
|
+
nic.send(:"#{k}=", v.presence)
|
|
447
|
+
end
|
|
448
|
+
nic.mac ||= interface[:mac].presence
|
|
449
|
+
nic.save
|
|
256
450
|
end
|
|
257
451
|
end
|
|
258
452
|
end
|
|
259
453
|
|
|
260
|
-
def
|
|
261
|
-
volumes = nested_attributes_for :volumes,
|
|
262
|
-
|
|
454
|
+
def validate_volumes(attr)
|
|
455
|
+
volumes = nested_attributes_for :volumes, attr[:volumes_attributes]
|
|
456
|
+
volumes.reject! { |vol| vol[:_delete] == '1' }
|
|
457
|
+
# logger.debug "Validate Volume #{volumes.inspect}"
|
|
458
|
+
volumes.each do |vol|
|
|
459
|
+
raise Foreman::Exception, 'Volume lacks name' unless vol[:basename].present?
|
|
460
|
+
raise Foreman::Exception, 'Volume name should not include a file extension' if vol[:basename] =~ /\.vhd[sx]?$/
|
|
461
|
+
raise Foreman::Exception, 'Volume lacks size' unless vol[:size_bytes].to_i > 0
|
|
462
|
+
end
|
|
463
|
+
raise Foreman::Exception, 'Volume names need to be unique' if volumes.group_by { |vol| vol[:basename].downcase }.any? { |k, v| v.count > 1 }
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
def create_volumes(vm, attr)
|
|
467
|
+
volumes = nested_attributes_for :volumes, attr[:volumes_attributes]
|
|
468
|
+
logger.debug "Creating volumes with: #{volumes}"
|
|
469
|
+
volumes.each do |vol|
|
|
470
|
+
vhd = vm.vhds.create basename: vol[:basename], size: vol[:size_bytes].to_i
|
|
471
|
+
vm.hard_drives.create vhd:
|
|
472
|
+
end
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
def update_volumes(vm, attr)
|
|
476
|
+
volumes = nested_attributes_for :volumes, attr[:volumes_attributes]
|
|
477
|
+
logger.debug "Updating volumes with: #{volumes}"
|
|
263
478
|
volumes.each do |volume|
|
|
264
|
-
if volume[:
|
|
265
|
-
hd = vm.hard_drives.
|
|
266
|
-
|
|
267
|
-
|
|
479
|
+
if volume[:id].present?
|
|
480
|
+
hd = vm.hard_drives.get volume[:id]
|
|
481
|
+
if volume[:_delete] == '1'
|
|
482
|
+
hd.destroy(underlying: true)
|
|
483
|
+
else
|
|
484
|
+
vhd = hd.vhd
|
|
485
|
+
vhd.size = volume[:size_bytes].to_i
|
|
486
|
+
vhd.save if vhd.dirty?
|
|
487
|
+
end
|
|
488
|
+
elsif volume[:_delete] != '1'
|
|
489
|
+
vhd = vm.vhds.create basename: volume[:basename], size: volume[:size_bytes].to_i
|
|
490
|
+
vm.hard_drives.create path: vhd.path
|
|
268
491
|
end
|
|
269
|
-
vm.hard_drives.create(path: volume[:path], size: volume[:size]) if volume[:id].blank? && volume[:_delete] != '1'
|
|
270
492
|
end
|
|
271
493
|
end
|
|
272
494
|
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>
|