Class: ForemanKubevirt::Kubevirt

Inherits:
ComputeResource
  • Object
show all
Defined in:
app/models/foreman_kubevirt/kubevirt.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.model_nameObject



51
52
53
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 51

def self.model_name
  ComputeResource.model_name
end

.provider_friendly_nameObject



43
44
45
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 43

def self.provider_friendly_name
  'KubeVirt'
end

Instance Method Details

#api_portObject



19
20
21
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 19

def api_port
  attrs[:api_port]
end

#api_port=(key) ⇒ Object



23
24
25
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 23

def api_port=(key)
  attrs[:api_port] = key
end

#associated_host(vm) ⇒ Object



268
269
270
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 268

def associated_host(vm)
  associate_by("mac", vm.mac)
end

#available_imagesObject



39
40
41
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 39

def available_images
  []
end

#ca_certObject



11
12
13
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 11

def ca_cert
  attrs[:ca_cert]
end

#ca_cert=(key) ⇒ Object



15
16
17
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 15

def ca_cert=(key)
  attrs[:ca_cert] = key
end

#caching_enabledObject



60
61
62
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 60

def caching_enabled
  false
end

#capabilitiesObject



27
28
29
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 27

def capabilities
  %i[build image new_volume]
end

#cni_providersObject



122
123
124
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 122

def cni_providers
  [[_("multus"), :multus], [_("genie"), :genie], [_("pod"), :pod]]
end

#connection_properties_valid?Boolean

Returns:

  • (Boolean)


76
77
78
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 76

def connection_properties_valid?
  errors[:hostname].empty? && errors[:token].empty? && errors[:namespace].empty? && errors[:api_port].empty?
end

#console(uuid) ⇒ Object



302
303
304
305
306
307
308
309
310
311
312
313
314
315
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 302

def console(uuid)
  vm = find_vm_by_uuid(uuid)
  vnc_details = client.vminstances.get_vnc_console_details(vm.name, namespace)
  token = Base64.encode64(vnc_details[:token]).delete!("\n").delete("==")
  {
    :host => vnc_details[:host],
    :port => vnc_details[:port],
    :path => vnc_details[:path],
    :token_protocol => token_protocol(token),
    :plain_protocol => plain_kubevirt_protocol,
    :type => 'vnc',
    :encrypt => true
  }
end

#convert_memory_to_bytes(memory) ⇒ Object

Converts a given memory to bytes

Parameters:

  • memory
    • The memory of the VM to convert



290
291
292
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 290

def convert_memory_to_bytes(memory)
  convert_memory(memory, :b)
end

#create_vm(args = {}) ⇒ Object

volumes_attributes - the attributes for the persistent volume claims:

{
  "1554394214729" => {
                       "storage_class" => "local-storage",
                       "name"          => "alvin-hinojosa1",
                       "capacity"      => "3",
                       "bootable"=>"true"
                     },
  "1554394230987" => {
                       "storage_class" => "local-storage",
                       "name"          => "alvin-hinojosa",
                       "capacity"=>"2"
                     }
}

Parameters:

  • args (Hash) (defaults to: {})

    contains VM creation parameters cpu_cores - the number of cpu cores memory - the memory for the VM start - indicates if the vm should be started name - the name of the VM interfaces_attributes - the attributes for the interfaces, i.e.:

    {
      "0" => {
               "network"      => "ovs-foreman",
               "boot"         => "1",
               "cni_provider" => "multus"
             },
      "1" => {
               "cni_provider" => "pod"
               "boot"         => "0"
             }
    }
    


158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 158

def create_vm(args = {})
  options = vm_instance_defaults.merge(args.to_hash.deep_symbolize_keys)
  logger.debug("creating VM with the following options: #{options.inspect}")

  # Add clound init user data
  user_data = { "userData" => options[:user_data] } if options[:user_data].present?

  begin
    volumes = create_volumes_for_vm(options)
    interfaces, networks = create_network_devices_for_vm(options, volumes)
    client.vms.create(:vm_name     => options[:name],
                      :cpus        => options[:cpu_cores].to_i,
                      :memory_size => convert_memory(options[:memory] + "b", :mi).to_s,
                      :memory_unit => "Mi",
                      :volumes     => volumes,
                      :cloudinit   => user_data,
                      :networks    => networks,
                      :interfaces  => interfaces)
    client.servers.get(options[:name])
  rescue Exception => e
    delete_pvcs(volumes) if volumes
    raise e
  end
end

#destroy_vm(vm_uuid) ⇒ Object



183
184
185
186
187
188
189
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 183

def destroy_vm(vm_uuid)
  vm = find_vm_by_uuid(vm_uuid)
  delete_pvcs(vm.volumes)
  vm.destroy
rescue ActiveRecord::RecordNotFound
  true
end

#find_vm_by_uuid(uuid) ⇒ Object



88
89
90
91
92
93
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 88

def find_vm_by_uuid(uuid)
  super
rescue Fog::Kubevirt::Errors::ClientError => e
  Foreman::Logging.exception("Failed retrieving KubeVirt vm by uuid #{uuid}", e)
  raise ActiveRecord::RecordNotFound
end

#host_compute_attrs(host) ⇒ Object



191
192
193
194
195
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 191

def host_compute_attrs(host)
  attrs = super
  attrs[:interfaces_attributes].each_value { |nic| nic["network"] = nil if nic["cni_provider"] == "pod" }
  attrs
end

#host_interfaces_attrs(host) ⇒ Object

Overrding base class implementation since ‘mac’ is required for the created interface



215
216
217
218
219
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 215

def host_interfaces_attrs(host)
  host.interfaces.select(&:physical?).each.with_index.reduce({}) do |hash, (nic, index)|
    hash.merge(index.to_s => nic.compute_attributes.merge(ip: nic.ip, mac: nic.mac, provision: nic.provision))
  end
end

#max_cpu_countObject

TODO: max supported values should be fetched according to namespace

capabilities: kubectl get limits namespace_name


274
275
276
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 274

def max_cpu_count
  16
end

#max_memoryObject



282
283
284
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 282

def max_memory
  64.gigabytes
end

#max_socket_countObject



278
279
280
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 278

def max_socket_count
  16
end

#networksObject



80
81
82
83
84
85
86
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 80

def networks
  client.networkattachmentdefs.all
rescue StandardError => e
  logger.warn("Failed to retrieve network attachments definition from KubeVirt,
    make sure KubeVirt has CNI provider and NetworkAttachmentDefinition CRD deployed: #{e.message}")
  []
end

#new_interface(attr = {}) ⇒ Object



208
209
210
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 208

def new_interface(attr = {})
  Fog::Kubevirt::Compute::VmNic.new attr
end

#new_vm(attr = {}) ⇒ Object



259
260
261
262
263
264
265
266
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 259

def new_vm(attr = {})
  vm = super
  interfaces = nested_attributes_for :interfaces, attr[:interfaces_attributes]
  interfaces.map { |i| vm.interfaces << new_interface(i) }
  volumes = nested_attributes_for :volumes, attr[:volumes_attributes]
  volumes.map { |v| vm.volumes << new_volume(v) }
  vm
end

#new_volume(attrs = {}) ⇒ Object



107
108
109
110
111
112
113
114
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 107

def new_volume(attrs = {})
  return unless new_volume_errors.empty?
  capacity = attrs.delete(:capacity)
  args = {capacity: capacity}.merge(attrs)
  vol = Fog::Kubevirt::Compute::Volume.new(args)
  vol.boot_order = 1 if args[:bootable] == "on" || args[:bootable] == "true"
  vol
end

#new_volume_errorsObject



116
117
118
119
120
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 116

def new_volume_errors
  errors = []
  errors.push _('no Storage Classes available on provider') if storage_classes.empty?
  errors
end

#plain_kubevirt_protocolObject



294
295
296
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 294

def plain_kubevirt_protocol
  "plain.kubevirt.io".freeze
end

#provided_attributesObject



35
36
37
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 35

def provided_attributes
  { :uuid => :name, :mac => :mac }
end

#set_vm_volumes_attributes(vm, vm_attrs) ⇒ Object

Overrding base class implementation since ‘pvc’ is required



224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 224

def set_vm_volumes_attributes(vm, vm_attrs)
  volumes = vm.volumes.collect do |vol|
    next unless vol.type == 'persistentVolumeClaim'

    begin
      vol.pvc = client.pvcs.get(vol.info)
      vol
    rescue StandardError => e
      # An import of a VM where one of its PVC doesn't exist
      Foreman::Logging.exception("Import VM fail: The PVC #{vol.info} does not exist for VM #{vm.name}", e)
      nil
    end
  end.compact
  vm_attrs[:volumes_attributes] = Hash[volumes.each_with_index.map { |volume, idx| [idx.to_s, volume.attributes] }]

  vm_attrs
end

#storage_classesObject



99
100
101
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 99

def storage_classes
  client.storageclasses.all
end

#storage_classes_for_selectObject



103
104
105
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 103

def storage_classes_for_select
  storage_classes.map { |sc| OpenStruct.new(id: sc.name, description: "#{sc.name} (#{sc.provisioner})") }
end

#test_connection(options = {}) ⇒ Object



55
56
57
58
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 55

def test_connection(options = {})
  super
  validate_connectivity(options)
end

#to_labelObject



47
48
49
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 47

def to_label
  "#{name} (#{provider_friendly_name})"
end

#token_protocol(token) ⇒ Object



298
299
300
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 298

def token_protocol(token)
  "base64url.bearer.authorization.k8s.io.#{token}"
end

#user_data_supported?Boolean

Returns:

  • (Boolean)


31
32
33
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 31

def user_data_supported?
  true
end

#validate_connectivity(options = {}) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 64

def validate_connectivity(options = {})
  return unless connection_properties_valid?
  return false if errors.any?
  client&.valid? && client&.virt_supported?
rescue StandardError => e
  if /401/.match?(e.message)
    errors[:base] << _('The compute resource could not be authenticated')
  else
    errors[:base] << e.message
  end
end

#vm_compute_attributes(vm) ⇒ Object



242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 242

def vm_compute_attributes(vm)
  vm_attrs = super
  interfaces = vm.interfaces || []
  vm_attrs[:interfaces_attributes] = interfaces.each_with_index.each_with_object({}) do |(interface, index), hsh|
    interface_attrs = {
      mac: interface.mac,
      compute_attributes: {
        network: interface.network,
        cni_provider: interface.cni_provider
      }
    }
    hsh[index.to_s] = interface_attrs
  end

  vm_attrs
end

#vm_instance_defaultsObject

Since ‘name’ is the identity/UUID of server/vm, we need to override default values that assign also value for ‘name’ so vm_exists? will return ‘false’



201
202
203
204
205
206
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 201

def vm_instance_defaults
  {
    :memory    => 1024.megabytes.to_s,
    :cpu_cores => '1'
  }
end

#volumesObject



95
96
97
# File 'app/models/foreman_kubevirt/kubevirt.rb', line 95

def volumes
  client.volumes.all
end