mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-09 19:56:48 +00:00
fix(i18n): refusals and checks users read are translated
English-only texts users read, now in their language: - the serial console's four refusals, which socketiod sends in the language LocaleMiddleware would choose (cookie, then browser) and logs in English; the texts live in console/refusals.py, as makemessages reads only .py files; - a submission key reused for another request, a malformed MAC address, the moved-VM refusal of a script's request, the API's "Templates cannot be started" (now the page's sentence), "Pool is not active" and the ISO upload's checks; - "Problem occurred with host" was a fragment with the host's name and error after it: one sentence with both.
This commit is contained in:
parent
6abea495f1
commit
6d22a18ba7
14 changed files with 187 additions and 41 deletions
|
|
@ -27,7 +27,7 @@
|
|||
<div class="col-lg-12">
|
||||
{% if compute.status is not True %}
|
||||
<div class="alert alert-danger shadow-sm">
|
||||
{% trans 'Problem occurred with host' %} {{ compute.name }}: {{ compute.connection_error }}
|
||||
{% blocktrans with name=compute.name error=compute.connection_error %}Problem occurred with host {{ name }}: {{ error }}{% endblocktrans %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if not instances %}
|
||||
|
|
|
|||
9
console/refusals.py
Normal file
9
console/refusals.py
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
"""socketiod's refusals, which its page shows in the user's language. Here,
|
||||
as makemessages reads only .py files and socketiod has no extension."""
|
||||
|
||||
from django.utils.translation import gettext_noop
|
||||
|
||||
TURNED_OFF = gettext_noop("The serial console is turned off on this server")
|
||||
NOT_ALLOWED = gettext_noop("The console link has expired, or you may no longer open this console: reload the page")
|
||||
OPEN_ELSEWHERE = gettext_noop("The console of this VM is open in another window")
|
||||
TOO_MANY = gettext_noop("Too many consoles are open on this server: try again later")
|
||||
|
|
@ -30,7 +30,11 @@ import socketio # noqa: E402
|
|||
from gunicorn.app.base import BaseApplication # noqa: E402
|
||||
from django import db # noqa: E402
|
||||
from django.conf import settings # noqa: E402
|
||||
from django.http import HttpRequest # noqa: E402
|
||||
from django.http.cookie import parse_cookie # noqa: E402
|
||||
from django.utils import translation # noqa: E402
|
||||
|
||||
from console import refusals # noqa: E402
|
||||
from console.tokens import authorize, session_user_id # noqa: E402
|
||||
|
||||
CONSOLECALLBACK = os.path.join(ROOT_PATH, "conf", "daemon", "consolecallback")
|
||||
|
|
@ -49,30 +53,34 @@ sessions = {}
|
|||
sessions_lock = threading.Lock()
|
||||
|
||||
|
||||
def refused(reason):
|
||||
"""The refusal the page shows; it is logged too."""
|
||||
def refused(environ, reason):
|
||||
"""The refusal the page shows, in its language (LocaleMiddleware's choice:
|
||||
the language cookie, else the browser's); it is logged too, in English."""
|
||||
logger.info("serial console refused: %s", reason)
|
||||
return socketio.exceptions.ConnectionRefusedError(reason)
|
||||
request = HttpRequest()
|
||||
request.COOKIES, request.META = parse_cookie(environ.get("HTTP_COOKIE", "")), environ
|
||||
with translation.override(translation.get_language_from_request(request)):
|
||||
return socketio.exceptions.ConnectionRefusedError(translation.gettext(reason))
|
||||
|
||||
|
||||
@sio.event
|
||||
def connect(sid, environ, auth):
|
||||
# the page shows the reason
|
||||
if not settings.SERIAL_CONSOLE_ENABLED:
|
||||
raise refused("The serial console is turned off on this server")
|
||||
raise refused(environ, refusals.TURNED_OFF)
|
||||
try:
|
||||
instance, user = authorize((auth or {}).get("token"), "pty")
|
||||
except PermissionError:
|
||||
raise refused("The console link has expired, or you may no longer open this console: reload the page")
|
||||
raise refused(environ, refusals.NOT_ALLOWED)
|
||||
finally:
|
||||
db.close_old_connections()
|
||||
taken = None
|
||||
with sessions_lock:
|
||||
open_sid = next((s for s, session in sessions.items() if session["vm"] == instance.pk), None)
|
||||
if open_sid is not None and not user.is_superuser:
|
||||
raise refused("The console of this VM is open in another window")
|
||||
raise refused(environ, refusals.OPEN_ELSEWHERE)
|
||||
if open_sid is None and len(sessions) >= MAX_SESSIONS:
|
||||
raise refused("Too many consoles are open on this server: try again later")
|
||||
raise refused(environ, refusals.TOO_MANY)
|
||||
if open_sid is not None: # a superuser takes over a console left open
|
||||
taken = open_sid, sessions.pop(open_sid)
|
||||
# the page tells its user who took the console; first, as its
|
||||
|
|
|
|||
|
|
@ -116,6 +116,18 @@ class SocketiodTestCase(Base):
|
|||
self.assertIn("Too many", self.refused("over", {"token": self.token(vm=other)}))
|
||||
self.assertEqual(list(self.d.sessions), ["first"])
|
||||
|
||||
@override_settings(SERIAL_CONSOLE_ENABLED=False)
|
||||
def test_a_refusal_is_in_the_page_s_language_and_logged_in_english(self):
|
||||
# as LocaleMiddleware: the language cookie first, then the browser's
|
||||
for environ, shown in (({"HTTP_COOKIE": "django_language=tr", "HTTP_ACCEPT_LANGUAGE": "zh-hans"}, "Seri konsol bu sunucuda kapalı"),
|
||||
({"HTTP_ACCEPT_LANGUAGE": "zh-CN,zh;q=0.9"}, "此服务器已关闭串行控制台"),
|
||||
({}, "The serial console is turned off on this server")):
|
||||
with self.subTest(shown), self.assertRaises(self.d.socketio.exceptions.ConnectionRefusedError) as refusal, \
|
||||
self.assertLogs("socketiod", level="INFO") as log:
|
||||
self.d.connect("sid", environ, {"token": self.token()})
|
||||
self.assertEqual(refusal.exception.error_args["message"], shown)
|
||||
self.assertIn("refused: The serial console is turned off on this server", log.output[0])
|
||||
|
||||
@override_settings(SERIAL_CONSOLE_ENABLED=True)
|
||||
def test_input_and_resize_go_to_the_session_and_are_checked(self):
|
||||
self.d.connect("sid-1", {}, {"token": self.token()})
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from django.contrib.messages import get_messages
|
|||
from django.http import Http404
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext as _
|
||||
from instances.jobs import MIGRATE_OPTIONS
|
||||
from instances.models import Flavor, Instance
|
||||
from instances.views import (
|
||||
|
|
@ -144,7 +145,7 @@ class InstanceViewSet(viewsets.ViewSet):
|
|||
if compute_pk is not None and str(queryset.compute_id) != str(compute_pk):
|
||||
raise Http404()
|
||||
if queryset.is_template:
|
||||
return Response({"status": "Templates cannot be started"}, status=status.HTTP_409_CONFLICT)
|
||||
return Response({"status": _("Templates cannot be started.")}, status=status.HTTP_409_CONFLICT)
|
||||
return instance_action_response(poweron, request, pk, "poweron command send")
|
||||
|
||||
@extend_schema(request=None, responses=ActionStatusSerializer)
|
||||
|
|
@ -162,7 +163,7 @@ class InstanceViewSet(viewsets.ViewSet):
|
|||
if compute_pk is not None and str(queryset.compute_id) != str(compute_pk):
|
||||
raise Http404()
|
||||
if queryset.is_template:
|
||||
return Response({"status": "Templates cannot be started"}, status=status.HTTP_409_CONFLICT)
|
||||
return Response({"status": _("Templates cannot be started.")}, status=status.HTTP_409_CONFLICT)
|
||||
return instance_action_response(powercycle, request, pk, "powercycle command send")
|
||||
|
||||
@extend_schema(request=None, responses=ActionStatusSerializer)
|
||||
|
|
|
|||
|
|
@ -640,7 +640,7 @@ def _busy(request, pk, busy):
|
|||
|
||||
def _moved(request, pk):
|
||||
if hasattr(request, "accepted_renderer") or from_script(request):
|
||||
return JsonResponse({"detail": "The VM was moved meanwhile; try again."}, status=409)
|
||||
return JsonResponse({"detail": _("The VM was moved meanwhile; try again.")}, status=409)
|
||||
messages.error(request, _("The VM was moved meanwhile; try again."))
|
||||
return get_safe_redirect(request, default=reverse("instances:instance", args=[pk]))
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -2119,8 +2119,10 @@ msgid "New Instance"
|
|||
msgstr "Yeni sanal makine"
|
||||
|
||||
#: computes/templates/computes/instances.html:30
|
||||
msgid "Problem occurred with host"
|
||||
msgstr "Sunucu ile ilgili bir problem meydana geldi"
|
||||
#, python-format
|
||||
#| msgid "Problem occurred with host"
|
||||
msgid "Problem occurred with host %(name)s: %(error)s"
|
||||
msgstr "%(name)s sunucusuyla ilgili bir sorun oluştu: %(error)s"
|
||||
|
||||
#: computes/templates/computes/instances.html:36
|
||||
#: computes/templates/computes/list.html:16
|
||||
|
|
@ -2379,7 +2381,7 @@ msgstr ""
|
|||
"Sunucudaki /etc/default/webvirt-gstfsd içindeki GSTFSD_KEY değerinin 64 "
|
||||
"onaltılık karakterini girin"
|
||||
|
||||
#: computes/views.py:202
|
||||
#: computes/views.py:205
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Its VMs keep running on the host and leave the panel; their owners are kept "
|
||||
|
|
@ -2390,6 +2392,28 @@ msgstr ""
|
|||
"Kaldırılan Sanal Makineler sayfasında %(days)s gün tutulur ve sunucu yeniden "
|
||||
"eklenirse geri gelir."
|
||||
|
||||
#: console/refusals.py:6
|
||||
#| msgid "The device is not on this host"
|
||||
msgid "The serial console is turned off on this server"
|
||||
msgstr "Seri konsol bu sunucuda kapalı"
|
||||
|
||||
#: console/refusals.py:7
|
||||
msgid ""
|
||||
"The console link has expired, or you may no longer open this console: reload "
|
||||
"the page"
|
||||
msgstr ""
|
||||
"Konsol bağlantısının süresi doldu ya da bu konsolu artık açamazsınız: sayfayı"
|
||||
" yeniden yükleyin"
|
||||
|
||||
#: console/refusals.py:8
|
||||
#| msgid "The console starts scaled to its window"
|
||||
msgid "The console of this VM is open in another window"
|
||||
msgstr "Bu sanal makinenin konsolu başka bir pencerede açık"
|
||||
|
||||
#: console/refusals.py:9
|
||||
msgid "Too many consoles are open on this server: try again later"
|
||||
msgstr "Bu sunucuda çok fazla konsol açık: daha sonra yeniden deneyin"
|
||||
|
||||
#: console/templates/console-menu.html:6
|
||||
msgid "Send key(s)"
|
||||
msgstr "Anahtar(ları) gönder"
|
||||
|
|
@ -2634,6 +2658,11 @@ msgstr ""
|
|||
"'%(type)s' konsol tipi desteklenmiyor. Lütfen sanal makinenin grafik tipini "
|
||||
"VNC olarak değiştirin."
|
||||
|
||||
#: instances/api/viewsets.py:148 instances/api/viewsets.py:166
|
||||
#: instances/views.py:792 instances/views.py:807
|
||||
msgid "Templates cannot be started."
|
||||
msgstr "Şablon makineler başlatılamaz."
|
||||
|
||||
#: instances/forms.py:13 instances/templates/create_instance_w2.html:69
|
||||
#: instances/templates/instances/settings_tab.html:857
|
||||
msgid "Memory (MiB)"
|
||||
|
|
@ -2999,7 +3028,6 @@ msgstr "Sanal makine tanımlanıyor"
|
|||
|
||||
#: instances/jobs.py:906
|
||||
#, python-format
|
||||
#| msgid "The panel failed unexpectedly; its log has the details."
|
||||
msgid ""
|
||||
"The panel failed unexpectedly; its log has the details. Remove these volumes "
|
||||
"by hand: %(volumes)s"
|
||||
|
|
@ -5924,7 +5952,7 @@ msgstr ""
|
|||
"Sunucu aygıtları olan bir sanal makine taşınamaz: hedefte bu aygıtlar "
|
||||
"olmayabilir. Önce onları kaldırın."
|
||||
|
||||
#: instances/views.py:644
|
||||
#: instances/views.py:643 instances/views.py:644
|
||||
msgid "The VM was moved meanwhile; try again."
|
||||
msgstr "Sanal makine bu arada taşındı; yeniden deneyin."
|
||||
|
||||
|
|
@ -5958,10 +5986,6 @@ msgstr ""
|
|||
msgid "Invalid disk options: %(options)s"
|
||||
msgstr "Geçersiz disk seçenekleri: %(options)s"
|
||||
|
||||
#: instances/views.py:792 instances/views.py:807
|
||||
msgid "Templates cannot be started."
|
||||
msgstr "Şablon makineler başlatılamaz."
|
||||
|
||||
#: instances/views.py:999
|
||||
#, python-format
|
||||
msgid "Set the gstfsd key of %(compute)s first (Computes, edit the host)"
|
||||
|
|
@ -7628,6 +7652,12 @@ msgstr ""
|
|||
msgid "%(phase)s (over its time limit)"
|
||||
msgstr "%(phase)s (süre sınırını aştı)"
|
||||
|
||||
#: operations/jobs.py:151
|
||||
#| msgid "The form has no valid submission key; reload the page"
|
||||
msgid "This form was already submitted for another request; reload the page"
|
||||
msgstr ""
|
||||
"Bu form başka bir istek için zaten gönderildi; sayfayı yeniden yükleyin"
|
||||
|
||||
#: operations/jobs.py:157
|
||||
msgid "The form has no valid submission key; reload the page"
|
||||
msgstr "Formun geçerli bir gönderim anahtarı yok; sayfayı yeniden yükleyin"
|
||||
|
|
@ -7907,10 +7937,14 @@ msgstr ""
|
|||
"Kurtarma bir saat boyunca işin durumunu okuyamadı; sanal makineyi kontrol "
|
||||
"edin, sonra bu işi çözün"
|
||||
|
||||
#: operations/worker.py:474
|
||||
#: operations/worker.py:479
|
||||
msgid "The worker stopped before the job changed anything"
|
||||
msgstr "Worker, iş herhangi bir şeyi değiştirmeden önce durdu"
|
||||
|
||||
#: storages/api/viewsets.py:203 storages/api/viewsets.py:224
|
||||
msgid "Pool is not active"
|
||||
msgstr "Havuz etkin değil"
|
||||
|
||||
#: storages/forms.py:30 storages/templates/storage.html:102
|
||||
#: storages/templates/storages.html:43
|
||||
msgid "Path"
|
||||
|
|
@ -8009,6 +8043,26 @@ msgstr "Kopya oluşturulmadı"
|
|||
msgid "Upload ISO image"
|
||||
msgstr "ISO imajı yükle"
|
||||
|
||||
#: storages/jobs.py:118
|
||||
#| msgid "Invalid file size."
|
||||
msgid "Invalid file name"
|
||||
msgstr "Geçersiz dosya adı"
|
||||
|
||||
#: storages/jobs.py:120
|
||||
#| msgid "Only .iso files can be uploaded."
|
||||
msgid "Only .iso files can be uploaded"
|
||||
msgstr "Yalnızca .iso dosyaları yüklenebilir"
|
||||
|
||||
#: storages/jobs.py:122
|
||||
#| msgid "Invalid file size."
|
||||
msgid "Invalid file size"
|
||||
msgstr "Geçersiz dosya boyutu"
|
||||
|
||||
#: storages/jobs.py:124
|
||||
#| msgid "Invalid chunk index or total chunks."
|
||||
msgid "Invalid chunk index or total chunks"
|
||||
msgstr "Geçersiz parça dizini veya toplam parça sayısı"
|
||||
|
||||
#: storages/templates/create_stg_block.html:7
|
||||
msgid "New pool"
|
||||
msgstr "Yeni havuz"
|
||||
|
|
@ -9834,6 +9888,11 @@ msgstr "XML geçerli değil: %(err)s"
|
|||
msgid "The XML may not declare a DTD"
|
||||
msgstr "XML bir DTD bildiremez"
|
||||
|
||||
#: vrtManager/util.py:143
|
||||
#, python-format
|
||||
msgid "MAC address must be of the format AA:BB:CC:DD:EE:FF, was %(mac)s"
|
||||
msgstr "MAC adresi AA:BB:CC:DD:EE:FF biçiminde olmalı; girilen: %(mac)s"
|
||||
|
||||
#: vrtManager/virtsecrets.py:26
|
||||
msgid "A Ceph key is base64 text, as `ceph auth get-key` prints it"
|
||||
msgstr ""
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -2050,8 +2050,10 @@ msgid "New Instance"
|
|||
msgstr "新建虚拟机"
|
||||
|
||||
#: computes/templates/computes/instances.html:30
|
||||
msgid "Problem occurred with host"
|
||||
msgstr "主机出现问题"
|
||||
#, python-format
|
||||
#| msgid "Problem occurred with host"
|
||||
msgid "Problem occurred with host %(name)s: %(error)s"
|
||||
msgstr "主机 %(name)s 出现问题:%(error)s"
|
||||
|
||||
#: computes/templates/computes/instances.html:36
|
||||
#: computes/templates/computes/list.html:16
|
||||
|
|
@ -2302,7 +2304,7 @@ msgid ""
|
|||
"/etc/default/webvirt-gstfsd"
|
||||
msgstr "请输入主机 /etc/default/webvirt-gstfsd 中 GSTFSD_KEY 的 64 个十六进制字符"
|
||||
|
||||
#: computes/views.py:202
|
||||
#: computes/views.py:205
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Its VMs keep running on the host and leave the panel; their owners are kept "
|
||||
|
|
@ -2310,6 +2312,26 @@ msgid ""
|
|||
"again."
|
||||
msgstr "其虚拟机继续在主机上运行并离开面板;其所有者在“已移除的虚拟机”页面保留 %(days)s 天,如果再次添加该主机则会恢复。"
|
||||
|
||||
#: console/refusals.py:6
|
||||
#| msgid "The device is not on this host"
|
||||
msgid "The serial console is turned off on this server"
|
||||
msgstr "此服务器已关闭串行控制台"
|
||||
|
||||
#: console/refusals.py:7
|
||||
msgid ""
|
||||
"The console link has expired, or you may no longer open this console: reload "
|
||||
"the page"
|
||||
msgstr "控制台链接已过期,或您已无权打开此控制台:请重新加载页面"
|
||||
|
||||
#: console/refusals.py:8
|
||||
#| msgid "The console starts scaled to its window"
|
||||
msgid "The console of this VM is open in another window"
|
||||
msgstr "此虚拟机的控制台已在另一个窗口中打开"
|
||||
|
||||
#: console/refusals.py:9
|
||||
msgid "Too many consoles are open on this server: try again later"
|
||||
msgstr "此服务器上打开的控制台过多:请稍后再试"
|
||||
|
||||
#: console/templates/console-menu.html:6
|
||||
msgid "Send key(s)"
|
||||
msgstr "发送按键"
|
||||
|
|
@ -2541,6 +2563,11 @@ msgid ""
|
|||
"VNC."
|
||||
msgstr "不支持控制台类型 '%(type)s'。请将虚拟机图形切换为 VNC。"
|
||||
|
||||
#: instances/api/viewsets.py:148 instances/api/viewsets.py:166
|
||||
#: instances/views.py:792 instances/views.py:807
|
||||
msgid "Templates cannot be started."
|
||||
msgstr "模板无法启动。"
|
||||
|
||||
#: instances/forms.py:13 instances/templates/create_instance_w2.html:69
|
||||
#: instances/templates/instances/settings_tab.html:857
|
||||
msgid "Memory (MiB)"
|
||||
|
|
@ -2879,7 +2906,6 @@ msgstr "正在定义虚拟机"
|
|||
|
||||
#: instances/jobs.py:906
|
||||
#, python-format
|
||||
#| msgid "The panel failed unexpectedly; its log has the details."
|
||||
msgid ""
|
||||
"The panel failed unexpectedly; its log has the details. Remove these volumes "
|
||||
"by hand: %(volumes)s"
|
||||
|
|
@ -5623,7 +5649,7 @@ msgid ""
|
|||
" Remove them first."
|
||||
msgstr "有主机设备的虚拟机无法迁移:目标主机可能没有这些设备。请先移除它们。"
|
||||
|
||||
#: instances/views.py:644
|
||||
#: instances/views.py:643 instances/views.py:644
|
||||
msgid "The VM was moved meanwhile; try again."
|
||||
msgstr "该虚拟机在此期间已被移动;请重试。"
|
||||
|
||||
|
|
@ -5652,10 +5678,6 @@ msgstr ""
|
|||
msgid "Invalid disk options: %(options)s"
|
||||
msgstr "无效的磁盘选项:%(options)s"
|
||||
|
||||
#: instances/views.py:792 instances/views.py:807
|
||||
msgid "Templates cannot be started."
|
||||
msgstr "模板无法启动。"
|
||||
|
||||
#: instances/views.py:999
|
||||
#, python-format
|
||||
msgid "Set the gstfsd key of %(compute)s first (Computes, edit the host)"
|
||||
|
|
@ -7226,6 +7248,11 @@ msgstr "字母、数字、点、短横线和下划线。副本包含该过滤器
|
|||
msgid "%(phase)s (over its time limit)"
|
||||
msgstr "%(phase)s(已超出时间限制)"
|
||||
|
||||
#: operations/jobs.py:151
|
||||
#| msgid "The form has no valid submission key; reload the page"
|
||||
msgid "This form was already submitted for another request; reload the page"
|
||||
msgstr "此表单已为另一个请求提交过;请重新加载页面"
|
||||
|
||||
#: operations/jobs.py:157
|
||||
msgid "The form has no valid submission key; reload the page"
|
||||
msgstr "表单没有有效的提交密钥;请重新加载页面"
|
||||
|
|
@ -7490,10 +7517,14 @@ msgid ""
|
|||
"resolve this job"
|
||||
msgstr "恢复在一小时内无法读取任务状态;请检查虚拟机,然后处理此任务"
|
||||
|
||||
#: operations/worker.py:474
|
||||
#: operations/worker.py:479
|
||||
msgid "The worker stopped before the job changed anything"
|
||||
msgstr "工作进程在任务做出任何更改之前停止"
|
||||
|
||||
#: storages/api/viewsets.py:203 storages/api/viewsets.py:224
|
||||
msgid "Pool is not active"
|
||||
msgstr "存储池未激活"
|
||||
|
||||
#: storages/forms.py:30 storages/templates/storage.html:102
|
||||
#: storages/templates/storages.html:43
|
||||
msgid "Path"
|
||||
|
|
@ -7591,6 +7622,26 @@ msgstr "未创建副本"
|
|||
msgid "Upload ISO image"
|
||||
msgstr "上传 ISO 镜像"
|
||||
|
||||
#: storages/jobs.py:118
|
||||
#| msgid "Invalid file size."
|
||||
msgid "Invalid file name"
|
||||
msgstr "文件名无效"
|
||||
|
||||
#: storages/jobs.py:120
|
||||
#| msgid "Only .iso files can be uploaded."
|
||||
msgid "Only .iso files can be uploaded"
|
||||
msgstr "只能上传 .iso 文件"
|
||||
|
||||
#: storages/jobs.py:122
|
||||
#| msgid "Invalid file size."
|
||||
msgid "Invalid file size"
|
||||
msgstr "文件大小无效"
|
||||
|
||||
#: storages/jobs.py:124
|
||||
#| msgid "Invalid chunk index or total chunks."
|
||||
msgid "Invalid chunk index or total chunks"
|
||||
msgstr "分块索引或分块总数无效"
|
||||
|
||||
#: storages/templates/create_stg_block.html:7
|
||||
msgid "New pool"
|
||||
msgstr "新建存储池"
|
||||
|
|
@ -9274,6 +9325,11 @@ msgstr "XML 无效:%(err)s"
|
|||
msgid "The XML may not declare a DTD"
|
||||
msgstr "XML 不能声明 DTD"
|
||||
|
||||
#: vrtManager/util.py:143
|
||||
#, python-format
|
||||
msgid "MAC address must be of the format AA:BB:CC:DD:EE:FF, was %(mac)s"
|
||||
msgstr "MAC 地址的格式必须为 AA:BB:CC:DD:EE:FF,实际为 %(mac)s"
|
||||
|
||||
#: vrtManager/virtsecrets.py:26
|
||||
msgid "A Ceph key is base64 text, as `ceph auth get-key` prints it"
|
||||
msgstr "Ceph 密钥是 base64 文本,即 `ceph auth get-key` 输出的内容"
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ from django.db import transaction
|
|||
from django.db.models import F, Value
|
||||
from django.db.models.functions import Coalesce
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext_noop
|
||||
from django.utils.translation import gettext, gettext_noop
|
||||
|
||||
from logs.audit import write
|
||||
from vrtManager.util import OperationError
|
||||
|
|
@ -148,7 +148,7 @@ def _existing(user, key, kind_name, params):
|
|||
Operation.objects.filter(pk=op.pk).update(submission_key=f"expired:{op.pk.hex}")
|
||||
return None
|
||||
if op.kind != kind_name or op.params != params:
|
||||
raise SubmissionConflict("This form was already submitted for another request; reload the page")
|
||||
raise SubmissionConflict(gettext("This form was already submitted for another request; reload the page"))
|
||||
return op
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from appsettings.settings import app_settings
|
|||
from computes.models import Compute
|
||||
from computes.utils import libvirt_compute_lock
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils.translation import gettext as _
|
||||
from drf_spectacular.utils import extend_schema, inline_serializer
|
||||
from libvirt import libvirtError
|
||||
from logs.audit import audit, failed
|
||||
|
|
@ -199,7 +200,7 @@ class VolumeViewSet(viewsets.ViewSet):
|
|||
return Response({"status": "Volume: " + ret + " is created"})
|
||||
else:
|
||||
failed(request, compute, subject, action, "pool is not active")
|
||||
return Response({"status": "Pool is not active"}, status=status.HTTP_409_CONFLICT)
|
||||
return Response({"status": _("Pool is not active")}, status=status.HTTP_409_CONFLICT)
|
||||
else:
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
|
@ -220,4 +221,4 @@ class VolumeViewSet(viewsets.ViewSet):
|
|||
return Response({"status": "Volume: " + pk + " is deleted"})
|
||||
else:
|
||||
failed(request, compute, subject, action, "pool is not active")
|
||||
return Response({"status": "Pool is not active"}, status=status.HTTP_409_CONFLICT)
|
||||
return Response({"status": _("Pool is not active")}, status=status.HTTP_409_CONFLICT)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import logging
|
|||
import os
|
||||
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.utils.translation import gettext_noop
|
||||
from django.utils.translation import gettext, gettext_noop
|
||||
from libvirt import libvirtError
|
||||
from vrtManager import util
|
||||
from vrtManager.storage import wvmStorage
|
||||
|
|
@ -115,13 +115,13 @@ class UploadVolume(Kind):
|
|||
raise PermissionDenied
|
||||
name = params["name"]
|
||||
if not name or name in (".", "..") or "\x00" in name:
|
||||
raise ValueError("Invalid file name")
|
||||
raise ValueError(gettext("Invalid file name"))
|
||||
if len(name) <= 4 or not is_iso(name):
|
||||
raise ValueError("Only .iso files can be uploaded")
|
||||
raise ValueError(gettext("Only .iso files can be uploaded"))
|
||||
if params["size"] <= 0:
|
||||
raise ValueError("Invalid file size")
|
||||
raise ValueError(gettext("Invalid file size"))
|
||||
if params["chunks"] < 1:
|
||||
raise ValueError("Invalid chunk index or total chunks")
|
||||
raise ValueError(gettext("Invalid chunk index or total chunks"))
|
||||
|
||||
def plan(self, user, params):
|
||||
compute, stg = _storage(params)
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ def validate_macaddr(val):
|
|||
|
||||
form = re.match("^([0-9a-fA-F]{1,2}:){5}[0-9a-fA-F]{1,2}$", val)
|
||||
if form is None:
|
||||
raise ValueError("MAC address must be of the format AA:BB:CC:DD:EE:FF, was {}".format(val))
|
||||
raise ValueError(gettext("MAC address must be of the format AA:BB:CC:DD:EE:FF, was %(mac)s") % {"mac": val})
|
||||
|
||||
|
||||
def vol_dev_type(vol_bus):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue