Commit ae45c595 by Kálmán Viktor

Merge branch 'master' into feature-pipeline

Conflicts:
	.gitignore
	circle/dashboard/static/dashboard/dashboard.js
parents 5fe2e959 687fccd6
......@@ -39,6 +39,7 @@ circle/bower_components
# jsi18n files
jsi18n
scripts.rc
# less
*.css
......@@ -273,3 +273,4 @@ def register_operation(op_cls, op_id=None, target_cls=None):
setattr(target_cls, operation_registry_name, dict())
getattr(target_cls, operation_registry_name)[op_id] = op_cls
return op_cls
......@@ -524,11 +524,7 @@ class TemplateForm(forms.ModelForm):
value = field.widget.value_from_datadict(
self.data, self.files, self.add_prefix(name))
try:
if isinstance(field, forms.FileField):
initial = self.initial.get(name, field.initial)
value = field.clean(value, initial)
else:
value = field.clean(value)
value = field.clean(value)
self.cleaned_data[name] = value
if hasattr(self, 'clean_%s' % name):
value = getattr(self, 'clean_%s' % name)()
......@@ -544,13 +540,14 @@ class TemplateForm(forms.ModelForm):
else:
self.cleaned_data[name] = getattr(old, name)
if "req_traits" not in self.allowed_fields:
self.cleaned_data['req_traits'] = self.instance.req_traits.all()
def save(self, commit=True):
data = self.cleaned_data
self.instance.max_ram_size = data.get('ram_size')
instance = super(TemplateForm, self).save(commit=False)
if commit:
instance.save()
instance = super(TemplateForm, self).save(commit=True)
# create and/or delete InterfaceTemplates
networks = InterfaceTemplate.objects.filter(
......@@ -755,6 +752,7 @@ class VmStateChangeForm(forms.Form):
"but don't interrupt any tasks."))
new_state = forms.ChoiceField(Instance.STATUS, label=_(
"New status"))
reset_node = forms.BooleanField(required=False, label=_("Reset node"))
def __init__(self, *args, **kwargs):
show_interrupt = kwargs.pop('show_interrupt')
......@@ -772,6 +770,17 @@ class VmStateChangeForm(forms.Form):
return helper
class RedeployForm(forms.Form):
with_emergency_change_state = forms.BooleanField(
required=False, initial=True, label=_("use emergency state change"))
@property
def helper(self):
helper = FormHelper(self)
helper.form_tag = False
return helper
class VmCreateDiskForm(forms.Form):
name = forms.CharField(max_length=100, label=_("Name"))
size = forms.CharField(
......@@ -793,6 +802,48 @@ class VmCreateDiskForm(forms.Form):
return helper
class VmDiskResizeForm(forms.Form):
size = forms.CharField(
widget=FileSizeWidget, initial=(10 << 30), label=_('Size'),
help_text=_('Size to resize the disk in bytes or with units '
'like MB or GB.'))
def __init__(self, *args, **kwargs):
choices = kwargs.pop('choices')
self.disk = kwargs.pop('default')
super(VmDiskResizeForm, self).__init__(*args, **kwargs)
self.fields.insert(0, 'disk', forms.ModelChoiceField(
queryset=choices, initial=self.disk, required=True,
empty_label=None, label=_('Disk')))
if self.disk:
self.fields['disk'].widget = HiddenInput()
self.fields['size'].initial += self.disk.size
def clean(self):
cleaned_data = super(VmDiskResizeForm, self).clean()
size_in_bytes = self.cleaned_data.get("size")
disk = self.cleaned_data.get('disk')
if not size_in_bytes.isdigit() and len(size_in_bytes) > 0:
raise forms.ValidationError(_("Invalid format, you can use "
" GB or MB!"))
if int(size_in_bytes) < int(disk.size):
raise forms.ValidationError(_("Disk size must be greater than the "
"actual size."))
return cleaned_data
@property
def helper(self):
helper = FormHelper(self)
helper.form_tag = False
if self.disk:
helper.layout = Layout(
HTML(_("<label>Disk:</label> %s") % self.disk),
Field('disk'), Field('size'))
return helper
class VmDownloadDiskForm(forms.Form):
name = forms.CharField(max_length=100, label=_("Name"))
url = forms.CharField(label=_('URL'), validators=[URLValidator(), ])
......
......@@ -234,6 +234,7 @@ $(function () {
'host': result[i].host,
'icon': result[i].icon,
'status': result[i].status,
'owner': result[i].owner,
});
}
});
......@@ -249,9 +250,9 @@ $(function () {
}
}
search_result.sort(compareVmByFav);
for(i=0; i<5 && i<search_result.length; i++)
html += generateVmHTML(search_result[i].pk, search_result[i].name,
search_result[i].host, search_result[i].icon,
for(var i=0; i<5 && i<search_result.length; i++)
html += generateVmHTML(search_result[i].pk, search_result[i].name,
search_result[i].owner ? search_result[i].owner : search_result[i].host, search_result[i].icon,
search_result[i].status, search_result[i].fav,
(search_result.length < 5));
if(search_result.length === 0)
......@@ -396,6 +397,20 @@ $(function () {
clientInstalledAction(connectUri);
return false;
});
/* change graphs */
$(".graph-buttons a").click(function() {
var time = $(this).data("graph-time");
$(".graph-images img").each(function() {
var src = $(this).prop("src");
var new_src = src.substring(0, src.lastIndexOf("/") + 1) + time;
$(this).prop("src", new_src);
});
// change the buttons too
$(".graph-buttons a").removeClass("btn-primary").addClass("btn-default");
$(this).removeClass("btn-default").addClass("btn-primary");
return false;
});
});
function generateVmHTML(pk, name, host, icon, _status, fav, is_last) {
......
......@@ -1026,3 +1026,41 @@ textarea[name="new_members"] {
.hilight .autocomplete-hl {
color: orange;
}
#vm-info-pane {
margin-bottom: 20px;
}
.node-list-table tbody>tr>td, .node-list-table thead>tr>th {
vertical-align: middle;
}
.node-list-table thead>tr>th,
.node-list-table .enabled, .node-list-table .priority,
.node-list-table .overcommit, .node-list-table .number_of_VMs {
text-align: center;
}
.node-list-table-thin {
width: 10px;
}
.node-list-table-monitor {
width: 250px;
}
.graph-images img {
max-width: 100%;
}
#vm-list-table tbody td:nth-child(3) {
white-space: nowrap;
}
#vm-list-table td {
vertical-align: middle;
}
.disk-resize-btn {
margin-right: 5px;
}
......@@ -19,8 +19,7 @@ from __future__ import absolute_import
from django.contrib.auth.models import Group, User
from django_tables2 import Table, A
from django_tables2.columns import (TemplateColumn, Column, BooleanColumn,
LinkColumn)
from django_tables2.columns import TemplateColumn, Column, LinkColumn
from vm.models import Node, InstanceTemplate, Lease
from django.utils.translation import ugettext_lazy as _
......@@ -40,8 +39,10 @@ class NodeListTable(Table):
attrs={'th': {'class': 'node-list-table-thin'}},
)
enabled = BooleanColumn(
get_status_display = Column(
verbose_name=_("Status"),
attrs={'th': {'class': 'node-list-table-thin'}},
order_by=("enabled", "schedule_enabled"),
)
name = TemplateColumn(
......@@ -66,20 +67,12 @@ class NodeListTable(Table):
orderable=False,
)
actions = TemplateColumn(
verbose_name=_("Actions"),
attrs={'th': {'class': 'node-list-table-thin'}},
template_code=('{% include "dashboard/node-list/column-'
'actions.html" with btn_size="btn-xs" %}'),
orderable=False,
)
class Meta:
model = Node
attrs = {'class': ('table table-bordered table-striped table-hover '
'node-list-table')}
fields = ('pk', 'name', 'host', 'enabled', 'priority', 'overcommit',
'number_of_VMs', )
fields = ('pk', 'name', 'host', 'get_status_display', 'priority',
'overcommit', 'number_of_VMs', )
class GroupListTable(Table):
......
......@@ -11,11 +11,18 @@
{% endif %}
{% else %}<span class="disk-list-disk-percentage" data-disk-pk="{{ d.pk }}">{{ d.get_download_percentage }}</span>%{% endif %}
{% if is_owner != False %}
<a href="{% url "dashboard.views.disk-remove" pk=d.pk %}?next={{ request.path }}"
data-disk-pk="{{ d.pk }}" class="btn btn-xs btn-danger pull-right disk-remove"
{% if not long_remove %}title="{% trans "Remove" %}"{% endif %}
>
<i class="fa fa-times"></i>{% if long_remove %} {% trans "Remove" %}{% endif %}
</a>
<a href="{% url "dashboard.views.disk-remove" pk=d.pk %}?next={{ request.path }}"
data-disk-pk="{{ d.pk }}" class="btn btn-xs btn-danger pull-right disk-remove"
{% if not long_remove %}title="{% trans "Remove" %}"{% endif %}>
<i class="fa fa-times"></i>{% if long_remove %} {% trans "Remove" %}{% endif %}
</a>
{% if op.resize_disk %}
<span class="operation-wrapper">
<a href="{{ op.resize_disk.get_url }}?disk={{d.pk}}"
class="btn btn-xs btn-warning pull-right operation disk-resize-btn">
<i class="fa fa-arrows-alt"></i> {% trans "Resize" %}
</a>
</span>
{% endif %}
{% endif %}
<div style="clear: both;"></div>
{% for o in graph_time_options %}
<a class="btn btn-xs
btn-{% if graph_time == o.time %}primary{% else %}default{% endif %}"
href="?graph_time={{ o.time }}"
data-graph-time="{{ o.time }}">
{{ o.name }}
</a>
{% endfor %}
......@@ -18,6 +18,8 @@ Choose a compute node to migrate {{obj}} to.
<li class="panel panel-default"><div class="panel-body">
<label for="migrate-to-{{n.pk}}">
<strong>{{ n }}</strong>
<div class="label label-primary"><i class="fa {{n.get_status_icon}}"></i>
{{n.get_status_display}}</div>
{% if current == n.pk %}<div class="label label-info">{% trans "current" %}</div>{% endif %}
{% if selected == n.pk %}<div class="label label-success">{% trans "recommended" %}</div>{% endif %}
</label>
......
{% load i18n %}
<div class="modal fade" id="confirmation-modal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body">
{% if text %}
{{ text }}
{% else %}
{%blocktrans with object=object%}
Are you sure you want to flush <strong>{{ object }}</strong>?
{%endblocktrans%}
{% endif %}
<div class="pull-right">
<form action="{% url "dashboard.views.flush-node" pk=node.pk %}?next={{next}}" method="POST">
{% csrf_token %}
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Cancel" %}</button>
<input type="hidden" name="flush" value=""/>
<button class="btn btn-warning">{% trans "Yes" %}</button>
</form>
</div>
<div class="clearfix"></div>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div>
......@@ -2,6 +2,8 @@
{% load crispy_forms_tags %}
{% load i18n %}
{% block title-page %}{{ group.name }} | {% trans "group" %}{% endblock %}
{% block content %}
<div class="body-content">
<div class="page-header">
......
......@@ -25,7 +25,10 @@
<i class="fa {{ i.get_status_icon }}" title="{{ i.get_status_display }}"></i>
{{ i.name }}
</span>
<small class="text-muted"> {{ i.short_hostname }}</small>
<small class="text-muted">
{% if i.owner == request.user %}{{ i.short_hostname }}
{% else %}{{i.owner.profile.get_display_name}}{% endif %}
</small>
<div class="pull-right dashboard-vm-favourite" data-vm="{{ i.pk }}">
{% if i.fav %}
<i class="fa fa-star text-primary title-favourite" title="{% trans "Unfavourite" %}"></i>
......
......@@ -7,13 +7,12 @@
{% block content %}
<div class="body-content">
<div class="page-header">
<div class="pull-right" id="ops">
{% include "dashboard/vm-detail/_operations.html" %}
</div>
<div class="pull-right" style="padding-top: 15px;">
<a title="{% trans "Rename" %}" href="#" class="btn btn-default btn-xs node-details-rename-button"><i class="fa fa-pencil"></i></a>
<a title="{% trans "Flush" %}" data-node-pk="{{ node.pk }}" class="btn btn-default btn-xs real-link node-flush" href="{% url "dashboard.views.flush-node" pk=node.pk %}"><i class="fa fa-cloud-upload"></i></a>
<a title="{% trans "Enable" %}" style="display:{% if node.enabled %}none{% else %}inline-block{% endif %}" data-node-pk="{{ node.pk }}" class="btn btn-default btn-xs real-link node-enable" href="{% url "dashboard.views.status-node" pk=node.pk %}?next={{ request.path }}"><i class="fa fa-check"></i></a>
<a title="{% trans "Disable" %}" style="display:{% if not node.enabled %}none{% else %}inline-block{% endif %}" data-node-pk="{{ node.pk }}" class="btn btn-default btn-xs real-link node-enable" href="{% url "dashboard.views.status-node" pk=node.pk %}?next={{ request.path }}"><i class="fa fa-ban"></i></a>
<a title="{% trans "Delete" %}" data-node-pk="{{ node.pk }}" class="btn btn-default btn-xs real-link node-delete" href="{% url "dashboard.views.delete-node" pk=node.pk %}"><i class="fa fa-trash-o"></i></a>
<a title="{% trans "Help" %}" href="#" class="btn btn-default btn-xs node-details-help-button"><i class="fa fa-question"></i></a>
</div>
<h1>
<div id="node-details-rename">
......@@ -27,42 +26,34 @@
{{ node.name }}
</div>
</h1>
<div class="node-details-help js-hidden">
<ul style="list-style: none;">
<li>
<strong>{% trans "Rename" %}:</strong>
{% trans "Change the name of the node." %}
</li>
<li>
<strong>{% trans "Flush" %}:</strong>
{% trans "Disable node and move all instances to other one." %}
</li>
<li>
<strong>{% trans "Enable" %}:</strong>
{% trans "Enables node." %}
</li>
<li>
<strong>{% trans "Disable" %}:</strong>
{% trans "Disables node." %}
</li>
<li>
<strong>{% trans "Delete" %}:</strong>
{% trans "Remove node and it's host." %}
</li>
</ul>
</div>
</div>
<div class="row">
<div class="col-md-2" id="node-info-pane">
<div id="node-info-data" class="big">
<span id="node-details-state" class="label
{% if node.state == 'ONLINE' %}label-success
{% elif node.state == 'MISSING' %}label-danger
{% elif node.state == 'DISABLED' %}label-warning
{% elif node.state == 'OFFLINE' %}label-warning{% endif %}">
{% if node.state == 'ACTIVE' %}label-success
{% elif node.state == 'PASSIVE' %}label-warning
{% else %}label-danger{% endif %}">
<i class="fa {{ node.get_status_icon }}"></i> {{ node.get_status_display|upper }}
</span>
</div>
<div>
{% if node.enabled %}
<span class="label label-success">{% trans "Enabled" %}</span>
{% if node.schedule_enabled %}
<span class="label label-success">{% trans "Schedule enabled" %}</span>
{% else %}
<span class="label label-warning">{% trans "Schedule disabled" %}</span>
{% endif %}
{% else %}
<span class="label label-warning">{% trans "Disabled" %}</span>
{% endif %}
{% if node.online %}
<span class="label label-success">{% trans "Online" %}</span>
{% else %}
<span class="label label-warning">{% trans "Offline" %}</span>
{% endif %}
</div>
</div>
<div class="col-md-10" id="node-detail-pane">
<div class="panel panel-default" id="node-detail-panel">
......
......@@ -30,15 +30,22 @@
</div>
<div class="col-md-8">
{% if graphite_enabled %}
<img src="{% url "dashboard.views.node-graph" node.pk "cpu" "6h" %}" style="width:100%"/>
<img src="{% url "dashboard.views.node-graph" node.pk "memory" "6h" %}" style="width:100%"/>
<img src="{% url "dashboard.views.node-graph" node.pk "network" "6h" %}" style="width:100%"/>
<div class="text-center graph-buttons">
{% include "dashboard/_graph-time-buttons.html" %}
</div>
<div class="graph-images text-center">
<img src="{% url "dashboard.views.node-graph" node.pk "cpu" graph_time %}"/>
<img src="{% url "dashboard.views.node-graph" node.pk "memory" graph_time %}"/>
<img src="{% url "dashboard.views.node-graph" node.pk "network" graph_time %}"/>
<img src="{% url "dashboard.views.node-graph" node.pk "vm" graph_time %}"/>
<img src="{% url "dashboard.views.node-graph" node.pk "alloc" graph_time %}"/>
</div>
{% endif %}
</div>
</div>
<style>
.form-group {
margin: 0px;
}
</div>
</style>
<style>
.form-group {
margin: 0px;
}
</style>
......@@ -22,25 +22,23 @@
</div>
</div>
<style>
.node-list-table tbody>tr>td, .node-list-table thead>tr>th {
vertical-align: middle;
}
.node-list-table thead>tr>th,
.node-list-table .enabled, .node-list-table .priority,
.node-list-table .overcommit, .node-list-table .number_of_VMs {
text-align: center;
}
.node-list-table-thin {
width: 10px;
}
<div class="row">
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading">
<div class="pull-right graph-buttons">
{% include "dashboard/_graph-time-buttons.html" %}
</div>
<h3 class="no-margin"><i class="fa fa-area-chart"></i> {% trans "Graphs" %}</h3>
</div>
<div class="text-center graph-images">
<img src="{% url "dashboard.views.node-list-graph" "alloc" graph_time %}"/>
<img src="{% url "dashboard.views.node-list-graph" "vm" graph_time %}"/>
</div>
</div>
</div><!-- -col-md-12 -->
</div><!-- .row -->
.node-list-table-monitor {
width: 250px;
}
</style>
{% endblock %}
{% block extra_js %}
......
{% load i18n %}
<div class="btn-group">
<button type="button" class="btn {{ btn_size }} btn-warning nojs-dropdown-toogle dropdown-toggle" data-toggle="dropdown">Action
<i class="fa fa-caret-down"></i>
</button>
<ul class="dropdown-menu nojs-dropdown-toogle" role="menu">
<li>
<a href="#" class="node-details-rename-button">
<i class="fa fa-pencil"></i> {% trans "Rename" %}
</a>
</li>
<li>
<a data-node-pk="{{ record.pk }}" class="real-link node-flush" href="{% url "dashboard.views.flush-node" pk=record.pk %}">
<i class="fa fa-cloud-upload"></i> {% trans "Flush" %}
</a>
</li>
<li>
<a style={% if record.enabled %}"display:none"{% else %}"display:block"{% endif %} data-node-pk="{{ record.pk }}" class="real-link node-enable" href="{% url "dashboard.views.status-node" pk=record.pk %}?next={{ request.path }}">
<i class="fa fa-check"></i> {% trans "Enable" %}
</a>
</li>
<li>
<a style={% if record.enabled %}"display:block"{% else %}"display:none"{% endif %} data-node-pk="{{ record.pk }}" class="real-link node-enable" href="{% url "dashboard.views.status-node" pk=record.pk %}?next={{ request.path }}">
<i class="fa fa-times"></i> {% trans "Disable" %}
</a>
</li>
<li>
<a data-node-pk="{{ record.pk }}" class="real-link node-delete" href="{% url "dashboard.views.delete-node" pk=record.pk %}?next={{ request.path }}">
<i class="fa fa-trash-o"></i> {% trans "Delete" %}
</a>
</li>
</ul>
</div>
......@@ -4,7 +4,7 @@
{% load sizefieldtags %}
{% load crispy_forms_tags %}
{% block title-page %}{% trans "Edit template" %}{% endblock %}
{% block title-page %}{{ form.name.value }} | {% trans "template" %}{% endblock %}
{% block content %}
......@@ -24,18 +24,15 @@
{% csrf_token %}
{{ form.name|as_crispy_field }}
<a {% if form.parent.value %}
href="{% url "dashboard.views.template-detail" pk=form.parent.value %}"
{% else %}
disabled %}
{% endif %}
class="btn btn-default pull-right" style="margin-top: 24px;">
{% trans "Visit" %}
<i class="fa fa-arrow-circle-right"></i>
</a>
<div style="width: 80%;">
{{ form.parent|as_crispy_field }}
</div>
<strong>{% trans "Parent template" %}:</strong>
{% if parent %}
<a href="{% url "dashboard.views.template-detail" pk=parent.pk %}">
{{ parent.name }}
</a>
{% else %}
-
{% endif %}
<fieldset class="resources-sliders">
<legend>{% trans "Resource configuration" %}</legend>
......
......@@ -49,7 +49,10 @@
<div class="input-group vm-details-home-name">
<input id="vm-details-rename-name" class="form-control input-sm" name="new_name" type="text" value="{{ instance.name }}"/>
<span class="input-group-btn">
<button type="submit" class="btn btn-sm vm-details-rename-submit">{% trans "Rename" %}</button>
<button type="submit" class="btn btn-sm vm-details-rename-submit
{% if not is_operator %}disabled{% endif %}">
{% trans "Rename" %}
</button>
</span>
</div>
</form>
......
......@@ -7,6 +7,7 @@
<span class="timeline-icon{% if a.has_failed %} timeline-icon-failed{% endif %}">
<i class="fa {% if not a.finished %}fa-refresh fa-spin {% else %}fa-{{a.icon}}{% endif %}"></i>
</span>
{% spaceless %}
<strong{% if a.result %} title="{{ a.result|get_text:user }}"{% endif %}>
<a href="{{ a.get_absolute_url }}">
{% if a.times > 1 %}({{ a.times }}x){% endif %}
......@@ -16,7 +17,7 @@
- {{ a.percentage }}%
{% endif %}
</strong>
{% if a.times < 2%}{{ a.started|date:"Y-m-d H:i" }}{% endif %}{% if a.user %},
{% endspaceless %}{% if a.times < 2%} {{ a.started|date:"Y-m-d H:i" }}{% endif %}{% if a.user %},
<a class="no-style-link" href="{% url "dashboard.views.profile" username=a.user.username %}">
{% include "dashboard/_display-name.html" with user=a.user show_org=True %}
</a>
......
......@@ -11,7 +11,8 @@
<span class="input-group-addon">/</span>
<select class="form-control" name="proto" style="width: 70px;"><option>tcp</option><option>udp</option></select>
<div class="input-group-btn">
<button type="submit" class="btn btn-success btn-sm">{% trans "Add" %}</button>
<button type="submit" class="btn btn-success btn-sm
{% if not is_operator %}disabled{% endif %}">{% trans "Add" %}</button>
</div>
</div>
</form>
......
......@@ -6,7 +6,9 @@
<dd><i class="fa fa-{{ os_type_icon }}"></i> {{ instance.system }}</dd>
<dt style="margin-top: 5px;">
{% trans "Name" %}:
<a href="#" class="vm-details-home-edit-name-click"><i class="fa fa-pencil"></i></a>
{% if is_operator %}
<a href="#" class="vm-details-home-edit-name-click"><i class="fa fa-pencil"></i></a>
{% endif %}
</dt>
<dd>
<div class="vm-details-home-edit-name-click">
......@@ -18,8 +20,9 @@
<div class="input-group">
<input type="text" name="new_name" value="{{ instance.name }}" class="form-control input-sm"/>
<span class="input-group-btn">
<button type="submit" class="btn btn-success btn-sm vm-details-rename-submit">
<i class="fa fa-pencil"></i> {% trans "Rename" %}
<button type="submit" class="btn btn-success btn-sm vm-details-rename-submit
{% if not is_operator %}disabled{% endif %}" title="{% trans "Rename" %}">
<i class="fa fa-pencil"></i>
</button>
</span>
</div>
......@@ -28,7 +31,9 @@
</dd>
<dt style="margin-top: 5px;">
{% trans "Description" %}:
<a href="#" class="vm-details-home-edit-description-click"><i class="fa fa-pencil"></i></a>
{% if is_operator %}
<a href="#" class="vm-details-home-edit-description-click"><i class="fa fa-pencil"></i></a>
{% endif %}
</dt>
<dd>
{% csrf_token %}
......@@ -38,7 +43,8 @@
<div id="vm-details-home-description" class="js-hidden">
<form method="POST">
<textarea name="new_description" class="form-control">{{ instance.description }}</textarea>
<button type="submit" class="btn btn-xs btn-success vm-details-description-submit">
<button type="submit" class="btn btn-xs btn-success vm-details-description-submit
{% if not is_operator %}disabled{% endif %}">
<i class="fa fa-pencil"></i> {% trans "Update" %}
</button>
</form>
......@@ -58,9 +64,17 @@
</h4>
<dl>
<dt>{% trans "Suspended at:" %}</dt>
<dd><i class="fa fa-moon-o"></i> {{ instance.time_of_suspend|timeuntil }}</dd>
<dd>
<span title="{{ instance.time_of_suspend }}">
<i class="fa fa-moon-o"></i> {{ instance.time_of_suspend|timeuntil }}
</span>
</dd>
<dt>{% trans "Destroyed at:" %}</dt>
<dd><i class="fa fa-times"></i> {{ instance.time_of_delete|timeuntil }}</dd>
<dd>
<span title="{{ instance.time_of_delete }}">
<i class="fa fa-times"></i> {{ instance.time_of_delete|timeuntil }}
</span>
</dd>
</dl>
<div style="font-weight: bold;">{% trans "Tags" %}</div>
......@@ -70,11 +84,13 @@
{% for t in instance.tags.all %}
<div class="label label-primary label-tag" style="display: inline-block">
{{ t }}
<a href="#" class="vm-details-remove-tag"><i class="fa fa-times"></i></a>
{% if is_operator %}
<a href="#" class="vm-details-remove-tag"><i class="fa fa-times"></i></a>
{% endif %}
</div>
{% endfor %}
{% else %}
<small>{% trans "No tag added!" %}</small>
<small>{% trans "No tag added." %}</small>
{% endif %}
</div>
<form action="" method="POST">
......@@ -85,11 +101,26 @@
<i class="fa fa-question"></i>
</div>-->
<div class="input-group-btn">
<input type="submit" class="btn btn-default btn-sm input-tags" value="{% trans "Add tag" %}"/>
<input type="submit" class="btn btn-default btn-sm input-tags
{% if not is_operator %}disabled{% endif %}" value="{% trans "Add tag" %}"/>
</div>
</div>
</form>
</div><!-- id:vm-details-tags -->
{% if request.user.is_superuser %}
<dl>
<dt>{% trans "Node" %}:</dt>
<dd>
{% if instance.node %}
<a href="{{ instance.node.get_absolute_url }}">
{{ instance.node.name }}
</a>
{% else %}
-
{% endif %}