Projekt

Obecné

Profil

« Předchozí | Další » 

Revize 9219c992

Přidáno uživatelem Jakub Šilhavý před více než 2 roky(ů)

re #9420 Created folders src and resources; renamed config.ini to client.conf

Zobrazit rozdíly:

client/api_client.py
1
import json
2
import requests
3
import logging
4
from time import sleep
5
from diskcache import Deque
6
from requests import HTTPError, ConnectionError
7

  
8

  
9
_uri = None
10
_cache = None
11
_config = None
12

  
13

  
14
def api_client_set_config(config):
15
    global _config, _cache, _uri
16
    _config = config
17
    _cache = Deque(directory=_config.cache_dir)
18
    _uri = config.server_url + ":" + config.server_port + config.server_endpoint
19

  
20

  
21
def send_data(payload: dict):
22
    try:
23
        logging.info(f"sending payload = {payload} to {_uri}")
24
        response = requests.post(url=_uri, data=json.dumps(payload))
25
        logging.info(f"response text: {response.text}")
26
    except ConnectionError:
27
        logging.warning(f"sending payload = {payload} to {_uri} failed")
28
        _cache_failed_payload(payload)
29
    except HTTPError as error:
30
        logging.error(f"HTTP Error ({_uri}) payload = {payload}, {error}")
31
        _cache_failed_payload(payload)
32

  
33

  
34
def _cache_failed_payload(payload: dict):
35
    if len(_cache) >= _config.cache_max_entries:
36
        oldest_payload = _cache.pop()
37
        logging.warning(f"cache is full - discarding payload = {oldest_payload}")
38

  
39
    logging.info(f"adding payload = {payload} into cache")
40
    _cache.append(payload)
41

  
42

  
43
def _resend_cached_payloads():
44
    retries = min(_config.cache_max_retries, len(_cache))
45
    logging.info(f"emptying the cache ({retries} records)")
46
    for _ in range(0, retries):
47
        payload = _cache.pop()
48
        send_data(payload)
49

  
50

  
51
def api_client_run():
52
    while True:
53
        _resend_cached_payloads()
54
        sleep(_config.cache_retry_period_seconds)
client/config.ini
1
[usb_detector]
2
scan_period_seconds = 1
3
connected_devices_filename = data/devices.json
4

  
5
[server]
6
url = http://127.0.0.1
7
port = 8000
8
end_point = /api/v1/usb-logs
9

  
10
[logger]
11
config_file = logger.conf
12

  
13
[cache]
14
directory = data
15
max_entries = 100
16
max_retries = 20
17
retry_period_seconds = 20
client/config_manager.py
1
from sys import exit
2
from configparser import RawConfigParser
3

  
4

  
5
class Config:
6

  
7
    def __init__(self, filepath):
8
        self.config = RawConfigParser()
9
        if not self.config.read(filepath):
10
            print(f"Failed to parse the config file {filepath}. Make sure you entered a valid path.")
11
            exit(1)
12

  
13
        self._parse_usb_detector_section()
14
        self._parse_server_section()
15
        self._parse_logger_section()
16
        self._parse_cache_section()
17

  
18
    def _parse_usb_detector_section(self):
19
        section_name = "usb_detector"
20
        self.scan_period_seconds = float(self.config[section_name]["scan_period_seconds"])
21
        self.connected_devices_filename = self.config[section_name]["connected_devices_filename"]
22

  
23
    def _parse_server_section(self):
24
        section_name = "server"
25
        self.server_url = self.config[section_name]["url"]
26
        self.server_port = self.config[section_name]["port"]
27
        self.server_endpoint = self.config[section_name]["end_point"]
28

  
29
    def _parse_logger_section(self):
30
        section_name = "logger"
31
        self.logger_config_file = self.config[section_name]["config_file"]
32

  
33
    def _parse_cache_section(self):
34
        section_name = "cache"
35
        self.cache_dir = self.config[section_name]["directory"]
36
        self.cache_max_entries = int(self.config[section_name]["max_entries"])
37
        self.cache_max_retries = int(self.config[section_name]["max_retries"])
38
        self.cache_retry_period_seconds = float(self.config[section_name]["retry_period_seconds"])
client/logger.conf
1
[loggers]
2
keys=root
3

  
4
[handlers]
5
keys=consoleHandler,logFileHandler
6

  
7
[formatters]
8
keys=simpleFormatter
9

  
10
[logger_root]
11
level=DEBUG
12
handlers=consoleHandler
13

  
14
[handler_consoleHandler]
15
class=StreamHandler
16
level=DEBUG
17
formatter=simpleFormatter
18
args=(sys.stdout,)
19

  
20
[handler_logFileHandler]
21
class=handlers.RotatingFileHandler
22
level=NOTSET
23
args=('log_file.log', 'a', 10485760, 1)
24
formatter=simpleFormatter
25

  
26
[formatter_simpleFormatter]
27
format=%(asctime)s - %(levelname)s - %(message)s
client/main.py
1
import logging
2
import logging.config
3
import argparse
4
from os.path import exists
5
from threading import Thread
6
from tendo import singleton
7
from sys import exit
8

  
9
from config_manager import Config
10
from usb_detector.detector import register_listener, usb_detector_run, usb_detector_set_config
11
from usb_detector.event_listener import usb_connected_callback, usb_disconnected_callback
12
from api_client import api_client_run, api_client_set_config
13

  
14

  
15
def init_logging(app_config: Config):
16
    if exists(app_config.logger_config_file):
17
        logging.config.fileConfig(fname=app_config.logger_config_file)
18
        api_client_set_config(app_config)
19
        usb_detector_set_config(app_config)
20
    else:
21
        print(f"Cannot find logger configuration \"{app_config.logger_config_file}\"! Please specify valid a path or define a new one.")
22
        exit(1)
23

  
24

  
25
if __name__ == "__main__":
26
    try:
27
        app_instance = singleton.SingleInstance()
28
    except singleton.SingleInstanceException:
29
        exit(1)
30

  
31
    arg_parser = argparse.ArgumentParser(description="ZF USB Licence Detector")
32
    arg_parser.add_argument("-c", "--config", dest="config", required=True, help="Path to the configuration file")
33
    args = arg_parser.parse_args()
34

  
35
    config = Config(args.config)
36
    init_logging(config)
37

  
38
    register_listener(callback=usb_connected_callback, connected=True)
39
    register_listener(callback=usb_disconnected_callback, connected=False)
40

  
41
    usb_detector_thread = Thread(target=usb_detector_run)
42
    usb_detector_thread.setDaemon(True)
43

  
44
    api_thread = Thread(target=api_client_run)
45
    api_thread.setDaemon(True)
46

  
47
    logging.info("Starting USB detector.")
48
    usb_detector_thread.start()
49

  
50
    logging.info("Starting API communication manager.")
51
    api_thread.start()
52

  
53
    usb_detector_thread.join()
54
    api_thread.join()
client/resources/client.conf
1
[usb_detector]
2
scan_period_seconds = 1
3
connected_devices_filename = data/devices.json
4

  
5
[server]
6
url = http://127.0.0.1
7
port = 8000
8
end_point = /api/v1/usb-logs
9

  
10
[logger]
11
config_file = ../resources/logger.conf
12

  
13
[cache]
14
directory = data
15
max_entries = 100
16
max_retries = 20
17
retry_period_seconds = 20
client/resources/logger.conf
1
[loggers]
2
keys=root
3

  
4
[handlers]
5
keys=consoleHandler,logFileHandler
6

  
7
[formatters]
8
keys=simpleFormatter
9

  
10
[logger_root]
11
level=DEBUG
12
handlers=consoleHandler
13

  
14
[handler_consoleHandler]
15
class=StreamHandler
16
level=DEBUG
17
formatter=simpleFormatter
18
args=(sys.stdout,)
19

  
20
[handler_logFileHandler]
21
class=handlers.RotatingFileHandler
22
level=NOTSET
23
args=('log_file.log', 'a', 10485760, 1)
24
formatter=simpleFormatter
25

  
26
[formatter_simpleFormatter]
27
format=%(asctime)s - %(levelname)s - %(message)s
client/src/api_client.py
1
import json
2
import requests
3
import logging
4
from time import sleep
5
from diskcache import Deque
6
from requests import HTTPError, ConnectionError
7

  
8

  
9
_uri = None
10
_cache = None
11
_config = None
12

  
13

  
14
def api_client_set_config(config):
15
    global _config, _cache, _uri
