diff --git a/changelog/undistributed/iosxe_show_run_dhcp_and_show_run_ip_route_20230815123927.rst b/changelog/undistributed/iosxe_show_run_dhcp_and_show_run_ip_route_20230815123927.rst new file mode 100644 index 0000000000..6520e107ea --- /dev/null +++ b/changelog/undistributed/iosxe_show_run_dhcp_and_show_run_ip_route_20230815123927.rst @@ -0,0 +1,9 @@ +-------------------------------------------------------------------------------- + New +-------------------------------------------------------------------------------- + +* iosxe + * Added ShowRunIpRoute + * show running-config | i ^ip route + * Added ShowRunDhcp + * show running-config dhcp \ No newline at end of file diff --git a/sdk_generator/outputs/github_parser.json b/sdk_generator/outputs/github_parser.json index 809993a974..db55852d76 100644 --- a/sdk_generator/outputs/github_parser.json +++ b/sdk_generator/outputs/github_parser.json @@ -52943,6 +52943,26 @@ "url": "https://github.com/CiscoTestAutomation/genieparser/tree/master/src/genie/libs/parser/nxos/show_bgp.py#L6210" } }, + "show running-config dhcp": { + "ios": { + "class": "ShowRunDhcp", + "doc": "\n Parser for: show running-config dhcp\n real command is: show running-config\n ", + "module_name": "show_run_dhcp", + "package": "genie.libs.parser", + "schema": "{\nAny (str) *: {\n Optional (str) domain: ,\n Optional (str) gateway: ,\n Optional (str) vrf: ,\n Optional (str) boot_file: ,\n Optional (str) netbios_servers: ,\n Optional (str) dns_servers: ,\n Optional (str) dhcp_excludes: {\n Any (str) *: {\n Optional (str) end: ,\n Optional (str) start: ,\n },\n },\n Optional (str) networks: {\n Any (str) *: {\n Optional (str) ip: ,\n Optional (str) subnet_mask: ,\n Optional (str) secondary: ,\n },\n },\n Optional (str) dhcp_options: {\n Any (str) *: {\n Optional (str) option: ,\n Optional (str) type: ,\n Optional (str) data: ,\n },\n },\n Optional (str) lease_time: ,\n },\n}", + "uid": "show_running-config_dhcp", + "url": "https://github.com/CiscoTestAutomation/genieparser/tree/master/src/genie/libs/parser/ios/show_run_dhcp.py#L4" + }, + "iosxe": { + "class": "ShowRunDhcp", + "doc": null, + "module_name": "show_run_dhcp", + "package": "genie.libs.parser", + "schema": "{\nAny (str) *: {\n Optional (str) domain: ,\n Optional (str) gateway: ,\n Optional (str) vrf: ,\n Optional (str) boot_file: ,\n Optional (str) netbios_servers: ,\n Optional (str) dns_servers: ,\n Optional (str) dhcp_excludes: {\n Any (str) *: {\n Optional (str) end: ,\n Optional (str) start: ,\n },\n },\n Optional (str) networks: {\n Any (str) *: {\n Optional (str) ip: ,\n Optional (str) subnet_mask: ,\n Optional (str) secondary: ,\n },\n },\n Optional (str) dhcp_options: {\n Any (str) *: {\n Optional (str) option: ,\n Optional (str) type: ,\n Optional (str) data: ,\n },\n },\n Optional (str) lease_time: ,\n },\n}", + "uid": "show_running-config_dhcp", + "url": "https://github.com/CiscoTestAutomation/genieparser/tree/master/src/genie/libs/parser/iosxe/show_run_dhcp.py#L100" + } + }, "show running-config flow exporter": { "iosxe": { "class": "ShowRunningConfigFlowExporter", @@ -53139,6 +53159,26 @@ "url": "https://github.com/CiscoTestAutomation/genieparser/tree/master/src/genie/libs/parser/nxos/show_pim.py#L4278" } }, + "show running-config | i ^ip route": { + "ios": { + "class": "ShowRunIpRoute", + "doc": "Parser for: show running-config | ^ip route", + "module_name": "show_run_ip_route", + "package": "genie.libs.parser", + "schema": "{\n'ip_routes': {\n Any (str) *: {\n 'vrf': ,\n 'vrf_global': ,\n 'subnet': ,\n 'subnet_mask': ,\n 'next_hop': ,\n 'forward_address': ,\n 'route_name': ,\n 'metric': ,\n 'track_object': ,\n 'permanent': ,\n 'tag': ,\n 'parsing_leftovers': ,\n },\n },\n}", + "uid": "show_running-config___i_^ip_route", + "url": "https://github.com/CiscoTestAutomation/genieparser/tree/master/src/genie/libs/parser/ios/show_run_ip_route.py#L4" + }, + "iosxe": { + "class": "ShowRunIpRoute", + "doc": null, + "module_name": "show_run_ip_route", + "package": "genie.libs.parser", + "schema": "{\n'ip_routes': {\n Any (str) *: {\n 'vrf': ,\n 'vrf_global': ,\n 'subnet': ,\n 'subnet_mask': ,\n 'next_hop': ,\n 'forward_address': ,\n 'route_name': ,\n 'metric': ,\n 'track_object': ,\n 'permanent': ,\n 'tag': ,\n 'parsing_leftovers': ,\n },\n },\n}", + "uid": "show_running-config___i_^ip_route", + "url": "https://github.com/CiscoTestAutomation/genieparser/tree/master/src/genie/libs/parser/iosxe/show_run_ip_route.py#L31" + } + }, "show running-config | inc peer": { "nxos": { "class": "ShowBgpPeerTemplate", diff --git a/src/genie/libs/parser/ios/show_run_dhcp.py b/src/genie/libs/parser/ios/show_run_dhcp.py new file mode 100644 index 0000000000..6bebdeda22 --- /dev/null +++ b/src/genie/libs/parser/ios/show_run_dhcp.py @@ -0,0 +1,9 @@ +from genie.libs.parser.iosxe.show_run_dhcp import ShowRunDhcp as ShowRunDhcp__iosxe + + +class ShowRunDhcp(ShowRunDhcp__iosxe): + """ + Parser for: show running-config dhcp + real command is: show running-config + """ + pass diff --git a/src/genie/libs/parser/ios/show_run_ip_route.py b/src/genie/libs/parser/ios/show_run_ip_route.py new file mode 100644 index 0000000000..e68395211c --- /dev/null +++ b/src/genie/libs/parser/ios/show_run_ip_route.py @@ -0,0 +1,6 @@ +from genie.libs.parser.iosxe.show_run_ip_route import ShowRunIpRoute as ShowRunIpRoute__iosxe + + +class ShowRunIpRoute(ShowRunIpRoute__iosxe): + """Parser for: show running-config | ^ip route""" + pass \ No newline at end of file diff --git a/src/genie/libs/parser/iosxe/show_run_dhcp.py b/src/genie/libs/parser/iosxe/show_run_dhcp.py new file mode 100644 index 0000000000..2c06cb0bc8 --- /dev/null +++ b/src/genie/libs/parser/iosxe/show_run_dhcp.py @@ -0,0 +1,309 @@ +import logging +from genie.metaparser import MetaParser +from genie.metaparser.util.schemaengine import Any, Optional +import re +from netaddr import AddrFormatError, IPAddress, IPNetwork + +logger = logging.getLogger(__name__) + + +def create_cidr_notation(subnet, mask): + try: + cidr = IPNetwork(f"{subnet}/{mask}").prefixlen + # ex: 10.0.0.0/24 + return f"{subnet}/{cidr}" + except AddrFormatError: + return "0.0.0.0" + + +def check_if_ip_in_network(ipaddress, network, subnet_mask): + cidr = create_cidr_notation(network, subnet_mask) + return IPAddress(ipaddress) in IPNetwork(cidr) + + +def extract_excluded_ip_address(exclude_addresses, subnet, subnet_mask, block_vrf=None): + """ + Used in: DHCP Pools + Example: ip dhcp excluded-address x.x.x.x x.x.x.x + two conditions: + exclude host: + check if ip is in range --> add in exclude address as start and end + exclude range + check if first is in range --> exit when not + if in range check, second --> if ok than add both to exclude address + as start and end address + If none matches return empty string + :param settings: + :return: + """ + excluded_ip_addresses = {} + + for exclude_address in exclude_addresses['data']: + exclude_vrf = exclude_addresses.get('vrf', None) + + # needs refactor. + # 2 cases: + # - a pool with vrf statement should only match exclude statements with vrf + # - a pool without vrf statement should only match exclude statements without vrf + if (block_vrf is not None and exclude_vrf is not None and exclude_vrf in block_vrf) or (block_vrf is None and exclude_vrf is None): + in_network = check_if_ip_in_network( + exclude_address, + subnet, + subnet_mask) + if not in_network: + break + + if len(exclude_addresses['data']) > 1: + excluded_ip_addresses = {'start': exclude_addresses['data'][0], + 'end': exclude_addresses['data'][1]} + else: + excluded_ip_addresses = {'start': exclude_addresses['data'][0], + 'end': exclude_addresses['data'][0]} + + return excluded_ip_addresses + + +class ShowRunDhcpSchema(MetaParser): + schema = { + Any(): { + Optional("domain"): str, + Optional("gateway"): str, + Optional("vrf"): str, + Optional("boot_file"): str, + Optional("netbios_servers"): list, + Optional("dns_servers"): list, + Optional("dhcp_excludes"): { + Any(): { + Optional('end'): str, + Optional('start'): str, + } + }, + Optional("networks"): { + Any(): { + Optional('ip'): str, + Optional('subnet_mask'): str, + Optional('secondary'): bool, + } + }, + Optional("dhcp_options"): { + Any(): { + Optional("option"): str, + Optional("type"): str, + Optional("data"): str, + } + }, + Optional("lease_time"): str, + }, + } + + +class ShowRunDhcp(ShowRunDhcpSchema): + # note below command does not exist. + # there is no command that filters all dhcp info (unless regex on cli) + # but that is not a good option. better to leverage python for that + # the real command we will run is show running-config. + cli_command = "show running-config dhcp" + + def cli(self, output=None): + real_cmd = "show running-config" + out = self.device.execute(real_cmd) if output is None else output + + # below regex extracts blocks op dhcp pools until the ! char: + # ! + # ip dhcp pool hatseflats-2 + # network 172.16.3.0 255.255.255.0 + # ! + p_get_dhcp_pool_blocks = re.compile( + r'(?Pip dhcp pool[\s\S]*?(?=\n.*?\!))') + + # note: can be multiples of below in a configuration + # ex: ip dhcp excluded-address 10.0.11.0 10.0.11.2 + # ex: ip dhcp excluded-address vrf lala 10.0.11.0 10.0.11.2 + p_get_dhcp_excluded = re.compile( + r"^ip dhcp excluded-address (.*?)(?P[0-9]+.*)$") + + p_get_dhcp_excluded_vrf = re.compile( + r"^ip dhcp excluded-address vrf (?P.*?)(?=\s)(.*?)(?P[0-9]+.*)$") + + # regex extraction patterns for inside a block + # note that we stripped the ident space of it + # + # ex: domain-name Wijnen.local + p_block_pool_name = re.compile(r"^ip dhcp pool (?P.*)$") + p_block_domain = re.compile(r"^domain-name (?P.*$)") + + # ex: default-router 10.24.125.254 + # ex: default-router hostname.com + p_block_gateway = re.compile( + r"^default-router (?P\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\w.*)$") + + # ex: network 10.0.10.160 255.255.255.240 + p_block_network = re.compile( + r"^network\s(?P\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(?P\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$") + # ex: network 10.0.10.160 255.255.255.240 secondary + p_block_network_secondary = re.compile( + r"^network\s(?P\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(?P\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(?Psecondary)$") + + # note: there are a lot of options. for parsing we do not opinionate + # we also dont do matching in the option data. + # because it can contain almost everything + p_block_options = re.compile( + r"^option\s(?P