1
0
mirror of https://github.com/checktheroads/hyperglass synced 2024-05-11 05:55:08 +00:00

203 lines
5.9 KiB
Python
Raw Normal View History

"""Custom exceptions for hyperglass."""
# Standard Library
import sys
import json as _json
2020-12-17 13:52:12 -07:00
from typing import Dict, List, Union, Optional
2020-10-17 08:51:19 -07:00
# Third Party
from rich.console import Console
2019-12-30 09:47:31 -07:00
2020-02-03 02:35:11 -07:00
# Project
2020-04-14 10:24:20 -07:00
from hyperglass.log import log
2020-02-03 02:35:11 -07:00
from hyperglass.constants import STATUS_CODE_MAP
2020-10-17 08:51:19 -07:00
console = Console()
def validation_error_message(*errors: Dict) -> str:
"""Parse errors return from pydantic.ValidationError.errors()."""
errs = ("\n",)
for err in errors:
loc = "".join(str(loc) for loc in err["loc"])
errs += (f'Field: {loc}\n Error: {err["msg"]}\n',)
return "\n".join(errs)
class HyperglassError(Exception):
"""hyperglass base exception."""
2020-10-17 08:51:19 -07:00
def __init__(
self,
message: str = "",
level: str = "warning",
keywords: Optional[List[str]] = None,
) -> None:
"""Initialize the hyperglass base exception class."""
2019-12-30 01:44:19 -07:00
self._message = message
2020-01-21 17:30:14 -07:00
self._level = level
2019-12-30 01:44:19 -07:00
self._keywords = keywords or []
2020-01-21 17:30:14 -07:00
if self._level == "warning":
log.error(repr(self))
2020-01-21 17:30:14 -07:00
elif self._level == "danger":
log.critical(repr(self))
else:
log.info(repr(self))
2019-08-31 23:50:02 -07:00
if all(sys.exc_info()):
# Rich will raise a ValueError if print_exception() is used
# outside of a try/except block. Only use Rich for traceback
# printing if the exception is caught.
console.print_exception(extra_lines=6)
2020-10-17 08:51:19 -07:00
def __str__(self) -> str:
"""Return the instance's error message."""
2019-12-30 01:44:19 -07:00
return self._message
2019-09-03 00:42:45 -07:00
2020-10-17 08:51:19 -07:00
def __repr__(self) -> str:
"""Return the instance's severity & error message in a string."""
2020-01-21 17:30:14 -07:00
return f"[{self.level.upper()}] {self._message}"
2020-10-17 08:51:19 -07:00
def dict(self) -> Dict:
"""Return the instance's attributes as a dictionary."""
2019-12-30 01:44:19 -07:00
return {
"message": self._message,
2020-01-21 17:30:14 -07:00
"level": self._level,
2019-12-30 01:44:19 -07:00
"keywords": self._keywords,
}
2020-10-17 08:51:19 -07:00
def json(self) -> str:
"""Return the instance's attributes as a JSON object."""
return _json.dumps(self.__dict__())
@property
2020-10-17 08:51:19 -07:00
def message(self) -> str:
"""Return the instance's `message` attribute."""
2019-12-30 01:44:19 -07:00
return self._message
@property
2020-10-17 08:51:19 -07:00
def level(self) -> str:
"""Return the instance's `level` attribute."""
2020-01-21 17:30:14 -07:00
return self._level
@property
2020-10-17 08:51:19 -07:00
def keywords(self) -> List[str]:
"""Return the instance's `keywords` attribute."""
2019-12-30 01:44:19 -07:00
return self._keywords
2020-01-21 17:30:14 -07:00
@property
2020-10-17 08:51:19 -07:00
def status_code(self) -> int:
"""Return HTTP status code based on level level."""
2020-01-21 17:30:14 -07:00
return STATUS_CODE_MAP.get(self._level, 500)
class _UnformattedHyperglassError(HyperglassError):
"""Base exception class for freeform error messages."""
2020-01-21 17:30:14 -07:00
_level = "warning"
2020-01-05 00:34:44 -07:00
2020-10-17 08:51:19 -07:00
def __init__(
self, unformatted_msg: str = "", level: Optional[str] = None, **kwargs
) -> None:
"""Format error message with keyword arguments."""
2019-12-30 01:44:19 -07:00
self._message = unformatted_msg.format(**kwargs)
2020-01-21 17:30:14 -07:00
self._level = level or self._level
2019-12-30 01:44:19 -07:00
self._keywords = list(kwargs.values())
super().__init__(
2020-01-21 17:30:14 -07:00
message=self._message, level=self._level, keywords=self._keywords
2019-12-30 01:44:19 -07:00
)
2020-01-05 00:34:44 -07:00
class _PredefinedHyperglassError(HyperglassError):
_message = "undefined"
2020-01-21 17:30:14 -07:00
_level = "warning"
2020-01-05 00:34:44 -07:00
2020-10-17 08:51:19 -07:00
def __init__(self, level: Optional[str] = None, **kwargs) -> None:
2020-01-05 00:34:44 -07:00
self._fmt_msg = self._message.format(**kwargs)
2020-01-21 17:30:14 -07:00
self._level = level or self._level
2020-01-05 00:34:44 -07:00
self._keywords = list(kwargs.values())
super().__init__(
2020-01-21 17:30:14 -07:00
message=self._fmt_msg, level=self._level, keywords=self._keywords
2020-01-05 00:34:44 -07:00
)
class ConfigInvalid(HyperglassError):
"""Raised when a config item fails type or option validation."""
2020-10-17 08:51:19 -07:00
def __init__(self, errors: List[str]) -> None:
"""Parse Pydantic ValidationError."""
super().__init__(message=validation_error_message(*errors))
class ConfigError(_UnformattedHyperglassError):
"""Raised for generic user-config issues."""
2020-01-05 00:34:44 -07:00
class ConfigMissing(_PredefinedHyperglassError):
"""Raised when a required config file or item is missing or undefined."""
2019-12-30 01:44:19 -07:00
_message = (
"{missing_item} is missing or undefined and is required to start "
"hyperglass. Please consult the installation documentation."
)
2019-08-29 07:10:40 -07:00
class ScrapeError(_UnformattedHyperglassError):
"""Raised when a scrape/netmiko error occurs."""
2020-01-21 17:30:14 -07:00
_level = "danger"
class AuthError(_UnformattedHyperglassError):
"""Raised when authentication to a device fails."""
2020-01-21 17:30:14 -07:00
_level = "danger"
class RestError(_UnformattedHyperglassError):
"""Raised upon a rest API client error."""
2020-01-21 17:30:14 -07:00
_level = "danger"
2019-09-03 00:42:45 -07:00
class DeviceTimeout(_UnformattedHyperglassError):
2019-09-03 00:42:45 -07:00
"""Raised when the connection to a device times out."""
2020-01-21 17:30:14 -07:00
_level = "danger"
class InputInvalid(_UnformattedHyperglassError):
"""Raised when input validation fails."""
class InputNotAllowed(_UnformattedHyperglassError):
"""Raised when input validation fails due to a configured check."""
class ResponseEmpty(_UnformattedHyperglassError):
"""Raised when hyperglass can connect to the device but the response is empty."""
class UnsupportedDevice(_UnformattedHyperglassError):
"""Raised when an input NOS is not in the supported NOS list."""
class ParsingError(_UnformattedHyperglassError):
"""Raised when there is a problem parsing a structured response."""
def __init__(
2020-12-17 13:52:12 -07:00
self, unformatted_msg: Union[List[Dict], str], level: str = "danger", **kwargs,
2020-10-17 08:51:19 -07:00
) -> None:
"""Format error message with keyword arguments."""
2020-12-17 13:52:12 -07:00
if isinstance(unformatted_msg, list):
self._message = validation_error_message(*unformatted_msg)
else:
self._message = unformatted_msg.format(**kwargs)
self._level = level or self._level
self._keywords = list(kwargs.values())
2020-12-17 13:52:12 -07:00
super().__init__(self._message, level=self._level, keywords=self._keywords)