16
    _config = config
17
    _cache = Deque(directory=_config.cache_dir)
18
    _uri = config.server_url + ":" + config.server_port + config.server_endpoint
19

  
20

  
21
def send_data(payload: dict):
22
    try:
23
        logging.info(f"sending payload = {payload} to {_uri}")
24
        response = requests.post(url=_uri, data=json.dumps(payload))
25
        logging.info(f"response text: {response.text}")
26
    except ConnectionError:
27
        logging.warning(f"sending payload = {payload} to {_uri} failed")
28
        _cache_failed_payload(payload)
29
    except HTTPError as error:
30
        logging.error(f"HTTP Error ({_uri}) payload = {payload}, {error}")
31
        _cache_failed_payload(payload)
32

  
33

  
34
def _cache_failed_payload(payload: dict):
35
    if len(_cache) >= _config.cache_max_entries:
36
        oldest_payload = _cache.pop()
37
        logging.warning(f"cache is full - discarding payload = {oldest_payload}")
38

  
39
    logging.info(f"adding payload = {payload} into cache")
40
    _cache.append(payload)
41

  
42

  
43
def _resend_cached_payloads():
44
    retries = min(_config.cache_max_retries, len(_cache))
45
    logging.info(f"emptying the cache ({retries} records)")
46
    for _ in range(0, retries):
47
        payload = _cache.pop()
48
        send_data(payload)
49

  
50

  
51
def api_client_run():
52
    while True:
53
        _resend_cached_payloads()
54
        sleep(_config.cache_retry_period_seconds)
client/src/config_manager.py
1
from sys import exit
2
from configparser import RawConfigParser
3

  
4

  
5
class Config:
6

  
7
    def __init__(self, filepath):
8
        self.config = RawConfigParser()
9
        if not self.config.read(filepath):
10
            print(f"Failed to parse the config file {filepath}. Make sure you entered a valid path.")
11
            exit(1)
12

  
13
        self._parse_usb_detector_section()
14
        self._parse_server_section()
15
        self._parse_logger_section()
16
        self._parse_cache_section()
17

  
18
    def _parse_usb_detector_section(self):
19
        section_name = "usb_detector"
20
        self.scan_period_seconds = float(self.config[section_name]["scan_period_seconds"])
21
        self.connected_devices_filename = self.config[section_name]["connected_devices_filename"]
22

  
23
    def _parse_server_section(self):
24
        section_name = "server"
25
        self.server_url = self.config[section_name]["url"]
26
        self.server_port = self.config[section_name]["port"]
27
        self.server_endpoint = self.config[section_name]["end_point"]
28

  
29
    def _parse_logger_section(self):
30
        section_name = "logger"
31
        self.logger_config_file = self.config[section_name]["config_file"]
32

  
33
    def _parse_cache_section(self):
34
        section_name = "cache"
35
        self.cache_dir = self.config[section_name]["directory"]
36
        self.cache_max_entries = int(self.config[section_name]["max_entries"])
37
        self.cache_max_retries = int(self.config[section_name]["max_retries"])
38
        self.cache_retry_period_seconds = float(self.config[section_name]["retry_period_seconds"])
client/src/main.py
1
import logging
2
import logging.config
3
import argparse
4
from os.path import exists
5
from threading import Thread
6
from tendo import singleton
7
from sys import exit
8

  
9
from config_manager import Config
10
from usb_detector.detector import register_listener, usb_detector_run, usb_detector_set_config
11
from usb_detector.event_listener import usb_connected_callback, usb_disconnected_callback
12
from api_client import api_client_run, api_client_set_config
13

  
14

  
15
def init_logging(app_config: Config):
16
    if exists(app_config.logger_config_file):
17
        logging.config.fileConfig(fname=app_config.logger_config_file)
18
        api_client_set_config(app_config)
19
        usb_detector_set_config(app_config)
20
    else:
21
        print(f"Cannot find logger configuration \"{app_config.logger_config_file}\"! Please specify valid a path or define a new one.")
22
        exit(1)
23

  
24

  
25
if __name__ == "__main__":
26
    try:
27
        app_instance = singleton.SingleInstance()
28
    except singleton.SingleInstanceException:
29
        exit(1)
30

  
31
    arg_parser = argparse.ArgumentParser(description="ZF USB License Detector")
32
    arg_parser.add_argument("-c", "--config", dest="config", required=True, help="Path to the configuration file")
33
    args = arg_parser.parse_args()
34

  
35
    config = Config(args.config)
36
    init_logging(config)
37

  
38
    register_listener(callback=usb_connected_callback, connected=True)
39
    register_listener(callback=usb_disconnected_callback, connected=False)
40

  
41
    usb_detector_thread = Thread(target=usb_detector_run)
42
    usb_detector_thread.setDaemon(True)
43

  
44
    api_thread = Thread(target=api_client_run)
45
    api_thread.setDaemon(True)
46

  
47
    logging.info("Starting USB detector.")
48
    usb_detector_thread.start()
49

  
50
    logging.info("Starting API communication manager.")
51
    api_thread.start()
52

  
53
    usb_detector_thread.join()
54
    api_thread.join()
client/src/usb_detector/detector.py
1
import json
2
import logging
3
from time import sleep
4

  
5
from .usb_reader import read_connected_devices
6

  
7

  
8
_listeners_connected = []
9
_listeners_disconnected = []
10
_last_connected_devices = []
11
_config = None
12

  
13

  
14
def usb_detector_set_config(config):
15
    global _config
16
    _config = config
17

  
18

  
19
def register_listener(callback, connected: bool = True):
20
    logging.info(f"Registering callback: {callback}.")
21
    if connected is True:
22
        _listeners_connected.append(callback)
23
    else:
24
        _listeners_disconnected.append(callback)
25

  
26

  
27
def _notify_listeners(listeners: list, devices: list):
28
    if listeners is None or devices is None:
29
        return
30
    for callback in listeners:
31
        for device in devices:
32
            callback(device)
33

  
34

  
35
def _store_connected_devices(devices: list):
36
    logging.debug("storing newly connected devices")
37
    with open(_config.connected_devices_filename, "w") as file:
38
        json.dump(devices, file)
39

  
40

  
41
def _load_last_connected_devices() -> list:
42
    logging.debug("loading last connected devices")
43
    try:
44
        with open(_config.connected_devices_filename, "r") as file:
45
            return json.loads(file.read())
46
    except IOError:
47
        logging.error("loading of last connected devices failed")
48
        return []
49

  
50

  
51
def _get_connected_devices(detected_devices: list, last_connected_devices: list) -> list:
52
    return [device for device in detected_devices if device not in last_connected_devices]
53

  
54

  
55
def _get_disconnected_devices(detected_devices: list, last_connected_devices: list) -> list:
56
    return [device for device in last_connected_devices if device not in detected_devices]
57

  
58

  
59
def _update():
60
    global _last_connected_devices
61
    detected_devices = read_connected_devices()
62

  
63
    connected_devices = _get_connected_devices(detected_devices, _last_connected_devices)
