Merge pull request #188 from jabbate19/master

Add more customization options
This commit is contained in:
Joe Abbate 2023-01-27 14:06:16 -05:00 committed by GitHub
commit f53a9915d6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 322 additions and 104 deletions

View file

@ -393,22 +393,6 @@ def vm_mem(vmid, mem):
return '', 403
@app.route('/vm/<string:vmid>/disk/<string:disk>/<int:size>', methods=['POST'])
@auth.oidc_auth
def vm_disk(vmid, disk, size):
user = User(session['userinfo']['preferred_username'])
connect_proxmox()
if user.rtp or int(vmid) in user.allowed_vms:
vm = VM(vmid)
usage_check = user.check_usage(0, 0, size)
if usage_check:
return usage_check
vm.resize_disk(disk, size)
return '', 200
else:
return '', 403
@app.route('/vm/<string:vmid>/renew', methods=['POST'])
@auth.oidc_auth
def vm_renew(vmid):
@ -425,28 +409,125 @@ def vm_renew(vmid):
return '', 403
@app.route('/vm/<string:vmid>/eject', methods=['POST'])
@app.route('/vm/<string:vmid>/disk/create/<int:size>', methods=['POST'])
@auth.oidc_auth
def iso_eject(vmid):
def create_disk(vmid, size):
user = User(session['userinfo']['preferred_username'])
connect_proxmox()
if user.rtp or int(vmid) in user.allowed_vms:
vm = VM(vmid)
vm.eject_iso()
usage_check = user.check_usage(0, 0, size)
if usage_check:
return usage_check
vm.create_disk(size)
return '', 200
else:
return '', 403
@app.route('/vm/<string:vmid>/mount/<string:iso>', methods=['POST'])
@app.route('/vm/<string:vmid>/disk/<string:disk>/resize/<int:size>', methods=['POST'])
@auth.oidc_auth
def iso_mount(vmid, iso):
def resize_disk(vmid, disk, size):
user = User(session['userinfo']['preferred_username'])
connect_proxmox()
if user.rtp or int(vmid) in user.allowed_vms:
vm = VM(vmid)
usage_check = user.check_usage(0, 0, size)
if usage_check:
return usage_check
vm.resize_disk(disk, size)
return '', 200
else:
return '', 403
@app.route('/vm/<string:vmid>/disk/<string:disk>/delete', methods=['POST'])
@auth.oidc_auth
def delete_disk(vmid, disk):
user = User(session['userinfo']['preferred_username'])
connect_proxmox()
if user.rtp or int(vmid) in user.allowed_vms:
vm = VM(vmid)
vm.delete_disk(disk)
return '', 200
else:
return '', 403
@app.route('/vm/<string:vmid>/iso/create', methods=['POST'])
@auth.oidc_auth
def iso_create(vmid):
user = User(session['userinfo']['preferred_username'])
connect_proxmox()
if user.rtp or int(vmid) in user.allowed_vms:
vm = VM(vmid)
vm.add_iso_drive()
return '', 200
else:
return '', 403
@app.route('/vm/<string:vmid>/iso/<string:iso_drive>/delete', methods=['POST'])
@auth.oidc_auth
def iso_delete(vmid, iso_drive):
user = User(session['userinfo']['preferred_username'])
connect_proxmox()
if user.rtp or int(vmid) in user.allowed_vms:
vm = VM(vmid)
vm.delete_iso_drive(iso_drive)
return '', 200
else:
return '', 403
@app.route('/vm/<string:vmid>/iso/<string:iso_drive>/eject', methods=['POST'])
@auth.oidc_auth
def iso_eject(vmid, iso_drive):
user = User(session['userinfo']['preferred_username'])
connect_proxmox()
if user.rtp or int(vmid) in user.allowed_vms:
vm = VM(vmid)
vm.eject_iso(iso_drive)
return '', 200
else:
return '', 403
@app.route('/vm/<string:vmid>/iso/<string:iso_drive>/mount/<string:iso>', methods=['POST'])
@auth.oidc_auth
def iso_mount(vmid, iso_drive, iso):
user = User(session['userinfo']['preferred_username'])
connect_proxmox()
if user.rtp or int(vmid) in user.allowed_vms:
iso = '{}:iso/{}'.format(app.config['PROXMOX_ISO_STORAGE'], iso)
vm = VM(vmid)
vm.mount_iso(iso)
vm.mount_iso(iso_drive, iso)
return '', 200
else:
return '', 403
@app.route('/vm/<string:vmid>/net/create', methods=['POST'])
@auth.oidc_auth
def create_net_interface(vmid):
user = User(session['userinfo']['preferred_username'])
connect_proxmox()
if user.rtp or int(vmid) in user.allowed_vms:
vm = VM(vmid)
vm.create_net('virtio')
return '', 200
else:
return '', 403
@app.route('/vm/<string:vmid>/net/<string:netid>/delete', methods=['POST'])
@auth.oidc_auth
def delete_net_interface(vmid, netid):
user = User(session['userinfo']['preferred_username'])
connect_proxmox()
if user.rtp or int(vmid) in user.allowed_vms:
vm = VM(vmid)
vm.delete_net(netid)
return '', 200
else:
return '', 403

