mirror of
https://github.com/ComputerScienceHouse/proxstar.git
synced 2025-02-14 14:01:51 +00:00
Merge pull request #111 from harmonherring/boot-order
Boot order handling
This commit is contained in:
commit
8e5f173a94
6 changed files with 178 additions and 27 deletions
|
@ -133,3 +133,15 @@ table, th, td {
|
||||||
#show-for-template {
|
#show-for-template {
|
||||||
display: none
|
display: none
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.boot-order-check {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.borderless td, .borderless th, table.borderless {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fa-bars {
|
||||||
|
cursor: move;
|
||||||
|
}
|
|
@ -815,24 +815,10 @@ $("#edit-boot-order").click(function(){
|
||||||
const vmid = $(this).data('vmid');
|
const vmid = $(this).data('vmid');
|
||||||
const vmname = $(this).data('vmname');
|
const vmname = $(this).data('vmname');
|
||||||
const boot_order = $(this).data('boot_order');
|
const boot_order = $(this).data('boot_order');
|
||||||
var options = document.createElement('div');
|
var options = renderBootOrder(boot_order);
|
||||||
for (i = 0; i < boot_order.length; i++) {
|
|
||||||
text = document.createElement('span');
|
|
||||||
text.innerHTML = `${i + 1}. `;
|
|
||||||
options.append(text);
|
|
||||||
var entry = document.createElement('select');
|
|
||||||
entry.setAttribute("id", `boot-order-${i + 1}`);
|
|
||||||
for (j = 0; j < boot_order.length; j++) {
|
|
||||||
entry.appendChild(new Option(boot_order[j], boot_order[j]));
|
|
||||||
}
|
|
||||||
entry.selectedIndex = i;
|
|
||||||
entry.setAttribute('style', 'width: 85px');
|
|
||||||
options.append(entry);
|
|
||||||
options.append(document.createElement('br'));
|
|
||||||
}
|
|
||||||
swal({
|
swal({
|
||||||
title: `Select the new boot order for ${vmname} (full shutdown required for settings to take effect):`,
|
title: `Select the new boot order for ${vmname} (full shutdown required for settings to take effect):`,
|
||||||
content: options,
|
content: renderBootOrder(boot_order),
|
||||||
buttons: {
|
buttons: {
|
||||||
cancel: {
|
cancel: {
|
||||||
text: "Cancel",
|
text: "Cancel",
|
||||||
|
@ -848,9 +834,18 @@ $("#edit-boot-order").click(function(){
|
||||||
.then((willChange) => {
|
.then((willChange) => {
|
||||||
if (willChange) {
|
if (willChange) {
|
||||||
var data = new FormData();
|
var data = new FormData();
|
||||||
for (k = 0; k < boot_order.length; k++) {
|
if (boot_order.legacy) {
|
||||||
e = document.getElementById(`boot-order-${k + 1}`);
|
for (k = 0; k < boot_order.order.length; k++) {
|
||||||
data.append(`${k + 1}`, e.options[e.selectedIndex].value);
|
e = document.getElementById(`boot-order-${k + 1}`);
|
||||||
|
data.append(`${k + 1}`, e.options[e.selectedIndex].value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
document.getElementById('boot-order-sortable').childNodes.forEach((order, index) => {
|
||||||
|
if (order.children[1].firstChild.checked) {
|
||||||
|
data.append(`${index + 1}`, order.children[2].innerHTML);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
fetch(`/vm/${vmid}/boot_order`, {
|
fetch(`/vm/${vmid}/boot_order`, {
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
|
@ -880,6 +875,81 @@ $("#edit-boot-order").click(function(){
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function renderBootOrder(boot_order) {
|
||||||
|
let options = document.createElement('div');
|
||||||
|
if (boot_order.legacy) {
|
||||||
|
for (i = 0; i < boot_order.order.length; i++) {
|
||||||
|
text = document.createElement('span');
|
||||||
|
text.innerHTML = `${i + 1}. `;
|
||||||
|
options.append(text);
|
||||||
|
var entry = document.createElement('select');
|
||||||
|
entry.setAttribute("id", `boot-order-${i + 1}`);
|
||||||
|
for (j = 0; j < boot_order.order.length; j++) {
|
||||||
|
entry.appendChild(new Option(boot_order.order[j].device, boot_order.order[j].device));
|
||||||
|
}
|
||||||
|
entry.selectedIndex = i;
|
||||||
|
entry.setAttribute('style', 'width: 85px');
|
||||||
|
options.append(entry);
|
||||||
|
options.append(document.createElement('br'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let table = document.createElement('table');
|
||||||
|
table.classList.add('table', 'table-sm', 'borderless', 'text-left');
|
||||||
|
let thead = table.createTHead();
|
||||||
|
thead.classList.add('font-weight-bold');
|
||||||
|
let tbody = table.createTBody();
|
||||||
|
tbody.classList.add('text-break', 'boot-order-sortable');
|
||||||
|
tbody.id = 'boot-order-sortable';
|
||||||
|
let hrow = thead.insertRow();
|
||||||
|
hrow.insertCell().innerHTML = 'Order';
|
||||||
|
hrow.insertCell().innerHTML = 'Enabled';
|
||||||
|
hrow.insertCell().innerHTML = 'Device';
|
||||||
|
hrow.insertCell().innerHTML = 'Description';
|
||||||
|
for (i = 0; i < boot_order.order.length; i++) {
|
||||||
|
let row = tbody.insertRow();
|
||||||
|
row.id = `boot-order-${i + 1}`;
|
||||||
|
$(row.insertCell()).append(
|
||||||
|
$('<i>', {
|
||||||
|
class: 'fas fa-bars'
|
||||||
|
}),
|
||||||
|
$('<span>', {
|
||||||
|
class: 'boot-order-number',
|
||||||
|
id: `boot-order-number-${i + 1}`,
|
||||||
|
style: 'margin-left: .25rem',
|
||||||
|
text: `${i + 1}`
|
||||||
|
})
|
||||||
|
);
|
||||||
|
let checkCell = $(row.insertCell()).addClass('text-center');
|
||||||
|
$(checkCell).append(
|
||||||
|
$('<input>', {
|
||||||
|
type: 'checkbox',
|
||||||
|
class: 'form-check-input boot-order-check',
|
||||||
|
checked: boot_order.order[i].enabled
|
||||||
|
})
|
||||||
|
);
|
||||||
|
row.insertCell().innerHTML = boot_order.order[i].device;
|
||||||
|
row.insertCell().innerHTML = boot_order.order[i].description;
|
||||||
|
}
|
||||||
|
new Sortable(tbody, {
|
||||||
|
animation: 150,
|
||||||
|
filter: '.boot-order-check',
|
||||||
|
onEnd: function(event) {
|
||||||
|
numberBootOrderTable();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
options.append(table);
|
||||||
|
}
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
function numberBootOrderTable() {
|
||||||
|
let i = 0;
|
||||||
|
$('[id^=boot-order-number]').each(function() {
|
||||||
|
this.innerHTML = ++i;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
$(document).on('focus click', "[id^=boot-order-]", function() {
|
$(document).on('focus click', "[id^=boot-order-]", function() {
|
||||||
previous = $(this).val();
|
previous = $(this).val();
|
||||||
}).on('change', "[id^=boot-order-]", function() {
|
}).on('change', "[id^=boot-order-]", function() {
|
||||||
|
|
|
@ -92,7 +92,6 @@ def create_vm_task(user, name, cores, memory, disk, iso):
|
||||||
register_starrs(starrs, name, app.config['STARRS_USER'], vm.get_mac(), ip)
|
register_starrs(starrs, name, app.config['STARRS_USER'], vm.get_mac(), ip)
|
||||||
set_job_status(job, 'setting VM expiration')
|
set_job_status(job, 'setting VM expiration')
|
||||||
get_vm_expire(db, vmid, app.config['VM_EXPIRE_MONTHS'])
|
get_vm_expire(db, vmid, app.config['VM_EXPIRE_MONTHS'])
|
||||||
vm.set_boot_order(['Hard Disk', 'CD-ROM', 'Network'])
|
|
||||||
logging.info('[{}] VM successfully provisioned.'.format(name))
|
logging.info('[{}] VM successfully provisioned.'.format(name))
|
||||||
set_job_status(job, 'complete')
|
set_job_status(job, 'complete')
|
||||||
|
|
||||||
|
|
|
@ -112,6 +112,10 @@
|
||||||
<script defer src="https://use.fontawesome.com/releases/v5.0.8/js/fontawesome.js"
|
<script defer src="https://use.fontawesome.com/releases/v5.0.8/js/fontawesome.js"
|
||||||
integrity="sha384-7ox8Q2yzO/uWircfojVuCQOZl+ZZBg2D2J5nkpLqzH1HY0C1dHlTKIbpRz/LG23c"
|
integrity="sha384-7ox8Q2yzO/uWircfojVuCQOZl+ZZBg2D2J5nkpLqzH1HY0C1dHlTKIbpRz/LG23c"
|
||||||
crossorigin="anonymous"></script>
|
crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.13.0/Sortable.min.js"
|
||||||
|
integrity="sha512-5x7t0fTAVo9dpfbp3WtE2N6bfipUwk7siViWncdDoSz2KwOqVC1N9fDxEOzk0vTThOua/mglfF8NO7uVDLRC8Q=="
|
||||||
|
crossorigin="anonymous"
|
||||||
|
referrerpolicy="no-referrer"></script>
|
||||||
<script src="/static/js/script.js"></script>
|
<script src="/static/js/script.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
<ul class="nav nav-list">
|
<ul class="nav nav-list">
|
||||||
<li class="nav-header">Boot Order</li>
|
<li class="nav-header">Boot Order</li>
|
||||||
<li>
|
<li>
|
||||||
{{ vm.boot_order|join(', ') }}
|
{{ vm.boot_order['order']|selectattr('enabled')|map(attribute='device')|join(', ') }}
|
||||||
<button class="btn btn-default proxstar-vmbtn" id="edit-boot-order" data-vmid="{{ vm.id }}" data-vmname="{{ vm.name }}" data-boot_order="{{ vm.boot_order_json }}">
|
<button class="btn btn-default proxstar-vmbtn" id="edit-boot-order" data-vmid="{{ vm.id }}" data-vmname="{{ vm.name }}" data-boot_order="{{ vm.boot_order_json }}">
|
||||||
<i class="fas fa-cog"></i>
|
<i class="fas fa-cog"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -119,11 +119,62 @@ class VM:
|
||||||
|
|
||||||
@lazy_property
|
@lazy_property
|
||||||
def boot_order(self):
|
def boot_order(self):
|
||||||
|
proxmox = connect_proxmox()
|
||||||
boot_order_lookup = {'a': 'Floppy', 'c': 'Hard Disk', 'd': 'CD-ROM', 'n': 'Network'}
|
boot_order_lookup = {'a': 'Floppy', 'c': 'Hard Disk', 'd': 'CD-ROM', 'n': 'Network'}
|
||||||
raw_boot_order = self.config.get('boot', 'cdn')
|
raw_boot_order = self.config.get('boot', 'cdn')
|
||||||
boot_order = []
|
boot_order = {'legacy': False, 'order': []}
|
||||||
for order in raw_boot_order:
|
try:
|
||||||
boot_order.append(boot_order_lookup[order])
|
# Proxmox version does not support 'order=' format
|
||||||
|
if float(proxmox.nodes(self.node).version.get()['release']) < 6.3:
|
||||||
|
boot_order['legacy'] = True
|
||||||
|
for order in raw_boot_order:
|
||||||
|
boot_order['order'].append({'device': boot_order_lookup[order]})
|
||||||
|
# Currently using 'order=' format
|
||||||
|
elif raw_boot_order.startswith('order='):
|
||||||
|
# Add enabled boot devices
|
||||||
|
for order in raw_boot_order[6:].split(';'):
|
||||||
|
boot_order['order'].append(
|
||||||
|
{'device': order, 'description': self.config.get(order), 'enabled': True}
|
||||||
|
)
|
||||||
|
# Add disabled boot devices
|
||||||
|
enabled_devices = [order['device'] for order in boot_order['order']]
|
||||||
|
for device in (
|
||||||
|
self.cdroms
|
||||||
|
+ [disk[0] for disk in self.disks]
|
||||||
|
+ [net[0] for net in self.interfaces]
|
||||||
|
):
|
||||||
|
if device not in enabled_devices:
|
||||||
|
boot_order['order'].append(
|
||||||
|
{
|
||||||
|
'device': device,
|
||||||
|
'description': self.config.get(device),
|
||||||
|
'enabled': False,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# Currently using legacy format
|
||||||
|
# Propose updating to the new format
|
||||||
|
else:
|
||||||
|
if raw_boot_order.startswith('legacy='):
|
||||||
|
raw_boot_order = raw_boot_order[7:]
|
||||||
|
# Arrange boot devices according to current format
|
||||||
|
devices = []
|
||||||
|
for order in raw_boot_order:
|
||||||
|
if order == 'c':
|
||||||
|
disks = [disk[0] for disk in self.disks]
|
||||||
|
if self.config.get('bootdisk'):
|
||||||
|
boot_order.append(self.config['bootdisk'])
|
||||||
|
disks.remove(self.config['bootdisk'])
|
||||||
|
devices.extend(disks)
|
||||||
|
elif order == 'd':
|
||||||
|
devices.extend(self.cdroms)
|
||||||
|
elif order == 'n':
|
||||||
|
devices.extend([net[0] for net in self.interfaces])
|
||||||
|
boot_order['order'].extend(
|
||||||
|
{'device': device, 'description': self.config.get(device), 'enabled': True}
|
||||||
|
for device in devices
|
||||||
|
)
|
||||||
|
except:
|
||||||
|
return {'legacy': False, 'order': []}
|
||||||
return boot_order
|
return boot_order
|
||||||
|
|
||||||
@lazy_property
|
@lazy_property
|
||||||
|
@ -134,9 +185,13 @@ class VM:
|
||||||
def set_boot_order(self, boot_order):
|
def set_boot_order(self, boot_order):
|
||||||
proxmox = connect_proxmox()
|
proxmox = connect_proxmox()
|
||||||
boot_order_lookup = {'Floppy': 'a', 'Hard Disk': 'c', 'CD-ROM': 'd', 'Network': 'n'}
|
boot_order_lookup = {'Floppy': 'a', 'Hard Disk': 'c', 'CD-ROM': 'd', 'Network': 'n'}
|
||||||
raw_boot_order = ''
|
# Check if legacy format
|
||||||
for order in boot_order:
|
if all(order in boot_order_lookup.keys() for order in boot_order):
|
||||||
raw_boot_order += boot_order_lookup[order]
|
raw_boot_order = ''
|
||||||
|
for order in boot_order:
|
||||||
|
raw_boot_order += boot_order_lookup[order]
|
||||||
|
else:
|
||||||
|
raw_boot_order = f"order={';'.join(boot_order)}"
|
||||||
proxmox.nodes(self.node).qemu(self.id).config.put(boot=raw_boot_order)
|
proxmox.nodes(self.node).qemu(self.id).config.put(boot=raw_boot_order)
|
||||||
|
|
||||||
@lazy_property
|
@lazy_property
|
||||||
|
@ -185,6 +240,17 @@ class VM:
|
||||||
disks = sorted(disks, key=lambda x: x[0])
|
disks = sorted(disks, key=lambda x: x[0])
|
||||||
return disks
|
return disks
|
||||||
|
|
||||||
|
@lazy_property
|
||||||
|
def cdroms(self):
|
||||||
|
cdroms = []
|
||||||
|
valid_cdrom_types = ['ide', 'sata', 'scsi']
|
||||||
|
for key, val in self.config.items():
|
||||||
|
if any(type in key for type in valid_cdrom_types):
|
||||||
|
if 'scsihw' not in key and 'cdrom' in val:
|
||||||
|
cdroms.append(key)
|
||||||
|
cdroms = sorted(cdroms)
|
||||||
|
return cdroms
|
||||||
|
|
||||||
@lazy_property
|
@lazy_property
|
||||||
def iso(self):
|
def iso(self):
|
||||||
if self.config.get('ide2'):
|
if self.config.get('ide2'):
|
||||||
|
|
Loading…
Reference in a new issue