1
0
Fork 1
mirror of https://github.com/thatmattlove/hyperglass.git synced 2026-04-17 21:38:27 +00:00

Merge branch 'thatmattlove:main' into main

This commit is contained in:
WilhelmZA 2025-09-30 16:55:15 +02:00 committed by GitHub
commit cc558e62e8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 29 additions and 16 deletions

View file

@ -7,10 +7,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
## [Unreleased] ## [Unreleased]
### Fixed ### Fixed
- [#280](https://github.com/thatmattlove/hyperglass/issues/280): Fix: `condition: None` caused error in directive - [#280](https://github.com/thatmattlove/hyperglass/issues/280): Fix: `condition: None` caused error in directive @Jimmy01240397
- [#306](https://github.com/thatmattlove/hyperglass/issues/306): Fix: allow integer values in ext_community_list_raw field for Arista BGP - @cooperwinser - [#306](https://github.com/thatmattlove/hyperglass/issues/306): Fix: allow integer values in ext_community_list_raw field for Arista BGP - @cooperwinser
- [#311](https://github.com/thatmattlove/hyperglass/issues/311): Fix: device and directive errors.
- [#315](https://github.com/thatmattlove/hyperglass/issues/315): Impossibile to use command "BGP Route" with Huawei NetEngine 8000
- [#315](https://github.com/thatmattlove/hyperglass/issues/187): Error in bgp_router query on Huawei
- [#325](https://github.com/thatmattlove/hyperglass/pull/325): Fix code block padding in the documentation - @jagardaniel - [#325](https://github.com/thatmattlove/hyperglass/pull/325): Fix code block padding in the documentation - @jagardaniel
- [#327](https://github.com/thatmattlove/hyperglass/pull/327): Fix huawei bgp route and plugin validation/transform order - @JelsonRodrigues - [#332](https://github.com/thatmattlove/hyperglass/pull/332): Fix custom proxy port support in SSH proxy tunnels @jessiebryan
### Updated ### Updated

View file

@ -166,6 +166,7 @@ your-directive:
- condition: null - condition: null
command: show ip route {target} command: show ip route {target}
field: field:
description: IP of target
validation: '[0-9a-f\.\:]+' validation: '[0-9a-f\.\:]+'
``` ```
@ -178,6 +179,7 @@ your-directive:
- condition: null - condition: null
command: show ip bgp community {target} command: show ip bgp community {target}
field: field:
description: BGP community to show
options: options:
- value: "65001:1" - value: "65001:1"
description: Provider A Routes description: Provider A Routes

View file

@ -48,7 +48,9 @@ class SSHConnection(Connection):
proxy.credential.password.get_secret_value() proxy.credential.password.get_secret_value()
) )
try: try:
return open_tunnel(proxy._target, proxy.port, **tunnel_kwargs) return open_tunnel(
ssh_address_or_host=proxy._target, ssh_port=proxy.port, **tunnel_kwargs
)
except BaseSSHTunnelForwarderError as scrape_proxy_error: except BaseSSHTunnelForwarderError as scrape_proxy_error:
log.bind(device=self.device.name, proxy=proxy.name).error( log.bind(device=self.device.name, proxy=proxy.name).error(

View file

@ -7,7 +7,8 @@ import secrets
from datetime import datetime from datetime import datetime
# Third Party # Third Party
from pydantic import Field, BaseModel, ConfigDict, field_validator from pydantic import BaseModel, ConfigDict, field_validator, StringConstraints
from typing_extensions import Annotated
# Project # Project
from hyperglass.log import log from hyperglass.log import log
@ -21,6 +22,11 @@ from hyperglass.exceptions.private import InputValidationError
from ..config.devices import Device from ..config.devices import Device
QueryLocation = Annotated[str, StringConstraints(strict=True, min_length=1, strip_whitespace=True)]
QueryTarget = Annotated[str, StringConstraints(min_length=1, strip_whitespace=True)]
QueryType = Annotated[str, StringConstraints(strict=True, min_length=1, strip_whitespace=True)]
class SimpleQuery(BaseModel): class SimpleQuery(BaseModel):
"""A simple representation of a post-validated query.""" """A simple representation of a post-validated query."""
@ -39,12 +45,12 @@ class Query(BaseModel):
model_config = ConfigDict(extra="allow", alias_generator=snake_to_camel, populate_by_name=True) model_config = ConfigDict(extra="allow", alias_generator=snake_to_camel, populate_by_name=True)
# Device `name` field # Device `name` field
query_location: str = Field(strict=True, min_length=1, strip_whitespace=True) query_location: QueryLocation
query_target: t.Union[t.List[str], str] = Field(min_length=1, strip_whitespace=True) query_target: t.Union[t.List[QueryTarget], QueryTarget]
# Directive `id` field # Directive `id` field
query_type: str = Field(strict=True, min_length=1, strip_whitespace=True) query_type: QueryType
_kwargs: t.Dict[str, t.Any] _kwargs: t.Dict[str, t.Any]
def __init__(self, **data) -> None: def __init__(self, **data) -> None:

View file

@ -170,7 +170,7 @@ class Device(HyperglassModelWithId, extra="allow"):
@field_validator("address") @field_validator("address")
def validate_address( def validate_address(
cls, value: t.Union[IPv4Address, IPv6Address, str], values: t.Dict[str, t.Any] cls, value: t.Union[IPv4Address, IPv6Address, str], info: ValidationInfo
) -> t.Union[IPv4Address, IPv6Address, str]: ) -> t.Union[IPv4Address, IPv6Address, str]:
"""Ensure a hostname is resolvable.""" """Ensure a hostname is resolvable."""
@ -178,14 +178,14 @@ class Device(HyperglassModelWithId, extra="allow"):
if not any(resolve_hostname(value)): if not any(resolve_hostname(value)):
raise ConfigError( raise ConfigError(
"Device '{d}' has an address of '{a}', which is not resolvable.", "Device '{d}' has an address of '{a}', which is not resolvable.",
d=values["name"], d=info.data["name"],
a=value, a=value,
) )
return value return value
@field_validator("avatar") @field_validator("avatar")
def validate_avatar( def validate_avatar(
cls, value: t.Union[FilePath, None], values: t.Dict[str, t.Any] cls, value: t.Union[FilePath, None], info: ValidationInfo
) -> t.Union[FilePath, None]: ) -> t.Union[FilePath, None]:
"""Migrate avatar to static directory.""" """Migrate avatar to static directory."""
if value is not None: if value is not None:
@ -198,7 +198,7 @@ class Device(HyperglassModelWithId, extra="allow"):
target = Settings.static_path / "images" / value.name target = Settings.static_path / "images" / value.name
copied = shutil.copy2(value, target) copied = shutil.copy2(value, target)
log.bind( log.bind(
device=values["name"], device=info.data["name"],
source=str(value), source=str(value),
destination=str(target), destination=str(target),
).debug("Copied device avatar") ).debug("Copied device avatar")
@ -210,24 +210,24 @@ class Device(HyperglassModelWithId, extra="allow"):
return value return value
@field_validator("platform", mode="before") @field_validator("platform", mode="before")
def validate_platform(cls: "Device", value: t.Any, values: t.Dict[str, t.Any]) -> str: def validate_platform(cls: "Device", value: t.Any, info: ValidationInfo) -> str:
"""Validate & rewrite device platform, set default `directives`.""" """Validate & rewrite device platform, set default `directives`."""
if value == "http": if value == "http":
if values.get("http") is None: if info.data.get("http") is None:
raise ConfigError( raise ConfigError(
"Device '{device}' has platform 'http' configured, but no http parameters are defined.", "Device '{device}' has platform 'http' configured, but no http parameters are defined.",
device=values["name"], device=info.data["name"],
) )
if value is None: if value is None:
if values.get("http") is not None: if info.data.get("http") is not None:
value = "http" value = "http"
else: else:
# Ensure device platform is defined. # Ensure device platform is defined.
raise ConfigError( raise ConfigError(
"Device '{device}' is missing a 'platform' (Network Operating System) property", "Device '{device}' is missing a 'platform' (Network Operating System) property",
device=values["name"], device=info.data["name"],
) )
if value in SCRAPE_HELPERS.keys(): if value in SCRAPE_HELPERS.keys():