View file

@ -4,7 +4,7 @@ $(document).ready(function(){
$('[data-toggle="tooltip"]').tooltip();
});
function confirmDialog(url, confirm, confirmButton, complete, error, location) {
function confirmDialog(url, confirm, confirmButton, complete, error, location, danger) {
swal({
title: confirm,
icon: "warning",
@ -13,7 +13,7 @@ function confirmDialog(url, confirm, confirmButton, complete, error, location) {
action: {
text: confirmButton,
closeModal: false,
className: "swal-button--danger",
className: danger ? "swal-button--danger" : "swal-button--confirm",
}
},
dangerMode: true,
@ -44,31 +44,31 @@ function confirmDialog(url, confirm, confirmButton, complete, error, location) {
$("#delete-vm").click(function(){
const vmname = $(this).data('vmname');
const vmid = $(this).data('vmid')
confirmDialog(`/vm/${vmid}/delete`, `Are you sure you want to delete ${vmname}?`, "Delete", `${vmname} is now being deleted.`, `Unable to delete ${vmname}. Please try again later.`, '/')
confirmDialog(`/vm/${vmid}/delete`, `Are you sure you want to delete ${vmname}?`, "Delete", `${vmname} is now being deleted.`, `Unable to delete ${vmname}. Please try again later.`, '/', true)
});
$("#stop-vm").click(function(){
const vmname = $(this).data('vmname');
const vmid = $(this).data('vmid')
confirmDialog(`/vm/${vmid}/power/stop`, `Are you sure you want to stop ${vmname}?`, "Stop", `${vmname} is now stopping!`, `Unable to stop ${vmname}. Please try again later.`, `/vm/${vmid}`)
confirmDialog(`/vm/${vmid}/power/stop`, `Are you sure you want to stop ${vmname}?`, "Stop", `${vmname} is now stopping!`, `Unable to stop ${vmname}. Please try again later.`, `/vm/${vmid}`, true)
});
$("#reset-vm").click(function(){
const vmname = $(this).data('vmname');
const vmid = $(this).data('vmid')
confirmDialog(`/vm/${vmid}/power/reset`, `Are you sure you want to reset ${vmname}?`, "Reset", `${vmname} is now resetting!`, `Unable to reset ${vmname}. Please try again later.`, `/vm/${vmid}`)
confirmDialog(`/vm/${vmid}/power/reset`, `Are you sure you want to reset ${vmname}?`, "Reset", `${vmname} is now resetting!`, `Unable to reset ${vmname}. Please try again later.`, `/vm/${vmid}`, true)
});
$("#shutdown-vm").click(function(){
const vmname = $(this).data('vmname');
const vmid = $(this).data('vmid')
confirmDialog(`/vm/${vmid}/power/shutdown`, `Are you sure you want to shutdown ${vmname}?`, "Shutdown", `${vmname} is now shutting down!`, `Unable to shutdown ${vmname}. Please try again later.`, `/vm/${vmid}`)
confirmDialog(`/vm/${vmid}/power/shutdown`, `Are you sure you want to shutdown ${vmname}?`, "Shutdown", `${vmname} is now shutting down!`, `Unable to shutdown ${vmname}. Please try again later.`, `/vm/${vmid}`, true)
});
$("#suspend-vm").click(function(){
const vmname = $(this).data('vmname');
const vmid = $(this).data('vmid')
confirmDialog(`/vm/${vmid}/power/suspend`, `Are you sure you want to suspend ${vmname}?`, "Suspend", `${vmname} is now suspending!`, `Unable to suspend ${vmname}. Please try again later.`, `/vm/${vmid}`)
confirmDialog(`/vm/${vmid}/power/suspend`, `Are you sure you want to suspend ${vmname}?`, "Suspend", `${vmname} is now suspending!`, `Unable to suspend ${vmname}. Please try again later.`, `/vm/${vmid}`, true)
});
$("#start-vm").click(function(){
@ -115,59 +115,14 @@ $("#resume-vm").click(function(){
});
});
$("#eject-iso").click(function(){
$(".eject-iso").click(function(){
const iso = $(this).data('iso');
swal({
title: `Are you sure you want to eject ${iso}?`,
icon: "warning",
buttons: {
cancel: {
text: "Cancel",
visible: true,
closeModal: true,
className: "",
},
eject: {
text: "Eject",
closeModal: false,
className: "swal-button--danger",
}
},
dangerMode: true,
})
.then((willEject) => {
if (willEject) {
const vmid = $(this).data('vmid');
fetch(`/vm/${vmid}/eject`, {
credentials: 'same-origin',
method: 'post'
}).then((response) => {
return swal(`${iso} is now ejecting!`, {
icon: "success",
buttons: {
ok: {
text: "OK",
closeModal: true,
className: "",
}
}
});
}).then(() => {
window.location = `/vm/${vmid}`;
}).catch(err => {
if (err) {
swal("Uh oh...", `Unable to eject ${iso}. Please try again later.`, "error");
} else {
swal.stopLoading();
swal.close();
}
});
}
});
const vmid = $(this).data('vmid');
confirmDialog(`/vm/${vmid}/iso/${iso}/eject`, `Are you sure you want to eject this ISO?`, "Eject", `Ejecting ISO!`, `Unable to eject ISO. Please try again later.`, `/vm/${vmid}`, true)
});
$("#change-iso").click(function(){
$(".change-iso").click(function(){
fetch(`/isos`, {
credentials: 'same-origin',
}).then((response) => {
@ -197,8 +152,9 @@ $("#change-iso").click(function(){
.then((willChange) => {
if (willChange) {
const vmid = $(this).data('vmid');
const iso_drive = $(this).data('iso');
const iso = $(iso_list).val();
fetch(`/vm/${vmid}/mount/${iso}`, {
fetch(`/vm/${vmid}/iso/${iso_drive}/mount/${iso}`, {
credentials: 'same-origin',
method: 'post'
}).then((response) => {
@ -838,7 +794,8 @@ $(".resize-disk").click(function(){
const usage = $(this).data('usage');
const limit = $(this).data('limit');
swal({
title: 'Enter how many GB you would like to expand this disk by:',
title: 'Expand Disk',
text: 'Enter how many GB you would like to expand this disk by (GB)',
content: {
element: 'input',
attributes: {
@ -853,7 +810,7 @@ $(".resize-disk").click(function(){
className: "",
},
confirm: {
text: "Select",
text: "Expand",
closeModal: false,
}
},
@ -861,7 +818,7 @@ $(".resize-disk").click(function(){
.then((size) => {
if (size) {
if ((parseInt(usage) + parseInt(size)) <= parseInt(limit)) {
fetch(`/vm/${vmid}/disk/${disk}/${size}`, {
fetch(`/vm/${vmid}/disk/${disk}/resize/${size}`, {
credentials: 'same-origin',
method: 'post'
}).then((response) => {
@ -1110,3 +1067,92 @@ $(document).on('focus click', "[id^=boot-order-]", function() {
}
});
});
$("#create-net").click(function(){
const vmid = $(this).data('vmid')
confirmDialog(`/vm/${vmid}/net/create`, `Are you sure you want to create a new interface?`, "Create", `Creating new interface!`, `Unable to create interface. Please try again later.`, `/vm/${vmid}`, false)
});
$(".delete-net").click(function(){
const vmid = $(this).data('vmid')
const interface = $(this).data('interface')
confirmDialog(`/vm/${vmid}/net/${interface}/delete`, `Are you sure you want to delete ${interface}?`, "Delete", `Deleting ${interface}!`, `Unable to delete interface. Please try again later.`, `/vm/${vmid}`, true)
});
$("#create-iso").click(function(){
const vmid = $(this).data('vmid')
confirmDialog(`/vm/${vmid}/iso/create`, `Are you sure you want to create a new ISO drive?`, "Create", `Creating new ISO drive!`, `Unable to create ISO drive. Please try again later.`, `/vm/${vmid}`, false)
});
$(".delete-iso").click(function(){
const vmid = $(this).data('vmid')
const iso = $(this).data('iso')
confirmDialog(`/vm/${vmid}/iso/${iso}/delete`, `Are you sure you want to delete ${iso}?`, "Delete", `Deleting ${iso}!`, `Unable to delete ISO drive. Please try again later.`, `/vm/${vmid}`, true)
});
$("#create-disk").click(function(){
const vmid = $(this).data('vmid');
const disk = $(this).data('disk');
const usage = $(this).data('usage');
const limit = $(this).data('limit');
swal({
title: 'Create New Disk',
text: 'Enter new disk size (GB)',
content: {
element: 'input',
attributes: {
type: 'number',
},
},
buttons: {
cancel: {
text: "Cancel",
visible: true,
closeModal: true,
className: "",
},
confirm: {
text: "Create",
closeModal: false,
}
},
})
.then((size) => {
if (size) {
if ((parseInt(usage) + parseInt(size)) <= parseInt(limit)) {
fetch(`/vm/${vmid}/disk/create/${size}`, {
credentials: 'same-origin',
method: 'post'
}).then((response) => {
return swal(`Disk has been created!`, {
icon: "success",
buttons: {
ok: {
text: "OK",
closeModal: true,
className: "",
}
}
});
}).then(() => {
window.location = `/vm/${vmid}`;
});
} else {
swal("Uh oh...", `You don't have enough disk resources! Try again with a smaller size.`, "error");
}
}
}).catch(err => {
if (err) {
swal("Uh oh...", `Unable to create the disk. Please try again later.`, "error");
} else {
swal.stopLoading();
swal.close();
}
});
});
$(".delete-disk").click(function(){
const vmid = $(this).data('vmid')
const disk = $(this).data('disk')
confirmDialog(`/vm/${vmid}/disk/${disk}/delete`, `Are you sure you want to delete ${disk}?`, "Delete", `Deleting ${disk}!`, `Unable to delete disk. Please try again later.`, `/vm/${vmid}`, true)
});

View file

@ -19,29 +19,56 @@
</li>
<li class="nav-header">Interfaces</li>
{% for interface in vm.interfaces %}
<li>{{ interface[0] }}: {{ interface[2] }}</li>
<li>{{ interface[0] }}: {{ interface[2] }}
<button class="btn btn-danger proxstar-vmbtn delete-net" id="delete-net" name="delete" data-vmid="{{ vm.id }}" data-interface="{{ interface[0] }}">
<i class="fas fa-trash"></i>
</button>
</li>
{% endfor %}
<li class="nav-header">Disks</li>
<li>
<button class="btn btn-success proxstar-vmbtn" id="create-net" name="create" data-vmid="{{ vm.id }}">
<i class="fas fa-plus"></i>
</button>
</li>
<li class="nav-header">Disks</li>
{% for disk in vm.disks %}
<li>
{{ disk[0] }}: {{ disk[1] }}GB
<button class="btn btn-default proxstar-vmbtn resize-disk" id="resize-disk" name="resize" data-vmid="{{ vm.id }}" data-disk="{{ disk[0] }}" data-usage="{{ usage['disk'] }}" data-limit="{{ limits['disk'] }}">
<i class="fas fa-cog"></i>
</button>
<button class="btn btn-danger proxstar-vmbtn delete-disk" id="delete-disk" name="delete" data-vmid="{{ vm.id }}" data-disk="{{ disk[0] }}">
<i class="fas fa-trash"></i>
</button>
</li>
<li>
<button class="btn btn-success proxstar-vmbtn" id="create-disk" name="create" data-vmid="{{ vm.id }}" data-usage="{{ usage['disk'] }}" data-limit="{{ limits['disk'] }}">
<i class="fas fa-plus"></i>
</button>
</li>
{% endfor %}
<li class="nav-header">ISO</li>
<li>
{{ vm.iso }}
{% if vm.iso != 'None' %}
<button class="btn btn-danger proxstar-vmbtn" id="eject-iso" name="eject" data-vmid="{{ vm.id }}" data-iso="{{ vm.iso }}">
{% for iso in vm.isos %}
<li>
{{iso[0]}}: {{iso[1]}}
<button class="btn btn-default proxstar-vmbtn change-iso" id="change-iso" name="change" data-vmid="{{ vm.id }}" data-iso="{{ iso[0] }}">
<i class="fas fa-cog"></i>
</button>
{% if iso[1] != 'None' %}
<button class="btn btn-warning proxstar-vmbtn eject-iso" id="eject-iso" name="eject" data-vmid="{{ vm.id }}" data-iso="{{ iso[0] }}">
<i class="fas fa-eject"></i>
</button>
{% endif %}
<button class="btn btn-default proxstar-vmbtn" id="change-iso" name="change" data-vmid="{{ vm.id }}" data-iso="{{ vm.iso }}">
<i class="fas fa-cog"></i>
</button>
<button class="btn btn-danger proxstar-vmbtn delete-iso" id="delete-iso" name="delete" data-vmid="{{ vm.id }}" data-iso="{{ iso[0] }}">
<i class="fas fa-trash"></i>
</button>
</li>
{% endfor %}
<li>
<button class="btn btn-success proxstar-vmbtn" id="create-iso" name="create" data-vmid="{{ vm.id }}">
<i class="fas fa-plus"></i>
</button>
</li>
</ul>
</div>
</div>

View file

@ -210,6 +210,32 @@ class VM:
interfaces = sorted(interfaces, key=lambda x: x[0])
return interfaces
@retry(wait=wait_fixed(2), stop=stop_after_attempt(5))
def create_net(self, int_type):
valid_int_types = ['virtio', 'e1000', 'rtl8139', 'vmxnet3']
if int_type not in valid_int_types:
return False
i = 0
while True:
name = f'net{i}'
if name not in self.config:
proxmox = connect_proxmox()
try:
proxmox.nodes(self.node).qemu(self.id).config.post(**{name: int_type})
return True
except Exception as e:
print(e)
raise e
i += 1
@retry(wait=wait_fixed(2), stop=stop_after_attempt(5))
def delete_net(self, net_id):
if net_id in self.config:
proxmox = connect_proxmox()
proxmox.nodes(self.node).qemu(self.id).config.post(delete=net_id)
return True
return False
def get_mac(self, interface='net0'):
mac = self.config[interface].split(',')
if 'virtio' in mac[0]:
@ -252,30 +278,68 @@ class VM:
return cdroms
@lazy_property
def iso(self):
if self.config.get('ide2'):
if self.config['ide2'].split(',')[0] == 'none':
iso = 'None'
def isos(self):
isos = []
for iso in filter(lambda interface: 'ide' in interface, self.config.keys()):
iso_info = self.config[iso]
if iso_info:
if iso_info.split(',')[0] == 'none':
isos.append((iso, 'None'))
else:
isos.append((iso, iso_info.split(',')[0].split('/')[1]))
else:
iso = self.config['ide2'].split(',')[0].split('/')[1]
else:
iso = 'None'
return iso
isos.append((iso, 'None'))
return isos
@retry(wait=wait_fixed(2), stop=stop_after_attempt(5))
def eject_iso(self):
proxmox = connect_proxmox()
proxmox.nodes(self.node).qemu(self.id).config.post(ide2='none,media=cdrom')
def add_iso_drive(self):
iso_drives = list(filter(lambda interface: 'ide' in interface, self.config.keys()))
for i in range(1, 5):
ide_name = f'ide{i}'
if ide_name not in iso_drives:
proxmox = connect_proxmox()
proxmox.nodes(self.node).qemu(self.id).config.post(**{ide_name: 'none,media=cdrom'})
return True
return False
@retry(wait=wait_fixed(2), stop=stop_after_attempt(5))
def mount_iso(self, iso):
def delete_iso_drive(self, iso_drive):
proxmox = connect_proxmox()
proxmox.nodes(self.node).qemu(self.id).config.post(ide2='{},media=cdrom'.format(iso))
proxmox.nodes(self.node).qemu(self.id).config.post(delete=iso_drive)
@retry(wait=wait_fixed(2), stop=stop_after_attempt(5))
def eject_iso(self, iso_drive):
proxmox = connect_proxmox()
proxmox.nodes(self.node).qemu(self.id).config.post(**{iso_drive: 'none,media=cdrom'})
@retry(wait=wait_fixed(2), stop=stop_after_attempt(5))
def mount_iso(self, iso_drive, iso):
proxmox = connect_proxmox()
proxmox.nodes(self.node).qemu(self.id).config.post(
**{iso_drive: '{},media=cdrom'.format(iso)}
)
@retry(wait=wait_fixed(2), stop=stop_after_attempt(5))
def create_disk(self, size):
drives = list(filter(lambda interface: 'virtio' in interface, self.config.keys()))
for i in range(0, 16):
disk_name = f'virtio{i}'
if disk_name not in drives:
proxmox = connect_proxmox()
proxmox.nodes(self.node).qemu(self.id).config.post(**{disk_name: f'ceph:{size}'})
return True
return False
@retry(wait=wait_fixed(2), stop=stop_after_attempt(5))
def resize_disk(self, disk, size):
proxmox = connect_proxmox()
proxmox.nodes(self.node).qemu(self.id).resize.put(disk=disk, size='+{}G'.format(size))
@retry(wait=wait_fixed(2), stop=stop_after_attempt(5))
def delete_disk(self, disk):
proxmox = connect_proxmox()
proxmox.nodes(self.node).qemu(self.id).config.post(delete=disk)
@lazy_property
def expire(self):
return get_vm_expire(db, self.id, app.config['VM_EXPIRE_MONTHS'])