foreman_fog_proxmox 0.15.0 → 0.16.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.
- checksums.yaml +4 -4
- data/app/assets/javascripts/foreman_fog_proxmox/proxmox_vm.js +54 -33
- data/app/assets/javascripts/foreman_fog_proxmox/proxmox_vm_server.js +25 -5
- data/app/assets/stylesheets/foreman_fog_proxmox/accordion.css +21 -0
- data/app/helpers/proxmox_compute_resources_helper.rb +1 -1
- data/app/helpers/proxmox_form_helper.rb +2 -2
- data/app/helpers/proxmox_vm_attrs_helper.rb +131 -0
- data/app/helpers/proxmox_vm_cloudinit_helper.rb +1 -1
- data/app/helpers/proxmox_vm_interfaces_helper.rb +3 -3
- data/app/helpers/proxmox_vm_volumes_helper.rb +3 -3
- data/app/models/concerns/fog_extensions/proxmox/node.rb +1 -1
- data/app/models/foreman_fog_proxmox/proxmox.rb +1 -1
- data/app/models/foreman_fog_proxmox/proxmox_compute_attributes.rb +3 -3
- data/app/models/foreman_fog_proxmox/proxmox_console.rb +1 -1
- data/app/models/foreman_fog_proxmox/proxmox_images.rb +5 -0
- data/app/models/foreman_fog_proxmox/proxmox_version.rb +1 -1
- data/app/models/foreman_fog_proxmox/proxmox_vm_commands.rb +5 -2
- data/app/models/foreman_fog_proxmox/proxmox_vm_new.rb +1 -1
- data/app/overrides/compute_resources_vms/form/add_from_profile_to_compute_attributes_form.rb +8 -0
- data/app/overrides/compute_resources_vms/form/add_react_component_to_host.rb +25 -0
- data/app/overrides/compute_resources_vms/form/update_react_component_to_host_form.rb +25 -0
- data/app/views/compute_resources_vms/form/proxmox/_add_react_component_to_host_form.html.erb +5 -0
- data/app/views/compute_resources_vms/form/proxmox/_base.html.erb +21 -21
- data/app/views/compute_resources_vms/form/proxmox/_update_react_component_to_host_form.html.erb +26 -0
- data/app/views/compute_resources_vms/form/proxmox/container/_config.html.erb +17 -13
- data/app/views/compute_resources_vms/form/proxmox/server/_config.html.erb +20 -20
- data/app/views/compute_resources_vms/form/proxmox/server/_volume_hard_disk.html.erb +4 -1
- data/config/routes.rb +3 -3
- data/lib/foreman_fog_proxmox/version.rb +1 -1
- data/package.json +42 -0
- data/test/factories/proxmox_factory.rb +7 -7
- data/test/unit/foreman_fog_proxmox/proxmox_compute_attributes_test.rb +1 -1
- data/test/unit/foreman_fog_proxmox/proxmox_interfaces_test.rb +6 -6
- data/webpack/components/GeneralTabContent.js +107 -0
- data/webpack/components/ProxmoxComputeSelectors.js +141 -0
- data/webpack/components/ProxmoxContainer/MountPoint.js +91 -0
- data/webpack/components/ProxmoxContainer/ProxmoxContainerHardware.js +85 -0
- data/webpack/components/ProxmoxContainer/ProxmoxContainerNetwork.js +179 -0
- data/webpack/components/ProxmoxContainer/ProxmoxContainerOptions.js +104 -0
- data/webpack/components/ProxmoxContainer/ProxmoxContainerStorage.js +194 -0
- data/webpack/components/ProxmoxContainer/components/NetworkInterface.js +193 -0
- data/webpack/components/ProxmoxServer/ProxmoxServerHardware.js +204 -0
- data/webpack/components/ProxmoxServer/ProxmoxServerNetwork.js +161 -0
- data/webpack/components/ProxmoxServer/ProxmoxServerOptions.js +105 -0
- data/webpack/components/ProxmoxServer/ProxmoxServerStorage.js +272 -0
- data/webpack/components/ProxmoxServer/components/CDRom.js +149 -0
- data/webpack/components/ProxmoxServer/components/CPUFlagsModal.js +88 -0
- data/webpack/components/ProxmoxServer/components/HardDisk.js +143 -0
- data/webpack/components/ProxmoxServer/components/NetworkInterface.js +150 -0
- data/webpack/components/ProxmoxStoragesUtils.js +50 -0
- data/webpack/components/ProxmoxVmType.js +256 -0
- data/webpack/components/ProxmoxVmUtils.js +62 -0
- data/webpack/components/common/FormInputs.js +143 -0
- data/webpack/global_index.js +15 -0
- data/webpack/index.js +7 -0
- metadata +50 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 66a00f7786605946bb2388d497b7a2f512fb29fcf00a12f35bc7059e8b0825f4
|
4
|
+
data.tar.gz: 3e2bf00ae5430cad3c5927ba4636582cd78d83cefa7065088fa4458da638b173
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9534979808ea0e6ad262d696497189b3129e6594478601649d74d475ef1cee060898988e662d50db447f04257b6e6cdcfa6d2d144fea0ef498e66c3a899bd140
|
7
|
+
data.tar.gz: 981cfaa3815ce58182a465490541051b4a79612f0153e3419142bbf5e3c7e58c68b7758b2cac8eaa4bae31a428717d239315695b6847f2d1d207b04f26ff8a41
|
@@ -15,8 +15,6 @@
|
|
15
15
|
// You should have received a copy of the GNU General Public License
|
16
16
|
// along with ForemanFogProxmox. If not, see <http://www.gnu.org/licenses/>.
|
17
17
|
|
18
|
-
//= require jquery-ui/widgets/accordion
|
19
|
-
|
20
18
|
$(document).ready(vmTypeSelected);
|
21
19
|
|
22
20
|
function vmTypeSelected() {
|
@@ -25,13 +23,21 @@ function vmTypeSelected() {
|
|
25
23
|
var host_uuid = $("input[id='host_uuid']").val();
|
26
24
|
var new_vm = host_uuid == undefined;
|
27
25
|
var fieldsets = [];
|
26
|
+
var fieldconfig = [];
|
28
27
|
fieldsets.push({id: 'config_advanced_options', toggle: true, new_vm: new_vm, selected: selected});
|
29
28
|
fieldsets.push({id: 'config_ext', toggle: true, new_vm: new_vm, selected: selected});
|
30
29
|
fieldsets.push({id: 'volume', toggle: true, new_vm: new_vm, selected: selected});
|
31
30
|
fieldsets.push({id: 'network', toggle: true, new_vm: true, selected: selected});
|
31
|
+
fieldconfig.push({id: 'config_options', new_vm: new_vm, selected: selected});
|
32
|
+
fieldconfig.push({id: 'config_cpu', new_vm: new_vm, selected: selected});
|
33
|
+
fieldconfig.push({id: 'config_memory', new_vm: new_vm, selected: selected});
|
34
|
+
fieldconfig.push({id: 'config_cdrom', new_vm: new_vm, selected: selected});
|
35
|
+
fieldconfig.push({id: 'config_os', new_vm: new_vm, selected: selected});
|
36
|
+
fieldconfig.push({id: 'config_dns', new_vm: new_vm, selected: selected});
|
32
37
|
fieldsets.forEach(toggleFieldsets);
|
33
|
-
toggleAccordion(selected);
|
34
38
|
toggleVolumes(selected);
|
39
|
+
fieldconfig.forEach(toggleConfigs);
|
40
|
+
toggleAccordions();
|
35
41
|
return false;
|
36
42
|
}
|
37
43
|
|
@@ -131,7 +137,7 @@ function enableFieldset(fieldsetId, fieldset) {
|
|
131
137
|
input_hidden_id(fieldsetId).removeAttr('disabled');
|
132
138
|
}
|
133
139
|
|
134
|
-
function disableFieldset(fieldsetId, fieldset) {
|
140
|
+
function disableFieldset(fieldsetId, fieldset) {
|
135
141
|
if (fieldset.toggle && fieldset.new_vm){
|
136
142
|
fieldset_id(fieldsetId, fieldset).hide();
|
137
143
|
}
|
@@ -139,57 +145,72 @@ function disableFieldset(fieldsetId, fieldset) {
|
|
139
145
|
input_hidden_id(fieldsetId).attr('disabled','disabled');
|
140
146
|
}
|
141
147
|
|
142
|
-
function
|
143
|
-
|
144
|
-
field.accordion({collapsible : true, heightStyle: "content"});
|
145
|
-
field.removeClass('disabled').find("*").prop("disabled", false);
|
146
|
-
field.removeClass('hide');
|
148
|
+
function toggleFieldset(fieldsetId, fieldset, type1, type2) {
|
149
|
+
type1 === type2 ? enableFieldset(fieldsetId, fieldset) : disableFieldset(fieldsetId, fieldset);
|
147
150
|
}
|
148
151
|
|
149
|
-
function
|
150
|
-
|
151
|
-
field.addClass('disabled').find("*").prop("disabled", true);
|
152
|
-
field.addClass('hide');
|
152
|
+
function input_hidden_id(volume_id){
|
153
|
+
return $("div[id^='"+ volume_id +"_volumes']" + " + input:hidden");
|
153
154
|
}
|
154
155
|
|
155
|
-
function
|
156
|
-
|
157
|
-
enableConfigOptions(fieldsetId);
|
158
|
-
} else {
|
159
|
-
disableConfigOptions(fieldsetId);
|
160
|
-
}
|
156
|
+
function fieldset_id(fieldsetId, fieldset){
|
157
|
+
return $("fieldset[id^='" + fieldsetId + "_"+fieldset.id+"']");
|
161
158
|
}
|
162
159
|
|
163
|
-
function
|
160
|
+
function fieldsets(type){
|
161
|
+
return type === 'qemu' ? ['server'] : ['container'];
|
162
|
+
}
|
163
|
+
|
164
|
+
function toggleFieldsets(fieldset){
|
165
|
+
var removable_input_hidden = $("div.removable-item[style='display: none;']" + " + input:hidden");
|
166
|
+
removable_input_hidden.attr('disabled','disabled');
|
164
167
|
['qemu', 'lxc'].forEach(function(type){
|
165
168
|
fieldsets(type).forEach(function(fieldsetId){
|
166
|
-
|
169
|
+
toggleFieldset(fieldsetId, fieldset, fieldset.selected, type);
|
167
170
|
});
|
168
171
|
});
|
169
172
|
}
|
170
173
|
|
171
|
-
function
|
172
|
-
|
174
|
+
function toggleAccordions() {
|
175
|
+
$('.accordion-content').hide();
|
176
|
+
$('.accordion-section').off('click').on('click', function(event) {
|
177
|
+
var $content = $(this).find('.accordion-content');
|
178
|
+
$content.slideToggle();
|
179
|
+
$(this).toggleClass('active');
|
180
|
+
$('.accordion-content').not($content).slideUp();
|
181
|
+
$('.accordion-section').not($(this)).removeClass('active');
|
182
|
+
event.stopPropagation();
|
183
|
+
});
|
184
|
+
$('.accordion-content').on('click', function(event) {
|
185
|
+
event.stopPropagation();
|
186
|
+
});
|
173
187
|
}
|
174
188
|
|
175
|
-
function
|
176
|
-
|
189
|
+
function enableConfig(fieldsetId, fieldset) {
|
190
|
+
fieldset_id(fieldsetId, fieldset).removeClass('hide');
|
191
|
+
fieldset_id(fieldsetId, fieldset).removeAttr('disabled');
|
192
|
+
input_hidden_id(fieldsetId).removeAttr('disabled');
|
193
|
+
|
177
194
|
}
|
178
195
|
|
179
|
-
function
|
180
|
-
|
196
|
+
function disableConfig(fieldsetId, fieldset) {
|
197
|
+
fieldset_id(fieldsetId, fieldset).addClass('hide');
|
198
|
+
fieldset_id(fieldsetId, fieldset).attr('disabled','disabled');
|
199
|
+
input_hidden_id(fieldsetId).attr('disabled','disabled');
|
181
200
|
}
|
182
201
|
|
183
|
-
function
|
184
|
-
|
202
|
+
function toggleConfig(fieldsetId, fieldset, type1, type2) {
|
203
|
+
if (type1 === type2) {
|
204
|
+
enableConfig(fieldsetId, fieldset);
|
205
|
+
} else {
|
206
|
+
disableConfig(fieldsetId, fieldset);
|
207
|
+
}
|
185
208
|
}
|
186
209
|
|
187
|
-
function
|
188
|
-
var removable_input_hidden = $("div.removable-item[style='display: none;']" + " + input:hidden");
|
189
|
-
removable_input_hidden.attr('disabled','disabled');
|
210
|
+
function toggleConfigs(fieldset){
|
190
211
|
['qemu', 'lxc'].forEach(function(type){
|
191
212
|
fieldsets(type).forEach(function(fieldsetId){
|
192
|
-
|
213
|
+
toggleConfig(fieldsetId, fieldset, fieldset.selected, type);
|
193
214
|
});
|
194
215
|
});
|
195
216
|
}
|
@@ -32,7 +32,7 @@ function getIndex(item) {
|
|
32
32
|
}
|
33
33
|
|
34
34
|
function isProfile() {
|
35
|
-
return $(volumesAttributesSelector(true,0,'id')) !== undefined;
|
35
|
+
return $(volumesAttributesSelector(true,0,'id')).val() !== undefined;
|
36
36
|
}
|
37
37
|
|
38
38
|
function controllerSelected(item) {
|
@@ -42,23 +42,43 @@ function controllerSelected(item) {
|
|
42
42
|
var profile = isProfile();
|
43
43
|
var device_selector = volumesAttributesSelector(profile,index,'device');
|
44
44
|
var id_selector = volumesAttributesSelector(profile,index,'id');
|
45
|
-
$(device_selector).
|
46
|
-
var device = $(device_selector).limitedSpinner('value');
|
45
|
+
var device = $(device_selector).val();
|
47
46
|
var id = controller + device;
|
47
|
+
validateDeviceLimit(device_selector, max);
|
48
48
|
$(id_selector).val(id);
|
49
49
|
}
|
50
50
|
|
51
51
|
function deviceSelected(item) {
|
52
|
-
var device = $(item).
|
52
|
+
var device = $(item).val();
|
53
53
|
var index = getIndex(item);
|
54
54
|
var profile = isProfile();
|
55
55
|
var controller_selector = volumesAttributesSelector(profile,index,'controller');
|
56
56
|
var id_selector = volumesAttributesSelector(profile,index,'id');
|
57
57
|
var controller = $(controller_selector).val();
|
58
58
|
var id = controller + device;
|
59
|
+
var max = computeControllerMaxDevice(controller);
|
60
|
+
validateDeviceLimit(item, max);
|
59
61
|
$(id_selector).val(id);
|
60
62
|
}
|
61
63
|
|
64
|
+
function validateDeviceLimit(item, maxLimit) {
|
65
|
+
var warningContainer = $(item).closest('form').find('.warning-container');
|
66
|
+
var deviceNumber = $(item).val();
|
67
|
+
var submitButton = $(item).closest('form').find('[data-disable-with]');
|
68
|
+
if (!isNaN(maxLimit) && !isNaN(deviceNumber) && deviceNumber > maxLimit) {
|
69
|
+
if (warningContainer.length === 0) {
|
70
|
+
warningContainer = $('<div class="warning-container" style="color: red;"><span style="color: red;">⚠</span> Warning: Value exceeds the maximum limit of ' + maxLimit + '.</div>');
|
71
|
+
$(item).closest('.col-md-4').append(warningContainer);
|
72
|
+
}
|
73
|
+
warningContainer.show();
|
74
|
+
submitButton.prop('disabled', true);
|
75
|
+
} else {
|
76
|
+
warningContainer.hide();
|
77
|
+
warningContainer.remove();
|
78
|
+
submitButton.prop('disabled', false);
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
62
82
|
function computeControllerMaxDevice(controller) {
|
63
83
|
switch (controller) {
|
64
84
|
case 'ide':
|
@@ -72,4 +92,4 @@ function computeControllerMaxDevice(controller) {
|
|
72
92
|
default:
|
73
93
|
return 1;
|
74
94
|
}
|
75
|
-
}
|
95
|
+
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
.accordion-section {
|
2
|
+
cursor: pointer;
|
3
|
+
}
|
4
|
+
|
5
|
+
.accordion-section > legend::before {
|
6
|
+
content: "\f054";
|
7
|
+
font-family: "Font Awesome 5 Free";
|
8
|
+
position: relative;
|
9
|
+
left: -1em;
|
10
|
+
top: 50%;
|
11
|
+
transform: translateY(-50%);
|
12
|
+
}
|
13
|
+
|
14
|
+
.accordion-section.active > legend::before {
|
15
|
+
content: "\f078";
|
16
|
+
font-family: "Font Awesome 5 Free";
|
17
|
+
position: relative;
|
18
|
+
left: -1em;
|
19
|
+
top: 50%;
|
20
|
+
transform: translateY(-50%);
|
21
|
+
}
|
@@ -34,7 +34,7 @@ module ProxmoxComputeResourcesHelper
|
|
34
34
|
def cluster_nodes(compute_resource)
|
35
35
|
nodes = compute_resource.nodes ? compute_resource.nodes.collect(&:node) : []
|
36
36
|
rescue ::Foreman::Exception => e
|
37
|
-
|
37
|
+
[] if e.message == 'User token expired'
|
38
38
|
rescue StandardError => e
|
39
39
|
logger.warn("failed to get cluster nodes: #{e}")
|
40
40
|
raise e
|
@@ -30,7 +30,7 @@ module ProxmoxFormHelper
|
|
30
30
|
addClass options, 'form-control'
|
31
31
|
pass = f.password_field(attr, options) +
|
32
32
|
tag(:span, '', class: 'glyphicon glyphicon-warning-sign input-addon', title: 'Caps lock ON',
|
33
|
-
style: 'display:none')
|
33
|
+
style: 'display:none')
|
34
34
|
if unset_button
|
35
35
|
button = link_to_function(icon_text('edit', '', :kind => 'pficon'), 'toggle_input_group(this)',
|
36
36
|
:id => 'disable-pass-btn', :class => 'btn btn-default', :title => _('Change the password'))
|
@@ -56,7 +56,7 @@ style: 'display:none')
|
|
56
56
|
options[:form_builder_attrs] ||= {}
|
57
57
|
|
58
58
|
content_tag(:div, :class => "#{options[:type]}_#{association}_fields_template form_template",
|
59
|
-
:style => 'display: none;') do
|
59
|
+
:style => 'display: none;') do
|
60
60
|
form_builder.fields_for(association, options[:object],
|
61
61
|
:child_index => "new_#{options[:type]}_#{association}") do |f|
|
62
62
|
render(:partial => options[:partial], :layout => options[:layout],
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2018 Tristan Robert
|
4
|
+
|
5
|
+
# This file is part of ForemanFogProxmox.
|
6
|
+
|
7
|
+
# ForemanFogProxmox is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU General Public License as published by
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
|
12
|
+
# ForemanFogProxmox is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
|
17
|
+
# You should have received a copy of the GNU General Public License
|
18
|
+
# along with ForemanFogProxmox. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
|
20
|
+
require 'fog/proxmox/helpers/disk_helper'
|
21
|
+
require 'fog/proxmox/helpers/nic_helper'
|
22
|
+
require 'fog/proxmox/helpers/cpu_helper'
|
23
|
+
require 'foreman_fog_proxmox/value'
|
24
|
+
require 'foreman_fog_proxmox/hash_collection'
|
25
|
+
|
26
|
+
# Convert a foreman form server hash into a fog-proxmox server attributes hash
|
27
|
+
module ProxmoxVmAttrsHelper
|
28
|
+
def object_to_attributes_hash(vms, from_profile, start_checked)
|
29
|
+
param_scope = from_profile ? "compute_attribute[vm_attrs]" : "host[compute_attributes]"
|
30
|
+
vm_h = ActiveSupport::HashWithIndifferentAccess.new
|
31
|
+
keys = [:vmid, :node_id, :type, :pool]
|
32
|
+
main = vms.attributes.select { |key, _value| keys.include? key }
|
33
|
+
vms.config.all_attributes.each do |key, value|
|
34
|
+
camel_key = key.to_s.include?('_') ? snake_to_camel(key.to_s).to_sym : key
|
35
|
+
vm_h[camel_key] = { :name => "#{param_scope}[config_attributes][#{key}]", :value => value } unless keys.include? key
|
36
|
+
end
|
37
|
+
main.each do |key, value|
|
38
|
+
camel_key = key.to_s.include?('_') ? snake_to_camel(key.to_s).to_sym : key
|
39
|
+
vm_h[camel_key] = { :name => "#{param_scope}[#{key}]", :value => value } if keys.include? key
|
40
|
+
end
|
41
|
+
|
42
|
+
vm_h.merge!(additional_attrs(vms, param_scope, start_checked))
|
43
|
+
vm_h[:interfaces] = network_attrs(param_scope, vms.interfaces)
|
44
|
+
vm_h[:disks] = volumes_attrs(param_scope, vms.volumes)
|
45
|
+
vm_h.merge(cpu_flags_attrs(param_scope, vms.config))
|
46
|
+
end
|
47
|
+
|
48
|
+
def cpu_flags_attrs(param_scope, config)
|
49
|
+
flag_attrs = ActiveSupport::HashWithIndifferentAccess.new
|
50
|
+
Fog::Proxmox::CpuHelper.flags.each do |key, _val|
|
51
|
+
flag_attrs.merge!({ key => { :name => "#{param_scope}[config_attributes][#{key}]", :value => config.public_send(key) } })
|
52
|
+
end
|
53
|
+
flag_attrs
|
54
|
+
end
|
55
|
+
|
56
|
+
def volumes_attrs(param_scope, volumes)
|
57
|
+
vol_attrs = []
|
58
|
+
volumes.each_with_index do |vol, id|
|
59
|
+
keys = []
|
60
|
+
type = ""
|
61
|
+
if vol.rootfs?
|
62
|
+
keys = ['id', 'volid', 'storage', 'size', 'storage_type']
|
63
|
+
type = 'rootfs'
|
64
|
+
elsif vol.hard_disk?
|
65
|
+
keys = ['id', 'volid', 'storage_type', 'storage', 'controller', 'device', 'cache', 'size']
|
66
|
+
type = 'hard_disk'
|
67
|
+
elsif vol.cdrom?
|
68
|
+
keys = ['id', 'storage_type', 'cdrom', 'storage', 'volid']
|
69
|
+
type = 'cdrom'
|
70
|
+
elsif vol.cloud_init?
|
71
|
+
keys = ['id', 'volid', 'storage_type', 'storage', 'controller', 'device']
|
72
|
+
type = 'cloud_init'
|
73
|
+
elsif vol.mount_point?
|
74
|
+
keys = ['id', 'volid', 'storage_type', 'storage', 'device', 'mp', 'size']
|
75
|
+
type = 'mount_point'
|
76
|
+
end
|
77
|
+
vol_attrs << { :name => type, :value => vol_keys(param_scope, keys, vol, id) }
|
78
|
+
end
|
79
|
+
vol_attrs
|
80
|
+
end
|
81
|
+
|
82
|
+
def vol_keys(param_scope, keys, vol, id)
|
83
|
+
attrs = ActiveSupport::HashWithIndifferentAccess.new
|
84
|
+
keys.each do |key|
|
85
|
+
camel_key = key.to_s.include?('_') ? snake_to_camel(key.to_s).to_sym : key
|
86
|
+
attrs[camel_key] = { :name => "#{param_scope}[volumes_attributes][#{id}][#{key}]", :value => vol.public_send(key) }
|
87
|
+
end
|
88
|
+
attrs
|
89
|
+
end
|
90
|
+
|
91
|
+
def network_attrs(param_scope, interfaces)
|
92
|
+
networks_attrs = []
|
93
|
+
interfaces.each_with_index do |interface, id|
|
94
|
+
attrs = ActiveSupport::HashWithIndifferentAccess.new
|
95
|
+
interface.all_attributes.each do |key, value|
|
96
|
+
camel_key = key.to_s.include?('_') ? snake_to_camel(key.to_s).to_sym : key
|
97
|
+
attrs[camel_key] = { :name => "#{param_scope}[interfaces_attributes][#{id}][#{key}]", :value => value }
|
98
|
+
end
|
99
|
+
networks_attrs << { :name => 'interface', :value => attrs }
|
100
|
+
end
|
101
|
+
networks_attrs
|
102
|
+
end
|
103
|
+
|
104
|
+
def additional_attrs(vms, param_scope, start_checked)
|
105
|
+
attributes = {
|
106
|
+
pool: vms.pool,
|
107
|
+
image_id: vms.image_id,
|
108
|
+
cpu_type: vms.config.cpu_type,
|
109
|
+
nameserver: vms.config.nameserver,
|
110
|
+
searchdomain: vms.config.searchdomain,
|
111
|
+
hostname: vms.config.hostname,
|
112
|
+
ostemplate_storage: vms.ostemplate_storage,
|
113
|
+
ostemplate_file: vms.ostemplate_file,
|
114
|
+
start_after_create: vms.start_after_create,
|
115
|
+
templated: vms.templated,
|
116
|
+
}
|
117
|
+
vms_keys = [:pool, :start_after_create]
|
118
|
+
extra_attrs = ActiveSupport::HashWithIndifferentAccess.new
|
119
|
+
attributes.each do |key, value|
|
120
|
+
camel_key = key.to_s.include?('_') ? snake_to_camel(key.to_s).to_sym : key
|
121
|
+
nested_key = vms_keys.include?(key) ? key : "config_attributes[#{key}]"
|
122
|
+
value = start_checked if key == :start_after_create
|
123
|
+
extra_attrs[camel_key] = { name: "#{param_scope}[#{nested_key}]", value: value }
|
124
|
+
end
|
125
|
+
extra_attrs
|
126
|
+
end
|
127
|
+
|
128
|
+
def snake_to_camel(str)
|
129
|
+
str.split('_').inject([]) { |buffer, e| buffer.push(buffer.empty? ? e : e.capitalize) }.join
|
130
|
+
end
|
131
|
+
end
|
@@ -48,7 +48,7 @@ module ProxmoxVmCloudinitHelper
|
|
48
48
|
wd = create_temp_directory(ssh)
|
49
49
|
|
50
50
|
configs.each do |config|
|
51
|
-
config_file = ssh.run(%(echo
|
51
|
+
config_file = ssh.run(%(echo '#{config[1]}' >> "#{wd}/#{config[0]}"))
|
52
52
|
unless config_file.first.status.zero?
|
53
53
|
delete_temp_dir(ssh, wd)
|
54
54
|
raise ::Foreman::Exception, "Failed to create file #{config[0]}: #{config_file.first.stdout}"
|
@@ -56,13 +56,13 @@ module ProxmoxVmInterfacesHelper
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def interface_common_typed_keys(type)
|
59
|
-
['id', type == 'qemu' ? 'macaddr' : 'hwaddr']
|
59
|
+
['id', (type == 'qemu') ? 'macaddr' : 'hwaddr']
|
60
60
|
end
|
61
61
|
|
62
62
|
def compute_dhcps(interface_attributes_h)
|
63
|
-
interface_attributes_h[:dhcp] = interface_attributes_h[:ip] == 'dhcp' ? '1' : '0'
|
63
|
+
interface_attributes_h[:dhcp] = (interface_attributes_h[:ip] == 'dhcp') ? '1' : '0'
|
64
64
|
interface_attributes_h[:ip] = '' if interface_attributes_h[:dhcp] == '1'
|
65
|
-
interface_attributes_h[:dhcp6] = interface_attributes_h[:ip6] == 'dhcp' ? '1' : '0'
|
65
|
+
interface_attributes_h[:dhcp6] = (interface_attributes_h[:ip6] == 'dhcp') ? '1' : '0'
|
66
66
|
interface_attributes_h[:ip6] = '' if interface_attributes_h[:dhcp6] == '1'
|
67
67
|
end
|
68
68
|
|
@@ -52,9 +52,9 @@ module ProxmoxVmVolumesHelper
|
|
52
52
|
logger.debug(format(_('parse_hard_disk_volume(): args=%<args>s'), args: args))
|
53
53
|
disk = {}
|
54
54
|
disk[:id] = args['id'] if args.key?('id')
|
55
|
-
disk[:volid] = args['volid'] if args.key?('volid')
|
56
|
-
disk[:storage] = args['storage'].to_s if args.key?('storage')
|
57
|
-
disk[:size] = args['size'].to_i if args.key?('size')
|
55
|
+
disk[:volid] = args['volid'] if args.key?('volid') && !args['volid'].empty?
|
56
|
+
disk[:storage] = args['storage'].to_s if args.key?('storage') && !args['storage'].empty?
|
57
|
+
disk[:size] = args['size'].to_i if args.key?('size') && !args['size'].empty?
|
58
58
|
add_disk_options(disk, args) unless args.key?('options')
|
59
59
|
disk[:options] = args['options'] if args.key?('options')
|
60
60
|
disk.key?(:storage) ? disk : {}
|
@@ -31,7 +31,7 @@ module FogExtensions
|
|
31
31
|
# We need to explicitly use the base 'each' method here on the page,
|
32
32
|
# otherwise we get infinite recursion
|
33
33
|
base_each = Fog::Collection.instance_method(:each)
|
34
|
-
base_each.
|
34
|
+
base_each.bind_call(page, &block)
|
35
35
|
end
|
36
36
|
end
|
37
37
|
self
|
@@ -30,7 +30,7 @@ module ForemanFogProxmox
|
|
30
30
|
unless compute_os_types(host).include?(ostype)
|
31
31
|
raise ::Foreman::Exception,
|
32
32
|
format(_('Operating system family %<type>s is not consistent with %<ostype>s'), type: host.operatingsystem.type,
|
33
|
-
ostype: ostype)
|
33
|
+
ostype: ostype)
|
34
34
|
end
|
35
35
|
end
|
36
36
|
super
|
@@ -42,8 +42,8 @@ ostype: ostype)
|
|
42
42
|
|
43
43
|
def interface_compute_attributes(interface_attributes)
|
44
44
|
vm_attrs = ForemanFogProxmox::HashCollection.new_hash_reject_keys(interface_attributes, [:identifier, :macaddr, :hwaddr])
|
45
|
-
vm_attrs[:dhcp] = interface_attributes[:ip] == 'dhcp' ? '1' : '0'
|
46
|
-
vm_attrs[:dhcp6] = interface_attributes[:ip6] == 'dhcp' ? '1' : '0'
|
45
|
+
vm_attrs[:dhcp] = (interface_attributes[:ip] == 'dhcp') ? '1' : '0'
|
46
|
+
vm_attrs[:dhcp6] = (interface_attributes[:ip6] == 'dhcp') ? '1' : '0'
|
47
47
|
vm_attrs
|
48
48
|
end
|
49
49
|
|
@@ -33,7 +33,7 @@ module ForemanFogProxmox
|
|
33
33
|
vnc_console = vm.start_console(options)
|
34
34
|
vmid = extract_vmid(uuid).to_i
|
35
35
|
vnc_host_port = vnc_console['port'].to_i + vmid
|
36
|
-
WsProxy.start(:host =>
|
36
|
+
WsProxy.start(:host => proxmox_host, :host_port => vnc_host_port, :password => vnc_console['ticket']).merge(
|
37
37
|
:name => vm.name, :type => type_console
|
38
38
|
)
|
39
39
|
rescue StandardError => e
|
@@ -48,9 +48,14 @@ module ForemanFogProxmox
|
|
48
48
|
volumes = []
|
49
49
|
nodes.each do |node|
|
50
50
|
storages(node.node).each do |storage|
|
51
|
+
# fetches volumes of QEMU servers for images
|
51
52
|
volumes += storage.volumes.list_by_content_type('images')
|
53
|
+
|
54
|
+
# fetches volumes of KVM containers for images
|
55
|
+
volumes += storage.volumes.list_by_content_type('rootdir')
|
52
56
|
end
|
53
57
|
end
|
58
|
+
# for creating image, only list volumes which are templated
|
54
59
|
volumes.select(&:template?)
|
55
60
|
end
|
56
61
|
|
@@ -36,7 +36,7 @@ module ForemanFogProxmox
|
|
36
36
|
v = identity_client.read_version if identity_client
|
37
37
|
v ? v['version'] : 'Unknown'
|
38
38
|
rescue ::Foreman::Exception => e
|
39
|
-
|
39
|
+
'Unkown' if e.message == 'User token expired'
|
40
40
|
rescue StandardError => e
|
41
41
|
logger.warn("failed to get identity client version: #{e}")
|
42
42
|
raise e
|
@@ -32,10 +32,9 @@ module ForemanFogProxmox
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def create_vm(args = {})
|
35
|
-
vmid = args[:vmid].to_i
|
36
35
|
type = args[:type]
|
37
36
|
node = client.nodes.get(args[:node_id])
|
38
|
-
vmid =
|
37
|
+
vmid = args[:vmid] = assign_vmid(args[:vmid].to_i, node)
|
39
38
|
raise ::Foreman::Exception, format(N_('invalid vmid=%<vmid>s'), vmid: vmid) unless node.servers.id_valid?(vmid)
|
40
39
|
|
41
40
|
image_id = args[:image_id]
|
@@ -55,6 +54,10 @@ module ForemanFogProxmox
|
|
55
54
|
raise e
|
56
55
|
end
|
57
56
|
|
57
|
+
def assign_vmid(vmid, node)
|
58
|
+
(vmid < 1) ? node.servers.next_id : vmid
|
59
|
+
end
|
60
|
+
|
58
61
|
def compute_clone_attributes(args, container, type)
|
59
62
|
args = parse_cloudinit_config(args) if args[:user_data]
|
60
63
|
parsed_args = parse_typed_vm(args, type)
|
@@ -191,7 +191,7 @@ module ForemanFogProxmox
|
|
191
191
|
new_attr_type ||= type
|
192
192
|
logger.debug("new_typed_vm(#{type}): new_attr_type=#{new_attr_type}")
|
193
193
|
logger.debug("new_typed_vm(#{type}): new_attr=#{new_attr}'")
|
194
|
-
options = !new_attr.key?('vmid') || ForemanFogProxmox::Value.empty?(new_attr['vmid']) ? vm_typed_instance_defaults(type).merge(new_attr).merge(type: type) : new_attr
|
194
|
+
options = (!new_attr.key?('vmid') || ForemanFogProxmox::Value.empty?(new_attr['vmid'])) ? vm_typed_instance_defaults(type).merge(new_attr).merge(type: type) : new_attr
|
195
195
|
logger.debug("new_typed_vm(#{type}): options=#{options}")
|
196
196
|
vm_h = parse_typed_vm(options, type).deep_symbolize_keys
|
197
197
|
logger.debug("new_typed_vm(#{type}): vm_h=#{vm_h}")
|
data/app/overrides/compute_resources_vms/form/add_from_profile_to_compute_attributes_form.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Copyright 2018 Tristan Robert
|
2
4
|
|
3
5
|
# This file is part of ForemanFogProxmox.
|
@@ -15,6 +17,12 @@
|
|
15
17
|
# You should have received a copy of the GNU General Public License
|
16
18
|
# along with ForemanFogProxmox. If not, see <http://www.gnu.org/licenses/>.
|
17
19
|
|
20
|
+
Deface::Override.new(
|
21
|
+
virtual_path: 'compute_attributes/_compute_form',
|
22
|
+
name: 'remove_networks_and_volumes_partial',
|
23
|
+
remove: "erb[loud]:contains('compute_resources_vms/form/networks'), erb[loud]:contains('compute_resources_vms/form/volumes')"
|
24
|
+
)
|
25
|
+
|
18
26
|
Deface::Override.new(
|
19
27
|
:virtual_path => 'compute_attributes/_form',
|
20
28
|
:name => 'add_from_profile_to_compute_attributes_form',
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2018 Tristan Robert
|
4
|
+
|
5
|
+
# This file is part of ForemanFogProxmox.
|
6
|
+
|
7
|
+
# ForemanFogProxmox is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU General Public License as published by
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
|
12
|
+
# ForemanFogProxmox is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
|
17
|
+
# You should have received a copy of the GNU General Public License
|
18
|
+
# along with ForemanFogProxmox. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
|
20
|
+
Deface::Override.new(
|
21
|
+
virtual_path: 'hosts/_form',
|
22
|
+
name: 'add_react_component_to_virtual_machine_tab',
|
23
|
+
insert_after: "li.active",
|
24
|
+
partial: 'compute_resources_vms/form/proxmox/add_react_component_to_host_form'
|
25
|
+
)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2018 Tristan Robert
|
4
|
+
|
5
|
+
# This file is part of ForemanFogProxmox.
|
6
|
+
|
7
|
+
# ForemanFogProxmox is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU General Public License as published by
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
|
12
|
+
# ForemanFogProxmox is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
|
17
|
+
# You should have received a copy of the GNU General Public License
|
18
|
+
# along with ForemanFogProxmox. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
|
20
|
+
Deface::Override.new(
|
21
|
+
:virtual_path => 'hosts/_compute',
|
22
|
+
:name => 'update_react_component_to_virtual_machine_tab',
|
23
|
+
:replace => "erb[loud]:contains('hosts/compute_detail')",
|
24
|
+
:partial => 'compute_resources_vms/form/proxmox/update_react_component_to_host_form'
|
25
|
+
)
|
@@ -0,0 +1,5 @@
|
|
1
|
+
<% content_for(:javascripts) do %>
|
2
|
+
<%= webpacked_plugins_js_for :foreman_fog_proxmox %>
|
3
|
+
<%= javascript_include_tag 'foreman_fog_proxmox/proxmox_vm', "data-turbolinks-track" => true %>
|
4
|
+
<% end %>
|
5
|
+
<%= react_component('ProxmoxVmType', { registerComp: true }) unless @host.managed %>
|