foreman_hyperv 0.0.3 → 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 +5 -5
- data/LICENSE.txt +1451 -12
- data/README.md +8 -2
- 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 +115 -12
- data/app/models/concerns/fog_extensions/hyperv/server.rb +90 -31
- data/app/models/concerns/foreman_hyperv/host_managed_extensions.rb +66 -0
- data/app/models/foreman_hyperv/hyperv.rb +366 -131
- data/app/views/compute_resources/form/_hyperv.html.erb +6 -2
- data/app/views/compute_resources_vms/form/hyperv/_base.html.erb +53 -21
- data/app/views/compute_resources_vms/form/hyperv/_network.html.erb +34 -10
- 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 -17
- 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,114 +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
|
|
|
69
|
+
def editable_network_interfaces?
|
|
70
|
+
true
|
|
71
|
+
end
|
|
72
|
+
|
|
28
73
|
# TODO
|
|
29
|
-
def max_cpu_count
|
|
30
|
-
hypervisor.logical_processor_count
|
|
74
|
+
def max_cpu_count(host = nil)
|
|
75
|
+
(host || hypervisor).logical_processor_count
|
|
31
76
|
end
|
|
32
77
|
|
|
33
|
-
def max_memory
|
|
34
|
-
hypervisor.memory_capacity
|
|
78
|
+
def max_memory(host = nil)
|
|
79
|
+
(host || hypervisor).memory_capacity
|
|
35
80
|
end
|
|
36
81
|
|
|
37
|
-
def
|
|
38
|
-
|
|
82
|
+
def available_hypervisors
|
|
83
|
+
client.hosts.load(
|
|
84
|
+
cache.cache(:available_hypervisor) do
|
|
85
|
+
client.hosts.all.map(&:attributes)
|
|
86
|
+
end
|
|
87
|
+
)
|
|
39
88
|
end
|
|
89
|
+
alias hosts available_hypervisors
|
|
40
90
|
|
|
41
|
-
def
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
volumes = nested_attributes_for :volumes, attr[:volumes_attributes]
|
|
46
|
-
volumes.map { |v| vm.volumes << new_volume(v) }
|
|
47
|
-
vm
|
|
91
|
+
def cluster(name)
|
|
92
|
+
return nil unless name
|
|
93
|
+
|
|
94
|
+
client.clusters.get name
|
|
48
95
|
end
|
|
49
96
|
|
|
50
|
-
def
|
|
51
|
-
|
|
97
|
+
def clusters
|
|
98
|
+
client.clusters.load(
|
|
99
|
+
cache.cache(:clusters) do
|
|
100
|
+
_clusters.map(&:attributes)
|
|
101
|
+
end
|
|
102
|
+
)
|
|
52
103
|
end
|
|
53
104
|
|
|
54
|
-
def
|
|
55
|
-
|
|
56
|
-
|
|
105
|
+
def hypervisor
|
|
106
|
+
client.hosts.new(
|
|
107
|
+
cache.cache(:hypervisor) do
|
|
108
|
+
client.hosts.first.attributes
|
|
109
|
+
end
|
|
110
|
+
)
|
|
111
|
+
end
|
|
57
112
|
|
|
58
|
-
|
|
59
|
-
boot_device: 'NetworkAdapter',
|
|
60
|
-
generation: args[:generation].to_i,
|
|
61
|
-
memory_startup: args[:memory_startup].presence.to_i,
|
|
62
|
-
name: args[:name],
|
|
63
|
-
no_vhd: true
|
|
64
|
-
}
|
|
113
|
+
delegate :servers, to: :client
|
|
65
114
|
|
|
66
|
-
|
|
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
|
|
67
123
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
|
77
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
|
|
78
148
|
|
|
79
|
-
|
|
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
|
|
80
171
|
|
|
81
|
-
if vm.generation ==
|
|
172
|
+
if vm.generation == :UEFI && attr[:secure_boot_enabled].present?
|
|
82
173
|
f = vm.firmware
|
|
83
|
-
f.secure_boot = Foreman::Cast.to_bool(
|
|
174
|
+
f.secure_boot = Foreman::Cast.to_bool(attr[:secure_boot_enabled]) ? :On : :Off
|
|
84
175
|
f.save if f.dirty?
|
|
85
176
|
end
|
|
86
177
|
|
|
87
|
-
create_interfaces(vm,
|
|
88
|
-
create_volumes(vm,
|
|
178
|
+
create_interfaces(vm, attr)
|
|
179
|
+
create_volumes(vm, attr)
|
|
89
180
|
|
|
181
|
+
vm.start if attr[:start] == '1'
|
|
90
182
|
vm
|
|
91
183
|
rescue StandardError => e
|
|
92
|
-
vm
|
|
184
|
+
if vm
|
|
185
|
+
vm.stop
|
|
186
|
+
vm.hard_drives.each { |hdd| hdd.destroy(underlying: true) }
|
|
187
|
+
vm.destroy
|
|
188
|
+
end
|
|
93
189
|
|
|
94
190
|
raise e
|
|
95
191
|
end
|
|
96
192
|
|
|
97
|
-
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
|
+
|
|
98
202
|
vm = find_vm_by_uuid(uuid)
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
|
102
211
|
end
|
|
103
|
-
|
|
104
|
-
if vm.generation == 2 && attr[:secure_boot_enabled].present?
|
|
212
|
+
if vm.generation == :UEFI && attr[:secure_boot_enabled].present?
|
|
105
213
|
f = vm.firmware
|
|
106
214
|
f.secure_boot = Foreman::Cast.to_bool(attr[:secure_boot_enabled]) ? :On : :Off
|
|
107
215
|
f.save if f.dirty?
|
|
108
216
|
end
|
|
109
217
|
|
|
110
|
-
update_interfaces(vm, attr
|
|
111
|
-
update_volumes(vm, attr
|
|
218
|
+
update_interfaces(vm, attr)
|
|
219
|
+
update_volumes(vm, attr)
|
|
112
220
|
|
|
113
221
|
vm.save if vm.dirty?
|
|
114
222
|
vm
|
|
@@ -116,10 +224,8 @@ module ForemanHyperv
|
|
|
116
224
|
|
|
117
225
|
def destroy_vm(uuid)
|
|
118
226
|
vm = find_vm_by_uuid(uuid)
|
|
119
|
-
vm.stop
|
|
120
|
-
vm.hard_drives.each
|
|
121
|
-
hd.vhd.destroy if hd.path
|
|
122
|
-
end
|
|
227
|
+
vm.stop turn_off: true
|
|
228
|
+
vm.hard_drives.each { |hdd| hdd.destroy(underlying: true) }
|
|
123
229
|
# TODO: Remove the empty VM folder
|
|
124
230
|
vm.destroy
|
|
125
231
|
rescue ActiveRecord::RecordNotFound, Fog::Errors::NotFound
|
|
@@ -127,44 +233,101 @@ module ForemanHyperv
|
|
|
127
233
|
true
|
|
128
234
|
end
|
|
129
235
|
|
|
130
|
-
def
|
|
131
|
-
|
|
132
|
-
|
|
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
|
|
133
255
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
|
137
260
|
|
|
138
|
-
|
|
139
|
-
|
|
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
|
|
140
277
|
end
|
|
141
278
|
|
|
142
|
-
def
|
|
143
|
-
|
|
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
|
|
144
290
|
end
|
|
145
291
|
|
|
146
|
-
def
|
|
147
|
-
|
|
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
|
|
148
298
|
end
|
|
149
299
|
|
|
150
|
-
def
|
|
151
|
-
|
|
300
|
+
def new_cdrom(attr = {})
|
|
301
|
+
Fog::Hyperv::Compute::DvdDrive.new attr
|
|
152
302
|
end
|
|
153
303
|
|
|
154
|
-
def
|
|
155
|
-
|
|
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
|
+
)
|
|
156
311
|
end
|
|
157
|
-
alias hosts available_hypervisors
|
|
158
312
|
|
|
159
|
-
def
|
|
160
|
-
|
|
161
|
-
|
|
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]
|
|
162
317
|
end
|
|
163
|
-
|
|
318
|
+
vm_attrs
|
|
164
319
|
end
|
|
165
320
|
|
|
166
|
-
def
|
|
167
|
-
|
|
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
|
|
168
331
|
end
|
|
169
332
|
|
|
170
333
|
protected
|
|
@@ -178,82 +341,154 @@ module ForemanHyperv
|
|
|
178
341
|
)
|
|
179
342
|
end
|
|
180
343
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
+
[]
|
|
188
354
|
end
|
|
189
355
|
|
|
190
|
-
def
|
|
191
|
-
|
|
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
|
|
361
|
+
|
|
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
|
|
192
367
|
|
|
193
|
-
|
|
194
|
-
|
|
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}"
|
|
195
372
|
interfaces.each do |iface|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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'
|
|
200
391
|
end
|
|
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)
|
|
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}"
|
|
201
411
|
nic.save
|
|
202
412
|
end
|
|
203
413
|
|
|
204
|
-
|
|
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
|
|
205
417
|
vm.start
|
|
206
418
|
vm.stop turn_off: true
|
|
207
419
|
|
|
208
|
-
vm.network_adapters.reload
|
|
209
|
-
vm.network_adapters.each do |nic|
|
|
420
|
+
vm.network_adapters.reload.each do |nic|
|
|
210
421
|
nic.dynamic_mac_address_enabled = false
|
|
211
422
|
nic.save if nic.dirty?
|
|
212
423
|
end
|
|
213
424
|
end
|
|
214
425
|
|
|
215
|
-
def
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
volumes.each do |vol|
|
|
219
|
-
vhd = vm.vhds.create path: vm.folder_name + '\\' + vol[:path], size: vol[:size]
|
|
220
|
-
vm.hard_drives.create path: vhd.path
|
|
221
|
-
end
|
|
222
|
-
vm.hard_drives.reload
|
|
223
|
-
end
|
|
426
|
+
def update_interfaces(vm, attr)
|
|
427
|
+
interfaces = nested_attributes_for :interfaces, attr[:interfaces_attributes]
|
|
428
|
+
logger.debug "Updating interfaces with: #{interfaces}"
|
|
224
429
|
|
|
225
|
-
def update_interfaces(vm, attrs)
|
|
226
|
-
interfaces = nested_attributes_for :interfaces, attrs
|
|
227
|
-
client.logger.debug "Updating interfaces with: #{interfaces}"
|
|
228
430
|
interfaces.each do |interface|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
nic
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
nic = vm.network_adapters.find { |n| n.id == interface[:id] }
|
|
235
|
-
if interface[:_delete] == '1'
|
|
236
|
-
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
|
|
237
436
|
else
|
|
238
|
-
|
|
239
|
-
nic.send("#{k}="
|
|
437
|
+
compute.each do |k, v|
|
|
438
|
+
nic.send(:"#{k}=", v.presence)
|
|
240
439
|
end
|
|
241
|
-
nic.
|
|
440
|
+
nic.mac ||= interface[:mac].presence
|
|
441
|
+
nic.save
|
|
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)
|
|
242
447
|
end
|
|
448
|
+
nic.mac ||= interface[:mac].presence
|
|
449
|
+
nic.save
|
|
243
450
|
end
|
|
244
451
|
end
|
|
245
452
|
end
|
|
246
453
|
|
|
247
|
-
def
|
|
248
|
-
volumes = nested_attributes_for :volumes,
|
|
249
|
-
|
|
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}"
|
|
250
478
|
volumes.each do |volume|
|
|
251
|
-
if volume[:
|
|
252
|
-
hd = vm.hard_drives.
|
|
253
|
-
|
|
254
|
-
|
|
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
|
|
255
491
|
end
|
|
256
|
-
vm.hard_drives.create(path: volume[:path], size: volume[:size]) if volume[:id].blank? && volume[:_delete] != '1'
|
|
257
492
|
end
|
|
258
493
|
end
|
|
259
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>
|