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
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
#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
147 148 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 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 147 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
223 224 225 226 227 228 229 230 231 232 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 223 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
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
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
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 122 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: 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
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: 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
191 192 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 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 191 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
26 27 28 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 26 def supports_update? true end |
#switches(host) ⇒ Object
113 114 115 116 117 118 119 120 |
# File 'app/models/foreman_hyperv/hyperv.rb', line 113 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
234 235 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 234 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 |old, new| old.merge(new) do |k, old_v, new_v| 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(old_v, new_v) elsif old_v.to_s != new_v.to_s Rails.logger.debug do "Scheduling compute instance update because #{k} 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_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 |