Class: ForemanHyperv::Hyperv
- Inherits:
-
ComputeResource
- Object
- ComputeResource
- ForemanHyperv::Hyperv
- Includes:
- ComputeResourceCaching
- Defined in:
- app/models/foreman_hyperv/hyperv.rb
Class Method Summary collapse
Instance Method Summary collapse
- #associated_host(vm) ⇒ Object
- #available_hypervisors ⇒ Object (also: #hosts)
- #caching_enabled ⇒ Object
- #capabilities ⇒ Object
- #cluster(name) ⇒ Object
- #clusters ⇒ Object
- #connection_properties_valid? ⇒ Boolean
- #connection_valid? ⇒ Boolean
- #create_vm(attr = {}) ⇒ Object
- #destroy_vm(uuid) ⇒ Object
- #editable_network_interfaces? ⇒ Boolean
- #hypervisor ⇒ Object
-
#max_cpu_count(host = nil) ⇒ Object
TODO.
- #max_memory(host = nil) ⇒ Object
- #new_cdrom(attr = {}) ⇒ Object
-
#new_interface(attr = {}) ⇒ Object
def console(uuid) vm = find_vm_by_uuid(uuid).
- #new_vm(attr = {}) ⇒ Object
- #new_volume(attr = {}) ⇒ Object
- #provided_attributes ⇒ Object
- #save_vm(uuid, web_attr) ⇒ Object
- #set_vm_interfaces_attributes(vm, vm_attrs) ⇒ Object
- #set_vm_volumes_attributes(vm, vm_attrs) ⇒ Object
- #supports_update? ⇒ Boolean
- #switches(host) ⇒ Object
- #test_connection(options = {}) ⇒ Object
- #to_label ⇒ Object
- #update_required?(old_attrs, new_attrs) ⇒ Boolean
- #validate_connectivity(_options = {}) ⇒ Object
- #vm_compute_attributes(vm) ⇒ Object
- #vm_instance_defaults ⇒ Object
Class Method Details
.available? ⇒ Boolean
18 19 20 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 18 def self.available? Fog::Compute.providers.include?(:hyperv) end |
.model_name ⇒ Object
22 23 24 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 22 def self.model_name ComputeResource.model_name end |
.provider_friendly_name ⇒ Object
10 11 12 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 10 def self.provider_friendly_name 'Hyper-V' end |
Instance Method Details
#associated_host(vm) ⇒ Object
111 112 113 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 111 def associated_host(vm) associate_by("mac", vm.network_adapters.map(&:mac)) end |
#available_hypervisors ⇒ Object Also known as: hosts
80 81 82 83 84 85 86 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 80 def available_hypervisors client.hosts.load( cache.cache(:available_hypervisor) do client.hosts.all.map(&:attributes) end ) end |
#caching_enabled ⇒ Object
63 64 65 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 63 def caching_enabled true end |
#capabilities ⇒ Object
34 35 36 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 34 def capabilities [:build] end |
#cluster(name) ⇒ Object
89 90 91 92 93 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 89 def cluster(name) return nil unless name client.clusters.get name end |
#clusters ⇒ Object
95 96 97 98 99 100 101 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 95 def clusters client.clusters.load( cache.cache(:clusters) do _clusters.map(&:attributes) end ) end |
#connection_properties_valid? ⇒ Boolean
59 60 61 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 59 def connection_properties_valid? errors[:url].empty? && errors[:user].empty? && errors[:password].empty? end |
#connection_valid? ⇒ Boolean
51 52 53 54 55 56 57 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 51 def connection_valid? return false if url.blank? || user.blank? || password.blank? client&.valid? rescue StandardError false end |
#create_vm(attr = {}) ⇒ Object
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 155 def create_vm(attr = {}) attr = vm_instance_defaults.merge(attr.to_hash.deep_symbolize_keys) attr.delete :computer_name if attr[:computer_name].blank? attr.delete :start firmware_type = attr.delete(:firmware_type).to_s attr.merge!(process_firmware_attributes(attr[:foreman_firmware], firmware_type)) #, attr[:provision_method])) validate_vm(attr, new: true) validate_interfaces(attr) validate_volumes(attr) # logger.debug "Creating VM with arguments; #{attr}" vm = client.servers.new( name: attr[:name], computer_name: attr[:computer_name].presence || hypervisor.name, generation: attr[:generation], dynamic_memory_enabled: Foreman::Cast.to_bool(attr[:dynamic_memory_enabled]), memory_startup: attr[:memory_startup].to_i, memory_minimum: attr[:memory_minimum].to_i, memory_maximum: attr[:memory_maximum].to_i, processor_count: attr[:processor_count].to_i, notes: attr[:notes] ) vm.create( # TODO: should be :VHD if image build - when image build support exists boot_device: attr[:boot_device].present? ? attr[:boot_device].to_sym : :NetworkAdapter, path: hypervisor.virtual_machine_path ) if vm.generation == :UEFI vm.secure_boot_enabled = attr[:secure_boot_enabled] if attr[:tpm_enabled] == '1' security = vm.security security.change_key_protector :new security.tpm_enabled = true security.save end end create_interfaces(vm, attr) create_volumes(vm, attr) vm.start if attr[:start] == '1' vm rescue StandardError if vm&.persisted? vm.stop vm.hard_drives.each { |hdd| hdd.destroy(underlying: true) } vm.destroy(vm.path.end_with?(vm.name)) end raise end |
#destroy_vm(uuid) ⇒ Object
250 251 252 253 254 255 256 257 258 259 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 250 def destroy_vm(uuid) vm = find_vm_by_uuid(uuid) vm.stop turn_off: true vm.hard_drives.each { |hdd| hdd.destroy(underlying: true) } # TODO: Remove the empty VM folder vm.destroy(vm.path.end_with?(vm.name)) rescue ActiveRecord::RecordNotFound, Fog::Errors::NotFound # if the VM does not exists, we don't really care. true end |
#editable_network_interfaces? ⇒ Boolean
30 31 32 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 30 def editable_network_interfaces? true end |
#hypervisor ⇒ Object
103 104 105 106 107 108 109 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 103 def hypervisor client.hosts.new( cache.cache(:hypervisor) do client.hosts.first.attributes end ) end |
#max_cpu_count(host = nil) ⇒ Object
TODO
72 73 74 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 72 def max_cpu_count(host = nil) (host || hypervisor).logical_processor_count end |
#max_memory(host = nil) ⇒ Object
76 77 78 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 76 def max_memory(host = nil) (host || hypervisor).memory_capacity end |
#new_cdrom(attr = {}) ⇒ Object
328 329 330 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 328 def new_cdrom(attr = {}) Fog::Hyperv::Compute::DvdDrive.new attr end |
#new_interface(attr = {}) ⇒ Object
def console(uuid)
vm = find_vm_by_uuid(uuid)
{
type: 'rdp',
host: vm.computer.fully_qualified_domain_name,
}
end
316 317 318 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 316 def new_interface(attr = {}) Fog::Hyperv::Compute::NetworkAdapter.new attr end |
#new_vm(attr = {}) ⇒ Object
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 126 def new_vm(attr = {}) firmware_type = attr.delete(:firmware_type).to_s attr.merge!(process_firmware_attributes(attr[:foreman_firmware], firmware_type)) #, attr[:provision_method])) attr.delete :id # logger.debug "New VM with arguments;\n#{attr}" vm = super iface_nested_attrs = nested_attributes_for :interfaces, attr[:interfaces_attributes] vm.network_adapters = iface_nested_attrs.map do |attr| attr.delete :id Fog::Hyperv::Compute::NetworkAdapter.new(service: vm.service, vm: vm).tap do |nic| attr.select { |_, v| v.present? }.each do |k, v| nic.send(:"#{k}=", v) end end end volume_nested_attrs = nested_attributes_for :volumes, attr[:volumes_attributes] vm.hard_drives = volume_nested_attrs.map do |attr| attr.delete :id Fog::Hyperv::Compute::HardDrive.new(service: vm.service, vm: vm).tap do |hdd| attr.select { |_, v| v.present? }.each do |k, v| hdd.send(:"#{k}=", v) end end end vm.id = nil vm end |
#new_volume(attr = {}) ⇒ Object
320 321 322 323 324 325 326 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 320 def new_volume(attr = {}) basename = attr.delete(:basename) { 'Disk' } size = attr.delete(:size) vhd = client.vhds.new({ basename: basename, size: size }.compact) Fog::Hyperv::Compute::HardDrive.new vhd: vhd, **attr end |
#provided_attributes ⇒ Object
67 68 69 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 67 def provided_attributes super.merge(mac: :mac) end |
#save_vm(uuid, web_attr) ⇒ Object
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 210 def save_vm(uuid, web_attr) attr = web_attr.deep_symbolize_keys attr.delete :start validate_vm(attr) validate_interfaces(attr) validate_volumes(attr) vm = find_vm_by_uuid(uuid) # logger.debug "Updating VM #{vm} with arguments; #{attr}" vm.processor_count = attr[:processor_count].to_i vm.notes = attr[:notes].presence vm.dynamic_memory_enabled = Foreman::Cast.to_bool(attr[:dynamic_memory_enabled]) if vm.dynamic_memory_enabled vm.memory_minimum = attr[:memory_minimum].to_i vm.memory_maximum = attr[:memory_maximum].to_i end if vm.generation == :UEFI && attr[:tpm_enabled].present? security = vm.security security.tpm_enabled = attr[:tpm_enabled] == '1' if security.tpm_enabled && security.key_protector.nil? security.change_key_protector :new end security.save end if attr[:foreman_firmware].present? firmware_type = attr.delete(:firmware_type).to_s attr.merge!(process_firmware_attributes(attr[:foreman_firmware], firmware_type)) #, attr[:provision_method])) vm.secure_boot_enabled = attr[:secure_boot_enabled] end update_interfaces(vm, attr) update_volumes(vm, attr) vm.save if vm.dirty? vm end |
#set_vm_interfaces_attributes(vm, vm_attrs) ⇒ Object
362 363 364 365 366 367 368 369 370 371 372 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 362 def set_vm_interfaces_attributes(vm, vm_attrs) interfaces = vm.interfaces || [] vm_attrs[:interfaces_attributes] = interfaces.each_with_index.to_h do |interface, index| interface_attrs = { mac: interface.mac, compute_attributes: interface.compute_attributes } [index.to_s, interface_attrs] end vm_attrs end |
#set_vm_volumes_attributes(vm, vm_attrs) ⇒ Object
354 355 356 357 358 359 360 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 354 def set_vm_volumes_attributes(vm, vm_attrs) volumes = vm.hard_drives || [] vm_attrs[:volumes_attributes] = volumes.each_with_index.to_h do |volume, index| [index.to_s, volume.compute_attributes] end vm_attrs end |
#supports_update? ⇒ Boolean
26 27 28 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 26 def supports_update? true end |
#switches(host) ⇒ Object
117 118 119 120 121 122 123 124 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 117 def switches(host) host ||= hosts.first client.switches.load( cache.cache(:"#{host.name}-switches") do host.switches.all.map(&:attributes) end ) end |
#test_connection(options = {}) ⇒ Object
38 39 40 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 38 def test_connection( = {}) validate_connectivity() end |
#to_label ⇒ Object
14 15 16 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 14 def to_label "#{name} (#{provider_friendly_name})" end |
#update_required?(old_attrs, new_attrs) ⇒ Boolean
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 261 def update_required?(old_attrs, new_attrs) new_attrs.deep_symbolize_keys[:volumes_attributes]&.each_value do |hdd| if hdd[:id].present? && hdd[:_delete] == '1' Rails.logger.debug 'Scheduling compute instance update because a volume was removed' return true elsif hdd[:id].blank? && hdd[:_delete] != '1' Rails.logger.debug 'Scheduling compute instance update because a new volume was added' return true end end new_attrs.deep_symbolize_keys[:interfaces_attributes]&.each_value do |iface| if iface[:id].present? && iface[:_destroy] == '1' Rails.logger.debug 'Scheduling compute instance update because an interface was removed' return true elsif iface[:id].blank? && iface[:_destroy] != '1' Rails.logger.debug 'Scheduling compute instance update because a new interface was added' return true end end deep_update_required = proc do |path, old, new| old.merge(new) do |k, old_v, new_v| k_path = path + [k] if %i[allowed_vlan_ids secondary_vlan_ids].include?(k) tmp = Fog::Hyperv::Compute::NetworkAdapter.new old_v = Fog::Hyperv::Compute::NetworkAdapterVlan.render_vlan_list(tmp.send(:parse_vlan_list, old_v.to_s)) new_v = Fog::Hyperv::Compute::NetworkAdapterVlan.render_vlan_list(tmp.send(:parse_vlan_list, new_v.to_s)) end if old_v.is_a?(Hash) && new_v.is_a?(Hash) deep_update_required.call(k_path, old_v, new_v) elsif old_v.to_s != new_v.to_s Rails.logger.debug do "Scheduling compute instance update because #{k_path.join('.')} changed it's value from '#{old_v}' (#{old_v.class}) to '#{new_v}' (#{new_v.class})" end return true end new_v end end deep_update_required.call([], old_attrs.deep_symbolize_keys, new_attrs.deep_symbolize_keys) false end |
#validate_connectivity(_options = {}) ⇒ Object
42 43 44 45 46 47 48 49 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 42 def validate_connectivity( = {}) return unless connection_properties_valid? return false if errors.any? client.valid? rescue Fog::Hyperv::Errors::ServiceError, ArgumentError, WinRM::WinRMAuthorizationError => e errors.add(:base, e.) end |
#vm_compute_attributes(vm) ⇒ Object
341 342 343 344 345 346 347 348 349 350 351 352 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 341 def vm_compute_attributes(vm) attr = super.slice( :computer_name, :name, :generation, :dynamic_memory_enabled, :memory_maximum, :memory_minimum, :memory_startup, :notes, :processor_count ) attr[:foreman_firmware] = vm.foreman_firmware_type attr[:tpm_enabled] = vm.tpm_enabled attr end |
#vm_instance_defaults ⇒ Object
332 333 334 335 336 337 338 339 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 332 def vm_instance_defaults super.merge( generation: 2, memory_startup: 1024.megabytes, processor_count: 1, interfaces: [new_interface] ) end |