1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2024-12-24 15:15:22 +00:00

Add boot menu/order functionality. Minor improvements. Some bug fixes

This commit is contained in:
catborise 2019-01-15 15:55:05 +03:00
parent 6b06ed25ff
commit 96efde814a
5 changed files with 553 additions and 284 deletions

View file

@ -1,6 +1,6 @@
{% load i18n %} {% load i18n %}
{% if request.user.is_superuser %} {% if request.user.is_superuser %}
<a href="#addvol" type="button" class="btn btn-success pull-right" data-toggle="modal"> <a href="#addvol" type="button" class="btn btn-success pull-right" data-toggle="modal" title="Add Volume">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span> <span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
</a> </a>

View file

@ -526,8 +526,8 @@
<!-- Nav tabs --> <!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist"> <ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active"> <li role="presentation" class="active">
<a href="#media" aria-controls="media" role="tab" data-toggle="tab"> <a href="#boot_opt" aria-controls="boot" role="tab" data-toggle="tab">
{% trans "Media" %} {% trans "Boot" %}
</a> </a>
</li> </li>
<li role="presentation"> <li role="presentation">
@ -535,13 +535,6 @@
{% trans "Disk" %} {% trans "Disk" %}
</a> </a>
</li> </li>
{% if request.user.is_superuser %}
<li role="presentation">
<a href="#autostart" aria-controls="autostart" role="tab" data-toggle="tab">
{% trans "Autostart" %}
</a>
</li>
{% endif %}
{% if request.user.is_superuser or userinstance.is_vnc %} {% if request.user.is_superuser or userinstance.is_vnc %}
<li role="presentation"> <li role="presentation">
@ -593,78 +586,152 @@
</ul> </ul>
<!-- Tab panes --> <!-- Tab panes -->
<div class="tab-content"> <div class="tab-content">
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="media"> {% if request.user.is_superuser %}
{% if request.user.is_superuser and status == 5 %} <div role="tabpanel" class="tab-pane tab-pane-bordered active" id="boot_opt">
<form class="form-horizontal" action="" method="post" role="form">{% csrf_token %} <p style="font-weight:bold;">{% trans 'Autostart' %}</p>
<div class="form-group">
<div class="col-sm-12">
<button type="submit" name="add_cdrom" type="button" class="btn btn-success pull-right">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
</button>
</div>
</div>
</form>
{% endif %}
{% for cd in media %}
<form class="form-horizontal" action="" method="post" role="form">{% csrf_token %} <form class="form-horizontal" action="" method="post" role="form">{% csrf_token %}
<div class="form-group"> <div class="form-group">
<a class="col-sm-2 control-label" <div class="col-sm-12 col-sm-offset-2">
name="details" <p>{% trans "Autostart your instance when host server is power on " %}
title="{% trans "Details" %}" {% ifequal autostart 0 %}
tabindex="0" <input type="submit" class="btn btn-success" name="set_autostart" value="{% trans "Enable" %}">
data-trigger="focus"
data-toggle="popover"
data-html="true"
data-content="<strong>Bus:</strong> {{ cd.bus }} <br/> <strong>Dev:</strong> {{ cd.dev }}">
{% trans "CDROM" %} {{ forloop.counter }}
</a>
{% if not cd.image %}
<div class="col-sm-6">
<select name="media" class="form-control">
{% if media_iso %}
{% for iso in media_iso %}
<option value="{{ iso }}">{{ iso }}</option>
{% endfor %}
{% else %}
<option value="none">{% trans "None" %}</option>
{% endif %}
</select>
</div>
<div class="col-sm-2">
{% if media_iso and allow_admin_or_not_template %}
<button type="submit" class="btn btn-sm btn-success pull-left" name="mount_iso" value="{{ cd.dev }}" style="margin-top: 2px;">{% trans "Mount" %}</button>
{% if status == 5 %}
<button type="submit" class="btn btn-sm btn-danger pull-left" name="detach_cdrom" value="{{ cd.dev }}" style="margin-top: 2px;"><i class="glyphicon glyphicon-remove-circle"></i></button>
{% endif %}
{% else %}
<button class="btn btn-sm btn-success pull-left disabled" style="margin-top: 2px;">{% trans "Mount" %}</button>
{% endif %}
</div>
{% else %} {% else %}
<div class="col-sm-6"> <input type="submit" class="btn btn-danger" name="unset_autostart" value="{% trans "Disable" %}">
<input class="form-control" value="{{ cd.image }}" disabled/> {% endifequal %}
</div> </p>
<div class="col-sm-2"> </div>
<input type="hidden" name="path" value="{{ cd.path }}">
{% if allow_admin_or_not_template %}
<button type="submit" class="btn btn-sm btn-success pull-left" value="{{ cd.dev }}" name="umount_iso" style="margin-top: 2px;">{% trans "Umount" %}</button>
{% else %}
<button class="btn btn-sm btn-success pull-left disabled" value="{{ cd.dev }}" name="umount_iso" style="margin-top: 2px;">{% trans "Umount" %}</button>
{% endif %}
</div>
{% endif %}
</div> </div>
</form> </form>
{% endfor %}
<div class="clearfix"></div>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="disks">
<p style="font-weight:bold;">
{% trans "Instance Volumes" %}
{% include 'add_instance_volume.html' %}
</p>
<div class="col-xs-12 col-sm-12"> <p style="font-weight:bold;">{% trans 'Boot Order' %}</p>
<form class="form-horizontal" action="" method="post" role="form">{% csrf_token %}
<div class="form-group">
<div class="col-sm-12 col-sm-offset-2">
{% ifequal status 5 %}
<p>{% trans "Enable Boot Menu for your instance when it starts up " %}
{% ifequal bootmenu 0 %}
<input type="submit" class="btn btn-success" name="set_bootmenu" value="{% trans "Enable" %}">
{% else %}
<input type="submit" class="btn btn-danger" name="unset_bootmenu" value="{% trans "Disable" %}">
{% endifequal %}
{% else %}
<p>{% trans "**** Please shutdown instance to modify boot menu ****" %}
{% endifequal %}
</p>
</div>
</div>
</form>
{% ifequal bootmenu 1 %}
<div class="col-sm-6 col-sm-offset-2">
<div class="well">
{% for idx, val in boot_order.items %}
<label>{{ idx|add:1 }}:{{ val.target }}, </label>
{% endfor %}
</div>
</div>
<form class="form-horizontal" action="" method="post" role="form">{% csrf_token %}
<input id="bootorder" name="bootorder" hidden>
<div class="form-group">
<div class="col-sm-6 col-sm-offset-2">
<div id="b_order" class="multiselect">
{% for disk in disks %}
<label><input type="checkbox" name="disk:{{ disk.dev }}" value="disk:{{ disk.dev }}" onclick="set_orderlist($('#bootorder'))" />{{ disk.dev }} - {{ disk.image }}</label>
{% endfor %}
{% for cd in media %}
<label><input type="checkbox" name="cdrom:{{ cd.dev }}" value="cdrom:{{ cd.dev }}" onclick="set_orderlist($('#bootorder'))"/>{{ cd.dev }} - {{ cd.image }}</label>
{% endfor %}
{% for net in networks %}
<label><input type="checkbox" name="network:{{ net.mac }}" value="network:{{ net.mac }}" onclick="set_orderlist($('#bootorder'))"/>NIC - {{ net.mac|slice:"9:" }}</label>
{% endfor %}
</div>
</div>
<div class="col-sm-3">
<div class="row" style="margin-top: 2em;">
<a href="#" id="boot_order_up" class="btn btn-default"><span class="glyphicon glyphicon-arrow-up" title="up"></span></a>
</div>
<div class="row" style="margin-top: 1em;">
<a href="#" id="boot_order_down" class="btn btn-default"><span class="glyphicon glyphicon-arrow-down" title="down"></span></a>
</div>
</div>
</div>
<div class="col-sm-6 col-sm-offset-2">
<input type="submit" class="btn btn-success btn-block" name="set_bootorder" value="{% trans "Apply" %}">
</div>
</form>
{% endifequal %}
<div class="clearfix"></div>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="disks">
{% if status == 5 %}
<form class="form-horizontal" action="" method="post" role="form">{% csrf_token %}
<p style="font-weight:bold;">
{% trans "Instance Media" %}
<button type="submit" name="add_cdrom" type="button" class="btn btn-success pull-right" title="Add CD-Rom">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
</button>
</p>
</form>
{% endif %}
{% for cd in media %}
<form class="form-horizontal" action="" method="post" role="form">{% csrf_token %}
<div class="form-group">
<a class="col-sm-3 control-label"
name="details"
title="{% trans "Details" %}"
tabindex="0"
data-trigger="focus"
data-toggle="popover"
data-html="true"
data-content="<strong>{% trans 'Bus' %}:</strong> {{ cd.bus }} <br/>
<strong>{% trans 'Dev' %}:</strong> {{ cd.dev }}">
{% trans "CDROM" %} {{ forloop.counter }}
</a>
{% if not cd.image %}
<div class="col-sm-6">
<select name="media" class="form-control">
{% if media_iso %}
{% for iso in media_iso %}
<option value="{{ iso }}">{{ iso }}</option>
{% endfor %}
{% else %}
<option value="none">{% trans "None" %}</option>
{% endif %}
</select>
</div>
<div class="col-sm-2">
{% if media_iso and allow_admin_or_not_template %}
<button type="submit" class="btn btn-sm btn-success pull-left" name="mount_iso" value="{{ cd.dev }}" style="margin-top: 2px;">{% trans "Mount" %}</button>
{% if status == 5 %}
<button type="submit" class="btn btn-sm btn-danger pull-left" name="detach_cdrom" value="{{ cd.dev }}" style="margin-top: 2px;"><i class="glyphicon glyphicon-remove-circle"></i></button>
{% endif %}
{% else %}
<button class="btn btn-sm btn-success pull-left disabled" style="margin-top: 2px;">{% trans "Mount" %}</button>
{% endif %}
</div>
{% else %}
<div class="col-sm-6">
<input class="form-control" value="{{ cd.image }}" disabled/>
</div>
<div class="col-sm-2">
<input type="hidden" name="path" value="{{ cd.path }}">
{% if allow_admin_or_not_template %}
<button type="submit" class="btn btn-sm btn-success pull-left" value="{{ cd.dev }}" name="umount_iso" style="margin-top: 2px;">{% trans "Umount" %}</button>
{% else %}
<button class="btn btn-sm btn-success pull-left disabled" value="{{ cd.dev }}" name="umount_iso" style="margin-top: 2px;">{% trans "Umount" %}</button>
{% endif %}
</div>
{% endif %}
</div>
</form>
{% endfor %}
<p style="font-weight:bold;">
{% trans "Instance Volume" %}
{% include 'add_instance_volume.html' %}
</p>
<div class="col-xs-12 col-sm-12">
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<th>{% trans "Device" %}</th> <th>{% trans "Device" %}</th>
@ -726,21 +793,167 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="network">
<p>
{% trans "Assign network device to bridge" %}
{% include 'add_instance_network_block.html' %}
</p>
<p style="font-weight:bold;">{% trans "Network devices" %}</p>
<div class="col-xs-12 col-sm-12">
<form method="post" role="form">{% csrf_token %}
{% for network in networks %}
<div class="panel panel-default">
<div class="panel-heading">
<label>eth{{ forloop.counter0 }}({{ network.target|default:"no target" }})</label>
</div>
<div class="panel-body">
<div class="form-group form-inline">
<label class="col-sm-2 col-sm-offset-1 control-label">{% trans "MAC" %} </label>
<input class="form-control" type="text" value="{{ network.mac }}" readonly/>
<label class="control-label"><em>to</em></label>
<input class="form-control" type="text" name="net-mac-{{ forloop.counter0 }}" value="{{ network.mac }}"/>
</div>
<div class="form-group form-inline">
<label class="col-sm-2 col-sm-offset-1 control-label">{% trans "NIC" %} </label>
<input class="form-control" type="text" value="{{ network.nic }}" readonly/>
<label class="control-label"><em>to</em></label>
<select class="form-control" name="net-source-{{ forloop.counter0 }}">
{% for c_net in compute_networks %}
<option value="net:{{ c_net }}" {% ifequal c_net network.nic %} selected {% endifequal %}>Network {{ c_net }}</option>
{% endfor %}
{% for c_iface in compute_interfaces %}
<option value="iface:{{ c_iface }}" {% ifequal c_iface network.nic %} selected {% endifequal %}>Interface {{ c_iface }}</option>
{% endfor %}
</select>
</div>
<div class="form-group form-inline">
<label class="col-sm-2 col-sm-offset-1">{% trans "Filter" %} </label>
<input class="form-control" type="text" value="{{ network.filterref }}" readonly/>
<label class="control-label"><em>to</em></label>
<select class="form-control" name="net-nwfilter-{{ forloop.counter0 }}">
<option value="">{% trans "None" %}</option>
{% for c_filters in compute_nwfilters %}
<option value="{{ c_filters }}" {% ifequal c_filters network.filterref %} selected {% endifequal %}>{{ c_filters }}</option>
{% endfor %}
</select>
</div>
</div>
{% if request.user.is_superuser %} <div class="panel-footer">
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="autostart"> <button class="btn btn-sm btn-primary" name="change_network" title="{% trans "Change" %}">{% trans "Change" %}</button>
<p>{% trans "Autostart your instance when host server is power on" %}</p> <button class="btn btn-sm pull-right btn-danger" value="{{ network.mac }}" name="delete_network" title="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure?" %}')">{% trans "Delete" %}</button>
<form class="form-horizontal" action="" method="post" role="form">{% csrf_token %} </div>
{% ifequal autostart 0 %} </div>
<input type="submit" class="btn btn-lg btn-success pull-right" name="set_autostart" value="{% trans "Enable" %}"> {% endfor %}
</form>
</div>
<div class="clearfix"></div>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="migrate">
<p>{% trans "For migration both host servers must have equal settings and OS type" %}</p>
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Original host" %}</label>
<div class="col-sm-6">
<p style="margin: 10px -10px 0 0;">{{ compute.name }}</p>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Host migration" %}</label>
<div class="col-sm-6">
<select name="compute_id" class="form-control">
{% if computes_count != 1 %}
{% for comp in computes %}
{% if comp.id != compute.id %}
<option value="{{ comp.id }}">{{ comp.name }}</option>
{% endif %}
{% endfor %}
{% endif %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Live migration" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="live_migrate" value="true" id="vm_live_migrate" {% ifequal status 1 %}checked{% endifequal %}>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Unsafe migration" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="unsafe_migrate" value="true" id="vm_unsafe_migrate">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Delete original" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="xml_delete" value="true" id="xml_delete" checked>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Offline migration" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="offline_migrate" value="true" id="offline_migrate" {% ifequal status 5 %}checked{% endifequal %}>
</div>
</div>
{% if computes_count != 1 %}
<button type="submit" class="btn btn-lg btn-success pull-right" name="migrate" onclick="showPleaseWaitDialog();">{% trans "Migrate" %}</button>
{% else %} {% else %}
<input type="submit" class="btn btn-lg btn-success pull-right" name="unset_autostart" value="{% trans "Disable" %}"> <button class="btn btn-lg btn-success pull-right disabled">{% trans "Migrate" %}</button>
{% endif %}
</form>
<div class="clearfix"></div></p>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="xmledit">
<p>{% trans "If you need to edit xml please Power Off the instance" %}</p>
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<div class="col-sm-12" id="xmlheight">
<textarea id="editor">{{ inst_xml }}</textarea>
</div>
{% ifequal status 5 %}
<input type="hidden" name="inst_xml">
<button type="submit" class="btn btn-lg btn-success pull-right" name="change_xml">
{% trans "Change" %}
</button>
{% else %}
<button class="btn btn-lg btn-success pull-right disabled">
{% trans "Change" %}
</button>
{% endifequal %} {% endifequal %}
</form> </form>
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="users">
<div>
<p style="font-weight:bold;">
{% trans "Instance owners" %}
{% include 'add_instance_owner_block.html' %}
</p>
</div>
<div class="table-responsive">
<table class="table table-striped sortable-theme-bootstrap" data-sortable>
<tbody class="searchable">
{% for userinstance in userinstances %}
<tr>
<td><a href="{% url 'account' userinstance.user.id %}">{{ userinstance.user }}</a></td>
<td style="width:30px;">
<form action="" method="post" style="height:10px" role="form">{% csrf_token %}
<input type="hidden" name="userinstance" value="{{ userinstance.pk }}">
<button type="submit" class="btn btn-sm btn-default" name="del_owner" title="{% trans "Delete" %}">
<i class="fa fa-trash"></i>
</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="clearfix"></div>
</div>
{% endif %} {% endif %}
{% if request.user.is_superuser or userinstance.is_vnc %} {% if request.user.is_superuser or userinstance.is_vnc %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="vncsettings"> <div role="tabpanel" class="tab-pane tab-pane-bordered" id="vncsettings">
@ -857,65 +1070,6 @@
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
{% endif %} {% endif %}
{% if request.user.is_superuser %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="network">
<p>
{% trans "Assign network device to bridge" %}
{% include 'add_instance_network_block.html' %}
</p>
<p style="font-weight:bold;">{% trans "Network devices" %}</p>
<div class="col-xs-12 col-sm-12">
<form method="post" role="form">{% csrf_token %}
{% for network in networks %}
<div class="panel panel-default">
<div class="panel-heading">
<label>eth{{ forloop.counter0 }}({{ network.target|default:"no target" }})</label>
</div>
<div class="panel-body">
<div class="form-group form-inline">
<label class="col-sm-2 col-sm-offset-1 control-label">{% trans "MAC" %} </label>
<input class="form-control" type="text" value="{{ network.mac }}" readonly/>
<label class="control-label"><em>to</em></label>
<input class="form-control" type="text" name="net-mac-{{ forloop.counter0 }}" value="{{ network.mac }}"/>
</div>
<div class="form-group form-inline">
<label class="col-sm-2 col-sm-offset-1 control-label">{% trans "NIC" %} </label>
<input class="form-control" type="text" value="{{ network.nic }}" readonly/>
<label class="control-label"><em>to</em></label>
<select class="form-control" name="net-source-{{ forloop.counter0 }}">
{% for c_net in compute_networks %}
<option value="net:{{ c_net }}" {% ifequal c_net network.nic %} selected {% endifequal %}>Network {{ c_net }}</option>
{% endfor %}
{% for c_iface in compute_interfaces %}
<option value="iface:{{ c_iface }}" {% ifequal c_iface network.nic %} selected {% endifequal %}>Interface {{ c_iface }}</option>
{% endfor %}
</select>
</div>
<div class="form-group form-inline">
<label class="col-sm-2 col-sm-offset-1">{% trans "Filter" %} </label>
<input class="form-control" type="text" value="{{ network.filterref }}" readonly/>
<label class="control-label"><em>to</em></label>
<select class="form-control" name="net-nwfilter-{{ forloop.counter0 }}">
<option value="">{% trans "None" %}</option>
{% for c_filters in compute_nwfilters %}
<option value="{{ c_filters }}" {% ifequal c_filters network.filterref %} selected {% endifequal %}>{{ c_filters }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="panel-footer">
<button class="btn btn-sm btn-primary" name="change_network" title="{% trans "Change" %}">{% trans "Change" %}</button>
<button class="btn btn-sm pull-right btn-danger" value="{{ network.mac }}" name="delete_network" title="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure?" %}')">{% trans "Delete" %}</button>
</div>
</div>
{% endfor %}
</form>
</div>
<div class="clearfix"></div>
</div>
{% endif %}
{% if request.user.is_superuser or request.user.userattributes.can_clone_instances %} {% if request.user.is_superuser or request.user.userattributes.can_clone_instances %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="clone"> <div role="tabpanel" class="tab-pane tab-pane-bordered" id="clone">
<p style="font-weight:bold;">{% trans "Create a clone" %}</p> <p style="font-weight:bold;">{% trans "Create a clone" %}</p>
@ -923,25 +1077,25 @@
<div class="form-group"> <div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Clone Name" %}</label> <label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Clone Name" %}</label>
{% if request.user.is_superuser %} {% if request.user.is_superuser %}
<div class="col-sm-4"> <div class="col-sm-4">
<input id="clone_name" type="text" class="form-control" name="name" value="{{ vname }}-clone"/> <input id="clone_name" type="text" class="form-control" name="name" value="{{ vname }}-clone"/>
</div> </div>
<div class="col-sm-4"> <div class="col-sm-4">
<button type="button" class="btn btn-sm btn-success pull-left" name="guess-clone-name" <button type="button" class="btn btn-sm btn-success pull-left" name="guess-clone-name"
onclick="guess_clone_name()" style="margin-top: 2px;">{% trans "Guess" %}</button> onclick="guess_clone_name()" style="margin-top: 2px;">{% trans "Guess" %}</button>
</div> </div>
{% elif clone_instance_auto_name %} {% elif clone_instance_auto_name %}
<div class="col-sm-4"> <div class="col-sm-4">
<input id="clone_instance_auto_name" type="text" class="form-control" name="auto_name" value="Automatic" disabled/> <input id="clone_instance_auto_name" type="text" class="form-control" name="auto_name" value="Automatic" disabled/>
</div> </div>
{% else %} {% else %}
<div class="col-sm-4"> <div class="col-sm-4">
<select id="select_clone_name" class="form-control" name="name" size="1"/> <select id="select_clone_name" class="form-control" name="name" size="1"/>
{% for name in clone_free_names %} {% for name in clone_free_names %}
<option value="{{ name }}">{{ name }}</option> <option value="{{ name }}">{{ name }}</option>
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
{% endif %} {% endif %}
</div> </div>
{% if request.user.is_superuser %} {% if request.user.is_superuser %}
@ -1006,84 +1160,6 @@
</form> </form>
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
{% endif %}
{% if request.user.is_superuser %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="migrate">
<p>{% trans "For migration both host servers must have equal settings and OS type" %}</p>
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Original host" %}</label>
<div class="col-sm-6">
<p style="margin: 10px -10px 0 0;">{{ compute.name }}</p>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Host migration" %}</label>
<div class="col-sm-6">
<select name="compute_id" class="form-control">
{% if computes_count != 1 %}
{% for comp in computes %}
{% if comp.id != compute.id %}
<option value="{{ comp.id }}">{{ comp.name }}</option>
{% endif %}
{% endfor %}
{% endif %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Live migration" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="live_migrate" value="true" id="vm_live_migrate" {% ifequal status 1 %}checked{% endifequal %}>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Unsafe migration" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="unsafe_migrate" value="true" id="vm_unsafe_migrate">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Delete original" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="xml_delete" value="true" id="xml_delete" checked>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Offline migration" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="offline_migrate" value="true" id="offline_migrate" {% ifequal status 5 %}checked{% endifequal %}>
</div>
</div>
{% if computes_count != 1 %}
<button type="submit" class="btn btn-lg btn-success pull-right" name="migrate" onclick="showPleaseWaitDialog();">{% trans "Migrate" %}</button>
{% else %}
<button class="btn btn-lg btn-success pull-right disabled">{% trans "Migrate" %}</button>
{% endif %}
</form>
<div class="clearfix"></div></p>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="xmledit">
<p>{% trans "If you need to edit xml please Power Off the instance" %}</p>
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<div class="col-sm-12" id="xmlheight">
<textarea id="editor">{{ inst_xml }}</textarea>
</div>
{% ifequal status 5 %}
<input type="hidden" name="inst_xml">
<button type="submit" class="btn btn-lg btn-success pull-right" name="change_xml">
{% trans "Change" %}
</button>
{% else %}
<button class="btn btn-lg btn-success pull-right disabled">
{% trans "Change" %}
</button>
{% endifequal %}
</form>
<div class="clearfix"></div>
</div>
{% endif %}
{% if request.user.is_superuser or request.user.userattributes.can_clone_instances %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="options"> <div role="tabpanel" class="tab-pane tab-pane-bordered" id="options">
<form class="form-horizontal" action="" method="post" role="form">{% csrf_token %} <form class="form-horizontal" action="" method="post" role="form">{% csrf_token %}
<div class="form-group"> <div class="form-group">
@ -1113,36 +1189,6 @@
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
{% endif %} {% endif %}
{% if request.user.is_superuser %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="users">
<div>
<p style="font-weight:bold;">
{% trans "Instance owners" %}
{% include 'add_instance_owner_block.html' %}
</p>
</div>
<div class="table-responsive">
<table class="table table-striped sortable-theme-bootstrap" data-sortable>
<tbody class="searchable">
{% for userinstance in userinstances %}
<tr>
<td><a href="{% url 'account' userinstance.user.id %}">{{ userinstance.user }}</a></td>
<td style="width:30px;">
<form action="" method="post" style="height:10px" role="form">{% csrf_token %}
<input type="hidden" name="userinstance" value="{{ userinstance.pk }}">
<button type="submit" class="btn btn-sm btn-default" name="del_owner" title="{% trans "Delete" %}">
<i class="fa fa-trash"></i>
</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="clearfix"></div>
</div>
{% endif %}
</div> </div>
</div> </div>
</div> </div>
@ -1217,7 +1263,7 @@
</tr> </tr>
</thead> </thead>
<tbody class="searchable"> <tbody class="searchable">
<tr><td colspan="3"><i>None ...</i></td></tr> <tr><td colspan="3"><i>{% trans 'None' %}...</i></td></tr>
</tbody> </tbody>
</table> </table>
</div> </div>
@ -1271,8 +1317,6 @@
<script src="{% static "js/ace.js" %}" type="text/javascript" charset="utf-8"></script> <script src="{% static "js/ace.js" %}" type="text/javascript" charset="utf-8"></script>
<script> <script>
function get_volumes(compute_id, pool) { function get_volumes(compute_id, pool) {
get_vol_url = "/computes/" + compute_id + "/storage/" + pool + "/volumes"; get_vol_url = "/computes/" + compute_id + "/storage/" + pool + "/volumes";
$.getJSON(get_vol_url, function (data) { $.getJSON(get_vol_url, function (data) {
$("#vols").find('option').remove(); $("#vols").find('option').remove();
@ -1281,8 +1325,7 @@
$.each(data['vols'], function(i, item) { $.each(data['vols'], function(i, item) {
$("#vols").append('<option value=' + item +'>' + item + '</option>'); $("#vols").append('<option value=' + item +'>' + item + '</option>');
}); })
}); });
var sto_drop = document.getElementById('select_storage'); var sto_drop = document.getElementById('select_storage');
@ -1292,7 +1335,6 @@
var sto_input = document.getElementById('selected_storage'); var sto_input = document.getElementById('selected_storage');
sto_input.value = pool; sto_input.value = pool;
sto_input.innerHTML = pool; sto_input.innerHTML = pool;
} }
</script> </script>
@ -1415,7 +1457,6 @@
}); });
$(document).ready(function () { $(document).ready(function () {
// set vdi url // set vdi url
$.get("{% url 'vdi_url' vname %}", function(data) { $.get("{% url 'vdi_url' vname %}", function(data) {
$("#vdi_url_input").attr("value", data); $("#vdi_url_input").attr("value", data);
$("#vdi_url").attr("href", data); $("#vdi_url").attr("href", data);
@ -1444,6 +1485,72 @@ $(document).ready(function(){
placement : 'top' 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').size();
var countSelected = $('#b_order label input:checked').size();
$('#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>
<script> <script>
$(function () { $(function () {
@ -1619,7 +1726,7 @@ $(document).ready(function(){
} }
}); });
} }
if (~$.inArray(hash, ['#media', "#disks", '#network', '#clone', '#autostart', '#xmledit', '#vncsettings', '#migrate', '#options', '#users'])) { if (~$.inArray(hash, ['#boot_opt', "#disks", '#network', '#clone', '#xmledit', '#vncsettings', '#migrate', '#options', '#users'])) {
var btnsect = $('#navbtn>li>a'); var btnsect = $('#navbtn>li>a');
$(btnsect).each(function () { $(btnsect).each(function () {
if ($(this).attr('href') === '#settings') { if ($(this).attr('href') === '#settings') {

View file

@ -255,6 +255,8 @@ def instance(request, compute_id, vname):
compute_nwfilters = conn.get_nwfilters() compute_nwfilters = conn.get_nwfilters()
status = conn.get_status() status = conn.get_status()
autostart = conn.get_autostart() autostart = conn.get_autostart()
bootmenu = conn.get_bootmenu()
boot_order = conn.get_bootorder()
vcpu = conn.get_vcpu() vcpu = conn.get_vcpu()
cur_vcpu = conn.get_cur_vcpu() cur_vcpu = conn.get_cur_vcpu()
uuid = conn.get_uuid() uuid = conn.get_uuid()
@ -525,7 +527,7 @@ def instance(request, compute_id, vname):
conn.attach_disk("", target, device='cdrom', cache='none', targetbus=bus) conn.attach_disk("", target, device='cdrom', cache='none', targetbus=bus)
msg = _('Add CD-Rom: ' + target) msg = _('Add CD-Rom: ' + target)
addlogmsg(request.user.username, instance.name, msg) addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#media') return HttpResponseRedirect(request.get_full_path() + '#disks')
if 'detach_cdrom' in request.POST and allow_admin_or_not_template: if 'detach_cdrom' in request.POST and allow_admin_or_not_template:
dev = request.POST.get('detach_cdrom', '') dev = request.POST.get('detach_cdrom', '')
@ -533,7 +535,7 @@ def instance(request, compute_id, vname):
conn.detach_disk(dev) conn.detach_disk(dev)
msg = _('Detach CD-Rom: ' + dev) msg = _('Detach CD-Rom: ' + dev)
addlogmsg(request.user.username, instance.name, msg) addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#media') return HttpResponseRedirect(request.get_full_path() + '#disks')
if 'umount_iso' in request.POST and allow_admin_or_not_template: if 'umount_iso' in request.POST and allow_admin_or_not_template:
image = request.POST.get('path', '') image = request.POST.get('path', '')
@ -541,7 +543,7 @@ def instance(request, compute_id, vname):
conn.umount_iso(dev, image) conn.umount_iso(dev, image)
msg = _("Mount media: " + dev) msg = _("Mount media: " + dev)
addlogmsg(request.user.username, instance.name, msg) addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#media') return HttpResponseRedirect(request.get_full_path() + '#disks')
if 'mount_iso' in request.POST and allow_admin_or_not_template: if 'mount_iso' in request.POST and allow_admin_or_not_template:
image = request.POST.get('media', '') image = request.POST.get('media', '')
@ -549,7 +551,7 @@ def instance(request, compute_id, vname):
conn.mount_iso(dev, image) conn.mount_iso(dev, image)
msg = _("Umount media: " + dev) msg = _("Umount media: " + dev)
addlogmsg(request.user.username, instance.name, msg) addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#media') return HttpResponseRedirect(request.get_full_path() + '#disks')
if 'snapshot' in request.POST and allow_admin_or_not_template: if 'snapshot' in request.POST and allow_admin_or_not_template:
name = request.POST.get('name', '') name = request.POST.get('name', '')
@ -591,13 +593,37 @@ def instance(request, compute_id, vname):
conn.set_autostart(1) conn.set_autostart(1)
msg = _("Set autostart") msg = _("Set autostart")
addlogmsg(request.user.username, instance.name, msg) addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#autostart') return HttpResponseRedirect(request.get_full_path() + '#boot_opt')
if 'unset_autostart' in request.POST: if 'unset_autostart' in request.POST:
conn.set_autostart(0) conn.set_autostart(0)
msg = _("Unset autostart") msg = _("Unset autostart")
addlogmsg(request.user.username, instance.name, msg) addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#autostart') return HttpResponseRedirect(request.get_full_path() + '#boot_opt')
if 'set_bootmenu' in request.POST:
conn.set_bootmenu(1)
msg = _("Enable boot menu")
addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#boot_opt')
if 'unset_bootmenu' in request.POST:
conn.set_bootmenu(0)
msg = _("Disable boot menu")
addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#boot_opt')
if 'set_bootorder' in request.POST:
bootorder = request.POST.get('bootorder', '')
if bootorder:
order_list = {}
for idx, val in enumerate(bootorder.split(',')):
type, dev = val.split(':', 1)
order_list[idx] = {"type": type, "dev": dev}
conn.set_bootorder(order_list)
msg = _("Set boot order")
addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#boot_opt')
if 'change_xml' in request.POST: if 'change_xml' in request.POST:
exit_xml = request.POST.get('inst_xml', '') exit_xml = request.POST.get('inst_xml', '')

View file

@ -133,3 +133,21 @@ p {
display: inline; display: inline;
min-width: 250px; min-width: 250px;
} }
.multiselect {
width:27em;
height:10em;
border:solid 2px #c0c0c0;
overflow:auto;
padding: 0 1em;
margin: 0.9em;
}
.multiselect label {
display:block;
}
.multiselect-on {
color:#ffffff;
background-color:#000099;
}

View file

@ -248,7 +248,7 @@ class wvmInstance(wvmConnect):
def disks(doc): def disks(doc):
result = [] result = []
dev = volume = storage = src_file = None dev = volume = storage = src_file = None
disk_format = used_size = disk_size = None disk_format = used_size = disk_size = disk_cache = None
for disk in doc.xpath('/domain/devices/disk'): for disk in doc.xpath('/domain/devices/disk'):
device = disk.xpath('@device')[0] device = disk.xpath('@device')[0]
@ -259,6 +259,9 @@ class wvmInstance(wvmConnect):
src_file = disk.xpath('source/@file|source/@dev|source/@name|source/@volume')[0] src_file = disk.xpath('source/@file|source/@dev|source/@name|source/@volume')[0]
try: try:
disk_format = disk.xpath('driver/@type')[0] disk_format = disk.xpath('driver/@type')[0]
except:
pass
try:
disk_cache = disk.xpath('driver/@cache')[0] disk_cache = disk.xpath('driver/@cache')[0]
except: except:
pass pass
@ -310,6 +313,121 @@ class wvmInstance(wvmConnect):
return util.get_xml_path(self._XMLDesc(0), func=disks) return util.get_xml_path(self._XMLDesc(0), func=disks)
def get_bootmenu(self):
menu = util.get_xml_path(self._XMLDesc(0), "/domain/os/bootmenu/@enable")
return True if menu == 'yes' else False
def set_bootmenu(self, flag):
tree = ElementTree.fromstring(self._XMLDesc(0))
os = tree.find('os')
menu = os.find("bootmenu")
if menu == None:
bootmenu = ElementTree.fromstring("<bootmenu enable='yes'/>")
os.append(bootmenu)
menu = os.find("bootmenu")
if flag == 0: # Disable
menu.attrib['enable'] = 'no'
elif flag == 1: # Enable
menu.attrib['enable'] = 'yes'
elif flag == -1: # Remove
os.remove(menu)
else:
raise Exception('Unknown boot menu option, please choose one of 0:disable, 1:enable, -1:remove')
xmldom = ElementTree.tostring(tree)
self._defineXML(xmldom)
def get_bootorder(self):
boot_order = {}
tree = ElementTree.fromstring(self._XMLDesc(0))
os = tree.find('os')
boot = os.findall('boot')
for idx, b in enumerate(boot):
dev = b.get('dev')
if dev == 'hd':
target = "disk"
type = "file"
elif dev == 'fd':
target = "floppy"
type = "file"
elif dev == 'cdrom':
target = "cdrom"
type = "file"
elif dev == 'network':
target = "network"
type = "network"
boot_order[idx] = {"type": type, "dev": dev, "target": target}
devices = tree.find('devices')
for dev in devices:
dev_target = dev_type = dev_device = dev_alias = None
boot_dev = dev.find('boot')
if boot_dev != None:
idx = boot_dev.get('order')
dev_type = dev.get('type')
dev_device = dev.get('device')
if dev_type == 'file':
dev_target = dev.find('target').get('dev')
elif dev_type == 'network':
dev_mac = dev.find('mac').get('address')
dev_device = "network"
dev_target = "nic-{}".format(dev_mac[9:])
elif dev_type == 'usb':
pass
boot_order[int(idx)-1] = {"type": dev_type, "dev": dev_device, "target": dev_target}
return boot_order
def set_bootorder(self, devorder):
if not devorder:
return
def remove_bootorder():
tree = ElementTree.fromstring(self._XMLDesc(0))
os = tree.find('os')
boot = os.findall('boot')
# Remove old style boot order
for b in boot:
os.remove(b)
# Remove rest of them
for dev in tree.find('devices'):
boot_dev = dev.find('boot')
if boot_dev != None:
dev.remove(boot_dev)
return tree
tree = remove_bootorder()
for idx, dev in devorder.items():
order = ElementTree.fromstring("<boot order='{}'/>".format(idx + 1))
if dev['type'] == 'disk':
devices = tree.findall("./devices/disk[@device='disk']")
for d in devices:
device = d.find("./target[@dev='{}']".format(dev['dev']))
if device != None:
d.append(order)
elif dev['type'] == 'cdrom':
devices = tree.findall("./devices/disk[@device='cdrom']")
for d in devices:
device = d.find("./target[@dev='{}']".format(dev['dev']))
if device != None:
d.append(order)
elif dev['type'] == 'network':
devices = tree.findall("./devices/interface[@type='network']")
for d in devices:
device = d.find("mac[@address='{}']".format(dev['dev']))
if device != None:
d.append(order)
else:
raise Exception('Invalid Device Type for boot order')
self._defineXML(ElementTree.tostring(tree))
def mount_iso(self, dev, image): def mount_iso(self, dev, image):
def attach_iso(dev, disk, vol): def attach_iso(dev, disk, vol):
if disk.get('device') == 'cdrom': if disk.get('device') == 'cdrom':