diff --git a/Readme.md b/Readme.md index 292860a..daeb18d 100644 --- a/Readme.md +++ b/Readme.md @@ -44,6 +44,10 @@ Set `net.ipv4.ip_forward` to 1. Enable ip packet forward. Enable DNS reloader for peers with endpoint. For each peer, a [transient timer and service](https://www.freedesktop.org/software/systemd/man/systemd-run.html) will be created and try resolving endpoint domain name every 30 seconds. If the dns record of a domain changes, wg-ops will try to update wireguard interface endpoint settings live. +**enable-collect-metrics** + +Enable metrics collector for this interface. + **iptables-forward** Add iptables rules to accept forward from this wireguard interface. Example: `iptables -A FORWARD -i wg0 -j ACCEPT` diff --git a/libwgopparser.py b/libwgopparser.py index c6b4452..fff91eb 100644 --- a/libwgopparser.py +++ b/libwgopparser.py @@ -97,10 +97,12 @@ def rsa_decrypt_base64(private_key, str_data): class Parser: def __init__(self, wgop_basepath): # paths + self._path_base = wgop_basepath self.path_get_gateway = os.path.join(wgop_basepath, 'tools/get-gateway.py') self.path_get_ip = os.path.join(wgop_basepath, 'tools/get-ip.py') self.path_get_lan_ip = os.path.join(wgop_basepath, 'tools/get-lan-ip.py') self.path_reload_dns = os.path.join(wgop_basepath, 'tools/reload-dns.py') + self.path_collect_metrics = os.path.join(wgop_basepath, 'tools/collect-metrics.py') self.path_bin_dir = os.path.join(wgop_basepath, 'bin') self.path_app_dir = os.path.join(wgop_basepath, 'app') self.path_bin_mux = os.path.join(wgop_basepath, 'bin/mux') @@ -179,6 +181,9 @@ class Parser: else: return command + def get_metrics_db_filepath(self): + return os.path.join(self._path_base, 'local/{}.db'.format(self.wg_name)) + def new_systemd_task_name(self, task_type='general'): self.flag_require_systemd_clean = True return "wg-ops-task-{}-{}-{}".format(self.wg_name, task_type, str(uuid.uuid4())) @@ -775,6 +780,10 @@ class Parser: self.result_postdown.append('iptables -t nat -D POSTROUTING -o {} -j MASQUERADE'.format(self.wg_name)) elif line.startswith('#enable-dns-reload'): self.flag_enable_dns_reload = True + elif line.startswith('#enable-collect-metrics'): + self.result_postup.append('systemd-run --unit {} --collect --timer-property AccuracySec=10 --on-calendar *:*:0/30 /usr/bin/python3 {} {} {}'.format( + self.new_systemd_task_name('metrics'), self.path_collect_metrics, self.wg_name, self.get_metrics_db_filepath() + )) elif line.startswith('#route-to'): self.flag_is_route_forward = True diff --git a/tools/collect-metrics.py b/tools/collect-metrics.py new file mode 100644 index 0000000..52f1532 --- /dev/null +++ b/tools/collect-metrics.py @@ -0,0 +1,46 @@ +import sys +import subprocess +import sqlite3 + + +if __name__ == "__main__": + if len(sys.argv) < 3: + sys.stderr.write('python3 collect-metrics.py \n') + exit(1) + + interface_name = sys.argv[1] + file_path = sys.argv[2] + + wg_raw_info = subprocess.check_output(["wg", "show", interface_name, "dump"]).decode().strip().split('\n') + if not wg_raw_info: + sys.stderr.write('wireguard interface {} not found.\n'.format(interface_name)) + exit(1) + + wg_raw_info = wg_raw_info[1:] + wg_info = [line.split('\t') for line in wg_raw_info] + + db = sqlite3.connect(file_path) + db.row_factory = sqlite3.Row + conn = db.cursor() + conn.execute("select count(1) as n from sqlite_master where type='table' and name='t_monitor'") + if not conn.fetchall()[0]['n']: + print('Table `t_monitor` not exists. Creating one...') + conn.execute(''' +CREATE TABLE t_monitor ( + f_interface varchar(64) not null, + f_peer_key varchar(64) not null, + f_endpoint varchar(256) not null default '', + f_rx_bytes bigint(20) not null default 0, + f_tx_bytes bigint(20) not null default 0, + f_last_handshake timestamp, + f_create_time timestamp not null default current_timestamp +) +''') + conn.execute("CREATE INDEX k_idx_ctime on t_monitor (f_create_time)") + db.commit() + + for info_parts in wg_info: + conn.execute('insert into t_monitor(f_interface, f_peer_key, f_endpoint, f_rx_bytes, f_tx_bytes, f_last_handshake) values (?, ?, ?, ?, ?, ?)', + (interface_name, info_parts[0], info_parts[2], info_parts[5], info_parts[6], info_parts[4])) + + db.commit()