Skip to content

Commit

Permalink
Add ciscoconfparse2 release info
Browse files Browse the repository at this point in the history
  • Loading branch information
mpenning committed Dec 14, 2023
1 parent e9b0135 commit cef3696
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 63 deletions.
7 changes: 7 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@
- Summary:
- Insert something here

## Version: 1.9.51

- Released: 2023-12-01
- Summary:
- Modify `ConfigList().append()` to use `BaseCfgLine()` instances
- Fix manual `indent` in `ccp_abc.BaseCfgLine().append_to_family()`

## Version: 1.9.50

- Released: 2023-12-01
Expand Down
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,41 @@

[![Snyk Package Health][37]][38]

# Important: ciscoconfparse2

As of December 14, 2023 [ciscoconfparse2][64] is released; this is
equivalent to version 2.0 of `ciscoconfparse`, but [ciscoconfparse2][64] is
a different [PYPI project][65].

You should upgrade; here's why, [ciscoconfparse2][64]:

- Streamlines the API towards a simpler user interface.
- Removes legacy and flawed methods from the original (this could be a breaking change for old scripts).
- Defaults `ignore_blank_lines=False` (this could be a breaking change for old scripts).
- Adds string methods to `BaseCfgLine()` objects
- Is better at handling multiple-child-level configurations (such as IOS XR and JunOS)
- Can search for parents and children using an *arbitrary list of ancestors*
- Adds the concept of change commits; this is a config-modification safety feature that [ciscoconfparse][64] lacks
- Adds an `auto_commit` keyword, which defaults True
- Documents much more of the API
- Intentionally requires a different import statement to minimize confusion between the original and [ciscoconfparse2][17]
- Vast improvements to Cisco IOS diffs

There is one key point above; [ciscoconfparse2][64] removes old APIs and thus introduces breaking changes to old scripts. That said,
[ciscoconfparse2][64] is worth the upgrade if you are involved in non-trivial [ciscoconfparse][17] development.

After heavy work on the new `CiscoConfParse()`, these APIs remain:

- [`CiscoConfParse().find_objects()`](http://www.pennington.net/py/ciscoconfparse2/api_CiscoConfParse.html#ciscoconfparse2.CiscoConfParse.find_objects)
- [`CiscoConfParse().find_parent_objects()`](http://www.pennington.net/py/ciscoconfparse2/api_CiscoConfParse.html#ciscoconfparse2.CiscoConfParse.find_parent_objects)
- [`CiscoConfParse().find_parent_objects_wo_child()`](http://www.pennington.net/py/ciscoconfparse2/api_CiscoConfParse.html#ciscoconfparse2.CiscoConfParse.find_parent_objects_wo_child)
- [`CiscoConfParse().find_child_objects()`](http://www.pennington.net/py/ciscoconfparse2/api_CiscoConfParse.html#ciscoconfparse2.CiscoConfParse.find_child_objects)
- [`CiscoConfParse().find_object_branches()`](http://www.pennington.net/py/ciscoconfparse2/api_CiscoConfParse.html#ciscoconfparse2.CiscoConfParse.find_object_branches)
- [`CiscoConfParse().save_as()`](http://www.pennington.net/py/ciscoconfparse2/api_CiscoConfParse.html#ciscoconfparse2.CiscoConfParse.save_as)
- [`CiscoConfParse().commit()`](http://www.pennington.net/py/ciscoconfparse2/api_CiscoConfParse.html#ciscoconfparse2.CiscoConfParse.commit)
- [`Diff()`](http://www.pennington.net/py/ciscoconfparse2/api_CiscoConfParse.html#ciscoconfparse2.Diff)

Install with: `pip install ciscoconfparse2`.

## Introduction: What is ciscoconfparse?

Expand Down Expand Up @@ -555,3 +590,5 @@ The following are featured [CiscoConfParse](https://github.com/mpenning/ciscocon
[61]: https://sonarcloud.io/api/project_badges/measure?project=mpenning_ciscoconfparse&metric=sqale_index
[62]: https://sonarcloud.io/summary/new_code?id=mpenning_ciscoconfparse
[63]: https://docs.pytest.org/en/
[64]: https://github.com/mpenning/ciscoconfparse2
[65]: https://pypi.python.org/pypi/ciscoconfparse2/
2 changes: 1 addition & 1 deletion ciscoconfparse/ccp_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -968,7 +968,7 @@ def append_to_family(self, insertstr, indent=-1, auto_indent_width=1, auto_inden
if auto_indent:
insertstr = (" " * (insertstr_parent_indent + auto_indent_width)) + insertstr.lstrip()
elif indent > 0:
insertstr = (" " * (self.indent + indent)) + insertstr.lstrip()
insertstr = (" " * indent) + insertstr.lstrip()
else:
# do not modify insertstr
pass
Expand Down
63 changes: 56 additions & 7 deletions ciscoconfparse/ciscoconfparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -748,11 +748,17 @@ def __init__(
# tmp_lines = self._get_ccp_lines(config=config, logger=logger)
if isinstance(config, (str, pathlib.Path,)):
if ignore_blank_lines is True and factory is False:
###############################################################
# ignore blank lines under this condition
###############################################################
tmp_lines = self.read_config_file(filepath=config, linesplit_rgx=r"\r*\n+")
else:
tmp_lines = self.read_config_file(filepath=config, linesplit_rgx=r"\r*\n")
elif isinstance(config, Sequence):
if ignore_blank_lines is True and factory is False:
###############################################################
# ignore blank lines under this condition
###############################################################
tmp_lines = [ii for ii in config if len(ii) != 0]
else:
tmp_lines = config
Expand Down Expand Up @@ -785,6 +791,13 @@ def __init__(
# IMPORTANT this MUST not be a lie :-)...
self.finished_config_parse = True

# This method is on CiscoConfParse()
@property
@logger.catch(reraise=True)
def config(self):
"""``config`` is an alias for ``ConfigObjs``"""
return self.ConfigObjs

# This method is on CiscoConfParse()
@logger.catch(reraise=True)
def handle_ccp_brace_syntax(self, tmp_lines=None, syntax=None):
Expand Down Expand Up @@ -1868,8 +1881,9 @@ def find_child_objects(
_parentspec_len = len(parentspec)
if _parentspec_len > 1:
_results = set()
_parentspec = parentspec[0]
for _idx, _childspec in enumerate(parentspec[1:]):
for _idx, _ in enumerate(parentspec[0:-1]):
_parentspec = parentspec[_idx]
_childspec = parentspec[_idx + 1]
_values = self.find_child_objects(
_parentspec,
_childspec,
Expand Down Expand Up @@ -2693,14 +2707,36 @@ def __getattribute__(self, arg):
logger.warning(message)
return ccp_method

@property
@logger.catch(reraise=True)
def as_text(self):
"""Return the configuration as a list of text lines"""
return [ii.text for ii in self._list]

# This method is on ConfigList()
@ junos_unsupported
@junos_unsupported
@logger.catch(reraise=True)
def append(self, val):
if self.debug >= 1:
logger.debug(" ConfigList().append(val={}) was called.".format(val))

self._list.append(val)
if bool(self.factory) is False:
obj = CFGLINE[self.syntax](
all_lines=self.as_text,
line=val,
comment_delimiter=self.comment_delimiter,
)
else:
obj = config_line_factory(
all_lines=self.as_text,
line=val,
comment_delimiter=self.comment_delimiter,
syntax=self.syntax,
)

self._list.append(obj)
# Rebuild object relationships after appending...
self._list = self.bootstrap_obj_init_ng(self.as_text, debug=self.debug)

# This method is on ConfigList()
@logger.catch(reraise=True)
Expand All @@ -2710,7 +2746,19 @@ def pop(self, ii=-1):
# This method is on ConfigList()
@logger.catch(reraise=True)
def remove(self, val):
self._list.remove(val)

if isinstance(val, str):
idx = self.as_text.index(val)
else:
idx = self._list.index(val)

# Remove all child objects...
for obj in self._list[idx].all_children:
self._list.remove(obj)
# Remove the parent...
self._list.pop(idx)
# Rebuild the family relationships
self._list = self.bootstrap_obj_init_ng(self.as_text, debug=self.debug)

# This method is on ConfigList()
@logger.catch(reraise=True)
Expand Down Expand Up @@ -2757,6 +2805,8 @@ def extend(self, other):
else:
self._list.extend(other)

self._list = self.bootstrap_obj_init_ng(self.as_text, debug=self.debug)

# This method is on ConfigList()
@logger.catch(reraise=True)
def insert_before(self, exist_val=None, new_val=None, atomic=False):
Expand Down Expand Up @@ -3028,8 +3078,7 @@ def insert(self, ii, val):
# Insert the object at index ii
self._list.insert(ii, obj)

# Just renumber lines...
self.reassign_linenums()
self._list = self.bootstrap_obj_init_ng(self.as_text, debug=self.debug)

# This method is on ConfigList()
@logger.catch(reraise=True)
Expand Down
9 changes: 4 additions & 5 deletions sphinx-doc/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ disk... and run from the unix shell.
.. code-block:: python
#!/usr/bin/env python
print("Hello world")
Using Python in Windows
Expand Down Expand Up @@ -84,9 +83,9 @@ You can check your python version with the ``-V`` switch...

.. code-block:: none
[mpenning@Mudslide ~]$ python -V
[mpenning@Mudslide ~]# python -V
Python 3.10.13
[mpenning@Mudslide ~]$
[mpenning@Mudslide ~]#
The best way to get ciscoconfparse is with pip_ or setuptools_.

Expand All @@ -102,7 +101,7 @@ Alternatively you can install with pip_: ::
If you have a specific version of ciscoconfparse in mind, you can specify that
at the command-line ::

pip install ciscoconfparse==1.9.48
pip install ciscoconfparse==1.9.41


Install with setuptools
Expand All @@ -116,7 +115,7 @@ If you don't have pip_, you can use setuptools_... ::
If you have a specific version of ciscoconfparse in mind, you can specify that
at the command-line ::

easy_install -U ciscoconfparse==1.9.48
easy_install -U ciscoconfparse==1.9.41

Install from the source
~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
92 changes: 46 additions & 46 deletions sphinx-doc/tutorial_audit.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,25 @@ Assume that you start with the following Cisco IOS configuration saved as ``shor

.. code-block:: none
! Filename: short.conf
!
interface FastEthernet0/1
switchport mode access
switchport access vlan 532
!
interface FastEthernet0/2
switchport mode trunk
switchport trunk allowed 300,532
switchport nonegotiate
switchport port-security maximum 2
switchport port-security violation restrict
switchport port-security
!
interface FastEthernet0/3
switchport mode access
switchport access vlan 300
!
end
! Filename: short.conf
!
interface FastEthernet0/1
switchport mode access
switchport access vlan 532
!
interface FastEthernet0/2
switchport mode trunk
switchport trunk allowed 300,532
switchport nonegotiate
switchport port-security maximum 2
switchport port-security violation restrict
switchport port-security
!
interface FastEthernet0/3
switchport mode access
switchport access vlan 300
!
end
Next, we build this script to read and change the config:

Expand Down Expand Up @@ -96,33 +96,33 @@ After the script runs, the new configuration (``short.conf.new``) looks like thi

.. code-block:: python
service timestamps log datetime msec localtime show-timezone
service timestamps debug datetime msec localtime show-timezone
!
interface FastEthernet0/1
switchport mode access
switchport access vlan 532
storm-control broadcast level 0.4 0.3
storm-control action trap
!
interface FastEthernet0/2
switchport mode trunk
switchport trunk allowed 300,532
switchport nonegotiate
!
interface FastEthernet0/3
switchport mode access
switchport access vlan 300
storm-control broadcast level 0.4 0.3
storm-control action trap
!
interface FastEthernet0/4
switchport
switchport mode access
storm-control broadcast level 0.4 0.3
storm-control action trap
!
end
service timestamps log datetime msec localtime show-timezone
service timestamps debug datetime msec localtime show-timezone
!
interface FastEthernet0/1
switchport mode access
switchport access vlan 532
storm-control broadcast level 0.4 0.3
storm-control action trap
!
interface FastEthernet0/2
switchport mode trunk
switchport trunk allowed 300,532
switchport nonegotiate
!
interface FastEthernet0/3
switchport mode access
switchport access vlan 300
storm-control broadcast level 0.4 0.3
storm-control action trap
!
interface FastEthernet0/4
switchport
switchport mode access
storm-control broadcast level 0.4 0.3
storm-control action trap
!
end
The script:

Expand Down
1 change: 0 additions & 1 deletion sphinx-doc/tutorial_parent_child.rst
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ This example:
Config text: interface Serial1/0
>>>
>>> quit()
[mpenning@tsunami ~]$
In the example, ``obj.text`` refers to the :class:`~models_cisco.IOSCfgLine`
``text`` attribute, which retrieves the text of the original IOS configuration
Expand Down
9 changes: 6 additions & 3 deletions tests/performance_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
import sys
import os


THIS_DIR = os.path.dirname(__file__)
# sys.path.insert(0, os.path.join(os.path.abspath(THIS_DIR), "../ciscoconfparse/"))
sys.path.insert(0, "..")

from loguru import logger
from ciscoconfparse import CiscoConfParse

def parse_cli_args(sys_argv1):
Expand Down Expand Up @@ -53,7 +55,8 @@ def parse_cli_args(sys_argv1):
args = parser.parse_args()
return args

if __name__=="__main__":

if __name__ == "__main__":
args = parse_cli_args(sys.argv[1:])
if any([args.small, args.acls, args.vlans]):
time.sleep(0.25)
Expand All @@ -66,6 +69,6 @@ def parse_cli_args(sys_argv1):
"CiscoConfParse('fixtures/configs/sample_06.ios', syntax='ios', factory=True)", sort=2
)
else:
error = f"Could not find a valid argument in {args}"
logger.error(error)
error = "CLI arguments are required; use -h for help"
logger.critical(error)
raise ValueError(error)

0 comments on commit cef3696

Please sign in to comment.