{% extends "base.html" %} {% load static %} {% load i18n %} {% load bootstrap_icons %} {% block title %}{% trans "Instance" %} - {{ instance.name }}{% endblock %} {% block page_heading %} {{ instance.name }}{% if instance.title %} ({{ instance.title }}){% endif %} {% endblock page_heading %} {% block content %} {% include 'pleasewaitdialog.html' %} <div> <div> {% if instance.status == 5 %} <span class="badge bg-danger">{% trans "Off" %}</span> {% endif %} {% if instance.status == 1 %} <span class="badge bg-success">{% trans "Active" %}</span> {% endif %} {% if instance.status == 3 %} <span class="badge bg-warning">{% trans "Suspend" %}</span> {% endif %} | <span {%if instance.guest_agent %} {% if instance.guest_agent_ready %} class="badge bg-success" title="{% trans "Guest Agent Enabled & Connected" %}" {% else %} class="badge bg-warning" title="{% trans "Guest Agent Enabled but not Connected" %}" {% endif %} {% else %} class="badge bg-danger" title="{% trans "Guest Agent Not Enabled & Not Connected" %}" {% endif %}>{% bs_icon 'broadcast' size='1em' %} </span> | {% if instance.snapshots %} {% bs_icon 'camera'%} <i title="There are {{ instance.snapshots|length }} snapshot(s)"></i> | {% endif %} {% if instance.cur_vcpu %} {{ instance.cur_vcpu }} {% trans "VCPU" %} {% else %} {{ instance.vcpu }} {% trans "VCPU" %} {% endif %} | {{ instance.cur_memory }} {% trans "MB" %} {% trans "RAM" %} | {% for disk in instance.disks %} {{ disk.size|filesizeformat }} {% trans "Disk" %} | {% endfor %} {% for net in instance.networks %} {% for ipv4 in net.ipv4 %} {{ ipv4 }} | {% endfor %} {% endfor %} {% if instance.guest_agent_ready %} <a class="link-warning" title="{% trans 'Show Instance OS details' %}" onclick="get_osinfo();"> <span>{% bs_icon 'info-circle' %} </span> </a> {% endif %} <a class="link-success" href="{% url 'instances:instance' instance.id %}" title="{% trans 'Refresh instance info' %}"> <span>{% bs_icon 'repeat'%} </span> </a> </div> {% if user_quota_msg %} <div class="alert alert-warning fade show"> {{ user_quota_msg|capfirst }} {% trans "quota reached" %}. </div> {% endif %} <hr> </div> <div id="mainTabMenu"> <!-- Nav tabs --> <ul class="nav nav-pills nav-fill" id="navbtn" role="tablist" aria-label="Instance actions"> <li class="nav-item" role="presentation"> <button class="nav-link action-button active" id="power-tab" aria-controls="power" data-bs-toggle="pill" data-bs-target="#power" type="button" role="tab" aria-selected="true"> <span id="action-block" aria-hidden="true">{% bs_icon 'power' size='2em'%} </span> {% trans "Power" %} </button> </li> <li class="nav-item" role="presentation"> <button class="nav-link action-button" id="access-tab" aria-controls="access" data-bs-toggle="pill" data-bs-target="#access" type="button" role="tab" aria-selected="false"> <span id="action-block" aria-hidden="true">{% bs_icon 'lock' size='2em'%} </span> {% trans "Access" %} </button> </li> <li class="nav-item" role="presentation"> <button class="nav-link action-button" id="resize-tab" aria-controls="resize" data-bs-toggle="pill" data-bs-target="#resize" type="button" role="tab" aria-selected="false"> <span id="action-block" aria-hidden="true">{% bs_icon 'arrows-angle-expand' size='2em'%} </span> {% trans "Resize" %} </button> </li> {% if allow_admin_or_not_template and 'instances.snapshot_instances' in perms %} <li class="nav-item" role="presentation"> <button class="nav-link action-button" id="snapshots-tab" aria-controls="snapshots" data-bs-toggle="pill" data-bs-target="#snapshots" type="button" role="tab" aria-selected="false"> <span id="action-block" aria-hidden="true">{% bs_icon 'camera' size='2em'%} </span> {% trans "Snapshot" %} </button> </li> {% endif %} <li class="nav-item" role="presentation"> <button class="nav-link action-button" id="settings-tab" aria-controls="settings" data-bs-toggle="pill" data-bs-target="#settings" type="button" role="tab" aria-selected="false"> <span id="action-block" aria-hidden="true">{% bs_icon 'gear-wide-connected' size='2em'%} </span> {% trans "Settings" %} </button> </li> <li class="nav-item" role="presentation"> <button class="nav-link action-button" id="graphics-tab" aria-controls="graphics" data-bs-toggle="pill" data-bs-target="#graphics" type="button" role="tab" aria-selected="false"> <span id="action-block" aria-hidden="true">{% bs_icon 'bar-chart-line' size='2em'%} </span> {% trans "Stats" %} </button> </li> <li class="nav-item" role="presentation"> <button class="nav-link action-button" id="undefine-tab" aria-controls="undefine" data-bs-toggle="pill" data-bs-target="#undefine" type="button" role="tab" aria-selected="false"> <span id="action-block" aria-hidden="true">{% bs_icon 'trash' size='2em'%} </span> {% trans "Destroy" %} </button> </li> </ul> <!-- Tab panes --> <div class="tab-content" id=mainTabMenuContent> {% include 'instances/power_tab.html' %} {% include 'instances/access_tab.html' %} {% include 'instances/resize_tab.html' %} {% include 'instances/snapshots_tab.html' %} {% include 'instances/settings_tab.html' %} {% include 'instances/stats_tab.html' %} {% include 'instances/destroy_tab.html' %} </div> </div> {% if app_settings.VIEW_INSTANCE_DETAIL_BOTTOM_BAR == 'True' %} {% include 'bottom_bar.html' %} {% endif %} {% endblock %} {% block script %} <script src="{% static 'js/ace/ace.js' %}" type="text/javascript" charset="utf-8"></script> <script> function get_volumes(compute_id, pool) { get_vol_url = "/computes/" + compute_id + "/storage/" + pool + "/volumes"; $.getJSON(get_vol_url, function (data) { $("#vols").find('option').remove(); $("#vols").removeAttr("disabled"); $.each(data['vols'], function(i, item) { $("#vols").append('<option value=' + item +'>' + item + '</option>'); }) }); var sto_drop = document.getElementById('select_storage'); sto_drop.value = pool; sto_drop.innerHTML = pool + "<span class=\"caret\"></span>"; var sto_input = document.getElementById('selected_storage'); sto_input.setAttribute('value', pool); //sto_input.innerHTML = pool; } </script> <script> var editor = ace.edit("editor"); editor.getSession().setMode("ace/mode/xml"); var input = $('input[name="inst_xml"]'); editor.getSession().on("change", function () { input.val(editor.getSession().getValue()); }); </script> <script> function random_mac(net) { $.getJSON("{% url 'instances:random_mac_address' %}", function (data) { $('input[name="' + net + '"]').val(data['mac']); }); } </script> <script> function show_console() { if ($('#console_show_pass').attr('type') == 'password') { $('#console_show_pass').attr('type', 'text'); } else { $('#console_show_pass').attr('type', 'password'); } } </script> <script> function guess_mac_address(src_elem, net) { new_vname = $(src_elem).val(); guess_mac_address_url = "{% url 'instances:guess_mac_address' 1 %}".replace(1, new_vname); $.getJSON(guess_mac_address_url, function(data) { $('input[name="clone-net-mac-'+net+'"]').val(data['mac']); }); } </script> <script> function guess_clone_name() { $.getJSON("{% url 'instances:guess_clone_name' %}", function(data) { guessed_name = data['name'].split(".")[0]; $('#clone_name').val(guessed_name); update_clone_disk_name(guessed_name); guess_mac_address('#clone_name', 0); }); } </script> <script> function update_clone_disk_name(new_vname) { vname = '{{ instance.name }}'; {% for disk in instance.disks %} disk_name = '{{ disk.image }}'; disk_dot = disk_name.split('.'); disk_dot_suffix = disk_dot[disk_dot.length-1]; if (disk_name.startsWith(vname)) { image = disk_name.replace(vname, new_vname); } else if (disk_name.lastIndexOf('.') > -1 && disk_dot_suffix.length <= 7) { disk_dot.pop(); disk_name_only = disk_dot.join('-'); image = new_vname + "-" + disk_name_only + "." + disk_dot_suffix } else if (new_vname != disk_name) { image = new_vname } else { image = new_vname + '-clone'; } $('#disk_name-{{ disk.dev }}').val(image); {% endfor %} } </script> <script> $(document).on('change', '#console_passwd_gen', function () { if ($(this).prop('checked')) { $('#console_passwd_manual').hide(); $('#console_passwd_clear').prop('checked', false); } else { $('#console_passwd_manual').show(); } }); $(document).on('change', '#console_passwd_clear', function () { if ($(this).prop('checked')) { $('#console_passwd_manual').hide(); $('#console_passwd_gen').prop('checked', false); } else { $('#console_passwd_manual').show(); } }); $(document).on('change', '#console_keymap_clear', function () { if ($(this).prop('checked')) { $('#console_keymap_selection').hide(); } else { $('#console_keymap_selection').show(); } }); $('#clone_name').on('input', function () { update_clone_disk_name($(this).val()); }); $(document).ready(function () { // set current console keymap or fall back to default var keymap = "{{ instance.console_keymap }}"; if (keymap != '') { $("#console_select_keymap option[value='" + keymap + "']").prop('selected', true); } }); $(document).ready(function () { // set current console type or fall back to default var console_type = "{{ instance.console_type }}"; if (console_type != '') { $("#console_select_type option[value='" + console_type + "']").prop('selected', true); } }); $(document).ready(function () { // set current console listen address or fall back to default var console_listener_address = "{{ instance.console_listener_address }}"; if (console_listener_address != '') { $("#console_select_listener_address option[value='" + console_listener_address + "']").prop('selected', true); } }); $(document).ready(function () { // get video model or fall back to default let video_model = "{{ instance.video_model }}"; if (video_model != '') { $("#video_model_select option[value='" + video_model + "']").prop('selected', true); } }); $(document).ready(function () { // set vdi url $.get("{% url 'vdi_url' compute.id instance.name %}", function(data) { $("#vdi_url_input").attr("value", data); $("#vdi_url").attr("href", data); }); }); {% if request.user.is_superuser %} $(document).ready(function () { random_mac('clone-net-mac-0'); random_mac('add-net-mac'); update_clone_disk_name($('#clone_name').val()); }); {% else %} $('#select_clone_name').on('change', function () { update_clone_disk_name($(this).val()); guess_mac_address('#select_clone_name', 0); }); $(document).ready(function () { update_clone_disk_name($('#select_clone_name').val()); guess_mac_address('#select_clone_name', 0); }); {% endif %} </script> <script> $(document).ready(function(){ $('[data-bs-toggle="popover"]').popover({ placement : 'top' }); }); </script> <script> function set_orderlist(obj){ var result = ''; $('#b_order label input:checked').each(function () { if (result != '') result += ','; result += $(this).val(); }); obj.val(result); } $(document).ready(function () { {# Boot Order Arragements #} jQuery.fn.multiselect = function() { $(this).each(function() { var checkboxes = $(this).find("input:checkbox"); checkboxes.each(function() { var checkbox = $(this); // Highlight pre-selected checkboxes if (checkbox.prop("checked")) checkbox.parent().addClass("multiselect-on"); // Highlight checkboxes that the user selects checkbox.click(function() { if (checkbox.prop("checked")) checkbox.parent().addClass("multiselect-on"); else checkbox.parent().removeClass("multiselect-on"); }); }); }); }; $(function() { $(".multiselect").multiselect(); }); $('#boot_order_up').bind('click', function() { $('#b_order label input:checked').each( function() { var label = $(this).parent(); var newPos = label.index() - 1; if (newPos > -1) { $('#b_order label').eq(newPos).before("<label><input type='checkbox' value='"+$(this).val()+"' name='"+$(this).val()+"' checked>"+$(this).parent().text()+"</label>"); label.remove(); } $(".multiselect").multiselect(); }); set_orderlist($("#bootorder")); }); $('#boot_order_down').bind('click', function() { var countOptions = $('#b_order label').length; var countSelected = $('#b_order label input:checked').length; $('#b_order label input:checked').each( function() { var label = $(this).parent(); var newPos = label.index() + countSelected; if (newPos < countOptions) { $('#b_order label').eq(newPos).after("<label><input type='checkbox' value='"+$(this).val()+"' name='"+$(this).val()+"' checked>"+$(this).parent().text()+"</label>"); label.remove(); } $(".multiselect").multiselect(); }); set_orderlist($("#bootorder")); }); }); </script> <script> $(function () { $('.js-custom__checkbox').change(function () { var container = $(this).closest('.js-custom__container'); var toggles = container.find('.js-custom__toggle'); toggles.toggle(); }); }); </script> <script src="{% static 'js/Chart.bundle.min.js' %}"></script> <script> var stats_tab = document.querySelector('#graphics-tab'); stats_tab.addEventListener('shown.bs.tab', function (event) { var cpu_ctx = $("#cpuChart").get(0).getContext("2d"); var cpuChart = new Chart(cpu_ctx, { type: 'line', data: { datasets : [{ backgroundColor: "rgba(44,127,184,0.5)", label: "Usage" }] }, options: { responsive: true, legend: { display: false }, scales: { xAxes:[{ offset: false, ticks: { beginAtZero: false, autoSkip: true, maxTicksLimit: 10, maxRotation: 0, minRotation: 0, stepSize: 10, }, }], yAxes: [{ ticks: { suggestedMax: 100, suggestedMin: 0, stepSize: 20, callback: function(value, index, values) { return value + ' %'; } }, }], }, tooltips: { callbacks: { label: function (tooltipItem, chart) { var label = chart.datasets[tooltipItem.datasetIndex].label || ''; if (label) { label += ': '; } return label += tooltipItem.yLabel + ' %'; } } } } }); var mem_ctx = $("#memChart").get(0).getContext("2d"); var memChart = new Chart(mem_ctx, { type: 'line', data: { datasets : [{ label: "Usage" }] }, options: { responsive: true, legend: { display: false }, scales: { xAxes:[{ offset: false, ticks: { beginAtZero: false, autoSkip: true, maxTicksLimit: 10, maxRotation: 0, minRotation: 0, stepSize: 10, }, }], yAxes: [{ ticks: { suggestedMax: 100, suggestedMin: 0, stepSize: 20, callback: function(value, index, values) { return value + ' MB'; } }, }], }, tooltips: { callbacks: { label: function (tooltipItem, chart) { var label = chart.datasets[tooltipItem.datasetIndex].label || ''; if (label) { label += '(RSS): '; } return label += tooltipItem.yLabel + ' MB'; } } } } }); var diskChart = {}; {% for disk in instance.disks %} var disk_ctx_{{ disk.dev }} = $("#blk{{ disk.dev }}Chart").get(0).getContext("2d"); diskChart['{{ disk.dev }}'] = new Chart(disk_ctx_{{ disk.dev }}, { type: 'line', data: { datasets : [{ backgroundColor: "rgba(127,205,187,0.5)", label: "Read" }, { backgroundColor: "rgba(44,127,184,0.5)", label: "Write" }] }, options: { responsive: true, legend: { display: false }, scales: { xAxes:[{ offset: false, ticks: { beginAtZero: false, autoSkip: true, maxTicksLimit: 10, maxRotation: 0, minRotation: 0, stepSize: 10, }, }], yAxes: [{ ticks: { suggestedmax: 100, suggestedMin: 0, callback: function(value, index, values) { return value + ' Mb/s'; } }, }], }, tooltips: { callbacks: { label: function (tooltipItem, chart) { var label = chart.datasets[tooltipItem.datasetIndex].label || ''; if (label) { label += ': '; } return label += tooltipItem.yLabel + ' Mb/s'; } } } } }); {% endfor %} var netChart = {}; {% for net in instance.networks %} var net_ctx_{{ forloop.counter0 }} = $("#netEth{{ forloop.counter0 }}Chart").get(0).getContext("2d"); netChart[{{ forloop.counter0 }}] = new Chart(net_ctx_{{ forloop.counter0 }}, { type: 'line', data: { datasets : [ { backgroundColor: "rgba(127,205,187,0.5)", label: "Inbound" }, { backgroundColor: "rgba(44,127,184,0.5)", label: "Outbound" }] }, options: { responsive: true, legend: { display: false }, scales: { xAxes:[{ offset: false, ticks: { beginAtZero: false, autoSkip: true, maxTicksLimit: 10, maxRotation: 0, minRotation: 0, stepSize: 10, }, }], yAxes: [{ ticks: { suggestedMax: 100, suggestedMin: 0, callback: function(value, index, values) { return value + ' Mbps'; } }, }], }, tooltips: { callbacks: { label: function (tooltipItem, chart) { var label = chart.datasets[tooltipItem.datasetIndex].label || ''; if (label) { label += ': '; } return label += tooltipItem.yLabel + ' Mbps'; } } } } }); {% endfor %} var graph_interval = window.setInterval(function graph_usage() { $.getJSON("{% url 'instances:stats' instance.id %}", function (data) { cpuChart.data.labels.push(data.timeline); cpuChart.data.datasets[0].data.push(data.cpudata); if (cpuChart.data.datasets[0].data.length > 10){ cpuChart.data.labels.shift(); cpuChart.data.datasets[0].data.shift(); } cpuChart.update(); memChart.data.labels.push(data.timeline); memChart.options.scales.yAxes[0].ticks.max = parseInt(data.memdata.total / 1024); memChart.options.scales.yAxes[0].ticks.stepSize = parseInt(data.memdata.total / (1024 * 5)); memChart.data.datasets[0].data.push(data.memdata.used / 1024); if (memChart.data.datasets[0].data.length > 10){ memChart.data.labels.shift(); memChart.data.datasets[0].data.shift(); } memChart.update(); for (let j = 0; j < data.blkdata.length; j++) { diskChart[data.blkdata[j].dev].data.labels.push(data.timeline); diskChart[data.blkdata[j].dev].data.datasets[0].data.push(data.blkdata[j].data[0]); diskChart[data.blkdata[j].dev].data.datasets[1].data.push(data.blkdata[j].data[1]); if (diskChart[data.blkdata[j].dev].data.datasets[0].data.length > 10){ diskChart[data.blkdata[j].dev].data.labels.shift(); diskChart[data.blkdata[j].dev].data.datasets[0].data.shift(); diskChart[data.blkdata[j].dev].data.datasets[1].data.shift(); } diskChart[data.blkdata[j].dev].update(); } for (let j = 0; j < data.netdata.length; j++) { netChart[data.netdata[j].dev].data.labels.push(data.timeline); netChart[data.netdata[j].dev].data.datasets[0].data.push(data.netdata[j].data[0]); netChart[data.netdata[j].dev].data.datasets[1].data.push(data.netdata[j].data[1]); if (netChart[data.netdata[j].dev].data.datasets[0].data.length > 10){ netChart[data.netdata[j].dev].data.labels.shift(); netChart[data.netdata[j].dev].data.datasets[0].data.shift(); netChart[data.netdata[j].dev].data.datasets[1].data.shift(); } netChart[data.netdata[j].dev].update(); } }); }, 10000); }); </script> <script> backgroundJobRunning = false; var status_interval = window.setInterval(function get_status() { var status = {{ instance.status|lower }}; $.getJSON("{% url 'instances:status' instance.id %}", function (data) { if (data['status'] != status && !backgroundJobRunning) { window.location.reload() } }); }, 5000); // Stop getting status info before delete instance $('#delete_form').submit(function(){ window.clearInterval(status_interval); return true; }); </script> <script> var hash = location.hash; if (~$.inArray(hash, ['#poweron', '#poweroff', '#powercycle', '#suspend', '#resume'])) { var btnsect = $('#navbtn>li>a'); $(btnsect).each(function () { if ($(this).attr('href') === '#power') { $(this).trigger('click'); } }); } if (~$.inArray(hash, ['#resize', "#resizevm_cpu", "#resizevm_mem", "#resizevm_disk"])) { var btnsect = $('#navbtn>li>a'); $(btnsect).each(function () { if ($(this).attr('href') === '#resize') { $(this).trigger('click'); } }); } if (~$.inArray(hash, ['#osinfo', '#boot_opt', "#disks", '#network', '#clone', '#xmledit', '#vncsettings', '#migrate', '#options', '#users'])) { var btnsect = $('#navbtn>li>a'); $(btnsect).each(function () { if ($(this).attr('href') === '#settings') { $(this).trigger('click'); } }); var btn = $('#settings>div>ul>li>a'); $(btn).each(function () { if ($(this).attr('href') === hash) { $(this).trigger('click'); } }); } if (~$.inArray(hash, ['#takesnapshot', "#takeextsnapshot", "#managesnapshot"])) { var btnsect = $('#navbtn>li>a'); $(btnsect).each(function () { if ($(this).attr('href') === '#snapshots') { $(this).trigger('click'); } }); var btn = $('#snapshots>div>ul>li>a'); $(btn).each(function () { if ($(this).attr('href') === hash) { $(this).trigger('click'); } }); } </script> <script> function get_osinfo() { document.querySelector("#settings-tab").click(); document.querySelector("#osinfo-tab").click(); $.getJSON("{% url 'instances:osinfo' instance.id %}", function (data) { $.each(data, function() { $('#oshostname').text(data['host-name']); $('#osname').text(data.id); $('#osprettyname').text(data['pretty-name']); $('#oskernelrelease').text(data['kernel-release']); $('#oskernelversion').text(data['kernel-version']); $('#osversion').text(data.version); $('#ostimezone').text(data.zone + " / " + data.offset); }) }); } </script> {% endblock %}