forked from shaba/openuds
advancing
This commit is contained in:
parent
72efa3221f
commit
3ed5d26245
54
actor/src/actor_client.py
Normal file
54
actor/src/actor_client.py
Normal file
@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2019 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
'''
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
# pylint: disable=invalid-name
|
||||
import sys
|
||||
import os
|
||||
|
||||
import PyQt5 # pylint: disable=unused-import
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
|
||||
from udsactor.log import logger, DEBUG
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logger.setLevel(DEBUG)
|
||||
|
||||
if 'linux' in sys.platform:
|
||||
os.environ['QT_X11_NO_MITSHM'] = '1'
|
||||
|
||||
logger.info('Started UDS Client Actor')
|
||||
|
||||
QApplication.setQuitOnLastWindowClosed(False)
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
# Execute backgroup thread for actions
|
@ -71,8 +71,8 @@ class UDSConfigDialog(QDialog):
|
||||
self.ui.password.setText('')
|
||||
|
||||
@property
|
||||
def api(self) -> udsactor.rest.REST:
|
||||
return udsactor.rest.REST(self.ui.host.text(), self.ui.validateCertificate.currentIndex() == 1)
|
||||
def api(self) -> udsactor.rest.UDSServerApi:
|
||||
return udsactor.rest.UDSServerApi(self.ui.host.text(), self.ui.validateCertificate.currentIndex() == 1)
|
||||
|
||||
def browse(self, lineEdit: 'QLineEdit', caption: str) -> None:
|
||||
name = QFileDialog.getOpenFileName(parent=self, caption=caption, directory=os.path.dirname(lineEdit.text()))[0]
|
||||
|
@ -30,6 +30,7 @@
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
import sys
|
||||
|
||||
if sys.platform == 'win32':
|
||||
from udsactor.windows import runner
|
||||
else:
|
@ -0,0 +1,143 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2019 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
'''
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
'''
|
||||
import threading
|
||||
import http.server
|
||||
import secrets
|
||||
import json
|
||||
import typing
|
||||
|
||||
from ..log import logger
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
UDSActorClient = typing.Any
|
||||
|
||||
class HTTPServerHandler(http.server.BaseHTTPRequestHandler):
|
||||
protocol_version = 'HTTP/1.0'
|
||||
server_version = 'UDS Actor Server'
|
||||
sys_version = ''
|
||||
|
||||
_id: typing.ClassVar[str] # Random id for server
|
||||
_app: typing.ClassVar['UDSActorClient']
|
||||
|
||||
def sendJsonResponse(self, result: typing.Optional[typing.Any] = None, error: typing.Optional[str] = None, code: int = 200) -> None:
|
||||
data = json.dumps({'result': result, 'error': error})
|
||||
self.send_response(code)
|
||||
self.send_header('Content-type', 'application/json')
|
||||
self.send_header('Content-Length', str(len(data)))
|
||||
self.send_header('Server: ', self.server_version)
|
||||
self.end_headers()
|
||||
self.wfile.write(data.encode())
|
||||
|
||||
def do_POST(self) -> None:
|
||||
# Only allows requests from localhost
|
||||
if self.client_address[0][:3] != '127':
|
||||
self.sendJsonResponse(error='Forbidden', code=403)
|
||||
|
||||
# Very simple path & params splitter
|
||||
path = self.path.split('?')[0][1:].split('/')
|
||||
if len(path) != 2 or path[0] != HTTPServerHandler._id:
|
||||
self.sendJsonResponse(error='Forbidden', code=403)
|
||||
|
||||
try:
|
||||
length = int(str(self.headers.get('content-length', '0')))
|
||||
content = self.rfile.read(length)
|
||||
params: typing.MutableMapping[str, str] = json.loads(content)
|
||||
except Exception as e:
|
||||
logger.error('Got exception executing POST {}: {}'.format(self.path, str(e)))
|
||||
self.sendJsonResponse(error='Invalid request', code=400)
|
||||
return
|
||||
|
||||
try:
|
||||
result = getattr(self, 'method_' + path[1])(params) # last part of path is method
|
||||
except AttributeError:
|
||||
self.sendJsonResponse(error='Invalid request', code=400)
|
||||
return
|
||||
except Exception as e:
|
||||
logger.error('Got exception executing {}: {}'.format('/'.join(path), str(e)))
|
||||
self.sendJsonResponse(error='Internal error', code=500)
|
||||
return
|
||||
|
||||
self.sendJsonResponse(result)
|
||||
|
||||
# Internal methods
|
||||
def method_ping(self, params: typing.MutableMapping[str, str]) -> typing.Any:
|
||||
return 'pong'
|
||||
|
||||
def method_logout(self, params: typing.MutableMapping[str, str]) -> typing.Any:
|
||||
return self._app.logout()
|
||||
|
||||
def method_message(self, params: typing.MutableMapping[str, str]) -> typing.Any:
|
||||
return self._app.message(params['message'])
|
||||
|
||||
def method_screenshot(self, params: typing.MutableMapping[str, str]) -> typing.Any:
|
||||
return self._app.screenshot()
|
||||
|
||||
def do_GET(self) -> None:
|
||||
self.sendJsonResponse(error='Forbidden', code=403)
|
||||
|
||||
def log_error(self, format: str, *args): # pylint: disable=redefined-builtin
|
||||
logger.error('ERROR ' + format, *args)
|
||||
|
||||
def log_message(self, format: str, *args): # pylint: disable=redefined-builtin
|
||||
logger.info('INFO ' + format, *args)
|
||||
|
||||
class HTTPServerThread(threading.Thread):
|
||||
_server: typing.Optional[http.server.HTTPServer]
|
||||
_app: 'UDSActorClient'
|
||||
|
||||
port: int
|
||||
id: str
|
||||
|
||||
def __init__(self, app: 'UDSActorClient'):
|
||||
super().__init__()
|
||||
|
||||
self._server = None
|
||||
self._app = app
|
||||
|
||||
self.port = -1
|
||||
self.id = secrets.token_urlsafe(16)
|
||||
|
||||
def stop(self) -> None:
|
||||
logger.debug('Stopping Http-client Service')
|
||||
if self._server:
|
||||
self._server.shutdown()
|
||||
self._server = None
|
||||
|
||||
def run(self):
|
||||
HTTPServerHandler._app = self._app # pylint: disable=protected-access
|
||||
HTTPServerHandler._id = self.id # pylint: disable=protected-access
|
||||
|
||||
self._server = http.server.HTTPServer(('0.0.0.0', 0), HTTPServerHandler)
|
||||
|
||||
self.port = self._server.socket.getsockname()[1]
|
||||
|
||||
self._server.serve_forever()
|
@ -35,17 +35,18 @@ import requests
|
||||
|
||||
from ..log import logger
|
||||
|
||||
class UDSActorClientRegistry:
|
||||
class UDSActorClientPool:
|
||||
_clientUrl: typing.List[str]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._clientUrl = []
|
||||
|
||||
def _post(self, method: str, data: typing.Any = None) -> None:
|
||||
def _post(self, method: str, data: typing.MutableMapping[str, str]) -> typing.List[requests.Response]:
|
||||
removables: typing.List[str] = []
|
||||
result: typing.List[typing.Any] = []
|
||||
for clientUrl in self._clientUrl:
|
||||
try:
|
||||
requests.post(clientUrl + '/' + method, data=json.dumps(data), verify=False)
|
||||
result.append(requests.post(clientUrl + '/' + method, data=json.dumps(data), verify=False))
|
||||
except Exception as e:
|
||||
# If cannot request to a clientUrl, remove it from list
|
||||
logger.info('Could not coneect with client %s: %s. Removed from registry.', e, clientUrl)
|
||||
@ -55,6 +56,8 @@ class UDSActorClientRegistry:
|
||||
for clientUrl in removables:
|
||||
self.unregister(clientUrl)
|
||||
|
||||
return result
|
||||
|
||||
def register(self, clientUrl: str) -> None:
|
||||
# Remove first if exists, to avoid duplicates
|
||||
self.unregister(clientUrl)
|
||||
@ -65,14 +68,17 @@ class UDSActorClientRegistry:
|
||||
self._clientUrl = list((i for i in self._clientUrl if i != clientUrl))
|
||||
|
||||
def executeScript(self, script: str) -> None:
|
||||
self._post('script', script)
|
||||
self._post('script', {'script': script})
|
||||
|
||||
def logout(self) -> None:
|
||||
self._post('logout', None)
|
||||
self._post('logout', {})
|
||||
|
||||
def message(self, message: str) -> None:
|
||||
self._post('message', message)
|
||||
self._post('message', {'message': message})
|
||||
|
||||
def ping(self) -> bool:
|
||||
self._post('ping', None)
|
||||
self._post('ping', {})
|
||||
return bool(self._clientUrl) # if no clients available
|
||||
|
||||
def screenshot(self) -> typing.List[bytes]:
|
||||
return []
|
@ -49,5 +49,8 @@ class LocalProvider(handler.Handler):
|
||||
return 'pong'
|
||||
|
||||
def post_register(self) -> typing.Any:
|
||||
self._service._registry.register(self._params['url']) # pylint: disable=protected-access
|
||||
self._service._clientsPool.register(self._params['callback_url']) # pylint: disable=protected-access
|
||||
return 'ok'
|
||||
|
||||
def post_unregister(self) -> typing.Any:
|
||||
self._service._clientsPool.unregister(self._params['callback_url']) # pylint: disable=protected-access
|
||||
|
@ -39,19 +39,19 @@ if typing.TYPE_CHECKING:
|
||||
from ..service import CommonService
|
||||
|
||||
class PublicProvider(handler.Handler):
|
||||
def post_logoff(self) -> typing.Any:
|
||||
def post_logout(self) -> typing.Any:
|
||||
logger.debug('Sending LOGOFF to clients')
|
||||
self._service._registry.logout() # pylint: disable=protected-access
|
||||
self._service._clientsPool.logout() # pylint: disable=protected-access
|
||||
return 'ok'
|
||||
|
||||
# Alias
|
||||
post_logout = post_logoff
|
||||
post_logoff = post_logout
|
||||
|
||||
def post_message(self) -> typing.Any:
|
||||
logger.debug('Sending MESSAGE to clients')
|
||||
if 'message' not in self._params:
|
||||
raise Exception('Invalid message parameters')
|
||||
self._service._registry.message(self._params['message']) # pylint: disable=protected-access
|
||||
self._service._clientsPool.message(self._params['message']) # pylint: disable=protected-access
|
||||
return 'ok'
|
||||
|
||||
def post_script(self) -> typing.Any:
|
||||
@ -60,7 +60,7 @@ class PublicProvider(handler.Handler):
|
||||
raise Exception('Invalid script parameters')
|
||||
if 'user' in self._params:
|
||||
logger.debug('Sending SCRIPT to client')
|
||||
self._service._registry.message(self._params['scripts']) # pylint: disable=protected-access
|
||||
self._service._clientsPool.message(self._params['scripts']) # pylint: disable=protected-access
|
||||
else:
|
||||
# Execute script at server space, that is, here
|
||||
# as a parallel thread
|
||||
@ -78,5 +78,9 @@ class PublicProvider(handler.Handler):
|
||||
# Return something useful? :)
|
||||
return 'UDS Actor Secure Server'
|
||||
|
||||
def get_screenshot(self) -> typing.Any:
|
||||
return ''
|
||||
|
||||
|
||||
def get_uuid(self) -> typing.Any:
|
||||
return self._service._cfg.own_token # pylint: disable=protected-access
|
||||
|
@ -32,7 +32,6 @@ import os
|
||||
import threading
|
||||
import http.server
|
||||
import json
|
||||
import time
|
||||
import ssl
|
||||
import typing
|
||||
|
||||
@ -47,8 +46,6 @@ if typing.TYPE_CHECKING:
|
||||
from ..service import CommonService
|
||||
from .handler import Handler
|
||||
|
||||
startTime = time.time()
|
||||
|
||||
class HTTPServerHandler(http.server.BaseHTTPRequestHandler):
|
||||
protocol_version = 'HTTP/1.0'
|
||||
server_version = 'UDS Actor Server'
|
||||
@ -111,11 +108,10 @@ class HTTPServerHandler(http.server.BaseHTTPRequestHandler):
|
||||
try:
|
||||
length = int(str(self.headers.get('content-length', '0')))
|
||||
content = self.rfile.read(length)
|
||||
logger.debug('length: {}, content >>{}<<'.format(length, content))
|
||||
params: typing.MutableMapping[str, str] = json.loads(content)
|
||||
except Exception as e:
|
||||
logger.error('Got exception executing POST {}: {}'.format(self.path, str(e)))
|
||||
self.sendJsonResponse(error=str(e), code=500)
|
||||
self.sendJsonResponse(error='Invalid parameters', code=400)
|
||||
return
|
||||
|
||||
self.process('post', params)
|
||||
@ -139,7 +135,7 @@ class HTTPServerThread(threading.Thread):
|
||||
self._certFile = None
|
||||
|
||||
def stop(self) -> None:
|
||||
logger.debug('Stopping REST Service')
|
||||
logger.debug('Stopping Http-server Service')
|
||||
if self._server:
|
||||
self._server.shutdown()
|
||||
self._server = None
|
||||
|
@ -34,10 +34,6 @@ import tempfile
|
||||
import logging
|
||||
import typing
|
||||
|
||||
# Valid logging levels, from UDS Broker (uds.core.utils.log)
|
||||
OTHER, DEBUG, INFO, WARN, ERROR, FATAL = (10000 * (x + 1) for x in range(6)) # @UndefinedVariable
|
||||
|
||||
|
||||
class LocalLogger: # pylint: disable=too-few-public-methods
|
||||
linux = False
|
||||
windows = True
|
||||
|
@ -183,3 +183,6 @@ def getCurrentUser() -> str:
|
||||
Returns current logged in user
|
||||
'''
|
||||
return os.environ['USER']
|
||||
|
||||
def forceTimeSync() -> None:
|
||||
return
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014 Virtual Cable S.L.
|
||||
# Copyright (c) 2014-2019 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -45,9 +45,8 @@ if typing.TYPE_CHECKING:
|
||||
# Valid logging levels, from UDS Broker (uds.core.utils.log)
|
||||
OTHER, DEBUG, INFO, WARN, ERROR, FATAL = (10000 * (x + 1) for x in range(6))
|
||||
|
||||
|
||||
class Logger:
|
||||
remoteLogger: typing.Optional['rest.REST']
|
||||
remoteLogger: typing.Optional['rest.UDSServerApi']
|
||||
own_token: str
|
||||
logLevel: int
|
||||
localLogger: LocalLogger
|
||||
@ -69,6 +68,10 @@ class Logger:
|
||||
self.remoteLogger = remoteLogger
|
||||
self.own_token = own_token
|
||||
|
||||
def enableServiceLogger(self):
|
||||
if self.localLogger.windows:
|
||||
self.localLogger.serviceLogger = True
|
||||
|
||||
def log(self, level: typing.Union[str, int], message: str, *args) -> None:
|
||||
level = int(level)
|
||||
if level < self.logLevel: # Skip not wanted messages
|
||||
|
@ -58,11 +58,21 @@ class RESTUserServiceNotFoundError(RESTError):
|
||||
class RESTOsManagerError(RESTError):
|
||||
ERRCODE = 4
|
||||
|
||||
class REST:
|
||||
#
|
||||
#
|
||||
#
|
||||
class UDSApi:
|
||||
"""
|
||||
Base for remote api accesses
|
||||
"""
|
||||
_host: str
|
||||
_validateCert: bool
|
||||
_url: str
|
||||
|
||||
def __init__(self, host: str, validateCert: bool) -> None:
|
||||
self.host = host
|
||||
self.validateCert = validateCert
|
||||
self.url = "https://{}/uds/rest/".format(self.host)
|
||||
self._host = host
|
||||
self._validateCert = validateCert
|
||||
self._url = "https://{}/uds/rest/".format(self._host)
|
||||
# Disable logging requests messages except for errors, ...
|
||||
logging.getLogger("requests").setLevel(logging.CRITICAL)
|
||||
logging.getLogger("urllib3").setLevel(logging.ERROR)
|
||||
@ -75,7 +85,7 @@ class REST:
|
||||
def _headers(self) -> typing.MutableMapping[str, str]:
|
||||
return {'content-type': 'application/json'}
|
||||
|
||||
def _actorPost(
|
||||
def _doPost(
|
||||
self,
|
||||
method: str, # i.e. 'initialize', 'ready', ....
|
||||
payLoad: typing.MutableMapping[str, typing.Any],
|
||||
@ -83,7 +93,7 @@ class REST:
|
||||
) -> typing.Any:
|
||||
headers = headers or self._headers
|
||||
try:
|
||||
result = requests.post(self.url + 'actor/v2/' + method, data=json.dumps(payLoad), headers=headers, verify=self.validateCert)
|
||||
result = requests.post(self._url + 'actor/v2/' + method, data=json.dumps(payLoad), headers=headers, verify=self._validateCert)
|
||||
if result.ok:
|
||||
return result.json()['result']
|
||||
except requests.ConnectionError as e:
|
||||
@ -93,9 +103,14 @@ class REST:
|
||||
|
||||
raise RESTError(result.content)
|
||||
|
||||
|
||||
#
|
||||
# UDS Broker API access
|
||||
#
|
||||
class UDSServerApi(UDSApi):
|
||||
def enumerateAuthenticators(self) -> typing.Iterable[types.AuthenticatorType]:
|
||||
try:
|
||||
result = requests.get(self.url + 'auth/auths', headers=self._headers, verify=self.validateCert, timeout=4)
|
||||
result = requests.get(self._url + 'auth/auths', headers=self._headers, verify=self._validateCert, timeout=4)
|
||||
if result.ok:
|
||||
for v in sorted(result.json(), key=lambda x: x['priority']):
|
||||
yield types.AuthenticatorType(
|
||||
@ -141,13 +156,13 @@ class REST:
|
||||
# First, try to login
|
||||
authInfo = {'auth': auth, 'username': username, 'password': password}
|
||||
headers = self._headers
|
||||
result = requests.post(self.url + 'auth/login', data=json.dumps(authInfo), headers=headers, verify=self.validateCert)
|
||||
result = requests.post(self._url + 'auth/login', data=json.dumps(authInfo), headers=headers, verify=self._validateCert)
|
||||
if not result.ok or result.json()['result'] == 'error':
|
||||
raise Exception() # Invalid credentials
|
||||
|
||||
headers['X-Auth-Token'] = result.json()['token']
|
||||
|
||||
result = requests.post(self.url + 'actor/v2/register', data=json.dumps(data), headers=headers, verify=self.validateCert)
|
||||
result = requests.post(self._url + 'actor/v2/register', data=json.dumps(data), headers=headers, verify=self._validateCert)
|
||||
if result.ok:
|
||||
return result.json()['result']
|
||||
except requests.ConnectionError as e:
|
||||
@ -166,7 +181,7 @@ class REST:
|
||||
'version': VERSION,
|
||||
'id': [{'mac': i.mac, 'ip': i.ip} for i in interfaces]
|
||||
}
|
||||
r = self._actorPost('initialize', payload)
|
||||
r = self._doPost('initialize', payload)
|
||||
os = r['os']
|
||||
return types.InitializationResultType(
|
||||
own_token=r['own_token'],
|
||||
@ -190,7 +205,7 @@ class REST:
|
||||
'ip': ip,
|
||||
'port': port
|
||||
}
|
||||
result = self._actorPost('ready', payload)
|
||||
result = self._doPost('ready', payload)
|
||||
|
||||
return types.CertificateInfoType(
|
||||
private_key=result['private_key'],
|
||||
@ -205,7 +220,7 @@ class REST:
|
||||
'ip': ip,
|
||||
'port': port
|
||||
}
|
||||
result = self._actorPost('ipchange', payload)
|
||||
result = self._doPost('ipchange', payload)
|
||||
|
||||
return types.CertificateInfoType(
|
||||
private_key=result['private_key'],
|
||||
@ -218,7 +233,7 @@ class REST:
|
||||
'token': own_token,
|
||||
'username': username
|
||||
}
|
||||
result = self._actorPost('login', payload)
|
||||
result = self._doPost('login', payload)
|
||||
return types.LoginResultInfoType(ip=result['ip'], hostname=result['hostname'], dead_line=result['dead_line'])
|
||||
|
||||
def logout(self, own_token: str, username: str) -> None:
|
||||
@ -226,7 +241,7 @@ class REST:
|
||||
'token': own_token,
|
||||
'username': username
|
||||
}
|
||||
self._actorPost('logout', payload)
|
||||
self._doPost('logout', payload)
|
||||
|
||||
|
||||
def log(self, own_token: str, level: int, message: str) -> None:
|
||||
@ -235,4 +250,39 @@ class REST:
|
||||
'level': level,
|
||||
'message': message
|
||||
}
|
||||
self._actorPost('log', payLoad) # Ignores result...
|
||||
self._doPost('log', payLoad) # Ignores result...
|
||||
|
||||
|
||||
class UDSClientApi(UDSApi):
|
||||
def register(self, callbackUrl: str) -> None:
|
||||
payLoad = {
|
||||
'callback_url': callbackUrl
|
||||
}
|
||||
self._doPost('register', payLoad)
|
||||
|
||||
def unregister(self, callbackUrl: str) -> None:
|
||||
payLoad = {
|
||||
'callback_url': callbackUrl
|
||||
}
|
||||
self._doPost('unregister', payLoad)
|
||||
|
||||
def login(self, username: str) -> types.LoginResultInfoType:
|
||||
payLoad = {
|
||||
'username': username
|
||||
}
|
||||
result = self._doPost('login', payLoad)
|
||||
return types.LoginResultInfoType(
|
||||
ip=result['ip'],
|
||||
hostname=result['hostname'],
|
||||
dead_line=result['dead_line'],
|
||||
max_idle=result['max_idle']
|
||||
)
|
||||
|
||||
def logout(self, username: str) -> None:
|
||||
payLoad = {
|
||||
'username': username
|
||||
}
|
||||
self._doPost('logout', payLoad)
|
||||
|
||||
def ping(self) -> bool:
|
||||
return self._doPost('ping', {}) == 'pong'
|
||||
|
@ -41,7 +41,7 @@ from . import rest
|
||||
from . import types
|
||||
|
||||
from .log import logger
|
||||
from .http import registry, server
|
||||
from .http import clients_pool, server
|
||||
|
||||
# def setup() -> None:
|
||||
# cfg = platform.store.readConfig()
|
||||
@ -61,11 +61,11 @@ class CommonService: # pylint: disable=too-many-instance-attributes
|
||||
_loggedIn = False
|
||||
|
||||
_cfg: types.ActorConfigurationType
|
||||
_api: rest.REST
|
||||
_api: rest.UDSServerApi
|
||||
_interfaces: typing.List[types.InterfaceInfoType]
|
||||
_secret: str
|
||||
_certificate: types.CertificateInfoType
|
||||
_registry: registry.UDSActorClientRegistry
|
||||
_clientsPool: clients_pool.UDSActorClientPool
|
||||
_http: typing.Optional[server.HTTPServerThread]
|
||||
|
||||
@staticmethod
|
||||
@ -81,17 +81,16 @@ class CommonService: # pylint: disable=too-many-instance-attributes
|
||||
def __init__(self) -> None:
|
||||
self._cfg = platform.store.readConfig()
|
||||
self._interfaces = []
|
||||
self._api = rest.REST(self._cfg.host, self._cfg.validateCertificate)
|
||||
self._api = rest.UDSServerApi(self._cfg.host, self._cfg.validateCertificate)
|
||||
self._secret = secrets.token_urlsafe(33)
|
||||
self._registry = registry.UDSActorClientRegistry()
|
||||
self._clientsPool = clients_pool.UDSActorClientPool()
|
||||
self._certificate = types.CertificateInfoType('', '', '')
|
||||
self._http = None
|
||||
|
||||
# Initialzies loglevel and serviceLogger
|
||||
logger.setLevel(self._cfg.log_level * 10000)
|
||||
# If windows, enable service logger
|
||||
if logger.localLogger.windows:
|
||||
logger.localLogger.serviceLogger = True
|
||||
logger.enableServiceLogger()
|
||||
|
||||
socket.setdefaulttimeout(20)
|
||||
|
||||
@ -135,14 +134,21 @@ class CommonService: # pylint: disable=too-many-instance-attributes
|
||||
srvInterface = self.serviceInterfaceInfo()
|
||||
if srvInterface:
|
||||
# Rery while RESTConnectionError (that is, cannot connect)
|
||||
counter = 8
|
||||
while self._isAlive:
|
||||
counter -= 1
|
||||
try:
|
||||
self._certificate = self._api.ready(self._cfg.own_token, self._secret, srvInterface.ip, self._cfg.port)
|
||||
except rest.RESTConnectionError:
|
||||
except rest.RESTConnectionError as e:
|
||||
logger.info('Error connecting with UDS Broker: %s', e)
|
||||
self.doWait(5000)
|
||||
continue
|
||||
except Exception as e:
|
||||
logger.error('Unhandled exception while setting ready: %s', e)
|
||||
if counter > 0:
|
||||
self.doWait(10000) # A long wait on other error...
|
||||
continue
|
||||
platform.operations.reboot() # On too many errors, simply reboot
|
||||
# Success or any error that is not recoverable (retunerd by UDS). if Error, service will be cleaned in a while.
|
||||
break
|
||||
|
||||
@ -169,8 +175,10 @@ class CommonService: # pylint: disable=too-many-instance-attributes
|
||||
# That is, the COMMAND itself has to restart the machine!
|
||||
return False # If the command fails, continue with the rest of the operations...
|
||||
|
||||
# Retry configuration while not stop service, config in case of error 10 times
|
||||
# Retry configuration while not stop service, config in case of error 10 times, reboot vm
|
||||
counter = 10
|
||||
while self._isAlive:
|
||||
counter -= 1
|
||||
try:
|
||||
if self._cfg.config and self._cfg.config.os:
|
||||
osData = self._cfg.config.os
|
||||
@ -188,7 +196,11 @@ class CommonService: # pylint: disable=too-many-instance-attributes
|
||||
break
|
||||
except Exception as e:
|
||||
logger.error('Got exception operating machine: {}'.format(e))
|
||||
self.doWait(5000)
|
||||
if counter > 0:
|
||||
self.doWait(5000)
|
||||
else:
|
||||
platform.operations.reboot()
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@ -196,6 +208,9 @@ class CommonService: # pylint: disable=too-many-instance-attributes
|
||||
if not self._cfg.host: # Not configured
|
||||
return False
|
||||
|
||||
# Force time sync, just in case...
|
||||
platform.operations.forceTimeSync()
|
||||
|
||||
# Wait for Broker to be ready
|
||||
while self._isAlive:
|
||||
if not self._interfaces:
|
||||
@ -312,11 +327,14 @@ class CommonService: # pylint: disable=too-many-instance-attributes
|
||||
|
||||
# Client notifications
|
||||
def login(self, username: str) -> types.LoginResultInfoType:
|
||||
result = types.LoginResultInfoType(ip='', hostname='', dead_line=None, max_idle=None)
|
||||
self._loggedIn = True
|
||||
if self._cfg.own_token:
|
||||
return self._api.login(self._cfg.own_token, username)
|
||||
return types.LoginResultInfoType(ip='', hostname='', dead_line=None)
|
||||
result = self._api.login(self._cfg.own_token, username)
|
||||
return result
|
||||
|
||||
def logout(self, username: str) -> None:
|
||||
self._loggedIn = False
|
||||
if self._cfg.own_token:
|
||||
self._api.logout(self._cfg.own_token, username)
|
||||
|
||||
|
@ -58,6 +58,7 @@ class LoginResultInfoType(typing.NamedTuple):
|
||||
ip: str
|
||||
hostname: str
|
||||
dead_line: typing.Optional[int]
|
||||
max_idle: typing.Optional[int] = None # Not provided by broker
|
||||
|
||||
class CertificateInfoType(typing.NamedTuple):
|
||||
private_key: str
|
||||
|
@ -35,7 +35,7 @@ import tempfile
|
||||
|
||||
import servicemanager # @UnresolvedImport, pylint: disable=import-error
|
||||
|
||||
# Valid logging levels, from UDS Broker (uds.core.utils.log)
|
||||
# Valid logging levels, from UDS Broker (uds.core.utils.log).
|
||||
OTHER, DEBUG, INFO, WARN, ERROR, FATAL = (10000 * (x + 1) for x in range(6))
|
||||
|
||||
class LocalLogger: # pylint: disable=too-few-public-methods
|
||||
|
@ -30,6 +30,7 @@
|
||||
'''
|
||||
# pylint: disable=invalid-name
|
||||
import os
|
||||
import subprocess
|
||||
import ctypes
|
||||
from ctypes.wintypes import DWORD, LPCWSTR
|
||||
import typing
|
||||
@ -217,7 +218,6 @@ def getIdleDuration() -> float:
|
||||
logger.error('Getting idle duration: {}'.format(e))
|
||||
return 0
|
||||
|
||||
|
||||
def getCurrentUser() -> str:
|
||||
'''
|
||||
Returns current logged in username
|
||||
@ -235,3 +235,9 @@ def writeToPipe(pipeName: str, bytesPayload: bytes, waitForResponse: bool) -> ty
|
||||
return b'ok'
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def forceTimeSync() -> None:
|
||||
try:
|
||||
subprocess.call([r'c:\WINDOWS\System32\w32tm.exe', ' /resync']) # , '/rediscover'])
|
||||
except Exception as e:
|
||||
logger.error('Error invoking time sync command: %s', e)
|
||||
|
Loading…
Reference in New Issue
Block a user