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 = {} datasets = {}
cookies = {} cookies = {}
compute = get_object_or_404(Compute, pk=compute_id) compute = get_object_or_404(Compute, pk=compute_id)
curent_time = time.strftime("%H:%M:%S") current_time = time.strftime("%H:%M:%S")
try: try:
conn = wvmHostDetails(compute.hostname, conn = wvmHostDetails(compute.hostname,
@ -208,7 +208,7 @@ def compute_graph(request, compute_id):
datasets['mem'] = eval(cookies['mem']) datasets['mem'] = eval(cookies['mem'])
datasets['timer'] = eval(cookies['timer']) datasets['timer'] = eval(cookies['timer'])
datasets['timer'].append(curent_time) datasets['timer'].append(current_time)
datasets['cpu'].append(int(cpu_usage['usage'])) datasets['cpu'].append(int(cpu_usage['usage']))
datasets['mem'].append(int(mem_usage['usage']) / 1048576) 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 websockify==0.8.0
gunicorn==19.9.0 gunicorn==19.9.0
lxml==4.2.3 lxml==4.2.5
libvirt-python==4.4.0 libvirt-python==4.10.0
pytz pytz

View file

@ -441,7 +441,7 @@
<label class="col-sm-3 control-label">{% trans "HDD" %}</label> <label class="col-sm-3 control-label">{% trans "HDD" %}</label>
<input id="images" name="images" type="hidden" value=""/> <input id="images" name="images" type="hidden" value=""/>
<div class="col-sm-3"> <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 %} {% if storages %}
<option value disabled selected>{% trans "Select pool" %}...</option> <option value disabled selected>{% trans "Select pool" %}...</option>
{% for storage in storages %} {% for storage in storages %}
@ -458,6 +458,20 @@
</select> </select>
</div> </div>
</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"> <div class="form-group meta-prealloc">
<label class="col-sm-3 control-label">{% trans "Metadata" %}</label> <label class="col-sm-3 control-label">{% trans "Metadata" %}</label>
<div class="col-sm-7"> <div class="col-sm-7">
@ -697,6 +711,9 @@
}); });
}); });
$("#template").removeAttr("disabled"); $("#template").removeAttr("disabled");
$("#storage").val(pool).change();
$("#storage").removeAttr("disabled");
} }
function get_disk_bus_choices(compute_id, dev_idx, disk_type){ 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_msg = _("Image has already exist. Please check volumes or change instance name")
error_messages.append(error_msg) error_messages.append(error_msg)
else: 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 = dict()
volume['path'] = clone_path volume['path'] = clone_path
volume['type'] = conn.get_volume_type(clone_path) volume['type'] = conn.get_volume_type(clone_path)

View file

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

View file

@ -40,7 +40,7 @@
</div> </div>
</div> </div>
{% if user_quota_msg %} {% 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 %} {% endif %}
<hr> <hr>
</div> </div>
@ -187,23 +187,18 @@
{% endif %} {% endif %}
{% endifequal %} {% endifequal %}
{% ifequal status 3 %} {% 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> <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" %}">
<input type="submit" name="resume" class="btn btn-lg btn-success pull-right" value="{% trans "Resume" %}"> {% else %}
<div class="clearfix"></div>
</form>
</div>
{% else %}
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="resume">
<p>{% trans "Administrator blocked your instance." %}</p> <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>
<button class="btn btn-lg btn-success disabled pull-right">{% trans "Resume" %}</button> {% endif %}
<div class="clearfix"></div> <div class="clearfix"></div>
</form> </form>
</div> </div>
{% endif %}
{% endifequal %} {% endifequal %}
{% ifequal status 5 %} {% ifequal status 5 %}
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="boot"> <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> <li><a href="#" title="Console port: {{ console_port }}" onclick="open_console('full')">{% trans "Console - Full" %}</a></li>
</ul> </ul>
</div> </div>
{% else %} {% else %}
<button class="btn btn-lg btn-success pull-right disabled">{% trans "Console" %}</button> <button class="btn btn-lg btn-success pull-right disabled">{% trans "Console" %}</button>
{% endifequal %} {% endifequal %}
@ -350,7 +344,7 @@
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="resizevm"> <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 %} {% if request.user.is_superuser or request.user.is_staff or userinstance.is_change %}
<form class="form-horizontal" method="post" role="form">{% csrf_token %} <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"> <div class="form-group">
<label class="col-sm-4 control-label" style="font-weight:normal;"> {% trans "Current allocation" %}</label> <label class="col-sm-4 control-label" style="font-weight:normal;"> {% trans "Current allocation" %}</label>
<div class="col-sm-4"> <div class="col-sm-4">
@ -381,8 +375,7 @@
<div class="col-sm-4 js-custom__container"> <div class="col-sm-4 js-custom__container">
<select name="cur_memory" class="form-control js-custom__toggle"> <select name="cur_memory" class="form-control js-custom__toggle">
{% for mem in memory_range %} {% for mem in memory_range %}
<option value="{{ mem }}" <option value="{{ mem }}" {% if mem == cur_memory %}selected{% endif %}>{{ mem }}</option>
{% if mem == cur_memory %}selected{% endif %}>{{ mem }}</option>
{% endfor %} {% endfor %}
</select> </select>
<input type="text" name="cur_memory_custom" class="form-control js-custom__toggle" style="display: none" /> <input type="text" name="cur_memory_custom" class="form-control js-custom__toggle" style="display: none" />
@ -392,7 +385,6 @@
<div class="form-group"> <div class="form-group">
<label class="col-sm-4 control-label" <label class="col-sm-4 control-label"
style="font-weight:normal;">{% trans "Maximum allocation" %} ({% trans "MB" %})</label> style="font-weight:normal;">{% trans "Maximum allocation" %} ({% trans "MB" %})</label>
<div class="col-sm-4 js-custom__container"> <div class="col-sm-4 js-custom__container">
<select name="memory" class="form-control js-custom__toggle"> <select name="memory" class="form-control js-custom__toggle">
{% for mem in memory_range %} {% for mem in memory_range %}
@ -425,7 +417,6 @@
{% endif %} {% endif %}
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
@ -473,11 +464,11 @@
<div class="table-responsive"> <div class="table-responsive">
<table class="table"> <table class="table">
<thead> <thead>
<tr> <tr>
<th>{% trans "Name" %}</th> <th>{% trans "Name" %}</th>
<th>{% trans "Date" %}</th> <th>{% trans "Date" %}</th>
<th colspan="2">{% trans "Action" %}</th> <th colspan="2">{% trans "Action" %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for snap in snapshots %} {% for snap in snapshots %}
@ -526,8 +517,8 @@
<!-- Nav tabs --> <!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist"> <ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active"> <li role="presentation" class="active">
<a href="#media" aria-controls="media" role="tab" data-toggle="tab"> <a href="#boot_opt" aria-controls="boot" role="tab" data-toggle="tab">
{% trans "Media" %} {% trans "Boot" %}
</a> </a>
</li> </li>
<li role="presentation"> <li role="presentation">
@ -535,13 +526,6 @@
{% trans "Disk" %} {% trans "Disk" %}
</a> </a>
</li> </li>
{% if request.user.is_superuser %}
<li role="presentation">
<a href="#autostart" aria-controls="autostart" role="tab" data-toggle="tab">
{% trans "Autostart" %}
</a>
</li>
{% endif %}
{% if request.user.is_superuser or userinstance.is_vnc %} {% if request.user.is_superuser or userinstance.is_vnc %}
<li role="presentation"> <li role="presentation">
@ -593,78 +577,150 @@
</ul> </ul>
<!-- Tab panes --> <!-- Tab panes -->
<div class="tab-content"> <div class="tab-content">
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="media"> {% if request.user.is_superuser %}
{% if request.user.is_superuser and status == 5 %} <div role="tabpanel" class="tab-pane tab-pane-bordered active" id="boot_opt">
<form class="form-horizontal" action="" method="post" role="form">{% csrf_token %} <p style="font-weight:bold;">{% trans 'Autostart' %}</p>
<div class="form-group">
<div class="col-sm-12">
<button type="submit" name="add_cdrom" type="button" class="btn btn-success pull-right">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
</button>
</div>
</div>
</form>
{% endif %}
{% for cd in media %}
<form class="form-horizontal" action="" method="post" role="form">{% csrf_token %} <form class="form-horizontal" action="" method="post" role="form">{% csrf_token %}
<div class="form-group"> <div class="form-group">
<a class="col-sm-2 control-label" <div class="col-sm-12 col-sm-offset-2">
name="details" <p>{% trans "Autostart your instance when host server is power on " %}
title="{% trans "Details" %}" {% ifequal autostart 0 %}
tabindex="0" <input type="submit" class="btn btn-success" name="set_autostart" value="{% trans "Enable" %}">
data-trigger="focus"
data-toggle="popover"
data-html="true"
data-content="<strong>Bus:</strong> {{ cd.bus }} <br/> <strong>Dev:</strong> {{ cd.dev }}">
{% trans "CDROM" %} {{ forloop.counter }}
</a>
{% if not cd.image %}
<div class="col-sm-6">
<select name="media" class="form-control">
{% if media_iso %}
{% for iso in media_iso %}
<option value="{{ iso }}">{{ iso }}</option>
{% endfor %}
{% else %}
<option value="none">{% trans "None" %}</option>
{% endif %}
</select>
</div>
<div class="col-sm-2">
{% if media_iso and allow_admin_or_not_template %}
<button type="submit" class="btn btn-sm btn-success pull-left" name="mount_iso" value="{{ cd.dev }}" style="margin-top: 2px;">{% trans "Mount" %}</button>
{% if status == 5 %}
<button type="submit" class="btn btn-sm btn-danger pull-left" name="detach_cdrom" value="{{ cd.dev }}" style="margin-top: 2px;"><i class="glyphicon glyphicon-remove-circle"></i></button>
{% endif %}
{% else %}
<button class="btn btn-sm btn-success pull-left disabled" style="margin-top: 2px;">{% trans "Mount" %}</button>
{% endif %}
</div>
{% else %} {% else %}
<div class="col-sm-6"> <input type="submit" class="btn btn-danger" name="unset_autostart" value="{% trans "Disable" %}">
<input class="form-control" value="{{ cd.image }}" disabled/> {% endifequal %}
</div> </p>
<div class="col-sm-2"> </div>
<input type="hidden" name="path" value="{{ cd.path }}">
{% if allow_admin_or_not_template %}
<button type="submit" class="btn btn-sm btn-success pull-left" value="{{ cd.dev }}" name="umount_iso" style="margin-top: 2px;">{% trans "Umount" %}</button>
{% else %}
<button class="btn btn-sm btn-success pull-left disabled" value="{{ cd.dev }}" name="umount_iso" style="margin-top: 2px;">{% trans "Umount" %}</button>
{% endif %}
</div>
{% endif %}
</div> </div>
</form> </form>
{% endfor %} <p style="font-weight:bold;">{% trans 'Boot Order' %}</p>
<div class="clearfix"></div> <form class="form-horizontal" action="" method="post" role="form">{% csrf_token %}
</div> <div class="form-group">
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="disks"> <div class="col-sm-12 col-sm-offset-2">
<p style="font-weight:bold;"> {% ifequal status 5 %}
{% trans "Instance Volumes" %} <p>{% trans "Enable Boot Menu for your instance when it starts up " %}
{% include 'add_instance_volume.html' %} {% ifequal bootmenu 0 %}
</p> <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"> <table class="table table-hover">
<thead> <thead>
<th>{% trans "Device" %}</th> <th>{% trans "Device" %}</th>
@ -685,7 +741,9 @@
data-trigger="focus" data-trigger="focus"
data-toggle="popover" data-toggle="popover"
data-html="true" 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> <i class="fa fa-info"></i>
</button> </button>
{{ disk.dev }} {{ disk.dev }}
@ -724,21 +782,167 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="network">
<p>
{% trans "Assign network device to bridge" %}
{% include 'add_instance_network_block.html' %}
</p>
<p style="font-weight:bold;">{% trans "Network devices" %}</p>
<div class="col-xs-12 col-sm-12">
<form method="post" role="form">{% csrf_token %}
{% for network in networks %}
<div class="panel panel-default">
<div class="panel-heading">
<label>eth{{ forloop.counter0 }}({{ network.target|default:"no target" }})</label>
</div>
<div class="panel-body">
<div class="form-group form-inline">
<label class="col-sm-2 col-sm-offset-1 control-label">{% trans "MAC" %} </label>
<input class="form-control" type="text" value="{{ network.mac }}" readonly/>
<label class="control-label"><em>to</em></label>
<input class="form-control" type="text" name="net-mac-{{ forloop.counter0 }}" value="{{ network.mac }}"/>
</div>
<div class="form-group form-inline">
<label class="col-sm-2 col-sm-offset-1 control-label">{% trans "NIC" %} </label>
<input class="form-control" type="text" value="{{ network.nic }}" readonly/>
<label class="control-label"><em>to</em></label>
<select class="form-control" name="net-source-{{ forloop.counter0 }}">
{% for c_net in compute_networks %}
<option value="net:{{ c_net }}" {% ifequal c_net network.nic %} selected {% endifequal %}>Network {{ c_net }}</option>
{% endfor %}
{% for c_iface in compute_interfaces %}
<option value="iface:{{ c_iface }}" {% ifequal c_iface network.nic %} selected {% endifequal %}>Interface {{ c_iface }}</option>
{% endfor %}
</select>
</div>
<div class="form-group form-inline">
<label class="col-sm-2 col-sm-offset-1">{% trans "Filter" %} </label>
<input class="form-control" type="text" value="{{ network.filterref }}" readonly/>
<label class="control-label"><em>to</em></label>
<select class="form-control" name="net-nwfilter-{{ forloop.counter0 }}">
<option value="">{% trans "None" %}</option>
{% for c_filters in compute_nwfilters %}
<option value="{{ c_filters }}" {% ifequal c_filters network.filterref %} selected {% endifequal %}>{{ c_filters }}</option>
{% endfor %}
</select>
</div>
</div>
{% if request.user.is_superuser %} <div class="panel-footer">
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="autostart"> <button class="btn btn-sm btn-primary" name="change_network" title="{% trans "Change" %}">{% trans "Change" %}</button>
<p>{% trans "Autostart your instance when host server is power on" %}</p> <button class="btn btn-sm pull-right btn-danger" value="{{ network.mac }}" name="delete_network" title="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure?" %}')">{% trans "Delete" %}</button>
<form class="form-horizontal" action="" method="post" role="form">{% csrf_token %} </div>
{% ifequal autostart 0 %} </div>
<input type="submit" class="btn btn-lg btn-success pull-right" name="set_autostart" value="{% trans "Enable" %}"> {% endfor %}
</form>
</div>
<div class="clearfix"></div>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="migrate">
<p>{% trans "For migration both host servers must have equal settings and OS type" %}</p>
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Original host" %}</label>
<div class="col-sm-6">
<p style="margin: 10px -10px 0 0;">{{ compute.name }}</p>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Host migration" %}</label>
<div class="col-sm-6">
<select name="compute_id" class="form-control">
{% if computes_count != 1 %}
{% for comp in computes %}
{% if comp.id != compute.id %}
<option value="{{ comp.id }}">{{ comp.name }}</option>
{% endif %}
{% endfor %}
{% endif %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Live migration" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="live_migrate" value="true" id="vm_live_migrate" {% ifequal status 1 %}checked{% endifequal %}>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Unsafe migration" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="unsafe_migrate" value="true" id="vm_unsafe_migrate">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Delete original" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="xml_delete" value="true" id="xml_delete" checked>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Offline migration" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="offline_migrate" value="true" id="offline_migrate" {% ifequal status 5 %}checked{% endifequal %}>
</div>
</div>
{% if computes_count != 1 %}
<button type="submit" class="btn btn-lg btn-success pull-right" name="migrate" onclick="showPleaseWaitDialog();">{% trans "Migrate" %}</button>
{% else %} {% else %}
<input type="submit" class="btn btn-lg btn-success pull-right" name="unset_autostart" value="{% trans "Disable" %}"> <button class="btn btn-lg btn-success pull-right disabled">{% trans "Migrate" %}</button>
{% endif %}
</form>
<div class="clearfix"></div></p>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="xmledit">
<p>{% trans "If you need to edit xml please Power Off the instance" %}</p>
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<div class="col-sm-12" id="xmlheight">
<textarea id="editor">{{ inst_xml }}</textarea>
</div>
{% ifequal status 5 %}
<input type="hidden" name="inst_xml">
<button type="submit" class="btn btn-lg btn-success pull-right" name="change_xml">
{% trans "Change" %}
</button>
{% else %}
<button class="btn btn-lg btn-success pull-right disabled">
{% trans "Change" %}
</button>
{% endifequal %} {% endifequal %}
</form> </form>
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="users">
<div>
<p style="font-weight:bold;">
{% trans "Instance owners" %}
{% include 'add_instance_owner_block.html' %}
</p>
</div>
<div class="table-responsive">
<table class="table table-striped sortable-theme-bootstrap" data-sortable>
<tbody class="searchable">
{% for userinstance in userinstances %}
<tr>
<td><a href="{% url 'account' userinstance.user.id %}">{{ userinstance.user }}</a></td>
<td style="width:30px;">
<form action="" method="post" style="height:10px" role="form">{% csrf_token %}
<input type="hidden" name="userinstance" value="{{ userinstance.pk }}">
<button type="submit" class="btn btn-sm btn-default" name="del_owner" title="{% trans "Delete" %}">
<i class="fa fa-trash"></i>
</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="clearfix"></div>
</div>
{% endif %} {% endif %}
{% if request.user.is_superuser or userinstance.is_vnc %} {% if request.user.is_superuser or userinstance.is_vnc %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="vncsettings"> <div role="tabpanel" class="tab-pane tab-pane-bordered" id="vncsettings">
@ -855,65 +1059,6 @@
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
{% endif %} {% endif %}
{% if request.user.is_superuser %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="network">
<p>
{% trans "Assign network device to bridge" %}
{% include 'add_instance_network_block.html' %}
</p>
<p style="font-weight:bold;">{% trans "Network devices" %}</p>
<div class="col-xs-12 col-sm-12">
<form method="post" role="form">{% csrf_token %}
{% for network in networks %}
<div class="panel panel-default">
<div class="panel-heading">
<label>eth{{ forloop.counter0 }}({{ network.target|default:"no target" }})</label>
</div>
<div class="panel-body">
<div class="form-group form-inline">
<label class="col-sm-2 col-sm-offset-1 control-label">{% trans "MAC" %} </label>
<input class="form-control" type="text" value="{{ network.mac }}" readonly/>
<label class="control-label"><em>to</em></label>
<input class="form-control" type="text" name="net-mac-{{ forloop.counter0 }}" value="{{ network.mac }}"/>
</div>
<div class="form-group form-inline">
<label class="col-sm-2 col-sm-offset-1 control-label">{% trans "NIC" %} </label>
<input class="form-control" type="text" value="{{ network.nic }}" readonly/>
<label class="control-label"><em>to</em></label>
<select class="form-control" name="net-source-{{ forloop.counter0 }}">
{% for c_net in compute_networks %}
<option value="net:{{ c_net }}" {% ifequal c_net network.nic %} selected {% endifequal %}>Network {{ c_net }}</option>
{% endfor %}
{% for c_iface in compute_interfaces %}
<option value="iface:{{ c_iface }}" {% ifequal c_iface network.nic %} selected {% endifequal %}>Interface {{ c_iface }}</option>
{% endfor %}
</select>
</div>
<div class="form-group form-inline">
<label class="col-sm-2 col-sm-offset-1">{% trans "Filter" %} </label>
<input class="form-control" type="text" value="{{ network.filterref }}" readonly/>
<label class="control-label"><em>to</em></label>
<select class="form-control" name="net-nwfilter-{{ forloop.counter0 }}">
<option value="">{% trans "None" %}</option>
{% for c_filters in compute_nwfilters %}
<option value="{{ c_filters }}" {% ifequal c_filters network.filterref %} selected {% endifequal %}>{{ c_filters }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="panel-footer">
<button class="btn btn-sm btn-primary" name="change_network" title="{% trans "Change" %}">{% trans "Change" %}</button>
<button class="btn btn-sm pull-right btn-danger" value="{{ network.mac }}" name="delete_network" title="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure?" %}')">{% trans "Delete" %}</button>
</div>
</div>
{% endfor %}
</form>
</div>
<div class="clearfix"></div>
</div>
{% endif %}
{% if request.user.is_superuser or request.user.userattributes.can_clone_instances %} {% if request.user.is_superuser or request.user.userattributes.can_clone_instances %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="clone"> <div role="tabpanel" class="tab-pane tab-pane-bordered" id="clone">
<p style="font-weight:bold;">{% trans "Create a clone" %}</p> <p style="font-weight:bold;">{% trans "Create a clone" %}</p>
@ -921,25 +1066,25 @@
<div class="form-group"> <div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Clone Name" %}</label> <label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Clone Name" %}</label>
{% if request.user.is_superuser %} {% if request.user.is_superuser %}
<div class="col-sm-4"> <div class="col-sm-4">
<input id="clone_name" type="text" class="form-control" name="name" value="{{ vname }}-clone"/> <input id="clone_name" type="text" class="form-control" name="name" value="{{ vname }}-clone"/>
</div> </div>
<div class="col-sm-4"> <div class="col-sm-4">
<button type="button" class="btn btn-sm btn-success pull-left" name="guess-clone-name" <button type="button" class="btn btn-sm btn-success pull-left" name="guess-clone-name"
onclick="guess_clone_name()" style="margin-top: 2px;">{% trans "Guess" %}</button> onclick="guess_clone_name()" style="margin-top: 2px;">{% trans "Guess" %}</button>
</div> </div>
{% elif clone_instance_auto_name %} {% elif clone_instance_auto_name %}
<div class="col-sm-4"> <div class="col-sm-4">
<input id="clone_instance_auto_name" type="text" class="form-control" name="auto_name" value="Automatic" disabled/> <input id="clone_instance_auto_name" type="text" class="form-control" name="auto_name" value="Automatic" disabled/>
</div> </div>
{% else %} {% else %}
<div class="col-sm-4"> <div class="col-sm-4">
<select id="select_clone_name" class="form-control" name="name" size="1"/> <select id="select_clone_name" class="form-control" name="name" size="1"/>
{% for name in clone_free_names %} {% for name in clone_free_names %}
<option value="{{ name }}">{{ name }}</option> <option value="{{ name }}">{{ name }}</option>
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
{% endif %} {% endif %}
</div> </div>
{% if request.user.is_superuser %} {% if request.user.is_superuser %}
@ -1004,84 +1149,6 @@
</form> </form>
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
{% endif %}
{% if request.user.is_superuser %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="migrate">
<p>{% trans "For migration both host servers must have equal settings and OS type" %}</p>
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Original host" %}</label>
<div class="col-sm-6">
<p style="margin: 10px -10px 0 0;">{{ compute.name }}</p>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Host migration" %}</label>
<div class="col-sm-6">
<select name="compute_id" class="form-control">
{% if computes_count != 1 %}
{% for comp in computes %}
{% if comp.id != compute.id %}
<option value="{{ comp.id }}">{{ comp.name }}</option>
{% endif %}
{% endfor %}
{% endif %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Live migration" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="live_migrate" value="true" id="vm_live_migrate" {% ifequal status 1 %}checked{% endifequal %}>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Unsafe migration" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="unsafe_migrate" value="true" id="vm_unsafe_migrate">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Delete original" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="xml_delete" value="true" id="xml_delete" checked>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Offline migration" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="offline_migrate" value="true" id="offline_migrate" {% ifequal status 5 %}checked{% endifequal %}>
</div>
</div>
{% if computes_count != 1 %}
<button type="submit" class="btn btn-lg btn-success pull-right" name="migrate" onclick="showPleaseWaitDialog();">{% trans "Migrate" %}</button>
{% else %}
<button class="btn btn-lg btn-success pull-right disabled">{% trans "Migrate" %}</button>
{% endif %}
</form>
<div class="clearfix"></div></p>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="xmledit">
<p>{% trans "If you need to edit xml please Power Off the instance" %}</p>
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<div class="col-sm-12" id="xmlheight">
<textarea id="editor">{{ inst_xml }}</textarea>
</div>
{% ifequal status 5 %}
<input type="hidden" name="inst_xml">
<button type="submit" class="btn btn-lg btn-success pull-right" name="change_xml">
{% trans "Change" %}
</button>
{% else %}
<button class="btn btn-lg btn-success pull-right disabled">
{% trans "Change" %}
</button>
{% endifequal %}
</form>
<div class="clearfix"></div>
</div>
{% endif %}
{% if request.user.is_superuser or request.user.userattributes.can_clone_instances %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="options"> <div role="tabpanel" class="tab-pane tab-pane-bordered" id="options">
<form class="form-horizontal" action="" method="post" role="form">{% csrf_token %} <form class="form-horizontal" action="" method="post" role="form">{% csrf_token %}
<div class="form-group"> <div class="form-group">
@ -1111,36 +1178,6 @@
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
{% endif %} {% endif %}
{% if request.user.is_superuser %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="users">
<div>
<p style="font-weight:bold;">
{% trans "Instance owners" %}
{% include 'add_instance_owner_block.html' %}
</p>
</div>
<div class="table-responsive">
<table class="table table-striped sortable-theme-bootstrap" data-sortable>
<tbody class="searchable">
{% for userinstance in userinstances %}
<tr>
<td><a href="{% url 'account' userinstance.user.id %}">{{ userinstance.user }}</a></td>
<td style="width:30px;">
<form action="" method="post" style="height:10px" role="form">{% csrf_token %}
<input type="hidden" name="userinstance" value="{{ userinstance.pk }}">
<button type="submit" class="btn btn-sm btn-default" name="del_owner" title="{% trans "Delete" %}">
<i class="fa fa-trash"></i>
</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="clearfix"></div>
</div>
{% endif %}
</div> </div>
</div> </div>
</div> </div>
@ -1215,7 +1252,7 @@
</tr> </tr>
</thead> </thead>
<tbody class="searchable"> <tbody class="searchable">
<tr><td colspan="3"><i>None ...</i></td></tr> <tr><td colspan="3"><i>{% trans 'None' %}...</i></td></tr>
</tbody> </tbody>
</table> </table>
</div> </div>
@ -1269,8 +1306,6 @@
<script src="{% static "js/ace.js" %}" type="text/javascript" charset="utf-8"></script> <script src="{% static "js/ace.js" %}" type="text/javascript" charset="utf-8"></script>
<script> <script>
function get_volumes(compute_id, pool) { function get_volumes(compute_id, pool) {
get_vol_url = "/computes/" + compute_id + "/storage/" + pool + "/volumes"; get_vol_url = "/computes/" + compute_id + "/storage/" + pool + "/volumes";
$.getJSON(get_vol_url, function (data) { $.getJSON(get_vol_url, function (data) {
$("#vols").find('option').remove(); $("#vols").find('option').remove();
@ -1279,8 +1314,7 @@
$.each(data['vols'], function(i, item) { $.each(data['vols'], function(i, item) {
$("#vols").append('<option value=' + item +'>' + item + '</option>'); $("#vols").append('<option value=' + item +'>' + item + '</option>');
}); })
}); });
var sto_drop = document.getElementById('select_storage'); var sto_drop = document.getElementById('select_storage');
@ -1290,7 +1324,6 @@
var sto_input = document.getElementById('selected_storage'); var sto_input = document.getElementById('selected_storage');
sto_input.value = pool; sto_input.value = pool;
sto_input.innerHTML = pool; sto_input.innerHTML = pool;
} }
</script> </script>
@ -1413,7 +1446,6 @@
}); });
$(document).ready(function () { $(document).ready(function () {
// set vdi url // set vdi url
$.get("{% url 'vdi_url' vname %}", function(data) { $.get("{% url 'vdi_url' vname %}", function(data) {
$("#vdi_url_input").attr("value", data); $("#vdi_url_input").attr("value", data);
$("#vdi_url").attr("href", data); $("#vdi_url").attr("href", data);
@ -1443,6 +1475,69 @@ $(document).ready(function(){
}); });
}); });
</script> </script>
<script>
function set_orderlist(obj){
var result = '';
$('#b_order label input:checked').each(function () {
if (result != '') result += ',';
result += $(this).val();
});
obj.val(result);
}
$(document).ready(function () {
{# Boot Order Arragements #}
jQuery.fn.multiselect = function() {
$(this).each(function() {
var checkboxes = $(this).find("input:checkbox");
checkboxes.each(function() {
var checkbox = $(this);
// Highlight pre-selected checkboxes
if (checkbox.prop("checked"))
checkbox.parent().addClass("multiselect-on");
// Highlight checkboxes that the user selects
checkbox.click(function() {
if (checkbox.prop("checked"))
checkbox.parent().addClass("multiselect-on");
else
checkbox.parent().removeClass("multiselect-on");
});
});
});
};
$(function() {
$(".multiselect").multiselect();
});
$('#boot_order_up').bind('click', function() {
$('#b_order label input:checked').each( function() {
var label = $(this).parent();
var newPos = label.index() - 1;
if (newPos > -1) {
$('#b_order label').eq(newPos).before("<label><input type='checkbox' value='"+$(this).val()+"' name='"+$(this).val()+"' checked>"+$(this).parent().text()+"</label>");
label.remove();
}
$(".multiselect").multiselect();
});
set_orderlist($("#bootorder"));
});
$('#boot_order_down').bind('click', function() {
var countOptions = $('#b_order label').size();
var countSelected = $('#b_order label input:checked').size();
$('#b_order label input:checked').each( function() {
var label = $(this).parent();
var newPos = label.index() + countSelected;
if (newPos < countOptions) {
$('#b_order label').eq(newPos).after("<label><input type='checkbox' value='"+$(this).val()+"' name='"+$(this).val()+"' checked>"+$(this).parent().text()+"</label>");
label.remove();
}
$(".multiselect").multiselect();
});
set_orderlist($("#bootorder"));
});
});
</script>
<script> <script>
$(function () { $(function () {
$('.js-custom__checkbox').change(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'); var btnsect = $('#navbtn>li>a');
$(btnsect).each(function () { $(btnsect).each(function () {
if ($(this).attr('href') === '#settings') { if ($(this).attr('href') === '#settings') {

View file

@ -52,16 +52,16 @@ def allinstances(request):
else: else:
for comp in computes: for comp in computes:
try: try:
all_host_vms.update(get_host_instances(request,comp)) all_host_vms.update(get_host_instances(request, comp))
except libvirtError as lib_err: except libvirtError as lib_err:
error_messages.append(lib_err) error_messages.append(lib_err)
if request.method == 'POST': if request.method == 'POST':
try: try:
instances_actions(request) return instances_actions(request)
except libvirtError as lib_err: except libvirtError as lib_err:
error_messages.append(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 view_style = settings.VIEW_INSTANCES_LIST_STYLE
@ -88,10 +88,10 @@ def instances(request, compute_id):
if request.method == 'POST': if request.method == 'POST':
try: try:
instances_actions(request) return instances_actions(request)
except libvirtError as lib_err: except libvirtError as lib_err:
error_messages.append(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()) return render(request, 'instances.html', locals())
@ -255,6 +255,8 @@ def instance(request, compute_id, vname):
compute_nwfilters = conn.get_nwfilters() compute_nwfilters = conn.get_nwfilters()
status = conn.get_status() status = conn.get_status()
autostart = conn.get_autostart() autostart = conn.get_autostart()
bootmenu = conn.get_bootmenu()
boot_order = conn.get_bootorder()
vcpu = conn.get_vcpu() vcpu = conn.get_vcpu()
cur_vcpu = conn.get_cur_vcpu() cur_vcpu = conn.get_cur_vcpu()
uuid = conn.get_uuid() uuid = conn.get_uuid()
@ -525,7 +527,7 @@ def instance(request, compute_id, vname):
conn.attach_disk("", target, device='cdrom', cache='none', targetbus=bus) conn.attach_disk("", target, device='cdrom', cache='none', targetbus=bus)
msg = _('Add CD-Rom: ' + target) msg = _('Add CD-Rom: ' + target)
addlogmsg(request.user.username, instance.name, msg) addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#media') return HttpResponseRedirect(request.get_full_path() + '#disks')
if 'detach_cdrom' in request.POST and allow_admin_or_not_template: if 'detach_cdrom' in request.POST and allow_admin_or_not_template:
dev = request.POST.get('detach_cdrom', '') dev = request.POST.get('detach_cdrom', '')
@ -533,7 +535,7 @@ def instance(request, compute_id, vname):
conn.detach_disk(dev) conn.detach_disk(dev)
msg = _('Detach CD-Rom: ' + dev) msg = _('Detach CD-Rom: ' + dev)
addlogmsg(request.user.username, instance.name, msg) addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#media') return HttpResponseRedirect(request.get_full_path() + '#disks')
if 'umount_iso' in request.POST and allow_admin_or_not_template: if 'umount_iso' in request.POST and allow_admin_or_not_template:
image = request.POST.get('path', '') image = request.POST.get('path', '')
@ -541,7 +543,7 @@ def instance(request, compute_id, vname):
conn.umount_iso(dev, image) conn.umount_iso(dev, image)
msg = _("Mount media: " + dev) msg = _("Mount media: " + dev)
addlogmsg(request.user.username, instance.name, msg) addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#media') return HttpResponseRedirect(request.get_full_path() + '#disks')
if 'mount_iso' in request.POST and allow_admin_or_not_template: if 'mount_iso' in request.POST and allow_admin_or_not_template:
image = request.POST.get('media', '') image = request.POST.get('media', '')
@ -549,7 +551,7 @@ def instance(request, compute_id, vname):
conn.mount_iso(dev, image) conn.mount_iso(dev, image)
msg = _("Umount media: " + dev) msg = _("Umount media: " + dev)
addlogmsg(request.user.username, instance.name, msg) addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#media') return HttpResponseRedirect(request.get_full_path() + '#disks')
if 'snapshot' in request.POST and allow_admin_or_not_template: if 'snapshot' in request.POST and allow_admin_or_not_template:
name = request.POST.get('name', '') name = request.POST.get('name', '')
@ -591,13 +593,37 @@ def instance(request, compute_id, vname):
conn.set_autostart(1) conn.set_autostart(1)
msg = _("Set autostart") msg = _("Set autostart")
addlogmsg(request.user.username, instance.name, msg) addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#autostart') return HttpResponseRedirect(request.get_full_path() + '#boot_opt')
if 'unset_autostart' in request.POST: if 'unset_autostart' in request.POST:
conn.set_autostart(0) conn.set_autostart(0)
msg = _("Unset autostart") msg = _("Unset autostart")
addlogmsg(request.user.username, instance.name, msg) addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#autostart') return HttpResponseRedirect(request.get_full_path() + '#boot_opt')
if 'set_bootmenu' in request.POST:
conn.set_bootmenu(1)
msg = _("Enable boot menu")
addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#boot_opt')
if 'unset_bootmenu' in request.POST:
conn.set_bootmenu(0)
msg = _("Disable boot menu")
addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#boot_opt')
if 'set_bootorder' in request.POST:
bootorder = request.POST.get('bootorder', '')
if bootorder:
order_list = {}
for idx, val in enumerate(bootorder.split(',')):
type, dev = val.split(':', 1)
order_list[idx] = {"type": type, "dev": dev}
conn.set_bootorder(order_list)
msg = _("Set boot order")
addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#boot_opt')
if 'change_xml' in request.POST: if 'change_xml' in request.POST:
exit_xml = request.POST.get('inst_xml', '') exit_xml = request.POST.get('inst_xml', '')
@ -802,7 +828,6 @@ def instance(request, compute_id, vname):
msg = _("Edit options") msg = _("Edit options")
addlogmsg(request.user.username, instance.name, msg) addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#options') return HttpResponseRedirect(request.get_full_path() + '#options')
conn.close() conn.close()
except libvirtError as lib_err: except libvirtError as lib_err:
@ -837,7 +862,7 @@ def inst_status(request, compute_id, vname):
return response return response
def get_host_instances(request,comp): def get_host_instances(request, comp):
def refresh_instance_database(comp, inst_name, info): def refresh_instance_database(comp, inst_name, info):
def get_userinstances_info(instance): 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) status = connection_manager.host_is_up(comp.type, comp.hostname)
if status: if status:
conn = wvmHostDetails(comp, comp.login, comp.password, comp.type) conn = wvmHostDetails(comp, comp.login, comp.password, comp.type)
comp_node_info = conn.get_node_info() comp_node_info = conn.get_node_info()
comp_mem = conn.get_memory_usage() comp_mem = conn.get_memory_usage()
@ -907,6 +931,7 @@ def get_host_instances(request,comp):
return all_host_vms return all_host_vms
def get_user_instances(request): def get_user_instances(request):
all_user_vms = {} all_user_vms = {}
user_instances = UserInstance.objects.filter(user_id=request.user.id) user_instances = UserInstance.objects.filter(user_id=request.user.id)
@ -975,7 +1000,6 @@ def instances_actions(request):
return response return response
if request.user.is_superuser: if request.user.is_superuser:
if 'suspend' in request.POST: if 'suspend' in request.POST:
msg = _("Suspend") msg = _("Suspend")
addlogmsg(request.user.username, instance.name, msg) addlogmsg(request.user.username, instance.name, msg)
@ -1003,7 +1027,7 @@ def inst_graph(request, compute_id, vname):
datasets_net = {} datasets_net = {}
cookies = {} cookies = {}
points = 5 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) compute = get_object_or_404(Compute, pk=compute_id)
response = HttpResponse() response = HttpResponse()
response['Content-Type'] = "text/javascript" response['Content-Type'] = "text/javascript"
@ -1030,18 +1054,15 @@ def inst_graph(request, compute_id, vname):
cookies['net'] = request.COOKIES['net'] cookies['net'] = request.COOKIES['net']
cookies['timer'] = request.COOKIES['timer'] cookies['timer'] = request.COOKIES['timer']
except KeyError: except KeyError:
cookies['cpu'] = None cookies['cpu'] = cookies['blk'] = cookies['net'] = None
cookies['blk'] = None
cookies['net'] = None
if not cookies['cpu']: if not cookies['cpu']:
datasets['cpu'] = [0] * points datasets['timer'] = datasets['cpu'] = [0] * points
datasets['timer'] = [0] * points
else: else:
datasets['cpu'] = eval(cookies['cpu']) datasets['cpu'] = eval(cookies['cpu'])
datasets['timer'] = eval(cookies['timer']) datasets['timer'] = eval(cookies['timer'])
datasets['timer'].append(curent_time) datasets['timer'].append(current_time)
datasets['cpu'].append(int(cpu_usage['cpu'])) datasets['cpu'].append(int(cpu_usage['cpu']))
datasets['timer'] = check_points(datasets['timer']) datasets['timer'] = check_points(datasets['timer'])
@ -1049,8 +1070,7 @@ def inst_graph(request, compute_id, vname):
for blk in blk_usage: for blk in blk_usage:
if not cookies['blk']: if not cookies['blk']:
datasets_wr = [0] * points datasets_rd = datasets_wr = [0] * points
datasets_rd = [0] * points
else: else:
datasets['blk'] = eval(cookies['blk']) datasets['blk'] = eval(cookies['blk'])
datasets_rd = datasets['blk'][blk['dev']][0] datasets_rd = datasets['blk'][blk['dev']][0]
@ -1067,8 +1087,7 @@ def inst_graph(request, compute_id, vname):
for net in net_usage: for net in net_usage:
if not cookies['net']: if not cookies['net']:
datasets_rx = [0] * points datasets_tx = datasets_rx = [0] * points
datasets_tx = [0] * points
else: else:
datasets['net'] = eval(cookies['net']) datasets['net'] = eval(cookies['net'])
datasets_rx = datasets['net'][net['dev']][0] datasets_rx = datasets['net'][net['dev']][0]
@ -1133,7 +1152,7 @@ def _get_random_mac_address():
@login_required @login_required
def random_mac_address(request): def random_mac_address(request):
data = {} data = dict()
data['mac'] = _get_random_mac_address() data['mac'] = _get_random_mac_address()
return HttpResponse(json.dumps(data)) return HttpResponse(json.dumps(data))
@ -1157,9 +1176,9 @@ def guess_clone_name(request):
@login_required @login_required
def check_instance(request, vname): def check_instance(request, vname):
check_instance = Instance.objects.filter(name=vname) instance = Instance.objects.filter(name=vname)
data = {'vname': vname, 'exists': False} data = {'vname': vname, 'exists': False}
if check_instance: if instance:
data['exists'] = True data['exists'] = True
return HttpResponse(json.dumps(data)) 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 --> <!-- /.row -->
{% include 'errors_block.html' %} {% include 'errors_block.html' %}
{% include 'messages_block.html' %}
<div class="row"> <div class="row">
<div class="col-xs-6 col-sm-4"> <div class="col-xs-6 col-sm-4">
@ -66,9 +67,11 @@
</p> </p>
</div> </div>
</div> </div>
{% if state %} {#{% if state %}#}
<div class="row">
<h3 class="page-header">{% trans "IPv4 Configuration" %}</h3>
</div>
<div class="row"> <div class="row">
<h3 class="page-header">{% trans "IPv4 configuration" %}</h3>
<div class="col-xs-6 col-sm-4"> <div class="col-xs-6 col-sm-4">
<p>{% trans "IPv4 Forwarding:" %}</p> <p>{% trans "IPv4 Forwarding:" %}</p>
<p>{% trans "Network:" %}</p> <p>{% trans "Network:" %}</p>
@ -102,43 +105,81 @@
{% endif %} {% endif %}
</p> </p>
{% if ipv4_dhcp_range_start and ipv4_dhcp_range_end %} {% if ipv4_dhcp_range_start and ipv4_dhcp_range_end %}
<p>{{ ipv4_dhcp_range_start }}</p> <form method="post" role="form">{% csrf_token %}
<p>{{ ipv4_dhcp_range_end }}</p> {% 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 %} {% endif %}
</div> </div>
</div> </div>
{% if fixed_address %} {% ifequal ipv4_forward.0 'nat' %}
{% if state %}
{% include 'modify_fixed_address.html' %}
{% endif %}
<div class="row"> <div class="row">
<h3 class="page-header">{% trans "Fixed Address" %}</h3> <h3 class="page-header">{% trans "Fixed Address" %}</h3>
</div>
{% endifequal %}
{% if fixed_address %}
<div class="row">
<div class="col-xs-12"> <div class="col-xs-12">
<div class="panel-group" id="accordion"> <div class="panel-group" id="accordion">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<a data-toggle="collapse" data-parent="#accordion" href="#collapseOne"> <a data-toggle="collapse" data-parent="#accordion" href="#collapseOne">
Show {% trans 'Show' %}
</a> </a>
</div> </div>
<div id="collapseOne" class="panel-collapse collapse"> <div id="collapseOne" class="panel-collapse collapse">
<div class="panel-body"> <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"> <div class="form-group">
<input type="text" class="form-control" id="filter_input"> <input type="text" class="form-control" id="filter_input">
</div> </div>
<input type="button" class="btn btn-default" id="filter_button" value="Filter"> <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> </div>
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr> <tr>
<th style="text-align: center">{% trans "Address" %}</th>
<th style="text-align: center">{% trans "MAC" %}</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> </tr>
</thead> </thead>
<tbody style="text-align: center"> <tbody style="text-align: center">
{% for fix in fixed_address %} {% for fix in fixed_address %}
<tr> <tr>
<td>{{ fix.host }}</td> <form method="post" role="form">{% csrf_token %}
<td>{{ fix.mac }}</td> <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> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@ -149,7 +190,7 @@
</div> </div>
</div> </div>
</div> </div>
{% endif %} {#{% endif %}#}
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block script %} {% block script %}
@ -175,7 +216,6 @@
$('tbody tr:not(:Contains(\'' + filter_val + '\'))').hide(); $('tbody tr:not(:Contains(\'' + filter_val + '\'))').hide();
} }
}); });
// add event button labeled "clear" // add event button labeled "clear"
$('#filter_clear').click(function (event) { $('#filter_clear').click(function (event) {
$('#filter_input').val(''); $('#filter_input').val('');

View file

@ -8,6 +8,7 @@ from networks.forms import AddNetPool
from vrtManager.network import wvmNetwork, wvmNetworks from vrtManager.network import wvmNetwork, wvmNetworks
from vrtManager.network import network_size from vrtManager.network import network_size
from libvirt import libvirtError from libvirt import libvirtError
from django.contrib import messages
@login_required @login_required
@ -121,6 +122,33 @@ def network(request, compute_id, pool):
return HttpResponseRedirect(request.get_full_path()) return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err: except libvirtError as lib_err:
error_messages.append(lib_err.message) 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() conn.close()

View file

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

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) vol = self.get_volume_by_path(vol_path)
return vol.storagePoolLookupByVolume() 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) 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") storage_type = util.get_xml_path(stg.XMLDesc(0), "/pool/@type")
format = util.get_xml_path(vol.XMLDesc(0), "/volume/target/format/@type") format = util.get_xml_path(vol.XMLDesc(0), "/volume/target/format/@type")
if storage_type == 'dir': if storage_type == 'dir':

View file

@ -201,7 +201,6 @@ class wvmInstance(wvmConnect):
return util.get_xml_path(self._XMLDesc(0), func=filterrefs) return util.get_xml_path(self._XMLDesc(0), func=filterrefs)
def get_description(self): def get_description(self):
description = util.get_xml_path(self._XMLDesc(0), "/domain/description") description = util.get_xml_path(self._XMLDesc(0), "/domain/description")
return description if description else '' return description if description else ''
@ -248,13 +247,8 @@ class wvmInstance(wvmConnect):
def get_disk_devices(self): def get_disk_devices(self):
def disks(doc): def disks(doc):
result = [] result = []
dev = None dev = volume = storage = src_file = None
volume = None disk_format = used_size = disk_size = disk_cache = None
storage = None
src_fl = None
disk_format = None
used_size = None
disk_size = None
for disk in doc.xpath('/domain/devices/disk'): for disk in doc.xpath('/domain/devices/disk'):
device = disk.xpath('@device')[0] device = disk.xpath('@device')[0]
@ -262,13 +256,17 @@ class wvmInstance(wvmConnect):
try: try:
dev = disk.xpath('target/@dev')[0] dev = disk.xpath('target/@dev')[0]
bus = disk.xpath('target/@bus')[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: try:
disk_format = disk.xpath('driver/@type')[0] disk_format = disk.xpath('driver/@type')[0]
except: except:
pass pass
try: 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() volume = vol.name()
disk_size = vol.info()[1] disk_size = vol.info()[1]
@ -276,13 +274,13 @@ class wvmInstance(wvmConnect):
stg = vol.storagePoolLookupByVolume() stg = vol.storagePoolLookupByVolume()
storage = stg.name() storage = stg.name()
except libvirtError: except libvirtError:
volume = src_fl volume = src_file
except: except:
pass pass
finally: finally:
result.append( result.append(
{'dev': dev, 'bus': bus, 'image': volume, 'storage': storage, 'path': src_fl, {'dev': dev, 'bus': bus, 'image': volume, 'storage': storage, 'path': src_file,
'format': disk_format, 'size': disk_size, 'used': used_size}) 'format': disk_format, 'size': disk_size, 'used': used_size, 'cache': disk_cache})
return result return result
return util.get_xml_path(self._XMLDesc(0), func=disks) return util.get_xml_path(self._XMLDesc(0), func=disks)
@ -290,10 +288,8 @@ class wvmInstance(wvmConnect):
def get_media_devices(self): def get_media_devices(self):
def disks(doc): def disks(doc):
result = [] result = []
dev = None dev = volume = storage = None
volume = None src_file = None
storage = None
src_fl = None
for media in doc.xpath('/domain/devices/disk'): for media in doc.xpath('/domain/devices/disk'):
device = media.xpath('@device')[0] device = media.xpath('@device')[0]
if device == 'cdrom': if device == 'cdrom':
@ -301,22 +297,137 @@ class wvmInstance(wvmConnect):
dev = media.xpath('target/@dev')[0] dev = media.xpath('target/@dev')[0]
bus = media.xpath('target/@bus')[0] bus = media.xpath('target/@bus')[0]
try: try:
src_fl = media.xpath('source/@file')[0] src_file = media.xpath('source/@file')[0]
vol = self.get_volume_by_path(src_fl) vol = self.get_volume_by_path(src_file)
volume = vol.name() volume = vol.name()
stg = vol.storagePoolLookupByVolume() stg = vol.storagePoolLookupByVolume()
storage = stg.name() storage = stg.name()
except: except:
src_fl = None src_file = None
volume = src_fl volume = src_file
except: except:
pass pass
finally: 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 result
return util.get_xml_path(self._XMLDesc(0), func=disks) return util.get_xml_path(self._XMLDesc(0), func=disks)
def get_bootmenu(self):
menu = util.get_xml_path(self._XMLDesc(0), "/domain/os/bootmenu/@enable")
return True if menu == 'yes' else False
def set_bootmenu(self, flag):
tree = ElementTree.fromstring(self._XMLDesc(0))
os = tree.find('os')
menu = os.find("bootmenu")
if menu == None:
bootmenu = ElementTree.fromstring("<bootmenu enable='yes'/>")
os.append(bootmenu)
menu = os.find("bootmenu")
if flag == 0: # Disable
menu.attrib['enable'] = 'no'
elif flag == 1: # Enable
menu.attrib['enable'] = 'yes'
elif flag == -1: # Remove
os.remove(menu)
else:
raise Exception('Unknown boot menu option, please choose one of 0:disable, 1:enable, -1:remove')
xmldom = ElementTree.tostring(tree)
self._defineXML(xmldom)
def get_bootorder(self):
boot_order = {}
tree = ElementTree.fromstring(self._XMLDesc(0))
os = tree.find('os')
boot = os.findall('boot')
for idx, b in enumerate(boot):
dev = b.get('dev')
if dev == 'hd':
target = "disk"
type = "file"
elif dev == 'fd':
target = "floppy"
type = "file"
elif dev == 'cdrom':
target = "cdrom"
type = "file"
elif dev == 'network':
target = "network"
type = "network"
boot_order[idx] = {"type": type, "dev": dev, "target": target}
devices = tree.find('devices')
for dev in devices:
dev_target = dev_type = dev_device = dev_alias = None
boot_dev = dev.find('boot')
if boot_dev != None:
idx = boot_dev.get('order')
dev_type = dev.get('type')
dev_device = dev.get('device')
if dev_type == 'file':
dev_target = dev.find('target').get('dev')
elif dev_type == 'network':
dev_mac = dev.find('mac').get('address')
dev_device = "network"
dev_target = "nic-{}".format(dev_mac[9:])
elif dev_type == 'usb':
pass
boot_order[int(idx)-1] = {"type": dev_type, "dev": dev_device, "target": dev_target}
return boot_order
def set_bootorder(self, devorder):
if not devorder:
return
def remove_bootorder():
tree = ElementTree.fromstring(self._XMLDesc(0))
os = tree.find('os')
boot = os.findall('boot')
# Remove old style boot order
for b in boot:
os.remove(b)
# Remove rest of them
for dev in tree.find('devices'):
boot_dev = dev.find('boot')
if boot_dev != None:
dev.remove(boot_dev)
return tree
tree = remove_bootorder()
for idx, dev in devorder.items():
order = ElementTree.fromstring("<boot order='{}'/>".format(idx + 1))
if dev['type'] == 'disk':
devices = tree.findall("./devices/disk[@device='disk']")
for d in devices:
device = d.find("./target[@dev='{}']".format(dev['dev']))
if device != None:
d.append(order)
elif dev['type'] == 'cdrom':
devices = tree.findall("./devices/disk[@device='cdrom']")
for d in devices:
device = d.find("./target[@dev='{}']".format(dev['dev']))
if device != None:
d.append(order)
elif dev['type'] == 'network':
devices = tree.findall("./devices/interface[@type='network']")
for d in devices:
device = d.find("mac[@address='{}']".format(dev['dev']))
if device != None:
d.append(order)
else:
raise Exception('Invalid Device Type for boot order')
self._defineXML(ElementTree.tostring(tree))
def mount_iso(self, dev, image): def mount_iso(self, dev, image):
def attach_iso(dev, disk, vol): def attach_iso(dev, disk, vol):
if disk.get('device') == 'cdrom': if disk.get('device') == 'cdrom':
@ -490,11 +601,9 @@ class wvmInstance(wvmConnect):
return telnet_port return telnet_port
def get_console_listen_addr(self): def get_console_listen_addr(self):
listen_addr = util.get_xml_path(self._XMLDesc(0), listen_addr = util.get_xml_path(self._XMLDesc(0), "/domain/devices/graphics/@listen")
"/domain/devices/graphics/@listen")
if listen_addr is None: if listen_addr is None:
listen_addr = util.get_xml_path(self._XMLDesc(0), listen_addr = util.get_xml_path(self._XMLDesc(0), "/domain/devices/graphics/listen/@address")
"/domain/devices/graphics/listen/@address")
if listen_addr is None: if listen_addr is None:
return "127.0.0.1" return "127.0.0.1"
return listen_addr return listen_addr

View file

@ -1,6 +1,10 @@
from vrtManager import util from vrtManager import util
from vrtManager.IPy import IP from vrtManager.IPy import IP
from vrtManager.connection import wvmConnect 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): def network_size(net, dhcp=None):
@ -106,6 +110,9 @@ class wvmNetwork(wvmConnect):
def delete(self): def delete(self):
self.net.undefine() 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): def get_ipv4_network(self):
xml = self._XMLDesc(0) xml = self._XMLDesc(0)
if util.get_xml_path(xml, "/network/ip") is None: if util.get_xml_path(xml, "/network/ip") is None:
@ -169,9 +176,57 @@ class wvmNetwork(wvmConnect):
def network(doc): def network(doc):
result = [] result = []
for net in doc.xpath('/network/ip/dhcp/host'): for net in doc.xpath('/network/ip/dhcp/host'):
host = net.xpath('@ip')[0] ip = net.xpath('@ip')[0]
mac = net.xpath('@mac')[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 result
return util.get_xml_path(self._XMLDesc(0), func=network) 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 = (val[0:8] + "-" + val[8:12] + "-" + val[12:16] +
"-" + val[16:20] + "-" + val[20:32]) "-" + val[16:20] + "-" + val[20:32])
return val 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)