Merge pull request #111 from harmonherring/boot-order

Boot order handling
This commit is contained in:
Will Nilges 2021-11-11 19:25:21 -05:00 committed by GitHub
commit 8e5f173a94
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 178 additions and 27 deletions

View file

@ -133,3 +133,15 @@ table, th, td {
#show-for-template {
display: none
}
.boot-order-check {
margin: 0 auto;
}
.borderless td, .borderless th, table.borderless {
border: none;
}
.fa-bars {
cursor: move;
}

View file

@ -815,24 +815,10 @@ $("#edit-boot-order").click(function(){
const vmid = $(this).data('vmid');
const vmname = $(this).data('vmname');
const boot_order = $(this).data('boot_order');
var options = document.createElement('div');
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'));
}
var options = renderBootOrder(boot_order);
swal({
title: `Select the new boot order for ${vmname} (full shutdown required for settings to take effect):`,
content: options,
content: renderBootOrder(boot_order),
buttons: {
cancel: {
text: "Cancel",
@ -848,9 +834,18 @@ $("#edit-boot-order").click(function(){
.then((willChange) => {
if (willChange) {
var data = new FormData();
for (k = 0; k < boot_order.length; k++) {
e = document.getElementById(`boot-order-${k + 1}`);
data.append(`${k + 1}`, e.options[e.selectedIndex].value);
if (boot_order.legacy) {
for (k = 0; k < boot_order.order.length; k++) {
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`, {
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() {
previous = $(this).val();
}).on('change', "[id^=boot-order-]", function() {

View file

@ -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)
set_job_status(job, 'setting VM expiration')
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))
set_job_status(job, 'complete')

View file

@ -112,6 +112,10 @@
<script defer src="https://use.fontawesome.com/releases/v5.0.8/js/fontawesome.js"
integrity="sha384-7ox8Q2yzO/uWircfojVuCQOZl+ZZBg2D2J5nkpLqzH1HY0C1dHlTKIbpRz/LG23c"
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>
</body>

View file

@ -12,7 +12,7 @@
<ul class="nav nav-list">
<li class="nav-header">Boot Order</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 }}">
<i class="fas fa-cog"></i>
</button>

View file

@ -119,11 +119,62 @@ class VM:
@lazy_property
def boot_order(self):
proxmox = connect_proxmox()
boot_order_lookup = {'a': 'Floppy', 'c': 'Hard Disk', 'd': 'CD-ROM', 'n': 'Network'}
raw_boot_order = self.config.get('boot', 'cdn')
boot_order = []
for order in raw_boot_order:
boot_order.append(boot_order_lookup[order])
boot_order = {'legacy': False, 'order': []}
try:
# 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
@lazy_property
@ -134,9 +185,13 @@ class VM:
def set_boot_order(self, boot_order):
proxmox = connect_proxmox()
boot_order_lookup = {'Floppy': 'a', 'Hard Disk': 'c', 'CD-ROM': 'd', 'Network': 'n'}
raw_boot_order = ''
for order in boot_order:
raw_boot_order += boot_order_lookup[order]
# Check if legacy format
if all(order in boot_order_lookup.keys() for order in boot_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)
@lazy_property
@ -185,6 +240,17 @@ class VM:
disks = sorted(disks, key=lambda x: x[0])
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
def iso(self):
if self.config.get('ide2'):