Improve the Capirca and related modules for ACL config generation

Since I added these modules in 2017.7.0, Capirca has been released on PyPI,
now available to be installed as a regular Python library.

Besides that, in the past there were various issues due to the way the modules
are interacting with the underlying library, solved by adding a hardcoded list
for the policy term fields. This has caused some issues in the past, and had
to be patches, e.g., db255d3702 or
1e74141cc0.
While the changes referenced above are not particularly big, it's tedious to
always keep an eye on Capirca and be aware of what's newly added or changed.
Besides, this may have caused issues for users in the past when using other
Capirca versions.
This commit is contained in:
Mircea Ulinic 2018-12-13 13:49:45 +00:00
parent 32dfabab50
commit 5176cd8798
3 changed files with 57 additions and 84 deletions

View file

@ -19,8 +19,7 @@ The firewall configuration is generated by Capirca_.
.. _Capirca: https://github.com/google/capirca
Capirca is not yet available on PyPI threrefore it has to be installed
directly form Git: ``pip install -e git+git@github.com:google/capirca.git#egg=aclgen``.
To install Capirca, execute: ``pip install capirca``.
'''
from __future__ import absolute_import
@ -32,15 +31,21 @@ import datetime
log = logging.getLogger(__file__)
# Import third party libs
from salt.ext import six
try:
import aclgen
import capirca
import capirca.aclgen
import capirca.lib.policy
import capirca.lib.aclgenerator
HAS_CAPIRCA = True
except ImportError:
HAS_CAPIRCA = False
# Import Salt modules
import salt.utils
from salt.ext import six
# Import Salt libs
try:
from salt.utils import fopen
except ImportError:
from salt.utils.files import fopen
# ------------------------------------------------------------------------------
# module properties
@ -69,71 +74,9 @@ def __virtual__():
# ------------------------------------------------------------------------------
# define the default values for all possible term fields
# we could also extract them from the `policy` module, inspecting the `Policy`
# class, but that might be overkill & it would make the code less obvious.
# we can revisit this later if necessary.
_TERM_FIELDS = {
'action': [],
'address': [],
'address_exclude': [],
'comment': [],
'counter': None,
'expiration': None,
'destination_address': [],
'destination_address_exclude': [],
'destination_port': [],
'destination_prefix': [],
'forwarding_class': [],
'forwarding_class_except': [],
'logging': [],
'log_name': None,
'loss_priority': None,
'option': [],
'owner': None,
'policer': None,
'port': [],
'precedence': [],
'principals': [],
'protocol': [],
'protocol_except': [],
'qos': None,
'pan_application': [],
'routing_instance': None,
'source_address': [],
'source_address_exclude': [],
'source_port': [],
'source_prefix': [],
'verbatim': [],
'packet_length': None,
'fragment_offset': None,
'hop_limit': None,
'icmp_type': [],
'icmp_code': None,
'ether_type': [],
'traffic_class_count': None,
'traffic_type': [],
'translated': False,
'dscp_set': None,
'dscp_match': [],
'dscp_except': [],
'next_ip': None,
'flexible_match_range': [],
'source_prefix_except': [],
'destination_prefix_except': [],
'vpn': None,
'source_tag': [],
'destination_tag': [],
'source_interface': None,
'destination_interface': None,
'platform': [],
'platform_exclude': [],
'timeout': None,
'flattened': False,
'flattened_addr': None,
'flattened_saddr': None,
'flattened_daddr': None,
'priority': None
}
# This mapping is currently built dynamically using the Term class from the
# underlying library, Capirca
_TERM_FIELDS = {}
# IP-type fields
# when it comes to IP fields, Capirca does not ingest raw text
@ -159,7 +102,19 @@ _SERVICES = {}
if HAS_CAPIRCA:
class _Policy(aclgen.policy.Policy):
_TempTerm = capirca.lib.policy.Term
def _add_object(self, obj):
return
setattr(_TempTerm, 'AddObject', _add_object)
dumy_term = _TempTerm(None)
for item in dir(dumy_term):
if hasattr(item, '__func__') or item.startswith('_') or item != item.lower():
continue
_TERM_FIELDS[item] = getattr(dumy_term, item)
class _Policy(capirca.lib.policy.Policy):
'''
Extending the Capirca Policy class to allow inserting custom filters.
'''
@ -167,7 +122,7 @@ if HAS_CAPIRCA:
self.filters = []
self.filename = ''
class _Term(aclgen.policy.Term):
class _Term(capirca.lib.policy.Term):
'''
Extending the Capirca Term class to allow setting field valued on the fly.
'''
@ -184,10 +139,10 @@ def _import_platform_generator(platform):
for a class inheriting the `ACLGenerator` class.
'''
log.debug('Using platform: {plat}'.format(plat=platform))
for mod_name, mod_obj in inspect.getmembers(aclgen):
for mod_name, mod_obj in inspect.getmembers(capirca.aclgen):
if mod_name == platform and inspect.ismodule(mod_obj):
for plat_obj_name, plat_obj in inspect.getmembers(mod_obj): # pylint: disable=unused-variable
if inspect.isclass(plat_obj) and issubclass(plat_obj, aclgen.aclgenerator.ACLGenerator):
if inspect.isclass(plat_obj) and issubclass(plat_obj, capirca.lib.aclgenerator.ACLGenerator):
log.debug('Identified Capirca class {cls} for {plat}'.format(
cls=plat_obj,
plat=platform))
@ -214,7 +169,7 @@ def _get_services_mapping():
return _SERVICES
services_txt = ''
try:
with salt.utils.fopen('/etc/services', 'r') as srv_f:
with fopen('/etc/services', 'r') as srv_f:
services_txt = srv_f.read()
except IOError as ioe:
log.error('Unable to read from /etc/services:')
@ -364,7 +319,11 @@ def _clean_term_opts(term_opts):
# IP-type fields need to be transformed
ip_values = []
for addr in value:
ip_values.append(aclgen.policy.nacaddr.IP(addr))
if six.PY2:
addr = six.text_type(addr)
# Adding this, as ipaddress would complain about valid
# addresses not being valid. #pythonIsFun
ip_values.append(capirca.lib.policy.nacaddr.IP(addr))
value = ip_values[:]
clean_opts[field] = value
return clean_opts
@ -425,7 +384,7 @@ def _merge_list_of_dict(first, second, prepend=True):
if first and not second:
return first
# Determine overlaps
# So we don't change the position of the existing terms/filters
# So we dont change the position of the existing terms/filters
overlaps = []
merged = []
appended = []
@ -512,7 +471,7 @@ def _get_policy_object(platform,
continue # go to the next filter
filter_name = filter_.keys()[0]
filter_config = filter_.values()[0]
header = aclgen.policy.Header() # same header everywhere
header = capirca.lib.policy.Header() # same header everywhere
target_opts = [
platform,
filter_name
@ -522,7 +481,7 @@ def _get_policy_object(platform,
filter_options = _make_it_list({}, filter_name, filter_options)
# make sure the filter options are sent as list
target_opts.extend(filter_options)
target = aclgen.policy.Target(target_opts)
target = capirca.lib.policy.Target(target_opts)
header.AddObject(target)
filter_terms = []
for term_ in filter_config.get('terms', []):

View file

@ -19,6 +19,8 @@ The firewall configuration is generated by Capirca_.
.. _Capirca: https://github.com/google/capirca
To install Capirca, execute: ``pip install capirca``.
To be able to load configuration on network devices,
it requires NAPALM_ library to be installed: ``pip install napalm``.
Please check Installation_ for complete details.
@ -34,7 +36,10 @@ log = logging.getLogger(__file__)
# Import third party libs
try:
# pylint: disable=W0611
import aclgen
import capirca
import capirca.aclgen
import capirca.lib.policy
import capirca.lib.aclgenerator
HAS_CAPIRCA = True
# pylint: enable=W0611
except ImportError:

View file

@ -3,7 +3,7 @@
Network ACL
===========
Manage the firewall configuration on the network device namaged through NAPALM.
Manage the firewall configuration on the network device managed through NAPALM.
The firewall configuration is generated by Capirca_.
.. _Capirca: https://github.com/google/capirca
@ -18,7 +18,13 @@ The firewall configuration is generated by Capirca_.
Dependencies
------------
Capirca: ``pip install -e git+git@github.com:google/capirca.git#egg=aclgen``
Capirca
~~~~~~~
To install Capirca, execute: ``pip install capirca``.
NAPALM
~~~~~~
To be able to load configuration on network devices,
it requires NAPALM_ library to be installed: ``pip install napalm``.
@ -35,7 +41,10 @@ log = logging.getLogger(__file__)
# Import third party libs
try:
# pylint: disable=W0611
import aclgen
import capirca
import capirca.aclgen
import capirca.lib.policy
import capirca.lib.aclgenerator
HAS_CAPIRCA = True
# pylint: enable=W0611
except ImportError: