mirror of
https://github.com/ComputerScienceHouse/proxstar.git
synced 2025-02-12 13: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 {
|
||||
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 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() {
|
||||
|
|
|
@ -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')
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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'):
|
||||
|
|
Loading…
Reference in a new issue