foreman_hyperv 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 65f805250cc59658795113cc0d201de9c2da857b8dc0868bc5a7450be52619ef
4
- data.tar.gz: dfbe2e15421b902d38f2747a07ff83dc68661178914a7b2de992d1d4d905003d
3
+ metadata.gz: 156c82d3cc4630c1d222fc97370cef6b5f3128da1d97732dcef072f8b4adbbe3
4
+ data.tar.gz: d812c0f6a6785879bbe4cacff1f943237b24951ea8b5b5f2e2add668d2480b58
5
5
  SHA512:
6
- metadata.gz: 3487dfa1b123b714d5fc093ff23d85fbc49253a0f7cd24308461b2117436e181c38380e9b2acdf3bc1bae3669eabb5dd87475e7946376c6da79aa1eaae5b982b
7
- data.tar.gz: 32dc60ede4f1bd60f7c74e56a7c9e648f58757a4bac64cef8e3d09858380984bdf059093e016fb72de3c2bede1f6c94f39cfb7db4a1c5071e4d846777939f0ea
6
+ metadata.gz: 89a21ca4968ec5adbc536b7cffde5e2619f4e54f704a259e52ec812ccb25271be31674c5bd0f6f154752c1a80d8b2eef836f497fe838781bd6f923ce1e8aa242
7
+ data.tar.gz: 16c7a09381db0854437d1620d708eabd7415459fc644577e508b45e94cd51e3c5b8bcd722eafeb049140615f15ea54fedbbf80e2a604dab17ecf82c4ea9395e2
@@ -1,20 +1,3 @@
1
- $(document).on('ContentLoad', function() { tfm.numFields.initAll(); });
2
-
3
- function hypervGenerationChange(item) {
4
- var toIter = ['#host_compute_attributes_secure_boot_enabled', '#compute_attribute_vm_attrs_secure_boot_enabled'];
5
- gen = $(item).val();
6
-
7
- if (gen == 'BIOS') {
8
- for (var i = 0; i < toIter.length; ++i) {
9
- $(toIter[i]).attr('disabled', true);
10
- }
11
- } else {
12
- for (var i = 0; i < toIter.length; ++i) {
13
- $(toIter[i]).removeAttr('disabled');
14
- }
15
- }
16
- }
17
-
18
1
  function hypervDynamicMemoryChange(item) {
19
2
  var toIter = [
20
3
  '#host_compute_attributes_memory_maximum',
@@ -1,17 +1,35 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ForemanHyperv
2
4
  module ComputeResourcesVmsHelper
3
5
  def hyperv_networks(compute_resource)
4
6
  compute_resource.switches(nil).map do |sw|
5
- [ sw.id, "#{sw.name}#{sw.switch_type ? " (#{sw.switch_type})" : nil}" ]
7
+ [sw.id, "#{sw.name}#{" (#{sw.switch_type})" if sw.switch_type}"]
8
+ end
9
+ end
10
+
11
+ def hyperv_boot_devices
12
+ devices = Fog::Hyperv::BOOT_DEVICE_ENUM_VALUES.dup
13
+ # devices.delete :Floppy if generation == :UEFI
14
+ devices.delete :IDE
15
+ devices.delete :LegacyNetworkAdapter
16
+ devices.map do |dev|
17
+ name = dev.to_s
18
+ name = 'Network Adapter' if dev == :NetworkAdapter
19
+ name = 'DVD' if dev == :CD
20
+ name = 'Floppy (Only for BIOS)' if dev == :Floppy
21
+ [dev, name]
6
22
  end
7
23
  end
8
24
 
9
25
  def hyperv_generations
10
26
  Fog::Hyperv::Compute::Server::VM_GENERATION_VALUES.map { |gen, num| [gen, "Generation #{num} (#{gen})"] }
11
27
  end
28
+
12
29
  def hyperv_vlan_modes
13
30
  Fog::Hyperv::Compute::NetworkAdapterVlan::VLAN_OPERATION_MODE.map { |mode| [mode, mode] }
14
31
  end
32
+
15
33
  def hyperv_private_vlan_modes
16
34
  Fog::Hyperv::Compute::NetworkAdapterVlan::PRIVATE_VLAN_MODE.reject { |mode| mode == :Unknown }.map { |mode| [mode, mode] }
17
35
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FogExtensions
2
4
  module Hyperv
3
5
  module Compute
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FogExtensions
2
4
  module Hyperv
3
5
  module HardDrive
@@ -27,8 +29,8 @@ module FogExtensions
27
29
  .slice(:id)
28
30
  .merge(
29
31
  {
30
- basename:,
31
- size_bytes:
32
+ basename: basename,
33
+ size_bytes: size_bytes
32
34
  }.compact
33
35
  )
34
36
  end
@@ -7,16 +7,17 @@ module FogExtensions
7
7
 
8
8
  def mac
9
9
  return unless mac_address
10
- return if mac_address.to_i(16) == 0
10
+ return if mac_address.to_i(16).zero?
11
11
 
12
12
  # Downcase and split every 2 chars, join with :
13
- mac_address.downcase.scan(%r{.{2}}).join(':')
13
+ mac_address.downcase.scan(/.{2}/).join(':')
14
14
  end
15
15
 
16
16
  def mac=(m)
17
17
  mac_address = m&.upcase&.delete(':')
18
18
  mac_address = Fog::Hyperv::Compute::NetworkAdapter::NIC_FALLBACK_MAC if mac_address.nil? || mac_address.blank?
19
- dynamic_mac_address_enabled = (mac_address.to_i(16) == 0)
19
+ mac_address.to_i(16)
20
+ 0
20
21
  end
21
22
 
22
23
  # VLAN settings
@@ -24,6 +25,7 @@ module FogExtensions
24
25
  def vlan_operation_mode
25
26
  vlan_setting.operation_mode
26
27
  end
28
+
27
29
  def vlan_operation_mode=(mode)
28
30
  vlan_setting.operation_mode = mode
29
31
  end
@@ -33,61 +35,60 @@ module FogExtensions
33
35
 
34
36
  vlan_setting.private_vlan_mode
35
37
  end
38
+
36
39
  def vlan_private_mode=(mode)
37
40
  vlan_setting.private_vlan_mode = mode
38
41
  end
39
42
 
40
43
  def access_vlan_id
41
- return nil if vlan_setting.access_vlan_id.zero?
44
+ return nil if (vlan_setting.access_vlan_id || 0).zero?
42
45
 
43
46
  vlan_setting.access_vlan_id
44
47
  end
45
- def access_vlan_id=(id)
46
- vlan_setting.access_vlan_id = id
47
- end
48
+
49
+ delegate :access_vlan_id=, to: :vlan_setting
48
50
 
49
51
  def native_vlan_id
50
- return nil if vlan_setting.native_vlan_id.zero?
52
+ return nil if (vlan_setting.native_vlan_id || 0).zero?
51
53
 
52
54
  vlan_setting.native_vlan_id
53
55
  end
54
- def native_vlan_id=(id)
55
- vlan_setting.native_vlan_id = id
56
- end
56
+
57
+ delegate :native_vlan_id=, to: :vlan_setting
57
58
 
58
59
  def allowed_vlan_ids
59
60
  return nil unless vlan_setting.allowed_vlan_id_list&.any?
60
61
 
61
62
  Fog::Hyperv::Compute::NetworkAdapterVlan.render_vlan_list vlan_setting.allowed_vlan_id_list
62
63
  end
64
+
63
65
  def allowed_vlan_ids=(ids)
64
66
  ids ||= ''
65
67
  vlan_setting.allowed_vlan_id_list = parse_vlan_list(ids)
66
68
  end
67
69
 
68
70
  def primary_vlan_id
69
- return nil if vlan_setting.primary_vlan_id.zero?
71
+ return nil if (vlan_setting.primary_vlan_id || 0).zero?
70
72
 
71
73
  vlan_setting.primary_vlan_id
72
74
  end
73
- def primary_vlan_id=(id)
74
- vlan_setting.primary_vlan_id = id
75
- end
75
+
76
+ delegate :primary_vlan_id=, to: :vlan_setting
76
77
 
77
78
  def secondary_vlan_id
78
- return nil if vlan_setting.secondary_vlan_id.zero?
79
+ return nil if (vlan_setting.secondary_vlan_id || 0).zero?
79
80
 
80
81
  vlan_setting.secondary_vlan_id
81
82
  end
82
- def secondary_vlan_id=(id)
83
- vlan_setting.secondary_vlan_id = id
84
- end
83
+
84
+ delegate :secondary_vlan_id=, to: :vlan_setting
85
85
 
86
86
  def secondary_vlan_ids
87
87
  return nil unless vlan_setting.secondary_vlan_id_list&.any?
88
88
 
89
89
  Fog::Hyperv::Compute::NetworkAdapterVlan.render_vlan_list vlan_setting.secondary_vlan_id_list
90
90
  end
91
+
91
92
  def secondary_vlan_ids=(ids)
92
93
  ids ||= ''
93
94
  vlan_setting.secondary_vlan_id_list = parse_vlan_list(ids)
@@ -100,17 +101,20 @@ module FogExtensions
100
101
  :switch_id
101
102
  )
102
103
  .merge(
103
- {
104
- vlan_operation_mode:,
105
- vlan_private_mode:,
106
- access_vlan_id:,
107
- native_vlan_id:,
108
- allowed_vlan_ids:,
109
- primary_vlan_id:,
110
- secondary_vlan_id:,
111
- secondary_vlan_ids:
112
- }.compact
104
+ vlan_setting.attributes.slice(
105
+ :vlan_operation_mode,
106
+ :vlan_private_mode,
107
+ :access_vlan_id,
108
+ :native_vlan_id,
109
+ :primary_vlan_id,
110
+ :secondary_vlan_id
111
+ )
112
+ )
113
+ .merge(
114
+ secondary_vlan_ids: secondary_vlan_ids,
115
+ allowed_vlan_ids: allowed_vlan_ids
113
116
  )
117
+ .compact
114
118
  end
115
119
 
116
120
  private
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FogExtensions
2
4
  module Hyperv
3
5
  module Server
@@ -9,7 +11,7 @@ module FogExtensions
9
11
  end
10
12
 
11
13
  def folder_name
12
- name.gsub(/[^0-9A-Za-z.\-]/, '_')
14
+ name.gsub(/[^0-9A-Za-z.-]/, '_')
13
15
  end
14
16
 
15
17
  def mac
@@ -40,7 +42,20 @@ module FogExtensions
40
42
  def cluster_name=(name)
41
43
  @cluster = service.clusters.get(name)
42
44
  end
43
- #
45
+
46
+ def boot_device
47
+ :NetworkAdapter
48
+ end
49
+
50
+ def foreman_firmware_type
51
+ return 'bios' if generation_num == 1
52
+
53
+ secure_boot_enabled ? 'uefi_secure_boot' : 'uefi'
54
+ end
55
+
56
+ def foreman_firmware
57
+ generation_num == 1 ? 'bios' : 'efi'
58
+ end
44
59
 
45
60
  def vlan
46
61
  nic = network_adapters.first
@@ -51,7 +66,7 @@ module FogExtensions
51
66
  def vlan=(vlan)
52
67
  logger.warn "using vlan=#{vlan.inspect} on Hyper-V VM, this can lead to unexpected results"
53
68
  nic = network_adapters.first
54
- if vlan.present? && vlan.to_i > 0
69
+ if vlan.present? && vlan.to_i.positive?
55
70
  nic.vlan_operation_mode = :Access if nic.vlan_operation_mode == :Untagged
56
71
  case nic.vlan_operation_mode
57
72
  when :Access
@@ -68,16 +83,15 @@ module FogExtensions
68
83
 
69
84
  def secure_boot_enabled=(enabled)
70
85
  return if generation != :UEFI
71
-
72
- @secure_boot = enabled
73
86
  return unless persisted?
74
87
 
75
88
  firmware.secure_boot = enabled ? :On : :Off
89
+ firmware.save
76
90
  end
77
91
 
78
92
  def secure_boot_enabled
79
93
  return false if generation != :UEFI
80
- return @secure_boot unless persisted?
94
+ return unless persisted?
81
95
 
82
96
  firmware.secure_boot == :On
83
97
  end
@@ -103,29 +117,35 @@ module FogExtensions
103
117
  match ||= fog_nics.detect { |fn| fn.mac == nic.mac }
104
118
  # match ||= fog_nics.detect { |fn| fn.name == nic_attrs['name'].presence }
105
119
 
106
- if !match
120
+ unless match
107
121
  # Match on networking, limit potentials down to identical configuration and then pick the first
108
122
  potential = fog_nics.select do |fn|
109
123
  fn.switch_id == nic_attrs['switch_id'].presence || fn.switch_name == nic_attrs['switch_name'].presence
110
124
  end
111
125
  potential.select! { |fn| fn.vlan_operation_mode.to_s == nic_attrs['vlan_operation_mode'] }
112
- if nic_attrs['vlan_operation_mode'] == 'Access'
126
+ case nic_attrs['vlan_operation_mode']
127
+ when 'Access'
113
128
  potential.select! { |fn| fn.access_vlan_id.to_s == nic_attrs['access_vlan_id'] }
114
- elsif nic_attrs['vlan_operation_mode'] == 'Trunk'
129
+ when 'Trunk'
115
130
  potential.select! { |fn| fn.native_vlan_id.to_s == nic_attrs['native_vlan_id'] }
116
- potential.select! { |fn| fn.allowed_vlan_ids.split(',').map(&:strip) == nic_attrs['allowed_vlan_ids'].split(',').map(&:strip) } \
117
- if nic_attrs['allowed_vlan_ids'].present?
118
- elsif nic_attrs['vlan_operation_mode'] == 'Private'
131
+ if nic_attrs['allowed_vlan_ids'].present?
132
+ potential.select! do |fn|
133
+ fn.allowed_vlan_ids.split(',').map(&:strip) == nic_attrs['allowed_vlan_ids'].split(',').map(&:strip)
134
+ end
135
+ end
136
+ when 'Private'
119
137
  potential.select! { |fn| fn.vlan_private_mode.to_s == nic_attrs['vlan_private_mode'] }
120
138
  potential.select! { |fn| fn.primary_vlan_id.to_s == nic_attrs['primary_vlan_id'].to_s } \
121
139
  if nic_attrs['primary_vlan_id'].present?
122
140
 
123
141
  if nic_attrs['vlan_private_mode'] == 'Promiscuous'
124
- potential.select! { |fn| fn.secondary_vlan_ids.split(',').map(&:strip) == nic_attrs['secondary_vlan_ids'].split(',').map(&:strip) } \
125
- if nic_attrs['secondary_vlan_ids'].present?
126
- else
127
- potential.select! { |fn| fn.secondary_vlan_id.to_s == nic_attrs['secondary_vlan_id'].to_s } \
128
- if nic_attrs['secondary_vlan_id'].present?
142
+ if nic_attrs['secondary_vlan_ids'].present?
143
+ potential.select! do |fn|
144
+ fn.secondary_vlan_ids.split(',').map(&:strip) == nic_attrs['secondary_vlan_ids'].split(',').map(&:strip)
145
+ end
146
+ end
147
+ elsif nic_attrs['secondary_vlan_id'].present?
148
+ potential.select! { |fn| fn.secondary_vlan_id.to_s == nic_attrs['secondary_vlan_id'].to_s }
129
149
  end
130
150
  end
131
151
 
@@ -16,9 +16,9 @@ module ForemanHyperv
16
16
 
17
17
  begin
18
18
  hyperv_sync_interfaces
19
- rescue => e
19
+ rescue StandardError => e
20
20
  failure _("Failed to update a compute %{compute_resource} instance %{name}: %{e}") %
21
- { compute_resource:, name:, e: }, e
21
+ { compute_resource: compute_resource, name: name, e: e }, e
22
22
  end
23
23
  true
24
24
  end
@@ -41,8 +41,7 @@ module ForemanHyperv
41
41
  selected_nic = vm.select_nic(fog_nics, nic)
42
42
  if selected_nic.nil?
43
43
  logger.warn "Orchestration::Compute: Could not match network interface #{nic.inspect}"
44
- raise ArgumentError, \
45
- _("Could not find virtual machine network interface matching %s") %
44
+ raise ArgumentError, _("Could not find virtual machine network interface matching %s") %
46
45
  [nic.identifier, nic.ip, nic.name, nic.type].find(&:present?)
47
46
  end
48
47
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ForemanHyperv
2
4
  class Hyperv < ::ComputeResource
3
5
  include ComputeResourceCaching
@@ -37,7 +39,7 @@ module ForemanHyperv
37
39
  validate_connectivity(options)
38
40
  end
39
41
 
40
- def validate_connectivity(options = {})
42
+ def validate_connectivity(_options = {})
41
43
  return unless connection_properties_valid?
42
44
  return false if errors.any?
43
45
 
@@ -66,10 +68,6 @@ module ForemanHyperv
66
68
  super.merge(mac: :mac)
67
69
  end
68
70
 
69
- def editable_network_interfaces?
70
- true
71
- end
72
-
73
71
  # TODO
74
72
  def max_cpu_count(host = nil)
75
73
  (host || hypervisor).logical_processor_count
@@ -110,6 +108,10 @@ module ForemanHyperv
110
108
  )
111
109
  end
112
110
 
111
+ def associated_host(vm)
112
+ associate_by("mac", vm.network_adapters.map(&:mac))
113
+ end
114
+
113
115
  delegate :servers, to: :client
114
116
 
115
117
  def switches(host)
@@ -122,12 +124,16 @@ module ForemanHyperv
122
124
  end
123
125
 
124
126
  def new_vm(attr = {})
127
+ firmware_type = attr.delete(:firmware_type).to_s
128
+ attr.merge!(process_firmware_attributes(attr[:foreman_firmware], firmware_type)) #, attr[:provision_method]))
125
129
  attr.delete :id
130
+
131
+ # logger.debug "New VM with arguments;\n#{attr}"
126
132
  vm = super
127
133
  iface_nested_attrs = nested_attributes_for :interfaces, attr[:interfaces_attributes]
128
134
  vm.network_adapters = iface_nested_attrs.map do |attr|
129
135
  attr.delete :id
130
- Fog::Hyperv::Compute::NetworkAdapter.new(service: vm.service, vm:).tap do |nic|
136
+ Fog::Hyperv::Compute::NetworkAdapter.new(service: vm.service, vm: vm).tap do |nic|
131
137
  attr.select { |_, v| v.present? }.each do |k, v|
132
138
  nic.send(:"#{k}=", v)
133
139
  end
@@ -136,7 +142,7 @@ module ForemanHyperv
136
142
  volume_nested_attrs = nested_attributes_for :volumes, attr[:volumes_attributes]
137
143
  vm.hard_drives = volume_nested_attrs.map do |attr|
138
144
  attr.delete :id
139
- Fog::Hyperv::Compute::HardDrive.new(service: vm.service, vm:).tap do |hdd|
145
+ Fog::Hyperv::Compute::HardDrive.new(service: vm.service, vm: vm).tap do |hdd|
140
146
  attr.select { |_, v| v.present? }.each do |k, v|
141
147
  hdd.send(:"#{k}=", v)
142
148
  end
@@ -151,13 +157,17 @@ module ForemanHyperv
151
157
  attr.delete :computer_name if attr[:computer_name].blank?
152
158
  attr.delete :start
153
159
 
160
+ firmware_type = attr.delete(:firmware_type).to_s
161
+ attr.merge!(process_firmware_attributes(attr[:foreman_firmware], firmware_type)) #, attr[:provision_method]))
162
+
154
163
  validate_vm(attr, new: true)
155
164
  validate_interfaces(attr)
156
165
  validate_volumes(attr)
157
166
 
167
+ # logger.debug "Creating VM with arguments; #{attr}"
158
168
  vm = client.servers.new(
159
169
  name: attr[:name],
160
- computer_name: attr[:computer_name],
170
+ computer_name: attr[:computer_name].presence || hypervisor.name,
161
171
  generation: attr[:generation],
162
172
  dynamic_memory_enabled: Foreman::Cast.to_bool(attr[:dynamic_memory_enabled]),
163
173
  memory_startup: attr[:memory_startup].to_i,
@@ -166,13 +176,20 @@ module ForemanHyperv
166
176
  processor_count: attr[:processor_count].to_i,
167
177
  notes: attr[:notes]
168
178
  )
169
- # TODO: Allow configuring boot device?
170
- vm.create boot_device: :NetworkAdapter
179
+ vm.create(
180
+ # TODO: should be :VHD if image build - when image build support exists
181
+ boot_device: attr[:boot_device].present? ? attr[:boot_device].to_sym : :NetworkAdapter,
182
+ path: hypervisor.virtual_machine_path
183
+ )
171
184
 
172
- if vm.generation == :UEFI && attr[:secure_boot_enabled].present?
173
- f = vm.firmware
174
- f.secure_boot = Foreman::Cast.to_bool(attr[:secure_boot_enabled]) ? :On : :Off
175
- f.save if f.dirty?
185
+ if vm.generation == :UEFI
186
+ vm.secure_boot_enabled = attr[:secure_boot_enabled]
187
+ if attr[:tpm_enabled] == '1'
188
+ security = vm.security
189
+ security.change_key_protector :new
190
+ security.tpm_enabled = true
191
+ security.save
192
+ end
176
193
  end
177
194
 
178
195
  create_interfaces(vm, attr)
@@ -180,27 +197,26 @@ module ForemanHyperv
180
197
 
181
198
  vm.start if attr[:start] == '1'
182
199
  vm
183
- rescue StandardError => e
184
- if vm
200
+ rescue StandardError
201
+ if vm&.persisted?
185
202
  vm.stop
186
203
  vm.hard_drives.each { |hdd| hdd.destroy(underlying: true) }
187
- vm.destroy
204
+ vm.destroy(vm.path.end_with?(vm.name))
188
205
  end
189
206
 
190
- raise e
207
+ raise
191
208
  end
192
209
 
193
210
  def save_vm(uuid, web_attr)
194
211
  attr = web_attr.deep_symbolize_keys
212
+ attr.delete :start
195
213
 
196
214
  validate_vm(attr)
197
215
  validate_interfaces(attr)
198
216
  validate_volumes(attr)
199
217
 
200
- attr.delete :start
201
-
202
218
  vm = find_vm_by_uuid(uuid)
203
- logger.debug "Updating VM #{vm} with arguments; #{attr}"
219
+ # logger.debug "Updating VM #{vm} with arguments; #{attr}"
204
220
 
205
221
  vm.processor_count = attr[:processor_count].to_i
206
222
  vm.notes = attr[:notes].presence
@@ -209,10 +225,19 @@ module ForemanHyperv
209
225
  vm.memory_minimum = attr[:memory_minimum].to_i
210
226
  vm.memory_maximum = attr[:memory_maximum].to_i
211
227
  end
212
- if vm.generation == :UEFI && attr[:secure_boot_enabled].present?
213
- f = vm.firmware
214
- f.secure_boot = Foreman::Cast.to_bool(attr[:secure_boot_enabled]) ? :On : :Off
215
- f.save if f.dirty?
228
+ if vm.generation == :UEFI && attr[:tpm_enabled].present?
229
+ security = vm.security
230
+ security.tpm_enabled = attr[:tpm_enabled] == '1'
231
+ if security.tpm_enabled && security.key_protector.nil?
232
+ security.change_key_protector :new
233
+ end
234
+ security.save
235
+ end
236
+ if attr[:foreman_firmware].present?
237
+ firmware_type = attr.delete(:firmware_type).to_s
238
+ attr.merge!(process_firmware_attributes(attr[:foreman_firmware], firmware_type)) #, attr[:provision_method]))
239
+
240
+ vm.secure_boot_enabled = attr[:secure_boot_enabled]
216
241
  end
217
242
 
218
243
  update_interfaces(vm, attr)
@@ -227,35 +252,36 @@ module ForemanHyperv
227
252
  vm.stop turn_off: true
228
253
  vm.hard_drives.each { |hdd| hdd.destroy(underlying: true) }
229
254
  # TODO: Remove the empty VM folder
230
- vm.destroy
255
+ vm.destroy(vm.path.end_with?(vm.name))
231
256
  rescue ActiveRecord::RecordNotFound, Fog::Errors::NotFound
232
257
  # if the VM does not exists, we don't really care.
233
258
  true
234
259
  end
235
260
 
236
261
  def update_required?(old_attrs, new_attrs)
237
- new_attrs.deep_symbolize_keys[:volumes_attributes]&.each do |_, hdd|
262
+ new_attrs.deep_symbolize_keys[:volumes_attributes]&.each_value do |hdd|
238
263
  if hdd[:id].present? && hdd[:_delete] == '1'
239
- Rails.logger.debug "Scheduling compute instance update because a volume was removed"
264
+ Rails.logger.debug 'Scheduling compute instance update because a volume was removed'
240
265
  return true
241
- elsif !hdd[:id].present? && hdd[:_delete] != '1'
242
- Rails.logger.debug "Scheduling compute instance update because a new volume was added"
266
+ elsif hdd[:id].blank? && hdd[:_delete] != '1'
267
+ Rails.logger.debug 'Scheduling compute instance update because a new volume was added'
243
268
  return true
244
269
  end
245
270
  end
246
- new_attrs.deep_symbolize_keys[:interfaces_attributes]&.each do |_, iface|
271
+ new_attrs.deep_symbolize_keys[:interfaces_attributes]&.each_value do |iface|
247
272
  if iface[:id].present? && iface[:_destroy] == '1'
248
- Rails.logger.debug "Scheduling compute instance update because an interface was removed"
273
+ Rails.logger.debug 'Scheduling compute instance update because an interface was removed'
249
274
  return true
250
- elsif !iface[:id].present? && iface[:_destroy] != '1'
251
- Rails.logger.debug "Scheduling compute instance update because a new interface was added"
275
+ elsif iface[:id].blank? && iface[:_destroy] != '1'
276
+ Rails.logger.debug 'Scheduling compute instance update because a new interface was added'
252
277
  return true
253
278
  end
254
279
  end
255
280
 
256
- deep_update_required = proc do |old, new|
281
+ deep_update_required = proc do |path, old, new|
257
282
  old.merge(new) do |k, old_v, new_v|
258
- if k == :allowed_vlan_ids || k == :secondary_vlan_ids
283
+ k_path = path + [k]
284
+ if %i[allowed_vlan_ids secondary_vlan_ids].include?(k)
259
285
  tmp = Fog::Hyperv::Compute::NetworkAdapter.new
260
286
 
261
287
  old_v = Fog::Hyperv::Compute::NetworkAdapterVlan.render_vlan_list(tmp.send(:parse_vlan_list, old_v.to_s))
@@ -263,15 +289,17 @@ module ForemanHyperv
263
289
  end
264
290
 
265
291
  if old_v.is_a?(Hash) && new_v.is_a?(Hash)
266
- deep_update_required.call(old_v, new_v)
292
+ deep_update_required.call(k_path, old_v, new_v)
267
293
  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})"
294
+ Rails.logger.debug do
295
+ "Scheduling compute instance update because #{k_path.join('.')} changed it's value from '#{old_v}' (#{old_v.class}) to '#{new_v}' (#{new_v.class})"
296
+ end
269
297
  return true
270
298
  end
271
299
  new_v
272
300
  end
273
301
  end
274
- deep_update_required.call(old_attrs.deep_symbolize_keys, new_attrs.deep_symbolize_keys)
302
+ deep_update_required.call([], old_attrs.deep_symbolize_keys, new_attrs.deep_symbolize_keys)
275
303
 
276
304
  false
277
305
  end
@@ -293,8 +321,8 @@ module ForemanHyperv
293
321
  basename = attr.delete(:basename) { 'Disk' }
294
322
  size = attr.delete(:size)
295
323
 
296
- vhd = client.vhds.new({ basename:, size: }.compact)
297
- Fog::Hyperv::Compute::HardDrive.new vhd:, **attr
324
+ vhd = client.vhds.new({ basename: basename, size: size }.compact)
325
+ Fog::Hyperv::Compute::HardDrive.new vhd: vhd, **attr
298
326
  end
299
327
 
300
328
  def new_cdrom(attr = {})
@@ -303,13 +331,26 @@ module ForemanHyperv
303
331
 
304
332
  def vm_instance_defaults
305
333
  super.merge(
306
- generation: 2,
307
- memory_startup: 1024.megabytes,
334
+ generation: 2,
335
+ memory_startup: 1024.megabytes,
308
336
  processor_count: 1,
309
- interfaces: [new_interface]
337
+ interfaces: [new_interface]
310
338
  )
311
339
  end
312
340
 
341
+ def vm_compute_attributes(vm)
342
+ attr = super.slice(
343
+ :computer_name, :name, :generation,
344
+ :dynamic_memory_enabled,
345
+ :memory_maximum, :memory_minimum, :memory_startup,
346
+ :notes, :processor_count
347
+ )
348
+ attr[:foreman_firmware] = vm.foreman_firmware_type
349
+ attr[:tpm_enabled] = vm.tpm_enabled
350
+
351
+ attr
352
+ end
353
+
313
354
  def set_vm_volumes_attributes(vm, vm_attrs)
314
355
  volumes = vm.hard_drives || []
315
356
  vm_attrs[:volumes_attributes] = volumes.each_with_index.to_h do |volume, index|
@@ -344,25 +385,30 @@ module ForemanHyperv
344
385
  private
345
386
 
346
387
  def _clusters
347
- if client.respond_to? :supports_clusters?
348
- return [] unless client.supports_clusters?
349
- end
388
+ return [] if client.respond_to?(:supports_clusters?) && !client.supports_clusters?
350
389
 
351
390
  client.clusters.all
352
- rescue
391
+ rescue StandardError
353
392
  []
354
393
  end
355
394
 
356
395
  def validate_vm(attr, new: false)
357
396
  # 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
397
+ raise Foreman::Exception, 'VM lacks generation' if new && attr[:generation].blank?
398
+ raise Foreman::Exception, 'VM lacks memory' unless attr[:memory_startup].to_i.positive?
399
+ raise Foreman::Exception, 'VM lacks CPUs' unless attr[:processor_count].to_i.positive?
361
400
 
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
401
+ return unless Foreman::Cast.to_bool(attr[:dynamic_memory_enabled])
402
+ raise Foreman::Exception, 'VM lacks memory minimum' unless attr[:memory_minimum].to_i.positive?
403
+ raise Foreman::Exception, 'VM lacks memory maximum' unless attr[:memory_maximum].to_i.positive?
404
+ end
405
+
406
+ def generate_secure_boot_settings(firmware)
407
+ return {} unless firmware.to_s.include?('efi')
408
+
409
+ {
410
+ secure_boot_enabled: firmware.to_s.end_with?('secure_boot')
411
+ }
366
412
  end
367
413
 
368
414
  def validate_interfaces(attr)
@@ -373,18 +419,26 @@ module ForemanHyperv
373
419
  compute = iface[:compute_attributes] || iface
374
420
  case compute[:vlan_operation_mode].to_s
375
421
  when 'Untagged'
422
+ # No VLAN settings to verify
376
423
  when 'Access'
377
- raise Foreman::Exception, 'Interface is missing access VLAN' unless compute[:access_vlan_id].to_i > 0
424
+ raise Foreman::Exception, 'Interface is missing access VLAN' unless compute[:access_vlan_id].to_i.positive?
378
425
  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?
426
+ raise Foreman::Exception, 'Interface is missing native VLAN' unless compute[:native_vlan_id].to_i.positive?
427
+ raise Foreman::Exception, 'Interface is missing allowed VLANs' if compute[:allowed_vlan_ids].blank?
381
428
  when 'Private'
382
- raise Foreman::Exception, 'Interface is missing primary VLAN' unless compute[:primary_vlan_id].to_i > 0
429
+ raise Foreman::Exception, 'Interface is missing primary VLAN' unless compute[:primary_vlan_id].to_i.positive?
430
+
383
431
  case compute[:vlan_private_mode].to_s
384
432
  when 'Promiscuous'
385
- raise Foreman::Exception, 'Interface is missing secondary VLANs' unless compute[:secondary_vlan_ids].present?
433
+ if compute[:secondary_vlan_ids].blank?
434
+ raise Foreman::Exception,
435
+ 'Interface is missing secondary VLANs'
436
+ end
386
437
  else
387
- raise Foreman::Exception, 'Interface is missing secondary VLAN' unless compute[:secondary_vlan_id].to_i > 0
438
+ unless compute[:secondary_vlan_id].to_i.positive?
439
+ raise Foreman::Exception,
440
+ 'Interface is missing secondary VLAN'
441
+ end
388
442
  end
389
443
  else
390
444
  raise Foreman::Exception, 'Interface has unknown VLAN mode'
@@ -411,7 +465,9 @@ module ForemanHyperv
411
465
  nic.save
412
466
  end
413
467
 
414
- return unless vm.network_adapters.all(_return_fields: %i[dynamic_mac_address_enabled]).any?(&:dynamic_mac_address_enabled)
468
+ unless vm.network_adapters.all(_return_fields: %i[dynamic_mac_address_enabled]).any?(&:dynamic_mac_address_enabled)
469
+ return
470
+ end
415
471
 
416
472
  # Populate all non-populated MAC addresses
417
473
  vm.start
@@ -456,11 +512,13 @@ module ForemanHyperv
456
512
  volumes.reject! { |vol| vol[:_delete] == '1' }
457
513
  # logger.debug "Validate Volume #{volumes.inspect}"
458
514
  volumes.each do |vol|
459
- raise Foreman::Exception, 'Volume lacks name' unless vol[:basename].present?
515
+ raise Foreman::Exception, 'Volume lacks name' if vol[:basename].blank?
460
516
  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
517
+ raise Foreman::Exception, 'Volume lacks size' unless vol[:size_bytes].to_i.positive?
462
518
  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 }
519
+ return unless volumes.group_by { |vol| vol[:basename].downcase }.any? { |_k, v| v.many? }
520
+
521
+ raise Foreman::Exception, 'Volume names need to be unique'
464
522
  end
465
523
 
466
524
  def create_volumes(vm, attr)
@@ -468,7 +526,7 @@ module ForemanHyperv
468
526
  logger.debug "Creating volumes with: #{volumes}"
469
527
  volumes.each do |vol|
470
528
  vhd = vm.vhds.create basename: vol[:basename], size: vol[:size_bytes].to_i
471
- vm.hard_drives.create vhd:
529
+ vm.hard_drives.create vhd: vhd
472
530
  end
473
531
  end
474
532
 
@@ -14,34 +14,12 @@
14
14
  cluster_errors << ex
15
15
  end
16
16
  -%>
17
- <% if clusters.any? %>
17
+ <% if clusters.any? -%>
18
18
  <%= select_f f, :cluster_name, clusters, :to_s, :to_s, { include_blank: true }, label: _('Cluster'), disabled: !new_host %>
19
- <% if computers.count > 1 %>
19
+ <% if computers.count > 1 -%>
20
20
  <%= select_f f, :computer_name, computers, :name, :name, {}, label: _('Computer Name'), disabled: !new_host, onload: 'hypervHostChange(this);', onchange: 'hypervHostChange(this);' %>
21
21
  <% end -%>
22
- <% end %>
23
- <div class="clearfix">
24
- <div class="form-group">
25
- <label class="col-md-2 control-label">Available resources</label>
26
- <div class="col-md-4 help-block" id="hypervComputerInformation">
27
- <% computers.each do |host| -%>
28
- <table class="hyperv-host-info" style="<%= (host.name == f.object.computer_name) ? '' : 'display: none' %>" data-host="<%= host.name %>">
29
- <tbody>
30
- <tr>
31
- <td>CPU:</td>
32
- <td style="padding-left:0.5em;"><%= host.logical_processor_count %></td>
33
- </tr>
34
- <tr>
35
- <td>Memory:</td>
36
- <td style="padding-left:0.5em;"><%= number_to_human_size(host.memory_capacity) %></td>
37
- </tr>
38
- </tbody>
39
- </table>
40
22
  <% end -%>
41
- </div>
42
- </div>
43
- </div>
44
-
45
23
  <% cluster_errors.each do |err| -%>
46
24
  <div class="alert alert-warning alert-dismissable">
47
25
  <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
@@ -49,10 +27,22 @@
49
27
  </div>
50
28
  <% end -%>
51
29
 
52
- <%= select_f f, :generation, hyperv_generations, :first, :last, {}, label: 'Firmware', disabled: !new_host, onchange: 'hypervGenerationChange(this);' %>
53
- <% if new_host || f.object.generation == :UEFI -%>
54
- <%= checkbox_f f, :secure_boot_enabled, { label: _('Enable Secure Boot'), disabled: f.object.generation == :BIOS }, 'true', 'false' %>
55
- <% end -%>
30
+ <%
31
+ firmware_type = compute_resource.firmware_type(f.object.foreman_firmware, f.object.secure_boot_enabled)
32
+ -%>
33
+ <%= field(f, :foreman_firmware, :label => _('Firmware'), :label_help => _("Choose 'Automatic' to set the firmware based on the PXE loader of the host. If no PXE loader is selected, firmware defaults to BIOS."), :label_size => "col-md-2") do
34
+ compute_resource.firmware_types.collect do |type, name|
35
+ enabled = new_vm
36
+ enabled ||= firmware_type != :bios && type.to_s.start_with?('uefi')
37
+
38
+ radio_button_f f, :foreman_firmware, { :checked => (firmware_type == type), :disabled => !enabled, :value => type, :text => _(name) }
39
+ end.join(' ').html_safe
40
+ end %>
41
+ <%= checkbox_f f, :tpm_enabled, { help_inline: _('Add Virtual TPM module to the VM.'),
42
+ label: _('Virtual TPM'),
43
+ label_help: _('Only compatible with UEFI firmware.'),
44
+ label_size: 'col-md-2' } %>
45
+ <%= select_f f, :boot_device, hyperv_boot_devices, :first, :last, {}, label: _('Boot device'), label_help: _('Has effect only for network based provisioning'), class: 'col-md-2' if new_vm %>
56
46
 
57
47
  <%= counter_f f, :processor_count, label: _('CPUs'), label_size: 'col-md-2' %>
58
48
  <%= byte_size_f f, :memory_startup, label: _('Memory (Startup)'), label_size: 'col-md-2' %>
@@ -61,7 +51,7 @@
61
51
  <%= byte_size_f f, :memory_maximum, class: 'col-md-2', label: _('Memory Maximum'), disabled: !f.object.dynamic_memory_enabled %>
62
52
  <%= byte_size_f f, :memory_minimum, class: 'col-md-2', label: _('Memory Minimum'), disabled: !f.object.dynamic_memory_enabled %>
63
53
 
64
- <% checked = params[:host] && params[:host][:compute_attributes] && params[:host][:compute_attributes][:start] || '1' %>
54
+ <% checked = params[:host] && params[:host][:compute_attributes] && params[:host][:compute_attributes][:start] || '1' -%>
65
55
  <%= checkbox_f f, :start, { checked: (checked == '1'), help_inline: _("Power ON this machine"), label: _('Start'), label_size: "col-md-2"} if new_host && controller_name != "compute_attributes" %>
66
56
 
67
57
  <%= textarea_f f, :notes, rows: '3', label: _('Notes') %>
@@ -9,26 +9,26 @@
9
9
 
10
10
  <%# If Access mode %>
11
11
  <div data-hyperv-vlan-mode="access" class="<%= 'hide' unless f.object.vlan_operation_mode.to_s == 'Access' %>">
12
- <%= number_f f, :access_vlan_id, label: _('Access VLAN'), label_size: 'col-md-3', size: 'col-md-8', required: true, disabled: (f.object.vlan_operation_mode.to_s != 'Access') %>
12
+ <%= number_f f, :access_vlan_id, label: _('Access VLAN'), label_size: 'col-md-3', size: 'col-md-8', required: true, disabled: (f.object.vlan_operation_mode.to_s != 'Access') %>
13
13
  </div>
14
14
  <%# If Trunk mode %>
15
15
  <div data-hyperv-vlan-mode="trunk" class="<%= 'hide' unless f.object.vlan_operation_mode.to_s == 'Trunk' %>">
16
- <%= number_f f, :native_vlan_id, label: _('Native VLAN'), label_size: 'col-md-3', size: 'col-md-8', required: true, disabled: (f.object.vlan_operation_mode.to_s != 'Trunk') %>
17
- <%= text_f f, :allowed_vlan_ids, placeholder: '10,20-40,50', label: _('Allowed VLANs'), label_size: 'col-md-3', size: 'col-md-8', required: true, disabled: (f.object.vlan_operation_mode.to_s != 'Trunk') %>
16
+ <%= number_f f, :native_vlan_id, label: _('Native VLAN'), label_size: 'col-md-3', size: 'col-md-8', required: true, disabled: (f.object.vlan_operation_mode.to_s != 'Trunk') %>
17
+ <%= text_f f, :allowed_vlan_ids, placeholder: '10,20-40,50', label: _('Allowed VLANs'), label_size: 'col-md-3', size: 'col-md-8', required: true, disabled: (f.object.vlan_operation_mode.to_s != 'Trunk') %>
18
18
  </div>
19
19
  <%# If Private mode %>
20
20
  <div data-hyperv-vlan-mode="private" class="<%= 'hide' unless f.object.vlan_operation_mode.to_s == 'Private' %>">
21
- <%= select_f f, :vlan_private_mode, hyperv_private_vlan_modes, :first, :last, {},
22
- label: _('VLAN private mode'), label_size: 'col-md-3', size: 'col-md-8',
23
- onchange: 'hypervVLANPrivateModeChange(this);',
24
- disabled: (f.object.vlan_operation_mode.to_s != 'Private') %>
25
- <%= number_f f, :primary_vlan_id, label: _('Primary VLAN'), label_size: 'col-md-3', size: 'col-md-8', required: true, disabled: (f.object.vlan_operation_mode.to_s != 'Private') %>
26
- <%# If Isolated or Community private mode %>
27
- <div data-hyperv-vlan-private-mode="singular" class="<%= 'hide' if f.object.vlan_private_mode.to_s == 'Promiscuous' %>">
28
- <%= number_f f, :secondary_vlan_id, label: _('Secondary VLAN'), label_size: 'col-md-3', size: 'col-md-8', required: true, disabled: (f.object.vlan_operation_mode.to_s != 'Private' || f.object.vlan_private_mode.to_s == 'Promiscuous') %>
29
- </div>
30
- <%# If Promiscuous private mode %>
31
- <div data-hyperv-vlan-private-mode="plural" class="<%= 'hide' unless f.object.vlan_private_mode.to_s == 'Promiscuous' %>">
32
- <%= text_f f, :secondary_vlan_ids, placeholder: '10,20-40,50', label: _('Secondary VLANs'), label_size: 'col-md-3', size: 'col-md-8', required: true, disabled: (f.object.vlan_operation_mode.to_s != 'Private' || f.object.vlan_private_mode.to_s != 'Promiscuous') %>
33
- </div>
21
+ <%= select_f f, :vlan_private_mode, hyperv_private_vlan_modes, :first, :last, {},
22
+ label: _('VLAN private mode'), label_size: 'col-md-3', size: 'col-md-8',
23
+ onchange: 'hypervVLANPrivateModeChange(this);',
24
+ disabled: (f.object.vlan_operation_mode.to_s != 'Private') %>
25
+ <%= number_f f, :primary_vlan_id, label: _('Primary VLAN'), label_size: 'col-md-3', size: 'col-md-8', required: true, disabled: (f.object.vlan_operation_mode.to_s != 'Private') %>
26
+ <%# If Isolated or Community private mode %>
27
+ <div data-hyperv-vlan-private-mode="singular" class="<%= 'hide' if f.object.vlan_private_mode.to_s == 'Promiscuous' %>">
28
+ <%= number_f f, :secondary_vlan_id, label: _('Secondary VLAN'), label_size: 'col-md-3', size: 'col-md-8', required: true, disabled: (f.object.vlan_operation_mode.to_s != 'Private' || f.object.vlan_private_mode.to_s == 'Promiscuous') %>
29
+ </div>
30
+ <%# If Promiscuous private mode %>
31
+ <div data-hyperv-vlan-private-mode="plural" class="<%= 'hide' unless f.object.vlan_private_mode.to_s == 'Promiscuous' %>">
32
+ <%= text_f f, :secondary_vlan_ids, placeholder: '10,20-40,50', label: _('Secondary VLANs'), label_size: 'col-md-3', size: 'col-md-8', required: true, disabled: (f.object.vlan_operation_mode.to_s != 'Private' || f.object.vlan_private_mode.to_s != 'Promiscuous') %>
33
+ </div>
34
34
  </div>
@@ -4,5 +4,5 @@
4
4
  <%= f.hidden_field :id %>
5
5
  <%= f.hidden_field :basename %>
6
6
  <% end -%>
7
- <%= text_f f, :basename, label: _('Name'), help_inline: _('This is the VHD filename on disk, without extension'), required: true, disabled: f.object.persisted? %>
7
+ <%= text_f f, :basename, label: _('Name'), label_help: _('This is the VHD filename on disk, without extension'), required: true, disabled: f.object.persisted? %>
8
8
  <%= byte_size_f f, :size_bytes, label: _('Size') %>
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ForemanHyperv
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman_hyperv
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Olofsson
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: '0.1'
18
+ version: '0.2'
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: '0.1'
25
+ version: '0.2'
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: bundler
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -51,6 +51,34 @@ dependencies:
51
51
  - - ">="
52
52
  - !ruby/object:Gem::Version
53
53
  version: '0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: rubocop
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: rubocop-rails
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
54
82
  - !ruby/object:Gem::Dependency
55
83
  name: minitest
56
84
  requirement: !ruby/object:Gem::Requirement
@@ -105,7 +133,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
105
133
  requirements:
106
134
  - - ">="
107
135
  - !ruby/object:Gem::Version
108
- version: '0'
136
+ version: '3.0'
109
137
  required_rubygems_version: !ruby/object:Gem::Requirement
110
138
  requirements:
111
139
  - - ">="