Add more customization options

This commit is contained in:
Joe Abbate 2023-01-27 12:30:37 -05:00
parent 95fbfcee80
commit 8fae5521be
4 changed files with 305 additions and 97 deletions

View file

@ -269,6 +269,7 @@ def vm_details(vmid):
if user.rtp or int(vmid) in user.allowed_vms:
vm = VM(vmid)
usage_check = user.check_usage(vm.cpu, vm.mem, 0)
print(vm.boot_order)
return render_template(
'vm_details.html',
user=user,
@ -392,23 +393,6 @@ def vm_mem(vmid, mem):
else:
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):
@ -424,29 +408,120 @@ def vm_renew(vmid):
else:
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>/disk/<string:disk>/resize/<int:size>', methods=['POST'])
@auth.oidc_auth
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>/mount/<string:iso>', methods=['POST'])
@app.route('/vm/<string:vmid>/iso/<string:iso_drive>/mount/<string:iso>', methods=['POST'])
@auth.oidc_auth
def iso_mount(vmid, iso):
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

@ -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}`)
});
$("#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) => {
@ -861,7 +817,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 +1066,91 @@ $(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}`)
});
$(".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}`)
});
$("#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}`)
});
$(".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}`)
});
$("#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: 'Enter how many GB you would like to make this disk:',
content: {
element: 'input',
attributes: {
type: 'number',
},
},
buttons: {
cancel: {
text: "Cancel",
visible: true,
closeModal: true,
className: "",
},
confirm: {
text: "Select",
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}`)
});

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,66 @@ 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'])