64
    disconnected_devices = _get_disconnected_devices(detected_devices, _last_connected_devices)
65

  
66
    _notify_listeners(_listeners_connected, connected_devices)
67
    _notify_listeners(_listeners_disconnected, disconnected_devices)
68

  
69
    if len(connected_devices) > 0 or len(disconnected_devices) > 0:
70
        _store_connected_devices(detected_devices)
71
        _last_connected_devices = detected_devices
72

  
73

  
74
def usb_detector_run():
75
    logging.info("USB device detector is now running")
76

  
77
    global _last_connected_devices
78
    _last_connected_devices = _load_last_connected_devices()
79

  
80
    while True:
81
        _update()
82
        sleep(_config.scan_period_seconds)
client/src/usb_detector/event_listener.py
1
import platform
2
import logging
3
import getpass
4
from datetime import datetime
5

  
6
from api_client import send_data
7

  
8

  
9
def _get_metadata() -> dict:
10
    logging.debug("getting computer metadata")
11
    return {
12
        "username": getpass.getuser(),
13
        "hostname": platform.uname().node,
14
        "timestamp": str(datetime.now())
15
    }
16

  
17

  
18
def _send_payload_to_server(device: dict, status: str):
19
    logging.debug("payload send preparation")
20
    payload = _get_metadata()
21
    payload["device"] = device
22
    payload["status"] = status
23
    send_data(payload)
24

  
25

  
26
def usb_connected_callback(device: dict):
27
    logging.info(f"Device {device} has been connected")
28
    _send_payload_to_server(device, "connected")
29

  
30

  
31
def usb_disconnected_callback(device: dict):
32
    logging.info(f"Device {device} has been disconnected")
33
    _send_payload_to_server(device, "disconnected")
client/src/usb_detector/usb_reader.py
1
import logging
2

  
3
import usb.core
4
import usb.util
5

  
6

  
7
_invalid_devices = []
8

  
9

  
10
def read_connected_devices():
11
    logging.debug("reading all currently connected devices")
12
    detected_devices = []
13

  
14
    busses = usb.busses()
15

  
16
    for bus in busses:
17
        devices = bus.devices
18
        for dev in devices:
19
            device = {
20
                "vendor_id": dev.idVendor,
21
                "product_id": dev.idProduct
22
            }
23
            serial_number = None
24
            device_info = usb.core.find(idProduct=dev.idProduct)
25
            try:
26
                serial_number = usb.util.get_string(device_info, device_info.iSerialNumber)
27
            except ValueError:
28
                if device not in _invalid_devices:
29
                    logging.warning(f"Could not retrieve serial number from device {device}")
30
                    _invalid_devices.append(device)
31

  
32
            if serial_number is not None:
33
                if device in _invalid_devices:
34
                    _invalid_devices.remove(device)
35

  
36
                device["serial_number"] = serial_number
37
                detected_devices.append(device)
38

  
39
    return detected_devices
client/usb_detector/detector.py
1
import json
2
import logging
3
from time import sleep
4

  
5
from .usb_reader import read_connected_devices
6

  
7

  
8
_listeners_connected = []
9
_listeners_disconnected = []
10
_last_connected_devices = []
11
_config = None
12

  
13

  
14
def usb_detector_set_config(config):
15
    global _config
16
    _config = config
17

  
18

  
19
def register_listener(callback, connected: bool = True):
20
    logging.info(f"Registering callback: {callback}.")
21
    if connected is True:
22
        _listeners_connected.append(callback)
23
    else:
24
        _listeners_disconnected.append(callback)
25

  
26

  
27
def _notify_listeners(listeners: list, devices: list):
28
    if listeners is None or devices is None:
29
        return
30
    for callback in listeners:
31
        for device in devices:
32
            callback(device)
33

  
34

  
35
def _store_connected_devices(devices: list):
36
    logging.debug("storing newly connected devices")
