Address some pylint issues
This commit is contained in:
parent
15c70db229
commit
5517b428fc
@ -119,6 +119,8 @@ class ActionQueue:
|
|||||||
app_queue.process()
|
app_queue.process()
|
||||||
# If the actions finished without errors, restore nominal state by deleting the item from action list
|
# If the actions finished without errors, restore nominal state by deleting the item from action list
|
||||||
self.clear_action(app_name)
|
self.clear_action(app_name)
|
||||||
|
except (SystemExit, KeyboardInterrupt):
|
||||||
|
raise
|
||||||
except BaseException as e:
|
except BaseException as e:
|
||||||
# If the action failed, store the exception and leave it in the list for manual clearance
|
# If the action failed, store the exception and leave it in the list for manual clearance
|
||||||
with self.lock:
|
with self.lock:
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import dns.exception
|
|
||||||
import dns.resolver
|
import dns.resolver
|
||||||
import requests
|
import requests
|
||||||
import socket
|
import socket
|
||||||
@ -21,7 +20,7 @@ def get_local_ip(version=None):
|
|||||||
output = subprocess.run(['/sbin/ip', 'route', 'get', '1' if version == 4 else '2003::'], check=True, stdout=subprocess.PIPE).stdout.decode().split()
|
output = subprocess.run(['/sbin/ip', 'route', 'get', '1' if version == 4 else '2003::'], check=True, stdout=subprocess.PIPE).stdout.decode().split()
|
||||||
# Get field right after 'src'
|
# Get field right after 'src'
|
||||||
return output[output.index('src')+1]
|
return output[output.index('src')+1]
|
||||||
except:
|
except (subprocess.CalledProcessError, ValueError, IndexError):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_external_ip(version):
|
def get_external_ip(version):
|
||||||
@ -31,7 +30,7 @@ def get_external_ip(version):
|
|||||||
try:
|
try:
|
||||||
requests.packages.urllib3.util.connection.allowed_gai_family = lambda: family
|
requests.packages.urllib3.util.connection.allowed_gai_family = lambda: family
|
||||||
return requests.get(paths.MYIP_URL, timeout=5).text
|
return requests.get(paths.MYIP_URL, timeout=5).text
|
||||||
except:
|
except requests.RequestException:
|
||||||
return None
|
return None
|
||||||
finally:
|
finally:
|
||||||
requests.packages.urllib3.util.connection.allowed_gai_family = allowed_gai_family
|
requests.packages.urllib3.util.connection.allowed_gai_family = allowed_gai_family
|
||||||
@ -45,15 +44,8 @@ def resolve_ip(domain, query_type):
|
|||||||
# Resolve domain name using Google Public DNS
|
# Resolve domain name using Google Public DNS
|
||||||
try:
|
try:
|
||||||
return resolver.query(domain, query_type)[0].address
|
return resolver.query(domain, query_type)[0].address
|
||||||
except dns.exception.Timeout:
|
except dns.resolver.NXDOMAIN:
|
||||||
raise
|
|
||||||
except:
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def ping_url(url):
|
def ping_url(url):
|
||||||
try:
|
return requests.get(paths.PING_URL, params={'url': url}, timeout=5).text == 'vm-pong'
|
||||||
return requests.get(paths.PING_URL, params={'url': url}, timeout=5).text == 'vm-pong'
|
|
||||||
except requests.exceptions.Timeout:
|
|
||||||
raise
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
@ -9,6 +9,8 @@ ACME_CRON = '/etc/periodic/daily/acme.sh'
|
|||||||
ACME_DIR = '/etc/acme.sh.d'
|
ACME_DIR = '/etc/acme.sh.d'
|
||||||
CERT_KEY_FILE = '/etc/ssl/services.key'
|
CERT_KEY_FILE = '/etc/ssl/services.key'
|
||||||
CERT_PUB_FILE = '/etc/ssl/services.pem'
|
CERT_PUB_FILE = '/etc/ssl/services.pem'
|
||||||
|
TMP_KEY_FILE = '/tmp/private.pem'
|
||||||
|
TMP_PUB_FILE = '/tmp/public.pem'
|
||||||
|
|
||||||
# OS
|
# OS
|
||||||
ISSUE_FILE = '/etc/issue'
|
ISSUE_FILE = '/etc/issue'
|
||||||
|
@ -13,9 +13,8 @@ def is_valid_port(port):
|
|||||||
try:
|
try:
|
||||||
port = int(port)
|
port = int(port)
|
||||||
return 0 < port < 65536 and port not in (22, 25, 80, 8080)
|
return 0 < port < 65536 and port not in (22, 25, 80, 8080)
|
||||||
except:
|
except TypeError:
|
||||||
pass
|
return False
|
||||||
return False
|
|
||||||
|
|
||||||
def is_valid_email(email):
|
def is_valid_email(email):
|
||||||
parts = email.split('@')
|
parts = email.split('@')
|
||||||
@ -26,6 +25,5 @@ def is_valid_repo_url(url):
|
|||||||
try:
|
try:
|
||||||
parsed = urlparse(url)
|
parsed = urlparse(url)
|
||||||
return parsed.scheme in ('http', 'https') and not parsed.params and not parsed.query and not parsed.fragment
|
return parsed.scheme in ('http', 'https') and not parsed.params and not parsed.query and not parsed.fragment
|
||||||
except:
|
except ValueError:
|
||||||
pass
|
return False
|
||||||
return False
|
|
||||||
|
@ -14,6 +14,15 @@ from spoc.image import Image
|
|||||||
|
|
||||||
from . import config, crypto, net, paths, templates
|
from . import config, crypto, net, paths, templates
|
||||||
|
|
||||||
|
BIN_ACME_SH = '/usr/bin/acme.sh'
|
||||||
|
BIN_BLKID = '/sbin/blkid'
|
||||||
|
BIN_CRYPTSETUP = '/sbin/cryptsetup'
|
||||||
|
BIN_NGINX = '/usr/sbin/nginx'
|
||||||
|
BIN_POWEROFF = '/sbin/poweroff'
|
||||||
|
BIN_REBOOT = '/sbin/reboot'
|
||||||
|
BIN_SERVICE = '/sbin/service'
|
||||||
|
CRYPTTAB_FILE = '/etc/crypttab'
|
||||||
|
|
||||||
def register_app(app, host, login, password):
|
def register_app(app, host, login, password):
|
||||||
# Register newly installed application, its subdomain and credentials (called at the end of package install.sh)
|
# Register newly installed application, its subdomain and credentials (called at the end of package install.sh)
|
||||||
config.register_app(app, {
|
config.register_app(app, {
|
||||||
@ -58,10 +67,10 @@ def update_host(domain, port):
|
|||||||
update_app_config()
|
update_app_config()
|
||||||
|
|
||||||
def reload_nginx():
|
def reload_nginx():
|
||||||
subprocess.run(['/usr/sbin/nginx', '-s', 'reload'])
|
subprocess.run([BIN_NGINX, '-s', 'reload'])
|
||||||
|
|
||||||
def restart_nginx():
|
def restart_nginx():
|
||||||
subprocess.run(['/sbin/service', 'nginx', 'restart'])
|
subprocess.run([BIN_SERVICE, 'nginx', 'restart'])
|
||||||
|
|
||||||
def rebuild_issue():
|
def rebuild_issue():
|
||||||
# Compile the URLs displayed in terminal banner and rebuild the issue and motd files
|
# Compile the URLs displayed in terminal banner and rebuild the issue and motd files
|
||||||
@ -83,9 +92,9 @@ def update_common_settings(email, gmaps_api_key):
|
|||||||
def update_password(oldpassword, newpassword):
|
def update_password(oldpassword, newpassword):
|
||||||
# Update LUKS password and adminpwd for WSGI application
|
# Update LUKS password and adminpwd for WSGI application
|
||||||
pwinput = f'{oldpassword}\n{newpassword}'.encode()
|
pwinput = f'{oldpassword}\n{newpassword}'.encode()
|
||||||
partition_uuid = open('/etc/crypttab').read().split()[1][5:]
|
partition_uuid = open(CRYPTTAB_FILE).read().split()[1][5:]
|
||||||
partition_name = subprocess.run(['/sbin/blkid', '-U', partition_uuid], check=True, stdout=subprocess.PIPE).stdout.decode().strip()
|
partition_name = subprocess.run([BIN_BLKID, '-U', partition_uuid], check=True, stdout=subprocess.PIPE).stdout.decode().strip()
|
||||||
subprocess.run(['/sbin/cryptsetup', 'luksChangeKey', partition_name], input=pwinput, check=True)
|
subprocess.run([BIN_CRYPTSETUP, 'luksChangeKey', partition_name], input=pwinput, check=True)
|
||||||
# Update bcrypt-hashed password in config
|
# Update bcrypt-hashed password in config
|
||||||
hash = crypto.adminpwd_hash(newpassword)
|
hash = crypto.adminpwd_hash(newpassword)
|
||||||
config.set_host('adminpwd', hash)
|
config.set_host('adminpwd', hash)
|
||||||
@ -106,12 +115,12 @@ def request_acme_cert():
|
|||||||
certs = [i for i in os.listdir(paths.ACME_DIR) if i not in ('account.conf', 'ca', 'http.header')]
|
certs = [i for i in os.listdir(paths.ACME_DIR) if i not in ('account.conf', 'ca', 'http.header')]
|
||||||
for cert in certs:
|
for cert in certs:
|
||||||
if cert != domain:
|
if cert != domain:
|
||||||
subprocess.run(['/usr/bin/acme.sh', '--home', paths.ACME_DIR, '--remove', '-d', cert])
|
subprocess.run([BIN_ACME_SH, '--home', paths.ACME_DIR, '--remove', '-d', cert])
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
# Compile an acme.sh command for certificate requisition only if the certificate hasn't been requested before
|
# Compile an acme.sh command for certificate requisition only if the certificate hasn't been requested before
|
||||||
if not os.path.exists(os.path.join(paths.ACME_DIR, domain)):
|
if not os.path.exists(os.path.join(paths.ACME_DIR, domain)):
|
||||||
cmd = ['/usr/bin/acme.sh', '--home', paths.ACME_DIR, '--issue', '-d', domain]
|
cmd = [BIN_ACME_SH, '--home', paths.ACME_DIR, '--issue', '-d', domain]
|
||||||
for app,definition in config.get_apps().items():
|
for app,definition in config.get_apps().items():
|
||||||
cmd += ['-d', f'{definition["host"]}.{domain}']
|
cmd += ['-d', f'{definition["host"]}.{domain}']
|
||||||
cmd += ['-w', paths.ACME_DIR]
|
cmd += ['-w', paths.ACME_DIR]
|
||||||
@ -120,13 +129,13 @@ def request_acme_cert():
|
|||||||
# Otherwise just try to renew
|
# Otherwise just try to renew
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
subprocess.run(['/usr/bin/acme.sh', '--home', paths.ACME_DIR, '--renew', '-d', domain], check=True)
|
subprocess.run([BIN_ACME_SH, '--home', paths.ACME_DIR, '--renew', '-d', domain], check=True)
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
# return code 2 means skipped renew, which is OK
|
# return code 2 means skipped renew, which is OK
|
||||||
if e.returncode != 2:
|
if e.returncode != 2:
|
||||||
raise
|
raise
|
||||||
# Install the issued certificate
|
# Install the issued certificate
|
||||||
subprocess.run(['/usr/bin/acme.sh', '--home', paths.ACME_DIR, '--install-cert', '-d', domain, '--key-file', paths.CERT_KEY_FILE, '--fullchain-file', paths.CERT_PUB_FILE, '--reloadcmd', '/sbin/service nginx reload'], check=True)
|
subprocess.run([BIN_ACME_SH, '--home', paths.ACME_DIR, '--install-cert', '-d', domain, '--key-file', paths.CERT_KEY_FILE, '--fullchain-file', paths.CERT_PUB_FILE, '--reloadcmd', f'{BIN_SERVICE} nginx reload'], check=True)
|
||||||
# Enable acme.sh cronjob
|
# Enable acme.sh cronjob
|
||||||
os.chmod(paths.ACME_CRON, 0o750)
|
os.chmod(paths.ACME_CRON, 0o750)
|
||||||
|
|
||||||
@ -141,10 +150,10 @@ def install_manual_cert(public_file, private_file):
|
|||||||
reload_nginx()
|
reload_nginx()
|
||||||
|
|
||||||
def shutdown_vm():
|
def shutdown_vm():
|
||||||
subprocess.run(['/sbin/poweroff'])
|
subprocess.run([BIN_POWEROFF])
|
||||||
|
|
||||||
def reboot_vm():
|
def reboot_vm():
|
||||||
subprocess.run(['/sbin/reboot'])
|
subprocess.run([BIN_REBOOT])
|
||||||
|
|
||||||
def start_app(app_name, queue):
|
def start_app(app_name, queue):
|
||||||
# Enqueue application start
|
# Enqueue application start
|
||||||
@ -182,7 +191,7 @@ def update_app_config(app_name=None):
|
|||||||
env['HOST'] = f'{apps[app_name]["host"]}.{host["domain"]}'
|
env['HOST'] = f'{apps[app_name]["host"]}.{host["domain"]}'
|
||||||
try:
|
try:
|
||||||
subprocess.run(script_path, cwd=install_dir, env=env)
|
subprocess.run(script_path, cwd=install_dir, env=env)
|
||||||
except:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def is_app_started(app_name):
|
def is_app_started(app_name):
|
||||||
|
@ -1,19 +1,22 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import cryptography.exceptions
|
||||||
|
import dns.exception
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from cryptography.exceptions import InvalidSignature
|
import requests.exceptions
|
||||||
|
import subprocess
|
||||||
from jinja2 import Environment, FileSystemLoader
|
from jinja2 import Environment, FileSystemLoader
|
||||||
from math import floor
|
from math import floor
|
||||||
from pkg_resources import parse_version
|
from pkg_resources import parse_version
|
||||||
from spoc import repo_online, repo_local
|
from spoc import repo_online, repo_local
|
||||||
from werkzeug.exceptions import HTTPException, NotFound, Unauthorized
|
from werkzeug.exceptions import HTTPException, NotFound
|
||||||
from werkzeug.routing import Map, Rule
|
from werkzeug.routing import Map, Rule
|
||||||
from werkzeug.utils import redirect
|
from werkzeug.utils import redirect
|
||||||
from werkzeug.wrappers import Request, Response
|
from werkzeug.wrappers import Request, Response
|
||||||
|
|
||||||
from . import config, crypto, net, remote, validator, vmmgr
|
from . import config, crypto, net, paths, remote, validator, vmmgr
|
||||||
from .actionqueue import ActionQueue, ActionItemType
|
from .actionqueue import ActionQueue, ActionItemType
|
||||||
from .wsgilang import WSGILang
|
from .wsgilang import WSGILang
|
||||||
from .wsgisession import WSGISession
|
from .wsgisession import WSGISession
|
||||||
@ -166,12 +169,13 @@ class WSGIApp:
|
|||||||
try:
|
try:
|
||||||
# Repopulate online_repo cache or fail early when the repo can't be reached
|
# Repopulate online_repo cache or fail early when the repo can't be reached
|
||||||
repo_online.load(True)
|
repo_online.load(True)
|
||||||
except InvalidSignature:
|
except cryptography.exceptions.InvalidSignature:
|
||||||
repo_error = request.session.lang.invalid_packages_signature()
|
repo_error = request.session.lang.invalid_packages_signature()
|
||||||
except Unauthorized:
|
except requests.exceptions.RequestException as e:
|
||||||
repo_error = request.session.lang.repo_invalid_credentials()
|
if isinstance(e, requests.exceptions.HTTPError) and e.response.status_code == 401:
|
||||||
except:
|
repo_error = request.session.lang.repo_invalid_credentials()
|
||||||
repo_error = request.session.lang.repo_unavailable()
|
else:
|
||||||
|
repo_error = request.session.lang.repo_unavailable()
|
||||||
table = self.render_setup_apps_table(request)
|
table = self.render_setup_apps_table(request)
|
||||||
message = self.get_session_message(request)
|
message = self.get_session_message(request)
|
||||||
repo_conf = vmmgr.get_repo_conf()
|
repo_conf = vmmgr.get_repo_conf()
|
||||||
@ -183,7 +187,7 @@ class WSGIApp:
|
|||||||
local_apps = repo_local.get_apps()
|
local_apps = repo_local.get_apps()
|
||||||
try:
|
try:
|
||||||
online_apps = repo_online.get_apps()
|
online_apps = repo_online.get_apps()
|
||||||
except:
|
except (cryptography.exceptions.InvalidSignature, requests.exceptions.RequestException):
|
||||||
online_apps = {}
|
online_apps = {}
|
||||||
apps_config = config.get_apps()
|
apps_config = config.get_apps()
|
||||||
actionable_apps = sorted(set(online_apps) | set(local_apps))
|
actionable_apps = sorted(set(online_apps) | set(local_apps))
|
||||||
@ -205,7 +209,7 @@ class WSGIApp:
|
|||||||
app_queue = pending_actions[app]
|
app_queue = pending_actions[app]
|
||||||
if app_queue.exception:
|
if app_queue.exception:
|
||||||
# Display failed task
|
# Display failed task
|
||||||
if isinstance(app_queue.exception, InvalidSignature):
|
if isinstance(app_queue.exception, cryptography.exceptions.InvalidSignature):
|
||||||
status = lang.repo_package_invalid_signature()
|
status = lang.repo_package_invalid_signature()
|
||||||
elif isinstance(app_queue.exception, NotFound):
|
elif isinstance(app_queue.exception, NotFound):
|
||||||
status = lang.repo_package_missing()
|
status = lang.repo_package_missing()
|
||||||
@ -314,8 +318,8 @@ class WSGIApp:
|
|||||||
return self.render_json({'error': request.session.lang.dns_record_mismatch(domain, a, ipv4)})
|
return self.render_json({'error': request.session.lang.dns_record_mismatch(domain, a, ipv4)})
|
||||||
if aaaa and aaaa != ipv6:
|
if aaaa and aaaa != ipv6:
|
||||||
return self.render_json({'error': request.session.lang.dns_record_mismatch(domain, aaaa, ipv6)})
|
return self.render_json({'error': request.session.lang.dns_record_mismatch(domain, aaaa, ipv6)})
|
||||||
except:
|
except dns.exception.DNSException:
|
||||||
return self.render_json({'error': request.session.lang.dns_timeout()})
|
return self.render_json({'error': request.session.lang.dns_error()})
|
||||||
return self.render_json({'ok': request.session.lang.dns_records_ok()})
|
return self.render_json({'ok': request.session.lang.dns_records_ok()})
|
||||||
|
|
||||||
def verify_http_action(self, request, **kwargs):
|
def verify_http_action(self, request, **kwargs):
|
||||||
@ -327,10 +331,11 @@ class WSGIApp:
|
|||||||
for domain in domains:
|
for domain in domains:
|
||||||
url = net.compile_url(domain, port, proto)
|
url = net.compile_url(domain, port, proto)
|
||||||
try:
|
try:
|
||||||
if not net.ping_url(url):
|
net.ping_url(url)
|
||||||
return self.render_json({'error': request.session.lang.http_host_not_reachable(url)})
|
except requests.exceptions.Timeout:
|
||||||
except:
|
|
||||||
return self.render_json({'error': request.session.lang.http_timeout()})
|
return self.render_json({'error': request.session.lang.http_timeout()})
|
||||||
|
except requests.exceptions.RequestException:
|
||||||
|
return self.render_json({'error': request.session.lang.http_host_not_reachable(url)})
|
||||||
return self.render_json({'ok': request.session.lang.http_hosts_ok(port)})
|
return self.render_json({'ok': request.session.lang.http_hosts_ok(port)})
|
||||||
|
|
||||||
def update_cert_action(self, request):
|
def update_cert_action(self, request):
|
||||||
@ -344,11 +349,11 @@ class WSGIApp:
|
|||||||
return self.render_json({'error': request.session.lang.cert_file_missing()})
|
return self.render_json({'error': request.session.lang.cert_file_missing()})
|
||||||
if not request.files['private']:
|
if not request.files['private']:
|
||||||
return self.render_json({'error': request.session.lang.key_file_missing()})
|
return self.render_json({'error': request.session.lang.key_file_missing()})
|
||||||
request.files['public'].save('/tmp/public.pem')
|
request.files['public'].save(paths.TMP_PUB_FILE)
|
||||||
request.files['private'].save('/tmp/private.pem')
|
request.files['private'].save(paths.TMP_KEY_FILE)
|
||||||
vmmgr.install_manual_cert('/tmp/public.pem', '/tmp/private.pem')
|
vmmgr.install_manual_cert(paths.TMP_PUB_FILE, paths.TMP_KEY_FILE)
|
||||||
os.unlink('/tmp/public.pem')
|
os.unlink(paths.TMP_PUB_FILE)
|
||||||
os.unlink('/tmp/private.pem')
|
os.unlink(paths.TMP_KEY_FILE)
|
||||||
else:
|
else:
|
||||||
return self.render_json({'error': request.session.lang.cert_request_error()})
|
return self.render_json({'error': request.session.lang.cert_request_error()})
|
||||||
host = config.get_host()
|
host = config.get_host()
|
||||||
@ -424,14 +429,14 @@ class WSGIApp:
|
|||||||
|
|
||||||
def update_password_action(self, request):
|
def update_password_action(self, request):
|
||||||
# Updates password for both HDD encryption (LUKS-on-LVM) and web interface admin account
|
# Updates password for both HDD encryption (LUKS-on-LVM) and web interface admin account
|
||||||
|
if request.form['newpassword'] != request.form['newpassword2']:
|
||||||
|
return self.render_json({'error': request.session.lang.password_mismatch()})
|
||||||
|
if request.form['newpassword'] == '':
|
||||||
|
return self.render_json({'error': request.session.lang.password_empty()})
|
||||||
try:
|
try:
|
||||||
if request.form['newpassword'] != request.form['newpassword2']:
|
|
||||||
return self.render_json({'error': request.session.lang.password_mismatch()})
|
|
||||||
if request.form['newpassword'] == '':
|
|
||||||
return self.render_json({'error': request.session.lang.password_empty()})
|
|
||||||
# No need to explicitly validate old password, vmmgr.update_password will raise exception if it's wrong
|
# No need to explicitly validate old password, vmmgr.update_password will raise exception if it's wrong
|
||||||
vmmgr.update_password(request.form['oldpassword'], request.form['newpassword'])
|
vmmgr.update_password(request.form['oldpassword'], request.form['newpassword'])
|
||||||
except:
|
except subprocess.CalledProcessError:
|
||||||
return self.render_json({'error': request.session.lang.bad_password()})
|
return self.render_json({'error': request.session.lang.bad_password()})
|
||||||
return self.render_json({'ok': request.session.lang.password_changed()})
|
return self.render_json({'ok': request.session.lang.password_changed()})
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ class WSGILang:
|
|||||||
'host_updated': 'Nastavení hostitele bylo úspěšně změněno. Přejděte na URL <a href="{}">{}</a> a pokračujte následujícími kroky.',
|
'host_updated': 'Nastavení hostitele bylo úspěšně změněno. Přejděte na URL <a href="{}">{}</a> a pokračujte následujícími kroky.',
|
||||||
'dns_record_does_not_exist': 'DNS záznam pro název "{}" neexistuje.',
|
'dns_record_does_not_exist': 'DNS záznam pro název "{}" neexistuje.',
|
||||||
'dns_record_mismatch': 'DNS záznam pro název "{}" směřuje na IP {} místo očekávané {}.',
|
'dns_record_mismatch': 'DNS záznam pro název "{}" směřuje na IP {} místo očekávané {}.',
|
||||||
'dns_timeout': 'Nepodařilo se kontaktovat DNS server. Zkontrolujte, zda má virtuální stroj přístup k internetu.',
|
'dns_error': 'Nepodařilo se získat platnou odpověď z DNS serveru. Zkontrolujte, zda má virtuální stroj přístup k internetu.',
|
||||||
'dns_records_ok': 'DNS záznamy jsou nastaveny správně.',
|
'dns_records_ok': 'DNS záznamy jsou nastaveny správně.',
|
||||||
'http_host_not_reachable': 'Adresa {} není dostupná z internetu. Zkontrolujte nastavení síťových komponent.',
|
'http_host_not_reachable': 'Adresa {} není dostupná z internetu. Zkontrolujte nastavení síťových komponent.',
|
||||||
'http_timeout': 'Nepodařilo se kontaktovat ping server. Zkontrolujte, zda má virtuální stroj přístup k internetu.',
|
'http_timeout': 'Nepodařilo se kontaktovat ping server. Zkontrolujte, zda má virtuální stroj přístup k internetu.',
|
||||||
|
Loading…
Reference in New Issue
Block a user