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
- #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_instance_defaults ⇒ Object
Class Method Details
.available? ⇒ Boolean
16 17 18 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 16 def self.available? Fog::Compute.providers.include?(:hyperv) end |
.model_name ⇒ Object
20 21 22 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 20 def self.model_name ComputeResource.model_name end |
.provider_friendly_name ⇒ Object
8 9 10 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 8 def self.provider_friendly_name 'Hyper-V' end |
Instance Method Details
#available_hypervisors ⇒ Object Also known as: hosts
82 83 84 85 86 87 88 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 82 def available_hypervisors client.hosts.load( cache.cache(:available_hypervisor) do client.hosts.all.map(&:attributes) end ) end |
#caching_enabled ⇒ Object
61 62 63 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 61 def caching_enabled true end |
#capabilities ⇒ Object
32 33 34 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 32 def capabilities [:build] end |
#cluster(name) ⇒ Object
91 92 93 94 95 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 91 def cluster(name) return nil unless name client.clusters.get name end |
#clusters ⇒ Object
97 98 99 100 101 102 103 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 97 def clusters client.clusters.load( cache.cache(:clusters) do _clusters.map(&:attributes) end ) end |
#connection_properties_valid? ⇒ Boolean
57 58 59 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 57 def connection_properties_valid? errors[:url].empty? && errors[:user].empty? && errors[:password].empty? end |
#connection_valid? ⇒ Boolean
49 50 51 52 53 54 55 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 49 def connection_valid? return false if url.blank? || user.blank? || password.blank? client&.valid? rescue StandardError false end |
#create_vm(attr = {}) ⇒ Object
149 150 151 152 153 154 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 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 149 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 validate_vm(attr, new: true) validate_interfaces(attr) validate_volumes(attr) vm = client.servers.new( name: attr[:name], computer_name: attr[:computer_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] ) # TODO: Allow configuring boot device? vm.create boot_device: :NetworkAdapter if vm.generation == :UEFI && attr[:secure_boot_enabled].present? f = vm.firmware f.secure_boot = Foreman::Cast.to_bool(attr[:secure_boot_enabled]) ? :On : :Off f.save if f.dirty? end create_interfaces(vm, attr) create_volumes(vm, attr) vm.start if attr[:start] == '1' vm rescue StandardError => e if vm vm.stop vm.hard_drives.each { |hdd| hdd.destroy(underlying: true) } vm.destroy end raise e end |
#destroy_vm(uuid) ⇒ Object
225 226 227 228 229 230 231 232 233 234 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 225 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 rescue ActiveRecord::RecordNotFound, Fog::Errors::NotFound # if the VM does not exists, we don't really care. true end |
#editable_network_interfaces? ⇒ Boolean
28 29 30 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 28 def editable_network_interfaces? true end |
#hypervisor ⇒ Object
105 106 107 108 109 110 111 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 105 def hypervisor client.hosts.new( cache.cache(:hypervisor) do client.hosts.first.attributes end ) end |
#max_cpu_count(host = nil) ⇒ Object
TODO
74 75 76 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 74 def max_cpu_count(host = nil) (host || hypervisor).logical_processor_count end |
#max_memory(host = nil) ⇒ Object
78 79 80 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 78 def max_memory(host = nil) (host || hypervisor).memory_capacity end |
#new_cdrom(attr = {}) ⇒ Object
300 301 302 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 300 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
288 289 290 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 288 def new_interface(attr = {}) Fog::Hyperv::Compute::NetworkAdapter.new attr end |
#new_vm(attr = {}) ⇒ Object
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 124 def new_vm(attr = {}) attr.delete :id 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:).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:).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
292 293 294 295 296 297 298 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 292 def new_volume(attr = {}) basename = attr.delete(:basename) { 'Disk' } size = attr.delete(:size) vhd = client.vhds.new({ basename:, size: }.compact) Fog::Hyperv::Compute::HardDrive.new vhd:, **attr end |
#provided_attributes ⇒ Object
65 66 67 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 65 def provided_attributes super.merge(mac: :mac) end |
#save_vm(uuid, web_attr) ⇒ Object
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 193 def save_vm(uuid, web_attr) attr = web_attr.deep_symbolize_keys validate_vm(attr) validate_interfaces(attr) validate_volumes(attr) attr.delete :start 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[:secure_boot_enabled].present? f = vm.firmware f.secure_boot = Foreman::Cast.to_bool(attr[:secure_boot_enabled]) ? :On : :Off f.save if f.dirty? end update_interfaces(vm, attr) update_volumes(vm, attr) vm.save if vm.dirty? vm end |
#set_vm_interfaces_attributes(vm, vm_attrs) ⇒ Object
321 322 323 324 325 326 327 328 329 330 331 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 321 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
313 314 315 316 317 318 319 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 313 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
24 25 26 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 24 def supports_update? true end |
#switches(host) ⇒ Object
115 116 117 118 119 120 121 122 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 115 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
36 37 38 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 36 def test_connection( = {}) validate_connectivity() end |
#to_label ⇒ Object
12 13 14 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 12 def to_label "#{name} (#{provider_friendly_name})" end |
#update_required?(old_attrs, new_attrs) ⇒ Boolean
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 236 def update_required?(old_attrs, new_attrs) new_attrs.deep_symbolize_keys[:volumes_attributes]&.each 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].present? && 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 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].present? && 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 |old, new| old.merge(new) do |k, old_v, new_v| if k == :allowed_vlan_ids || k == :secondary_vlan_ids 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(old_v, new_v) elsif old_v.to_s != new_v.to_s 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})" 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
40 41 42 43 44 45 46 47 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 40 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_instance_defaults ⇒ Object
304 305 306 307 308 309 310 311 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 304 def vm_instance_defaults super.merge( generation: 2, memory_startup: 1024.megabytes, processor_count: 1, interfaces: [new_interface] ) end |