foreman_kubevirt 0.1.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.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +619 -0
  3. data/README.md +156 -0
  4. data/Rakefile +44 -0
  5. data/app/assets/javascripts/foreman_kubevirt/kubevirt.js +31 -0
  6. data/app/assets/javascripts/foreman_kubevirt/nic_info.js +9 -0
  7. data/app/controllers/foreman_kubevirt/concerns/api/compute_resources_controller_extensions.rb +26 -0
  8. data/app/models/concerns/fog_extensions/kubevirt/network.rb +11 -0
  9. data/app/models/concerns/fog_extensions/kubevirt/server.rb +55 -0
  10. data/app/models/concerns/fog_extensions/kubevirt/vmnic.rb +12 -0
  11. data/app/models/concerns/fog_extensions/kubevirt/volume.rb +26 -0
  12. data/app/models/foreman_kubevirt/kubevirt.rb +370 -0
  13. data/app/views/api/v2/compute_resources/kubevirt.json.rabl +1 -0
  14. data/app/views/compute_resources/form/_kubevirt.html.erb +14 -0
  15. data/app/views/compute_resources/show/_kubevirt.html.erb +14 -0
  16. data/app/views/compute_resources_vms/form/kubevirt/_base.html.erb +27 -0
  17. data/app/views/compute_resources_vms/form/kubevirt/_network.html.erb +15 -0
  18. data/app/views/compute_resources_vms/form/kubevirt/_volume.html.erb +21 -0
  19. data/app/views/compute_resources_vms/index/_kubevirt.html.erb +29 -0
  20. data/app/views/compute_resources_vms/show/_kubevirt.html.erb +55 -0
  21. data/app/views/images/form/_kubevirt.html.erb +4 -0
  22. data/config/routes.rb +2 -0
  23. data/lib/foreman_kubevirt/engine.rb +75 -0
  24. data/lib/foreman_kubevirt/version.rb +3 -0
  25. data/lib/foreman_kubevirt.rb +4 -0
  26. data/lib/tasks/foreman_kubevirt_tasks.rake +47 -0
  27. data/locale/Makefile +60 -0
  28. data/locale/en/foreman_kubevirt.po +19 -0
  29. data/locale/foreman_kubevirt.pot +19 -0
  30. data/locale/gemspec.rb +2 -0
  31. data/test/factories/compute_resource_factories.rb +10 -0
  32. data/test/factories/host_factories.rb +49 -0
  33. data/test/factories/nic_factories.rb +31 -0
  34. data/test/models/compute_resources/kubevirt_test.rb +69 -0
  35. data/test/test_plugin_helper.rb +7 -0
  36. data/test/unit/foreman_kubevirt_test.rb +45 -0
  37. metadata +127 -0
