diff --git a/server/src/uds/services/PhysicalMachines/provider.py b/server/src/uds/services/PhysicalMachines/provider.py index 5b37c083..c71b5e1d 100644 --- a/server/src/uds/services/PhysicalMachines/provider.py +++ b/server/src/uds/services/PhysicalMachines/provider.py @@ -30,13 +30,60 @@ """ @author: Adolfo Gómez, dkmaster at dkmon dot com """ +import configparser +import logging +import typing + from django.utils.translation import ugettext_noop as _ from uds.core import services +from uds.core.ui.user_interface import gui +from uds.core.util import net +if typing.TYPE_CHECKING: + from uds.core.module import Module + +logger = logging.getLogger(__name__) + +VALID_CONFIG_SECTIONS = set(('wol',)) class PhysicalMachinesProvider(services.ServiceProvider): # No extra data needed + config = gui.TextField( + length=8192, multiline=6, label=_('Advanced configuration'), order=3, + tooltip=_('Advanced configuration data for the provider'), + required=True, tab=gui.ADVANCED_TAB + ) + + def initialize(self, values: 'Module.ValuesType') -> None: + if values is None: + return + + self.config.value = self.config.value.strip() + + if self.config.value: + config = configparser.ConfigParser() + try: + config.read_string(self.config.value) + # Seems a valid configuration file, let's see if all se + except Exception as e: + raise services.ServiceProvider.ValidationException(_('Invalid advanced configuration: ') + str(e)) + + for section in config.sections(): + if section not in VALID_CONFIG_SECTIONS: + raise services.ServiceProvider.ValidationException(_('Invalid section in advanced configuration: ') + section) + + # Sections are valid, check values + # wol section + for key in config['wol']: + try: + net.networksFromString(key) # Raises exception if net is invalid + except Exception: + raise services.ServiceProvider.ValidationException(_('Invalid network in advanced configuration: ') + key) + # Now check value is an url + if config['wol'][key][:4] != 'http': + raise services.ServiceProvider.ValidationException(_('Invalid url in advanced configuration: ') + key) + # What services do we offer? typeName = _('Static IP Machines Provider') @@ -48,5 +95,20 @@ class PhysicalMachinesProvider(services.ServiceProvider): from .service_single import IPSingleMachineService offers = [IPMachinesService, IPSingleMachineService] + def wolURL(self, ip: str): + if not self.config.value: + return '' + + url = '' + try: + config = configparser.ConfigParser() + config.read_string(self.config.value) + for key in config['wol']: + if net.ipInNetwork(ip, key): + return config['wol'][key] + except Exception as e: + logger.error('Error parsing advanced configuration: %s', e) + + def __str__(self): return "Physical Machines Provider" diff --git a/server/src/uds/services/PhysicalMachines/service_base.py b/server/src/uds/services/PhysicalMachines/service_base.py index ebb2a861..b9fe21fd 100644 --- a/server/src/uds/services/PhysicalMachines/service_base.py +++ b/server/src/uds/services/PhysicalMachines/service_base.py @@ -30,7 +30,7 @@ """ @author: Adolfo Gómez, dkmaster at dkmon dot com """ -import subprocess +import requests import logging import typing @@ -38,19 +38,8 @@ from uds.core import services logger = logging.getLogger(__name__) -# We have included a "hidden testing" for adding ip+mac as static machines list. -# (This is done using IP;MAC as IP on the IP list) -# This is a test for WOL, and to be used at your risk. -# Example: -# WOLAPP = "/usr/sbin/etherwake {MAC} -i eth0 -b" -# Remember that you MUST setuid /usr/sbin/etherwake (chmod +s ....) and allow only for uds user, -# so it allows uds user to execute "privileged" etherwake program -# Note: -# {MAC} will be replaced with the MAC if it exists -# {IP} will be replaced with the IP of the machine -# If empty, no WOL will be tried NEVER, if not empty -WOLAPP = '' - +if typing.TYPE_CHECKING: + from . import provider class IPServiceBase(services.Service): @@ -65,6 +54,9 @@ class IPServiceBase(services.Service): except Exception: return None + def parent(self) -> 'provider.PhysicalMachinesProvider': + return typing.cast('provider.PhysicalMachinesProvider', super().parent()) + def getUnassignedMachine(self) -> typing.Optional[str]: raise NotADirectoryError('getUnassignedMachine') @@ -72,11 +64,12 @@ class IPServiceBase(services.Service): raise NotADirectoryError('unassignMachine') def wakeup(self, ip: str, mac: typing.Optional[str]) -> None: - if WOLAPP and mac: - cmd = WOLAPP.replace('{MAC}', mac or '').replace('{IP}', ip or '') - logger.info('Launching WOL: %s', cmd) - try: - result = subprocess.run(cmd, shell=True, check=True) - # logger.debug('Result: %s', result) - except Exception as e: - logger.error('Error on WOL: %s', e) + if mac: + wolurl = self.parent().wolURL(ip).replace('{MAC}', mac or '').replace('{IP}', ip or '') + if wolurl: + logger.info('Launching WOL: %s', wolurl) + try: + requests.get(wolurl, verify=False) + # logger.debug('Result: %s', result) + except Exception as e: + logger.error('Error on WOL: %s', e) diff --git a/server/src/uds/services/PhysicalMachines/service_multi.py b/server/src/uds/services/PhysicalMachines/service_multi.py index c40c71fb..04a7b573 100644 --- a/server/src/uds/services/PhysicalMachines/service_multi.py +++ b/server/src/uds/services/PhysicalMachines/service_multi.py @@ -46,7 +46,7 @@ from uds.core.util import config from uds.core.services import types as serviceTypes from .deployment import IPMachineDeployed -from .service_base import IPServiceBase, WOLAPP +from .service_base import IPServiceBase # Not imported at runtime, just for type checking if typing.TYPE_CHECKING: @@ -204,7 +204,7 @@ class IPMachinesService(IPServiceBase): continue # The check failed not so long ago, skip it... self.storage.putPickle(theIP, now) # Now, check if it is available on port, if required... - if self._port > 0 and not WOLAPP: # If configured WOLAPP, then check port is a nonsense... + if self._port > 0 and not self.parent().wolURL(theIP): # If configured WOL, check is a nonsense if ( connection.testServer(theIP, self._port, timeOut=0.5) is False