foreman_kubevirt 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +619 -0
- data/README.md +156 -0
- data/Rakefile +44 -0
- data/app/assets/javascripts/foreman_kubevirt/kubevirt.js +31 -0
- data/app/assets/javascripts/foreman_kubevirt/nic_info.js +9 -0
- data/app/controllers/foreman_kubevirt/concerns/api/compute_resources_controller_extensions.rb +26 -0
- data/app/models/concerns/fog_extensions/kubevirt/network.rb +11 -0
- data/app/models/concerns/fog_extensions/kubevirt/server.rb +55 -0
- data/app/models/concerns/fog_extensions/kubevirt/vmnic.rb +12 -0
- data/app/models/concerns/fog_extensions/kubevirt/volume.rb +26 -0
- data/app/models/foreman_kubevirt/kubevirt.rb +370 -0
- data/app/views/api/v2/compute_resources/kubevirt.json.rabl +1 -0
- data/app/views/compute_resources/form/_kubevirt.html.erb +14 -0
- data/app/views/compute_resources/show/_kubevirt.html.erb +14 -0
- data/app/views/compute_resources_vms/form/kubevirt/_base.html.erb +27 -0
- data/app/views/compute_resources_vms/form/kubevirt/_network.html.erb +15 -0
- data/app/views/compute_resources_vms/form/kubevirt/_volume.html.erb +21 -0
- data/app/views/compute_resources_vms/index/_kubevirt.html.erb +29 -0
- data/app/views/compute_resources_vms/show/_kubevirt.html.erb +55 -0
- data/app/views/images/form/_kubevirt.html.erb +4 -0
- data/config/routes.rb +2 -0
- data/lib/foreman_kubevirt/engine.rb +75 -0
- data/lib/foreman_kubevirt/version.rb +3 -0
- data/lib/foreman_kubevirt.rb +4 -0
- data/lib/tasks/foreman_kubevirt_tasks.rake +47 -0
- data/locale/Makefile +60 -0
- data/locale/en/foreman_kubevirt.po +19 -0
- data/locale/foreman_kubevirt.pot +19 -0
- data/locale/gemspec.rb +2 -0
- data/test/factories/compute_resource_factories.rb +10 -0
- data/test/factories/host_factories.rb +49 -0
- data/test/factories/nic_factories.rb +31 -0
- data/test/models/compute_resources/kubevirt_test.rb +69 -0
- data/test/test_plugin_helper.rb +7 -0
- data/test/unit/foreman_kubevirt_test.rb +45 -0
- 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,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
|