mirror of
https://github.com/checktheroads/hyperglass
synced 2024-05-11 05:55:08 +00:00
Add directives to global state
This commit is contained in:
1
.flake8
1
.flake8
@@ -12,6 +12,7 @@ per-file-ignores=
|
|||||||
hyperglass/models/api/*.py:N805,E0213,R0903,E501,C0301
|
hyperglass/models/api/*.py:N805,E0213,R0903,E501,C0301
|
||||||
hyperglass/models/commands/*.py:N805,E0213,R0903,E501,C0301
|
hyperglass/models/commands/*.py:N805,E0213,R0903,E501,C0301
|
||||||
hyperglass/parsing/models/*.py:N805,E0213,R0903
|
hyperglass/parsing/models/*.py:N805,E0213,R0903
|
||||||
|
hyperglass/defaults/*/*.py:E501
|
||||||
hyperglass/configuration/models/*.py:N805,E0213,R0903,E501,C0301
|
hyperglass/configuration/models/*.py:N805,E0213,R0903,E501,C0301
|
||||||
# Disable unused import warning for modules
|
# Disable unused import warning for modules
|
||||||
hyperglass/*/__init__.py:F401
|
hyperglass/*/__init__.py:F401
|
||||||
|
|||||||
25
hyperglass/defaults/directives/__init__.py
Normal file
25
hyperglass/defaults/directives/__init__.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
"""Built-in hyperglass directives."""
|
||||||
|
|
||||||
|
# Standard Library
|
||||||
|
import pkgutil
|
||||||
|
import importlib
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Project
|
||||||
|
from hyperglass.log import log
|
||||||
|
from hyperglass.state import use_state
|
||||||
|
|
||||||
|
|
||||||
|
def register_builtin_directives() -> None:
|
||||||
|
"""Find all directives and register them with global state manager."""
|
||||||
|
directives_dir = Path(__file__).parent
|
||||||
|
state = use_state()
|
||||||
|
for _, name, __ in pkgutil.iter_modules([directives_dir]):
|
||||||
|
module = importlib.import_module(f"hyperglass.defaults.directives.{name}")
|
||||||
|
|
||||||
|
if not all((hasattr(module, "__all__"), len(getattr(module, "__all__", ())) > 0)):
|
||||||
|
# Warn if there is no __all__ export or if it is empty.
|
||||||
|
log.warning("Module '{!s}' is missing an '__all__' export", module)
|
||||||
|
|
||||||
|
exports = (getattr(module, p) for p in module.__all__ if hasattr(module, p))
|
||||||
|
state.add_directive(*exports)
|
||||||
165
hyperglass/defaults/directives/juniper.py
Normal file
165
hyperglass/defaults/directives/juniper.py
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
"""Default Juniper Directives."""
|
||||||
|
|
||||||
|
# Project
|
||||||
|
from hyperglass.models.directive import Rule, Text, NativeDirective
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
"JuniperBGPRoute",
|
||||||
|
"JuniperBGPASPath",
|
||||||
|
"JuniperBGPCommunity",
|
||||||
|
"JuniperPing",
|
||||||
|
"JuniperTraceroute",
|
||||||
|
"JuniperBGPRouteTable",
|
||||||
|
"JuniperBGPASPathTable",
|
||||||
|
"JuniperBGPCommunityTable",
|
||||||
|
)
|
||||||
|
|
||||||
|
JuniperBGPRoute = NativeDirective(
|
||||||
|
id="__hyperglass_juniper_bgp_route__",
|
||||||
|
name="BGP Route",
|
||||||
|
rules=[
|
||||||
|
Rule(
|
||||||
|
condition="0.0.0.0/0",
|
||||||
|
action="permit",
|
||||||
|
command="show route protocol table inet.0 {target} detail",
|
||||||
|
),
|
||||||
|
Rule(
|
||||||
|
condition="::/0",
|
||||||
|
action="permit",
|
||||||
|
command="show route protocol table inet6.0 {target} detail",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
field=Text(description="IP Address, Prefix, or Hostname"),
|
||||||
|
platforms=["juniper"],
|
||||||
|
)
|
||||||
|
|
||||||
|
JuniperBGPASPath = NativeDirective(
|
||||||
|
id="__hyperglass_juniper_bgp_aspath__",
|
||||||
|
name="BGP AS Path",
|
||||||
|
rules=[
|
||||||
|
Rule(
|
||||||
|
condition="*",
|
||||||
|
action="permit",
|
||||||
|
commands=[
|
||||||
|
'show route protocol table inet.0 aspath-regex "{target}"',
|
||||||
|
'show route protocol table inet6.0 aspath-regex "{target}"',
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
field=Text(description="AS Path Regular Expression"),
|
||||||
|
platforms=["juniper"],
|
||||||
|
)
|
||||||
|
|
||||||
|
JuniperBGPCommunity = NativeDirective(
|
||||||
|
id="__hyperglass_juniper_bgp_community__",
|
||||||
|
name="BGP Community",
|
||||||
|
rules=[
|
||||||
|
Rule(
|
||||||
|
condition="*",
|
||||||
|
action="permit",
|
||||||
|
commands=[
|
||||||
|
'show route protocol table inet.0 community "{target}" detail',
|
||||||
|
'show route protocol table inet6.0 community "{target}" detail',
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
field=Text(description="AS Path Regular Expression"),
|
||||||
|
platforms=["juniper"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
JuniperPing = NativeDirective(
|
||||||
|
id="__hyperglass_juniper_ping__",
|
||||||
|
name="Ping",
|
||||||
|
rules=[
|
||||||
|
Rule(
|
||||||
|
condition="0.0.0.0/0",
|
||||||
|
action="permit",
|
||||||
|
command="ping inet {target} count 5 source {source4}",
|
||||||
|
),
|
||||||
|
Rule(
|
||||||
|
condition="::/0",
|
||||||
|
action="permit",
|
||||||
|
command="ping inet6 {target} count 5 source {source6}",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
field=Text(description="IP Address, Prefix, or Hostname"),
|
||||||
|
platforms=["juniper"],
|
||||||
|
)
|
||||||
|
|
||||||
|
JuniperTraceroute = NativeDirective(
|
||||||
|
id="__hyperglass_juniper_traceroute__",
|
||||||
|
name="Traceroute",
|
||||||
|
rules=[
|
||||||
|
Rule(
|
||||||
|
condition="0.0.0.0/0",
|
||||||
|
action="permit",
|
||||||
|
command="traceroute inet {target} wait 1 source {source4}",
|
||||||
|
),
|
||||||
|
Rule(
|
||||||
|
condition="::/0",
|
||||||
|
action="permit",
|
||||||
|
command="traceroute inet6 {target} wait 1 source {source6}",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
field=Text(description="IP Address, Prefix, or Hostname"),
|
||||||
|
platforms=["juniper"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Table Output Directives
|
||||||
|
|
||||||
|
JuniperBGPRouteTable = NativeDirective(
|
||||||
|
id="__hyperglass_juniper_bgp_route_table__",
|
||||||
|
name="BGP Route",
|
||||||
|
rules=[
|
||||||
|
Rule(
|
||||||
|
condition="0.0.0.0/0",
|
||||||
|
action="permit",
|
||||||
|
command="show route protocol bgp table inet.0 {target} best detail | display xml",
|
||||||
|
),
|
||||||
|
Rule(
|
||||||
|
condition="::/0",
|
||||||
|
action="permit",
|
||||||
|
command="show route protocol bgp table inet6.0 {target} best detail | display xml",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
field=Text(description="IP Address, Prefix, or Hostname"),
|
||||||
|
table_output=True,
|
||||||
|
platforms=["juniper"],
|
||||||
|
)
|
||||||
|
|
||||||
|
JuniperBGPASPathTable = NativeDirective(
|
||||||
|
id="__hyperglass_juniper_bgp_aspath_table__",
|
||||||
|
name="BGP AS Path",
|
||||||
|
rules=[
|
||||||
|
Rule(
|
||||||
|
condition="*",
|
||||||
|
action="permit",
|
||||||
|
commands=[
|
||||||
|
'show route protocol bgp table inet.0 aspath-regex "{target}" detail | display xml',
|
||||||
|
'show route protocol bgp table inet6.0 aspath-regex "{target}" detail | display xml',
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
field=Text(description="AS Path Regular Expression"),
|
||||||
|
table_output=True,
|
||||||
|
platforms=["juniper"],
|
||||||
|
)
|
||||||
|
|
||||||
|
JuniperBGPCommunityTable = NativeDirective(
|
||||||
|
id="__hyperglass_juniper_bgp_community_table__",
|
||||||
|
name="BGP Community",
|
||||||
|
rules=[
|
||||||
|
Rule(
|
||||||
|
condition="*",
|
||||||
|
action="permit",
|
||||||
|
commands=[
|
||||||
|
"show route protocol bgp table inet.0 community {target} detail | display xml",
|
||||||
|
"show route protocol bgp table inet6.0 community {target} detail | display xml",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
field=Text(description="AS Path Regular Expression"),
|
||||||
|
table_output=True,
|
||||||
|
platforms=["juniper"],
|
||||||
|
)
|
||||||
@@ -21,6 +21,7 @@ from .plugins import (
|
|||||||
)
|
)
|
||||||
from .constants import MIN_NODE_VERSION, MIN_PYTHON_VERSION, __version__
|
from .constants import MIN_NODE_VERSION, MIN_PYTHON_VERSION, __version__
|
||||||
from .util.frontend import get_node_version
|
from .util.frontend import get_node_version
|
||||||
|
from .defaults.directives import register_builtin_directives
|
||||||
|
|
||||||
if t.TYPE_CHECKING:
|
if t.TYPE_CHECKING:
|
||||||
# Local
|
# Local
|
||||||
@@ -88,6 +89,8 @@ def on_starting(server: "Arbiter"):
|
|||||||
|
|
||||||
state = use_state()
|
state = use_state()
|
||||||
|
|
||||||
|
register_builtin_directives()
|
||||||
|
|
||||||
register_all_plugins(state.devices)
|
register_all_plugins(state.devices)
|
||||||
|
|
||||||
asyncio.run(build_ui())
|
asyncio.run(build_ui())
|
||||||
|
|||||||
@@ -214,7 +214,16 @@ class HyperglassMultiModel(GenericModel, t.Generic[MultiModelT]):
|
|||||||
for o in (*self, *to_add)
|
for o in (*self, *to_add)
|
||||||
if getattr(o, unique_by) == v
|
if getattr(o, unique_by) == v
|
||||||
}
|
}
|
||||||
self.__root__ = list(unique_by_objects.values())
|
new: t.List[MultiModelT] = list(unique_by_objects.values())
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.__root__ = [*self.__root__, *to_add]
|
new: t.List[MultiModelT] = [*self.__root__, *to_add]
|
||||||
|
self.__root__ = new
|
||||||
self._count = len(self.__root__)
|
self._count = len(self.__root__)
|
||||||
|
for item in new:
|
||||||
|
log.debug(
|
||||||
|
"Added {} '{!s}' to {}",
|
||||||
|
item.__class__.__name__,
|
||||||
|
getattr(item, self.accessor),
|
||||||
|
self.__class__.__name__,
|
||||||
|
)
|
||||||
|
|||||||
@@ -122,9 +122,9 @@ class BGPRoutePluginJuniper(OutputPlugin):
|
|||||||
__hyperglass_builtin__: bool = PrivateAttr(True)
|
__hyperglass_builtin__: bool = PrivateAttr(True)
|
||||||
platforms: Sequence[str] = ("juniper",)
|
platforms: Sequence[str] = ("juniper",)
|
||||||
directives: Sequence[str] = (
|
directives: Sequence[str] = (
|
||||||
"__hyperglass_juniper_bgp_route__",
|
"__hyperglass_juniper_bgp_route_table__",
|
||||||
"__hyperglass_juniper_bgp_aspath__",
|
"__hyperglass_juniper_bgp_aspath_table__",
|
||||||
"__hyperglass_juniper_bgp_community__",
|
"__hyperglass_juniper_bgp_community_table__",
|
||||||
)
|
)
|
||||||
|
|
||||||
def process(self, output: "OutputType", device: "Device") -> "OutputType":
|
def process(self, output: "OutputType", device: "Device") -> "OutputType":
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from ..settings import Settings
|
|||||||
if t.TYPE_CHECKING:
|
if t.TYPE_CHECKING:
|
||||||
# Project
|
# Project
|
||||||
from hyperglass.models.ui import UIParameters
|
from hyperglass.models.ui import UIParameters
|
||||||
|
from hyperglass.models.directive import Directives
|
||||||
from hyperglass.models.config.params import Params
|
from hyperglass.models.config.params import Params
|
||||||
from hyperglass.models.config.devices import Devices
|
from hyperglass.models.config.devices import Devices
|
||||||
|
|
||||||
@@ -58,6 +59,11 @@ def use_state(attr: t.Literal["cache", "redis"]) -> "RedisManager":
|
|||||||
"""Directly access hyperglass Redis cache manager."""
|
"""Directly access hyperglass Redis cache manager."""
|
||||||
|
|
||||||
|
|
||||||
|
@t.overload
|
||||||
|
def use_state(attr: t.Literal["directives"]) -> "Directives":
|
||||||
|
"""Access all hyperglass directives."""
|
||||||
|
|
||||||
|
|
||||||
@t.overload
|
@t.overload
|
||||||
def use_state(attr=None) -> "HyperglassState":
|
def use_state(attr=None) -> "HyperglassState":
|
||||||
"""Access entire global state.
|
"""Access entire global state.
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ from redis import Redis, ConnectionPool
|
|||||||
|
|
||||||
# Project
|
# Project
|
||||||
from hyperglass.util import repr_from_attrs
|
from hyperglass.util import repr_from_attrs
|
||||||
from hyperglass.configuration import params, devices, ui_params
|
|
||||||
|
|
||||||
# Local
|
# Local
|
||||||
from .redis import RedisManager
|
from .redis import RedisManager
|
||||||
@@ -36,11 +35,6 @@ class StateManager:
|
|||||||
redis = Redis(connection_pool=connection_pool)
|
redis = Redis(connection_pool=connection_pool)
|
||||||
self.redis = RedisManager(instance=redis, namespace=self._namespace)
|
self.redis = RedisManager(instance=redis, namespace=self._namespace)
|
||||||
|
|
||||||
# Add configuration objects.
|
|
||||||
self.redis.set("params", params)
|
|
||||||
self.redis.set("devices", devices)
|
|
||||||
self.redis.set("ui_params", ui_params)
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
"""Represent state manager by name and namespace."""
|
"""Represent state manager by name and namespace."""
|
||||||
return repr_from_attrs(self, ("redis", "namespace"))
|
return repr_from_attrs(self, ("redis", "namespace"))
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import codecs
|
|||||||
import pickle
|
import pickle
|
||||||
import typing as t
|
import typing as t
|
||||||
|
|
||||||
|
# Project
|
||||||
|
from hyperglass.configuration import params, devices, ui_params, directives
|
||||||
|
|
||||||
# Local
|
# Local
|
||||||
from .manager import StateManager
|
from .manager import StateManager
|
||||||
|
|
||||||
@@ -13,6 +16,7 @@ if t.TYPE_CHECKING:
|
|||||||
from hyperglass.models.ui import UIParameters
|
from hyperglass.models.ui import UIParameters
|
||||||
from hyperglass.models.system import HyperglassSystem
|
from hyperglass.models.system import HyperglassSystem
|
||||||
from hyperglass.plugins._base import HyperglassPlugin
|
from hyperglass.plugins._base import HyperglassPlugin
|
||||||
|
from hyperglass.models.directive import Directive, Directives
|
||||||
from hyperglass.models.config.params import Params
|
from hyperglass.models.config.params import Params
|
||||||
from hyperglass.models.config.devices import Devices
|
from hyperglass.models.config.devices import Devices
|
||||||
|
|
||||||
@@ -26,6 +30,13 @@ class HyperglassState(StateManager):
|
|||||||
def __init__(self, *, settings: "HyperglassSystem") -> None:
|
def __init__(self, *, settings: "HyperglassSystem") -> None:
|
||||||
"""Initialize state store and reset plugins."""
|
"""Initialize state store and reset plugins."""
|
||||||
super().__init__(settings=settings)
|
super().__init__(settings=settings)
|
||||||
|
|
||||||
|
# Add configuration objects.
|
||||||
|
self.redis.set("params", params)
|
||||||
|
self.redis.set("devices", devices)
|
||||||
|
self.redis.set("ui_params", ui_params)
|
||||||
|
self.redis.set("directives", directives)
|
||||||
|
|
||||||
# Ensure plugins are empty.
|
# Ensure plugins are empty.
|
||||||
self.reset_plugins("output")
|
self.reset_plugins("output")
|
||||||
self.reset_plugins("input")
|
self.reset_plugins("input")
|
||||||
@@ -57,6 +68,12 @@ class HyperglassState(StateManager):
|
|||||||
"""Remove all plugins of `_type`."""
|
"""Remove all plugins of `_type`."""
|
||||||
self.redis.set(("plugins", _type), [])
|
self.redis.set(("plugins", _type), [])
|
||||||
|
|
||||||
|
def add_directive(self, *directives: t.Union["Directive", t.Dict[str, t.Any]]) -> None:
|
||||||
|
"""Add a directive."""
|
||||||
|
current = self.directives
|
||||||
|
current.add(*directives, unique_by="id")
|
||||||
|
self.redis.set("directives", current)
|
||||||
|
|
||||||
def clear(self) -> None:
|
def clear(self) -> None:
|
||||||
"""Delete all cache keys."""
|
"""Delete all cache keys."""
|
||||||
self.redis.instance.flushdb(asynchronous=True)
|
self.redis.instance.flushdb(asynchronous=True)
|
||||||
@@ -76,6 +93,11 @@ class HyperglassState(StateManager):
|
|||||||
"""UI parameters, built from params."""
|
"""UI parameters, built from params."""
|
||||||
return self.redis.get("ui_params", raise_if_none=True)
|
return self.redis.get("ui_params", raise_if_none=True)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def directives(self) -> "Directives":
|
||||||
|
"""All directives."""
|
||||||
|
return self.redis.get("directives", raise_if_none=True)
|
||||||
|
|
||||||
def plugins(self, _type: str) -> t.List[PluginT]:
|
def plugins(self, _type: str) -> t.List[PluginT]:
|
||||||
"""Get plugins by type."""
|
"""Get plugins by type."""
|
||||||
current = self.redis.get(("plugins", _type), raise_if_none=False, value_if_none=[])
|
current = self.redis.get(("plugins", _type), raise_if_none=False, value_if_none=[])
|
||||||
|
|||||||
Reference in New Issue
Block a user