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

Merge pull request #218 from catborise/master

Boot Menu/Order Functionality And some fixes
This commit is contained in:
Anatoliy Guskov 2019-01-22 22:37:19 +02:00 committed by GitHub
commit 6d178a67d8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 1560 additions and 766 deletions

View file

@ -177,7 +177,7 @@ def compute_graph(request, compute_id):
datasets = {}
cookies = {}
compute = get_object_or_404(Compute, pk=compute_id)
curent_time = time.strftime("%H:%M:%S")
current_time = time.strftime("%H:%M:%S")
try:
conn = wvmHostDetails(compute.hostname,
@ -208,7 +208,7 @@ def compute_graph(request, compute_id):
datasets['mem'] = eval(cookies['mem'])
datasets['timer'] = eval(cookies['timer'])
datasets['timer'].append(curent_time)
datasets['timer'].append(current_time)
datasets['cpu'].append(int(cpu_usage['usage']))
datasets['mem'].append(int(mem_usage['usage']) / 1048576)

View file

@ -1,7 +1,7 @@
Django==1.11.14
Django==1.11.17
websockify==0.8.0
gunicorn==19.9.0
lxml==4.2.3
libvirt-python==4.4.0
lxml==4.2.5
libvirt-python==4.10.0
pytz

View file

@ -441,7 +441,7 @@
<label class="col-sm-3 control-label">{% trans "HDD" %}</label>
<input id="images" name="images" type="hidden" value=""/>
<div class="col-sm-3">
<select id="storage" name="storage" class="form-control" onchange="get_template_vols({{ compute_id }}, value);">
<select class="form-control" onchange="get_template_vols({{ compute_id }}, value);">
{% if storages %}
<option value disabled selected>{% trans "Select pool" %}...</option>
{% for storage in storages %}
@ -458,6 +458,20 @@
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Storage" %}</label>
<div class="col-sm-7">
<select id="storage" name="storage" class="form-control" disabled>
{% if storages %}
{% for storage in storages %}
<option value="{{ storage }}" >{{ storage }}</option>
{% endfor %}
{% else %}
<option value="">{% trans "None" %}</option>
{% endif %}
</select>
</div>
</div>
<div class="form-group meta-prealloc">
<label class="col-sm-3 control-label">{% trans "Metadata" %}</label>
<div class="col-sm-7">
@ -697,6 +711,9 @@
});
});
$("#template").removeAttr("disabled");
$("#storage").val(pool).change();
$("#storage").removeAttr("disabled");
}
function get_disk_bus_choices(compute_id, dev_idx, disk_type){

View file

@ -134,7 +134,7 @@ def create_instance(request, compute_id):
error_msg = _("Image has already exist. Please check volumes or change instance name")
error_messages.append(error_msg)
else:
clone_path = conn.clone_from_template(data['name'], templ_path, metadata=meta_prealloc)
clone_path = conn.clone_from_template(data['name'], templ_path, data['storage'], metadata=meta_prealloc)
volume = dict()
volume['path'] = clone_path
volume['type'] = conn.get_volume_type(clone_path)

View file

@ -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>

View file

@ -40,7 +40,7 @@
</div>
</div>
{% if user_quota_msg %}
<span class="label label-warning">{{ user_quota_msg|capfirst }} quota reached.</span>
<span class="label label-warning">{{ user_quota_msg|capfirst }} {% trans "quota reached" %}.</span>
{% endif %}
<hr>
</div>
@ -187,23 +187,18 @@
{% endif %}
{% endifequal %}
{% ifequal status 3 %}
{% if request.user.is_superuser %}
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="resume">
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="resume">
<form action="" method="post" role="form">{% csrf_token %}
{% if request.user.is_superuser %}
<p>{% trans "This action restore the instance after suspend." %}</p>
<form action="" method="post" role="form">{% csrf_token %}
<input type="submit" name="resume" class="btn btn-lg btn-success pull-right" value="{% trans "Resume" %}">
<div class="clearfix"></div>
</form>
</div>
{% else %}
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="resume">
<input type="submit" name="resume" class="btn btn-lg btn-success pull-right" value="{% trans "Resume" %}">
{% else %}
<p>{% trans "Administrator blocked your instance." %}</p>
<form action="" method="post" role="form">{% csrf_token %}
<button class="btn btn-lg btn-success disabled pull-right">{% trans "Resume" %}</button>
<div class="clearfix"></div>
</form>
</div>
{% endif %}
<button class="btn btn-lg btn-success disabled pull-right">{% trans "Resume" %}</button>
{% endif %}
<div class="clearfix"></div>
</form>
</div>
{% endifequal %}
{% ifequal status 5 %}
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="boot">
@ -270,7 +265,6 @@
<li><a href="#" title="Console port: {{ console_port }}" onclick="open_console('full')">{% trans "Console - Full" %}</a></li>
</ul>
</div>
{% else %}
<button class="btn btn-lg btn-success pull-right disabled">{% trans "Console" %}</button>
{% endifequal %}
@ -350,7 +344,7 @@
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="resizevm">
{% if request.user.is_superuser or request.user.is_staff or userinstance.is_change %}
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<p style="font-weight:bold;">{% trans "Logical host CPUs:" %} {{ vcpu_host }}</p>
<p style="font-weight:bold;">{% trans "Logical host CPUs" %} : {{ vcpu_host }}</p>
<div class="form-group">
<label class="col-sm-4 control-label" style="font-weight:normal;"> {% trans "Current allocation" %}</label>
<div class="col-sm-4">
@ -381,8 +375,7 @@
<div class="col-sm-4 js-custom__container">
<select name="cur_memory" class="form-control js-custom__toggle">
{% for mem in memory_range %}
<option value="{{ mem }}"
{% if mem == cur_memory %}selected{% endif %}>{{ mem }}</option>
<option value="{{ mem }}" {% if mem == cur_memory %}selected{% endif %}>{{ mem }}</option>
{% endfor %}
</select>
<input type="text" name="cur_memory_custom" class="form-control js-custom__toggle" style="display: none" />
@ -392,7 +385,6 @@
<div class="form-group">
<label class="col-sm-4 control-label"
style="font-weight:normal;">{% trans "Maximum allocation" %} ({% trans "MB" %})</label>
<div class="col-sm-4 js-custom__container">
<select name="memory" class="form-control js-custom__toggle">
{% for mem in memory_range %}
@ -425,7 +417,6 @@
{% endif %}
<div class="clearfix"></div>
</div>
</div>
</div>
</div>
@ -473,11 +464,11 @@
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th>{% trans "Name" %}</th>
<th>{% trans "Date" %}</th>
<th colspan="2">{% trans "Action" %}</th>
</tr>
<tr>
<th>{% trans "Name" %}</th>
<th>{% trans "Date" %}</th>
<th colspan="2">{% trans "Action" %}</th>
</tr>
</thead>
<tbody>
{% for snap in snapshots %}
@ -526,8 +517,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 +526,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 +577,150 @@
</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>
<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" title="Show boot menu" value="{% trans "Enable" %}">
{% else %}
<input type="submit" class="btn btn-danger" name="unset_bootmenu" title="Hide boot menu" 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: move selected devices"></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: move selected devices"></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>
{% else %}
<button class="btn btn-sm btn-success pull-left disabled" style="margin-top: 2px;">{% trans "Mount" %}</button>
{% endif %}
{% if status == 5 and allow_admin_or_not_template %}
<button type="submit" class="btn btn-sm btn-danger pull-left" title="Detach CD-Rom (remove device)" name="detach_cdrom" value="{{ cd.dev }}" style="margin-top: 2px;"><i class="glyphicon glyphicon-remove-circle"></i></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 %}
<div class="col-xs-12 col-sm-12">
<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>
@ -685,7 +741,9 @@
data-trigger="focus"
data-toggle="popover"
data-html="true"
data-content="<strong>Bus:</strong> {{ disk.bus }} <br/> <strong>Format:</strong> {{ disk.format }}">
data-content="<strong>Bus:</strong> {{ disk.bus }} <br/>
<strong>Format:</strong> {{ disk.format }} <br/>
<strong>Cache:</strong> {{ disk.cache }}">
<i class="fa fa-info"></i>
</button>
{{ disk.dev }}
@ -724,21 +782,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">
@ -855,65 +1059,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>
@ -921,25 +1066,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 %}
@ -1004,84 +1149,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">
@ -1111,36 +1178,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>
@ -1215,7 +1252,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>
@ -1269,8 +1306,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();
@ -1279,8 +1314,7 @@
$.each(data['vols'], function(i, item) {
$("#vols").append('<option value=' + item +'>' + item + '</option>');
});
})
});
var sto_drop = document.getElementById('select_storage');
@ -1290,7 +1324,6 @@
var sto_input = document.getElementById('selected_storage');
sto_input.value = pool;
sto_input.innerHTML = pool;
}
</script>
@ -1413,7 +1446,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);
@ -1443,6 +1475,69 @@ $(document).ready(function(){
});
});
</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 () {
$('.js-custom__checkbox').change(function () {
@ -1617,7 +1712,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') {

View file

@ -52,16 +52,16 @@ def allinstances(request):
else:
for comp in computes:
try:
all_host_vms.update(get_host_instances(request,comp))
all_host_vms.update(get_host_instances(request, comp))
except libvirtError as lib_err:
error_messages.append(lib_err)
if request.method == 'POST':
try:
instances_actions(request)
return instances_actions(request)
except libvirtError as lib_err:
error_messages.append(lib_err)
addlogmsg(request.user.username, instance.name, lib_err.message)
addlogmsg(request.user.username, request.POST.get("name", "instance"), lib_err.message)
view_style = settings.VIEW_INSTANCES_LIST_STYLE
@ -88,10 +88,10 @@ def instances(request, compute_id):
if request.method == 'POST':
try:
instances_actions(request)
return instances_actions(request)
except libvirtError as lib_err:
error_messages.append(lib_err)
addlogmsg(request.user.username, instance.name, lib_err.message)
addlogmsg(request.user.username, request.POST.get("name", "instance"), lib_err.message)
return render(request, 'instances.html', locals())
@ -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', '')
@ -802,7 +828,6 @@ def instance(request, compute_id, vname):
msg = _("Edit options")
addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#options')
conn.close()
except libvirtError as lib_err:
@ -837,7 +862,7 @@ def inst_status(request, compute_id, vname):
return response
def get_host_instances(request,comp):
def get_host_instances(request, comp):
def refresh_instance_database(comp, inst_name, info):
def get_userinstances_info(instance):
@ -883,7 +908,6 @@ def get_host_instances(request,comp):
status = connection_manager.host_is_up(comp.type, comp.hostname)
if status:
conn = wvmHostDetails(comp, comp.login, comp.password, comp.type)
comp_node_info = conn.get_node_info()
comp_mem = conn.get_memory_usage()
@ -907,6 +931,7 @@ def get_host_instances(request,comp):
return all_host_vms
def get_user_instances(request):
all_user_vms = {}
user_instances = UserInstance.objects.filter(user_id=request.user.id)
@ -975,7 +1000,6 @@ def instances_actions(request):
return response
if request.user.is_superuser:
if 'suspend' in request.POST:
msg = _("Suspend")
addlogmsg(request.user.username, instance.name, msg)
@ -1003,7 +1027,7 @@ def inst_graph(request, compute_id, vname):
datasets_net = {}
cookies = {}
points = 5
curent_time = time.strftime("%H:%M:%S")
current_time = time.strftime("%H:%M:%S")
compute = get_object_or_404(Compute, pk=compute_id)
response = HttpResponse()
response['Content-Type'] = "text/javascript"
@ -1030,18 +1054,15 @@ def inst_graph(request, compute_id, vname):
cookies['net'] = request.COOKIES['net']
cookies['timer'] = request.COOKIES['timer']
except KeyError:
cookies['cpu'] = None
cookies['blk'] = None
cookies['net'] = None
cookies['cpu'] = cookies['blk'] = cookies['net'] = None
if not cookies['cpu']:
datasets['cpu'] = [0] * points
datasets['timer'] = [0] * points
datasets['timer'] = datasets['cpu'] = [0] * points
else:
datasets['cpu'] = eval(cookies['cpu'])
datasets['timer'] = eval(cookies['timer'])
datasets['timer'].append(curent_time)
datasets['timer'].append(current_time)
datasets['cpu'].append(int(cpu_usage['cpu']))
datasets['timer'] = check_points(datasets['timer'])
@ -1049,8 +1070,7 @@ def inst_graph(request, compute_id, vname):
for blk in blk_usage:
if not cookies['blk']:
datasets_wr = [0] * points
datasets_rd = [0] * points
datasets_rd = datasets_wr = [0] * points
else:
datasets['blk'] = eval(cookies['blk'])
datasets_rd = datasets['blk'][blk['dev']][0]
@ -1067,8 +1087,7 @@ def inst_graph(request, compute_id, vname):
for net in net_usage:
if not cookies['net']:
datasets_rx = [0] * points
datasets_tx = [0] * points
datasets_tx = datasets_rx = [0] * points
else:
datasets['net'] = eval(cookies['net'])
datasets_rx = datasets['net'][net['dev']][0]
@ -1133,7 +1152,7 @@ def _get_random_mac_address():
@login_required
def random_mac_address(request):
data = {}
data = dict()
data['mac'] = _get_random_mac_address()
return HttpResponse(json.dumps(data))
@ -1157,9 +1176,9 @@ def guess_clone_name(request):
@login_required
def check_instance(request, vname):
check_instance = Instance.objects.filter(name=vname)
instance = Instance.objects.filter(name=vname)
data = {'vname': vname, 'exists': False}
if check_instance:
if instance:
data['exists'] = True
return HttpResponse(json.dumps(data))

View file

@ -0,0 +1,53 @@
{% load i18n %}
{% if request.user.is_superuser %}
<a href="#AddFixedNet" type="button" class="btn btn-success pull-right" data-toggle="modal">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
</a>
<!-- Modal pool -->
<div class="modal fade" id="AddFixedNet" tabindex="-1" role="dialog" aria-labelledby="AddFixedNetLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">{% trans "Add Fixed Address" %}</h4>
</div>
<form method="post" action="" role="form">{% csrf_token %}
<div class="modal-body">
<div class="row">
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Subnet Pool" %}</label>
<div class="col-sm-6">
<input type="text" readonly class="form-control" name="subnet" value="{{ ipv4_network }}" required pattern="[0-9\/\.]+">
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Name" %}</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="name" required pattern="[a-zA-Z0-9_]+">
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Address" %}</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="address" required pattern="[0-9\/\.]+">
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "MAC" %}</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="mac" required pattern="[0-9\/\:]+">
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
<button type="submit" class="btn btn-primary" name="modify_fixed_address">{% trans "Create" %}</button>
</div>
</form>
</div> <!-- /.modal-content -->
</div> <!-- /.modal-dialog -->
</div> <!-- /.modal -->
{% endif %}

View file

@ -34,6 +34,7 @@
<!-- /.row -->
{% include 'errors_block.html' %}
{% include 'messages_block.html' %}
<div class="row">
<div class="col-xs-6 col-sm-4">
@ -66,9 +67,11 @@
</p>
</div>
</div>
{% if state %}
{#{% if state %}#}
<div class="row">
<h3 class="page-header">{% trans "IPv4 Configuration" %}</h3>
</div>
<div class="row">
<h3 class="page-header">{% trans "IPv4 configuration" %}</h3>
<div class="col-xs-6 col-sm-4">
<p>{% trans "IPv4 Forwarding:" %}</p>
<p>{% trans "Network:" %}</p>
@ -102,43 +105,81 @@
{% endif %}
</p>
{% if ipv4_dhcp_range_start and ipv4_dhcp_range_end %}
<p>{{ ipv4_dhcp_range_start }}</p>
<p>{{ ipv4_dhcp_range_end }}</p>
<form method="post" role="form">{% csrf_token %}
{% if state %}
<p>{{ ipv4_dhcp_range_start }}</p>
<p>{{ ipv4_dhcp_range_end }}</p>
{% else %}
<p><input name="range_start" value="{{ ipv4_dhcp_range_start }}"/></p>
<p><input name="range_end" value="{{ ipv4_dhcp_range_end }}"/></p>
<div class="col-xs-10 col-sm-8">
<input type="submit" class="btn btn-primary btn-block" value="Apply"
name="modify_dhcp_range"
title="Edit DHCP Range" onclick="return confirm('{% trans "Are you sure?" %}')"/>
</div>
{% endif %}
</form>
{% endif %}
</div>
</div>
{% if fixed_address %}
{% ifequal ipv4_forward.0 'nat' %}
{% if state %}
{% include 'modify_fixed_address.html' %}
{% endif %}
<div class="row">
<h3 class="page-header">{% trans "Fixed Address" %}</h3>
</div>
{% endifequal %}
{% if fixed_address %}
<div class="row">
<div class="col-xs-12">
<div class="panel-group" id="accordion">
<div class="panel panel-default">
<div class="panel-heading">
<a data-toggle="collapse" data-parent="#accordion" href="#collapseOne">
Show
{% trans 'Show' %}
</a>
</div>
<div id="collapseOne" class="panel-collapse collapse">
<div class="panel-body">
<div class="input-append form-inline pull-right" style="">
<div class="input-append form-inline pull-right">
<div class="form-group">
<input type="text" class="form-control" id="filter_input">
</div>
<input type="button" class="btn btn-default" id="filter_button" value="Filter">
<button type="button" class="btn btn-default" id="filter_clear">Clear</button>
<button type="button" class="btn btn-default" id="filter_clear">{% trans 'Clear' %}</button>
</div>
<table class="table table-hover">
<thead>
<tr>
<th style="text-align: center">{% trans "Address" %}</th>
<th style="text-align: center">{% trans "MAC" %}</th>
<th style="text-align: center">{% trans "Address" %}</th>
<th style="text-align: center">{% trans "Name" %}</th>
<th style="text-align: center">{% trans "Action" %}</th>
</tr>
</thead>
<tbody style="text-align: center">
{% for fix in fixed_address %}
<tr>
<td>{{ fix.host }}</td>
<td>{{ fix.mac }}</td>
<form method="post" role="form">{% csrf_token %}
<td><label class="form-control" disabled="true">{{ fix.mac }}</label></td>
<td><input class="form-control" value="{{ fix.ip }}" name="address" /></td>
<td><input class="form-control" value="{{ fix.name }}" name="name" /></td>
<td>
<input hidden name="mac" value="{{ fix.mac }}"/>
<button type="submit" class="btn btn-sm btn-primary"
name="modify_fixed_address"
title="Edit entry" onclick="return confirm('{% trans "Are you sure?" %}')">
<i class="glyphicon glyphicon-save"></i>
</button>
<button type="submit" class="btn btn-sm btn-danger"
name="delete_fixed_address"
title="Delete entry" onclick="return confirm('{% trans "Are you sure?" %}')">
<i class="glyphicon glyphicon-trash"></i>
</button>
</td>
</form>
</tr>
{% endfor %}
</tbody>
@ -149,7 +190,7 @@
</div>
</div>
</div>
{% endif %}
{#{% endif %}#}
{% endif %}
{% endblock %}
{% block script %}
@ -175,7 +216,6 @@
$('tbody tr:not(:Contains(\'' + filter_val + '\'))').hide();
}
});
// add event button labeled "clear"
$('#filter_clear').click(function (event) {
$('#filter_input').val('');

View file

@ -8,6 +8,7 @@ from networks.forms import AddNetPool
from vrtManager.network import wvmNetwork, wvmNetworks
from vrtManager.network import network_size
from libvirt import libvirtError
from django.contrib import messages
@login_required
@ -121,6 +122,33 @@ def network(request, compute_id, pool):
return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err:
error_messages.append(lib_err.message)
if 'modify_fixed_address' in request.POST:
name = request.POST.get('name', '')
address = request.POST.get('address', '')
mac = request.POST.get('mac', '')
try:
ret_val = conn.modify_fixed_address(name, address, mac)
messages.success(request, "Fixed Address Operation Completed.")
return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err:
error_messages.append(lib_err.message)
except ValueError as val_err:
error_messages.append(val_err.message)
if 'delete_fixed_address' in request.POST:
mac = request.POST.get('mac', '')
conn.delete_fixed_address(mac)
messages.success(request, "Fixed Address is Deleted.")
return HttpResponseRedirect(request.get_full_path())
if 'modify_dhcp_range' in request.POST:
range_start = request.POST.get('range_start', '')
range_end = request.POST.get('range_end', '')
try:
conn.modify_dhcp_range(range_start, range_end)
messages.success(request, "DHCP Range is Changed.")
return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err:
error_messages.append(lib_err.message)
conn.close()

View file

@ -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;
}

File diff suppressed because it is too large Load diff

View file

@ -118,9 +118,13 @@ class wvmCreate(wvmConnect):
vol = self.get_volume_by_path(vol_path)
return vol.storagePoolLookupByVolume()
def clone_from_template(self, clone, template, metadata=False, owner=default_owner):
def clone_from_template(self, clone, template, storage=None, metadata=False, owner=default_owner):
vol = self.get_volume_by_path(template)
stg = vol.storagePoolLookupByVolume()
if not storage:
stg = vol.storagePoolLookupByVolume()
else:
stg = self.get_storage(storage)
storage_type = util.get_xml_path(stg.XMLDesc(0), "/pool/@type")
format = util.get_xml_path(vol.XMLDesc(0), "/volume/target/format/@type")
if storage_type == 'dir':

View file

@ -201,7 +201,6 @@ class wvmInstance(wvmConnect):
return util.get_xml_path(self._XMLDesc(0), func=filterrefs)
def get_description(self):
description = util.get_xml_path(self._XMLDesc(0), "/domain/description")
return description if description else ''
@ -248,13 +247,8 @@ class wvmInstance(wvmConnect):
def get_disk_devices(self):
def disks(doc):
result = []
dev = None
volume = None
storage = None
src_fl = None
disk_format = None
used_size = None
disk_size = None
dev = volume = storage = src_file = None
disk_format = used_size = disk_size = disk_cache = None
for disk in doc.xpath('/domain/devices/disk'):
device = disk.xpath('@device')[0]
@ -262,13 +256,17 @@ class wvmInstance(wvmConnect):
try:
dev = disk.xpath('target/@dev')[0]
bus = disk.xpath('target/@bus')[0]
src_fl = 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:
disk_format = disk.xpath('driver/@type')[0]
except:
pass
try:
vol = self.get_volume_by_path(src_fl)
disk_cache = disk.xpath('driver/@cache')[0]
except:
pass
try:
vol = self.get_volume_by_path(src_file)
volume = vol.name()
disk_size = vol.info()[1]
@ -276,13 +274,13 @@ class wvmInstance(wvmConnect):
stg = vol.storagePoolLookupByVolume()
storage = stg.name()
except libvirtError:
volume = src_fl
volume = src_file
except:
pass
finally:
result.append(
{'dev': dev, 'bus': bus, 'image': volume, 'storage': storage, 'path': src_fl,
'format': disk_format, 'size': disk_size, 'used': used_size})
{'dev': dev, 'bus': bus, 'image': volume, 'storage': storage, 'path': src_file,
'format': disk_format, 'size': disk_size, 'used': used_size, 'cache': disk_cache})
return result
return util.get_xml_path(self._XMLDesc(0), func=disks)
@ -290,10 +288,8 @@ class wvmInstance(wvmConnect):
def get_media_devices(self):
def disks(doc):
result = []
dev = None
volume = None
storage = None
src_fl = None
dev = volume = storage = None
src_file = None
for media in doc.xpath('/domain/devices/disk'):
device = media.xpath('@device')[0]
if device == 'cdrom':
@ -301,22 +297,137 @@ class wvmInstance(wvmConnect):
dev = media.xpath('target/@dev')[0]
bus = media.xpath('target/@bus')[0]
try:
src_fl = media.xpath('source/@file')[0]
vol = self.get_volume_by_path(src_fl)
src_file = media.xpath('source/@file')[0]
vol = self.get_volume_by_path(src_file)
volume = vol.name()
stg = vol.storagePoolLookupByVolume()
storage = stg.name()
except:
src_fl = None
volume = src_fl
src_file = None
volume = src_file
except:
pass
finally:
result.append({'dev': dev, 'image': volume, 'storage': storage, 'path': src_fl, 'bus': bus})
result.append({'dev': dev, 'image': volume, 'storage': storage, 'path': src_file, 'bus': bus})
return result
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':
@ -490,11 +601,9 @@ class wvmInstance(wvmConnect):
return telnet_port
def get_console_listen_addr(self):
listen_addr = util.get_xml_path(self._XMLDesc(0),
"/domain/devices/graphics/@listen")
listen_addr = util.get_xml_path(self._XMLDesc(0), "/domain/devices/graphics/@listen")
if listen_addr is None:
listen_addr = util.get_xml_path(self._XMLDesc(0),
"/domain/devices/graphics/listen/@address")
listen_addr = util.get_xml_path(self._XMLDesc(0), "/domain/devices/graphics/listen/@address")
if listen_addr is None:
return "127.0.0.1"
return listen_addr

View file

@ -1,6 +1,10 @@
from vrtManager import util
from vrtManager.IPy import IP
from vrtManager.connection import wvmConnect
from xml.etree import ElementTree
from libvirt import VIR_NETWORK_SECTION_IP_DHCP_HOST, VIR_NETWORK_SECTION_IP_DHCP_RANGE
from libvirt import VIR_NETWORK_UPDATE_COMMAND_ADD_LAST, VIR_NETWORK_UPDATE_COMMAND_DELETE, VIR_NETWORK_UPDATE_COMMAND_MODIFY
from libvirt import VIR_NETWORK_UPDATE_AFFECT_LIVE, VIR_NETWORK_UPDATE_AFFECT_CONFIG
def network_size(net, dhcp=None):
@ -106,6 +110,9 @@ class wvmNetwork(wvmConnect):
def delete(self):
self.net.undefine()
def update(self, command, section, parentIndex, xml, flags=0):
return self.net.update(command, section, parentIndex, xml, flags)
def get_ipv4_network(self):
xml = self._XMLDesc(0)
if util.get_xml_path(xml, "/network/ip") is None:
@ -169,9 +176,57 @@ class wvmNetwork(wvmConnect):
def network(doc):
result = []
for net in doc.xpath('/network/ip/dhcp/host'):
host = net.xpath('@ip')[0]
ip = net.xpath('@ip')[0]
mac = net.xpath('@mac')[0]
result.append({'host': host, 'mac': mac})
name = net.xpath('@name')[0]
result.append({'ip': ip, 'mac': mac, 'name': name})
return result
return util.get_xml_path(self._XMLDesc(0), func=network)
def modify_fixed_address(self, name, address, mac):
util.validate_macaddr(mac)
new_xml = '<host mac="{}" name="{}" ip="{}"/>'.format(mac, name, IP(address))
new_host_xml = ElementTree.fromstring(new_xml)
tree = ElementTree.fromstring(self._XMLDesc(0))
hosts = tree.findall("./ip/dhcp/host")
host = None
for h in hosts:
if h.get('mac') == mac:
host = h
break
if host is None:
self.update(VIR_NETWORK_UPDATE_COMMAND_ADD_LAST, VIR_NETWORK_SECTION_IP_DHCP_HOST, -1, new_xml,
VIR_NETWORK_UPDATE_AFFECT_LIVE|VIR_NETWORK_UPDATE_AFFECT_CONFIG)
else:
# change the host
if host.get('name') == new_host_xml.get('name') and host.get('ip') == new_host_xml.get('ip'):
return False
else:
self.update(VIR_NETWORK_UPDATE_COMMAND_MODIFY, VIR_NETWORK_SECTION_IP_DHCP_HOST, -1, new_xml,
VIR_NETWORK_UPDATE_AFFECT_LIVE|VIR_NETWORK_UPDATE_AFFECT_CONFIG)
def delete_fixed_address(self, mac):
util.validate_macaddr(mac)
tree = ElementTree.fromstring(self._XMLDesc(0))
hosts = tree.findall("./ip/dhcp/host")
for h in hosts:
if h.get('mac') == mac:
new_xml = '<host mac="{}" name="{}" ip="{}"/>'.format(mac, h.get('name'), h.get('ip'))
self.update(VIR_NETWORK_UPDATE_COMMAND_DELETE, VIR_NETWORK_SECTION_IP_DHCP_HOST, -1, new_xml,
VIR_NETWORK_UPDATE_AFFECT_LIVE|VIR_NETWORK_UPDATE_AFFECT_CONFIG)
break
def modify_dhcp_range(self, range_start, range_end):
if not self.is_active():
new_range = '<range start="{}" end="{}"/>'.format(range_start, range_end)
tree = ElementTree.fromstring(self._XMLDesc(0))
dhcp = tree.find("./ip/dhcp")
old_range = dhcp.find('range')
dhcp.remove(old_range)
dhcp.append(ElementTree.fromstring(new_range))
self.wvm.networkDefineXML(ElementTree.tostring(tree))

View file

@ -153,3 +153,15 @@ def validate_uuid(val):
val = (val[0:8] + "-" + val[8:12] + "-" + val[12:16] +
"-" + val[16:20] + "-" + val[20:32])
return val
def validate_macaddr(val):
if val is None:
return
if not (isinstance(val, str) or isinstance(val, basestring)):
raise ValueError("MAC address must be a string.")
form = re.match("^([0-9a-fA-F]{1,2}:){5}[0-9a-fA-F]{1,2}$", val)
if form is None:
raise ValueError("MAC address must be of the format AA:BB:CC:DD:EE:FF, was '%s'" % val)