diff --git a/circle/acl/models.py b/circle/acl/models.py index a85fcc3..590857a 100644 --- a/circle/acl/models.py +++ b/circle/acl/models.py @@ -246,8 +246,8 @@ class AclBase(Model): def save(self, *args, **kwargs): super(AclBase, self).save(*args, **kwargs) - if 'owner' in dict(self.ACL_LEVELS) and (hasattr(self, 'owner') - and self.owner): + if 'owner' in dict(self.ACL_LEVELS) and (hasattr(self, 'owner') and + self.owner): self.set_user_level(self.owner, 'owner') class Meta: diff --git a/circle/circle/settings/base.py b/circle/circle/settings/base.py index 144e3ca..157382e 100644 --- a/circle/circle/settings/base.py +++ b/circle/circle/settings/base.py @@ -505,6 +505,8 @@ if get_env_variable('DJANGO_SAML', 'FALSE') == 'TRUE': if get_env_variable('DJANGO_SAML_ORG_ID_ATTRIBUTE', False) is not False: SAML_ORG_ID_ATTRIBUTE = get_env_variable( 'DJANGO_SAML_ORG_ID_ATTRIBUTE') + SAML_MAIN_ATTRIBUTE_MAX_LENGTH = int(get_env_variable( + "DJANGO_SAML_MAIN_ATTRIBUTE_MAX_LENGTH", 0)) LOGIN_REDIRECT_URL = "/" diff --git a/circle/circle/settings/test.py b/circle/circle/settings/test.py index 931eee5..a4dd668 100644 --- a/circle/circle/settings/test.py +++ b/circle/circle/settings/test.py @@ -71,3 +71,5 @@ STORE_URL = "" # buildbot doesn't love pipeline STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage' + +SAML_MAIN_ATTRIBUTE_MAX_LENGTH=0 # doctest on SAML2 backend runs either way diff --git a/circle/circle/urls.py b/circle/circle/urls.py index 6447a52..6587616 100644 --- a/circle/circle/urls.py +++ b/circle/circle/urls.py @@ -25,7 +25,7 @@ from django.shortcuts import redirect from circle.settings.base import get_env_variable -from dashboard.views import circle_login, HelpView +from dashboard.views import circle_login, HelpView, ResizeHelpView from dashboard.forms import CirclePasswordResetForm, CircleSetPasswordForm from firewall.views import add_blacklist_item @@ -65,6 +65,8 @@ urlpatterns = patterns( url(r'^info/support/$', TemplateView.as_view(template_name="info/support.html"), name="info.support"), + url(r'^info/resize-how-to/$', ResizeHelpView.as_view(), + name="info.resize"), ) diff --git a/circle/circle/wsgi.py b/circle/circle/wsgi.py index 125aae5..b2bc95a 100644 --- a/circle/circle/wsgi.py +++ b/circle/circle/wsgi.py @@ -46,7 +46,7 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "circle.settings.production") # This application object is used by any WSGI server configured to use this # file. This includes Django's development server, if the WSGI_APPLICATION # setting points here. -from django.core.wsgi import get_wsgi_application +from django.core.wsgi import get_wsgi_application # noqa _application = get_wsgi_application() diff --git a/circle/common/backends.py b/circle/common/backends.py index 3d6e53e..6e20572 100644 --- a/circle/common/backends.py +++ b/circle/common/backends.py @@ -17,9 +17,14 @@ # with CIRCLE. If not, see <http://www.gnu.org/licenses/>. import re +import logging +import sha +from django.conf import settings from djangosaml2.backends import Saml2Backend as Saml2BackendBase +logger = logging.getLogger(__name__) + class Saml2Backend(Saml2BackendBase): u""" @@ -41,7 +46,14 @@ class Saml2Backend(Saml2BackendBase): if isinstance(main_attribute, str): main_attribute = main_attribute.decode('UTF-8') assert isinstance(main_attribute, unicode) - return re.sub(r'[^\w.@-]', replace, main_attribute) + attr = re.sub(r'[^\w.@-]', replace, main_attribute) + max_length = settings.SAML_MAIN_ATTRIBUTE_MAX_LENGTH + if max_length > 0 and len(attr) > max_length: + logger.info("Main attribute '%s' is too long." % attr) + hashed = sha.new(attr).hexdigest() + attr = hashed[:max_length] + logger.info("New main attribute: %s" % attr) + return attr def _set_attribute(self, obj, attr, value): if attr == 'username': diff --git a/circle/common/models.py b/circle/common/models.py index 171b75b..d48d76b 100644 --- a/circle/common/models.py +++ b/circle/common/models.py @@ -97,7 +97,7 @@ def has_prefix(activity_code, *prefixes): >>> assert has_prefix('foo.bar.buz', 'foo', 'bar', 'buz') >>> assert not has_prefix('foo.bar.buz', 'foo', 'buz') """ - equal = lambda a, b: a == b + def equal(a, b): return a == b act_code_parts = split_activity_code(activity_code) prefixes = chain(*imap(split_activity_code, prefixes)) return all(imap(equal, act_code_parts, prefixes)) @@ -112,7 +112,7 @@ def has_suffix(activity_code, *suffixes): >>> assert has_suffix('foo.bar.buz', 'foo', 'bar', 'buz') >>> assert not has_suffix('foo.bar.buz', 'foo', 'buz') """ - equal = lambda a, b: a == b + def equal(a, b): return a == b act_code_parts = split_activity_code(activity_code) suffixes = list(chain(*imap(split_activity_code, suffixes))) return all(imap(equal, reversed(act_code_parts), reversed(suffixes))) @@ -441,8 +441,8 @@ class HumanReadableObject(object): @classmethod def create(cls, user_text_template, admin_text_template=None, **params): return cls(user_text_template=user_text_template, - admin_text_template=(admin_text_template - or user_text_template), params=params) + admin_text_template=(admin_text_template or + user_text_template), params=params) def set(self, user_text_template, admin_text_template=None, **params): self._set_values(user_text_template, diff --git a/circle/dashboard/arrow_local.py b/circle/dashboard/arrow_local.py deleted file mode 100644 index 5472109..0000000 --- a/circle/dashboard/arrow_local.py +++ /dev/null @@ -1,80 +0,0 @@ -# -*- coding: utf-8 -*- - -# 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 arrow import locales - - -class HungarianLocale(locales.Locale): - - names = ['hu', 'HU'] - - past = '{0} ezelőtt' - future = '{0} múlva' - - timeframes = { - 'now': 'éppen most', - 'seconds': { - 'past': 'másodpercekkel', - 'future': 'pár másodperc'}, - 'minute': {'past': 'egy perccel', 'future': 'egy perc'}, - 'minutes': {'past': '{0} perccel', 'future': '{0} perc'}, - 'hour': {'past': 'egy órával', 'future': 'egy óra'}, - 'hours': {'past': '{0} órával', 'future': '{0} óra'}, - 'day': { - 'past': 'egy nappal', - 'future': 'egy nap' - }, - 'days': { - 'past': '{0} nappal', - 'future': '{0} nap' - }, - 'month': {'past': 'egy hónappal', 'future': 'egy hónap'}, - 'months': {'past': '{0} hónappal', 'future': '{0} hónap'}, - 'year': {'past': 'egy évvel', 'future': 'egy év'}, - 'years': {'past': '{0} évvel', 'future': '{0} év'}, - } - - month_names = ['', 'Január', 'Február', 'Március', 'Április', 'Május', - 'Június', 'Július', 'Augusztus', 'Szeptember', - 'Október', 'November', 'December'] - month_abbreviations = ['', 'Jan', 'Febr', 'Márc', 'Ápr', 'Máj', 'Jún', - 'Júl', 'Aug', 'Szept', 'Okt', 'Nov', 'Dec'] - - day_names = ['', 'Hétfő', 'Kedd', 'Szerda', 'Csütörtök', 'Péntek', - 'Szombat', 'Vasárnap'] - day_abbreviations = ['', 'Hét', 'Kedd', 'Szer', 'Csüt', 'Pént', - 'Szom', 'Vas'] - - meridians = { - 'am': 'de', - 'pm': 'du', - 'AM': 'DE', - 'PM': 'DU', - } - - def _format_timeframe(self, timeframe, delta): - form = self.timeframes[timeframe] - if isinstance(form, dict): - if delta > 0: - form = form['future'] - else: - form = form['past'] - delta = abs(delta) - - return form.format(delta) diff --git a/circle/dashboard/autocomplete_light_registry.py b/circle/dashboard/autocomplete_light_registry.py index 8f6fad5..47e5d8e 100644 --- a/circle/dashboard/autocomplete_light_registry.py +++ b/circle/dashboard/autocomplete_light_registry.py @@ -38,10 +38,10 @@ def highlight(field, q, none_wo_match=True): match = None if q and match is not None: match_end = match + len(q) - return (escape(field[:match]) - + '<span class="autocomplete-hl">' - + escape(field[match:match_end]) - + '</span>' + escape(field[match_end:])) + return (escape(field[:match]) + + '<span class="autocomplete-hl">' + + escape(field[match:match_end]) + + '</span>' + escape(field[match_end:])) elif none_wo_match: return None else: diff --git a/circle/dashboard/forms.py b/circle/dashboard/forms.py index 7c3a9c1..4fb8df1 100644 --- a/circle/dashboard/forms.py +++ b/circle/dashboard/forms.py @@ -506,8 +506,8 @@ class TemplateForm(forms.ModelForm): self.allowed_fields = ( 'name', 'access_method', 'description', 'system', 'tags', 'arch', 'lease', 'has_agent') - if (self.user.has_perm('vm.change_template_resources') - or not self.instance.pk): + if (self.user.has_perm('vm.change_template_resources') or + not self.instance.pk): self.allowed_fields += tuple(set(self.fields.keys()) - set(['raw_data'])) if self.user.is_superuser: @@ -523,8 +523,8 @@ class TemplateForm(forms.ModelForm): self.initial['max_ram_size'] = 512 lease_queryset = ( - Lease.get_objects_with_level("operator", self.user).distinct() - | Lease.objects.filter(pk=self.instance.lease_id).distinct()) + Lease.get_objects_with_level("operator", self.user).distinct() | + Lease.objects.filter(pk=self.instance.lease_id).distinct()) self.fields["lease"].queryset = lease_queryset diff --git a/circle/dashboard/management/commands/init.py b/circle/dashboard/management/commands/init.py index 44580af..7efd9e5 100644 --- a/circle/dashboard/management/commands/init.py +++ b/circle/dashboard/management/commands/init.py @@ -64,8 +64,8 @@ class Command(BaseCommand): def handle(self, *args, **options): self.changed = False - if (DataStore.objects.exists() and Vlan.objects.exists() - and not options['force']): + if (DataStore.objects.exists() and Vlan.objects.exists() and + not options['force']): return self.print_state() admin = self.create(User, 'username', username=options['admin_user'], diff --git a/circle/dashboard/static/dashboard/dashboard.less b/circle/dashboard/static/dashboard/dashboard.less index e239d37..48a6453 100644 --- a/circle/dashboard/static/dashboard/dashboard.less +++ b/circle/dashboard/static/dashboard/dashboard.less @@ -1488,3 +1488,38 @@ textarea[name="new_members"] { .acl-table td:first-child { text-align: center; } + +#resize-help { + table { + background-color: #f5f5f5; + } + + .panel { + padding: 2px 20px; + background-color: #f5f5f5; + margin: 20px 0px; + } + + ol li { + margin-top: 15px; + } + + img { + display: block; + margin: 15px 0 5px 0; + } + + pre { + margin-top: 5px; + } + + hr { + margin: 50px 0; + } +} + +#vm-details-resize-how-to { + font-size: 1.5em; + text-align: center; + width: 100%; +} diff --git a/circle/dashboard/static/dashboard/img/resize/1.png b/circle/dashboard/static/dashboard/img/resize/1.png new file mode 100644 index 0000000..926d3bc Binary files /dev/null and b/circle/dashboard/static/dashboard/img/resize/1.png differ diff --git a/circle/dashboard/static/dashboard/img/resize/2.png b/circle/dashboard/static/dashboard/img/resize/2.png new file mode 100644 index 0000000..541ed00 Binary files /dev/null and b/circle/dashboard/static/dashboard/img/resize/2.png differ diff --git a/circle/dashboard/static/dashboard/img/resize/3.png b/circle/dashboard/static/dashboard/img/resize/3.png new file mode 100644 index 0000000..b463519 Binary files /dev/null and b/circle/dashboard/static/dashboard/img/resize/3.png differ diff --git a/circle/dashboard/static/dashboard/img/resize/4.png b/circle/dashboard/static/dashboard/img/resize/4.png new file mode 100644 index 0000000..2da01c9 Binary files /dev/null and b/circle/dashboard/static/dashboard/img/resize/4.png differ diff --git a/circle/dashboard/static/dashboard/img/resize/5.png b/circle/dashboard/static/dashboard/img/resize/5.png new file mode 100644 index 0000000..a7c83b2 Binary files /dev/null and b/circle/dashboard/static/dashboard/img/resize/5.png differ diff --git a/circle/dashboard/static/dashboard/img/resize/6.png b/circle/dashboard/static/dashboard/img/resize/6.png new file mode 100644 index 0000000..e4e57ed Binary files /dev/null and b/circle/dashboard/static/dashboard/img/resize/6.png differ diff --git a/circle/dashboard/static/dashboard/img/resize/7_1.png b/circle/dashboard/static/dashboard/img/resize/7_1.png new file mode 100644 index 0000000..f6f9e90 Binary files /dev/null and b/circle/dashboard/static/dashboard/img/resize/7_1.png differ diff --git a/circle/dashboard/static/dashboard/img/resize/7_2.png b/circle/dashboard/static/dashboard/img/resize/7_2.png new file mode 100644 index 0000000..70baec8 Binary files /dev/null and b/circle/dashboard/static/dashboard/img/resize/7_2.png differ diff --git a/circle/dashboard/static/dashboard/img/resize/7_3.png b/circle/dashboard/static/dashboard/img/resize/7_3.png new file mode 100644 index 0000000..dc50b00 Binary files /dev/null and b/circle/dashboard/static/dashboard/img/resize/7_3.png differ diff --git a/circle/dashboard/static/dashboard/img/resize/8_1.png b/circle/dashboard/static/dashboard/img/resize/8_1.png new file mode 100644 index 0000000..f0f6214 Binary files /dev/null and b/circle/dashboard/static/dashboard/img/resize/8_1.png differ diff --git a/circle/dashboard/static/dashboard/img/resize/8_2.png b/circle/dashboard/static/dashboard/img/resize/8_2.png new file mode 100644 index 0000000..9970c61 Binary files /dev/null and b/circle/dashboard/static/dashboard/img/resize/8_2.png differ diff --git a/circle/dashboard/templates/dashboard/_disk-list-element.html b/circle/dashboard/templates/dashboard/_disk-list-element.html index 348b2f3..dd635f9 100644 --- a/circle/dashboard/templates/dashboard/_disk-list-element.html +++ b/circle/dashboard/templates/dashboard/_disk-list-element.html @@ -4,24 +4,33 @@ <i class="fa fa-file"></i> {{ d.name }} (#{{ d.id }}) - {{ d.size|filesize }} -{% if op.remove_disk %} - <span class="operation-wrapper"> + +<span class="operation-wrapper pull-right"> + {% if d.is_resizable %} + {% if op.resize_disk %} + <a href="{{ op.resize_disk.get_url }}?disk={{d.pk}}" + class="btn btn-xs btn-{{ op.resize_disk.effect }} operation disk-resize-btn + {% if op.resize_disk.disabled %}disabled{% endif %}"> + <i class="fa fa-{{ op.resize_disk.icon }} fa-fw-12"></i> {% trans "Resize" %} + </a> + {% else %} + <a href="{% url "request.views.request-resize" vm_pk=instance.pk disk_pk=d.pk %}" class="btn btn-xs btn-primary operation"> + <i class="fa fa-arrows-alt fa-fw-12"></i> {% trans "Request resize" %} + </a> + {% endif %} + {% else %} + <small class="btn-xs"> + {% trans "Not resizable" %} + </small> + {% endif %} + {% if op.remove_disk %} <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 + class="btn btn-xs btn-{{ op.remove_disk.effect}} operation disk-remove-btn {% if op.remove_disk.disabled %}disabled{% endif %}"> <i class="fa fa-{{ op.remove_disk.icon }} fa-fw-12"></i> {% trans "Remove" %} </a> - </span> -{% endif %} -{% if op.resize_disk %} - <span class="operation-wrapper"> - <a href="{{ op.resize_disk.get_url }}?disk={{d.pk}}" - class="btn btn-xs btn-{{ op.resize_disk.effect }} pull-right operation disk-resize-btn - {% if op.resize_disk.disabled %}disabled{% endif %}"> - <i class="fa fa-{{ op.resize_disk.icon }} fa-fw-12"></i> {% trans "Resize" %} - </a> - </span> -{% endif %} + {% endif %} +</span> <div style="clear: both;"></div> {% if request.user.is_superuser %} diff --git a/circle/dashboard/templates/dashboard/template-edit.html b/circle/dashboard/templates/dashboard/template-edit.html index 0e132de..0e8a096 100644 --- a/circle/dashboard/templates/dashboard/template-edit.html +++ b/circle/dashboard/templates/dashboard/template-edit.html @@ -166,6 +166,28 @@ </ul> </div> </div> + + {% if show_graph %} + <div class="panel panel-default"> + <div class="panel-heading"> + <h3 class="no-margin"><i class="fa fa-area-chart"></i> {% trans "Graphs" %}</h3> + </div> + <div class="text-center panel-body"> + <div class="graph-buttons"> + {% include "dashboard/_graph-time-buttons.html" %} + </div> + <div class="text-center graph-images"> + <img src="{% url "dashboard.views.template-graph" object.pk "instances" graph_time %}"/> + </div> + + {% if request.user.is_superuser %} + <a href="{% url "dashboard.views.vm-list" %}?s=template:{{object.pk}}&stype=all"> + {% trans "List all template instances" %} + </a> + {% endif %} + </div> + </div> + {% endif %} </div><!-- .col-md-4 --> </div><!-- .row --> diff --git a/circle/dashboard/templates/dashboard/vm-detail/resources.html b/circle/dashboard/templates/dashboard/vm-detail/resources.html index e2b3bd1..546bd3a 100644 --- a/circle/dashboard/templates/dashboard/vm-detail/resources.html +++ b/circle/dashboard/templates/dashboard/vm-detail/resources.html @@ -66,6 +66,17 @@ {% endfor %} </div> +<hr /> +{% if instance.disks.all %} + <div id="vm-details-resize-how-to"> + <i class="fa fa-question"></i> + {% url "info.resize" as resize_url %} + {% blocktrans with url=resize_url %} + If you need help resizing the disks check out our <a href="{{ url }}">resize how-to.</a> + {% endblocktrans %} + </div> +{% endif %} + {% if user.is_superuser %} <hr/> diff --git a/circle/dashboard/templatetags/arrowfilter.py b/circle/dashboard/templatetags/arrowfilter.py index c5eacba..2a0c79c 100644 --- a/circle/dashboard/templatetags/arrowfilter.py +++ b/circle/dashboard/templatetags/arrowfilter.py @@ -18,9 +18,6 @@ from django.template import Library import arrow -from dashboard.arrow_local import HungarianLocale -for name in HungarianLocale.names: - arrow.locales._locales[name] = HungarianLocale register = Library() diff --git a/circle/dashboard/tests/selenium/util.py b/circle/dashboard/tests/selenium/util.py index ed1b1e4..7561673 100644 --- a/circle/dashboard/tests/selenium/util.py +++ b/circle/dashboard/tests/selenium/util.py @@ -601,8 +601,8 @@ class CircleSeleniumMixin(SeleniumMixin): choices = self.driver.find_elements_by_css_selector( "input[type='radio']") choice_list = [item for item in choices if ( - 'test' not in item.get_attribute('value') - and item.get_attribute('value') != 'base_vm')] + 'test' not in item.get_attribute('value') and + item.get_attribute('value') != 'base_vm')] chosen = random.randint(0, len(choice_list) - 1) choice_list[chosen].click() self.driver.find_element_by_id( diff --git a/circle/dashboard/urls.py b/circle/dashboard/urls.py index 11d2f11..7df4bb5 100644 --- a/circle/dashboard/urls.py +++ b/circle/dashboard/urls.py @@ -47,7 +47,7 @@ from .views import ( LeaseAclUpdateView, toggle_template_tutorial, ClientCheck, TokenLogin, - VmGraphView, NodeGraphView, NodeListGraphView, + VmGraphView, NodeGraphView, NodeListGraphView, TemplateGraphView, TransferInstanceOwnershipView, TransferInstanceOwnershipConfirmView, TransferTemplateOwnershipView, TransferTemplateOwnershipConfirmView, OpenSearchDescriptionView, @@ -152,6 +152,10 @@ urlpatterns = patterns( r'(?P<time>[0-9]{1,2}[hdwy])$'), NodeListGraphView.as_view(), name='dashboard.views.node-list-graph'), + url((r'^template/(?P<pk>\d+)/graph/(?P<metric>[a-z]+)/' + r'(?P<time>[0-9]{1,2}[hdwy])$'), + TemplateGraphView.as_view(), + name='dashboard.views.template-graph'), url(r'^group/(?P<pk>\d+)/$', GroupDetailView.as_view(), name='dashboard.views.group-detail'), url(r'^group/(?P<pk>\d+)/update/$', GroupProfileUpdate.as_view(), diff --git a/circle/dashboard/views/graph.py b/circle/dashboard/views/graph.py index a09dd54..07593ab 100644 --- a/circle/dashboard/views/graph.py +++ b/circle/dashboard/views/graph.py @@ -28,7 +28,7 @@ from django.views.generic import View from braces.views import LoginRequiredMixin -from vm.models import Instance, Node +from vm.models import Instance, Node, InstanceTemplate logger = logging.getLogger(__name__) @@ -152,6 +152,28 @@ class NodeGraphView(GraphViewBase): return self.model.objects.get(id=pk) +class TemplateGraphView(GraphViewBase): + model = InstanceTemplate + base = Metric + + def get_object(self, request, pk): + instance = super(TemplateGraphView, self).get_object(request, pk) + if not instance.has_level(request.user, 'operator'): + raise PermissionDenied() + return instance + + +class TemplateVms(object): + metric_name = "instances.running" + title = _("Instance count") + label = _("instance count") + + def get_minmax(self): + return (0, None) + +register_graph(TemplateVms, 'instances', TemplateGraphView) + + class NodeListGraphView(GraphViewBase): model = Node base = Metric diff --git a/circle/dashboard/views/index.py b/circle/dashboard/views/index.py index b92fad3..24d964d 100644 --- a/circle/dashboard/views/index.py +++ b/circle/dashboard/views/index.py @@ -136,6 +136,10 @@ class HelpView(TemplateView): return ctx +class ResizeHelpView(TemplateView): + template_name = "info/resize.html" + + class OpenSearchDescriptionView(TemplateView): template_name = "dashboard/vm-opensearch.xml" content_type = "application/opensearchdescription+xml" diff --git a/circle/dashboard/views/template.py b/circle/dashboard/views/template.py index 2638030..2580135 100644 --- a/circle/dashboard/views/template.py +++ b/circle/dashboard/views/template.py @@ -47,7 +47,8 @@ from ..tables import TemplateListTable, LeaseListTable from .util import ( AclUpdateView, FilterMixin, TransferOwnershipConfirmView, TransferOwnershipView, - DeleteViewBase + DeleteViewBase, + GraphMixin ) logger = logging.getLogger(__name__) @@ -258,7 +259,8 @@ class TemplateDelete(DeleteViewBase): object.delete() -class TemplateDetail(LoginRequiredMixin, SuccessMessageMixin, UpdateView): +class TemplateDetail(LoginRequiredMixin, GraphMixin, + SuccessMessageMixin, UpdateView): model = InstanceTemplate template_name = "dashboard/template-edit.html" form_class = TemplateForm @@ -300,6 +302,7 @@ class TemplateDetail(LoginRequiredMixin, SuccessMessageMixin, UpdateView): context['is_owner'] = obj.has_level(self.request.user, 'owner') context['aclform'] = AclUserOrGroupAddForm() context['parent'] = obj.parent + context['show_graph'] = obj.has_level(self.request.user, 'operator') return context def get_success_url(self): diff --git a/circle/dashboard/views/user.py b/circle/dashboard/views/user.py index 14cce91..bc8493b 100644 --- a/circle/dashboard/views/user.py +++ b/circle/dashboard/views/user.py @@ -545,8 +545,8 @@ class UserList(LoginRequiredMixin, PermissionRequiredMixin, SingleTableView): q = self.search_form.cleaned_data.get('s') if q: - filters = (Q(username__icontains=q) | Q(email__icontains=q) - | Q(profile__org_id__icontains=q)) + filters = (Q(username__icontains=q) | Q(email__icontains=q) | + Q(profile__org_id__icontains=q)) for w in q.split()[:3]: filters |= ( Q(first_name__icontains=w) | Q(last_name__icontains=w)) diff --git a/circle/dashboard/views/vm.py b/circle/dashboard/views/vm.py index 60d2541..7f43a3b 100644 --- a/circle/dashboard/views/vm.py +++ b/circle/dashboard/views/vm.py @@ -150,8 +150,8 @@ class VmDetailView(GraphMixin, CheckedDetailView): # resources forms can_edit = ( - instance.has_level(user, "owner") - and self.request.user.has_perm("vm.change_resources")) + instance.has_level(user, "owner") and + self.request.user.has_perm("vm.change_resources")) context['resources_form'] = VmResourcesForm( can_edit=can_edit, instance=instance) @@ -174,8 +174,10 @@ class VmDetailView(GraphMixin, CheckedDetailView): context['is_owner'] = is_owner # operation also allows RUNNING (if with_shutdown is present) - context['save_resources_enabled'] = instance.status not in ("RUNNING", - "PENDING") + context['save_resources_enabled'] = instance.status in ( + "STOPPED", + "PENDING", + ) return context @@ -567,8 +569,8 @@ class VmResourcesChangeView(VmOperationView): content_type="application=json" ) else: - return HttpResponseRedirect(instance.get_absolute_url() - + "#resources") + return HttpResponseRedirect(instance.get_absolute_url() + + "#resources") else: extra = form.cleaned_data extra['max_ram_size'] = extra['ram_size'] @@ -1259,8 +1261,9 @@ def vm_activity(request, pk): response['status'] = instance.status response['icon'] = instance.get_status_icon() latest = instance.get_latest_activity_in_progress() - response['is_new_state'] = (latest and latest.resultant_state is not None - and instance.status != latest.resultant_state) + response['is_new_state'] = (latest and + latest.resultant_state is not None and + instance.status != latest.resultant_state) context = { 'instance': instance, diff --git a/circle/firewall/fields.py b/circle/firewall/fields.py index 66eb9be..8f93b33 100644 --- a/circle/firewall/fields.py +++ b/circle/firewall/fields.py @@ -188,11 +188,11 @@ class IPNetworkField(models.Field): if isinstance(value, IPNetwork): if self.version == 4: - return ('.'.join("%03d" % x for x in value.ip.words) - + '/%02d' % value.prefixlen) + return ('.'.join("%03d" % x for x in value.ip.words) + + '/%02d' % value.prefixlen) else: - return (':'.join("%04X" % x for x in value.ip.words) - + '/%03d' % value.prefixlen) + return (':'.join("%04X" % x for x in value.ip.words) + + '/%03d' % value.prefixlen) return value def formfield(self, **kwargs): diff --git a/circle/firewall/management/commands/reload_firewall.py b/circle/firewall/management/commands/reload_firewall.py index 43d3176..8ed5bd8 100644 --- a/circle/firewall/management/commands/reload_firewall.py +++ b/circle/firewall/management/commands/reload_firewall.py @@ -21,6 +21,8 @@ from django.core.management.base import BaseCommand from firewall.tasks.local_tasks import reloadtask +from argparse import ArgumentTypeError + class Command(BaseCommand): @@ -33,6 +35,20 @@ class Command(BaseCommand): default=False, help='synchronous reload') + parser.add_argument('--timeout', + action='store', + dest='timeout', + default=15, + type=self.positive_int, + help='timeout for synchronous reload') + def handle(self, *args, **options): - reloadtask('Vlan', sync=options["sync"]) + reloadtask('Vlan', sync=options["sync"], timeout=options["timeout"]) + + def positive_int(self, val): + + if not val.isdigit(): + raise ArgumentTypeError("'%s' is not a valid positive int" % val) + + return int(val) diff --git a/circle/firewall/models.py b/circle/firewall/models.py index 4dd4b9f..3af2d72 100644 --- a/circle/firewall/models.py +++ b/circle/firewall/models.py @@ -700,8 +700,8 @@ class Host(models.Model): return self.vlan.network_type != 'public' def clean(self): - if (self.external_ipv4 and not self.shared_ip and self.behind_nat - and Host.objects.exclude(id=self.id).filter( + if (self.external_ipv4 and not self.shared_ip and self.behind_nat and + Host.objects.exclude(id=self.id).filter( external_ipv4=self.external_ipv4)): raise ValidationError(_("If shared_ip has been checked, " "external_ipv4 has to be unique.")) diff --git a/circle/firewall/tasks/local_tasks.py b/circle/firewall/tasks/local_tasks.py index 3d40563..7c3c2d1 100644 --- a/circle/firewall/tasks/local_tasks.py +++ b/circle/firewall/tasks/local_tasks.py @@ -109,4 +109,4 @@ def reloadtask(type='Host', timeout=15, sync=False): if all([cache.add("%s_lock" % i, 'true', 30) for i in reload]): res = reloadtask_worker.apply_async(queue='localhost.man', countdown=5) if sync: - res.get(15) + res.get(timeout) diff --git a/circle/locale/hu/LC_MESSAGES/django.po b/circle/locale/hu/LC_MESSAGES/django.po index 8885a0e..e2a459a 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: 2015-06-08 13:38+0200\n" -"PO-Revision-Date: 2015-06-08 13:46+0116\n" +"POT-Creation-Date: 2015-08-28 12:59+0200\n" +"PO-Revision-Date: 2015-09-04 11:15+0116\n" "Last-Translator: <>\n" "Language-Team: Hungarian <cloud@ik.bme.hu>\n" "MIME-Version: 1.0\n" @@ -58,7 +58,7 @@ msgstr "feladat uuid" #: common/models.py:158 #: dashboard/templates/dashboard/instanceactivity_detail.html:37 -#: firewall/models.py:285 request/models.py:226 vm/models/common.py:84 +#: firewall/models.py:289 request/models.py:226 vm/models/common.py:84 #: vm/models/instance.py:129 vm/models/instance.py:210 msgid "user" msgstr "felhasználó" @@ -142,7 +142,7 @@ msgstr "valós idejű" #: dashboard/forms.py:1352 dashboard/tables.py:270 #: dashboard/templates/dashboard/_vm-create-2.html:20 #: dashboard/templates/dashboard/vm-detail/home.html:9 -#: dashboard/templates/dashboard/vm-list.html:62 firewall/models.py:297 +#: dashboard/templates/dashboard/vm-list.html:62 firewall/models.py:301 #: network/templates/network/index.html:24 #: network/templates/network/switch-port-edit.html:45 request/models.py:57 msgid "Name" @@ -437,8 +437,8 @@ msgstr "Választott nyelv" #: dashboard/templates/dashboard/profile.html:61 #: dashboard/templates/dashboard/vm-detail/network.html:41 #: network/templates/network/host-edit.html:32 -#: templates/info/help/overview.html:402 -#: templates/info/help/overview_toc.html:78 +#: templates/info/help/overview.html:404 +#: templates/info/help/overview_toc.html:73 msgid "Groups" msgstr "Csoportok" @@ -511,12 +511,10 @@ msgstr "" "szerint meghatározva." #: dashboard/forms.py:1644 -#| msgid "set time" msgid "Start time" msgstr "Kezdési időpont" #: dashboard/forms.py:1645 -#| msgid "time" msgid "End time" msgstr "Befejezési időpont" @@ -586,8 +584,8 @@ msgstr "elérés módja" msgid "Type of the remote access method." msgstr "Távoli elérési mód típusa." -#: dashboard/models.py:154 firewall/models.py:535 firewall/models.py:567 -#: firewall/models.py:956 firewall/models.py:1001 firewall/models.py:1027 +#: dashboard/models.py:154 firewall/models.py:539 firewall/models.py:571 +#: firewall/models.py:960 firewall/models.py:1005 firewall/models.py:1031 #: storage/models.py:49 storage/models.py:120 vm/models/common.py:65 #: vm/models/common.py:89 vm/models/common.py:165 vm/models/instance.py:133 #: vm/models/instance.py:223 vm/models/node.py:121 @@ -654,14 +652,14 @@ msgstr "Lemezkvóta mebibyte-okban." msgid "Can use autocomplete." msgstr "Használhat automatikus kiegészítést." -#: dashboard/models.py:278 firewall/models.py:286 request/models.py:227 +#: dashboard/models.py:278 firewall/models.py:290 request/models.py:227 #: vm/models/common.py:85 vm/models/instance.py:130 vm/models/instance.py:211 msgid "operator" msgstr "operátor" -#: dashboard/models.py:279 firewall/models.py:105 firewall/models.py:391 -#: firewall/models.py:544 firewall/models.py:572 firewall/models.py:642 -#: firewall/models.py:1002 firewall/models.py:1036 vm/models/common.py:86 +#: dashboard/models.py:279 firewall/models.py:105 firewall/models.py:395 +#: firewall/models.py:548 firewall/models.py:576 firewall/models.py:646 +#: firewall/models.py:1006 firewall/models.py:1040 vm/models/common.py:86 #: vm/models/instance.py:131 vm/models/instance.py:212 msgid "owner" msgstr "tulajdonos" @@ -737,7 +735,7 @@ msgstr "<abbr data-placement=\"left\" title=\"Rendszergazda státusz\">SU</abbr> #: dashboard/templates/dashboard/node-detail.html:72 #: dashboard/templates/dashboard/vm-detail.html:211 #: templates/info/help/overview.html:182 -#: templates/info/help/overview_toc.html:46 +#: templates/info/help/overview_toc.html:43 msgid "Resources" msgstr "Erőforrások" @@ -805,7 +803,6 @@ msgid "No disk found." msgstr "Nem található lemez." #: dashboard/tables.py:373 -#| msgid "messages" msgid "No messages." msgstr "Nincsenek üzenetek." @@ -1042,7 +1039,7 @@ msgstr "Lemezek" #: dashboard/views/graph.py:198 dashboard/views/graph.py:221 #: network/forms.py:147 network/templates/network/base.html:7 #: templates/info/help/overview.html:253 -#: templates/info/help/overview_toc.html:53 +#: templates/info/help/overview_toc.html:50 msgid "Network" msgstr "Hálózat" @@ -1513,7 +1510,7 @@ msgstr "" #: dashboard/templates/dashboard/template-list.html:6 #: dashboard/templates/dashboard/template-list.html:17 request/models.py:162 #: request/tables.py:81 templates/info/help/overview.html:307 -#: templates/info/help/overview_toc.html:68 +#: templates/info/help/overview_toc.html:65 msgid "Templates" msgstr "Sablonok" @@ -1653,7 +1650,7 @@ msgstr "Nincs jogosultsága virtuális gépek indítására vagy kezelésére." #: dashboard/templates/dashboard/vm-detail.html:231 #: dashboard/templates/dashboard/vm-detail/activity.html:3 #: templates/info/help/overview.html:275 -#: templates/info/help/overview_toc.html:58 +#: templates/info/help/overview_toc.html:55 msgid "Activity" msgstr "Tevékenységek" @@ -1667,7 +1664,7 @@ msgid "time" msgstr "idő" #: dashboard/templates/dashboard/instanceactivity_detail.html:40 -#: firewall/models.py:1032 network/tables.py:181 +#: firewall/models.py:1036 network/tables.py:181 msgid "type" msgstr "típus" @@ -1799,13 +1796,13 @@ msgstr "Offline" #: dashboard/templates/dashboard/node-detail.html:66 #: dashboard/templates/dashboard/vm-detail.html:206 #: templates/info/help/overview.html:143 -#: templates/info/help/overview_toc.html:38 +#: templates/info/help/overview_toc.html:36 msgid "Home" msgstr "Kezdőoldal" #: dashboard/templates/dashboard/node-detail.html:79 #: templates/info/help/overview.html:72 -#: templates/info/help/overview_toc.html:21 +#: templates/info/help/overview_toc.html:19 msgid "Virtual Machines" msgstr "Virtuális gépek" @@ -1926,8 +1923,8 @@ msgstr "" #: dashboard/templates/dashboard/profile.html:7 #: dashboard/templates/dashboard/profile_form.html:6 -#: templates/info/help/overview.html:484 -#: templates/info/help/overview_toc.html:92 +#: templates/info/help/overview.html:486 +#: templates/info/help/overview_toc.html:87 msgid "Profile" msgstr "Profil" @@ -2132,8 +2129,8 @@ msgstr "" " " #: dashboard/templates/dashboard/store/index-files.html:17 -#: templates/info/help/overview.html:455 -#: templates/info/help/overview_toc.html:86 +#: templates/info/help/overview.html:457 +#: templates/info/help/overview_toc.html:81 msgid "Files" msgstr "Fájlok" @@ -2508,13 +2505,13 @@ msgstr "A virtuális gép elindult, most már csatlakozhat." #: dashboard/templates/dashboard/vm-detail.html:216 #: templates/info/help/overview.html:198 -#: templates/info/help/overview_toc.html:47 +#: templates/info/help/overview_toc.html:44 msgid "Console" msgstr "Konzol" #: dashboard/templates/dashboard/vm-detail.html:220 #: templates/info/help/overview.html:208 -#: templates/info/help/overview_toc.html:48 +#: templates/info/help/overview_toc.html:45 msgid "Access" msgstr "Hozzáférés" @@ -2579,7 +2576,7 @@ msgid "" " " msgstr "" "\n" -"A Tulajdonos szint minden műveletet engedélyez. A Tulajdonosok adhatnak/visszavonhatnak Felhasználó, Operátor is Tulajdonos szintű hozzáféréseket. A virtuális gépért felelős tulajdonos nem fokozható le. A felelős tulajdonosi cím átruházható másik felhasználó számára, a \"Tulajdon átruházása\" gombbal." +"A Tulajdonos szint minden műveletet engedélyez. A Tulajdonosok adhatnak/visszavonhatnak Felhasználó, Operátor és Tulajdonos szintű hozzáféréseket. A virtuális gépért felelős tulajdonos nem fokozható le. A felelős tulajdonosi cím átruházható másik felhasználó számára, a \"Tulajdon átruházása\" gombbal." #: dashboard/templates/dashboard/vm-detail/console.html:6 msgid "You are not authorized to access the VNC console." @@ -2613,7 +2610,7 @@ msgstr "Frissítés" #: dashboard/templates/dashboard/vm-detail/home.html:58 #: templates/info/help/overview.html:152 -#: templates/info/help/overview_toc.html:39 +#: templates/info/help/overview_toc.html:37 msgid "Expiration" msgstr "Lejárat" @@ -2653,7 +2650,7 @@ msgstr "RAM-használat" msgid "Network usage" msgstr "Hálózathasználat" -#: dashboard/templates/dashboard/vm-detail/network.html:8 vm/operations.py:207 +#: dashboard/templates/dashboard/vm-detail/network.html:8 vm/operations.py:208 msgid "add interface" msgstr "új interfész" @@ -2670,12 +2667,12 @@ msgid "edit" msgstr "szerkesztés" #: dashboard/templates/dashboard/vm-detail/network.html:38 -#: firewall/models.py:614 +#: firewall/models.py:618 msgid "IPv4 address" msgstr "IPv4 cím" #: dashboard/templates/dashboard/vm-detail/network.html:39 -#: firewall/models.py:624 +#: firewall/models.py:628 msgid "IPv6 address" msgstr "IPv6 cím" @@ -3336,9 +3333,9 @@ msgstr "irány" msgid "If the rule matches egress or ingress packets." msgstr "A szabály kimenő vagy bejövő csomagokra illeszkedik." -#: firewall/models.py:73 firewall/models.py:346 firewall/models.py:541 -#: firewall/models.py:569 firewall/models.py:631 firewall/models.py:1008 -#: firewall/models.py:1037 firewall/models.py:1109 vm/models/instance.py:135 +#: firewall/models.py:73 firewall/models.py:350 firewall/models.py:545 +#: firewall/models.py:573 firewall/models.py:635 firewall/models.py:1012 +#: firewall/models.py:1041 firewall/models.py:1113 vm/models/instance.py:135 #: vm/models/instance.py:225 msgid "description" msgstr "leírás" @@ -3426,17 +3423,17 @@ msgstr "Célport számának átírása a megadottra NAT esetén." msgid "external IPv4 address" msgstr "külső IPv4 cím" -#: firewall/models.py:123 firewall/models.py:389 firewall/models.py:546 -#: firewall/models.py:574 firewall/models.py:650 +#: firewall/models.py:123 firewall/models.py:393 firewall/models.py:550 +#: firewall/models.py:578 firewall/models.py:654 msgid "created at" msgstr "létrehozva" -#: firewall/models.py:126 firewall/models.py:393 firewall/models.py:548 -#: firewall/models.py:576 firewall/models.py:652 +#: firewall/models.py:126 firewall/models.py:397 firewall/models.py:552 +#: firewall/models.py:580 firewall/models.py:656 msgid "modified at" msgstr "módosítva" -#: firewall/models.py:129 firewall/models.py:397 firewall/models.py:639 +#: firewall/models.py:129 firewall/models.py:401 firewall/models.py:643 #: network/templates/network/vlan-create.html:8 #: network/templates/network/vlan-edit.html:8 vm/models/network.py:39 #: vm/models/network.py:67 @@ -3447,7 +3444,7 @@ msgstr "vlan" msgid "Vlan the rule applies to (if type is vlan)." msgstr "Erre a vlanra vonatkozik a szabály (ha a típus vlan)." -#: firewall/models.py:134 firewall/models.py:552 +#: firewall/models.py:134 firewall/models.py:556 #: network/templates/network/vlan-group-create.html:8 #: network/templates/network/vlan-group-edit.html:8 msgid "vlan group" @@ -3457,7 +3454,7 @@ msgstr "vlan-csoport" msgid "Group of vlans the rule applies to (if type is vlan)." msgstr "Erre a vlan-csoportra vonatkozik a szabály (ha a típus vlan)." -#: firewall/models.py:138 firewall/models.py:1030 firewall/models.py:1162 +#: firewall/models.py:138 firewall/models.py:1034 firewall/models.py:1166 #: network/templates/network/host-create.html:8 #: network/templates/network/host-edit.html:8 vm/models/network.py:69 #: vm/models/node.py:126 @@ -3468,7 +3465,7 @@ msgstr "gép" msgid "Host the rule applies to (if type is host)." msgstr "Erre a gépre vonatkozik a szabály (ha a típus gép)." -#: firewall/models.py:142 firewall/models.py:580 +#: firewall/models.py:142 firewall/models.py:584 #: network/templates/network/group-create.html:8 #: network/templates/network/group-edit.html:8 msgid "host group" @@ -3478,7 +3475,7 @@ msgstr "gépcsoport" msgid "Group of hosts the rule applies to (if type is host)." msgstr "Erre a gépcsoportra vonatkozik a szabály (ha a típus gép)." -#: firewall/models.py:146 firewall/models.py:960 +#: firewall/models.py:146 firewall/models.py:964 #: network/templates/network/firewall-create.html:6 #: network/templates/network/firewall-edit.html:7 msgid "firewall" @@ -3492,40 +3489,46 @@ msgstr "Erre a tűzfalra vonatkozik a szabály (ha a típus tűzfal)." msgid "Only one field can be selected." msgstr "Csak egy mező választható ki." -#: firewall/models.py:259 network/templates/network/rule-create.html:8 +#: firewall/models.py:162 +msgid "" +"One of the following fields must be selected: vlan, vlan group, host, host " +"group, firewall." +msgstr "" + +#: firewall/models.py:263 network/templates/network/rule-create.html:8 #: network/templates/network/rule-edit.html:8 msgid "rule" msgstr "szabály" -#: firewall/models.py:260 +#: firewall/models.py:264 msgid "rules" msgstr "szabályok" -#: firewall/models.py:288 +#: firewall/models.py:292 msgid "public" msgstr "nyilvános" -#: firewall/models.py:289 +#: firewall/models.py:293 msgid "portforward" msgstr "porttovábbítás" -#: firewall/models.py:291 +#: firewall/models.py:295 msgid "VID" msgstr "VID" -#: firewall/models.py:292 +#: firewall/models.py:296 msgid "The vlan ID of the subnet." msgstr "Az alhálózat vlan-azonosítója." -#: firewall/models.py:298 +#: firewall/models.py:302 msgid "The short name of the subnet." msgstr "Az alhálózat rövid neve." -#: firewall/models.py:302 +#: firewall/models.py:306 msgid "IPv4 address/prefix" msgstr "IPv4 cím/prefixhossz" -#: firewall/models.py:304 +#: firewall/models.py:308 msgid "" "The IPv4 address and the prefix length of the gateway. Recommended value is " "the last valid address of the subnet, for example 10.4.255.254/16 for " @@ -3534,11 +3537,11 @@ msgstr "" "Az útválasztó IPv4 címe és prefixhossza. Az ajánlott érték az alhálózat " "utolsó érvényes címe, például 10.4.255.254/16 a 10.4.0.0/16 hálózat esetén." -#: firewall/models.py:311 +#: firewall/models.py:315 msgid "IPv6 prefixlen/host" msgstr "IPv6 prefixhossz/gép" -#: firewall/models.py:312 +#: firewall/models.py:316 msgid "" "The prefix length of the subnet assigned to a host. For example /112 = 65536" " addresses/host." @@ -3546,19 +3549,19 @@ msgstr "" "A géphez rendelt alhálózat prefixhossza. Például a /112 beállítás 65536 " "címet jelent gépenként." -#: firewall/models.py:320 +#: firewall/models.py:324 msgid "IPv6 address/prefix" msgstr "IPv6 cím/prefixhossz" -#: firewall/models.py:322 +#: firewall/models.py:326 msgid "The IPv6 address and the prefix length of the gateway." msgstr "Az útválasztó IPv6 címe és prefixhossza." -#: firewall/models.py:326 +#: firewall/models.py:330 msgid "NAT IP address" msgstr "NAT IP cím" -#: firewall/models.py:328 +#: firewall/models.py:332 msgid "" "Common IPv4 address used for address translation of connections to the " "networks selected below (typically to the internet)." @@ -3566,11 +3569,11 @@ msgstr "" "Közös címfordításra használt IPv4 cím a kiválasztott hálózatok felé irányuló" " kapcsolatokhoz (tipikusan az internet felé)." -#: firewall/models.py:334 +#: firewall/models.py:338 msgid "NAT to" msgstr "NAT ide" -#: firewall/models.py:336 +#: firewall/models.py:340 msgid "" "Connections to these networks should be network address translated, i.e. " "their source address is rewritten to the value of NAT IP address." @@ -3578,39 +3581,39 @@ msgstr "" "A megadott hálózatok felé induló kapcsolatok címfordításra kerülnek: a " "forráscímük a megadott NAT IP címre lesz átírva." -#: firewall/models.py:342 +#: firewall/models.py:346 msgid "network type" msgstr "hálózat típusa" -#: firewall/models.py:345 vm/models/network.py:41 +#: firewall/models.py:349 vm/models/network.py:41 msgid "managed" msgstr "menedzselt" -#: firewall/models.py:348 +#: firewall/models.py:352 msgid "Description of the goals and elements of the vlan network." msgstr "A vlan hálózat céljainak és elemeinek leírása." -#: firewall/models.py:350 +#: firewall/models.py:354 msgid "comment" msgstr "megjegyzés" -#: firewall/models.py:352 +#: firewall/models.py:356 msgid "Notes, comments about the network" msgstr "Jegyzetek, megjegyzések a hálózatról" -#: firewall/models.py:353 +#: firewall/models.py:357 msgid "domain name" msgstr "tartománynév" -#: firewall/models.py:354 +#: firewall/models.py:358 msgid "Domain name of the members of this network." msgstr "A hálózat tagjainak tartományneve." -#: firewall/models.py:358 +#: firewall/models.py:362 msgid "reverse domain" msgstr "reverz tartomány" -#: firewall/models.py:359 +#: firewall/models.py:363 #, python-format msgid "" "Template of the IPv4 reverse domain name that should be generated for each " @@ -3625,7 +3628,7 @@ msgstr "" "Például a szabványos reverz név sablonja: \"%(d)d.%(c)d.%(b)d.%(a)d.in-" "addr.arpa\"." -#: firewall/models.py:369 +#: firewall/models.py:373 #, python-format msgid "" "Template for translating IPv4 addresses to IPv6. Automatically generated " @@ -3647,15 +3650,15 @@ msgstr "" "szokásos választás lehet a \"2001:db8:1:1:%(d)d::\" és a " "\"2001:db8:1:1:%(d)02x00::\"." -#: firewall/models.py:380 +#: firewall/models.py:384 msgid "ipv6 template" msgstr "ipv6 sablon" -#: firewall/models.py:381 +#: firewall/models.py:385 msgid "DHCP pool" msgstr "DHCP készlet" -#: firewall/models.py:383 +#: firewall/models.py:387 msgid "" "The address range of the DHCP pool: empty for no DHCP service, \"manual\" " "for no DHCP pool, or the first and last address of the range separated by a " @@ -3665,60 +3668,60 @@ msgstr "" "tiltásához adja meg a „manual” értéket, engedélyezéséhez az első és utolsó " "érvényes címet szóközzel elválasztva." -#: firewall/models.py:398 firewall/models.py:538 +#: firewall/models.py:402 firewall/models.py:542 msgid "vlans" msgstr "vlanok" -#: firewall/models.py:406 +#: firewall/models.py:410 msgid "You cannot specify an IPv6 template if there is no IPv6 network set." msgstr "Nem adhat meg IPv6 sablont, ha nincs IPv6 hálózat beállítva." -#: firewall/models.py:412 +#: firewall/models.py:416 #, python-format msgid "%(ip6)s (translated from %(ip4)s) is outside of the IPv6 network." msgstr "%(ip6)s (ebből képezve: %(ip4)s) kívül esik az IPv6 hálózaton." -#: firewall/models.py:456 +#: firewall/models.py:460 msgid "IPv6 network is too small to map IPv4 addresses to it." msgstr "Az IPv6 hálózat túl kicsi az IPv4 címek leképezéséhez." -#: firewall/models.py:513 +#: firewall/models.py:517 msgid "All IP addresses are already in use." msgstr "Minden IP cím használatban van." -#: firewall/models.py:536 firewall/models.py:568 +#: firewall/models.py:540 firewall/models.py:572 msgid "The name of the group." msgstr "A csoport neve." -#: firewall/models.py:539 +#: firewall/models.py:543 msgid "The vlans which are members of the group." msgstr "A csoport tagjait képező vlanok." -#: firewall/models.py:542 firewall/models.py:570 +#: firewall/models.py:546 firewall/models.py:574 msgid "Description of the group." msgstr "A csoport leírása." -#: firewall/models.py:553 +#: firewall/models.py:557 msgid "vlan groups" msgstr "vlan-csoportok" -#: firewall/models.py:581 +#: firewall/models.py:585 msgid "host groups" msgstr "gépcsoportok" -#: firewall/models.py:597 network/tables.py:139 storage/models.py:52 +#: firewall/models.py:601 network/tables.py:139 storage/models.py:52 msgid "hostname" msgstr "gépnév" -#: firewall/models.py:598 +#: firewall/models.py:602 msgid "The alphanumeric hostname of the host, the first part of the FQDN." msgstr "A gép alfanumerikus gépneve, az FQDN első része." -#: firewall/models.py:604 +#: firewall/models.py:608 msgid "reverse" msgstr "reverz" -#: firewall/models.py:605 +#: firewall/models.py:609 msgid "" "The fully qualified reverse hostname of the host, if different than " "hostname.domain." @@ -3726,165 +3729,165 @@ msgstr "" "A gép teljes reverz tartományneve, amennyiben különbözik ettől: " "gépnév.tartomány." -#: firewall/models.py:609 network/tables.py:138 +#: firewall/models.py:613 network/tables.py:138 msgid "MAC address" msgstr "MAC cím" -#: firewall/models.py:610 +#: firewall/models.py:614 msgid "" "The MAC (Ethernet) address of the network interface. For example: " "99:AA:BB:CC:DD:EE." msgstr "A hálózati interfész MAC (Ethernet) címe. Például 99:AA:BB:CC:DD:EE." -#: firewall/models.py:615 +#: firewall/models.py:619 msgid "The real IPv4 address of the host, for example 10.5.1.34." msgstr "A gép valódi IPv4 címe, például 10.5.1.34." -#: firewall/models.py:619 +#: firewall/models.py:623 msgid "WAN IPv4 address" msgstr "WAN IPv4 cím" -#: firewall/models.py:620 +#: firewall/models.py:624 msgid "" "The public IPv4 address of the host on the wide area network, if different." msgstr "A gép nyilvános IPv4 címe a nagy kiterjedésű hálózaton, ha eltér." -#: firewall/models.py:625 +#: firewall/models.py:629 msgid "The global IPv6 address of the host, for example 2001:db:88:200::10." msgstr "A gép globális IPv6 címe, például 2001:db:88:200::10." -#: firewall/models.py:627 +#: firewall/models.py:631 msgid "shared IP" msgstr "osztott IP" -#: firewall/models.py:629 +#: firewall/models.py:633 msgid "If the given WAN IPv4 address is used by multiple hosts." msgstr "A WAN IPv4 címet több gép használja-e." -#: firewall/models.py:632 +#: firewall/models.py:636 msgid "What is this host for, what kind of machine is it." msgstr "Mi a gép célja, milyen gép ez." -#: firewall/models.py:635 +#: firewall/models.py:639 msgid "Notes" msgstr "Jegyzetek" -#: firewall/models.py:636 +#: firewall/models.py:640 msgid "location" msgstr "elhelyezés" -#: firewall/models.py:638 +#: firewall/models.py:642 msgid "The physical location of the machine." msgstr "A gép fizikai helye." -#: firewall/models.py:641 +#: firewall/models.py:645 msgid "Vlan network that the host is part of." msgstr "Az a vlan hálózat, amelynek a gép része." -#: firewall/models.py:644 +#: firewall/models.py:648 msgid "The person responsible for this host." msgstr "A gépért felelős személy." -#: firewall/models.py:646 +#: firewall/models.py:650 msgid "groups" msgstr "csoportok" -#: firewall/models.py:648 +#: firewall/models.py:652 msgid "Host groups the machine is part of." msgstr "Gépcsoportok, amelyeknek tagja a gép." -#: firewall/models.py:702 +#: firewall/models.py:706 msgid "If shared_ip has been checked, external_ipv4 has to be unique." msgstr "" "Amennyiben az osztott IP mező igaz, a külső IPv4 cím mező egyedi kell " "legyen." -#: firewall/models.py:705 +#: firewall/models.py:709 msgid "You can't use another host's NAT'd address as your own IPv4." msgstr "Nem használható másik gép NAT-olt címe saját IPv4 címként." -#: firewall/models.py:810 +#: firewall/models.py:814 #, python-format msgid "All %s ports are already in use." msgstr "Minden %s port használatban van." -#: firewall/models.py:828 +#: firewall/models.py:832 #, python-format msgid "Port %(proto)s %(public)s is already in use." msgstr "A(z) %(public)s %(proto)s port használatban van." -#: firewall/models.py:961 +#: firewall/models.py:965 msgid "firewalls" msgstr "tűzfalak" -#: firewall/models.py:1004 firewall/models.py:1039 firewall/models.py:1111 -#: firewall/models.py:1144 firewall/models.py:1171 +#: firewall/models.py:1008 firewall/models.py:1043 firewall/models.py:1115 +#: firewall/models.py:1148 firewall/models.py:1175 msgid "created_at" msgstr "létrehozva" -#: firewall/models.py:1006 firewall/models.py:1041 firewall/models.py:1113 -#: firewall/models.py:1146 firewall/models.py:1173 +#: firewall/models.py:1010 firewall/models.py:1045 firewall/models.py:1117 +#: firewall/models.py:1150 firewall/models.py:1177 msgid "modified_at" msgstr "módosítva" -#: firewall/models.py:1007 firewall/models.py:1035 +#: firewall/models.py:1011 firewall/models.py:1039 msgid "ttl" msgstr "ttl" -#: firewall/models.py:1012 firewall/models.py:1028 +#: firewall/models.py:1016 firewall/models.py:1032 #: network/templates/network/domain-create.html:8 #: network/templates/network/domain-edit.html:8 msgid "domain" msgstr "tartomány" -#: firewall/models.py:1013 +#: firewall/models.py:1017 msgid "domains" msgstr "tartományok" -#: firewall/models.py:1034 +#: firewall/models.py:1038 msgid "address" msgstr "cím" -#: firewall/models.py:1056 +#: firewall/models.py:1060 msgid "Address must be specified!" msgstr "A cím megadása kötelező." -#: firewall/models.py:1069 +#: firewall/models.py:1073 msgid "Unknown record type." msgstr "Ismeretlen rekordtípus." -#: firewall/models.py:1094 network/templates/network/record-create.html:8 +#: firewall/models.py:1098 network/templates/network/record-create.html:8 #: network/templates/network/record-edit.html:8 msgid "record" msgstr "rekord" -#: firewall/models.py:1095 +#: firewall/models.py:1099 msgid "records" msgstr "rekordok" -#: firewall/models.py:1105 +#: firewall/models.py:1109 msgid "untagged vlan" msgstr "untagged vlan" -#: firewall/models.py:1108 +#: firewall/models.py:1112 msgid "tagged vlans" msgstr "tagged vlanok" -#: firewall/models.py:1117 firewall/models.py:1142 +#: firewall/models.py:1121 firewall/models.py:1146 #: network/templates/network/switch-port-create.html:8 #: network/templates/network/switch-port-edit.html:8 msgid "switch port" msgstr "switch port" -#: firewall/models.py:1118 +#: firewall/models.py:1122 msgid "switch ports" msgstr "switch portok" -#: firewall/models.py:1136 +#: firewall/models.py:1140 msgid "interface" msgstr "interfész" -#: firewall/models.py:1137 +#: firewall/models.py:1141 msgid "" "The name of network interface the gateway should serve this network on. For " "example eth2." @@ -3892,35 +3895,35 @@ msgstr "" "Azon hálózati interfész nevve, amelyen az útválasztó ezt a hálózatot " "kiszolgálja. Például eth2." -#: firewall/models.py:1150 +#: firewall/models.py:1154 msgid "ethernet device" msgstr "ethernet-eszköz" -#: firewall/models.py:1151 +#: firewall/models.py:1155 msgid "ethernet devices" msgstr "ethernet-eszközök" -#: firewall/models.py:1164 +#: firewall/models.py:1168 msgid "reason" msgstr "indok" -#: firewall/models.py:1166 +#: firewall/models.py:1170 msgid "short message" msgstr "rövid üzenet" -#: firewall/models.py:1169 +#: firewall/models.py:1173 msgid "whitelisted" msgstr "engedélyezőlistán" -#: firewall/models.py:1175 +#: firewall/models.py:1179 msgid "expires at" msgstr "lejár" -#: firewall/models.py:1186 +#: firewall/models.py:1190 msgid "blacklist item" msgstr "tiltólista eleme" -#: firewall/models.py:1187 +#: firewall/models.py:1191 msgid "blacklist items" msgstr "tiltólista elemek" @@ -4797,19 +4800,19 @@ msgstr "" "\n" "A sablon hozzáférés típus segítségével a felhasználók felhasználó vagy operátor jogosultságot igényelhetnek a sablonokhoz." -#: request/views.py:106 +#: request/views.py:108 msgid "Template access type successfully updated." msgstr "A sablon hozzáférés típus frissítésre került." -#: request/views.py:114 +#: request/views.py:116 msgid "New template access type successfully created." msgstr "A sablon hozzáférés típus létrehozásra került." -#: request/views.py:131 +#: request/views.py:133 msgid "Lease type successfully updated." msgstr "A bérlet típus frissítésre kerül." -#: request/views.py:139 +#: request/views.py:141 msgid "New lease type successfully created." msgstr "A bérlet típus létrehozása került." @@ -5012,7 +5015,7 @@ msgstr "" "A gép \"Hozzáférés\" oldalán hozzáférést adhat felhasználóknak vagy " "csoportoknak." -#: templates/info/help/faq.html:32 +#: templates/info/help/faq.html:32 templates/info/help/faq_toc.html:7 msgid "How can I open ports?" msgstr "Hogyan tudok portot nyitni?" @@ -5047,7 +5050,7 @@ msgstr "Most már csatlakozhat a virtuális géphez a generált porton keresztü #: templates/info/help/faq.html:53 msgid "My machine's lease is too short. How can I extend it?" -msgstr "A gépen bérleti ideje túl rövid. Hogyan hosszabbíthatom meg?" +msgstr "A gépem bérleti ideje túl rövid. Hogyan hosszabbíthatom meg?" #: templates/info/help/faq.html:57 msgid "" @@ -5069,6 +5072,8 @@ msgid "" " On the VM's resources panel click <strong>Request more resources</strong>, modify the values, explain your request and finally hit save.\n" " " msgstr "" +"\n" +"Küldhet egy kérést az adminisztrátoroknak. A virtuális gép erőforrások oldalán kattintson az <strong>Erőforrások igénylése</strong>, majd a kérés küldése gombra. Magyarázza meg, hogy miért kéri az adott erőforrásokat." #: templates/info/help/faq.html:77 templates/info/help/faq_toc.html:12 msgid "How can I get access to a template?" @@ -5081,14 +5086,12 @@ msgid "" " Select which template you want, explain why you need it and then submit the form.\n" " " msgstr "" - -#: templates/info/help/faq_toc.html:7 -msgid "How can I portforward?" -msgstr "Hogyan tudok porttovábbítást beállítani?" +"\n" +"Ha olyan sablont akar használni, amihez nincs hozzáférése, akkor a meglévő sablonok listája alatti link segítségével tud egy kérést küldeni az adminisztrátoroknak." #: templates/info/help/faq_toc.html:8 msgid "My machine’s lease is short. How can I extend it?" -msgstr "A gépen bérleti ideje rövid. Hogyan hosszabbíthatom meg?" +msgstr "A gépem bérleti ideje rövid. Hogyan hosszabbíthatom meg?" #: templates/info/help/overview.html:6 templates/info/help/overview_toc.html:5 msgid "Introduction" @@ -5194,9 +5197,12 @@ msgid "" " and show the <strong>activity log</strong>.\n" " " msgstr "" +"\n" +"Egy virtuális gép kiválasztása után az adott gépet részletező oldalra jut.\n" +"Itt láthatóak a <strong>kapcsolat részletei</strong>, <strong>statisztikák</strong> és a korábbi <strong>tevékenységek</strong>, valamint módosíthatóak a virtuális gép <strong>beállításai</strong>, a <strong>hozzáférési jogosultságok</strong>, <strong>hálózati interfészek</strong> és az <strong>erőforrások</strong>." #: templates/info/help/overview.html:90 -#: templates/info/help/overview_toc.html:24 +#: templates/info/help/overview_toc.html:22 msgid "Details" msgstr "Részletek" @@ -5212,7 +5218,7 @@ msgstr "" "Ha kiválaszt egy virtuális gépet, a gép adatait és műveleteit elérhetővé tévő oldalra jut. Baloldalt a gép állapotát és a <strong>csatlakozáshoz</strong> szükséges adatokat találja. Középen egy több lapból álló panel van, amely a gép összes részletét kategorizálva mutatja be." #: templates/info/help/overview.html:100 -#: templates/info/help/overview_toc.html:25 +#: templates/info/help/overview_toc.html:23 msgid "How can I connect to the virtual machine?" msgstr "Hogyan tudok csatlakozni a virtuális géphez?" @@ -5227,7 +5233,7 @@ msgstr "" "Ha a gép már el van indítva, akkor kattintson a \"csatlakozás\" gombra vagy egyszerűen másolja be a parancsot a terminálba. A másik lehetőség a VNC konzol használata" #: templates/info/help/overview.html:111 -#: templates/info/help/overview_toc.html:26 +#: templates/info/help/overview_toc.html:24 msgid "How can I change the VM’s password?" msgstr "Hogyan tudom megváltoztatni a VM jelszavát?" @@ -5238,10 +5244,10 @@ msgid "" " " msgstr "" "\n" -"Kattintson az \"Űj jelszó generálása\" linkre." +"Kattintson az \"Új jelszó generálása\" linkre." #: templates/info/help/overview.html:121 -#: templates/info/help/overview_toc.html:31 +#: templates/info/help/overview_toc.html:29 msgid "Operations" msgstr "Műveletek" @@ -5259,7 +5265,7 @@ msgstr "" "Az eszköztár gombjai színkódoltak a hatásuk szerint, valamint a gép állapotától függően kerülnek engedélyezésre/tiltásra. Az ajánlott művelet gombja a legnagyobb, amelyen szerepel az adott művelet megnevezése is. Nyugodtan rákattinthat a gombokra, a megerősítő ablak részletesen bemutatja az egyes műveleteket." #: templates/info/help/overview.html:132 -#: templates/info/help/overview_toc.html:32 +#: templates/info/help/overview_toc.html:30 msgid "What kind of operations are allowed to do with my VM?" msgstr "Milyen műveleteket végezhetek a virtuális gépemmel?" @@ -5270,6 +5276,9 @@ msgid "" " We strongly advise to add a description to make the work of the cloud administrators easier in case of maintenance.\n" " " msgstr "" +"\n" +"Itt találhatóak statisztikák a virtuális gépről, illetve módosítható a gép neve, leírása és címkéi.\n" +"Erősen ajánljuk, hogy írjon leírást a géphez, ezzel is segítve a rendszergazdák munkáját egy esetleges karbantartás esetén." #: templates/info/help/overview.html:154 msgid "" @@ -5278,8 +5287,12 @@ msgid "" " Each virtual machine has a deadline for suspending and destroying based on predefined leases.\n" " " msgstr "" +"\n" +"Itt található továbbá egy összefoglalás a virtuális gép lejárati idejeiről is. \n" +"Minden gépnek van egy lejárati ideje külön felfüggesztésre és megsemmisítésre. " #: templates/info/help/overview.html:161 +#: templates/info/help/overview_toc.html:38 msgid "How can I extend the VM's expiration date?" msgstr "Hogyan tudom növelni a VM lejárati idejét?" @@ -5294,7 +5307,7 @@ msgstr "" "A megújítás gombbal meg tudja növelni a gép felfüggesztési és törlési idejét. A virtuális gép felfüggesztése vagy törlése előtt értesítést kap." #: templates/info/help/overview.html:172 -#: templates/info/help/overview_toc.html:42 +#: templates/info/help/overview_toc.html:39 msgid "How can I share previously uploaded files with the VM?" msgstr "" "Hogyan tudom megosztani a korábban feltöltött fájlokat a virtuális géppel?" @@ -5305,6 +5318,8 @@ msgid "" " With the ‘Mount Store’ option the virtual machine mounts your personal storage.\n" " " msgstr "" +"\n" +"A 'tárhely csatolása' gombra kattintva a virtuális gép csatolja a személyes tárhelyét." #: templates/info/help/overview.html:184 msgid "" @@ -5314,6 +5329,8 @@ msgid "" " Users with specific permission can change these settings if the machine is stopped.\n" " " msgstr "" +"\n" +"Az erőforrások fülön látható, hogy mennyi <strong>memória</strong>, <strong>CPU</strong> áll a virtuális gép rendelkezésére, és milyen <strong>prioritással</strong> rendelkezik." #: templates/info/help/overview.html:192 msgid "" @@ -5332,6 +5349,10 @@ msgid "" " You should normally use the machine by connecting remotely over SSH or Remote Desktop.\n" " " msgstr "" +"\n" +"Ez a fül lehetővé teszi a virtuális gép konzolon keresztüli használatát, főként hibakeresési és operációs rendszer telepítési célokra.\n" +"Használható a billentyűzet és az egér is.\n" +"Alapesetben a virtuális géphez SSH-val, vagy távoli asztali kapcsolattal célszerű kapcsolódni." #: templates/info/help/overview.html:210 msgid "" @@ -5344,7 +5365,7 @@ msgstr "" "A gép megosztását és a tulajdon átruházását teszi lehetővé. A gép felhasználói láthatják a gép részleteit, az operátorok használhatják a legtöbb műveletet, a tulajdonosok törölhetik is a gépet." #: templates/info/help/overview.html:217 -#: templates/info/help/overview_toc.html:49 +#: templates/info/help/overview_toc.html:46 msgid "How can I give access to others?" msgstr "Hogyan tudok hozzáférést adni másnak" @@ -5359,7 +5380,7 @@ msgstr "" "Írja be a felhasználó/csoport azonosítóját, állítsa be a jogosultságot és nyomja meg a <span class=\"btn btn-success disabled\">Mentés</span> gombot." #: templates/info/help/overview.html:228 -#: templates/info/help/overview_toc.html:50 +#: templates/info/help/overview_toc.html:47 msgid "What kind of permissions are available?" msgstr "Milyen jogosultságok állnak rendelkezésre?" @@ -5409,7 +5430,7 @@ msgstr "" "Megmutatja, hogy a virtuális gépen hogyan csatlakozik a hálózathoz. Hozzá tud adni és törölni interfészeket, valamint engedélyezheti a TCP/UDP portok távoli hozzáférését." #: templates/info/help/overview.html:262 -#: templates/info/help/overview_toc.html:54 +#: templates/info/help/overview_toc.html:51 msgid "How can I add a network interface?" msgstr "Hogyan adhatok a géphez új interfészt?" @@ -5437,7 +5458,7 @@ msgstr "" "Itt látszik a virtuális gép teljes élettörténete. Itt lehet a műveletek meghiúsulásának okait is megtekinteni (húzza az egeret a művelet nevére)." #: templates/info/help/overview.html:284 -#: templates/info/help/overview_toc.html:59 +#: templates/info/help/overview_toc.html:56 msgid "Multiple VM operations" msgstr "Csoportos műveletek" @@ -5450,6 +5471,8 @@ msgid "" " Here the owner can execute operations on multiple virtual machines simultaneously.\n" " " msgstr "" +"\n" +"A műszerfalon a <span class=\"btn btn-primary btn-xs disabled\"><i class=\"fa fa-chevron-circle-right\"></i> felsorolás</span> gombra kattintva, megnyílik a virtuális gépek listája. Itt a tulajdonos több gépen egyszerre tud végrehajtani műveleteket." #: templates/info/help/overview.html:296 msgid "How can I see shared or destroyed VMs?" @@ -5490,7 +5513,7 @@ msgstr "" "Az irányítópulti dobozban a saját sablonait, valamint azokat látja, amelyekhez legalább operátor jogosultsága van. Ez azt jelenti, hogy megoszthatja őket csoportjaival vagy egyes felhasználókkal." #: templates/info/help/overview.html:330 -#: templates/info/help/overview_toc.html:69 +#: templates/info/help/overview_toc.html:66 msgid "How can I create templates?" msgstr "Hogyan tudok létrehozni sablonokat?" @@ -5516,31 +5539,81 @@ msgstr "" "A sablon részletező oldalán módosíthatja a sablon nevét és erőforrásait (pl. magok száma, memória mérete)." #: templates/info/help/overview.html:347 -#: templates/info/help/overview_toc.html:70 +#: templates/info/help/overview_toc.html:67 msgid "What kind of options are customizable in the template?" +msgstr "Milyen testreszabható beállítások vannak a sablonoknál?" + +#: templates/info/help/overview.html:351 +msgid "The user can choose the template's architecture (x86 or x86-64)." +msgstr "" +"A felhasználó kiválaszthatja a sablon architektúráját (x86 vagy x86-64)." + +#: templates/info/help/overview.html:355 +msgid "" +"The default access method is modifiable. Currently SSH, RDP and NX are " +"supported." msgstr "" +"Az alapértelmezett kapcsolódási mód módosítható. Jelenleg támogatott " +"protokollok: SSH, RDP és NX." #: templates/info/help/overview.html:358 msgid "Boot menu" msgstr "Rendszerbetöltő menüje" +#: templates/info/help/overview.html:359 +msgid "Check it to turn on the boot menu." +msgstr "Pipálja be a rendszerindítási menü bekapcsolásához." + #: templates/info/help/overview.html:362 msgid "Traits" msgstr "Jellemző" +#: templates/info/help/overview.html:363 +msgid "" +"By adding or removing traits we can guarantee specific features the host " +"node will have (like <em>GPU</em>) for the virtual machine." +msgstr "" +"A jellemzők kiválasztásával garantálható, hogy a virtuális gépet futtató " +"node rendelkezzen bizonyos tulajdonságokkal (pl. <em>GPU</em>)." + #: templates/info/help/overview.html:366 msgid "Operating system" msgstr "Operációs rendszer" +#: templates/info/help/overview.html:367 +#| msgid "The name of the group." +msgid "The name of the operating system." +msgstr "Az operációs rendszer neve." + #: templates/info/help/overview.html:370 msgid "Agent" msgstr "Ügynök" -#: templates/info/help/overview.html:381 +#: templates/info/help/overview.html:371 +#, fuzzy +#| msgid "has agent installed, and the manager should wait for its rt." +msgid "" +"Check this if the machine has agent installed and the manager should wait " +"for its start." +msgstr "" +"A gépre telepítve van-e az ügynökszoftver, vagyis a menedzser várjon-e az " +"indulására." + +#: templates/info/help/overview.html:375 +msgid "" +"\n" +" The CIRCLE Cloud is using libvirt, so the owner can customize the running VM's options here by\n" +" <a href=\"https://libvirt.org/formatdomain.html\">libvirt domain parameters</a>.\n" +" " +msgstr "" +"\n" +"A CIRCLE Cloud a libvirt API-t használja, így a virtuális gép beállításai személyre szabhatóak <a href=\"https://libvirt.org/formatdomain.html\">libvirt domén paraméterekkel</a>." + +#: templates/info/help/overview.html:383 msgid "How can I transfer the template ownership to someone else?" -msgstr "Hogyan tudom a sablont átruházni másnak?" +msgstr "Hogyan tudom a sablont átruházni másra?" -#: templates/info/help/overview.html:384 +#: templates/info/help/overview.html:386 msgid "" "After clicking the ‘Transfer ownership’ you can select a user to give the " "template to." @@ -5548,25 +5621,29 @@ msgstr "" "A \"Tulajdon átruházása\" gombra kattintva ki tudja választani a " "felhasználót." -#: templates/info/help/overview.html:390 +#: templates/info/help/overview.html:392 +#: templates/info/help/overview_toc.html:68 msgid "How can I grant access for users or groups to the template?" msgstr "" -"Hogyan tudok hozzáférést adni a sablonhoz a felhasználóknak vagy " +"Hogyan tudok hozzáférést adni a sablonhoz felhasználóknak vagy " "csoportoknak?" -#: templates/info/help/overview.html:393 +#: templates/info/help/overview.html:395 msgid "" "\n" " Same as the VM access rights handling.\n" " You can type the user or group ID at the template detail page's Manage access box and select a suitable access level.\n" " " msgstr "" +"\n" +"Hasonlóan a virtuális gép jogosultság kezeléséhez.\n" +"Írja be a felhasználó/csoport azonosítóját a sablon részletek oldalán és állítsa be a kívánt hozzáférési szintet." -#: templates/info/help/overview.html:405 +#: templates/info/help/overview.html:407 msgid "Sorry, you have no permission to create groups." msgstr "Nincs jogosultsága csoportok létrehozására." -#: templates/info/help/overview.html:410 +#: templates/info/help/overview.html:412 msgid "" "\n" " Groups are the main building blocks of permission management. On the dashboard you see a list of groups you have access to.\n" @@ -5575,12 +5652,12 @@ msgstr "" "\n" "A csoportok a jogosultságkezelés epítőelemei. Az irányítópulton azon csoportokat látja, amelykhez hozzáférése van." -#: templates/info/help/overview.html:415 -#: templates/info/help/overview_toc.html:79 +#: templates/info/help/overview.html:417 +#: templates/info/help/overview_toc.html:74 msgid "How can I create groups?" msgstr "Hogyan tudok csoportot létrehozni?" -#: templates/info/help/overview.html:418 +#: templates/info/help/overview.html:420 msgid "" "\n" " You can create your own groups by clicking on the new button of the groups box.\n" @@ -5589,7 +5666,7 @@ msgstr "" "\n" "Saját csoportot a csoportok dobozban található \"új\" gombra kattintva készíthet." -#: templates/info/help/overview.html:425 +#: templates/info/help/overview.html:427 msgid "" "\n" " Users logged in with SSO authentication can automatically become members of groups based on its organizational identifier.\n" @@ -5601,12 +5678,12 @@ msgstr "" "\n" "SSO azonosítással belépett felhasználók automatikusan tagjai lehetnek csoportoknak a szervezeti azonosítójuk alapján. Akik adminisztrátorai egy csoportnak (vagy oktatói egy tantárgynak az akadémiai szférában) olyan csoportokat is létrehozhatnak, amelyeknek a tagjai bejelentkezéskor automatikusan bekerülnek." -#: templates/info/help/overview.html:434 -#: templates/info/help/overview_toc.html:80 +#: templates/info/help/overview.html:436 +#: templates/info/help/overview_toc.html:75 msgid "How can I manage the users in a group?" msgstr "Hogyan tudom kezelni a csoport felhasználóit?" -#: templates/info/help/overview.html:437 +#: templates/info/help/overview.html:439 msgid "" "\n" " On the group page the owner can add or remove users or change the access rights over the group.\n" @@ -5615,23 +5692,25 @@ msgstr "" "\n" "A csoport oldalán hozzá tud adni vagy törölni felhasználókat, illetve kezelni a csoportra szóló jogokat." -#: templates/info/help/overview.html:444 -#: templates/info/help/overview_toc.html:81 +#: templates/info/help/overview.html:446 +#: templates/info/help/overview_toc.html:76 msgid "How can I manage privileges with the group?" -msgstr "" +msgstr "Hogyan tudom kezelni a csoport jogosultságait?" -#: templates/info/help/overview.html:447 +#: templates/info/help/overview.html:449 msgid "" "\n" " The owner can add or remove privileges at the bottom of the group page like ‘can download disk’ or ‘can configure port forwards’.\n" " " msgstr "" +"\n" +"A tulajdonosnak van joga módosítani a csoport jogosultságait. Ezt a csoport oldalának alján teheti meg. " -#: templates/info/help/overview.html:458 +#: templates/info/help/overview.html:460 msgid "Sorry, this deployment of CIRCLE does not support file store." msgstr "Ez a CIRCLE-telepítés nem támogatja a tárhelyet." -#: templates/info/help/overview.html:463 +#: templates/info/help/overview.html:465 msgid "" "\n" " Each user has a simple personal file store, which is the easiest way to keep and retrieve your work done on virtual machines.\n" @@ -5640,7 +5719,7 @@ msgstr "" "\n" "Minden felhasználónak van egy személyes tárhelye, ami a egyszerű módot ad a virtuális gépeken elkészített munka tárolására és letöltésére." -#: templates/info/help/overview.html:468 +#: templates/info/help/overview.html:470 msgid "" "\n" " You can get and upload files from both the web interface and from virtual machines.\n" @@ -5652,12 +5731,12 @@ msgstr "" "\n" "Fájljait a webes felületről és a virtuális gépekről is eléri. A webes felület olyan, mint bármelyik fájlböngésző. A virtuális gépek alapesetben nem kapják meg a tárhely eléréséhez szükséges azonosítókat elkerülendő megosztásukat a gép esetleges többi használójával." -#: templates/info/help/overview.html:477 -#: templates/info/help/overview_toc.html:87 +#: templates/info/help/overview.html:479 +#: templates/info/help/overview_toc.html:82 msgid "How can I share my files with a VM?" msgstr "Hogyan tudom megosztani a fájljaimat a VM-mel?" -#: templates/info/help/overview.html:479 +#: templates/info/help/overview.html:481 msgid "" "To access the file store press the <span class=\"btn btn-info disabled btn-" "xs\"><i class=\"fa fa-briefcase\"></i> mount store</span> button on the " @@ -5667,12 +5746,12 @@ msgstr "" "btn-xs\"><i class=\"fa fa-briefcase\"></i> tárhely csatolása</span> gombot a" " virtuális gépen." -#: templates/info/help/overview.html:487 -#: templates/info/help/overview_toc.html:93 +#: templates/info/help/overview.html:489 +#: templates/info/help/overview_toc.html:88 msgid "How can I change my password?" msgstr "Hogyan tudom megváltoztatni a jelszavamat?" -#: templates/info/help/overview.html:490 +#: templates/info/help/overview.html:492 msgid "" "\n" " On the profile page type the new password twice and the old password. Users can’t change passwords if the profile is using SSO.\n" @@ -5681,12 +5760,12 @@ msgstr "" "\n" "A profil szerkesztése oldalon írja be az új jelszót kétszer, valamint a régi jelszavát. Az SSO-s felhasználók nem változtathatnak jelszót." -#: templates/info/help/overview.html:497 -#: templates/info/help/overview_toc.html:94 +#: templates/info/help/overview.html:499 +#: templates/info/help/overview_toc.html:89 msgid "How can I store public keys on the VMs?" msgstr "Hogyan tudok publikus kulcsokat tárolni a VM-en?" -#: templates/info/help/overview.html:500 +#: templates/info/help/overview.html:502 msgid "" "Go to your profile page, click on the ‘add SSH key’ and paste your public " "key's content to the textarea." @@ -5694,44 +5773,10 @@ msgstr "" "Nyissa meg a profilját, nyomja meg az SSH kulcs hozzáadása gombot és másolja" " be a szövegdobozba a publikus kulcsot." -#: templates/info/help/overview_toc.html:13 -msgid "How can I search for VMs?" -msgstr "Hogyan kereshetek a VM-ek között?" - -#: templates/info/help/overview_toc.html:16 -msgid "Templates box" -msgstr "Sablonok" - -#: templates/info/help/overview_toc.html:40 -msgid "How can I expand the VM’s expiration date?" -msgstr "Hogyan tudom növelni a VM lejárati idejét?" - -#: templates/info/help/overview_toc.html:41 -msgid "File management" -msgstr "Fájlkezelés" - -#: templates/info/help/overview_toc.html:60 +#: templates/info/help/overview_toc.html:57 msgid "How can I show shared or destroyed VMs?" msgstr "Hogyan tudom megnézni a megosztott vagy törölt gépeket?" -#: templates/info/help/overview_toc.html:71 -msgid "How can I change the expiration of the tempalte's VMs" -msgstr "" - -#: templates/info/help/overview_toc.html:72 -msgid "How can I give the template to other user?" -msgstr "Hogyan tudom egy másik felhasználónak adni a sablont?" - -#: templates/info/help/overview_toc.html:73 -msgid "How can I give access to users or groups to the template?" -msgstr "" -"Hogyan tudok a sablonhoz hozzáférést adni a felhasználóknak vagy " -"csoportoknak?" - -#: templates/info/help/overview_toc.html:95 -msgid "How can I change connection template?" -msgstr "Hogyan tudok változtatni parancssablont?" - #: templates/info/legal.html:14 msgid "" "\n" @@ -6336,62 +6381,62 @@ msgstr "aktív" 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:208 +#: vm/operations.py:209 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:216 +#: vm/operations.py:217 msgid "destroy network (rollback)" msgstr "hálózat megsemmisítése (visszagörgetés)" -#: vm/operations.py:223 +#: vm/operations.py:224 #, 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:244 +#: vm/operations.py:245 #, python-format msgid "add %(vlan)s interface" msgstr "új %(vlan)s interfész" -#: vm/operations.py:252 +#: vm/operations.py:253 msgid "create disk" msgstr "lemez létrehozása" -#: vm/operations.py:253 +#: vm/operations.py:254 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:274 +#: vm/operations.py:275 msgid "deploying disk" msgstr "lemez létrehozása" -#: vm/operations.py:281 +#: vm/operations.py:282 #, python-format msgid "create disk %(name)s (%(size)s)" msgstr "%(name)s lemez létrehozása (%(size)s)" -#: vm/operations.py:289 +#: vm/operations.py:290 msgid "resize disk" msgstr "lemez átméretezése" -#: vm/operations.py:290 +#: vm/operations.py:291 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:304 +#: vm/operations.py:305 #, python-format msgid "resize disk %(name)s to %(size)s" msgstr "%(name)s lemez átméretezése (%(size)s)" -#: vm/operations.py:316 +#: vm/operations.py:317 msgid "download disk" msgstr "lemez letöltése" -#: vm/operations.py:317 +#: vm/operations.py:318 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 " @@ -6401,21 +6446,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:339 +#: vm/operations.py:340 #, python-format msgid "download %(name)s" msgstr "%(name)s letöltése" -#: vm/operations.py:342 +#: vm/operations.py:343 #, 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:353 +#: vm/operations.py:354 msgid "deploy" msgstr "indítás" -#: vm/operations.py:354 +#: vm/operations.py:355 msgid "" "Deploy and start the virtual machine (including storage and network " "configuration)." @@ -6423,78 +6468,78 @@ 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:371 +#: vm/operations.py:372 #, 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:399 vm/operations.py:577 vm/operations.py:946 +#: vm/operations.py:400 vm/operations.py:578 vm/operations.py:947 msgid "deploy network" msgstr "hálózati kapcsolat létrehozása" -#: vm/operations.py:411 vm/operations.py:595 vm/operations.py:694 +#: vm/operations.py:412 vm/operations.py:596 vm/operations.py:695 msgid "wait operating system loading" msgstr "várakozás az operációs rendszer betöltésére" -#: vm/operations.py:416 +#: vm/operations.py:417 msgid "deploy vm" msgstr "vm indítása" -#: vm/operations.py:417 +#: vm/operations.py:418 msgid "Deploy virtual machine." msgstr "Virtuális gép létrehozása." -#: vm/operations.py:426 +#: vm/operations.py:427 msgid "deploy virtual machine" msgstr "virtuális gép létrehozása" -#: vm/operations.py:427 +#: vm/operations.py:428 #, python-format msgid "deploy vm to %(node)s" msgstr "vm létrehozása: %(node)s" -#: vm/operations.py:433 +#: vm/operations.py:434 msgid "deploy disks" msgstr "lemez létrehozása" -#: vm/operations.py:434 +#: vm/operations.py:435 msgid "Deploy all associated disks." msgstr "Csatolt lemezek létrehozása." -#: vm/operations.py:451 +#: vm/operations.py:452 msgid "boot virtual machine" msgstr "virtuális gép indítása" -#: vm/operations.py:459 +#: vm/operations.py:460 msgid "destroy" msgstr "megsemmisítés" -#: vm/operations.py:460 +#: vm/operations.py:461 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:472 +#: vm/operations.py:473 msgid "destroy network" msgstr "hálózat megsemmisítése" -#: vm/operations.py:483 +#: vm/operations.py:484 msgid "destroy disks" msgstr "lemez megsemmisítése" -#: vm/operations.py:502 +#: vm/operations.py:503 msgid "destroy virtual machine" msgstr "virtuális gép megsemmisítése" -#: vm/operations.py:510 +#: vm/operations.py:511 msgid "removing memory dump" msgstr "memóriamentés törlése" -#: vm/operations.py:524 +#: vm/operations.py:525 msgid "migrate" msgstr "migrálás" -#: vm/operations.py:525 +#: vm/operations.py:526 msgid "" "Move a running virtual machine to an other worker node keeping its full " "state." @@ -6502,39 +6547,39 @@ msgstr "" "A virtuális gép mozgatása egy másik számítási csomópontra állapotának " "megtartásával." -#: vm/operations.py:542 +#: vm/operations.py:543 msgid "redeploy network (rollback)" msgstr "hálózati kapcsolat újraépítése (visszagörgetés)" -#: vm/operations.py:549 +#: vm/operations.py:550 msgid "schedule" msgstr "ütemezés" -#: vm/operations.py:556 +#: vm/operations.py:557 #, python-format msgid "migrate to %(node)s" msgstr "migrálás %(node)s csomópontra" -#: vm/operations.py:567 vm/operations.py:895 +#: vm/operations.py:568 vm/operations.py:896 msgid "shutdown network" msgstr "hálózati kapcsolat leállítása" -#: vm/operations.py:584 +#: vm/operations.py:585 msgid "reboot" msgstr "újraindítás" -#: vm/operations.py:585 +#: vm/operations.py:586 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:601 +#: vm/operations.py:602 msgid "remove interface" msgstr "interfész törlése" -#: vm/operations.py:602 +#: vm/operations.py:603 msgid "" "Remove the specified network interface and erase IP address allocations, " "related firewall rules and hostnames." @@ -6542,68 +6587,68 @@ 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:618 +#: vm/operations.py:619 #, python-format msgid "remove %(vlan)s interface" msgstr "%(vlan)s interfész törlése" -#: vm/operations.py:625 +#: vm/operations.py:626 msgid "close port" msgstr "port bezárása" -#: vm/operations.py:626 +#: vm/operations.py:627 msgid "Close the specified port." msgstr "A megadott port bezárása." -#: vm/operations.py:635 +#: vm/operations.py:636 #, python-format msgid "close %(proto)s/%(port)d on %(host)s" msgstr "%(proto)s/%(port)d bezárása ezen: %(host)s" -#: vm/operations.py:643 +#: vm/operations.py:644 msgid "open port" msgstr "port nyitása" -#: vm/operations.py:644 +#: vm/operations.py:645 msgid "Open the specified port." msgstr "A megadott port kinyitása." -#: vm/operations.py:653 +#: vm/operations.py:654 #, python-format msgid "open %(proto)s/%(port)d on %(host)s" msgstr "%(proto)s/%(port)d kinyitása ezen: %(host)s" -#: vm/operations.py:660 +#: vm/operations.py:661 msgid "remove disk" msgstr "lemez eltávolítása" -#: vm/operations.py:661 +#: vm/operations.py:662 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:671 +#: vm/operations.py:672 msgid "destroy disk" msgstr "lemez megsemmisítése" -#: vm/operations.py:677 +#: vm/operations.py:678 #, python-format msgid "remove disk %(name)s" msgstr "%(name)s lemez eltávolítása" -#: vm/operations.py:684 vm/operations.py:1120 +#: vm/operations.py:685 vm/operations.py:1121 msgid "reset" msgstr "reset" -#: vm/operations.py:685 +#: vm/operations.py:686 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:700 +#: vm/operations.py:701 msgid "save as template" msgstr "mentés sablonként" -#: vm/operations.py:701 +#: vm/operations.py:702 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) " @@ -6613,21 +6658,21 @@ 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:777 +#: vm/operations.py:778 #, python-format msgid "saving disk %(name)s" msgstr "%(name)s lemez mentése" -#: vm/operations.py:803 +#: vm/operations.py:804 #, python-format msgid "New template: %(template)s" msgstr "Új sablon: %(template)s" -#: vm/operations.py:812 +#: vm/operations.py:813 msgid "shutdown" msgstr "leállítás" -#: vm/operations.py:813 +#: vm/operations.py:814 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 " @@ -6637,7 +6682,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:832 +#: vm/operations.py:833 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 " @@ -6647,11 +6692,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:844 +#: vm/operations.py:845 msgid "shut off" msgstr "kikapcsolás" -#: vm/operations.py:845 +#: vm/operations.py:846 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" @@ -6664,11 +6709,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:868 +#: vm/operations.py:869 msgid "sleep" msgstr "altatás" -#: vm/operations.py:869 +#: vm/operations.py:870 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 " @@ -6684,15 +6729,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:903 +#: vm/operations.py:904 msgid "suspend virtual machine" msgstr "virtuális gép felfüggesztése" -#: vm/operations.py:917 +#: vm/operations.py:918 msgid "wake up" msgstr "virtuális gép ébresztése" -#: vm/operations.py:918 +#: vm/operations.py:919 msgid "" "Wake up sleeping (suspended) virtual machine. This will load the saved " "memory of the system and start the virtual machine from this state." @@ -6700,15 +6745,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:957 +#: vm/operations.py:958 msgid "resume virtual machine" msgstr "virtuális gép ébresztése" -#: vm/operations.py:971 +#: vm/operations.py:972 msgid "renew" msgstr "megújítás" -#: vm/operations.py:972 +#: vm/operations.py:973 msgid "" "Virtual machines are suspended and destroyed after they expire. This " "operation renews expiration times according to the lease type. If the " @@ -6718,11 +6763,11 @@ 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:983 +#: vm/operations.py:984 msgid "set time of suspend" msgstr "felfüggesztés idejének beállítása" -#: vm/operations.py:987 +#: vm/operations.py:988 msgid "" "Renewing the machine with the selected lease would result in its suspension " "time get earlier than before." @@ -6730,11 +6775,11 @@ 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:994 +#: vm/operations.py:995 msgid "set time of delete" msgstr "törlés idejének beállítása" -#: vm/operations.py:998 +#: vm/operations.py:999 msgid "" "Renewing the machine with the selected lease would result in its delete time" " get earlier than before." @@ -6742,17 +6787,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:1019 +#: vm/operations.py:1020 #, 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:1027 +#: vm/operations.py:1028 msgid "emergency state change" msgstr "vész-állapotváltás" -#: vm/operations.py:1028 +#: vm/operations.py:1029 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 " @@ -6763,15 +6808,15 @@ 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:1041 +#: vm/operations.py:1042 msgid "Activity is forcibly interrupted." msgstr "A tevékenység erőszakos megszakításra került." -#: vm/operations.py:1056 +#: vm/operations.py:1057 msgid "redeploy" msgstr "újbóli létrehozás" -#: vm/operations.py:1057 +#: vm/operations.py:1058 msgid "" "Change the virtual machine state to NOSTATE and redeploy the VM. This " "operation allows starting machines formerly running on a failed node." @@ -6780,57 +6825,57 @@ msgstr "" "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:1093 +#: vm/operations.py:1094 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:1121 +#: vm/operations.py:1122 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:1131 +#: vm/operations.py:1132 msgid "You cannot reset a disabled or online node." msgstr "Tiltott vagy elérhető csomópont resetelése nem lehetséges." -#: vm/operations.py:1136 +#: vm/operations.py:1137 #, python-format msgid "redeploy %(instance)s (%(pk)s)" msgstr "%(instance)s (%(pk)s) újbóli létrehozása" -#: vm/operations.py:1149 +#: vm/operations.py:1150 msgid "flush" msgstr "ürítés" -#: vm/operations.py:1150 +#: vm/operations.py:1151 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:1160 +#: vm/operations.py:1161 #, python-format msgid "migrate %(instance)s (%(pk)s)" msgstr "%(instance)s (%(pk)s) migrálása" -#: vm/operations.py:1169 +#: vm/operations.py:1170 msgid "activate" msgstr "aktiválás" -#: vm/operations.py:1170 +#: vm/operations.py:1171 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:1178 +#: vm/operations.py:1179 msgid "You cannot activate an active node." msgstr "Aktív csomópont aktiválása nem lehetséges." -#: vm/operations.py:1190 +#: vm/operations.py:1191 msgid "passivate" msgstr "passziválás" -#: vm/operations.py:1191 +#: vm/operations.py:1192 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 " @@ -6839,31 +6884,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:1199 +#: vm/operations.py:1200 msgid "You cannot passivate a passive node." msgstr "Passzív csomópont passziválása nem lehetséges." -#: vm/operations.py:1212 +#: vm/operations.py:1213 msgid "disable" msgstr "tiltás" -#: vm/operations.py:1213 +#: vm/operations.py:1214 msgid "Disable node." msgstr "Csomópont tiltása." -#: vm/operations.py:1220 +#: vm/operations.py:1221 msgid "You cannot disable a disabled node." msgstr "Tiltott csomópont tiltása nem lehetséges." -#: vm/operations.py:1223 +#: vm/operations.py:1224 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:1236 +#: vm/operations.py:1237 msgid "update node" msgstr "csomópont frissítése" -#: vm/operations.py:1237 +#: vm/operations.py:1238 msgid "" "Upgrade or install node software (vmdriver, agentdriver, monitor-client) " "with Salt." @@ -6871,41 +6916,41 @@ msgstr "" "Csomópont frissítése vagy telepítése (vmdriver, agentdriver, monitor-client)" " Salt segítségével." -#: vm/operations.py:1255 +#: vm/operations.py:1256 #, python-format msgid "No minions matched the target (%(target)s). Data: (%(data)s)" msgstr "" -#: vm/operations.py:1260 vm/operations.py:1271 +#: vm/operations.py:1261 vm/operations.py:1272 #, python-format msgid "Unhandled exception: %(msg)s" msgstr "Kezeletlen kivétel: %(msg)s" -#: vm/operations.py:1267 +#: vm/operations.py:1268 msgid "upgrade packages" msgstr "csomagok frissítése" -#: vm/operations.py:1282 +#: vm/operations.py:1283 #, python-format msgid "Upgraded: %(upgraded)s, Installed: %(installed)s, Removed: %(removed)s" msgstr "" "Frissítve: %(upgraded)s, Telepítve: %(installed)s, Törölve: %(removed)s" -#: vm/operations.py:1295 +#: vm/operations.py:1296 #, python-format msgid "Changes: %(changes)s Comment: %(comment)s" msgstr "Változások: %(changes)s Megjegyzés: %(comment)s" -#: vm/operations.py:1303 +#: vm/operations.py:1304 #, python-format msgid "Failed: %(failed)s" msgstr "Meghiúsult: %(failed)s" -#: vm/operations.py:1309 +#: vm/operations.py:1310 msgid "screenshot" msgstr "képernyőkép" -#: vm/operations.py:1310 +#: vm/operations.py:1311 msgid "" "Get a screenshot about the virtual machine's console. A key will be pressed " "on the keyboard to stop screensaver." @@ -6913,11 +6958,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:1322 +#: vm/operations.py:1323 msgid "recover" msgstr "visszaállítás" -#: vm/operations.py:1323 +#: vm/operations.py:1324 msgid "" "Try to recover virtual machine disks from destroyed state. Network resources" " (allocations) are already lost, so you will have to manually add interfaces" @@ -6927,19 +6972,19 @@ 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:1340 +#: vm/operations.py:1341 msgid "recover instance" msgstr "példány helyreállítása" -#: vm/operations.py:1363 +#: vm/operations.py:1364 msgid "resources change" msgstr "erőforrások módosítása" -#: vm/operations.py:1364 +#: vm/operations.py:1365 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:1391 +#: vm/operations.py:1392 #, python-format msgid "" "Priority: %(priority)s, Num cores: %(num_cores)s, Ram size: %(ram_size)s" @@ -6947,11 +6992,11 @@ msgstr "" "Prioritás: %(priority)s, magok száma: %(num_cores)s, memória mérete: " "%(ram_size)s" -#: vm/operations.py:1400 +#: vm/operations.py:1401 msgid "password reset" msgstr "jelszó visszaállítása" -#: vm/operations.py:1401 +#: vm/operations.py:1402 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 " @@ -6961,11 +7006,11 @@ 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:1425 +#: vm/operations.py:1426 msgid "install SSH keys" msgstr "SSH kulcs hozzáadása" -#: vm/operations.py:1426 +#: vm/operations.py:1427 msgid "" "Copy your public keys to the virtual machines. Only works on UNIX-like " "operating systems." @@ -6973,56 +7018,56 @@ msgstr "" "A publikus kulcsok átmásolása a virtuális gépre. Csak UNIX-szerű operációs " "rendszereken működik." -#: vm/operations.py:1442 +#: vm/operations.py:1443 msgid "remove SSH keys" msgstr "SSH kulcs törlése" -#: vm/operations.py:1455 +#: vm/operations.py:1456 msgid "agent" msgstr "ügynök" -#: vm/operations.py:1496 +#: vm/operations.py:1497 msgid "starting" msgstr "indítás" -#: vm/operations.py:1514 +#: vm/operations.py:1515 msgid "wait agent restarting" msgstr "várakozás az ügynök újraindulására" -#: vm/operations.py:1532 +#: vm/operations.py:1533 msgid "cleanup" msgstr "takarítás" -#: vm/operations.py:1538 +#: vm/operations.py:1539 msgid "set time" msgstr "óra beállítása" -#: vm/operations.py:1549 +#: vm/operations.py:1550 msgid "set hostname" msgstr "gépnév beállítása" -#: vm/operations.py:1560 +#: vm/operations.py:1561 msgid "restart networking" msgstr "hálózat újratöltése" -#: vm/operations.py:1566 +#: vm/operations.py:1567 msgid "change ip" msgstr "IP cím beállítása" -#: vm/operations.py:1581 +#: vm/operations.py:1582 msgid "update agent" msgstr "ügynök frissítése" -#: vm/operations.py:1587 +#: vm/operations.py:1588 #, python-format msgid "update agent to %(version)s" msgstr "ügynökfrissítés erre: %(version)s" -#: vm/operations.py:1670 +#: vm/operations.py:1671 msgid "mount store" msgstr "tárhely csatolása" -#: vm/operations.py:1672 +#: vm/operations.py:1673 msgid "" "This operation attaches your personal file store. Other users who have " "access to this machine can see these files as well." @@ -7030,28 +7075,28 @@ 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/operations.py:1706 +#: vm/operations.py:1707 msgid "attach disk" msgstr "lemez csatolása" -#: vm/operations.py:1717 +#: vm/operations.py:1718 msgid "Resource was not found." msgstr "Nem található az erőforrás." -#: vm/operations.py:1718 +#: vm/operations.py:1719 #, python-format msgid "Resource was not found. %(exception)s" msgstr "Nem található az erőforrás. %(exception)s" -#: vm/operations.py:1727 +#: vm/operations.py:1728 msgid "detach disk" msgstr "lemez leválasztása" -#: vm/operations.py:1742 +#: vm/operations.py:1743 msgid "attach network" msgstr "hálózat csatolása" -#: vm/operations.py:1749 +#: vm/operations.py:1750 msgid "detach network" msgstr "hálózat lecsatolása" @@ -7091,6 +7136,32 @@ msgstr "" msgid "x" msgstr "x" +#~ msgid "How can I portforward?" +#~ msgstr "Hogyan tudok porttovábbítást beállítani?" + +#~ msgid "How can I search for VMs?" +#~ msgstr "Hogyan kereshetek a VM-ek között?" + +#~ msgid "Templates box" +#~ msgstr "Sablonok" + +#~ msgid "How can I expand the VM’s expiration date?" +#~ msgstr "Hogyan tudom növelni a VM lejárati idejét?" + +#~ msgid "File management" +#~ msgstr "Fájlkezelés" + +#~ msgid "How can I give the template to other user?" +#~ msgstr "Hogyan tudom egy másik felhasználónak adni a sablont?" + +#~ msgid "How can I give access to users or groups to the template?" +#~ msgstr "" +#~ "Hogyan tudok a sablonhoz hozzáférést adni a felhasználóknak vagy " +#~ "csoportoknak?" + +#~ msgid "How can I change connection template?" +#~ msgstr "Hogyan tudok változtatni parancssablont?" + #~ msgid "How can I get acces to a template?" #~ msgstr "Változtathatja egy sablon erőforrásait." @@ -7590,4 +7661,4 @@ msgstr "x" #~ msgstr "Igen, törlés" #~ msgid "?" -#~ msgstr "?" +#~ msgstr "?" \ No newline at end of file diff --git a/circle/manager/scheduler.py b/circle/manager/scheduler.py index adb3c80..05f3217 100644 --- a/circle/manager/scheduler.py +++ b/circle/manager/scheduler.py @@ -55,8 +55,8 @@ def select_node(instance, nodes): ''' # check required traits nodes = [n for n in nodes - if n.schedule_enabled and n.online - and has_traits(instance.req_traits.all(), n)] + if n.schedule_enabled and n.online and + has_traits(instance.req_traits.all(), n)] if not nodes: logger.warning('select_node: no usable node for %s', unicode(instance)) raise TraitsUnsatisfiableException() diff --git a/circle/monitor/tasks/local_periodic_tasks.py b/circle/monitor/tasks/local_periodic_tasks.py index 3b0d055..07b9b19 100644 --- a/circle/monitor/tasks/local_periodic_tasks.py +++ b/circle/monitor/tasks/local_periodic_tasks.py @@ -54,7 +54,7 @@ def measure_response_time(): @celery.task(ignore_result=True) def check_celery_queues(): - graphite_string = lambda component, hostname, celery, is_alive, time: ( + def graphite_string(component, hostname, celery, is_alive, time): return ( "%s.%s.celery-queues.%s %d %s" % ( component, hostname, celery, 1 if is_alive else 0, time) ) @@ -92,7 +92,7 @@ def check_celery_queues(): @celery.task(ignore_result=True) def instance_per_template(): - graphite_string = lambda pk, state, val, time: ( + def graphite_string(pk, state, val, time): return ( "template.%d.instances.%s %d %s" % ( pk, state, val, time) ) @@ -111,7 +111,7 @@ def instance_per_template(): @celery.task(ignore_result=True) def allocated_memory(): - graphite_string = lambda hostname, val, time: ( + def graphite_string(hostname, val, time): return ( "circle.%s.memory.allocated %d %s" % ( hostname, val, time) ) diff --git a/circle/network/views.py b/circle/network/views.py index 3cbe926..8c99a80 100644 --- a/circle/network/views.py +++ b/circle/network/views.py @@ -979,8 +979,8 @@ def remove_switch_port_device(request, **kwargs): def add_switch_port_device(request, **kwargs): device_name = request.POST.get('device_name') - if (request.method == "POST" and device_name and len(device_name) > 0 - and EthernetDevice.objects.filter(name=device_name).count() == 0): + if (request.method == "POST" and device_name and len(device_name) > 0 and + EthernetDevice.objects.filter(name=device_name).count() == 0): switch_port = SwitchPort.objects.get(pk=kwargs['pk']) new_device = EthernetDevice(name=device_name, switch_port=switch_port) diff --git a/circle/request/forms.py b/circle/request/forms.py index 435e59b..4b76207 100644 --- a/circle/request/forms.py +++ b/circle/request/forms.py @@ -22,6 +22,8 @@ from django.utils.translation import ugettext_lazy as _ from django.template import RequestContext from django.template.loader import render_to_string +from sizefield.widgets import FileSizeWidget +from sizefield.utils import filesizeformat from crispy_forms.helper import FormHelper from crispy_forms.layout import Submit @@ -70,34 +72,32 @@ class InitialFromFileMixin(object): RequestContext(request, {}), ) - def clean(self): - cleaned_data = super(InitialFromFileMixin, self).clean() - if cleaned_data['message'].strip() == self.initial['message'].strip(): - raise ValidationError( - _("Fill in the message."), - code="invalid") - return cleaned_data + def clean_message(self): + message = self.cleaned_data['message'] + if message.strip() == self.initial['message'].strip(): + raise ValidationError(_("Fill in the message."), code="invalid") + return message.strip() class TemplateRequestForm(InitialFromFileMixin, Form): + message = CharField(widget=Textarea, label=_("Message")) template = ModelChoiceField(TemplateAccessType.objects.all(), label=_("Template share")) level = ChoiceField(TemplateAccessAction.LEVELS, widget=RadioSelect, initial=TemplateAccessAction.LEVELS.user) - message = CharField(widget=Textarea, label=_("Message")) initial_template = "request/initials/template.html" class LeaseRequestForm(InitialFromFileMixin, Form): lease = ModelChoiceField(LeaseType.objects.all(), label=_("Lease")) - message = CharField(widget=Textarea) + message = CharField(widget=Textarea, label=_("Message")) initial_template = "request/initials/lease.html" class ResourceRequestForm(InitialFromFileMixin, VmResourcesForm): - message = CharField(widget=Textarea) + message = CharField(widget=Textarea, label=_("Message")) initial_template = "request/initials/resources.html" @@ -110,3 +110,28 @@ class ResourceRequestForm(InitialFromFileMixin, VmResourcesForm): raise ValidationError( _("You haven't changed any of the resources."), code="invalid") + + +class ResizeRequestForm(InitialFromFileMixin, Form): + message = CharField(widget=Textarea, label=_("Message")) + size = CharField(widget=FileSizeWidget, label=_('Size'), + help_text=_('Size to resize the disk in bytes or with' + ' units like MB or GB.')) + + initial_template = "request/initials/resize.html" + + def __init__(self, *args, **kwargs): + self.disk = kwargs.pop("disk") + super(ResizeRequestForm, self).__init__(*args, **kwargs) + + def clean_size(self): + cleaned_data = super(ResizeRequestForm, self).clean() + disk = self.disk + size_in_bytes = cleaned_data.get("size") + + if not size_in_bytes.isdigit() and len(size_in_bytes) > 0: + raise ValidationError(_("Invalid format, you can use GB or MB!")) + if int(size_in_bytes) < int(disk.size): + raise ValidationError(_("Disk size must be greater than the actual" + "size (%s).") % filesizeformat(disk.size)) + return size_in_bytes diff --git a/circle/request/migrations/0004_auto_20150629_1605.py b/circle/request/migrations/0004_auto_20150629_1605.py new file mode 100644 index 0000000..865e22b --- /dev/null +++ b/circle/request/migrations/0004_auto_20150629_1605.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import sizefield.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('vm', '0002_interface_model'), + ('storage', '0002_disk_bus'), + ('request', '0003_auto_20150410_1917'), + ] + + operations = [ + migrations.CreateModel( + name='DiskResizeAction', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('size', sizefield.models.FileSizeField(default=None, null=True)), + ('disk', models.ForeignKey(to='storage.Disk')), + ('instance', models.ForeignKey(to='vm.Instance')), + ], + options={ + 'abstract': False, + }, + ), + migrations.AlterField( + model_name='request', + name='type', + field=models.CharField(max_length=10, choices=[(b'resource', 'resource request'), (b'lease', 'lease request'), (b'template', 'template access request'), (b'resize', 'disk resize request')]), + ), + ] diff --git a/circle/request/models.py b/circle/request/models.py index 81d3229..4d4eabb 100644 --- a/circle/request/models.py +++ b/circle/request/models.py @@ -32,10 +32,14 @@ from django.utils.translation import ( from django.core.urlresolvers import reverse import requests +from sizefield.models import FileSizeField from model_utils.models import TimeStampedModel from model_utils import Choices +from sizefield.utils import filesizeformat from vm.models import Instance, InstanceTemplate, Lease +from vm.operations import ResourcesOperation, ResizeDiskOperation +from storage.models import Disk logger = logging.getLogger(__name__) @@ -49,6 +53,9 @@ class RequestAction(Model): def accept_msg(self): raise NotImplementedError + def is_acceptable(self): + return True + class Meta: abstract = True @@ -77,6 +84,7 @@ class Request(TimeStampedModel): ('resource', _('resource request')), ('lease', _("lease request")), ('template', _("template access request")), + ('resize', _("disk resize request")), ) type = CharField(choices=TYPES, max_length=10) message = TextField(verbose_name=_("Message")) @@ -99,7 +107,8 @@ class Request(TimeStampedModel): return { 'resource': "tasks", 'lease': "clock-o", - 'template': "puzzle-piece" + 'template': "puzzle-piece", + 'resize': "arrows-alt", }.get(self.type) def get_effect(self): @@ -143,6 +152,10 @@ class Request(TimeStampedModel): decline_msg, url=self.get_absolute_url(), reason=self.reason, ) + @property + def is_acceptable(self): + return self.action.is_acceptable() + class LeaseType(RequestType): lease = ForeignKey(Lease, verbose_name=_("Lease")) @@ -200,6 +213,9 @@ class ResourceChangeAction(RequestAction): 'priority': self.priority, } + def is_acceptable(self): + return self.instance.status in ResourcesOperation.accept_states + class ExtendLeaseAction(RequestAction): instance = ForeignKey(Instance) @@ -246,6 +262,30 @@ class TemplateAccessAction(RequestAction): ) % ", ".join([x.name for x in self.template_type.templates.all()]) +class DiskResizeAction(RequestAction): + instance = ForeignKey(Instance) + disk = ForeignKey(Disk) + size = FileSizeField(null=True, default=None) + + def accept(self, user): + self.instance.resize_disk(disk=self.disk, size=self.size, user=user) + + @property + def accept_msg(self): + return _( + 'The disk <em class="text-muted">%(disk_name)s (#%(id)d)</em> of ' + '<a href="%(url)s">%(vm_name)s</a> got resized. ' + 'The new size is: %(bytes)d bytes (%(size)s).' + ) % {'disk_name': self.disk.name, 'id': self.disk.id, + 'url': self.instance.get_absolute_url(), + 'vm_name': self.instance.name, + 'bytes': self.size, 'size': filesizeformat(self.size), + } + + def is_acceptable(self): + return self.instance.status in ResizeDiskOperation.accept_states + + def send_notifications(sender, instance, created, **kwargs): if not created: return diff --git a/circle/request/templates/request/_request-resize-form.html b/circle/request/templates/request/_request-resize-form.html new file mode 100644 index 0000000..7bf727c --- /dev/null +++ b/circle/request/templates/request/_request-resize-form.html @@ -0,0 +1,26 @@ +{% load i18n %} +{% load crispy_forms_tags %} +{% load sizefieldtags %} + + +<dl> + <dt>{% trans "Virtual machine" %}</dt> + <dd><a href="{{ vm.get_absolute_url }}">{{ vm.name }}</a></dd> + <dt>{% trans "Disk" %}</dt> + <dd> + {% if request.user.is_superuser %} + <a href="{{ disk.get_absolute_url }}">{{ disk.name }} (#{{ disk.id }})</a> + {% else %} + {{ disk.name }} (#{{ disk.id }}) + {% endif %} + - {{ disk.size|filesize }} + </dd> +</dl> + +<form action="{% url "request.views.request-resize" vm_pk=vm.pk disk_pk=disk.pk %}" method="POST"> + {% include "display-form-errors.html" %} + {% csrf_token %} + {{ form.size|as_crispy_field }} + {{ form.message|as_crispy_field }} + <input type="submit" class="btn btn-primary" id="op-form-send"/> +</form> diff --git a/circle/request/templates/request/detail.html b/circle/request/templates/request/detail.html index 5092377..7dbb006 100644 --- a/circle/request/templates/request/detail.html +++ b/circle/request/templates/request/detail.html @@ -3,6 +3,7 @@ {% load i18n %} {% load render_table from django_tables2 %} {% load arrowfilter %} +{% load sizefieldtags %} {% block title-page %}{% trans "Request" %}{% endblock %} @@ -65,6 +66,15 @@ <dd>{{ action.get_readable_level }}</dd> </dl> {% elif object.type == "resource" %} + {% if not is_acceptable %} + <div class="alert alert-warning"> + {% blocktrans %} + To change the resources the virtual machine must be in one of the following states: + STOPPED, PENDING, RUNNING. If the virtual machine is running it will be + automatically stopped when accepting the request. + {% endblocktrans %} + </div> + {% endif %} <dl> <dt>{% trans "VM name" %}</dt> <dd><a href="{{ action.instance.get_absolute_url }}">{{ action.instance.name }}</a></dd> @@ -74,7 +84,7 @@ {{ action.instance.get_status_display|upper }} </dd> <dt>{% trans "VM description" %}</dt> - <dd>{{ action.instance.description }}</dd> + <dd>{{ action.instance.description|default:"-" }}</dd> <dt> {% trans "Priority" %} <span class="text-muted" style="font-weight: normal;">{% trans "(old values in parentheses)" %}</span> @@ -85,8 +95,39 @@ <dt>{% trans "Ram size" %}</dt> <dd>{{ action.ram_size }} ({{ action.instance.ram_size }}) MiB</dd> </dl> + {% elif object.type == "resize" %} + {% if not is_acceptable %} + <div class="alert alert-warning"> + {% trans "To resize the disk the virtual machine must be in RUNNING state." %} + </div> + {% endif %} + <dl> + <dt>{% trans "VM name" %}</dt> + <dd><a href="{{ action.instance.get_absolute_url }}">{{ action.instance.name }}</a></dd> + <dt>{% trans "Status" %}</dt> + <dd> + <i class="fa {{ action.instance.get_status_icon }}"></i> + {{ action.instance.get_status_display|upper }} + </dd> + <dt>{% trans "VM description" %}</dt> + <dd>{{ action.instance.description|default:"-" }}</dd> + <dt>{% trans "Disk" %}</dt> + <dd> + {% if request.user.is_superuser %} + <a href="{{ action.disk.get_absolute_url }}"> + {{ action.disk.name }} (#{{ action.disk.id}}) + </a> + {% else %} + {{ action.disk.name }} (#{{ action.disk.id}})</dd> + {% endif %} + </dd> + <dt>{% trans "Current size" %}</dt> + <dd>{{ action.disk.size|filesize}} ({{ action.disk.size }} bytes)</dd> + <dt>{% trans "Requested size" %}</dt> + <dd>{{ action.size|filesize}} ({{ action.size }} bytes)</dd> + </dl> {% else %} - hacks!!! + Are you adding a new action type? {% endif %} {% if object.status == "PENDING" and request.user.is_superuser %} @@ -103,7 +144,7 @@ {% trans "Decline" %} </button> </form> - {% if object.type == "resource" and action.instance.status not in accept_states %} + {% if not is_acceptable %} {% trans "You can't accept this request because of the VM's state." %} {% else %} <form method="POST"> diff --git a/circle/request/templates/request/initials/resize.html b/circle/request/templates/request/initials/resize.html new file mode 100644 index 0000000..a1a45c3 --- /dev/null +++ b/circle/request/templates/request/initials/resize.html @@ -0,0 +1,7 @@ +{% spaceless %} +{% if LANGUAGE_CODE == "en" %} +Why do you need a bigger disk? +{% else %} {# place your translations here #} +Why do you need a bigger disk? +{% endif %} +{% endspaceless %} diff --git a/circle/request/urls.py b/circle/request/urls.py index 14ff19b..c69aa8d 100644 --- a/circle/request/urls.py +++ b/circle/request/urls.py @@ -23,7 +23,7 @@ from .views import ( LeaseTypeCreate, LeaseTypeDetail, TemplateAccessTypeCreate, TemplateAccessTypeDetail, TemplateRequestView, LeaseRequestView, ResourceRequestView, - LeaseTypeDelete, TemplateAccessTypeDelete, + LeaseTypeDelete, TemplateAccessTypeDelete, ResizeRequestView, ) urlpatterns = patterns( @@ -60,4 +60,6 @@ urlpatterns = patterns( name="request.views.request-lease"), url(r'resource/(?P<vm_pk>\d+)/$', ResourceRequestView.as_view(), name="request.views.request-resource"), + url(r'resize/(?P<vm_pk>\d+)/(?P<disk_pk>\d+)/$', + ResizeRequestView.as_view(), name="request.views.request-resize"), ) diff --git a/circle/request/views.py b/circle/request/views.py index b218a54..7e3c6f8 100644 --- a/circle/request/views.py +++ b/circle/request/views.py @@ -19,27 +19,29 @@ from __future__ import unicode_literals, absolute_import from django.views.generic import ( UpdateView, TemplateView, DetailView, CreateView, FormView, DeleteView, ) +from django.contrib import messages from django.contrib.messages.views import SuccessMessageMixin from django.shortcuts import redirect, get_object_or_404 from django.core.exceptions import PermissionDenied, SuspiciousOperation from django.core.urlresolvers import reverse from django.utils.translation import ugettext as _ +from django.http import JsonResponse from braces.views import SuperuserRequiredMixin, LoginRequiredMixin from django_tables2 import SingleTableView from request.models import ( Request, TemplateAccessType, LeaseType, TemplateAccessAction, - ExtendLeaseAction, ResourceChangeAction, + ExtendLeaseAction, ResourceChangeAction, DiskResizeAction ) +from storage.models import Disk from vm.models import Instance -from vm.operations import ResourcesOperation from request.tables import ( RequestTable, TemplateAccessTypeTable, LeaseTypeTable, ) from request.forms import ( LeaseTypeForm, TemplateAccessTypeForm, TemplateRequestForm, - LeaseRequestForm, ResourceRequestForm, + LeaseRequestForm, ResourceRequestForm, ResizeRequestForm, ) @@ -93,7 +95,7 @@ class RequestDetail(LoginRequiredMixin, DetailView): context = super(RequestDetail, self).get_context_data(**kwargs) context['action'] = request.action - context['accept_states'] = ResourcesOperation.accept_states + context['is_acceptable'] = request.is_acceptable # workaround for http://git.io/vIIYi context['request'] = self.request @@ -167,6 +169,7 @@ class RequestTypeList(LoginRequiredMixin, SuperuserRequiredMixin, class TemplateRequestView(LoginRequiredMixin, FormView): form_class = TemplateRequestForm template_name = "request/request-template.html" + success_message = _("Request successfully sent.") def get_form_kwargs(self): kwargs = super(TemplateRequestView, self).get_form_kwargs() @@ -192,7 +195,8 @@ class TemplateRequestView(LoginRequiredMixin, FormView): ) req.save() - return redirect("/") + messages.success(self.request, self.success_message) + return redirect(reverse("dashboard.index")) class VmRequestMixin(LoginRequiredMixin, object): @@ -224,6 +228,7 @@ class LeaseRequestView(VmRequestMixin, FormView): form_class = LeaseRequestForm template_name = "request/request-lease.html" user_level = "operator" + success_message = _("Request successfully sent.") def form_valid(self, form): data = form.cleaned_data @@ -244,6 +249,7 @@ class LeaseRequestView(VmRequestMixin, FormView): ) req.save() + messages.success(self.request, self.success_message) return redirect(vm.get_absolute_url()) @@ -251,6 +257,7 @@ class ResourceRequestView(VmRequestMixin, FormView): form_class = ResourceRequestForm template_name = "request/request-resource.html" user_level = "user" + success_message = _("Request successfully sent.") def get_form_kwargs(self): kwargs = super(ResourceRequestView, self).get_form_kwargs() @@ -287,4 +294,60 @@ class ResourceRequestView(VmRequestMixin, FormView): ) req.save() + messages.success(self.request, self.success_message) return redirect(vm.get_absolute_url()) + + +class ResizeRequestView(VmRequestMixin, FormView): + form_class = ResizeRequestForm + template_name = "request/_request-resize-form.html" + user_level = "owner" + success_message = _("Request successfully sent.") + + def get_disk(self, *args, **kwargs): + disk = get_object_or_404(Disk, pk=self.kwargs['disk_pk']) + if disk not in self.get_vm().disks.all(): + raise SuspiciousOperation + return disk + + def get_form_kwargs(self): + kwargs = super(ResizeRequestView, self).get_form_kwargs() + kwargs['disk'] = self.get_disk() + return kwargs + + def get_template_names(self): + if self.request.is_ajax(): + return ['dashboard/_modal.html'] + else: + return ['dashboard/_base.html'] + + def get_context_data(self, **kwargs): + context = super(ResizeRequestView, self).get_context_data(**kwargs) + context['disk'] = self.get_disk() + context['template'] = self.template_name + context['box_title'] = context['title'] = _("Disk resize request") + context['ajax_title'] = True + return context + + def form_valid(self, form): + disk = self.get_disk() + if not disk.is_resizable: + raise SuspiciousOperation + + vm = self.get_vm() + data = form.cleaned_data + user = self.request.user + + dra = DiskResizeAction(instance=vm, disk=disk, size=data['size']) + dra.save() + + req = Request(user=user, message=data['message'], action=dra, + type=Request.TYPES.resize) + req.save() + + if self.request.is_ajax(): + return JsonResponse({'success': True, + 'messages': [self.success_message]}) + else: + messages.success(self.request, self.success_message) + return redirect(vm.get_absolute_url()) diff --git a/circle/storage/models.py b/circle/storage/models.py index fb2b6f6..0b5431d 100644 --- a/circle/storage/models.py +++ b/circle/storage/models.py @@ -28,6 +28,7 @@ from celery.contrib.abortable import AbortableAsyncResult from django.db.models import (Model, BooleanField, CharField, DateTimeField, ForeignKey) from django.core.exceptions import ObjectDoesNotExist +from django.core.urlresolvers import reverse from django.utils import timezone from django.utils.translation import ugettext_lazy as _, ugettext_noop from model_utils.models import TimeStampedModel @@ -535,3 +536,10 @@ class Disk(TimeStampedModel): disk.is_ready = True disk.save() return disk + + def get_absolute_url(self): + return reverse('dashboard.views.disk-detail', kwargs={'pk': self.pk}) + + @property + def is_resizable(self): + return self.type in ('qcow2-norm', 'raw-rw') diff --git a/circle/templates/info/help/faq.html b/circle/templates/info/help/faq.html index cd7168a..6ec8bc9 100644 --- a/circle/templates/info/help/faq.html +++ b/circle/templates/info/help/faq.html @@ -29,7 +29,7 @@ </blockquote> -<h3 id="how-can-i-portforward">{% trans "How can I open ports?" %}</h3> +<h3 id="how-can-i-open-ports">{% trans "How can I open ports?" %}</h3> <blockquote> <ol> diff --git a/circle/templates/info/help/faq_toc.html b/circle/templates/info/help/faq_toc.html index 134c7c9..4343596 100644 --- a/circle/templates/info/help/faq_toc.html +++ b/circle/templates/info/help/faq_toc.html @@ -4,7 +4,7 @@ <li><a href="#how-can-i-create-a-vm-and-give-to-another-user">{% trans "How can I create a VM and give to another user?" %}</a></li> - <li><a href="#how-can-i-portforward">{% trans "How can I portforward?" %}</a></li> + <li><a href="#how-can-i-open-ports">{% trans "How can I open ports?" %}</a></li> <li><a href="#my-machines-lease-is-short-how-can-i-extend-it">{% trans "My machine’s lease is short. How can I extend it?" %}</a></li> <li><a href="#how-can-i-have-more-cpumemory">{% trans "How can I have more CPU/memory?" %}</a></li> diff --git a/circle/templates/info/help/overview.html b/circle/templates/info/help/overview.html index 1a2349a..8e6e2b1 100644 --- a/circle/templates/info/help/overview.html +++ b/circle/templates/info/help/overview.html @@ -348,32 +348,34 @@ <blockquote> <p> <h4>{% trans "Architecture" %}</h4> - The user can choose the template's architecture (x86 or x86-64). + {% trans "The user can choose the template's architecture (x86 or x86-64)." %} </p> <p> <h4>{% trans "Access method" %}</h4> - The default access method is modifiable. Currently SSH, RDP and NX are supported. + {% trans "The default access method is modifiable. Currently SSH, RDP and NX are supported." %} </p> <p> <h4>{% trans "Boot menu" %} </h4> - Check it to turn on the boot menu. + {% trans "Check it to turn on the boot menu." %} </p> <p> <h4>{% trans "Traits" %}</h4> - By adding or removing traits we can guarantee specific features the host node will have (like <em>GPU</em>) for the virtual machine. + {% trans "By adding or removing traits we can guarantee specific features the host node will have (like <em>GPU</em>) for the virtual machine." %} </p> <p> <h4>{% trans "Operating system" %}</h4> - The name of the operating system. + {% trans "The name of the operating system." %} </p> <p> <h4>{% trans "Agent" %}</h4> - Check this if the machine has agent installed and the manager should wait for its start. + {% trans "Check this if the machine has agent installed and the manager should wait for its start." %} </p> <p> <h4>{% trans "Raw data" %}</h4> - The CIRCLE Cloud is using libvirt, so the owner can customize the running VM's options here by - <a href="https://libvirt.org/formatdomain.html">libvirt domain parameters</a>. + {% blocktrans %} + The CIRCLE Cloud is using libvirt, so the owner can customize the running VM's options here by + <a href="https://libvirt.org/formatdomain.html">libvirt domain parameters</a>. + {% endblocktrans %} </p> </blockquote> @@ -387,7 +389,7 @@ - <h4 id="how-can-i-give-access-to-users-or-groups-to-the-template">{% trans "How can I grant access for users or groups to the template?" %}</h4> + <h4 id="how-can-i-grant-access-for-users-or-groups-to-the-template">{% trans "How can I grant access for users or groups to the template?" %}</h4> <blockquote> <p> {% blocktrans %} diff --git a/circle/templates/info/help/overview_toc.html b/circle/templates/info/help/overview_toc.html index c7e0ea2..d1a447f 100644 --- a/circle/templates/info/help/overview_toc.html +++ b/circle/templates/info/help/overview_toc.html @@ -10,10 +10,8 @@ <ul> <li><a href="#how-can-i-create-a-vm">{% trans "How can I create a VM?" %}</a></li> <li><a href="#how-can-i-mark-frequently-used-vms">{% trans "How can I mark frequently used VMs?" %}</a></li> - <li><a href="#how-can-i-search-for-vms">{% trans "How can I search for VMs?" %}</a></li> </ul> </li> - <li><a href="#templates-box">{% trans "Templates box" %}</a></li> </ul> </li> @@ -37,8 +35,7 @@ <li> <ul><a href="#home">{% trans "Home" %}</a> <li><a href="#expiration">{% trans "Expiration" %}</a></li> - <li><a href="#how-can-i-expand-the-vms-expiration-date">{% trans "How can I expand the VM’s expiration date?" %}</a></li> - <li><a href="#file-management">{% trans "File management" %}</a></li> + <li><a href="#how-can-i-extend-the-vms-expiration-date">{% trans "How can I extend the VM's expiration date?" %}</a></li> <li><a href="#how-can-i-share-previously-uploaded-files-with-the-vm">{% trans "How can I share previously uploaded files with the VM?" %}</a></li> </ul> </li> @@ -68,9 +65,7 @@ <ul><a href="#templates">{% trans "Templates" %}</a> <li><a href="#how-can-i-create-templates">{% trans "How can I create templates?" %}</a></li> <li><a href="#what-kind-of-options-are-customizable-in-the-template">{% trans "What kind of options are customizable in the template?" %}</a></li> - <li><a href="#how-can-i-change-the-expiration-of-the-templates-vms">{% trans "How can I change the expiration of the tempalte's VMs" %}</a></li> - <li><a href="#how-can-i-give-the-template-to-other-user">{% trans "How can I give the template to other user?" %}</a></li> - <li><a href="#how-can-i-give-access-to-users-or-groups-to-the-template">{% trans "How can I give access to users or groups to the template?"%}</a></li> + <li><a href="#how-can-i-grant-access-for-users-or-groups-to-the-template">{% trans "How can I grant access for users or groups to the template?"%}</a></li> </ul> </li> @@ -92,7 +87,6 @@ <ul><a href="#profile">{% trans "Profile" %}</a> <li><a href="#how-can-i-change-my-password">{% trans "How can I change my password?" %}</a></li> <li><a href="#how-can-i-store-public-keys-on-the-vms">{% trans "How can I store public keys on the VMs?" %}</a></li> - <li><a href="#how-can-i-change-connection-template">{% trans "How can I change connection template?" %}</a></li> </ul> </li> diff --git a/circle/templates/info/resize.html b/circle/templates/info/resize.html new file mode 100644 index 0000000..bc1b8e1 --- /dev/null +++ b/circle/templates/info/resize.html @@ -0,0 +1,534 @@ +{% extends "dashboard/base.html" %} + +{% load staticfiles %} +{% load i18n %} + +{% block title-page %}{% trans "Resize how-to" %}{% endblock %} +{% block content %} +<div class="row" id="resize-help"> + <div class="col-lg-12"> + <div class="page-header"> + <h1 id="disk-linux"> + <i class="fa fa-linux"></i> + {% trans "Expanding disk on Linux" %} + </h1> + </div> + + <p> + {% blocktrans %} + If you don't have enogh space on your virtual machine you can ask for more. + After a request has been made an administrator can extend your HDD. + If the request is granted you have to manually rescan + and extend a logical volume on your machine to acquire the extra space. + To do so you need root access/administrator rights. + {% endblocktrans %} + </p> + + <ol> + <li> + {% trans "Ask the administrator for more space. After it has been granted do the following steps." %} + </li> + <li> + {% blocktrans %} + You can check how much free space is left on your machine + (on Debian based distributions like Ubuntu) with the + <strong><code>df -h</code></strong> command. + As you can see below we need more space on + <strong>/</strong> so we will extend + <strong>/dev/mapper/cloud–x–vg-root</strong>. + {% endblocktrans %} + + <div class="panel panel-default table-responsive"> + <table class="table"> + <thead> + <tr> + <th>Filesystem</th> + <th>Size</th> + <th>Used</th> + <th>Avail</th> + <th>Use%</th> + <th>Mounted on</th> + </tr> + </thead> + <tbody><tr> + <td><strong>/dev/mapper/cloud–x–vg-root</strong></td> + <td>39G</td> + <td>37G</td> + <td>65M</td> + <td>100%</td> + <td><strong>/</strong></td> + </tr> + <tr> + <td>none</td> + <td>4.0K</td> + <td>0</td> + <td>4.0K</td> + <td>0%</td> + <td>/sys/fs/cgroup</td> + </tr> + <tr> + <td>udev</td> + <td>487M</td> + <td>4.0K</td> + <td>487M</td> + <td>1%</td> + <td>/dev</td> + </tr> + <tr> + <td>tmpfs</td> + <td>100M</td> + <td>368K</td> + <td>100M</td> + <td>1%</td> + <td>/run</td> + </tr> + <tr> + <td>none</td> + <td>5.0M</td> + <td>0</td> + <td>5.0M</td> + <td>0%</td> + <td>/run/lock</td> + </tr> + <tr> + <td>none</td> + <td>498M</td> + <td>0</td> + <td>498M</td> + <td>0%</td> + <td>/run/shm</td> + </tr> + <tr> + <td>none</td> + <td>100M</td> + <td>0</td> + <td>100M</td> + <td>0%</td> + <td>/run/user</td> + </tr> + <tr> + <td>/dev/vda1</td> + <td>236M</td> + <td>37M</td> + <td>187M</td> + <td>17%</td> + <td>/boot</td> + </tr> + </tbody> + </table> + </div> + </li> + <li> + {% blocktrans %} + List logical volumes and find the + <strong>VG Name</strong> + (volume group name) of + <strong>/dev/mapper/cloud–x–vg-root</strong>: + <code>lvdisplay</code></p> + {% endblocktrans %} + + <pre> + — Logical volume — + <em>LV Path /dev/cloud-x-vg/root</em> + LV Name root + <strong>VG Name cloud-x-vg</strong> + LV UUID xlGizo-eVyj-aqRn-Us7d-BRzj-dsKW-U6kp0F + LV Write Access read/write + LV Creation host, time cloud-x, 2014-07-31 13:17:53 +0200 + LV Status available + <code>#</code> open 1 + LV Size 38.76 GiB + Current LE 9923 + Segments 2 + Allocation inherit + Read ahead sectors auto + <code>-</code> currently set to 256 + Block device 252:0</pre> + + </li> + <li> + {% blocktrans %} + List physical volumes to get the + <strong>PV Name</strong> (partition name) of the + <strong>cloud-x-vg</strong> volume group: + <code>pvdisplay</code> + {% endblocktrans %} + + <pre> + — Physical volume — + <strong>PV Name /dev/vda5</strong> + <em>VG Name cloud-x-vg</em> + PV Size 39.76 GiB / not usable 2.00 MiB + Allocatable yes (but full) + PE Size 4.00 MiB + Total PE 10178 + Free PE 0 + Allocated PE 10178 + PV UUID JDp5TP-PHjT-Cgwk-MN4h-iAnk-9dfT-lYoldd</pre> + + </li> + <li> + {% blocktrans %} + List the partitions with fdisk: + <strong><code>fdisk /dev/vda</code></strong> + and press <strong>p</strong>. + This will show something similar: + {% endblocktrans %} + + <div class="panel panel-default table-responsive"> + <table class="table"> + <thead> + <tr> + <th>Device</th> + <th>Boot</th> + <th>Start</th> + <th>End</th> + <th>Blocks</th> + <th>Id</th> + <th>System</th> + </tr> + </thead> + <tbody><tr> + <td>/dev/vda1</td> + <td>*</td> + <td>2048</td> + <td>499711</td> + <td>248832</td> + <td>83</td> + <td>Linux</td> + </tr> + <tr> + <td>/dev/vda2</td> + <td></td> + <td>501758</td> + <td>83884031</td> + <td>41691137</td> + <td>5</td> + <td>Extended</td> + </tr> + <tr> + <td>/dev/vda5</td> + <td></td> + <td>501760</td> + <td>83884031</td> + <td>41691136</td> + <td>8e</td> + <td>Linux LVM</td> + </tr> + </tbody> + </table> + </div> + <p> + {% blocktrans %} + As you can see, the <strong>/dev/vda5</strong> is in the + <strong>/dev/vda2</strong> Extended partition. + To resize it we have to recreate the Extended partition. + {% endblocktrans %} + </p> + </li> + <li> + <p>{% trans "Delete the Extended partition:" %}</p> + <p> + {% blocktrans %} + Press <strong>d</strong> and the number of the partition. + In the example above the extended partition name is + <strong>vda2</strong> so press <strong>2</strong>. + {% endblocktrans %} + </p> + </li> + <li> + <p>{% trans "Create extended partition:" %}</p> + <p> + {% blocktrans %} + Press <strong>n</strong> to create new partition. + Type <strong>e</strong> to choose extended type. + Set partition number - the same as you deleted above: + <strong>2</strong>. + You can use the default starting and ending sector. + {% endblocktrans %} + </p> + </li> + <li> + <p>{% trans "Create logical partition:" %}</p> + <p> + {% blocktrans %} + Press <strong>n</strong> to create new partition. + Type <strong>l</strong> to choose logical type. + Set partition number - the same as the Linux LVM (vda5) has above: <strong>5</strong>. + You can use the default starting and ending sector. + {% endblocktrans %} + </p> + </li> + <li> + <p>{% trans "Change the logical partition’s type:" %}</p> + <p> + {% blocktrans %} + Press <strong>t</strong> to change type. + Set the logical partition’s number: <strong>5</strong> (vda5). + Type <strong>8e</strong> to choose Linux LVM type. + (to show the full list, press L). + {% endblocktrans %} + </p> + </li> + <li> + <p>{% trans "Save and exit: Press <strong>w</strong>." %}</p> + <p>{% trans "If you list the partitions again, you will see the difference:" %}</p> + + <div class="panel panel-default table-responsive"> + <table class="table"> + <thead> + <tr> + <th>Device</th> + <th>Boot</th> + <th>Start</th> + <th>End</th> + <th>Blocks</th> + <th>Id</th> + <th>System</th> + </tr> + </thead> + <tbody> + <tr> + <td>/dev/vda1</td> + <td>*</td> + <td>2048</td> + <td>499711</td> + <td>248832</td> + <td>83</td> + <td>Linux</td> + </tr> + <tr> + <td>/dev/vda2</td> + <td></td> + <td>499712</td> + <td>89338673</td> + <td><strong>44419481</strong></td> + <td>5</td> + <td>Extended</td> + </tr> + <tr> + <td>/dev/vda5</td> + <td></td> + <td>501760</td> + <td>89338673</td> + <td><strong>44418457</strong></td> + <td>8e</td> + <td>Linux LVM</td> + </tr> + </tbody> + </table> + </div> + </li> + <li> + <p> + {% trans "Reread partition table:" %} + <code>partprobe -s /dev/vda</code> + </p> + + <pre> + /dev/vda: msdos partitions 1 2 <5></pre> + + </li> + <li> + <p> + {% trans "Resize logical partition:" %} + <code>pvresize /dev/vda5</code> + </p> + + <pre> + Physical volume “/dev/vda5” changed + 1 physical volume(s) resized / 0 physical volume(s) not resized</pre> + + </li> + <li> + <p> + {% trans "Check Free PE / Size:" %} + <code>vgdisplay</code> + </p> + + <pre> + ... + Free PE / Size 666 / <strong>2.60 GiB</strong> + ...</pre> + + </li> + <li> + <p> + {% trans "Extend LVM:" %} + <code>lvextend -L</code>+<strong>2.6G</strong> + <code>/dev/mapper/cloud--x--vg-root</code> + </p> + + <pre> + Rounding size to boundary between physical extents: 2.60 GiB + Extending logical volume root to 41.36 GiB + Logical volume root successfully resized</pre> + + </li> + <li> + <p> + {% trans "Finally, resize filesystem:" %} + <code>resize2fs /dev/mapper/cloud--x--vg-root</code> + </p> + + <pre> + resize2fs 1.42.9 (4-Feb-2014) + Filesystem at /dev/mapper/cloud–x–vg-root is mounted on /; on-line resizing required + old_desc_blocks = 3, new_desc_blocks = 3 + The filesystem on /dev/mapper/cloud–x–vg-root is now 10843136 blocks long.</pre> + + </li> + </ol> + + <p> + {% blocktrans %} + The <strong><code>df -h</code></strong> will show now some free space on your <strong>/</strong>: + {% endblocktrans %} + </p> + + <div class="panel panel-default table-responsive"> + <table class="table"> + <thead> + <tr> + <th>Filesystem</th> + <th>Size</th> + <th>Used</th> + <th>Avail</th> + <th>Use%</th> + <th>Mounted on</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong>/dev/mapper/cloud–x–vg-root</strong></td> + <td><strong>41G</strong></td> + <td>37G</td> + <td><strong>2.6G</strong></td> + <td><strong>94%</strong></td> + <td><strong>/</strong></td> + </tr> + <tr> + <td>none</td> + <td>4.0K</td> + <td>0</td> + <td>4.0K</td> + <td>0%</td> + <td>/sys/fs/cgroup</td> + </tr> + <tr> + <td>udev</td> + <td>487M</td> + <td>4.0K</td> + <td>487M</td> + <td>1%</td> + <td>/dev</td> + </tr> + <tr> + <td>tmpfs</td> + <td>100M</td> + <td>368K</td> + <td>100M</td> + <td>1%</td> + <td>/run</td> + </tr> + <tr> + <td>none</td> + <td>5.0M</td> + <td>0</td> + <td>5.0M</td> + <td>0%</td> + <td>/run/lock</td> + </tr> + <tr> + <td>none</td> + <td>498M</td> + <td>0</td> + <td>498M</td> + <td>0%</td> + <td>/run/shm</td> + </tr> + <tr> + <td>none</td> + <td>100M</td> + <td>0</td> + <td>100M</td> + <td>0%</td> + <td>/run/user</td> + </tr> + <tr> + <td>/dev/vda1</td> + <td>236M</td> + <td>37M</td> + <td>187M</td> + <td>17%</td> + <td>/boot</td> + </tr> + </tbody> + </table> + </div><!-- .panel panel-default --> + + <hr /> + + <h1 id="disk-win7"> + <i class="fa fa-windows"></i> + {% trans "Expanding disk on Windows 7" %} + </h1> + + <p> + {% blocktrans %} + If we don’t have enogh space on our virtual machine, we can ask more. + After the request, the administrator will extend your HDD, but you have + to rescan and extend it manually on your machine. + {% endblocktrans %} + </p> + + <ol> + <li> + {% trans "Ask the administrator for more space. After they had given more, do the following steps." %} + <img src="{% static "dashboard/img/resize/1.png" %}" + alt="Sometimes we don't have enough space"/> + </li> + <li> + {% blocktrans %} + Click on the Start menu, and type: <code>disk management</code>. + Click the <code>Create and format hard disk partitions</code> + {% endblocktrans %} + <img src="{% static "dashboard/img/resize/2.png" %}" alt="{% trans "Start menu" %}" class="img-responsive"> + </li> + <li> + {% trans "Currently you can’t see the extended size." %} + <img src="{% static "dashboard/img/resize/3.png" %}" alt="{% trans "Disk Management" %}" class="img-responsive"> + </li> + <li> + {% blocktrans %} + To update the disk information, click <code>Rescan Disks</code> on the <code>Action</code> menu. + {% endblocktrans %} + <img src="{% static "dashboard/img/resize/4.png" %}" alt="{% trans "Rescan Disks" %}" class="img-responsive"> + </li> + <li> + {% trans "After scanning Unallocated space appeared." %} + <img src="{% static "dashboard/img/resize/5.png" %}" alt="{% trans "New unallocated space" %}" class="img-responsive"> + </li> + <li> + {% trans "To extend the C drive, right click on it, and select <code>Extend Volume</code>." %} + <img src="{% static "dashboard/img/resize/6.png" %}" alt="{% trans "Extend Volume..." %}" class="img-responsive"> + <pre>{% trans "You can also create a new partition from the unallocated space." %}</pre> + </li> + <li> + {% blocktrans %} + In the wizard you can change, how much space will you using from the unallocated space. + The default is to use all, so press <kbd>Next</kbd>,<kbd>Next</kbd>,<kbd>Finish</kbd>. + {% endblocktrans %} + <img src="{% static "dashboard/img/resize/7_1.png" %}" alt="{% trans "Extend Volume Wizard" %}" class="img-responsive"> + <img src="{% static "dashboard/img/resize/7_2.png" %}" alt="{% trans "Next" %}" class="img-responsive"> + <img src="{% static "dashboard/img/resize/7_3.png" %}" alt="{% trans "Finish" %}" class="img-responsive"> + </li> + <li> + {% trans "Your partition is now bigger." %} + <img src="{% static "dashboard/img/resize/8_1.png" %}" alt="{% trans "Bigger partition" %}" class="img-responsive"> + <img src="{% static "dashboard/img/resize/8_2.png" %}" alt="{% trans "More free space" %}" class="img-responsive"> + </li> + </ol> + </div><!-- .col-lg-12 --> +</div><!-- .row --> +{% endblock %} diff --git a/circle/vm/models/activity.py b/circle/vm/models/activity.py index ad7736b..7a2ab77 100644 --- a/circle/vm/models/activity.py +++ b/circle/vm/models/activity.py @@ -145,8 +145,8 @@ class InstanceActivity(ActivityModel): def has_percentage(self): op = self.instance.get_operation_from_activity_code(self.activity_code) - return (self.task_uuid and op and op.has_percentage - and not self.finished) + return (self.task_uuid and op and op.has_percentage and + not self.finished) def get_percentage(self): """Returns the percentage of the running operation if available. diff --git a/circle/vm/models/common.py b/circle/vm/models/common.py index fd3efa2..3cd6f04 100644 --- a/circle/vm/models/common.py +++ b/circle/vm/models/common.py @@ -174,6 +174,6 @@ class Trait(Model): @property def in_use(self): return ( - self.instance_set.exists() or self.node_set.exists() - or self.instancetemplate_set.exists() + self.instance_set.exists() or self.node_set.exists() or + self.instancetemplate_set.exists() ) diff --git a/circle/vm/models/instance.py b/circle/vm/models/instance.py index 0fb86c4..3ab5d3d 100644 --- a/circle/vm/models/instance.py +++ b/circle/vm/models/instance.py @@ -200,6 +200,10 @@ class InstanceTemplate(AclBase, VirtualMachineDescModel, TimeStampedModel): def get_running_instances(self): return Instance.active.filter(template=self, status="RUNNING") + @property + def metric_prefix(self): + return 'template.%d' % self.pk + class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin, TimeStampedModel): @@ -848,8 +852,8 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin, def is_in_status_change(self): latest = self.get_latest_activity_in_progress() - return (latest and latest.resultant_state is not None - and self.status != latest.resultant_state) + return (latest and latest.resultant_state is not None and + self.status != latest.resultant_state) @property def metric_prefix(self): diff --git a/circle/vm/operations.py b/circle/vm/operations.py index 7898c84..63b1f22 100644 --- a/circle/vm/operations.py +++ b/circle/vm/operations.py @@ -133,8 +133,8 @@ class InstanceOperation(Operation): super(InstanceOperation, self).check_auth(user=user) - if (self.instance.node and not self.instance.node.online - and not user.is_superuser): + if (self.instance.node and not self.instance.node.online and + not user.is_superuser): raise self.instance.WrongStateError(self.instance) def create_activity(self, parent, user, kwargs): @@ -306,6 +306,9 @@ class ResizeDiskOperation(RemoteInstanceOperation): size=filesizeformat(kwargs['size']), name=kwargs['disk'].name) def _operation(self, disk, size): + if not disk.is_resizable: + raise HumanReadableException.create(ugettext_noop( + 'Disk type "%(type)s" is not resizable.'), type=disk.type) super(ResizeDiskOperation, self)._operation(disk=disk, size=size) disk.size = size disk.save() @@ -534,8 +537,8 @@ class MigrateOperation(RemoteInstanceOperation): remote_timeout = 1000 def _get_remote_args(self, to_node, live_migration, **kwargs): - return (super(MigrateOperation, self)._get_remote_args(**kwargs) - + [to_node.host.hostname, live_migration]) + return (super(MigrateOperation, self)._get_remote_args(**kwargs) + + [to_node.host.hostname, live_migration]) def rollback(self, activity): with activity.sub_activity( @@ -908,8 +911,8 @@ class SleepOperation(InstanceOperation): def _get_remote_args(self, **kwargs): return (super(SleepOperation.SuspendVmOperation, self) - ._get_remote_args(**kwargs) - + [self.instance.mem_dump['path']]) + ._get_remote_args(**kwargs) + + [self.instance.mem_dump['path']]) @register_operation @@ -962,8 +965,8 @@ class WakeUpOperation(InstanceOperation): def _get_remote_args(self, **kwargs): return (super(WakeUpOperation.WakeUpVmOperation, self) - ._get_remote_args(**kwargs) - + [self.instance.mem_dump['path']]) + ._get_remote_args(**kwargs) + + [self.instance.mem_dump['path']]) @register_operation @@ -1408,9 +1411,9 @@ class PasswordResetOperation(RemoteAgentOperation): task = agent_tasks.change_password required_perms = () - def _get_remote_args(self, password, **kwargs): - return (super(PasswordResetOperation, self)._get_remote_args(**kwargs) - + [password]) + def _get_remote_args(self, password, **kwrgs): + return (super(PasswordResetOperation, self)._get_remote_args(**kwrgs) + + [password]) def _operation(self, password=None): if not password: @@ -1433,8 +1436,8 @@ class InstallKeysOperation(RemoteAgentOperation): def _get_remote_args(self, user, keys=None, **kwargs): if keys is None: keys = list(user.userkey_set.values_list('key', flat=True)) - return (super(InstallKeysOperation, self)._get_remote_args(**kwargs) - + [keys]) + return (super(InstallKeysOperation, self)._get_remote_args(**kwargs) + + [keys]) @register_operation @@ -1446,8 +1449,8 @@ class RemoveKeysOperation(RemoteAgentOperation): required_perms = () def _get_remote_args(self, user, keys, **kwargs): - return (super(RemoveKeysOperation, self)._get_remote_args(**kwargs) - + [keys]) + return (super(RemoveKeysOperation, self)._get_remote_args(**kwargs) + + [keys]) @register_operation @@ -1541,8 +1544,8 @@ class AgentStartedOperation(InstanceOperation): def _get_remote_args(self, **kwargs): cls = AgentStartedOperation.SetTimeOperation - return (super(cls, self)._get_remote_args(**kwargs) - + [time.time()]) + return (super(cls, self)._get_remote_args(**kwargs) + + [time.time()]) @register_operation class SetHostnameOperation(SubOperationMixin, RemoteAgentOperation): @@ -1552,8 +1555,8 @@ class AgentStartedOperation(InstanceOperation): def _get_remote_args(self, **kwargs): cls = AgentStartedOperation.SetHostnameOperation - return (super(cls, self)._get_remote_args(**kwargs) - + [self.instance.short_hostname]) + return (super(cls, self)._get_remote_args(**kwargs) + + [self.instance.short_hostname]) @register_operation class RestartNetworkingOperation(SubOperationMixin, RemoteAgentOperation): @@ -1572,8 +1575,8 @@ class AgentStartedOperation(InstanceOperation): 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']]) + return (super(cls, self)._get_remote_args(**kwargs) + + [interfaces, settings.FIREWALL_SETTINGS['rdns_ip']]) @register_operation @@ -1697,8 +1700,8 @@ class AbstractDiskOperation(SubOperationMixin, RemoteInstanceOperation): required_perms = () def _get_remote_args(self, disk, **kwargs): - return (super(AbstractDiskOperation, self)._get_remote_args(**kwargs) - + [disk.get_vmdisk_desc()]) + return (super(AbstractDiskOperation, self)._get_remote_args(**kwargs) + + [disk.get_vmdisk_desc()]) @register_operation diff --git a/docs/deploy.rst b/docs/deploy.rst index 80ea112..9f33a72 100644 --- a/docs/deploy.rst +++ b/docs/deploy.rst @@ -1,5 +1,5 @@ -Deploy -====== +Deploying CIRCLE +================ This tutorial describes the installation of a production environment. To have a fully working environment, you have to set up the other components @@ -145,4 +145,4 @@ the portal application server:: sudo cp miscellaneous/mancelery.conf /etc/init/ sudo start mancelery sudo cp miscellaneous/portal-uwsgi.conf /etc/init/ - sudo start portal-uwsgi + sudo start portal-uwsgi \ No newline at end of file diff --git a/requirements/base.txt b/requirements/base.txt index f053bf2..4a8efc2 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,6 +1,6 @@ amqp==1.4.6 anyjson==0.3.3 -arrow==0.5.4 +arrow==0.6.0 billiard==3.3.0.20 bpython==0.14.1 celery==3.1.18