diff --git a/app.py b/app.py index 3c63f91..34b3a7b 100644 --- a/app.py +++ b/app.py @@ -35,6 +35,12 @@ def list_vms(): return render_template('list_vms.html', username='com6056', vms=vms) +@app.route("/isos") +def isos(): + isos = get_isos(proxmox, app.config['PROXMOX_ISO_STORAGE']) + return ','.join(isos) + + @app.route("/vm/") def vm_details(vmid): if int(vmid) in get_user_allowed_vms(proxmox, user): @@ -42,7 +48,7 @@ def vm_details(vmid): vm['vmid'] = vmid vm['config'] = get_vm_config(proxmox, vmid) vm['disks'] = get_vm_disks(proxmox, vmid, config=vm['config']) - vm['isos'] = get_vm_isos(proxmox, vmid, config=vm['config']) + vm['iso'] = get_vm_iso(proxmox, vmid, config=vm['config']) vm['interfaces'] = get_vm_interfaces( proxmox, vm['vmid'], config=vm['config']) return render_template('vm_details.html', username='com6056', vm=vm) @@ -59,6 +65,25 @@ def vm_power(vmid, action): return '', 403 +@app.route("/vm//eject", methods=['POST']) +def iso_eject(vmid): + if int(vmid) in get_user_allowed_vms(proxmox, user): + eject_vm_iso(proxmox, vmid) + return '', 200 + else: + return '', 403 + + +@app.route("/vm//mount/", methods=['POST']) +def iso_mount(vmid, iso): + if int(vmid) in get_user_allowed_vms(proxmox, user): + iso = "{}:iso/{}".format(app.config['PROXMOX_ISO_STORAGE'], iso) + mount_vm_iso(proxmox, vmid, iso) + return '', 200 + else: + return '', 403 + + @app.route("/vm//delete", methods=['POST']) def delete(vmid): if int(vmid) in get_user_allowed_vms(proxmox, user): diff --git a/proxmox.py b/proxmox.py index dbfeb49..8fdd527 100644 --- a/proxmox.py +++ b/proxmox.py @@ -106,21 +106,17 @@ def get_vm_disks(proxmox, vmid, config=None): return disks -def get_vm_isos(proxmox, vmid, config=None): +def get_vm_iso(proxmox, vmid, config=None): if not config: config = get_vm_config(proxmox, vmid) - drives = [] - for key, val in config.items(): - valid_drive_types = ['ide', 'sata', 'scsi'] - if any(drive_type in key for drive_type in valid_drive_types): - if 'cdrom' in val: - if val.split(',')[0] == 'none': - iso = 'None' - else: - iso = val.split(',')[0].split('/')[1] - drives.append([key, iso]) - drives = sorted(drives, key=lambda x: x[0]) - return drives + if config.get('ide2'): + if config['ide2'].split(',')[0] == 'none': + iso = 'None' + else: + iso = config['ide2'].split(',')[0].split('/')[1] + else: + iso = 'None' + return iso def get_user_usage_limits(user): @@ -230,3 +226,13 @@ def get_isos(proxmox, storage): for iso in proxmox.nodes('proxmox01').storage(storage).content.get(): isos.append(iso['volid'].split('/')[1]) return isos + + +def eject_vm_iso(proxmox, vmid): + node = proxmox.nodes(get_vm_node(proxmox, vmid)) + node.qemu(vmid).config.post(ide2='none,media=cdrom') + + +def mount_vm_iso(proxmox, vmid, iso): + node = proxmox.nodes(get_vm_node(proxmox, vmid)) + node.qemu(vmid).config.post(ide2="{},media=cdrom".format(iso)) diff --git a/static/css/styles.css b/static/css/styles.css index 0e21765..786f6b3 100644 --- a/static/css/styles.css +++ b/static/css/styles.css @@ -65,6 +65,22 @@ table, th, td { margin-top: 5px; } +.proxstar-ejectbtn { + width: 20px; + height: 20px; + text-align: center; + padding: 0px 1px 0px 0px; + border-radius: 15px; +} + +.proxstar-changebtn { + width: 20px; + height: 20px; + text-align: center; + padding: 0px 0px 0px 1px; + border-radius: 15px; +} + .resource-usage { width: 50%; margin: 0 auto; diff --git a/static/js/script.js b/static/js/script.js index 6256314..2a551c0 100644 --- a/static/js/script.js +++ b/static/js/script.js @@ -236,3 +236,102 @@ $("#resume-vm").click(function(){ } }); }); + +$("#eject-iso").click(function(){ + const vmid = $(this).data('vmid') + const iso = $(this).data('iso') + swal({ + title: `Are you sure you want to eject ${iso}?`, + icon: "warning", + buttons: { + cancel: true, + delete: { + text: "Eject", + closeModal: false, + className: "swal-button--danger", + } + }, + dangerMode: true, + }) + .then((willDelete) => { + if (willDelete) { + const vmid = $(this).data('vmid') + fetch(`/proxstar/vm/${vmid}/eject`, { + credentials: 'same-origin', + method: 'post' + }).then((response) => { + return swal(`${iso} is now ejecting!`, { + icon: "success", + }); + }).then(() => { + window.location = `/proxstar/vm/${vmid}`; + }).catch(err => { + if (err) { + swal("Uh oh...", `Unable to eject ${iso}. Please try again later.`, "error"); + } else { + swal.stopLoading(); + swal.close(); + } + }); + } + }); +}); + + +$("#change-iso").click(function(){ + const vmid = $(this).data('vmid') + fetch(`/proxstar/isos`, { + credentials: 'same-origin', + }).then((response) => { + return response.text() + }).then((text) => { + var isos = text.split(','); + var iso_list = document.createElement('select'); + for (i = 0; i < isos.length; i++) { + iso_list.appendChild(new Option(isos[i], isos[i])); + } + swal({ + title: 'Choose an ISO to mount:', + content: iso_list, + buttons: { + cancel: true, + select: { + text: "Select", + closeModal: false, + className: "swal-button", + } + }, + dangerMode: true, + }) + .then((willDelete) => { + if (willDelete) { + const vmid = $(this).data('vmid') + const iso = $(iso_list).val() + fetch(`/proxstar/vm/${vmid}/mount/${iso}`, { + credentials: 'same-origin', + method: 'post' + }).then((response) => { + return swal(`${iso} is now being mounted!`, { + icon: "success", + }); + }).then(() => { + window.location = `/proxstar/vm/${vmid}`; + }).catch(err => { + if (err) { + swal("Uh oh...", `Unable to mount ${iso}. Please try again later.`, "error"); + } else { + swal.stopLoading(); + swal.close(); + } + }); + } + }); + }).catch(err => { + if (err) { + swal("Uh oh...", `Unable to retrieve available ISOs. Please try again later.`, "error"); + } else { + swal.stopLoading(); + swal.close(); + } + }); +}); diff --git a/templates/vm_details.html b/templates/vm_details.html index 5bad0e0..224ff8c 100644 --- a/templates/vm_details.html +++ b/templates/vm_details.html @@ -43,9 +43,17 @@
  • {{ disk[0] }}: {{ disk[1] }}
  • {% endfor %} - {% for iso in vm['isos'] %} -
  • {{ iso[1] }}
  • - {% endfor %} +
  • + {{ vm['iso'] }} + {% if vm['iso'] != 'None' %} + + {% endif %} + +