diff --git a/ciscoconfparse/ccp_abc.py b/ciscoconfparse/ccp_abc.py index c8be64c..257724d 100644 --- a/ciscoconfparse/ccp_abc.py +++ b/ciscoconfparse/ccp_abc.py @@ -35,9 +35,19 @@ class BaseCfgLine(metaclass=ABCMeta): # deprecating py2.foo metaclass syntax in version 1.6.8... # __metaclass__ = ABCMeta - def __init__(self, text=DEFAULT_TEXT, comment_delimiter="!"): - """Accept an IOS line number and initialize family relationship - attributes""" + def __init__(self, all_lines=None, line=DEFAULT_TEXT, comment_delimiter="!"): + """Accept an IOS line number and initialize family relationship attributes""" + + if not isinstance(all_lines, list): + error = f"BaseCfgLine() expected `type(all_lines)` to be a list, but got {type(all_lines)}" + error.critical(error) + raise InvalidParameters(error) + + if not isinstance(line, str): + error = f"BaseCfgLine() expected BaseCfgLine(line=`{line}`) to be a string, but got {type(line)}" + error.critical(error) + raise InvalidParameters(error) + self.comment_delimiter = comment_delimiter self._uncfgtext_to_be_deprecated = "" self._text = DEFAULT_TEXT @@ -51,7 +61,7 @@ def __init__(self, text=DEFAULT_TEXT, comment_delimiter="!"): self.blank_line_keep = False # CiscoConfParse() uses blank_line_keep # Call set_comment_bool() in the self.text setter method... - self.text = text # Use self.text setter method to set this value + self.text = line # Use self.text setter method to set this value self._line_id = None self.diff_rendered = None diff --git a/ciscoconfparse/ciscoconfparse.py b/ciscoconfparse/ciscoconfparse.py index daca60d..9b307f7 100644 --- a/ciscoconfparse/ciscoconfparse.py +++ b/ciscoconfparse/ciscoconfparse.py @@ -47,6 +47,7 @@ from ciscoconfparse.models_cisco import IOSAaaLoginAuthenticationLine from ciscoconfparse.models_cisco import IOSAaaEnableAuthenticationLine from ciscoconfparse.models_cisco import IOSAaaCommandsAuthorizationLine +from ciscoconfparse.models_cisco import IOSAaaConsoleAuthorizationLine from ciscoconfparse.models_cisco import IOSAaaCommandsAccountingLine from ciscoconfparse.models_cisco import IOSAaaExecAccountingLine from ciscoconfparse.models_cisco import IOSAaaGroupServerLine @@ -57,6 +58,7 @@ from ciscoconfparse.models_nxos import NXOSAaaLoginAuthenticationLine from ciscoconfparse.models_nxos import NXOSAaaEnableAuthenticationLine from ciscoconfparse.models_nxos import NXOSAaaCommandsAuthorizationLine +from ciscoconfparse.models_nxos import NXOSAaaConsoleAuthorizationLine from ciscoconfparse.models_nxos import NXOSAaaCommandsAccountingLine from ciscoconfparse.models_nxos import NXOSAaaExecAccountingLine from ciscoconfparse.models_nxos import NXOSCfgLine, NXOSIntfLine @@ -100,6 +102,7 @@ IOSAaaLoginAuthenticationLine, IOSAaaEnableAuthenticationLine, IOSAaaCommandsAuthorizationLine, + IOSAaaConsoleAuthorizationLine, IOSAaaCommandsAccountingLine, IOSAaaExecAccountingLine, IOSAaaGroupServerLine, @@ -114,6 +117,7 @@ NXOSAaaLoginAuthenticationLine, NXOSAaaEnableAuthenticationLine, NXOSAaaCommandsAuthorizationLine, + NXOSAaaConsoleAuthorizationLine, NXOSAaaCommandsAccountingLine, NXOSAaaExecAccountingLine, NXOSAaaGroupServerLine, @@ -391,23 +395,47 @@ def _parse_line_braces(line_txt=None, comment_delimiter=None) -> tuple: # This method was on ConfigList() @logger.catch(reraise=True) def _cfgobj_from_text( - txt, idx, syntax=None, comment_delimiter=None, factory=None + text_list, txt, idx, syntax=None, comment_delimiter=None, factory=None ): """Build cfgobj from configuration text syntax, and factory inputs.""" + + if not isinstance(txt, str): + error = f"_cfgobj_from_text(txt=`{txt}`) must be a string" + logger.error(error) + raise InvalidParameters(error) + + if not isinstance(idx, int): + error = f"_cfgobj_from_text(idx=`{idx}`) must be an int" + logger.error(error) + raise InvalidParameters(error) + # if not factory is **faster** than factory is False - if not factory: + if syntax in ALL_VALID_SYNTAX and not factory: obj = CFGLINE[syntax]( text=txt, comment_delimiter=comment_delimiter, ) + if isinstance(obj, BaseCfgLine): + obj.linenum = idx + else: + error = f"{CFGLINE[syntax]}(txt=`{txt}`) must return an instance of BaseCfgLine(), but it returned {obj}" + logger.error(error) + raise ValueError(error) # if factory is **faster** than if factory is True elif syntax in ALL_VALID_SYNTAX and factory: - obj = ConfigLineFactory( - txt, - comment_delimiter, + obj = config_line_factory( + all_lines=text_list, + line=txt, + comment_delimiter=comment_delimiter, syntax=syntax, ) + if isinstance(obj, BaseCfgLine): + obj.linenum = idx + else: + error = f"config_line_factory(line=`{txt}`) must return an instance of BaseCfgLine(), but it returned {obj}" + logger.error(error) + raise ValueError(error) else: err_txt = ( @@ -416,7 +444,6 @@ def _cfgobj_from_text( logger.error(err_txt) raise ValueError(err_txt) - obj.linenum = idx return obj @@ -702,15 +729,24 @@ def __init__( # This method is on CiscoConfParse() @logger.catch(reraise=True) def _handle_ccp_syntax(self, tmp_lines=None, syntax=None): - """Deal with syntax issues, such as conditionally discarding junos closing brace-lines.""" + """Deal with brace-delimited syntax issues, such as conditionally discarding junos closing brace-lines.""" - assert tmp_lines is not None - if syntax == "junos": - err_msg = "junos parser factory is not yet enabled; use factory=False" - assert self.factory is False, err_msg - config_lines = convert_junos_to_ios(tmp_lines, comment_delimiter="#") - else: + if not syntax in ALL_VALID_SYNTAX: + error = f"{syntax} parser factory is not yet enabled; use factory=False" + logger.critical(error) + raise InvalidParameters(error) + + if tmp_lines is None: + error = f"_handle_ccp_syntax(tmp_lines={tmp_lines}) must not be None" + logger.error(error) + raise InvalidParameters(error) + + if syntax in ALL_VALID_SYNTAX: config_lines = tmp_lines + else: + error = f"_handle_ccp_syntax(syntax=`{syntax}`) is not yet supported" + logger.error(error) + raise InvalidParameters(error) return config_lines @@ -4909,7 +4945,7 @@ def insert_before(self, exist_val=None, new_val=None, atomic=False): ) elif self.factory is True: - new_obj = ConfigLineFactory( + new_obj = config_line_factory( text=new_val, comment_delimiter=self.comment_delimiter, syntax=self.syntax, @@ -5029,7 +5065,7 @@ def insert_after(self, exist_val=None, new_val=None, atomic=False, new_val_inden ) elif self.factory is True: - new_obj = ConfigLineFactory( + new_obj = config_line_factory( text=new_val, comment_delimiter=self.comment_delimiter, syntax=self.syntax, @@ -5068,7 +5104,7 @@ def insert(self, ii, val): # Coerce a string into the appropriate object if getattr(val, "capitalize", False): if self.factory: - obj = ConfigLineFactory( + obj = config_line_factory( text=val, comment_delimiter=self.comment_delimiter, syntax=self.syntax, @@ -5342,6 +5378,7 @@ def _bootstrap_obj_init_ng(self, text_list=None, debug=0): # Assign a custom *CfgLine() based on factory... obj = _cfgobj_from_text( + text_list, txt=txt, idx=idx, syntax=syntax, @@ -5662,40 +5699,52 @@ def decrypt(self, ep=""): @logger.catch(reraise=True) -def ConfigLineFactory(text="", comment_delimiter="!", syntax="ios"): - """A factory method to assign a custom *CfgLine() object based on the contents of the input text parameter and input syntax parameter.""" +def config_line_factory(all_lines=None, line=None, comment_delimiter="!", syntax="ios"): + """A factory method to assign a custom BaseCfgLine() subclass based on `all_lines`, `line`, `comment_delimiter`, and `syntax` parameters.""" # Complicted & Buggy # classes = [j for (i,j) in globals().iteritems() if isinstance(j, TypeType) and issubclass(j, BaseCfgLine)] + if not isinstance(all_lines, list): + error = f"config_line_factory(all_lines=`{all_lines}`) must be a list, but we got {type(all_lines)}" + logger.error(error) + raise InvalidParameters(error) - ## Manual and simple - if syntax == "ios": - classes = ALL_IOS_FACTORY_CLASSES - elif syntax == "nxos": - classes = ALL_NXOS_FACTORY_CLASSES - elif syntax == "asa": - classes = ALL_ASA_FACTORY_CLASSES - elif syntax == "junos": - classes = ALL_JUNOS_FACTORY_CLASSES + if not isinstance(line, str): + error = f"config_line_factory(text=`{line}`) must be a string, but we got {type(line)}" + logger.error(error) + raise InvalidParameters(error) - else: - err_txt = "'{}' is an unknown syntax".format(syntax) - logger.error(err_txt) - raise ValueError(err_txt) + if not isinstance(comment_delimiter, str): + error = f"config_line_factory(comment_delimiter=`{comment_delimiter}`) must be a string, but we got {type(comment_delimiter)}" + logger.error(error) + raise InvalidParameters(error) + + if not isinstance(syntax, str): + error = f"config_line_factory(syntax=`{syntax}`) must be a string, but we got {type(syntax)}" + logger.error(error) + raise InvalidParameters(error) + + if syntax not in ALL_VALID_SYNTAX: + error = f"`{syntax}` is an unknown syntax" + logger.error(error) + raise ValueError(error) # Walk all the classes and return the first class that # matches `.is_object_for(text)`. try: - for cls in classes: - if cls.is_object_for(text): - inst = cls( - text=text, + for cls in ALL_IOS_FACTORY_CLASSES: + print(f" Consider config_line_factory() CLASS {cls}") + if cls.is_object_for(all_lines=all_lines, line=line): + basecfgline_subclass = cls( + all_lines=all_lines, line=line, comment_delimiter=comment_delimiter, ) # instance of the proper subclass - return inst + return basecfgline_subclass except ValueError: - err_txt = "Could not find an object for '%s'" % text - logger.error(err_txt) - raise ValueError(err_txt) + error = f"ciscoconfparse.py config_line_factory(all_lines={all_lines}, line=`{line}`, comment_delimiter=`{comment_delimiter}`, syntax=`{syntax}`) could not find a subclass of BaseCfgLine()" + logger.error(error) + raise ValueError(error) + + return IOSCfgLine(all_lines=all_lines, line=line, comment_delimiter=comment_delimiter) ### TODO: Add unit tests below if __name__ == "__main__": diff --git a/ciscoconfparse/models_all.py b/ciscoconfparse/models_all.py index 080a532..7cae699 100644 --- a/ciscoconfparse/models_all.py +++ b/ciscoconfparse/models_all.py @@ -112,7 +112,7 @@ def __repr__(self): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): ## Default object, for now raise NotImplementedError() @@ -2144,7 +2144,7 @@ def __init__(self, *args, **kwargs): # This method is on IOSIntfLine() @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): intf_regex = re.search(r"^interface\s+(?P\S+.+)", line.strip()) if isinstance(intf_regex, re.Match): interface = intf_regex.groupdict()["interface"] @@ -2469,7 +2469,7 @@ def __init__(self, *args, **kwargs): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search(r"^aaa\sgroup\sserver", line): return True return False @@ -2526,7 +2526,7 @@ def __init__(self, *args, **kwargs): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search(r"^aaa\sauthentication\slogin", line): return True return False @@ -2551,7 +2551,7 @@ def __init__(self, *args, **kwargs): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search(r"^aaa\sauthentication\senable", line): return True return False @@ -2577,7 +2577,7 @@ def __init__(self, *args, **kwargs): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search(r"^aaa\sauthorization\scommands", line): return True return False @@ -2604,7 +2604,7 @@ def __init__(self, *args, **kwargs): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search(r"^aaa\saccounting\scommands", line): return True return False @@ -2630,7 +2630,7 @@ def __init__(self, *args, **kwargs): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search(r"^aaa\saccounting\sexec", line): return True return False diff --git a/ciscoconfparse/models_asa.py b/ciscoconfparse/models_asa.py index b47964e..20ca1ee 100644 --- a/ciscoconfparse/models_asa.py +++ b/ciscoconfparse/models_asa.py @@ -94,7 +94,7 @@ def __init__(self, *args, **kwargs): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): ## Default object, for now return True @@ -220,7 +220,7 @@ def verbose(self): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): return False ##------------- Basic interface properties @@ -554,7 +554,7 @@ def __init__(self, *args, **kwargs): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if "name " in line[0:5].lower(): return True return False @@ -585,7 +585,7 @@ def __init__(self, *args, **kwargs): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if "object network " in line[0:15].lower(): return True return False @@ -605,7 +605,7 @@ def __init__(self, *args, **kwargs): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if "object service " in line[0:15].lower(): return True return False @@ -636,7 +636,7 @@ def __init__(self, *args, **kwargs): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if "object-group network " in line[0:21].lower(): return True return False @@ -770,7 +770,7 @@ def __init__(self, *args, **kwargs): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if "object-group service " in line[0:21].lower(): return True return False @@ -873,7 +873,7 @@ def __init__(self, *args, **kwargs): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): intf_regex = r"^interface\s+(\S+.+)" if re.search(intf_regex, line): return True @@ -897,7 +897,7 @@ def __repr__(self): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search("^mtu", line): return True return False @@ -920,7 +920,7 @@ def __repr__(self): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search("^hostname", line): return True return False @@ -968,7 +968,7 @@ def routeinfo(self): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): return False @property @@ -1019,7 +1019,7 @@ def __init__(self, *args, **kwargs): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search(r"^(ip|ipv6)\s+route\s+\S", line): return True return False @@ -1267,7 +1267,7 @@ def __init__(self, *args, **kwargs): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): # if _RE_ACLOBJECT.search(line): if "access-list " in line[0:12].lower(): return True diff --git a/ciscoconfparse/models_cisco.py b/ciscoconfparse/models_cisco.py index 2f3624f..bf13e26 100644 --- a/ciscoconfparse/models_cisco.py +++ b/ciscoconfparse/models_cisco.py @@ -517,15 +517,96 @@ class IOSCfgLine(BaseCfgLine): @logger.catch(reraise=True) def __init__(self, *args, **kwargs): - r"""Accept an IOS line number and initialize family relationship - attributes""" + r"""Accept an IOS line number and initialize family relationship attributes""" super(IOSCfgLine, self).__init__(*args, **kwargs) @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): + """Return True if this object should be used for a given configuration line; otherwise return False""" ## Default object, for now - return True + if cls.is_object_for_hostname(line=line): + return False + elif cls.is_object_for_interface(line=line): + return False + elif cls.is_object_for_aaa_authentication(line=line): + return False + elif cls.is_object_for_aaa_authorization(line=line): + return False + elif cls.is_object_for_aaa_accounting(line=line): + return False + elif cls.is_object_for_ip_route(line=line): + return False + elif cls.is_object_for_ipv6_route(line=line): + return False + else: + return True + + @classmethod + @logger.catch(reraise=True) + def is_object_for_hostname(cls, line): + if isinstance(line, str): + line_parts = line.strip().split() + if len(line_parts) > 0 and line_parts[0]=="hostname": + return True + return False + + @classmethod + @logger.catch(reraise=True) + def is_object_for_interface(cls, line): + if isinstance(line, str): + line_parts = line.strip().split() + if len(line_parts) > 0 and line_parts[0]=="interface": + return True + return False + + @classmethod + @logger.catch(reraise=True) + def is_object_for_aaa_authentication(cls, line): + """Return True if this is an object for aaa authentication. Be sure to reject 'aaa new-model'""" + if isinstance(line, str): + line_parts = line.strip().split() + if len(line_parts) > 0 and line_parts[0:2]==["aaa", "authentication"]: + return True + return False + + @classmethod + @logger.catch(reraise=True) + def is_object_for_aaa_authorization(cls, line): + """Return True if this is an object for aaa authorization. Be sure to reject 'aaa new-model'""" + if isinstance(line, str): + line_parts = line.strip().split() + if len(line_parts) > 0 and line_parts[0:2]==["aaa", "authorization"]: + return True + return False + + @classmethod + @logger.catch(reraise=True) + def is_object_for_aaa_accounting(cls, line): + """Return True if this is an object for aaa accounting. Be sure to reject 'aaa new-model'""" + if isinstance(line, str): + line_parts = line.strip().split() + if len(line_parts) > 0 and line_parts[0:2]==["aaa", "accounting"]: + return True + return False + + @classmethod + @logger.catch(reraise=True) + def is_object_for_ip_route(cls, line): + if isinstance(line, str): + line_parts = line.strip().split() + if len(line_parts) > 0 and line_parts[0:2]==["ip", "route"]: + return True + return False + + @classmethod + @logger.catch(reraise=True) + def is_object_for_ipv6_route(cls, line): + if isinstance(line, str): + line_parts = line.strip().split() + if len(line_parts) > 0 and line_parts[0:2]==["ipv6", "route"]: + return True + return False @property @logger.catch(reraise=True) @@ -872,7 +953,7 @@ def verbose(self): # This method is on BaseIOSIntfLine() @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): return False ##------------- Basic interface properties @@ -2526,20 +2607,15 @@ def __init__(self, *args, **kwargs): # This method is on IOSIntfLine() @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): - intf_regex = re.search(r"^interface\s+(?P\S+.+)", line.strip()) - if isinstance(intf_regex, re.Match): - interface = intf_regex.groupdict()["interface"] - return True - else: - return False + def is_object_for(cls, all_lines, line, re=re): + return cls.is_object_for_interface(line) ## ##------------- IOS Interface Globals ## -class IOSIntfGlobal(BaseCfgLine): +class IOSIntfGlobal(IOSCfgLine): # This method is on IOSIntGlobal() @logger.catch(reraise=True) def __init__(self, *args, **kwargs): @@ -2553,7 +2629,7 @@ def __repr__(self): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search( r"^(no\s+cdp\s+run)|(logging\s+event\s+link-status\s+global)|(spanning-tree\sportfast\sdefault)|(spanning-tree\sportfast\sbpduguard\sdefault)", line, @@ -2602,7 +2678,7 @@ def has_stp_mode_rapidpvst(self): ## -class IOSHostnameLine(BaseCfgLine): +class IOSHostnameLine(IOSCfgLine): @logger.catch(reraise=True) def __init__(self, *args, **kwargs): super(IOSHostnameLine, self).__init__(*args, **kwargs) @@ -2614,7 +2690,7 @@ def __repr__(self): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search(r"^hostname", line): return True return False @@ -2631,7 +2707,7 @@ def hostname(self): ## -class IOSAccessLine(BaseCfgLine): +class IOSAccessLine(IOSCfgLine): @logger.catch(reraise=True) def __init__(self, *args, **kwargs): super(IOSAccessLine, self).__init__(*args, **kwargs) @@ -2648,7 +2724,7 @@ def __repr__(self): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search(r"^line", line): return True return False @@ -2722,7 +2798,7 @@ def parse_exectimeout(self): ## -class BaseIOSRouteLine(BaseCfgLine): +class BaseIOSRouteLine(IOSCfgLine): @logger.catch(reraise=True) def __init__(self, *args, **kwargs): super(BaseIOSRouteLine, self).__init__(*args, **kwargs) @@ -2753,7 +2829,7 @@ def routeinfo(self): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): return False @property @@ -2841,7 +2917,7 @@ def tracking_object_name(self): ) -class IOSRouteLine(BaseIOSRouteLine): +class IOSRouteLine(IOSCfgLine): @logger.catch(reraise=True) def __init__(self, *args, **kwargs): super(IOSRouteLine, self).__init__(*args, **kwargs) @@ -2864,8 +2940,8 @@ def __init__(self, *args, **kwargs): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): - if (line[0:8] == "ip route") or (line[0:11] == "ipv6 route "): + def is_object_for(cls, all_lines, line, re=re): + if (line[0:9] == "ip route ") or (line[0:11] == "ipv6 route "): return True return False @@ -3056,7 +3132,7 @@ def tag(self): ## ##------------- IOS TACACS+ Group ## -class IOSAaaGroupServerLine(BaseCfgLine): +class IOSAaaGroupServerLine(IOSCfgLine): @logger.catch(reraise=True) def __init__(self, *args, **kwargs): super(IOSAaaGroupServerLine, self).__init__(*args, **kwargs) @@ -3073,7 +3149,7 @@ def __init__(self, *args, **kwargs): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search(r"^aaa\sgroup\sserver", line): return True return False @@ -3114,7 +3190,7 @@ def server_private(self, re=re): ##------------- IOS AAA Login Authentication Lines ## -class IOSAaaLoginAuthenticationLine(BaseCfgLine): +class IOSAaaLoginAuthenticationLine(IOSCfgLine): @logger.catch(reraise=True) def __init__(self, *args, **kwargs): super(IOSAaaLoginAuthenticationLine, self).__init__(*args, **kwargs) @@ -3130,8 +3206,8 @@ def __init__(self, *args, **kwargs): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): - if re.search(r"^aaa\sauthentication\slogin", line): + def is_object_for(cls, all_lines, line, re=re): + if re.search(r"^aaa\s+authentication\s+login", line.strip()): return True return False @@ -3139,13 +3215,13 @@ def is_object_for(cls, line="", re=re): ##------------- IOS AAA Enable Authentication Lines ## -class IOSAaaEnableAuthenticationLine(BaseCfgLine): +class IOSAaaEnableAuthenticationLine(IOSCfgLine): @logger.catch(reraise=True) def __init__(self, *args, **kwargs): super(IOSAaaEnableAuthenticationLine, self).__init__(*args, **kwargs) self.feature = "aaa authentication enable" - regex = r"^aaa\sauthentication\senable\s(\S+)\sgroup\s(\S+)(.+?)$" + regex = r"^aaa\s+authentication\s+enable\s+(\S+)\s+group\s+(\S+)(.+?)$" self.list_name = self.re_match_typed( regex, group=1, result_type=str, default="" ) @@ -3155,8 +3231,8 @@ def __init__(self, *args, **kwargs): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): - if re.search(r"^aaa\sauthentication\senable", line): + def is_object_for(cls, all_lines, line, re=re): + if re.search(r"^aaa\s+authentication\s+enable", line.strip()): return True return False @@ -3164,13 +3240,40 @@ def is_object_for(cls, line="", re=re): ##------------- IOS AAA Commands Authorization Lines ## -class IOSAaaCommandsAuthorizationLine(BaseCfgLine): +class IOSAaaCommandsAuthorizationLine(IOSCfgLine): @logger.catch(reraise=True) def __init__(self, *args, **kwargs): super(IOSAaaCommandsAuthorizationLine, self).__init__(*args, **kwargs) self.feature = "aaa authorization commands" - regex = r"^aaa\sauthorization\scommands\s(\d+)\s(\S+)\sgroup\s(\S+)(.+?)$" + regex = r"^aaa\s+authorization\s+commands\s+(\d+)\s+(\S+)\s+group\s+(\S+)(.+?)$" + self.level = self.re_match_typed(regex, group=1, result_type=int, default=0) + self.list_name = self.re_match_typed( + regex, group=2, result_type=str, default="" + ) + self.group = self.re_match_typed(regex, group=3, result_type=str, default="") + methods_str = self.re_match_typed(regex, group=4, result_type=str, default="") + self.methods = methods_str.strip().split(r"\s") + + @classmethod + @logger.catch(reraise=True) + def is_object_for(cls, all_lines, line, re=re): + print(f"DBG IOSAaaCommandsAuthorizationLine {line}") + if re.search(r"^aaa\s+authorization\s+commands", line.strip()): + return True + return False + +## +##------------- IOS AAA Console Authorization Lines +## + +class IOSAaaConsoleAuthorizationLine(IOSCfgLine): + @logger.catch(reraise=True) + def __init__(self, *args, **kwargs): + super(IOSAaaConsoleAuthorizationLine, self).__init__(*args, **kwargs) + self.feature = "aaa authorization console" + + regex = r"^aaa\s+authorization\s+console\s+(\d+)\s+(\S+)\s+group\s+(\S+)(.+?)$" self.level = self.re_match_typed(regex, group=1, result_type=int, default=0) self.list_name = self.re_match_typed( regex, group=2, result_type=str, default="" @@ -3181,8 +3284,9 @@ def __init__(self, *args, **kwargs): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): - if re.search(r"^aaa\sauthorization\scommands", line): + def is_object_for(cls, all_lines, line, re=re): + print(f"DBG IOSAaaConsoleAuthorizationLine {line}") + if re.search(r"^aaa\s+authorization\s+console", line.strip()): return True return False @@ -3190,13 +3294,13 @@ def is_object_for(cls, line="", re=re): ##------------- IOS AAA Commands Accounting Lines ## -class IOSAaaCommandsAccountingLine(BaseCfgLine): +class IOSAaaCommandsAccountingLine(IOSCfgLine): @logger.catch(reraise=True) def __init__(self, *args, **kwargs): super(IOSAaaCommandsAccountingLine, self).__init__(*args, **kwargs) self.feature = "aaa accounting commands" - regex = r"^aaa\saccounting\scommands\s(\d+)\s(\S+)\s(none|stop\-only|start\-stop)\sgroup\s(\S+)$" + regex = r"^aaa\s+accounting\s+commands\s+(\d+)\s+(\S+)\s+(none|stop\-only|start\-stop)\s+group\s+(\S+)$" self.level = self.re_match_typed(regex, group=1, result_type=int, default=0) self.list_name = self.re_match_typed( regex, group=2, result_type=str, default="" @@ -3208,8 +3312,8 @@ def __init__(self, *args, **kwargs): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): - if re.search(r"^aaa\saccounting\scommands", line): + def is_object_for(cls, all_lines, line, re=re): + if re.search(r"^aaa\s+accounting\s+commands", line.strip()): return True return False @@ -3217,13 +3321,13 @@ def is_object_for(cls, line="", re=re): ##------------- IOS AAA Exec Accounting Lines ## -class IOSAaaExecAccountingLine(BaseCfgLine): +class IOSAaaExecAccountingLine(IOSCfgLine): @logger.catch(reraise=True) def __init__(self, *args, **kwargs): super(IOSAaaExecAccountingLine, self).__init__(*args, **kwargs) self.feature = "aaa accounting exec" - regex = r"^aaa\saccounting\sexec\s(\S+)\s(none|stop\-only|start\-stop)\sgroup\s(\S+)$" + regex = r"^aaa\s+accounting\s+exec\s+(\S+)\s+(none|stop\-only|start\-stop)\s+group\s(\S+)$" self.list_name = self.re_match_typed( regex, group=1, result_type=str, default="" ) @@ -3234,7 +3338,7 @@ def __init__(self, *args, **kwargs): @classmethod @logger.catch(reraise=True) - def is_object_for(cls, line="", re=re): - if re.search(r"^aaa\saccounting\sexec", line): + def is_object_for(cls, all_lines, line, re=re): + if re.search(r"^aaa\s+accounting\s+exec", line.strip()): return True return False diff --git a/ciscoconfparse/models_iosxr.py b/ciscoconfparse/models_iosxr.py index fa436c6..b20b48d 100644 --- a/ciscoconfparse/models_iosxr.py +++ b/ciscoconfparse/models_iosxr.py @@ -105,7 +105,7 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): ## Default object, for now return True @@ -430,7 +430,7 @@ def verbose(self): ) @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): return False ##------------- Basic interface properties @@ -1740,7 +1740,7 @@ def __init__(self, *args, **kwargs): self.feature = "interface" @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): intf_regex = r"^interface\s+(\S+.+)" if re.search(intf_regex, line): return True @@ -1761,7 +1761,7 @@ def __repr__(self): return "<{} # {} '{}'>".format(self.classname, self.linenum, self.text) @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search( r"^(no\s+cdp\s+run)|(logging\s+event\s+link-status\s+global)|(spanning-tree\sportfast\sdefault)|(spanning-tree\sportfast\sbpduguard\sdefault)", line, @@ -1814,7 +1814,7 @@ def __repr__(self): return "<{} # {} '{}'>".format(self.classname, self.linenum, self.hostname) @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search(r"^hostname", line): return True return False @@ -1844,7 +1844,7 @@ def __repr__(self): ) @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search(r"^line", line): return True return False @@ -1936,7 +1936,7 @@ def routeinfo(self): return self.nexthop_str + " AD: " + str(self.admin_distance) @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): return False @property @@ -2038,7 +2038,7 @@ def __init__(self, *args, **kwargs): raise ValueError("Could not parse '{}'".format(self.text)) @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if (line[0:8] == "ip route") or (line[0:11] == "ipv6 route "): return True return False @@ -2230,7 +2230,7 @@ def __init__(self, *args, **kwargs): raise ValueError @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search(r"^aaa\sgroup\sserver", line): return True return False @@ -2282,7 +2282,7 @@ def __init__(self, *args, **kwargs): self.methods = methods_str.strip().split(r"\s") @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search(r"^aaa\sauthentication\slogin", line): return True return False @@ -2306,7 +2306,7 @@ def __init__(self, *args, **kwargs): self.methods = methods_str.strip().split(r"\s") @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search(r"^aaa\sauthentication\senable", line): return True return False @@ -2330,7 +2330,7 @@ def __init__(self, *args, **kwargs): self.methods = methods_str.strip().split(r"\s") @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search(r"^aaa\sauthorization\scommands", line): return True return False @@ -2355,7 +2355,7 @@ def __init__(self, *args, **kwargs): self.group = self.re_match_typed(regex, group=4, result_type=str, default="") @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search(r"^aaa\saccounting\scommands", line): return True return False @@ -2380,7 +2380,7 @@ def __init__(self, *args, **kwargs): self.group = self.re_match_typed(regex, group=3, result_type=str, default="") @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search(r"^aaa\saccounting\sexec", line): return True return False diff --git a/ciscoconfparse/models_junos.py b/ciscoconfparse/models_junos.py index 3a7be21..f8e369c 100644 --- a/ciscoconfparse/models_junos.py +++ b/ciscoconfparse/models_junos.py @@ -91,15 +91,14 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): ## Default object, for now return True @property def is_intf(self): # Includes subinterfaces - r"""Returns a boolean (True or False) to answer whether this - :class:`~models_junos.JunosCfgLine` is an interface; subinterfaces + r"""Returns a boolean (True or False) to answer whether this :class:`~models_junos.JunosCfgLine` is an interface; subinterfaces also return True. Returns @@ -110,34 +109,42 @@ def is_intf(self): -------- .. code-block:: python - :emphasize-lines: 17,20 >>> config = [ - ... '!', - ... 'interface Serial1/0', - ... ' ip address 1.1.1.1 255.255.255.252', - ... '!', - ... 'interface ATM2/0', - ... ' no ip address', - ... '!', - ... 'interface ATM2/0.100 point-to-point', - ... ' ip address 1.1.1.5 255.255.255.252', - ... ' pvc 0/100', - ... ' vbr-nrt 704 704', - ... '!', + ... "interfaces {", + ... " ge-0/0/0 {", + ... " unit 0 {", + ... " }", + ... " }", + ... "}", ... ] >>> parse = CiscoConfParse(config) - >>> obj = parse.find_objects('^interface\sSerial')[0] + >>> obj = parse.find_objects('^\s+ge-0.0.0')[0] >>> obj.is_intf True - >>> obj = parse.find_objects('^interface\sATM')[0] + >>> obj = parse.find_objects('^\s+unit\s0')[0] >>> obj.is_intf True >>> """ - intf_regex = r"^interface\s+(\S+.+)" - if self.re_match(intf_regex): - return True + # Check whether the oldest parent is "interfaces {"... + if len(self.all_parents) >= 1: + in_intf_block = bool(self.all_parents[0].text[0:10].strip()=="interfaces") + else: + in_intf_block = False + + if in_intf_block: + # Walk all children of interfaces {...} + for childobj in self.parent.children: + # We are in the children of the interface or unit number... + if len(self.all_parents) > 2: + continue + elif childobj.text == self.text: + return True + else: + for grandcobj in childobj.children: + if grandcobj.text.strip()[0:5]=="unit": + return True return False @property @@ -346,7 +353,7 @@ def verbose(self): ) @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): return False ##------------- Basic interface properties @@ -558,7 +565,7 @@ def __repr__(self): return "<{} # {} '{}'>".format(self.classname, self.linenum, self.text) @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search( r"^(no\s+cdp\s+run)|(logging\s+event\s+link-status\s+global)|(spanning-tree\sportfast\sdefault)|(spanning-tree\sportfast\sbpduguard\sdefault)", line, @@ -611,7 +618,7 @@ def __repr__(self): return "<{} # {} '{}'>".format(self.classname, self.linenum, self.hostname) @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search("^hostname", line): return True return False @@ -654,7 +661,7 @@ def routeinfo(self): return self.nexthop_str + " AD: " + str(self.admin_distance) @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): return False @property @@ -701,7 +708,7 @@ def __init__(self, *args, **kwargs): self.feature = "ip route" @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search(r"^(ip|ipv6)\s+route\s+\S", line): return True return False diff --git a/ciscoconfparse/models_nxos.py b/ciscoconfparse/models_nxos.py index a7bbf1a..39d0c48 100644 --- a/ciscoconfparse/models_nxos.py +++ b/ciscoconfparse/models_nxos.py @@ -83,7 +83,7 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): ## Default object, for now return True @@ -387,7 +387,7 @@ def verbose(self): ) @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): return False ##------------- Basic interface properties @@ -1727,7 +1727,7 @@ def __init__(self, *args, **kwargs): self.feature = "interface" @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search(r"^interface\s+(\S.+)", line): return True return False @@ -1747,7 +1747,7 @@ def __repr__(self): return "<{} # {} '{}'>".format(self.classname, self.linenum, self.text) @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search( r"^(no\s+cdp\s+run)|(logging\s+event\s+link-status\s+global)|(spanning-tree\sportfast\sdefault)|(spanning-tree\sportfast\sbpduguard\sdefault)", line, @@ -1792,7 +1792,7 @@ def __repr__(self): return "<{} # {} '{}'>".format(self.classname, self.linenum, self.vpc_domain_id) @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search(r"^vpc\s+domain", line): return True return False @@ -1941,7 +1941,7 @@ def __repr__(self): return "<{} # {} '{}'>".format(self.classname, self.linenum, self.hostname) @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search("^hostname", line): return True return False @@ -1971,7 +1971,7 @@ def __repr__(self): ) @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search("^line", line): return True return False @@ -2063,7 +2063,7 @@ def routeinfo(self): return self.nexthop_str + " AD: " + str(self.admin_distance) @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): return False @property @@ -2159,7 +2159,7 @@ def __init__(self, *args, **kwargs): raise ValueError("Could not parse '{}'".format(self.text)) @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if (line[0:8] == "ip route") or (line[0:11] == "ipv6 route "): return True return False @@ -2296,7 +2296,7 @@ def __init__(self, *args, **kwargs): raise ValueError @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search(r"^aaa\sgroup\sserver", line): return True return False @@ -2344,7 +2344,7 @@ def __init__(self, *args, **kwargs): self.methods = methods_str.strip().split(r"\s") @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search(r"^aaa\sauthentication\slogin", line): return True return False @@ -2367,7 +2367,7 @@ def __init__(self, *args, **kwargs): self.methods = methods_str.strip().split(r"\s") @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search(r"^aaa\sauthentication\senable", line): return True return False @@ -2391,11 +2391,35 @@ def __init__(self, *args, **kwargs): self.methods = methods_str.strip().split(r"\s") @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search(r"^aaa\sauthorization\scommands", line): return True return False +## +##------------- NXOS AAA Console Authorization Lines +## + +class NXOSAaaConsoleAuthorizationLine(BaseCfgLine): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.feature = "aaa authorization console" + + regex = r"^aaa\s+authorization\s+console\s+(\d+)\s(\S+)\s+group\s+(\S+)(.+?)$" + self.level = self.re_match_typed(regex, group=1, result_type=int, default=0) + self.list_name = self.re_match_typed( + regex, group=2, result_type=str, default="" + ) + self.group = self.re_match_typed(regex, group=3, result_type=str, default="") + methods_str = self.re_match_typed(regex, group=4, result_type=str, default="") + self.methods = methods_str.strip().split(r"\s") + + @classmethod + def is_object_for(cls, all_lines, line, re=re): + if re.search(r"^aaa\s+authorization\s+console", line.strip()): + return True + return False + ## ##------------- NXOS AAA Commands Accounting Lines ## @@ -2416,7 +2440,7 @@ def __init__(self, *args, **kwargs): self.group = self.re_match_typed(regex, group=4, result_type=str, default="") @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search(r"^aaa\saccounting\scommands", line): return True return False @@ -2440,7 +2464,7 @@ def __init__(self, *args, **kwargs): self.group = self.re_match_typed(regex, group=3, result_type=str, default="") @classmethod - def is_object_for(cls, line="", re=re): + def is_object_for(cls, all_lines, line, re=re): if re.search(r"^aaa\saccounting\sexec", line): return True return False