37
    with open(_config.connected_devices_filename, "w") as file:
38
        json.dump(devices, file)
39

  
40

  
41
def _load_last_connected_devices() -> list:
42
    logging.debug("loading last connected devices")
43
    try:
44
        with open(_config.connected_devices_filename, "r") as file:
45
            return json.loads(file.read())
46
    except IOError:
47
        logging.error("loading of last connected devices failed")
48
        return []
49

  
50

  
51
def _get_connected_devices(detected_devices: list, last_connected_devices: list) -> list:
52
    return [device for device in detected_devices if device not in last_connected_devices]
53

  
54

  
55
def _get_disconnected_devices(detected_devices: list, last_connected_devices: list) -> list:
56
    return [device for device in last_connected_devices if device not in detected_devices]
57

  
58

  
59
def _update():
60
    global _last_connected_devices
61
    detected_devices = read_connected_devices()
62

  
63
    connected_devices = _get_connected_devices(detected_devices, _last_connected_devices)
64
    disconnected_devices = _get_disconnected_devices(detected_devices, _last_connected_devices)
65

  
66
    _notify_listeners(_listeners_connected, connected_devices)
67
    _notify_listeners(_listeners_disconnected, disconnected_devices)
68

  
69
    if len(connected_devices) > 0 or len(disconnected_devices) > 0:
70
        _store_connected_devices(detected_devices)
71
        _last_connected_devices = detected_devices
72

  
73

  
74
def usb_detector_run():
75
    logging.info("USB device detector is now running")
76

  
77
    global _last_connected_devices
78
    _last_connected_devices = _load_last_connected_devices()
79

  
80
    while True:
81
        _update()
82
        sleep(_config.scan_period_seconds)
client/usb_detector/event_listener.py
1
import platform
2
import logging
3
import getpass
4
from datetime import datetime
5

  
6
from api_client import send_data
7

  
8

  
9
def _get_metadata() -> dict:
10
    logging.debug("getting computer metadata")
11
    return {
12
        "username": getpass.getuser(),
13
        "hostname": platform.uname().node,
14
        "timestamp": str(datetime.now())
15
    }
16

  
17

  
18
def _send_payload_to_server(device: dict, status: str):
19
    logging.debug("payload send preparation")
20
    payload = _get_metadata()
21
    payload["device"] = device
22
    payload["status"] = status
23
    send_data(payload)
24

  
25

  
26
def usb_connected_callback(device: dict):
27
    logging.info(f"Device {device} has been connected")
28
    _send_payload_to_server(device, "connected")
29

  
30

  
31
def usb_disconnected_callback(device: dict):
32
    logging.info(f"Device {device} has been disconnected")
33
    _send_payload_to_server(device, "disconnected")
client/usb_detector/usb_reader.py
1
import logging
2

  
3
import usb.core
4
import usb.util
5

  
6

  
7
_invalid_devices = []
8

  
9

  
10
def read_connected_devices():
11
    logging.debug("reading all currently connected devices")
12
    detected_devices = []
13

  
14
    busses = usb.busses()
15

  
16
    for bus in busses:
17
        devices = bus.devices
18
        for dev in devices:
19
            device = {
20
                "vendor_id": dev.idVendor,
21
                "product_id": dev.idProduct
22
            }
23
            serial_number = None
24
            device_info = usb.core.find(idProduct=dev.idProduct)
25
            try:
26
                serial_number = usb.util.get_string(device_info, device_info.iSerialNumber)
27
            except ValueError:
28
                if device not in _invalid_devices:
29
                    logging.warning(f"Could not retrieve serial number from device {device}")
30
                    _invalid_devices.append(device)
31

  
32
            if serial_number is not None:
33
                if device in _invalid_devices:
34
                    _invalid_devices.remove(device)
35

  
36
                device["serial_number"] = serial_number
37
                detected_devices.append(device)
38

  
39
    return detected_devices

Také k dispozici: Unified diff