diff --git a/.gitignore b/.gitignore
index 75a1da9..6f4d056 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,6 +23,7 @@ celerybeat-schedule
 .coverage
 *,cover
 coverage.xml
+.noseids
 
 # Gettext object file:
 *.mo
diff --git a/circle/acl/models.py b/circle/acl/models.py
index a47067a..2bce06d 100644
--- a/circle/acl/models.py
+++ b/circle/acl/models.py
@@ -71,6 +71,17 @@ class AclBase(Model):
     """Define permission levels for Users/Groups per object."""
     object_level_set = GenericRelation(ObjectLevel)
 
+    def clone_acl(self, other):
+        """Clone full ACL from other object."""
+        assert self.id != other.id or type(self) != type(other)
+        self.object_level_set.clear()
+        for i in other.object_level_set.all():
+            ol = self.object_level_set.create(level=i.level)
+            for j in i.users.all():
+                ol.users.add(j)
+            for j in i.groups.all():
+                ol.groups.add(j)
+
     @classmethod
     def get_level_object(cls, level):
 
diff --git a/circle/circle/settings/local.py b/circle/circle/settings/local.py
index 4336c71..1d5cb37 100644
--- a/circle/circle/settings/local.py
+++ b/circle/circle/settings/local.py
@@ -64,6 +64,13 @@ CACHES = {
 ########## END CACHE CONFIGURATION
 
 
+########## ROSETTA CONFIGURATION
+INSTALLED_APPS += (
+    'rosetta',
+)
+########## END ROSETTA CONFIGURATION
+
+
 ########## TOOLBAR CONFIGURATION
 # https://github.com/django-debug-toolbar/django-debug-toolbar#installation
 if get_env_variable('DJANGO_TOOLBAR', 'FALSE') == 'TRUE':
diff --git a/circle/circle/urls.py b/circle/circle/urls.py
index b049a1a..2671c23 100644
--- a/circle/circle/urls.py
+++ b/circle/circle/urls.py
@@ -18,9 +18,11 @@
 from django.conf.urls import patterns, include, url
 from django.views.generic import TemplateView
 
+from django.conf import settings
 from django.contrib import admin
-from django.shortcuts import redirect
 from django.core.urlresolvers import reverse
+from django.shortcuts import redirect
+
 
 from circle.settings.base import get_env_variable
 from dashboard.views import circle_login, HelpView
@@ -72,6 +74,13 @@ urlpatterns = patterns(
 )
 
 
+if 'rosetta' in settings.INSTALLED_APPS:
+    urlpatterns += patterns(
+        '',
+        url(r'^rosetta/', include('rosetta.urls')),
+    )
+
+
 if get_env_variable('DJANGO_SAML', 'FALSE') == 'TRUE':
     urlpatterns += patterns(
         '',
diff --git a/circle/common/models.py b/circle/common/models.py
index 3b80758..c5c4a7b 100644
--- a/circle/common/models.py
+++ b/circle/common/models.py
@@ -32,6 +32,7 @@ from django.core.serializers.json import DjangoJSONEncoder
 from django.db.models import (
     CharField, DateTimeField, ForeignKey, NullBooleanField
 )
+from django.template import defaultfilters
 from django.utils import timezone
 from django.utils.encoding import force_text
 from django.utils.functional import Promise
@@ -48,7 +49,15 @@ class WorkerNotFound(Exception):
     pass
 
 
+def get_error_msg(exception):
+    try:
+        return unicode(exception)
+    except UnicodeDecodeError:
+        return unicode(str(exception), encoding='utf-8', errors='replace')
+
+
 def activitycontextimpl(act, on_abort=None, on_commit=None):
+    result = None
     try:
         try:
             yield act
@@ -61,7 +70,7 @@ def activitycontextimpl(act, on_abort=None, on_commit=None):
             result = create_readable(
                 ugettext_noop("Failure."),
                 ugettext_noop("Unhandled exception: %(error)s"),
-                error=unicode(e))
+                error=get_error_msg(e))
             raise
     except:
         logger.exception("Failed activity %s" % unicode(act))
@@ -428,6 +437,14 @@ class HumanReadableObject(object):
             admin_text_template = admin_text_template._proxy____args[0]
         self.user_text_template = user_text_template
         self.admin_text_template = admin_text_template
+        for k, v in params.iteritems():
+            try:
+                v = timezone.datetime.strptime(
+                    v, "%Y-%m-%dT%H:%M:%S.%fZ").replace(tzinfo=timezone.UTC())
+            except (ValueError, TypeError):  # Mock raises TypeError
+                pass
+            if isinstance(v, timezone.datetime):
+                params[k] = defaultfilters.date(v, "DATETIME_FORMAT")
         self.params = params
 
     @classmethod
@@ -444,24 +461,27 @@ class HumanReadableObject(object):
     def from_dict(cls, d):
         return None if d is None else cls(**d)
 
-    def get_admin_text(self):
-        if self.admin_text_template == "":
+    def _get_parsed_text(self, key):
+        value = getattr(self, key)
+        if value == "":
             return ""
         try:
-            return _(self.admin_text_template) % self.params
+            return _(value) % self.params
+        except KeyError:
+            logger.exception("Can't render %s '%s' %% %s",
+                             key, value, unicode(self.params))
+            raise
+
+    def get_admin_text(self):
+        try:
+            return self._get_parsed_text("admin_text_template")
         except KeyError:
-            logger.exception("Can't render admin_text_template '%s' %% %s",
-                             self.admin_text_template, unicode(self.params))
             return self.get_user_text()
 
     def get_user_text(self):
-        if self.user_text_template == "":
-            return ""
         try:
-            return _(self.user_text_template) % self.params
+            return self._get_parsed_text("user_text_template")
         except KeyError:
-            logger.exception("Can't render user_text_template '%s' %% %s",
-                             self.user_text_template, unicode(self.params))
             return self.user_text_template
 
     def get_text(self, user):
diff --git a/circle/common/tests/test_models.py b/circle/common/tests/test_models.py
index 0e71aa0..8bdba74 100644
--- a/circle/common/tests/test_models.py
+++ b/circle/common/tests/test_models.py
@@ -22,6 +22,7 @@ from mock import MagicMock
 
 from .models import TestClass
 from ..models import HumanSortField
+from ..models import activitycontextimpl
 
 
 class MethodCacheTestCase(TestCase):
@@ -80,3 +81,22 @@ class TestHumanSortField(TestCase):
 
             test_result = HumanSortField.get_normalized_value(obj, val)
             self.assertEquals(test_result, result)
+
+
+class ActivityContextTestCase(TestCase):
+    class MyException(Exception):
+        pass
+
+    def test_unicode(self):
+        act = MagicMock()
+        gen = activitycontextimpl(act)
+        gen.next()
+        with self.assertRaises(self.MyException):
+            gen.throw(self.MyException(u'test\xe1'))
+
+    def test_str(self):
+        act = MagicMock()
+        gen = activitycontextimpl(act)
+        gen.next()
+        with self.assertRaises(self.MyException):
+            gen.throw(self.MyException('test\xbe'))
diff --git a/circle/dashboard/forms.py b/circle/dashboard/forms.py
index b2a8f4a..ad1b0ec 100644
--- a/circle/dashboard/forms.py
+++ b/circle/dashboard/forms.py
@@ -40,7 +40,7 @@ from django.contrib.auth.forms import UserCreationForm as OrgUserCreationForm
 from django.forms.widgets import TextInput, HiddenInput
 from django.template import Context
 from django.template.loader import render_to_string
-from django.utils.html import escape
+from django.utils.html import escape, format_html
 from django.utils.translation import ugettext_lazy as _
 from sizefield.widgets import FileSizeWidget
 from django.core.urlresolvers import reverse_lazy
@@ -71,9 +71,7 @@ priority_choices = (
 )
 
 
-class VmSaveForm(forms.Form):
-    name = forms.CharField(max_length=100, label=_('Name'),
-                           help_text=_('Human readable name of template.'))
+class NoFormTagMixin(object):
 
     @property
     def helper(self):
@@ -81,11 +79,26 @@ class VmSaveForm(forms.Form):
         helper.form_tag = False
         return helper
 
+
+class OperationForm(NoFormTagMixin, forms.Form):
+    pass
+
+
+class VmSaveForm(OperationForm):
+    name = forms.CharField(max_length=100, label=_('Name'),
+                           help_text=_('Human readable name of template.'))
+
     def __init__(self, *args, **kwargs):
         default = kwargs.pop('default', None)
+        clone = kwargs.pop('clone', False)
         super(VmSaveForm, self).__init__(*args, **kwargs)
         if default:
             self.fields['name'].initial = default
+        if clone:
+            self.fields.insert(2, "clone", forms.BooleanField(
+                required=False, label=_("Clone template permissions"),
+                help_text=_("Clone the access list of parent template. Useful "
+                            "for updating a template.")))
 
 
 class VmCustomizeForm(forms.Form):
@@ -193,7 +206,7 @@ class VmCustomizeForm(forms.Form):
                         del self.cleaned_data[name]
 
 
-class GroupCreateForm(forms.ModelForm):
+class GroupCreateForm(NoFormTagMixin, forms.ModelForm):
 
     description = forms.CharField(label=_("Description"), required=False,
                                   widget=forms.Textarea(attrs={'rows': 3}))
@@ -232,9 +245,8 @@ class GroupCreateForm(forms.ModelForm):
 
     @property
     def helper(self):
-        helper = FormHelper(self)
+        helper = super(GroupCreateForm, self).helper
         helper.add_input(Submit("submit", _("Create")))
-        helper.form_tag = False
         return helper
 
     class Meta:
@@ -242,7 +254,7 @@ class GroupCreateForm(forms.ModelForm):
         fields = ('name', )
 
 
-class GroupProfileUpdateForm(forms.ModelForm):
+class GroupProfileUpdateForm(NoFormTagMixin, forms.ModelForm):
 
     def __init__(self, *args, **kwargs):
         new_groups = kwargs.pop('new_groups', None)
@@ -261,9 +273,8 @@ class GroupProfileUpdateForm(forms.ModelForm):
 
     @property
     def helper(self):
-        helper = FormHelper(self)
+        helper = super(GroupProfileUpdateForm, self).helper
         helper.add_input(Submit("submit", _("Save")))
-        helper.form_tag = False
         return helper
 
     def save(self, commit=True):
@@ -278,17 +289,16 @@ class GroupProfileUpdateForm(forms.ModelForm):
         fields = ('description', 'org_id')
 
 
-class HostForm(forms.ModelForm):
+class HostForm(NoFormTagMixin, forms.ModelForm):
 
     def setowner(self, user):
         self.instance.owner = user
 
-    def __init__(self, *args, **kwargs):
-        super(HostForm, self).__init__(*args, **kwargs)
-        self.helper = FormHelper(self)
-        self.helper.form_show_labels = False
-        self.helper.form_tag = False
-        self.helper.layout = Layout(
+    @property
+    def helper(self):
+        helper = super(HostForm, self).helper
+        helper.form_show_labels = False
+        helper.layout = Layout(
             Div(
                 Div(  # host
                     Div(
@@ -345,6 +355,7 @@ class HostForm(forms.ModelForm):
                 ),
             ),
         )
+        return helper
 
     class Meta:
         model = Host
@@ -725,7 +736,7 @@ class LeaseForm(forms.ModelForm):
         model = Lease
 
 
-class VmRenewForm(forms.Form):
+class VmRenewForm(OperationForm):
 
     force = forms.BooleanField(required=False, label=_(
         "Set expiration times even if they are shorter than "
@@ -745,16 +756,15 @@ class VmRenewForm(forms.Form):
             self.fields['lease'].widget = HiddenInput()
             self.fields['save'].widget = HiddenInput()
 
-    @property
-    def helper(self):
-        helper = FormHelper(self)
-        helper.form_tag = False
-        return helper
-
 
 class VmMigrateForm(forms.Form):
     live_migration = forms.BooleanField(
-        required=False, initial=True, label=_("live migration"))
+        required=False, initial=True, label=_("Live migration"),
+        help_text=_(
+            "Live migration is a way of moving virtual machines between "
+            "hosts with a service interruption of at most some seconds. "
+            "Please note that it can take very long and cause "
+            "much network traffic in case of busy machines."))
 
     def __init__(self, *args, **kwargs):
         choices = kwargs.pop('choices')
@@ -766,7 +776,7 @@ class VmMigrateForm(forms.Form):
             widget=forms.RadioSelect(), label=_("Node")))
 
 
-class VmStateChangeForm(forms.Form):
+class VmStateChangeForm(OperationForm):
 
     interrupt = forms.BooleanField(required=False, label=_(
         "Forcibly interrupt all running activities."),
@@ -785,25 +795,13 @@ class VmStateChangeForm(forms.Form):
             self.fields['interrupt'].widget = HiddenInput()
         self.fields['new_state'].initial = status
 
-    @property
-    def helper(self):
-        helper = FormHelper(self)
-        helper.form_tag = False
-        return helper
-
 
-class RedeployForm(forms.Form):
+class RedeployForm(OperationForm):
     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):
+class VmCreateDiskForm(OperationForm):
     name = forms.CharField(max_length=100, label=_("Name"))
     size = forms.CharField(
         widget=FileSizeWidget, initial=(10 << 30), label=_('Size'),
@@ -823,14 +821,8 @@ class VmCreateDiskForm(forms.Form):
                                           " GB or MB!"))
         return size_in_bytes
 
-    @property
-    def helper(self):
-        helper = FormHelper(self)
-        helper.form_tag = False
-        return helper
-
 
-class VmDiskResizeForm(forms.Form):
+class VmDiskResizeForm(OperationForm):
     size = forms.CharField(
         widget=FileSizeWidget, initial=(10 << 30), label=_('Size'),
         help_text=_('Size to resize the disk in bytes or with units '
@@ -863,8 +855,7 @@ class VmDiskResizeForm(forms.Form):
 
     @property
     def helper(self):
-        helper = FormHelper(self)
-        helper.form_tag = False
+        helper = super(VmDiskResizeForm, self).helper
         if self.disk:
             helper.layout = Layout(
                 HTML(_("<label>Disk:</label> %s") % escape(self.disk)),
@@ -872,7 +863,7 @@ class VmDiskResizeForm(forms.Form):
         return helper
 
 
-class VmDiskRemoveForm(forms.Form):
+class VmDiskRemoveForm(OperationForm):
     def __init__(self, *args, **kwargs):
         choices = kwargs.pop('choices')
         self.disk = kwargs.pop('default')
@@ -887,8 +878,7 @@ class VmDiskRemoveForm(forms.Form):
 
     @property
     def helper(self):
-        helper = FormHelper(self)
-        helper.form_tag = False
+        helper = super(VmDiskRemoveForm, self).helper
         if self.disk:
             helper.layout = Layout(
                 AnyTag(
@@ -901,16 +891,10 @@ class VmDiskRemoveForm(forms.Form):
         return helper
 
 
-class VmDownloadDiskForm(forms.Form):
+class VmDownloadDiskForm(OperationForm):
     name = forms.CharField(max_length=100, label=_("Name"), required=False)
     url = forms.CharField(label=_('URL'), validators=[URLValidator(), ])
 
-    @property
-    def helper(self):
-        helper = FormHelper(self)
-        helper.form_tag = False
-        return helper
-
     def clean(self):
         cleaned_data = super(VmDownloadDiskForm, self).clean()
         if not cleaned_data['name']:
@@ -924,7 +908,7 @@ class VmDownloadDiskForm(forms.Form):
         return cleaned_data
 
 
-class VmAddInterfaceForm(forms.Form):
+class VmAddInterfaceForm(OperationForm):
     def __init__(self, *args, **kwargs):
         choices = kwargs.pop('choices')
         super(VmAddInterfaceForm, self).__init__(*args, **kwargs)
@@ -936,10 +920,68 @@ class VmAddInterfaceForm(forms.Form):
             field.empty_label = _('No more networks.')
         self.fields['vlan'] = field
 
+
+class VmDeployForm(OperationForm):
+
+    def __init__(self, *args, **kwargs):
+        choices = kwargs.pop('choices', None)
+
+        super(VmDeployForm, self).__init__(*args, **kwargs)
+
+        if choices is not None:
+            self.fields.insert(0, 'node', forms.ModelChoiceField(
+                queryset=choices, required=False, label=_('Node'), help_text=_(
+                    "Deploy virtual machine to this node "
+                    "(blank allows scheduling automatically).")))
+
+
+class VmPortRemoveForm(OperationForm):
+    def __init__(self, *args, **kwargs):
+        choices = kwargs.pop('choices')
+        self.rule = kwargs.pop('default')
+
+        super(VmPortRemoveForm, self).__init__(*args, **kwargs)
+
+        self.fields.insert(0, 'rule', forms.ModelChoiceField(
+            queryset=choices, initial=self.rule, required=True,
+            empty_label=None, label=_('Port')))
+        if self.rule:
+            self.fields['rule'].widget = HiddenInput()
+
+
+class VmPortAddForm(OperationForm):
+    port = forms.IntegerField(required=True, label=_('Port'),
+                              min_value=1, max_value=65535)
+    proto = forms.ChoiceField((('tcp', 'tcp'), ('udp', 'udp')),
+                              required=True, label=_('Protocol'))
+
+    def __init__(self, *args, **kwargs):
+        choices = kwargs.pop('choices')
+        self.host = kwargs.pop('default')
+
+        super(VmPortAddForm, self).__init__(*args, **kwargs)
+
+        self.fields.insert(0, 'host', forms.ModelChoiceField(
+            queryset=choices, initial=self.host, required=True,
+            empty_label=None, label=_('Host')))
+        if self.host:
+            self.fields['host'].widget = HiddenInput()
+
     @property
     def helper(self):
-        helper = FormHelper(self)
-        helper.form_tag = False
+        helper = super(VmPortAddForm, self).helper
+        if self.host:
+            helper.layout = Layout(
+                AnyTag(
+                    "div",
+                    HTML(format_html(
+                        _("<label>Host:</label> {0}"), self.host)),
+                    css_class="form-group",
+                ),
+                Field("host"),
+                Field("proto"),
+                Field("port"),
+            )
         return helper
 
 
diff --git a/circle/dashboard/static/dashboard/bootstrap-tour.min.css b/circle/dashboard/static/dashboard/bootstrap-tour.min.css
deleted file mode 100644
index 746e4c7..0000000
--- a/circle/dashboard/static/dashboard/bootstrap-tour.min.css
+++ /dev/null
@@ -1,19 +0,0 @@
-/* ===========================================================
-# bootstrap-tour - v0.9.1
-# http://bootstraptour.com
-# ==============================================================
-# Copyright 2012-2013 Ulrich Sossou
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-*/
-.tour-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1100;background-color:#000;opacity:.8}.tour-step-backdrop{position:relative;z-index:1101;background:inherit}.tour-step-background{position:absolute;z-index:1100;background:inherit;border-radius:6px}.popover[class*=tour-]{z-index:1100}.popover[class*=tour-] .popover-navigation{padding:9px 14px}.popover[class*=tour-] .popover-navigation [data-role=end]{float:right}.popover[class*=tour-] .popover-navigation [data-role=prev],.popover[class*=tour-] .popover-navigation [data-role=next],.popover[class*=tour-] .popover-navigation [data-role=end]{cursor:pointer}.popover[class*=tour-] .popover-navigation [data-role=prev].disabled,.popover[class*=tour-] .popover-navigation [data-role=next].disabled,.popover[class*=tour-] .popover-navigation [data-role=end].disabled{cursor:default}.popover[class*=tour-].orphan{position:fixed;margin-top:0}.popover[class*=tour-].orphan .arrow{display:none}
\ No newline at end of file
diff --git a/circle/dashboard/static/dashboard/bootstrap-tour.min.js b/circle/dashboard/static/dashboard/bootstrap-tour.min.js
deleted file mode 100644
index dbf5f12..0000000
--- a/circle/dashboard/static/dashboard/bootstrap-tour.min.js
+++ /dev/null
@@ -1,19 +0,0 @@
-/* ===========================================================
-# bootstrap-tour - v0.9.1
-# http://bootstraptour.com
-# ==============================================================
-# Copyright 2012-2013 Ulrich Sossou
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-*/
-!function(a,b){var c,d;return d=b.document,c=function(){function c(c){this._options=a.extend({name:"tour",steps:[],container:"body",keyboard:!0,storage:b.localStorage,debug:!1,backdrop:!1,redirect:!0,orphan:!1,duration:!1,basePath:"",template:"<div class='popover'> <div class='arrow'></div> <h3 class='popover-title'></h3> <div class='popover-content'></div> <div class='popover-navigation'> <div class='btn-group'> <button class='btn btn-sm btn-default' data-role='prev'>&laquo; Prev</button> <button class='btn btn-sm btn-default' data-role='next'>Next &raquo;</button> <button class='btn btn-sm btn-default' data-role='pause-resume' data-pause-text='Pause' data-resume-text='Resume'>Pause</button> </div> <button class='btn btn-sm btn-default' data-role='end'>End tour</button> </div> </div>",afterSetState:function(){},afterGetState:function(){},afterRemoveState:function(){},onStart:function(){},onEnd:function(){},onShow:function(){},onShown:function(){},onHide:function(){},onHidden:function(){},onNext:function(){},onPrev:function(){},onPause:function(){},onResume:function(){}},c),this._force=!1,this._inited=!1,this.backdrop={overlay:null,$element:null,$background:null,backgroundShown:!1,overlayElementShown:!1}}return c.prototype.addSteps=function(a){var b,c,d;for(c=0,d=a.length;d>c;c++)b=a[c],this.addStep(b);return this},c.prototype.addStep=function(a){return this._options.steps.push(a),this},c.prototype.getStep=function(b){return null!=this._options.steps[b]?a.extend({id:"step-"+b,path:"",placement:"right",title:"",content:"<p></p>",next:b===this._options.steps.length-1?-1:b+1,prev:b-1,animation:!0,container:this._options.container,backdrop:this._options.backdrop,redirect:this._options.redirect,orphan:this._options.orphan,duration:this._options.duration,template:this._options.template,onShow:this._options.onShow,onShown:this._options.onShown,onHide:this._options.onHide,onHidden:this._options.onHidden,onNext:this._options.onNext,onPrev:this._options.onPrev,onPause:this._options.onPause,onResume:this._options.onResume},this._options.steps[b]):void 0},c.prototype.init=function(a){return this._force=a,this.ended()?(this._debug("Tour ended, init prevented."),this):(this.setCurrentStep(),this._initMouseNavigation(),this._initKeyboardNavigation(),this._onResize(function(a){return function(){return a.showStep(a._current)}}(this)),null!==this._current&&this.showStep(this._current),this._inited=!0,this)},c.prototype.start=function(a){var b;return null==a&&(a=!1),this._inited||this.init(a),null===this._current&&(b=this._makePromise(null!=this._options.onStart?this._options.onStart(this):void 0),this._callOnPromiseDone(b,this.showStep,0)),this},c.prototype.next=function(){var a;return a=this.hideStep(this._current),this._callOnPromiseDone(a,this._showNextStep)},c.prototype.prev=function(){var a;return a=this.hideStep(this._current),this._callOnPromiseDone(a,this._showPrevStep)},c.prototype.goTo=function(a){var b;return b=this.hideStep(this._current),this._callOnPromiseDone(b,this.showStep,a)},c.prototype.end=function(){var c,e;return c=function(c){return function(){return a(d).off("click.tour-"+c._options.name),a(d).off("keyup.tour-"+c._options.name),a(b).off("resize.tour-"+c._options.name),c._setState("end","yes"),c._inited=!1,c._force=!1,c._clearTimer(),null!=c._options.onEnd?c._options.onEnd(c):void 0}}(this),e=this.hideStep(this._current),this._callOnPromiseDone(e,c)},c.prototype.ended=function(){return!this._force&&!!this._getState("end")},c.prototype.restart=function(){return this._removeState("current_step"),this._removeState("end"),this.setCurrentStep(0),this.start()},c.prototype.pause=function(){var a;return a=this.getStep(this._current),a&&a.duration?(this._paused=!0,this._duration-=(new Date).getTime()-this._start,b.clearTimeout(this._timer),this._debug("Paused/Stopped step "+(this._current+1)+" timer ("+this._duration+" remaining)."),null!=a.onPause?a.onPause(this,this._duration):void 0):this},c.prototype.resume=function(){var a;return a=this.getStep(this._current),a&&a.duration?(this._paused=!1,this._start=(new Date).getTime(),this._duration=this._duration||a.duration,this._timer=b.setTimeout(function(a){return function(){return a._isLast()?a.next():a.end()}}(this),this._duration),this._debug("Started step "+(this._current+1)+" timer with duration "+this._duration),null!=a.onResume&&this._duration!==a.duration?a.onResume(this,this._duration):void 0):this},c.prototype.hideStep=function(b){var c,d,e;return(e=this.getStep(b))?(this._clearTimer(),d=this._makePromise(null!=e.onHide?e.onHide(this,b):void 0),c=function(c){return function(){var d;return d=a(e.element),d.data("bs.popover")||d.data("popover")||(d=a("body")),d.popover("destroy").removeClass("tour-"+c._options.name+"-element tour-"+c._options.name+"-"+b+"-element"),e.reflex&&d.css("cursor","").off("click.tour-"+c._options.name),e.backdrop&&c._hideBackdrop(),null!=e.onHidden?e.onHidden(c):void 0}}(this),this._callOnPromiseDone(d,c),d):void 0},c.prototype.showStep=function(a){var b,c,e,f;return this.ended()?(this._debug("Tour ended, showStep prevented."),this):(f=this.getStep(a))?(e=a<this._current,b=this._makePromise(null!=f.onShow?f.onShow(this,a):void 0),c=function(b){return function(){var c,g;if(b.setCurrentStep(a),g=function(){switch({}.toString.call(f.path)){case"[object Function]":return f.path();case"[object String]":return this._options.basePath+f.path;default:return f.path}}.call(b),c=[d.location.pathname,d.location.hash].join(""),b._isRedirect(g,c))return void b._redirect(f,g);if(b._isOrphan(f)){if(!f.orphan)return b._debug("Skip the orphan step "+(b._current+1)+". Orphan option is false and the element doesn't exist or is hidden."),void(e?b._showPrevStep():b._showNextStep());b._debug("Show the orphan step "+(b._current+1)+". Orphans option is true.")}return f.backdrop&&b._showBackdrop(b._isOrphan(f)?void 0:f.element),b._scrollIntoView(f.element,function(){return null!=f.element&&f.backdrop&&b._showOverlayElement(f.element),b._showPopover(f,a),null!=f.onShown&&f.onShown(b),b._debug("Step "+(b._current+1)+" of "+b._options.steps.length)}),f.duration?b.resume():void 0}}(this),this._callOnPromiseDone(b,c),b):void 0},c.prototype.getCurrentStep=function(){return this._current},c.prototype.setCurrentStep=function(a){return null!=a?(this._current=a,this._setState("current_step",a)):(this._current=this._getState("current_step"),this._current=null===this._current?null:parseInt(this._current,10)),this},c.prototype._setState=function(a,b){var c,d;if(this._options.storage){d=""+this._options.name+"_"+a;try{this._options.storage.setItem(d,b)}catch(e){c=e,c.code===DOMException.QUOTA_EXCEEDED_ERR&&this.debug("LocalStorage quota exceeded. State storage failed.")}return this._options.afterSetState(d,b)}return null==this._state&&(this._state={}),this._state[a]=b},c.prototype._removeState=function(a){var b;return this._options.storage?(b=""+this._options.name+"_"+a,this._options.storage.removeItem(b),this._options.afterRemoveState(b)):null!=this._state?delete this._state[a]:void 0},c.prototype._getState=function(a){var b,c;return this._options.storage?(b=""+this._options.name+"_"+a,c=this._options.storage.getItem(b)):null!=this._state&&(c=this._state[a]),(void 0===c||"null"===c)&&(c=null),this._options.afterGetState(a,c),c},c.prototype._showNextStep=function(){var a,b,c;return c=this.getStep(this._current),b=function(a){return function(){return a.showStep(c.next)}}(this),a=this._makePromise(null!=c.onNext?c.onNext(this):void 0),this._callOnPromiseDone(a,b)},c.prototype._showPrevStep=function(){var a,b,c;return c=this.getStep(this._current),b=function(a){return function(){return a.showStep(c.prev)}}(this),a=this._makePromise(null!=c.onPrev?c.onPrev(this):void 0),this._callOnPromiseDone(a,b)},c.prototype._debug=function(a){return this._options.debug?b.console.log("Bootstrap Tour '"+this._options.name+"' | "+a):void 0},c.prototype._isRedirect=function(a,b){return null!=a&&""!==a&&("[object RegExp]"===toString.call(a)&&!a.test(b)||"[object String]"===toString.call(a)&&a.replace(/\?.*$/,"").replace(/\/?$/,"")!==b.replace(/\/?$/,""))},c.prototype._redirect=function(b,c){return a.isFunction(b.redirect)?b.redirect.call(this,c):b.redirect===!0?(this._debug("Redirect to "+c),d.location.href=c):void 0},c.prototype._isOrphan=function(b){return null==b.element||!a(b.element).length||a(b.element).is(":hidden")&&"http://www.w3.org/2000/svg"!==a(b.element)[0].namespaceURI},c.prototype._isLast=function(){return this._current<this._options.steps.length-1},c.prototype._showPopover=function(b,c){var d,e,f,g,h,i;return i=a.extend({},this._options),f=a(a.isFunction(b.template)?b.template(c,b):b.template),e=f.find(".popover-navigation"),h=this._isOrphan(b),h&&(b.element="body",b.placement="top",f=f.addClass("orphan")),d=a(b.element),f.addClass("tour-"+this._options.name+" tour-"+this._options.name+"-"+c),d.addClass("tour-"+this._options.name+"-element tour-"+this._options.name+"-"+c+"-element"),b.options&&a.extend(i,b.options),b.reflex&&d.css("cursor","pointer").on("click.tour-"+this._options.name,function(a){return function(){return a._isLast()?a.next():a.end()}}(this)),b.prev<0&&e.find("[data-role='prev']").addClass("disabled"),b.next<0&&e.find("[data-role='next']").addClass("disabled"),b.duration||e.find("[data-role='pause-resume']").remove(),b.template=f.clone().wrap("<div>").parent().html(),d.popover({placement:b.placement,trigger:"manual",title:b.title,content:b.content,html:!0,animation:b.animation,container:b.container,template:b.template,selector:b.element}).popover("show"),g=d.data("bs.popover")?d.data("bs.popover").tip():d.data("popover").tip(),g.attr("id",b.id),this._reposition(g,b),h?this._center(g):void 0},c.prototype._reposition=function(b,c){var e,f,g,h,i,j,k;if(h=b[0].offsetWidth,f=b[0].offsetHeight,k=b.offset(),i=k.left,j=k.top,e=a(d).outerHeight()-k.top-b.outerHeight(),0>e&&(k.top=k.top+e),g=a("html").outerWidth()-k.left-b.outerWidth(),0>g&&(k.left=k.left+g),k.top<0&&(k.top=0),k.left<0&&(k.left=0),b.offset(k),"bottom"===c.placement||"top"===c.placement){if(i!==k.left)return this._replaceArrow(b,2*(k.left-i),h,"left")}else if(j!==k.top)return this._replaceArrow(b,2*(k.top-j),f,"top")},c.prototype._center=function(c){return c.css("top",a(b).outerHeight()/2-c.outerHeight()/2)},c.prototype._replaceArrow=function(a,b,c,d){return a.find(".arrow").css(d,b?50*(1-b/c)+"%":"")},c.prototype._scrollIntoView=function(c,d){var e,f,g,h,i,j;return e=a(c),e.length?(f=a(b),h=e.offset().top,j=f.height(),i=Math.max(0,h-j/2),this._debug("Scroll into view. ScrollTop: "+i+". Element offset: "+h+". Window height: "+j+"."),g=0,a("body,html").stop(!0,!0).animate({scrollTop:Math.ceil(i)},function(a){return function(){return 2===++g?(d(),a._debug("Scroll into view. Animation end element offset: "+e.offset().top+". Window height: "+f.height()+".")):void 0}}(this))):d()},c.prototype._onResize=function(c,d){return a(b).on("resize.tour-"+this._options.name,function(){return clearTimeout(d),d=setTimeout(c,100)})},c.prototype._initMouseNavigation=function(){var b;return b=this,a(d).off("click.tour-"+this._options.name,".popover.tour-"+this._options.name+" *[data-role='prev']:not(.disabled)").off("click.tour-"+this._options.name,".popover.tour-"+this._options.name+" *[data-role='next']:not(.disabled)").off("click.tour-"+this._options.name,".popover.tour-"+this._options.name+" *[data-role='end']").off("click.tour-"+this._options.name,".popover.tour-"+this._options.name+" *[data-role='pause-resume']").on("click.tour-"+this._options.name,".popover.tour-"+this._options.name+" *[data-role='next']:not(.disabled)",function(a){return function(b){return b.preventDefault(),a.next()}}(this)).on("click.tour-"+this._options.name,".popover.tour-"+this._options.name+" *[data-role='prev']:not(.disabled)",function(a){return function(b){return b.preventDefault(),a.prev()}}(this)).on("click.tour-"+this._options.name,".popover.tour-"+this._options.name+" *[data-role='end']",function(a){return function(b){return b.preventDefault(),a.end()}}(this)).on("click.tour-"+this._options.name,".popover.tour-"+this._options.name+" *[data-role='pause-resume']",function(c){var d;return c.preventDefault(),d=a(this),d.text(d.data(b._paused?"pause-text":"resume-text")),b._paused?b.resume():b.pause()})},c.prototype._initKeyboardNavigation=function(){return this._options.keyboard?a(d).on("keyup.tour-"+this._options.name,function(a){return function(b){if(b.which)switch(b.which){case 39:return b.preventDefault(),a._isLast()?a.next():a.end();case 37:if(b.preventDefault(),a._current>0)return a.prev();break;case 27:return b.preventDefault(),a.end()}}}(this)):void 0},c.prototype._makePromise=function(b){return b&&a.isFunction(b.then)?b:null},c.prototype._callOnPromiseDone=function(a,b,c){return a?a.then(function(a){return function(){return b.call(a,c)}}(this)):b.call(this,c)},c.prototype._showBackdrop=function(){return this.backdrop.backgroundShown?void 0:(this.backdrop=a("<div/>",{"class":"tour-backdrop"}),this.backdrop.backgroundShown=!0,a("body").append(this.backdrop))},c.prototype._hideBackdrop=function(){return this._hideOverlayElement(),this._hideBackground()},c.prototype._hideBackground=function(){return this.backdrop.remove(),this.backdrop.overlay=null,this.backdrop.backgroundShown=!1},c.prototype._showOverlayElement=function(b){var c,d,e;return d=a(b),d&&0!==d.length&&!this.backdrop.overlayElementShown?(this.backdrop.overlayElementShown=!0,c=a("<div/>"),e=d.offset(),e.top=e.top,e.left=e.left,c.width(d.innerWidth()).height(d.innerHeight()).addClass("tour-step-background").offset(e),d.addClass("tour-step-backdrop"),a("body").append(c),this.backdrop.$element=d,this.backdrop.$background=c):void 0},c.prototype._hideOverlayElement=function(){return this.backdrop.overlayElementShown?(this.backdrop.$element.removeClass("tour-step-backdrop"),this.backdrop.$background.remove(),this.backdrop.$element=null,this.backdrop.$background=null,this.backdrop.overlayElementShown=!1):void 0},c.prototype._clearTimer=function(){return b.clearTimeout(this._timer),this._timer=null,this._duration=null},c}(),b.Tour=c}(jQuery,window);
\ No newline at end of file
diff --git a/circle/dashboard/static/dashboard/dashboard.css b/circle/dashboard/static/dashboard/dashboard.css
index c0a12ed..1e00494 100644
--- a/circle/dashboard/static/dashboard/dashboard.css
+++ b/circle/dashboard/static/dashboard/dashboard.css
@@ -216,7 +216,7 @@ html {
 }
 
 #vm-list-rename-name, #node-list-rename-name, #group-list-rename-name {
-  max-width: 100px;
+  max-width: 150px;
 }
 
 .label-tag {
@@ -688,7 +688,7 @@ textarea[name="new_members"] {
 
 .dashboard-vm-details-connect-command {
   /* for mobile view */
-  margin-bottom: 20px;
+  padding-bottom: 20px;
 }
 
 #store-list-list {
@@ -961,6 +961,14 @@ textarea[name="new_members"] {
   cursor: pointer
 }
 
+#vm-details-resources-disk {
+  padding: 2px 5px 10px 5px;
+}
+
+#vm-details-start-template-tour {
+  margin-right: 5px;
+}
+
 #vm-activity-state {
   margin-bottom: 15px;
 }
@@ -974,6 +982,24 @@ textarea[name="new_members"] {
   color: orange;
 }
 
+.introjs-skipbutton {
+  color: #333;
+}
+
+.introjs-button:focus {
+  text-decoration: none;
+  color: #333;
+  outline: none;
+}
+
+.introjs-button:hover:not(.introjs-disabled) {
+  color: #428bca;
+}
+
+.introjs-tooltip {
+  min-width: 250px;
+}
+
 #vm-info-pane {
   margin-bottom: 20px;
 }
@@ -1016,3 +1042,12 @@ textarea[name="new_members"] {
 #vm-migrate-node-list li {
   cursor: pointer;
 }
+
+.group-list-table .actions,
+.group-list-table .admin,
+.group-list-table .number_of_users,
+.group-list-table .pk {
+  width: 1px;
+  white-space: nowrap;
+  text-align: center;
+}
diff --git a/circle/dashboard/static/dashboard/dashboard.js b/circle/dashboard/static/dashboard/dashboard.js
index 3f1d22d..cd7ccfa 100644
--- a/circle/dashboard/static/dashboard/dashboard.js
+++ b/circle/dashboard/static/dashboard/dashboard.js
@@ -50,6 +50,21 @@ $(function () {
     return false;
   });
 
+  $('.tx-tpl-ownership').click(function(e) {
+    $.ajax({
+      type: 'GET',
+      url: $('.tx-tpl-ownership').attr('href'),
+      success: function(data) {
+        $('body').append(data);
+        $('#confirmation-modal').modal('show');
+        $('#confirmation-modal').on('hidden.bs.modal', function() {
+          $('#confirmation-modal').remove();
+        });
+      }
+    });
+    return false;
+  });
+
   $('.template-choose').click(function(e) {
     $.ajax({
       type: 'GET',
@@ -117,7 +132,7 @@ $(function () {
   $('.js-hidden').hide();
 
   /* favourite star */
-  $("#dashboard-vm-list").on('click', '.dashboard-vm-favourite', function(e) {
+  $("#dashboard-vm-list, .page-header").on('click', '.dashboard-vm-favourite', function(e) {
     var star = $(this).children("i");
     var pk = $(this).data("vm");
     if(star.hasClass("fa-star-o")) {
@@ -428,7 +443,7 @@ function generateVmHTML(pk, name, host, icon, _status, fav, is_last) {
   return '<a href="/dashboard/vm/' + pk + '/" class="list-group-item' +
          (is_last ? ' list-group-item-last' : '') + '">' +      
         '<span class="index-vm-list-name">' + 
-          '<i class="fa ' + icon + '" title="' + _status + '"></i> ' + name +
+          '<i class="fa ' + icon + '" title="' + _status + '"></i> ' + safe_tags_replace(name) +
         '</span>' + 
         '<small class="text-muted"> ' + host + '</small>' +
         '<div class="pull-right dashboard-vm-favourite" data-vm="' + pk + '">' +  
@@ -441,14 +456,14 @@ function generateVmHTML(pk, name, host, icon, _status, fav, is_last) {
 
 function generateGroupHTML(url, name, is_last) {
   return '<a href="' + url + '" class="list-group-item real-link' + (is_last ? " list-group-item-last" : "") +'">'+
-         '<i class="fa fa-users"></i> '+ name +
+         '<i class="fa fa-users"></i> '+ safe_tags_replace(name) +
          '</a>';
 }
 
 function generateNodeHTML(name, icon, _status, url, is_last) {
   return '<a href="' + url + '" class="list-group-item real-link' + (is_last ? ' list-group-item-last' : '') + '">' +
         '<span class="index-node-list-name">' +
-        '<i class="fa ' + icon + '" title="' + _status + '"></i> ' + name +
+        '<i class="fa ' + icon + '" title="' + _status + '"></i> ' + safe_tags_replace(name) +
         '</span>' +
         '<div style="clear: both;"></div>' +
         '</a>';
@@ -456,7 +471,7 @@ function generateNodeHTML(name, icon, _status, url, is_last) {
 
 function generateNodeTagHTML(name, icon, _status, label , url) {
   return '<a href="' + url + '" class="label ' + label + '" >' +
-        '<i class="fa ' + icon + '" title="' + _status + '"></i> ' + name +
+        '<i class="fa ' + icon + '" title="' + _status + '"></i> ' + safe_tags_replace(name) +
         '</a> ';
 }
 
@@ -678,3 +693,18 @@ function getParameterByName(name) {
         results = regex.exec(location.search);
     return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
 }
+
+
+var tagsToReplace = {
+    '&': '&amp;',
+    '<': '&lt;',
+    '>': '&gt;'
+};
+
+function replaceTag(tag) {
+    return tagsToReplace[tag] || tag;
+}
+
+function safe_tags_replace(str) {
+    return str.replace(/[&<>]/g, replaceTag);
+}
diff --git a/circle/dashboard/static/dashboard/disk-list.js b/circle/dashboard/static/dashboard/disk-list.js
deleted file mode 100644
index 4796106..0000000
--- a/circle/dashboard/static/dashboard/disk-list.js
+++ /dev/null
@@ -1,23 +0,0 @@
-$(function() {
-  $(".disk-list-disk-percentage").each(function() {
-    var disk = $(this).data("disk-pk");
-    var element = $(this);
-    refreshDisk(disk, element);
-  });
-});
-
-function refreshDisk(disk, element) {
-    $.get("/dashboard/disk/" + disk + "/status/", function(result) {
-      if(result.percentage == null || result.failed == "True") {
-        location.reload(); 
-      } else {
-        var diff = result.percentage - parseInt(element.html());
-        var refresh = 5 - diff;
-        refresh = refresh < 1 ? 1 : (result.percentage == 0 ? 1 : refresh);
-        if(isNaN(refresh)) refresh = 2; // this should not happen
-
-        element.html(result.percentage);
-        setTimeout(function() {refreshDisk(disk, element)}, refresh * 1000);
-      }
-    });
-}
diff --git a/circle/dashboard/static/dashboard/group-details.js b/circle/dashboard/static/dashboard/group-details.js
index 11b696e..664619f 100644
--- a/circle/dashboard/static/dashboard/group-details.js
+++ b/circle/dashboard/static/dashboard/group-details.js
@@ -14,7 +14,7 @@
       data: {'new_name': name},
       headers: {"X-CSRFToken": getCookie('csrftoken')},
       success: function(data, textStatus, xhr) {
-        $("#group-details-h1-name").html(data['new_name']).show();
+        $("#group-details-h1-name").text(data['new_name']).show();
         $('#group-details-rename').hide();
         // addMessage(data['message'], "success");
       },
diff --git a/circle/dashboard/static/dashboard/group-list.js b/circle/dashboard/static/dashboard/group-list.js
index d5cb480..d5159e5 100644
--- a/circle/dashboard/static/dashboard/group-list.js
+++ b/circle/dashboard/static/dashboard/group-list.js
@@ -3,6 +3,7 @@ $(function() {
   $("#group-list-rename-button, .group-details-rename-button").click(function() {
     $("#group-list-column-name", $(this).closest("tr")).hide();
     $("#group-list-rename", $(this).closest("tr")).css('display', 'inline');
+    $("#group-list-rename").find("input").select();
   });
 
   /* rename ajax */
diff --git a/circle/dashboard/static/dashboard/introjs/intro.min.js b/circle/dashboard/static/dashboard/introjs/intro.min.js
new file mode 100644
index 0000000..e152d52
--- /dev/null
+++ b/circle/dashboard/static/dashboard/introjs/intro.min.js
@@ -0,0 +1,27 @@
+(function(p,f){"object"===typeof exports?f(exports):"function"===typeof define&&define.amd?define(["exports"],f):f(p)})(this,function(p){function f(a){this._targetElement=a;this._options={nextLabel:"Next &rarr;",prevLabel:"&larr; Back",skipLabel:"Skip",doneLabel:"Done",tooltipPosition:"bottom",tooltipClass:"",exitOnEsc:!0,exitOnOverlayClick:!0,showStepNumbers:!0,keyboardNavigation:!0,showButtons:!0,showBullets:!0,scrollToElement:!0,overlayOpacity:0.8}}function r(a){if(null==a||"object"!=typeof a||
+"undefined"!=typeof a.nodeType)return a;var b={},c;for(c in a)b[c]=r(a[c]);return b}function s(){this._direction="forward";"undefined"===typeof this._currentStep?this._currentStep=0:++this._currentStep;if(this._introItems.length<=this._currentStep)"function"===typeof this._introCompleteCallback&&this._introCompleteCallback.call(this),t.call(this,this._targetElement);else{var a=this._introItems[this._currentStep];"undefined"!==typeof this._introBeforeChangeCallback&&this._introBeforeChangeCallback.call(this,
+a.element);A.call(this,a)}}function x(){this._direction="backward";if(0===this._currentStep)return!1;var a=this._introItems[--this._currentStep];"undefined"!==typeof this._introBeforeChangeCallback&&this._introBeforeChangeCallback.call(this,a.element);A.call(this,a)}function t(a){var b=a.querySelector(".introjs-overlay");if(null!=b){b.style.opacity=0;setTimeout(function(){b.parentNode&&b.parentNode.removeChild(b)},500);(a=a.querySelector(".introjs-helperLayer"))&&a.parentNode.removeChild(a);(a=document.querySelector(".introjsFloatingElement"))&&
+a.parentNode.removeChild(a);if(a=document.querySelector(".introjs-showElement"))a.className=a.className.replace(/introjs-[a-zA-Z]+/g,"").replace(/^\s+|\s+$/g,"");if((a=document.querySelectorAll(".introjs-fixParent"))&&0<a.length)for(var c=a.length-1;0<=c;c--)a[c].className=a[c].className.replace(/introjs-fixParent/g,"").replace(/^\s+|\s+$/g,"");window.removeEventListener?window.removeEventListener("keydown",this._onKeyDown,!0):document.detachEvent&&document.detachEvent("onkeydown",this._onKeyDown);
+this._currentStep=void 0}}function B(a,b,c,d){var e="";b.style.top=null;b.style.right=null;b.style.bottom=null;b.style.left=null;b.style.marginLeft=null;b.style.marginTop=null;c.style.display="inherit";"undefined"!=typeof d&&null!=d&&(d.style.top=null,d.style.left=null);if(this._introItems[this._currentStep])switch(e=this._introItems[this._currentStep],e="string"===typeof e.tooltipClass?e.tooltipClass:this._options.tooltipClass,b.className=("introjs-tooltip "+e).replace(/^\s+|\s+$/g,""),currentTooltipPosition=
+this._introItems[this._currentStep].position,currentTooltipPosition){case "top":b.style.left="15px";b.style.top="-"+(h(b).height+10)+"px";c.className="introjs-arrow bottom";break;case "right":b.style.left=h(a).width+20+"px";c.className="introjs-arrow left";break;case "left":!0==this._options.showStepNumbers&&(b.style.top="15px");b.style.right=h(a).width+20+"px";c.className="introjs-arrow right";break;case "floating":c.style.display="none";a=h(b);b.style.left="50%";b.style.top="50%";b.style.marginLeft=
+"-"+a.width/2+"px";b.style.marginTop="-"+a.height/2+"px";"undefined"!=typeof d&&null!=d&&(d.style.left="-"+(a.width/2+18)+"px",d.style.top="-"+(a.height/2+18)+"px");break;case "bottom-right-aligned":c.className="introjs-arrow top-right";b.style.right="0px";b.style.bottom="-"+(h(b).height+10)+"px";break;case "bottom-middle-aligned":d=h(a);a=h(b);c.className="introjs-arrow top-middle";b.style.left=d.width/2-a.width/2+"px";b.style.bottom="-"+(a.height+10)+"px";break;default:b.style.bottom="-"+(h(b).height+
+10)+"px",c.className="introjs-arrow top"}}function v(a){if(a&&this._introItems[this._currentStep]){var b=this._introItems[this._currentStep],c=h(b.element),d=10;"floating"==b.position&&(d=0);a.setAttribute("style","width: "+(c.width+d)+"px; height:"+(c.height+d)+"px; top:"+(c.top-5)+"px;left: "+(c.left-5)+"px;")}}function A(a){var b;"undefined"!==typeof this._introChangeCallback&&this._introChangeCallback.call(this,a.element);var c=this,d=document.querySelector(".introjs-helperLayer");h(a.element);
+if(null!=d){var e=d.querySelector(".introjs-helperNumberLayer"),C=d.querySelector(".introjs-tooltiptext"),g=d.querySelector(".introjs-arrow"),y=d.querySelector(".introjs-tooltip"),k=d.querySelector(".introjs-skipbutton"),n=d.querySelector(".introjs-prevbutton"),l=d.querySelector(".introjs-nextbutton");y.style.opacity=0;if(null!=e&&(b=this._introItems[0<=a.step-2?a.step-2:0],null!=b&&"forward"==this._direction&&"floating"==b.position||"backward"==this._direction&&"floating"==a.position))e.style.opacity=
+0;v.call(c,d);var m=document.querySelectorAll(".introjs-fixParent");if(m&&0<m.length)for(b=m.length-1;0<=b;b--)m[b].className=m[b].className.replace(/introjs-fixParent/g,"").replace(/^\s+|\s+$/g,"");b=document.querySelector(".introjs-showElement");b.className=b.className.replace(/introjs-[a-zA-Z]+/g,"").replace(/^\s+|\s+$/g,"");c._lastShowElementTimer&&clearTimeout(c._lastShowElementTimer);c._lastShowElementTimer=setTimeout(function(){null!=e&&(e.innerHTML=a.step);C.innerHTML=a.intro;B.call(c,a.element,
+y,g,e);d.querySelector(".introjs-bullets li > a.active").className="";d.querySelector('.introjs-bullets li > a[data-stepnumber="'+a.step+'"]').className="active";y.style.opacity=1;e&&(e.style.opacity=1)},350)}else{var k=document.createElement("div"),m=document.createElement("div"),j=document.createElement("div"),n=document.createElement("div"),l=document.createElement("div"),f=document.createElement("div");k.className="introjs-helperLayer";v.call(c,k);this._targetElement.appendChild(k);m.className=
+"introjs-arrow";n.className="introjs-tooltiptext";n.innerHTML=a.intro;l.className="introjs-bullets";!1===this._options.showBullets&&(l.style.display="none");var p=document.createElement("ul");b=0;for(var u=this._introItems.length;b<u;b++){var r=document.createElement("li"),q=document.createElement("a");q.onclick=function(){c.goToStep(this.getAttribute("data-stepnumber"))};0===b&&(q.className="active");q.href="javascript:void(0);";q.innerHTML="&nbsp;";q.setAttribute("data-stepnumber",this._introItems[b].step);
+r.appendChild(q);p.appendChild(r)}l.appendChild(p);f.className="introjs-tooltipbuttons";!1===this._options.showButtons&&(f.style.display="none");j.className="introjs-tooltip";j.appendChild(n);j.appendChild(l);if(!0==this._options.showStepNumbers){var w=document.createElement("span");w.className="introjs-helperNumberLayer";w.innerHTML=a.step;k.appendChild(w)}j.appendChild(m);k.appendChild(j);l=document.createElement("a");l.onclick=function(){c._introItems.length-1!=c._currentStep&&s.call(c)};l.href=
+"javascript:void(0);";l.innerHTML=this._options.nextLabel;n=document.createElement("a");n.onclick=function(){0!=c._currentStep&&x.call(c)};n.href="javascript:void(0);";n.innerHTML=this._options.prevLabel;k=document.createElement("a");k.className="introjs-button introjs-skipbutton";k.href="javascript:void(0);";k.innerHTML=this._options.skipLabel;k.onclick=function(){c._introItems.length-1==c._currentStep&&"function"===typeof c._introCompleteCallback&&c._introCompleteCallback.call(c);c._introItems.length-
+1!=c._currentStep&&"function"===typeof c._introExitCallback&&c._introExitCallback.call(c);t.call(c,c._targetElement)};f.appendChild(k);1<this._introItems.length&&(f.appendChild(n),f.appendChild(l));j.appendChild(f);B.call(c,a.element,j,m,w)}0==this._currentStep&&1<this._introItems.length?(n.className="introjs-button introjs-prevbutton introjs-disabled",l.className="introjs-button introjs-nextbutton",k.innerHTML=this._options.skipLabel):this._introItems.length-1==this._currentStep||1==this._introItems.length?
+(k.innerHTML=this._options.doneLabel,n.className="introjs-button introjs-prevbutton",l.className="introjs-button introjs-nextbutton introjs-disabled"):(n.className="introjs-button introjs-prevbutton",l.className="introjs-button introjs-nextbutton",k.innerHTML=this._options.skipLabel);l.focus();a.element.className+=" introjs-showElement";b=z(a.element,"position");"absolute"!==b&&"relative"!==b&&(a.element.className+=" introjs-relativePosition");for(b=a.element.parentNode;null!=b&&"body"!==b.tagName.toLowerCase();){m=
+z(b,"z-index");j=parseFloat(z(b,"opacity"));if(/[0-9]+/.test(m)||1>j)b.className+=" introjs-fixParent";b=b.parentNode}b=a.element.getBoundingClientRect();!(0<=b.top&&0<=b.left&&b.bottom+80<=window.innerHeight&&b.right<=window.innerWidth)&&!0===this._options.scrollToElement&&(j=a.element.getBoundingClientRect(),b=void 0!=window.innerWidth?window.innerHeight:document.documentElement.clientHeight,m=j.bottom-(j.bottom-j.top),j=j.bottom-b,0>m||a.element.clientHeight>b?window.scrollBy(0,m-30):window.scrollBy(0,
+j+100));"undefined"!==typeof this._introAfterChangeCallback&&this._introAfterChangeCallback.call(this,a.element)}function z(a,b){var c="";a.currentStyle?c=a.currentStyle[b]:document.defaultView&&document.defaultView.getComputedStyle&&(c=document.defaultView.getComputedStyle(a,null).getPropertyValue(b));return c&&c.toLowerCase?c.toLowerCase():c}function D(a){var b=document.createElement("div"),c="",d=this;b.className="introjs-overlay";if("body"===a.tagName.toLowerCase())c+="top: 0;bottom: 0; left: 0;right: 0;position: fixed;",
+b.setAttribute("style",c);else{var e=h(a);e&&(c+="width: "+e.width+"px; height:"+e.height+"px; top:"+e.top+"px;left: "+e.left+"px;",b.setAttribute("style",c))}a.appendChild(b);b.onclick=function(){!0==d._options.exitOnOverlayClick&&(t.call(d,a),void 0!=d._introExitCallback&&d._introExitCallback.call(d))};setTimeout(function(){c+="opacity: "+d._options.overlayOpacity.toString()+";";b.setAttribute("style",c)},10);return!0}function h(a){var b={};b.width=a.offsetWidth;b.height=a.offsetHeight;for(var c=
+0,d=0;a&&!isNaN(a.offsetLeft)&&!isNaN(a.offsetTop);)c+=a.offsetLeft,d+=a.offsetTop,a=a.offsetParent;b.top=d;b.left=c;return b}var u=function(a){if("object"===typeof a)return new f(a);if("string"===typeof a){if(a=document.querySelector(a))return new f(a);throw Error("There is no element with given selector.");}return new f(document.body)};u.version="0.9.0";u.fn=f.prototype={clone:function(){return new f(this)},setOption:function(a,b){this._options[a]=b;return this},setOptions:function(a){var b=this._options,
+c={},d;for(d in b)c[d]=b[d];for(d in a)c[d]=a[d];this._options=c;return this},start:function(){a:{var a=this._targetElement,b=[],c=this;if(this._options.steps)for(var d=[],e=0,d=this._options.steps.length;e<d;e++){var f=r(this._options.steps[e]);f.step=b.length+1;"string"===typeof f.element&&(f.element=document.querySelector(f.element));if("undefined"===typeof f.element||null==f.element){var g=document.querySelector(".introjsFloatingElement");null==g&&(g=document.createElement("div"),g.className=
+"introjsFloatingElement",document.body.appendChild(g));f.element=g;f.position="floating"}null!=f.element&&b.push(f)}else{d=a.querySelectorAll("*[data-intro]");if(1>d.length)break a;e=0;for(f=d.length;e<f;e++){var g=d[e],h=parseInt(g.getAttribute("data-step"),10);0<h&&(b[h-1]={element:g,intro:g.getAttribute("data-intro"),step:parseInt(g.getAttribute("data-step"),10),tooltipClass:g.getAttribute("data-tooltipClass"),position:g.getAttribute("data-position")||this._options.tooltipPosition})}e=h=0;for(f=
+d.length;e<f;e++)if(g=d[e],null==g.getAttribute("data-step")){for(;"undefined"!=typeof b[h];)h++;b[h]={element:g,intro:g.getAttribute("data-intro"),step:h+1,tooltipClass:g.getAttribute("data-tooltipClass"),position:g.getAttribute("data-position")||this._options.tooltipPosition}}}e=[];for(d=0;d<b.length;d++)b[d]&&e.push(b[d]);b=e;b.sort(function(a,b){return a.step-b.step});c._introItems=b;D.call(c,a)&&(s.call(c),a.querySelector(".introjs-skipbutton"),a.querySelector(".introjs-nextbutton"),c._onKeyDown=
+function(b){if(27===b.keyCode&&!0==c._options.exitOnEsc)t.call(c,a),void 0!=c._introExitCallback&&c._introExitCallback.call(c);else if(37===b.keyCode)x.call(c);else if(39===b.keyCode||13===b.keyCode)s.call(c),b.preventDefault?b.preventDefault():b.returnValue=!1},c._onResize=function(){v.call(c,document.querySelector(".introjs-helperLayer"))},window.addEventListener?(this._options.keyboardNavigation&&window.addEventListener("keydown",c._onKeyDown,!0),window.addEventListener("resize",c._onResize,!0)):
+document.attachEvent&&(this._options.keyboardNavigation&&document.attachEvent("onkeydown",c._onKeyDown),document.attachEvent("onresize",c._onResize)))}return this},goToStep:function(a){this._currentStep=a-2;"undefined"!==typeof this._introItems&&s.call(this);return this},nextStep:function(){s.call(this);return this},previousStep:function(){x.call(this);return this},exit:function(){t.call(this,this._targetElement)},refresh:function(){v.call(this,document.querySelector(".introjs-helperLayer"));return this},
+onbeforechange:function(a){if("function"===typeof a)this._introBeforeChangeCallback=a;else throw Error("Provided callback for onbeforechange was not a function");return this},onchange:function(a){if("function"===typeof a)this._introChangeCallback=a;else throw Error("Provided callback for onchange was not a function.");return this},onafterchange:function(a){if("function"===typeof a)this._introAfterChangeCallback=a;else throw Error("Provided callback for onafterchange was not a function");return this},
+oncomplete:function(a){if("function"===typeof a)this._introCompleteCallback=a;else throw Error("Provided callback for oncomplete was not a function.");return this},onexit:function(a){if("function"===typeof a)this._introExitCallback=a;else throw Error("Provided callback for onexit was not a function.");return this}};return p.introJs=u});
diff --git a/circle/dashboard/static/dashboard/introjs/introjs.min.css b/circle/dashboard/static/dashboard/introjs/introjs.min.css
new file mode 100644
index 0000000..70ed6e6
--- /dev/null
+++ b/circle/dashboard/static/dashboard/introjs/introjs.min.css
@@ -0,0 +1,12 @@
+.introjs-overlay{position:absolute;z-index:999999;background-color:#000;opacity:0;background:-moz-radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%);background:-webkit-gradient(radial,center center,0px,center center,100%,color-stop(0%,rgba(0,0,0,0.4)),color-stop(100%,rgba(0,0,0,0.9)));background:-webkit-radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%);background:-o-radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%);background:-ms-radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%);background:radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#66000000',endColorstr='#e6000000',GradientType=1);-ms-filter:"alpha(opacity=50)";filter:alpha(opacity=50);-webkit-transition:all .3s ease-out;-moz-transition:all .3s ease-out;-ms-transition:all .3s ease-out;-o-transition:all .3s ease-out;transition:all .3s ease-out}.introjs-fixParent{z-index:auto !important;opacity:1.0 !important}.introjs-showElement,tr.introjs-showElement>td,tr.introjs-showElement>th{z-index:9999999 !important}.introjs-relativePosition,tr.introjs-showElement>td,tr.introjs-showElement>th{position:relative}.introjs-helperLayer{position:absolute;z-index:9999998;background-color:#FFF;background-color:rgba(255,255,255,.9);border:1px solid #777;border:1px solid rgba(0,0,0,.5);border-radius:4px;box-shadow:0 2px 15px rgba(0,0,0,.4);-webkit-transition:all .3s ease-out;-moz-transition:all .3s ease-out;-ms-transition:all .3s ease-out;-o-transition:all .3s ease-out;transition:all .3s ease-out}.introjs-helperNumberLayer{position:absolute;top:-16px;left:-16px;z-index:9999999999 !important;padding:2px;font-family:Arial,verdana,tahoma;font-size:13px;font-weight:bold;color:white;text-align:center;text-shadow:1px 1px 1px rgba(0,0,0,.3);background:#ff3019;background:-webkit-linear-gradient(top,#ff3019 0,#cf0404 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0%,#ff3019),color-stop(100%,#cf0404));background:-moz-linear-gradient(top,#ff3019 0,#cf0404 100%);background:-ms-linear-gradient(top,#ff3019 0,#cf0404 100%);background:-o-linear-gradient(top,#ff3019 0,#cf0404 100%);background:linear-gradient(to bottom,#ff3019 0,#cf0404 100%);width:20px;height:20px;line-height:20px;border:3px solid white;border-radius:50%;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3019',endColorstr='#cf0404',GradientType=0);filter:progid:DXImageTransform.Microsoft.Shadow(direction=135,strength=2,color=ff0000);box-shadow:0 2px 5px rgba(0,0,0,.4)}.introjs-arrow{border:5px solid white;content:'';position:absolute}.introjs-arrow.top{top:-10px;border-top-color:transparent;border-right-color:transparent;border-bottom-color:white;border-left-color:transparent}.introjs-arrow.top-right{top:-10px;right:10px;border-top-color:transparent;border-right-color:transparent;border-bottom-color:white;border-left-color:transparent}.introjs-arrow.top-middle{top:-10px;left:50%;margin-left:-5px;border-top-color:transparent;border-right-color:transparent;border-bottom-color:white;border-left-color:transparent}.introjs-arrow.right{right:-10px;top:10px;border-top-color:transparent;border-right-color:transparent;border-bottom-color:transparent;border-left-color:white}.introjs-arrow.bottom{bottom:-10px;border-top-color:white;border-right-color:transparent;border-bottom-color:transparent;border-left-color:transparent}.introjs-arrow.left{left:-10px;top:10px;border-top-color:transparent;border-right-color:white;border-bottom-color:transparent;border-left-color:transparent}.introjs-tooltip{position:absolute;padding:10px;background-color:white;min-width:200px;max-width:300px;border-radius:3px;box-shadow:0 1px 10px rgba(0,0,0,.4);-webkit-transition:opacity .1s ease-out;-moz-transition:opacity .1s ease-out;-ms-transition:opacity .1s ease-out;-o-transition:opacity .1s ease-out;transition:opacity .1s ease-out}.introjs-tooltipbuttons{text-align:right}.introjs-button{position:relative;overflow:visible;display:inline-block;padding:.3em .8em;border:1px solid #d4d4d4;margin:0;text-decoration:none;text-shadow:1px 1px 0 #fff;font:11px/normal sans-serif;color:#333;white-space:nowrap;cursor:pointer;outline:0;background-color:#ececec;background-image:-webkit-gradient(linear,0 0,0 100%,from(#f4f4f4),to(#ececec));background-image:-moz-linear-gradient(#f4f4f4,#ececec);background-image:-o-linear-gradient(#f4f4f4,#ececec);background-image:linear-gradient(#f4f4f4,#ececec);-webkit-background-clip:padding;-moz-background-clip:padding;-o-background-clip:padding-box;-webkit-border-radius:.2em;-moz-border-radius:.2em;border-radius:.2em;zoom:1;*display:inline;margin-top:10px}.introjs-button:hover{border-color:#bcbcbc;text-decoration:none;box-shadow:0 1px 1px #e3e3e3}.introjs-button:focus,.introjs-button:active{background-image:-webkit-gradient(linear,0 0,0 100%,from(#ececec),to(#f4f4f4));background-image:-moz-linear-gradient(#ececec,#f4f4f4);background-image:-o-linear-gradient(#ececec,#f4f4f4);background-image:linear-gradient(#ececec,#f4f4f4)}.introjs-button::-moz-focus-inner{padding:0;border:0}.introjs-skipbutton{margin-right:5px;color:#7a7a7a}.introjs-prevbutton{-webkit-border-radius:.2em 0 0 .2em;-moz-border-radius:.2em 0 0 .2em;border-radius:.2em 0 0 .2em;border-right:0}.introjs-nextbutton{-webkit-border-radius:0 .2em .2em 0;-moz-border-radius:0 .2em .2em 0;border-radius:0 .2em .2em 0}.introjs-disabled,.introjs-disabled:hover,.introjs-disabled:focus{color:#9a9a9a;border-color:#d4d4d4;box-shadow:none;cursor:default;background-color:#f4f4f4;background-image:none;text-decoration:none}.introjs-bullets{text-align:center}.introjs-bullets ul{clear:both;margin:15px auto 0;padding:0;display:inline-block}.introjs-bullets ul li{list-style:none;float:left;margin:0 2px}.introjs-bullets ul li a{display:block;width:6px;height:6px;background:#ccc;border-radius:10px;-moz-border-radius:10px;-webkit-border-radius:10px;text-decoration:none}.introjs-bullets ul li a:hover{background:#999}.introjs-bullets ul li a.active{background:#999}.introjsFloatingElement{position:absolute;height:0;width:0;left:50%;top:50%}
+
+
+.introjs-helperLayer *,
+.introjs-helperLayer *:before,
+.introjs-helperLayer *:after {
+  -webkit-box-sizing: content-box;
+    -moz-box-sizing: content-box;
+      -ms-box-sizing: content-box;
+      -o-box-sizing: content-box;
+          box-sizing: content-box;
+}
diff --git a/circle/dashboard/static/dashboard/node-details.js b/circle/dashboard/static/dashboard/node-details.js
index 15d87e8..832a33c 100644
--- a/circle/dashboard/static/dashboard/node-details.js
+++ b/circle/dashboard/static/dashboard/node-details.js
@@ -15,7 +15,7 @@ $(function() {
       data: {'new_name': name},
       headers: {"X-CSRFToken": getCookie('csrftoken')},
       success: function(data, textStatus, xhr) {
- 	$("#node-details-h1-name").html(data['new_name']).show();
+        $("#node-details-h1-name").text(data['new_name']).show();
         $('#node-details-rename').hide();
         // addMessage(data['message'], "success");
       },
diff --git a/circle/dashboard/static/dashboard/node-list.js b/circle/dashboard/static/dashboard/node-list.js
index 4411422..9fbea33 100644
--- a/circle/dashboard/static/dashboard/node-list.js
+++ b/circle/dashboard/static/dashboard/node-list.js
@@ -12,40 +12,6 @@ $(function() {
 	tr.removeClass('danger');
   }
 
-  /* rename */
-  $("#node-list-rename-button, .node-details-rename-button").click(function() {
-    $("#node-list-column-name", $(this).closest("tr")).hide();
-    $("#node-list-rename", $(this).closest("tr")).css('display', 'inline');
-  });
-
-  /* rename ajax */
-  $('.node-list-rename-submit').click(function() {
-    var row = $(this).closest("tr")
-    var name = $('#node-list-rename-name', row).val();
-    var url = '/dashboard/node/' + row.children("td:first-child").text().replace(" ", "") + '/';
-    $.ajax({
-      method: 'POST',
-      url: url,
-      data: {'new_name': name},
-      headers: {"X-CSRFToken": getCookie('csrftoken')},
-      success: function(data, textStatus, xhr) {
-        
-        $("#node-list-column-name", row).html(
-          $("<a/>", {
-            'class': "real-link",
-            href: "/dashboard/node/" + data['node_pk'] + "/",
-            text: data['new_name']
-          })
-        ).show();
-        $('#node-list-rename', row).hide();
-        // addMessage(data['message'], "success");
-      },
-      error: function(xhr, textStatus, error) {
-	 addMessage("Error during renaming!", "danger");
-      }
-    });
-    return false;
-  });
 
   function statuschangeSuccess(tr){
    var tspan=tr.children('.enabled').children();
diff --git a/circle/dashboard/static/dashboard/template-list.js b/circle/dashboard/static/dashboard/template-list.js
index e0d82b5..a3e45ff 100644
--- a/circle/dashboard/static/dashboard/template-list.js
+++ b/circle/dashboard/static/dashboard/template-list.js
@@ -2,7 +2,7 @@ $(function() {
   /* for template removes buttons */
   $('.template-delete').click(function() {
     var template_pk = $(this).data('template-pk');
-    addModalConfirmation(deleteTemplate,
+    addModalConfirmationOrDisplayMessage(deleteTemplate,
       { 'url': '/dashboard/template/delete/' + template_pk + '/',
         'data': [],
         'template_pk': template_pk,
@@ -13,7 +13,7 @@ $(function() {
   /* for lease removes buttons */
   $('.lease-delete').click(function() {
     var lease_pk = $(this).data('lease-pk');
-    addModalConfirmation(deleteLease,
+    addModalConfirmationOrDisplayMessage(deleteLease,
       { 'url': '/dashboard/lease/delete/' + lease_pk + '/',
         'data': [],
         'lease_pk': lease_pk,
@@ -81,3 +81,29 @@ function deleteLease(data) {
     }
   });
 }
+
+function addModalConfirmationOrDisplayMessage(func, data) {
+  $.ajax({
+    type: 'GET',
+    url: data['url'],
+    data: jQuery.param(data['data']),
+    success: function(result) {
+      $('body').append(result);
+      $('#confirmation-modal').modal('show');
+      $('#confirmation-modal').on('hidden.bs.modal', function() {
+        $('#confirmation-modal').remove();
+      });
+      $('#confirmation-modal-button').click(function() {
+        func(data);
+        $('#confirmation-modal').modal('hide');
+      });
+    },
+    error: function(xhr, textStatus, error) {
+      if(xhr.status === 403) {
+        addMessage(gettext("Only the owners can delete the selected object."), "warning");
+      } else {
+        addMessage(gettext("An error occurred. (") + xhr.status + ")", 'danger')
+      }
+    }
+  });
+}
diff --git a/circle/dashboard/static/dashboard/vm-common.js b/circle/dashboard/static/dashboard/vm-common.js
index 66d6f71..10111e8 100644
--- a/circle/dashboard/static/dashboard/vm-common.js
+++ b/circle/dashboard/static/dashboard/vm-common.js
@@ -19,7 +19,7 @@ $(function() {
         $('#vm-migrate-node-list li input:checked').closest('li').addClass('panel-primary');
       }
     });
-    return false;
+    e.preventDefault();
   });
 
   /* if the operation fails show the modal again */
diff --git a/circle/dashboard/static/dashboard/vm-details.js b/circle/dashboard/static/dashboard/vm-details.js
index dbaf4b4..e851d86 100644
--- a/circle/dashboard/static/dashboard/vm-details.js
+++ b/circle/dashboard/static/dashboard/vm-details.js
@@ -28,7 +28,7 @@ $(function() {
   });
 
   /* save resources */
-  $('#vm-details-resources-save').click(function() {
+  $('#vm-details-resources-save').click(function(e) {
     var error = false;
     $(".cpu-count-input, .ram-input").each(function() {
       if(!$(this)[0].checkValidity()) {
@@ -61,7 +61,7 @@ $(function() {
         }  
       }
     });
-    return false;
+    e.preventDefault();
   });
 
   /* remove tag */
@@ -205,11 +205,11 @@ $(function() {
   });
 
   /* rename in home tab */
-  $(".vm-details-home-edit-name-click").click(function() {
+  $(".vm-details-home-edit-name-click").click(function(e) {
     $(".vm-details-home-edit-name-click").hide();
     $("#vm-details-home-rename").show();
     $("input", $("#vm-details-home-rename")).select();
-    return false;
+    e.preventDefault();
   });
 
   /* rename ajax */
@@ -236,7 +236,7 @@ $(function() {
   });
   
   /* update description click */
-  $(".vm-details-home-edit-description-click").click(function() {
+  $(".vm-details-home-edit-description-click").click(function(e) {
     $(".vm-details-home-edit-description-click").hide();
     $("#vm-details-home-description").show();
     var ta = $("#vm-details-home-description textarea");
@@ -244,7 +244,7 @@ $(function() {
     ta.val("");
     ta.focus();
     ta.val(tmp)
-    return false;
+    e.preventDefault();
   });
   
   /* description update ajax */
@@ -316,6 +316,24 @@ $(function() {
     if(Boolean($(this).data("disabled"))) return false;
   });
 
+  $("#dashboard-tutorial-toggle").click(function() {
+    var box = $("#alert-new-template");
+    var list = box.find("ol")
+    list.stop().slideToggle(function() {
+      var url = box.find("form").prop("action");
+      var hidden = list.css("display") === "none";
+      box.find("button i").prop("class", "fa fa-caret-" + (hidden ? "down" : "up"));
+      $.ajax({
+        type: 'POST',
+        url: url,
+        data: {'hidden': hidden},
+        headers: {"X-CSRFToken": getCookie('csrftoken')},
+        success: function(re, textStatus, xhr) {}
+      });
+    }); 
+    return false;
+  });
+
 });
 
 
@@ -344,10 +362,7 @@ function decideActivityRefresh() {
   /* if something is still spinning */
   if($('.timeline .activity i').hasClass('fa-spin'))
     check = true;
-  /* if there is only one activity */
-  if($('#activity-timeline div[class="activity"]').length < 2)
-    check = true;
-  
+
   return check;
 }
 
@@ -377,6 +392,7 @@ function checkNewActivity(runs) {
       } else {
         icon.prop("class", "fa " + data['icon']);
       }
+      $("#vm-details-state").data("status", data['status']);
       $("#vm-details-state span").html(data['human_readable_status'].toUpperCase());
       if(data['status'] == "RUNNING") {
         if(data['connect_uri']) {
diff --git a/circle/dashboard/static/dashboard/vm-tour.js b/circle/dashboard/static/dashboard/vm-tour.js
index cfffc7c..1c1065d 100644
--- a/circle/dashboard/static/dashboard/vm-tour.js
+++ b/circle/dashboard/static/dashboard/vm-tour.js
@@ -1,142 +1,129 @@
+var intro;
 $(function() {
-  $(".vm-details-start-template-tour").click(function() {
-    ttour = createTemplateTour();
-    ttour.init();
-    ttour.start();
-  });
-});
+  $("#vm-details-start-template-tour").click(function() {
+    intro = introJs();
+    intro.setOptions({
+      'nextLabel': gettext("Next") + ' <i class="fa fa-chevron-right"></i>',
+      'prevLabel': '<i class="fa fa-chevron-left"></i> ' + gettext("Previous"),
+      'skipLabel': '<i class="fa fa-times"></i> ' + gettext("End tour"),
+      'doneLabel': gettext("Done"),
+    });
+    intro.setOptions({
+      'steps': get_steps(),
+    });
+    intro.onbeforechange(function(target) {
+      /* if the tab menu item is highlighted */
+      if($(target).data("toggle") == "pill") {
+        $(target).trigger("click");
+      }
 
-function createTemplateTour() {
-  var ttour = new Tour({
-      storage: false,
-      name: "template",
-      template: "<div class='popover'>" +
-                  "<div class='arrow'></div>" +
-                  "<h3 class='popover-title'></h3>" +
-                  "<div class='popover-content'></div>" +
-                  "<div class='popover-navigation'>" +
-                    "<div class='btn-group'>" +
-                      "<button class='btn btn-sm btn-default' data-role='prev'>" +
-                        '<i class="fa fa-chevron-left"></i> ' + gettext("Prev") + "</button> " +
-                      "<button class='btn btn-sm btn-default' data-role='next'>" +
-                        gettext("Next") + ' <i class="fa fa-chevron-right"></i></button> ' +
-                      "<button class='btn btn-sm btn-default' data-role='pause-resume' data-pause-text='Pause' data-resume-text='Resume'>Pause</button> " +
-                    "</div>" +
-                    "<button class='btn btn-sm btn-default' data-role='end'>" +
-                      gettext("End tour") + ' <i class="fa fa-flag-checkered"></i></button>' +
-                  "</div>" +
-                "</div>",
-  });
+      /* if anything in a tab is highlighted change to that tab */
+      var tab = $(target).closest('.tab-pane:not([id^="ipv"])');
+      var id = tab.prop("id");
+      if(id) {
+        id = id.substring(1, id.length);
+        $('a[href="#' + id + '"]').trigger("click");
+      }
+    });
+    intro.start();
 
-  ttour.addStep({
-    element: "#vm-details-template-tour-button",
-    title: gettext("Template Tutorial Tour"),
-    content: "<p>" + gettext("Welcome to the template tutorial. In this quick tour, we gonna show you how to do the steps described above.") + "</p>" +
-             "<p>" + gettext('For the next tour step press the "Next" button or the right arrow (or "Back" button/left arrow for the previous step).') + "</p>" +
-             "<p>" + gettext("During the tour please don't try the functions because it may lead to graphical glitches, however " +
-                             "you can end the tour any time you want with the End Tour button!") + "</p>",
-    placement: "bottom",
-    backdrop: true,
+    return false;
   });
 
-  ttour.addStep({
-    backdrop: true,
-    element: 'a[href="#home"]',
-    title: gettext("Home tab"), 
-    content: gettext("In this tab you can tag your virtual machine and modify the name and description."),
-    placement: 'top',
-    onShow: function() {
-      $('a[href="#home"]').trigger("click");
-    },
+  $(document).on('click', '#vm-details-resources-save, .vm-details-home-edit-name-click, .vm-details-home-edit-description-click, a.operation', function() {
+    if(intro)
+      intro.exit();
   });
+});
 
-  ttour.addStep({
-    element: 'a[href="#resources"]',
-    title: gettext("Resources tab"),
-    backdrop: true,
-    placement: 'top',
-    content: gettext("On the resources tab you can edit the CPU/RAM options and add/remove disks!"),
-    onShow: function() {
-      $('a[href="#resources"]').trigger("click");
-    },
-  });
 
-  ttour.addStep({
-    element: '#vm-details-resources-form',
-    placement: 'top',
-    backdrop: true,
-    title: gettext("Resources"),
-    content: '<p><strong>' + gettext("CPU priority") + ":</strong> " + gettext("higher is better") + "</p>" + 
-             '<p><strong>' + gettext("CPU count") + ":</strong> " + gettext("number of CPU cores.") + "</p>" +
-             '<p><strong>' + gettext("RAM amount") + ":</strong> " + gettext("amount of RAM.") + "</p>", 
-    onShow: function() {
-      $('a[href="#resources"]').trigger("click");
-    },
-  });
+function get_steps() {
+  // if an activity is running the #ops will be refreshed
+  // and the intro will break
+  deploy_selector = "#ops";
+  save_as_selector = "#ops";
+  if(!$('.timeline .activity i').hasClass('fa-spin')) {
+    vm_status = $("#vm-details-state").data("status");
+    if(vm_status === "PENDING")
+      deploy_selector += ' a[class*="operation-deploy"]';
+    if(vm_status === "RUNNING" || vm_status === "STOPPED")
+      save_as_selector += ' a[class*="operation-save_as_template"]';
+  }
 
-  ttour.addStep({
-    element: '#vm-details-resources-disk',
-    backdrop: true,
-    placement: 'top',
-    title: gettext("Disks"),
-    content: gettext("You can add empty disks, download new ones and remove existing ones here."),
-    onShow: function() {
-      $('a[href="#resources"]').trigger("click");
+  steps = [
+    {
+      element: document.querySelector("#vm-details-start-template-tour"),
+      intro: "<p>" + gettext("Welcome to the template tutorial. In this quick tour, we are going to show you how to do the steps described above.") + "</p>" +
+             "<p>" + gettext('For the next tour step press the "Next" button or the right arrow (or "Back" button/left arrow for the previous step).') + "</p>"
     },
-  });
-
-  ttour.addStep({
-    element: 'a[href="#network"]',
-    backdrop: true,
-    placement: 'top',
-    title: gettext("Network tab"),
-    content: gettext('You can add new network interfaces or remove existing ones here.'),
-    onShow: function() {
-      $('a[href="#network"]').trigger("click");
+    {
+      element: document.querySelector('a[href="#home"]'),
+      intro: gettext("In this tab you can extend the expiration date of your virtual machine, add tags and modify the name and description."),
     },
-  });
-
+    {
+      element: document.querySelector('#home_name_and_description'),
+      intro: gettext("Please add a meaningful description to the virtual machine. Changing the name is also recommended, however you can choose a new name when saving the template."),
+    },
+    {
+      element: document.querySelector('#home_expiration_and_lease'),
+      intro: gettext("You can change the lease to extend the expiration date. This will be the lease of the new template."),
+    },
+    {
+      element: document.querySelector('a[href="#resources"]'),
+      intro: gettext("On the resources tab you can edit the CPU/RAM options and add/remove disks if you have required permissions."),
+    }
+  ];
 
-  ttour.addStep({
-    element: "#ops",
-    title: '<i class="fa fa-play"></i> ' + gettext("Deploy"),
-    placement: "left",
-    backdrop: true,
-    content: gettext("Deploy the virtual machine."),
-  });
+  if($("#vm-details-resources-save").length) {
+    steps.push(
+      {
+        element: document.querySelector('#vm-details-resources-form'),
+        intro: '<p><strong>' + gettext("CPU priority") + ":</strong> " +
+                gettext("higher is better") + "</p>" +
+                "<p><strong>" + gettext("CPU count") + ":</strong> " +
+                gettext("number of CPU cores.") + "</p>" +
+                "<p><strong>" + gettext("RAM amount") + ":</strong> " +
+                gettext("amount of RAM.") + "</p>",
+        position: "top",
+      }
+    );
+  }
 
-  ttour.addStep({
-    element: "#vm-info-pane",
-    title: gettext("Connect"),
-    placement: "top",
-    backdrop: true,
-    content: gettext("Use the connection string or connect with your choice of client!"),
-    
-  });
+  if($(".operation-create_disk").length || $(".operation-download_disk").length) {
+    steps.push(
+      {
+        element: document.querySelector('#vm-details-resources-disk'),
+        intro: gettext("You can add empty disks, download new ones and remove existing ones here."),
+        position: "top",
+      }
+    );
+  }
 
-  ttour.addStep({
-    element: "#vm-info-pane",
-    placement: "top",
-    title: gettext("Customize the virtual machine"),
-    content: gettext("After you have connected to the virtual machine do your modifications then log off."),
-  });
-
-  ttour.addStep({
-    element: "#ops",
-    title: '<i class="fa fa-floppy-o"></i> ' + gettext("Save as"),
-    placement: "left",
-    backdrop: true,
-    content: gettext('Press the "Save as template" button and wait until the activity finishes.'),
-  });
-  
-  
-  ttour.addStep({
-    element: ".alert-new-template",
-    title: gettext("Finish"),
-    backdrop: true,
-    placement: "bottom",
-    content: gettext("This is the last message, if something is not clear you can do the the tour again!"),
-  });
-  
-  return ttour;
+  steps.push(
+    {
+      element: document.querySelector('a[href="#network"]'),
+      intro: gettext('You can add new network interfaces or remove existing ones here.'),
+    },
+    {
+      element: document.querySelector(deploy_selector),
+      intro: gettext("Deploy the virtual machine."),
+    },
+    {
+      element: document.querySelector("#vm-info-pane"),
+      intro: gettext("Use the CIRCLE client or the connection string to connect to the virtual machine."),
+    },
+    {
+      element: document.querySelector("#vm-info-pane"),
+      intro: gettext("After you have connected to the virtual machine do your modifications then log off."),
+    },
+    {
+      element: document.querySelector(save_as_selector),
+      intro: gettext('Press the "Save as template" button and wait until the activity finishes.'),
+    },
+    {
+      element: document.querySelector(".alert-new-template"),
+      intro: gettext("This is the last message, if something is not clear you can do the the tour again."),
+    }
+  );
+  return steps;
 }
diff --git a/circle/dashboard/tables.py b/circle/dashboard/tables.py
index 27dc8e1..063ada3 100644
--- a/circle/dashboard/tables.py
+++ b/circle/dashboard/tables.py
@@ -88,18 +88,21 @@ class GroupListTable(Table):
 
     number_of_users = TemplateColumn(
         orderable=False,
+        verbose_name=_("Number of users"),
         template_name='dashboard/group-list/column-users.html',
         attrs={'th': {'class': 'group-list-table-admin'}},
     )
 
     admin = TemplateColumn(
         orderable=False,
+        verbose_name=_("Admin"),
         template_name='dashboard/group-list/column-admin.html',
         attrs={'th': {'class': 'group-list-table-admin'}},
     )
 
     actions = TemplateColumn(
         orderable=False,
+        verbose_name=_("Actions"),
         attrs={'th': {'class': 'group-list-table-thin'}},
         template_code=('{% include "dashboard/group-list/column-'
                        'actions.html" with btn_size="btn-xs" %}'),
diff --git a/circle/dashboard/templates/dashboard/_disk-list-element.html b/circle/dashboard/templates/dashboard/_disk-list-element.html
index 23ae790..3cbc2e1 100644
--- a/circle/dashboard/templates/dashboard/_disk-list-element.html
+++ b/circle/dashboard/templates/dashboard/_disk-list-element.html
@@ -8,7 +8,7 @@
   <span class="operation-wrapper">
     <a href="{{ op.remove_disk.get_url }}?disk={{d.pk}}"
       class="btn btn-xs btn-{{ op.remove_disk.effect}} pull-right operation disk-remove-btn
-      {% if op.resize_disk.disabled %}disabled{% endif %}">
+      {% if op.remove_disk.disabled %}disabled{% endif %}">
       <i class="fa fa-{{ op.remove_disk.icon }}"></i> {% trans "Remove" %}
     </a>
   </span>
diff --git a/circle/dashboard/templates/dashboard/_vm-remove-port.html b/circle/dashboard/templates/dashboard/_vm-remove-port.html
new file mode 100644
index 0000000..44fe133
--- /dev/null
+++ b/circle/dashboard/templates/dashboard/_vm-remove-port.html
@@ -0,0 +1,23 @@
+{% extends "dashboard/operate.html" %}
+{% load i18n %}
+{% load crispy_forms_tags %}
+
+{% block formfields %}
+  {% if form %}
+    {% crispy form %}
+  {% endif %}
+
+  {% if form.fields.rule.initial != None %}
+    {% with rule=form.fields.rule.initial %}
+      <dl>
+        <dt>{% trans "Port" %}:</dt>
+        <dd>{{ rule.dport }}/{{ rule.proto }}</dd>
+        <dt>{% trans "Host" %}:</dt>
+        <dd>{{ rule.host.hostname }}</dd>
+        <dt>{% trans "Vlan" %}:</dt>
+        <dd>{{ rule.host.vlan.name }}</dd>
+      </dl>
+    {% endwith %}
+  {% endif %}
+
+{% endblock %}
diff --git a/circle/dashboard/templates/dashboard/base.html b/circle/dashboard/templates/dashboard/base.html
index 3bc73bb..7a1ebcc 100644
--- a/circle/dashboard/templates/dashboard/base.html
+++ b/circle/dashboard/templates/dashboard/base.html
@@ -6,7 +6,7 @@
 
 {% block extra_link %}
   <link rel="stylesheet" href="{{ STATIC_URL }}dashboard/loopj-jquery-simple-slider/css/simple-slider.css"/>
-  <link href="{{ STATIC_URL }}dashboard/bootstrap-tour.min.css" rel="stylesheet">
+  <link rel="stylesheet" href="{{ STATIC_URL }}dashboard/introjs/introjs.min.css">
   <link href="{{ STATIC_URL }}dashboard/dashboard.css" rel="stylesheet">
 {% endblock %}
 
diff --git a/circle/dashboard/templates/dashboard/confirm/ajax-node-status.html b/circle/dashboard/templates/dashboard/confirm/ajax-node-status.html
index 8b96540..74b2035 100644
--- a/circle/dashboard/templates/dashboard/confirm/ajax-node-status.html
+++ b/circle/dashboard/templates/dashboard/confirm/ajax-node-status.html
@@ -15,11 +15,11 @@
           <form action="{% url "dashboard.views.status-node" pk=object.pk %}" method="POST">
             {% csrf_token %}
             <button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Cancel" %}</button>
-	    <input type="hidden" name="change_status" value=""/>
+            <input type="hidden" name="change_status" value=""/>
             <button class="btn btn-warning">{% blocktrans with status=status %}Yes, {{status}}{% endblocktrans %}</button>
           </form>
         </div>
-	<div class="clearfix"></div>
+        <div class="clearfix"></div>
 
       </div>
     </div><!-- /.modal-content -->
diff --git a/circle/dashboard/templates/dashboard/confirm/ajax-remove.html b/circle/dashboard/templates/dashboard/confirm/ajax-remove.html
index 60a6400..32d4402 100644
--- a/circle/dashboard/templates/dashboard/confirm/ajax-remove.html
+++ b/circle/dashboard/templates/dashboard/confirm/ajax-remove.html
@@ -7,7 +7,7 @@
           {{ text }}
         {% else %}
           {%blocktrans with object=object%}
-	  Are you sure you want to remove <strong>{{ member }}</strong> from <strong>{{ object }}</strong>?
+            Are you sure you want to remove <strong>{{ member }}</strong> from <strong>{{ object }}</strong>?
           {%endblocktrans%}
         {% endif %}
         <br />
diff --git a/circle/dashboard/templates/dashboard/confirm/node-flush.html b/circle/dashboard/templates/dashboard/confirm/node-flush.html
index 0ed51a1..a26e325 100644
--- a/circle/dashboard/templates/dashboard/confirm/node-flush.html
+++ b/circle/dashboard/templates/dashboard/confirm/node-flush.html
@@ -23,9 +23,9 @@
         <div class="pull-right">
           <form action="" method="POST">
             {% csrf_token %}
-	    <a class="btn btn-default">{% trans "Back" %}</a>
-	    <input type="hidden" name="flush" value=""/>
-	    <button class="btn btn-warning">{% trans "Yes" %}</button>
+            <a class="btn btn-default">{% trans "Back" %}</a>
+            <input type="hidden" name="flush" value=""/>
+            <button class="btn btn-warning">{% trans "Yes" %}</button>
           </form>
         </div>
       </div>
diff --git a/circle/dashboard/templates/dashboard/confirm/node-status.html b/circle/dashboard/templates/dashboard/confirm/node-status.html
index 3b6b56a..f4a2f84 100644
--- a/circle/dashboard/templates/dashboard/confirm/node-status.html
+++ b/circle/dashboard/templates/dashboard/confirm/node-status.html
@@ -26,7 +26,7 @@
             {% csrf_token %}
             <a class="btn btn-default">{% trans "Cancel" %}</a>
             <button type="button" class="btn btn-default" data-dismiss="modal"></button>
-	    <input type="hidden" name="change_status" value=""/>
+            <input type="hidden" name="change_status" value=""/>
             <button class="btn btn-warning">{% blocktrans with status=status %}Yes, {{status}}{% endblocktrans %}</button>
           </form>
         </div>
diff --git a/circle/dashboard/templates/dashboard/confirm/base-transfer-ownership.html b/circle/dashboard/templates/dashboard/confirm/transfer-instance-ownership.html
similarity index 100%
rename from circle/dashboard/templates/dashboard/confirm/base-transfer-ownership.html
rename to circle/dashboard/templates/dashboard/confirm/transfer-instance-ownership.html
index 2182c63..217ea5f 100644
--- a/circle/dashboard/templates/dashboard/confirm/base-transfer-ownership.html
+++ b/circle/dashboard/templates/dashboard/confirm/transfer-instance-ownership.html
@@ -6,15 +6,15 @@
     <div class="panel panel-default">
       <div class="panel-heading">
         <h3 class="no-margin">
-            {% trans "Ownership transfer" %}
+          {% trans "Ownership transfer" %}
         </h3>
       </div>
       <div class="panel-body">
-          {% blocktrans with owner=instance.owner name=instance.name id=instance.id%}
-          <strong>{{ owner }}</strong> offered to take the ownership of
-          virtual machine <strong>{{name}} ({{id}})</strong>.
-          Do you accept the responsility of being the host's owner?
-          {% endblocktrans %}
+        {% blocktrans with owner=instance.owner name=instance.name id=instance.id%}
+        <strong>{{ owner }}</strong> offered to take the ownership of
+        virtual machine <strong>{{name}} ({{id}})</strong>.
+        Do you accept the responsility of being the host's owner?
+        {% endblocktrans %}
         <div class="pull-right">
           <form action="" method="POST">
             {% csrf_token %}
diff --git a/circle/dashboard/templates/dashboard/confirm/transfer-template-ownership.html b/circle/dashboard/templates/dashboard/confirm/transfer-template-ownership.html
new file mode 100644
index 0000000..077202c
--- /dev/null
+++ b/circle/dashboard/templates/dashboard/confirm/transfer-template-ownership.html
@@ -0,0 +1,28 @@
+{% extends "dashboard/base.html" %}
+{% load i18n %}
+
+{% block content %}
+  <div class="body-content">
+    <div class="panel panel-default">
+      <div class="panel-heading">
+        <h3 class="no-margin">
+          {% trans "Ownership transfer" %}
+        </h3>
+      </div>
+      <div class="panel-body">
+        {% blocktrans with owner=instance.owner name=instance.name id=instance.id%}
+        <strong>{{ owner }}</strong> offered to take the ownership of
+        template <strong>{{name}} ({{id}})</strong>.
+        Do you accept the responsility of being the template's owner?
+        {% endblocktrans %}
+        <div class="pull-right">
+          <form action="" method="POST">
+            {% csrf_token %}
+            <a class="btn btn-default" href="{% url "dashboard.index" %}">{% trans "No" %}</a>
+            <input type="hidden" name="key" value="{{ key }}"/>
+            <button class="btn btn-danger" type="submit">{% trans "Yes" %}</button>
+          </form>
+        </div>
+      </div>
+  </div>
+{% endblock %}
diff --git a/circle/dashboard/templates/dashboard/group-detail.html b/circle/dashboard/templates/dashboard/group-detail.html
index d18aa86..22e1350 100644
--- a/circle/dashboard/templates/dashboard/group-detail.html
+++ b/circle/dashboard/templates/dashboard/group-detail.html
@@ -1,6 +1,7 @@
 {% extends "dashboard/base.html" %}
 {% load crispy_forms_tags %}
 {% load i18n %}
+{% load static %}
 
 {% block title-page %}{{ group.name }} | {% trans "group" %}{% endblock %}
 
@@ -8,9 +9,15 @@
 <div class="body-content">
   <div class="page-header">
     <div class="pull-right" style="padding-top: 15px;">
-      <a title="{% trans "Rename" %}" href="#" class="btn btn-default btn-xs group-details-rename-button"><i class="fa fa-pencil"></i></a>
-      <a title="{% trans "Delete" %}" data-group-pk="{{ group.pk }}" class="btn btn-default btn-xs real-link group-delete" href="{% url "dashboard.views.delete-group" pk=group.pk %}"><i class="fa fa-trash-o"></i></a>
-      <a title="{% trans "Help" %}" href="#" class="btn btn-default btn-xs group-details-help-button"><i class="fa fa-question"></i></a>
+      <a title="{% trans "Rename" %}" href="#" class="btn btn-default btn-xs group-details-rename-button">
+        <i class="fa fa-pencil"></i>
+      </a>
+      <a title="{% trans "Delete" %}" data-group-pk="{{ group.pk }}" class="btn btn-default btn-xs real-link group-delete" href="{% url "dashboard.views.delete-group" pk=group.pk %}">
+        <i class="fa fa-trash-o"></i>
+      </a>
+      <a title="{% trans "Help" %}" href="#" class="btn btn-default btn-xs group-details-help-button">
+        <i class="fa fa-question"></i>
+      </a>
     </div>
    <h1>
       <div id="group-details-rename">
@@ -39,122 +46,125 @@
         </li>
       </ul>
     </div>
- </div>
+  </div><!-- .page-header -->
   <div class="row">
     <div class="col-md-12" id="group-detail-pane">
-      <div class="panel panel-default" id="group-detail-panel">
-        <div class="tab-content panel-body" id="group-form-body">
-
-          <form method="POST" action="{% url "dashboard.views.group-update" pk=group.pk %}">
-{% csrf_token %}
-{% crispy group_profile_form %}
-</form>
-
-<hr />
-
-<h3>{% trans "Available objects for this group" %}</h3>
-<ul class="dashboard-profile-vm-list fa-ul">
-  {% for i in vm_objects %}
-    <li>
-      <a href="{{ i.get_absolute_url }}">
-        <i class="fa fa-li {{ i.get_status_icon }}"></i>
-        {{ i }}
-      </a>
-    </li>
-  {% endfor %}
-  {% for t in template_objects %}
-    <li>
-      <a href="{{ t.get_absolute_url }}">
-        <i class="fa fa-li fa-puzzle-piece"></i>
-        {{ t }}
-      </a>
-    </li>
-  {% endfor %}
-  {% for g in group_objects %}
-    <li>
-      <a href="{{ g.get_absolute_url }}">
-        <i class="fa fa-li fa-users"></i>
-        {{ g }}
-      </a>
-    </li>
-  {% endfor %}
-</ul>
-
-<hr />
+      <div class="panel panel-default panel-body" id="group-detail-panel">
+        <form method="POST" action="{% url "dashboard.views.group-update" pk=group.pk %}">
+          {% csrf_token %}
+          {% crispy group_profile_form %}
+        </form>
+        <hr />
 
-<h3>{% trans "User list"|capfirst %}
-    {% if perms.auth.add_user %}
-        <a href="{% url "dashboard.views.create-user" group.pk %}" class="btn btn-success pull-right">{% trans "Create user" %}</a>
-    {% endif %}
-</h3>
-<form action="" method="post">{% csrf_token %}
-<table class="table table-striped table-with-form-fields table-bordered" id="group-detail-user-table">
-    <tbody>
-      <thead><tr><th></th><th>{% trans "Who" %}</th><th>{% trans "Remove" %}</th></tr></thead>
-        {% for i in users %}
-	  <tr>
-            <td>
-              <i class="fa fa-user"></i>
-            </td>
-            <td>
-              <a href="{% url "dashboard.views.profile" username=i.username %}" title="{{ i.username }}"
-                >{% include "dashboard/_display-name.html" with user=i show_org=True %}</a>
-            </td>
-	    <td>
-              <a data-group_pk="{{ group.pk }}" data-member_pk="{{i.pk}}" href="{% url "dashboard.views.remove-user" member_pk=i.pk group_pk=group.pk %}" class="real-link delete-from-group btn btn-link btn-xs"><i class="fa fa-times"><span class="sr-only">{% trans "remove" %}</span></i></a>
-            </td>
-          </tr>
-        {% endfor %}
-        {% for i in future_users %}
-	  <tr>
-            <td>
-              <i class="fa fa-user text-muted"></i>
-            </td>
-            <td> {{ i.org_id }} </td>
-	    <td>
-              <a href="{% url "dashboard.views.remove-future-user" member_org_id=i.org_id group_pk=group.pk %}"
-                  class="real-link btn-link btn-xs">
-                  <i class="fa fa-times"><span class="sr-only">{% trans "remove" %}</span></i></a>
-            </td>
-          </tr>
-        {% endfor %}
-        <tr>
-          <td><i class="fa fa-plus"></i></td>
-          <td colspan="2">
-            {{addmemberform.new_member}}
-          </td>
-        </tr>
-  </tbody>
-  </table>
-  <textarea name="new_members" class="form-control"
-    placeholder="{% trans "Add multiple users at once (one identifier per line)." %}"></textarea>
-  <div class="form-actions">
-    <button type="submit" class="btn btn-success">{% trans "Save" %}</button>
-  </div>
-  </form>
+        <h3>{% trans "Available objects for this group" %}</h3>
+        <ul class="dashboard-profile-vm-list fa-ul">
+          {% for i in vm_objects %}
+            <li>
+              <a href="{{ i.get_absolute_url }}">
+                <i class="fa fa-li {{ i.get_status_icon }}"></i>
+                {{ i }}
+              </a>
+            </li>
+          {% endfor %}
+          {% for t in template_objects %}
+            <li>
+              <a href="{{ t.get_absolute_url }}">
+                <i class="fa fa-li fa-puzzle-piece"></i>
+                {{ t }}
+              </a>
+            </li>
+          {% endfor %}
+          {% for g in group_objects %}
+            <li>
+              <a href="{{ g.get_absolute_url }}">
+                <i class="fa fa-li fa-users"></i>
+                {{ g }}
+              </a>
+            </li>
+          {% endfor %}
+        </ul>
+        <hr />
 
-  <hr />
-<h3 id="group-detail-perm-header">{% trans "Access permissions"|capfirst %}</h3>
-{% include "dashboard/_manage_access.html" with table_id="group-detail-perm-table" %}
-{% if user.is_superuser %}
-  <hr />
+        <h3>
+          {% trans "User list" %}
+          {% if perms.auth.add_user %}
+          <a href="{% url "dashboard.views.create-user" group.pk %}" class="btn btn-success pull-right">
+            {% trans "Create user" %}
+          </a>
+          {% endif %}
+        </h3>
+        <form action="" method="post">{% csrf_token %}
+        <table class="table table-striped table-with-form-fields table-bordered" id="group-detail-user-table">
+          <tbody>
+            <thead><tr><th></th><th>{% trans "Who" %}</th><th>{% trans "Remove" %}</th></tr></thead>
+              {% for i in users %}
+                <tr>
+                  <td>
+                    <i class="fa fa-user"></i>
+                  </td>
+                  <td>
+                    <a href="{% url "dashboard.views.profile" username=i.username %}" title="{{ i.username }}"
+                      >{% include "dashboard/_display-name.html" with user=i show_org=True %}</a>
+                  </td>
+                  <td>
+                    <a data-group_pk="{{ group.pk }}" data-member_pk="{{i.pk}}" href="{% url "dashboard.views.remove-user" member_pk=i.pk group_pk=group.pk %}" class="real-link delete-from-group btn btn-link btn-xs"><i class="fa fa-times">
+                        <span class="sr-only">{% trans "remove" %}</span></i>
+                    </a>
+                  </td>
+                </tr>
+              {% endfor %}
+              {% for i in future_users %}
+                <tr>
+                  <td>
+                    <i class="fa fa-user text-muted"></i>
+                  </td>
+                  <td> {{ i.org_id }} </td>
+                  <td>
+                    <a href="{% url "dashboard.views.remove-future-user" member_org_id=i.org_id group_pk=group.pk %}"
+                        class="real-link btn-link btn-xs">
+                        <i class="fa fa-times"><span class="sr-only">{% trans "remove" %}</span></i></a>
+                  </td>
+                </tr>
+              {% endfor %}
+              <tr>
+                <td><i class="fa fa-plus"></i></td>
+                <td colspan="2">
+                  {{addmemberform.new_member}}
+                </td>
+              </tr>
+            </tbody>
+          </table>
+          <textarea name="new_members" class="form-control"
+            placeholder="{% trans "Add multiple users at once (one identifier per line)." %}"></textarea>
+          <div class="form-actions">
+            <button type="submit" class="btn btn-success">{% trans "Save" %}</button>
+          </div>
+        </form>
+        <hr />
+        <h3 id="group-detail-perm-header">{% trans "Access permissions" %}</h3>
+        {% include "dashboard/_manage_access.html" with table_id="group-detail-perm-table" %}
 
-<script type="text/javascript" src="/static/admin/js/jquery.min.js"></script>
-<script type="text/javascript" src="/static/admin/js/jquery.init.js"></script>
-{{ group_perm_form.media }}
+        {% if user.is_superuser %}
+          <hr />
 
-<h3>{% trans "Group permissions" %}</h3>
+          <script type="text/javascript" src="/static/admin/js/jquery.min.js"></script>
+          <script type="text/javascript" src="/static/admin/js/jquery.init.js"></script>
+          {{ group_perm_form.media }}
 
-<div id="group-detail-permissions">
-{% crispy group_perm_form %}
-</div>
+          <h3>{% trans "Group permissions" %}</h3>
 
-<link rel="stylesheet" type="text/css" href="/static/admin/css/widgets.css" />
+          <div id="group-detail-permissions">
+          {% crispy group_perm_form %}
+          </div>
 
-{% endif %}
-	</div>
-     </div>
+          <link rel="stylesheet" type="text/css" href="/static/admin/css/widgets.css" />
+        {% endif %}
+      </div>
     </div>
   </div>
 </div>
 {% endblock %}
+
+{% block extra_js %}
+  <script type="text/javascript" src="{% static "dashboard/group-details.js" %}"></script>
+{% endblock %}
diff --git a/circle/dashboard/templates/dashboard/group-list.html b/circle/dashboard/templates/dashboard/group-list.html
index 92dfaa5..a47f2cd 100644
--- a/circle/dashboard/templates/dashboard/group-list.html
+++ b/circle/dashboard/templates/dashboard/group-list.html
@@ -10,10 +10,10 @@
   <div class="col-md-12">
     <div class="panel panel-default">
       <div class="panel-heading">
-        <h3 class="no-margin"><i class="fa fa-group"></i> Your groups</h3>
+        <h3 class="no-margin"><i class="fa fa-group"></i> {% trans "Groups" %}</h3>
       </div>
       <div class="panel-body">
-	<div id="table_container">
+        <div id="table_container">
           <div id="rendered_table" class="panel-body">
             {% render_table table %}
           </div>
diff --git a/circle/dashboard/templates/dashboard/index-groups.html b/circle/dashboard/templates/dashboard/index-groups.html
index 9f867b3..e1aba82 100644
--- a/circle/dashboard/templates/dashboard/index-groups.html
+++ b/circle/dashboard/templates/dashboard/index-groups.html
@@ -26,7 +26,7 @@
         <div class="col-sm-6 text-right">
           <a class="btn btn-primary btn-xs" href="{% url "dashboard.views.group-list" %}">
             <i class="fa fa-chevron-circle-right"></i>
-	    {% if more_groups > 0 %}
+            {% if more_groups > 0 %}
               {% blocktrans count more=more_groups %}
                 <strong>{{ more }}</strong>  more
               {% plural %}
diff --git a/circle/dashboard/templates/dashboard/index-nodes.html b/circle/dashboard/templates/dashboard/index-nodes.html
index 0ff215c..8574869 100644
--- a/circle/dashboard/templates/dashboard/index-nodes.html
+++ b/circle/dashboard/templates/dashboard/index-nodes.html
@@ -74,9 +74,11 @@
               {% trans "list" %}
           {% endif %}
         </a>
+        {% if request.user.is_superuser %}
         <a class="btn btn-success btn-xs node-create" href="{% url "dashboard.views.node-create" %}">
           <i class="fa fa-plus-circle"></i> {% trans "new" %}
         </a>
+        {% endif  %}
       </div>
     </div>
   </div>
diff --git a/circle/dashboard/templates/dashboard/index.html b/circle/dashboard/templates/dashboard/index.html
index 4a7da1b..0662e22 100644
--- a/circle/dashboard/templates/dashboard/index.html
+++ b/circle/dashboard/templates/dashboard/index.html
@@ -35,7 +35,7 @@
     </div>
     {% endif %}
 
-    {% if user.is_superuser %}
+    {% if perms.vm.view_statistics %}
     <div class="col-lg-4 col-sm-6">
       {% include "dashboard/index-nodes.html" %}
     </div>
diff --git a/circle/dashboard/templates/dashboard/instanceactivity_detail.html b/circle/dashboard/templates/dashboard/instanceactivity_detail.html
index 3733550..2784e99 100644
--- a/circle/dashboard/templates/dashboard/instanceactivity_detail.html
+++ b/circle/dashboard/templates/dashboard/instanceactivity_detail.html
@@ -50,7 +50,7 @@
             <dd>{{ object.task_uuid|default:'n/a' }}</dd>
 
             <dt>{% trans "status" %}</dt>
-            <dd>{{ object.get_status_id }}</dd>
+            <dd id="activity_status">{{ object.get_status_id }}</dd>
 
 
             <dt>{% trans "result" %}</dt>
diff --git a/circle/dashboard/templates/dashboard/node-add-trait.html b/circle/dashboard/templates/dashboard/node-add-trait.html
index fac24bf..38ccbd1 100644
--- a/circle/dashboard/templates/dashboard/node-add-trait.html
+++ b/circle/dashboard/templates/dashboard/node-add-trait.html
@@ -16,7 +16,7 @@
   <div class="col-md-12">
     <div class="panel panel-default">
       <div class="panel-heading">
-	      <h3 class="no-margin"><i class="fa fa-plus"></i> {% trans "Add Trait" %}</h3>
+        <h3 class="no-margin"><i class="fa fa-plus"></i> {% trans "Add Trait" %}</h3>
       </div>
       <div class="panel-body">
         {% with form=form %}
diff --git a/circle/dashboard/templates/dashboard/node-detail.html b/circle/dashboard/templates/dashboard/node-detail.html
index 1797092..b7ddc48 100644
--- a/circle/dashboard/templates/dashboard/node-detail.html
+++ b/circle/dashboard/templates/dashboard/node-detail.html
@@ -6,14 +6,16 @@
 {% block content %}
 <div class="body-content">
   <div class="page-header">
+    {% if request.user.is_superuser %}
     <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 "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>
+    <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 "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>
     </div>
-   <h1>
+    {% endif  %}
+    <h1>
       <div id="node-details-rename">
        <form action="" method="POST" id="node-details-rename-form">
          {% csrf_token %}
@@ -69,26 +71,26 @@
               {% trans "Resources" %}
             </a>
           </li>
-	  <li>
+          <li>
             <a href="{% url "dashboard.views.vm-list" %}?s=node:{{ node.name }}" 
               target="blank" class="text-center">
               <i class="fa fa-desktop fa-2x"></i><br>
               {% trans "Virtual Machines" %}
             </a>
           </li>
- 	  <li>
+          <li>
             <a href="#activity" data-toggle="pill" class="text-center">
               <i class="fa fa-clock-o fa-2x"></i><br>
               {% trans "Activity" %}
             </a>
           </li>
-	</ul>
+        </ul>
 
         <div id="panel-body"  class="tab-content panel-body">
           <div class="tab-pane active" id="home">{% include "dashboard/node-detail/home.html" %}</div>
-	  <div class="tab-pane" id="resources">{% include "dashboard/node-detail/resources.html" %}</div>
+          <div class="tab-pane" id="resources">{% include "dashboard/node-detail/resources.html" %}</div>
           <div class="tab-pane" id="activity">{% include "dashboard/node-detail/activity.html" %}</div>
-  	</div>
+        </div>
       </div>
     </div>
   </div>
diff --git a/circle/dashboard/templates/dashboard/node-detail/home.html b/circle/dashboard/templates/dashboard/node-detail/home.html
index e13dcfa..cf335c8 100644
--- a/circle/dashboard/templates/dashboard/node-detail/home.html
+++ b/circle/dashboard/templates/dashboard/node-detail/home.html
@@ -8,24 +8,27 @@
           {% for t in node.traits.all %}
             <div class="label label-success label-tag" style="display: inline-block">
               {{ t }}
-	      <a data-trait-pk="{{ t.pk }}" href="#" class="node-details-remove-trait"><i class="fa fa-times"></i></a>
+              <a data-trait-pk="{{ t.pk }}" href="#" class="node-details-remove-trait"><i class="fa fa-times"></i></a>
             </div>
           {% endfor %}
         {% else %}
-	<small>{% trans "No trait added!" %}</small>
+          <small>{% trans "No trait added!" %}</small>
         {% endif %}
       </div>
  {% load crispy_forms_tags %}
+
 <style>
   .row {
     margin-bottom: 15px;
   }
 </style>
 
-  <form action="{% url "dashboard.views.node-addtrait" node.pk %}" method="POST">
-  {% csrf_token %}
-  {% crispy trait_form %}
-  </form>
+  {% if request.user.is_superuser %}
+    <form action="{% url "dashboard.views.node-addtrait" node.pk %}" method="POST">
+    {% csrf_token %}
+    {% crispy trait_form %}
+    </form>
+  {% endif %}
     </div><!-- id:node-details-traits -->
   </div>
   <div class="col-md-8">
diff --git a/circle/dashboard/templates/dashboard/node-detail/resources.html b/circle/dashboard/templates/dashboard/node-detail/resources.html
index 6a0c288..00c4751 100644
--- a/circle/dashboard/templates/dashboard/node-detail/resources.html
+++ b/circle/dashboard/templates/dashboard/node-detail/resources.html
@@ -10,6 +10,16 @@
     <dt>{% trans "Enabled" %}:</dt><dd>{{ node.enabled }}</dd>
     <dt>{% trans "Host online" %}:</dt><dd> {{ node.online }}</dd>
     <dt>{% trans "Priority" %}:</dt><dd>{{ node.priority }}</dd>
+    <dt>{% trans "Driver Version:" %}</dt>
+    <dd>
+      {% if node.driver_version %}
+        {{ node.driver_version.branch }} at
+        {{ node.driver_version.commit }} ({{ node.driver_version.commit_text }})
+        {% if node.driver_version.is_dirty %}
+          <span class="label label-danger">{% trans "with uncommitted changes!" %}</span>
+        {% endif %}
+      {% endif %}
+    </dd>
     <dt>{% trans "Host owner" %}:</dt>
     <dd>
       {% include "dashboard/_display-name.html" with user=node.host.owner show_org=True %}
@@ -18,10 +28,12 @@
     <dt>{% trans "Host name" %}:</dt>
     <dd>
       {{ node.host.hostname }}
+      {% if request.user.is_superuser %}
       <a href="{{ node.host.get_absolute_url }}" class="btn btn-default btn-xs">
         <i class="fa fa-pencil"></i>
         {% trans "Edit host" %}
       </a>
+      {% endif  %}
     </dd>
     </dl>
 
diff --git a/circle/dashboard/templates/dashboard/store/_list-box.html b/circle/dashboard/templates/dashboard/store/_list-box.html
index 412717b..e9e4606 100644
--- a/circle/dashboard/templates/dashboard/store/_list-box.html
+++ b/circle/dashboard/templates/dashboard/store/_list-box.html
@@ -4,23 +4,23 @@
   <div class="list-group-item">
     <div class="row">
       <div class="col-sm-6">
-        <a href="{% url "dashboard.views.store-upload"%}?directory={{ current }}"
+        <a href="{% url "dashboard.views.store-upload"%}?directory={{ current|urlencode }}"
           class="btn btn-info btn-xs js-hidden">
           {% trans "Upload" %}
         </a>
-        <form action="" data-action="{% url "dashboard.views.store-upload-url" %}" 
+        <form action="" data-action="{% url "dashboard.views.store-upload-url" %}"
           method="POST" enctype="multipart/form-data" class="no-js-hidden"
           id="store-upload-form">
           {% csrf_token %}
-          <input type="hidden" name="current_dir" value="{{ current }}"/>
+          <input type="hidden" name="current_dir" value="{{ current|urlencode }}"/>
           <input type="hidden" name="next" value="{{ next_url }}"/>
           <div class="input-group" style="max-width: 350px;">
             <span class="input-group-btn" id="store-upload-browse">
               <span class="btn btn-primary btn-xs">
                 {% trans "Browse..." %}
-              </span>     
+              </span>
             </span>
-            <input type="text" class="form-control input-tags" 
+            <input type="text" class="form-control input-tags"
              id="store-upload-filename"/>
             <span class="input-group-btn">
               <button type="submit" class="btn btn-primary btn-xs" disabled>
@@ -33,13 +33,13 @@
       </div><!-- .col-sm-6 upload -->
 
       <div class="col-sm-6">
-        <a href="{% url "dashboard.views.store-remove" %}?path={{ current }}" 
-          class="btn btn-danger btn-xs pull-right store-action-button" 
+        <a href="{% url "dashboard.views.store-remove" %}?path={{ current|urlencode }}"
+          class="btn btn-danger btn-xs pull-right store-action-button"
           title="{% trans "Remove directory" %}">
           <i class="fa fa-times"></i>
         </a>
-        <a href="{% url "dashboard.views.store-download" %}?path={{ current }}" 
-          class="btn btn-primary btn-xs pull-right store-action-button" 
+        <a href="{% url "dashboard.views.store-download" %}?path={{ current|urlencode }}"
+          class="btn btn-primary btn-xs pull-right store-action-button"
           title="{% trans "Download directory" %}">
           <i class="fa fa-cloud-download"></i>
         </a>
@@ -51,7 +51,7 @@
             <span class="input-group-addon input-tags" title="{% trans "New directory" %}">
               <i class="fa fa-folder-open"></i>
             </span>
-            <input type="text" class="form-control input-tags" name="name" 
+            <input type="text" class="form-control input-tags" name="name"
              placeholder="{% trans "Name "%}" required/>
             <span class="input-group-btn">
               <input type="submit" class="btn btn-success btn-xs" value="{% trans "Create" %}"/>
@@ -64,7 +64,7 @@
 </div><!-- .list-group -->
 
 <div class="list-group" id="store-list-list">
-  <a href="{% url "dashboard.views.store-list" %}?directory={{ up_url }}" 
+  <a href="{% url "dashboard.views.store-list" %}?directory={{ up_url|urlencode }}"
     class="list-group-item store-list-item" data-item-type="D">
     {% if current == "/" %}
       <div class="store-list-item-icon">
@@ -85,8 +85,8 @@
 
   {% for f in root %}
   <a class="list-group-item store-list-item" data-item-type="{{ f.TYPE }}"
-    href="{% if f.TYPE == "D" %}{% url "dashboard.views.store-list" %}?directory={{ f.path }}{% else %}
-    {% url "dashboard.views.store-download" %}?path={{ f.path }}{% endif %}"
+    href="{% if f.TYPE == "D" %}{% url "dashboard.views.store-list" %}?directory={{ f.path|urlencode }}{% else %}
+    {% url "dashboard.views.store-download" %}?path={{ f.path|urlencode }}{% endif %}"
     >
     <div class="store-list-item-icon">
       <i class="
@@ -101,7 +101,7 @@
         <span class="badge badge-pulse">{% trans "new" %}</span>
       {% endif %}
     </div>
-    
+
     <div class="store-list-item-size">
       {{ f.human_readable_size }}
     </div>
@@ -122,12 +122,12 @@
         </dl>
       </div>
       <div class="col-sm-2" style="text-align: right;">
-        <a href="{% url "dashboard.views.store-download" %}?path={{ f.path }}" 
+        <a href="{% url "dashboard.views.store-download" %}?path={{ f.path|urlencode }}"
           class="btn btn-primary btn-sm store-download-button">
           <i class="fa fa-download"></i>
           {% trans "Download" %}
         </a>
-        <a href="{% url "dashboard.views.store-remove" %}?path={{ f.path }}" 
+        <a href="{% url "dashboard.views.store-remove" %}?path={{ f.path|urlencode }}"
           class="btn btn-danger btn-xs store-remove-button">
           <i class="fa fa-times"></i>
           {% trans "Remove" %}
diff --git a/circle/dashboard/templates/dashboard/template-edit.html b/circle/dashboard/templates/dashboard/template-edit.html
index d6bc6b5..64963e7 100644
--- a/circle/dashboard/templates/dashboard/template-edit.html
+++ b/circle/dashboard/templates/dashboard/template-edit.html
@@ -11,7 +11,9 @@
   <div class="col-md-7">
     <div class="panel panel-default">
       <div class="panel-heading">
-        <a class="pull-right btn btn-default btn-xs" href="{% url "dashboard.views.template-list" %}">{% trans "Back" %}</a>
+        <a class="pull-right btn btn-default btn-xs" href="{% url "dashboard.views.template-list" %}">
+          {% trans "Back" %}
+        </a>
         <h3 class="no-margin"><i class="fa fa-puzzle-piece"></i> {% trans "Edit template" %}</h3>
       </div>
       <div class="panel-body">
@@ -65,6 +67,38 @@
   </div>
 
   <div class="col-md-5">
+    {% if is_owner %}
+    <div class="panel panel-default">
+      <div class="panel-heading">
+        <a href="{% url "dashboard.views.template-delete" pk=object.pk %}" 
+         class="btn btn-xs btn-danger pull-right">
+          {% trans "Delete" %}
+        </a>
+        <h4 class="no-margin"><i class="fa fa-times"></i> {% trans "Delete template"  %}</h4>
+      </div>
+    </div>
+    {% endif %}
+
+    <div class="panel panel-default">
+      <div class="panel-heading">
+        <h4 class="no-margin"><i class="fa fa-user"></i> {% trans "Owner"  %}</h4>
+      </div>
+      <div class="panel-body">
+        {% if user == object.owner %}
+        {% blocktrans %}You are the current owner of this template.{% endblocktrans %}
+        {% else %}
+        {% url "dashboard.views.profile" username=object.owner.username as url %}
+        {% blocktrans with owner=object.owner name=object.owner.get_full_name%}
+          The current owner of this template is <a href="{{url}}">{{name}} ({{owner}})</a>.
+        {% endblocktrans %}
+        {% endif %}
+        {% if user == object.owner or user.is_superuser %}
+        <a href="{% url "dashboard.views.template-transfer-ownership" object.pk %}"
+           class="btn btn-link tx-tpl-ownership">{% trans "Transfer ownership..." %}</a>
+        {% endif %}
+      </div>
+    </div>
+
     <div class="panel panel-default">
       <div class="panel-heading">
         <h4 class="no-margin"><i class="fa fa-group"></i> {% trans "Manage access"  %}</h4>
diff --git a/circle/dashboard/templates/dashboard/template-tx-owner.html b/circle/dashboard/templates/dashboard/template-tx-owner.html
new file mode 100644
index 0000000..91e82af
--- /dev/null
+++ b/circle/dashboard/templates/dashboard/template-tx-owner.html
@@ -0,0 +1,16 @@
+{% load i18n %}
+
+<div class="pull-right">
+  <form action="{% url "dashboard.views.template-transfer-ownership" pk=object.pk %}" method="POST" style="max-width: 400px;">
+    {% csrf_token %}
+    <label>
+      {{ form.name.label }}
+    </label>
+    <div class="input-group">
+      {{form.name}}
+      <div class="input-group-btn">
+        <input type="submit" value="{% trans "Save" %}" class="btn btn-primary">
+      </div>
+    </div>
+  </form>
+</div>
diff --git a/circle/dashboard/templates/dashboard/vm-detail.html b/circle/dashboard/templates/dashboard/vm-detail.html
index 8d9b43f..ef7752a 100644
--- a/circle/dashboard/templates/dashboard/vm-detail.html
+++ b/circle/dashboard/templates/dashboard/vm-detail.html
@@ -6,14 +6,25 @@
 {% block content %}
 
 {% if instance.is_base %}
-<div class="alert alert-info alert-new-template">
-  <strong>{% trans "This is the master vm of your new template" %}</strong>
-  <div id="vm-details-template-tour-button" class="pull-right">
-    <a href="#" class="btn btn-default btn-lg pull-right vm-details-start-template-tour">
+<div class="alert alert-info alert-new-template" id="alert-new-template" style="position: relative;">
+  <form action="{% url "dashboard.views.vm-toggle-tutorial" pk=instance.pk %}"
+    method="POST">
+    {% csrf_token %}
+    <input name="hidden" type="hidden"
+     value="{{ hide_tutorial|yesno:"false,true" }}"/>
+    <button type="submit"
+      id="dashboard-tutorial-toggle" class="btn btn-sm pull-right btn-success">
+      <i class="fa fa-caret-{% if hide_tutorial %}down{% else %}up{% endif %}"></i>
+      {% trans "Toggle tutorial panel" %}
+    </button>
+
+    <a href="#" class="btn btn-default btn-sm pull-right"
+      id="vm-details-start-template-tour">
       <i class="fa fa-play"></i> {% trans "Start template tutorial" %}
     </a>
-  </div>
-  <ol>
+  </form>
+  <strong>{% trans "This is the master vm of your new template" %}</strong>
+  <ol {% if hide_tutorial %}style="display: none;"{% endif %}>
     <li>{% trans "Modify the virtual machine to suit your needs <strong>(optional)</strong>" %}
       <ul>
         <li>{% trans "Change the description" %}</li>
@@ -59,13 +70,20 @@
         {{ instance.name }}
       </div>
       <small>{{ instance.primary_host.get_fqdn }}</small>
+      <small class="dashboard-vm-favourite" style="line-height: 39.6px;" data-vm="{{ instance.pk }}">
+        {% if fav %}
+          <i class="fa fa-star text-primary title-favourite" title="{% trans "Unfavourite" %}"></i>
+        {% else %}
+          <i class="fa fa-star-o text-primary title-favourite" title="{% trans "Mark as favorite" %}"></i>
+        {% endif %}
+      </small>
     </h1>
     <div style="clear: both;"></div>
   </div>
   <div class="row">
     <div class="col-md-4" id="vm-info-pane">
       <div class="big">
-        <span id="vm-details-state" class="label label-success">
+        <span id="vm-details-state" class="label label-success" data-status="{{ instance.status }}">
           <i class="fa
             {% if is_new_state %}
             fa-spinner fa-spin
@@ -110,7 +128,14 @@
         <dd style="font-size: 10px; text-align: right; padding-top: 8px;">
           <div id="vm-details-pw-reset">
           {% with op=op.password_reset %}{% if op %}
-          <a href="{% if op.disabled %}#{% else %}{{op.get_url}}{% endif %}" class="operation operation-{{op.op}}" data-disabled="{% if op.disabled %}true" title="{% trans "Start the VM to change the password." %}"{% else %}false" {% endif %}>{% trans "Generate new password!" %}</a>
+          <a href="{% if op.disabled %}#{% else %}{{op.get_url}}{% endif %}"
+            class="operation operation-{{op.op}}"
+            {% if op.disabled %}
+            data-disabled="true"
+            title="{% if instance.has_agent %}{% trans "Start the VM to change the password." %}{% else %}{% trans "This machine has no agent installed." %}{% endif %}"
+            {% endif %}>
+            {% trans "Generate new password!" %}
+          </a>
           {% endif %}{% endwith %}
           </div>
         </dd>
@@ -138,7 +163,7 @@
       </div>
       {% endfor %}
       {% if instance.get_connect_uri %}
-      <div id="dashboard-vm-details-connect" class="operation-wrapper">  
+      <div id="dashboard-vm-details-connect" class="operation-wrapper">
         {% if client_download %}
         <a id="dashboard-vm-details-connect-button" class="btn btn-xs btn-default operation " href="{{ instance.get_connect_uri}}" title="{% trans "Connect via the CIRCLE Client" %}">
                 <i class="fa fa-external-link"></i> {% trans "Connect" %}
@@ -202,7 +227,7 @@
 {% endblock %}
 
 {% block extra_js %}
-  <script src="{{ STATIC_URL }}dashboard/bootstrap-tour.min.js"></script>
+  <script src="{{ STATIC_URL }}dashboard/introjs/intro.min.js"></script>
   <script src="{{ STATIC_URL }}dashboard/vm-details.js"></script>
   <script src="{{ STATIC_URL }}dashboard/vm-common.js"></script>
   <script src="{{ STATIC_URL }}dashboard/vm-console.js"></script>
diff --git a/circle/dashboard/templates/dashboard/vm-detail/_activity-timeline.html b/circle/dashboard/templates/dashboard/vm-detail/_activity-timeline.html
index 84a3a24..7f80150 100644
--- a/circle/dashboard/templates/dashboard/vm-detail/_activity-timeline.html
+++ b/circle/dashboard/templates/dashboard/vm-detail/_activity-timeline.html
@@ -3,7 +3,8 @@
 <div id="activity-timeline" class="timeline">
 
 {% for a in activities %}
-<div class="activity{% if a.pk == active.pk %} activity-active{%endif%}" data-activity-id="{{ a.pk }}">
+<div class="activity{% if a.pk == active.pk %} activity-active{%endif%}"
+  data-activity-id="{{ a.pk }}" data-activity-code="{{ a.activity_code }}">
   <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>
@@ -33,7 +34,8 @@
   {% if a.children.count > 0 %}
     <div class="sub-timeline">
       {% for s in a.children.all %}
-      <div data-activity-id="{{ s.pk }}" class="sub-activity{% if s.has_failed %} sub-activity-failed{% endif %}{% if s.pk == active.pk %} sub-activity-active{% endif %}">
+      <div data-activity-id="{{ s.pk }}" data-activity-code="{{ s.activity_code }}"
+        class="sub-activity{% if s.has_failed %} sub-activity-failed{% endif %}{% if s.pk == active.pk %} sub-activity-active{% endif %}">
         <span{% if s.result %} title="{{ s.result|get_text:user }}"{% endif %}>
           <a href="{{ s.get_absolute_url }}">
               {{ s.readable_name|get_text:user|capfirst }}</a></span> &ndash;
diff --git a/circle/dashboard/templates/dashboard/vm-detail/_network-port-add.html b/circle/dashboard/templates/dashboard/vm-detail/_network-port-add.html
index 7601a01..8cae3ae 100644
--- a/circle/dashboard/templates/dashboard/vm-detail/_network-port-add.html
+++ b/circle/dashboard/templates/dashboard/vm-detail/_network-port-add.html
@@ -1,13 +1,14 @@
 {% load i18n %}
 <div class="vm-details-network-port-add pull-right">
-  <form action="" method="POST">
+  <form action="{{ op.add_port.get_url }}" method="POST">
     {% csrf_token %}
-    <input type="hidden" name="host_pk" value="{{ i.host.pk }}"/>
+    <input type="hidden" name="host" value="{{ i.host.pk }}"/>
     <div class="input-group input-group-sm">
       <span class="input-group-addon">
         <i class="fa fa-plus"></i> <i class="fa fa-long-arrow-right"></i>
       </span>
-      <input type="text" class="form-control" size="5" style="width: 80px;" name="port"/>
+      <input type="number" class="form-control" size="5" min="1" max="65535"
+             style="width: 80px;" name="port" required/>
       <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">
diff --git a/circle/dashboard/templates/dashboard/vm-detail/access.html b/circle/dashboard/templates/dashboard/vm-detail/access.html
index b716552..065770d 100644
--- a/circle/dashboard/templates/dashboard/vm-detail/access.html
+++ b/circle/dashboard/templates/dashboard/vm-detail/access.html
@@ -4,8 +4,9 @@
   {% if user == instance.owner %}
   {% blocktrans %}You are the current owner of this instance.{% endblocktrans %}
   {% else %}
-  {% blocktrans with owner=instance.owner %}
-    The current owner of this instance is {{owner}}.
+  {% url "dashboard.views.profile" username=instance.owner.username as url %}
+  {% blocktrans with owner=instance.owner name=instance.owner.get_full_name%}
+    The current owner of this instance is <a href="{{url}}">{{name}} ({{owner}})</a>.
   {% endblocktrans %}
   {% endif %}
   {% if user == instance.owner or user.is_superuser %}
diff --git a/circle/dashboard/templates/dashboard/vm-detail/home.html b/circle/dashboard/templates/dashboard/vm-detail/home.html
index 6ad3508..1586ca6 100644
--- a/circle/dashboard/templates/dashboard/vm-detail/home.html
+++ b/circle/dashboard/templates/dashboard/vm-detail/home.html
@@ -1,7 +1,7 @@
 {% load i18n %}
 <div class="row">
   <div class="col-md-4">
-    <dl>
+    <dl id="home_name_and_description">
       <dt>{% trans "System" %}:</dt>
       <dd><i class="fa fa-{{ os_type_icon }}"></i> {{ instance.system }}</dd>
       <dt style="margin-top: 5px;">
@@ -52,30 +52,34 @@
       </dd>
     </dl>
 
-    <h4>{% trans "Expiration" %} {% if instance.is_expiring %}<i class="fa fa-warning-sign text-danger"></i>{% endif %}
-      <span id="vm-details-renew-op">
-        {% with op=op.renew %}{% if op %}
-          <a href="{{op.get_url}}" class="btn btn-success btn-xs
-                  operation operation-{{op.op}}">
-            <i class="fa fa-{{op.icon}}"></i>
-                  {{op.name}} </a>
-        {% endif %}{% endwith %}
-      </span>
-    </h4>
-    <dl>
-      <dt>{% trans "Suspended at:" %}</dt>
-      <dd>
-        <span title="{{ instance.time_of_suspend }}">
-          <i class="fa fa-moon-o"></i> {{ instance.time_of_suspend|timeuntil }}
+    <div id="home_expiration_and_lease">
+      <h4>
+        {% trans "Expiration" %}
+        {% if instance.is_expiring %}<i class="fa fa-warning-sign text-danger"></i>{% endif %}
+        <span id="vm-details-renew-op">
+          {% with op=op.renew %}{% if op %}
+            <a href="{{op.get_url}}" class="btn btn-success btn-xs
+                    operation operation-{{op.op}}">
+              <i class="fa fa-{{op.icon}}"></i>
+                    {{op.name}} </a>
+          {% endif %}{% endwith %}
         </span>
-      </dd>
-      <dt>{% trans "Destroyed at:" %}</dt>
-      <dd>
-        <span title="{{ instance.time_of_delete }}">
-          <i class="fa fa-times"></i> {{ instance.time_of_delete|timeuntil }}
-        </span>
-      </dd>
-    </dl>
+      </h4>
+      <dl>
+        <dt>{% trans "Suspended at:" %}</dt>
+        <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>
+          <span title="{{ instance.time_of_delete }}">
+            <i class="fa fa-times"></i> {{ instance.time_of_delete|timeuntil }}
+          </span>
+        </dd>
+      </dl>
+    </div>
 
     <div style="font-weight: bold;">{% trans "Tags" %}</div>
     <div id="vm-details-tags" style="margin-bottom: 20px;">
diff --git a/circle/dashboard/templates/dashboard/vm-detail/network.html b/circle/dashboard/templates/dashboard/vm-detail/network.html
index 9e49384..d4d63b8 100644
--- a/circle/dashboard/templates/dashboard/vm-detail/network.html
+++ b/circle/dashboard/templates/dashboard/vm-detail/network.html
@@ -78,7 +78,7 @@
                         {{ l.private }}/{{ l.proto }}
                       </td>
                       <td>
-                        <a href="{% url "dashboard.views.remove-port" pk=instance.pk rule=l.ipv4.pk %}" class="btn btn-link btn-xs vm-details-remove-port" data-rule="{{ l.ipv4.pk }}" title="{% trans "Remove" %}"><i class="fa fa-times"><span class="sr-only">{% trans "Remove" %}</span></i></a>
+                        <a href="{{ op.remove_port.get_url }}?rule={{ l.ipv4.pk }}" class="btn btn-link btn-xs vm-details-remove-port" data-rule="{{ l.ipv4.pk }}" title="{% trans "Remove" %}"><i class="fa fa-times"><span class="sr-only">{% trans "Remove" %}</span></i></a>
                       </td>
                     </tr>
                   {% endif %}
@@ -110,7 +110,7 @@
                         {{ l.private }}/{{ l.proto }}
                       </td>
                       <td>
-                        <a href="{% url "dashboard.views.remove-port" pk=instance.pk rule=l.ipv4.pk %}" class="btn btn-link btn-xs vm-details-remove-port" data-rule="{{ l.ipv6.pk }}" title="{% trans "Remove" %}"><i class="fa fa-times"><span class="sr-only">{% trans "Remove" %}</span></i></a>
+                        <a href="{{ op.remove_port.get_url }}?rule={{ l.ipv4.pk }}" class="btn btn-link btn-xs vm-details-remove-port" data-rule="{{ l.ipv6.pk }}" title="{% trans "Remove" %}"><i class="fa fa-times"><span class="sr-only">{% trans "Remove" %}</span></i></a>
                       </td>
                     </tr>
                   {% endif %}
diff --git a/circle/dashboard/templates/dashboard/vm-detail/resources.html b/circle/dashboard/templates/dashboard/vm-detail/resources.html
index e14cf6b..51e6d48 100644
--- a/circle/dashboard/templates/dashboard/vm-detail/resources.html
+++ b/circle/dashboard/templates/dashboard/vm-detail/resources.html
@@ -18,34 +18,28 @@
 {% endif %}
 </form>
 
-
 <hr />
 
-
-<div class="row" id="vm-details-resources-disk">
-  <div class="col-sm-11">
-    <h3>
-      {% trans "Disks" %}
-      <div class="pull-right">
-        <div id="disk-ops">
-          {% include "dashboard/vm-detail/_disk-operations.html" %}
-        </div>
+<div id="vm-details-resources-disk">
+  <h3>
+    {% trans "Disks" %}
+    <div class="pull-right">
+      <div id="disk-ops">
+        {% include "dashboard/vm-detail/_disk-operations.html" %}
       </div>
-    </h3>
-
-    <div class="row" id="vm-details-disk-add-for-form"></div>
-
-    {% if not instance.disks.all %}
-      {% trans "No disks are added!" %}
-    {% endif %}
-    {% for d in instance.disks.all %}
-      <h4 class="list-group-item-heading dashboard-vm-details-network-h3">
-        {% with long_remove=True %}
-          {% include "dashboard/_disk-list-element.html" %}
-        {% endwith %}
-      </h4>
-    {% endfor %}
-  </div>
+    </div>
+  </h3>
+
+  {% if not instance.disks.all %}
+    {% trans "No disks are added." %}
+  {% endif %}
+  {% for d in instance.disks.all %}
+    <h4 class="list-group-item-heading dashboard-vm-details-network-h3">
+      {% with long_remove=True %}
+        {% include "dashboard/_disk-list-element.html" %}
+      {% endwith %}
+    </h4>
+  {% endfor %}
 </div>
 
 {% if user.is_superuser %}
diff --git a/circle/dashboard/tests/test_templates.py b/circle/dashboard/tests/test_templates.py
new file mode 100644
index 0000000..2592bc4
--- /dev/null
+++ b/circle/dashboard/tests/test_templates.py
@@ -0,0 +1,52 @@
+# Copyright 2014 Budapest University of Technology and Economics (BME IK)
+#
+# This file is part of CIRCLE Cloud.
+#
+# CIRCLE is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option)
+# any later version.
+#
+# CIRCLE is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along
+# with CIRCLE.  If not, see <http://www.gnu.org/licenses/>.
+
+from os import listdir
+from os.path import isfile, isdir, join
+import unittest
+
+from django.conf import settings
+from django.template import Template, Context, VariableDoesNotExist
+from django.template.loader import find_template_loader
+from django.core.urlresolvers import NoReverseMatch
+
+
+class TemplateSyntaxTestCase(unittest.TestCase):
+
+    def test_templates(self):
+        """Test all templates for syntax errors."""
+        for loader_name in settings.TEMPLATE_LOADERS:
+            print loader_name
+            loader = find_template_loader(loader_name)
+            self._test_dir(loader.get_template_sources(''))
+
+    def _test_dir(self, dir, path="/"):
+        for i in dir:
+            i = join(path, i)
+            if isfile(i):
+                self._test_template(join(path, i))
+            elif isdir(i):
+                print "%s:" % i
+                self._test_dir(listdir(i), i)
+
+    def _test_template(self, path):
+        print path
+        try:
+            Template(open(path).read()).render(Context({}))
+        except (NoReverseMatch, VariableDoesNotExist, KeyError, AttributeError,
+                ValueError, ) as e:
+            print e
diff --git a/circle/dashboard/tests/test_views.py b/circle/dashboard/tests/test_views.py
index 8268da3..7aca07e 100644
--- a/circle/dashboard/tests/test_views.py
+++ b/circle/dashboard/tests/test_views.py
@@ -26,7 +26,8 @@ from django.contrib.auth import authenticate
 
 from dashboard.views import VmAddInterfaceView
 from vm.models import Instance, InstanceTemplate, Lease, Node, Trait
-from vm.operations import WakeUpOperation, AddInterfaceOperation
+from vm.operations import (WakeUpOperation, AddInterfaceOperation,
+                           AddPortOperation)
 from ..models import Profile
 from firewall.models import Vlan, Host, VlanGroup
 from mock import Mock, patch
@@ -299,7 +300,7 @@ class VmDetailTest(LoginMixin, TestCase):
         leases = Lease.objects.count()
         response = c.post("/dashboard/lease/delete/1/")
         # redirect to the login page
-        self.assertEqual(response.status_code, 302)
+        self.assertEqual(response.status_code, 403)
         self.assertEqual(leases, Lease.objects.count())
 
     def test_notification_read(self):
@@ -335,51 +336,48 @@ class VmDetailTest(LoginMixin, TestCase):
         self.login(c, "user2")
         inst = Instance.objects.get(pk=1)
         inst.set_level(self.u2, 'owner')
-        response = c.post("/dashboard/vm/1/", {'port': True,
-                                               'proto': 'tcp',
-                                               'port': '1337'})
+        vlan = Vlan.objects.get(id=1)
+        vlan.set_level(self.u2, 'user')
+        inst.add_interface(user=self.u2, vlan=vlan)
+        host = Host.objects.get(
+            interface__in=inst.interface_set.all())
+        with patch.object(AddPortOperation, 'async') as mock_method:
+            mock_method.side_effect = inst.add_port
+            response = c.post("/dashboard/vm/1/op/add_port/", {
+                'proto': 'tcp', 'host': host.pk, 'port': '1337'})
         self.assertEqual(response.status_code, 403)
 
     def test_unpermitted_add_port_wo_obj_levels(self):
         c = Client()
         self.login(c, "user2")
-        self.u2.user_permissions.add(Permission.objects.get(
-            name='Can configure port forwards.'))
-        response = c.post("/dashboard/vm/1/", {'port': True,
-                                               'proto': 'tcp',
-                                               'port': '1337'})
-        self.assertEqual(response.status_code, 403)
-
-    def test_unpermitted_add_port_w_bad_host(self):
-        c = Client()
-        self.login(c, "user2")
         inst = Instance.objects.get(pk=1)
-        inst.set_level(self.u2, 'owner')
+        vlan = Vlan.objects.get(id=1)
+        vlan.set_level(self.u2, 'user')
+        inst.add_interface(user=self.u2, vlan=vlan, system=True)
+        host = Host.objects.get(
+            interface__in=inst.interface_set.all())
         self.u2.user_permissions.add(Permission.objects.get(
             name='Can configure port forwards.'))
-        response = c.post("/dashboard/vm/1/", {'proto': 'tcp',
-                                               'host_pk': '9999',
-                                               'port': '1337'})
+        with patch.object(AddPortOperation, 'async') as mock_method:
+            mock_method.side_effect = inst.add_port
+            response = c.post("/dashboard/vm/1/op/add_port/", {
+                'proto': 'tcp', 'host': host.pk, 'port': '1337'})
+            assert not mock_method.called
         self.assertEqual(response.status_code, 403)
 
-    def test_permitted_add_port_w_unhandled_exception(self):
+    def test_unpermitted_add_port_w_bad_host(self):
         c = Client()
         self.login(c, "user2")
         inst = Instance.objects.get(pk=1)
         inst.set_level(self.u2, 'owner')
-        vlan = Vlan.objects.get(id=1)
-        vlan.set_level(self.u2, 'user')
-        inst.add_interface(user=self.u2, vlan=vlan)
-        host = Host.objects.get(
-            interface__in=inst.interface_set.all())
         self.u2.user_permissions.add(Permission.objects.get(
             name='Can configure port forwards.'))
-        port_count = len(host.list_ports())
-        response = c.post("/dashboard/vm/1/", {'proto': 'tcp',
-                                               'host_pk': host.pk,
-                                               'port': 'invalid_port'})
-        self.assertEqual(response.status_code, 302)
-        self.assertEqual(len(host.list_ports()), port_count)
+        with patch.object(AddPortOperation, 'async') as mock_method:
+            mock_method.side_effect = inst.add_port
+            response = c.post("/dashboard/vm/1/op/add_port/", {
+                'proto': 'tcp', 'host': '9999', 'port': '1337'})
+            assert not mock_method.called
+        self.assertEqual(response.status_code, 200)
 
     def test_permitted_add_port(self):
         c = Client()
@@ -394,9 +392,11 @@ class VmDetailTest(LoginMixin, TestCase):
         self.u2.user_permissions.add(Permission.objects.get(
             name='Can configure port forwards.'))
         port_count = len(host.list_ports())
-        response = c.post("/dashboard/vm/1/", {'proto': 'tcp',
-                                               'host_pk': host.pk,
-                                               'port': '1337'})
+        with patch.object(AddPortOperation, 'async') as mock_method:
+            mock_method.side_effect = inst.add_port
+            response = c.post("/dashboard/vm/1/op/add_port/", {
+                'proto': 'tcp', 'host': host.pk, 'port': '1337'})
+            assert mock_method.called
         self.assertEqual(response.status_code, 302)
         self.assertEqual(len(host.list_ports()), port_count + 1)
 
@@ -637,7 +637,7 @@ class NodeDetailTest(LoginMixin, TestCase):
         c = Client()
         self.login(c, 'user1')
         response = c.get('/dashboard/node/25555/')
-        self.assertEqual(response.status_code, 302)
+        self.assertEqual(response.status_code, 403)
 
     def test_anon_node_page(self):
         c = Client()
@@ -667,7 +667,7 @@ class NodeDetailTest(LoginMixin, TestCase):
         node = Node.objects.get(pk=1)
         old_name = node.name
         response = c.post("/dashboard/node/1/", {'new_name': 'test1235'})
-        self.assertEqual(response.status_code, 302)
+        self.assertEqual(response.status_code, 403)
         self.assertEqual(Node.objects.get(pk=1).name, old_name)
 
     def test_permitted_set_name(self):
@@ -721,7 +721,7 @@ class NodeDetailTest(LoginMixin, TestCase):
         c = Client()
         self.login(c, "user2")
         response = c.post("/dashboard/node/1/", {'to_remove': traitid})
-        self.assertEqual(response.status_code, 302)
+        self.assertEqual(response.status_code, 403)
         self.assertEqual(Node.objects.get(pk=1).traits.count(), trait_count)
 
     def test_permitted_remove_trait(self):
diff --git a/circle/dashboard/urls.py b/circle/dashboard/urls.py
index a104389..9bbc5b2 100644
--- a/circle/dashboard/urls.py
+++ b/circle/dashboard/urls.py
@@ -26,9 +26,9 @@ from .views import (
     InstanceActivityDetail, LeaseCreate, LeaseDelete, LeaseDetail,
     MyPreferencesView, NodeAddTraitView, NodeCreate, NodeDelete,
     NodeDetailView, NodeList, NodeStatus,
-    NotificationView, PortDelete, TemplateAclUpdateView, TemplateCreate,
-    TemplateDelete, TemplateDetail, TemplateList, TransferOwnershipConfirmView,
-    TransferOwnershipView, vm_activity, VmCreate, VmDetailView,
+    NotificationView, TemplateAclUpdateView, TemplateCreate,
+    TemplateDelete, TemplateDetail, TemplateList,
+    vm_activity, VmCreate, VmDetailView,
     VmDetailVncTokenView, VmList,
     DiskRemoveView, get_disk_download_status, InterfaceDeleteView,
     GroupRemoveUserView,
@@ -45,8 +45,11 @@ from .views import (
     VmTraitsUpdate, VmRawDataUpdate,
     GroupPermissionsView,
     LeaseAclUpdateView,
+    toggle_template_tutorial,
     ClientCheck, TokenLogin,
     VmGraphView, NodeGraphView, NodeListGraphView,
+    TransferInstanceOwnershipView, TransferInstanceOwnershipConfirmView,
+    TransferTemplateOwnershipView, TransferTemplateOwnershipConfirmView,
 )
 from .views.vm import vm_ops, vm_mass_ops
 from .views.node import node_ops
@@ -77,15 +80,15 @@ urlpatterns = patterns(
         name="dashboard.views.template-list"),
     url(r"^template/delete/(?P<pk>\d+)/$", TemplateDelete.as_view(),
         name="dashboard.views.template-delete"),
-    url(r'^vm/(?P<pk>\d+)/remove_port/(?P<rule>\d+)/$', PortDelete.as_view(),
-        name='dashboard.views.remove-port'),
+    url(r'^template/(?P<pk>\d+)/tx/$', TransferTemplateOwnershipView.as_view(),
+        name='dashboard.views.template-transfer-ownership'),
     url(r'^vm/(?P<pk>\d+)/$', VmDetailView.as_view(),
         name='dashboard.views.detail'),
     url(r'^vm/(?P<pk>\d+)/vnctoken/$', VmDetailVncTokenView.as_view(),
         name='dashboard.views.detail-vnc'),
     url(r'^vm/(?P<pk>\d+)/acl/$', AclUpdateView.as_view(model=Instance),
         name='dashboard.views.vm-acl'),
-    url(r'^vm/(?P<pk>\d+)/tx/$', TransferOwnershipView.as_view(),
+    url(r'^vm/(?P<pk>\d+)/tx/$', TransferInstanceOwnershipView.as_view(),
         name='dashboard.views.vm-transfer-ownership'),
     url(r'^vm/list/$', VmList.as_view(), name='dashboard.views.vm-list'),
     url(r'^vm/create/$', VmCreate.as_view(),
@@ -99,14 +102,20 @@ urlpatterns = patterns(
         name='dashboard.views.vm-traits'),
     url(r'^vm/(?P<pk>\d+)/raw_data/$', VmRawDataUpdate.as_view(),
         name='dashboard.views.vm-raw-data'),
+    url(r'^vm/(?P<pk>\d+)/toggle_tutorial/$', toggle_template_tutorial,
+        name='dashboard.views.vm-toggle-tutorial'),
 
     url(r'^node/list/$', NodeList.as_view(), name='dashboard.views.node-list'),
     url(r'^node/(?P<pk>\d+)/$', NodeDetailView.as_view(),
         name='dashboard.views.node-detail'),
     url(r'^node/(?P<pk>\d+)/add-trait/$', NodeAddTraitView.as_view(),
         name='dashboard.views.node-addtrait'),
-    url(r'^tx/(?P<key>.*)/?$', TransferOwnershipConfirmView.as_view(),
+    url(r'^vm/tx/(?P<key>.*)/?$',
+        TransferInstanceOwnershipConfirmView.as_view(),
         name='dashboard.views.vm-transfer-ownership-confirm'),
+    url(r'^template/tx/(?P<key>.*)/?$',
+        TransferTemplateOwnershipConfirmView.as_view(),
+        name='dashboard.views.template-transfer-ownership-confirm'),
     url(r'^node/delete/(?P<pk>\d+)/$', NodeDelete.as_view(),
         name="dashboard.views.delete-node"),
     url(r'^node/status/(?P<pk>\d+)/$', NodeStatus.as_view(),
diff --git a/circle/dashboard/views/graph.py b/circle/dashboard/views/graph.py
index fdd9699..472ea1e 100644
--- a/circle/dashboard/views/graph.py
+++ b/circle/dashboard/views/graph.py
@@ -26,7 +26,7 @@ from django.http import HttpResponse, Http404
 from django.utils.translation import ugettext_lazy as _
 from django.views.generic import View
 
-from braces.views import LoginRequiredMixin, SuperuserRequiredMixin
+from braces.views import LoginRequiredMixin
 
 from vm.models import Instance, Node
 
@@ -142,22 +142,28 @@ class VmGraphView(GraphViewBase):
     base = VmMetric
 
 
-class NodeGraphView(SuperuserRequiredMixin, GraphViewBase):
+class NodeGraphView(GraphViewBase):
     model = Node
     base = NodeMetric
 
     def get_object(self, request, pk):
+        if not self.request.user.has_perm('vm.view_statistics'):
+            raise PermissionDenied()
         return self.model.objects.get(id=pk)
 
 
-class NodeListGraphView(SuperuserRequiredMixin, GraphViewBase):
+class NodeListGraphView(GraphViewBase):
     model = Node
     base = Metric
 
     def get_object(self, request, pk):
+        if not self.request.user.has_perm('vm.view_statistics'):
+            raise PermissionDenied()
         return Node.objects.filter(enabled=True)
 
     def get(self, request, metric, time, *args, **kwargs):
+        if not self.request.user.has_perm('vm.view_statistics'):
+            raise PermissionDenied()
         return super(NodeListGraphView, self).get(request, None, metric, time)
 
 
diff --git a/circle/dashboard/views/index.py b/circle/dashboard/views/index.py
index 9f7bba0..bd4839a 100644
--- a/circle/dashboard/views/index.py
+++ b/circle/dashboard/views/index.py
@@ -62,7 +62,7 @@ class IndexView(LoginRequiredMixin, TemplateView):
         })
 
         # nodes
-        if user.is_superuser:
+        if user.has_perm('vm.view_statistics'):
             nodes = Node.objects.all()
             context.update({
                 'nodes': nodes[:5],
diff --git a/circle/dashboard/views/node.py b/circle/dashboard/views/node.py
index 2f44962..1d2293c 100644
--- a/circle/dashboard/views/node.py
+++ b/circle/dashboard/views/node.py
@@ -75,13 +75,18 @@ node_ops = OrderedDict([
 ])
 
 
-class NodeDetailView(LoginRequiredMixin, SuperuserRequiredMixin,
+class NodeDetailView(LoginRequiredMixin,
                      GraphMixin, DetailView):
     template_name = "dashboard/node-detail.html"
     model = Node
     form = None
     form_class = TraitForm
 
+    def get(self, *args, **kwargs):
+        if not self.request.user.has_perm('vm.view_statistics'):
+            raise PermissionDenied()
+        return super(NodeDetailView, self).get(*args, **kwargs)
+
     def get_context_data(self, form=None, **kwargs):
         if form is None:
             form = self.form_class()
@@ -98,6 +103,8 @@ class NodeDetailView(LoginRequiredMixin, SuperuserRequiredMixin,
         return context
 
     def post(self, request, *args, **kwargs):
+        if not request.user.is_superuser:
+            raise PermissionDenied()
         if request.POST.get('new_name'):
             return self.__set_name(request)
         if request.POST.get('to_remove'):
@@ -145,13 +152,14 @@ class NodeDetailView(LoginRequiredMixin, SuperuserRequiredMixin,
             return redirect(self.object.get_absolute_url())
 
 
-class NodeList(LoginRequiredMixin, SuperuserRequiredMixin,
-               GraphMixin, SingleTableView):
+class NodeList(LoginRequiredMixin, GraphMixin, SingleTableView):
     template_name = "dashboard/node-list.html"
     table_class = NodeListTable
     table_pagination = False
 
     def get(self, *args, **kwargs):
+        if not self.request.user.has_perm('vm.view_statistics'):
+            raise PermissionDenied()
         if self.request.is_ajax():
             nodes = Node.objects.all()
             nodes = [{
diff --git a/circle/dashboard/views/store.py b/circle/dashboard/views/store.py
index a7debdc..d7acf3c 100644
--- a/circle/dashboard/views/store.py
+++ b/circle/dashboard/views/store.py
@@ -23,6 +23,7 @@ from os.path import join, normpath, dirname, basename
 from django.conf import settings
 from django.contrib import messages
 from django.contrib.auth.decorators import login_required
+from django.template.defaultfilters import urlencode
 from django.core.cache import get_cache
 from django.core.exceptions import SuspiciousOperation
 from django.core.urlresolvers import reverse
@@ -55,7 +56,7 @@ class StoreList(LoginRequiredMixin, TemplateView):
         context['current'] = directory
         context['next_url'] = "%s%s?directory=%s" % (
             settings.DJANGO_URL.rstrip("/"),
-            reverse("dashboard.views.store-list"), directory)
+            reverse("dashboard.views.store-list"), urlencode(directory))
         return context
 
     def get(self, *args, **kwargs):
@@ -112,7 +113,7 @@ def store_upload(request):
 
     next_url = "%s%s?directory=%s" % (
         settings.DJANGO_URL.rstrip("/"),
-        reverse("dashboard.views.store-list"), directory)
+        reverse("dashboard.views.store-list"), urlencode(directory))
 
     return render(request, "dashboard/store/upload.html",
                   {'directory': directory, 'action': action,
@@ -168,7 +169,7 @@ class StoreRemove(LoginRequiredMixin, TemplateView):
 
         return redirect("%s?directory=%s" % (
             reverse("dashboard.views.store-list"),
-            dirname(dirname(path)),
+            urlencode(dirname(dirname(path))),
         ))
 
 
@@ -185,7 +186,7 @@ def store_new_directory(request):
                          name, path, unicode(request.user))
         messages.error(request, _("Unable to create folder."))
     return redirect("%s?directory=%s" % (
-        reverse("dashboard.views.store-list"), path))
+        reverse("dashboard.views.store-list"), urlencode(path)))
 
 
 @require_POST
diff --git a/circle/dashboard/views/template.py b/circle/dashboard/views/template.py
index d5fd13b..568d903 100644
--- a/circle/dashboard/views/template.py
+++ b/circle/dashboard/views/template.py
@@ -26,13 +26,13 @@ from django.core.urlresolvers import reverse, reverse_lazy
 from django.core.exceptions import PermissionDenied, SuspiciousOperation
 from django.http import HttpResponse, HttpResponseRedirect
 from django.shortcuts import redirect, get_object_or_404
-from django.utils.translation import ugettext as _
+from django.utils.translation import ugettext as _, ugettext_noop
 from django.views.generic import (
     TemplateView, CreateView, DeleteView, UpdateView,
 )
 
 from braces.views import (
-    LoginRequiredMixin, PermissionRequiredMixin, SuperuserRequiredMixin,
+    LoginRequiredMixin, PermissionRequiredMixin,
 )
 from django_tables2 import SingleTableView
 
@@ -44,7 +44,10 @@ from ..forms import (
 )
 from ..tables import TemplateListTable, LeaseListTable
 
-from .util import AclUpdateView, FilterMixin
+from .util import (
+    AclUpdateView, FilterMixin,
+    TransferOwnershipConfirmView, TransferOwnershipView,
+)
 
 logger = logging.getLogger(__name__)
 
@@ -240,6 +243,16 @@ class TemplateDelete(LoginRequiredMixin, DeleteView):
         else:
             return ['dashboard/confirm/base-delete.html']
 
+    def get(self, request, *args, **kwargs):
+        if not self.get_object().has_level(request.user, "owner"):
+            message = _("Only the owners can delete the selected template.")
+            if request.is_ajax():
+                raise PermissionDenied()
+            else:
+                messages.warning(request, message)
+                return redirect(self.get_success_url())
+        return super(TemplateDelete, self).get(request, *args, **kwargs)
+
     def delete(self, request, *args, **kwargs):
         object = self.get_object()
         if not object.has_level(request.user, 'owner'):
@@ -382,13 +395,17 @@ class LeaseCreate(LoginRequiredMixin, PermissionRequiredMixin,
     def get_success_url(self):
         return reverse_lazy("dashboard.views.template-list")
 
+    def form_valid(self, form):
+        retval = super(LeaseCreate, self).form_valid(form)
+        self.object.set_level(self.request.user, "owner")
+        return retval
+
 
 class LeaseAclUpdateView(AclUpdateView):
     model = Lease
 
 
-class LeaseDetail(LoginRequiredMixin, SuperuserRequiredMixin,
-                  SuccessMessageMixin, UpdateView):
+class LeaseDetail(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
     model = Lease
     form_class = LeaseForm
     template_name = "dashboard/lease-edit.html"
@@ -404,8 +421,21 @@ class LeaseDetail(LoginRequiredMixin, SuperuserRequiredMixin,
     def get_success_url(self):
         return reverse_lazy("dashboard.views.lease-detail", kwargs=self.kwargs)
 
+    def get(self, request, *args, **kwargs):
+        if not self.get_object().has_level(request.user, "owner"):
+            message = _("Only the owners can modify the selected lease.")
+            messages.warning(request, message)
+            return redirect(reverse_lazy("dashboard.views.template-list"))
+        return super(LeaseDetail, self).get(request, *args, **kwargs)
+
+    def post(self, request, *args, **kwargs):
+        if not self.get_object().has_level(request.user, "owner"):
+            raise PermissionDenied()
+
+        return super(LeaseDetail, self).post(request, *args, **kwargs)
+
 
-class LeaseDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView):
+class LeaseDelete(LoginRequiredMixin, DeleteView):
     model = Lease
 
     def get_success_url(self):
@@ -431,10 +461,22 @@ class LeaseDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView):
             c['disable_submit'] = True
         return c
 
+    def get(self, request, *args, **kwargs):
+        if not self.get_object().has_level(request.user, "owner"):
+            message = _("Only the owners can delete the selected lease.")
+            if request.is_ajax():
+                raise PermissionDenied()
+            else:
+                messages.warning(request, message)
+                return redirect(self.get_success_url())
+        return super(LeaseDelete, self).get(request, *args, **kwargs)
+
     def delete(self, request, *args, **kwargs):
         object = self.get_object()
 
-        if (object.instancetemplate_set.count() > 0):
+        if not object.has_level(request.user, "owner"):
+            raise PermissionDenied()
+        if object.instancetemplate_set.count() > 0:
             raise SuspiciousOperation()
 
         object.delete()
@@ -449,3 +491,20 @@ class LeaseDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView):
         else:
             messages.success(request, success_message)
             return HttpResponseRedirect(success_url)
+
+
+class TransferTemplateOwnershipConfirmView(TransferOwnershipConfirmView):
+    template = "dashboard/confirm/transfer-template-ownership.html"
+    model = InstanceTemplate
+
+
+class TransferTemplateOwnershipView(TransferOwnershipView):
+    confirm_view = TransferTemplateOwnershipConfirmView
+    model = InstanceTemplate
+    notification_msg = ugettext_noop(
+        '%(user)s offered you to take the ownership of '
+        'his/her template called %(instance)s. '
+        '<a href="%(token)s" '
+        'class="btn btn-success btn-small">Accept</a>')
+    token_url = 'dashboard.views.template-transfer-ownership-confirm'
+    template = "dashboard/template-tx-owner.html"
diff --git a/circle/dashboard/views/util.py b/circle/dashboard/views/util.py
index c870c4a..73e74d7 100644
--- a/circle/dashboard/views/util.py
+++ b/circle/dashboard/views/util.py
@@ -24,14 +24,15 @@ from urlparse import urljoin
 
 from django.conf import settings
 from django.contrib.auth.models import User, Group
-from django.core.exceptions import PermissionDenied
+from django.core import signing
+from django.core.exceptions import PermissionDenied, SuspiciousOperation
 from django.core.urlresolvers import reverse
 from django.contrib import messages
 from django.contrib.auth.views import redirect_to_login
 from django.db.models import Q
-from django.http import HttpResponse, HttpResponseRedirect
-from django.shortcuts import redirect
-from django.utils.translation import ugettext_lazy as _
+from django.http import HttpResponse, Http404, HttpResponseRedirect
+from django.shortcuts import redirect, render
+from django.utils.translation import ugettext_lazy as _, ugettext_noop
 from django.views.generic import DetailView, View
 from django.views.generic.detail import SingleObjectMixin
 
@@ -40,7 +41,8 @@ from braces.views._access import AccessMixin
 from celery.exceptions import TimeoutError
 
 from common.models import HumanReadableException, HumanReadableObject
-from ..models import GroupProfile
+from ..models import GroupProfile, Profile
+from ..forms import TransferOwnershipForm
 
 logger = logging.getLogger(__name__)
 saml_available = hasattr(settings, "SAML_CONFIG")
@@ -563,3 +565,132 @@ class GraphMixin(object):
 
 def absolute_url(url):
     return urljoin(settings.DJANGO_URL, url)
+
+
+class TransferOwnershipView(CheckedDetailView, DetailView):
+    def get_template_names(self):
+        if self.request.is_ajax():
+            return ['dashboard/_modal.html']
+        else:
+            return ['dashboard/nojs-wrapper.html']
+
+    def get_context_data(self, *args, **kwargs):
+        context = super(TransferOwnershipView, self).get_context_data(
+            *args, **kwargs)
+        context['form'] = TransferOwnershipForm()
+        context.update({
+            'box_title': _("Transfer ownership"),
+            'ajax_title': True,
+            'template': self.template,
+        })
+        return context
+
+    def post(self, request, *args, **kwargs):
+        form = TransferOwnershipForm(request.POST)
+        if not form.is_valid():
+            return self.get(request)
+        try:
+            new_owner = search_user(request.POST['name'])
+        except User.DoesNotExist:
+            messages.error(request, _('Can not find specified user.'))
+            return self.get(request, *args, **kwargs)
+        except KeyError:
+            raise SuspiciousOperation()
+
+        obj = self.get_object()
+        if not (obj.owner == request.user or
+                request.user.is_superuser):
+            raise PermissionDenied()
+
+        token = signing.dumps(
+            (obj.pk, new_owner.pk),
+            salt=self.confirm_view.get_salt())
+        token_path = reverse(self.token_url, args=[token])
+        try:
+            new_owner.profile.notify(
+                ugettext_noop('Ownership offer'),
+                self.notification_msg,
+                {'instance': obj, 'token': token_path})
+        except Profile.DoesNotExist:
+            messages.error(request, _('Can not notify selected user.'))
+        else:
+            messages.success(request,
+                             _('User %s is notified about the offer.') % (
+                                 unicode(new_owner), ))
+
+        return redirect(obj.get_absolute_url())
+
+
+class TransferOwnershipConfirmView(LoginRequiredMixin, View):
+    """User can accept an ownership offer."""
+
+    max_age = 3 * 24 * 3600
+    success_message = _("Ownership successfully transferred to you.")
+
+    @classmethod
+    def get_salt(cls):
+        return unicode(cls) + unicode(cls.model)
+
+    def get(self, request, key, *args, **kwargs):
+        """Confirm ownership transfer based on token.
+        """
+        logger.debug('Confirm dialog for token %s.', key)
+        try:
+            instance, new_owner = self.get_instance(key, request.user)
+        except PermissionDenied:
+            messages.error(request, _('This token is for an other user.'))
+            raise
+        except SuspiciousOperation:
+            messages.error(request, _('This token is invalid or has expired.'))
+            raise PermissionDenied()
+        return render(request, self.template,
+                      dictionary={'instance': instance, 'key': key})
+
+    def change_owner(self, instance, new_owner):
+        instance.owner = new_owner
+        instance.clean()
+        instance.save()
+
+    def post(self, request, key, *args, **kwargs):
+        """Really transfer ownership based on token.
+        """
+        instance, owner = self.get_instance(key, request.user)
+
+        old = instance.owner
+        self.change_owner(instance, request.user)
+        messages.success(request, self.success_message)
+        logger.info('Ownership of %s transferred from %s to %s.',
+                    unicode(instance), unicode(old), unicode(request.user))
+        if old.profile:
+            old.profile.notify(
+                ugettext_noop('Ownership accepted'),
+                ugettext_noop('Your ownership offer of %(instance)s has been '
+                              'accepted by %(user)s.'),
+                {'instance': instance})
+        return redirect(instance.get_absolute_url())
+
+    def get_instance(self, key, user):
+        """Get object based on signed token.
+        """
+        try:
+            instance, new_owner = (
+                signing.loads(key, max_age=self.max_age,
+                              salt=self.get_salt()))
+        except (signing.BadSignature, ValueError, TypeError) as e:
+            logger.error('Tried invalid token. Token: %s, user: %s. %s',
+                         key, unicode(user), unicode(e))
+            raise SuspiciousOperation()
+
+        try:
+            instance = self.model.objects.get(id=instance)
+        except self.model.DoesNotExist as e:
+            logger.error('Tried token to nonexistent instance %d. '
+                         'Token: %s, user: %s. %s',
+                         instance, key, unicode(user), unicode(e))
+            raise Http404()
+
+        if new_owner != user.pk:
+            logger.error('%s (%d) tried the token for %s. Token: %s.',
+                         unicode(user), user.pk, new_owner, key)
+            raise PermissionDenied()
+        return (instance, new_owner)
diff --git a/circle/dashboard/views/vm.py b/circle/dashboard/views/vm.py
index 38f3dfe..b02153f 100644
--- a/circle/dashboard/views/vm.py
+++ b/circle/dashboard/views/vm.py
@@ -24,11 +24,12 @@ from os import getenv
 from django.conf import settings
 from django.contrib import messages
 from django.contrib.auth.models import User
+from django.contrib.auth.decorators import login_required
 from django.core import signing
 from django.core.exceptions import PermissionDenied, SuspiciousOperation
 from django.core.urlresolvers import reverse, reverse_lazy
 from django.http import HttpResponse, Http404, HttpResponseRedirect
-from django.shortcuts import redirect, get_object_or_404, render
+from django.shortcuts import redirect, get_object_or_404
 from django.template import RequestContext
 from django.template.loader import render_to_string
 from django.utils.translation import (
@@ -36,7 +37,7 @@ from django.utils.translation import (
 )
 from django.views.decorators.http import require_GET
 from django.views.generic import (
-    UpdateView, ListView, TemplateView, DeleteView, DetailView, View,
+    UpdateView, ListView, TemplateView, DeleteView
 )
 
 from braces.views import SuperuserRequiredMixin, LoginRequiredMixin
@@ -53,16 +54,18 @@ from vm.models import (
 )
 from .util import (
     CheckedDetailView, AjaxOperationMixin, OperationView, AclUpdateView,
-    FormOperationMixin, FilterMixin, search_user, GraphMixin,
+    FormOperationMixin, FilterMixin, GraphMixin,
+    TransferOwnershipConfirmView, TransferOwnershipView,
 )
 from ..forms import (
     AclUserOrGroupAddForm, VmResourcesForm, TraitsForm, RawDataForm,
     VmAddInterfaceForm, VmCreateDiskForm, VmDownloadDiskForm, VmSaveForm,
     VmRenewForm, VmStateChangeForm, VmListSearchForm, VmCustomizeForm,
-    TransferOwnershipForm, VmDiskResizeForm, RedeployForm, VmDiskRemoveForm,
-    VmMigrateForm,
+    VmDiskResizeForm, RedeployForm, VmDiskRemoveForm,
+    VmMigrateForm, VmDeployForm,
+    VmPortRemoveForm, VmPortAddForm,
 )
-from ..models import Favourite, Profile
+from ..models import Favourite
 
 logger = logging.getLogger(__name__)
 
@@ -102,13 +105,17 @@ class VmDetailView(GraphMixin, CheckedDetailView):
         is_operator = instance.has_level(user, "operator")
         is_owner = instance.has_level(user, "owner")
         ops = get_operations(instance, user)
+        hide_tutorial = self.request.COOKIES.get(
+            "hide_tutorial_for_%s" % instance.pk) == "True"
         context.update({
             'graphite_enabled': settings.GRAPHITE_URL is not None,
             'vnc_url': reverse_lazy("dashboard.views.detail-vnc",
                                     kwargs={'pk': self.object.pk}),
             'ops': ops,
             'op': {i.op: i for i in ops},
-            'connect_commands': user.profile.get_connect_commands(instance)
+            'connect_commands': user.profile.get_connect_commands(instance),
+            'hide_tutorial': hide_tutorial,
+            'fav': instance.favourite_set.filter(user=user).exists(),
         })
 
         # activity data
@@ -170,7 +177,6 @@ class VmDetailView(GraphMixin, CheckedDetailView):
             'new_description': self.__set_description,
             'new_tag': self.__add_tag,
             'to_remove': self.__remove_tag,
-            'port': self.__add_port,
             'abort_operation': self.__abort_operation,
         }
         for k, v in options.iteritems():
@@ -266,40 +272,6 @@ class VmDetailView(GraphMixin, CheckedDetailView):
             return redirect(reverse_lazy("dashboard.views.detail",
                             kwargs={'pk': self.object.pk}))
 
-    def __add_port(self, request):
-        object = self.get_object()
-        if not (object.has_level(request.user, "operator") and
-                request.user.has_perm('vm.config_ports')):
-            raise PermissionDenied()
-
-        port = request.POST.get("port")
-        proto = request.POST.get("proto")
-
-        try:
-            error = None
-            interfaces = object.interface_set.all()
-            host = Host.objects.get(pk=request.POST.get("host_pk"),
-                                    interface__in=interfaces)
-            host.add_port(proto, private=port)
-        except Host.DoesNotExist:
-            logger.error('Tried to add port to nonexistent host %d. User: %s. '
-                         'Instance: %s', request.POST.get("host_pk"),
-                         unicode(request.user), object)
-            raise PermissionDenied()
-        except ValueError:
-            error = _("There is a problem with your input.")
-        except Exception as e:
-            error = _("Unknown error.")
-            logger.error(e)
-
-        if request.is_ajax():
-            pass
-        else:
-            if error:
-                messages.error(request, error)
-            return redirect(reverse_lazy("dashboard.views.detail",
-                                         kwargs={'pk': self.get_object().pk}))
-
     def __abort_operation(self, request):
         self.object = self.get_object()
 
@@ -446,6 +418,62 @@ class VmMigrateView(FormOperationMixin, VmOperationView):
         return val
 
 
+class VmPortRemoveView(FormOperationMixin, VmOperationView):
+
+    template_name = 'dashboard/_vm-remove-port.html'
+    op = 'remove_port'
+    show_in_toolbar = False
+    with_reload = True
+    wait_for_result = 0.5
+    icon = 'times'
+    effect = "danger"
+    form_class = VmPortRemoveForm
+
+    def get_form_kwargs(self):
+        instance = self.get_op().instance
+        choices = Rule.portforwards().filter(
+            host__interface__instance=instance)
+        rule_pk = self.request.GET.get('rule')
+        if rule_pk:
+            try:
+                default = choices.get(pk=rule_pk)
+            except (ValueError, Rule.DoesNotExist):
+                raise Http404()
+        else:
+            default = None
+
+        val = super(VmPortRemoveView, self).get_form_kwargs()
+        val.update({'choices': choices, 'default': default})
+        return val
+
+
+class VmPortAddView(FormOperationMixin, VmOperationView):
+
+    op = 'add_port'
+    show_in_toolbar = False
+    with_reload = True
+    wait_for_result = 0.5
+    icon = 'plus'
+    effect = "success"
+    form_class = VmPortAddForm
+
+    def get_form_kwargs(self):
+        instance = self.get_op().instance
+        choices = Host.objects.filter(interface__instance=instance)
+        host_pk = self.request.GET.get('host')
+        if host_pk:
+            try:
+                default = choices.get(pk=host_pk)
+            except (ValueError, Host.DoesNotExist):
+                raise Http404()
+        else:
+            default = None
+
+        val = super(VmPortAddView, self).get_form_kwargs()
+        val.update({'choices': choices, 'default': default})
+        return val
+
+
 class VmSaveView(FormOperationMixin, VmOperationView):
 
     op = 'save_as_template'
@@ -457,6 +485,10 @@ class VmSaveView(FormOperationMixin, VmOperationView):
         op = self.get_op()
         val = super(VmSaveView, self).get_form_kwargs()
         val['default'] = op._rename(op.instance.name)
+        obj = self.get_object()
+        if obj.template and obj.template.has_level(
+                self.request.user, "owner"):
+            val['clone'] = True
         return val
 
 
@@ -605,7 +637,6 @@ class VmStateChangeView(FormOperationMixin, VmOperationView):
     op = 'emergency_change_state'
     icon = 'legal'
     effect = 'danger'
-    show_in_toolbar = True
     form_class = VmStateChangeForm
     wait_for_result = 0.5
 
@@ -628,9 +659,23 @@ class RedeployView(FormOperationMixin, VmOperationView):
     wait_for_result = 0.5
 
 
+class VmDeployView(FormOperationMixin, VmOperationView):
+    op = 'deploy'
+    icon = 'play'
+    effect = 'success'
+    form_class = VmDeployForm
+
+    def get_form_kwargs(self):
+        kwargs = super(VmDeployView, self).get_form_kwargs()
+        if self.request.user.is_superuser:
+            online = (n.pk for n in
+                      Node.objects.filter(enabled=True) if n.online)
+            kwargs['choices'] = Node.objects.filter(pk__in=online)
+        return kwargs
+
+
 vm_ops = OrderedDict([
-    ('deploy', VmOperationView.factory(
-        op='deploy', icon='play', effect='success')),
+    ('deploy', VmDeployView),
     ('wake_up', VmOperationView.factory(
         op='wake_up', icon='sun-o', effect='success')),
     ('sleep', VmOperationView.factory(
@@ -645,7 +690,7 @@ vm_ops = OrderedDict([
     ('shutdown', VmOperationView.factory(
         op='shutdown', icon='power-off', effect='warning')),
     ('shut_off', VmOperationView.factory(
-        op='shut_off', icon='ban', effect='warning')),
+        op='shut_off', icon='plug', effect='warning')),
     ('recover', VmOperationView.factory(
         op='recover', icon='medkit', effect='warning')),
     ('nostate', VmStateChangeView),
@@ -662,6 +707,8 @@ vm_ops = OrderedDict([
         op='remove_disk', form_class=VmDiskRemoveForm,
         icon='times', effect="danger")),
     ('add_interface', VmAddInterfaceView),
+    ('remove_port', VmPortRemoveView),
+    ('add_port', VmPortAddView),
     ('renew', VmRenewView),
     ('resources_change', VmResourcesChangeView),
     ('password_reset', VmOperationView.factory(
@@ -1145,52 +1192,6 @@ def get_disk_download_status(request, pk):
     )
 
 
-class PortDelete(LoginRequiredMixin, DeleteView):
-    model = Rule
-    pk_url_kwarg = 'rule'
-
-    def get_template_names(self):
-        if self.request.is_ajax():
-            return ['dashboard/confirm/ajax-delete.html']
-        else:
-            return ['dashboard/confirm/base-delete.html']
-
-    def get_context_data(self, **kwargs):
-        context = super(PortDelete, self).get_context_data(**kwargs)
-        rule = kwargs.get('object')
-        instance = rule.host.interface_set.get().instance
-        context['title'] = _("Port delete confirmation")
-        context['text'] = _("Are you sure you want to close %(port)d/"
-                            "%(proto)s on %(vm)s?" % {'port': rule.dport,
-                                                      'proto': rule.proto,
-                                                      'vm': instance})
-        return context
-
-    def delete(self, request, *args, **kwargs):
-        rule = Rule.objects.get(pk=kwargs.get("rule"))
-        instance = rule.host.interface_set.get().instance
-        if not instance.has_level(request.user, 'owner'):
-            raise PermissionDenied()
-
-        super(PortDelete, self).delete(request, *args, **kwargs)
-
-        success_url = self.get_success_url()
-        success_message = _("Port successfully removed.")
-
-        if request.is_ajax():
-            return HttpResponse(
-                json.dumps({'message': success_message}),
-                content_type="application/json",
-            )
-        else:
-            messages.success(request, success_message)
-            return HttpResponseRedirect("%s#network" % success_url)
-
-    def get_success_url(self):
-        return reverse_lazy('dashboard.views.detail',
-                            kwargs={'pk': self.kwargs.get("pk")})
-
-
 class ClientCheck(LoginRequiredMixin, TemplateView):
 
     def get_template_names(self):
@@ -1285,136 +1286,36 @@ class FavouriteView(TemplateView):
             return HttpResponse("Added.")
 
 
-class TransferOwnershipView(CheckedDetailView, DetailView):
+class TransferInstanceOwnershipConfirmView(TransferOwnershipConfirmView):
+    template = "dashboard/confirm/transfer-instance-ownership.html"
     model = Instance
 
-    def get_template_names(self):
-        if self.request.is_ajax():
-            return ['dashboard/_modal.html']
-        else:
-            return ['dashboard/nojs-wrapper.html']
-
-    def get_context_data(self, *args, **kwargs):
-        context = super(TransferOwnershipView, self).get_context_data(
-            *args, **kwargs)
-        context['form'] = TransferOwnershipForm()
-        context.update({
-            'box_title': _("Transfer ownership"),
-            'ajax_title': True,
-            'template': "dashboard/vm-detail/tx-owner.html",
-        })
-        return context
-
-    def post(self, request, *args, **kwargs):
-        form = TransferOwnershipForm(request.POST)
-        if not form.is_valid():
-            return self.get(request)
-        try:
-            new_owner = search_user(request.POST['name'])
-        except User.DoesNotExist:
-            messages.error(request, _('Can not find specified user.'))
-            return self.get(request, *args, **kwargs)
-        except KeyError:
-            raise SuspiciousOperation()
-
-        obj = self.get_object()
-        if not (obj.owner == request.user or
-                request.user.is_superuser):
-            raise PermissionDenied()
-
-        token = signing.dumps((obj.pk, new_owner.pk),
-                              salt=TransferOwnershipConfirmView.get_salt())
-        token_path = reverse(
-            'dashboard.views.vm-transfer-ownership-confirm', args=[token])
-        try:
-            new_owner.profile.notify(
-                ugettext_noop('Ownership offer'),
-                ugettext_noop('%(user)s offered you to take the ownership of '
-                              'his/her virtual machine called %(instance)s. '
-                              '<a href="%(token)s" '
-                              'class="btn btn-success btn-small">Accept</a>'),
-                {'instance': obj, 'token': token_path})
-        except Profile.DoesNotExist:
-            messages.error(request, _('Can not notify selected user.'))
-        else:
-            messages.success(request,
-                             _('User %s is notified about the offer.') % (
-                                 unicode(new_owner), ))
-
-        return redirect(reverse_lazy("dashboard.views.detail",
-                                     kwargs={'pk': obj.pk}))
-
-
-class TransferOwnershipConfirmView(LoginRequiredMixin, View):
-    """User can accept an ownership offer."""
+    def change_owner(self, instance, new_owner):
+        with instance.activity(
+            code_suffix='ownership-transferred',
+                readable_name=ugettext_noop("transfer ownership"),
+                concurrency_check=False, user=new_owner):
+            super(TransferInstanceOwnershipConfirmView, self).change_owner(
+                instance, new_owner)
 
-    max_age = 3 * 24 * 3600
-    success_message = _("Ownership successfully transferred to you.")
-
-    @classmethod
-    def get_salt(cls):
-        return unicode(cls)
 
-    def get(self, request, key, *args, **kwargs):
-        """Confirm ownership transfer based on token.
-        """
-        logger.debug('Confirm dialog for token %s.', key)
-        try:
-            instance, new_owner = self.get_instance(key, request.user)
-        except PermissionDenied:
-            messages.error(request, _('This token is for an other user.'))
-            raise
-        except SuspiciousOperation:
-            messages.error(request, _('This token is invalid or has expired.'))
-            raise PermissionDenied()
-        return render(request,
-                      "dashboard/confirm/base-transfer-ownership.html",
-                      dictionary={'instance': instance, 'key': key})
-
-    def post(self, request, key, *args, **kwargs):
-        """Really transfer ownership based on token.
-        """
-        instance, owner = self.get_instance(key, request.user)
-
-        old = instance.owner
-        with instance.activity(code_suffix='ownership-transferred',
-                               concurrency_check=False, user=request.user):
-            instance.owner = request.user
-            instance.clean()
-            instance.save()
-        messages.success(request, self.success_message)
-        logger.info('Ownership of %s transferred from %s to %s.',
-                    unicode(instance), unicode(old), unicode(request.user))
-        if old.profile:
-            old.profile.notify(
-                ugettext_noop('Ownership accepted'),
-                ugettext_noop('Your ownership offer of %(instance)s has been '
-                              'accepted by %(user)s.'),
-                {'instance': instance})
-        return redirect(instance.get_absolute_url())
-
-    def get_instance(self, key, user):
-        """Get object based on signed token.
-        """
-        try:
-            instance, new_owner = (
-                signing.loads(key, max_age=self.max_age,
-                              salt=self.get_salt()))
-        except (signing.BadSignature, ValueError, TypeError) as e:
-            logger.error('Tried invalid token. Token: %s, user: %s. %s',
-                         key, unicode(user), unicode(e))
-            raise SuspiciousOperation()
-
-        try:
-            instance = Instance.objects.get(id=instance)
-        except Instance.DoesNotExist as e:
-            logger.error('Tried token to nonexistent instance %d. '
-                         'Token: %s, user: %s. %s',
-                         instance, key, unicode(user), unicode(e))
-            raise Http404()
-
-        if new_owner != user.pk:
-            logger.error('%s (%d) tried the token for %s. Token: %s.',
-                         unicode(user), user.pk, new_owner, key)
-            raise PermissionDenied()
-        return (instance, new_owner)
+class TransferInstanceOwnershipView(TransferOwnershipView):
+    confirm_view = TransferInstanceOwnershipConfirmView
+    model = Instance
+    notification_msg = ugettext_noop(
+        '%(user)s offered you to take the ownership of '
+        'his/her virtual machine called %(instance)s. '
+        '<a href="%(token)s" '
+        'class="btn btn-success btn-small">Accept</a>')
+    token_url = 'dashboard.views.vm-transfer-ownership-confirm'
+    template = "dashboard/vm-detail/tx-owner.html"
+
+
+@login_required
+def toggle_template_tutorial(request, pk):
+    hidden = request.POST.get("hidden", "").lower() == "true"
+    instance = get_object_or_404(Instance, pk=pk)
+    response = HttpResponseRedirect(instance.get_absolute_url())
+    response.set_cookie(  # for a week
+        "hide_tutorial_for_%s" % pk, hidden, 7 * 24 * 60 * 60)
+    return response
diff --git a/circle/fabfile.py b/circle/fabfile.py
index bc97eaf..4c595ff 100755
--- a/circle/fabfile.py
+++ b/circle/fabfile.py
@@ -84,6 +84,10 @@ def make_messages():
 def test(test=""):
     "Run portal tests"
     with _workon("circle"), cd("~/circle/circle"):
+        if test == "f":
+            test = "--failed"
+        else:
+            test += " --with-id"
         run("./manage.py test --settings=circle.settings.test %s" % test)
 
 
@@ -99,10 +103,12 @@ def pull(dir="~/circle/circle"):
 
 
 @roles('portal')
-def update_portal(test=False):
+def update_portal(test=False, git=True):
     "Update and restart portal+manager"
     with _stopped("portal", "manager"):
-        pull()
+        if git:
+            pull()
+        cleanup()
         pip("circle", "~/circle/requirements.txt")
         migrate()
         compile_things()
@@ -111,6 +117,12 @@ def update_portal(test=False):
 
 
 @roles('portal')
+def build_portal():
+    "Update portal without pulling from git"
+    return update_portal(False, False)
+
+
+@roles('portal')
 def stop_portal(test=False):
     "Stop portal and manager"
     _stop_services("portal", "manager")
@@ -122,10 +134,15 @@ def update_node():
     with _stopped("node", "agentdriver", "monitor-client"):
         pull("~/vmdriver")
         pip("vmdriver", "~/vmdriver/requirements/production.txt")
+        _cleanup("~/vmdriver")
+
         pull("~/agentdriver")
         pip("agentdriver", "~/agentdriver/requirements.txt")
+        _cleanup("~/agentdriver")
+
         pull("~/monitor-client")
         pip("monitor-client", "~/monitor-client/requirements.txt")
+        _cleanup("~/monitor-client")
 
 
 @parallel
@@ -147,6 +164,18 @@ def checkout(vmdriver="master", agent="master"):
         run("git checkout %s" % agent)
 
 
+@roles('portal')
+def cleanup():
+    "Clean pyc files of portal"
+    _cleanup()
+
+
+def _cleanup(dir="~/circle/circle"):
+    "Clean pyc files"
+    with cd("~/circle/circle"):
+        run("find -name '*.py[co]' -exec rm -f {} +")
+
+
 def _stop_services(*services):
     "Stop given services (warn only if not running)"
     with settings(warn_only=True):
@@ -175,3 +204,12 @@ def _stopped(*services):
 def _workon(name):
     return prefix("source ~/.virtualenvs/%s/bin/activate && "
                   "source ~/.virtualenvs/%s/bin/postactivate" % (name, name))
+
+
+@roles('portal')
+def install_bash_completion_script():
+    sudo("wget https://raw.githubusercontent.com/marcelor/fabric-bash-"
+         "autocompletion/48baf5735bafbb2be5be8787d2c2c04a44b6cdb0/fab "
+         "-O /etc/bash_completion.d/fab")
+    print("To have bash completion instantly, run\n"
+          "  source /etc/bash_completion.d/fab")
diff --git a/circle/firewall/fields.py b/circle/firewall/fields.py
index 47f5539..8da33a4 100644
--- a/circle/firewall/fields.py
+++ b/circle/firewall/fields.py
@@ -34,6 +34,10 @@ reverse_domain_re = re.compile(r'^(%\([abcd]\)d|[a-z0-9.-])+$')
 ipv6_template_re = re.compile(r'^(%\([abcd]\)[dxX]|[A-Za-z0-9:-])+$')
 
 
+class mac_custom(mac_unix):
+    word_fmt = '%.2X'
+
+
 class MACAddressFormField(forms.Field):
     default_error_messages = {
         'invalid': _(u'Enter a valid MAC address. %s'),
@@ -51,9 +55,6 @@ class MACAddressField(models.Field):
     description = _('MAC Address object')
     __metaclass__ = models.SubfieldBase
 
-    class mac_custom(mac_unix):
-        word_fmt = '%.2X'
-
     def __init__(self, *args, **kwargs):
         kwargs['max_length'] = 17
         super(MACAddressField, self).__init__(*args, **kwargs)
@@ -65,7 +66,7 @@ class MACAddressField(models.Field):
         if isinstance(value, EUI):
             return value
 
-        return EUI(value, dialect=MACAddressField.mac_custom)
+        return EUI(value, dialect=mac_custom)
 
     def get_internal_type(self):
         return 'CharField'
diff --git a/circle/firewall/models.py b/circle/firewall/models.py
index cfa4a99..2a7e6ba 100644
--- a/circle/firewall/models.py
+++ b/circle/firewall/models.py
@@ -243,6 +243,13 @@ class Rule(models.Model):
 
         return retval
 
+    @classmethod
+    def portforwards(cls, host=None):
+        qs = cls.objects.filter(dport__isnull=False, direction='in')
+        if host is not None:
+            qs = qs.filter(host=host)
+        return qs
+
     class Meta:
         verbose_name = _("rule")
         verbose_name_plural = _("rules")
@@ -762,7 +769,7 @@ class Host(models.Model):
         Return a list of ports with forwarding rules set.
         """
         retval = []
-        for rule in self.rules.filter(dport__isnull=False, direction='in'):
+        for rule in Rule.portforwards(host=self):
             forward = {
                 'proto': rule.proto,
                 'private': rule.dport,
diff --git a/circle/locale/hu/LC_MESSAGES/django.po b/circle/locale/hu/LC_MESSAGES/django.po
index c50c2b4..b8c8759 100644
--- a/circle/locale/hu/LC_MESSAGES/django.po
+++ b/circle/locale/hu/LC_MESSAGES/django.po
@@ -6,8 +6,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: \n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-09-24 13:16+0200\n"
-"PO-Revision-Date: 2014-09-24 13:18+0200\n"
+"POT-Creation-Date: 2014-10-20 12:09+0200\n"
+"PO-Revision-Date: 2014-10-20 13:01+0200\n"
 "Last-Translator: Mate Ory <ory.mate@ik.bme.hu>\n"
 "Language-Team: Hungarian <cloud@ik.bme.hu>\n"
 "Language: hu\n"
@@ -25,90 +25,90 @@ msgstr "Angol"
 msgid "Hungarian"
 msgstr "Magyar"
 
-#: common/models.py:62
+#: common/models.py:71
 msgid "Failure."
 msgstr "Hiba."
 
-#: common/models.py:63
+#: common/models.py:72
 #, python-format
 msgid "Unhandled exception: %(error)s"
 msgstr "Kezeletlen kivétel: %(error)s"
 
-#: common/models.py:138
+#: common/models.py:147
 #: dashboard/templates/dashboard/instanceactivity_detail.html:28
 msgid "activity code"
 msgstr "tevékenységkód"
 
-#: common/models.py:141
+#: common/models.py:150
 msgid "human readable name"
 msgstr "olvasható név"
 
-#: common/models.py:142
+#: common/models.py:151
 msgid "Human readable name of activity."
 msgstr "A tevékenység neve olvasható formában."
 
-#: common/models.py:146
+#: common/models.py:155
 msgid "Celery task unique identifier."
 msgstr "Celery feladat egyedi azonosítója."
 
-#: common/models.py:147
+#: common/models.py:156
 msgid "task_uuid"
 msgstr "feladat uuid"
 
-#: common/models.py:148
+#: common/models.py:157
 #: dashboard/templates/dashboard/instanceactivity_detail.html:37
-#: firewall/models.py:273 vm/models/common.py:84 vm/models/instance.py:140
-#: vm/models/instance.py:221
+#: firewall/models.py:273 vm/models/common.py:84 vm/models/instance.py:131
+#: vm/models/instance.py:212
 msgid "user"
 msgstr "felhasználó"
 
-#: common/models.py:149
+#: common/models.py:158
 msgid "The person who started this activity."
 msgstr "A tevékenységet indító felhasználó."
 
-#: common/models.py:150
+#: common/models.py:159
 msgid "started at"
 msgstr "indítás ideje"
 
-#: common/models.py:152
+#: common/models.py:161
 msgid "Time of activity initiation."
 msgstr "A tevékenység megkezdésének időpontja."
 
-#: common/models.py:153
+#: common/models.py:162
 msgid "finished at"
 msgstr "befejezés ideje"
 
-#: common/models.py:155
+#: common/models.py:164
 msgid "Time of activity finalization."
 msgstr "A tevékenység befejeztének ideje."
 
-#: common/models.py:157
+#: common/models.py:166
 msgid "True, if the activity has finished successfully."
 msgstr "Igaz, ha a tevékenység sikeresen befejeződött."
 
-#: common/models.py:159
+#: common/models.py:168
 #: dashboard/templates/dashboard/instanceactivity_detail.html:56
 msgid "result"
 msgstr "eredmény"
 
-#: common/models.py:161
+#: common/models.py:170
 msgid "Human readable result of activity."
 msgstr "A tevékenység eredménye olvasható formában."
 
-#: common/models.py:523
+#: common/models.py:543
 msgid "Permission Denied"
 msgstr "Hozzáférés megtagadva"
 
-#: common/models.py:525
+#: common/models.py:545
 msgid "Unknown error"
 msgstr "Ismeretlen hiba"
 
-#: common/models.py:526
+#: common/models.py:546
 #, python-format
 msgid "Unknown error: %(ex)s"
 msgstr "Ismeretlen hiba: %(ex)s"
 
-#: common/operations.py:160
+#: common/operations.py:177
 msgid "Superuser privileges are required."
 msgstr "Rendszergazdai jogosultság szükséges."
 
@@ -121,24 +121,24 @@ msgstr "%s (csoport)"
 msgid "no matches found"
 msgstr "nincs találat"
 
-#: dashboard/forms.py:65
+#: dashboard/forms.py:67
 msgid "idle"
 msgstr "üresjáratban"
 
-#: dashboard/forms.py:66
+#: dashboard/forms.py:68
 msgid "normal"
 msgstr "normál"
 
-#: dashboard/forms.py:67
+#: dashboard/forms.py:69
 msgid "server"
 msgstr "szerver"
 
-#: dashboard/forms.py:68
+#: dashboard/forms.py:70
 msgid "realtime"
 msgstr "valós idejű"
 
-#: dashboard/forms.py:73 dashboard/forms.py:776 dashboard/forms.py:797
-#: dashboard/forms.py:1084 dashboard/tables.py:225
+#: dashboard/forms.py:88 dashboard/forms.py:805 dashboard/forms.py:895
+#: dashboard/forms.py:1196 dashboard/tables.py:225
 #: dashboard/templates/dashboard/_vm-create-2.html:20
 #: dashboard/templates/dashboard/vm-list.html:60
 #: dashboard/templates/dashboard/vm-detail/home.html:8 firewall/models.py:285
@@ -147,20 +147,31 @@ msgstr "valós idejű"
 msgid "Name"
 msgstr "Név"
 
-#: dashboard/forms.py:74 vm/models/instance.py:145
+#: dashboard/forms.py:89 vm/models/instance.py:136
 msgid "Human readable name of template."
 msgstr "A sablon olvasható neve."
 
-#: dashboard/forms.py:190 dashboard/templates/dashboard/_vm-create-1.html:53
-#: dashboard/templates/dashboard/vm-detail/home.html:30
+#: dashboard/forms.py:99
+msgid "Clone template permissions"
+msgstr "Sablon jogosultságainak klónozása"
+
+#: dashboard/forms.py:100
+msgid ""
+"Clone the access list of parent template. Useful for updating a template."
+msgstr ""
+"A szülősablon hozzáférési listájának másolása. Sablonok frissítéséhez "
+"ajánlott."
+
+#: dashboard/forms.py:211 dashboard/templates/dashboard/_vm-create-1.html:53
+#: dashboard/templates/dashboard/vm-detail/home.html:33
 msgid "Description"
 msgstr "Leírás"
 
-#: dashboard/forms.py:201 dashboard/forms.py:249
+#: dashboard/forms.py:222 dashboard/forms.py:269
 msgid "Directory identifier"
 msgstr "Címtári azonosító"
 
-#: dashboard/forms.py:204
+#: dashboard/forms.py:225
 msgid ""
 "If you select an item here, the members of this directory group will be "
 "automatically added to the group at the time they log in. Please note that "
@@ -171,7 +182,7 @@ msgstr ""
 "kerülnek, ha bejelentkeznek. Vegye figyelembe, hogy más, az önhöz hasonló "
 "jogosultságú felhasználók is csoportadminisztrátorrá válhatnak."
 
-#: dashboard/forms.py:228
+#: dashboard/forms.py:249
 #: dashboard/templates/dashboard/store/_list-box.html:57
 #: network/templates/network/blacklist-create.html:8
 #: network/templates/network/dashboard.html:25
@@ -193,13 +204,13 @@ msgstr ""
 msgid "Create"
 msgstr "Létrehozás"
 
-#: dashboard/forms.py:257 dashboard/forms.py:1004 dashboard/forms.py:1021
-#: dashboard/forms.py:1047 dashboard/forms.py:1097 dashboard/forms.py:1138
-#: dashboard/forms.py:1158 dashboard/forms.py:1187
+#: dashboard/forms.py:277 dashboard/forms.py:1116 dashboard/forms.py:1133
+#: dashboard/forms.py:1159 dashboard/forms.py:1209 dashboard/forms.py:1250
+#: dashboard/forms.py:1270 dashboard/forms.py:1299
 #: dashboard/templates/dashboard/_manage_access.html:73
 #: dashboard/templates/dashboard/connect-command-create.html:37
 #: dashboard/templates/dashboard/connect-command-edit.html:37
-#: dashboard/templates/dashboard/group-detail.html:102
+#: dashboard/templates/dashboard/group-detail.html:132
 #: dashboard/templates/dashboard/lease-edit.html:96
 #: dashboard/templates/dashboard/vm-detail/tx-owner.html:12
 #: network/forms.py:82 network/forms.py:103 network/forms.py:139
@@ -208,175 +219,226 @@ msgstr "Létrehozás"
 msgid "Save"
 msgstr "Mentés"
 
-#: dashboard/forms.py:289 dashboard/templates/dashboard/vm-detail.html:78
+#: dashboard/forms.py:307 dashboard/templates/dashboard/vm-detail.html:92
 msgid "Host"
 msgstr "Gép"
 
-#: dashboard/forms.py:359 dashboard/templates/dashboard/node-detail.html:4
-#: dashboard/templates/dashboard/vm-list.html:81
+#: dashboard/forms.py:378 dashboard/forms.py:776 dashboard/forms.py:933
+#: dashboard/templates/dashboard/node-detail.html:4
+#: dashboard/templates/dashboard/vm-list.html:85
+#: dashboard/templates/dashboard/vm-detail/home.html:116
 msgid "Node"
 msgstr "Csomópont"
 
-#: dashboard/forms.py:438
+#: dashboard/forms.py:457
 msgid "Networks"
 msgstr "Hálózatok"
 
-#: dashboard/forms.py:667
+#: dashboard/forms.py:683
 msgid "Suspend in"
 msgstr "Felfüggesztés ideje"
 
-#: dashboard/forms.py:671 dashboard/forms.py:695
+#: dashboard/forms.py:687 dashboard/forms.py:711
 msgid "hours"
 msgstr "óra"
 
-#: dashboard/forms.py:676 dashboard/forms.py:700
+#: dashboard/forms.py:692 dashboard/forms.py:716
 msgid "days"
 msgstr "nap"
 
-#: dashboard/forms.py:681 dashboard/forms.py:705
+#: dashboard/forms.py:697 dashboard/forms.py:721
 msgid "weeks"
 msgstr "hét"
 
-#: dashboard/forms.py:686 dashboard/forms.py:710
+#: dashboard/forms.py:702 dashboard/forms.py:726
 msgid "months"
 msgstr "hónap"
 
-#: dashboard/forms.py:691
+#: dashboard/forms.py:707
 msgid "Delete in"
 msgstr "Törlés ideje"
 
-#: dashboard/forms.py:716 dashboard/templates/dashboard/template-edit.html:64
+#: dashboard/forms.py:732 dashboard/templates/dashboard/template-edit.html:63
 #: network/forms.py:60
 msgid "Save changes"
 msgstr "Változások mentése"
 
-#: dashboard/forms.py:726
+#: dashboard/forms.py:742
 msgid "Set expiration times even if they are shorter than the current value."
 msgstr ""
 "Akkor is állítsa át a lejárati időket, ha rövidebbek lesznek a jelenleginél."
 
-#: dashboard/forms.py:729
+#: dashboard/forms.py:745
 msgid "Save selected lease."
 msgstr "Kiválasztott bérlet mentése."
 
-#: dashboard/forms.py:738
+#: dashboard/forms.py:754
 msgid "Length"
 msgstr "Hossz"
 
-#: dashboard/forms.py:753
+#: dashboard/forms.py:762
+msgid "Live migration"
+msgstr "Live migration"
+
+#: dashboard/forms.py:764
+msgid ""
+"Live migration is a way of moving virtual machines between hosts with a "
+"service interruption of at most some seconds. Please note that it can take "
+"very long and cause much network traffic in case of busy machines."
+msgstr ""
+"A live migration lehetővé teszi virtuális gépek csomópontok közti mozgatását "
+"legfeljebb néhány másodperces szolgáltatáskimaradással. Vegye figyelembe, "
+"hogy ez terhelt gépek esetén sokáig tarthat és nagy hálózati forgalommal jár."
+
+#: dashboard/forms.py:782
 msgid "Forcibly interrupt all running activities."
 msgstr "Futó tevékenységek erőltetett befejezése."
 
-#: dashboard/forms.py:754
+#: dashboard/forms.py:783
 msgid "Set all activities to finished state, but don't interrupt any tasks."
 msgstr ""
 "Minden tevékenység befejezettre állítása (a feladatok megszakítása nélkül)."
 
-#: dashboard/forms.py:757
+#: dashboard/forms.py:786
 msgid "New status"
 msgstr "Új állapot"
 
-#: dashboard/forms.py:778
+#: dashboard/forms.py:787
+msgid "Reset node"
+msgstr "Csomópont visszaállítása"
+
+#: dashboard/forms.py:801
+msgid "use emergency state change"
+msgstr "vész-állapotváltás használata"
+
+#: dashboard/forms.py:807 dashboard/forms.py:827
 #: dashboard/templates/dashboard/store/_list-box.html:117
 msgid "Size"
 msgstr "Méret"
 
-#: dashboard/forms.py:779
+#: dashboard/forms.py:808
 msgid "Size of disk to create in bytes or with units like MB or GB."
 msgstr "Létrehozandó lemez mérete byte-okban vagy mértékegységgel (MB, GB)."
 
-#: dashboard/forms.py:785
+#: dashboard/forms.py:820 dashboard/forms.py:849
 msgid "Invalid format, you can use  GB or MB!"
 msgstr "Érvénytelen formátum. „GB” és „MB” is használható."
 
-#: dashboard/forms.py:798
+#: dashboard/forms.py:828
+msgid "Size to resize the disk in bytes or with units like MB or GB."
+msgstr "A lemez kívánt mérete byte-okban vagy mértékegységgel (MB, GB)."
+
+#: dashboard/forms.py:839 dashboard/forms.py:875
+msgid "Disk"
+msgstr "Lemez"
+
+#: dashboard/forms.py:852
+msgid "Disk size must be greater than the actual size."
+msgstr "A lemez mérete nagyobb kell legyen a jelenleginél."
+
+#: dashboard/forms.py:861 dashboard/forms.py:886
+#, python-format
+msgid "<label>Disk:</label> %s"
+msgstr "<label>Lemez:</label> %s"
+
+#: dashboard/forms.py:896
 msgid "URL"
 msgstr "URL"
 
-#: dashboard/forms.py:813
+#: dashboard/forms.py:906
+msgid "Could not find filename in URL, please specify a name explicitly."
+msgstr "Az URL-ben nem található fájlnév. Kérem adja meg explicite."
+
+#: dashboard/forms.py:917
 #: dashboard/templates/dashboard/node-detail/resources.html:17
 msgid "Vlan"
 msgstr "Vlan"
 
-#: dashboard/forms.py:816
+#: dashboard/forms.py:920
 msgid "No more networks."
 msgstr "Nincs több hálózat."
 
-#: dashboard/forms.py:844 dashboard/templates/dashboard/profile.html:31
-#: dashboard/templates/dashboard/vm-detail.html:94
+#: dashboard/forms.py:934
+msgid ""
+"Deploy virtual machine to this node (blank allows scheduling automatically)."
+msgstr ""
+"A virtuális gép elindítása ezen a csomóponton (üresen hagyva automatikus "
+"ütemezés)."
+
+#: dashboard/forms.py:956 dashboard/templates/dashboard/profile.html:31
+#: dashboard/templates/dashboard/vm-detail.html:108
 msgid "Username"
 msgstr "Felhasználónév"
 
-#: dashboard/forms.py:858 dashboard/templates/dashboard/vm-detail.html:96
+#: dashboard/forms.py:970 dashboard/templates/dashboard/vm-detail.html:110
 msgid "Password"
 msgstr "Jelszó"
 
-#: dashboard/forms.py:863
+#: dashboard/forms.py:975
 msgid "Sign in"
 msgstr "Bejelentkezés"
 
-#: dashboard/forms.py:886 dashboard/templates/dashboard/profile.html:37
+#: dashboard/forms.py:998 dashboard/templates/dashboard/profile.html:37
 msgid "Email address"
 msgstr "E-mail cím"
 
-#: dashboard/forms.py:891
+#: dashboard/forms.py:1003
 msgid "Reset password"
 msgstr "Új jelszó"
 
-#: dashboard/forms.py:907 dashboard/forms.py:1030
+#: dashboard/forms.py:1019 dashboard/forms.py:1142
 msgid "Change password"
 msgstr "Jelszóváltoztatás"
 
-#: dashboard/forms.py:979
+#: dashboard/forms.py:1091
 msgid "Add trait"
 msgstr "Jellemző hozzáadása"
 
-#: dashboard/forms.py:1061 dashboard/templates/dashboard/lease-edit.html:86
+#: dashboard/forms.py:1173 dashboard/templates/dashboard/lease-edit.html:86
 msgid "Name of group or user"
 msgstr "Csoport vagy felhasználó neve"
 
-#: dashboard/forms.py:1069 dashboard/forms.py:1078
+#: dashboard/forms.py:1181 dashboard/forms.py:1190
 msgid "Name of user"
 msgstr "Felhasználó neve"
 
-#: dashboard/forms.py:1071 dashboard/forms.py:1080
+#: dashboard/forms.py:1183 dashboard/forms.py:1192
 msgid "E-mail address or identifier of user"
 msgstr "A felhasználó e-mail címe vagy azonosítója"
 
-#: dashboard/forms.py:1086
+#: dashboard/forms.py:1198
 msgid "Key"
 msgstr "Kulcs"
 
-#: dashboard/forms.py:1087
+#: dashboard/forms.py:1199
 msgid "For example: ssh-rsa AAAAB3NzaC1yc2ED..."
 msgstr "Például: ssh-rsa AAAAB3NzaC1yc2ED…"
 
-#: dashboard/forms.py:1173
+#: dashboard/forms.py:1285
 msgid "permissions"
 msgstr "jogosultságok"
 
-#: dashboard/forms.py:1230
+#: dashboard/forms.py:1342
 msgid "owned"
 msgstr "saját"
 
-#: dashboard/forms.py:1231
+#: dashboard/forms.py:1343
 msgid "shared"
 msgstr "osztott"
 
-#: dashboard/forms.py:1232
+#: dashboard/forms.py:1344
 msgid "all"
 msgstr "összes"
 
-#: dashboard/forms.py:1239 dashboard/forms.py:1263
+#: dashboard/forms.py:1351 dashboard/forms.py:1375
 #: dashboard/templates/dashboard/index-groups.html:21
-#: dashboard/templates/dashboard/index-nodes.html:34
+#: dashboard/templates/dashboard/index-nodes.html:61
 #: dashboard/templates/dashboard/index-vm.html:57
 msgid "Search..."
 msgstr "Keresés..."
 
 #: dashboard/models.py:65 dashboard/templates/dashboard/index-groups.html:39
-#: dashboard/templates/dashboard/index-nodes.html:48
-#: dashboard/templates/dashboard/index-nodes.html:73
+#: dashboard/templates/dashboard/index-nodes.html:78
 #: dashboard/templates/dashboard/index-templates.html:38
 #: dashboard/templates/dashboard/index-vm.html:76
 #: dashboard/templates/dashboard/store/_list-box.html:101
@@ -391,7 +453,7 @@ msgstr "kézbesített"
 msgid "read"
 msgstr "olvasott"
 
-#: dashboard/models.py:108 vm/models/instance.py:107
+#: dashboard/models.py:108 vm/models/instance.py:98
 msgid "access method"
 msgstr "elérés módja"
 
@@ -402,8 +464,8 @@ msgstr "Távoli elérési mód típusa."
 #: dashboard/models.py:110 firewall/models.py:413 firewall/models.py:440
 #: firewall/models.py:828 firewall/models.py:850 firewall/models.py:871
 #: storage/models.py:47 storage/models.py:86 vm/models/common.py:65
-#: vm/models/common.py:89 vm/models/common.py:165 vm/models/instance.py:144
-#: vm/models/instance.py:234 vm/models/node.py:65
+#: vm/models/common.py:89 vm/models/common.py:165 vm/models/instance.py:135
+#: vm/models/instance.py:225 vm/models/node.py:65
 msgid "name"
 msgstr "név"
 
@@ -468,14 +530,14 @@ msgid "Can use autocomplete."
 msgstr "Használhat automatikus kiegészítést."
 
 #: dashboard/models.py:229 firewall/models.py:274 vm/models/common.py:85
-#: vm/models/instance.py:141 vm/models/instance.py:222
+#: vm/models/instance.py:132 vm/models/instance.py:213
 msgid "operator"
 msgstr "operátor"
 
 #: dashboard/models.py:230 firewall/models.py:100 firewall/models.py:369
 #: firewall/models.py:422 firewall/models.py:445 firewall/models.py:510
 #: firewall/models.py:851 firewall/models.py:880 vm/models/common.py:86
-#: vm/models/instance.py:142 vm/models/instance.py:223
+#: vm/models/instance.py:133 vm/models/instance.py:214
 msgid "owner"
 msgstr "tulajdonos"
 
@@ -501,12 +563,12 @@ msgstr "Állapot"
 
 #: dashboard/tables.py:145 dashboard/templates/dashboard/_vm-create-2.html:38
 #: dashboard/templates/dashboard/node-detail.html:69
-#: dashboard/templates/dashboard/vm-detail.html:163
+#: dashboard/templates/dashboard/vm-detail.html:177
 msgid "Resources"
 msgstr "Erőforrások"
 
 #: dashboard/tables.py:151 dashboard/templates/dashboard/vm-list.html:72
-#: vm/models/instance.py:113
+#: vm/models/instance.py:104
 msgid "Lease"
 msgstr "Bérlet"
 
@@ -545,7 +607,7 @@ msgid "Access method"
 msgstr "Elérés módja"
 
 #: dashboard/tables.py:265
-#: dashboard/templates/dashboard/vm-detail/home.html:94
+#: dashboard/templates/dashboard/vm-detail/home.html:129
 msgid "Template"
 msgstr "Sablon"
 
@@ -659,33 +721,37 @@ msgstr ""
 msgid "I have the Client installed"
 msgstr "Már telepítve van"
 
-#: dashboard/templates/dashboard/_disk-list-element.html:10
-#: dashboard/templates/dashboard/node-detail/_activity-timeline.html:28
-#: dashboard/templates/dashboard/vm-detail/_activity-timeline.html:45
-msgid "failed"
-msgstr "meghiúsult"
-
-#: dashboard/templates/dashboard/_disk-list-element.html:16
-#: dashboard/templates/dashboard/_disk-list-element.html:18
+#: dashboard/templates/dashboard/_disk-list-element.html:12
 #: dashboard/templates/dashboard/_manage_access.html:34
 #: dashboard/templates/dashboard/_manage_access.html:56
-#: dashboard/templates/dashboard/group-detail.html:63
+#: dashboard/templates/dashboard/group-detail.html:93
 #: dashboard/templates/dashboard/lease-edit.html:60
 #: dashboard/templates/dashboard/lease-edit.html:80
+#: dashboard/templates/dashboard/template-edit.html:107
+#: dashboard/templates/dashboard/template-edit.html:108
 #: dashboard/templates/dashboard/confirm/base-remove.html:12
 #: dashboard/templates/dashboard/store/_list-box.html:133
 #: dashboard/templates/dashboard/store/remove.html:36
-#: dashboard/templates/dashboard/vm-detail/network.html:79
-#: dashboard/templates/dashboard/vm-detail/network.html:111
+#: dashboard/templates/dashboard/vm-detail/network.html:81
+#: dashboard/templates/dashboard/vm-detail/network.html:113
 msgid "Remove"
 msgstr "Eltávolítás"
 
+#: dashboard/templates/dashboard/_disk-list-element.html:21
+msgid "Resize"
+msgstr "Átméretezés"
+
+#: dashboard/templates/dashboard/_disk-list-element.html:28
+#: dashboard/templates/dashboard/store/remove.html:20
+msgid "File name"
+msgstr "Fájlnév"
+
 #: dashboard/templates/dashboard/_display-name.html:10
 msgid "username"
 msgstr "felhasználónév"
 
 #: dashboard/templates/dashboard/_manage_access.html:7
-#: dashboard/templates/dashboard/group-detail.html:63
+#: dashboard/templates/dashboard/group-detail.html:93
 #: dashboard/templates/dashboard/lease-edit.html:35
 msgid "Who"
 msgstr "Ki"
@@ -746,17 +812,17 @@ msgid "Next"
 msgstr "Tovább"
 
 #: dashboard/templates/dashboard/_template-create.html:15
-#: dashboard/templates/dashboard/template-edit.html:40
+#: dashboard/templates/dashboard/template-edit.html:39
 msgid "Resource configuration"
 msgstr "Erőforrásbeállítások"
 
 #: dashboard/templates/dashboard/_template-create.html:21
-#: dashboard/templates/dashboard/template-edit.html:46
+#: dashboard/templates/dashboard/template-edit.html:45
 msgid "Virtual machine settings"
 msgstr "Virtuális gépek beállításai"
 
 #: dashboard/templates/dashboard/_template-create.html:31
-#: dashboard/templates/dashboard/template-edit.html:57
+#: dashboard/templates/dashboard/template-edit.html:56
 msgid "External resources"
 msgstr "Külső erőforrások"
 
@@ -780,20 +846,21 @@ msgid "CPU"
 msgstr "CPU"
 
 #: dashboard/templates/dashboard/_vm-create-1.html:23
+#: dashboard/templates/dashboard/vm-list.html:76
 #: dashboard/templates/dashboard/node-list/column-monitor.html:20
 msgid "Memory"
 msgstr "Memória"
 
 #: dashboard/templates/dashboard/_vm-create-1.html:33
 #: dashboard/templates/dashboard/_vm-create-2.html:49
-#: dashboard/templates/dashboard/vm-detail/resources.html:28
+#: dashboard/templates/dashboard/vm-detail/resources.html:25
 msgid "Disks"
 msgstr "Lemezek"
 
 #: dashboard/templates/dashboard/_vm-create-1.html:40
 #: dashboard/templates/dashboard/_vm-create-2.html:65
 #: dashboard/templates/dashboard/base.html:46
-#: dashboard/templates/dashboard/vm-detail.html:177
+#: dashboard/templates/dashboard/vm-detail.html:191
 #: dashboard/views/graph.py:192 dashboard/views/graph.py:215
 #: network/forms.py:123 network/templates/network/base.html:7
 msgid "Network"
@@ -826,6 +893,7 @@ msgid "Amount"
 msgstr "Mennyiség"
 
 #: dashboard/templates/dashboard/_vm-create-2.html:56
+#: dashboard/templates/dashboard/vm-detail/resources.html:34
 msgid "No disks are added."
 msgstr "Egy lemez sincs hozzáadva."
 
@@ -833,25 +901,25 @@ msgstr "Egy lemez sincs hozzáadva."
 msgid "Not added to any network."
 msgstr "Egy hálózathoz sincs hozzáadva."
 
-#: dashboard/templates/dashboard/_vm-mass-migrate.html:12
+#: dashboard/templates/dashboard/_vm-mass-migrate.html:13
 msgid "Reschedule"
 msgstr "Újraütemezés"
 
-#: dashboard/templates/dashboard/_vm-mass-migrate.html:16
+#: dashboard/templates/dashboard/_vm-mass-migrate.html:17
 msgid "This option will reschedule each virtual machine to the optimal node."
 msgstr "Ez a lehetőség minden virtuális gépet az optimális csomópontra migrál."
 
-#: dashboard/templates/dashboard/_vm-mass-migrate.html:28
-#: dashboard/templates/dashboard/_vm-migrate.html:27
+#: dashboard/templates/dashboard/_vm-mass-migrate.html:29
+#: dashboard/templates/dashboard/_vm-migrate.html:31
 msgid "CPU load"
 msgstr "CPU-terhelés"
 
-#: dashboard/templates/dashboard/_vm-mass-migrate.html:29
-#: dashboard/templates/dashboard/_vm-migrate.html:28
+#: dashboard/templates/dashboard/_vm-mass-migrate.html:30
+#: dashboard/templates/dashboard/_vm-migrate.html:33
 msgid "RAM usage"
 msgstr "RAM-használat"
 
-#: dashboard/templates/dashboard/_vm-migrate.html:7
+#: dashboard/templates/dashboard/_vm-migrate.html:8
 #, python-format
 msgid ""
 "\n"
@@ -860,11 +928,11 @@ msgstr ""
 "\n"
 "Válasszon csomópontot %(obj)s migrálásához.\n"
 
-#: dashboard/templates/dashboard/_vm-migrate.html:21
+#: dashboard/templates/dashboard/_vm-migrate.html:24
 msgid "current"
 msgstr "jelenlegi"
 
-#: dashboard/templates/dashboard/_vm-migrate.html:22
+#: dashboard/templates/dashboard/_vm-migrate.html:25
 msgid "recommended"
 msgstr "javasolt"
 
@@ -914,7 +982,7 @@ msgstr "Parancssablon létrehozása"
 #: dashboard/templates/dashboard/lease-create.html:13
 #: dashboard/templates/dashboard/lease-edit.html:12
 #: dashboard/templates/dashboard/profile.html:19
-#: dashboard/templates/dashboard/template-edit.html:14
+#: dashboard/templates/dashboard/template-edit.html:15
 #: dashboard/templates/dashboard/userkey-create.html:13
 #: dashboard/templates/dashboard/userkey-edit.html:14
 #: dashboard/templates/dashboard/confirm/base-renew.html:26
@@ -956,10 +1024,10 @@ msgstr "csoport"
 #: dashboard/templates/dashboard/group-detail.html:33
 #: dashboard/templates/dashboard/node-detail.html:13
 #: dashboard/templates/dashboard/node-detail.html:21
-#: dashboard/templates/dashboard/vm-detail.html:50
+#: dashboard/templates/dashboard/vm-detail.html:63
 #: dashboard/templates/dashboard/group-list/column-name.html:7
 #: dashboard/templates/dashboard/node-list/column-name.html:7
-#: dashboard/templates/dashboard/vm-detail/home.html:22
+#: dashboard/templates/dashboard/vm-detail/home.html:24
 #: dashboard/templates/dashboard/vm-list/column-name.html:7
 msgid "Rename"
 msgstr "Átnevezés"
@@ -967,6 +1035,7 @@ msgstr "Átnevezés"
 #: dashboard/templates/dashboard/group-detail.html:12
 #: dashboard/templates/dashboard/group-detail.html:37
 #: dashboard/templates/dashboard/node-detail.html:14
+#: dashboard/templates/dashboard/template-edit.html:75
 #: dashboard/templates/dashboard/confirm/ajax-delete.html:18
 #: dashboard/templates/dashboard/confirm/mass-delete.html:12
 #: dashboard/templates/dashboard/connect-command-list/column-command-actions.html:5
@@ -985,28 +1054,32 @@ msgid "Delete group."
 msgstr "Csoport törlése."
 
 #: dashboard/templates/dashboard/group-detail.html:55
+msgid "Available objects for this group"
+msgstr "Csoport számára elérhető objektumok"
+
+#: dashboard/templates/dashboard/group-detail.html:85
 msgid "User list"
 msgstr "Felhasználók"
 
-#: dashboard/templates/dashboard/group-detail.html:57
+#: dashboard/templates/dashboard/group-detail.html:87
 msgid "Create user"
 msgstr "Új felhasználó"
 
-#: dashboard/templates/dashboard/group-detail.html:74
-#: dashboard/templates/dashboard/group-detail.html:87
-#: dashboard/templates/dashboard/vm-detail/network.html:27
+#: dashboard/templates/dashboard/group-detail.html:104
+#: dashboard/templates/dashboard/group-detail.html:117
+#: dashboard/templates/dashboard/vm-detail/network.html:28
 msgid "remove"
 msgstr "eltávolítás"
 
-#: dashboard/templates/dashboard/group-detail.html:100
+#: dashboard/templates/dashboard/group-detail.html:130
 msgid "Add multiple users at once (one identifier per line)."
 msgstr "Több felhasználó hozzáadása (egy azonosító soronként)."
 
-#: dashboard/templates/dashboard/group-detail.html:107
+#: dashboard/templates/dashboard/group-detail.html:137
 msgid "Access permissions"
 msgstr "Hozzáférési jogosultságok"
 
-#: dashboard/templates/dashboard/group-detail.html:116
+#: dashboard/templates/dashboard/group-detail.html:146
 msgid "Group permissions"
 msgstr "Csoportjogosultságok"
 
@@ -1024,7 +1097,7 @@ msgstr "Azon csoportok, amelyekhez hozzáférése van."
 
 #: dashboard/templates/dashboard/index-groups.html:7
 #: dashboard/templates/dashboard/profile.html:56
-#: dashboard/templates/dashboard/vm-detail/network.html:37
+#: dashboard/templates/dashboard/vm-detail/network.html:39
 #: network/templates/network/host-edit.html:32 templates/info/help.html:192
 msgid "Groups"
 msgstr "Csoportok"
@@ -1049,12 +1122,12 @@ msgstr[1] ""
 "        "
 
 #: dashboard/templates/dashboard/index-groups.html:36
-#: dashboard/templates/dashboard/index-nodes.html:45
+#: dashboard/templates/dashboard/index-nodes.html:74
 #: dashboard/templates/dashboard/index-vm.html:73
 msgid "list"
 msgstr "felsorolás"
 
-#: dashboard/templates/dashboard/index-nodes.html:12
+#: dashboard/templates/dashboard/index-nodes.html:11
 msgid ""
 "List of compute nodes, also called worker nodes or hypervisors, which run "
 "the virtual machines."
@@ -1062,13 +1135,16 @@ msgstr ""
 "A virtuális gépeket futtató számítási csomópontok (más néven worker node-ok, "
 "hypervisorok) listája."
 
-#: dashboard/templates/dashboard/index-nodes.html:15
+#: dashboard/templates/dashboard/index-nodes.html:16
 #: dashboard/templates/dashboard/node-list.html:5
 msgid "Nodes"
 msgstr "Csomópontok"
 
-#: dashboard/templates/dashboard/index-nodes.html:43
-#: dashboard/templates/dashboard/index-nodes.html:70
+#: dashboard/templates/dashboard/index-nodes.html:63
+msgid "Search"
+msgstr "Keresés"
+
+#: dashboard/templates/dashboard/index-nodes.html:72
 #, python-format
 msgid "<strong>%(count)s</strong>  more"
 msgstr "még <strong>%(count)s</strong>"
@@ -1132,7 +1208,7 @@ msgid "Mark as favorite"
 msgstr "Kedvencnek jelölés"
 
 #: dashboard/templates/dashboard/index-vm.html:43
-#: dashboard/templates/dashboard/vm-list.html:124
+#: dashboard/templates/dashboard/vm-list.html:135
 msgid "You have no virtual machines."
 msgstr "Még nincs virtuális gépe."
 
@@ -1195,14 +1271,14 @@ msgstr "Nincs jogosultsága virtuális gépek indítására vagy kezelésére."
 
 #: dashboard/templates/dashboard/instanceactivity_detail.html:25
 #: dashboard/templates/dashboard/node-detail.html:82
-#: dashboard/templates/dashboard/vm-detail.html:182
+#: dashboard/templates/dashboard/vm-detail.html:196
 #: dashboard/templates/dashboard/node-detail/activity.html:3
 #: dashboard/templates/dashboard/vm-detail/activity.html:3
 msgid "Activity"
 msgstr "Tevékenységek"
 
 #: dashboard/templates/dashboard/instanceactivity_detail.html:31
-#: vm/models/activity.py:71 vm/models/instance.py:282 vm/models/network.py:68
+#: vm/models/activity.py:70 vm/models/instance.py:274 vm/models/network.py:67
 msgid "instance"
 msgstr "példány"
 
@@ -1242,6 +1318,20 @@ msgstr "állapot"
 msgid "resultant state"
 msgstr "új állapot"
 
+#: dashboard/templates/dashboard/instanceactivity_detail.html:62
+msgid "subactivities"
+msgstr "altevékenységek"
+
+#: dashboard/templates/dashboard/instanceactivity_detail.html:74
+#: dashboard/templates/dashboard/node-detail/_activity-timeline.html:28
+#: dashboard/templates/dashboard/vm-detail/_activity-timeline.html:48
+msgid "failed"
+msgstr "meghiúsult"
+
+#: dashboard/templates/dashboard/instanceactivity_detail.html:78
+msgid "none"
+msgstr "nincs"
+
 #: dashboard/templates/dashboard/lease-create.html:5
 #: dashboard/templates/dashboard/lease-create.html:14
 msgid "Create lease"
@@ -1253,7 +1343,7 @@ msgid "Edit lease"
 msgstr "Bérlési mód szerkesztése"
 
 #: dashboard/templates/dashboard/lease-edit.html:27
-#: dashboard/templates/dashboard/template-edit.html:73
+#: dashboard/templates/dashboard/template-edit.html:84
 #: network/templates/network/vlan-edit.html:26
 msgid "Manage access"
 msgstr "Jogosultságok kezelése"
@@ -1307,7 +1397,7 @@ msgid "Offline"
 msgstr "Offline"
 
 #: dashboard/templates/dashboard/node-detail.html:63
-#: dashboard/templates/dashboard/vm-detail.html:158
+#: dashboard/templates/dashboard/vm-detail.html:172
 msgid "Home"
 msgstr "Kezdőoldal"
 
@@ -1431,24 +1521,27 @@ msgid "Command templates"
 msgstr "Parancssablonok"
 
 #: dashboard/templates/dashboard/template-edit.html:6
-#: vm/models/instance.py:167 vm/models/instance.py:240 vm/models/network.py:45
+#: vm/models/instance.py:158 vm/models/instance.py:231 vm/models/network.py:44
 msgid "template"
 msgstr "sablon"
 
-#: dashboard/templates/dashboard/template-edit.html:15
+#: dashboard/templates/dashboard/template-edit.html:17
 msgid "Edit template"
 msgstr "Sablon szerkesztése"
 
-#: dashboard/templates/dashboard/template-edit.html:32
-msgid "Visit"
-msgstr "Megtekintés"
+#: dashboard/templates/dashboard/template-edit.html:29
+msgid "Parent template"
+msgstr "Szülősablon"
 
-#: dashboard/templates/dashboard/template-edit.html:83
+#: dashboard/templates/dashboard/template-edit.html:77
+msgid "Delete template"
+msgstr "Sablon törlése"
+
+#: dashboard/templates/dashboard/template-edit.html:94
 msgid "Disk list"
 msgstr "Lemezek"
 
-#: dashboard/templates/dashboard/template-edit.html:88
-#: dashboard/templates/dashboard/vm-detail/resources.html:39
+#: dashboard/templates/dashboard/template-edit.html:99
 msgid "No disks are added!"
 msgstr "Egy lemez sincs hozzáadva!"
 
@@ -1478,131 +1571,135 @@ msgstr "SSH publikus kulcs létrehozása"
 msgid "Edit SSH public key"
 msgstr "SSH publikus kulcs módosítása"
 
-#: dashboard/templates/dashboard/vm-detail.html:10
-msgid "This is the master vm of your new template"
-msgstr "Ez a mesterpéldány egy új sablonhoz"
+#: dashboard/templates/dashboard/vm-detail.html:18
+msgid "Toggle tutorial panel"
+msgstr "Kalauz engedélyezése/tiltása"
 
-#: dashboard/templates/dashboard/vm-detail.html:13
+#: dashboard/templates/dashboard/vm-detail.html:23
 msgid "Start template tutorial"
 msgstr "Sablon-kalauz indítása"
 
-#: dashboard/templates/dashboard/vm-detail.html:17
+#: dashboard/templates/dashboard/vm-detail.html:26
+msgid "This is the master vm of your new template"
+msgstr "Ez a mesterpéldány egy új sablonhoz"
+
+#: dashboard/templates/dashboard/vm-detail.html:28
 msgid ""
 "Modify the virtual machine to suit your needs <strong>(optional)</strong>"
 msgstr "Módosítsa a virtuális gépet <strong>(igény szerint)</strong>"
 
-#: dashboard/templates/dashboard/vm-detail.html:19
+#: dashboard/templates/dashboard/vm-detail.html:30
 msgid "Change the description"
 msgstr "Változtassa meg a leírást"
 
-#: dashboard/templates/dashboard/vm-detail.html:20
+#: dashboard/templates/dashboard/vm-detail.html:31
 msgid "Change resources (CPU and RAM)"
 msgstr "Állítsa be az erőforrásokat (CPU és memória)"
 
-#: dashboard/templates/dashboard/vm-detail.html:21
+#: dashboard/templates/dashboard/vm-detail.html:32
 msgid "Attach or detach disks"
 msgstr "Csatoljon vagy válasszon le lemezeket"
 
-#: dashboard/templates/dashboard/vm-detail.html:22
+#: dashboard/templates/dashboard/vm-detail.html:33
 msgid "Add or remove network interfaces"
 msgstr "Adjon hozz vagy törljön hálózati interfészeket"
 
-#: dashboard/templates/dashboard/vm-detail.html:25
+#: dashboard/templates/dashboard/vm-detail.html:36
 msgid "Deploy the virtual machine"
 msgstr "Indítsa el a virtuális gépet"
 
-#: dashboard/templates/dashboard/vm-detail.html:26
+#: dashboard/templates/dashboard/vm-detail.html:37
 msgid "Connect to the machine"
 msgstr "Csatlakozzon a géphez"
 
-#: dashboard/templates/dashboard/vm-detail.html:27
+#: dashboard/templates/dashboard/vm-detail.html:38
 msgid "Do all the needed installations/customizations"
 msgstr "Végezze el a szükséges telepítéseket, testreszabásokat"
 
-#: dashboard/templates/dashboard/vm-detail.html:28
+#: dashboard/templates/dashboard/vm-detail.html:39
 msgid "Log off from the machine"
 msgstr "Jelentkezzen ki a gépből"
 
-#: dashboard/templates/dashboard/vm-detail.html:30
+#: dashboard/templates/dashboard/vm-detail.html:41
 msgid "Press the Save as template button"
 msgstr "Kattintson a Mentés sablonként gombra"
 
-#: dashboard/templates/dashboard/vm-detail.html:33
+#: dashboard/templates/dashboard/vm-detail.html:44
 msgid "Delete this virtual machine <strong>(optional)</strong>"
 msgstr "Törölje a virtális gépet <strong>(ha szükséges)</strong>"
 
-#: dashboard/templates/dashboard/vm-detail.html:74
+#: dashboard/templates/dashboard/vm-detail.html:88
 msgid "Connection details"
 msgstr "Kapcsolat részletei"
 
-#: dashboard/templates/dashboard/vm-detail.html:76
+#: dashboard/templates/dashboard/vm-detail.html:90
 msgid "Protocol"
 msgstr "Protokoll"
 
-#: dashboard/templates/dashboard/vm-detail.html:83
+#: dashboard/templates/dashboard/vm-detail.html:97
 msgid "The VM doesn't have any network interface."
 msgstr "A VM-nek nincs hálózati interfésze."
 
-#: dashboard/templates/dashboard/vm-detail.html:85
+#: dashboard/templates/dashboard/vm-detail.html:99
 msgid "The required port for this protocol is not forwarded."
 msgstr "A csatlakozáshoz szükséges port nincs továbbítva."
 
-#: dashboard/templates/dashboard/vm-detail.html:90
+#: dashboard/templates/dashboard/vm-detail.html:104
 msgid "Host (IPv6)"
 msgstr "Gép (IPv6)"
 
-#: dashboard/templates/dashboard/vm-detail.html:102
+#: dashboard/templates/dashboard/vm-detail.html:116
 msgid "Show password"
 msgstr "Jelszó megjelenítése"
 
-#: dashboard/templates/dashboard/vm-detail.html:110
+#: dashboard/templates/dashboard/vm-detail.html:124
 msgid "Start the VM to change the password."
 msgstr "Jelszóváltoztatáshoz el kell indítani a gépet."
 
-#: dashboard/templates/dashboard/vm-detail.html:110
+#: dashboard/templates/dashboard/vm-detail.html:124
 msgid "Generate new password!"
 msgstr "Új jelszó generálása"
 
-#: dashboard/templates/dashboard/vm-detail.html:117
-#: dashboard/templates/dashboard/vm-detail.html:129
+#: dashboard/templates/dashboard/vm-detail.html:131
+#: dashboard/templates/dashboard/vm-detail.html:143
 msgid "Command"
 msgstr "Parancs"
 
-#: dashboard/templates/dashboard/vm-detail.html:122
-#: dashboard/templates/dashboard/vm-detail.html:133
+#: dashboard/templates/dashboard/vm-detail.html:136
+#: dashboard/templates/dashboard/vm-detail.html:147
 #: dashboard/templates/dashboard/vm-list.html:22
 msgid "Select all"
 msgstr "Összes kiválasztása"
 
-#: dashboard/templates/dashboard/vm-detail.html:130
+#: dashboard/templates/dashboard/vm-detail.html:144
 msgid "Connection is not possible."
 msgstr "A csatlakozás nem lehetséges."
 
-#: dashboard/templates/dashboard/vm-detail.html:140
+#: dashboard/templates/dashboard/vm-detail.html:154
 msgid "Connect via the CIRCLE Client"
 msgstr "Csatlakozás CIRCLE klienssel"
 
-#: dashboard/templates/dashboard/vm-detail.html:141
+#: dashboard/templates/dashboard/vm-detail.html:155
 msgid "Connect"
 msgstr "Csatlakozás"
 
-#: dashboard/templates/dashboard/vm-detail.html:143
+#: dashboard/templates/dashboard/vm-detail.html:157
 msgid "Download client"
 msgstr "Kliens letöltése"
 
-#: dashboard/templates/dashboard/vm-detail.html:145
+#: dashboard/templates/dashboard/vm-detail.html:159
 msgid "Download the CIRCLE Client"
 msgstr "A CIRCLE kliens letöltése"
 
-#: dashboard/templates/dashboard/vm-detail.html:146
+#: dashboard/templates/dashboard/vm-detail.html:160
 msgid "Connect (download client)"
 msgstr "Csatlakozás (kliens letöltése)"
 
-#: dashboard/templates/dashboard/vm-detail.html:168
+#: dashboard/templates/dashboard/vm-detail.html:182
 msgid "Console"
 msgstr "Konzol"
 
-#: dashboard/templates/dashboard/vm-detail.html:172
+#: dashboard/templates/dashboard/vm-detail.html:186
 msgid "Access"
 msgstr "Hozzáférés"
 
@@ -1626,15 +1723,15 @@ msgstr "ID"
 msgid "State"
 msgstr "Állapot"
 
-#: dashboard/templates/dashboard/vm-list.html:77
+#: dashboard/templates/dashboard/vm-list.html:81
 msgid "IP address"
 msgstr "IP cím"
 
-#: dashboard/templates/dashboard/vm-list.html:122
+#: dashboard/templates/dashboard/vm-list.html:133
 msgid "No result."
 msgstr "Nincs eredmény."
 
-#: dashboard/templates/dashboard/vm-list.html:141
+#: dashboard/templates/dashboard/vm-list.html:152
 msgid ""
 "You can select multiple vm instances while holding down the <strong>CTRL</"
 "strong> key."
@@ -1642,7 +1739,7 @@ msgstr ""
 "Több virtuális gépet is kiválaszthat a  <strong>CTRL</strong> billentyű "
 "lenyomásával."
 
-#: dashboard/templates/dashboard/vm-list.html:142
+#: dashboard/templates/dashboard/vm-list.html:153
 msgid ""
 "If you want to select multiple instances by one click select an instance "
 "then hold down <strong>SHIFT</strong> key and select another one!"
@@ -1990,7 +2087,7 @@ msgstr "Felsorolás"
 
 #: dashboard/templates/dashboard/store/list.html:4
 #: dashboard/templates/dashboard/store/upload.html:4
-#: dashboard/templates/dashboard/vm-detail/home.html:111
+#: dashboard/templates/dashboard/vm-detail/home.html:146
 msgid "Store"
 msgstr "Tárhely"
 
@@ -2025,10 +2122,6 @@ msgstr "Fájl törlésének megerősítése"
 msgid "File directory"
 msgstr "Fájl helye"
 
-#: dashboard/templates/dashboard/store/remove.html:20
-msgid "File name"
-msgstr "Fájlnév"
-
 #: dashboard/templates/dashboard/store/remove.html:22
 #, python-format
 msgid ""
@@ -2077,19 +2170,19 @@ msgstr[1] ""
 "\n"
 "  %(num_cores)s CPU mag\n"
 
-#: dashboard/templates/dashboard/vm-detail/_activity-timeline.html:29
+#: dashboard/templates/dashboard/vm-detail/_activity-timeline.html:31
 msgid "Abort"
 msgstr "Megszakítás"
 
-#: dashboard/templates/dashboard/vm-detail/_activity-timeline.html:59
+#: dashboard/templates/dashboard/vm-detail/_activity-timeline.html:62
 msgid "Show less activities"
 msgstr "Kevesebb tevékenység megjelenítése"
 
-#: dashboard/templates/dashboard/vm-detail/_activity-timeline.html:61
+#: dashboard/templates/dashboard/vm-detail/_activity-timeline.html:64
 msgid "Show all activities"
 msgstr "Összes tevékenység megjelenítése"
 
-#: dashboard/templates/dashboard/vm-detail/_network-port-add.html:14
+#: dashboard/templates/dashboard/vm-detail/_network-port-add.html:15
 msgid "Add"
 msgstr "Hozzáadás"
 
@@ -2137,35 +2230,35 @@ msgstr "Bezárás"
 msgid "System"
 msgstr "Rendszer"
 
-#: dashboard/templates/dashboard/vm-detail/home.html:42
+#: dashboard/templates/dashboard/vm-detail/home.html:48
 msgid "Update"
 msgstr "Frissítés"
 
-#: dashboard/templates/dashboard/vm-detail/home.html:49
+#: dashboard/templates/dashboard/vm-detail/home.html:57
 msgid "Expiration"
 msgstr "Lejárat"
 
-#: dashboard/templates/dashboard/vm-detail/home.html:60
+#: dashboard/templates/dashboard/vm-detail/home.html:69
 msgid "Suspended at:"
 msgstr "Felfüggesztve:"
 
-#: dashboard/templates/dashboard/vm-detail/home.html:62
+#: dashboard/templates/dashboard/vm-detail/home.html:75
 msgid "Destroyed at:"
 msgstr "Megsemmisítve:"
 
-#: dashboard/templates/dashboard/vm-detail/home.html:66
+#: dashboard/templates/dashboard/vm-detail/home.html:84
 msgid "Tags"
 msgstr "Címkék"
 
-#: dashboard/templates/dashboard/vm-detail/home.html:77
-msgid "No tag added!"
+#: dashboard/templates/dashboard/vm-detail/home.html:97
+msgid "No tag added."
 msgstr "Nincs címke."
 
-#: dashboard/templates/dashboard/vm-detail/home.html:88
+#: dashboard/templates/dashboard/vm-detail/home.html:109
 msgid "Add tag"
 msgstr "Címke hozzáadása"
 
-#: dashboard/templates/dashboard/vm-detail/network.html:8 vm/operations.py:123
+#: dashboard/templates/dashboard/vm-detail/network.html:8 vm/operations.py:201
 msgid "add interface"
 msgstr "új interfész"
 
@@ -2181,35 +2274,35 @@ msgstr "nem menedzselt"
 msgid "edit"
 msgstr "szerkesztés"
 
-#: dashboard/templates/dashboard/vm-detail/network.html:34
+#: dashboard/templates/dashboard/vm-detail/network.html:36
 #: firewall/models.py:482
 msgid "IPv4 address"
 msgstr "IPv4 cím"
 
-#: dashboard/templates/dashboard/vm-detail/network.html:35
+#: dashboard/templates/dashboard/vm-detail/network.html:37
 #: firewall/models.py:492
 msgid "IPv6 address"
 msgstr "IPv6 cím"
 
-#: dashboard/templates/dashboard/vm-detail/network.html:36
+#: dashboard/templates/dashboard/vm-detail/network.html:38
 msgid "DNS name"
 msgstr "DNS név"
 
-#: dashboard/templates/dashboard/vm-detail/network.html:49
+#: dashboard/templates/dashboard/vm-detail/network.html:51
 #: network/forms.py:246
 msgid "IPv4"
 msgstr "IPv4"
 
-#: dashboard/templates/dashboard/vm-detail/network.html:50
+#: dashboard/templates/dashboard/vm-detail/network.html:52
 #: network/forms.py:253
 msgid "IPv6"
 msgstr "IPv6"
 
-#: dashboard/templates/dashboard/vm-detail/network.html:52
+#: dashboard/templates/dashboard/vm-detail/network.html:54
 msgid "Port access"
 msgstr "Portok elérése"
 
-#: dashboard/templates/dashboard/vm-detail/network.html:119
+#: dashboard/templates/dashboard/vm-detail/network.html:121
 msgid "This VM doesn't have an IPv6 address!"
 msgstr "A VM-nek nincs IPv6 címe."
 
@@ -2226,11 +2319,11 @@ msgstr "Erőforrások mentése"
 msgid "Stop your VM to change resources."
 msgstr "Állítsa le a VM-et az erőforrások módosításához."
 
-#: dashboard/templates/dashboard/vm-detail/resources.html:57
+#: dashboard/templates/dashboard/vm-detail/resources.html:51
 msgid "Required traits"
 msgstr "Elvárt jellemzők"
 
-#: dashboard/templates/dashboard/vm-detail/resources.html:69
+#: dashboard/templates/dashboard/vm-detail/resources.html:63
 msgid "Raw data"
 msgstr "Nyers adat"
 
@@ -2254,121 +2347,146 @@ msgstr "példányok száma"
 msgid "Allocated memory (bytes)"
 msgstr "Foglalt memória (byte)"
 
-#: dashboard/views/group.py:140
+#: dashboard/views/group.py:150
 #, python-format
 msgid "User \"%s\" not found."
 msgstr "Nem található „%s” felhasználó."
 
-#: dashboard/views/group.py:154
+#: dashboard/views/group.py:164
 msgid "Group successfully renamed."
 msgstr "A csoport átnevezésre került."
 
-#: dashboard/views/group.py:261
+#: dashboard/views/group.py:268
 msgid "Member successfully removed from group."
 msgstr "A csoporttag eltávolításra került."
 
-#: dashboard/views/group.py:302
+#: dashboard/views/group.py:309
 msgid "Future user successfully removed from group."
 msgstr "A leendő csoporttag eltávolításra került."
 
-#: dashboard/views/group.py:329
+#: dashboard/views/group.py:336
 msgid "Group successfully deleted."
 msgstr "A csoport törlésre került."
 
-#: dashboard/views/group.py:369
+#: dashboard/views/group.py:376
 msgid "Create a Group"
 msgstr "Csoport létrehozása"
 
-#: dashboard/views/group.py:385
+#: dashboard/views/group.py:392
 msgid "Group successfully created."
 msgstr "A csoport létrehozásra került."
 
-#: dashboard/views/group.py:399
+#: dashboard/views/group.py:406
 msgid "Group is successfully updated."
 msgstr "A csoport frissítésre került."
 
-#: dashboard/views/node.py:112
+#: dashboard/views/node.py:114
 msgid "Node successfully renamed."
 msgstr "A csomópont átnevezésre került."
 
-#: dashboard/views/node.py:220
+#: dashboard/views/node.py:222
 msgid "Node successfully created."
 msgstr "A csomópont létrehozásra került."
 
-#: dashboard/views/node.py:248
+#: dashboard/views/node.py:250
 msgid "Node successfully deleted."
 msgstr "A csomópont törlésre került."
 
-#: dashboard/views/node.py:295
+#: dashboard/views/node.py:297
 msgid "Trait successfully added to node."
 msgstr "A csomópontjellemző hozzáadásra került."
 
-#: dashboard/views/node.py:340
+#: dashboard/views/node.py:342
 msgid "Node successfully changed status."
 msgstr "A csomópont állapota megváltoztatásra került."
 
-#: dashboard/views/store.py:72
+#: dashboard/views/store.py:73
 msgid "No store."
 msgstr "Nincs tárhely."
 
-#: dashboard/views/store.py:74
+#: dashboard/views/store.py:75
 msgid "Store has some problems now. Try again later."
 msgstr "A tárhely nem működik. Próbálja később."
 
-#: dashboard/views/store.py:78
+#: dashboard/views/store.py:79
 msgid "Unknown store error."
 msgstr "Ismeretlen tárhelyhiba."
 
-#: dashboard/views/store.py:95
+#: dashboard/views/store.py:96
 msgid "Something went wrong during download."
 msgstr "Hiba a letöltésben."
 
-#: dashboard/views/store.py:110 dashboard/views/store.py:130
+#: dashboard/views/store.py:111 dashboard/views/store.py:131
 msgid "Unable to upload file."
 msgstr "Fájl feltöltése sikertelen."
 
-#: dashboard/views/store.py:167
+#: dashboard/views/store.py:168
 #, python-format
 msgid "Unable to remove %s."
 msgstr "%s törlése sikertelen."
 
-#: dashboard/views/store.py:186
+#: dashboard/views/store.py:187
 msgid "Unable to create folder."
 msgstr "Mappa létrehozása sikertelen."
 
-#: dashboard/views/template.py:64
+#: dashboard/views/template.py:65
 msgid "Choose template"
 msgstr "Válasszon sablont"
 
-#: dashboard/views/template.py:79
+#: dashboard/views/template.py:80
 msgid "Select an option to proceed."
 msgstr "Válasszon a folytatáshoz."
 
-#: dashboard/views/template.py:110
+#: dashboard/views/template.py:111
 msgid "Create a new base VM"
 msgstr "Alap VM létrehozása"
 
-#: dashboard/views/template.py:225
+#: dashboard/views/template.py:226
 msgid "Error during filtering."
 msgstr "A szűrés sikertelen."
 
-#: dashboard/views/template.py:250
+#: dashboard/views/template.py:245
+msgid "Only the owners can delete the selected template."
+msgstr "A kiválasztott sablont csak a tulajdonosok törölhetik."
+
+#: dashboard/views/template.py:261
 msgid "Template successfully deleted."
 msgstr "A sablon törlésre került."
 
-#: dashboard/views/template.py:266
+#: dashboard/views/template.py:277
 msgid "Successfully modified template."
 msgstr "A sablon módosításra került."
 
-#: dashboard/views/template.py:327
+#: dashboard/views/template.py:352
+msgid "Disk remove confirmation"
+msgstr "Lemez törlésének megerősítése"
+
+#: dashboard/views/template.py:353
+#, python-format
+msgid ""
+"Are you sure you want to remove <strong>%(disk)s</strong> from <strong>"
+"%(app)s</strong>?"
+msgstr ""
+"Biztosan eltávolítja a(z) <strong>%(disk)s</strong> lemezt a következőből: "
+"%(app)s?"
+
+#: dashboard/views/template.py:372
+msgid "Disk successfully removed."
+msgstr "A lemez eltávolításra került."
+
+#: dashboard/views/template.py:390
 msgid "Successfully created a new lease."
 msgstr "Új bérlési mód létrehozásra került."
 
-#: dashboard/views/template.py:342
+#: dashboard/views/template.py:409
 msgid "Successfully modified lease."
 msgstr "A bérlési mód megváltoztatásra került."
 
-#: dashboard/views/template.py:372
+#: dashboard/views/template.py:423
+msgid "Only the owners can modify the selected lease."
+msgstr "Csak a tulajdonosai törölhetik a kiválasztott bérleti módot."
+
+#: dashboard/views/template.py:452
 msgid ""
 "You can't delete this lease because some templates are still using it, "
 "modify these to proceed: "
@@ -2376,7 +2494,11 @@ msgstr ""
 "Nem törölhető a bérleti mód, mivel az alábbi sablonok még használják. A "
 "folytatáshoz módosítsa őket: "
 
-#: dashboard/views/template.py:389
+#: dashboard/views/template.py:463
+msgid "Only the owners can delete the selected lease."
+msgstr "Csak a tulajdonos törölheti a kiválasztott bérleti módot."
+
+#: dashboard/views/template.py:481
 msgid "Lease successfully deleted."
 msgstr "A bérlési mód törlésre került."
 
@@ -2393,27 +2515,27 @@ msgstr "Nincs profilja."
 msgid "Successfully modified subscription."
 msgstr "A feliratkozás módosításra került."
 
-#: dashboard/views/user.py:350
+#: dashboard/views/user.py:369
 msgid "Successfully modified SSH key."
 msgstr "Az SSH kulcs módosításra került."
 
-#: dashboard/views/user.py:388
+#: dashboard/views/user.py:407
 msgid "SSH key successfully deleted."
 msgstr "Az SSH kulcs törlésre került."
 
-#: dashboard/views/user.py:404
+#: dashboard/views/user.py:423
 msgid "Successfully created a new SSH key."
 msgstr "Az új SSH kulcs hozzáadásra került."
 
-#: dashboard/views/user.py:420
+#: dashboard/views/user.py:439
 msgid "Successfully modified command template."
 msgstr "A parancssablon módosításra került."
 
-#: dashboard/views/user.py:463
+#: dashboard/views/user.py:482
 msgid "Command template successfully deleted."
 msgstr "A parancssablon törlésre került."
 
-#: dashboard/views/user.py:480
+#: dashboard/views/user.py:499
 msgid "Successfully created a new command template."
 msgstr "A parancssablon létrehozásra került."
 
@@ -2448,170 +2570,153 @@ msgstr "A(z) %(w)s ACL felhasználó/csoport hozzáadásra került."
 msgid "Acl user/group %(w)s successfully removed."
 msgstr "A(z) %(w)s ACL felhasználó/csoport törlésre került."
 
-#: dashboard/views/util.py:474
+#: dashboard/views/util.py:475
 msgid ""
 "The original owner cannot be removed, however you can transfer ownership."
 msgstr "Az eredeti tulajdonos nem törölhető, azonban a tulajdon átruházható."
 
-#: dashboard/views/util.py:510
+#: dashboard/views/util.py:511
 #, python-format
 msgid "User \"%s\" has already access to this object."
 msgstr "„%s” felhasználó már hozzáfér az objektumhoz."
 
-#: dashboard/views/util.py:519
+#: dashboard/views/util.py:520
 #, python-format
 msgid "Group \"%s\" has already access to this object."
 msgstr "„%s” csoport már hozzáfér az objektumhoz."
 
-#: dashboard/views/util.py:524
+#: dashboard/views/util.py:525
 #, python-format
 msgid "User or group \"%s\" not found."
 msgstr "Nem található „%s” felhasználó vagy csoport."
 
-#: dashboard/views/util.py:540
+#: dashboard/views/util.py:541
 msgid "1 hour"
 msgstr "1 óra"
 
-#: dashboard/views/util.py:541
+#: dashboard/views/util.py:542
 msgid "6 hours"
 msgstr "6 óra"
 
-#: dashboard/views/util.py:542
+#: dashboard/views/util.py:543
 msgid "1 day"
 msgstr "1 nap"
 
-#: dashboard/views/util.py:543
+#: dashboard/views/util.py:544
 msgid "1 week"
 msgstr "1 hét"
 
-#: dashboard/views/util.py:544
+#: dashboard/views/util.py:545
 msgid "1 month"
 msgstr "1 hónap"
 
-#: dashboard/views/util.py:545
+#: dashboard/views/util.py:546
 msgid "6 months"
 msgstr "6 hónap"
 
-#: dashboard/views/util.py:554
+#: dashboard/views/util.py:555
 msgid "Bad graph time format, available periods are: h, d, w, and y."
 msgstr "Hibás grafikon időformátum. Lehetséges egységek: h, d, w és y."
 
-#: dashboard/views/vm.py:82
+#: dashboard/views/vm.py:84
 msgid "console access"
 msgstr "konzolhozzáférés"
 
-#: dashboard/views/vm.py:183
+#: dashboard/views/vm.py:193
 msgid "VM successfully renamed."
 msgstr "A virtuális gép átnevezésre került."
 
-#: dashboard/views/vm.py:207
+#: dashboard/views/vm.py:217
 msgid "VM description successfully updated."
 msgstr "A VM leírása megváltoztatásra került."
 
-#: dashboard/views/vm.py:284
+#: dashboard/views/vm.py:294
 msgid "There is a problem with your input."
 msgstr "A megadott érték nem megfelelő."
 
-#: dashboard/views/vm.py:286
+#: dashboard/views/vm.py:296
 msgid "Unknown error."
 msgstr "Ismeretlen hiba."
 
-#: dashboard/views/vm.py:491
+#: dashboard/views/vm.py:543
 msgid "The token has expired."
 msgstr "A token lejárt."
 
-#: dashboard/views/vm.py:676
+#: dashboard/views/vm.py:757
 #, python-format
 msgid "Failed to execute %(op)s operation on instance %(instance)s."
 msgstr "%(op)s végrehajtása meghiúsult a következőn: %(instance)s."
 
-#: dashboard/views/vm.py:692
+#: dashboard/views/vm.py:773
 #, python-format
 msgid "You are not permitted to execute %(op)s on instance %(instance)s."
 msgstr "Nem engedélyezett a(z) %(op)s végrehajtása a(z) %(instance)s gépen."
 
-#: dashboard/views/vm.py:868
+#: dashboard/views/vm.py:955
 msgid "Customize VM"
 msgstr "VM testreszabása"
 
-#: dashboard/views/vm.py:876
+#: dashboard/views/vm.py:963
 msgid "Create a VM"
 msgstr "VM létrehozása"
 
-#: dashboard/views/vm.py:941
+#: dashboard/views/vm.py:1028
 #, python-format
 msgid "Successfully created %(count)d VM."
 msgid_plural "Successfully created %(count)d VMs."
 msgstr[0] "%(count)d VM létrehozásra került."
 msgstr[1] "%(count)d VM létrehozásra került."
 
-#: dashboard/views/vm.py:946
+#: dashboard/views/vm.py:1033
 msgid "VM successfully created."
 msgstr "VM létrehozásra került."
 
-#: dashboard/views/vm.py:975
+#: dashboard/views/vm.py:1062
 #, python-format
 msgid "Instance limit (%d) exceeded."
 msgstr "A példányok létrehozási korlátját (%d) túllépte."
 
-#: dashboard/views/vm.py:1013
+#: dashboard/views/vm.py:1100
 #, python-format
 msgid ""
 "Are you sure you want to remove this interface from <strong>%(vm)s</strong>?"
 msgstr ""
 "Biztosan eltávolítja az interfészt a(z) <strong>%(vm)s</strong> gépből?"
 
-#: dashboard/views/vm.py:1027
+#: dashboard/views/vm.py:1114
 msgid "Interface successfully deleted."
 msgstr "Az interfész törlésre került."
 
-#: dashboard/views/vm.py:1080
-msgid "Disk remove confirmation"
-msgstr "Lemez törlésének megerősítése"
-
-#: dashboard/views/vm.py:1081
-#, python-format
-msgid ""
-"Are you sure you want to remove <strong>%(disk)s</strong> from <strong>"
-"%(app)s</strong>?"
-msgstr ""
-"Biztosan eltávolítja a(z) <strong>%(disk)s</strong> lemezt a következőből: "
-"%(app)s?"
-
-#: dashboard/views/vm.py:1100
-msgid "Disk successfully removed."
-msgstr "A lemez eltávolításra került."
-
-#: dashboard/views/vm.py:1141
+#: dashboard/views/vm.py:1183
 msgid "Port delete confirmation"
 msgstr "Porteltávolítás megerősítése"
 
-#: dashboard/views/vm.py:1142
+#: dashboard/views/vm.py:1184
 #, python-format
 msgid "Are you sure you want to close %(port)d/%(proto)s on %(vm)s?"
 msgstr "Biztosan bezárja a(z) %(port)d/%(proto)s portot a következőn: %(vm)s?"
 
-#: dashboard/views/vm.py:1157
+#: dashboard/views/vm.py:1199
 msgid "Port successfully removed."
 msgstr "A port eltávolításra került."
 
-#: dashboard/views/vm.py:1184
+#: dashboard/views/vm.py:1226
 msgid "About CIRCLE Client"
 msgstr "A CIRCLE kliensről"
 
-#: dashboard/views/vm.py:1281
+#: dashboard/views/vm.py:1323
 msgid "Transfer ownership"
 msgstr "Tulajdon átruházása"
 
-#: dashboard/views/vm.py:1294
+#: dashboard/views/vm.py:1336
 msgid "Can not find specified user."
 msgstr "Nem található a megadott felhasználó."
 
-#: dashboard/views/vm.py:1310
+#: dashboard/views/vm.py:1352
 msgid "Ownership offer"
 msgstr "Átruházási ajánlat"
 
-#: dashboard/views/vm.py:1311
+#: dashboard/views/vm.py:1353
 #, python-format
 msgid ""
 "%(user)s offered you to take the ownership of his/her virtual machine called "
@@ -2621,32 +2726,32 @@ msgstr ""
 "%(user)s át kívánja ruházni %(instance)s nevű virtuális gépét Önre. <a href="
 "\"%(token)s\" class=\"btn btn-success btn-small\">Elfogadás</a>"
 
-#: dashboard/views/vm.py:1317
+#: dashboard/views/vm.py:1359
 msgid "Can not notify selected user."
 msgstr "A kiválaszott felhasználó értesítése sikertelen."
 
-#: dashboard/views/vm.py:1320
+#: dashboard/views/vm.py:1362
 #, python-format
 msgid "User %s is notified about the offer."
 msgstr "%s felhasználó értesítésre került az ajánlatról."
 
-#: dashboard/views/vm.py:1331
+#: dashboard/views/vm.py:1373
 msgid "Ownership successfully transferred to you."
 msgstr "A tulajdon átruházásra került."
 
-#: dashboard/views/vm.py:1344
+#: dashboard/views/vm.py:1386
 msgid "This token is for an other user."
 msgstr "A token más felhasználó nevére szól."
 
-#: dashboard/views/vm.py:1347
+#: dashboard/views/vm.py:1389
 msgid "This token is invalid or has expired."
 msgstr "A token érvénytelen vagy lejárt."
 
-#: dashboard/views/vm.py:1370
+#: dashboard/views/vm.py:1411
 msgid "Ownership accepted"
 msgstr "Átruházás elfogadva"
 
-#: dashboard/views/vm.py:1371
+#: dashboard/views/vm.py:1412
 #, python-format
 msgid "Your ownership offer of %(instance)s has been accepted by %(user)s."
 msgstr "%(instance)s gépre vonatkozó átruházási ajánlatát elfogadta %(user)s."
@@ -2728,8 +2833,8 @@ msgstr "A szabály kimenő vagy bejövő csomagokra illeszkedik."
 
 #: firewall/models.py:68 firewall/models.py:334 firewall/models.py:419
 #: firewall/models.py:442 firewall/models.py:499 firewall/models.py:857
-#: firewall/models.py:881 firewall/models.py:951 vm/models/instance.py:146
-#: vm/models/instance.py:236
+#: firewall/models.py:881 firewall/models.py:951 vm/models/instance.py:137
+#: vm/models/instance.py:227
 msgid "description"
 msgstr "leírás"
 
@@ -2828,8 +2933,8 @@ msgstr "módosítva"
 
 #: firewall/models.py:124 firewall/models.py:507
 #: network/templates/network/vlan-create.html:8
-#: network/templates/network/vlan-edit.html:8 vm/models/network.py:40
-#: vm/models/network.py:65
+#: network/templates/network/vlan-edit.html:8 vm/models/network.py:39
+#: vm/models/network.py:64
 msgid "vlan"
 msgstr "vlan"
 
@@ -2848,7 +2953,7 @@ msgstr "Erre a vlan-csoportra vonatkozik a szabály (ha a típus vlan)."
 
 #: firewall/models.py:133 firewall/models.py:874 firewall/models.py:994
 #: network/templates/network/host-create.html:8
-#: network/templates/network/host-edit.html:8 vm/models/network.py:67
+#: network/templates/network/host-edit.html:8 vm/models/network.py:66
 #: vm/models/node.py:70
 msgid "host"
 msgstr "gép"
@@ -2968,7 +3073,7 @@ msgstr ""
 msgid "network type"
 msgstr "hálózat típusa"
 
-#: firewall/models.py:333 vm/models/network.py:42
+#: firewall/models.py:333 vm/models/network.py:41
 msgid "managed"
 msgstr "menedzselt"
 
@@ -3788,7 +3893,7 @@ msgstr "eszközazonosító"
 msgid "disk"
 msgstr "lemez"
 
-#: storage/models.py:104 vm/models/instance.py:151 vm/models/instance.py:257
+#: storage/models.py:104 vm/models/instance.py:142 vm/models/instance.py:248
 msgid "disks"
 msgstr "lemezek"
 
@@ -3800,77 +3905,81 @@ msgstr "Létrehozhat új lemezt."
 msgid "Can download a disk."
 msgstr "Letölthet lemezt."
 
-#: storage/models.py:120
+#: storage/models.py:108
+msgid "Can resize a disk."
+msgstr "Átméretezhet lemezt."
+
+#: storage/models.py:122
 #, python-format
 msgid "Operation can't be invoked on disk '%(name)s' of type '%(type)s'."
 msgstr ""
-"A kér művelet nem hajtható végre a(z) %(type)s típusú „%(name)s” lemezen."
+"A kért művelet nem hajtható végre a(z) %(type)s típusú „%(name)s” lemezen."
 
-#: storage/models.py:124
+#: storage/models.py:126
 #, python-format
 msgid ""
 "Operation can't be invoked on disk '%(name)s' (%(pk)s) of type '%(type)s'."
 msgstr ""
-"A kér művelet nem hajtható végre a(z) %(type)s típusú „%(name)s” (%(pk)s) "
+"A kért művelet nem hajtható végre a(z) %(type)s típusú „%(name)s” (%(pk)s) "
 "lemezen."
 
-#: storage/models.py:133
+#: storage/models.py:135
 #, python-format
 msgid ""
 "The requested operation can't be performed on disk '%(name)s' because it is "
 "in use."
 msgstr ""
-"A kér művelet nem hajtható végre a(z) „%(name)s” lemezen, mivel használatban "
+"A kért művelet nem hajtható végre a(z) „%(name)s” lemezen, mivel használatban "
 "van."
 
-#: storage/models.py:137
+#: storage/models.py:139
 #, python-format
 msgid ""
 "The requested operation can't be performed on disk '%(name)s' (%(pk)s) "
 "because it is in use."
 msgstr ""
-"A kér művelet nem hajtható végre a(z) „%(name)s” (%(pk)s) lemezen, mivel "
+"A kért művelet nem hajtható végre a(z) „%(name)s” (%(pk)s) lemezen, mivel "
 "használatban van."
 
-#: storage/models.py:146
+#: storage/models.py:148
 #, python-format
 msgid ""
 "The requested operation can't be performed on disk '%(name)s' because it has "
 "never been deployed."
 msgstr ""
-"A kér művelet nem hajtható végre a(z) „%(name)s” lemezen, mivel nem volt még "
+"A kért művelet nem hajtható végre a(z) „%(name)s” lemezen, mivel nem volt még "
 "csatolva."
 
-#: storage/models.py:150
+#: storage/models.py:152
 #, python-format
 msgid ""
 "The requested operation can't be performed on disk '%(name)s' (%(pk)s) "
 "[%(filename)s] because it has never beendeployed."
 msgstr ""
-"A kér művelet nem hajtható végre a(z) „%(name)s” (%(pk)s) [%(filename)s] "
+"A kért művelet nem hajtható végre a(z) „%(name)s” (%(pk)s) [%(filename)s] "
 "lemezen, mivel nem volt még csatolva."
 
-#: storage/models.py:161
+#: storage/models.py:163
 #, python-format
 msgid ""
 "The requested operation can't be performed on disk '%(name)s' because its "
 "base has never been deployed."
 msgstr ""
-"A kér művelet nem hajtható végre a(z) „%(name)s” lemezen, mivel az alapja "
+"A kért művelet nem hajtható végre a(z) „%(name)s” lemezen, mivel az alapja "
 "nem volt még csatolva."
 
-#: storage/models.py:165
+#: storage/models.py:167
 #, python-format
 msgid ""
 "The requested operation can't be performed on disk '%(name)s' (%(pk)s) "
 "[%(filename)s] because its base '%(b_name)s' (%(b_pk)s) [%(b_filename)s] has "
 "never beendeployed."
 msgstr ""
-"A kér művelet nem hajtható végre a(z) „%(name)s” (%(pk)s) [%(filename)s] "
+"A kért művelet nem hajtható végre a(z) „%(name)s” (%(pk)s) [%(filename)s] "
 "lemezen, mivel az alapja,  „%(b_name)s” (%(b_pk)s) [%(b_filename)s]  nem "
 "volt még csatolva."
 
-#: storage/models.py:414 storage/models.py:489 vm/models/instance.py:890
+#: storage/models.py:416 storage/models.py:493 vm/operations.py:92
 msgid "Operation aborted by user."
 msgstr "A műveletet a felhasználó megszakította."
 
@@ -4518,59 +4627,68 @@ msgstr "Jelszó visszaállítása"
 msgid "Enter your email address to reset your password."
 msgstr "Adja meg e-mail címét a jelszó visszaállításához."
 
-#: vm/operations.py:86
+#: vm/operations.py:129
 #, python-format
 msgid "%(acl_level)s level is required for this operation."
 msgstr "%(acl_level)s jogosultság szükséges a művelethez."
 
-#: vm/operations.py:124
+#: vm/operations.py:202
 msgid "Add a new network interface for the specified VLAN to the VM."
 msgstr "Új hálózati interfész hozzáadása a megadott VLAN-ba."
 
-#: vm/operations.py:132
+#: vm/operations.py:210
 msgid "destroy network (rollback)"
 msgstr "hálózat megsemmisítése (visszagörgetés)"
 
-#: vm/operations.py:139
+#: vm/operations.py:217
 #, python-format
 msgid "User acces to vlan %(vlan)s is required."
 msgstr "Használói jogosultság szükséges a(z) %(vlan)s vlan-hoz."
 
-#: vm/operations.py:151
-msgid "attach network"
-msgstr "hálózat csatolása"
-
-#: vm/operations.py:161
+#: vm/operations.py:238
 #, python-format
 msgid "add %(vlan)s interface"
 msgstr "új %(vlan)s interfész"
 
-#: vm/operations.py:170
+#: vm/operations.py:246
 msgid "create disk"
 msgstr "lemez létrehozása"
 
-#: vm/operations.py:171
+#: vm/operations.py:247
 msgid "Create and attach empty disk to the virtual machine."
 msgstr "Üres lemez létehozása és virtuális géphez csatolása."
 
-#: vm/operations.py:192
+#: vm/operations.py:268
 msgid "deploying disk"
 msgstr "lemez létrehozása"
 
-#: vm/operations.py:197 vm/operations.py:241
-msgid "attach disk"
-msgstr "lemez csatolása"
-
-#: vm/operations.py:203
+#: vm/operations.py:275
 #, python-format
 msgid "create disk %(name)s (%(size)s)"
 msgstr "%(name)s lemez létrehozása (%(size)s)"
 
-#: vm/operations.py:211
+#: vm/operations.py:283
+msgid "resize disk"
+msgstr "lemez átméretezése"
+
+#: vm/operations.py:284
+msgid ""
+"Resize the virtual disk image. Size must be greater value than the actual "
+"size."
+msgstr ""
+"Virtuális lemezkép átméretezése. Az új méret meg kell haladja "
+"a jelenlegit."
+
+#: vm/operations.py:298
+#, python-format
+msgid "resize disk %(name)s to %(size)s"
+msgstr "%(name)s lemez átméretezése (%(size)s)"
+
+#: vm/operations.py:310
 msgid "download disk"
 msgstr "lemez letöltése"
 
-#: vm/operations.py:212
+#: vm/operations.py:311
 msgid ""
 "Download and attach disk image (ISO file) for the virtual machine. Most "
 "operating systems do not detect a new optical drive, so you may have to "
@@ -4580,16 +4698,21 @@ msgstr ""
 "operációs rendszer nem érzékeli az új optikai meghajtót, így valószínűleg "
 "újra kell indítania a virtuális gépet."
 
-#: vm/operations.py:235
+#: vm/operations.py:333
 #, python-format
 msgid "download %(name)s"
 msgstr "%(name)s letöltése"
 
-#: vm/operations.py:250
+#: vm/operations.py:336
+#, python-format
+msgid "Downloading %(url)s is finished. The file md5sum is: '%(checksum)s'."
+msgstr "%(url)s letöltése sikeres. A fájl md5sum összege: '%(checksum)s'."
+
+#: vm/operations.py:347
 msgid "deploy"
 msgstr "indítás"
 
-#: vm/operations.py:251
+#: vm/operations.py:348
 msgid ""
 "Deploy and start the virtual machine (including storage and network "
 "configuration)."
@@ -4597,101 +4720,117 @@ msgstr ""
 "Virtuális gép elhelyezése és indítása (valamint a lemezek és a hálózat "
 "beállítása)."
 
-#: vm/operations.py:268
+#: vm/operations.py:365
 #, python-format
 msgid "virtual machine successfully deployed to node: %(node)s"
 msgstr "a virtuális gép sikeresen elindítva a következő csomóponton: %(node)s"
 
-#: vm/operations.py:280
-msgid "deploy disks"
-msgstr "lemez létrehozása"
+#: vm/operations.py:388 vm/operations.py:563 vm/operations.py:889
+msgid "deploy network"
+msgstr "hálózati kapcsolat létrehozása"
+
+#: vm/operations.py:400 vm/operations.py:581 vm/operations.py:645
+msgid "wait operating system loading"
+msgstr "várakozás az operációs rendszer betöltésére"
+
+#: vm/operations.py:405
+msgid "deploy vm"
+msgstr "vm indítása"
+
+#: vm/operations.py:406
+msgid "Deploy virtual machine."
+msgstr "Virtuális gép létrehozása."
 
-#: vm/operations.py:285
+#: vm/operations.py:415
 msgid "deploy virtual machine"
 msgstr "virtuális gép létrehozása"
 
-#: vm/operations.py:286
+#: vm/operations.py:416
 #, python-format
 msgid "deploy vm to %(node)s"
 msgstr "vm létrehozása: %(node)s"
 
-#: vm/operations.py:295 vm/operations.py:408 vm/operations.py:734
-msgid "deploy network"
-msgstr "hálózati kapcsolat létrehozása"
+#: vm/operations.py:422
+msgid "deploy disks"
+msgstr "lemez létrehozása"
+
+#: vm/operations.py:423
+msgid "Deploy all associated disks."
+msgstr "Csatolt lemezek létrehozása."
 
-#: vm/operations.py:306
+#: vm/operations.py:440
 msgid "boot virtual machine"
 msgstr "virtuális gép indítása"
 
-#: vm/operations.py:311 vm/operations.py:426 vm/operations.py:498
-msgid "wait operating system loading"
-msgstr "várakozás az operációs rendszer betöltésére"
-
-#: vm/operations.py:318
+#: vm/operations.py:448
 msgid "destroy"
 msgstr "megsemmisítés"
 
-#: vm/operations.py:319
+#: vm/operations.py:449
 msgid "Permanently destroy virtual machine, its network settings and disks."
 msgstr ""
 "Virtuális gép és lemezeinek, hálózati beállításainak végleges eltávolítása."
 
-#: vm/operations.py:328
+#: vm/operations.py:458
 msgid "destroy network"
 msgstr "hálózat megsemmisítése"
 
-#: vm/operations.py:337
+#: vm/operations.py:469
+msgid "destroy disks"
+msgstr "lemez megsemmisítése"
+
+#: vm/operations.py:488
 msgid "destroy virtual machine"
 msgstr "virtuális gép megsemmisítése"
 
-#: vm/operations.py:343
-msgid "destroy disks"
-msgstr "lemez megsemmisítése"
+#: vm/operations.py:496
+msgid "removing memory dump"
+msgstr "memóriamentés törlése"
 
-#: vm/operations.py:364
+#: vm/operations.py:510
 msgid "migrate"
 msgstr "migrálás"
 
-#: vm/operations.py:365
+#: vm/operations.py:511
 msgid ""
-"Move virtual machine to an other worker node with a few seconds of "
-"interruption (live migration)."
+"Move a running virtual machine to an other worker node keeping its full "
+"state."
 msgstr ""
-"A virtuális gép áthelyezése egy másik számítási csomópontra néhány másodperc "
-"kimaradással (live migration)."
+"A virtuális gép mozgatása egy másik számítási csomópontra állapotának "
+"megtartásával."
 
-#: vm/operations.py:375
+#: vm/operations.py:528
 msgid "redeploy network (rollback)"
 msgstr "hálózati kapcsolat újraépítése (visszagörgetés)"
 
-#: vm/operations.py:382
+#: vm/operations.py:535
 msgid "schedule"
 msgstr "ütemezés"
 
-#: vm/operations.py:389
+#: vm/operations.py:542
 #, python-format
 msgid "migrate to %(node)s"
 msgstr "migrálás %(node)s csomópontra"
 
-#: vm/operations.py:399 vm/operations.py:686
+#: vm/operations.py:553 vm/operations.py:838
 msgid "shutdown network"
 msgstr "hálózati kapcsolat leállítása"
 
-#: vm/operations.py:416
+#: vm/operations.py:570
 msgid "reboot"
 msgstr "újraindítás"
 
-#: vm/operations.py:417
+#: vm/operations.py:571
 msgid ""
 "Warm reboot virtual machine by sending Ctrl+Alt+Del signal to its console."
 msgstr ""
 "Virtuális gép újraindítása a konzoljára a Ctrl+Alt+Del kombináció küldésével."
 
-#: vm/operations.py:433
+#: vm/operations.py:587
 msgid "remove interface"
 msgstr "interfész törlése"
 
-#: vm/operations.py:434
+#: vm/operations.py:588
 msgid ""
 "Remove the specified network interface and erase IP address allocations, "
 "related firewall rules and hostnames."
@@ -4699,50 +4838,42 @@ msgstr ""
 "A kiválasztott hálózati interfész eltávolítása, a foglalt IP címek, "
 "tűzfalszabályok és gépnevek törlése."
 
-#: vm/operations.py:444
-msgid "detach network"
-msgstr "hálózat lecsatolása"
-
-#: vm/operations.py:453
+#: vm/operations.py:604
 #, python-format
 msgid "remove %(vlan)s interface"
 msgstr "%(vlan)s interfész törlése"
 
-#: vm/operations.py:461
+#: vm/operations.py:611
 msgid "remove disk"
 msgstr "lemez eltávolítása"
 
-#: vm/operations.py:462
+#: vm/operations.py:612
 msgid ""
 "Remove the specified disk from the virtual machine, and destroy the data."
 msgstr "A megadott lemez eltávolítása a virtuális gépből és az adat törlése."
 
-#: vm/operations.py:471
-msgid "detach disk"
-msgstr "lemez leválasztása"
-
-#: vm/operations.py:476
+#: vm/operations.py:622
 msgid "destroy disk"
 msgstr "lemez megsemmisítése"
 
-#: vm/operations.py:481
+#: vm/operations.py:628
 #, python-format
 msgid "remove disk %(name)s"
 msgstr "%(name)s lemez eltávolítása"
 
-#: vm/operations.py:489
+#: vm/operations.py:635 vm/operations.py:1041
 msgid "reset"
 msgstr "reset"
 
-#: vm/operations.py:490
+#: vm/operations.py:636
 msgid "Cold reboot virtual machine (power cycle)."
 msgstr "Virtuális gép hideg újraindítása (hálózati tápellátás megszakítása)."
 
-#: vm/operations.py:505
+#: vm/operations.py:651
 msgid "save as template"
 msgstr "mentés sablonként"
 
-#: vm/operations.py:506
+#: vm/operations.py:652
 msgid ""
 "Save virtual machine as a template so they can be shared with users and "
 "groups.  Anyone who has access to a template (and to the networks it uses) "
@@ -4752,16 +4883,16 @@ msgstr ""
 "felhasználókkal és csoportokkal. Mindenki, aki hozzáférést kap egy sablonhoz "
 "(és az általa használt hálózatokhoz), képes lesz egy példányát elindítani."
 
-#: vm/operations.py:576
+#: vm/operations.py:728
 #, python-format
 msgid "saving disk %(name)s"
 msgstr "%(name)s lemez mentése"
 
-#: vm/operations.py:601
+#: vm/operations.py:755
 msgid "shutdown"
 msgstr "leállítás"
 
-#: vm/operations.py:602
+#: vm/operations.py:756
 msgid ""
 "Try to halt virtual machine by a standard ACPI signal, allowing the "
 "operating system to keep a consistent state. The operation will fail if the "
@@ -4771,7 +4902,7 @@ msgstr ""
 "operációs rendszer számár a szabályos leállást. A művelet meghiúsul, ha a "
 "gép nem áll le."
 
-#: vm/operations.py:618
+#: vm/operations.py:775
 msgid ""
 "The virtual machine did not switch off in the provided time limit. Most of "
 "the time this is caused by incorrect ACPI settings. You can also try to "
@@ -4781,11 +4912,11 @@ msgstr ""
 "ez a nem megfelelő ACPI beállítások miatt van. Megpróbálhatja a gépet az "
 "operációs rendszerből, kézzel leállítani."
 
-#: vm/operations.py:631
+#: vm/operations.py:787
 msgid "shut off"
 msgstr "kikapcsolás"
 
-#: vm/operations.py:632
+#: vm/operations.py:788
 msgid ""
 "Forcibly halt a virtual machine without notifying the operating system. This "
 "operation will even work in cases when shutdown does not, but the operating "
@@ -4798,11 +4929,11 @@ msgstr ""
 "rendszer és a fájlrendszer sérülhet, adatvesztés történhet. A művelet hatása "
 "hasonló, mint egy fizikai gép tápellátásának megszüntetése."
 
-#: vm/operations.py:659
+#: vm/operations.py:811
 msgid "sleep"
 msgstr "altatás"
 
-#: vm/operations.py:660
+#: vm/operations.py:812
 msgid ""
 "Suspend virtual machine. This means the machine is stopped and its memory is "
 "saved to disk, so if the machine is waked up, all the applications will keep "
@@ -4818,15 +4949,15 @@ msgstr ""
 "megállhatnak visszaállítás után. A felfüggesztés ideje alatt a virtuális gép "
 "csak tárterületet és hálózati erőforrásokat foglal."
 
-#: vm/operations.py:692
+#: vm/operations.py:846
 msgid "suspend virtual machine"
 msgstr "virtuális gép felfüggesztése"
 
-#: vm/operations.py:703
+#: vm/operations.py:860
 msgid "wake up"
 msgstr "virtuális gép ébresztése"
 
-#: vm/operations.py:704
+#: vm/operations.py:861
 msgid ""
 "Wake up sleeping (suspended) virtual machine. This will load the saved "
 "memory of the system and start the virtual machine from this state."
@@ -4834,15 +4965,15 @@ msgstr ""
 "Alvó (felfüggesztett) gép ébresztése: az elmentett memóriatartalom "
 "visszatöltése és a virtuális gép indítása ebből a mentett állapotból."
 
-#: vm/operations.py:728
+#: vm/operations.py:900
 msgid "resume virtual machine"
 msgstr "virtuális gép ébresztése"
 
-#: vm/operations.py:747
+#: vm/operations.py:914
 msgid "renew"
 msgstr "megújítás"
 
-#: vm/operations.py:748
+#: vm/operations.py:915
 msgid ""
 "Virtual machines are suspended and destroyed after they expire. This "
 "operation renews expiration times according to the lease type. If the "
@@ -4852,7 +4983,7 @@ msgstr ""
 "a művelet megújítja a bérletet a kiválasztott típusnak megfelelően. Ha egy "
 "gép közeledik a lejárathoz, a tulajdonost értesítjük."
 
-#: vm/operations.py:761
+#: vm/operations.py:928
 msgid ""
 "Renewing the machine with the selected lease would result in its suspension "
 "time get earlier than before."
@@ -4860,7 +4991,7 @@ msgstr ""
 "A gép megújítása a kiválasztott bérleti mód mellett a felfüggesztési időt "
 "korábbra állította volna, mint a jelenlegi érték."
 
-#: vm/operations.py:766
+#: vm/operations.py:933
 msgid ""
 "Renewing the machine with the selected lease would result in its delete time "
 "get earlier than before."
@@ -4868,17 +4999,17 @@ msgstr ""
 "A gép megújítása a kiválasztott bérleti mód mellett a törlési időt korábbra "
 "állította volna, mint a jelenlegi érték."
 
-#: vm/operations.py:774
+#: vm/operations.py:941
 #, python-format
 msgid "Renewed to suspend at %(suspend)s and destroy at %(delete)s."
 msgstr ""
 "Megújítás után felfüggesztés ideje: %(suspend)s, a törlésé: %(delete)s."
 
-#: vm/operations.py:782
+#: vm/operations.py:948
 msgid "emergency state change"
 msgstr "vész-állapotváltás"
 
-#: vm/operations.py:783
+#: vm/operations.py:949
 msgid ""
 "Change the virtual machine state to NOSTATE. This should only be used if "
 "manual intervention was needed in the virtualization layer, and the machine "
@@ -4889,46 +5020,68 @@ msgstr ""
 "rétegben, és úgy szeretné a gépet újból elindítani, hogy ne vesszenek el "
 "lemezei vagy hálózati erőforrásai."
 
-#: vm/operations.py:795
+#: vm/operations.py:962
 msgid "Activity is forcibly interrupted."
 msgstr "A tevékenység erőszakos megszakításra került."
 
-#: vm/operations.py:817
+#: vm/operations.py:977
+msgid "redeploy"
+msgstr "újbóli létrehozás"
+
+#: vm/operations.py:978
+msgid ""
+"Change the virtual machine state to NOSTATE and redeploy the VM. This "
+"operation allows starting machines formerly running on a failed node."
+msgstr ""
+"A virtuális gép állapotának átállítása NOSTATE-re, majd a VM újbóli "
+"létrehozása. "
+"Ez a művelet lehetővé teszi olyan gépek elindítását, amelyek korábban egy "
+"meghibásodott csomóponton futnak."
+
+#: vm/operations.py:1014
 msgid "You cannot call this operation on an offline node."
 msgstr "Nem hívható ez a művelet elérhetetlen csomópontra."
 
-#: vm/operations.py:844
+#: vm/operations.py:1042
+msgid "Disable missing node and redeploy all instances on other ones."
+msgstr "Hiányzó csomópont letiltása és az összes példány elindítása a többin."
+
+#: vm/operations.py:1052
+msgid "You cannot reset a disabled or online node."
+msgstr "Tiltott vagy elérhető csomópont resetelése nem lehetséges."
+
+#: vm/operations.py:1060 vm/operations.py:1080
+#, python-format
+msgid "migrate %(instance)s (%(pk)s)"
+msgstr "%(instance)s (%(pk)s) migrálása"
+
+#: vm/operations.py:1069
 msgid "flush"
 msgstr "ürítés"
 
-#: vm/operations.py:845
+#: vm/operations.py:1070
 msgid "Passivate node and move all instances to other ones."
 msgstr ""
 "A csomópont passzívra állítása és az összes példány másikakra mozgatása."
 
-#: vm/operations.py:855
-#, python-format
-msgid "migrate %(instance)s (%(pk)s)"
-msgstr "%(instance)s (%(pk)s) migrálása"
-
-#: vm/operations.py:865
+#: vm/operations.py:1089
 msgid "activate"
 msgstr "aktiválás"
 
-#: vm/operations.py:866
+#: vm/operations.py:1090
 msgid ""
 "Make node active, i.e. scheduler is allowed to deploy virtual machines to it."
 msgstr "Csomópont aktívvá tétele: az ütemező indíthat virtuális gépeket rajta."
 
-#: vm/operations.py:874
+#: vm/operations.py:1098
 msgid "You cannot activate an active node."
 msgstr "Aktív csomópont aktiválása nem lehetséges."
 
-#: vm/operations.py:886
+#: vm/operations.py:1109
 msgid "passivate"
 msgstr "passziválás"
 
-#: vm/operations.py:887
+#: vm/operations.py:1110
 msgid ""
 "Make node passive, i.e. scheduler is denied to deploy virtual machines to "
 "it, but remaining instances and the ones manually migrated will continue "
@@ -4937,31 +5090,31 @@ msgstr ""
 "Csomópont passzívvá tétele: az ütemező nem indíthat rajta virtuális gépeket, "
 "azonban a megmaradt példányok és a kézzel idemigráltak tovább működnek."
 
-#: vm/operations.py:895
+#: vm/operations.py:1118
 msgid "You cannot passivate a passive node."
 msgstr "Passzív csomópont passziválása nem lehetséges."
 
-#: vm/operations.py:908
+#: vm/operations.py:1130
 msgid "disable"
 msgstr "tiltás"
 
-#: vm/operations.py:909
+#: vm/operations.py:1131
 msgid "Disable node."
 msgstr "Csomópont tiltása."
 
-#: vm/operations.py:916
+#: vm/operations.py:1138
 msgid "You cannot disable a disabled node."
 msgstr "Tiltott csomópont tiltása nem lehetséges."
 
-#: vm/operations.py:919
+#: vm/operations.py:1141
 msgid "You cannot disable a node which is hosting instances."
 msgstr "Nem tiltható le olyan csomópont, amelyen még futnak példányok."
 
-#: vm/operations.py:933
+#: vm/operations.py:1154
 msgid "screenshot"
 msgstr "képernyőkép"
 
-#: vm/operations.py:934
+#: vm/operations.py:1155
 msgid ""
 "Get a screenshot about the virtual machine's console. A key will be pressed "
 "on the keyboard to stop screensaver."
@@ -4969,11 +5122,11 @@ msgstr ""
 "Képernyőkép készítése a virtuális gép konzoljáról. Egy billentyűnyomást "
 "követően készül a kép a képernyővédő miatt."
 
-#: vm/operations.py:949
+#: vm/operations.py:1167
 msgid "recover"
 msgstr "visszaállítás"
 
-#: vm/operations.py:950
+#: vm/operations.py:1168
 msgid ""
 "Try to recover virtual machine disks from destroyed state. Network resources "
 "(allocations) are already lost, so you will have to manually add interfaces "
@@ -4983,15 +5136,15 @@ msgstr ""
 "hálózati erőforrások foglalásai már végleg elvesztek, így az interfészeket "
 "kézzel kell a visszaállítás után pótolni."
 
-#: vm/operations.py:977
+#: vm/operations.py:1194
 msgid "resources change"
 msgstr "erőforrások módosítása"
 
-#: vm/operations.py:978
+#: vm/operations.py:1195
 msgid "Change resources of a stopped virtual machine."
 msgstr "Leállított virtuális gép erőforrásainak változtatása."
 
-#: vm/operations.py:995
+#: vm/operations.py:1212
 #, python-format
 msgid ""
 "Priority: %(priority)s, Num cores: %(num_cores)s, Ram size: %(ram_size)s"
@@ -4999,11 +5152,11 @@ msgstr ""
 "Prioritás: %(priority)s, magok száma: %(num_cores)s, memória mérete: "
 "%(ram_size)s"
 
-#: vm/operations.py:1024
+#: vm/operations.py:1221
 msgid "password reset"
 msgstr "jelszó visszaállítása"
 
-#: vm/operations.py:1025
+#: vm/operations.py:1222
 msgid ""
 "Generate and set a new login password on the virtual machine. This operation "
 "requires the agent running. Resetting the password is not warranted to allow "
@@ -5013,11 +5166,52 @@ msgstr ""
 "művelet megköveteli az ügynök futását. A jelszó átállítása nem garantálja a "
 "sikeres belépést, mivel más beállítások is megakadályozhatják ezt."
 
-#: vm/operations.py:1045
+#: vm/operations.py:1246
+msgid "agent"
+msgstr "ügynök"
+
+#: vm/operations.py:1287
+msgid "starting"
+msgstr "indítás"
+
+#: vm/operations.py:1305
+msgid "wait agent restarting"
+msgstr "várakozás az ügynök újraindulására"
+
+#: vm/operations.py:1322
+msgid "cleanup"
+msgstr "takarítás"
+
+#: vm/operations.py:1328
+msgid "set time"
+msgstr "óra beállítása"
+
+#: vm/operations.py:1339
+msgid "set hostname"
+msgstr "gépnév beállítása"
+
+#: vm/operations.py:1350
+msgid "restart networking"
+msgstr "hálózat újratöltése"
+
+#: vm/operations.py:1356
+msgid "change ip"
+msgstr "IP cím beállítása"
+
+#: vm/operations.py:1371
+msgid "update agent"
+msgstr "ügynök frissítése"
+
+#: vm/operations.py:1377
+#, python-format
+msgid "update agent to %(version)s"
+msgstr "ügynökfrissítés erre: %(version)s"
+
+#: vm/operations.py:1460
 msgid "mount store"
 msgstr "tárhely csatolása"
 
-#: vm/operations.py:1047
+#: vm/operations.py:1462
 msgid ""
 "This operation attaches your personal file store. Other users who have "
 "access to this machine can see these files as well."
@@ -5025,37 +5219,62 @@ msgstr ""
 "Ez a művelet csatolja az ön személyes tárhelyét. A gép más felhasználói is "
 "elérhetik fájljait."
 
-#: vm/models/activity.py:47
+#: vm/operations.py:1496
+msgid "attach disk"
+msgstr "lemez csatolása"
+
+#: vm/operations.py:1507
+msgid "Resource was not found."
+msgstr "Nem található az erőforrás."
+
+#: vm/operations.py:1508
+#, python-format
+msgid "Resource was not found. %(exception)s"
+msgstr "Nem található az erőforrás. %(exception)s"
+
+#: vm/operations.py:1517
+msgid "detach disk"
+msgstr "lemez leválasztása"
+
+#: vm/operations.py:1532
+msgid "attach network"
+msgstr "hálózat csatolása"
+
+#: vm/operations.py:1539
+msgid "detach network"
+msgstr "hálózat lecsatolása"
+
+#: vm/models/activity.py:46
 #, python-format
 msgid "%(activity)s activity is currently in progress."
 msgstr "%(activity)s folyamatban van."
 
-#: vm/models/activity.py:48
+#: vm/models/activity.py:47
 #, python-format
 msgid "%(activity)s (%(pk)s) activity is currently in progress."
 msgstr "%(activity)s (%(pk)s) folyamatban van."
 
-#: vm/models/activity.py:70
+#: vm/models/activity.py:69
 msgid "Instance this activity works on."
 msgstr "A tevékenység tárgyát képező példány."
 
-#: vm/models/activity.py:74
+#: vm/models/activity.py:73
 msgid "Other activities can interrupt this one."
 msgstr "Más tevékenységek megszakíthatják ezt."
 
-#: vm/models/activity.py:105
+#: vm/models/activity.py:104
 msgid "Interrupted by other activity."
 msgstr "Egy másik tevékenység megszakította."
 
-#: vm/models/activity.py:227
+#: vm/models/activity.py:211
 msgid "Node this activity works on."
 msgstr "A tevékenység tárgyát képező csomópont."
 
-#: vm/models/activity.py:228
+#: vm/models/activity.py:212
 msgid "node"
 msgstr "csomópont"
 
-#: vm/models/activity.py:286
+#: vm/models/activity.py:267
 msgid "Manager is restarted, activity is cleaned up. You can try again now."
 msgstr ""
 "A menedzser újraindítása miatt a tevékenység lezárásra került. Próbálja újra."
@@ -5125,60 +5344,60 @@ msgstr "soha"
 msgid "%(name)s (suspend: %(s)s, remove: %(r)s)"
 msgstr "%(name)s (felfüggesztés: %(s)s, törlés: %(r)s)"
 
-#: vm/models/instance.py:108
+#: vm/models/instance.py:99
 msgid "Primary remote access method."
 msgstr "Elsődleges távoli elérési mód."
 
-#: vm/models/instance.py:109
+#: vm/models/instance.py:100
 msgid "boot menu"
 msgstr "rendszerbetöltő menüje"
 
-#: vm/models/instance.py:111
+#: vm/models/instance.py:102
 msgid "Show boot device selection menu on boot."
 msgstr ""
 "A rendszerbetöltés eszközének kiválasztását lehetővé tevő menü megjelenítése "
 "indításkor."
 
-#: vm/models/instance.py:112
+#: vm/models/instance.py:103
 msgid "Preferred expiration periods."
 msgstr "Javasolt bérlési mód."
 
-#: vm/models/instance.py:114
+#: vm/models/instance.py:105
 msgid "raw_data"
 msgstr "nyers adat"
 
-#: vm/models/instance.py:115
+#: vm/models/instance.py:106
 msgid "Additional libvirt domain parameters in XML format."
 msgstr "További libvirt domain-paraméterek XML formátumban."
 
-#: vm/models/instance.py:117
+#: vm/models/instance.py:108
 msgid ""
 "A set of traits required for a node to declare to be suitable for hosting "
 "the VM."
 msgstr "A VM indításához szükséges csomópontjellemzők halmaza."
 
-#: vm/models/instance.py:120
+#: vm/models/instance.py:111
 msgid "required traits"
 msgstr "elvárt jellemzők"
 
-#: vm/models/instance.py:121
+#: vm/models/instance.py:112
 msgid "operating system"
 msgstr "operációs rendszer"
 
-#: vm/models/instance.py:122
+#: vm/models/instance.py:113
 #, python-format
 msgid "Name of operating system in format like \"%s\"."
 msgstr "Az operációs rendszer neve. Például „%s”."
 
-#: vm/models/instance.py:125 vm/models/node.py:83
+#: vm/models/instance.py:116 vm/models/node.py:83
 msgid "tags"
 msgstr "címkék"
 
-#: vm/models/instance.py:126
+#: vm/models/instance.py:117
 msgid "has agent"
 msgstr "van ügynöke"
 
-#: vm/models/instance.py:128
+#: vm/models/instance.py:119
 msgid ""
 "If the machine has agent installed, and the manager should wait for its "
 "start."
@@ -5186,161 +5405,165 @@ msgstr ""
 "A gépre telepítve van-e az ügynökszoftver, vagyis a menedzser várjon-e az "
 "indulására."
 
-#: vm/models/instance.py:148
+#: vm/models/instance.py:139
 msgid "parent template"
 msgstr "szülősablon"
 
-#: vm/models/instance.py:150
+#: vm/models/instance.py:141
 msgid "Template which this one is derived of."
 msgstr "Az a sablon, amelyből az aktuális származik."
 
-#: vm/models/instance.py:153
+#: vm/models/instance.py:144
 msgid "Disks which are to be mounted."
 msgstr "A csatolandó lemezek."
 
-#: vm/models/instance.py:161
+#: vm/models/instance.py:152
 msgid "Can create an instance template."
 msgstr "Létrehozhat példánysablont."
 
-#: vm/models/instance.py:163
+#: vm/models/instance.py:154
 msgid "Can create an instance template (base)."
 msgstr "Létrehozhat példánysablont (alapokból)."
 
-#: vm/models/instance.py:165
+#: vm/models/instance.py:156
 msgid "Can change resources of a template."
 msgstr "Változtathatja egy sablon erőforrásait."
 
-#: vm/models/instance.py:168
+#: vm/models/instance.py:159
 msgid "templates"
 msgstr "sablonok"
 
-#: vm/models/instance.py:226
+#: vm/models/instance.py:217
 msgid "no state"
 msgstr "nincs állapot"
 
-#: vm/models/instance.py:227
+#: vm/models/instance.py:218
 msgid "running"
 msgstr "fut"
 
-#: vm/models/instance.py:228
+#: vm/models/instance.py:219
 msgid "stopped"
 msgstr "leállítva"
 
-#: vm/models/instance.py:229
+#: vm/models/instance.py:220
 msgid "suspended"
 msgstr "felfüggesztve"
 
-#: vm/models/instance.py:230
+#: vm/models/instance.py:221
 msgid "error"
 msgstr "hiba"
 
-#: vm/models/instance.py:231
+#: vm/models/instance.py:222
 msgid "pending"
 msgstr "függő"
 
-#: vm/models/instance.py:232
+#: vm/models/instance.py:223
 msgid "destroyed"
 msgstr "megsemmisítve"
 
-#: vm/models/instance.py:235
+#: vm/models/instance.py:226
 msgid "Human readable name of instance."
 msgstr "A példány olvasható neve."
 
-#: vm/models/instance.py:239
+#: vm/models/instance.py:230
 msgid "Template the instance derives from."
 msgstr "Az a sablon, amelyből a példány származik."
 
-#: vm/models/instance.py:241
+#: vm/models/instance.py:232
 msgid "Original password of the instance."
 msgstr "A példány eredeti jelszava."
 
-#: vm/models/instance.py:242
+#: vm/models/instance.py:233
 msgid "password"
 msgstr "jelszó"
 
-#: vm/models/instance.py:244
+#: vm/models/instance.py:235
 msgid "time of suspend"
 msgstr "felfüggesztés ideje"
 
-#: vm/models/instance.py:245
+#: vm/models/instance.py:236
 msgid "Proposed time of automatic suspension."
 msgstr "A felfüggesztés kijelölt ideje."
 
-#: vm/models/instance.py:248
+#: vm/models/instance.py:239
 msgid "time of delete"
 msgstr "törlés ideje"
 
-#: vm/models/instance.py:249
+#: vm/models/instance.py:240
 msgid "Proposed time of automatic deletion."
 msgstr "Automatikus törlés kijelölt ideje."
 
-#: vm/models/instance.py:253
+#: vm/models/instance.py:244
 msgid "Current hypervisor of this instance."
 msgstr "A példány jelenlegi hypervisorja."
 
-#: vm/models/instance.py:254
+#: vm/models/instance.py:245
 msgid "host node"
 msgstr "csomópont"
 
-#: vm/models/instance.py:256
+#: vm/models/instance.py:247
 msgid "Set of mounted disks."
 msgstr "1Csatolt lemezek halmaza."
 
-#: vm/models/instance.py:259
+#: vm/models/instance.py:250
 msgid "TCP port where VNC console listens."
 msgstr "Az a TCP port, amelyen a VNC konzol hallgat."
 
-#: vm/models/instance.py:260
+#: vm/models/instance.py:251
 msgid "vnc_port"
 msgstr "VNC port"
 
-#: vm/models/instance.py:264
+#: vm/models/instance.py:255
 msgid "The virtual machine's time of destruction."
 msgstr "A virtuális gép megsemmisítésének ideje."
 
-#: vm/models/instance.py:274
+#: vm/models/instance.py:265
 msgid "Can access the graphical console of a VM."
 msgstr "Elérheti a VM grafikus konzolját."
 
-#: vm/models/instance.py:275
+#: vm/models/instance.py:266
 msgid "Can change resources of a running VM."
 msgstr "Megváltoztathatja a VM erőforrásait."
 
-#: vm/models/instance.py:276
+#: vm/models/instance.py:267
 msgid "Can change resources of a new VM."
 msgstr "Megválaszthatja egy új VM erőforrásait."
 
-#: vm/models/instance.py:277
+#: vm/models/instance.py:268
 msgid "Can create a new VM."
 msgstr "Létrehozhat új VM-et."
 
-#: vm/models/instance.py:278
+#: vm/models/instance.py:269
+msgid "Can redeploy a VM."
+msgstr "Újból létrehozhat futó VM-et."
+
+#: vm/models/instance.py:270
 msgid "Can configure port forwards."
 msgstr "Beállíthat porttovábbításokat."
 
-#: vm/models/instance.py:279
+#: vm/models/instance.py:271
 msgid "Can recover a destroyed VM."
 msgstr "Visszaállíthat egy megsemmisített VM-et."
 
-#: vm/models/instance.py:280
+#: vm/models/instance.py:272
 msgid "Can change VM state to NOSTATE."
 msgstr "Átállíthatja a VM állapotát NOSTATE-re."
 
-#: vm/models/instance.py:283
+#: vm/models/instance.py:275
 msgid "instances"
 msgstr "példányok"
 
-#: vm/models/instance.py:295
+#: vm/models/instance.py:287
 #, python-format
 msgid "Instance %(instance)s has already been destroyed."
 msgstr "%(instance)s példány már meg van semmisítve."
 
-#: vm/models/instance.py:299
+#: vm/models/instance.py:291
 #, python-format
 msgid "No agent software is running on instance %(instance)s."
 msgstr "Nem fut ügynökszoftver a következőn: %(instance)s."
 
-#: vm/models/instance.py:303
+#: vm/models/instance.py:295
 #, python-format
 msgid ""
 "Current state (%(state)s) of instance %(instance)s is inappropriate for the "
@@ -5349,16 +5572,16 @@ msgstr ""
 "A(z) %(instance)s példány aktuális állapota (%(state)s) nem megfelelő a "
 "választott művelethez."
 
-#: vm/models/instance.py:377
+#: vm/models/instance.py:369
 msgid "create instance"
 msgstr "példány létrehozása"
 
-#: vm/models/instance.py:458
+#: vm/models/instance.py:450
 #, python-format
 msgid "vm state changed to %(state)s on %(node)s"
-msgstr "VM állapota erre változott: %(state)s, %(node)s"
+msgstr "VM állapota erre változott: %(state)s (ezen: %(node)s)"
 
-#: vm/models/instance.py:654
+#: vm/models/instance.py:646
 #, python-format
 msgid ""
 "Your instance <a href=\"%(url)s\">%(instance)s</a> is going to expire. It "
@@ -5370,7 +5593,7 @@ msgstr ""
 "kerül.  Kérjük, <a href=\"%(token)s\">újítsa meg</a> vagy <a href=\"%(url)s"
 "\">törölje</a> most."
 
-#: vm/models/instance.py:666
+#: vm/models/instance.py:658
 #, python-format
 msgid ""
 "%(failed)s notifications failed and %(success) succeeded. Failed ones are: "
@@ -5379,7 +5602,7 @@ msgstr ""
 "%(failed)s értesítés sikertelen és %(success) sikeres. A sikertelenek: "
 "%(faileds)s."
 
-#: vm/models/instance.py:668
+#: vm/models/instance.py:660
 #, python-format
 msgid ""
 "%(failed)s notifications failed and %(success) succeeded. Failed ones are: "
@@ -5388,49 +5611,49 @@ msgstr ""
 "%(failed)s értesítés sikertelen és %(success) sikeres. A sikertelenek: "
 "%(faileds_ex)s."
 
-#: vm/models/instance.py:676
+#: vm/models/instance.py:668
 #, python-format
 msgid "%(success)s notifications succeeded."
 msgstr "%(success)s sikeres értesítés."
 
-#: vm/models/instance.py:681
+#: vm/models/instance.py:673
 msgid "notify owner about expiration"
 msgstr "tulaj értesítése a lejáratról"
 
-#: vm/models/instance.py:689
+#: vm/models/instance.py:681
 #, python-format
 msgid "%(instance)s expiring soon"
 msgstr "%(instance)s hamarosan lejár"
 
-#: vm/models/network.py:41
+#: vm/models/network.py:40
 msgid "Network the interface belongs to."
 msgstr "Az a hálózat, amelyhez a példány tartozik."
 
-#: vm/models/network.py:43
+#: vm/models/network.py:42
 msgid "If a firewall host (i.e. IP address association) should be generated."
 msgstr "Tűzfal host generálása (IP cím hozzárendelése)."
 
-#: vm/models/network.py:47
+#: vm/models/network.py:46
 msgid "Template the interface template belongs to."
 msgstr "Sablon, amelyhez az interfészsablon tartozik."
 
-#: vm/models/network.py:54
+#: vm/models/network.py:53
 msgid "interface template"
 msgstr "interfészsablon"
 
-#: vm/models/network.py:55
+#: vm/models/network.py:54
 msgid "interface templates"
 msgstr "interfészsablonok"
 
-#: vm/models/network.py:125 vm/models/network.py:130
+#: vm/models/network.py:124 vm/models/network.py:129
 msgid "allocate IP address"
 msgstr "IP cím foglalása"
 
-#: vm/models/network.py:136
+#: vm/models/network.py:135
 msgid "Interface successfully created."
 msgstr "Az interfész létrehozásra került."
 
-#: vm/models/network.py:137
+#: vm/models/network.py:136
 #, python-format
 msgid ""
 "Interface successfully created. New addresses: ipv4: %(ip4)s, ipv6: %(ip6)s, "
@@ -5503,60 +5726,10 @@ msgstr "passzív"
 msgid "active"
 msgstr "aktív"
 
-#: vm/tasks/local_agent_tasks.py:40
-msgid "cleanup"
-msgstr "takarítás"
-
-#: vm/tasks/local_agent_tasks.py:43
-msgid "change password"
-msgstr "jelszóváltoztatás"
-
-#: vm/tasks/local_agent_tasks.py:45
-msgid "set time"
-msgstr "óra beállítása"
-
-#: vm/tasks/local_agent_tasks.py:48
-msgid "set hostname"
-msgstr "gépnév beállítása"
-
-#: vm/tasks/local_agent_tasks.py:56
-msgid "change ip"
-msgstr "IP cím beállítása"
-
-#: vm/tasks/local_agent_tasks.py:60
-msgid "restart networking"
-msgstr "hálózat újratöltése"
-
-#: vm/tasks/local_agent_tasks.py:93
-msgid "agent"
-msgstr "ügynök"
-
-#: vm/tasks/local_agent_tasks.py:97
-msgid "starting"
-msgstr "indítás"
-
-#: vm/tasks/local_agent_tasks.py:113
-msgid "wait agent restarting"
-msgstr "várakozás az ügynök újraindulására"
-
-#: vm/tasks/local_agent_tasks.py:123
-msgid "start access server"
-msgstr "távoli elérés indítása"
-
-#: vm/tasks/local_agent_tasks.py:154
+#: vm/tasks/local_agent_tasks.py:39
 msgid "stopping"
 msgstr "leállítás"
 
-#: vm/tasks/local_agent_tasks.py:170
-#, python-format
-msgid "update to %(version)s"
-msgstr "frissítés erre: %(version)s"
-
-#: vm/tasks/local_agent_tasks.py:177
-#, python-format
-msgid "update agent to %(version)s"
-msgstr "ügynökfrissítés erre: %(version)s"
-
 #: vm/tasks/local_periodic_tasks.py:51
 #, python-format
 msgid "%(instance)s destroyed"
@@ -5585,19 +5758,28 @@ msgstr ""
 "<a href=\"%(url)s\">%(instance)s</a> gépe felfüggesztésre került, mivel "
 "lejárt. Felébresztheti vagy megsemmisítheti."
 
-#: vm/tests/test_models.py:215
+#: vm/tests/test_models.py:218
 msgid "x"
 msgstr "x"
 
+#~ msgid "Visit"
+#~ msgstr "Megtekintés"
+
+#~ msgid "change password"
+#~ msgstr "jelszóváltoztatás"
+
+#~ msgid "start access server"
+#~ msgstr "távoli elérés indítása"
+
+#~ msgid "update to %(version)s"
+#~ msgstr "frissítés erre: %(version)s"
+
 #~ msgid "Change the name of the node."
 #~ msgstr "Válasszon nevet a csomópontnak."
 
 #~ msgid "Flush"
 #~ msgstr "Ürítés"
 
-#~ msgid "Disable node and move all instances to other one."
-#~ msgstr "Csomópont letiltása és az összes példány migrálása a többire."
-
 #~ msgid "Enable"
 #~ msgstr "Engedélyezés"
 
diff --git a/circle/locale/hu/LC_MESSAGES/djangojs.po b/circle/locale/hu/LC_MESSAGES/djangojs.po
index d441eb8..8add889 100644
--- a/circle/locale/hu/LC_MESSAGES/djangojs.po
+++ b/circle/locale/hu/LC_MESSAGES/djangojs.po
@@ -6,8 +6,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: \n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-09-24 12:19+0200\n"
-"PO-Revision-Date: 2014-09-03 12:51+0200\n"
+"POT-Creation-Date: 2014-10-20 12:09+0200\n"
+"PO-Revision-Date: 2014-10-20 12:21+0200\n"
 "Last-Translator: Mate Ory <ory.mate@ik.bme.hu>\n"
 "Language-Team: Hungarian <cloud@ik.bme.hu>\n"
 "Language: en_US\n"
@@ -88,9 +88,9 @@ msgstr "Válasszon a folytatáshoz."
 #: static_collected/dashboard/dashboard.fe0a2f126346.js:258
 #: static_collected/dashboard/dashboard.fe0a2f126346.js:306
 #: static_collected/dashboard/dashboard.fe0a2f126346.js:316
-#: static_collected/dashboard/dashboard.js:258
-#: static_collected/dashboard/dashboard.js:306
-#: static_collected/dashboard/dashboard.js:316
+#: static_collected/dashboard/dashboard.js:259
+#: static_collected/dashboard/dashboard.js:307
+#: static_collected/dashboard/dashboard.js:317
 msgid "No result"
 msgstr "Nincs eredmény"
 
@@ -128,6 +128,14 @@ msgstr "Nincs jogosultsága a profil módosításához."
 msgid "Unknown error."
 msgstr "Ismeretlen hiba."
 
+#: dashboard/static/dashboard/template-list.js:103
+msgid "Only the owners can delete the selected object."
+msgstr "Csak a tulajdonos törölheti a kiválasztott elemet."
+
+#: dashboard/static/dashboard/template-list.js:105
+msgid "An error occurred. ("
+msgstr "Hiba történt. ("
+
 #: dashboard/static/dashboard/vm-create.js:111
 #: dashboard/static/dashboard/vm-create.js:174
 #: static_collected/all.047675ebf594.js:4813
@@ -176,33 +184,17 @@ msgstr "Nincs több hálózat."
 msgid "Not added to any network"
 msgstr "Nincs hálózathoz adva"
 
-#: dashboard/static/dashboard/vm-details.js:115
-#: static_collected/dashboard/vm-details.js:115
+#: dashboard/static/dashboard/vm-details.js:116
+#: static_collected/dashboard/vm-details.js:116
 msgid "Hide password"
 msgstr "Jelszó rejtése"
 
-#: dashboard/static/dashboard/vm-details.js:119
-#: static_collected/dashboard/vm-details.js:119
+#: dashboard/static/dashboard/vm-details.js:120
+#: static_collected/dashboard/vm-details.js:120
 msgid "Show password"
 msgstr "Jelszó megjelenítése"
 
-#: dashboard/static/dashboard/vm-tour.js:20
-#: static_collected/vm-detail.09737c69abc3.js:5853
-#: static_collected/vm-detail.15d710d8ccf0.js:6389
-#: static_collected/vm-detail.234990ca6ec1.js:6962
-#: static_collected/vm-detail.47b1d21da259.js:5853
-#: static_collected/vm-detail.9e1734ade019.js:5854
-#: static_collected/vm-detail.c47949114749.js:6962
-#: static_collected/vm-detail.e3f398067c8a.js:6891
-#: static_collected/vm-detail.e81fe84bf4c0.js:9
-#: static_collected/vm-detail.js:6389
-#: static_collected/dashboard/vm-tour.1562cc89a659.js:20
-#: static_collected/dashboard/vm-tour.7b4cf596f543.js:20
-#: static_collected/dashboard/vm-tour.js:20
-msgid "Prev"
-msgstr "Vissza"
-
-#: dashboard/static/dashboard/vm-tour.js:22
+#: dashboard/static/dashboard/vm-tour.js:6
 #: static_collected/vm-detail.09737c69abc3.js:5855
 #: static_collected/vm-detail.15d710d8ccf0.js:6391
 #: static_collected/vm-detail.234990ca6ec1.js:6964
@@ -218,7 +210,11 @@ msgstr "Vissza"
 msgid "Next"
 msgstr "Tovább"
 
-#: dashboard/static/dashboard/vm-tour.js:26
+#: dashboard/static/dashboard/vm-tour.js:7
+msgid "Previous"
+msgstr "Vissza"
+
+#: dashboard/static/dashboard/vm-tour.js:8
 #: static_collected/vm-detail.09737c69abc3.js:5859
 #: static_collected/vm-detail.15d710d8ccf0.js:6395
 #: static_collected/vm-detail.234990ca6ec1.js:6968
@@ -234,43 +230,19 @@ msgstr "Tovább"
 msgid "End tour"
 msgstr "Befejezés"
 
-#: dashboard/static/dashboard/vm-tour.js:33
-#: static_collected/vm-detail.09737c69abc3.js:5866
-#: static_collected/vm-detail.15d710d8ccf0.js:6402
-#: static_collected/vm-detail.234990ca6ec1.js:6975
-#: static_collected/vm-detail.47b1d21da259.js:5866
-#: static_collected/vm-detail.9e1734ade019.js:5867
-#: static_collected/vm-detail.c47949114749.js:6975
-#: static_collected/vm-detail.e3f398067c8a.js:6904
-#: static_collected/vm-detail.e81fe84bf4c0.js:9
-#: static_collected/vm-detail.js:6402
-#: static_collected/dashboard/vm-tour.1562cc89a659.js:33
-#: static_collected/dashboard/vm-tour.7b4cf596f543.js:33
-#: static_collected/dashboard/vm-tour.js:33
-msgid "Template Tutorial Tour"
-msgstr "Sablon-kalauz"
+#: dashboard/static/dashboard/vm-tour.js:9
+msgid "Done"
+msgstr "Kész"
 
-#: dashboard/static/dashboard/vm-tour.js:34
-#: static_collected/vm-detail.09737c69abc3.js:5867
-#: static_collected/vm-detail.15d710d8ccf0.js:6403
-#: static_collected/vm-detail.234990ca6ec1.js:6976
-#: static_collected/vm-detail.47b1d21da259.js:5867
-#: static_collected/vm-detail.9e1734ade019.js:5868
-#: static_collected/vm-detail.c47949114749.js:6976
-#: static_collected/vm-detail.e3f398067c8a.js:6905
-#: static_collected/vm-detail.e81fe84bf4c0.js:9
-#: static_collected/vm-detail.js:6403
-#: static_collected/dashboard/vm-tour.1562cc89a659.js:34
-#: static_collected/dashboard/vm-tour.7b4cf596f543.js:34
-#: static_collected/dashboard/vm-tour.js:34
+#: dashboard/static/dashboard/vm-tour.js:56
 msgid ""
-"Welcome to the template tutorial. In this quick tour, we gonna show you how "
-"to do the steps described above."
+"Welcome to the template tutorial. In this quick tour, we are going to show "
+"you how to do the steps described above."
 msgstr ""
 "Üdvözöli a sablon-kalauz. A túra során bemutatjuk, hogyan végezze el a fenti "
 "lépéseket."
 
-#: dashboard/static/dashboard/vm-tour.js:35
+#: dashboard/static/dashboard/vm-tour.js:57
 #: static_collected/vm-detail.09737c69abc3.js:5868
 #: static_collected/vm-detail.15d710d8ccf0.js:6404
 #: static_collected/vm-detail.234990ca6ec1.js:6977
@@ -290,111 +262,41 @@ msgstr ""
 "A következő lépéshez kattintson a \"Tovább\" gombra vagy használja a "
 "nyílbillentyűket."
 
-#: dashboard/static/dashboard/vm-tour.js:36
-#: static_collected/vm-detail.09737c69abc3.js:5869
-#: static_collected/vm-detail.15d710d8ccf0.js:6405
-#: static_collected/vm-detail.234990ca6ec1.js:6978
-#: static_collected/vm-detail.47b1d21da259.js:5869
-#: static_collected/vm-detail.9e1734ade019.js:5870
-#: static_collected/vm-detail.c47949114749.js:6978
-#: static_collected/vm-detail.e3f398067c8a.js:6907
-#: static_collected/vm-detail.js:6405
-#: static_collected/dashboard/vm-tour.1562cc89a659.js:36
-#: static_collected/dashboard/vm-tour.7b4cf596f543.js:36
-#: static_collected/dashboard/vm-tour.js:36
+#: dashboard/static/dashboard/vm-tour.js:61
 msgid ""
-"During the tour please don't try the functions because it may lead to "
-"graphical glitches, however "
-msgstr "A túra során még ne próbálja ki a bemutatott funkciókat."
-
-#: dashboard/static/dashboard/vm-tour.js:45
-#: static_collected/vm-detail.09737c69abc3.js:5878
-#: static_collected/vm-detail.15d710d8ccf0.js:6414
-#: static_collected/vm-detail.234990ca6ec1.js:6987
-#: static_collected/vm-detail.47b1d21da259.js:5878
-#: static_collected/vm-detail.9e1734ade019.js:5879
-#: static_collected/vm-detail.c47949114749.js:6987
-#: static_collected/vm-detail.e3f398067c8a.js:6916
-#: static_collected/vm-detail.e81fe84bf4c0.js:9
-#: static_collected/vm-detail.js:6414
-#: static_collected/dashboard/vm-tour.1562cc89a659.js:45
-#: static_collected/dashboard/vm-tour.7b4cf596f543.js:45
-#: static_collected/dashboard/vm-tour.js:45
-msgid "Home tab"
-msgstr "Kezdőoldal"
+"In this tab you can extend the expiration date of your virtual machine, add "
+"tags and modify the name and description."
+msgstr ""
+"Ezen a lapon megújíthatja a virtuális gép lejáratát, címkéket adhat hozzá, "
+"vagy módosíthatja a nevét, leírását."
 
-#: dashboard/static/dashboard/vm-tour.js:46
-#: static_collected/vm-detail.09737c69abc3.js:5879
-#: static_collected/vm-detail.15d710d8ccf0.js:6415
-#: static_collected/vm-detail.234990ca6ec1.js:6988
-#: static_collected/vm-detail.47b1d21da259.js:5879
-#: static_collected/vm-detail.9e1734ade019.js:5880
-#: static_collected/vm-detail.c47949114749.js:6988
-#: static_collected/vm-detail.e3f398067c8a.js:6917
-#: static_collected/vm-detail.e81fe84bf4c0.js:9
-#: static_collected/vm-detail.js:6415
-#: static_collected/dashboard/vm-tour.1562cc89a659.js:46
-#: static_collected/dashboard/vm-tour.7b4cf596f543.js:46
-#: static_collected/dashboard/vm-tour.js:46
+#: dashboard/static/dashboard/vm-tour.js:65
 msgid ""
-"In this tab you can tag your virtual machine and modify the name and "
-"description."
+"Please add a meaningful description to the virtual machine. Changing the "
+"name is also recommended, however you can choose a new name when saving the "
+"template."
 msgstr ""
-"Ezen a lapon címkéket adhat a virtuális géphez, vagy módosíthatja a nevét, "
-"leírását."
+"Kérjük, adjon meg egy informatív leírást. A név megváltoztatása is "
+"ajánlott, azonban a mentéskor is van a sablon nevének "
+"megválasztására."
 
-#: dashboard/static/dashboard/vm-tour.js:55
-#: static_collected/vm-detail.09737c69abc3.js:5888
-#: static_collected/vm-detail.15d710d8ccf0.js:6424
-#: static_collected/vm-detail.234990ca6ec1.js:6997
-#: static_collected/vm-detail.47b1d21da259.js:5888
-#: static_collected/vm-detail.9e1734ade019.js:5889
-#: static_collected/vm-detail.c47949114749.js:6997
-#: static_collected/vm-detail.e3f398067c8a.js:6926
-#: static_collected/vm-detail.e81fe84bf4c0.js:9
-#: static_collected/vm-detail.js:6424
-#: static_collected/dashboard/vm-tour.1562cc89a659.js:55
-#: static_collected/dashboard/vm-tour.7b4cf596f543.js:55
-#: static_collected/dashboard/vm-tour.js:55
-msgid "Resources tab"
-msgstr "Erőforrások lap"
+#: dashboard/static/dashboard/vm-tour.js:69
+msgid ""
+"You can change the lease to extend the expiration date. This will be the "
+"lease of the new template."
+msgstr ""
+"Megváltoztathatja a bérleti módot is a lejárat bővítéséhez. Az gép "
+"bérleti módját örökli majd a sablon is."
 
-#: dashboard/static/dashboard/vm-tour.js:58
-#: static_collected/vm-detail.09737c69abc3.js:5891
-#: static_collected/vm-detail.15d710d8ccf0.js:6427
-#: static_collected/vm-detail.234990ca6ec1.js:7000
-#: static_collected/vm-detail.47b1d21da259.js:5891
-#: static_collected/vm-detail.9e1734ade019.js:5892
-#: static_collected/vm-detail.c47949114749.js:7000
-#: static_collected/vm-detail.e3f398067c8a.js:6929
-#: static_collected/vm-detail.e81fe84bf4c0.js:9
-#: static_collected/vm-detail.js:6427
-#: static_collected/dashboard/vm-tour.1562cc89a659.js:58
-#: static_collected/dashboard/vm-tour.7b4cf596f543.js:58
-#: static_collected/dashboard/vm-tour.js:58
+#: dashboard/static/dashboard/vm-tour.js:73
 msgid ""
-"On the resources tab you can edit the CPU/RAM options and add/remove disks!"
+"On the resources tab you can edit the CPU/RAM options and add/remove disks "
+"if you have required permissions."
 msgstr ""
 "Az erőforrások lapon szerkesztheti a CPU/memória-beállításokat, valamint "
-"hozzáadhat és törölhet lemezeket."
-
-#: dashboard/static/dashboard/vm-tour.js:68
-#: static_collected/vm-detail.09737c69abc3.js:5901
-#: static_collected/vm-detail.15d710d8ccf0.js:6437
-#: static_collected/vm-detail.234990ca6ec1.js:7010
-#: static_collected/vm-detail.47b1d21da259.js:5901
-#: static_collected/vm-detail.9e1734ade019.js:5902
-#: static_collected/vm-detail.c47949114749.js:7010
-#: static_collected/vm-detail.e3f398067c8a.js:6939
-#: static_collected/vm-detail.e81fe84bf4c0.js:9
-#: static_collected/vm-detail.js:6437
-#: static_collected/dashboard/vm-tour.1562cc89a659.js:68
-#: static_collected/dashboard/vm-tour.7b4cf596f543.js:68
-#: static_collected/dashboard/vm-tour.js:68
-msgid "Resources"
-msgstr "Erőforrások"
+"hozzáadhat és törölhet lemezeket, ha van ehhez jogosultsága."
 
-#: dashboard/static/dashboard/vm-tour.js:69
+#: dashboard/static/dashboard/vm-tour.js:81
 #: static_collected/vm-detail.09737c69abc3.js:5902
 #: static_collected/vm-detail.15d710d8ccf0.js:6438
 #: static_collected/vm-detail.234990ca6ec1.js:7011
@@ -410,7 +312,7 @@ msgstr "Erőforrások"
 msgid "CPU priority"
 msgstr "CPU prioritás"
 
-#: dashboard/static/dashboard/vm-tour.js:69
+#: dashboard/static/dashboard/vm-tour.js:82
 #: static_collected/vm-detail.09737c69abc3.js:5902
 #: static_collected/vm-detail.15d710d8ccf0.js:6438
 #: static_collected/vm-detail.234990ca6ec1.js:7011
@@ -426,7 +328,7 @@ msgstr "CPU prioritás"
 msgid "higher is better"
 msgstr "a nagyobb érték a jobb"
 
-#: dashboard/static/dashboard/vm-tour.js:70
+#: dashboard/static/dashboard/vm-tour.js:83
 #: static_collected/vm-detail.09737c69abc3.js:5903
 #: static_collected/vm-detail.15d710d8ccf0.js:6439
 #: static_collected/vm-detail.234990ca6ec1.js:7012
@@ -442,7 +344,7 @@ msgstr "a nagyobb érték a jobb"
 msgid "CPU count"
 msgstr "CPU-k száma"
 
-#: dashboard/static/dashboard/vm-tour.js:70
+#: dashboard/static/dashboard/vm-tour.js:84
 #: static_collected/vm-detail.09737c69abc3.js:5903
 #: static_collected/vm-detail.15d710d8ccf0.js:6439
 #: static_collected/vm-detail.234990ca6ec1.js:7012
@@ -458,7 +360,7 @@ msgstr "CPU-k száma"
 msgid "number of CPU cores."
 msgstr "A CPU-magok száma."
 
-#: dashboard/static/dashboard/vm-tour.js:71
+#: dashboard/static/dashboard/vm-tour.js:85
 #: static_collected/vm-detail.09737c69abc3.js:5904
 #: static_collected/vm-detail.15d710d8ccf0.js:6440
 #: static_collected/vm-detail.234990ca6ec1.js:7013
@@ -474,7 +376,7 @@ msgstr "A CPU-magok száma."
 msgid "RAM amount"
 msgstr "RAM mennyiség"
 
-#: dashboard/static/dashboard/vm-tour.js:71
+#: dashboard/static/dashboard/vm-tour.js:86
 #: static_collected/vm-detail.09737c69abc3.js:5904
 #: static_collected/vm-detail.15d710d8ccf0.js:6440
 #: static_collected/vm-detail.234990ca6ec1.js:7013
@@ -490,23 +392,7 @@ msgstr "RAM mennyiség"
 msgid "amount of RAM."
 msgstr "a memória mennyisége."
 
-#: dashboard/static/dashboard/vm-tour.js:81
-#: static_collected/vm-detail.09737c69abc3.js:5914
-#: static_collected/vm-detail.15d710d8ccf0.js:6450
-#: static_collected/vm-detail.234990ca6ec1.js:7023
-#: static_collected/vm-detail.47b1d21da259.js:5914
-#: static_collected/vm-detail.9e1734ade019.js:5915
-#: static_collected/vm-detail.c47949114749.js:7023
-#: static_collected/vm-detail.e3f398067c8a.js:6952
-#: static_collected/vm-detail.e81fe84bf4c0.js:9
-#: static_collected/vm-detail.js:6450
-#: static_collected/dashboard/vm-tour.1562cc89a659.js:81
-#: static_collected/dashboard/vm-tour.7b4cf596f543.js:81
-#: static_collected/dashboard/vm-tour.js:81
-msgid "Disks"
-msgstr "Lemezek"
-
-#: dashboard/static/dashboard/vm-tour.js:82
+#: dashboard/static/dashboard/vm-tour.js:96
 #: static_collected/vm-detail.09737c69abc3.js:5915
 #: static_collected/vm-detail.15d710d8ccf0.js:6451
 #: static_collected/vm-detail.234990ca6ec1.js:7024
@@ -525,23 +411,7 @@ msgstr ""
 "Hozzáadhat üres lemezeket, letölthet lemezképeket, vagy törölheti a "
 "meglévőket."
 
-#: dashboard/static/dashboard/vm-tour.js:92
-#: static_collected/vm-detail.09737c69abc3.js:5925
-#: static_collected/vm-detail.15d710d8ccf0.js:6461
-#: static_collected/vm-detail.234990ca6ec1.js:7034
-#: static_collected/vm-detail.47b1d21da259.js:5925
-#: static_collected/vm-detail.9e1734ade019.js:5926
-#: static_collected/vm-detail.c47949114749.js:7034
-#: static_collected/vm-detail.e3f398067c8a.js:6963
-#: static_collected/vm-detail.e81fe84bf4c0.js:10
-#: static_collected/vm-detail.js:6461
-#: static_collected/dashboard/vm-tour.1562cc89a659.js:92
-#: static_collected/dashboard/vm-tour.7b4cf596f543.js:92
-#: static_collected/dashboard/vm-tour.js:92
-msgid "Network tab"
-msgstr "Hálózat lap"
-
-#: dashboard/static/dashboard/vm-tour.js:93
+#: dashboard/static/dashboard/vm-tour.js:105
 #: static_collected/vm-detail.09737c69abc3.js:5926
 #: static_collected/vm-detail.15d710d8ccf0.js:6462
 #: static_collected/vm-detail.234990ca6ec1.js:7035
@@ -557,23 +427,7 @@ msgstr "Hálózat lap"
 msgid "You can add new network interfaces or remove existing ones here."
 msgstr "Hozzáadhat új hálózati interfészeket, vagy törölheti a meglévőket."
 
-#: dashboard/static/dashboard/vm-tour.js:102
-#: static_collected/vm-detail.09737c69abc3.js:5935
-#: static_collected/vm-detail.15d710d8ccf0.js:6471
-#: static_collected/vm-detail.234990ca6ec1.js:7044
-#: static_collected/vm-detail.47b1d21da259.js:5935
-#: static_collected/vm-detail.9e1734ade019.js:5936
-#: static_collected/vm-detail.c47949114749.js:7044
-#: static_collected/vm-detail.e3f398067c8a.js:6973
-#: static_collected/vm-detail.e81fe84bf4c0.js:10
-#: static_collected/vm-detail.js:6471
-#: static_collected/dashboard/vm-tour.1562cc89a659.js:102
-#: static_collected/dashboard/vm-tour.7b4cf596f543.js:102
-#: static_collected/dashboard/vm-tour.js:102
-msgid "Deploy"
-msgstr "Indítás"
-
-#: dashboard/static/dashboard/vm-tour.js:105
+#: dashboard/static/dashboard/vm-tour.js:109
 #: static_collected/vm-detail.09737c69abc3.js:5938
 #: static_collected/vm-detail.15d710d8ccf0.js:6474
 #: static_collected/vm-detail.234990ca6ec1.js:7047
@@ -589,55 +443,15 @@ msgstr "Indítás"
 msgid "Deploy the virtual machine."
 msgstr "A virtuális gép elindítása."
 
-#: dashboard/static/dashboard/vm-tour.js:110
-#: static_collected/vm-detail.09737c69abc3.js:5943
-#: static_collected/vm-detail.15d710d8ccf0.js:6479
-#: static_collected/vm-detail.234990ca6ec1.js:7052
-#: static_collected/vm-detail.47b1d21da259.js:5943
-#: static_collected/vm-detail.9e1734ade019.js:5944
-#: static_collected/vm-detail.c47949114749.js:7052
-#: static_collected/vm-detail.e3f398067c8a.js:6981
-#: static_collected/vm-detail.e81fe84bf4c0.js:10
-#: static_collected/vm-detail.js:6479
-#: static_collected/dashboard/vm-tour.1562cc89a659.js:110
-#: static_collected/dashboard/vm-tour.7b4cf596f543.js:110
-#: static_collected/dashboard/vm-tour.js:110
-msgid "Connect"
-msgstr "Csatlakozás"
-
 #: dashboard/static/dashboard/vm-tour.js:113
-#: static_collected/vm-detail.09737c69abc3.js:5946
-#: static_collected/vm-detail.15d710d8ccf0.js:6482
-#: static_collected/vm-detail.234990ca6ec1.js:7055
-#: static_collected/vm-detail.47b1d21da259.js:5946
-#: static_collected/vm-detail.9e1734ade019.js:5947
-#: static_collected/vm-detail.c47949114749.js:7055
-#: static_collected/vm-detail.e3f398067c8a.js:6984
-#: static_collected/vm-detail.e81fe84bf4c0.js:10
-#: static_collected/vm-detail.js:6482
-#: static_collected/dashboard/vm-tour.1562cc89a659.js:113
-#: static_collected/dashboard/vm-tour.7b4cf596f543.js:113
-#: static_collected/dashboard/vm-tour.js:113
-msgid "Use the connection string or connect with your choice of client!"
-msgstr "Használja a megadott parancsot, vagy kedvenc kliensét."
-
-#: dashboard/static/dashboard/vm-tour.js:120
-#: static_collected/vm-detail.09737c69abc3.js:5953
-#: static_collected/vm-detail.15d710d8ccf0.js:6489
-#: static_collected/vm-detail.234990ca6ec1.js:7062
-#: static_collected/vm-detail.47b1d21da259.js:5953
-#: static_collected/vm-detail.9e1734ade019.js:5954
-#: static_collected/vm-detail.c47949114749.js:7062
-#: static_collected/vm-detail.e3f398067c8a.js:6991
-#: static_collected/vm-detail.e81fe84bf4c0.js:10
-#: static_collected/vm-detail.js:6489
-#: static_collected/dashboard/vm-tour.1562cc89a659.js:120
-#: static_collected/dashboard/vm-tour.7b4cf596f543.js:120
-#: static_collected/dashboard/vm-tour.js:120
-msgid "Customize the virtual machine"
-msgstr "Szabja testre a gépet"
+msgid ""
+"Use the CIRCLE client or the connection string to connect to the virtual "
+"machine."
+msgstr ""
+"Használja a CIRCLE klienst vagy a kapcsolódási adatokat a "
+"virtuális géphez való csatlakozáshoz."
 
-#: dashboard/static/dashboard/vm-tour.js:121
+#: dashboard/static/dashboard/vm-tour.js:117
 #: static_collected/vm-detail.09737c69abc3.js:5954
 #: static_collected/vm-detail.15d710d8ccf0.js:6490
 #: static_collected/vm-detail.234990ca6ec1.js:7063
@@ -657,23 +471,7 @@ msgstr ""
 "Miután csatlakozott, végezze el a szükséges módosításokat, majd jelentkezzen "
 "ki."
 
-#: dashboard/static/dashboard/vm-tour.js:126
-#: static_collected/vm-detail.09737c69abc3.js:5959
-#: static_collected/vm-detail.15d710d8ccf0.js:6495
-#: static_collected/vm-detail.234990ca6ec1.js:7068
-#: static_collected/vm-detail.47b1d21da259.js:5959
-#: static_collected/vm-detail.9e1734ade019.js:5960
-#: static_collected/vm-detail.c47949114749.js:7068
-#: static_collected/vm-detail.e3f398067c8a.js:6997
-#: static_collected/vm-detail.e81fe84bf4c0.js:10
-#: static_collected/vm-detail.js:6495
-#: static_collected/dashboard/vm-tour.1562cc89a659.js:126
-#: static_collected/dashboard/vm-tour.7b4cf596f543.js:126
-#: static_collected/dashboard/vm-tour.js:126
-msgid "Save as"
-msgstr "Mentés sablonként"
-
-#: dashboard/static/dashboard/vm-tour.js:129
+#: dashboard/static/dashboard/vm-tour.js:121
 #: static_collected/vm-detail.09737c69abc3.js:5962
 #: static_collected/vm-detail.15d710d8ccf0.js:6498
 #: static_collected/vm-detail.234990ca6ec1.js:7071
@@ -692,39 +490,13 @@ msgstr ""
 "Kattintson a „mentés sablonként” gombra, majd várjon, amíg a lemez mentése "
 "elkészül."
 
-#: dashboard/static/dashboard/vm-tour.js:135
-#: static_collected/vm-detail.09737c69abc3.js:5968
-#: static_collected/vm-detail.15d710d8ccf0.js:6504
-#: static_collected/vm-detail.234990ca6ec1.js:7077
-#: static_collected/vm-detail.47b1d21da259.js:5968
-#: static_collected/vm-detail.9e1734ade019.js:5969
-#: static_collected/vm-detail.c47949114749.js:7077
-#: static_collected/vm-detail.e3f398067c8a.js:7006
-#: static_collected/vm-detail.e81fe84bf4c0.js:10
-#: static_collected/vm-detail.js:6504
-#: static_collected/dashboard/vm-tour.1562cc89a659.js:135
-#: static_collected/dashboard/vm-tour.7b4cf596f543.js:135
-#: static_collected/dashboard/vm-tour.js:135
-msgid "Finish"
-msgstr "Befejezés"
-
-#: dashboard/static/dashboard/vm-tour.js:138
-#: static_collected/vm-detail.09737c69abc3.js:5971
-#: static_collected/vm-detail.15d710d8ccf0.js:6507
-#: static_collected/vm-detail.234990ca6ec1.js:7080
-#: static_collected/vm-detail.47b1d21da259.js:5971
-#: static_collected/vm-detail.9e1734ade019.js:5972
-#: static_collected/vm-detail.c47949114749.js:7080
-#: static_collected/vm-detail.e3f398067c8a.js:7009
-#: static_collected/vm-detail.e81fe84bf4c0.js:10
-#: static_collected/vm-detail.js:6507
-#: static_collected/dashboard/vm-tour.1562cc89a659.js:138
-#: static_collected/dashboard/vm-tour.7b4cf596f543.js:138
-#: static_collected/dashboard/vm-tour.js:138
+#: dashboard/static/dashboard/vm-tour.js:125
 msgid ""
 "This is the last message, if something is not clear you can do the the tour "
-"again!"
-msgstr "A túra véget ért. Ha valami nem érthető, újrakezdheti az útmutatót."
+"again."
+msgstr ""
+"A túra véget ért. Ha valami nem érthető, újrakezdheti az "
+"útmutatót."
 
 #: network/static/js/host.js:10 static_collected/all.047675ebf594.js:5239
 #: static_collected/all.0aecd87e873a.js:5309
@@ -834,6 +606,290 @@ msgstr "Eltávolítás"
 msgid "Are you sure you want to delete this device?"
 msgstr "Biztosan törli ezt az eszközt?"
 
+#: static_collected/vm-detail.09737c69abc3.js:5853
+#: static_collected/vm-detail.15d710d8ccf0.js:6389
+#: static_collected/vm-detail.234990ca6ec1.js:6962
+#: static_collected/vm-detail.47b1d21da259.js:5853
+#: static_collected/vm-detail.9e1734ade019.js:5854
+#: static_collected/vm-detail.c47949114749.js:6962
+#: static_collected/vm-detail.e3f398067c8a.js:6891
+#: static_collected/vm-detail.e81fe84bf4c0.js:9
+#: static_collected/vm-detail.js:6389
+#: static_collected/dashboard/vm-tour.1562cc89a659.js:20
+#: static_collected/dashboard/vm-tour.7b4cf596f543.js:20
+#: static_collected/dashboard/vm-tour.js:20
+msgid "Prev"
+msgstr "Vissza"
+
+#: static_collected/vm-detail.09737c69abc3.js:5866
+#: static_collected/vm-detail.15d710d8ccf0.js:6402
+#: static_collected/vm-detail.234990ca6ec1.js:6975
+#: static_collected/vm-detail.47b1d21da259.js:5866
+#: static_collected/vm-detail.9e1734ade019.js:5867
+#: static_collected/vm-detail.c47949114749.js:6975
+#: static_collected/vm-detail.e3f398067c8a.js:6904
+#: static_collected/vm-detail.e81fe84bf4c0.js:9
+#: static_collected/vm-detail.js:6402
+#: static_collected/dashboard/vm-tour.1562cc89a659.js:33
+#: static_collected/dashboard/vm-tour.7b4cf596f543.js:33
+#: static_collected/dashboard/vm-tour.js:33
+msgid "Template Tutorial Tour"
+msgstr "Sablon-kalauz"
+
+#: static_collected/vm-detail.09737c69abc3.js:5867
+#: static_collected/vm-detail.15d710d8ccf0.js:6403
+#: static_collected/vm-detail.234990ca6ec1.js:6976
+#: static_collected/vm-detail.47b1d21da259.js:5867
+#: static_collected/vm-detail.9e1734ade019.js:5868
+#: static_collected/vm-detail.c47949114749.js:6976
+#: static_collected/vm-detail.e3f398067c8a.js:6905
+#: static_collected/vm-detail.e81fe84bf4c0.js:9
+#: static_collected/vm-detail.js:6403
+#: static_collected/dashboard/vm-tour.1562cc89a659.js:34
+#: static_collected/dashboard/vm-tour.7b4cf596f543.js:34
+#: static_collected/dashboard/vm-tour.js:34
+msgid ""
+"Welcome to the template tutorial. In this quick tour, we gonna show you how "
+"to do the steps described above."
+msgstr ""
+"Üdvözöli a sablon-kalauz. A túra során bemutatjuk, hogyan végezze el a fenti "
+"lépéseket."
+
+#: static_collected/vm-detail.09737c69abc3.js:5869
+#: static_collected/vm-detail.15d710d8ccf0.js:6405
+#: static_collected/vm-detail.234990ca6ec1.js:6978
+#: static_collected/vm-detail.47b1d21da259.js:5869
+#: static_collected/vm-detail.9e1734ade019.js:5870
+#: static_collected/vm-detail.c47949114749.js:6978
+#: static_collected/vm-detail.e3f398067c8a.js:6907
+#: static_collected/vm-detail.js:6405
+#: static_collected/dashboard/vm-tour.1562cc89a659.js:36
+#: static_collected/dashboard/vm-tour.7b4cf596f543.js:36
+#: static_collected/dashboard/vm-tour.js:36
+msgid ""
+"During the tour please don't try the functions because it may lead to "
+"graphical glitches, however "
+msgstr "A túra során még ne próbálja ki a bemutatott funkciókat."
+
+#: static_collected/vm-detail.09737c69abc3.js:5878
+#: static_collected/vm-detail.15d710d8ccf0.js:6414
+#: static_collected/vm-detail.234990ca6ec1.js:6987
+#: static_collected/vm-detail.47b1d21da259.js:5878
+#: static_collected/vm-detail.9e1734ade019.js:5879
+#: static_collected/vm-detail.c47949114749.js:6987
+#: static_collected/vm-detail.e3f398067c8a.js:6916
+#: static_collected/vm-detail.e81fe84bf4c0.js:9
+#: static_collected/vm-detail.js:6414
+#: static_collected/dashboard/vm-tour.1562cc89a659.js:45
+#: static_collected/dashboard/vm-tour.7b4cf596f543.js:45
+#: static_collected/dashboard/vm-tour.js:45
+msgid "Home tab"
+msgstr "Kezdőoldal"
+
+#: static_collected/vm-detail.09737c69abc3.js:5879
+#: static_collected/vm-detail.15d710d8ccf0.js:6415
+#: static_collected/vm-detail.234990ca6ec1.js:6988
+#: static_collected/vm-detail.47b1d21da259.js:5879
+#: static_collected/vm-detail.9e1734ade019.js:5880
+#: static_collected/vm-detail.c47949114749.js:6988
+#: static_collected/vm-detail.e3f398067c8a.js:6917
+#: static_collected/vm-detail.e81fe84bf4c0.js:9
+#: static_collected/vm-detail.js:6415
+#: static_collected/dashboard/vm-tour.1562cc89a659.js:46
+#: static_collected/dashboard/vm-tour.7b4cf596f543.js:46
+#: static_collected/dashboard/vm-tour.js:46
+msgid ""
+"In this tab you can tag your virtual machine and modify the name and "
+"description."
+msgstr ""
+"Ezen a lapon címkéket adhat a virtuális géphez, vagy módosíthatja a nevét, "
+"leírását."
+
+#: static_collected/vm-detail.09737c69abc3.js:5888
+#: static_collected/vm-detail.15d710d8ccf0.js:6424
+#: static_collected/vm-detail.234990ca6ec1.js:6997
+#: static_collected/vm-detail.47b1d21da259.js:5888
+#: static_collected/vm-detail.9e1734ade019.js:5889
+#: static_collected/vm-detail.c47949114749.js:6997
+#: static_collected/vm-detail.e3f398067c8a.js:6926
+#: static_collected/vm-detail.e81fe84bf4c0.js:9
+#: static_collected/vm-detail.js:6424
+#: static_collected/dashboard/vm-tour.1562cc89a659.js:55
+#: static_collected/dashboard/vm-tour.7b4cf596f543.js:55
+#: static_collected/dashboard/vm-tour.js:55
+msgid "Resources tab"
+msgstr "Erőforrások lap"
+
+#: static_collected/vm-detail.09737c69abc3.js:5891
+#: static_collected/vm-detail.15d710d8ccf0.js:6427
+#: static_collected/vm-detail.234990ca6ec1.js:7000
+#: static_collected/vm-detail.47b1d21da259.js:5891
+#: static_collected/vm-detail.9e1734ade019.js:5892
+#: static_collected/vm-detail.c47949114749.js:7000
+#: static_collected/vm-detail.e3f398067c8a.js:6929
+#: static_collected/vm-detail.e81fe84bf4c0.js:9
+#: static_collected/vm-detail.js:6427
+#: static_collected/dashboard/vm-tour.1562cc89a659.js:58
+#: static_collected/dashboard/vm-tour.7b4cf596f543.js:58
+#: static_collected/dashboard/vm-tour.js:58
+msgid ""
+"On the resources tab you can edit the CPU/RAM options and add/remove disks!"
+msgstr ""
+"Az erőforrások lapon szerkesztheti a CPU/memória-beállításokat, valamint "
+"hozzáadhat és törölhet lemezeket."
+
+#: static_collected/vm-detail.09737c69abc3.js:5901
+#: static_collected/vm-detail.15d710d8ccf0.js:6437
+#: static_collected/vm-detail.234990ca6ec1.js:7010
+#: static_collected/vm-detail.47b1d21da259.js:5901
+#: static_collected/vm-detail.9e1734ade019.js:5902
+#: static_collected/vm-detail.c47949114749.js:7010
+#: static_collected/vm-detail.e3f398067c8a.js:6939
+#: static_collected/vm-detail.e81fe84bf4c0.js:9
+#: static_collected/vm-detail.js:6437
+#: static_collected/dashboard/vm-tour.1562cc89a659.js:68
+#: static_collected/dashboard/vm-tour.7b4cf596f543.js:68
+#: static_collected/dashboard/vm-tour.js:68
+msgid "Resources"
+msgstr "Erőforrások"
+
+#: static_collected/vm-detail.09737c69abc3.js:5914
+#: static_collected/vm-detail.15d710d8ccf0.js:6450
+#: static_collected/vm-detail.234990ca6ec1.js:7023
+#: static_collected/vm-detail.47b1d21da259.js:5914
+#: static_collected/vm-detail.9e1734ade019.js:5915
+#: static_collected/vm-detail.c47949114749.js:7023
+#: static_collected/vm-detail.e3f398067c8a.js:6952
+#: static_collected/vm-detail.e81fe84bf4c0.js:9
+#: static_collected/vm-detail.js:6450
+#: static_collected/dashboard/vm-tour.1562cc89a659.js:81
+#: static_collected/dashboard/vm-tour.7b4cf596f543.js:81
+#: static_collected/dashboard/vm-tour.js:81
+msgid "Disks"
+msgstr "Lemezek"
+
+#: static_collected/vm-detail.09737c69abc3.js:5925
+#: static_collected/vm-detail.15d710d8ccf0.js:6461
+#: static_collected/vm-detail.234990ca6ec1.js:7034
+#: static_collected/vm-detail.47b1d21da259.js:5925
+#: static_collected/vm-detail.9e1734ade019.js:5926
+#: static_collected/vm-detail.c47949114749.js:7034
+#: static_collected/vm-detail.e3f398067c8a.js:6963
+#: static_collected/vm-detail.e81fe84bf4c0.js:10
+#: static_collected/vm-detail.js:6461
+#: static_collected/dashboard/vm-tour.1562cc89a659.js:92
+#: static_collected/dashboard/vm-tour.7b4cf596f543.js:92
+#: static_collected/dashboard/vm-tour.js:92
+msgid "Network tab"
+msgstr "Hálózat lap"
+
+#: static_collected/vm-detail.09737c69abc3.js:5935
+#: static_collected/vm-detail.15d710d8ccf0.js:6471
+#: static_collected/vm-detail.234990ca6ec1.js:7044
+#: static_collected/vm-detail.47b1d21da259.js:5935
+#: static_collected/vm-detail.9e1734ade019.js:5936
+#: static_collected/vm-detail.c47949114749.js:7044
+#: static_collected/vm-detail.e3f398067c8a.js:6973
+#: static_collected/vm-detail.e81fe84bf4c0.js:10
+#: static_collected/vm-detail.js:6471
+#: static_collected/dashboard/vm-tour.1562cc89a659.js:102
+#: static_collected/dashboard/vm-tour.7b4cf596f543.js:102
+#: static_collected/dashboard/vm-tour.js:102
+msgid "Deploy"
+msgstr "Indítás"
+
+#: static_collected/vm-detail.09737c69abc3.js:5943
+#: static_collected/vm-detail.15d710d8ccf0.js:6479
+#: static_collected/vm-detail.234990ca6ec1.js:7052
+#: static_collected/vm-detail.47b1d21da259.js:5943
+#: static_collected/vm-detail.9e1734ade019.js:5944
+#: static_collected/vm-detail.c47949114749.js:7052
+#: static_collected/vm-detail.e3f398067c8a.js:6981
+#: static_collected/vm-detail.e81fe84bf4c0.js:10
+#: static_collected/vm-detail.js:6479
+#: static_collected/dashboard/vm-tour.1562cc89a659.js:110
+#: static_collected/dashboard/vm-tour.7b4cf596f543.js:110
+#: static_collected/dashboard/vm-tour.js:110
+msgid "Connect"
+msgstr "Csatlakozás"
+
+#: static_collected/vm-detail.09737c69abc3.js:5946
+#: static_collected/vm-detail.15d710d8ccf0.js:6482
+#: static_collected/vm-detail.234990ca6ec1.js:7055
+#: static_collected/vm-detail.47b1d21da259.js:5946
+#: static_collected/vm-detail.9e1734ade019.js:5947
+#: static_collected/vm-detail.c47949114749.js:7055
+#: static_collected/vm-detail.e3f398067c8a.js:6984
+#: static_collected/vm-detail.e81fe84bf4c0.js:10
+#: static_collected/vm-detail.js:6482
+#: static_collected/dashboard/vm-tour.1562cc89a659.js:113
+#: static_collected/dashboard/vm-tour.7b4cf596f543.js:113
+#: static_collected/dashboard/vm-tour.js:113
+msgid "Use the connection string or connect with your choice of client!"
+msgstr "Használja a megadott parancsot, vagy kedvenc kliensét."
+
+#: static_collected/vm-detail.09737c69abc3.js:5953
+#: static_collected/vm-detail.15d710d8ccf0.js:6489
+#: static_collected/vm-detail.234990ca6ec1.js:7062
+#: static_collected/vm-detail.47b1d21da259.js:5953
+#: static_collected/vm-detail.9e1734ade019.js:5954
+#: static_collected/vm-detail.c47949114749.js:7062
+#: static_collected/vm-detail.e3f398067c8a.js:6991
+#: static_collected/vm-detail.e81fe84bf4c0.js:10
+#: static_collected/vm-detail.js:6489
+#: static_collected/dashboard/vm-tour.1562cc89a659.js:120
+#: static_collected/dashboard/vm-tour.7b4cf596f543.js:120
+#: static_collected/dashboard/vm-tour.js:120
+msgid "Customize the virtual machine"
+msgstr "Szabja testre a gépet"
+
+#: static_collected/vm-detail.09737c69abc3.js:5959
+#: static_collected/vm-detail.15d710d8ccf0.js:6495
+#: static_collected/vm-detail.234990ca6ec1.js:7068
+#: static_collected/vm-detail.47b1d21da259.js:5959
+#: static_collected/vm-detail.9e1734ade019.js:5960
+#: static_collected/vm-detail.c47949114749.js:7068
+#: static_collected/vm-detail.e3f398067c8a.js:6997
+#: static_collected/vm-detail.e81fe84bf4c0.js:10
+#: static_collected/vm-detail.js:6495
+#: static_collected/dashboard/vm-tour.1562cc89a659.js:126
+#: static_collected/dashboard/vm-tour.7b4cf596f543.js:126
+#: static_collected/dashboard/vm-tour.js:126
+msgid "Save as"
+msgstr "Mentés sablonként"
+
+#: static_collected/vm-detail.09737c69abc3.js:5968
+#: static_collected/vm-detail.15d710d8ccf0.js:6504
+#: static_collected/vm-detail.234990ca6ec1.js:7077
+#: static_collected/vm-detail.47b1d21da259.js:5968
+#: static_collected/vm-detail.9e1734ade019.js:5969
+#: static_collected/vm-detail.c47949114749.js:7077
+#: static_collected/vm-detail.e3f398067c8a.js:7006
+#: static_collected/vm-detail.e81fe84bf4c0.js:10
+#: static_collected/vm-detail.js:6504
+#: static_collected/dashboard/vm-tour.1562cc89a659.js:135
+#: static_collected/dashboard/vm-tour.7b4cf596f543.js:135
+#: static_collected/dashboard/vm-tour.js:135
+msgid "Finish"
+msgstr "Befejezés"
+
+#: static_collected/vm-detail.09737c69abc3.js:5971
+#: static_collected/vm-detail.15d710d8ccf0.js:6507
+#: static_collected/vm-detail.234990ca6ec1.js:7080
+#: static_collected/vm-detail.47b1d21da259.js:5971
+#: static_collected/vm-detail.9e1734ade019.js:5972
+#: static_collected/vm-detail.c47949114749.js:7080
+#: static_collected/vm-detail.e3f398067c8a.js:7009
+#: static_collected/vm-detail.e81fe84bf4c0.js:10
+#: static_collected/vm-detail.js:6507
+#: static_collected/dashboard/vm-tour.1562cc89a659.js:138
+#: static_collected/dashboard/vm-tour.7b4cf596f543.js:138
+#: static_collected/dashboard/vm-tour.js:138
+msgid ""
+"This is the last message, if something is not clear you can do the the tour "
+"again!"
+msgstr "A túra véget ért. Ha valami nem érthető, újrakezdheti az útmutatót."
+
 #: static_collected/vm-detail.e81fe84bf4c0.js:9
 msgid ""
 "During the tour please don't try the functions because it may lead to "
diff --git a/circle/network/views.py b/circle/network/views.py
index 26f6d76..7193035 100644
--- a/circle/network/views.py
+++ b/circle/network/views.py
@@ -655,12 +655,7 @@ class VlanDetail(LoginRequiredMixin, SuperuserRequiredMixin,
 
     def get_context_data(self, **kwargs):
         context = super(VlanDetail, self).get_context_data(**kwargs)
-
-        q = Host.objects.filter(interface__in=Interface.objects.filter(
-            vlan=self.object
-        ))
-
-        context['host_list'] = SmallHostTable(q)
+        context['host_list'] = SmallHostTable(self.object.host_set.all())
         context['vlan_vid'] = self.kwargs.get('vid')
         context['acl'] = AclUpdateView.get_acl_data(
             self.object, self.request.user, 'network.vlan-acl')
diff --git a/circle/storage/models.py b/circle/storage/models.py
index 9809b6f..d1a4b1d 100644
--- a/circle/storage/models.py
+++ b/circle/storage/models.py
@@ -416,6 +416,7 @@ class Disk(TimeStampedModel):
                         "Operation aborted by user."), e)
         disk.size = result['size']
         disk.type = result['type']
+        disk.checksum = result.get('checksum', None)
         disk.is_ready = True
         disk.save()
         return disk
diff --git a/circle/vm/models/instance.py b/circle/vm/models/instance.py
index cc69d5a..6f2aa96 100644
--- a/circle/vm/models/instance.py
+++ b/circle/vm/models/instance.py
@@ -817,8 +817,9 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
         return acts
 
     def get_merged_activities(self, user=None):
-        whitelist = ("create_disk", "download_disk", "attach_disk",
-                     "detach_disk", )
+        whitelist = ("create_disk", "download_disk",
+                     "add_port", "remove_port",
+                     "attach_disk", "detach_disk", )
         acts = self.get_activities(user)
         merged_acts = []
         latest = None
diff --git a/circle/vm/models/node.py b/circle/vm/models/node.py
index bf82ccc..81710db 100644
--- a/circle/vm/models/node.py
+++ b/circle/vm/models/node.py
@@ -88,7 +88,9 @@ class Node(OperatedMixin, TimeStampedModel):
     class Meta:
         app_label = 'vm'
         db_table = 'vm_node'
-        permissions = ()
+        permissions = (
+            ('view_statistics', _('Can view Node box and statistics.')),
+        )
         ordering = ('-enabled', 'normalized_name')
 
     def __unicode__(self):
@@ -288,6 +290,11 @@ class Node(OperatedMixin, TimeStampedModel):
 
     @property
     @node_available
+    def driver_version(self):
+        return self.info.get('driver_version')
+
+    @property
+    @node_available
     def cpu_usage(self):
         return self.monitor_info.get('cpu.percent') / 100
 
diff --git a/circle/vm/operations.py b/circle/vm/operations.py
index cd8fef6..2b3948f 100644
--- a/circle/vm/operations.py
+++ b/circle/vm/operations.py
@@ -16,15 +16,22 @@
 # with CIRCLE.  If not, see <http://www.gnu.org/licenses/>.
 
 from __future__ import absolute_import, unicode_literals
+from base64 import encodestring
+from hashlib import md5
 from logging import getLogger
+import os
 from re import search
 from string import ascii_lowercase
+from StringIO import StringIO
+from tarfile import TarFile, TarInfo
+import time
 from urlparse import urlsplit
 
-from django.core.exceptions import PermissionDenied
+from django.core.exceptions import PermissionDenied, SuspiciousOperation
 from django.utils import timezone
 from django.utils.translation import ugettext_lazy as _, ugettext_noop
 from django.conf import settings
+from django.db.models import Q
 
 from sizefield.utils import filesizeformat
 
@@ -43,9 +50,11 @@ from .models import (
     Instance, InstanceActivity, InstanceTemplate, Interface, Node,
     NodeActivity, pwgen
 )
-from .tasks import agent_tasks, local_agent_tasks, vm_tasks
+from .tasks import agent_tasks, vm_tasks
 
 from dashboard.store_api import Store, NoStoreException
+from firewall.models import Host
+from monitor.client import Client
 from storage.tasks import storage_tasks
 
 logger = getLogger(__name__)
@@ -162,6 +171,30 @@ class RemoteInstanceOperation(RemoteOperationMixin, InstanceOperation):
         return [self.instance.vm_name]
 
 
+class EnsureAgentMixin(object):
+    accept_states = ('RUNNING', )
+
+    def check_precond(self):
+        super(EnsureAgentMixin, self).check_precond()
+
+        last_boot_time = self.instance.activity_log.filter(
+            succeeded=True, activity_code__in=(
+                "vm.Instance.deploy", "vm.Instance.reset",
+                "vm.Instance.reboot")).latest("finished").finished
+
+        try:
+            InstanceActivity.objects.filter(
+                activity_code="vm.Instance.agent.starting",
+                started__gt=last_boot_time).latest("started")
+        except InstanceActivity.DoesNotExist:  # no agent since last boot
+            raise self.instance.NoAgentError(self.instance)
+
+
+class RemoteAgentOperation(EnsureAgentMixin, RemoteInstanceOperation):
+    remote_queue = ('agent', )
+    concurrency_check = False
+
+
 @register_operation
 class AddInterfaceOperation(InstanceOperation):
     id = 'add_interface'
@@ -198,7 +231,8 @@ class AddInterfaceOperation(InstanceOperation):
                     self.rollback(net, activity)
                 raise
             net.deploy()
-            local_agent_tasks.send_networking_commands(self.instance, activity)
+            self.instance._change_ip(parent_activity=activity)
+            self.instance._restart_networking(parent_activity=activity)
 
     def get_activity_name(self, kwargs):
         return create_readable(ugettext_noop("add %(vlan)s interface"),
@@ -264,6 +298,11 @@ class ResizeDiskOperation(RemoteInstanceOperation):
             ugettext_noop("resize disk %(name)s to %(size)s"),
             size=filesizeformat(kwargs['size']), name=kwargs['disk'].name)
 
+    def _operation(self, disk, size):
+        super(ResizeDiskOperation, self)._operation(disk=disk, size=size)
+        disk.size = size
+        disk.save()
+
 
 @register_operation
 class DownloadDiskOperation(InstanceOperation):
@@ -280,7 +319,6 @@ class DownloadDiskOperation(InstanceOperation):
     async_queue = "localhost.man.slow"
 
     def _operation(self, user, url, task, activity, name=None):
-        activity.result = url
         from storage.models import Disk
 
         disk = Disk.download(url=url, name=name, task=task)
@@ -294,6 +332,10 @@ class DownloadDiskOperation(InstanceOperation):
         activity.readable_name = create_readable(
             ugettext_noop("download %(name)s"), name=disk.name)
 
+        activity.result = create_readable(ugettext_noop(
+            "Downloading %(url)s is finished. The file md5sum "
+            "is: '%(checksum)s'."),
+            url=url, checksum=disk.checksum)
         # TODO iso (cd) hot-plug is not supported by kvm/guests
         if self.instance.is_running and disk.type not in ["iso"]:
             self.instance._attach_disk(parent_activity=activity, disk=disk)
@@ -324,10 +366,14 @@ class DeployOperation(InstanceOperation):
                           "deployed to node: %(node)s"),
             node=self.instance.node)
 
-    def _operation(self, activity):
+    def _operation(self, activity, node=None):
         # Allocate VNC port and host node
         self.instance.allocate_vnc_port()
-        self.instance.allocate_node()
+        if node is not None:
+            self.instance.node = node
+            self.instance.save()
+        else:
+            self.instance.allocate_node()
 
         # Deploy virtual images
         self.instance._deploy_disks(parent_activity=activity)
@@ -462,8 +508,8 @@ class DestroyOperation(InstanceOperation):
 class MigrateOperation(RemoteInstanceOperation):
     id = 'migrate'
     name = _("migrate")
-    description = _("Move virtual machine to an other worker node with a few "
-                    "seconds of interruption (live migration).")
+    description = _("Move a running virtual machine to an other worker node "
+                    "keeping its full state.")
     required_perms = ()
     superuser_required = True
     accept_states = ('RUNNING', )
@@ -560,6 +606,41 @@ class RemoveInterfaceOperation(InstanceOperation):
 
 
 @register_operation
+class RemovePortOperation(InstanceOperation):
+    id = 'remove_port'
+    name = _("close port")
+    description = _("Close the specified port.")
+    concurrency_check = False
+    required_perms = ('vm.config_ports', )
+
+    def _operation(self, activity, rule):
+        interface = rule.host.interface_set.get()
+        if interface.instance != self.instance:
+            raise SuspiciousOperation()
+        activity.readable_name = create_readable(
+            ugettext_noop("close %(proto)s/%(port)d on %(host)s"),
+            proto=rule.proto, port=rule.dport, host=rule.host)
+        rule.delete()
+
+
+@register_operation
+class AddPortOperation(InstanceOperation):
+    id = 'add_port'
+    name = _("open port")
+    description = _("Open the specified port.")
+    concurrency_check = False
+    required_perms = ('vm.config_ports', )
+
+    def _operation(self, activity, host, proto, port):
+        if host.interface_set.get().instance != self.instance:
+            raise SuspiciousOperation()
+        host.add_port(proto, private=port)
+        activity.readable_name = create_readable(
+            ugettext_noop("open %(proto)s/%(port)d on %(host)s"),
+            proto=proto, port=port, host=host)
+
+
+@register_operation
 class RemoveDiskOperation(InstanceOperation):
     id = 'remove_disk'
     name = _("remove disk")
@@ -633,7 +714,12 @@ class SaveAsTemplateOperation(InstanceOperation):
                 disk.destroy()
 
     def _operation(self, activity, user, system, name=None,
-                   with_shutdown=True, task=None, **kwargs):
+                   with_shutdown=True, clone=False, task=None, **kwargs):
+        try:
+            self.instance._cleanup(parent_activity=activity, user=user)
+        except:
+            pass
+
         if with_shutdown:
             try:
                 ShutdownOperation(self.instance).call(parent_activity=activity,
@@ -683,6 +769,13 @@ class SaveAsTemplateOperation(InstanceOperation):
         tmpl = InstanceTemplate(**params)
         tmpl.full_clean()  # Avoiding database errors.
         tmpl.save()
+        # Copy traits from the VM instance
+        tmpl.req_traits.add(*self.instance.req_traits.all())
+        if clone:
+            tmpl.clone_acl(self.instance.template)
+            # Add permission for the original owner of the template
+            tmpl.set_level(self.instance.template.owner, 'owner')
+            tmpl.set_level(user, 'owner')
         try:
             tmpl.disks.add(*self.disks)
             # create interface templates
@@ -740,7 +833,7 @@ class ShutOffOperation(InstanceOperation):
                     "operation is the same as interrupting the power supply "
                     "of a physical machine.")
     required_perms = ()
-    accept_states = ('RUNNING', )
+    accept_states = ('RUNNING', 'PAUSED')
     resultant_state = 'STOPPED'
 
     def _operation(self, activity):
@@ -811,6 +904,7 @@ class WakeUpOperation(InstanceOperation):
     required_perms = ()
     accept_states = ('SUSPENDED', )
     resultant_state = 'RUNNING'
+    async_queue = "localhost.man.slow"
 
     def is_preferred(self):
         return self.instance.status == self.instance.STATUS.SUSPENDED
@@ -1046,6 +1140,7 @@ class ActivateOperation(NodeOperation):
     def _operation(self):
         self.node.enabled = True
         self.node.schedule_enabled = True
+        self.node.get_info(invalidate_cache=True)
         self.node.save()
 
 
@@ -1067,6 +1162,7 @@ class PassivateOperation(NodeOperation):
     def _operation(self):
         self.node.enabled = True
         self.node.schedule_enabled = False
+        self.node.get_info(invalidate_cache=True)
         self.node.save()
 
 
@@ -1125,13 +1221,27 @@ class RecoverOperation(InstanceOperation):
         except Instance.InstanceDestroyedError:
             pass
 
-    def _operation(self):
-        for disk in self.instance.disks.all():
-            disk.destroyed = None
-            disk.restore()
-            disk.save()
-        self.instance.destroyed_at = None
-        self.instance.save()
+    def _operation(self, user, activity):
+        with activity.sub_activity(
+            'recover_instance',
+                readable_name=ugettext_noop("recover instance")):
+            self.instance.destroyed_at = None
+            for disk in self.instance.disks.all():
+                disk.destroyed = None
+                disk.restore()
+                disk.save()
+            self.instance.status = 'PENDING'
+            self.instance.save()
+
+        try:
+            self.instance.renew(parent_activity=activity)
+        except:
+            pass
+
+        if self.instance.template:
+            for net in self.instance.template.interface_set.all():
+                self.instance.add_interface(
+                    parent_activity=activity, user=user, vlan=net.vlan)
 
 
 @register_operation
@@ -1161,27 +1271,8 @@ class ResourcesOperation(InstanceOperation):
         )
 
 
-class EnsureAgentMixin(object):
-    accept_states = ('RUNNING', )
-
-    def check_precond(self):
-        super(EnsureAgentMixin, self).check_precond()
-
-        last_boot_time = self.instance.activity_log.filter(
-            succeeded=True, activity_code__in=(
-                "vm.Instance.deploy", "vm.Instance.reset",
-                "vm.Instance.reboot")).latest("finished").finished
-
-        try:
-            InstanceActivity.objects.filter(
-                activity_code="vm.Instance.agent.starting",
-                started__gt=last_boot_time).latest("started")
-        except InstanceActivity.DoesNotExist:  # no agent since last boot
-            raise self.instance.NoAgentError(self.instance)
-
-
 @register_operation
-class PasswordResetOperation(EnsureAgentMixin, InstanceOperation):
+class PasswordResetOperation(RemoteAgentOperation):
     id = 'password_reset'
     name = _("password reset")
     description = _("Generate and set a new login password on the virtual "
@@ -1190,17 +1281,236 @@ class PasswordResetOperation(EnsureAgentMixin, InstanceOperation):
                     "logging in as other settings are possible to prevent "
                     "it.")
     acl_level = "owner"
+    task = agent_tasks.change_password
     required_perms = ()
 
-    def _operation(self):
-        self.instance.pw = pwgen()
-        queue = self.instance.get_remote_queue_name("agent")
-        agent_tasks.change_password.apply_async(
-            queue=queue, args=(self.instance.vm_name, self.instance.pw))
+    def _get_remote_args(self, password, **kwargs):
+        return (super(PasswordResetOperation, self)._get_remote_args(**kwargs)
+                + [password])
+
+    def _operation(self, password=None):
+        if not password:
+            password = pwgen()
+        super(PasswordResetOperation, self)._operation(password=password)
+        self.instance.pw = password
         self.instance.save()
 
 
 @register_operation
+class AgentStartedOperation(InstanceOperation):
+    id = 'agent_started'
+    name = _("agent")
+    acl_level = "owner"
+    required_perms = ()
+    concurrency_check = False
+
+    @classmethod
+    def get_activity_code_suffix(cls):
+        return 'agent'
+
+    @property
+    def initialized(self):
+        return self.instance.activity_log.filter(
+            activity_code='vm.Instance.agent._cleanup').exists()
+
+    def measure_boot_time(self):
+        if not self.instance.template:
+            return
+
+        deploy_time = InstanceActivity.objects.filter(
+            instance=self.instance, activity_code="vm.Instance.deploy"
+        ).latest("finished").finished
+
+        total_boot_time = (timezone.now() - deploy_time).total_seconds()
+
+        Client().send([
+            "template.%(pk)d.boot_time %(val)f %(time)s" % {
+                'pk': self.instance.template.pk,
+                'val': total_boot_time,
+                'time': time.time(),
+            }
+        ])
+
+    def finish_agent_wait(self):
+        for i in InstanceActivity.objects.filter(
+                (Q(activity_code__endswith='.os_boot') |
+                 Q(activity_code__endswith='.agent_wait')),
+                instance=self.instance, finished__isnull=True):
+            i.finish(True)
+
+    def _operation(self, user, activity, old_version=None, agent_system=None):
+        with activity.sub_activity('starting', concurrency_check=False,
+                                   readable_name=ugettext_noop('starting')):
+            pass
+
+        self.finish_agent_wait()
+
+        self.instance._change_ip(parent_activity=activity)
+        self.instance._restart_networking(parent_activity=activity)
+
+        new_version = settings.AGENT_VERSION
+        if new_version and old_version and new_version != old_version:
+            try:
+                self.instance.update_agent(
+                    parent_activity=activity, agent_system=agent_system)
+            except TimeoutError:
+                pass
+            else:
+                activity.sub_activity(
+                    'agent_wait', readable_name=ugettext_noop(
+                        "wait agent restarting"), interruptible=True)
+                return  # agent is going to restart
+
+        if not self.initialized:
+            try:
+                self.measure_boot_time()
+            except:
+                logger.exception('Unhandled error in measure_boot_time()')
+            self.instance._cleanup(parent_activity=activity)
+            self.instance.password_reset(
+                parent_activity=activity, password=self.instance.pw)
+            self.instance._set_time(parent_activity=activity)
+            self.instance._set_hostname(parent_activity=activity)
+
+    @register_operation
+    class CleanupOperation(SubOperationMixin, RemoteAgentOperation):
+        id = '_cleanup'
+        name = _("cleanup")
+        task = agent_tasks.cleanup
+
+    @register_operation
+    class SetTimeOperation(SubOperationMixin, RemoteAgentOperation):
+        id = '_set_time'
+        name = _("set time")
+        task = agent_tasks.set_time
+
+        def _get_remote_args(self, **kwargs):
+            cls = AgentStartedOperation.SetTimeOperation
+            return (super(cls, self)._get_remote_args(**kwargs)
+                    + [time.time()])
+
+    @register_operation
+    class SetHostnameOperation(SubOperationMixin, RemoteAgentOperation):
+        id = '_set_hostname'
+        name = _("set hostname")
+        task = agent_tasks.set_hostname
+
+        def _get_remote_args(self, **kwargs):
+            cls = AgentStartedOperation.SetHostnameOperation
+            return (super(cls, self)._get_remote_args(**kwargs)
+                    + [self.instance.short_hostname])
+
+    @register_operation
+    class RestartNetworkingOperation(SubOperationMixin, RemoteAgentOperation):
+        id = '_restart_networking'
+        name = _("restart networking")
+        task = agent_tasks.restart_networking
+
+    @register_operation
+    class ChangeIpOperation(SubOperationMixin, RemoteAgentOperation):
+        id = '_change_ip'
+        name = _("change ip")
+        task = agent_tasks.change_ip
+
+        def _get_remote_args(self, **kwargs):
+            hosts = Host.objects.filter(interface__instance=self.instance)
+            interfaces = {str(host.mac): host.get_network_config()
+                          for host in hosts}
+            cls = AgentStartedOperation.ChangeIpOperation
+            return (super(cls, self)._get_remote_args(**kwargs)
+                    + [interfaces, settings.FIREWALL_SETTINGS['rdns_ip']])
+
+
+@register_operation
+class UpdateAgentOperation(RemoteAgentOperation):
+    id = 'update_agent'
+    name = _("update agent")
+    acl_level = "owner"
+    required_perms = ()
+
+    def get_activity_name(self, kwargs):
+        return create_readable(
+            ugettext_noop('update agent to %(version)s'),
+            version=settings.AGENT_VERSION)
+
+    @staticmethod
+    def create_linux_tar():
+        def exclude(tarinfo):
+            ignored = ('./.', './misc', './windows')
+            if any(tarinfo.name.startswith(x) for x in ignored):
+                return None
+            else:
+                return tarinfo
+
+        f = StringIO()
+
+        with TarFile.open(fileobj=f, mode='w:gz') as tar:
+            agent_path = os.path.join(settings.AGENT_DIR, "agent-linux")
+            tar.add(agent_path, arcname='.', filter=exclude)
+
+            version_fileobj = StringIO(settings.AGENT_VERSION)
+            version_info = TarInfo(name='version.txt')
+            version_info.size = len(version_fileobj.buf)
+            tar.addfile(version_info, version_fileobj)
+
+        return encodestring(f.getvalue()).replace('\n', '')
+
+    @staticmethod
+    def create_windows_tar():
+        f = StringIO()
+
+        agent_path = os.path.join(settings.AGENT_DIR, "agent-win")
+        with TarFile.open(fileobj=f, mode='w|gz') as tar:
+            tar.add(agent_path, arcname='.')
+
+            version_fileobj = StringIO(settings.AGENT_VERSION)
+            version_info = TarInfo(name='version.txt')
+            version_info.size = len(version_fileobj.buf)
+            tar.addfile(version_info, version_fileobj)
+
+        return encodestring(f.getvalue()).replace('\n', '')
+
+    def _operation(self, user, activity, agent_system):
+        queue = self._get_remote_queue()
+        instance = self.instance
+        if agent_system == "Windows":
+            executable = os.listdir(
+                os.path.join(settings.AGENT_DIR, "agent-win"))[0]
+            data = self.create_windows_tar()
+        elif agent_system == "Linux":
+            executable = ""
+            data = self.create_linux_tar()
+        else:
+            # Legacy update method
+            executable = ""
+            return agent_tasks.update_legacy.apply_async(
+                queue=queue,
+                args=(instance.vm_name, self.create_linux_tar())
+            ).get(timeout=60)
+
+        checksum = md5(data).hexdigest()
+        chunk_size = 1024 * 1024
+        chunk_number = 0
+        index = 0
+        filename = settings.AGENT_VERSION + ".tar"
+        while True:
+            chunk = data[index:index+chunk_size]
+            if chunk:
+                agent_tasks.append.apply_async(
+                    queue=queue,
+                    args=(instance.vm_name, chunk,
+                          filename, chunk_number)).get(timeout=60)
+                index = index + chunk_size
+                chunk_number = chunk_number + 1
+            else:
+                agent_tasks.update.apply_async(
+                    queue=queue,
+                    args=(instance.vm_name, filename, executable, checksum)
+                ).get(timeout=60)
+                break
+
+
+@register_operation
 class MountStoreOperation(EnsureAgentMixin, InstanceOperation):
     id = 'mount_store'
     name = _("mount store")
diff --git a/circle/vm/tasks/local_agent_tasks.py b/circle/vm/tasks/local_agent_tasks.py
index f5a678b..90e1134 100644
--- a/circle/vm/tasks/local_agent_tasks.py
+++ b/circle/vm/tasks/local_agent_tasks.py
@@ -15,226 +15,26 @@
 # You should have received a copy of the GNU General Public License along
 # with CIRCLE.  If not, see <http://www.gnu.org/licenses/>.
 
-from common.models import create_readable
-from manager.mancelery import celery
-from vm.tasks.agent_tasks import (restart_networking, change_password,
-                                  set_time, set_hostname, start_access_server,
-                                  cleanup, update, append,
-                                  change_ip, update_legacy)
-from firewall.models import Host
-
-import time
-import os
-from base64 import encodestring
-from hashlib import md5
-from StringIO import StringIO
-from tarfile import TarFile, TarInfo
-from django.conf import settings
-from django.db.models import Q
-from django.utils import timezone
 from django.utils.translation import ugettext_noop
-from celery.result import TimeoutError
-from monitor.client import Client
-
-
-def send_init_commands(instance, act):
-    vm = instance.vm_name
-    queue = instance.get_remote_queue_name("agent")
-    with act.sub_activity('cleanup', readable_name=ugettext_noop('cleanup')):
-        cleanup.apply_async(queue=queue, args=(vm, ))
-    with act.sub_activity('change_password',
-                          readable_name=ugettext_noop('change password')):
-        change_password.apply_async(queue=queue, args=(vm, instance.pw))
-    with act.sub_activity('set_time', readable_name=ugettext_noop('set time')):
-        set_time.apply_async(queue=queue, args=(vm, time.time()))
-    with act.sub_activity('set_hostname',
-                          readable_name=ugettext_noop('set hostname')):
-        set_hostname.apply_async(
-            queue=queue, args=(vm, instance.short_hostname))
-
-
-def send_networking_commands(instance, act):
-    queue = instance.get_remote_queue_name("agent")
-    with act.sub_activity('change_ip',
-                          readable_name=ugettext_noop('change ip')):
-        change_ip.apply_async(queue=queue, args=(
-            instance.vm_name, ) + get_network_configs(instance))
-    with act.sub_activity('restart_networking',
-                          readable_name=ugettext_noop('restart networking')):
-        restart_networking.apply_async(queue=queue, args=(instance.vm_name, ))
-
-
-def create_linux_tar():
-    def exclude(tarinfo):
-        ignored = ('./.', './misc', './windows')
-        if any(tarinfo.name.startswith(x) for x in ignored):
-            return None
-        else:
-            return tarinfo
-
-    f = StringIO()
 
-    with TarFile.open(fileobj=f, mode='w:gz') as tar:
-        agent_path = os.path.join(settings.AGENT_DIR, "agent-linux")
-        tar.add(agent_path, arcname='.', filter=exclude)
-
-        version_fileobj = StringIO(settings.AGENT_VERSION)
-        version_info = TarInfo(name='version.txt')
-        version_info.size = len(version_fileobj.buf)
-        tar.addfile(version_info, version_fileobj)
-
-    return encodestring(f.getvalue()).replace('\n', '')
-
-
-def create_windows_tar():
-    f = StringIO()
-
-    agent_path = os.path.join(settings.AGENT_DIR, "agent-win")
-    with TarFile.open(fileobj=f, mode='w|gz') as tar:
-        tar.add(agent_path, arcname='.')
-
-        version_fileobj = StringIO(settings.AGENT_VERSION)
-        version_info = TarInfo(name='version.txt')
-        version_info.size = len(version_fileobj.buf)
-        tar.addfile(version_info, version_fileobj)
-
-    return encodestring(f.getvalue()).replace('\n', '')
+from manager.mancelery import celery
 
 
 @celery.task
 def agent_started(vm, version=None, system=None):
-    from vm.models import Instance, InstanceActivity
+    from vm.models import Instance
     instance = Instance.objects.get(id=int(vm.split('-')[-1]))
-    queue = instance.get_remote_queue_name("agent")
-    initialized = instance.activity_log.filter(
-        activity_code='vm.Instance.agent.cleanup').exists()
-
-    with instance.activity(code_suffix='agent',
-                           readable_name=ugettext_noop('agent'),
-                           concurrency_check=False) as act:
-        with act.sub_activity('starting',
-                              readable_name=ugettext_noop('starting')):
-            pass
-
-        for i in InstanceActivity.objects.filter(
-                (Q(activity_code__endswith='.os_boot') |
-                 Q(activity_code__endswith='.agent_wait')),
-                instance=instance, finished__isnull=True):
-            i.finish(True)
-
-        if version and version != settings.AGENT_VERSION:
-            try:
-                update_agent(instance, act, system, settings.AGENT_VERSION)
-            except TimeoutError:
-                pass
-            else:
-                act.sub_activity('agent_wait', readable_name=ugettext_noop(
-                    "wait agent restarting"), interruptible=True)
-                return  # agent is going to restart
-
-        if not initialized:
-            measure_boot_time(instance)
-            send_init_commands(instance, act)
-
-        send_networking_commands(instance, act)
-        with act.sub_activity('start_access_server',
-                              readable_name=ugettext_noop(
-                                  'start access server')):
-            start_access_server.apply_async(queue=queue, args=(vm, ))
-
-
-def measure_boot_time(instance):
-    if not instance.template:
-        return
-
-    from vm.models import InstanceActivity
-    deploy_time = InstanceActivity.objects.filter(
-        instance=instance, activity_code="vm.Instance.deploy"
-    ).latest("finished").finished
-
-    total_boot_time = (timezone.now() - deploy_time).total_seconds()
-
-    Client().send([
-        "template.%(pk)d.boot_time %(val)f %(time)s" % {
-            'pk': instance.template.pk,
-            'val': total_boot_time,
-            'time': time.time(),
-        }
-    ])
+    instance.agent_started(
+        user=instance.owner, old_version=version, agent_system=system)
 
 
 @celery.task
 def agent_stopped(vm):
     from vm.models import Instance, InstanceActivity
-    from vm.models.activity import ActivityInProgressError
     instance = Instance.objects.get(id=int(vm.split('-')[-1]))
-    qs = InstanceActivity.objects.filter(instance=instance,
-                                         activity_code='vm.Instance.agent')
+    qs = InstanceActivity.objects.filter(
+        instance=instance, activity_code='vm.Instance.agent')
     act = qs.latest('id')
-    try:
-        with act.sub_activity('stopping',
-                              readable_name=ugettext_noop('stopping')):
-            pass
-    except ActivityInProgressError:
+    with act.sub_activity('stopping', concurrency_check=False,
+                          readable_name=ugettext_noop('stopping')):
         pass
-
-
-def get_network_configs(instance):
-    interfaces = {}
-    for host in Host.objects.filter(interface__instance=instance):
-        interfaces[str(host.mac)] = host.get_network_config()
-    return (interfaces, settings.FIREWALL_SETTINGS['rdns_ip'])
-
-
-def update_agent(instance, act=None, system=None, version=None):
-    if act:
-        act = act.sub_activity(
-            'update',
-            readable_name=create_readable(
-                ugettext_noop('update to %(version)s'),
-                version=settings.AGENT_VERSION))
-    else:
-        act = instance.activity(
-            code_suffix='agent.update',
-            readable_name=create_readable(
-                ugettext_noop('update agent to %(version)s'),
-                version=settings.AGENT_VERSION))
-    with act:
-        queue = instance.get_remote_queue_name("agent")
-        if system == "Windows":
-            executable = os.listdir(os.path.join(settings.AGENT_DIR,
-                                                 "agent-win"))[0]
-            # executable = "agent-winservice-%(version)s.exe" % {
-            #   'version': version}
-            data = create_windows_tar()
-        elif system == "Linux":
-            executable = ""
-            data = create_linux_tar()
-        else:
-            executable = ""
-            # Legacy update method
-            return update_legacy.apply_async(
-                queue=queue,
-                args=(instance.vm_name, create_linux_tar())
-            ).get(timeout=60)
-
-        checksum = md5(data).hexdigest()
-        chunk_size = 1024 * 1024
-        chunk_number = 0
-        index = 0
-        filename = version + ".tar"
-        while True:
-            chunk = data[index:index+chunk_size]
-            if chunk:
-                append.apply_async(
-                    queue=queue,
-                    args=(instance.vm_name, chunk,
-                          filename, chunk_number)).get(timeout=60)
-                index = index + chunk_size
-                chunk_number = chunk_number + 1
-            else:
-                update.apply_async(
-                    queue=queue,
-                    args=(instance.vm_name, filename, executable, checksum)
-                ).get(timeout=60)
-                break
diff --git a/requirements/local.txt b/requirements/local.txt
index 0234d08..8a10ecc 100644
--- a/requirements/local.txt
+++ b/requirements/local.txt
@@ -2,4 +2,5 @@
 -r base.txt
 coverage==3.7.1
 django-debug-toolbar==1.1
+django-rosetta==0.7.4
 Sphinx==1.2.2