fixed traffic debug

This commit is contained in:
tomaae 2020-03-11 23:34:26 +01:00
parent f111131a9b
commit df9f551b96

View file

@ -1,361 +1,361 @@
"""Mikrotik API for Mikrotik Router.""" """Mikrotik API for Mikrotik Router."""
import ssl import ssl
import logging import logging
import os import os
import sys import sys
import time import time
import importlib import importlib
from threading import Lock from threading import Lock
from .exceptions import ApiEntryNotFound from .exceptions import ApiEntryNotFound
from .const import ( from .const import (
DEFAULT_LOGIN_METHOD, DEFAULT_LOGIN_METHOD,
DEFAULT_ENCODING, DEFAULT_ENCODING,
) )
MODULE_PATH = os.path.join(os.path.dirname(__file__), "librouteros_custom", "__init__.py") MODULE_PATH = os.path.join(os.path.dirname(__file__), "librouteros_custom", "__init__.py")
MODULE_NAME = "librouteros_custom" MODULE_NAME = "librouteros_custom"
spec = importlib.util.spec_from_file_location(MODULE_NAME, MODULE_PATH) spec = importlib.util.spec_from_file_location(MODULE_NAME, MODULE_PATH)
librouteros_custom = importlib.util.module_from_spec(spec) librouteros_custom = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = librouteros_custom sys.modules[spec.name] = librouteros_custom
spec.loader.exec_module(librouteros_custom) spec.loader.exec_module(librouteros_custom)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
# --------------------------- # ---------------------------
# MikrotikAPI # MikrotikAPI
# --------------------------- # ---------------------------
class MikrotikAPI: class MikrotikAPI:
"""Handle all communication with the Mikrotik API.""" """Handle all communication with the Mikrotik API."""
def __init__(self, host, username, password, port=0, use_ssl=True, login_method=DEFAULT_LOGIN_METHOD, encoding=DEFAULT_ENCODING): def __init__(self, host, username, password, port=0, use_ssl=True, login_method=DEFAULT_LOGIN_METHOD, encoding=DEFAULT_ENCODING):
"""Initialize the Mikrotik Client.""" """Initialize the Mikrotik Client."""
self._host = host self._host = host
self._use_ssl = use_ssl self._use_ssl = use_ssl
self._port = port self._port = port
self._username = username self._username = username
self._password = password self._password = password
self._login_method = login_method self._login_method = login_method
self._encoding = encoding self._encoding = encoding
self._ssl_wrapper = None self._ssl_wrapper = None
self.lock = Lock() self.lock = Lock()
self._connection = None self._connection = None
self._connected = False self._connected = False
self._connection_epoch = 0 self._connection_epoch = 0
self._connection_retry_sec = 58 self._connection_retry_sec = 58
self.error = None self.error = None
# Default ports # Default ports
if not self._port: if not self._port:
self._port = 8729 if self._use_ssl else 8728 self._port = 8729 if self._use_ssl else 8728
# --------------------------- # ---------------------------
# disconnect # disconnect
# --------------------------- # ---------------------------
def disconnect(self) -> bool: def disconnect(self) -> bool:
"""Disconnect from Mikrotik device.""" """Disconnect from Mikrotik device."""
self._connected = False self._connected = False
self._connection = None self._connection = None
self._connection_epoch = 0 self._connection_epoch = 0
# --------------------------- # ---------------------------
# connect # connect
# --------------------------- # ---------------------------
def connect(self) -> bool: def connect(self) -> bool:
"""Connect to Mikrotik device.""" """Connect to Mikrotik device."""
self.error = "" self.error = ""
self._connected = None self._connected = None
self._connection_epoch = time.time() self._connection_epoch = time.time()
kwargs = { kwargs = {
"encoding": self._encoding, "encoding": self._encoding,
"login_methods": self._login_method, "login_methods": self._login_method,
"port": self._port, "port": self._port,
} }
if self._use_ssl: if self._use_ssl:
if self._ssl_wrapper is None: if self._ssl_wrapper is None:
ssl_context = ssl.create_default_context() ssl_context = ssl.create_default_context()
ssl_context.check_hostname = False ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE ssl_context.verify_mode = ssl.CERT_NONE
self._ssl_wrapper = ssl_context.wrap_socket self._ssl_wrapper = ssl_context.wrap_socket
kwargs["ssl_wrapper"] = self._ssl_wrapper kwargs["ssl_wrapper"] = self._ssl_wrapper
self.lock.acquire() self.lock.acquire()
try: try:
self._connection = librouteros_custom.connect(self._host, self._username, self._password, **kwargs) self._connection = librouteros_custom.connect(self._host, self._username, self._password, **kwargs)
except ( except (
librouteros_custom.exceptions.TrapError, librouteros_custom.exceptions.TrapError,
librouteros_custom.exceptions.MultiTrapError, librouteros_custom.exceptions.MultiTrapError,
librouteros_custom.exceptions.ConnectionClosed, librouteros_custom.exceptions.ConnectionClosed,
librouteros_custom.exceptions.ProtocolError, librouteros_custom.exceptions.ProtocolError,
librouteros_custom.exceptions.FatalError, librouteros_custom.exceptions.FatalError,
ssl.SSLError, ssl.SSLError,
BrokenPipeError, BrokenPipeError,
OSError OSError
) as api_error: ) as api_error:
self.lock.release() self.lock.release()
_LOGGER.error("Mikrotik %s error while connecting: %s", self._host, api_error) _LOGGER.error("Mikrotik %s error while connecting: %s", self._host, api_error)
self.error_to_strings("%s" % api_error) self.error_to_strings("%s" % api_error)
self._connection = None self._connection = None
return False return False
except: except:
self.lock.release() self.lock.release()
_LOGGER.error("Mikrotik %s error while connecting: %s", self._host, "Unknown") _LOGGER.error("Mikrotik %s error while connecting: %s", self._host, "Unknown")
self._connection = None self._connection = None
return False return False
else: else:
self.lock.release() self.lock.release()
_LOGGER.info("Mikrotik Connected to %s", self._host) _LOGGER.info("Mikrotik Connected to %s", self._host)
self._connected = True self._connected = True
return self._connected return self._connected
# --------------------------- # ---------------------------
# error_to_strings # error_to_strings
# --------------------------- # ---------------------------
def error_to_strings(self, error): def error_to_strings(self, error):
"""Translate error output to error string.""" """Translate error output to error string."""
self.error = "cannot_connect" self.error = "cannot_connect"
if error == "invalid user name or password (6)": if error == "invalid user name or password (6)":
self.error = "wrong_login" self.error = "wrong_login"
if "ALERT_HANDSHAKE_FAILURE" in error: if "ALERT_HANDSHAKE_FAILURE" in error:
self.error = "ssl_handshake_failure" self.error = "ssl_handshake_failure"
# --------------------------- # ---------------------------
# connected # connected
# --------------------------- # ---------------------------
def connected(self) -> bool: def connected(self) -> bool:
"""Return connected boolean.""" """Return connected boolean."""
return self._connected return self._connected
# --------------------------- # ---------------------------
# path # path
# --------------------------- # ---------------------------
def path(self, path) -> list: def path(self, path) -> list:
"""Retrieve data from Mikrotik API.""" """Retrieve data from Mikrotik API."""
if not self._connected or not self._connection: if not self._connected or not self._connection:
if self._connection_epoch > time.time() - self._connection_retry_sec: if self._connection_epoch > time.time() - self._connection_retry_sec:
return None return None
if not self.connect(): if not self.connect():
return None return None
self.lock.acquire() self.lock.acquire()
try: try:
response = self._connection.path(path) response = self._connection.path(path)
tmp = tuple(response) tmp = tuple(response)
_LOGGER.debug("API response (%s): %s", path, tmp) _LOGGER.debug("API response (%s): %s", path, tmp)
except librouteros_custom.exceptions.ConnectionClosed: except librouteros_custom.exceptions.ConnectionClosed:
self.lock.release() self.lock.release()
_LOGGER.error("Mikrotik %s connection closed", self._host) _LOGGER.error("Mikrotik %s connection closed", self._host)
self._connected = False self._connected = False
self._connection = None self._connection = None
return None return None
except ( except (
librouteros_custom.exceptions.TrapError, librouteros_custom.exceptions.TrapError,
librouteros_custom.exceptions.MultiTrapError, librouteros_custom.exceptions.MultiTrapError,
librouteros_custom.exceptions.ProtocolError, librouteros_custom.exceptions.ProtocolError,
librouteros_custom.exceptions.FatalError, librouteros_custom.exceptions.FatalError,
ssl.SSLError, ssl.SSLError,
BrokenPipeError, BrokenPipeError,
OSError, OSError,
ValueError ValueError
) as api_error: ) as api_error:
self.lock.release() self.lock.release()
_LOGGER.error("Mikrotik %s error while path %s", self._host, api_error) _LOGGER.error("Mikrotik %s error while path %s", self._host, api_error)
self.disconnect() self.disconnect()
return None return None
except: except:
self.lock.release() self.lock.release()
_LOGGER.error("Mikrotik %s error while path %s", self._host, "unknown") _LOGGER.error("Mikrotik %s error while path %s", self._host, "unknown")
self.disconnect() self.disconnect()
return None return None
else: else:
self.lock.release() self.lock.release()
return response if response else None return response if response else None
# --------------------------- # ---------------------------
# update # update
# --------------------------- # ---------------------------
def update(self, path, param, value, mod_param, mod_value) -> bool: def update(self, path, param, value, mod_param, mod_value) -> bool:
"""Modify a parameter""" """Modify a parameter"""
entry_found = False entry_found = False
if not self._connected or not self._connection: if not self._connected or not self._connection:
if self._connection_epoch > time.time() - self._connection_retry_sec: if self._connection_epoch > time.time() - self._connection_retry_sec:
return None return None
if not self.connect(): if not self.connect():
return False return False
response = self.path(path) response = self.path(path)
if response is None: if response is None:
return False return False
for tmp in response: for tmp in response:
if param not in tmp: if param not in tmp:
continue continue
if tmp[param] != value: if tmp[param] != value:
continue continue
entry_found = True entry_found = True
params = { params = {
'.id': tmp['.id'], '.id': tmp['.id'],
mod_param: mod_value mod_param: mod_value
} }
self.lock.acquire() self.lock.acquire()
try: try:
response.update(**params) response.update(**params)
except librouteros_custom.exceptions.ConnectionClosed: except librouteros_custom.exceptions.ConnectionClosed:
self.lock.release() self.lock.release()
_LOGGER.error("Mikrotik %s connection closed", self._host) _LOGGER.error("Mikrotik %s connection closed", self._host)
self._connected = False self._connected = False
self._connection = None self._connection = None
return False return False
except ( except (
librouteros_custom.exceptions.TrapError, librouteros_custom.exceptions.TrapError,
librouteros_custom.exceptions.MultiTrapError, librouteros_custom.exceptions.MultiTrapError,
librouteros_custom.exceptions.ProtocolError, librouteros_custom.exceptions.ProtocolError,
librouteros_custom.exceptions.FatalError, librouteros_custom.exceptions.FatalError,
ssl.SSLError, ssl.SSLError,
BrokenPipeError, BrokenPipeError,
OSError, OSError,
ValueError ValueError
) as api_error: ) as api_error:
self.lock.release() self.lock.release()
_LOGGER.error("Mikrotik %s error while update %s", self._host, api_error) _LOGGER.error("Mikrotik %s error while update %s", self._host, api_error)
self.disconnect() self.disconnect()
return False return False
except: except:
self.lock.release() self.lock.release()
_LOGGER.error("Mikrotik %s error while update %s", self._host, "unknown") _LOGGER.error("Mikrotik %s error while update %s", self._host, "unknown")
self.disconnect() self.disconnect()
return False return False
else: else:
self.lock.release() self.lock.release()
if not entry_found: if not entry_found:
error = "Parameter \"{}\" with value \"{}\" not found".format(param, value) error = "Parameter \"{}\" with value \"{}\" not found".format(param, value)
raise ApiEntryNotFound(error) raise ApiEntryNotFound(error)
return True return True
# --------------------------- # ---------------------------
# run_script # run_script
# --------------------------- # ---------------------------
def run_script(self, name) -> bool: def run_script(self, name) -> bool:
"""Run script""" """Run script"""
entry_found = False entry_found = False
if not self._connected or not self._connection: if not self._connected or not self._connection:
if self._connection_epoch > time.time() - self._connection_retry_sec: if self._connection_epoch > time.time() - self._connection_retry_sec:
return None return None
if not self.connect(): if not self.connect():
return False return False
response = self.path('/system/script') response = self.path('/system/script')
if response is None: if response is None:
return False return False
for tmp in response: for tmp in response:
if 'name' not in tmp: if 'name' not in tmp:
continue continue
if tmp['name'] != name: if tmp['name'] != name:
continue continue
entry_found = True entry_found = True
self.lock.acquire() self.lock.acquire()
try: try:
run = response('run', **{'.id': tmp['.id']}) run = response('run', **{'.id': tmp['.id']})
tuple(run) tuple(run)
except librouteros_custom.exceptions.ConnectionClosed: except librouteros_custom.exceptions.ConnectionClosed:
self.lock.release() self.lock.release()
_LOGGER.error("Mikrotik %s connection closed", self._host) _LOGGER.error("Mikrotik %s connection closed", self._host)
self._connected = False self._connected = False
self._connection = None self._connection = None
return False return False
except ( except (
librouteros_custom.exceptions.TrapError, librouteros_custom.exceptions.TrapError,
librouteros_custom.exceptions.MultiTrapError, librouteros_custom.exceptions.MultiTrapError,
librouteros_custom.exceptions.ProtocolError, librouteros_custom.exceptions.ProtocolError,
librouteros_custom.exceptions.FatalError, librouteros_custom.exceptions.FatalError,
ssl.SSLError, ssl.SSLError,
BrokenPipeError, BrokenPipeError,
OSError, OSError,
ValueError ValueError
) as api_error: ) as api_error:
self.lock.release() self.lock.release()
_LOGGER.error("Mikrotik %s error while run_script %s", self._host, api_error) _LOGGER.error("Mikrotik %s error while run_script %s", self._host, api_error)
self.disconnect() self.disconnect()
return False return False
except: except:
self.lock.release() self.lock.release()
_LOGGER.error("Mikrotik %s error while run_script %s", self._host, "unknown") _LOGGER.error("Mikrotik %s error while run_script %s", self._host, "unknown")
self.disconnect() self.disconnect()
return False return False
else: else:
self.lock.release() self.lock.release()
if not entry_found: if not entry_found:
error = "Script \"{}\" not found".format(name) error = "Script \"{}\" not found".format(name)
raise ApiEntryNotFound(error) raise ApiEntryNotFound(error)
return True return True
# --------------------------- # ---------------------------
# get_traffic # get_traffic
# --------------------------- # ---------------------------
def get_traffic(self, interfaces) -> list: def get_traffic(self, interfaces) -> list:
"""Get traffic stats""" """Get traffic stats"""
traffic = None traffic = None
if not self._connected or not self._connection: if not self._connected or not self._connection:
if self._connection_epoch > time.time() - self._connection_retry_sec: if self._connection_epoch > time.time() - self._connection_retry_sec:
return None return None
if not self.connect(): if not self.connect():
return None return None
response = self.path('/interface') response = self.path('/interface')
if response is None: if response is None:
return None return None
args = {'interface': interfaces, 'once': True} args = {'interface': interfaces, 'once': True}
self.lock.acquire() self.lock.acquire()
try: try:
traffic = response('monitor-traffic', **args) traffic = response('monitor-traffic', **args)
_LOGGER.debug("API response (%s): %s", "/interface/monitor-traffic", tuple(response)) _LOGGER.debug("API response (%s): %s", "/interface/monitor-traffic", traffic)
except librouteros_custom.exceptions.ConnectionClosed: except librouteros_custom.exceptions.ConnectionClosed:
self.lock.release() self.lock.release()
_LOGGER.error("Mikrotik %s connection closed", self._host) _LOGGER.error("Mikrotik %s connection closed", self._host)
self._connected = False self._connected = False
self._connection = None self._connection = None
return None return None
except ( except (
librouteros_custom.exceptions.TrapError, librouteros_custom.exceptions.TrapError,
librouteros_custom.exceptions.MultiTrapError, librouteros_custom.exceptions.MultiTrapError,
librouteros_custom.exceptions.ProtocolError, librouteros_custom.exceptions.ProtocolError,
librouteros_custom.exceptions.FatalError, librouteros_custom.exceptions.FatalError,
ssl.SSLError, ssl.SSLError,
BrokenPipeError, BrokenPipeError,
OSError, OSError,
ValueError ValueError
) as api_error: ) as api_error:
self.lock.release() self.lock.release()
_LOGGER.error("Mikrotik %s error while get_traffic %s", self._host, api_error) _LOGGER.error("Mikrotik %s error while get_traffic %s", self._host, api_error)
self.disconnect() self.disconnect()
return None return None
except: except:
self.lock.release() self.lock.release()
_LOGGER.error("Mikrotik %s error while get_traffic %s", self._host, "unknown") _LOGGER.error("Mikrotik %s error while get_traffic %s", self._host, "unknown")
self.disconnect() self.disconnect()
return None return None
else: else:
self.lock.release() self.lock.release()
return traffic if traffic else None return traffic if traffic else None