1
0
mirror of https://github.com/dkmstr/openuds.git synced 2025-01-08 21:18:00 +03:00

Fixed servers basic information

This commit is contained in:
Adolfo Gómez García 2024-04-17 19:56:22 +02:00
parent 60c5f37591
commit 2822013b36
No known key found for this signature in database
GPG Key ID: DD1ABF20724CDA23
9 changed files with 76 additions and 40 deletions

View File

@ -37,7 +37,7 @@ from django.utils.translation import gettext_lazy as _
from uds import models from uds import models
from uds.core import consts, types, ui from uds.core import consts, types, ui
from uds.core.util import permissions, ensure from uds.core.util import net, permissions, ensure
from uds.core.util.model import sql_datetime, process_uuid from uds.core.util.model import sql_datetime, process_uuid
from uds.core.exceptions.rest import NotFound, RequestError from uds.core.exceptions.rest import NotFound, RequestError
from uds.REST.model import DetailHandler, ModelHandler from uds.REST.model import DetailHandler, ModelHandler
@ -127,7 +127,7 @@ class ServersServers(DetailHandler):
'hostname': i.hostname, 'hostname': i.hostname,
'ip': i.ip, 'ip': i.ip,
'listen_port': i.listen_port, 'listen_port': i.listen_port,
'mac': i.mac if not multi or i.mac != consts.MAC_UNKNOWN else '', 'mac': i.mac if not multi and i.mac != consts.MAC_UNKNOWN else '',
'maintenance_mode': i.maintenance_mode, 'maintenance_mode': i.maintenance_mode,
} }
res.append(val) res.append(val)
@ -149,14 +149,23 @@ class ServersServers(DetailHandler):
def get_fields(self, parent: 'Model') -> list[typing.Any]: def get_fields(self, parent: 'Model') -> list[typing.Any]:
parent = ensure.is_instance(parent, models.ServerGroup) parent = ensure.is_instance(parent, models.ServerGroup)
return [ return (
[
{ {
'hostname': { 'hostname': {
'title': _('Hostname'), 'title': _('Hostname'),
} }
}, },
{'ip': {'title': _('Ip')}}, {'ip': {'title': _('Ip')}},
] # If not managed, we can show mac, else listen port (related to UDS Server)
+ (
[
{'mac': {'title': _('Mac')}}, {'mac': {'title': _('Mac')}},
]
if not parent.is_managed()
else [{'listen_port': {'title': _('Port')}}]
)
+ [
{ {
'maintenance_mode': { 'maintenance_mode': {
'title': _('State'), 'title': _('State'),
@ -165,6 +174,7 @@ class ServersServers(DetailHandler):
} }
}, },
] ]
)
def get_row_style(self, parent: 'Model') -> types.ui.RowStyleInfo: def get_row_style(self, parent: 'Model') -> types.ui.RowStyleInfo:
return types.ui.RowStyleInfo(prefix='row-maintenance-', field='maintenance_mode') return types.ui.RowStyleInfo(prefix='row-maintenance-', field='maintenance_mode')
@ -194,11 +204,11 @@ class ServersServers(DetailHandler):
'order': 101, # At end 'order': 101, # At end
}, },
{ {
'name': 'listen_port', 'name': 'mac',
'value': 0, 'value': '',
'label': gettext('Port'), 'label': gettext('Server MAC'),
'tooltip': gettext('Port of server. 0 means "service default"'), 'tooltip': gettext('Optional MAC address of the server'),
'type': types.ui.FieldType.NUMERIC, 'type': types.ui.FieldType.TEXT,
'order': 102, # At end 'order': 102, # At end
}, },
{ {
@ -241,12 +251,17 @@ class ServersServers(DetailHandler):
if item is None: if item is None:
# Create new, depending on server type # Create new, depending on server type
if parent.type == types.servers.ServerType.UNMANAGED: if parent.type == types.servers.ServerType.UNMANAGED:
# Ensure mac is emty or valid
mac: str = self._params['mac'].strip().upper()
if mac and not net.is_valid_mac(mac):
raise self.invalid_request_response('Invalid MAC address')
# Create a new one, and add it to group # Create a new one, and add it to group
server = models.Server.objects.create( server = models.Server.objects.create(
ip_from='::1', ip_from='::1',
ip=self._params['ip'], ip=self._params['ip'],
hostname=self._params['hostname'], hostname=self._params['hostname'],
listen_port=self._params['listen_port'] or 0, listen_port=0,
mac=mac,
type=parent.type, type=parent.type,
subtype=parent.subtype, subtype=parent.subtype,
stamp=sql_datetime(), stamp=sql_datetime(),
@ -266,6 +281,20 @@ class ServersServers(DetailHandler):
except Exception: except Exception:
raise self.invalid_item_response() from None raise self.invalid_item_response() from None
pass pass
else:
if parent.type == types.servers.ServerType.UNMANAGED:
mac: str = self._params['mac'].strip().upper()
if mac and not net.is_valid_mac(mac):
raise self.invalid_request_response('Invalid MAC address')
try:
models.Server.objects.filter(uuid=process_uuid(item)).update(
ip=self._params['ip'],
hostname=self._params['hostname'],
mac=mac,
)
except Exception:
raise self.invalid_item_response() from None
else: else:
try: try:
server = models.Server.objects.get(uuid=process_uuid(item)) server = models.Server.objects.get(uuid=process_uuid(item))
@ -273,8 +302,6 @@ class ServersServers(DetailHandler):
except Exception: except Exception:
raise self.invalid_item_response() from None raise self.invalid_item_response() from None
raise self.invalid_request_response() from None
def delete_item(self, parent: 'Model', item: str) -> None: def delete_item(self, parent: 'Model', item: str) -> None:
parent = ensure.is_instance(parent, models.ServerGroup) parent = ensure.is_instance(parent, models.ServerGroup)
try: try:

View File

@ -63,9 +63,10 @@ def restrain_server(func: collections.abc.Callable[..., typing.Any]) -> collecti
try: try:
return func(self, *args, **kwargs) return func(self, *args, **kwargs)
except Exception as e: except Exception as e:
logger.error('Error executing %s: %s', func.__name__, e) restrained_until = sql_datetime() + datetime.timedelta(seconds=consts.system.FAILURE_TIMEOUT)
logger.exception('Error executing %s: %s. Server restrained until %s', func.__name__, e, restrained_until)
self.server.set_restrained_until( self.server.set_restrained_until(
sql_datetime() + datetime.timedelta(seconds=consts.system.FAILURE_TIMEOUT) restrained_until
) # Block server for a while ) # Block server for a while
return False return False

View File

@ -278,6 +278,9 @@ def is_valid_host(value: str) -> bool:
return is_valid_ip(value) or is_valid_fqdn(value) return is_valid_ip(value) or is_valid_fqdn(value)
def is_valid_mac(value: str) -> bool:
return re.match(r'^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$', value) is not None
def test_connectivity(host: str, port: int, timeout: float = 4) -> bool: def test_connectivity(host: str, port: int, timeout: float = 4) -> bool:
try: try:
logger.debug('Checking connection to %s:%s with %s seconds timeout', host, port, timeout) logger.debug('Checking connection to %s:%s with %s seconds timeout', host, port, timeout)

View File

@ -47,12 +47,12 @@ def resolve(hostname: str, rdtype: typing.Optional[str] = None) -> list[str]:
for i in ('A', 'AAAA'): for i in ('A', 'AAAA'):
try: try:
ips.extend([str(ip) for ip in typing.cast(collections.abc.Iterable[typing.Any], dns.resolver.resolve(hostname, i))]) ips.extend([str(ip) for ip in typing.cast(collections.abc.Iterable[typing.Any], dns.resolver.resolve(hostname, i))])
except dns.resolver.NXDOMAIN: except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
pass pass
else: else:
try: try:
ips.extend([str(ip) for ip in typing.cast(collections.abc.Iterable[typing.Any], dns.resolver.resolve(hostname, rdtype))]) ips.extend([str(ip) for ip in typing.cast(collections.abc.Iterable[typing.Any], dns.resolver.resolve(hostname, rdtype))])
except dns.resolver.NXDOMAIN: except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer): # Ignore NoAnswer, that is, the record does not exists
pass pass
return ips return ips
@ -63,5 +63,5 @@ def reverse_resolve(ip: str) -> list[str]:
""" """
try: try:
return[str(i).rstrip('.') for i in typing.cast(collections.abc.Iterable[typing.Any], dns.resolver.query(dns.reversename.from_address(ip).to_text(), 'PTR'))] return[str(i).rstrip('.') for i in typing.cast(collections.abc.Iterable[typing.Any], dns.resolver.query(dns.reversename.from_address(ip).to_text(), 'PTR'))]
except dns.resolver.NXDOMAIN: except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
return [] return []

View File

@ -396,10 +396,10 @@ class Storage:
for v in query: # @UndefinedVariable for v in query: # @UndefinedVariable
yield (v.key, codecs.decode(v.data.encode(), 'base64'), v.attr1) yield (v.key, codecs.decode(v.data.encode(), 'base64'), v.attr1)
def filter_unpickle( def filter_unpickle_by_attr(
self, attr1: typing.Optional[str] = None, forUpdate: bool = False self, attr1: typing.Optional[str] = None, for_update: bool = False
) -> collections.abc.Iterable[tuple[str, typing.Any, 'str|None']]: ) -> collections.abc.Iterable[tuple[str, typing.Any, 'str|None']]:
for v in self.filter(attr1, forUpdate): for v in self.filter(attr1, for_update):
yield (v[0], pickle.loads(v[1]), v[2]) # nosec: secure pickle load yield (v[0], pickle.loads(v[1]), v[2]) # nosec: secure pickle load
def clear(self) -> None: def clear(self) -> None:

View File

@ -108,6 +108,10 @@ class ServerGroup(UUIDModel, TaggingMixin, properties.PropertiesMixin):
"""Sets the server type of this server""" """Sets the server type of this server"""
self.type = value self.type = value
def is_managed(self) -> bool:
"""Returns if this server group is managed or not"""
return self.server_type != types.servers.ServerType.UNMANAGED
def https_url(self, path: str) -> str: def https_url(self, path: str) -> str:
if not path.startswith('/'): if not path.startswith('/'):
path = '/' + path path = '/' + path

File diff suppressed because one or more lines are too long

View File

@ -167,6 +167,7 @@ gettext("Exit maintenance mode?");
gettext("Enter maintenance mode?"); gettext("Enter maintenance mode?");
gettext("Maintenance mode for"); gettext("Maintenance mode for");
gettext("New server"); gettext("New server");
gettext("Edit server");
gettext("Remove server from server group"); gettext("Remove server from server group");
gettext("Information"); gettext("Information");
gettext("New service"); gettext("New service");

View File

@ -102,6 +102,6 @@
</svg> </svg>
</div> </div>
</uds-root> </uds-root>
<script src="/uds/res/admin/runtime.js?stamp=1710438355" type="module"></script><script src="/uds/res/admin/polyfills.js?stamp=1710438355" type="module"></script><script src="/uds/res/admin/main.js?stamp=1710438355" type="module"></script></body> <script src="/uds/res/admin/runtime.js?stamp=1713376179" type="module"></script><script src="/uds/res/admin/polyfills.js?stamp=1713376179" type="module"></script><script src="/uds/res/admin/main.js?stamp=1713376179" type="module"></script></body>
</html> </html>