Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace confparse w netutils #1565

Merged
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ ignore_missing_imports = True
[mypy-lxml.*]
ignore_missing_imports = True

[mypy-ciscoconfparse]
[mypy-netutils.*]
ignore_missing_imports = True

[mypy-textfsm]
Expand Down
80 changes: 62 additions & 18 deletions napalm/base/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
# third party libs
import jinja2
import textfsm
from ciscoconfparse import CiscoConfParse
from lxml import etree
from netaddr import EUI
from netaddr import IPAddress
from netaddr import mac_unix
from netutils.config.parser import IOSConfigParser

# local modules
import napalm.base.exceptions
Expand Down Expand Up @@ -127,42 +127,86 @@ def load_template(
return cls.load_merge_candidate(config=configuration)


def cisco_conf_parse_parents(
def netutils_parse_parents(
parent: str, child: str, config: Union[str, List[str]]
) -> List[str]:
"""
Use CiscoConfParse to find parent lines that contain a specific child line.
Use Netutils to find parent lines that contain a specific child line.

:param parent: The parent line to search for
:param child: The child line required under the given parent
:param config: The device running/startup config
"""
if type(config) == str:
config = config.splitlines() # type: ignore
parse = CiscoConfParse(config)
cfg_obj = parse.find_parents_w_child(parent, child)
return cfg_obj
# Check if the config is a list, if it is a list, then join it to make a string.
logger.error(config)
jvanderaa marked this conversation as resolved.
Show resolved Hide resolved
if isinstance(config, list):
config = "\n".join(config)
config = config + "\n"
logger.error(config)

# Config tree is the entire configuration in a tree format,
# followed by getting the individual lines that has the formats:
# ConfigLine(config_line=' ip address 192.0.2.10 255.255.255.0',
# parents=('interface GigabitEthernet1',))
# ConfigLine(config_line='Current configuration : 1624 bytes', parents=())
config_tree = IOSConfigParser(str(config))
configuration_lines = config_tree.build_config_relationship()

# Return config is the list that will be returned
return_config = []

# Loop over each of the configuration lines
for line in configuration_lines:
# Loop over any line that has a parent line. If there are no parents for a line item then
# the parents is an empty tuple.
for parent_line in line.parents:
if (
child in line.config_line
and re.match(parent, parent_line) is not None
and parent_line not in return_config
):
return_config.append(parent_line)

return return_config


def cisco_conf_parse_objects(
def netutils_parse_objects(
cfg_section: str, config: Union[str, List[str]]
) -> List[str]:
"""
Use CiscoConfParse to find and return a section of Cisco IOS config.
Use Netutils to find and return a section of Cisco IOS config.
Similar to "show run | section <cfg_section>"

:param cfg_section: The section of the config to return eg. "router bgp"
:param config: The running/startup config of the device to parse
"""
# Check if the config is a list, if it is a list, then join it to make a string.
if isinstance(config, list):
config = "\n".join(config)
config = config + "\n"

# Config tree is the entire configuration in a tree format,
# followed by getting the individual lines that has the formats:
# ConfigLine(config_line=' ip address 192.0.2.10 255.255.255.0',
# parents=('interface GigabitEthernet1',))
# ConfigLine(config_line='Current configuration : 1624 bytes', parents=())
config_tree = IOSConfigParser(str(config))
lines = config_tree.build_config_relationship()

# Return config is the list that will be returned
return_config = []
if type(config) is str:
config = config.splitlines() # type: ignore
parse = CiscoConfParse(config)
cfg_obj = parse.find_objects(cfg_section)
for parent in cfg_obj:
return_config.append(parent.text)
for child in parent.all_children:
return_config.append(child.text)
for line in lines:
# The parent configuration is expected on the function that this is replacing,
# add the parent line to the base of the return_config
if cfg_section in line.config_line:
return_config.append(line.config_line)
# Check if the tuple is greater than 0
if len(line.parents) > 0:
# Check the eldest parent, if that is part of the config section, then append
# the current line being checked to it.
if cfg_section in line.parents[0]:
return_config.append(line.config_line)

return return_config


Expand Down
31 changes: 15 additions & 16 deletions napalm/ios/ios.py
Original file line number Diff line number Diff line change
Expand Up @@ -1320,21 +1320,20 @@ def build_prefix_limit(af_table, limit, prefix_percent, prefix_timeout):
}
return prefix_limit

# Get BGP config using ciscoconfparse because some old devices dont support "| sec bgp"
# Get BGP config using netutils because some old devices dont support "| sec bgp"
cfg = self.get_config(retrieve="running")
cfg = cfg["running"].splitlines()
bgp_config_text = napalm.base.helpers.cisco_conf_parse_objects(
"router bgp", cfg
bgp_config_list = napalm.base.helpers.netutils_parse_objects(
"router bgp", cfg["running"]
)
bgp_asn = napalm.base.helpers.regex_find_txt(
r"router bgp (\d+)", bgp_config_text, default=0
r"router bgp (\d+)", bgp_config_list, default=0
)
# Get a list of all neighbors and groups in the config
all_neighbors = set()
all_groups = set()
bgp_group_neighbors = {}
all_groups.add("_")
for line in bgp_config_text:
for line in bgp_config_list:
if " neighbor " in line:
if re.search(IP_ADDR_REGEX, line) is not None:
all_neighbors.add(re.search(IP_ADDR_REGEX, line).group())
Expand All @@ -1351,8 +1350,8 @@ def build_prefix_limit(af_table, limit, prefix_percent, prefix_timeout):
if neighbor:
if bgp_neighbor != neighbor:
continue
afi_list = napalm.base.helpers.cisco_conf_parse_parents(
r"\s+address-family.*", bgp_neighbor, bgp_config_text
afi_list = napalm.base.helpers.netutils_parse_parents(
r"\s+address-family.*", bgp_neighbor, bgp_config_list
)
try:
afi = afi_list[0]
Expand All @@ -1363,8 +1362,8 @@ def build_prefix_limit(af_table, limit, prefix_percent, prefix_timeout):
if "vrf" in str(afi_list):
continue
else:
neighbor_config = napalm.base.helpers.cisco_conf_parse_objects(
bgp_neighbor, bgp_config_text
neighbor_config = napalm.base.helpers.netutils_parse_objects(
bgp_neighbor, bgp_config_list
)
# For group_name- use peer-group name, else VRF name, else "_" for no group
group_name = napalm.base.helpers.regex_find_txt(
Expand Down Expand Up @@ -1454,16 +1453,16 @@ def build_prefix_limit(af_table, limit, prefix_percent, prefix_timeout):
"neighbors": bgp_group_neighbors.get("_", {}),
}
continue
neighbor_config = napalm.base.helpers.cisco_conf_parse_objects(
group_name, bgp_config_text
neighbor_config = napalm.base.helpers.netutils_parse_objects(
group_name, bgp_config_list
)
multipath = False
afi_list = napalm.base.helpers.cisco_conf_parse_parents(
r"\s+address-family.*", group_name, neighbor_config
afi_list = napalm.base.helpers.netutils_parse_parents(
r"\s+address-family.*", group_name, bgp_config_list
)
for afi in afi_list:
afi_config = napalm.base.helpers.cisco_conf_parse_objects(
afi, bgp_config_text
afi_config = napalm.base.helpers.netutils_parse_objects(
afi, bgp_config_list
)
multipath = bool(
napalm.base.helpers.regex_find_txt(r" multipath", str(afi_config))
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pyYAML
pyeapi>=0.8.2
netmiko>=3.3.0,<4.0.0
junos-eznc>=2.2.1
ciscoconfparse
scp
lxml>=4.3.0
ncclient
netutils>=1.0.0