diff --git a/instances/templates/add_instance_volume.html b/instances/templates/add_instance_volume.html index 9cea2fc..6b0fed3 100644 --- a/instances/templates/add_instance_volume.html +++ b/instances/templates/add_instance_volume.html @@ -1,6 +1,6 @@ {% load i18n %} {% 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> </a> diff --git a/instances/templates/instance.html b/instances/templates/instance.html index 4413b51..21d1531 100644 --- a/instances/templates/instance.html +++ b/instances/templates/instance.html @@ -526,8 +526,8 @@ <!-- Nav tabs --> <ul class="nav nav-tabs" role="tablist"> <li role="presentation" class="active"> - <a href="#media" aria-controls="media" role="tab" data-toggle="tab"> - {% trans "Media" %} + <a href="#boot_opt" aria-controls="boot" role="tab" data-toggle="tab"> + {% trans "Boot" %} </a> </li> <li role="presentation"> @@ -535,13 +535,6 @@ {% trans "Disk" %} </a> </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 %} <li role="presentation"> @@ -593,78 +586,152 @@ </ul> <!-- Tab panes --> <div class="tab-content"> - <div role="tabpanel" class="tab-pane tab-pane-bordered active" id="media"> - {% if request.user.is_superuser and status == 5 %} - <form class="form-horizontal" action="" method="post" role="form">{% csrf_token %} - <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 %} + {% if request.user.is_superuser %} + <div role="tabpanel" class="tab-pane tab-pane-bordered active" id="boot_opt"> + <p style="font-weight:bold;">{% trans 'Autostart' %}</p> <form class="form-horizontal" action="" method="post" role="form">{% csrf_token %} <div class="form-group"> - <a class="col-sm-2 control-label" - name="details" - title="{% trans "Details" %}" - tabindex="0" - 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> + <div class="col-sm-12 col-sm-offset-2"> + <p>{% trans "Autostart your instance when host server is power on " %} + {% ifequal autostart 0 %} + <input type="submit" class="btn btn-success" name="set_autostart" value="{% trans "Enable" %}"> {% 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 %} + <input type="submit" class="btn btn-danger" name="unset_autostart" value="{% trans "Disable" %}"> + {% endifequal %} + </p> + </div> </div> </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"> <thead> <th>{% trans "Device" %}</th> @@ -726,21 +793,167 @@ </tbody> </table> </div> - <div class="clearfix"></div> - </div> + <div class="clearfix"></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 role="tabpanel" class="tab-pane tab-pane-bordered" id="autostart"> - <p>{% trans "Autostart your instance when host server is power on" %}</p> - <form class="form-horizontal" action="" method="post" role="form">{% csrf_token %} - {% ifequal autostart 0 %} - <input type="submit" class="btn btn-lg btn-success pull-right" name="set_autostart" value="{% trans "Enable" %}"> + <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> + <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 %} - <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 %} </form> <div class="clearfix"></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 %} {% if request.user.is_superuser or userinstance.is_vnc %} <div role="tabpanel" class="tab-pane tab-pane-bordered" id="vncsettings"> @@ -857,65 +1070,6 @@ <div class="clearfix"></div> </div> {% 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 %} <div role="tabpanel" class="tab-pane tab-pane-bordered" id="clone"> <p style="font-weight:bold;">{% trans "Create a clone" %}</p> @@ -923,25 +1077,25 @@ <div class="form-group"> <label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Clone Name" %}</label> {% if request.user.is_superuser %} - <div class="col-sm-4"> - <input id="clone_name" type="text" class="form-control" name="name" value="{{ vname }}-clone"/> - </div> - <div class="col-sm-4"> - <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> - </div> + <div class="col-sm-4"> + <input id="clone_name" type="text" class="form-control" name="name" value="{{ vname }}-clone"/> + </div> + <div class="col-sm-4"> + <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> + </div> {% elif clone_instance_auto_name %} - <div class="col-sm-4"> - <input id="clone_instance_auto_name" type="text" class="form-control" name="auto_name" value="Automatic" disabled/> - </div> + <div class="col-sm-4"> + <input id="clone_instance_auto_name" type="text" class="form-control" name="auto_name" value="Automatic" disabled/> + </div> {% else %} - <div class="col-sm-4"> - <select id="select_clone_name" class="form-control" name="name" size="1"/> - {% for name in clone_free_names %} - <option value="{{ name }}">{{ name }}</option> - {% endfor %} - </select> - </div> + <div class="col-sm-4"> + <select id="select_clone_name" class="form-control" name="name" size="1"/> + {% for name in clone_free_names %} + <option value="{{ name }}">{{ name }}</option> + {% endfor %} + </select> + </div> {% endif %} </div> {% if request.user.is_superuser %} @@ -1006,84 +1160,6 @@ </form> <div class="clearfix"></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"> <form class="form-horizontal" action="" method="post" role="form">{% csrf_token %} <div class="form-group"> @@ -1113,36 +1189,6 @@ <div class="clearfix"></div> </div> {% 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> @@ -1217,7 +1263,7 @@ </tr> </thead> <tbody class="searchable"> - <tr><td colspan="3"><i>None ...</i></td></tr> + <tr><td colspan="3"><i>{% trans 'None' %}...</i></td></tr> </tbody> </table> </div> @@ -1271,8 +1317,6 @@ <script src="{% static "js/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(); @@ -1281,8 +1325,7 @@ $.each(data['vols'], function(i, item) { $("#vols").append('<option value=' + item +'>' + item + '</option>'); - }); - + }) }); var sto_drop = document.getElementById('select_storage'); @@ -1292,7 +1335,6 @@ var sto_input = document.getElementById('selected_storage'); sto_input.value = pool; sto_input.innerHTML = pool; - } </script> @@ -1415,7 +1457,6 @@ }); $(document).ready(function () { // set vdi url - $.get("{% url 'vdi_url' vname %}", function(data) { $("#vdi_url_input").attr("value", data); $("#vdi_url").attr("href", data); @@ -1444,6 +1485,72 @@ $(document).ready(function(){ 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> $(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'); $(btnsect).each(function () { if ($(this).attr('href') === '#settings') { diff --git a/instances/views.py b/instances/views.py index 9ee9540..2b5f9e4 100644 --- a/instances/views.py +++ b/instances/views.py @@ -255,6 +255,8 @@ def instance(request, compute_id, vname): compute_nwfilters = conn.get_nwfilters() status = conn.get_status() autostart = conn.get_autostart() + bootmenu = conn.get_bootmenu() + boot_order = conn.get_bootorder() vcpu = conn.get_vcpu() cur_vcpu = conn.get_cur_vcpu() uuid = conn.get_uuid() @@ -525,7 +527,7 @@ def instance(request, compute_id, vname): conn.attach_disk("", target, device='cdrom', cache='none', targetbus=bus) msg = _('Add CD-Rom: ' + target) 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: dev = request.POST.get('detach_cdrom', '') @@ -533,7 +535,7 @@ def instance(request, compute_id, vname): conn.detach_disk(dev) msg = _('Detach CD-Rom: ' + dev) 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: image = request.POST.get('path', '') @@ -541,7 +543,7 @@ def instance(request, compute_id, vname): conn.umount_iso(dev, image) msg = _("Mount media: " + dev) 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: image = request.POST.get('media', '') @@ -549,7 +551,7 @@ def instance(request, compute_id, vname): conn.mount_iso(dev, image) msg = _("Umount media: " + dev) 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: name = request.POST.get('name', '') @@ -591,13 +593,37 @@ def instance(request, compute_id, vname): conn.set_autostart(1) msg = _("Set autostart") 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: conn.set_autostart(0) msg = _("Unset autostart") 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: exit_xml = request.POST.get('inst_xml', '') diff --git a/static/css/webvirtcloud.css b/static/css/webvirtcloud.css index 542e8b9..2149dea 100644 --- a/static/css/webvirtcloud.css +++ b/static/css/webvirtcloud.css @@ -133,3 +133,21 @@ p { display: inline; 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; +} \ No newline at end of file diff --git a/vrtManager/instance.py b/vrtManager/instance.py index c0c53af..143465b 100644 --- a/vrtManager/instance.py +++ b/vrtManager/instance.py @@ -248,7 +248,7 @@ class wvmInstance(wvmConnect): def disks(doc): result = [] 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'): 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] try: disk_format = disk.xpath('driver/@type')[0] + except: + pass + try: disk_cache = disk.xpath('driver/@cache')[0] except: pass @@ -310,6 +313,121 @@ class wvmInstance(wvmConnect): 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 attach_iso(dev, disk, vol): if disk.get('device') == 'cdrom':