Revize 9219c992
Přidáno uživatelem Jakub Šilhavý před více než 2 roky(ů)
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
re #9420 Created folders src and resources; renamed config.ini to client.conf