Hello community,
here is the log from the commit of package python-ciscoconfparse for
openSUSE:Factory checked in at 2019-03-28 22:48:21
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-ciscoconfparse (Old)
and /work/SRC/openSUSE:Factory/.python-ciscoconfparse.new.25356 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-ciscoconfparse"
Thu Mar 28 22:48:21 2019 rev:6 rq:688723 version:1.3.32
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-ciscoconfparse/python-ciscoconfparse.changes
2018-08-10 09:49:44.298266105 +0200
+++
/work/SRC/openSUSE:Factory/.python-ciscoconfparse.new.25356/python-ciscoconfparse.changes
2019-03-28 22:48:26.995057314 +0100
@@ -1,0 +2,27 @@
+Tue Mar 26 13:28:11 UTC 2019 - Tomáš Chvátal <[email protected]>
+
+- Update to 1.3.32:
+ * No good changelog
+- Remove unused patch ciscoconfparse-1.2.40-fix-tests.patch
+
+-------------------------------------------------------------------
+Wed Mar 6 19:57:31 UTC 2019 - Tomáš Chvátal <[email protected]>
+
+- Update to 1.3.30:
+ * Fix bugs related to Python3 (Github issue #117)
+ * Add IP helper-address parsing in models_cisco.py
+ * Revert universal wheel packages (universal=0)
+ * Build universal wheel packages
+ * Build improvements - ref Github issue #127, #128
+ * Another swing at Github issue #127
+ * Rollback fix for Github issue #127
+ * Attempt to fix Github issue #127
+ * Fix Github issue #124-126 and Github issue #110
+ * Fix Github issue #121 and Github issue #123
+ * Fix Github issue #114 (Py3.5 requires different open() syntax)
+ * Fix Github issue #111 (banner parsing broken in some cases)
+ * Add * to MANIFEST.in
+ * Attempt to resolve Github issue #106
+ * Add dns_query() zone transfer as text
+
+-------------------------------------------------------------------
Old:
----
ciscoconfparse-1.2.40-fix-tests.patch
ciscoconfparse-1.3.15.tar.gz
New:
----
ciscoconfparse-1.3.32.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-ciscoconfparse.spec ++++++
--- /var/tmp/diff_new_pack.Gh7tzQ/_old 2019-03-28 22:48:27.823057165 +0100
+++ /var/tmp/diff_new_pack.Gh7tzQ/_new 2019-03-28 22:48:27.827057164 +0100
@@ -1,7 +1,7 @@
#
# spec file for package python-ciscoconfparse
#
-# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany.
+# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany.
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -12,30 +12,27 @@
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.
-# Please submit bugfixes or comments via http://bugs.opensuse.org/
+# Please submit bugfixes or comments via https://bugs.opensuse.org/
#
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
Name: python-ciscoconfparse
-Version: 1.3.15
+Version: 1.3.32
Release: 0
-Summary: Python library for parsing, querying and modifying Cisco
IOS-style configurations
+Summary: Library for parsing, querying and modifying Cisco IOS-style
configurations
License: GPL-3.0-or-later
Group: Development/Languages/Python
URL: https://github.com/mpenning/ciscoconfparse
Source:
https://files.pythonhosted.org/packages/source/c/ciscoconfparse/ciscoconfparse-%{version}.tar.gz
-# PATCH fix tests
-Patch0: ciscoconfparse-1.2.40-fix-tests.patch
BuildRequires: %{python_module colorama}
BuildRequires: %{python_module dnspython}
-BuildRequires: %{python_module setuptools}
-BuildRequires: python2-ipaddress
-BuildRequires: python2-mock
-# For tests
BuildRequires: %{python_module pytest}
+BuildRequires: %{python_module setuptools}
BuildRequires: fdupes
BuildRequires: python-rpm-macros
+BuildRequires: python2-ipaddress
+BuildRequires: python2-mock
Requires: python-colorama
Requires: python-dnspython
Requires: python-ipaddr
@@ -57,7 +54,6 @@
%prep
%setup -q -n ciscoconfparse-%{version}
-%autopatch -p1
%build
%python_build
@@ -67,9 +63,8 @@
%python_expand %fdupes %{buildroot}%{$python_sitelib}
%check
-# Upstream has broken tests.
-# https://github.com/mpenning/ciscoconfparse/issues/106
-# %%python_expand PYTHONPATH=%%{buildroot}%%{$python_sitelib}
py.test-%%{$python_version}
+# Upstream stubbornly refuses PRs and does not distribute tests
+#%%pytest
%files %{python_files}
%license LICENSE
++++++ ciscoconfparse-1.3.15.tar.gz -> ciscoconfparse-1.3.32.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ciscoconfparse-1.3.15/CHANGES
new/ciscoconfparse-1.3.32/CHANGES
--- old/ciscoconfparse-1.3.15/CHANGES 2018-04-21 16:08:55.000000000 +0200
+++ new/ciscoconfparse-1.3.32/CHANGES 2019-03-17 13:46:40.000000000 +0100
@@ -1,3 +1,20 @@
+1.3.32 20190317 Fix Github issue #135
+1.3.31 20190316 Fix Github issues #131, 132, 133, 134
+1.3.30 20190218 Fix bugs related to Python3 (Github issue #117)
+1.3.29 20190207 Add IP helper-address parsing in models_cisco.py
+1.3.28 20190206 Revert universal wheel packages (universal=0)
+1.3.27 20190126 Build universal wheel packages
+1.3.26 20190126 Build improvements - ref Github issue #127, #128
+1.3.25 20190123 Another swing at Github issue #127
+1.3.24 20190123 Rollback fix for Github issue #127
+1.3.23 20190123 Attempt to fix Github issue #127
+1.3.22 20181216 Fix Github issue #124-126 and Github issue #110
+1.3.21 20181216 Fix Github issue #121 and Github issue #123
+1.3.20 20180702 Fix Github issue #114 (Py3.5 requires different open() syntax)
+1.3.19 20180623 Fix Github issue #111 (banner parsing broken in some cases)
+1.3.18 20180609 Add * to MANIFEST.in
+1.3.17 20180608 Attempt to resolve Github issue #106
+1.3.16 20180601 Add dns_query() zone transfer as text
1.3.15 20180421 Distrbution change
1.3.14 20180421 Attempt to fix unit tests
1.3.13 20180421 Fix Github issue #103, Python3 ccp_util imports
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ciscoconfparse-1.3.15/MANIFEST.in
new/ciscoconfparse-1.3.32/MANIFEST.in
--- old/ciscoconfparse-1.3.15/MANIFEST.in 2018-01-20 13:00:09.000000000
+0100
+++ new/ciscoconfparse-1.3.32/MANIFEST.in 2019-02-19 03:02:10.000000000
+0100
@@ -5,5 +5,6 @@
recursive-exclude * *.orig
recursive-exclude * BUILD.ME
recursive-exclude * BITBUCKET_HG
+recursive-include tests*
prune sphinx-doc/_static*
prune sphinx-doc/_build*
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ciscoconfparse-1.3.15/PKG-INFO
new/ciscoconfparse-1.3.32/PKG-INFO
--- old/ciscoconfparse-1.3.15/PKG-INFO 2018-04-21 16:09:21.000000000 +0200
+++ new/ciscoconfparse-1.3.32/PKG-INFO 2019-03-17 13:53:07.000000000 +0100
@@ -1,12 +1,11 @@
Metadata-Version: 1.1
Name: ciscoconfparse
-Version: 1.3.15
+Version: 1.3.32
Summary: Parse, Audit, Query, Build, and Modify Cisco IOS-style configurations
Home-page: http://www.pennington.net/py/ciscoconfparse/
Author: David Michael Pennington
Author-email: [email protected]
License: GPLv3
-Description-Content-Type: UNKNOWN
Description: ==============
ciscoconfparse
==============
@@ -83,9 +82,9 @@
Pre-requisites
==============
- ciscoconfparse_ requires Python versions 2.6, 2.7 or 3.2+; the OS
should not
- matter. If you want to run it under a Python virtualenv_, it's been
heavily
- tested in that environment as well.
+ As of version 1.3.32, ciscoconfparse_ requires Python versions 2.7 or
3.4+
+ (note: version 3.7.0 has a bug - ref Github issue #117, but version
3.7.1
+ works); the OS should not matter.
.. _Installation:
@@ -98,7 +97,7 @@
::
# Substitute whatever ciscoconfparse version you like...
- easy_install -U ciscoconfparse==1.3.14
+ easy_install -U ciscoconfparse==1.3.32
Alternatively you can install into Python2.x with pip_:
@@ -133,7 +132,7 @@
FAQ
===
- #) *QUESTION*: I want to use ciscoconfparse_ with Python3; is that
safe? *ANSWER*: As long as you're using Python 3.3 or higher, it's safe. I
test every release against Python 3.2+; however, Python 3.2 is currently
exposed to a small bug for some configurations (see `Github Issue #14`_).
+ #) *QUESTION*: I want to use ciscoconfparse_ with Python3; is that
safe? *ANSWER*: *ANSWER*: Yes.
#) *QUESTION*: Some of the code in the documentation looks different
than what I'm used to seeing. Did you change something? *ANSWER*: Yes,
starting around ciscoconfparse_ v0.9.10 I introducted more methods directly on
``IOSConfigLine()`` objects; going forward, these methods are the preferred way
to use ciscoconfparse_. Please start using the new methods shown in the
example, since they're faster, and you type much less code this way.
@@ -167,7 +166,7 @@
Unit-Tests
==========
- `Travis CI project <https://travis-ci.org>`_ tests ciscoconfparse on
Python versions 2.6 through 3.6, as well as a `pypy JIT`_ executable.
+ `Travis CI project <https://travis-ci.org>`_ tests ciscoconfparse on
Python versions 2.7 through 3.7, as well as a `pypy JIT`_ executable.
Click the image below for details; the current build status is:
@@ -182,7 +181,7 @@
=====================
ciscoconfparse_ is licensed GPLv3_; Copyright `David Michael
Pennington`_,
- 2007-2018.
+ 2007-2019.
ciscoconfparse_ is not affiliated with Cisco Systems in any way; the
word "Cisco" is a registered trademark of Cisco Systems
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ciscoconfparse-1.3.15/README.rst
new/ciscoconfparse-1.3.32/README.rst
--- old/ciscoconfparse-1.3.15/README.rst 2018-04-21 16:03:24.000000000
+0200
+++ new/ciscoconfparse-1.3.32/README.rst 2019-03-17 13:45:26.000000000
+0100
@@ -74,9 +74,9 @@
Pre-requisites
==============
-ciscoconfparse_ requires Python versions 2.6, 2.7 or 3.2+; the OS should not
-matter. If you want to run it under a Python virtualenv_, it's been heavily
-tested in that environment as well.
+As of version 1.3.32, ciscoconfparse_ requires Python versions 2.7 or 3.4+
+(note: version 3.7.0 has a bug - ref Github issue #117, but version 3.7.1
+works); the OS should not matter.
.. _Installation:
@@ -89,7 +89,7 @@
::
# Substitute whatever ciscoconfparse version you like...
- easy_install -U ciscoconfparse==1.3.14
+ easy_install -U ciscoconfparse==1.3.32
Alternatively you can install into Python2.x with pip_:
@@ -124,7 +124,7 @@
FAQ
===
-#) *QUESTION*: I want to use ciscoconfparse_ with Python3; is that safe?
*ANSWER*: As long as you're using Python 3.3 or higher, it's safe. I test every
release against Python 3.2+; however, Python 3.2 is currently exposed to a
small bug for some configurations (see `Github Issue #14`_).
+#) *QUESTION*: I want to use ciscoconfparse_ with Python3; is that safe?
*ANSWER*: *ANSWER*: Yes.
#) *QUESTION*: Some of the code in the documentation looks different than what
I'm used to seeing. Did you change something? *ANSWER*: Yes, starting around
ciscoconfparse_ v0.9.10 I introducted more methods directly on
``IOSConfigLine()`` objects; going forward, these methods are the preferred way
to use ciscoconfparse_. Please start using the new methods shown in the
example, since they're faster, and you type much less code this way.
@@ -158,7 +158,7 @@
Unit-Tests
==========
-`Travis CI project <https://travis-ci.org>`_ tests ciscoconfparse on Python
versions 2.6 through 3.6, as well as a `pypy JIT`_ executable.
+`Travis CI project <https://travis-ci.org>`_ tests ciscoconfparse on Python
versions 2.7 through 3.7, as well as a `pypy JIT`_ executable.
Click the image below for details; the current build status is:
@@ -173,7 +173,7 @@
=====================
ciscoconfparse_ is licensed GPLv3_; Copyright `David Michael Pennington`_,
-2007-2018.
+2007-2019.
ciscoconfparse_ is not affiliated with Cisco Systems in any way; the word
"Cisco" is a registered trademark of Cisco Systems
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ciscoconfparse-1.3.15/ciscoconfparse/ccp_abc.py
new/ciscoconfparse-1.3.32/ciscoconfparse/ccp_abc.py
--- old/ciscoconfparse-1.3.15/ciscoconfparse/ccp_abc.py 2018-04-21
15:41:44.000000000 +0200
+++ new/ciscoconfparse-1.3.32/ciscoconfparse/ccp_abc.py 2019-03-16
18:45:59.000000000 +0100
@@ -6,8 +6,8 @@
from ccp_util import IPv4Obj
-""" ccp_abc.py - Parse, Query, Build, and Modify IOS-style configurations
- Copyright (C) 2014-2015 David Michael Pennington
+r""" ccp_abc.py - Parse, Query, Build, and Modify IOS-style configurations
+ Copyright (C) 2014-2015, 2019 David Michael Pennington
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -197,7 +197,7 @@
"""unconftext is defined during special method calls. Do not assume it
is automatically populated."""
## remove any preceeding "no "
- conftext = re.sub("\s*no\s+", "", unconftext)
+ conftext = re.sub(r"\s*no\s+", "", unconftext)
myindent = self.parent.child_indent
self.uncfgtext = myindent * " " + "no " + conftext
@@ -457,7 +457,7 @@
return retval
def re_match(self, regex, group=1, default=""):
- """Use ``regex`` to search the :class:`~models_cisco.IOSCfgLine` text
and return the regular expression group, at the integer index.
+ r"""Use ``regex`` to search the :class:`~models_cisco.IOSCfgLine` text
and return the regular expression group, at the integer index.
Args:
- regex (str): A string or python regular expression, which should
be matched. This regular expression should contain parenthesis, which bound a
match group.
@@ -530,8 +530,9 @@
"""
return [cobj for cobj in self.children if cobj.re_search(regex)]
- def re_match_typed(self, regex, group=1, result_type=str, default=''):
- """Use ``regex`` to search the :class:`~models_cisco.IOSCfgLine` text
+ def re_match_typed(self, regex, group=1, untyped_default=False,
+ result_type=str, default=''):
+ r"""Use ``regex`` to search the :class:`~models_cisco.IOSCfgLine` text
and return the contents of the regular expression group, at the
integer ``group`` index, cast as ``result_type``; if there is no
match,
``default`` is returned.
@@ -542,6 +543,7 @@
- group (int): An integer which specifies the desired regex group
to be returned. ``group`` defaults to 1.
- result_type (type): A type (typically one of: ``str``, ``int``,
``float``, or ``IPv4Obj``). All returned values are cast as ``result_type``,
which defaults to ``str``.
- default (any): The default value to be returned, if there is no
match.
+ - untyped_default (bool): Set True if you don't want the default
value to be typed
Returns:
- ``result_type``. The text matched by the regular expression
group; if there is no match, ``default`` is returned. All values are cast as
``result_type``.
@@ -584,13 +586,15 @@
if not (mm is None):
if not (mm.group(group) is None):
return result_type(mm.group(group))
- else:
- return result_type(default)
- return result_type(default)
+
+ if untyped_default:
+ return default
+ else:
+ return result_type(default)
def re_match_iter_typed(self, regex, group=1, result_type=str, default='',
- all_children=False):
- """Use ``regex`` to search the children of
+ untyped_default=False, all_children=False):
+ r"""Use ``regex`` to search the children of
:class:`~models_cisco.IOSCfgLine` text and return the contents of
the regular expression group, at the integer ``group`` index, cast as
``result_type``; if there is no match, ``default`` is returned.
@@ -602,6 +606,7 @@
- result_type (type): A type (typically one of: ``str``, ``int``,
``float``, or :class:`~ccp_util.IPv4Obj`). All returned values are cast
as ``result_type``, which defaults to ``str``.
- default (any): The default value to be returned, if there is no
match.
- all_children (bool): Set True if you want to search all children
(children, grand children, great grand children, etc...)
+ - untyped_default (bool): Set True if you don't want the default
value to be typed
Returns:
- ``result_type``. The text matched by the regular expression
group; if there is no match, ``default`` is returned. All values are cast as
``result_type``.
@@ -647,13 +652,21 @@
mm = re.search(regex, cobj.text)
if not (mm is None):
return result_type(mm.group(group))
- return result_type(default)
+ ## Ref Github issue #121
+ if untyped_default:
+ return default
+ else:
+ return result_type(default)
else:
for cobj in self.all_children:
mm = re.search(regex, cobj.text)
if not (mm is None):
return result_type(mm.group(group))
- return result_type(default)
+ ## Ref Github issue #121
+ if untyped_default:
+ return default
+ else:
+ return result_type(default)
def reset(self):
# For subclass APIs
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ciscoconfparse-1.3.15/ciscoconfparse/ccp_util.py
new/ciscoconfparse-1.3.32/ciscoconfparse/ccp_util.py
--- old/ciscoconfparse-1.3.15/ciscoconfparse/ccp_util.py 2018-04-21
16:01:00.000000000 +0200
+++ new/ciscoconfparse-1.3.32/ciscoconfparse/ccp_util.py 2019-03-17
13:42:33.000000000 +0100
@@ -1,4 +1,3 @@
-from collections import MutableSequence
import itertools
import socket
import time
@@ -6,17 +5,23 @@
import re
import os
+if (sys.version_info>=(3, 0, 0,)):
+ from collections.abc import MutableSequence
+else:
+ ## This syntax is not supported in Python 3...
+ from collections import MutableSequence
+
from protocol_values import ASA_TCP_PORTS, ASA_UDP_PORTS
from dns.exception import DNSException
from dns.resolver import Resolver
-from dns import reversename, query
+from dns import reversename, query, zone
if sys.version_info[0] < 3:
from ipaddr import IPv4Network, IPv6Network, IPv4Address, IPv6Address
else:
from ipaddress import IPv4Network, IPv6Network, IPv4Address, IPv6Address
""" ccp_util.py - Parse, Query, Build, and Modify IOS-style configurations
- Copyright (C) 2014-2015, 2018 David Michael Pennington
+ Copyright (C) 2014-2015, 2018-2019 David Michael Pennington
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -132,7 +137,7 @@
>>> net.network_object
IPv4Network('172.16.1.0/24')
>>> str(net.network_object)
-'172.16.1.0'
+'172.16.1.0/24'
>>> net.prefixlen
24
>>> net.network_object.iterhosts()
@@ -777,15 +782,16 @@
A set() of :class:`~ccp_util.DNSResponse` instances
>>> from ciscoconfparse.ccp_util import dns_query
->>> dns_query('www.pennington.net', 'A', '4.2.2.2')
-set([<DNSResponse 'A' result_str='65.19.187.2'>])
+>>> dns_query('www.pennington.net', "A", "4.2.2.2")
+set([<DNSResponse "A" result_str="65.19.187.2">])
>>> answer = dns_query('www.pennington.net', 'A', '4.2.2.2')
>>> str(answer.pop())
'65.19.187.2'
>>>
"""
-
- valid_records = set(['A', 'AAAA', 'CNAME', 'MX', 'NS', 'PTR', 'TXT'])
+
+ valid_records = set(['A', 'AAAA', 'AXFR', 'CNAME', 'MX', 'NS', 'PTR',
+ 'TXT'])
query_type = query_type.upper()
assert query_type in valid_records
assert server!=""
@@ -814,6 +820,10 @@
response.has_error = True
response.error_str = e
retval.add(response)
+ elif query_type=="AXFR":
+ """This is a hack: return text of zone transfer, instead of axfr
objs"""
+ _zone = zone.from_xfr(query.xfr(server, input, lifetime=timeout))
+ return [_zone[node].to_text(node) for node in _zone.nodes.keys()]
elif query_type=="CNAME":
try:
answer = resolver.query(input, query_type)
@@ -984,23 +994,29 @@
>>> from ciscoconfparse.ccp_util import CiscoRange
>>> CiscoRange('1-3,5,9-11,13')
-<CiscoRange ['1', '2', '3', '5', '9', '10', '11', '13']>
+<CiscoRange 1-3,5,9-11,13>
>>> CiscoRange('Eth1/1-3,7')
-<CiscoRange ['Eth1/1', 'Eth1/2', 'Eth1/3', 'Eth1/7']>
+<CiscoRange Eth1/1-3,7>
+>>> CiscoRange()
+<CiscoRange none>
"""
- def __init__(self, text, result_type=str):
+ def __init__(self, text="", result_type=str):
super(CiscoRange, self).__init__()
self.text = text
self.result_type = result_type
if text:
- self.line_prefix, self.slot_prefix, self.range_text =
self._parse_range_text(
- )
+ self.line_prefix, self.slot_prefix, self.range_text =
self._parse_range_text()
self._list = self._range()
else:
+ self.line_prefix = ""
+ self.slot_prefix = ""
self._list = list()
def __repr__(self):
- return """<CiscoRange {0}>""".format(self._list)
+ if len(self._list)==0:
+ return """<CiscoRange none>"""
+ else:
+ return """<CiscoRange {0}>""".format(self.compressed_str)
def __len__(self):
return len(self._list)
@@ -1017,12 +1033,24 @@
def __str__(self):
return self.__repr__()
+ # Github issue #124
+ def __eq__(self, other):
+ assert hasattr(other, 'line_prefix')
+ self_prefix_str = self.line_prefix + self.slot_prefix
+ other_prefix_str = other.line_prefix + other.slot_prefix
+ cmp1 = self_prefix_str.lower()==other_prefix_str.lower()
+ cmp2 = sorted(self._list)==sorted(other._list)
+ return cmp1 and cmp2
+
def insert(self, ii, val):
## Insert something at index ii
for idx, obj in enumerate(
CiscoRange(
val, result_type=self.result_type)):
self._list.insert(ii + idx, obj)
+
+ # Prune out any duplicate entries, and sort...
+ self._list = sorted(map(self.result_type, set(self._list)))
return self
def append(self, val):
@@ -1048,7 +1076,8 @@
range_text = mm_result['range_text']
return line_prefix, slot_prefix, range_text
- def _dash_range(self, text):
+ def _parse_dash_range(self, text):
+ """Parse a dash Cisco range into a discrete list of items"""
retval = list()
for range_atom in text.split(','):
try:
@@ -1068,14 +1097,16 @@
def combine(arg):
return self.line_prefix + self.slot_prefix + str(arg)
- return [self.result_type(ii) for ii in map(combine,
self._dash_range(self.range_text))]
+ return [self.result_type(ii) for ii in map(combine,
self._parse_dash_range(self.range_text))]
def remove(self, arg):
remove_obj = CiscoRange(arg)
for ii in remove_obj:
try:
- index = self.index(self.result_type(ii))
- self.pop(index)
+ ## Remove arg, even if duplicated... Ref Github issue #126
+ while True:
+ index = self.index(self.result_type(ii))
+ self.pop(index)
except ValueError:
pass
return self
@@ -1083,3 +1114,55 @@
@property
def as_list(self):
return self._list
+
+ ## Github issue #125
+ @property
+ def compressed_str(self):
+ """Return a text string with a compressed csv of values
+
+>>> from ciscoconfparse.ccp_util import CiscoRange
+>>> range_obj = CiscoRange('1,3,5,6,7')
+>>> range_obj.compressed_str
+'1,3,5-7'
+>>>
+ """
+ retval = list()
+ prefix_str = self.line_prefix + self.slot_prefix
+
+ # Build a list of integers (without prefix_str)
+ input = list()
+ for ii in self._list:
+ try:
+ unicode_ii = str(ii, 'utf-8') # Python2.7...
+ except:
+ unicode_ii = str(ii)
+ ii = re.sub(r'^{0}(\d+)$'.format(prefix_str), '\g<1>', unicode_ii)
+ input.append(int(ii))
+
+ if len(input)==0: # Special case, handle empty list
+ return ''
+
+ # source - https://stackoverflow.com/a/51227915/667301
+ input = sorted(list(set(input)))
+ range_list = [input[0]]
+ for ii in range(len(input)):
+ if ii+1<len(input) and ii-1>-1:
+ if (input[ii]-input[ii-1]==1) and (input[ii+1]-input[ii]==1):
+ if range_list[-1]!='-':
+ range_list += ['-']
+ else:
+ range_list = range_list
+ else:
+ range_list+=[input[ii]]
+ if len(input)>1:
+ range_list+=[input[len(input)-1]]
+
+ # Build the return value from range_list...
+ retval = prefix_str + str(range_list[0])
+ for ii in range(1,len(range_list)):
+ if str(type(range_list[ii]))!=str(type(range_list[ii-1])):
+ retval += str(range_list[ii])
+ else:
+ retval += ',' + str(range_list[ii])
+
+ return retval
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/ciscoconfparse-1.3.15/ciscoconfparse/ciscoconfparse.py
new/ciscoconfparse-1.3.32/ciscoconfparse/ciscoconfparse.py
--- old/ciscoconfparse-1.3.15/ciscoconfparse/ciscoconfparse.py 2018-04-21
15:43:19.000000000 +0200
+++ new/ciscoconfparse-1.3.32/ciscoconfparse/ciscoconfparse.py 2019-03-17
13:50:43.000000000 +0100
@@ -1,4 +1,3 @@
-from collections import MutableSequence, Iterator
from operator import methodcaller, attrgetter
from colorama import Fore, Back, Style
from difflib import SequenceMatcher
@@ -8,6 +7,13 @@
import re
import os
+if (sys.version_info>=(3, 0, 0,)):
+ from collections.abc import MutableSequence, Iterator
+else:
+ ## This syntax is not supported in Python 3...
+ from collections import MutableSequence, Iterator
+
+
from models_cisco import IOSHostnameLine, IOSRouteLine, IOSIntfLine
from models_cisco import IOSAccessLine, IOSIntfGlobal
from models_cisco import IOSAaaLoginAuthenticationLine
@@ -41,8 +47,8 @@
from models_junos import JunosCfgLine
-""" ciscoconfparse.py - Parse, Query, Build, and Modify IOS-style
configurations
- Copyright (C) 2007-2018 David Michael Pennington
+r""" ciscoconfparse.py - Parse, Query, Build, and Modify IOS-style configs
+ Copyright (C) 2007-2019 David Michael Pennington
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -62,9 +68,15 @@
"""
## Docstring props: http://stackoverflow.com/a/1523456/667301
-__version__ = open(os.path.join(os.path.dirname(os.path.abspath(__file__)),
- 'version')).read().strip()
-__email__ = "mike /at\ pennington [dot] net"
+versionfilepath = os.path.join(os.path.dirname(os.path.abspath(__file__)),
+ 'version')
+# __version__ if-else below fixes Github issue #123
+if os.path.isfile(versionfilepath):
+ __version__ = open(versionfilepath).read().strip()
+else:
+ # This case is required for importing from a zipfile... Github issue #123
+ __version__ = "0.0.0" # __version__ read failed
+__email__ = r"mike /at\ pennington [dot] net"
__author__ = "David Michael Pennington <{0}>".format(__email__)
__copyright__ = "2007-{0}, {1}".format(time.strftime('%Y'), __author__)
__license__ = "GPLv3"
@@ -209,7 +221,7 @@
if self.debug:
_log.debug("parsing from '{0}' with ios syntax".format(
config))
- f = open(config, mode="rU")
+ f = open(config, **self.openargs)
text = f.read()
rgx = re.compile(linesplit_rgx)
self.ConfigObjs = IOSConfigList(
@@ -225,7 +237,7 @@
if self.debug:
_log.debug("parsing from '{0}' with nxos
syntax".format(
config))
- f = open(config, mode="rU")
+ f = open(config, **self.openargs)
text = f.read()
rgx = re.compile(linesplit_rgx)
self.ConfigObjs = NXOSConfigList(
@@ -241,7 +253,7 @@
if self.debug:
_log.debug("parsing from '{0}' with asa syntax".format(
config))
- f = open(config, mode="rU")
+ f = open(config, **self.openargs)
text = f.read()
rgx = re.compile(linesplit_rgx)
self.ConfigObjs = ASAConfigList(
@@ -258,7 +270,7 @@
if self.debug:
_log.debug("parsing from '{0}' with junos syntax".
format(config))
- f = open(config, mode="rU")
+ f = open(config, **self.openargs)
text = f.read()
rgx = re.compile(linesplit_rgx)
@@ -290,6 +302,17 @@
self.factory)
@property
+ def openargs(self):
+ """Fix for Py3.5 deprecation of universal newlines - Ref Github #114
+ also see https://softwareengineering.stackexchange.com/q/298677/23144
+ """
+ if (sys.version_info>=(3, 5, 0,)):
+ retval = {'mode': 'r', 'newline': None}
+ else:
+ retval = {'mode': 'rU'}
+ return retval
+
+ @property
def ioscfg(self):
"""A list containing all text configuration statements"""
## I keep this here to emulate the legacy ciscoconfparse behavior
@@ -367,7 +390,7 @@
line = results.get('line', '')
## Hack to fix Github issue #49 (empty double braces at end)
- nn = re.search('^(.+?)\{\s*\}\s*$', input)
+ nn = re.search(r'^(.+?)\{\s*\}\s*$', input)
if nn is not None:
# Detect double braces at the end of a line and strip them
line = nn.group(1)
@@ -1120,7 +1143,7 @@
return list(map(attrgetter('text'), tmp))
def find_objects_wo_child(self, parentspec, childspec, ignore_ws=False):
- """Return a list of parent :class:`~models_cisco.IOSCfgLine` objects,
which matched the ``parentspec`` and whose children did not match
``childspec``. Only the parent :class:`~models_cisco.IOSCfgLine` objects will
be returned. For simplicity, this method only finds oldest_ancestors without
immediate children that match.
+ r"""Return a list of parent :class:`~models_cisco.IOSCfgLine` objects,
which matched the ``parentspec`` and whose children did not match
``childspec``. Only the parent :class:`~models_cisco.IOSCfgLine` objects will
be returned. For simplicity, this method only finds oldest_ancestors without
immediate children that match.
Args:
- parentspec (str): Text regular expression for the
:class:`~models_cisco.IOSCfgLine` object to be matched; this must match the
parent's line
@@ -1198,7 +1221,7 @@
]
def find_parents_wo_child(self, parentspec, childspec, ignore_ws=False):
- """Parse through all parents matching parentspec, and return a list of
parents that did NOT have children match the childspec. For simplicity, this
method only finds oldest_ancestors without immediate children that match.
+ r"""Parse through all parents matching parentspec, and return a list
of parents that did NOT have children match the childspec. For simplicity,
this method only finds oldest_ancestors without immediate children that match.
Args:
- parentspec (str): Text regular expression for the line to be
matched; this must match the parent's line
@@ -1272,7 +1295,7 @@
return list(map(attrgetter('text'), tmp))
def find_children_w_parents(self, parentspec, childspec, ignore_ws=False):
- """Parse through the children of all parents matching parentspec,
+ r"""Parse through the children of all parents matching parentspec,
and return a list of children that matched the childspec.
Args:
@@ -1353,8 +1376,8 @@
... '!',
... ]
>>> p = CiscoConfParse(config)
- >>> p.find_children_w_parents('^interface\sFastEthernet0/1', \
- 'port-security')
+ >>> p.find_children_w_parents('^interface\sFastEthernet0/1',
+ ... 'port-security')
[' switchport port-security', ' switchport port-security violation
protect', ' switchport port-security aging time 5', ' switchport port-security
aging type inactivity']
>>>
@@ -1374,7 +1397,7 @@
return list(map(attrgetter('text'), sorted(retval)))
def find_objects_w_parents(self, parentspec, childspec, ignore_ws=False):
- """Parse through the children of all parents matching parentspec,
+ r"""Parse through the children of all parents matching parentspec,
and return a list of child objects, which matched the childspec.
Args:
@@ -1445,8 +1468,8 @@
... ' address 172.16.15.5/22',
... ]
>>> p = CiscoConfParse(config)
- >>> p.find_objects_w_parents('^\s*interfaces', \
- r'\s+ge-0/0/1')
+ >>> p.find_objects_w_parents('^\s*interfaces',
+ ... r'\s+ge-0/0/1')
[<IOSCfgLine # 7 ' ge-0/0/1' (parent is # 0)>]
>>>
@@ -1690,7 +1713,7 @@
excludespec=None,
exactmatch=False,
atomic=False):
- """Replace lines matching `childspec` within the `parentspec`'s
+ r"""Replace lines matching `childspec` within the `parentspec`'s
immediate children.
Args:
@@ -1846,7 +1869,7 @@
return retval
def req_cfgspec_excl_diff(self, linespec, uncfgspec, cfgspec):
- """
+ r"""
req_cfgspec_excl_diff accepts a linespec, an unconfig spec, and
a list of required configuration elements. Return a list of
configuration diffs to make the configuration comply. **All** other
@@ -2025,7 +2048,7 @@
ignore_order=True,
remove_lines=True,
debug=False):
- """
+ r"""
``sync_diff()`` accepts a list of required configuration elements,
a linespec, and an unconfig spec. This method return a list of
configuration diffs to make the configuration comply with cfgspec.
@@ -2077,22 +2100,22 @@
b = CiscoConfParse(cfgspec, factory=False)
b_lines = b.ioscfg
- a_heirarchy = list()
- b_heirarchy = list()
+ a_hierarchy = list()
+ b_hierarchy = list()
## Build heirarchical, equal-length lists of parents / non-parents
- a_parents, a_nonparents = a.ConfigObjs.config_heirarchy()
- b_parents, b_nonparents = b.ConfigObjs.config_heirarchy()
+ a_parents, a_nonparents = a.ConfigObjs.config_hierarchy()
+ b_parents, b_nonparents = b.ConfigObjs.config_hierarchy()
obj = DiffObject(0, a_nonparents, a_parents)
- a_heirarchy.append(obj)
+ a_hierarchy.append(obj)
obj = DiffObject(0, b_nonparents, b_parents)
- b_heirarchy.append(obj)
+ b_hierarchy.append(obj)
retval = list()
## Assign config_this and unconfig_this attributes by "diff level"
- for adiff_level, bdiff_level in zip(a_heirarchy, b_heirarchy):
+ for adiff_level, bdiff_level in zip(a_hierarchy, b_hierarchy):
for attr in ['parents', 'nonparents']:
if attr == 'parents':
if ignore_order:
@@ -2341,7 +2364,7 @@
## Strip out 'double negatives' (i.e. 'no no ')
for idx in range(0, len(retval)):
- retval[idx] = re.sub(r'(\s+)no\s+no\s+(\S+.+?)$', '\g<1>\g<2>',
+ retval[idx] = re.sub(r'(\s+)no\s+no\s+(\S+.+?)$', r'\g<1>\g<2>',
retval[idx])
if debug:
@@ -2365,22 +2388,27 @@
### The methods below are marked SEMI-PRIVATE because they return an object
### or iterable of objects instead of the configuration text itself.
def _build_space_tolerant_regex(self, linespec):
- """SEMI-PRIVATE: Accept a string, and return a string with all
+ r"""SEMI-PRIVATE: Accept a string, and return a string with all
spaces replaced with '\s+'"""
- # Unicode below
+ # Unicode below...
backslash = '\x5c'
+ # escaped_space = "\\s+" (not a raw string)
+ if (sys.version_info>=(3, 0, 0,)):
+ escaped_space = (backslash + backslash + "s+").translate('utf-8')
+ else:
+ escaped_space = backslash + backslash + "s+"
LINESPEC_LIST_TYPE = bool(getattr(linespec, 'append', False))
if not LINESPEC_LIST_TYPE:
assert bool(getattr(linespec, 'upper', False)) # Ensure it's a str
- linespec = re.sub(r'\s+', backslash + "s+", linespec)
+ linespec = re.sub(r'\s+', escaped_space, linespec)
else:
for idx in range(0, len(linespec)):
## Ensure this element is a string
assert bool(getattr(linespec[idx], 'upper', False))
- linespec[idx] = re.sub(r'\s+', backslash + "s+", linespec[idx])
+ linespec[idx] = re.sub(r'\s+', escaped_space, linespec[idx])
return linespec
@@ -2615,7 +2643,7 @@
list_idx = len(self._list)
self.insert(list_idx, val)
- def config_heirarchy(self):
+ def config_hierarchy(self):
"""Walk this configuration and return the following tuple
at each parent 'level':
(list_of_parent_sibling_objs, list_of_nonparent_sibling_objs)
@@ -2623,7 +2651,7 @@
parent_siblings = list()
nonparent_siblings = list()
- for obj in self.CiscoConfParse.find_objects('^\S+'):
+ for obj in self.CiscoConfParse.find_objects(r'^\S+'):
if obj.is_comment:
continue
elif len(obj.children) == 0:
@@ -2638,7 +2666,7 @@
banner_objs = list(
filter(lambda obj: REGEX.search(obj.text), self._list))
- BANNER_STR_RE =
r'^(?:(?P<btype>(?:set\s+)*banner\s\w+\s+)(?P<bchar>\S)(?:\S)?)$'
+ BANNER_STR_RE =
r'^(?:(?P<btype>(?:set\s+)*banner\s\w+\s+)(?P<bchar>\S))'
for parent in banner_objs:
parent.oldest_ancestor = True
@@ -2670,6 +2698,7 @@
parent.linenum))
break
+ ## Use code below to identify children of the banner line
idx += 1
try:
obj = self._list[idx]
@@ -2687,8 +2716,9 @@
parent.child_indent = 0
obj.parent = parent
break
- elif obj.is_comment and (obj.indent == 0):
- break
+ # Commenting the following lines out; fix Github issue #115
+ #elif obj.is_comment and (obj.indent == 0):
+ # break
parent.children.append(obj)
parent.child_indent = 0
obj.parent = parent
@@ -3026,7 +3056,7 @@
list_idx = len(self._list)
self.insert(list_idx, val)
- def config_heirarchy(self):
+ def config_hierarchy(self):
"""Walk this configuration and return the following tuple
at each parent 'level':
(list_of_parent_sibling_objs, list_of_nonparent_sibling_objs)
@@ -3034,7 +3064,7 @@
parent_siblings = list()
nonparent_siblings = list()
- for obj in self.CiscoConfParse.find_objects('^\S+'):
+ for obj in self.CiscoConfParse.find_objects(r'^\S+'):
if obj.is_comment:
continue
elif len(obj.children) == 0:
@@ -3049,7 +3079,7 @@
banner_objs = list(
filter(lambda obj: REGEX.search(obj.text), self._list))
- BANNER_STR_RE =
r'^(?:(?P<btype>(?:set\s+)*banner\s\w+\s+)(?P<bchar>\S)(?:\S)?)$'
+ BANNER_STR_RE =
r'^(?:(?P<btype>(?:set\s+)*banner\s\w+\s+)(?P<bchar>\S))'
for parent in banner_objs:
parent.oldest_ancestor = True
@@ -3437,14 +3467,14 @@
list_idx = len(self._list)
self.insert(list_idx, val, atomic)
- def config_heirarchy(self):
+ def config_hierarchy(self):
"""Walk this configuration and return the following tuple
at each parent 'level':
(list_of_parent_siblings, list_of_nonparent_siblings)"""
parent_siblings = list()
nonparent_siblings = list()
- for obj in self.CiscoConfParse.find_objects('^\S+'):
+ for obj in self.CiscoConfParse.find_objects(r'^\S+'):
if obj.is_comment:
continue
elif len(obj.children) == 0:
@@ -3633,7 +3663,7 @@
class DiffObject(object):
- """This object should be used at every level of heirarchy"""
+ """This object should be used at every level of hierarchy"""
def __init__(self, level, nonparents, parents):
self.level = level
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ciscoconfparse-1.3.15/ciscoconfparse/models_cisco.py
new/ciscoconfparse-1.3.32/ciscoconfparse/models_cisco.py
--- old/ciscoconfparse-1.3.15/ciscoconfparse/models_cisco.py 2018-01-24
12:16:08.000000000 +0100
+++ new/ciscoconfparse-1.3.32/ciscoconfparse/models_cisco.py 2019-03-17
02:57:15.000000000 +0100
@@ -18,7 +18,7 @@
###
### Use models_cisco.py at your own risk. You have been warned :-)
""" models_cisco.py - Parse, Query, Build, and Modify IOS-style configurations
- Copyright (C) 2014-2018 David Michael Pennington
+ Copyright (C) 2014-2019 David Michael Pennington
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -37,6 +37,8 @@
mike [~at~] pennington [/dot\] net
"""
+MAX_VLAN = 4094
+
##
##------------- IOS Configuration line object
##
@@ -158,10 +160,10 @@
... '!',
... ]
>>> parse = CiscoConfParse(config)
- >>> obj = parse.find_objects('^interface\sSerial')[0]
+ >>> obj = parse.find_objects(r'^interface\sSerial')[0]
>>> obj.is_subintf
False
- >>> obj = parse.find_objects('^interface\sATM')[0]
+ >>> obj = parse.find_objects(r'^interface\sATM')[0]
>>> obj.is_subintf
True
>>>
@@ -203,10 +205,10 @@
... '!',
... ]
>>> parse = CiscoConfParse(config)
- >>> obj = parse.find_objects('^interface\sFast')[0]
+ >>> obj = parse.find_objects(r'^interface\sFast')[0]
>>> obj.is_loopback_intf
False
- >>> obj = parse.find_objects('^interface\sLoop')[0]
+ >>> obj = parse.find_objects(r'^interface\sLoop')[0]
>>> obj.is_loopback_intf
True
>>>
@@ -1104,6 +1106,23 @@
return retval
@property
+ def has_ip_helper_addresses(self):
+ """Return a True if the intf has helper-addresses; False if not"""
+ if len(self.ip_helper_addresses)>0:
+ return True
+ return False
+
+ @property
+ def ip_helper_addresses(self):
+ """Return a list of IP helper-addresses"""
+ retval = list()
+ for child in self.children:
+ if 'helper-address' in child.text:
+ addr = child.re_match_typed('ip\s+helper-address\s+(\S+)')
+ retval.append(addr)
+ return retval
+
+ @property
def is_switchport(self):
retval = self.re_match_iter_typed(
r'^\s*(switchport)\s*', result_type=bool, default=False)
@@ -1187,54 +1206,63 @@
@property
def trunk_vlans_allowed(self):
- """Return a CiscoRange() with the list of allowed vlan numbers.
Return 0 if the port isn't a switchport"""
- if self.is_switchport:
- default_val = '1-4094'
- else:
- default_val = 0
+ """Return a CiscoRange() with the list of allowed vlan numbers (as
int). Return 0 if the port isn't a switchport in trunk mode"""
- allowed = self.re_match_iter_typed(
- r'^\s*switchport\s+trunk\s+allowed\s+vlan\s+(\S+)$',
- result_type=str,
- default='1-4094')
- if allowed == 'all':
- allowed = '1-4094'
- elif allowed == 'none':
- allowed = ''
- add = self.re_match_iter_typed(
- r'^\s*switchport\s+trunk\s+allowed\s+vlan\s+add\s+(\S+)$',
- result_type=str,
- default='')
-
- if allowed and add:
- combined = allowed + ',' + add
- elif allowed:
- combined = allowed
- elif add:
- combined = add
+ # The default values...
+ if self.is_switchport and not self.has_manual_switch_access:
+ retval = CiscoRange('1-{0}'.format(MAX_VLAN), result_type=int)
else:
- combined = allowed
-
- remove = self.re_match_iter_typed(
- r'^\s*switchport\s+trunk\s+allowed\s+vlan\s+remove\s+(\S+)$',
- result_type=str,
- default='')
+ return 0
- if remove:
- retval = CiscoRange(combined, result_type=int).remove(remove)
- else:
- retval = CiscoRange(combined, result_type=int)
-
- _except = self.re_match_iter_typed(
- r'^\s*switchport\s+trunk\s+allowed\s+vlan\s+except\s+(\S+)$',
- result_type=str,
- default='')
+ ## Iterate over switchport trunk statements
+ for obj in self.children:
- if _except:
- retval = CiscoRange(combined, result_type=int).remove(_except)
+ ## For every child object, check whether the vlan list is modified
+ abs_str = obj.re_match_typed(
+ '^\s+switchport\s+trunk\s+allowed\s+vlan\s(all|none|\d.*?)$',
+ default='_nomatch_', result_type=str).lower()
+ add_str = obj.re_match_typed(
+ '^\s+switchport\s+trunk\s+allowed\s+vlan\s+add\s+(\d.*?)$',
+ default='_nomatch_', result_type=str).lower()
+ exc_str = obj.re_match_typed(
+ '^\s+switchport\s+trunk\s+allowed\s+vlan\s+except\s+(\d.*?)$',
+ default='_nomatch_', result_type=str).lower()
+ rem_str = obj.re_match_typed(
+ '^\s+switchport\s+trunk\s+allowed\s+vlan\s+remove\s+(\d.*?)$',
+ default='_nomatch_', result_type=str).lower()
+
+ ## Build a vdict for each vlan modification statement
+ vdict = {
+ 'absolute_str': abs_str,
+ 'add_str': add_str,
+ 'except_str': exc_str,
+ 'remove_str': rem_str,
+ }
+
+ ## Analyze each vdict in sequence and apply to retval sequentially
+ for key, val in vdict.items():
+ if val!='_nomatch_':
+ ## absolute in the key overrides previous values
+ if 'absolute' in key:
+ if val.lower()=='all':
+ retval = CiscoRange('1-{0}'.format(MAX_VLAN),
+ result_type=int)
+ elif val.lower()=='none':
+ retval = CiscoRange(result_type=int)
+ else:
+ retval = CiscoRange(val, result_type=int)
+ elif 'add' in key:
+ retval.append(val)
+ elif 'except' in key:
+ retval = CiscoRange('1-{0}'.format(MAX_VLAN),
+ result_type=int)
+ retval.remove(val)
+ elif 'remove' in key:
+ retval.remove(val)
return retval
+
@property
def native_vlan(self):
"""Return an integer with the native vlan number. Return 1, if the
switchport has no explicit native vlan configured; return 0 if the port isn't a
switchport"""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ciscoconfparse-1.3.15/ciscoconfparse/models_nxos.py
new/ciscoconfparse-1.3.32/ciscoconfparse/models_nxos.py
--- old/ciscoconfparse-1.3.15/ciscoconfparse/models_nxos.py 2018-01-24
12:14:54.000000000 +0100
+++ new/ciscoconfparse-1.3.32/ciscoconfparse/models_nxos.py 2019-03-17
13:50:56.000000000 +0100
@@ -17,7 +17,7 @@
###
### You have been warned :-)
""" models_nxos.py - Parse, Query, Build, and Modify IOS-style configurations
- Copyright (C) 2016-2018 David Michael Pennington
+ Copyright (C) 2016-2019 David Michael Pennington
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -40,6 +40,7 @@
##------------- IOS Configuration line object
##
+MAX_VLAN = 4094
class NXOSCfgLine(BaseCfgLine):
"""An object for a parsed IOS-style configuration line.
@@ -1181,54 +1182,64 @@
@property
def trunk_vlans_allowed(self):
- """Return a CiscoRange() with the list of allowed vlan numbers.
Return 0 if the port isn't a switchport"""
- if self.is_switchport:
- default_val = '1-4094'
- else:
- default_val = 0
-
- allowed = self.re_match_iter_typed(
- r'^\s*switchport\s+trunk\s+allowed\s+vlan\s+(\S+)$',
- result_type=str,
- default='1-4094')
- if allowed == 'all':
- allowed = '1-4094'
- elif allowed == 'none':
- allowed = ''
- add = self.re_match_iter_typed(
- r'^\s*switchport\s+trunk\s+allowed\s+vlan\s+add\s+(\S+)$',
- result_type=str,
- default='')
-
- if allowed and add:
- combined = allowed + ',' + add
- elif allowed:
- combined = allowed
- elif add:
- combined = add
- else:
- combined = allowed
-
- remove = self.re_match_iter_typed(
- r'^\s*switchport\s+trunk\s+allowed\s+vlan\s+remove\s+(\S+)$',
- result_type=str,
- default='')
+ """Return a CiscoRange() with the list of allowed vlan numbers (as
int). Return 0 if the port isn't a switchport in trunk mode"""
- if remove:
- retval = CiscoRange(combined, result_type=int).remove(remove)
+ # The default values...
+ if self.is_switchport and not self.has_manual_switch_access:
+ retval = CiscoRange('1-{0}'.format(MAX_VLAN), result_type=int)
else:
- retval = CiscoRange(combined, result_type=int)
+ return 0
- _except = self.re_match_iter_typed(
- r'^\s*switchport\s+trunk\s+allowed\s+vlan\s+except\s+(\S+)$',
- result_type=str,
- default='')
+ ## Iterate over switchport trunk statements
+ for obj in self.children:
- if _except:
- retval = CiscoRange(combined, result_type=int).remove(_except)
+ ## For every child object, check whether the vlan list is modified
+ abs_str = obj.re_match_typed(
+ '^\s+switchport\s+trunk\s+allowed\s+vlan\s(all|none|\d.*?)$',
+ default='_nomatch_', result_type=str).lower()
+ add_str = obj.re_match_typed(
+ '^\s+switchport\s+trunk\s+allowed\s+vlan\s+add\s+(\d.*?)$',
+ default='_nomatch_', result_type=str).lower()
+ exc_str = obj.re_match_typed(
+ '^\s+switchport\s+trunk\s+allowed\s+vlan\s+except\s+(\d.*?)$',
+ default='_nomatch_', result_type=str).lower()
+ rem_str = obj.re_match_typed(
+ '^\s+switchport\s+trunk\s+allowed\s+vlan\s+remove\s+(\d.*?)$',
+ default='_nomatch_', result_type=str).lower()
+
+
+ ## Build a vdict for each vlan modification statement
+ vdict = {
+ 'absolute_str': abs_str,
+ 'add_str': add_str,
+ 'except_str': exc_str,
+ 'remove_str': rem_str,
+ }
+
+ ## Analyze each vdict in sequence and apply to retval sequentially
+ for key, val in vdict.items():
+ if val!='_nomatch_':
+ ## absolute in the key overrides previous values
+ if 'absolute' in key:
+ if val.lower()=='all':
+ retval = CiscoRange('1-{0}'.format(MAX_VLAN),
+ result_type=int)
+ elif val.lower()=='none':
+ retval = CiscoRange(result_type=int)
+ else:
+ retval = CiscoRange(val, result_type=int)
+ elif 'add' in key:
+ retval.append(val)
+ elif 'except' in key:
+ retval = CiscoRange('1-{0}'.format(MAX_VLAN),
+ result_type=int)
+ retval.remove(val)
+ elif 'remove' in key:
+ retval.remove(val)
return retval
+
@property
def native_vlan(self):
"""Return an integer with the native vlan number. Return 1, if the
switchport has no explicit native vlan configured; return 0 if the port isn't a
switchport"""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ciscoconfparse-1.3.15/ciscoconfparse/version
new/ciscoconfparse-1.3.32/ciscoconfparse/version
--- old/ciscoconfparse-1.3.15/ciscoconfparse/version 2018-04-21
16:08:38.000000000 +0200
+++ new/ciscoconfparse-1.3.32/ciscoconfparse/version 2019-03-17
13:45:07.000000000 +0100
@@ -1 +1 @@
-1.3.15
+1.3.32
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/ciscoconfparse-1.3.15/ciscoconfparse.egg-info/PKG-INFO
new/ciscoconfparse-1.3.32/ciscoconfparse.egg-info/PKG-INFO
--- old/ciscoconfparse-1.3.15/ciscoconfparse.egg-info/PKG-INFO 2018-04-21
16:09:21.000000000 +0200
+++ new/ciscoconfparse-1.3.32/ciscoconfparse.egg-info/PKG-INFO 2019-03-17
13:53:07.000000000 +0100
@@ -1,12 +1,11 @@
Metadata-Version: 1.1
Name: ciscoconfparse
-Version: 1.3.15
+Version: 1.3.32
Summary: Parse, Audit, Query, Build, and Modify Cisco IOS-style configurations
Home-page: http://www.pennington.net/py/ciscoconfparse/
Author: David Michael Pennington
Author-email: [email protected]
License: GPLv3
-Description-Content-Type: UNKNOWN
Description: ==============
ciscoconfparse
==============
@@ -83,9 +82,9 @@
Pre-requisites
==============
- ciscoconfparse_ requires Python versions 2.6, 2.7 or 3.2+; the OS
should not
- matter. If you want to run it under a Python virtualenv_, it's been
heavily
- tested in that environment as well.
+ As of version 1.3.32, ciscoconfparse_ requires Python versions 2.7 or
3.4+
+ (note: version 3.7.0 has a bug - ref Github issue #117, but version
3.7.1
+ works); the OS should not matter.
.. _Installation:
@@ -98,7 +97,7 @@
::
# Substitute whatever ciscoconfparse version you like...
- easy_install -U ciscoconfparse==1.3.14
+ easy_install -U ciscoconfparse==1.3.32
Alternatively you can install into Python2.x with pip_:
@@ -133,7 +132,7 @@
FAQ
===
- #) *QUESTION*: I want to use ciscoconfparse_ with Python3; is that
safe? *ANSWER*: As long as you're using Python 3.3 or higher, it's safe. I
test every release against Python 3.2+; however, Python 3.2 is currently
exposed to a small bug for some configurations (see `Github Issue #14`_).
+ #) *QUESTION*: I want to use ciscoconfparse_ with Python3; is that
safe? *ANSWER*: *ANSWER*: Yes.
#) *QUESTION*: Some of the code in the documentation looks different
than what I'm used to seeing. Did you change something? *ANSWER*: Yes,
starting around ciscoconfparse_ v0.9.10 I introducted more methods directly on
``IOSConfigLine()`` objects; going forward, these methods are the preferred way
to use ciscoconfparse_. Please start using the new methods shown in the
example, since they're faster, and you type much less code this way.
@@ -167,7 +166,7 @@
Unit-Tests
==========
- `Travis CI project <https://travis-ci.org>`_ tests ciscoconfparse on
Python versions 2.6 through 3.6, as well as a `pypy JIT`_ executable.
+ `Travis CI project <https://travis-ci.org>`_ tests ciscoconfparse on
Python versions 2.7 through 3.7, as well as a `pypy JIT`_ executable.
Click the image below for details; the current build status is:
@@ -182,7 +181,7 @@
=====================
ciscoconfparse_ is licensed GPLv3_; Copyright `David Michael
Pennington`_,
- 2007-2018.
+ 2007-2019.
ciscoconfparse_ is not affiliated with Cisco Systems in any way; the
word "Cisco" is a registered trademark of Cisco Systems
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/ciscoconfparse-1.3.15/ciscoconfparse.egg-info/requires.txt
new/ciscoconfparse-1.3.32/ciscoconfparse.egg-info/requires.txt
--- old/ciscoconfparse-1.3.15/ciscoconfparse.egg-info/requires.txt
2018-04-21 16:09:21.000000000 +0200
+++ new/ciscoconfparse-1.3.32/ciscoconfparse.egg-info/requires.txt
2019-03-17 13:53:07.000000000 +0100
@@ -1,3 +1,6 @@
-ipaddr>=2.1.11
-dnspython
colorama
+passlib
+dnspython
+
+[:python_version<'3']
+ipaddr>=2.1.11
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ciscoconfparse-1.3.15/requirements.txt
new/ciscoconfparse-1.3.32/requirements.txt
--- old/ciscoconfparse-1.3.15/requirements.txt 2018-03-03 15:34:43.000000000
+0100
+++ new/ciscoconfparse-1.3.32/requirements.txt 2019-02-19 03:02:10.000000000
+0100
@@ -1,4 +1,6 @@
-ipaddr>=2.1.11
-passlib
colorama
+passlib
dnspython
+
+[:python_version<'3']
+ipaddr>=2.1.11
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/ciscoconfparse-1.3.15/setup.py
new/ciscoconfparse-1.3.32/setup.py
--- old/ciscoconfparse-1.3.15/setup.py 2018-01-20 12:47:16.000000000 +0100
+++ new/ciscoconfparse-1.3.32/setup.py 2019-02-19 03:02:10.000000000 +0100
@@ -16,12 +16,11 @@
## Conditionally require the correct ipaddr package in Python2 vs Python3
-if sys.version_info[0]<3:
- IPADDR = "ipaddr>=2.1.11"
- DNSPYTHON = "dnspython"
-else:
- IPADDR = "ipaddress"
- DNSPYTHON = "dnspython3"
+# Ref Github issue #127 - sdist improvements
+REQUIRES = ['colorama', 'passlib', 'dnspython']
+EXTRAS = {
+ ":python_version<'3'": ['ipaddr>=2.1.11'],
+}
setup(name='ciscoconfparse',
version=open(os.path.join(os.path.dirname(os.path.abspath(__file__)),
@@ -39,7 +38,8 @@
packages=find_packages(),
use_2to3=True, # Reqd for Windows + Py3 - ref Github issue
#32
zip_safe=False,
- install_requires = [IPADDR, DNSPYTHON, 'colorama'], # Package
dependencies here
+ install_requires = REQUIRES,
+ extras_require = EXTRAS, # Conditional dependencies Github isssue #127
#setup_requires=["setuptools_hg"], # setuptools_hg must be installed as
a python module
classifiers=[
'Development Status :: 5 - Production/Stable',