Commit ca137eda by Bach Dániel

Merge remote-tracking branch 'origin/master' into feature-management-commands

parents bad4afd4 1ca7edbb
......@@ -108,9 +108,9 @@ $(function () {
e.stopImmediatePropagation();
return false;
});
$('[title]:not(.title-favourite)').tooltip();
$('.title-favourite').tooltip({'placement': 'right'});
$(':input[title]').tooltip({trigger: 'focus', placement: 'auto right'});
$('body [title]:not(.title-favourite)').tooltip();
$('body .title-favourite').tooltip({'placement': 'right'});
$('body :input[title]').tooltip({trigger: 'focus', placement: 'auto right'});
$(".knob").knob();
$('[data-toggle="pill"]').click(function() {
......
......@@ -8,6 +8,7 @@
<link rel="stylesheet" href="{{ STATIC_URL }}dashboard/loopj-jquery-simple-slider/css/simple-slider.css"/>
<link rel="stylesheet" href="{{ STATIC_URL }}dashboard/introjs/introjs.min.css">
<link href="{{ STATIC_URL }}dashboard/dashboard.css" rel="stylesheet">
{% block extra_link_2 %}{% endblock %}
{% endblock %}
......
......@@ -3,6 +3,13 @@
{% block title-page %}{% trans "Index" %}{% endblock %}
{% block extra_link_2 %}
<link rel="search"
type="application/opensearchdescription+xml"
href="{% url "dashboard.views.vm-opensearch" %}"
title="{% blocktrans with name=COMPANY_NAME %}{{name}} virtual machines{% endblocktrans %}" />
{% endblock %}
{% block content %}
<div class="body-content dashboard-index">
<div class="row">
......
{% load i18n %}<?xml version="1.0" encoding="UTF-8"?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
<ShortName>{% blocktrans with name=COMPANY_NAME %}{{name}} virtual machines{% endblocktrans %}</ShortName>
<Url type="text/html"
template="{{ url }}?s={searchTerms}&amp;stype=shared" />
</OpenSearchDescription>
......@@ -50,6 +50,7 @@ from .views import (
VmGraphView, NodeGraphView, NodeListGraphView,
TransferInstanceOwnershipView, TransferInstanceOwnershipConfirmView,
TransferTemplateOwnershipView, TransferTemplateOwnershipConfirmView,
OpenSearchDescriptionView,
)
from .views.vm import vm_ops, vm_mass_ops
from .views.node import node_ops
......@@ -221,6 +222,8 @@ urlpatterns = patterns(
name="dashboard.views.client-check"),
url(r'^token-login/(?P<token>.*)/$', TokenLogin.as_view(),
name="dashboard.views.token-login"),
url(r'^vm/opensearch.xml$', OpenSearchDescriptionView.as_view(),
name="dashboard.views.vm-opensearch"),
)
urlpatterns += patterns(
......
......@@ -19,6 +19,7 @@ from __future__ import unicode_literals, absolute_import
import logging
from django.core.cache import get_cache
from django.core.urlresolvers import reverse
from django.conf import settings
from django.contrib.auth.models import Group
from django.views.generic import TemplateView
......@@ -121,3 +122,15 @@ class HelpView(TemplateView):
ctx.update({"saml": hasattr(settings, "SAML_CONFIG"),
"store": settings.STORE_URL})
return ctx
class OpenSearchDescriptionView(TemplateView):
template_name = "dashboard/vm-opensearch.xml"
content_type = "application/opensearchdescription+xml"
def get_context_data(self, **kwargs):
context = super(OpenSearchDescriptionView, self).get_context_data(
**kwargs)
context['url'] = self.request.build_absolute_uri(
reverse("dashboard.views.vm-list"))
return context
......@@ -15,6 +15,7 @@
# You should have received a copy of the GNU General Public License along
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
from string import ascii_letters
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import ugettext_lazy as _
......@@ -22,7 +23,7 @@ from django.utils.ipv6 import is_valid_ipv6_address
from south.modelsinspector import add_introspection_rules
from django import forms
from netaddr import (IPAddress, IPNetwork, AddrFormatError, ZEROFILL,
EUI, mac_unix)
EUI, mac_unix, AddrConversionError)
import re
......@@ -31,7 +32,6 @@ domain_re = re.compile(r'^([A-Za-z0-9_-]\.?)+$')
domain_wildcard_re = re.compile(r'^(\*\.)?([A-Za-z0-9_-]\.?)+$')
ipv4_re = re.compile('^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$')
reverse_domain_re = re.compile(r'^(%\([abcd]\)d|[a-z0-9.-])+$')
ipv6_template_re = re.compile(r'^(%\([abcd]\)[dxX]|[A-Za-z0-9:-])+$')
class mac_custom(mac_unix):
......@@ -246,15 +246,60 @@ def val_reverse_domain(value):
raise ValidationError(u'%s - invalid reverse domain name' % value)
def is_valid_ipv6_template(value):
"""Check whether the parameter is a valid ipv6 template."""
return ipv6_template_re.match(value) is not None
def val_ipv6_template(value):
"""Validate whether the parameter is a valid ipv6 template."""
if not is_valid_ipv6_template(value):
raise ValidationError(u'%s - invalid reverse ipv6 template' % value)
"""Validate whether the parameter is a valid ipv6 template.
Normal use:
>>> val_ipv6_template("123::%(a)d:%(b)d:%(c)d:%(d)d")
>>> val_ipv6_template("::%(a)x:%(b)x:%(c)d:%(d)d")
Don't have to use all bytes from the left (no a):
>>> val_ipv6_template("::%(b)x:%(c)d:%(d)d")
But have to use all ones to the right (a, but no b):
>>> val_ipv6_template("::%(a)x:%(c)d:%(d)d")
Traceback (most recent call last):
...
ValidationError: [u"template doesn't use parameter b"]
Detects valid templates building invalid ips:
>>> val_ipv6_template("xxx::%(a)d:%(b)d:%(c)d:%(d)d")
Traceback (most recent call last):
...
ValidationError: [u'template renders invalid IPv6 address']
Also IPv4-compatible addresses are invalid:
>>> val_ipv6_template("::%(a)02x%(b)02x:%(c)d:%(d)d")
Traceback (most recent call last):
...
ValidationError: [u'template results in IPv4 address']
"""
tpl = {ascii_letters[i]: 255 for i in range(4)}
try:
v6 = value % tpl
except:
raise ValidationError(_('%s: invalid template') % value)
used = False
for i in ascii_letters[:4]:
try:
value % {k: tpl[k] for k in tpl if k != i}
except KeyError:
used = True # ok, it misses this key
else:
if used:
raise ValidationError(
_("template doesn't use parameter %s") % i)
try:
v6 = IPAddress(v6, 6)
except:
raise ValidationError(_('template renders invalid IPv6 address'))
try:
v6.ipv4()
except (AddrConversionError, AddrFormatError):
pass # can't converted to ipv4 == it's real ipv6
else:
raise ValidationError(_('template results in IPv4 address'))
def is_valid_ipv4_address(value):
......@@ -284,12 +329,3 @@ def val_mx(value):
domain_re.match(mx[1])):
raise ValidationError(_("Bad MX address format. "
"Should be: <priority>:<hostname>"))
def convert_ipv4_to_ipv6(ipv6_template, ipv4):
"""Convert IPv4 address string to IPv6 address string."""
m = ipv4.words
return IPAddress(ipv6_template % {'a': int(m[0]),
'b': int(m[1]),
'c': int(m[2]),
'd': int(m[3])})
......@@ -17,9 +17,11 @@
# You should have received a copy of the GNU General Public License along
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
from itertools import islice, ifilter
from string import ascii_letters
from itertools import islice, ifilter, chain
from math import ceil
import logging
from netaddr import IPSet, EUI, IPNetwork
import random
from django.contrib.auth.models import User
from django.db import models
......@@ -28,15 +30,17 @@ from django.utils.translation import ugettext_lazy as _
from firewall.fields import (MACAddressField, val_alfanum, val_reverse_domain,
val_ipv6_template, val_domain, val_ipv4,
val_domain_wildcard,
val_ipv6, val_mx, convert_ipv4_to_ipv6,
val_ipv6, val_mx,
IPNetworkField, IPAddressField)
from django.core.validators import MinValueValidator, MaxValueValidator
import django.conf
from django.db.models.signals import post_save, post_delete
import random
from celery.exceptions import TimeoutError
from netaddr import IPSet, EUI, IPNetwork, IPAddress, ipv6_full
from common.models import method_cache, WorkerNotFound, HumanSortField
from firewall.tasks.local_tasks import reloadtask
from firewall.tasks.remote_tasks import get_dhcp_clients
from .iptables import IptRule
from acl.models import AclBase
......@@ -360,9 +364,19 @@ class Vlan(AclBase, models.Model):
'address is: "%(d)d.%(c)d.%(b)d.%(a)d.in-addr.arpa".'),
default="%(d)d.%(c)d.%(b)d.%(a)d.in-addr.arpa")
ipv6_template = models.TextField(
validators=[val_ipv6_template],
verbose_name=_('ipv6 template'),
default="2001:738:2001:4031:%(b)d:%(c)d:%(d)d:0")
blank=True,
help_text=_('Template for translating IPv4 addresses to IPv6. '
'Automatically generated hosts in dual-stack networks '
'will get this address. The template '
'can contain four tokens: "%(a)d", "%(b)d", '
'"%(c)d", and "%(d)d", representing the four bytes '
'of the IPv4 address, respectively, in decimal notation. '
'Moreover you can use any standard printf format '
'specification like %(a)02x to get the first byte as two '
'hexadecimal digits. Usual choices for mapping '
'198.51.100.0/24 to 2001:0DB8:1:1::/64 would be '
'"2001:db8:1:1:%(d)d::" and "2001:db8:1:1:%(d)02x00::".'),
validators=[val_ipv6_template], verbose_name=_('ipv6 template'))
dhcp_pool = models.TextField(blank=True, verbose_name=_('DHCP pool'),
help_text=_(
'The address range of the DHCP pool: '
......@@ -377,6 +391,87 @@ class Vlan(AclBase, models.Model):
modified_at = models.DateTimeField(auto_now=True,
verbose_name=_('modified at'))
def clean(self):
super(Vlan, self).clean()
if self.ipv6_template:
if not self.network6:
raise ValidationError(
_("You cannot specify an IPv6 template if there is no "
"IPv6 network set."))
for i in (self.network4[1], self.network4[-1]):
i6 = self.convert_ipv4_to_ipv6(i)
if i6 not in self.network6:
raise ValidationError(
_("%(ip6)s (translated from %(ip4)s) is outside of "
"the IPv6 network.") % {"ip4": i, "ip6": i6})
if self.network6:
tpl, prefixlen = self._magic_ipv6_template(self.network4,
self.network6)
if not self.ipv6_template:
self.ipv6_template = tpl
if not self.host_ipv6_prefixlen:
self.host_ipv6_prefixlen = prefixlen
@staticmethod
def _host_bytes(prefixlen, maxbytes):
return int(ceil((maxbytes - prefixlen / 8.0)))
@staticmethod
def _append_hexa(s, v, lasthalf):
if lasthalf: # can use last half word
assert s[-1] == "0" or s[-1].endswith("00")
if s[-1].endswith("00"):
s[-1] = s[-1][:-2]
s[-1] += "%({})02x".format(v)
s[-1].lstrip("0")
else:
s.append("%({})02x00".format(v))
@classmethod
def _magic_ipv6_template(cls, network4, network6, verbose=None):
"""Offer a sensible ipv6_template value.
Based on prefix lengths the method magically selects verbose (decimal)
format:
>>> Vlan._magic_ipv6_template(IPNetwork("198.51.100.0/24"),
... IPNetwork("2001:0DB8:1:1::/64"))
('2001:db8:1:1:%(d)d::', 80)
However you can explicitly select non-verbose, i.e. hexa format:
>>> Vlan._magic_ipv6_template(IPNetwork("198.51.100.0/24"),
... IPNetwork("2001:0DB8:1:1::/64"), False)
('2001:db8:1:1:%(d)02x00::', 72)
"""
host4_bytes = cls._host_bytes(network4.prefixlen, 4)
host6_bytes = cls._host_bytes(network6.prefixlen, 16)
if host4_bytes > host6_bytes:
raise ValidationError(
_("IPv6 network is too small to map IPv4 addresses to it."))
letters = ascii_letters[4-host4_bytes:4]
remove = host6_bytes // 2
ipstr = network6.network.format(ipv6_full)
s = ipstr.split(":")[0:-remove]
if verbose is None: # use verbose format if net6 much wider
verbose = 2 * (host4_bytes + 1) < host6_bytes
if verbose:
for i in letters:
s.append("%({})d".format(i))
else:
remain = host6_bytes
for i in letters:
cls._append_hexa(s, i, remain % 2 == 1)
remain -= 1
if host6_bytes > host4_bytes:
s.append(":")
tpl = ":".join(s)
# compute prefix length
mask = int(IPAddress(tpl % {"a": 1, "b": 1, "c": 1, "d": 1}))
prefixlen = 128
while mask % 2 == 0:
mask /= 2
prefixlen -= 1
return (tpl, prefixlen)
def __unicode__(self):
return "%s - %s" % ("managed" if self.managed else "unmanaged",
self.name)
......@@ -402,7 +497,7 @@ class Vlan(AclBase, models.Model):
logger.debug("Found unused IPv4 address %s.", ipv4)
ipv6 = None
if self.network6 is not None:
ipv6 = convert_ipv4_to_ipv6(self.ipv6_template, ipv4)
ipv6 = self.convert_ipv4_to_ipv6(ipv4)
if ipv6 in used_v6:
continue
else:
......@@ -411,6 +506,20 @@ class Vlan(AclBase, models.Model):
else:
raise ValidationError(_("All IP addresses are already in use."))
def convert_ipv4_to_ipv6(self, ipv4):
"""Convert IPv4 address string to IPv6 address string."""
if isinstance(ipv4, basestring):
ipv4 = IPAddress(ipv4, 4)
nums = {ascii_letters[i]: int(ipv4.words[i]) for i in range(4)}
return IPAddress(self.ipv6_template % nums)
def get_dhcp_clients(self):
macs = set(i.mac for i in self.host_set.all())
return [{"mac": k, "ip": v["ip"], "hostname": v["hostname"]}
for k, v in chain(*(fw.get_dhcp_clients().iteritems()
for fw in Firewall.objects.all() if fw))
if v["interface"] == self.name and EUI(k) not in macs]
class VlanGroup(models.Model):
"""
......@@ -581,8 +690,7 @@ class Host(models.Model):
def save(self, *args, **kwargs):
if not self.id and self.ipv6 == "auto":
self.ipv6 = convert_ipv4_to_ipv6(self.vlan.ipv6_template,
self.ipv4)
self.ipv6 = self.vlan.convert_ipv4_to_ipv6(self.ipv4)
self.full_clean()
super(Host, self).save(*args, **kwargs)
......@@ -835,7 +943,7 @@ class Firewall(models.Model):
return self.name
@method_cache(30)
def get_remote_queue_name(self, queue_id):
def get_remote_queue_name(self, queue_id="firewall"):
"""Returns the name of the remote celery queue for this node.
Throws Exception if there is no worker on the queue.
......@@ -848,6 +956,20 @@ class Firewall(models.Model):
else:
raise WorkerNotFound()
@method_cache(20)
def get_dhcp_clients(self):
try:
return get_dhcp_clients.apply_async(
queue=self.get_remote_queue_name(), expires=60).get(timeout=2)
except TimeoutError:
logger.info("get_dhcp_clients task timed out")
except IOError:
logger.exception("get_dhcp_clients failed. "
"maybe syslog isn't readble by firewall worker")
except:
logger.exception("get_dhcp_clients failed")
return {}
class Domain(models.Model):
name = models.CharField(max_length=40, validators=[val_domain],
......
......@@ -62,5 +62,6 @@ def reload_blacklist(data):
@celery.task(name='firewall.get_dhcp_clients')
def get_dhcp_clients(data):
def get_dhcp_clients():
# {'00:21:5a:73:72:cd': {'interface': 'OFF', 'ip': None, 'hostname': None}}
pass
......@@ -78,6 +78,7 @@ class GetNewAddressTestCase(TestCase):
self.vlan = Vlan(vid=1, name='test', network4='10.0.0.0/29',
network6='2001:738:2001:4031::/80', domain=d,
owner=self.u1)
self.vlan.clean()
self.vlan.save()
self.vlan.host_set.all().delete()
for i in [1] + range(3, 6):
......@@ -85,6 +86,9 @@ class GetNewAddressTestCase(TestCase):
ipv4='10.0.0.%d' % i, vlan=self.vlan,
owner=self.u1).save()
def tearDown(self):
self.vlan.delete()
def test_new_addr_w_empty_vlan(self):
self.vlan.host_set.all().delete()
self.vlan.get_new_address()
......@@ -96,12 +100,6 @@ class GetNewAddressTestCase(TestCase):
owner=self.u1).save()
self.assertRaises(ValidationError, self.vlan.get_new_address)
def test_all_addr_in_use_w_ipv6(self):
Host(hostname='h-x', mac='01:02:03:04:05:06',
ipv4='10.0.0.6', ipv6='2001:738:2001:4031:0:0:2:0',
vlan=self.vlan, owner=self.u1).save()
self.assertRaises(ValidationError, self.vlan.get_new_address)
def test_new_addr(self):
used_v4 = IPSet(self.vlan.host_set.values_list('ipv4', flat=True))
assert self.vlan.get_new_address()['ipv4'] not in used_v4
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-10-20 12:09+0200\n"
"POT-Creation-Date: 2014-11-14 13:43+0100\n"
"PO-Revision-Date: 2014-10-20 12:21+0200\n"
"Last-Translator: Mate Ory <ory.mate@ik.bme.hu>\n"
"Language-Team: Hungarian <cloud@ik.bme.hu>\n"
......@@ -17,7 +17,7 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Lokalize 1.5\n"
#: dashboard/static/dashboard/dashboard.js:68
#: dashboard/static/dashboard/dashboard.js:83
#: static_collected/all.047675ebf594.js:3443
#: static_collected/all.0aecd87e873a.js:3443
#: static_collected/all.0db607331718.js:3443
......@@ -34,13 +34,13 @@ msgstr ""
#: static_collected/dashboard/dashboard.be8725cd91bf.js:68
#: static_collected/dashboard/dashboard.e740d80401b2.js:68
#: static_collected/dashboard/dashboard.fe0a2f126346.js:68
#: static_collected/dashboard/dashboard.js:68
#: static_collected/dashboard/dashboard.js:83
msgid "Select an option to proceed!"
msgstr "Válasszon a folytatáshoz."
#: dashboard/static/dashboard/dashboard.js:259
#: dashboard/static/dashboard/dashboard.js:307
#: dashboard/static/dashboard/dashboard.js:317
#: dashboard/static/dashboard/dashboard.js:274
#: dashboard/static/dashboard/dashboard.js:322
#: dashboard/static/dashboard/dashboard.js:332
#: static_collected/all.047675ebf594.js:3633
#: static_collected/all.047675ebf594.js:3681
#: static_collected/all.047675ebf594.js:3691
......@@ -88,9 +88,9 @@ msgstr "Válasszon a folytatáshoz."
#: static_collected/dashboard/dashboard.fe0a2f126346.js:258
#: static_collected/dashboard/dashboard.fe0a2f126346.js:306
#: static_collected/dashboard/dashboard.fe0a2f126346.js:316
#: static_collected/dashboard/dashboard.js:259
#: static_collected/dashboard/dashboard.js:307
#: static_collected/dashboard/dashboard.js:317
#: static_collected/dashboard/dashboard.js:274
#: static_collected/dashboard/dashboard.js:322
#: static_collected/dashboard/dashboard.js:332
msgid "No result"
msgstr "Nincs eredmény"
......@@ -129,10 +129,12 @@ msgid "Unknown error."
msgstr "Ismeretlen hiba."
#: dashboard/static/dashboard/template-list.js:103
#: static_collected/dashboard/template-list.js:103
msgid "Only the owners can delete the selected object."
msgstr "Csak a tulajdonos törölheti a kiválasztott elemet."
#: dashboard/static/dashboard/template-list.js:105
#: static_collected/dashboard/template-list.js:105
msgid "An error occurred. ("
msgstr "Hiba történt. ("
......@@ -206,11 +208,12 @@ msgstr "Jelszó megjelenítése"
#: static_collected/vm-detail.js:6391
#: static_collected/dashboard/vm-tour.1562cc89a659.js:22
#: static_collected/dashboard/vm-tour.7b4cf596f543.js:22
#: static_collected/dashboard/vm-tour.js:22
#: static_collected/dashboard/vm-tour.js:6
msgid "Next"
msgstr "Tovább"
#: dashboard/static/dashboard/vm-tour.js:7
#: static_collected/dashboard/vm-tour.js:7
msgid "Previous"
msgstr "Vissza"
......@@ -226,15 +229,17 @@ msgstr "Vissza"
#: static_collected/vm-detail.js:6395
#: static_collected/dashboard/vm-tour.1562cc89a659.js:26
#: static_collected/dashboard/vm-tour.7b4cf596f543.js:26
#: static_collected/dashboard/vm-tour.js:26
#: static_collected/dashboard/vm-tour.js:8
msgid "End tour"
msgstr "Befejezés"
#: dashboard/static/dashboard/vm-tour.js:9
#: static_collected/dashboard/vm-tour.js:9
msgid "Done"
msgstr "Kész"
#: dashboard/static/dashboard/vm-tour.js:56
#: static_collected/dashboard/vm-tour.js:56
msgid ""
"Welcome to the template tutorial. In this quick tour, we are going to show "
"you how to do the steps described above."
......@@ -254,7 +259,7 @@ msgstr ""
#: static_collected/vm-detail.js:6404
#: static_collected/dashboard/vm-tour.1562cc89a659.js:35
#: static_collected/dashboard/vm-tour.7b4cf596f543.js:35
#: static_collected/dashboard/vm-tour.js:35
#: static_collected/dashboard/vm-tour.js:57
msgid ""
"For the next tour step press the \"Next\" button or the right arrow (or "
"\"Back\" button/left arrow for the previous step)."
......@@ -263,6 +268,7 @@ msgstr ""
"nyílbillentyűket."
#: dashboard/static/dashboard/vm-tour.js:61
#: static_collected/dashboard/vm-tour.js:61
msgid ""
"In this tab you can extend the expiration date of your virtual machine, add "
"tags and modify the name and description."
......@@ -271,24 +277,26 @@ msgstr ""
"vagy módosíthatja a nevét, leírását."
#: dashboard/static/dashboard/vm-tour.js:65
#: static_collected/dashboard/vm-tour.js:65
msgid ""
"Please add a meaningful description to the virtual machine. Changing the "
"name is also recommended, however you can choose a new name when saving the "
"template."
msgstr ""
"Kérjük, adjon meg egy informatív leírást. A név megváltoztatása is "
"ajánlott, azonban a mentéskor is van a sablon nevének "
"megválasztására."
"Kérjük, adjon meg egy informatív leírást. A név megváltoztatása is ajánlott, "
"azonban a mentéskor is van a sablon nevének megválasztására."
#: dashboard/static/dashboard/vm-tour.js:69
#: static_collected/dashboard/vm-tour.js:69
msgid ""
"You can change the lease to extend the expiration date. This will be the "
"lease of the new template."
msgstr ""
"Megváltoztathatja a bérleti módot is a lejárat bővítéséhez. Az gép "
"bérleti módját örökli majd a sablon is."
"Megváltoztathatja a bérleti módot is a lejárat bővítéséhez. Az gép bérleti "
"módját örökli majd a sablon is."
#: dashboard/static/dashboard/vm-tour.js:73
#: static_collected/dashboard/vm-tour.js:73
msgid ""
"On the resources tab you can edit the CPU/RAM options and add/remove disks "
"if you have required permissions."
......@@ -308,7 +316,7 @@ msgstr ""
#: static_collected/vm-detail.js:6438
#: static_collected/dashboard/vm-tour.1562cc89a659.js:69
#: static_collected/dashboard/vm-tour.7b4cf596f543.js:69
#: static_collected/dashboard/vm-tour.js:69
#: static_collected/dashboard/vm-tour.js:81
msgid "CPU priority"
msgstr "CPU prioritás"
......@@ -324,7 +332,7 @@ msgstr "CPU prioritás"
#: static_collected/vm-detail.js:6438
#: static_collected/dashboard/vm-tour.1562cc89a659.js:69
#: static_collected/dashboard/vm-tour.7b4cf596f543.js:69
#: static_collected/dashboard/vm-tour.js:69
#: static_collected/dashboard/vm-tour.js:82
msgid "higher is better"
msgstr "a nagyobb érték a jobb"
......@@ -340,7 +348,7 @@ msgstr "a nagyobb érték a jobb"
#: static_collected/vm-detail.js:6439
#: static_collected/dashboard/vm-tour.1562cc89a659.js:70
#: static_collected/dashboard/vm-tour.7b4cf596f543.js:70
#: static_collected/dashboard/vm-tour.js:70
#: static_collected/dashboard/vm-tour.js:83
msgid "CPU count"
msgstr "CPU-k száma"
......@@ -356,7 +364,7 @@ msgstr "CPU-k száma"
#: static_collected/vm-detail.js:6439
#: static_collected/dashboard/vm-tour.1562cc89a659.js:70
#: static_collected/dashboard/vm-tour.7b4cf596f543.js:70
#: static_collected/dashboard/vm-tour.js:70
#: static_collected/dashboard/vm-tour.js:84
msgid "number of CPU cores."
msgstr "A CPU-magok száma."
......@@ -372,7 +380,7 @@ msgstr "A CPU-magok száma."
#: static_collected/vm-detail.js:6440
#: static_collected/dashboard/vm-tour.1562cc89a659.js:71
#: static_collected/dashboard/vm-tour.7b4cf596f543.js:71
#: static_collected/dashboard/vm-tour.js:71
#: static_collected/dashboard/vm-tour.js:85
msgid "RAM amount"
msgstr "RAM mennyiség"
......@@ -388,7 +396,7 @@ msgstr "RAM mennyiség"
#: static_collected/vm-detail.js:6440
#: static_collected/dashboard/vm-tour.1562cc89a659.js:71
#: static_collected/dashboard/vm-tour.7b4cf596f543.js:71
#: static_collected/dashboard/vm-tour.js:71
#: static_collected/dashboard/vm-tour.js:86
msgid "amount of RAM."
msgstr "a memória mennyisége."
......@@ -404,7 +412,7 @@ msgstr "a memória mennyisége."
#: static_collected/vm-detail.js:6451
#: static_collected/dashboard/vm-tour.1562cc89a659.js:82
#: static_collected/dashboard/vm-tour.7b4cf596f543.js:82
#: static_collected/dashboard/vm-tour.js:82
#: static_collected/dashboard/vm-tour.js:96
msgid ""
"You can add empty disks, download new ones and remove existing ones here."
msgstr ""
......@@ -423,7 +431,7 @@ msgstr ""
#: static_collected/vm-detail.js:6462
#: static_collected/dashboard/vm-tour.1562cc89a659.js:93
#: static_collected/dashboard/vm-tour.7b4cf596f543.js:93
#: static_collected/dashboard/vm-tour.js:93
#: static_collected/dashboard/vm-tour.js:105
msgid "You can add new network interfaces or remove existing ones here."
msgstr "Hozzáadhat új hálózati interfészeket, vagy törölheti a meglévőket."
......@@ -439,17 +447,18 @@ msgstr "Hozzáadhat új hálózati interfészeket, vagy törölheti a meglévők
#: static_collected/vm-detail.js:6474
#: static_collected/dashboard/vm-tour.1562cc89a659.js:105
#: static_collected/dashboard/vm-tour.7b4cf596f543.js:105
#: static_collected/dashboard/vm-tour.js:105
#: static_collected/dashboard/vm-tour.js:109
msgid "Deploy the virtual machine."
msgstr "A virtuális gép elindítása."
#: dashboard/static/dashboard/vm-tour.js:113
#: static_collected/dashboard/vm-tour.js:113
msgid ""
"Use the CIRCLE client or the connection string to connect to the virtual "
"machine."
msgstr ""
"Használja a CIRCLE klienst vagy a kapcsolódási adatokat a "
"virtuális géphez való csatlakozáshoz."
"Használja a CIRCLE klienst vagy a kapcsolódási adatokat a virtuális géphez "
"való csatlakozáshoz."
#: dashboard/static/dashboard/vm-tour.js:117
#: static_collected/vm-detail.09737c69abc3.js:5954
......@@ -463,7 +472,7 @@ msgstr ""
#: static_collected/vm-detail.js:6490
#: static_collected/dashboard/vm-tour.1562cc89a659.js:121
#: static_collected/dashboard/vm-tour.7b4cf596f543.js:121
#: static_collected/dashboard/vm-tour.js:121
#: static_collected/dashboard/vm-tour.js:117
msgid ""
"After you have connected to the virtual machine do your modifications then "
"log off."
......@@ -483,7 +492,7 @@ msgstr ""
#: static_collected/vm-detail.js:6498
#: static_collected/dashboard/vm-tour.1562cc89a659.js:129
#: static_collected/dashboard/vm-tour.7b4cf596f543.js:129
#: static_collected/dashboard/vm-tour.js:129
#: static_collected/dashboard/vm-tour.js:121
msgid ""
"Press the \"Save as template\" button and wait until the activity finishes."
msgstr ""
......@@ -491,12 +500,11 @@ msgstr ""
"elkészül."
#: dashboard/static/dashboard/vm-tour.js:125
#: static_collected/dashboard/vm-tour.js:125
msgid ""
"This is the last message, if something is not clear you can do the the tour "
"again."
msgstr ""
"A túra véget ért. Ha valami nem érthető, újrakezdheti az "
"útmutatót."
msgstr "A túra véget ért. Ha valami nem érthető, újrakezdheti az útmutatót."
#: network/static/js/host.js:10 static_collected/all.047675ebf594.js:5239
#: static_collected/all.0aecd87e873a.js:5309
......@@ -617,7 +625,6 @@ msgstr "Biztosan törli ezt az eszközt?"
#: static_collected/vm-detail.js:6389
#: static_collected/dashboard/vm-tour.1562cc89a659.js:20
#: static_collected/dashboard/vm-tour.7b4cf596f543.js:20
#: static_collected/dashboard/vm-tour.js:20
msgid "Prev"
msgstr "Vissza"
......@@ -632,7 +639,6 @@ msgstr "Vissza"
#: static_collected/vm-detail.js:6402
#: static_collected/dashboard/vm-tour.1562cc89a659.js:33
#: static_collected/dashboard/vm-tour.7b4cf596f543.js:33
#: static_collected/dashboard/vm-tour.js:33
msgid "Template Tutorial Tour"
msgstr "Sablon-kalauz"
......@@ -647,7 +653,6 @@ msgstr "Sablon-kalauz"
#: static_collected/vm-detail.js:6403
#: static_collected/dashboard/vm-tour.1562cc89a659.js:34
#: static_collected/dashboard/vm-tour.7b4cf596f543.js:34
#: static_collected/dashboard/vm-tour.js:34
msgid ""
"Welcome to the template tutorial. In this quick tour, we gonna show you how "
"to do the steps described above."
......@@ -665,7 +670,6 @@ msgstr ""
#: static_collected/vm-detail.js:6405
#: static_collected/dashboard/vm-tour.1562cc89a659.js:36
#: static_collected/dashboard/vm-tour.7b4cf596f543.js:36
#: static_collected/dashboard/vm-tour.js:36
msgid ""
"During the tour please don't try the functions because it may lead to "
"graphical glitches, however "
......@@ -682,7 +686,6 @@ msgstr "A túra során még ne próbálja ki a bemutatott funkciókat."
#: static_collected/vm-detail.js:6414
#: static_collected/dashboard/vm-tour.1562cc89a659.js:45
#: static_collected/dashboard/vm-tour.7b4cf596f543.js:45
#: static_collected/dashboard/vm-tour.js:45
msgid "Home tab"
msgstr "Kezdőoldal"
......@@ -697,7 +700,6 @@ msgstr "Kezdőoldal"
#: static_collected/vm-detail.js:6415
#: static_collected/dashboard/vm-tour.1562cc89a659.js:46
#: static_collected/dashboard/vm-tour.7b4cf596f543.js:46
#: static_collected/dashboard/vm-tour.js:46
msgid ""
"In this tab you can tag your virtual machine and modify the name and "
"description."
......@@ -716,7 +718,6 @@ msgstr ""
#: static_collected/vm-detail.js:6424
#: static_collected/dashboard/vm-tour.1562cc89a659.js:55
#: static_collected/dashboard/vm-tour.7b4cf596f543.js:55
#: static_collected/dashboard/vm-tour.js:55
msgid "Resources tab"
msgstr "Erőforrások lap"
......@@ -731,7 +732,6 @@ msgstr "Erőforrások lap"
#: static_collected/vm-detail.js:6427
#: static_collected/dashboard/vm-tour.1562cc89a659.js:58
#: static_collected/dashboard/vm-tour.7b4cf596f543.js:58
#: static_collected/dashboard/vm-tour.js:58
msgid ""
"On the resources tab you can edit the CPU/RAM options and add/remove disks!"
msgstr ""
......@@ -749,7 +749,6 @@ msgstr ""