mirror of
https://github.com/tomaae/homeassistant-mikrotik_router.git
synced 2025-07-16 04:14:31 +02:00
api locking and improved error handling
This commit is contained in:
parent
ec82b33cb9
commit
f7ef7a7660
1 changed files with 87 additions and 26 deletions
|
@ -5,6 +5,7 @@ import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import importlib
|
import importlib
|
||||||
|
from threading import Lock
|
||||||
from .exceptions import ApiEntryNotFound
|
from .exceptions import ApiEntryNotFound
|
||||||
from .const import (
|
from .const import (
|
||||||
DEFAULT_LOGIN_METHOD,
|
DEFAULT_LOGIN_METHOD,
|
||||||
|
@ -37,6 +38,7 @@ class MikrotikAPI:
|
||||||
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._connection = None
|
self._connection = None
|
||||||
self._connected = False
|
self._connected = False
|
||||||
|
@ -49,7 +51,7 @@ class MikrotikAPI:
|
||||||
# ---------------------------
|
# ---------------------------
|
||||||
# connect
|
# connect
|
||||||
# ---------------------------
|
# ---------------------------
|
||||||
def connect(self):
|
def connect(self) -> bool:
|
||||||
"""Connect to Mikrotik device."""
|
"""Connect to Mikrotik device."""
|
||||||
self.error = ""
|
self.error = ""
|
||||||
self._connected = None
|
self._connected = None
|
||||||
|
@ -67,7 +69,7 @@ class MikrotikAPI:
|
||||||
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()
|
||||||
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 (
|
||||||
|
@ -80,11 +82,18 @@ class MikrotikAPI:
|
||||||
BrokenPipeError,
|
BrokenPipeError,
|
||||||
OSError
|
OSError
|
||||||
) as api_error:
|
) as api_error:
|
||||||
_LOGGER.error("Mikrotik %s: %s", self._host, api_error)
|
self.lock.release()
|
||||||
|
_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:
|
||||||
|
self.lock.release()
|
||||||
|
_LOGGER.error("Mikrotik %s error while connecting: %s", self._host, "Unknown")
|
||||||
|
self._connection = None
|
||||||
|
return False
|
||||||
else:
|
else:
|
||||||
|
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
|
||||||
|
|
||||||
|
@ -107,23 +116,26 @@ class MikrotikAPI:
|
||||||
# ---------------------------
|
# ---------------------------
|
||||||
# connected
|
# connected
|
||||||
# ---------------------------
|
# ---------------------------
|
||||||
def connected(self):
|
def connected(self) -> bool:
|
||||||
"""Return connected boolean."""
|
"""Return connected boolean."""
|
||||||
return self._connected
|
return self._connected
|
||||||
|
|
||||||
# ---------------------------
|
# ---------------------------
|
||||||
# path
|
# path
|
||||||
# ---------------------------
|
# ---------------------------
|
||||||
def path(self, path):
|
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 not self.connect():
|
if not self.connect():
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
self.lock.acquire()
|
||||||
try:
|
try:
|
||||||
response = self._connection.path(path)
|
response = self._connection.path(path)
|
||||||
_LOGGER.debug("API response (%s): %s", path, tuple(response))
|
tmp = tuple(response)
|
||||||
|
_LOGGER.debug("API response (%s): %s", path, tmp)
|
||||||
except librouteros_custom.exceptions.ConnectionClosed:
|
except librouteros_custom.exceptions.ConnectionClosed:
|
||||||
|
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
|
||||||
|
@ -132,22 +144,33 @@ class MikrotikAPI:
|
||||||
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,
|
||||||
|
BrokenPipeError,
|
||||||
|
OSError,
|
||||||
|
ValueError
|
||||||
) as api_error:
|
) as api_error:
|
||||||
_LOGGER.error("Mikrotik %s connection error %s", self._host, api_error)
|
self.lock.release()
|
||||||
|
_LOGGER.error("Mikrotik %s error while path %s", self._host, api_error)
|
||||||
return None
|
return None
|
||||||
|
except:
|
||||||
|
self.lock.release()
|
||||||
|
_LOGGER.error("Mikrotik %s error while path %s", self._host, "unknown")
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
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):
|
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 not self.connect():
|
if not self.connect():
|
||||||
return None
|
return False
|
||||||
|
|
||||||
response = self.path(path)
|
response = self.path(path)
|
||||||
if response is None:
|
if response is None:
|
||||||
|
@ -166,21 +189,34 @@ class MikrotikAPI:
|
||||||
mod_param: mod_value
|
mod_param: mod_value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.lock.acquire()
|
||||||
try:
|
try:
|
||||||
response.update(**params)
|
response.update(**params)
|
||||||
except librouteros_custom.exceptions.ConnectionClosed:
|
except librouteros_custom.exceptions.ConnectionClosed:
|
||||||
|
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 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,
|
||||||
|
BrokenPipeError,
|
||||||
|
OSError,
|
||||||
|
ValueError
|
||||||
) as api_error:
|
) as api_error:
|
||||||
_LOGGER.error("Mikrotik %s connection error %s", self._host, api_error)
|
self.lock.release()
|
||||||
return None
|
_LOGGER.error("Mikrotik %s error while update %s", self._host, api_error)
|
||||||
|
return False
|
||||||
|
except:
|
||||||
|
self.lock.release()
|
||||||
|
_LOGGER.error("Mikrotik %s error while update %s", self._host, "unknown")
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
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)
|
||||||
|
@ -191,12 +227,12 @@ class MikrotikAPI:
|
||||||
# ---------------------------
|
# ---------------------------
|
||||||
# run_script
|
# run_script
|
||||||
# ---------------------------
|
# ---------------------------
|
||||||
def run_script(self, name):
|
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 not self.connect():
|
if not self.connect():
|
||||||
return None
|
return False
|
||||||
|
|
||||||
response = self.path('/system/script')
|
response = self.path('/system/script')
|
||||||
if response is None:
|
if response is None:
|
||||||
|
@ -210,23 +246,35 @@ class MikrotikAPI:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
entry_found = True
|
entry_found = True
|
||||||
|
self.lock.acquire()
|
||||||
try:
|
try:
|
||||||
run = response('run', **{'.id': tmp['.id']})
|
run = response('run', **{'.id': tmp['.id']})
|
||||||
|
tuple(run)
|
||||||
except librouteros_custom.exceptions.ConnectionClosed:
|
except librouteros_custom.exceptions.ConnectionClosed:
|
||||||
|
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 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,
|
||||||
|
BrokenPipeError,
|
||||||
|
OSError,
|
||||||
|
ValueError
|
||||||
) as api_error:
|
) as api_error:
|
||||||
_LOGGER.error("Mikrotik %s connection error %s", self._host, api_error)
|
self.lock.release()
|
||||||
return None
|
_LOGGER.error("Mikrotik %s error while run_script %s", self._host, api_error)
|
||||||
|
return False
|
||||||
tuple(run)
|
except:
|
||||||
|
self.lock.release()
|
||||||
|
_LOGGER.error("Mikrotik %s error while run_script %s", self._host, "unknown")
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
self.lock.release()
|
||||||
|
|
||||||
if not entry_found:
|
if not entry_found:
|
||||||
error = "Script \"{}\" not found".format(name)
|
error = "Script \"{}\" not found".format(name)
|
||||||
|
@ -237,7 +285,7 @@ class MikrotikAPI:
|
||||||
# ---------------------------
|
# ---------------------------
|
||||||
# get_traffic
|
# get_traffic
|
||||||
# ---------------------------
|
# ---------------------------
|
||||||
def get_traffic(self, interfaces):
|
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:
|
||||||
|
@ -249,10 +297,12 @@ class MikrotikAPI:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
args = {'interface': interfaces, 'once': True}
|
args = {'interface': interfaces, 'once': True}
|
||||||
|
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", tuple(response))
|
||||||
except librouteros_custom.exceptions.ConnectionClosed:
|
except librouteros_custom.exceptions.ConnectionClosed:
|
||||||
|
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
|
||||||
|
@ -261,9 +311,20 @@ class MikrotikAPI:
|
||||||
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,
|
||||||
|
BrokenPipeError,
|
||||||
|
OSError,
|
||||||
|
ValueError
|
||||||
) as api_error:
|
) as api_error:
|
||||||
_LOGGER.error("Mikrotik %s connection error %s", self._host, api_error)
|
self.lock.release()
|
||||||
|
_LOGGER.error("Mikrotik %s error while get_traffic %s", self._host, api_error)
|
||||||
return None
|
return None
|
||||||
|
except:
|
||||||
|
self.lock.release()
|
||||||
|
_LOGGER.error("Mikrotik %s error while get_traffic %s", self._host, "unknown")
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
self.lock.release()
|
||||||
|
|
||||||
return traffic
|
return traffic if traffic else None
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue