diff --git a/hyperglass/api/models/response.py b/hyperglass/api/models/response.py index 8bfa9f2..c2a96de 100644 --- a/hyperglass/api/models/response.py +++ b/hyperglass/api/models/response.py @@ -62,6 +62,7 @@ class QueryResponse(BaseModel): runtime: StrictInt keywords: List[StrictStr] = [] timestamp: StrictStr + _format: constr(regex=r"(application\/json|text\/plain)") = "text/plain" class Config: """Pydantic model configuration.""" @@ -89,6 +90,12 @@ class QueryResponse(BaseModel): "description": "UTC Time at which the backend application received the query.", "example": "2020-04-18 14:45:37", }, + "format": { + "alias": "format", + "title": "Format", + "description": "Response [MIME Type](http://www.iana.org/assignments/media-types/media-types.xhtml). Supported values: `text/plain` and `application/json`.", + "example": "text/plain", + }, "keywords": { "title": "Keywords", "description": "Relevant keyword values contained in the `output` field, which can be used for formatting.", diff --git a/hyperglass/configuration/models/commands.py b/hyperglass/configuration/models/commands.py deleted file mode 100644 index 6ce7c7c..0000000 --- a/hyperglass/configuration/models/commands.py +++ /dev/null @@ -1,373 +0,0 @@ -"""Validate command configuration variables.""" - -# Third Party -from pydantic import StrictStr - -# Project -from hyperglass.models import HyperglassModel, HyperglassModelExtra - - -class Command(HyperglassModel): - """Validation model for non-default commands.""" - - class IPv4(HyperglassModel): - """Validation model for non-default dual afi commands.""" - - bgp_route: StrictStr - bgp_aspath: StrictStr - bgp_community: StrictStr - ping: StrictStr - traceroute: StrictStr - - class IPv6(HyperglassModel): - """Validation model for non-default ipv4 commands.""" - - bgp_route: StrictStr - bgp_aspath: StrictStr - bgp_community: StrictStr - ping: StrictStr - traceroute: StrictStr - - class VPNIPv4(HyperglassModel): - """Validation model for non-default ipv6 commands.""" - - bgp_route: StrictStr - bgp_aspath: StrictStr - bgp_community: StrictStr - ping: StrictStr - traceroute: StrictStr - - class VPNIPv6(HyperglassModel): - """Validation model for non-default ipv6 commands.""" - - bgp_route: StrictStr - bgp_aspath: StrictStr - bgp_community: StrictStr - ping: StrictStr - traceroute: StrictStr - - ipv4_default: IPv4 - ipv6_default: IPv6 - ipv4_vpn: VPNIPv4 - ipv6_vpn: VPNIPv6 - - -class CiscoIOS(Command): - """Validation model for default cisco_ios commands.""" - - class VPNIPv4(Command.VPNIPv4): - """Default commands for dual afi commands.""" - - bgp_community: StrictStr = "show bgp vpnv4 unicast vrf {vrf} community {target}" - bgp_aspath: StrictStr = 'show bgp vpnv4 unicast vrf {vrf} quote-regexp "{target}"' - bgp_route: StrictStr = "show bgp vpnv4 unicast vrf {vrf} {target}" - ping: StrictStr = "ping vrf {vrf} {target} repeat 5 source {source}" - traceroute: StrictStr = ( - "traceroute vrf {vrf} {target} timeout 1 probe 2 source {source}" - ) - - class VPNIPv6(Command.VPNIPv6): - """Default commands for dual afi commands.""" - - bgp_community: StrictStr = "show bgp vpnv6 unicast vrf {vrf} community {target}" - bgp_aspath: StrictStr = 'show bgp vpnv6 unicast vrf {vrf} quote-regexp "{target}"' - bgp_route: StrictStr = "show bgp vpnv6 unicast vrf {vrf} {target}" - ping: StrictStr = "ping vrf {vrf} {target} repeat 5 source {source}" - traceroute: StrictStr = ( - "traceroute vrf {vrf} {target} timeout 1 probe 2 source {source}" - ) - - class IPv4(Command.IPv4): - """Default commands for ipv4 commands.""" - - bgp_community: StrictStr = "show bgp ipv4 unicast community {target}" - bgp_aspath: StrictStr = 'show bgp ipv4 unicast quote-regexp "{target}"' - bgp_route: StrictStr = "show bgp ipv4 unicast {target} | exclude pathid:|Epoch" - ping: StrictStr = "ping {target} repeat 5 source {source}" - traceroute: StrictStr = "traceroute {target} timeout 1 probe 2 source {source}" - - class IPv6(Command.IPv6): - """Default commands for ipv6 commands.""" - - bgp_community: StrictStr = "show bgp ipv6 unicast community {target}" - bgp_aspath: StrictStr = 'show bgp ipv6 unicast quote-regexp "{target}"' - bgp_route: StrictStr = "show bgp ipv6 unicast {target} | exclude pathid:|Epoch" - ping: StrictStr = ("ping ipv6 {target} repeat 5 source {source}") - traceroute: StrictStr = ( - "traceroute ipv6 {target} timeout 1 probe 2 source {source}" - ) - - ipv4_default: IPv4 = IPv4() - ipv6_default: IPv6 = IPv6() - ipv4_vpn: VPNIPv4 = VPNIPv4() - ipv6_vpn: VPNIPv6 = VPNIPv6() - - -class CiscoXR(Command): - """Validation model for default cisco_xr commands.""" - - class IPv4(Command.IPv4): - """Validation model for non-default dual afi commands.""" - - bgp_route: StrictStr = "show bgp ipv4 unicast {target}" - bgp_aspath: StrictStr = "show bgp ipv4 unicast regexp {target}" - bgp_community: StrictStr = "show bgp ipv4 unicast community {target}" - ping: StrictStr = "ping ipv4 {target} count 5 source {source}" - traceroute: StrictStr = "traceroute ipv4 {target} timeout 1 probe 2 source {source}" - - class IPv6(Command.IPv6): - """Validation model for non-default ipv4 commands.""" - - bgp_route: StrictStr = "show bgp ipv6 unicast {target}" - bgp_aspath: StrictStr = "show bgp ipv6 unicast regexp {target}" - bgp_community: StrictStr = "show bgp ipv6 unicast community {target}" - ping: StrictStr = "ping ipv6 {target} count 5 source {source}" - traceroute: StrictStr = "traceroute ipv6 {target} timeout 1 probe 2 source {source}" - - class VPNIPv4(Command.VPNIPv4): - """Validation model for non-default ipv6 commands.""" - - bgp_route: StrictStr = "show bgp vpnv4 unicast vrf {vrf} {target}" - bgp_aspath: StrictStr = "show bgp vpnv4 unicast vrf {vrf} regexp {target}" - bgp_community: StrictStr = "show bgp vpnv4 unicast vrf {vrf} community {target}" - ping: StrictStr = "ping vrf {vrf} {target} count 5 source {source}" - traceroute: StrictStr = "traceroute vrf {vrf} {target} timeout 1 probe 2 source {source}" - - class VPNIPv6(Command.VPNIPv6): - """Validation model for non-default ipv6 commands.""" - - bgp_route: StrictStr = "show bgp vpnv6 unicast vrf {vrf} {target}" - bgp_aspath: StrictStr = "show bgp vpnv6 unicast vrf {vrf} regexp {target}" - bgp_community: StrictStr = "show bgp vpnv6 unicast vrf {vrf} community {target}" - ping: StrictStr = "ping vrf {vrf} {target} count 5 source {source}" - traceroute: StrictStr = "traceroute vrf {vrf} {target} timeout 1 probe 2 source {source}" - - ipv4_default: IPv4 = IPv4() - ipv6_default: IPv6 = IPv6() - ipv4_vpn: VPNIPv4 = VPNIPv4() - ipv6_vpn: VPNIPv6 = VPNIPv6() - - -class Juniper(Command): - """Validation model for default juniper commands.""" - - class IPv4(Command.IPv4): - """Validation model for non-default dual afi commands.""" - - bgp_route: StrictStr = 'show route protocol bgp table inet.0 {target} detail | except Label | except Label | except "Next hop type" | except Task | except Address | except "Session Id" | except State | except "Next-hop reference" | except destinations | except "Announcement bits"' - bgp_aspath: StrictStr = 'show route protocol bgp table inet.0 aspath-regex "{target}"' - bgp_community: StrictStr = "show route protocol bgp table inet.0 community {target}" - ping: StrictStr = "ping inet {target} count 5 source {source}" - traceroute: StrictStr = "traceroute inet {target} wait 1 source {source}" - - class IPv6(Command.IPv6): - """Validation model for non-default ipv4 commands.""" - - bgp_route: StrictStr = 'show route protocol bgp table inet6.0 {target} detail | except Label | except Label | except "Next hop type" | except Task | except Address | except "Session Id" | except State | except "Next-hop reference" | except destinations | except "Announcement bits"' - bgp_aspath: StrictStr = 'show route protocol bgp table inet6.0 aspath-regex "{target}"' - bgp_community: StrictStr = "show route protocol bgp table inet6.0 community {target}" - ping: StrictStr = "ping inet6 {target} count 5 source {source}" - traceroute: StrictStr = "traceroute inet6 {target} wait 2 source {source}" - - class VPNIPv4(Command.VPNIPv4): - """Validation model for non-default ipv6 commands.""" - - bgp_route: StrictStr = 'show route protocol bgp table {vrf}.inet.0 {target} detail | except Label | except Label | except "Next hop type" | except Task | except Address | except "Session Id" | except State | except "Next-hop reference" | except destinations | except "Announcement bits"' - bgp_aspath: StrictStr = 'show route protocol bgp table {vrf}.inet.0 aspath-regex "{target}"' - bgp_community: StrictStr = "show route protocol bgp table {vrf}.inet.0 community {target}" - ping: StrictStr = "ping inet routing-instance {vrf} {target} count 5 source {source}" - traceroute: StrictStr = "traceroute inet routing-instance {vrf} {target} wait 1 source {source}" - - class VPNIPv6(Command.VPNIPv6): - """Validation model for non-default ipv6 commands.""" - - bgp_route: StrictStr = 'show route protocol bgp table {vrf}.inet6.0 {target} detail | except Label | except Label | except "Next hop type" | except Task | except Address | except "Session Id" | except State | except "Next-hop reference" | except destinations | except "Announcement bits"' - bgp_aspath: StrictStr = 'show route protocol bgp table {vrf}.inet6.0 aspath-regex "{target}"' - bgp_community: StrictStr = "show route protocol bgp table {vrf}.inet6.0 community {target}" - ping: StrictStr = "ping inet6 routing-instance {vrf} {target} count 5 source {source}" - traceroute: StrictStr = "traceroute inet6 routing-instance {vrf} {target} wait 2 source {source}" - - ipv4_default: IPv4 = IPv4() - ipv6_default: IPv6 = IPv6() - ipv4_vpn: VPNIPv4 = VPNIPv4() - ipv6_vpn: VPNIPv6 = VPNIPv6() - - -class Huawei(Command): - """Validation model for default huawei commands.""" - - class IPv4(Command.IPv4): - """Default commands for ipv4 commands.""" - - bgp_community: StrictStr = "display bgp routing-table regular-expression {target}" - bgp_aspath: StrictStr = "display bgp routing-table regular-expression {target}" - bgp_route: StrictStr = "display bgp routing-table {target}" - ping: StrictStr = "ping -c 5 -a {source} {target}" - traceroute: StrictStr = "tracert -q 2 -f 1 -a {source} {target}" - - class IPv6(Command.IPv6): - """Default commands for ipv6 commands.""" - - bgp_community: StrictStr = "display bgp ipv6 routing-table community {target}" - bgp_aspath: StrictStr = "display bgp ipv6 routing-table regular-expression {target}" - bgp_route: StrictStr = "display bgp ipv6 routing-table {target}" - ping: StrictStr = "ping ipv6 -c 5 -a {source} {target}" - traceroute: StrictStr = "tracert ipv6 -q 2 -f 1 -a {source} {target}" - - class VPNIPv4(Command.VPNIPv4): - """Default commands for dual afi commands.""" - - bgp_community: StrictStr = "display bgp vpnv4 vpn-instance {vrf} routing-table regular-expression {target}" - bgp_aspath: StrictStr = "display bgp vpnv4 vpn-instance {vrf} routing-table regular-expression {target}" - bgp_route: StrictStr = "display bgp vpnv4 vpn-instance {vrf} routing-table {target}" - ping: StrictStr = "ping -vpn-instance {vrf} -c 5 -a {source} {target}" - traceroute: StrictStr = "tracert -q 2 -f 1 -vpn-instance {vrf} -a {source} {target}" - - class VPNIPv6(Command.VPNIPv6): - """Default commands for dual afi commands.""" - - bgp_community: StrictStr = "display bgp vpnv6 vpn-instance {vrf} routing-table regular-expression {target}" - bgp_aspath: StrictStr = "display bgp vpnv6 vpn-instance {vrf} routing-table regular-expression {target}" - bgp_route: StrictStr = "display bgp vpnv6 vpn-instance {vrf} routing-table {target}" - ping: StrictStr = "ping vpnv6 vpn-instance {vrf} -c 5 -a {source} {target}" - traceroute: StrictStr = "tracert -q 2 -f 1 vpn-instance {vrf} -a {source} {target}" - - ipv4_default: IPv4 = IPv4() - ipv6_default: IPv6 = IPv6() - ipv4_vpn: VPNIPv4 = VPNIPv4() - ipv6_vpn: VPNIPv6 = VPNIPv6() - - -class Arista(Command): - """Validation model for non-default commands.""" - - class IPv4(Command.IPv4): - """Validation model for non-default dual afi commands.""" - - bgp_route: StrictStr = "show ip bgp {target}" - bgp_aspath: StrictStr = "show ip bgp regexp {target}" - bgp_community: StrictStr = "show ip bgp community {target}" - ping: StrictStr = "ping ip {target} source {source}" - traceroute: StrictStr = "traceroute ip {target} source {source}" - - class IPv6(Command.IPv6): - """Validation model for non-default ipv4 commands.""" - - bgp_route: StrictStr = "show ipv6 bgp {target}" - bgp_aspath: StrictStr = "show ipv6 bgp regexp {target}" - bgp_community: StrictStr = "show ipv6 bgp community {target}" - ping: StrictStr = "ping ipv6 {target} source {source}" - traceroute: StrictStr = "traceroute ipv6 {target} source {source}" - - class VPNIPv4(Command.VPNIPv4): - """Validation model for non-default ipv6 commands.""" - - bgp_route: StrictStr = "show ip bgp {target} vrf {vrf}" - bgp_aspath: StrictStr = "show ip bgp regexp {target} vrf {vrf}" - bgp_community: StrictStr = "show ip bgp community {target} vrf {vrf}" - ping: StrictStr = "ping vrf {vrf} ip {target} source {source}" - traceroute: StrictStr = "traceroute vrf {vrf} ip {target} source {source}" - - class VPNIPv6(Command.VPNIPv6): - """Validation model for non-default ipv6 commands.""" - - bgp_route: StrictStr = "show ipv6 bgp {target} vrf {vrf}" - bgp_aspath: StrictStr = "show ipv6 bgp regexp {target} vrf {vrf}" - bgp_community: StrictStr = "show ipv6 bgp community {target} vrf {vrf}" - ping: StrictStr = "ping vrf {vrf} ipv6 {target} source {source}" - traceroute: StrictStr = "traceroute vrf {vrf} ipv6 {target} source {source}" - - ipv4_default: IPv4 = IPv4() - ipv6_default: IPv6 = IPv6() - ipv4_vpn: VPNIPv4 = VPNIPv4() - ipv6_vpn: VPNIPv6 = VPNIPv6() - - -class CiscoNXOS(Command): - """Validation model for non-default commands.""" - - class IPv4(Command.IPv4): - """Validation model for non-default dual afi commands.""" - - bgp_route: StrictStr = "show bgp ipv4 unicast {target}" - bgp_aspath: StrictStr = 'show bgp ipv4 unicast regexp "{target}"' - bgp_community: StrictStr = "show bgp ipv4 unicast community {target}" - ping: StrictStr = "ping {target} source {source}" - traceroute: StrictStr = "traceroute {target} source {source}" - - class IPv6(Command.IPv6): - """Validation model for non-default ipv4 commands.""" - - bgp_route: StrictStr = "show bgp ipv6 unicast {target}" - bgp_aspath: StrictStr = 'show bgp ipv6 unicast regexp "{target}"' - bgp_community: StrictStr = "show bgp ipv6 unicast community {target}" - ping: StrictStr = "ping6 {target} source {source}" - traceroute: StrictStr = "traceroute6 {target} source {source}" - - class VPNIPv4(Command.VPNIPv4): - """Validation model for non-default ipv6 commands.""" - - bgp_route: StrictStr = "show bgp ipv4 unicast {target} vrf {vrf}" - bgp_aspath: StrictStr = 'show bgp ipv4 unicast regexp "{target}" vrf {vrf}' - bgp_community: StrictStr = "show bgp ipv4 unicast community {target} vrf {vrf}" - ping: StrictStr = "ping {target} source {source} vrf {vrf}" - traceroute: StrictStr = "traceroute {target} source {source} vrf {vrf}" - - class VPNIPv6(Command.VPNIPv6): - """Validation model for non-default ipv6 commands.""" - - bgp_route: StrictStr = "show bgp ipv6 unicast {target} vrf {vrf}" - bgp_aspath: StrictStr = 'show bgp ipv6 unicast regexp "{target}" vrf {vrf}' - bgp_community: StrictStr = "show bgp ipv6 unicast community {target} vrf {vrf}" - ping: StrictStr = "ping6 {target} source {source} vrf {vrf}" - traceroute: StrictStr = "traceroute6 {target} source {source} vrf {vrf}" - - ipv4_default: IPv4 = IPv4() - ipv6_default: IPv6 = IPv6() - ipv4_vpn: VPNIPv4 = VPNIPv4() - ipv6_vpn: VPNIPv6 = VPNIPv6() - - -_NOS_MAP = { - "arista": Arista, - "huawei": Huawei, - "juniper": Juniper, - "cisco_ios": CiscoIOS, - "cisco_nxos": CiscoNXOS, - "cisco_xr": CiscoXR, -} - - -class Commands(HyperglassModelExtra): - """Base class for command definitions.""" - - arista: Command = Arista() - cisco_ios: Command = CiscoIOS() - cisco_nxos: Command = CiscoNXOS() - cisco_xr: Command = CiscoXR() - huawei: Command = Huawei() - juniper: Command = Juniper() - - @classmethod - def import_params(cls, input_params): - """Import loaded YAML, initialize per-command definitions. - - Dynamically set attributes for the command class. - - Arguments: - input_params {dict} -- Unvalidated command definitions - - Returns: - {object} -- Validated commands object - """ - obj = Commands() - for nos, cmds in input_params.items(): - nos_cmd_set = _NOS_MAP.get(nos, Command) - nos_cmds = nos_cmd_set(**cmds) - setattr(obj, nos, nos_cmds) - return obj - - class Config: - """Override pydantic config.""" - - validate_all = False diff --git a/hyperglass/configuration/models/commands/__init__.py b/hyperglass/configuration/models/commands/__init__.py new file mode 100644 index 0000000..69db541 --- /dev/null +++ b/hyperglass/configuration/models/commands/__init__.py @@ -0,0 +1,55 @@ +"""Validate command configuration variables.""" + +# Project +from hyperglass.models import HyperglassModelExtra +from hyperglass.configuration.models.commands.arista import AristaCommands +from hyperglass.configuration.models.commands.common import CommandGroup +from hyperglass.configuration.models.commands.huawei import HuaweiCommands +from hyperglass.configuration.models.commands.juniper import JuniperCommands +from hyperglass.configuration.models.commands.cisco_xr import CiscoXRCommands +from hyperglass.configuration.models.commands.cisco_ios import CiscoIOSCommands +from hyperglass.configuration.models.commands.cisco_nxos import CiscoNXOSCommands + +_NOS_MAP = { + "juniper": JuniperCommands, + "cisco_ios": CiscoIOSCommands, + "cisco_xr": CiscoXRCommands, + "cisco_nxos": CiscoNXOSCommands, + "arista": AristaCommands, + "huawei": HuaweiCommands, +} + + +class Commands(HyperglassModelExtra): + """Base class for command definitions.""" + + juniper: CommandGroup = JuniperCommands() + arista: CommandGroup = AristaCommands() + cisco_ios: CommandGroup = CiscoIOSCommands() + cisco_xr: CommandGroup = CiscoXRCommands() + cisco_nxos: CommandGroup = CiscoNXOSCommands() + huawei: CommandGroup = HuaweiCommands() + + @classmethod + def import_params(cls, input_params): + """Import loaded YAML, initialize per-command definitions. + + Dynamically set attributes for the command class. + + Arguments: + input_params {dict} -- Unvalidated command definitions + + Returns: + {object} -- Validated commands object + """ + obj = Commands() + for nos, cmds in input_params.items(): + nos_cmd_set = _NOS_MAP.get(nos, CommandGroup) + nos_cmds = nos_cmd_set(**cmds) + setattr(obj, nos, nos_cmds) + return obj + + class Config: + """Override pydantic config.""" + + validate_all = False diff --git a/hyperglass/configuration/models/commands/arista.py b/hyperglass/configuration/models/commands/arista.py new file mode 100644 index 0000000..7b447c7 --- /dev/null +++ b/hyperglass/configuration/models/commands/arista.py @@ -0,0 +1,42 @@ +"""Arista Command Model.""" + +# Project +from hyperglass.configuration.models.commands.common import CommandSet, CommandGroup + +_ipv4_default = { + "bgp_route": "show ip bgp {target}", + "bgp_aspath": "show ip bgp regexp {target}", + "bgp_community": "show ip bgp community {target}", + "ping": "ping ip {target} source {source}", + "traceroute": "traceroute ip {target} source {source}", +} +_ipv6_default = { + "bgp_route": "show ipv6 bgp {target}", + "bgp_aspath": "show ipv6 bgp regexp {target}", + "bgp_community": "show ipv6 bgp community {target}", + "ping": "ping ipv6 {target} source {source}", + "traceroute": "traceroute ipv6 {target} source {source}", +} +_ipv4_vpn = { + "bgp_route": "show ip bgp {target} vrf {vrf}", + "bgp_aspath": "show ip bgp regexp {target} vrf {vrf}", + "bgp_community": "show ip bgp community {target} vrf {vrf}", + "ping": "ping vrf {vrf} ip {target} source {source}", + "traceroute": "traceroute vrf {vrf} ip {target} source {source}", +} +_ipv6_vpn = { + "bgp_route": "show ipv6 bgp {target} vrf {vrf}", + "bgp_aspath": "show ipv6 bgp regexp {target} vrf {vrf}", + "bgp_community": "show ipv6 bgp community {target} vrf {vrf}", + "ping": "ping vrf {vrf} ipv6 {target} source {source}", + "traceroute": "traceroute vrf {vrf} ipv6 {target} source {source}", +} + + +class AristaCommands(CommandGroup): + """Validation model for default arista commands.""" + + ipv4_default: CommandSet = CommandSet(**_ipv4_default) + ipv6_default: CommandSet = CommandSet(**_ipv6_default) + ipv4_vpn: CommandSet = CommandSet(**_ipv4_vpn) + ipv6_vpn: CommandSet = CommandSet(**_ipv6_vpn) diff --git a/hyperglass/configuration/models/commands/cisco_ios.py b/hyperglass/configuration/models/commands/cisco_ios.py new file mode 100644 index 0000000..ba84515 --- /dev/null +++ b/hyperglass/configuration/models/commands/cisco_ios.py @@ -0,0 +1,42 @@ +"""Cisco IOS Command Model.""" + +# Project +from hyperglass.configuration.models.commands.common import CommandSet, CommandGroup + +_ipv4_default = { + "bgp_route": "show bgp ipv4 unicast {target} | exclude pathid:|Epoch", + "bgp_aspath": 'show bgp ipv4 unicast quote-regexp "{target}"', + "bgp_community": "show bgp ipv4 unicast community {target}", + "ping": "ping {target} repeat 5 source {source}", + "traceroute": "traceroute {target} timeout 1 probe 2 source {source}", +} +_ipv6_default = { + "bgp_route": "show bgp ipv6 unicast {target} | exclude pathid:|Epoch", + "bgp_aspath": 'show bgp ipv6 unicast quote-regexp "{target}"', + "bgp_community": "show bgp ipv6 unicast community {target}", + "ping": "ping ipv6 {target} repeat 5 source {source}", + "traceroute": "traceroute ipv6 {target} timeout 1 probe 2 source {source}", +} +_ipv4_vpn = { + "bgp_route": "show bgp vpnv4 unicast vrf {vrf} {target}", + "bgp_aspath": 'show bgp vpnv4 unicast vrf {vrf} quote-regexp "{target}"', + "bgp_community": "show bgp vpnv4 unicast vrf {vrf} community {target}", + "ping": "ping vrf {vrf} {target} repeat 5 source {source}", + "traceroute": "traceroute vrf {vrf} {target} timeout 1 probe 2 source {source}", +} +_ipv6_vpn = { + "bgp_route": "show bgp vpnv6 unicast vrf {vrf} {target}", + "bgp_aspath": 'show bgp vpnv6 unicast vrf {vrf} quote-regexp "{target}"', + "bgp_community": "show bgp vpnv6 unicast vrf {vrf} community {target}", + "ping": "ping vrf {vrf} {target} repeat 5 source {source}", + "traceroute": "traceroute vrf {vrf} {target} timeout 1 probe 2 source {source}", +} + + +class CiscoIOSCommands(CommandGroup): + """Validation model for default cisco_ios commands.""" + + ipv4_default: CommandSet = CommandSet(**_ipv4_default) + ipv6_default: CommandSet = CommandSet(**_ipv6_default) + ipv4_vpn: CommandSet = CommandSet(**_ipv4_vpn) + ipv6_vpn: CommandSet = CommandSet(**_ipv6_vpn) diff --git a/hyperglass/configuration/models/commands/cisco_nxos.py b/hyperglass/configuration/models/commands/cisco_nxos.py new file mode 100644 index 0000000..4248038 --- /dev/null +++ b/hyperglass/configuration/models/commands/cisco_nxos.py @@ -0,0 +1,42 @@ +"""Cisco NX-OS Command Model.""" + +# Project +from hyperglass.configuration.models.commands.common import CommandSet, CommandGroup + +_ipv4_default = { + "bgp_route": "show bgp ipv4 unicast {target}", + "bgp_aspath": 'show bgp ipv4 unicast regexp "{target}"', + "bgp_community": "show bgp ipv4 unicast community {target}", + "ping": "ping {target} source {source}", + "traceroute": "traceroute {target} source {source}", +} +_ipv6_default = { + "bgp_route": "show bgp ipv6 unicast {target}", + "bgp_aspath": 'show bgp ipv6 unicast regexp "{target}"', + "bgp_community": "show bgp ipv6 unicast community {target}", + "ping": "ping6 {target} source {source}", + "traceroute": "traceroute6 {target} source {source}", +} +_ipv4_vpn = { + "bgp_route": "show bgp ipv4 unicast {target} vrf {vrf}", + "bgp_aspath": 'show bgp ipv4 unicast regexp "{target}" vrf {vrf}', + "bgp_community": "show bgp ipv4 unicast community {target} vrf {vrf}", + "ping": "ping {target} source {source} vrf {vrf}", + "traceroute": "traceroute {target} source {source} vrf {vrf}", +} +_ipv6_vpn = { + "bgp_route": "show bgp ipv6 unicast {target} vrf {vrf}", + "bgp_aspath": 'show bgp ipv6 unicast regexp "{target}" vrf {vrf}', + "bgp_community": "show bgp ipv6 unicast community {target} vrf {vrf}", + "ping": "ping6 {target} source {source} vrf {vrf}", + "traceroute": "traceroute6 {target} source {source} vrf {vrf}", +} + + +class CiscoNXOSCommands(CommandGroup): + """Validation model for default cisco_nxos commands.""" + + ipv4_default: CommandSet = CommandSet(**_ipv4_default) + ipv6_default: CommandSet = CommandSet(**_ipv6_default) + ipv4_vpn: CommandSet = CommandSet(**_ipv4_vpn) + ipv6_vpn: CommandSet = CommandSet(**_ipv6_vpn) diff --git a/hyperglass/configuration/models/commands/cisco_xr.py b/hyperglass/configuration/models/commands/cisco_xr.py new file mode 100644 index 0000000..478e324 --- /dev/null +++ b/hyperglass/configuration/models/commands/cisco_xr.py @@ -0,0 +1,42 @@ +"""Cisco IOS XR Command Model.""" + +# Project +from hyperglass.configuration.models.commands.common import CommandSet, CommandGroup + +_ipv4_default = { + "bgp_route": "show bgp ipv4 unicast {target}", + "bgp_aspath": "show bgp ipv4 unicast regexp {target}", + "bgp_community": "show bgp ipv4 unicast community {target}", + "ping": "ping ipv4 {target} count 5 source {source}", + "traceroute": "traceroute ipv4 {target} timeout 1 probe 2 source {source}", +} +_ipv6_default = { + "bgp_route": "show bgp ipv6 unicast {target}", + "bgp_aspath": "show bgp ipv6 unicast regexp {target}", + "bgp_community": "show bgp ipv6 unicast community {target}", + "ping": "ping ipv6 {target} count 5 source {source}", + "traceroute": "traceroute ipv6 {target} timeout 1 probe 2 source {source}", +} +_ipv4_vpn = { + "bgp_route": "show bgp vpnv4 unicast vrf {vrf} {target}", + "bgp_aspath": "show bgp vpnv4 unicast vrf {vrf} regexp {target}", + "bgp_community": "show bgp vpnv4 unicast vrf {vrf} community {target}", + "ping": "ping vrf {vrf} {target} count 5 source {source}", + "traceroute": "traceroute vrf {vrf} {target} timeout 1 probe 2 source {source}", +} +_ipv6_vpn = { + "bgp_route": "show bgp vpnv6 unicast vrf {vrf} {target}", + "bgp_aspath": "show bgp vpnv6 unicast vrf {vrf} regexp {target}", + "bgp_community": "show bgp vpnv6 unicast vrf {vrf} community {target}", + "ping": "ping vrf {vrf} {target} count 5 source {source}", + "traceroute": "traceroute vrf {vrf} {target} timeout 1 probe 2 source {source}", +} + + +class CiscoXRCommands(CommandGroup): + """Validation model for default cisco_xr commands.""" + + ipv4_default: CommandSet = CommandSet(**_ipv4_default) + ipv6_default: CommandSet = CommandSet(**_ipv6_default) + ipv4_vpn: CommandSet = CommandSet(**_ipv4_vpn) + ipv6_vpn: CommandSet = CommandSet(**_ipv6_vpn) diff --git a/hyperglass/configuration/models/commands/common.py b/hyperglass/configuration/models/commands/common.py new file mode 100644 index 0000000..e0e74a1 --- /dev/null +++ b/hyperglass/configuration/models/commands/common.py @@ -0,0 +1,32 @@ +"""Models common to entire commands module.""" + +from typing import Optional + +# Third Party +from pydantic import StrictStr + +# Project +from hyperglass.models import HyperglassModel + + +class CommandSet(HyperglassModel): + """Command set, defined per-AFI.""" + + bgp_route: StrictStr + bgp_aspath: StrictStr + bgp_community: StrictStr + ping: StrictStr + traceroute: StrictStr + + +class CommandGroup(HyperglassModel): + """Validation model for all commands.""" + + ipv4_default: CommandSet + ipv6_default: CommandSet + ipv4_vpn: CommandSet + ipv6_vpn: CommandSet + structured: Optional["CommandGroup"] + + +CommandGroup.update_forward_refs() diff --git a/hyperglass/configuration/models/commands/huawei.py b/hyperglass/configuration/models/commands/huawei.py new file mode 100644 index 0000000..8d09652 --- /dev/null +++ b/hyperglass/configuration/models/commands/huawei.py @@ -0,0 +1,42 @@ +"""Huawei Command Model.""" + +# Project +from hyperglass.configuration.models.commands.common import CommandSet, CommandGroup + +_ipv4_default = { + "bgp_route": "display bgp routing-table {target}", + "bgp_aspath": "display bgp routing-table regular-expression {target}", + "bgp_community": "display bgp routing-table regular-expression {target}", + "ping": "ping -c 5 -a {source} {target}", + "traceroute": "tracert -q 2 -f 1 -a {source} {target}", +} +_ipv6_default = { + "bgp_route": "display bgp ipv6 routing-table {target}", + "bgp_aspath": "display bgp ipv6 routing-table regular-expression {target}", + "bgp_community": "display bgp ipv6 routing-table community {target}", + "ping": "ping ipv6 -c 5 -a {source} {target}", + "traceroute": "tracert ipv6 -q 2 -f 1 -a {source} {target}", +} +_ipv4_vpn = { + "bgp_route": "display bgp vpnv4 vpn-instance {vrf} routing-table {target}", + "bgp_aspath": "display bgp vpnv4 vpn-instance {vrf} routing-table regular-expression {target}", + "bgp_community": "display bgp vpnv4 vpn-instance {vrf} routing-table regular-expression {target}", + "ping": "ping -vpn-instance {vrf} -c 5 -a {source} {target}", + "traceroute": "tracert -q 2 -f 1 -vpn-instance {vrf} -a {source} {target}", +} +_ipv6_vpn = { + "bgp_route": "display bgp vpnv6 vpn-instance {vrf} routing-table {target}", + "bgp_aspath": "display bgp vpnv6 vpn-instance {vrf} routing-table regular-expression {target}", + "bgp_community": "display bgp vpnv6 vpn-instance {vrf} routing-table regular-expression {target}", + "ping": "ping vpnv6 vpn-instance {vrf} -c 5 -a {source} {target}", + "traceroute": "tracert -q 2 -f 1 vpn-instance {vrf} -a {source} {target}", +} + + +class HuaweiCommands(CommandGroup): + """Validation model for default huawei commands.""" + + ipv4_default: CommandSet = CommandSet(**_ipv4_default) + ipv6_default: CommandSet = CommandSet(**_ipv6_default) + ipv4_vpn: CommandSet = CommandSet(**_ipv4_vpn) + ipv6_vpn: CommandSet = CommandSet(**_ipv6_vpn) diff --git a/hyperglass/configuration/models/commands/juniper.py b/hyperglass/configuration/models/commands/juniper.py new file mode 100644 index 0000000..15bcfef --- /dev/null +++ b/hyperglass/configuration/models/commands/juniper.py @@ -0,0 +1,74 @@ +"""Juniper Command Model.""" + +# Project +from hyperglass.configuration.models.commands.common import CommandSet, CommandGroup + +_ipv4_default = { + "bgp_route": 'show route protocol bgp table inet.0 {target} detail | except Label | except Label | except "Next hop type" | except Task | except Address | except "Session Id" | except State | except "Next-hop reference" | except destinations | except "Announcement bits"', + "bgp_aspath": 'show route protocol bgp table inet.0 aspath-regex "{target}"', + "bgp_community": "show route protocol bgp table inet.0 community {target}", + "ping": "ping inet {target} count 5 source {source}", + "traceroute": "traceroute inet {target} wait 1 source {source}", +} +_ipv6_default = { + "bgp_route": 'show route protocol bgp table inet6.0 {target} detail | except Label | except Label | except "Next hop type" | except Task | except Address | except "Session Id" | except State | except "Next-hop reference" | except destinations | except "Announcement bits"', + "bgp_aspath": 'show route protocol bgp table inet6.0 aspath-regex "{target}"', + "bgp_community": "show route protocol bgp table inet6.0 community {target}", + "ping": "ping inet6 {target} count 5 source {source}", + "traceroute": "traceroute inet6 {target} wait 2 source {source}", +} +_ipv4_vpn = { + "bgp_route": 'show route protocol bgp table {vrf}.inet.0 {target} detail | except Label | except Label | except "Next hop type" | except Task | except Address | except "Session Id" | except State | except "Next-hop reference" | except destinations | except "Announcement bits"', + "bgp_aspath": 'show route protocol bgp table {vrf}.inet.0 aspath-regex "{target}"', + "bgp_community": "show route protocol bgp table {vrf}.inet.0 community {target}", + "ping": "ping inet routing-instance {vrf} {target} count 5 source {source}", + "traceroute": "traceroute inet routing-instance {vrf} {target} wait 1 source {source}", +} +_ipv6_vpn = { + "bgp_route": 'show route protocol bgp table {vrf}.inet6.0 {target} detail | except Label | except Label | except "Next hop type" | except Task | except Address | except "Session Id" | except State | except "Next-hop reference" | except destinations | except "Announcement bits"', + "bgp_aspath": 'show route protocol bgp table {vrf}.inet6.0 aspath-regex "{target}"', + "bgp_community": "show route protocol bgp table {vrf}.inet6.0 community {target}", + "ping": "ping inet6 routing-instance {vrf} {target} count 5 source {source}", + "traceroute": "traceroute inet6 routing-instance {vrf} {target} wait 2 source {source}", +} + +_structured = CommandGroup( + ipv4_default=CommandSet( + bgp_route="show route protocol bgp table inet.0 {target} detail | display json", + bgp_aspath='show route protocol bgp table inet.0 aspath-regex "{target}" | display json', + bgp_community="show route protocol bgp table inet.0 community {target} | display json", + ping="ping inet {target} count 5 source {source}", + traceroute="traceroute inet {target} wait 1 source {source}", + ), + ipv6_default=CommandSet( + bgp_route="show route protocol bgp table inet6.0 {target} detail | display json", + bgp_aspath='show route protocol bgp table inet6.0 aspath-regex "{target}" | display json', + bgp_community="show route protocol bgp table inet6.0 community {target} | display json", + ping="ping inet6 {target} count 5 source {source}", + traceroute="traceroute inet6 {target} wait 2 source {source}", + ), + ipv4_vpn=CommandSet( + bgp_route="show route protocol bgp table {vrf}.inet.0 {target} detail | display json", + bgp_aspath='show route protocol bgp table {vrf}.inet.0 aspath-regex "{target}" | display json', + bgp_community="show route protocol bgp table {vrf}.inet.0 community {target} | display json", + ping="ping inet routing-instance {vrf} {target} count 5 source {source}", + traceroute="traceroute inet routing-instance {vrf} {target} wait 1 source {source}", + ), + ipv6_vpn=CommandSet( + bgp_route="show route protocol bgp table {vrf}.inet6.0 {target} detail | display json", + bgp_aspath='show route protocol bgp table {vrf}.inet6.0 aspath-regex "{target}" | display json', + bgp_community="show route protocol bgp table {vrf}.inet6.0 community {target} | display json", + ping="ping inet6 routing-instance {vrf} {target} count 5 source {source}", + traceroute="traceroute inet6 routing-instance {vrf} {target} wait 1 source {source}", + ), +) + + +class JuniperCommands(CommandGroup): + """Validation model for default juniper commands.""" + + ipv4_default: CommandSet = CommandSet(**_ipv4_default) + ipv6_default: CommandSet = CommandSet(**_ipv6_default) + ipv4_vpn: CommandSet = CommandSet(**_ipv4_vpn) + ipv6_vpn: CommandSet = CommandSet(**_ipv6_vpn) + structured: CommandGroup = _structured diff --git a/hyperglass/configuration/models/routers.py b/hyperglass/configuration/models/routers.py index e99334e..980543c 100644 --- a/hyperglass/configuration/models/routers.py +++ b/hyperglass/configuration/models/routers.py @@ -7,20 +7,25 @@ from typing import List, Optional from pathlib import Path # Third Party -from pydantic import StrictInt, StrictStr, validator +from pydantic import StrictInt, StrictStr, StrictBool, validator # Project from hyperglass.log import log from hyperglass.util import clean_name from hyperglass.models import HyperglassModel, HyperglassModelExtra -from hyperglass.constants import SCRAPE_HELPERS, TRANSPORT_REST, TRANSPORT_SCRAPE +from hyperglass.constants import ( + SCRAPE_HELPERS, + TRANSPORT_REST, + TRANSPORT_SCRAPE, + SUPPORTED_STRUCTURED_OUTPUT, +) from hyperglass.exceptions import ConfigError, UnsupportedDevice from hyperglass.configuration.models.ssl import Ssl from hyperglass.configuration.models.vrfs import Vrf, Info from hyperglass.configuration.models.proxies import Proxy -from hyperglass.configuration.models.commands import Command from hyperglass.configuration.models.networks import Network from hyperglass.configuration.models.credentials import Credential +from hyperglass.configuration.models.commands.common import CommandGroup _default_vrf = { "name": "default", @@ -51,10 +56,36 @@ class Router(HyperglassModel): port: StrictInt ssl: Optional[Ssl] nos: StrictStr - commands: Optional[Command] + commands: Optional[CommandGroup] vrfs: List[Vrf] = [_default_vrf] display_vrfs: List[StrictStr] = [] vrf_names: List[StrictStr] = [] + structured_output: Optional[StrictBool] + + @validator("structured_output", pre=True, always=True) + def validate_structured_output(cls, value, values): + """Validate structured output is supported on the device & set a default. + + Raises: + ConfigError: Raised if true on a device that doesn't support structured output. + + Returns: + {bool} -- True if hyperglass should return structured output for this device. + """ + if value is True and values["nos"] not in SUPPORTED_STRUCTURED_OUTPUT: + raise ConfigError( + "The 'structured_output' field is set to 'true' on device '{d}' with " + + "NOS '{n}', which does not support structured output", + d=values["name"], + n=values["nos"], + ) + + elif value is None and values["nos"] in SUPPORTED_STRUCTURED_OUTPUT: + value = True + else: + value = False + + return value @validator("nos") def supported_nos(cls, value): diff --git a/hyperglass/constants.py b/hyperglass/constants.py index 40fa3f4..2dd5e19 100644 --- a/hyperglass/constants.py +++ b/hyperglass/constants.py @@ -13,12 +13,12 @@ METADATA = (__name__, __version__, __author__, __copyright__, __license__) MIN_PYTHON_VERSION = (3, 6) -protocol_map = {80: "http", 8080: "http", 443: "https", 8443: "https"} - TARGET_FORMAT_SPACE = ("huawei", "huawei_vrpv8") TARGET_JUNIPER_ASPATH = ("juniper", "juniper_junos") +SUPPORTED_STRUCTURED_OUTPUT = ("juniper",) + STATUS_CODE_MAP = {"warning": 400, "error": 400, "danger": 500} DNS_OVER_HTTPS = { diff --git a/hyperglass/ui/components/Table/TableBody.js b/hyperglass/ui/components/Table/TableBody.js index 28d9e68..78e0038 100644 --- a/hyperglass/ui/components/Table/TableBody.js +++ b/hyperglass/ui/components/Table/TableBody.js @@ -1,6 +1,6 @@ -import * as React from "react"; -import { Box } from "@chakra-ui/core"; -import css from "@styled-system/css"; +/** @jsx jsx */ +import { jsx } from "@emotion/core"; +import { Box, css } from "@chakra-ui/core"; const TableBody = ({ children, ...props }) => { return ( @@ -9,7 +9,7 @@ const TableBody = ({ children, ...props }) => { overflowY="scroll" css={css({ "&::-webkit-scrollbar": { display: "none" }, - "&": { "-ms-overflow-style": "none" } + "&": { msOverflowStyle: "none" } })} overflowX="hidden" {...props} diff --git a/hyperglass/ui/components/Table/index.js b/hyperglass/ui/components/Table/index.js index 6abd68a..b5be5c8 100644 --- a/hyperglass/ui/components/Table/index.js +++ b/hyperglass/ui/components/Table/index.js @@ -77,12 +77,6 @@ const Table = ({ usePagination ); - // tableColumns.map(col => { - // if (col.hidden === true) { - // setHiddenColumns(old => [...old, col.id]); - // } - // }); - return ( {!!tableHeading && {tableHeading}} diff --git a/hyperglass/ui/pages/structured.js b/hyperglass/ui/pages/structured.js index 9404f78..3e5ef83 100644 --- a/hyperglass/ui/pages/structured.js +++ b/hyperglass/ui/pages/structured.js @@ -324,7 +324,7 @@ const Structured = () => { return ( - +