diff --git a/docs/docs/queries.mdx b/docs/docs/queries.mdx index 1660dbe..02ed12f 100644 --- a/docs/docs/queries.mdx +++ b/docs/docs/queries.mdx @@ -6,11 +6,12 @@ keywords: [hyperglass, queries] description: hyperglass query types --- -import Link from "@docusaurus/Link"; import Code from "../src/components/JSXCode"; import MiniNote from "../src/components/MiniNote"; import RP from "../src/components/RegexPattern"; +
+ Each query type may be disabled, enabled, or customized. The `display_name` parameter defines how the query type will be displayed in the UI's Query Type select element. ## `bgp_route` @@ -20,17 +21,55 @@ Each query type may be disabled, enabled, or customized. The `display_name` para | `enable` | Boolean | `true` | Enable or disable the BGP Route query type. | | `display_name` | String | `'BGP Route'` | Text displayed for the BGP Route query type in the UI. | +#### Example + + +```yaml +queries: + bgp_route: + display_name: BGP Route + enable: true +``` + ## `bgp_community` -| Parameter | Type | Default | Description | -| :------------- | :-----: | :---------------- | :------------------------------------------------------------------------------ | -| `enable` | Boolean | `true` | Enable or disable the BGP Community query type. | -| `display_name` | String | `'BGP Community'` | Text displayed for the BGP Community query type in the UI. | -| `pattern` | | | BGP Community Regular Expression Patterns | +| Parameter | Type | Default | Description | +| :------------- | :-----: | :---------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `enable` | Boolean | `true` | Enable or disable the BGP Community query type. | +| `display_name` | String | `'BGP Community'` | Text displayed for the BGP Community query type in the UI. | +| `mode` | String | `'input'` | `input` mode requires the user to type the community value in the target element. `select` mode allows the user to select a community from a preconfigured list of communities. | +| `communities` | | | [BGP Community Definitions](#community-definitions) for `select` mode. | +| `pattern` | | | [BGP Community Regular Expression Patterns](#community-patterns) for `input` mode. | + +### Community Definitions + +If using `select` mode, you may define a list of communities the users can choose from. Each community definition uses the following schema: + +| Parameter | Type | Description | Example | +| :------------- | :----: | :--------------------- | :----------------------- | +| `community` | String | Community value | `'64496:1001'` | +| `display_name` | String | Community display name | `'64496:1001'` | +| `description` | String | Community description | `'North America Routes'` | + +#### Example + +```yaml +queries: + bgp_community: + enable: true + mode: select + communities: + - community: 64496:1001 + display_name: 64496:1001 + description: North America Routes + - community: 64496:1002 + display_name: 64496:1002 + description: Europe Routes +``` ### Community Patterns -hyperglass allows you to override the default regular expression patterns used to validate UI and API queries. hyperglass supports [Decimal (well known)](https://tools.ietf.org/html/rfc1997) communities, [Extended AS](https://tools.ietf.org/html/rfc4360) communities, and [Large](https://tools.ietf.org/html/rfc8092) communities. +If using `input` mode, hyperglass allows you to override the default regular expression patterns used to validate UI and API queries. hyperglass supports [Decimal (well known)](https://tools.ietf.org/html/rfc1997) communities, [Extended AS](https://tools.ietf.org/html/rfc4360) communities, and [Large](https://tools.ietf.org/html/rfc8092) communities. | Parameter | Type | Default | Description | | :------------ | :----: | :--------------------------------- | :----------------------------------------------------------------------------------------------------------------------------- | @@ -38,13 +77,45 @@ hyperglass allows you to override the default regular expression patterns used t | `extended_as` | String | | Regular expression pattern for validating extended AS type BGP Community strings, e.g. `65000:1` | | `large` | String | | Regular expression pattern for validating [large community](http://largebgpcommunities.net/) strings, e.g. `65000:65001:65002` | +#### Example + + +```yaml +queries: + bgp_community: + enable: true + mode: input + pattern: + decimal: '^[0-9]{1,10}$' + extended_as: '^([0-9]{0,5})\:([0-9]{1,5})$' + large: '^([0-9]{1,10})\:([0-9]{1,10})\:[0-9]{1,10}$' +``` + +:::caution +Regular expression patterns must be enclosed in single quotes, e.g. `'^.*$'` +::: + ## `bgp_aspath` -| Parameter | Type | Default | Description | -| :------------- | :-----: | :-------------- | :------------------------------------------------------------------------------------- | -| `enable` | Boolean | `true` | Enable or disable the BGP AS Path query type. | -| `display_name` | String | `'BGP AS Path'` | Text displayed for the BGP AS Path query type in the UI. | -| `pattern` | | | BGP AS Path Settings & Regular Expression Patterns | +| Parameter | Type | Default | Description | +| :------------- | :-----: | :-------------- | :---------------------------------------------------------------------- | +| `enable` | Boolean | `true` | Enable or disable the BGP AS Path query type. | +| `display_name` | String | `'BGP AS Path'` | Text displayed for the BGP AS Path query type in the UI. | +| `pattern` | | | [BGP AS Path Settings & Regular Expression Patterns](#as-path-patterns) | + +#### Example + + +```yaml +queries: + bgp_aspath: + display_name: BGP AS Path + enable: true + pattern: + asdot: '^(\^|^\_)((\d+\.\d+)\_|(\d+\.\d+)\$|(\d+\.\d+)\(\_\.\+\_\))+$' + asplain: '^(\^|^\_)(\d+\_|\d+\$|\d+\(\_\.\+\_\))+$' + mode: asplain +``` ### AS Path Patterns @@ -63,6 +134,16 @@ AS Path regular expression patterns may also be customized, should you wish to m | `enable` | Boolean | `true` | Enable or disable the Ping query type. | | `display_name` | String | `'Ping'` | Text displayed for the Ping query type in the UI. | +#### Example + + +```yaml +queries: + ping: + display_name: Ping + enable: true +``` + ## `traceroute` | Parameter | Type | Default | Description | @@ -70,31 +151,12 @@ AS Path regular expression patterns may also be customized, should you wish to m | `enable` | Boolean | `true` | Enable or disable the Traceroute query type. | | `display_name` | String | `'Traceroute'` | Text displayed for the Traceroute query type in the UI. | -## Example +#### Example + ```yaml queries: - bgp_aspath: - display_name: BGP AS Path - enable: true - pattern: - asdot: '^(\^|^\_)((\d+\.\d+)\_|(\d+\.\d+)\$|(\d+\.\d+)\(\_\.\+\_\))+$' - asplain: '^(\^|^\_)(\d+\_|\d+\$|\d+\(\_\.\+\_\))+$' - mode: asplain - bgp_community: - display_name: BGP Community - enable: true - pattern: - decimal: "^[0-9]{1,10}$" - extended_as: '^([0-9]{0,5})\:([0-9]{1,5})$' - large: '^([0-9]{1,10})\:([0-9]{1,10})\:[0-9]{1,10}$' - bgp_route: - display_name: BGP Route - enable: true - ping: - display_name: Ping - enable: true - traceroute: - display_name: Traceroute - enable: true + traceroute: + display_name: Traceroute + enable: true ``` diff --git a/hyperglass/api/__init__.py b/hyperglass/api/__init__.py index 972a9bc..2eb723c 100644 --- a/hyperglass/api/__init__.py +++ b/hyperglass/api/__init__.py @@ -18,7 +18,14 @@ from hyperglass.log import log from hyperglass.util import cpu_count from hyperglass.constants import TRANSPORT_REST, __version__ from hyperglass.api.events import on_startup, on_shutdown -from hyperglass.api.routes import docs, query, queries, routers, import_certificate +from hyperglass.api.routes import ( + docs, + query, + queries, + routers, + import_certificate, + communities, +) from hyperglass.exceptions import HyperglassError from hyperglass.configuration import URL_DEV, STATIC_PATH, params, devices from hyperglass.api.error_handlers import ( @@ -31,6 +38,7 @@ from hyperglass.api.models.response import ( QueryError, QueryResponse, RoutersResponse, + CommunityResponse, SupportedQueryResponse, ) @@ -105,7 +113,7 @@ def _custom_openapi(): description=params.docs.description, routes=app.routes, ) - openapi_schema["info"]["x-logo"] = {"url": str(params.web.logo.light)} + openapi_schema["info"]["x-logo"] = {"url": "/" + str(params.web.logo.dark)} query_samples = [] queries_samples = [] @@ -176,6 +184,16 @@ app.add_api_route( description=params.docs.devices.description, tags=[params.docs.devices.title], ) + +app.add_api_route( + path="/api/communities", + endpoint=communities, + methods=["GET"], + response_model=List[CommunityResponse], + summary=params.docs.communities.summary, + tags=[params.docs.communities.title], +) + app.add_api_route( path="/api/queries", endpoint=queries, @@ -186,6 +204,7 @@ app.add_api_route( description=params.docs.queries.description, tags=[params.docs.queries.title], ) + app.add_api_route( path="/api/query/", endpoint=query, diff --git a/hyperglass/api/models/query.py b/hyperglass/api/models/query.py index 6db5bea..8ab2077 100644 --- a/hyperglass/api/models/query.py +++ b/hyperglass/api/models/query.py @@ -17,7 +17,8 @@ from hyperglass.api.models.types import SupportedQuery from hyperglass.api.models.validators import ( validate_ip, validate_aspath, - validate_community, + validate_community_input, + validate_community_select, ) @@ -215,7 +216,7 @@ class Query(BaseModel): # Use relevant function based on query_type. validator_map = { "bgp_aspath": validate_aspath, - "bgp_community": validate_community, + "bgp_community": validate_community_input, "bgp_route": validate_ip, "ping": validate_ip, "traceroute": validate_ip, @@ -227,6 +228,10 @@ class Query(BaseModel): "ping": (value, values["query_type"], values["query_vrf"]), "traceroute": (value, values["query_type"], values["query_vrf"]), } + + if params.queries.bgp_community.mode == "select": + validator_map["bgp_community"] = validate_community_select + validate_func = validator_map[query_type] validate_args = validator_args_map[query_type] diff --git a/hyperglass/api/models/response.py b/hyperglass/api/models/response.py index 2c2d860..8bfa9f2 100644 --- a/hyperglass/api/models/response.py +++ b/hyperglass/api/models/response.py @@ -189,6 +189,14 @@ class RoutersResponse(BaseModel): } +class CommunityResponse(BaseModel): + """Response model for /api/communities.""" + + community: StrictStr + display_name: StrictStr + description: StrictStr + + class SupportedQueryResponse(BaseModel): """Response model for /api/queries list items.""" diff --git a/hyperglass/api/models/validators.py b/hyperglass/api/models/validators.py index 1e3bed5..ae03297 100644 --- a/hyperglass/api/models/validators.py +++ b/hyperglass/api/models/validators.py @@ -150,7 +150,7 @@ def validate_ip(value, query_type, query_vrf): # noqa: C901 return valid_ip -def validate_community(value): +def validate_community_input(value): """Validate input communities against configured or default regex pattern.""" # RFC4360: Extended Communities (New Format) @@ -174,6 +174,19 @@ def validate_community(value): return value +def validate_community_select(value): + """Validate selected community against configured communities.""" + + communities = tuple(c.community for c in params.queries.bgp_community.communities) + if value not in communities: + raise InputInvalid( + params.messages.invalid_input, + target=value, + query_type=params.queries.bgp_community.display_name, + ) + return value + + def validate_aspath(value): """Validate input AS_PATH against configured or default regext pattern.""" diff --git a/hyperglass/api/routes.py b/hyperglass/api/routes.py index 8960983..9ea46c9 100644 --- a/hyperglass/api/routes.py +++ b/hyperglass/api/routes.py @@ -194,6 +194,14 @@ async def routers(): ] +async def communities(): + """Serve list of configured communities if mode is select.""" + if params.queries.bgp_community.mode != "select": + raise HTTPException(detail="BGP community mode is not select", status_code=404) + + return [c.export_dict() for c in params.queries.bgp_community.communities] + + async def queries(): """Serve list of enabled query types.""" return params.queries.list diff --git a/hyperglass/configuration/models/docs.py b/hyperglass/configuration/models/docs.py index 45bedd2..22cdcbc 100644 --- a/hyperglass/configuration/models/docs.py +++ b/hyperglass/configuration/models/docs.py @@ -77,6 +77,11 @@ class Docs(HyperglassModel): description="List of supported query types.", summary="Query Types", ) + communities: EndpointConfig = EndpointConfig( + title="BGP Communities", + description="List of BGP communities.", + summary="BGP Communities List", + ) class Config: """Pydantic model configuration.""" @@ -96,4 +101,8 @@ class Docs(HyperglassModel): "title": "Queries API Endpoint", "description": "`/api/devices` API documentation options.", }, + "communities": { + "title": "BGP Communities API Endpoint", + "description": "`/api/communities` API documentation options.", + }, } diff --git a/hyperglass/examples/devices.yaml b/hyperglass/examples/devices.yaml index faf548f..770e14f 100644 --- a/hyperglass/examples/devices.yaml +++ b/hyperglass/examples/devices.yaml @@ -1,98 +1,98 @@ --- # Credentials credentials: - - credential: &credential1 - username: user1 - password: secret1 - - credential: &credential2 - username: user2 - password: secret2 + - credential: &credential1 + username: user1 + password: secret1 + - credential: &credential2 + username: user2 + password: secret2 # SSH Proxy/Tunnel Servers proxies: - - proxy: &proxy1 - name: server01 - address: 10.11.6.204 - port: 22 - credential: *credential1 - nos: linux_ssh + - proxy: &proxy1 + name: server01 + address: 10.11.6.204 + port: 22 + credential: *credential1 + nos: linux_ssh # Networks networks: - - network: &net_primary - name: primary - display_name: Main Network - - network: &net_secondary - name: secondary - display_name: That Other Network + - network: &net_primary + name: primary + display_name: Main Network + - network: &net_secondary + name: secondary + display_name: That Other Network # VRFs vrfs: - - &vrf_default - name: default - display_name: Global - ipv4: - access_list: &vrf_default_ipv4_acl - - network: 10.0.0.0/8 - action: deny - - network: 192.168.0.0/16 - action: deny - - network: 172.16.0.0/12 - action: deny - - network: 0.0.0.0/0 - action: permit - ge: 8 - le: 24 - ipv6: - access_list: &vrf_default_ipv6_acl - - network: ::/0 - action: permit - ge: 32 - le: 64 - - &vrf_customer_a - name: customer_a - display_name: Customer A - ipv4: - access_list: &vrf_customer_a_ipv4_acl - - network: 192.0.2.0/24 - action: deny - - network: 10.0.0.0/8 - action: permit - ipv6: null + - &vrf_default + name: default + display_name: Global + ipv4: + access_list: &vrf_default_ipv4_acl + - network: 10.0.0.0/8 + action: deny + - network: 192.168.0.0/16 + action: deny + - network: 172.16.0.0/12 + action: deny + - network: 0.0.0.0/0 + action: permit + ge: 8 + le: 24 + ipv6: + access_list: &vrf_default_ipv6_acl + - network: ::/0 + action: permit + ge: 32 + le: 64 + - &vrf_customer_a + name: customer_a + display_name: Customer A + ipv4: + access_list: &vrf_customer_a_ipv4_acl + - network: 192.0.2.0/24 + action: deny + - network: 10.0.0.0/8 + action: permit + ipv6: null # Routers routers: - - name: sfo_router01 - address: 10.0.0.1 - network: *net_primary - credential: *credential1 - display_name: San Francisco, CA - port: 22 - nos: cisco_ios - vrfs: - - <<: *vrf_default - ipv4: - source_address: 192.0.2.1 - access_list: *vrf_default_ipv4_acl - ipv6: - source_address: 2001:db8::1 - access_list: *vrf_default_ipv6_acl - - <<: *vrf_customer_a - ipv4: - access_list: *vrf_customer_a_ipv4_acl - source_address: 192.168.1.1 - proxy: null - - name: atl_router01 - address: 10.0.0.2 - network: *net_secondary - credential: *credential2 - display_name: Atlanta, GA - port: 22 - nos: juniper - vrfs: - - <<: *vrf_default - ipv4: - source_address: 192.0.2.2 - ipv6: - source_address: 2001:db8::2 - proxy: *proxy1 + - name: sfo_router01 + address: 10.0.0.1 + network: *net_primary + credential: *credential1 + display_name: San Francisco, CA + port: 22 + nos: cisco_ios + vrfs: + - <<: *vrf_default + ipv4: + source_address: 192.0.2.1 + access_list: *vrf_default_ipv4_acl + ipv6: + source_address: 2001:db8::1 + access_list: *vrf_default_ipv6_acl + - <<: *vrf_customer_a + ipv4: + access_list: *vrf_customer_a_ipv4_acl + source_address: 192.168.1.1 + proxy: null + - name: atl_router01 + address: 10.0.0.2 + network: *net_secondary + credential: *credential2 + display_name: Atlanta, GA + port: 22 + nos: juniper + vrfs: + - <<: *vrf_default + ipv4: + source_address: 192.0.2.2 + ipv6: + source_address: 2001:db8::2 + proxy: *proxy1