@@ -0,0 +1,370 @@
1
+ require 'foreman/exception'
2
+
3
+ module ForemanKubevirt
4
+ class Kubevirt < ComputeResource
5
+ alias_attribute :hostname, :url
6
+ alias_attribute :token, :password
7
+ alias_attribute :namespace, :user
8
+ validates :hostname, :api_port, :namespace, :token, :presence => true
9
+ validate :test_connection
10
+
11
+ def ca_cert
12
+ attrs[:ca_cert]
13
+ end
14
+
15
+ def ca_cert=(key)
16
+ attrs[:ca_cert] = key
17
+ end
18
+
19
+ def api_port
20
+ attrs[:api_port]
21
+ end
22
+
23
+ def api_port=(key)
24
+ attrs[:api_port] = key
25
+ end
26
+
27
+ def capabilities
28
+ %i[build image new_volume]
29
+ end
30
+
31
+ def provided_attributes
32
+ { :uuid => :name, :mac => :mac }
33
+ end
34
+
35
+ def available_images
36
+ []
37
+ end
38
+
39
+ def self.provider_friendly_name
40
+ 'KubeVirt'
41
+ end
42
+
43
+ def to_label
44
+ "#{name} (#{provider_friendly_name})"
45
+ end
46
+
47
+ def self.model_name
48
+ ComputeResource.model_name
49
+ end
50
+
51
+ def test_connection(_options = {})
52
+ client&.valid? && client&.virt_supported?
53
+ rescue StandardError => e
54
+ errors[:base] << e.message
55
+ end
56
+
57
+ def networks
58
+ client.networkattachmentdefs.all
59
+ rescue StandardError => e
60
+ logger.warn("Failed to retrieve network attachments definition from KubeVirt, make sure KubeVirt has CNI provider and NetworkAttachmentDefinition CRD deployed: #{e.message}")
61
+ []
62
+ end
63
+
64
+ def find_vm_by_uuid(uuid)
65
+ super
66
+ rescue Fog::Kubevirt::Errors::ClientError => e
67
+ Foreman::Logging.exception("Failed retrieving KubeVirt vm by uuid #{uuid}", e)
68
+ raise ActiveRecord::RecordNotFound
69
+ end
70
+
71
+ def volumes
72
+ client.volumes.all
73
+ end
74
+
75
+ def storage_classes
76
+ client.storageclasses.all
77
+ end
78
+
79
+ def storage_classes_for_select
80
+ storage_classes.map { |sc| OpenStruct.new(id: sc.name, description: "#{sc.name} (#{sc.provisioner})") }
81
+ end
82
+
83
+ def new_volume(attr = {})
84
+ return unless new_volume_errors.empty?
85
+
86
+ Fog::Kubevirt::Compute::Volume.new(attr)
87
+ end
88
+
89
+ def new_volume_errors
90
+ errors = []
91
+ errors.push _('no Persistent Volumes available on provider') if storage_classes.empty?
92
+ errors
93
+ end
94
+
95
+ def cni_providers
96
+ [[_("multus"), :multus], [_("genie"), :genie], [_("pod"), :pod]]
97
+ end
98
+
99
+ # @param args[Hash] contains VM creation parameters
100
+ # cpu_cores[str] - the number of cpu cores
101
+ # memory[str] - the memory for the VM
102
+ # start[bool] - indicates if the vm should be started
103
+ # name[str] - the name of the VM
104
+ # interfaces_attributes[Hash] - the attributes for the interfaces, i.e.:
105
+ # {
106
+ # "0" => {
107
+ # "network" => "ovs-foreman",
108
+ # "boot" => "1",
109
+ # "cni_provider" => "multus"
110
+ # },
111
+ # "1" => {
112
+ # "cni_provider" => "pod"
113
+ # "boot" => "0"
114
+ # }
115
+ # }
116
+ #
117
+ # volumes_attributes[Hash] - the attributes for the persistent volume claims:
118
+ # {
119
+ # "1554394214729" => {
120
+ # "storage_class" => "local-storage",
121
+ # "name" => "alvin-hinojosa1",
122
+ # "capacity" => "3",
123
+ # "bootable"=>"true"
124
+ # },
125
+ # "1554394230987" => {
126
+ # "storage_class" => "local-storage",
127
+ # "name" => "alvin-hinojosa",
128
+ # "capacity"=>"2"
129
+ # }
130
+ # }
131
+ def create_vm(args = {})
132
+ options = vm_instance_defaults.merge(args.to_hash.deep_symbolize_keys)
133
+ logger.debug("creating VM with the following options: #{options.inspect}")
134
+ volumes = []
135
+
136
+ image = args["image_id"]
137
+ volumes_attributes = args["volumes_attributes"]
138
+ raise "VM should be created based on Persistent Volume Claim or Image" unless (volumes_attributes.present? || image)
139
+
140
+ # Add image as volume to the virtual machine
141
+ image_provision = args["provision_method"] == "image"
142
+ if image_provision
143
+ volume = Fog::Kubevirt::Compute::Volume.new
144
+ raise "VM should be created based on an image" unless image
145
+
146
+ volume.info = image
147
+ volume.boot_order = 1
148
+ volume.type = 'containerDisk'
149
+ volumes << volume
150
+ end
151
+
152
+ volumes_attributes&.each_with_index do |(_, v), index|
153
+ # Add PVC as volumes to the virtual machine
154
+ pvc_name = options[:name].gsub(/[._]+/, '-') + "-claim-" + (index + 1).to_s
155
+ capacity = v["capacity"]
156
+ storage_class = v["storage_class"]
157
+ bootable = v["bootable"] && !image_provision
158
+
159
+ volume = create_vm_volume(pvc_name, capacity, storage_class, bootable)
160
+ volumes << volume
161
+ end
162
+
163
+ # FIXME: Add cloud-init support
164
+ # init = { 'userData' => "#!/bin/bash\necho \"fedora\" | passwd fedora --stdin"}
165
+
166
+ interfaces = []
167
+ networks = []
168
+
169
+ args["interfaces_attributes"].values.each do |iface|
170
+ if iface["cni_provider"] == 'pod'
171
+ nic = {
172
+ :bridge => {},
173
+ :name => 'pod'
174
+ }
175
+
176
+ net = { :name => 'pod', :pod => {} }
177
+ else
178
+ nic = {
179
+ :bridge => {},
180
+ :name => iface["network"]
181
+ }
182
+
183
+ cni = iface["cni_provider"].to_sym
184
+ net = {
185
+ :name => iface["network"],
186
+ cni => { :networkName => iface["network"] }
187
+ }
188
+ end
189
+
190
+ # TODO: Consider replacing with 'free' boot order, also verify uniqueness
191
+ # there is a bug with bootOrder https://bugzilla.redhat.com/show_bug.cgi?id=1687341
192
+ # therefore adding to the condition not to boot from netwotk device if already asked
193
+ # to boot from disk
194
+ if iface["provision"] == true && volumes.select { |v| v.boot_order == 1 }.empty?
195
+ nic[:bootOrder] = 1
196
+ end
197
+ nic[:macAddress] = iface["mac"] if iface["mac"]
198
+ interfaces << nic
199
+ networks << net
200
+ end
201
+
202
+ begin
203
+ client.vms.create(:vm_name => options[:name],
204
+ :cpus => options[:cpu_cores].to_i,
205
+ :memory_size => options[:memory].to_i / 2**20,
206
+ :volumes => volumes,
207
+ # :cloudinit => init,
208
+ :networks => networks,
209
+ :interfaces => interfaces)
210
+ client.servers.get(options[:name])
211
+ rescue Fog::Kubevirt::Errors::ClientError => e
212
+ delete_pvcs(volumes)
213
+ raise e
214
+ end
215
+ end
216
+
217
+ def create_new_pvc(pvc_name, capacity, storage_class)
218
+ client.pvcs.create(:name => pvc_name,
219
+ :namespace => namespace,
220
+ :storage_class => storage_class,
221
+ :access_modes => ['ReadWriteOnce'],
222
+ :requests => { :storage => capacity + "G" })
223
+ end
224
+
225
+ def delete_pvcs(volumes)
226
+ volumes.each do |volume|
227
+ begin
228
+ client.pvcs.delete(volume.info) if volume.type == "persistentVolumeClaim"
229
+ rescue StandardError => e
230
+ logger.error("The PVC #{volume.info} couldn't be delete due to #{e.message}")
231
+ end
232
+ end
233
+ end
234
+
235
+ def create_vm_volume(pvc_name, capacity, storage_class, bootable)
236
+ create_new_pvc(pvc_name, capacity, storage_class)
237
+
238
+ volume = Fog::Kubevirt::Compute::Volume.new
239
+ volume.type = 'persistentVolumeClaim'
240
+ volume.info = pvc_name
241
+ volume.boot_order = 1 if bootable == "true"
242
+ volume
243
+ end
244
+
245
+ def destroy_vm(vm_uuid)
246
+ vm = find_vm_by_uuid(vm_uuid)
247
+ delete_pvcs(vm.volumes)
248
+ vm.destroy
249
+ rescue ActiveRecord::RecordNotFound
250
+ true
251
+ end
252
+
253
+ def host_compute_attrs(host)
254
+ attrs = super
255
+ attrs[:interfaces_attributes].each_value { |nic| nic["network"] = nil if nic["cni_provider"] == "pod" }
256
+ attrs
257
+ end
258
+
259
+ #
260
+ # Since 'name' is the identity/UUID of server/vm, we need to override
261
+ # default values that assign also value for 'name' so vm_exists? will
262
+ # return 'false'
263
+ def vm_instance_defaults
264
+ {
265
+ :memory => 1024.megabytes,
266
+ :cpu_cores => '1'
267
+ }
268
+ end
269
+
270
+ def new_interface(attr = {})
271
+ Fog::Kubevirt::Compute::VmNic.new attr
272
+ end
273
+
274
+ #
275
+ # Overrding base class implementation since 'mac' is required for the created interface
276
+ #
277
+ def host_interfaces_attrs(host)
278
+ host.interfaces.select(&:physical?).each.with_index.reduce({}) do |hash, (nic, index)|
279
+ hash.merge(index.to_s => nic.compute_attributes.merge(ip: nic.ip, mac: nic.mac, provision: nic.provision))
280
+ end
281
+ end
282
+
283
+ #
284
+ # Overrding base class implementation since 'pvc' is required
285
+ #
286
+ def set_vm_volumes_attributes(vm, vm_attrs)
287
+ volumes = vm.volumes.collect do |vol|
288
+ next unless vol.type == 'persistentVolumeClaim'
289
+
290
+ begin
291
+ vol.pvc = client.pvcs.get(vol.info)
292
+ vol
293
+ rescue StandardError => e
294
+ # An import of a VM where one of its PVC doesn't exist
295
+ Foreman::Logging.exception("Import VM fail: The PVC #{vol.info} does not exist for VM #{vm.name}", e)
296
+ nil
297
+ end
298
+ end.compact
299
+ vm_attrs[:volumes_attributes] = Hash[volumes.each_with_index.map { |volume, idx| [idx.to_s, volume.attributes] }]
300
+
301
+ vm_attrs
302
+ end
303
+
304
+ def vm_compute_attributes(vm)
305
+ vm_attrs = super
306
+ interfaces = vm.interfaces || []
307
+ vm_attrs[:interfaces_attributes] = interfaces.each_with_index.each_with_object({}) do |(interface, index), hsh|
308
+ interface_attrs = {}
309
+ interface_attrs[:compute_attributes] = {}
310
+ interface_attrs[:mac] = interface.mac
311
+ interface_attrs[:compute_attributes][:network] = interface.network
312
+ interface_attrs[:compute_attributes][:cni_provider] = interface.cni_provider
313
+ hsh[index.to_s] = interface_attrs
314
+ end
315
+
316
+ vm_attrs
317
+ end
318
+
319
+ def associated_host(vm)
320
+ associate_by("mac", vm.mac)
321
+ end
322
+
323
+ # TODO: max supported values should be fetched according to namespace
324
+ # capabilities: kubectl get limits namespace_name
325
+ def max_cpu_count
326
+ 16
327
+ end
328
+
329
+ def max_socket_count
330
+ 16
331
+ end
332
+
333
+ def max_memory
334
+ 64.gigabytes
335
+ end
336
+
337
+ protected
338
+
339
+ def client
340
+ return @client if @client
341
+
342
+ @client ||= Fog::Kubevirt::Compute.new(
343
+ :kubevirt_hostname => hostname,
344
+ :kubevirt_port => api_port,
345
+ :kubevirt_namespace => namespace,
346
+ :kubevirt_token => token,
347
+ :kubevirt_log => logger,
348
+ :kubevirt_verify_ssl => ca_cert.present?,
349
+ :kubevirt_ca_cert => ca_cert
350
+ )
351
+ rescue OpenSSL::X509::CertificateError
352
+ raise_certification_failure_exception
353
+ rescue StandardError => e
354
+ if e.message =~ /SSL_connect.*certificate verify failed/ ||
355
+ e.message =~ /Peer certificate cannot be authenticated with given CA certificates/
356
+ raise_certification_failure_exception
357
+ else
358
+ raise e
359
+ end
360
+ end
361
+
362
+ def raise_certification_failure_exception
363
+ raise Foreman::FingerprintException.new(
364
+ N_("The remote system presented a public key signed by an unidentified certificate authority.
365
+ If you are sure the remote system is authentic, go to the compute resource edit page, press the 'Test Connection' button and submit"),
366
+ ca_cert
367
+ )
368
+ end
369
+ end
370
+ end
@@ -0,0 +1 @@
1
+ attributes :hostname, :api_port, :namespace
@@ -0,0 +1,14 @@
1
+ <%= text_f f, :hostname, :label => _("Hostname") %>
2
+ <%= text_f f, :api_port, :label => _("API Port"), :required => true %>
3
+ <%= text_f f, :namespace, :label => _("Namespace"), :required => true %>
4
+ <%= password_f f, :password, :label => _("Token"), :required => true,
5
+ :keep_value => true, :unset => unset_password?,
6
+ :help_block => _("e.g. eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9..."),
7
+ :help_inline => documentation_button('5.2.10PasswordEncryption') %>
8
+
9
+ <%= textarea_f f, :ca_cert, :label => _("X509 Certification Authorities"),
10
+ :placeholder => _("Optionally provide a CA, or a correctly ordered CA chain or a path to a file. If left blank - insecure.") %>
11
+
12
+ <div class="col-md-offset-2">
13
+ <%= test_connection_button_f(f, true) %>
14
+ </div>
@@ -0,0 +1,14 @@
1
+ <tr>
2
+ <td><%= _("Hostname") %></td>
3
+ <td><%= @compute_resource.hostname %></td>
4
+ </tr>
5
+
6
+ <tr>
7
+ <td><%= _("API Port") %></td>
8
+ <td><%= @compute_resource.api_port %></td>
9
+ </tr>
10
+
11
+ <tr>
12
+ <td><%= _("Namespace") %></td>
13
+ <td><%= @compute_resource.user %></td>
14
+ </tr>
@@ -0,0 +1,27 @@
1
+ <%= javascript_tag("$(document).on('ContentLoad', tfm.numFields.initAll)"); %>
2
+
3
+ <%= text_f f, :name, :label => _('Name'), :label_size => "col-md-2", :disabled => !new_vm if show_vm_name? %>
4
+
5
+ <%= counter_f f, :cpu_cores, :disabled => !new_vm, :label => _('CPUs'), :label_size => 'col-md-2', :'data-soft-max' => compute_resource.max_cpu_count %>
6
+
7
+ <%= byte_size_f f, :memory, :disabled => !new_vm, :label => _('Memory'), :label_size => "col-md-2", :'data-soft-max' => compute_resource.max_memory %>
8
+
9
+ <% checked = params[:host] && params[:host][:compute_attributes] && params[:host][:compute_attributes][:start] || '1' %>
10
+ <%= checkbox_f f, :start, { :checked => (checked == '1'), :help_inline => _("Power ON this machine"), :label => _('Start'), :label_size => "col-md-2"} if new_vm && controller_name != "compute_attributes" %>
11
+
12
+
13
+ <% unless local_assigns[:hide_image] %>
14
+ <%
15
+ arch ||= nil ; os ||= nil
16
+ images = possible_images(compute_resource, arch, os)
17
+ -%>
18
+
19
+ <div id='image_selection'>
20
+ <%= select_f f, :image_id, images, :uuid, :name,
21
+ { :include_blank => (images.empty? || images.size == 1) ? false : _('Please select an image') },
22
+ { :disabled => images.empty?, :label => _('Image'), :label_size => "col-md-2" } %>
23
+ </div>
24
+ <% end %>
25
+
26
+ <%= javascript_include_tag("foreman_kubevirt/nic_info.js") %>
27
+ <%= javascript_include_tag("foreman_kubevirt/kubevirt.js") %>
@@ -0,0 +1,15 @@
1
+ <%= selectable_f f, :cni_provider, compute_resource.cni_providers, {},
2
+ :class => "col-md-3 kubevirt-cni-provider",
3
+ :disabled => !new_vm,
4
+ :label => _('CNI Provider'),
5
+ :label_size => "col-md-3",
6
+ :onchange => "cniProviderSelected(this)" %>
7
+
8
+ <%= select_f f, :network, compute_resource.networks,
9
+ :id,
10
+ :name,
11
+ {},
12
+ :disabled => !new_vm || f.object.cni_provider == 'pod',
13
+ :class => "col-md-3 kubevirt-network",
14
+ :label => _('Network'),
15
+ :label_size => "col-md-3" %>
@@ -0,0 +1,21 @@
1
+ <% if f.object.type.nil? || f.object.type == 'persistentVolumeClaim' %>
2
+ <% storage_classes = compute_resource.storage_classes %>
3
+
4
+ <%= select_f f, :storage_class,
5
+ compute_resource.storage_classes_for_select,
6
+ :id,
7
+ :description,
8
+ { },
9
+ :label => _('Storage Class'),
10
+ :label_size => "col-md-2",
11
+ :disabled => !new_vm,
12
+ :class => "col-md-2" %>
13
+
14
+ <%= text_f f,:capacity, :label => _('Size (GB)'), :label_size => "col-md-2", :disabled => !new_vm, :class => "col-md-2 pvc-size" %>
15
+
16
+ <% bootable = f.object.boot_order.nil? ? false : f.object.bootable %>
17
+ <%= field(f, :bootable, :label => _('Bootable'), :label_size => "col-md-2") do
18
+ radio_button_f f, :bootable, {:disabled => !new_vm, :checked => bootable, :onclick => 'bootableRadio(this)',
19
+ :text => _('Only one volume can be bootable')}
20
+ end %>
21
+ <% end %>
@@ -0,0 +1,29 @@
1
+ <thead>
2
+ <tr>
3
+ <th><%= _('Name') %></th>
4
+ <th><%= _('CPUs') %></th>
5
+ <th><%= _('Memory') %></th>
6
+ <th><%= _('Power') %></th>
7
+ <th><%= _('Actions') %></th>
8
+ </tr>
9
+ </thead>
10
+ <tbody>
11
+ <% @vms.each do |vm| %>
12
+ <tr>
13
+ <td><%= link_to_if_authorized vm.name, hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity).merge(:auth_object => @compute_resource, :auth_action => 'view', :authorizer => authorizer) %></td>
14
+ <td><%= vm.cpu_cores %></td>
15
+ <td><%= vm.memory %></td>
16
+ <td>
17
+ <span <%= vm_power_class(vm.ready?) %>>
18
+ <%= vm_state(vm) %></span>
19
+ </td>
20
+ <td>
21
+ <%=
22
+ action_buttons(vm_power_action(vm, authorizer),
23
+ vm_import_action(vm),
24
+ display_delete_if_authorized(hash_for_compute_resource_vm_path(:compute_resource_id => @compute_resource, :id => vm.identity).merge(:auth_object => @compute_resource, :authorizer => authorizer)))
25
+ %>
26
+ </td>
27
+ </tr>
28
+ <% end %>
29
+ </tbody>
@@ -0,0 +1,55 @@
1
+ <% title @vm.name %>
2
+
3
+ <div class='col-md-12'>
4
+ <table class="<%= table_css_classes %>">
5
+ <thead>
6
+ <tr><th colspan="2"><%=_('Properties') %></th></tr>
7
+ </thead>
8
+ <tbody>
9
+ <tr>
10
+ <td><%= _('Name') %></td>
11
+ <td><%= @vm.identity %></td>
12
+ </tr>
13
+ <tr>
14
+ <td><%= _('VCPU(s)') %></td>
15
+ <td><%= @vm.cpu_cores %></td>
16
+ </tr>
17
+ <tr>
18
+ <td><%= _('UUID') %></td>
19
+ <td><%= @vm.uid %></td>
20
+ </tr>
21
+ <tr>
22
+ <td><%= _('Memory') %></td>
23
+ <td><%= number_to_human_size @vm.memory %>
24
+ </td>
25
+ </tr>
26
+ <tr>
27
+ <td><%= _('Namespace') %></td>
28
+ <td><%= @vm.namespace %></td>
29
+ </tr>
30
+ <tr>
31
+ <td><%= _('Type') %></td>
32
+ <td><%= @vm.machine_type %></td>
33
+ </tr>
34
+
35
+ <% @vm.interfaces.each do |nic| %>
36
+ <tr>
37
+ <td><%= _('NIC') %></td>
38
+ <td><%= "#{nic.name} - #{nic.mac_address} (#{nic.type})" %></td>
39
+ </tr>
40
+ <% end %>
41
+
42
+ <% @vm.volumes.each do |vol| %>
43
+ <tr>
44
+ <td><%= _('Disk') %></td>
45
+ <td><%= "#{vol.name} - #{vol.type} (#{vol.info})" %></td>
46
+ </tr>
47
+ <% end %>
48
+
49
+ <tr>
50
+ <td><%= _('Running on') %></td>
51
+ <td><%= link_to @compute_resource, compute_resource_path(@compute_resource) %></td>
52
+ </tr>
53
+ </tbody>
54
+ </table>
55
+ </div>
@@ -0,0 +1,4 @@
1
+ <%= text_f f, :username, :value => @image.username || "root", :help_inline => _("The user that is used to ssh into the instance, normally cloud-user, ec2-user, ubuntu, root etc") %>
2
+ <%= password_f f, :password, :help_inline => _("Password to authenticate with - used for SSH finish step.") %>
3
+ <%= checkbox_f f, :user_data, :help_inline => _("Does this image support user data input (e.g. via cloud-init)?") %>
4
+ <%= image_field(f, :label => _("Image"), :help_inline => _("The name of the image in the registry.")) %>
data/config/routes.rb ADDED
@@ -0,0 +1,2 @@
1
+ Rails.application.routes.draw do
2
+ end
@@ -0,0 +1,75 @@
1
+ module ForemanKubevirt
2
+ class Engine < ::Rails::Engine
3
+ engine_name "foreman_kubevirt"
4
+ config.autoload_paths += Dir["#{config.root}/app/models/concerns"]
5
+ initializer "foreman_kubevirt.register_plugin", :before => :finisher_hook do |_app|
6
+ Foreman::Plugin.register :foreman_kubevirt do
7
+ requires_foreman ">= 1.7"
8
+ compute_resource(ForemanKubevirt::Kubevirt)
9
+
10
+ parameter_filter(ComputeResource, :hostname, :url)
11
+ parameter_filter(ComputeResource, :namespace, :user)
12
+ parameter_filter(ComputeResource, :token, :password)
13
+ parameter_filter(ComputeResource, :ca_cert)
14
+ parameter_filter(ComputeResource, :api_port)
15
+ end
16
+ end
17
+
18
+ assets_to_precompile =
19
+ Dir.chdir(root) do
20
+ Dir['app/assets/javascripts/**/*', 'app/assets/stylesheets/**/*'].map do |f|
21
+ f.split(File::SEPARATOR, 4).last
22
+ end
23
+ end
24
+
25
+ initializer 'foreman_kubevirt.assets.precompile' do |app|
26
+ app.config.assets.precompile += assets_to_precompile
27
+ end
28
+
29
+ initializer 'foreman_kubevirt.filter_parameters' do |app|
30
+ app.config.filter_parameters += [:token]
31
+ end
32
+
33
+ initializer 'foreman_kubevirt.configure_assets', group: :assets do
34
+ SETTINGS[:foreman_kubevirt] = { assets: { precompile: assets_to_precompile } }
35
+ end
36
+
37
+ initializer "foreman_kubevirt.add_rabl_view_path" do
38
+ Rabl.configure do |config|
39
+ config.view_paths << ForemanKubevirt::Engine.root.join('app', 'views')
40
+ end
41
+ end
42
+
43
+ # Include concerns in this config.to_prepare block
44
+ config.to_prepare do
45
+ begin
46
+ require "fog/kubevirt"
47
+ require "fog/kubevirt/compute/models/server"
48
+ require File.expand_path("../../app/models/concerns/fog_extensions/kubevirt/server", __dir__)
49
+
50
+ ::Api::V2::ComputeResourcesController.send :include, ForemanKubevirt::Concerns::Api::ComputeResourcesControllerExtensions
51
+ Fog::Kubevirt::Compute::Server.send(:include, ::FogExtensions::Kubevirt::Server)
52
+
53
+ require "fog/kubevirt/compute/models/volume"
54
+ require File.expand_path("../../app/models/concerns/fog_extensions/kubevirt/volume", __dir__)
55
+ Fog::Kubevirt::Compute::Volume.send(:include, ::FogExtensions::Kubevirt::Volume)
56
+
57
+ require "fog/kubevirt/compute/models/vmnic"
58
+ require File.expand_path("../../app/models/concerns/fog_extensions/kubevirt/vmnic", __dir__)
59
+ Fog::Kubevirt::Compute::VmNic.send(:include, ::FogExtensions::Kubevirt::VmNic)
60
+
61
+ require "fog/kubevirt/compute/models/networkattachmentdef"
62
+ require File.expand_path("../../app/models/concerns/fog_extensions/kubevirt/network", __dir__)
63
+ Fog::Kubevirt::Compute::Networkattachmentdef.send(:include, ::FogExtensions::Kubevirt::Network)
64
+ rescue StandardError => e
65
+ Rails.logger.warn "Foreman-Kubevirt: skipping engine hook (#{e})"
66
+ end
67
+ end
68
+
69
+ initializer "foreman_kubevirt.register_gettext", after: :load_config_initializers do |_app|
70
+ locale_dir = File.join(File.expand_path("../..", __dir__), "locale")
71
+ locale_domain = "foreman_kubevirt"
72
+ Foreman::Gettext::Support.add_text_domain locale_domain, locale_dir
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,3 @@
1
+ module ForemanKubevirt
2
+ VERSION = '0.1.0'.freeze
3
+ end
@@ -0,0 +1,4 @@
1
+ require 'foreman_kubevirt/engine'
2
+
3
+ module ForemanKubevirt
4
+ end