]> git.proxmox.com Git - mirror_ifupdown2.git/commitdiff
ifupdown2 2.0.0 release
authorJulien Fortin <julien@cumulusnetworks.com>
Thu, 13 Dec 2018 19:43:32 +0000 (11:43 -0800)
committerJulien Fortin <julien@cumulusnetworks.com>
Thu, 13 Dec 2018 19:43:32 +0000 (11:43 -0800)
This is a major update coming all at once from master-next branch
master-next branch was started with --orphan option which is basically a new
branch without history.

The major changes are:
    - repackaging
    - cleanup the directory tree
    - rewritte setup.py to allow install from deb file or pypi (pip install)
    - add a Makefile to make things (like building a deb) easier
    - review all debian files

Signed-off-by: Julien Fortin <julien@cumulusnetworks.com>
175 files changed:
KNOWN_ISSUES [deleted file]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
README.md [deleted file]
README.rst [deleted file]
TODO [deleted file]
TODO.addons [deleted file]
addons/__init__.py [deleted file]
addons/address.py [deleted file]
addons/addressvirtual.py [deleted file]
addons/batman_adv.py [deleted file]
addons/bond.py [deleted file]
addons/bridge.py [deleted file]
addons/bridgevlan.py [deleted file]
addons/dhcp.py [deleted file]
addons/ethtool.py [deleted file]
addons/link.py [deleted file]
addons/loopback.py [deleted file]
addons/mstpctl.py [deleted file]
addons/ppp.py [deleted file]
addons/tunnel.py [deleted file]
addons/usercmds.py [deleted file]
addons/vlan.py [deleted file]
addons/vrf.py [deleted file]
addons/vrrpd.py [deleted file]
addons/vxlan.py [deleted file]
build.sh [deleted file]
completion/ifup [deleted file]
config/addons.conf [deleted file]
config/ifupdown2.conf [deleted file]
config/networking [deleted file]
debian/changelog
debian/clean [deleted file]
debian/control
debian/copyright
debian/ifupdown2.bash-completion
debian/ifupdown2.dirs
debian/ifupdown2.install
debian/ifupdown2.links
debian/ifupdown2.manpages
debian/ifupdown2.networking.service
debian/ifupdown2.postinst
debian/ifupdown2.postrm
debian/ifupdown2.preinst [deleted file]
debian/networking [deleted file]
debian/python-ifupdown2.postinst [deleted file]
debian/python-ifupdown2.postrm [deleted file]
debian/python-ifupdown2.preinst [deleted file]
debian/rules
debian/source/format
debian/watch [new file with mode: 0644]
docs/examples/batman_adv/configure_batman_adv.sh [deleted file]
docs/examples/batman_adv/interfaces_batman [deleted file]
docs/examples/generate_interfaces.py
docs/examples/vlan_aware_bridges/interfaces.with_bonds
docs/examples/vlan_aware_bridges/interfaces.with_clag
docs/source/addonsapiref.rst
docs/source/addonshelperapiref.rst
docs/source/userguide.rst
etc/default/networking [new file with mode: 0644]
etc/init.d/networking [new file with mode: 0644]
etc/network/ifupdown2/addons.conf [new file with mode: 0644]
etc/network/ifupdown2/ifupdown2.conf [new file with mode: 0644]
ifupdown/__init__.py [deleted file]
ifupdown/exceptions.py [deleted file]
ifupdown/graph.py [deleted file]
ifupdown/iface.py [deleted file]
ifupdown/iff.py [deleted file]
ifupdown/ifupdownbase.py [deleted file]
ifupdown/ifupdownconfig.py [deleted file]
ifupdown/ifupdownflags.py [deleted file]
ifupdown/ifupdownmain.py [deleted file]
ifupdown/netlink.py [deleted file]
ifupdown/networkinterfaces.py [deleted file]
ifupdown/policymanager.py [deleted file]
ifupdown/rtnetlink.py [deleted file]
ifupdown/rtnetlink_api.py [deleted file]
ifupdown/scheduler.py [deleted file]
ifupdown/statemanager.py [deleted file]
ifupdown/template.py [deleted file]
ifupdown/utils.py [deleted file]
ifupdown2/__init__.py [new file with mode: 0644]
ifupdown2/__main__.py [new file with mode: 0755]
ifupdown2/addons/__init__.py [new file with mode: 0644]
ifupdown2/addons/address.py [new file with mode: 0644]
ifupdown2/addons/addressvirtual.py [new file with mode: 0644]
ifupdown2/addons/bond.py [new file with mode: 0644]
ifupdown2/addons/bridge.py [new file with mode: 0644]
ifupdown2/addons/bridgevlan.py [new file with mode: 0644]
ifupdown2/addons/dhcp.py [new file with mode: 0644]
ifupdown2/addons/ethtool.py [new file with mode: 0644]
ifupdown2/addons/link.py [new file with mode: 0644]
ifupdown2/addons/mstpctl.py [new file with mode: 0644]
ifupdown2/addons/usercmds.py [new file with mode: 0644]
ifupdown2/addons/vlan.py [new file with mode: 0644]
ifupdown2/addons/vrf.py [new file with mode: 0644]
ifupdown2/addons/vrrpd.py [new file with mode: 0644]
ifupdown2/addons/vxlan.py [new file with mode: 0644]
ifupdown2/completion/ifup [new file with mode: 0644]
ifupdown2/ifupdown/__init__.py [new file with mode: 0644]
ifupdown2/ifupdown/argv.py [new file with mode: 0644]
ifupdown2/ifupdown/config.py [new file with mode: 0644]
ifupdown2/ifupdown/exceptions.py [new file with mode: 0644]
ifupdown2/ifupdown/graph.py [new file with mode: 0644]
ifupdown2/ifupdown/iface.py [new file with mode: 0644]
ifupdown2/ifupdown/iff.py [new file with mode: 0644]
ifupdown2/ifupdown/ifupdownbase.py [new file with mode: 0644]
ifupdown2/ifupdown/ifupdownconfig.py [new file with mode: 0644]
ifupdown2/ifupdown/ifupdownflags.py [new file with mode: 0644]
ifupdown2/ifupdown/ifupdownmain.py [new file with mode: 0644]
ifupdown2/ifupdown/log.py [new file with mode: 0644]
ifupdown2/ifupdown/main.py [new file with mode: 0644]
ifupdown2/ifupdown/netlink.py [new file with mode: 0644]
ifupdown2/ifupdown/networkinterfaces.py [new file with mode: 0644]
ifupdown2/ifupdown/policymanager.py [new file with mode: 0644]
ifupdown2/ifupdown/scheduler.py [new file with mode: 0644]
ifupdown2/ifupdown/statemanager.py [new file with mode: 0644]
ifupdown2/ifupdown/template.py [new file with mode: 0644]
ifupdown2/ifupdown/utils.py [new file with mode: 0644]
ifupdown2/ifupdownaddons/LinkUtils.py [new file with mode: 0644]
ifupdown2/ifupdownaddons/__init__.py [new file with mode: 0644]
ifupdown2/ifupdownaddons/cache.py [new file with mode: 0644]
ifupdown2/ifupdownaddons/dhclient.py [new file with mode: 0644]
ifupdown2/ifupdownaddons/modulebase.py [new file with mode: 0644]
ifupdown2/ifupdownaddons/mstpctlutil.py [new file with mode: 0644]
ifupdown2/ifupdownaddons/systemutils.py [new file with mode: 0644]
ifupdown2/ifupdownaddons/utilsbase.py [new file with mode: 0644]
ifupdown2/man/genmanpages.sh [new file with mode: 0755]
ifupdown2/man/ifdown.8.rst [new file with mode: 0644]
ifupdown2/man/ifquery.8.rst [new file with mode: 0644]
ifupdown2/man/ifreload.8.rst [new file with mode: 0644]
ifupdown2/man/ifup.8.rst [new file with mode: 0644]
ifupdown2/man/ifupdown-addons-interfaces.5.rst [new file with mode: 0644]
ifupdown2/man/interfaces.5.rst [new file with mode: 0644]
ifupdown2/nlmanager/README [new file with mode: 0644]
ifupdown2/nlmanager/__init__.py [new file with mode: 0644]
ifupdown2/nlmanager/nllistener.py [new file with mode: 0644]
ifupdown2/nlmanager/nlmanager.py [new file with mode: 0644]
ifupdown2/nlmanager/nlpacket.py [new file with mode: 0644]
ifupdown2/sbin/ifaddon [new file with mode: 0755]
ifupdown2/sbin/ifupdown2d [new file with mode: 0755]
ifupdown2/sbin/start-networking [new file with mode: 0755]
ifupdownaddons/__init__.py [deleted file]
ifupdownaddons/bondutil.py [deleted file]
ifupdownaddons/bridgeutils.py [deleted file]
ifupdownaddons/cache.py [deleted file]
ifupdownaddons/dhclient.py [deleted file]
ifupdownaddons/ifenslaveutil.py [deleted file]
ifupdownaddons/iproute2.py [deleted file]
ifupdownaddons/modulebase.py [deleted file]
ifupdownaddons/mstpctlutil.py [deleted file]
ifupdownaddons/systemutils.py [deleted file]
ifupdownaddons/utilsbase.py [deleted file]
init.d/networking [deleted file]
man.rst/ifquery.8.rst [deleted file]
man.rst/ifreload.8.rst [deleted file]
man.rst/ifup.8.rst [deleted file]
man.rst/ifupdown-addons-interfaces.5.rst [deleted file]
man.rst/interfaces.5.rst [deleted file]
man/interfaces.5 [deleted file]
nlmanager/README [deleted file]
nlmanager/__init__.py [deleted file]
nlmanager/nllistener.py [deleted file]
nlmanager/nlmanager.py [deleted file]
nlmanager/nlpacket.py [deleted file]
packages/ifupdown2/ifupdown/ifupdownconfig.py [deleted file]
sbin/ifaddon [deleted file]
sbin/ifupdown [deleted file]
sbin/ifupdown2 [deleted file]
sbin/start-networking [deleted file]
scripts/genmanpages.sh [deleted file]
setup.py
stdeb.cfg [deleted file]
tests/ifstatetest [deleted file]
tests/test_ifupdown2.py [new file with mode: 0644]

diff --git a/KNOWN_ISSUES b/KNOWN_ISSUES
deleted file mode 100644 (file)
index bcc3a4e..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-- There is a state issue if multiple configuration blocks are present for the same interface in the interfaces file
-- `ifquery -r` can give wrong result if dhclient is running + static addresses are configured
-- `ifquery -r` status is success for success case and also for cases where there
-is no support for query yet
-- setup.py has ifupdown listed in data section instead of scripts: This is because default location for scripts is /usr/bin/. And ifupdown default location is /sbin. With newer versions we can specify --install-scripts directory. This needs to be fixed then.
-- and more :)
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..37b51d1
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,96 @@
+.PHONY: clean clean-test clean-pyc clean-build docs help
+.DEFAULT_GOAL := help
+
+define BROWSER_PYSCRIPT
+import os, webbrowser, sys
+
+try:
+       from urllib import pathname2url
+except:
+       from urllib.request import pathname2url
+
+webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1])))
+endef
+export BROWSER_PYSCRIPT
+
+define PRINT_HELP_PYSCRIPT
+import re, sys
+
+for line in sys.stdin:
+       match = re.match(r'^([a-zA-Z_-]+):.*?## (.*)$$', line)
+       if match:
+               target, help = match.groups()
+               print("%-20s %s" % (target, help))
+endef
+export PRINT_HELP_PYSCRIPT
+
+BROWSER := python -c "$$BROWSER_PYSCRIPT"
+
+help:
+       @python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST)
+
+clean: clean-build clean-pyc clean-test ## remove all build, test, coverage and Python artifacts
+
+clean-build: ## remove build artifacts
+       rm -fr build/
+       rm -fr dist/
+       rm -fr .eggs/
+       find . -name '*.egg-info' -exec rm -fr {} +
+       find . -name '*.egg' -exec rm -f {} +
+
+clean-pyc: ## remove Python file artifacts
+       find . -name '*.pyc' -exec rm -f {} +
+       find . -name '*.pyo' -exec rm -f {} +
+       find . -name '*~' -exec rm -f {} +
+       find . -name '__pycache__' -exec rm -fr {} +
+
+clean-test: ## remove test and coverage artifacts
+       rm -fr .tox/
+       rm -f .coverage
+       rm -fr htmlcov/
+       rm -fr .pytest_cache
+
+lint: ## check style with flake8
+       flake8 ifupdown2 tests
+
+test: ## run tests quickly with the default Python
+       py.test
+
+test-all: ## run tests on every Python version with tox
+       tox
+
+coverage: ## check code coverage quickly with the default Python
+       coverage run --source ifupdown2 -m pytest
+       coverage report -m
+       coverage html
+       $(BROWSER) htmlcov/index.html
+
+docs: ## generate Sphinx HTML documentation, including API docs
+       rm -f docs/ifupdown2.rst
+       rm -f docs/modules.rst
+       sphinx-apidoc -o docs/ ifupdown2
+       $(MAKE) -C docs clean
+       $(MAKE) -C docs html
+       $(BROWSER) docs/_build/html/index.html
+
+servedocs: docs ## compile the docs watching for changes
+       watchmedo shell-command -p '*.rst' -c '$(MAKE) -C docs html' -R -D .
+
+testpy: ## package and upload to testpy
+       python setup.py sdist bdist_wheel
+       twine upload --repository-url https://test.pypi.org/legacy/ dist/*
+       @echo "Install ifupdown2 from testpy 'pip install --index-url https://test.pypi.org/simple/ ifupdown2'"
+
+release: dist ## package and upload a release
+       twine upload dist/*
+
+dist: clean ## builds source and wheel package
+       python setup.py sdist
+       python setup.py bdist_wheel
+       ls -l dist
+
+install: clean ## install the package to the active Python's site-packages
+       python setup.py install
+
+deb: clean ## create a debian package (.deb)
+       debuild -b -rfakeroot -us -uc
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..b9d2815
--- /dev/null
+++ b/README
@@ -0,0 +1,162 @@
+=========
+ifupdown2
+=========
+
+Linux Interface Network Manager
+
+* Free software: GNU General Public License v2
+
+============
+Installation
+============
+
+As of today (early june 2018), the preferred method to install ifupdown2, is by
+building the source code (as it will always install the most recent stable
+release). See `Installing latest stable release from sources`_ chapter.
+
+Installing latest stable release from sources
+---------------------------------------------
+
+The sources for ifupdown2 can be downloaded from the `Github repo`_.
+
+You can either clone the public repository:
+
+.. code-block:: console
+
+    $ git clone git://github.com/CumulusNetworks/ifupdown2
+
+Or download the `tarball`_:
+
+.. code-block:: console
+
+    $ curl  -OL https://github.com/CumulusNetworks/ifupdown2/tarball/master
+
+Once you have a copy of the source, you should build a deb-package and install it
+
+.. code-block:: console
+
+    $ cd ifupdown2 && make deb
+
+The generated deb should be in the root directory (``../ifupdown2_2.0.0_all.deb``)
+
+.. code-block:: console
+
+    $ dpkg -i ../ifupdown2_2.0.0_all.deb
+
+We don't recommend using ``setup.py install`` directly, as it's still missing systemd/init.d scripts.
+This capability should be added in the near future.
+
+You might need to manually download dependencies. Mandatory dependencies:
+
+.. code-block:: console
+
+    $ apt-get install dh-systemd python-all python-docutils rst2man iproute2 python-ipaddr python-argcomplete
+
+Suggested dependencies:
+
+.. code-block:: console
+
+    $ apt-get install ethtool bridge-utils python-gvgen python-mako
+
+.. _Github repo: https://github.com/CumulusNetworks/ifupdown2
+.. _tarball: https://github.com/CumulusNetworks/ifupdown2/tarball/master
+
+
+============
+Contributing
+============
+
+Contributions are welcome, and they are greatly appreciated! Every little bit
+helps, and credit will always be given.
+
+You can contribute in many ways:
+
+Types of Contributions
+----------------------
+
+Report Bugs
+~~~~~~~~~~~
+
+Report bugs at https://github.com/CumulusNetworks/ifupdown2/issues.
+
+If you are reporting a bug, please include:
+
+* Your operating system name and version (``uname -a``).
+* Any details about your setup that might be helpful in troubleshooting.
+* Content of configuration files such as ``/etc/network/interfaces``
+* Detailed steps to reproduce the bug.
+* Debug output of the ifupdown2 command (see ``--debug`` option)
+
+Write Documentation
+~~~~~~~~~~~~~~~~~~~
+
+ifupdown2 could always use more documentation, whether as part of the
+official ifupdown2 docs, in docstrings, or even on the web in blog posts,
+articles, and such.
+
+Submit Feedback
+~~~~~~~~~~~~~~~
+
+The best way to send feedback is to file an issue at https://github.com/CumulusNetworks/ifupdown2/issues.
+
+If you are proposing a feature:
+
+* Explain in detail how it would work.
+* Keep the scope as narrow as possible, to make it easier to implement.
+
+=======
+Credits
+=======
+
+Development Lead
+----------------
+
+* Roopa Prabhu <roopa@cumulusnetworks.com>
+* Julien Fortin <julien@cumulusnetworks.com>
+
+Contributors
+------------
+
+* Nikhil Gajendrakumar <nikhil.gajendrakumar@gmail.com>
+* Maximilian Wilhelm <max@sdn.clinic>
+* Sven Auhagen <sven.auhagen@voleatech.de>
+* skorpy <magnus@skorpy.space>
+* Sam Tannous <stannous@cumulusnetworks.com>
+* Wilson Kok <wkok@cumulusnetworks.com>
+* John Berezovik <berezovik@gmail.com>
+* Daniel Walton <dwalton76@gmail.com>
+* Anuradha Karuppiah <anuradhak@cumulusnetworks.com>
+* Balakrishnan Raman <balkee@yahoo.com>
+* Scott Emery <scotte@cumulusnetworks.com>
+* Dave Olson <olson@cumulusnetworks.com>
+* David Ahern <dsa@cumulusnetworks.com>
+* Jonathan Toppins <>
+* Nolan Leake <nolan@cumulusnetworks.com>
+* Sergey Sudakovich <sergey@cumulusnetworks.com>
+* Andy Gospodarek <>
+* Satish Ashok <sashok@cumulusnetworks.com>
+* Scott Laffer <slaffer@cumulusnetworks.com>
+* Vidya Sagar Ravipati <vidya.ravipati@gmail.com>
+* Marek Grzybowski <marek.grzybowski@rtbhouse.com>
+* Gaudenz Steinlin <gaudenz@users.noreply.github.com>
+* Nigel Kukard <nkukard@lbsd.net>
+* Jeffrey <jeffrey.bosma@gmail.com>
+* kokel <kokel@users.noreply.github.com>
+
+Why not you too? :)
+
+
+=======
+History
+=======
+
+See changelog here: https://github.com/CumulusNetworks/ifupdown2/blob/master/debian/changelog
+
+
+Credits
+-------
+
+This package was created with Cookiecutter_ and the `audreyr/cookiecutter-pypackage`_ project template.
+
+.. _Cookiecutter: https://github.com/audreyr/cookiecutter
+.. _`audreyr/cookiecutter-pypackage`: https://github.com/audreyr/cookiecutter-pypackage
diff --git a/README.md b/README.md
deleted file mode 100644 (file)
index ad44a4f..0000000
--- a/README.md
+++ /dev/null
@@ -1,75 +0,0 @@
-ifupdown2
-=========
-
-ifupdown2 is a alternate implementation of debian's network interface manager
-ifupdown.
-
-ifupdown2 provides the required infrastructure to parse, schedule and
-manage interface configuration. Also provides default python addon modules
-for network interface configuration.
-
-
-Note: Previously ifupdown2 came as two packages: python-ifupdown2 and
-python-ifupdown2-addons. python-ifupdown2-addons contents are now merged into
-python-ifupdown2 package (python-ifupdown2-addons package is hence deprecated).
-
-Install
-=======
-
-## dependencies
-
-To install the mandatory dependencies please execute the following command:
-```
-apt-get install python-ipaddr
-apt-get install python-argcomplete
-```
-
-More recommended dependencies:
-```
-apt-get install bridge-utils
-apt-get install ethtool
-```
-
-## debian & Ubuntu
-
-To get our lastest version that is available on the debian repositories for your current OS just type
-`apt-get install ifupdown2`
-
-## Ubuntu users (anything bellow version Artful)
-
-We highly recommend that you build your own debs or WGET a deb from the debian repo as we have trouble backporting our latest fixes and features in Zesty, Xenial and bellow.
-
-```
-wget http://ftp.us.debian.org/debian/pool/main/i/ifupdown2/ifupdown2_1.0~git20170314-1_all.deb
-dpkg -i ifupdown2_1.0~git20170314-1_all.deb
-```
-
-## build your own deb
-
-If ever the repositories for your OS version doesn't include latest ifupdown2 you can always build your own deb
-
-To buid on Ubuntu and debian you'll some extra packages:
-
-```
-apt-get install build-essential dh-systemd dh-make python-docutils
-```
-
-Then run the following commands:
-
-```
-dpkg-buildpackage -us -uc -d
-```
-
-On the master branch this simple command should produce a `.deb` file that you can install using `dpkg -i`
-
-On the `debian-prep2` branch, you'll need to run:
-
-```
-cd /path/to/ifupdown2/source/folder
-git archive --format=tar HEAD | xz -9 -c >../ifupdown2_1.0~git20170314.orig.tar.xz && dpkg-buildpackage -us -uc -d
-```
-
-Note that the name of the tar archive needs to match the latest version present in the changelog. We usually use the date of the upload to tag a new version. In this previous example it was `20170314`
-
-If you are experiencing any issues please feel free to open a new issue.
-
diff --git a/README.rst b/README.rst
deleted file mode 100644 (file)
index e4bfd54..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-python-ifupdown2
-================
-
-This package is a replacement for the debian ifupdown package.
-It is ifupdown re-written in python. It maintains the original ifupdown
-pluggable architecture and extends it further.
-
-The python-ifupdown2 package provides the infrastructure for
-parsing /etc/network/interfaces file, loading, scheduling and state
-management of interfaces.
-
-It dynamically loads python modules from /usr/share/ifupdownaddons.
-To remain compatible with other packages that depend on ifupdown,
-it also executes scripts under /etc/network/.
-To make the transition smoother, a python module under
-/usr/share/ifupdownaddons will override a script by the same name under
-/etc/network/.
-
-It publishes an interface object which is passed to all loadble python
-modules. For more details on adding a addon module, see the section on
-adding python modules.
-
-
-pluggable python modules:
-=========================
-Unlike original ifupdown, all interface configuration is moved to external
-python modules. That includes inet, inet6 and dhcp configurations.
-
-A set of default modules are included in the package.
-
-python-ifupdown2 expects a few things from the pluggable modules:
-- the module should implement a class by the same name
-- the interface object (class iface) and the operation to be performed is
-  passed to the modules
-- the python addon class should provide a few methods:
-       - run() : method to configure the interface.
-       - get_ops() : must return a list of operations it supports.
-               eg: 'pre-up', 'post-down'
-       - get_dependent_ifacenames() : must return a list of interfaces the
-         interface is dependent on. This is used to build the dependency list
-         for sorting and executing interfaces in dependency order.
-       - if the module supports -r option to ifquery, ie ability to construct the
-      ifaceobj from running state, it can optionally implement the
-      get_dependent_ifacenames_running() method, to return the list of
-      dependent interfaces derived from running state of the interface.
-      This is different from get_dependent_ifacenames() where the dependent
-      interfaces are derived from the interfaces config file (provided by the
-      user).
-
-Example: Address handling module /usr/share/ifupdownaddons/address.py
-
-
-build
-=====
-- get source
-
-- install build dependencies:
-    apt-get install python-stdeb python-docutils
-
-- cd <python-ifupdown2 sourcedir> && ./build.sh
-
-  (generates python-ifupdown2-<ver>.deb)
-
-install
-=======
-
-- remove existing ifupdown package
-  dpkg -r ifupdown
-
-- install python-ifupdown2 using `dpkg -i`
-
-- or install from deb
-    dpkg -i python-ifupdown2-<ver>.deb
diff --git a/TODO b/TODO
deleted file mode 100644 (file)
index aff9cd5..0000000
--- a/TODO
+++ /dev/null
@@ -1,36 +0,0 @@
-TODO:
-====
-- support old ifupdown state file /run/network/ifstate. Because some app's seem to use it
-- support for debian ifupdown methods: tunnel, v4tunnel, 6to4, ppp, wvdial, ipv4ll
-- support for debian ifupdown ipv6 auto method
-- support for debian ifupdown CAN address family
-- Compat : support for LOGICAL interfaces
-- dry-run improvement: It skips the cache completely. Which means It tells you the commands it would execute if the system is clean. Its not smart enought to say what it will really execute given the state of the system
-
-- Ifquery does not report link status, mainly because it reports only in terms of /etc/network/interfaces attributes. Plan to fix that
-- more Documentation
-- Priorities for addon modules
-- have ability to also run uninstall on interfaces that dont have any config
-- ifup hotplug support (basically needs some testing and fixing broken things)
-- -q quiet option
-- support for /etc/networking.defaults
-- implement legacy ifupdown mapping feature
-- support for the following ifupdown options:
-    -o OPTION=VALUE     set OPTION to VALUE as though it were in
-                        /etc/network/interfaces
-    --no-mappings       don't run any mappings
-    --no-scripts        don't run any hook scripts
-- parallel implementation
-- Test all original ifupdown options for compatibility
-- Test with ifupdown-extra, ifmetric, ifupdown-scripts-zg2
-- export other environment variables to bash scripts (for backward compatibility):
-       IFACE  physical name of the interface being processed
-        LOGICAL logical name of the interface being processed
-        ADDRFAM address family of the interface
-        METHOD method of the interface (e.g., static)
-        MODE   start if run from ifup, stop if run from ifdown
-        PHASE  as per MODE, but with finer granularity, distinguishing the pre-
-               up, post-up, pre-down and post-down phases.
-        VERBOSITY indicates  whether --verbose was used; set to 1 if so, 0 if not.
-        PATH   the  command   search   path:   /usr/local/sbin:/usr/local/bin:‐
-               /usr/sbin:/usr/bin:/sbin:/bin
diff --git a/TODO.addons b/TODO.addons
deleted file mode 100644 (file)
index 5436284..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-TODO:
-====
-- run python code guideline checker
-- more code documentation
-- move all cache handling to decorators in ifupdownaddons package
-- input validation (present in some cases not all)
-- support the vlan0004 style vlans
-- ifquery coverage. currently it is at 80%.
-- vxlan module
-- fix and release ifaddon utility to manage module priorities
-- Deep compare in query for address module (does not compare address attributes like scope)
-- Maybe a pure netlink backend 
-- improve caching
diff --git a/addons/__init__.py b/addons/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/addons/address.py b/addons/address.py
deleted file mode 100644 (file)
index e67c29f..0000000
+++ /dev/null
@@ -1,739 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-
-import os
-
-try:
-    from ipaddr import IPNetwork, IPv4Network, IPv6Network, IPv4Address, IPv6Address
-    from sets import Set
-    from ifupdown.iface import *
-    from ifupdown.utils import utils
-    from ifupdownaddons.modulebase import moduleBase
-    from ifupdownaddons.iproute2 import iproute2
-    from ifupdownaddons.dhclient import dhclient
-    import ifupdown.policymanager as policymanager
-    from ifupdown.netlink import netlink
-    import ifupdown.ifupdownconfig as ifupdownConfig
-    import ifupdown.ifupdownflags as ifupdownflags
-    import ifupdown.statemanager as statemanager
-except ImportError, e:
-    raise ImportError (str(e) + "- required module not found")
-
-class address(moduleBase):
-    """  ifupdown2 addon module to configure address, mtu, hwaddress, alias
-    (description) on an interface """
-
-    _modinfo = {'mhelp' : 'address configuration module for interfaces',
-                'attrs': {
-                      'address' :
-                            {'help' : 'ipv4 or ipv6 addresses',
-                             'validvals' : ['<ipv4/prefixlen>', '<ipv6/prefixlen>'],
-                             'multiline' : True,
-                             'example' : ['address 10.0.12.3/24',
-                             'address 2000:1000:1000:1000:3::5/128']},
-                      'netmask' :
-                            {'help': 'netmask',
-                             'example' : ['netmask 255.255.255.0'],
-                             'compat' : True},
-                      'broadcast' :
-                            {'help': 'broadcast address',
-                             'validvals' : ['<ipv4>', ],
-                             'example' : ['broadcast 10.0.1.255']},
-                      'scope' :
-                            {'help': 'scope',
-                             'validvals' : ['universe', 'site', 'link', 'host', 'nowhere'],
-                             'example' : ['scope host']},
-                      'preferred-lifetime' :
-                            {'help': 'preferred lifetime',
-                              'validrange' : ['0', '65535'],
-                             'example' : ['preferred-lifetime forever',
-                                          'preferred-lifetime 10']},
-                      'gateway' :
-                            {'help': 'default gateway',
-                             'validvals' : ['<ipv4>', '<ipv6>'],
-                             'multiline' : True,
-                             'example' : ['gateway 255.255.255.0']},
-                      'mtu' :
-                            { 'help': 'interface mtu',
-                              'validrange' : ['552', '9216'],
-                              'example' : ['mtu 1600'],
-                              'default' : '1500'},
-                      'hwaddress' :
-                            {'help' : 'hw address',
-                             'validvals' : ['<mac>',],
-                             'example': ['hwaddress 44:38:39:00:27:b8']},
-                      'alias' :
-                            { 'help': 'description/alias',
-                              'example' : ['alias testnetwork']},
-                      'address-purge' :
-                            { 'help': 'purge existing addresses. By default ' +
-                              'any existing ip addresses on an interface are ' +
-                              'purged to match persistant addresses in the ' +
-                              'interfaces file. Set this attribute to \'no\'' +
-                              'if you want to preserve existing addresses',
-                              'validvals' : ['yes', 'no'],
-                              'default' : 'yes',
-                              'example' : ['address-purge yes/no']},
-                      'clagd-vxlan-anycast-ip' :
-                            { 'help'     : 'Anycast local IP address for ' +
-                              'dual connected VxLANs',
-                              'validvals' : ['<ipv4>', ],
-                              'example'  : ['clagd-vxlan-anycast-ip 36.0.0.11']}}}
-
-    def __init__(self, *args, **kargs):
-        moduleBase.__init__(self, *args, **kargs)
-        self.ipcmd = None
-        self._bridge_fdb_query_cache = {}
-        self.default_mtu = policymanager.policymanager_api.get_attr_default(module_name=self.__class__.__name__, attr='mtu')
-        self.max_mtu = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='max_mtu')
-
-        if not self.default_mtu:
-            self.default_mtu = '1500'
-
-        self.logger.info('address: using default mtu %s' %self.default_mtu)
-
-        if self.max_mtu:
-            self.logger.info('address: using max mtu %s' %self.max_mtu)
-
-    def syntax_check(self, ifaceobj, ifaceobj_getfunc=None):
-        return (self.syntax_check_multiple_gateway(ifaceobj)
-                and self.syntax_check_addr_allowed_on(ifaceobj, True)
-                and self.syntax_check_mtu(ifaceobj, ifaceobj_getfunc))
-
-    def syntax_check_mtu(self, ifaceobj, ifaceobj_getfunc):
-        mtu = ifaceobj.get_attr_value_first('mtu')
-        if mtu:
-            return self._check_mtu_config(ifaceobj, mtu, ifaceobj_getfunc,
-                                          syntaxcheck=True)
-        return True
-
-    def syntax_check_addr_allowed_on(self, ifaceobj, syntax_check=False):
-        if ifaceobj.get_attr_value('address'):
-            return utils.is_addr_ip_allowed_on(ifaceobj, syntax_check=syntax_check)
-        return True
-
-    def _syntax_check_multiple_gateway(self, family, found, addr, type_obj):
-        if type(IPNetwork(addr)) == type_obj:
-            if found:
-                raise Exception('%s: multiple gateways for %s family'
-                                % (addr, family))
-            return True
-        return False
-
-    def syntax_check_multiple_gateway(self, ifaceobj):
-        result = True
-        inet = False
-        inet6 = False
-        gateways = ifaceobj.get_attr_value('gateway')
-        for addr in gateways if gateways else []:
-            try:
-                if self._syntax_check_multiple_gateway('inet', inet, addr,
-                                                       IPv4Network):
-                    inet = True
-                if self._syntax_check_multiple_gateway('inet6', inet6, addr,
-                                                       IPv6Network):
-                    inet6 = True
-            except Exception as e:
-                self.logger.warning('%s: address: %s' % (ifaceobj.name, str(e)))
-                result = False
-        return result
-
-    def _address_valid(self, addrs):
-        if not addrs:
-           return False
-        if any(map(lambda a: True if a[:7] != '0.0.0.0'
-                else False, addrs)):
-           return True
-        return False
-
-    def _get_hwaddress(self, ifaceobj):
-        hwaddress = ifaceobj.get_attr_value_first('hwaddress')
-        if hwaddress and hwaddress.startswith("ether"):
-            hwaddress = hwaddress[5:].strip()
-        return hwaddress
-
-    def _process_bridge(self, ifaceobj, up):
-        hwaddress = self._get_hwaddress(ifaceobj)
-        addrs = ifaceobj.get_attr_value_first('address')
-        is_vlan_dev_on_vlan_aware_bridge = False
-        is_bridge = self.ipcmd.is_bridge(ifaceobj.name)
-        if not is_bridge:
-            if '.' in ifaceobj.name:
-                (bridgename, vlan) = ifaceobj.name.split('.')
-                is_vlan_dev_on_vlan_aware_bridge = self.ipcmd.bridge_is_vlan_aware(bridgename)
-        if ((is_bridge and not self.ipcmd.bridge_is_vlan_aware(ifaceobj.name))
-                        or is_vlan_dev_on_vlan_aware_bridge):
-           if self._address_valid(addrs):
-              if up:
-                self.write_file('/proc/sys/net/ipv4/conf/%s' %ifaceobj.name +
-                                '/arp_accept', '1')
-              else:
-                self.write_file('/proc/sys/net/ipv4/conf/%s' %ifaceobj.name +
-                                '/arp_accept', '0')
-        if hwaddress and is_vlan_dev_on_vlan_aware_bridge:
-           if up:
-              self.ipcmd.bridge_fdb_add(bridgename, hwaddress, vlan)
-           else:
-              self.ipcmd.bridge_fdb_del(bridgename, hwaddress, vlan)
-
-    def _get_anycast_addr(self, ifaceobjlist):
-        for ifaceobj in ifaceobjlist:
-            anycast_addr = ifaceobj.get_attr_value_first('clagd-vxlan-anycast-ip')
-            if anycast_addr:
-                anycast_addr = anycast_addr+'/32'
-                return anycast_addr
-        return None
-
-    def _inet_address_convert_to_cidr(self, ifaceobjlist):
-        newaddrs = []
-        newaddr_attrs = {}
-
-        for ifaceobj in ifaceobjlist:
-            addrs = ifaceobj.get_attr_value('address')
-            if not addrs:
-                continue
-
-            if not self.syntax_check_addr_allowed_on(ifaceobj,
-                                                     syntax_check=False):
-                return (False, newaddrs, newaddr_attrs)
-            # If user address is not in CIDR notation, convert them to CIDR
-            for addr_index in range(0, len(addrs)):
-                addr = addrs[addr_index]
-                newaddr = addr
-                if '/' in addr:
-                    newaddrs.append(addr)
-                else:
-                    netmask = ifaceobj.get_attr_value_n('netmask', addr_index)
-                    if netmask:
-                        prefixlen = IPNetwork('%s' %addr +
-                                    '/%s' %netmask).prefixlen
-                        newaddr = addr + '/%s' %prefixlen
-                    else:
-                        # we are here because there is no slash (/xx) and no netmask
-                        # just let IPNetwork handle the ipv4 or ipv6 address mask
-                        prefixlen = IPNetwork(addr).prefixlen
-                        newaddr = addr + '/%s' %prefixlen
-                    newaddrs.append(newaddr)
-
-                attrs = {}
-                for a in ['broadcast', 'pointopoint', 'scope',
-                        'preferred-lifetime']:
-                    aval = ifaceobj.get_attr_value_n(a, addr_index)
-                    if aval:
-                        attrs[a] = aval
-
-                if attrs:
-                    newaddr_attrs[newaddr]= attrs
-        return (True, newaddrs, newaddr_attrs)
-
-    def _inet_address_list_config(self, ifaceobj, newaddrs, newaddr_attrs):
-        for addr_index in range(0, len(newaddrs)):
-            try:
-                if newaddr_attrs:
-                    self.ipcmd.addr_add(ifaceobj.name, newaddrs[addr_index],
-                        newaddr_attrs.get(newaddrs[addr_index],
-                                          {}).get('broadcast'),
-                        newaddr_attrs.get(newaddrs[addr_index],
-                                          {}).get('pointopoint'),
-                        newaddr_attrs.get(newaddrs[addr_index],
-                                          {}).get('scope'),
-                        newaddr_attrs.get(newaddrs[addr_index],
-                                          {}).get('preferred-lifetime'))
-                else:
-                    self.ipcmd.addr_add(ifaceobj.name, newaddrs[addr_index])
-            except Exception, e:
-                self.log_error(str(e), ifaceobj)
-
-    def _inet_address_config(self, ifaceobj, ifaceobj_getfunc=None,
-                             force_reapply=False):
-        squash_addr_config = (True if \
-                              ifupdownConfig.config.get('addr_config_squash', \
-                              '0')  == '1' else False)
-
-        if (squash_addr_config and
-            not (ifaceobj.flags & ifaceobj.YOUNGEST_SIBLING)):
-            return
-
-        purge_addresses = ifaceobj.get_attr_value_first('address-purge')
-        if not purge_addresses:
-           purge_addresses = 'yes'
-
-        if squash_addr_config and ifaceobj.flags & iface.HAS_SIBLINGS:
-            ifaceobjlist = ifaceobj_getfunc(ifaceobj.name)
-        else:
-            ifaceobjlist = [ifaceobj]
-
-        (addr_supported, newaddrs, newaddr_attrs) = self._inet_address_convert_to_cidr(ifaceobjlist)
-        newaddrs = utils.get_normalized_ip_addr(ifaceobj.name, newaddrs)
-        if not addr_supported:
-            return
-        if (not squash_addr_config and (ifaceobj.flags & iface.HAS_SIBLINGS)):
-            # if youngest sibling and squash addr is not set
-            # print a warning that addresses will not be purged
-            if (ifaceobj.flags & iface.YOUNGEST_SIBLING):
-                self.logger.warn('%s: interface has multiple ' %ifaceobj.name +
-                               'iface stanzas, skip purging existing addresses')
-            purge_addresses = 'no'
-
-        if not ifupdownflags.flags.PERFMODE and purge_addresses == 'yes':
-            # if perfmode is not set and purge addresses is not set to 'no'
-            # lets purge addresses not in the config
-            runningaddrs = utils.get_normalized_ip_addr(ifaceobj.name, self.ipcmd.addr_get(ifaceobj.name, details=False))
-
-            # if anycast address is configured on 'lo' and is in running config
-            # add it to newaddrs so that ifreload doesn't wipe it out
-            anycast_addr = utils.get_normalized_ip_addr(ifaceobj.name, self._get_anycast_addr(ifaceobjlist))
-
-            if runningaddrs and anycast_addr and anycast_addr in runningaddrs:
-                newaddrs.append(anycast_addr)
-            if newaddrs == runningaddrs:
-                if force_reapply:
-                    self._inet_address_list_config(ifaceobj, newaddrs, newaddr_attrs)
-                return
-            try:
-                # if primary address is not same, there is no need to keep any.
-                # reset all addresses
-                if (newaddrs and runningaddrs and
-                        (newaddrs[0] != runningaddrs[0])):
-                    self.ipcmd.del_addr_all(ifaceobj.name)
-                else:
-                    self.ipcmd.del_addr_all(ifaceobj.name, newaddrs)
-            except Exception, e:
-                self.log_warn(str(e))
-        if not newaddrs:
-            return
-        self._inet_address_list_config(ifaceobj, newaddrs, newaddr_attrs)
-
-    def _add_delete_gateway(self, ifaceobj, gateways=[], prev_gw=[]):
-        vrf = ifaceobj.get_attr_value_first('vrf')
-        metric = ifaceobj.get_attr_value_first('metric')
-        for del_gw in list(set(prev_gw) - set(gateways)):
-            try:
-                self.ipcmd.route_del_gateway(ifaceobj.name, del_gw, vrf, metric)
-            except Exception as e:
-                self.logger.debug('%s: %s' % (ifaceobj.name, str(e)))
-        for add_gw in gateways:
-            try:
-                self.ipcmd.route_add_gateway(ifaceobj.name, add_gw, vrf)
-            except Exception as e:
-                self.log_error('%s: %s' % (ifaceobj.name, str(e)))
-
-    def _get_prev_gateway(self, ifaceobj, gateways):
-        ipv = []
-        saved_ifaceobjs = statemanager.statemanager_api.get_ifaceobjs(ifaceobj.name)
-        if not saved_ifaceobjs:
-            return ipv
-        prev_gateways = saved_ifaceobjs[0].get_attr_value('gateway')
-        if not prev_gateways:
-            return ipv
-        return prev_gateways
-
-    def _check_mtu_config(self, ifaceobj, mtu, ifaceobj_getfunc, syntaxcheck=False):
-        retval = True
-        if (ifaceobj.link_kind & ifaceLinkKind.BRIDGE):
-            if syntaxcheck:
-                self.logger.warn('%s: bridge inherits mtu from its ports. There is no need to assign mtu on a bridge' %ifaceobj.name)
-                retval = False
-            else:
-                self.logger.info('%s: bridge inherits mtu from its ports. There is no need to assign mtu on a bridge' %ifaceobj.name)
-        elif ifaceobj_getfunc:
-            if ((ifaceobj.link_privflags & ifaceLinkPrivFlags.BOND_SLAVE) and
-                ifaceobj.upperifaces):
-                masterobj = ifaceobj_getfunc(ifaceobj.upperifaces[0])
-                if masterobj:
-                    master_mtu = masterobj[0].get_attr_value_first('mtu')
-                    if master_mtu and master_mtu != mtu:
-                        if syntaxcheck:
-                            self.logger.warn('%s: bond slave mtu %s is different from bond master %s mtu %s. There is no need to configure mtu on a bond slave.' %(ifaceobj.name, mtu, masterobj[0].name, master_mtu))
-                            retval = False
-                        else:
-                            self.logger.info('%s: bond slave mtu %s is different from bond master %s mtu %s. There is no need to configure mtu on a bond slave.' %(ifaceobj.name, mtu, masterobj[0].name, master_mtu))
-            elif ((ifaceobj.link_kind & ifaceLinkKind.VLAN) and
-                  ifaceobj.lowerifaces):
-                lowerobj = ifaceobj_getfunc(ifaceobj.lowerifaces[0])
-                if lowerobj:
-                    if syntaxcheck:
-                        lowerdev_mtu = lowerobj[0].get_attr_value_first('mtu')
-                    else:
-                        lowerdev_mtu = self.ipcmd.link_get_mtu(lowerobj[0].name)
-                    if lowerdev_mtu and int(mtu) > int(lowerdev_mtu):
-                        self.logger.warn('%s: vlan dev mtu %s is greater than lower realdev %s mtu %s'
-                                         %(ifaceobj.name, mtu, lowerobj[0].name, lowerdev_mtu))
-                        retval = False
-                    elif (not lowerobj[0].link_kind and
-                          not (lowerobj[0].link_privflags & ifaceLinkPrivFlags.LOOPBACK) and
-                          self.default_mtu and (int(mtu) > int(self.default_mtu))):
-                        # only check default mtu on lower device which is a physical interface
-                        self.logger.warn('%s: vlan dev mtu %s is greater than lower realdev %s mtu %s'
-                                         %(ifaceobj.name, mtu, lowerobj[0].name, self.default_mtu))
-                        retval = False
-            if self.max_mtu and mtu > self.max_mtu:
-                self.logger.warn('%s: specified mtu %s is greater than max mtu %s'
-                                 %(ifaceobj.name, mtu, self.max_mtu))
-                retval = False
-        return retval
-
-    def _propagate_mtu_to_upper_devs(self, ifaceobj, mtu, ifaceobj_getfunc):
-        if (not ifaceobj.upperifaces or
-            (ifaceobj.link_privflags & ifaceLinkPrivFlags.BOND_SLAVE) or
-            (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE) or
-            (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT)):
-            return
-        for u in ifaceobj.upperifaces:
-            upperobjs = ifaceobj_getfunc(u)
-            if (not upperobjs or
-                not (upperobjs[0].link_kind & ifaceLinkKind.VLAN)):
-                continue
-            # only adjust mtu for vlan devices on ifaceobj
-            umtu = upperobjs[0].get_attr_value_first('mtu')
-            if not umtu:
-                running_mtu = self.ipcmd.link_get_mtu(upperobjs[0].name)
-                if not running_mtu or (running_mtu != mtu):
-                    self.ipcmd.link_set(u, 'mtu', mtu)
-
-    def _process_mtu_config(self, ifaceobj, ifaceobj_getfunc):
-        mtu = ifaceobj.get_attr_value_first('mtu')
-        if mtu:
-            if not self._check_mtu_config(ifaceobj, mtu, ifaceobj_getfunc):
-                return
-            running_mtu = self.ipcmd.link_get_mtu(ifaceobj.name)
-            if not running_mtu or (running_mtu and running_mtu != mtu):
-                self.ipcmd.link_set(ifaceobj.name, 'mtu', mtu)
-                if (not ifupdownflags.flags.ALL and
-                    not ifaceobj.link_kind and
-                    ifupdownConfig.config.get('adjust_logical_dev_mtu', '1') != '0'):
-                    # This is additional cost to us, so do it only when
-                    # ifupdown2 is called on a particular interface and
-                    # it is a physical interface
-                    self._propagate_mtu_to_upper_devs(ifaceobj, mtu, ifaceobj_getfunc)
-            return
-
-        if ifaceobj.link_kind:
-            # bonds and vxlan devices need an explicit set of mtu.
-            # bridges don't need mtu set
-            if (ifaceobj.link_kind & ifaceLinkKind.BOND or
-                ifaceobj.link_kind & ifaceLinkKind.VXLAN):
-                running_mtu = self.ipcmd.link_get_mtu(ifaceobj.name)
-                if (self.default_mtu and running_mtu != self.default_mtu):
-                    self.ipcmd.link_set(ifaceobj.name, 'mtu', self.default_mtu)
-                return
-            if (ifupdownConfig.config.get('adjust_logical_dev_mtu', '1') != '0'
-                and ifaceobj.lowerifaces):
-                # set vlan interface mtu to lower device mtu
-                if (ifaceobj.link_kind & ifaceLinkKind.VLAN):
-                    lower_iface_mtu = self.ipcmd.link_get_mtu(ifaceobj.lowerifaces[0], refresh=True)
-                    if not lower_iface_mtu == self.ipcmd.link_get_mtu(ifaceobj.name):
-                        self.ipcmd.link_set_mtu(ifaceobj.name, lower_iface_mtu)
-
-        elif (not (ifaceobj.name == 'lo') and not ifaceobj.link_kind and
-              not (ifaceobj.link_privflags & ifaceLinkPrivFlags.BOND_SLAVE) and
-              self.default_mtu):
-            # logical devices like bridges and vlan devices rely on mtu
-            # from their lower devices. ie mtu travels from
-            # lower devices to upper devices. For bonds mtu travels from
-            # upper to lower devices. running mtu depends on upper and
-            # lower device mtu. With all this implicit mtu
-            # config by the kernel in play, we try to be cautious here
-            # on which devices we want to reset mtu to default.
-            # essentially only physical interfaces which are not bond slaves
-            running_mtu = self.ipcmd.link_get_mtu(ifaceobj.name)
-            if running_mtu != self.default_mtu:
-                self.ipcmd.link_set(ifaceobj.name, 'mtu', self.default_mtu)
-
-    def _up(self, ifaceobj, ifaceobj_getfunc=None):
-        if not self.ipcmd.link_exists(ifaceobj.name):
-            return
-
-        alias = ifaceobj.get_attr_value_first('alias')
-        current_alias = self.ipcmd.link_get_alias(ifaceobj.name)
-        if alias and alias != current_alias:
-            self.ipcmd.link_set_alias(ifaceobj.name, alias)
-        elif not alias and current_alias:
-            self.ipcmd.link_set_alias(ifaceobj.name, '')
-
-        addr_method = ifaceobj.addr_method
-        force_reapply = False
-        try:
-            # release any stale dhcp addresses if present
-            if (addr_method not in ["dhcp", "ppp"] and not ifupdownflags.flags.PERFMODE and
-                    not (ifaceobj.flags & iface.HAS_SIBLINGS)):
-                # if not running in perf mode and ifaceobj does not have
-                # any sibling iface objects, kill any stale dhclient
-                # processes
-                dhclientcmd = dhclient()
-                if dhclientcmd.is_running(ifaceobj.name):
-                    # release any dhcp leases
-                    dhclientcmd.release(ifaceobj.name)
-                    force_reapply = True
-                elif dhclientcmd.is_running6(ifaceobj.name):
-                    dhclientcmd.release6(ifaceobj.name)
-                    force_reapply = True
-        except:
-            pass
-
-        self.ipcmd.batch_start()
-        if addr_method not in ["dhcp", "ppp"]:
-            self._inet_address_config(ifaceobj, ifaceobj_getfunc,
-                                      force_reapply)
-        self._process_mtu_config(ifaceobj, ifaceobj_getfunc)
-
-        try:
-            self.ipcmd.batch_commit()
-        except Exception as e:
-            self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj, raise_error=False)
-
-        try:
-            hwaddress = self._get_hwaddress(ifaceobj)
-            if hwaddress:
-                running_hwaddress = None
-                if not ifupdownflags.flags.PERFMODE: # system is clean
-                    running_hwaddress = self.ipcmd.link_get_hwaddress(ifaceobj.name)
-                if hwaddress != running_hwaddress:
-                    slave_down = False
-                    netlink.link_set_updown(ifaceobj.name, "down")
-                    if ifaceobj.link_kind & ifaceLinkKind.BOND:
-                        # if bond, down all the slaves
-                        if ifaceobj.lowerifaces:
-                            for l in ifaceobj.lowerifaces:
-                                netlink.link_set_updown(l, "down")
-                            slave_down = True
-                    try:
-                        self.ipcmd.link_set(ifaceobj.name, 'address', hwaddress)
-                    finally:
-                        netlink.link_set_updown(ifaceobj.name, "up")
-                        if slave_down:
-                            for l in ifaceobj.lowerifaces:
-                                netlink.link_set_updown(l, "up")
-
-            # Handle special things on a bridge
-            self._process_bridge(ifaceobj, True)
-        except Exception, e:
-            self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
-
-        if addr_method not in ["dhcp", "ppp"]:
-            gateways = ifaceobj.get_attr_value('gateway')
-            if not gateways:
-                gateways = []
-            prev_gw = self._get_prev_gateway(ifaceobj, gateways)
-            self._add_delete_gateway(ifaceobj, gateways, prev_gw)
-        return
-
-    def _down(self, ifaceobj, ifaceobj_getfunc=None):
-        try:
-            if not self.ipcmd.link_exists(ifaceobj.name):
-                return
-            addr_method = ifaceobj.addr_method
-            if addr_method not in ["dhcp", "ppp"]:
-                if ifaceobj.get_attr_value_first('address-purge')=='no':
-                    addrlist = ifaceobj.get_attr_value('address')
-                    for addr in addrlist:
-                        self.ipcmd.addr_del(ifaceobj.name, addr)
-                    #self.ipcmd.addr_del(ifaceobj.name, ifaceobj.get_attr_value('address')[0])
-                else:
-                    self.ipcmd.del_addr_all(ifaceobj.name)
-            mtu = ifaceobj.get_attr_value_first('mtu')
-            if (not ifaceobj.link_kind and mtu and
-                self.default_mtu and (mtu != self.default_mtu)):
-                self.ipcmd.link_set(ifaceobj.name, 'mtu', self.default_mtu)
-            alias = ifaceobj.get_attr_value_first('alias')
-            if alias:
-                filename = '/sys/class/net/%s/ifalias' %ifaceobj.name
-                self.logger.info('executing echo "" > %s' %filename)
-                os.system('echo "" > %s' %filename)
-            # XXX hwaddress reset cannot happen because we dont know last
-            # address.
-
-            # Handle special things on a bridge
-            self._process_bridge(ifaceobj, False)
-        except Exception, e:
-            self.logger.debug('%s : %s' %(ifaceobj.name, str(e)))
-            pass
-
-    def _get_iface_addresses(self, ifaceobj):
-        addrlist = ifaceobj.get_attr_value('address')
-        outaddrlist = []
-
-        if not addrlist: return None
-        for addrindex in range(0, len(addrlist)):
-            addr = addrlist[addrindex]
-            netmask = ifaceobj.get_attr_value_n('netmask', addrindex)
-            if netmask:
-                prefixlen = IPNetwork('%s' %addr +
-                                '/%s' %netmask).prefixlen
-                addr = addr + '/%s' %prefixlen
-            outaddrlist.append(addr)
-        return outaddrlist
-
-    def _get_bridge_fdbs(self, bridgename, vlan):
-        fdbs = self._bridge_fdb_query_cache.get(bridgename)
-        if not fdbs:
-           fdbs = self.ipcmd.bridge_fdb_show_dev(bridgename)
-           if not fdbs:
-              return
-           self._bridge_fdb_query_cache[bridgename] = fdbs
-        return fdbs.get(vlan)
-
-    def _check_addresses_in_bridge(self, ifaceobj, hwaddress):
-        """ If the device is a bridge, make sure the addresses
-        are in the bridge """
-        if '.' in ifaceobj.name:
-            (bridgename, vlan) = ifaceobj.name.split('.')
-            if self.ipcmd.bridge_is_vlan_aware(bridgename):
-                fdb_addrs = self._get_bridge_fdbs(bridgename, vlan)
-                if not fdb_addrs or hwaddress not in fdb_addrs:
-                   return False
-        return True
-
-    def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
-        runningaddrsdict = None
-        if not self.ipcmd.link_exists(ifaceobj.name):
-            self.logger.debug('iface %s not found' %ifaceobj.name)
-            return
-        addr_method = ifaceobj.addr_method
-        self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr,
-                'mtu', self.ipcmd.link_get_mtu)
-        hwaddress = self._get_hwaddress(ifaceobj)
-        if hwaddress:
-            rhwaddress = self.ipcmd.link_get_hwaddress(ifaceobj.name)
-            if not rhwaddress  or rhwaddress != hwaddress:
-               ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress,
-                       1)
-            elif not self._check_addresses_in_bridge(ifaceobj, hwaddress):
-               # XXX: hw address is not in bridge
-               ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress,
-                       1)
-               ifaceobjcurr.status_str = 'bridge fdb error'
-            else:
-               ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress,
-                       0)
-        self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr,
-                    'alias', self.ipcmd.link_get_alias)
-        # compare addresses
-        if addr_method in ["dhcp", "ppp"]:
-           return
-        addrs = utils.get_normalized_ip_addr(ifaceobj.name,
-                                             self._get_iface_addresses(ifaceobj))
-        runningaddrsdict = self.ipcmd.addr_get(ifaceobj.name)
-        # if anycast address is configured on 'lo' and is in running config
-        # add it to addrs so that query_check doesn't fail
-        anycast_addr = utils.get_normalized_ip_addr(ifaceobj.name, ifaceobj.get_attr_value_first('clagd-vxlan-anycast-ip'))
-        if anycast_addr:
-            anycast_addr = anycast_addr+'/32'
-        if runningaddrsdict and anycast_addr and runningaddrsdict.get(anycast_addr):
-            addrs.append(anycast_addr)
-
-        # Set ifaceobjcurr method and family
-        ifaceobjcurr.addr_method = ifaceobj.addr_method
-        ifaceobjcurr.addr_family = ifaceobj.addr_family
-        if not runningaddrsdict and not addrs:
-            return
-        runningaddrs = runningaddrsdict.keys() if runningaddrsdict else []
-        # Add /32 netmask to configured address without netmask.
-        # This may happen on interfaces where pointopoint is used.
-        runningaddrs = [ addr if '/' in addr else addr + '/32' for addr in runningaddrs]
-        if runningaddrs != addrs:
-            runningaddrsset = set(runningaddrs) if runningaddrs else set([])
-            addrsset = set(addrs) if addrs else set([])
-            if (ifaceobj.flags & iface.HAS_SIBLINGS):
-                if not addrsset:
-                    return
-                # only check for addresses present in running config
-                addrsdiff = addrsset.difference(runningaddrsset)
-                for addr in addrs:
-                    if addr in addrsdiff:
-                        ifaceobjcurr.update_config_with_status('address',
-                                    addr, 1)
-                    else:
-                        ifaceobjcurr.update_config_with_status('address',
-                                    addr, 0)
-            else:
-                addrsdiff = addrsset.symmetric_difference(runningaddrsset)
-                for addr in addrsset.union(runningaddrsset):
-                    if addr in addrsdiff:
-                        ifaceobjcurr.update_config_with_status('address',
-                                                               addr, 1)
-                    else:
-                        ifaceobjcurr.update_config_with_status('address',
-                                                               addr, 0)
-        elif addrs:
-            [ifaceobjcurr.update_config_with_status('address',
-                       addr, 0) for addr in addrs]
-        #XXXX Check broadcast address, scope, etc
-        return
-
-    def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
-        if not self.ipcmd.link_exists(ifaceobjrunning.name):
-            self.logger.debug('iface %s not found' %ifaceobjrunning.name)
-            return
-        dhclientcmd = dhclient()
-        if (dhclientcmd.is_running(ifaceobjrunning.name) or
-                dhclientcmd.is_running6(ifaceobjrunning.name)):
-            # If dhcp is configured on the interface, we skip it
-            return
-        isloopback = self.ipcmd.link_isloopback(ifaceobjrunning.name)
-        if isloopback:
-            default_addrs = ['127.0.0.1/8', '::1/128']
-            ifaceobjrunning.addr_family.append('inet')
-            ifaceobjrunning.addr_method = 'loopback'
-        else:
-            default_addrs = []
-        runningaddrsdict = self.ipcmd.addr_get(ifaceobjrunning.name)
-        if runningaddrsdict:
-            [ifaceobjrunning.update_config('address', addr)
-                for addr, addrattrs in runningaddrsdict.items()
-                if addr not in default_addrs]
-        mtu = self.ipcmd.link_get_mtu(ifaceobjrunning.name)
-        if (mtu and
-                (ifaceobjrunning.name == 'lo' and mtu != '16436') or
-                (ifaceobjrunning.name != 'lo' and
-                    mtu != self.get_mod_subattr('mtu', 'default'))):
-                ifaceobjrunning.update_config('mtu', mtu)
-        alias = self.ipcmd.link_get_alias(ifaceobjrunning.name)
-        if alias:
-            ifaceobjrunning.update_config('alias', alias)
-
-
-    _run_ops = {'up' : _up,
-               'down' : _down,
-               'query-checkcurr' : _query_check,
-               'query-running' : _query_running }
-
-    def get_ops(self):
-        """ returns list of ops supported by this module """
-        return self._run_ops.keys()
-
-    def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = iproute2()
-
-    def run(self, ifaceobj, operation, query_ifaceobj=None, ifaceobj_getfunc=None):
-        """ run address configuration on the interface object passed as argument
-
-        Args:
-            **ifaceobj** (object): iface object
-
-            **operation** (str): any of 'up', 'down', 'query-checkcurr',
-                                 'query-running'
-        Kwargs:
-            query_ifaceobj (object): query check ifaceobject. This is only
-                valid when op is 'query-checkcurr'. It is an object same as
-                ifaceobj, but contains running attribute values and its config
-                status. The modules can use it to return queried running state
-                of interfaces. status is success if the running state is same
-                as user required state in ifaceobj. error otherwise.
-        """
-        if ifaceobj.type == ifaceType.BRIDGE_VLAN:
-           return
-        op_handler = self._run_ops.get(operation)
-        if not op_handler:
-            return
-        self._init_command_handlers()
-        if operation == 'query-checkcurr':
-            op_handler(self, ifaceobj, query_ifaceobj,
-                       ifaceobj_getfunc=ifaceobj_getfunc)
-        else:
-            op_handler(self, ifaceobj,
-                       ifaceobj_getfunc=ifaceobj_getfunc)
diff --git a/addons/addressvirtual.py b/addons/addressvirtual.py
deleted file mode 100644 (file)
index fe32a2a..0000000
+++ /dev/null
@@ -1,501 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-
-from ifupdown.iface import *
-from ifupdownaddons.modulebase import moduleBase
-from ifupdownaddons.iproute2 import iproute2
-import ifupdown.ifupdownconfig as ifupdownConfig
-import ifupdown.statemanager as statemanager
-from ifupdown.netlink import netlink
-import ifupdown.ifupdownflags as ifupdownflags
-
-from ipaddr import IPNetwork, IPv4Network
-import logging
-import os
-import glob
-
-class addressvirtual(moduleBase):
-    """  ifupdown2 addon module to configure virtual addresses """
-
-    _modinfo = {'mhelp' : 'address module configures virtual addresses for ' +
-                          'interfaces. It creates a macvlan interface for ' +
-                          'every mac ip address-virtual line',
-                'attrs' : {
-                    'address-virtual' :
-                        { 'help' : 'bridge router virtual mac and ips',
-                          'multivalue' : True,
-                          'validvals' : ['<mac-ip/prefixlen-list>',],
-                          'example' : ['address-virtual 00:11:22:33:44:01 11.0.1.1/24 11.0.1.2/24']}
-                 }}
-
-
-    def __init__(self, *args, **kargs):
-        moduleBase.__init__(self, *args, **kargs)
-        self.ipcmd = None
-        self._bridge_fdb_query_cache = {}
-
-    def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
-        if ifaceobj.get_attr_value('address-virtual'):
-            ifaceobj.link_privflags |= ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE
-
-    def _get_macvlan_prefix(self, ifaceobj):
-        return '%s-v' %ifaceobj.name[0:13].replace('.', '-')
-
-    def _add_addresses_to_bridge(self, ifaceobj, hwaddress):
-        # XXX: batch the addresses
-        if '.' in ifaceobj.name:
-            (bridgename, vlan) = ifaceobj.name.split('.')
-            if self.ipcmd.bridge_is_vlan_aware(bridgename):
-                [self.ipcmd.bridge_fdb_add(bridgename, addr,
-                    vlan) for addr in hwaddress]
-        elif self.ipcmd.is_bridge(ifaceobj.name):
-            [self.ipcmd.bridge_fdb_add(ifaceobj.name, addr)
-                    for addr in hwaddress]
-
-    def _remove_addresses_from_bridge(self, ifaceobj, hwaddress):
-        # XXX: batch the addresses
-        if '.' in ifaceobj.name:
-            (bridgename, vlan) = ifaceobj.name.split('.')
-            if self.ipcmd.bridge_is_vlan_aware(bridgename):
-                for addr in hwaddress:
-                    try:
-                        self.ipcmd.bridge_fdb_del(bridgename, addr, vlan)
-                    except Exception, e:
-                        self.logger.debug("%s: %s" %(ifaceobj.name, str(e)))
-                        pass
-        elif self.ipcmd.is_bridge(ifaceobj.name):
-            for addr in hwaddress:
-                try:
-                    self.ipcmd.bridge_fdb_del(ifaceobj.name, addr)
-                except Exception, e:
-                    self.logger.debug("%s: %s" %(ifaceobj.name, str(e)))
-                    pass
-
-    def _get_bridge_fdbs(self, bridgename, vlan):
-        fdbs = self._bridge_fdb_query_cache.get(bridgename)
-        if not fdbs:
-           fdbs = self.ipcmd.bridge_fdb_show_dev(bridgename)
-           if not fdbs:
-              return
-           self._bridge_fdb_query_cache[bridgename] = fdbs
-        return fdbs.get(vlan)
-
-    def _check_addresses_in_bridge(self, ifaceobj, hwaddress):
-        """ If the device is a bridge, make sure the addresses
-        are in the bridge """
-        if '.' in ifaceobj.name:
-            (bridgename, vlan) = ifaceobj.name.split('.')
-            if self.ipcmd.bridge_is_vlan_aware(bridgename):
-                fdb_addrs = self._get_bridge_fdbs(bridgename, vlan)
-                if not fdb_addrs or hwaddress not in fdb_addrs:
-                   return False
-        return True
-
-    def _fix_connected_route(self, ifaceobj, vifacename, addr):
-        #
-        # XXX: Hack to make sure the primary address 
-        # is the first in the routing table.
-        #
-        # We use `ip route get` on the vrr network to see which
-        # device the kernel returns. if it is the mac vlan device,
-        # flap the macvlan device to adjust the routing table entry.
-        # 
-        # flapping the macvlan device makes sure the macvlan
-        # connected route goes through delete + add, hence adjusting
-        # the order in the routing table.
-        #
-        try:
-            self.logger.info('%s: checking route entry ...' %ifaceobj.name)
-            ip = IPNetwork(addr)
-            route_prefix = '%s/%d' %(ip.network, ip.prefixlen)
-
-            dev = self.ipcmd.ip_route_get_dev(route_prefix)
-            if dev and dev == vifacename:
-                self.logger.info('%s: preferred routing entry ' %ifaceobj.name +
-                                 'seems to be of the macvlan dev %s'
-                                 %vifacename +
-                                 ' .. flapping macvlan dev to fix entry.')
-                self.ipcmd.link_down(vifacename)
-                self.ipcmd.link_up(vifacename)
-        except Exception, e:
-            self.logger.debug('%s: fixing route entry failed (%s)'
-                              %str(e))
-            pass
-
-    def _handle_vrf_slaves(self, macvlan_ifacename, ifaceobj):
-        vrfname = self.ipcmd.link_get_master(ifaceobj.name)
-        if vrfname:
-            netlink.link_set_master(macvlan_ifacename, vrfname)
-
-    def _get_macs_from_old_config(self, ifaceobj=None):
-        """ This method returns a list of the mac addresses
-        in the address-virtual attribute for the bridge. """
-        maclist = []
-        saved_ifaceobjs = statemanager.statemanager_api.get_ifaceobjs(ifaceobj.name)
-        if not saved_ifaceobjs:
-            return maclist
-        # we need the old saved configs from the statemanager
-        for oldifaceobj in saved_ifaceobjs:
-            if not oldifaceobj.get_attr_value('address-virtual'):
-                continue
-            for av in oldifaceobj.get_attr_value('address-virtual'):
-                macip = av.split()
-                if len(macip) < 2:
-                    self.logger.debug("%s: incorrect old address-virtual attrs '%s'"
-                                      %(oldifaceobj.name,  av))
-                    continue
-                maclist.append(macip[0])
-        return maclist
-
-    def _apply_address_config(self, ifaceobj, address_virtual_list):
-        purge_existing = False if ifupdownflags.flags.PERFMODE else True
-
-        lower_iface_mtu = update_mtu = None
-        if ifupdownConfig.config.get('adjust_logical_dev_mtu', '1') != '0':
-            if ifaceobj.lowerifaces and address_virtual_list:
-                update_mtu = True
-
-        hwaddress = []
-        self.ipcmd.batch_start()
-        av_idx = 0
-        macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
-        for av in address_virtual_list:
-            av_attrs = av.split()
-            if len(av_attrs) < 2:
-                self.log_error("%s: incorrect address-virtual attrs '%s'"
-                               %(ifaceobj.name,  av), ifaceobj,
-                               raise_error=False)
-                av_idx += 1
-                continue
-
-            mac = av_attrs[0]
-            if not self.check_mac_address(ifaceobj, mac):
-                continue
-            # Create a macvlan device on this device and set the virtual
-            # router mac and ip on it
-            link_created = False
-            macvlan_ifacename = '%s%d' %(macvlan_prefix, av_idx)
-            if not self.ipcmd.link_exists(macvlan_ifacename):
-                netlink.link_add_macvlan(ifaceobj.name, macvlan_ifacename)
-                link_created = True
-            ips = av_attrs[1:]
-            if mac != 'None':
-                mac = mac.lower()
-                # customer could have used UPPERCASE for MAC
-                self.ipcmd.link_set_hwaddress(macvlan_ifacename, mac)
-                hwaddress.append(mac)
-            self.ipcmd.addr_add_multiple(macvlan_ifacename, ips,
-                                         purge_existing)
-
-            # If link existed before, flap the link
-            if not link_created:
-                self._fix_connected_route(ifaceobj, macvlan_ifacename,
-                                          ips[0])
-                if update_mtu:
-                    lower_iface_mtu = self.ipcmd.link_get_mtu(ifaceobj.name, refresh=True)
-                    update_mtu = False
-
-                if lower_iface_mtu and lower_iface_mtu != self.ipcmd.link_get_mtu(macvlan_ifacename):
-                    try:
-                        self.ipcmd.link_set_mtu(macvlan_ifacename,
-                                                lower_iface_mtu)
-                    except Exception as e:
-                        self.logger.info('%s: failed to set mtu %s: %s' %
-                                         (macvlan_ifacename, lower_iface_mtu, e))
-
-                # set macvlan device to up in anycase.
-                # since we auto create them here..we are responsible
-                # to bring them up here in the case they were brought down
-                # by some other entity in the system.
-                netlink.link_set_updown(macvlan_ifacename, "up")
-
-            # handle vrf slaves
-            if (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE):
-                self._handle_vrf_slaves(macvlan_ifacename, ifaceobj)
-
-            # Disable IPv6 duplicate address detection on VRR interfaces
-            for key, sysval in { 'accept_dad' : '0', 'dad_transmits' : '0' }.iteritems():
-                syskey = 'net.ipv6.conf.%s.%s' % (macvlan_ifacename, key)
-                if self.sysctl_get(syskey) != sysval:
-                    self.sysctl_set(syskey, sysval)
-
-            av_idx += 1
-        self.ipcmd.batch_commit()
-
-        # check the statemanager for old configs.
-        # We need to remove only the previously configured FDB entries
-        oldmacs = self._get_macs_from_old_config(ifaceobj)
-        # get a list of fdbs in old that are not in new config meaning they should
-        # be removed since they are gone from the config
-        removed_macs = [mac for mac in oldmacs if mac.lower() not in hwaddress]
-        self._remove_addresses_from_bridge(ifaceobj, removed_macs)
-        # if ifaceobj is a bridge and bridge is a vlan aware bridge
-        # add the vid to the bridge
-        self._add_addresses_to_bridge(ifaceobj, hwaddress)
-
-    def _remove_running_address_config(self, ifaceobj):
-        if not self.ipcmd.link_exists(ifaceobj.name):
-            return
-        hwaddress = []
-        self.ipcmd.batch_start()
-        macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
-        for macvlan_ifacename in glob.glob("/sys/class/net/%s*" %macvlan_prefix):
-            macvlan_ifacename = os.path.basename(macvlan_ifacename)
-            if not self.ipcmd.link_exists(macvlan_ifacename):
-                continue
-            hwaddress.append(self.ipcmd.link_get_hwaddress(macvlan_ifacename))
-            self.ipcmd.link_delete(os.path.basename(macvlan_ifacename))
-            # XXX: Also delete any fdb addresses. This requires, checking mac address
-            # on individual macvlan interfaces and deleting the vlan from that.
-        self.ipcmd.batch_commit()
-        if any(hwaddress):
-            self._remove_addresses_from_bridge(ifaceobj, hwaddress)
-
-    def _remove_address_config(self, ifaceobj, address_virtual_list=None):
-        if not address_virtual_list:
-            self._remove_running_address_config(ifaceobj)
-            return
-
-        if not self.ipcmd.link_exists(ifaceobj.name):
-            return
-        hwaddress = []
-        self.ipcmd.batch_start()
-        av_idx = 0
-        macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
-        for av in address_virtual_list:
-            av_attrs = av.split()
-            if len(av_attrs) < 2:
-                self.log_error("%s: incorrect address-virtual attrs '%s'"
-                               %(ifaceobj.name,  av), ifaceobj,
-                               raise_error=False)
-                av_idx += 1
-                continue
-
-            # Delete the macvlan device on this device
-            macvlan_ifacename = '%s%d' %(macvlan_prefix, av_idx)
-            self.ipcmd.link_delete(os.path.basename(macvlan_ifacename))
-            if av_attrs[0] != 'None':
-                hwaddress.append(av_attrs[0])
-            av_idx += 1
-        self.ipcmd.batch_commit()
-        self._remove_addresses_from_bridge(ifaceobj, hwaddress)
-
-    def check_mac_address(self, ifaceobj, mac):
-        if mac == 'None':
-            return True 
-        mac = mac.lower()
-        try:
-            if int(mac.split(":")[0], 16) & 1 :
-                self.logger.error("%s: Multicast bit is set in the virtual mac address '%s'" %(ifaceobj.name, mac))
-                return False
-            return True
-        except Exception:
-            return False
-
-    def _fixup_vrf_enslavements(self, ifaceobj, ifaceobj_getfunc=None):
-        """ This function fixes up address virtual interfaces
-        (macvlans) on vrf slaves. Since this fixup is an overhead,
-        this must be called only in cases when ifupdown2 is
-        called on the vrf device or its slave and not when
-        ifupdown2 is called for all devices. When all
-        interfaces are brought up, the expectation is that
-        the normal path will fix up a vrf device or its slaves"""
-
-        if not ifaceobj_getfunc:
-            return
-        if ((ifaceobj.link_kind & ifaceLinkKind.VRF) and
-            self.ipcmd.link_exists(ifaceobj.name)):
-            # if I am a vrf device and I have slaves
-            # that have address virtual config,
-            # enslave the slaves 'address virtual
-            # interfaces (macvlans)' to myself:
-            running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
-            if running_slaves:
-                # pick up any existing slaves of a vrf device and
-                # look for their upperdevices and enslave them to the
-                # vrf device:
-                for s in running_slaves:
-                    sobjs = ifaceobj_getfunc(s)
-                    if (sobjs and
-                        (sobjs[0].link_privflags & ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE)):
-                        # enslave all its upper devices to
-                        # the vrf device
-                        upperdevs = self.ipcmd.link_get_uppers(sobjs[0].name)
-                        if not upperdevs:
-                            continue
-                        for u in upperdevs:
-                            # skip vrf device which
-                            # will also show up in the
-                            # upper device list
-                            if u == ifaceobj.name:
-                                continue
-                            netlink.link_set_master(u, ifaceobj.name, state='up')
-        elif ((ifaceobj.link_privflags & ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE) and
-              (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE) and
-              self.ipcmd.link_exists(ifaceobj.name)):
-            # If I am a vrf slave and I have 'address virtual'
-            # config, make sure my addrress virtual interfaces
-            # (macvlans) are also enslaved to the vrf device
-            vrfname = ifaceobj.get_attr_value_first('vrf')
-            if not vrfname or not self.ipcmd.link_exists(vrfname):
-                return
-            running_uppers = self.ipcmd.link_get_uppers(ifaceobj.name)
-            if not running_uppers:
-                return
-            macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
-            if not macvlan_prefix:
-                return
-            for u in running_uppers:
-                if u == vrfname:
-                    continue
-                if u.startswith(macvlan_prefix):
-                    netlink.link_set_master(u, vrfname, state='up')
-
-    def _up(self, ifaceobj, ifaceobj_getfunc=None):
-        if not ifupdownflags.flags.ALL:
-            self._fixup_vrf_enslavements(ifaceobj, ifaceobj_getfunc)
-        address_virtual_list = ifaceobj.get_attr_value('address-virtual')
-        if not address_virtual_list:
-            # XXX: address virtual is not present. In which case,
-            # delete stale macvlan devices.
-            self._remove_address_config(ifaceobj, address_virtual_list)
-            return
-
-        if (ifaceobj.upperifaces and
-            not ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE):
-            self.log_error('%s: invalid placement of address-virtual lines (must be configured under an interface with no upper interfaces or parent interfaces)'
-                % (ifaceobj.name), ifaceobj)
-            return
-
-        if not self.ipcmd.link_exists(ifaceobj.name):
-            return
-        self._apply_address_config(ifaceobj, address_virtual_list)
-
-    def _down(self, ifaceobj, ifaceobj_getfunc=None):
-        try:
-            self._remove_address_config(ifaceobj,
-                         ifaceobj.get_attr_value('address-virtual'))
-        except Exception, e:
-            self.log_warn(str(e))
-
-    def _query_check(self, ifaceobj, ifaceobjcurr):
-        address_virtual_list = ifaceobj.get_attr_value('address-virtual')
-        if not address_virtual_list:
-            return
-        if not self.ipcmd.link_exists(ifaceobj.name):
-            return
-        av_idx = 0
-        macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
-        for address_virtual in address_virtual_list:
-            av_attrs = address_virtual.split()
-            if len(av_attrs) < 2:
-                self.logger.warn("%s: incorrect address-virtual attrs '%s'"
-                             %(ifaceobj.name,  address_virtual))
-                av_idx += 1
-                continue
-
-            # Check if the macvlan device on this interface
-            macvlan_ifacename = '%s%d' %(macvlan_prefix, av_idx)
-            if not self.ipcmd.link_exists(macvlan_ifacename):
-                ifaceobjcurr.update_config_with_status('address-virtual',
-                            '', 1)
-                av_idx += 1
-                continue
-            # Check mac and ip address
-            rhwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename)
-            raddrs = self.ipcmd.addr_get(macvlan_ifacename)
-            if not raddrs or not rhwaddress:
-               ifaceobjcurr.update_config_with_status('address-virtual', '', 1)
-               av_idx += 1
-               continue
-            raddrs = raddrs.keys()
-            try:
-                av_attrs[0] = ':'.join([i if len(i) == 2 else '0%s' % i
-                                        for i in av_attrs[0].split(':')])
-            except:
-                self.logger.info('%s: %s: invalid value for address-virtual (%s)'
-                                 % (ifaceobj.name,
-                                    macvlan_ifacename,
-                                    ' '.join(av_attrs)))
-            try:
-                cmp_av_addr = av_attrs[1:][0]
-                cmp_raddr = raddrs[0]
-
-                if '/' in cmp_raddr and '/' not in cmp_av_addr:
-                    cmp_av_addr = str(IPNetwork(cmp_av_addr))
-                elif '/' in cmp_av_addr and '/' not in cmp_raddr:
-                    cmp_raddr = str(IPNetwork(cmp_raddr))
-
-                if (rhwaddress == av_attrs[0] and cmp_raddr == cmp_av_addr and
-                        self._check_addresses_in_bridge(ifaceobj, av_attrs[0])):
-                    ifaceobjcurr.update_config_with_status('address-virtual',
-                                                           address_virtual, 0)
-                else:
-                    raddress_virtual = '%s %s' % (rhwaddress, ' '.join(raddrs))
-                    ifaceobjcurr.update_config_with_status('address-virtual',
-                                                           raddress_virtual, 1)
-            except:
-                raddress_virtual = '%s %s' % (rhwaddress, ' '.join(raddrs))
-                ifaceobjcurr.update_config_with_status('address-virtual',
-                                                       raddress_virtual, 1)
-            av_idx += 1
-        return
-
-    def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
-        macvlan_prefix = self._get_macvlan_prefix(ifaceobjrunning)
-        address_virtuals = glob.glob("/sys/class/net/%s*" %macvlan_prefix)
-        for av in address_virtuals:
-            macvlan_ifacename = os.path.basename(av)
-            rhwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename)
-            raddress = self.ipcmd.addr_get(macvlan_ifacename)
-            if not raddress:
-                self.logger.warn('%s: no running addresses'
-                                 %ifaceobjrunning.name)
-                raddress = []
-            ifaceobjrunning.update_config('address-virtual',
-                            '%s %s' %(rhwaddress, ''.join(raddress)))
-        return
-
-    _run_ops = {'up' : _up,
-               'down' : _down,
-               'query-checkcurr' : _query_check,
-               'query-running' : _query_running}
-
-    def get_ops(self):
-        """ returns list of ops supported by this module """
-        return self._run_ops.keys()
-
-    def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = iproute2()
-
-    def run(self, ifaceobj, operation, query_ifaceobj=None,
-            ifaceobj_getfunc=None, **extra_args):
-        """ run vlan configuration on the interface object passed as argument
-
-        Args:
-            **ifaceobj** (object): iface object
-
-            **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
-                                 'query-running'
-        Kwargs:
-            **query_ifaceobj** (object): query check ifaceobject. This is only
-                valid when op is 'query-checkcurr'. It is an object same as
-                ifaceobj, but contains running attribute values and its config
-                status. The modules can use it to return queried running state
-                of interfaces. status is success if the running state is same
-                as user required state in ifaceobj. error otherwise.
-        """
-        if ifaceobj.type == ifaceType.BRIDGE_VLAN:
-            return
-        op_handler = self._run_ops.get(operation)
-        if not op_handler:
-            return
-        self._init_command_handlers()
-        if operation == 'query-checkcurr':
-            op_handler(self, ifaceobj, query_ifaceobj)
-        else:
-            op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)
diff --git a/addons/batman_adv.py b/addons/batman_adv.py
deleted file mode 100644 (file)
index 0fda862..0000000
+++ /dev/null
@@ -1,344 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2016-2017 Maximilian Wilhelm <max@sdn.clinic>
-# Author: Maximilian Wilhelm, max@sdn.clinic
-#
-
-from ifupdown.iface import *
-from ifupdownaddons.modulebase import moduleBase
-from ifupdownaddons.iproute2 import iproute2
-from ifupdown.netlink import netlink
-import ifupdown.ifupdownflags as ifupdownflags
-import logging
-import re
-import subprocess
-
-
-class batman_adv (moduleBase):
-    """  ifupdown2 addon module to configure B.A.T.M.A.N. advanced interfaces """
-
-    _modinfo = {
-        'mhelp' : 'batman_adv module configures B.A.T.M.A.N. advanced interfaces.' +
-                  'Every B.A.T.M.A.N. advanced interface needs at least on ethernet ' +
-                  'interface to be creatable. You can specify a space separated list' +
-                  'of interfaces by using the "batma-ifaces" paramater. If this parameter' +
-                  'is set for an interfaces this module will do the magic.',
-
-        'attrs' : {
-            'batman-ifaces' : {
-                'help' : 'Interfaces to be part of this B.A.T.M.A.N. advanced instance',
-               'validvals' : [ '<interface-list>' ],
-                'required' : True,
-            },
-
-            'batman-ifaces-ignore-regex' : {
-                'help' : 'Interfaces to ignore when verifying configuration (regexp)',
-                'required' : False,
-            },
-
-            'batman-distributed-arp-table' : {
-                'help' : 'B.A.T.M.A.N. distributed ARP table',
-                'validvals' : [ 'enabled', 'disabled' ],
-                'required' : False,
-                'batman-attr' : True,
-            },
-
-            'batman-gw-mode' : {
-                'help' : 'B.A.T.M.A.N. gateway mode',
-                'validvals' : [ 'off', 'client', 'server' ],
-                'required' : False,
-                'example' : [ 'batman-gw-mode client' ],
-                'batman-attr' : True,
-            },
-
-            'batman-hop-penalty' : {
-                'help' : 'B.A.T.M.A.N. hop penalty',
-                'validvals' : [ '<number>' ],
-                'required' : False,
-                'batman-attr' : True,
-            },
-
-            'batman-multicast-mode' : {
-                'help' : 'B.A.T.M.A.N. multicast mode',
-                'validvals' : [ 'enabled', 'disabled' ],
-                'required' : False,
-                'batman-attr' : True,
-            },
-        }
-    }
-
-    _batman_attrs = {
-    }
-
-
-    def __init__ (self, *args, **kargs):
-        moduleBase.__init__ (self, *args, **kargs)
-        self.ipcmd = None
-
-        for longname, entry in self._modinfo['attrs'].items ():
-            if entry.get ('batman-attr', False) == False:
-                continue
-
-            attr = longname.replace ("batman-", "")
-            self._batman_attrs[attr] = {
-                 'filename' : attr.replace ("-", "_"),
-            }
-
-
-    def _is_batman_device (self, ifaceobj):
-        if ifaceobj.get_attr_value_first ('batman-ifaces'):
-            return True
-        return False
-
-
-    def _get_batman_ifaces (self, ifaceobj ):
-        batman_ifaces = ifaceobj.get_attr_value_first ('batman-ifaces')
-        if batman_ifaces:
-            return sorted (batman_ifaces.split ())
-        return None
-
-
-    def _get_batman_ifaces_ignore_regex (self, ifaceobj):
-        ifaces_ignore_regex = ifaceobj.get_attr_value_first ('batman-ifaces-ignore-regex')
-        if ifaces_ignore_regex:
-            return re.compile (r"%s" % ifaces_ignore_regex)
-        return None
-
-
-    def _get_batman_attr (self, ifaceobj, attr):
-        if attr not in self._batman_attrs:
-            raise ValueError ("_get_batman_attr: Invalid or unsupported B.A.T.M.A.N. adv. attribute: %s" % attr)
-
-        value = ifaceobj.get_attr_value_first ('batman-%s' % attr)
-        if value:
-            return value
-
-        return None
-
-
-    def _read_current_batman_attr (self, ifaceobj, attr):
-        if attr not in self._batman_attrs:
-            raise ValueError ("_read_current_batman_attr: Invalid or unsupported B.A.T.M.A.N. adv. attribute: %s" % attr)
-
-        attr_file_name = self._batman_attrs[attr]['filename']
-        attr_file_path = "/sys/class/net/%s/mesh/%s" % (ifaceobj.name, attr_file_name)
-        try:
-            with open (attr_file_path, "r") as fh:
-                 return fh.readline ().strip ()
-        except IOError as i:
-            raise Exception ("_read_current_batman_attr (%s) %s" % (attr, i))
-        except ValueError:
-            raise Exception ("_read_current_batman_attr: Integer value expected, got: %s" % value)
-
-
-    def _set_batman_attr (self, ifaceobj, attr, value):
-        if attr not in self._batman_attrs:
-            raise ValueError ("_set_batman_attr: Invalid or unsupported B.A.T.M.A.N. adv. attribute: %s" % attr)
-
-        attr_file_name = self._batman_attrs[attr]['filename']
-        attr_file_path = "/sys/class/net/%s/mesh/%s" % (ifaceobj.name, attr_file_name)
-        try:
-            with open (attr_file_path, "w") as fh:
-                 fh.write ("%s\n" % value)
-        except IOError as i:
-            raise Exception ("_set_batman_attr (%s): %s" % (attr, i))
-
-
-    def _batctl_if (self, bat_iface, mesh_iface, op):
-        if op not in [ 'add', 'del' ]:
-            raise Exception ("_batctl_if() called with invalid \"op\" value: %s" % op)
-
-        try:
-            self.logger.debug ("Running batctl -m %s if %s %s" % (bat_iface, op, mesh_iface))
-            batctl_output = subprocess.check_output (["batctl", "-m", bat_iface, "if", op, mesh_iface], stderr = subprocess.STDOUT)
-        except subprocess.CalledProcessError as c:
-            raise Exception ("Command \"batctl -m %s if %s %s\" failed: %s" % (bat_iface, op, mesh_iface, c.output))
-        except Exception as e:
-            raise Exception ("_batctl_if: %s" % e)
-
-
-    def _find_member_ifaces (self, ifaceobj, ignore = True):
-        members = []
-        iface_ignore_re = self._get_batman_ifaces_ignore_regex (ifaceobj)
-        batctl_fh = subprocess.Popen (["batctl", "-m", ifaceobj.name, "if"], bufsize = 4194304, stdout = subprocess.PIPE).stdout
-        for line in batctl_fh.readlines ():
-            iface = line.split (':')[0]
-            if iface_ignore_re and iface_ignore_re.match (iface) and ignore:
-                 continue
-
-            members.append (iface)
-
-        return sorted (members)
-
-
-    def get_dependent_ifacenames (self, ifaceobj, ifaceobjs_all=None):
-        if not self._is_batman_device (ifaceobj):
-            return None
-
-        ifaceobj.link_kind |= ifaceLinkKind.BATMAN_ADV
-        batman_ifaces = self._get_batman_ifaces (ifaceobj)
-        if batman_ifaces:
-            return batman_ifaces
-
-        return [None]
-
-
-    def _up (self, ifaceobj):
-        if self._get_batman_ifaces (ifaceobj) == None:
-            raise Exception ('could not determine batman interfacaes')
-
-        # Verify existance of batman interfaces (should be present already)
-        batman_ifaces = []
-        for iface in self._get_batman_ifaces (ifaceobj):
-            if not self.ipcmd.link_exists (iface):
-                self.logger.warn ('batman iface %s not present' % iface)
-                continue
-
-            batman_ifaces.append (iface)
-
-        if len (batman_ifaces) == 0:
-            raise Exception ("None of the configured batman interfaces are available!")
-
-        if_ignore_re = self._get_batman_ifaces_ignore_regex (ifaceobj)
-        # Is the batman main interface already present?
-        if self.ipcmd.link_exists (ifaceobj.name):
-            # Verify which member interfaces are present
-            members = self._find_member_ifaces (ifaceobj)
-            for iface in members:
-                if iface not in batman_ifaces:
-                    self._batctl_if (ifaceobj.name, iface, 'del')
-            for iface in batman_ifaces:
-                if iface not in members:
-                    self._batctl_if (ifaceobj.name, iface, 'add')
-
-        # Batman interfaces no present, add member interfaces to create it
-        else:
-            for iface in batman_ifaces:
-                self._batctl_if (ifaceobj.name, iface, 'add')
-
-        # Check/set any B.A.T.M.A.N. adv. set within interface configuration
-        for attr in self._batman_attrs:
-            value_cfg = self._get_batman_attr (ifaceobj, attr)
-            if value_cfg and value_cfg != self._read_current_batman_attr (ifaceobj, attr):
-                self._set_batman_attr (ifaceobj, attr, value_cfg)
-
-        if ifaceobj.addr_method == 'manual':
-            netlink.link_set_updown(ifaceobj.name, "up")
-
-
-
-    def _down (self, ifaceobj):
-        if not ifupdownflags.flags.PERFMODE and not self.ipcmd.link_exists (ifaceobj.name):
-           return
-
-        members = self._find_member_ifaces (ifaceobj)
-        for iface in members:
-            self._batctl_if (ifaceobj.name, iface, 'del')
-
-        # The main interface will automagically vanish after the last member
-        # interface has been deleted.
-
-
-    def _query_check (self, ifaceobj, ifaceobjcurr):
-        if not self.ipcmd.link_exists (ifaceobj.name):
-            return
-
-        batman_ifaces_cfg = self._get_batman_ifaces (ifaceobj)
-        batman_ifaces_real = self._find_member_ifaces (ifaceobj, False)
-        # Produce list of all current interfaces, tag interfaces ignored by
-        # regex with () around the iface name.
-        batman_ifaces_real_tagged = []
-        iface_ignore_re_str = ifaceobj.get_attr_value_first ('batman-ifaces-ignore-regex')
-        iface_ignore_re = self._get_batman_ifaces_ignore_regex (ifaceobj)
-
-        # Assume everything's fine and wait for reality to prove us otherwise
-        ifaces_ok = 0
-
-        # Interfaces configured but not active?
-        for iface in batman_ifaces_cfg:
-            if iface not in batman_ifaces_real:
-                 ifaces_ok = 1
-
-        # Interfaces active but not configured (or ignored)?
-        for iface in batman_ifaces_real:
-            if iface not in batman_ifaces_cfg:
-                 if iface_ignore_re and iface_ignore_re.match (iface):
-                     batman_ifaces_real_tagged.append ("(%s)" % iface)
-                     continue
-                 ifaces_ok = 1
-            else:
-                batman_ifaces_real_tagged.append (iface)
-
-        # Produce sorted list of active and ignored interfaces
-        ifaces_str = " ".join (batman_ifaces_real_tagged)
-        ifaceobjcurr.update_config_with_status ('batman-ifaces', ifaces_str, ifaces_ok)
-        ifaceobjcurr.update_config_with_status ('batman-ifaces-ignore-regex', iface_ignore_re_str, 0)
-
-        # Check any B.A.T.M.A.N. adv. set within interface configuration
-        for attr in self._batman_attrs:
-            value_cfg = self._get_batman_attr (ifaceobj, attr)
-            value_curr = self._read_current_batman_attr (ifaceobj, attr)
-
-            # Ignore this attribute if its'nt configured for this interface
-            if not value_cfg:
-                continue
-
-            value_ok = 0
-            if value_cfg != value_curr:
-                value_ok = 1
-
-            ifaceobjcurr.update_config_with_status ('batman-%s' % attr, value_curr, value_ok)
-
-
-    def _query_running (self, ifaceobjrunning):
-        if not self.ipcmd.link_exists (ifaceobjrunning.name):
-            return
-
-        # XXX Now what?
-
-
-    _run_ops = {'pre-up' : _up,
-               'post-down' : _down,
-               'query-checkcurr' : _query_check}
-# XXX              'query-running' : _query_running}
-
-
-    def get_ops (self):
-        """ returns list of ops supported by this module """
-        return self._run_ops.keys ()
-
-
-    def _init_command_handlers (self):
-        if not self.ipcmd:
-            self.ipcmd = iproute2()
-
-
-    def run (self, ifaceobj, operation, query_ifaceobj = None, **extra_args):
-        """ run B.A.T.M.A.N. configuration on the interface object passed as argument
-
-        Args:
-            **ifaceobj** (object): iface object
-
-            **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
-                                 'query-running'
-        Kwargs:
-            **query_ifaceobj** (object): query check ifaceobject. This is only
-                valid when op is 'query-checkcurr'. It is an object same as
-                ifaceobj, but contains running attribute values and its config
-                status. The modules can use it to return queried running state
-                of interfaces. status is success if the running state is same
-                as user required state in ifaceobj. error otherwise.
-        """
-        op_handler = self._run_ops.get (operation)
-        if not op_handler:
-            return
-
-        if (operation != 'query-running' and not self._is_batman_device (ifaceobj)):
-            return
-
-        self._init_command_handlers ()
-
-        if operation == 'query-checkcurr':
-            op_handler (self, ifaceobj, query_ifaceobj)
-        else:
-            op_handler (self, ifaceobj)
diff --git a/addons/bond.py b/addons/bond.py
deleted file mode 100644 (file)
index 0516b6b..0000000
+++ /dev/null
@@ -1,485 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-
-from sets import Set
-from ifupdown.iface import *
-import ifupdownaddons
-from ifupdownaddons.modulebase import moduleBase
-from ifupdownaddons.bondutil import bondutil
-from ifupdownaddons.iproute2 import iproute2
-from ifupdown.netlink import netlink
-import ifupdown.policymanager as policymanager
-import ifupdown.ifupdownflags as ifupdownflags
-from ifupdown.utils import utils
-
-class bond(moduleBase):
-    """  ifupdown2 addon module to configure bond interfaces """
-
-    overrides_ifupdown_scripts = ['ifenslave', ]
-
-    _modinfo = { 'mhelp' : 'bond configuration module',
-                    'attrs' : {
-                    'bond-use-carrier':
-                         {'help' : 'bond use carrier',
-                          'validvals' : ['yes', 'no', '0', '1'],
-                          'default' : 'yes',
-                          'example': ['bond-use-carrier yes']},
-                     'bond-num-grat-arp':
-                         {'help' : 'bond use carrier',
-                          'validrange' : ['0', '255'],
-                          'default' : '1',
-                          'example' : ['bond-num-grat-arp 1']},
-                     'bond-num-unsol-na' :
-                         {'help' : 'bond slave devices',
-                          'validrange' : ['0', '255'],
-                          'default' : '1',
-                          'example' : ['bond-num-unsol-na 1']},
-                     'bond-xmit-hash-policy' :
-                         {'help' : 'bond slave devices',
-                          'validvals' : ['layer2', 'layer3+4', 'layer2+3'],
-                          'default' : 'layer2',
-                          'example' : ['bond-xmit-hash-policy layer2']},
-                     'bond-miimon' :
-                         {'help' : 'bond miimon',
-                          'validrange' : ['0', '255'],
-                          'default' : '0',
-                          'example' : ['bond-miimon 0']},
-                     'bond-mode' :
-                         {'help': 'bond mode',
-                          'validvals': ['0', 'balance-rr',
-                                        '1', 'active-backup',
-                                        '2', 'balance-xor',
-                                        '3', 'broadcast',
-                                        '4', '802.3ad',
-                                        '5', 'balance-tlb',
-                                        '6', 'balance-alb'],
-                          'default': 'balance-rr',
-                          'example': ['bond-mode 802.3ad']},
-                     'bond-lacp-rate':
-                         {'help' : 'bond lacp rate',
-                          'validvals' : ['0', '1'],
-                          'default' : '0',
-                          'example' : ['bond-lacp-rate 0']},
-                     'bond-min-links':
-                         {'help' : 'bond min links',
-                          'default' : '0',
-                          'validrange' : ['0', '255'],
-                          'example' : ['bond-min-links 0']},
-                     'bond-ad-sys-priority':
-                         {'help' : '802.3ad system priority',
-                          'default' : '65535',
-                          'validrange' : ['0', '65535'],
-                          'example' : ['bond-ad-sys-priority 65535'],
-                          'deprecated' : True,
-                          'new-attribute' : 'bond-ad-actor-sys-prio'},
-                     'bond-ad-actor-sys-prio':
-                         {'help' : '802.3ad system priority',
-                          'default' : '65535',
-                          'validrange' : ['0', '65535'],
-                          'example' : ['bond-ad-actor-sys-prio 65535']},
-                     'bond-ad-sys-mac-addr':
-                         {'help' : '802.3ad system mac address',
-                          'default' : '00:00:00:00:00:00',
-                          'validvals': ['<mac>', ],
-                         'example' : ['bond-ad-sys-mac-addr 00:00:00:00:00:00'],
-                         'deprecated' : True,
-                         'new-attribute' : 'bond-ad-actor-system'},
-                     'bond-ad-actor-system':
-                         {'help' : '802.3ad system mac address',
-                          'default' : '00:00:00:00:00:00',
-                          'validvals': ['<mac>', ],
-                         'example' : ['bond-ad-actor-system 00:00:00:00:00:00'],},
-                     'bond-lacp-bypass-allow':
-                         {'help' : 'allow lacp bypass',
-                          'validvals' : ['yes', 'no', '0', '1'],
-                          'default' : 'no',
-                          'example' : ['bond-lacp-bypass-allow no']},
-                     'bond-slaves' :
-                        {'help' : 'bond slaves',
-                         'required' : True,
-                         'multivalue' : True,
-                         'validvals': ['<interface-list>'],
-                         'example' : ['bond-slaves swp1 swp2',
-                                      'bond-slaves glob swp1-2',
-                                      'bond-slaves regex (swp[1|2)'],
-                         'aliases': ['bond-ports']},
-                     'bond-updelay' :
-                        {'help' : 'bond updelay',
-                         'default' : '0',
-                         'validrange' : ['0', '65535'],
-                         'example' : ['bond-updelay 100']},
-                     'bond-downdelay':
-                        {'help' : 'bond downdelay',
-                         'default' : '0',
-                         'validrange' : ['0', '65535'],
-                         'example' : ['bond-downdelay 100']}
-                    }}
-
-    _bond_mode_num = {'0': 'balance-rr',
-                      '1': 'active-backup',
-                      '2': 'balance-xor',
-                      '3': 'broadcast',
-                      '4': '802.3ad',
-                      '5': 'balance-tlb',
-                      '6': 'balance-alb'}
-
-    _bond_mode_string = {'balance-rr': '0',
-                         'active-backup': '1',
-                         'balance-xor': '2',
-                         'broadcast': '3',
-                         '802.3ad': '4',
-                         'balance-tlb': '5',
-                         'balance-alb': '6'}
-
-    @staticmethod
-    def _get_readable_bond_mode(mode):
-        if mode in bond._bond_mode_num:
-            return bond._bond_mode_num[mode]
-        return mode
-
-    @staticmethod
-    def _get_num_bond_mode(mode):
-        if mode in bond._bond_mode_string:
-            return bond._bond_mode_string[mode]
-        return mode
-
-    def __init__(self, *args, **kargs):
-        ifupdownaddons.modulebase.moduleBase.__init__(self, *args, **kargs)
-        self.ipcmd = None
-        self.bondcmd = None
-
-    def get_bond_slaves(self, ifaceobj):
-        slaves = ifaceobj.get_attr_value_first('bond-slaves')
-        if not slaves:
-            slaves = ifaceobj.get_attr_value_first('bond-ports')
-        return slaves
-
-    def _is_bond(self, ifaceobj):
-        if self.get_bond_slaves(ifaceobj):
-            return True
-        return False
-
-    def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
-        """ Returns list of interfaces dependent on ifaceobj """
-
-        if not self._is_bond(ifaceobj):
-            return None
-        slave_list = self.parse_port_list(ifaceobj.name,
-                                          self.get_bond_slaves(ifaceobj),
-                                          ifacenames_all)
-        ifaceobj.dependency_type = ifaceDependencyType.MASTER_SLAVE
-        # Also save a copy for future use
-        ifaceobj.priv_data = list(slave_list)
-        if ifaceobj.link_type != ifaceLinkType.LINK_NA:
-           ifaceobj.link_type = ifaceLinkType.LINK_MASTER
-        ifaceobj.link_kind |= ifaceLinkKind.BOND
-        ifaceobj.role |= ifaceRole.MASTER
-
-        return slave_list
-
-    def get_dependent_ifacenames_running(self, ifaceobj):
-        self._init_command_handlers()
-        return self.bondcmd.get_slaves(ifaceobj.name)
-
-    def _get_slave_list(self, ifaceobj):
-        """ Returns slave list present in ifaceobj config """
-
-        # If priv data already has slave list use that first.
-        if ifaceobj.priv_data:
-            return ifaceobj.priv_data
-        slaves = self.get_bond_slaves(ifaceobj)
-        if slaves:
-            return self.parse_port_list(ifaceobj.name, slaves)
-        else:
-            return None
-
-    def _is_clag_bond(self, ifaceobj):
-        if self.get_bond_slaves(ifaceobj):
-            attrval = ifaceobj.get_attr_value_first('clag-id')
-            if attrval and attrval != '0':
-                return True
-        return False
-
-    def fetch_attr(self, ifaceobj, attrname):
-        attrval = ifaceobj.get_attr_value_first(attrname)
-        # grab the defaults from the policy file in case the
-        # user did not specify something.
-        policy_default_val = policymanager.policymanager_api.\
-                             get_iface_default(module_name=self.__class__.__name__,
-                                               ifname=ifaceobj.name,
-                                               attr=attrname)
-        if attrval:
-            if attrname == 'bond-mode':
-                attrval = bond._get_readable_bond_mode(attrval)
-                if attrval == '802.3ad':
-                   dattrname = 'bond-min-links'
-                   min_links = ifaceobj.get_attr_value_first(dattrname)
-                   if not min_links:
-                       min_links = self.bondcmd.get_min_links(ifaceobj.name)
-                   if min_links == '0':
-                       self.logger.warn('%s: attribute %s'
-                            %(ifaceobj.name, dattrname) +
-                            ' is set to \'0\'')
-        elif policy_default_val:
-            return policy_default_val
-        return attrval
-
-    def _apply_master_settings(self, ifaceobj):
-        have_attrs_to_set = 0
-        linkup = False
-        bondcmd_attrmap =  OrderedDict([('bond-mode' , 'mode'),
-                                 ('bond-miimon' , 'miimon'),
-                                 ('bond-use-carrier', 'use_carrier'),
-                                 ('bond-lacp-rate' , 'lacp_rate'),
-                                 ('bond-xmit-hash-policy' , 'xmit_hash_policy'),
-                                 ('bond-min-links' , 'min_links'),
-                                 ('bond-num-grat-arp' , 'num_grat_arp'),
-                                 ('bond-num-unsol-na' , 'num_unsol_na'),
-                                 ('bond-ad-sys-mac-addr' , 'ad_actor_system'),
-                                 ('bond-ad-actor-system' , 'ad_actor_system'),
-                                 ('bond-ad-sys-priority' , 'ad_actor_sys_prio'),
-                                 ('bond-ad-actor-sys-prio' , 'ad_actor_sys_prio'),
-                                 ('bond-lacp-bypass-allow', 'lacp_bypass'),
-                                 ('bond-updelay', 'updelay'),
-                                 ('bond-downdelay', 'downdelay')])
-        linkup = self.ipcmd.is_link_up(ifaceobj.name)
-        try:
-            # order of attributes set matters for bond, so
-            # construct the list sequentially
-            attrstoset = OrderedDict()
-            for k, dstk in bondcmd_attrmap.items():
-                v = self.fetch_attr(ifaceobj, k)
-                if v:
-                    attrstoset[dstk] = v
-            if not attrstoset:
-                return
-
-            # support yes/no attrs
-            utils.support_yesno_attrs(attrstoset, ['use_carrier', 'lacp_bypass'])
-
-            have_attrs_to_set = 1
-            self.bondcmd.set_attrs(ifaceobj.name, attrstoset,
-                    self.ipcmd.link_down if linkup else None)
-        except:
-            raise
-        finally:
-            if have_attrs_to_set and linkup:
-                netlink.link_set_updown(ifaceobj.name, 'up')
-
-    def _add_slaves(self, ifaceobj):
-        runningslaves = []
-
-        slaves = self._get_slave_list(ifaceobj)
-        if not slaves:
-            self.logger.debug('%s: no slaves found' %ifaceobj.name)
-            return
-
-        if not ifupdownflags.flags.PERFMODE:
-            runningslaves = self.bondcmd.get_slaves(ifaceobj.name);
-
-        clag_bond = self._is_clag_bond(ifaceobj)
-
-        for slave in Set(slaves).difference(Set(runningslaves)):
-            if (not ifupdownflags.flags.PERFMODE and
-                not self.ipcmd.link_exists(slave)):
-                    self.log_error('%s: skipping slave %s, does not exist'
-                                   %(ifaceobj.name, slave), ifaceobj,
-                                     raise_error=False)
-                    continue
-            link_up = False
-            if self.ipcmd.is_link_up(slave):
-                netlink.link_set_updown(slave, "down")
-                link_up = True
-            # If clag bond place the slave in a protodown state; clagd
-            # will protoup it when it is ready
-            if clag_bond:
-                try:
-                    netlink.link_set_protodown(slave, "on")
-                except Exception, e:
-                    self.logger.error('%s: %s' % (ifaceobj.name, str(e)))
-            netlink.link_set_master(slave, ifaceobj.name)
-            if link_up or ifaceobj.link_type != ifaceLinkType.LINK_NA:
-               try:
-                    netlink.link_set_updown(slave, "up")
-               except Exception, e:
-                    self.logger.debug('%s: %s' % (ifaceobj.name, str(e)))
-                    pass
-
-        if runningslaves:
-            for s in runningslaves:
-                if s not in slaves:
-                    self.bondcmd.remove_slave(ifaceobj.name, s)
-                    if clag_bond:
-                        try:
-                            netlink.link_set_protodown(s, "off")
-                        except Exception, e:
-                            self.logger.error('%s: %s' % (ifaceobj.name, str(e)))
-
-    def _up(self, ifaceobj):
-        try:
-            if not self.ipcmd.link_exists(ifaceobj.name):
-                self.bondcmd.create_bond(ifaceobj.name)
-            self._apply_master_settings(ifaceobj)
-            self._add_slaves(ifaceobj)
-        except Exception, e:
-            self.log_error(str(e), ifaceobj)
-
-    def _down(self, ifaceobj):
-        try:
-            self.bondcmd.delete_bond(ifaceobj.name)
-        except Exception, e:
-            self.log_warn(str(e))
-
-    def _query_check(self, ifaceobj, ifaceobjcurr):
-        slaves = None
-
-        if not self.bondcmd.bond_exists(ifaceobj.name):
-            self.logger.debug('bond iface %s' %ifaceobj.name +
-                              ' does not exist')
-            return
-
-        ifaceattrs = self.dict_key_subset(ifaceobj.config,
-                                          self.get_mod_attrs())
-        if not ifaceattrs: return
-        runningattrs = self._query_running_attrs(ifaceobj.name)
-
-        # support yes/no attributes
-        utils.support_yesno_attrs(runningattrs, ['bond-use-carrier',
-                                                 'bond-lacp-bypass-allow'],
-                                  ifaceobj=ifaceobj)
-
-        # support for numerical bond-mode
-        mode = ifaceobj.get_attr_value_first('bond-mode')
-        if mode in bond._bond_mode_num:
-            if 'bond-mode' in runningattrs:
-                runningattrs['bond-mode'] = bond._get_num_bond_mode(runningattrs['bond-mode'])
-
-        bond_slaves = True
-        for k in ifaceattrs:
-            v = ifaceobj.get_attr_value_first(k)
-            if not v:
-                continue
-            if k == 'bond-slaves':
-                slaves = self._get_slave_list(ifaceobj)
-                continue
-            elif k == 'bond-ports':
-                bond_slaves = False
-                slaves = self._get_slave_list(ifaceobj)
-                continue
-            rv = runningattrs.get(k)
-            if not rv:
-                ifaceobjcurr.update_config_with_status(k, 'None', 1)
-            else:
-                ifaceobjcurr.update_config_with_status(k, rv,
-                                                       1 if v != rv else 0)
-        runningslaves = runningattrs.get('bond-slaves')
-        if not slaves and not runningslaves:
-            return
-        retslave = 1
-        if slaves and runningslaves:
-            if slaves and runningslaves:
-                difference = set(slaves).symmetric_difference(runningslaves)
-                if not difference:
-                    retslave = 0
-        # we want to display the same bond-slaves list as provided
-        # in the interfaces file but if this list contains regexes or
-        # globs, for now, we won't try to change it.
-        if 'regex' in slaves or 'glob' in slaves:
-            slaves = runningslaves
-        else:
-            ordered = []
-            for i in range(0, len(slaves)):
-                if slaves[i] in runningslaves:
-                    ordered.append(slaves[i])
-            slaves = ordered
-        ifaceobjcurr.update_config_with_status('bond-slaves' if bond_slaves else 'bond-ports',
-                        ' '.join(slaves)
-                        if slaves else 'None', retslave)
-
-    def _query_running_attrs(self, bondname):
-        bondattrs = {'bond-mode' :
-                            self.bondcmd.get_mode(bondname),
-                     'bond-miimon' :
-                            self.bondcmd.get_miimon(bondname),
-                     'bond-use-carrier' :
-                            self.bondcmd.get_use_carrier(bondname),
-                     'bond-lacp-rate' :
-                            self.bondcmd.get_lacp_rate(bondname),
-                     'bond-min-links' :
-                            self.bondcmd.get_min_links(bondname),
-                     'bond-ad-actor-system' :
-                            self.bondcmd.get_ad_actor_system(bondname),
-                     'bond-ad-actor-sys-prio' :
-                            self.bondcmd.get_ad_actor_sys_prio(bondname),
-                     'bond-xmit-hash-policy' :
-                            self.bondcmd.get_xmit_hash_policy(bondname),
-                     'bond-lacp-bypass-allow' :
-                            self.bondcmd.get_lacp_bypass_allow(bondname),
-                     'bond-num-unsol-na' :
-                            self.bondcmd.get_num_unsol_na(bondname),
-                     'bond-num-grat-arp' :
-                            self.bondcmd.get_num_grat_arp(bondname),
-                     'bond-updelay' :
-                            self.bondcmd.get_updelay(bondname),
-                     'bond-downdelay' :
-                            self.bondcmd.get_downdelay(bondname)}
-        slaves = self.bondcmd.get_slaves(bondname)
-        if slaves:
-            bondattrs['bond-slaves'] = slaves
-        return bondattrs
-
-    def _query_running(self, ifaceobjrunning):
-        if not self.bondcmd.bond_exists(ifaceobjrunning.name):
-            return
-        bondattrs = self._query_running_attrs(ifaceobjrunning.name)
-        if bondattrs.get('bond-slaves'):
-            bondattrs['bond-slaves'] = ' '.join(bondattrs.get('bond-slaves'))
-        [ifaceobjrunning.update_config(k, v)
-                    for k, v in bondattrs.items()
-                        if v and v != self.get_mod_subattr(k, 'default')]
-
-    _run_ops = {'pre-up' : _up,
-               'post-down' : _down,
-               'query-running' : _query_running,
-               'query-checkcurr' : _query_check}
-
-    def get_ops(self):
-        """ returns list of ops supported by this module """
-        return self._run_ops.keys()
-
-    def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = iproute2()
-        if not self.bondcmd:
-            self.bondcmd = bondutil()
-
-    def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
-        """ run bond configuration on the interface object passed as argument
-
-        Args:
-            **ifaceobj** (object): iface object
-
-            **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
-                'query-running'
-
-        Kwargs:
-            **query_ifaceobj** (object): query check ifaceobject. This is only
-                valid when op is 'query-checkcurr'. It is an object same as
-                ifaceobj, but contains running attribute values and its config
-                status. The modules can use it to return queried running state
-                of interfaces. status is success if the running state is same
-                as user required state in ifaceobj. error otherwise.
-        """
-        op_handler = self._run_ops.get(operation)
-        if not op_handler:
-            return
-        if operation != 'query-running' and not self._is_bond(ifaceobj):
-            return
-        self._init_command_handlers()
-        if operation == 'query-checkcurr':
-            op_handler(self, ifaceobj, query_ifaceobj)
-        else:
-            op_handler(self, ifaceobj)
diff --git a/addons/bridge.py b/addons/bridge.py
deleted file mode 100644 (file)
index a75795c..0000000
+++ /dev/null
@@ -1,2109 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-
-from sets import Set
-from ifupdown.iface import *
-import ifupdown.policymanager as policymanager
-from ifupdown.utils import utils
-from ifupdownaddons.modulebase import moduleBase
-from ifupdownaddons.bridgeutils import brctl
-from ifupdownaddons.iproute2 import iproute2
-from collections import Counter
-from ifupdown.netlink import netlink
-import ifupdown.ifupdownflags as ifupdownflags
-import itertools
-import re
-import time
-
-class bridgeFlags:
-    PORT_PROCESSED = 0x1
-    PORT_PROCESSED_OVERRIDE = 0x2
-
-class bridge(moduleBase):
-    """  ifupdown2 addon module to configure linux bridges """
-
-    _modinfo = { 'mhelp' : 'Bridge configuration module. Supports both ' +
-                    'vlan aware and non vlan aware bridges. For the vlan ' +
-                    'aware bridge, the port specific attributes must be ' +
-                    'specified under the port. And for vlan unaware bridge ' +
-                    'port specific attributes must be specified under the ' +
-                    'bridge.',
-                 'attrs' : {
-                   'bridge-vlan-aware' :
-                        {'help' : 'vlan aware bridge. Setting this ' +
-                                  'attribute to yes enables vlan filtering' +
-                                  ' on the bridge',
-                         'validvals' : ['yes', 'no'],
-                         'example' : ['bridge-vlan-aware yes/no']},
-                   'bridge-ports' :
-                        {'help' : 'bridge ports',
-                         'multivalue' : True,
-                         'required' : True,
-                         'validvals': ['<interface-list>'],
-                         'example' : ['bridge-ports swp1.100 swp2.100 swp3.100',
-                                      'bridge-ports glob swp1-3.100',
-                                      'bridge-ports regex (swp[1|2|3].100)']},
-                   'bridge-stp' :
-                        {'help': 'bridge-stp yes/no',
-                         'example' : ['bridge-stp no'],
-                         'validvals' : ['yes', 'on', 'off', 'no'],
-                         'default' : 'no'},
-                   'bridge-bridgeprio' :
-                        {'help': 'bridge priority',
-                         'validrange' : ['0', '65535'],
-                         'example' : ['bridge-bridgeprio 32768'],
-                         'default' : '32768'},
-                   'bridge-ageing' :
-                       {'help': 'bridge ageing',
-                         'validrange' : ['0', '65535'],
-                         'example' : ['bridge-ageing 300'],
-                         'default' : '300'},
-                   'bridge-fd' :
-                        { 'help' : 'bridge forward delay',
-                          'validrange' : ['0', '255'],
-                          'example' : ['bridge-fd 15'],
-                          'default' : '15'},
-                   'bridge-gcint' :
-                        # XXX: recheck values
-                        { 'help' : 'bridge garbage collection interval in secs',
-                          'validrange' : ['0', '255'],
-                          'example' : ['bridge-gcint 4'],
-                          'default' : '4',
-                          'compat' : True,
-                          'deprecated': True},
-                   'bridge-hello' :
-                        { 'help' : 'bridge set hello time',
-                          'validrange' : ['0', '255'],
-                          'example' : ['bridge-hello 2'],
-                          'default' : '2'},
-                   'bridge-maxage' :
-                        { 'help' : 'bridge set maxage',
-                          'validrange' : ['0', '255'],
-                          'example' : ['bridge-maxage 20'],
-                          'default' : '20'},
-                   'bridge-pathcosts' :
-                        { 'help' : 'bridge set port path costs',
-                          'validvals': ['<interface-range-list>'],
-                          'validrange' : ['0', '65535'],
-                          'example' : ['under the bridge: bridge-pathcosts swp1=100 swp2=100',
-                                       'under the port (recommended): bridge-pathcosts 100'],
-                          'default' : '100'},
-                   'bridge-portprios' :
-                        { 'help' : 'bridge port prios',
-                          'validvals': ['<interface-range-list>'],
-                          'validrange' : ['0', '65535'],
-                          'example' : ['under the bridge: bridge-portprios swp1=32 swp2=32',
-                                       'under the port (recommended): bridge-portprios 32'],
-                          'default' : '32'},
-                   'bridge-mclmc' :
-                        { 'help' : 'set multicast last member count',
-                          'validrange' : ['0', '255'],
-                          'example' : ['bridge-mclmc 2'],
-                          'default' : '2'},
-                    'bridge-mcrouter' :
-                        { 'help' : 'set multicast router',
-                          'validvals' : ['yes', 'no', '0', '1'],
-                          'default' : 'yes',
-                          'example' : ['bridge-mcrouter yes']},
-                    'bridge-mcsnoop' :
-                        { 'help' : 'set multicast snooping',
-                          'validvals' : ['yes', 'no', '0', '1'],
-                          'default' : 'yes',
-                          'example' : ['bridge-mcsnoop yes']},
-                    'bridge-mcsqc' :
-                        { 'help' : 'set multicast startup query count',
-                          'validrange' : ['0', '255'],
-                          'default' : '2',
-                          'example' : ['bridge-mcsqc 2']},
-                    'bridge-mcqifaddr' :
-                        { 'help' : 'set multicast query to use ifaddr',
-                          'validvals' : ['yes', 'no', '0', '1'],
-                          'default' : 'no',
-                          'example' : ['bridge-mcqifaddr no']},
-                    'bridge-mcquerier' :
-                        { 'help' : 'set multicast querier',
-                          'validvals' : ['yes', 'no', '0', '1'],
-                          'default' : 'no',
-                          'example' : ['bridge-mcquerier no']},
-                    'bridge-hashel' :
-                        { 'help' : 'set hash elasticity',
-                          'validrange' : ['0', '4096'],
-                          'default' : '4096',
-                          'example' : ['bridge-hashel 4096']},
-                    'bridge-hashmax' :
-                        { 'help' : 'set hash max',
-                          'validrange' : ['0', '4096'],
-                          'default' : '4096',
-                          'example' : ['bridge-hashmax 4096']},
-                    'bridge-mclmi' :
-                        { 'help' : 'set multicast last member interval (in secs)',
-                          'validrange' : ['0', '255'],
-                          'default' : '1',
-                          'example' : ['bridge-mclmi 1']},
-                    'bridge-mcmi' :
-                        { 'help' : 'set multicast membership interval (in secs)',
-                          'validrange' : ['0', '255'],
-                          'default' : '260',
-                          'example' : ['bridge-mcmi 260']},
-                    'bridge-mcqpi' :
-                        { 'help' : 'set multicast querier interval (in secs)',
-                          'validrange' : ['0', '255'],
-                          'default' : '255',
-                          'example' : ['bridge-mcqpi 255']},
-                    'bridge-mcqi' :
-                        { 'help' : 'set multicast query interval (in secs)',
-                          'validrange' : ['0', '255'],
-                          'default' : '125',
-                          'example' : ['bridge-mcqi 125']},
-                    'bridge-mcqri' :
-                        { 'help' : 'set multicast query response interval (in secs)',
-                          'validrange' : ['0', '255'],
-                          'default' : '10',
-                          'example' : ['bridge-mcqri 10']},
-                    'bridge-mcsqi' :
-                        { 'help' : 'set multicast startup query interval (in secs)',
-                          'validrange' : ['0', '255'],
-                          'default' : '31',
-                          'example' : ['bridge-mcsqi 31']},
-                    'bridge-mcqv4src' :
-                        { 'help' : 'set per VLAN v4 multicast querier source address',
-                          'validvals' : ['<number-ipv4-list>', ],
-                          'multivalue' : True,
-                          'compat' : True,
-                          'example' : ['bridge-mcqv4src 100=172.16.100.1 101=172.16.101.1']},
-                    'bridge-portmcrouter' :
-                        { 'help' : 'set port multicast routers',
-                          'validvals' : ['<interface-yes-no-0-1-list>'],
-                          'default' : 'yes',
-                          'example' : ['under the bridge: bridge-portmcrouter swp1=yes swp2=yes',
-                                       'under the port (recommended): bridge-portmcrouter yes']},
-                    'bridge-portmcfl' :
-                        { 'help' : 'port multicast fast leave.',
-                          'validvals': ['<interface-yes-no-0-1-list>'],
-                          'validrange' : ['yes', 'no', '0', '1'],
-                          'default' : 'no',
-                          'example' : ['under the bridge: bridge-portmcfl swp1=no swp2=no',
-                                       'under the port (recommended): bridge-portmcfl no']},
-                    'bridge-waitport' :
-                        { 'help' : 'wait for a max of time secs for the' +
-                                ' specified ports to become available,' +
-                                'if no ports are specified then those' +
-                                ' specified on bridge-ports will be' +
-                                ' used here. Specifying no ports here ' +
-                                'should not be used if we are using ' +
-                                'regex or \"all\" on bridge_ports,' +
-                                'as it wouldnt work.',
-                          'default' : '0',
-                          'validvals': ['<number-interface-list>'],
-                          'example' : ['bridge-waitport 4 swp1 swp2']},
-                    'bridge-maxwait' :
-                        { 'help' : 'forces to time seconds the maximum time ' +
-                                'that the Debian bridge setup scripts will ' +
-                                'wait for the bridge ports to get to the ' +
-                                'forwarding status, doesn\'t allow factional ' +
-                                'part. If it is equal to 0 then no waiting' +
-                                ' is done',
-                          'validrange' : ['0', '255'],
-                          'default' : '0',
-                          'example' : ['bridge-maxwait 3']},
-                    'bridge-vids' :
-                        { 'help' : 'bridge port vids. Can be specified ' +
-                                   'under the bridge or under the port. ' +
-                                   'If specified under the bridge the ports ' +
-                                   'inherit it unless overridden by a ' +
-                                   'bridge-vids attribute under the port',
-                          'multivalue' : True,
-                          'validvals': ['<number-range-list>'],
-                          'example' : ['bridge-vids 4000',
-                                       'bridge-vids 2000 2200-3000'],
-                          'aliases': ['bridge-trunk']},
-                    'bridge-pvid' :
-                        { 'help' : 'bridge port pvid. Must be specified under' +
-                                   ' the bridge port',
-                          'validrange' : ['0', '4096'],
-                          'example' : ['bridge-pvid 1']},
-                    'bridge-access' :
-                        { 'help' : 'bridge port access vlan. Must be ' +
-                                   'specified under the bridge port',
-                          'validrange' : ['1', '4094'],
-                          'example' : ['bridge-access 300']},
-                    'bridge-allow-untagged' :
-                        { 'help' : 'indicate if the bridge port accepts ' +
-                                   'untagged packets or not.  Must be ' +
-                                   'specified under the bridge port. ' +
-                                   'Default is \'yes\'',
-                          'validvals' : ['yes', 'no'],
-                          'example' : ['bridge-allow-untagged yes'],
-                          'default' : 'yes'},
-                    'bridge-port-vids' :
-                        { 'help' : 'bridge vlans',
-                          'compat': True,
-                          'example' : ['bridge-port-vids bond0=1-1000,1010-1020']},
-                    'bridge-port-pvids' :
-                        { 'help' : 'bridge port vlans',
-                          'compat': True,
-                          'example' : ['bridge-port-pvids bond0=100 bond1=200']},
-                    'bridge-learning' :
-                        { 'help' : 'bridge port learning flag',
-                          'validvals': ['on', 'off'],
-                          'default': 'on',
-                          'example' : ['bridge-learning off']},
-                     }}
-
-    def __init__(self, *args, **kargs):
-        moduleBase.__init__(self, *args, **kargs)
-        self.ipcmd = None
-        self.name = self.__class__.__name__
-        self.brctlcmd = None
-        self._running_vidinfo = {}
-        self._running_vidinfo_valid = False
-        self._resv_vlan_range =  self._get_reserved_vlan_range()
-        self.logger.debug('%s: using reserved vlan range %s'
-                  %(self.__class__.__name__, str(self._resv_vlan_range)))
-        default_stp_attr = policymanager.policymanager_api.get_attr_default(
-                module_name=self.__class__.__name__, attr='bridge-stp')
-        if (default_stp_attr and (default_stp_attr == 'on' or default_stp_attr == 'yes')):
-            self.default_stp_on = True
-        else:
-            self.default_stp_on = False
-
-        should_warn = policymanager.policymanager_api.\
-            get_module_globals(module_name=self.__class__.__name__,
-                               attr='warn_on_untagged_bridge_absence')
-        self.warn_on_untagged_bridge_absence = should_warn == 'yes'
-
-    def syntax_check(self, ifaceobj, ifaceobj_getfunc):
-        retval = self.check_bridge_vlan_aware_port(ifaceobj, ifaceobj_getfunc)
-        if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
-            if not self.check_bridge_port_vid_attrs(ifaceobj):
-                retval = False
-        c1 = self.syntax_check_vxlan_in_vlan_aware_br(ifaceobj,
-                                                      ifaceobj_getfunc=ifaceobj_getfunc)
-        return retval and c1
-
-    def check_bridge_port_vid_attrs(self, ifaceobj):
-        if (ifaceobj.get_attr_value('bridge-access') and
-            (self.get_ifaceobj_bridge_vids_value(ifaceobj) or
-             ifaceobj.get_attr_value('bridge-pvid'))):
-            self.logger.warn('%s: bridge-access given, bridge-vids and bridge-pvid '
-                             'will be ignored' % ifaceobj.name)
-            return False
-        return True
-
-    def check_bridge_vlan_aware_port(self, ifaceobj, ifaceobj_getfunc):
-        if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE:
-            ports = self._get_bridge_port_list(ifaceobj)
-            if not ports:
-                return True
-            result = True
-            for port_name in ports:
-                port_obj_l = ifaceobj_getfunc(port_name)
-                if port_obj_l and port_obj_l[0].link_kind & ifaceLinkKind.VLAN:
-                    self.logger.error('%s: %s: vlan sub-interface is not '
-                                      'supported in a vlan-aware bridge'
-                                      % (ifaceobj.name, port_name))
-                    result = False
-            return result
-        return True
-
-    def _error_vxlan_in_vlan_aware_br(self, ifaceobj, bridgename):
-        self.log_error('`bridge-access` attribute is mandatory when vxlan '
-                       'device (%s) is part of vlan aware bridge (%s)'
-                       % (ifaceobj.name, bridgename), ifaceobj)
-
-    def syntax_check_vxlan_in_vlan_aware_br(self, ifaceobj, ifaceobj_getfunc):
-        if not ifaceobj_getfunc:
-            return True
-        if (ifaceobj.link_kind & ifaceLinkKind.VXLAN
-                and ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT):
-            if ifaceobj.get_attr_value('bridge-access'):
-                return True
-            for iface in ifaceobj.upperifaces if ifaceobj.upperifaces else []:
-                ifaceobj_upper_list = ifaceobj_getfunc(iface)
-                if not ifaceobj_upper_list:
-                    continue
-                ifaceobj_upper = ifaceobj_upper_list[0]
-                bridge_vids = self._get_bridge_vids(iface, ifaceobj_getfunc)
-                if ifaceobj_upper.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE:
-                    vids = self.get_ifaceobj_bridge_vids_value(ifaceobj)
-                    pvid = ifaceobj.get_attr_value_first('bridge-pvid')
-                    if (not vids
-                        or not pvid
-                        or not self._compare_vids(bridge_vids,
-                                                  vids,
-                                                  pvid=pvid)):
-                        self._error_vxlan_in_vlan_aware_br(ifaceobj,
-                                                           ifaceobj_upper.name)
-                        return False
-        return True
-
-    def _is_bridge(self, ifaceobj):
-        if (ifaceobj.get_attr_value_first('bridge-ports') or
-            ifaceobj.get_attr_value_first('bridge-vlan-aware')):
-            return True
-        return False
-
-    def _get_ifaceobj_bridge_ports(self, ifaceobj, warn=True):
-        ports = ifaceobj.get_attr_value('bridge-ports')
-        if warn and ports and len(ports) > 1:
-            self.log_warn('%s: ignoring duplicate bridge-ports lines: %s'
-                          %(ifaceobj.name, ports[1:]))
-        if ports:
-            if 'none' in ports[0]:
-                return []
-
-            return ports[0]
-
-        return None
-
-    def _is_bridge_port(self, ifaceobj):
-        if self.brctlcmd.is_bridge_port(ifaceobj.name):
-            return True
-        return False
-
-    def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
-        if not self._is_bridge(ifaceobj):
-            return None
-        if ifaceobj.link_type != ifaceLinkType.LINK_NA:
-           ifaceobj.link_type = ifaceLinkType.LINK_MASTER
-        ifaceobj.link_kind |= ifaceLinkKind.BRIDGE
-        # for special vlan aware bridges, we need to add another bit
-        if ifaceobj.get_attr_value_first('bridge-vlan-aware') == 'yes':
-            ifaceobj.link_kind |= ifaceLinkKind.BRIDGE
-            ifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE
-        ifaceobj.role |= ifaceRole.MASTER
-        ifaceobj.dependency_type = ifaceDependencyType.MASTER_SLAVE
-        return self.parse_port_list(ifaceobj.name,
-                                    self._get_ifaceobj_bridge_ports(ifaceobj),
-                                    ifacenames_all)
-
-    def get_dependent_ifacenames_running(self, ifaceobj):
-        self._init_command_handlers()
-        if not self.brctlcmd.bridge_exists(ifaceobj.name):
-            return None
-        return self.brctlcmd.get_bridge_ports(ifaceobj.name)
-
-    def _get_bridge_port_list(self, ifaceobj):
-
-        # port list is also available in the previously
-        # parsed dependent list. Use that if available, instead
-        # of parsing port expr again
-        port_list = ifaceobj.lowerifaces
-        if port_list:
-            return port_list
-        ports = self._get_ifaceobj_bridge_ports(ifaceobj)
-        if ports:
-            return self.parse_port_list(ifaceobj.name, ports)
-        else:
-            return None
-
-    def _process_bridge_waitport(self, ifaceobj, portlist):
-        waitport_value = ifaceobj.get_attr_value_first('bridge-waitport')
-        if not waitport_value: return
-        try:
-            waitportvals = re.split(r'[\s\t]\s*', waitport_value, 1)
-            if not waitportvals: return
-            try:
-                waitporttime = int(waitportvals[0])
-            except:
-                self.log_warn('%s: invalid waitport value \'%s\''
-                        %(ifaceobj.name, waitporttime))
-                return
-            if waitporttime <= 0: return
-            try:
-                waitportlist = self.parse_port_list(ifaceobj.name,
-                                                    waitportvals[1])
-            except IndexError, e:
-                # ignore error and use all bridge ports
-                waitportlist = portlist
-                pass
-            if not waitportlist: return
-            self.logger.info('%s: waiting for ports %s to exist ...'
-                    %(ifaceobj.name, str(waitportlist)))
-            starttime = time.time()
-            while ((time.time() - starttime) < waitporttime):
-                if all([False for p in waitportlist
-                        if not self.ipcmd.link_exists(p)]):
-                    break;
-                time.sleep(1)
-        except Exception, e:
-            self.log_warn('%s: unable to process waitport: %s'
-                    %(ifaceobj.name, str(e)))
-
-    def _enable_disable_ipv6(self, port, enable='1'):
-        try:
-            self.write_file('/proc/sys/net/ipv6/conf/%s/disable_ipv6' % port, enable)
-        except Exception, e:
-            self.logger.info(str(e))
-
-    def handle_ipv6(self, ports, state, ifaceobj=None):
-        if ifaceobj and (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN):
-            self._enable_disable_ipv6(ifaceobj.name, state)
-        for p in ports:
-            self._enable_disable_ipv6(p, state)
-
-    def _pretty_print_add_ports_error(self, errstr, bridgeifaceobj, bridgeports):
-        """ pretty print bridge port add errors.
-            since the commands are batched and the kernel only returns error
-            codes, this function tries to interpret some error codes
-            and prints clearer errors """
-
-        if re.search('RTNETLINK answers: Invalid argument', errstr):
-            # Cumulus Linux specific error checks
-            try:
-                if self.sysctl_get('net.bridge.bridge-allow-multiple-vlans') == '0':
-                    vlanid = None
-                    for bport in bridgeports:
-                        ifattrs = bport.split('.')
-                        if vlanid:
-                            if (len(ifattrs) == 1 or ifattrs[1] != vlanid):
-                                self.log_error('%s: ' %bridgeifaceobj.name +
-                                               'net.bridge.bridge-allow-multiple-vlans not set, multiple vlans not allowed', bridgeifaceobj)
-                                break
-                        if len(ifattrs) == 2:
-                            vlanid = ifattrs[1]
-            except:
-                pass
-        self.log_error(bridgeifaceobj.name + ': ' + errstr, bridgeifaceobj)
-
-    def _add_ports(self, ifaceobj):
-        bridgeports = self._get_bridge_port_list(ifaceobj)
-        runningbridgeports = []
-        removedbridgeports = []
-
-        self.ipcmd.batch_start()
-        self._process_bridge_waitport(ifaceobj, bridgeports)
-        self.ipcmd.batch_start()
-        # Delete active ports not in the new port list
-        if not ifupdownflags.flags.PERFMODE:
-            runningbridgeports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
-            if runningbridgeports:
-                for bport in runningbridgeports:
-                    if not bridgeports or bport not in bridgeports:
-                        netlink.link_set_nomaster(bport)
-                        removedbridgeports.append(bport)
-            else:
-                runningbridgeports = []
-        if not bridgeports:
-            self.ipcmd.batch_commit()
-            return
-        err = 0
-        ports = 0
-        newbridgeports = Set(bridgeports).difference(Set(runningbridgeports))
-        for bridgeport in newbridgeports:
-            try:
-                if (not ifupdownflags.flags.DRYRUN and
-                    not self.ipcmd.link_exists(bridgeport)):
-                    self.log_error('%s: bridge port %s does not exist'
-                                   %(ifaceobj.name, bridgeport), ifaceobj)
-                    err += 1
-                    continue
-                hwaddress = self.ipcmd.link_get_hwaddress(bridgeport)
-                if not self._valid_ethaddr(hwaddress):
-                    self.log_warn('%s: skipping port %s, ' %(ifaceobj.name,
-                                  bridgeport) + 'invalid ether addr %s'
-                                  %hwaddress)
-                    continue
-                netlink.link_set_master(bridgeport, ifaceobj.name)
-                self.ipcmd.addr_flush(bridgeport)
-                ports += 1
-                if ports == 250:
-                    ports = 0
-                    self.ipcmd.batch_commit()
-                    self.ipcmd.batch_start()
-            except Exception, e:
-                self.logger.error(str(e))
-                pass
-        try:
-            self.ipcmd.batch_commit()
-        except Exception, e:
-            self._pretty_print_add_ports_error(str(e), ifaceobj,
-                                               bridgeports)
-            pass
-
-        # enable ipv6 for ports that were removed
-        self.handle_ipv6(removedbridgeports, '0')
-        if err:
-            self.log_error('bridge configuration failed (missing ports)')
-
-
-    def _process_bridge_maxwait(self, ifaceobj, portlist):
-        maxwait = ifaceobj.get_attr_value_first('bridge-maxwait')
-        if not maxwait: return
-        try:
-            maxwait = int(maxwait)
-        except:
-            self.log_warn('%s: invalid maxwait value \'%s\'' %(ifaceobj.name,
-                    maxwait))
-            return
-        if not maxwait: return
-        self.logger.info('%s: waiting for ports to go to fowarding state ..'
-                %ifaceobj.name)
-        try:
-            starttime = time.time()
-            while ((time.time() - starttime) < maxwait):
-                if all([False for p in portlist
-                    if self.read_file_oneline(
-                            '/sys/class/net/%s/brif/%s/state'
-                            %(ifaceobj.name, p)) != '3']):
-                    break;
-                time.sleep(1)
-        except Exception, e:
-            self.log_warn('%s: unable to process maxwait: %s'
-                    %(ifaceobj.name, str(e)))
-
-    def _ints_to_ranges(self, ints):
-        for a, b in itertools.groupby(enumerate(ints), lambda (x, y): y - x):
-            b = list(b)
-            yield b[0][1], b[-1][1]
-
-    def _ranges_to_ints(self, rangelist):
-        """ returns expanded list of integers given set of string ranges
-        example: ['1', '2-4', '6'] returns [1, 2, 3, 4, 6]
-        """
-        result = []
-        try:
-            for part in rangelist:
-                if '-' in part:
-                    a, b = part.split('-')
-                    a, b = int(a), int(b)
-                    result.extend(range(a, b + 1))
-                else:
-                    a = int(part)
-                    result.append(a)
-        except:
-            self.logger.warn('unable to parse vids \'%s\''
-                             %''.join(rangelist))
-            pass
-        return result
-
-    def _compress_into_ranges(self, vids_ints):
-        return ['%d' %start if start == end else '%d-%d' %(start, end)
-                       for start, end in self._ints_to_ranges(vids_ints)]
-
-    def _diff_vids(self, vids1_ints, vids2_ints):
-        vids_to_add = Set(vids1_ints).difference(vids2_ints)
-        vids_to_del = Set(vids2_ints).difference(vids1_ints)
-
-        return (vids_to_del, vids_to_add)
-
-    def _compare_vids(self, vids1, vids2, pvid=None):
-        """ Returns true if the vids are same else return false """
-
-        vids1_ints = self._ranges_to_ints(vids1)
-        vids2_ints = self._ranges_to_ints(vids2)
-        set_diff = Set(vids1_ints).symmetric_difference(vids2_ints)
-        if pvid and int(pvid) in set_diff:
-            set_diff.remove(int(pvid))
-        if set_diff:
-            return False
-        else:
-            return True
-
-    def _set_bridge_mcqv4src_compat(self, ifaceobj):
-        #
-        # Sets old style igmp querier
-        #
-        attrval = ifaceobj.get_attr_value_first('bridge-mcqv4src')
-        if attrval:
-            running_mcqv4src = {}
-            if not ifupdownflags.flags.PERFMODE:
-                running_mcqv4src = self.brctlcmd.get_mcqv4src(ifaceobj.name)
-            mcqs = {}
-            srclist = attrval.split()
-            for s in srclist:
-                k, v = s.split('=')
-                mcqs[k] = v
-
-            k_to_del = Set(running_mcqv4src.keys()).difference(mcqs.keys())
-            for v in k_to_del:
-                self.brctlcmd.del_mcqv4src(ifaceobj.name, v)
-            for v in mcqs.keys():
-                self.brctlcmd.set_mcqv4src(ifaceobj.name, v, mcqs[v])
-
-    def _get_running_vidinfo(self):
-        if self._running_vidinfo_valid:
-            return self._running_vidinfo
-        self._running_vidinfo = {}
-
-        # CM-8161.  Removed check for PERFMODE.  Need the get in all cases
-        # including reboot, so that we can configure the pvid correctly.
-        self._running_vidinfo = self.ipcmd.bridge_port_vids_get_all_json()
-        self._running_vidinfo_valid = True
-        return self._running_vidinfo
-
-    def _flush_running_vidinfo(self):
-        self._running_vidinfo = {}
-        self._running_vidinfo_valid = False
-
-    def _set_bridge_vidinfo_compat(self, ifaceobj):
-        #
-        # Supports old style vlan vid info format
-        # for compatibility
-        #
-        bridge_port_pvids = ifaceobj.get_attr_value_first('bridge-port-pvids')
-        bridge_port_vids = ifaceobj.get_attr_value_first('bridge-port-vids')
-        if not bridge_port_pvids and not bridge_port_vids:
-            return
-
-        # Handle bridge vlan attrs
-        # Install pvids
-        if bridge_port_pvids:
-            portlist = self.parse_port_list(ifaceobj.name, bridge_port_pvids)
-            if not portlist:
-                self.log_warn('%s: could not parse \'%s %s\''
-                              %(ifaceobj.name, 'bridge-port-pvids',
-                                bridge_port_pvids))
-                return
-            for p in portlist:
-                try:
-                    (port, pvid) = p.split('=')
-                    pvid = int(pvid)
-                    running_pvid = self._get_running_pvid(port)
-                    if running_pvid:
-                        if running_pvid == pvid:
-                            continue
-                        else:
-                            self.ipcmd.bridge_port_pvid_del(port, running_pvid)
-                    self.ipcmd.bridge_port_pvid_add(port, pvid)
-                except Exception, e:
-                    self.log_warn('%s: failed to set pvid `%s` (%s)'
-                            %(ifaceobj.name, p, str(e)))
-
-        # install port vids
-        if bridge_port_vids:
-            portlist = self.parse_port_list(ifaceobj.name, bridge_port_vids)
-            if not portlist:
-                self.log_warn('%s: could not parse \'%s %s\'' %(ifaceobj.name,
-                              'bridge-port-vids', bridge_port_vids))
-                return
-            for p in portlist:
-                try:
-                    (port, val) = p.split('=')
-                    vids = val.split(',')
-                    vids_int =  self._ranges_to_ints(vids)
-                    running_vids = self._get_running_vids(port)
-                    if running_vids:
-                        (vids_to_del, vids_to_add) = \
-                                self._diff_vids(vids_int, running_vids)
-                        if vids_to_del:
-                            self.ipcmd.bridge_port_vids_del(port,
-                                    self._compress_into_ranges(vids_to_del))
-                        if vids_to_add:
-                            self.ipcmd.bridge_port_vids_add(port,
-                                    self._compress_into_ranges(vids_to_add))
-                    else:
-                        self.ipcmd.bridge_port_vids_add(port, vids_int)
-                except Exception, e:
-                    self.log_warn('%s: failed to set vid `%s` (%s)'
-                        %(ifaceobj.name, p, str(e)))
-
-    def _is_running_stp_state_on(self, bridgename):
-        """ Returns True if running stp state is on, else False """
-
-        stp_state_file = '/sys/class/net/%s/bridge/stp_state' %bridgename
-        if not stp_state_file:
-            return False
-        running_stp_state = self.read_file_oneline(stp_state_file)
-        if running_stp_state and running_stp_state != '0':
-            return True
-        return False
-
-    def _is_config_stp_state_on(self, ifaceobj):
-        """ Returns true if user specified stp state is on, else False """
-
-        stp_attr = ifaceobj.get_attr_value_first('bridge-stp')
-        if not stp_attr:
-            return self.default_stp_on
-        if (stp_attr and (stp_attr == 'on' or stp_attr == 'yes')):
-            return True
-        return False
-
-    def _set_bridge_forwarding(self, ifaceobj):
-        """ set ip forwarding to 0 if bridge interface does not have a
-        ip nor svi """
-        if not ifaceobj.upperifaces and not ifaceobj.get_attr_value('address'):
-            # set forwarding = 0
-            if self.sysctl_get('net.ipv4.conf.%s.forwarding' %ifaceobj.name) == '1':
-                self.sysctl_set('net.ipv4.conf.%s.forwarding' %ifaceobj.name, 0)
-            if self.sysctl_get('net.ipv6.conf.%s.forwarding' %ifaceobj.name) == '1':
-                self.sysctl_set('net.ipv6.conf.%s.forwarding' %ifaceobj.name, 0)
-        else:
-            if self.sysctl_get('net.ipv4.conf.%s.forwarding' %ifaceobj.name) == '0':
-                self.sysctl_set('net.ipv4.conf.%s.forwarding' %ifaceobj.name, 1)
-            if self.sysctl_get('net.ipv6.conf.%s.forwarding' %ifaceobj.name) == '0':
-                self.sysctl_set('net.ipv6.conf.%s.forwarding' %ifaceobj.name, 1)
-
-    def _apply_bridge_settings(self, ifaceobj):
-        try:
-            if self._is_config_stp_state_on(ifaceobj):
-                if not self._is_running_stp_state_on(ifaceobj.name):
-                    self.brctlcmd.set_stp(ifaceobj.name, "on")
-                    self.logger.info('%s: stp state reset, reapplying port '
-                                     'settings' %ifaceobj.name)
-                    ifaceobj.module_flags[ifaceobj.name] = \
-                        ifaceobj.module_flags.setdefault(self.name,0) | \
-                        bridgeFlags.PORT_PROCESSED_OVERRIDE
-            else:
-                # If stp not specified and running stp state on, set it to off
-                if self._is_running_stp_state_on(ifaceobj.name):
-                   self.brctlcmd.set_stp(ifaceobj.name, 'no')
-
-            self._set_bridge_forwarding(ifaceobj)
-
-            # Use the brctlcmd bulk set method: first build a dictionary
-            # and then call set
-            bridgeattrs = { k:v for k,v in
-                             {'ageing' :
-                                ifaceobj.get_attr_value_first('bridge-ageing'),
-                              'bridgeprio' :
-                                ifaceobj.get_attr_value_first(
-                                                        'bridge-bridgeprio'),
-                              'fd' :
-                                ifaceobj.get_attr_value_first('bridge-fd'),
-                              'hello' :
-                                ifaceobj.get_attr_value_first('bridge-hello'),
-                              'maxage' :
-                                ifaceobj.get_attr_value_first('bridge-maxage'),
-                              'mclmc' :
-                                ifaceobj.get_attr_value_first('bridge-mclmc'),
-                              'mcrouter' :
-                                ifaceobj.get_attr_value_first(
-                                                            'bridge-mcrouter'),
-                              'mcsnoop' :
-                                ifaceobj.get_attr_value_first('bridge-mcsnoop'),
-                              'mcsqc' :
-                                ifaceobj.get_attr_value_first('bridge-mcsqc'),
-                              'mcqifaddr' :
-                                ifaceobj.get_attr_value_first(
-                                                            'bridge-mcqifaddr'),
-                              'mcquerier' :
-                                ifaceobj.get_attr_value_first(
-                                                            'bridge-mcquerier'),
-                              'hashel' :
-                                ifaceobj.get_attr_value_first('bridge-hashel'),
-                              'hashmax' :
-                                ifaceobj.get_attr_value_first('bridge-hashmax'),
-                              'mclmi' :
-                                ifaceobj.get_attr_value_first('bridge-mclmi'),
-                              'mcmi' :
-                                ifaceobj.get_attr_value_first('bridge-mcmi'),
-                              'mcqpi' :
-                                ifaceobj.get_attr_value_first('bridge-mcqpi'),
-                              'mcqi' :
-                                ifaceobj.get_attr_value_first('bridge-mcqi'),
-                              'mcqri' :
-                                ifaceobj.get_attr_value_first('bridge-mcqri'),
-                              'mcsqi' :
-                                ifaceobj.get_attr_value_first('bridge-mcsqi')
-                               }.items()
-                            if v }
-            if bridgeattrs:
-                utils.support_yesno_attrs(bridgeattrs, ['mcqifaddr',
-                                                        'mcquerier',
-                                                        'mcrouter',
-                                                        'mcsnoop'])
-                self.brctlcmd.set_bridge_attrs(ifaceobj.name, bridgeattrs)
-            portattrs = {}
-            for attrname, dstattrname in {'bridge-pathcosts' : 'pathcost',
-                                'bridge-portprios' : 'portprio',
-                                'bridge-portmcrouter' : 'portmcrouter',
-                                'bridge-portmcfl' : 'portmcfl'}.items():
-                attrval = ifaceobj.get_attr_value_first(attrname)
-                if not attrval:
-                    continue
-                portlist = self.parse_port_list(ifaceobj.name, attrval)
-                if not portlist:
-                    self.log_error('%s: could not parse \'%s %s\''
-                         %(ifaceobj.name, attrname, attrval), ifaceobj,
-                           raise_error=False)
-                    continue
-                for p in portlist:
-                    try:
-                        (port, val) = p.split('=')
-                        if not portattrs.get(port):
-                            portattrs[port] = {}
-                        if (attrname == 'bridge-portmcrouter'
-                                or attrname == 'bridge-portmcfl'):
-                            portattrs[port].update({dstattrname: utils.boolean_support_binary(val)})
-                        else:
-                            portattrs[port].update({dstattrname : val})
-                    except Exception, e:
-                        self.log_error('%s: could not parse %s (%s)'
-                                       %(ifaceobj.name, attrname, str(e)),
-                                         ifaceobj, raise_error=False)
-            for port, attrdict in portattrs.iteritems():
-                try:
-                    self.brctlcmd.set_bridgeport_attrs(ifaceobj.name, port,
-                                                       attrdict)
-                except Exception, e:
-                    self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj,
-                                   raise_error=False)
-                    pass
-            self._set_bridge_vidinfo_compat(ifaceobj)
-            self._set_bridge_mcqv4src_compat(ifaceobj)
-            self._process_bridge_maxwait(ifaceobj,
-                    self._get_bridge_port_list(ifaceobj))
-        except Exception, e:
-            self.log_error(str(e), ifaceobj)
-
-    def _check_vids(self, ifaceobj, vids):
-        ret = True
-        for v in vids:
-            try:
-                if '-' in v:
-                    va, vb = v.split('-')
-                    va, vb = int(va), int(vb)
-                    if (self._handle_reserved_vlan(va, ifaceobj.name) or
-                        self._handle_reserved_vlan(vb, ifaceobj.name)):
-                        ret = False
-                else:
-                    va = int(v)
-                    if self._handle_reserved_vlan(va, ifaceobj.name):
-                        ret = False
-            except Exception:
-                self.logger.warn('%s: unable to parse vid \'%s\''
-                                 %(ifaceobj.name, v))
-        return ret
-
-    def _apply_bridge_port_pvids(self, bportifaceobj, pvid, running_pvid):
-        # Install pvids
-        try:
-            if running_pvid:
-                if running_pvid != pvid:
-                    self.ipcmd.bridge_port_pvid_del(bportifaceobj.name,
-                                                    running_pvid)
-                self.ipcmd.bridge_port_pvid_add(bportifaceobj.name, pvid)
-            else:
-                self.ipcmd.bridge_port_pvid_add(bportifaceobj.name, pvid)
-        except Exception, e:
-            self.log_error('%s: failed to set pvid `%s` (%s)'
-                           %(bportifaceobj.name, pvid, str(e)), bportifaceobj)
-
-    def _get_running_pvid(self, ifacename):
-        pvid = 0
-
-        running_vidinfo = self._get_running_vidinfo()
-        for vinfo in running_vidinfo.get(ifacename, {}):
-            v = vinfo.get('vlan')
-            pvid = v if 'PVID' in vinfo.get('flags', []) else 0
-            if pvid:
-                return pvid
-        return pvid
-
-    def _get_running_vids(self, ifacename):
-        vids = []
-
-        running_vidinfo = self._get_running_vidinfo()
-        for vinfo in running_vidinfo.get(ifacename, {}):
-            v = vinfo.get('vlan')
-            ispvid = True if 'PVID' in vinfo.get('flags', []) else False
-            if ispvid:
-                pvid = v if 'PVID' in vinfo.get('flags', []) else 0
-                if pvid == 1:
-                    continue
-            vEnd = vinfo.get('vlanEnd')
-            if vEnd:
-               vids.extend(range(v, vEnd + 1))
-            else:
-               vids.append(v)
-        return vids
-
-    def _get_running_vids_n_pvid(self, ifacename):
-        vids = []
-        pvid = 0
-
-        running_vidinfo = self._get_running_vidinfo()
-        for vinfo in running_vidinfo.get(ifacename, {}):
-            v = vinfo.get('vlan')
-            ispvid = True if 'PVID' in vinfo.get('flags', []) else False
-            if ispvid:
-                pvid = v if 'PVID' in vinfo.get('flags', []) else 0
-            vEnd = vinfo.get('vlanEnd')
-            if vEnd:
-               vids.extend(range(v, vEnd + 1))
-            else:
-               vids.append(v)
-        return (vids, pvid)
-
-    def _get_running_vids_n_pvid_str(self, ifacename):
-        vids = []
-        pvid = None
-
-        (vids, pvid) = self._get_running_vids_n_pvid(ifacename)
-
-        if vids:
-            ret_vids = self._compress_into_ranges(vids)
-        else:
-            ret_vids = None
-
-        if pvid:
-            ret_pvid = '%s' %pvid
-        else:
-            ret_pvid = None
-        return (ret_vids, ret_pvid)
-
-    def _get_running_vids_n_pvid_str2(self, ifacename):
-        vids = []
-        pvid = None
-
-
-        running_vidinfo = self._get_running_vidinfo()
-        for vinfo in running_vidinfo.get(ifacename, {}):
-            v = vinfo.get('vlan')
-            if not pvid:
-                pvid = '%s' %v if 'PVID' in vinfo.get('flags', []) else None
-            vEnd = vinfo.get('vlanEnd')
-            if vEnd:
-               vids.append('%s-%s' %(v, vEnd))
-            else:
-               vids.append('%s' %v)
-        return (vids, pvid)
-
-    def _apply_bridge_vids_and_pvid(self, bportifaceobj, vids, pvid,
-                                    isbridge):
-        """ This method is a combination of methods _apply_bridge_vids and
-            _apply_bridge_port_pvids above. A combined function is
-            found necessary to do the deletes first and the adds later
-            because kernel does honor vid info flags during deletes.
-
-        """
-        if not isbridge and bportifaceobj.link_kind & ifaceLinkKind.VXLAN:
-            if not vids or not pvid or len(vids) > 1 or vids[0] != pvid:
-                self._error_vxlan_in_vlan_aware_br(bportifaceobj,
-                                                   bportifaceobj.upperifaces[0])
-                return
-
-        vids_int =  self._ranges_to_ints(vids)
-        try:
-            pvid_int = int(pvid) if pvid else 0
-        except Exception:
-            self.logger.warn('%s: unable to parse pvid \'%s\''
-                             %(bportifaceobj.name, pvid))
-            pvid_int = 0
-            pass
-
-        vids_to_del = []
-        vids_to_add = vids_int
-        pvid_to_del = None
-        pvid_to_add = pvid_int
-
-        try:
-            if not self._check_vids(bportifaceobj, vids):
-               return
-
-            (running_vids, running_pvid) = self._get_running_vids_n_pvid(
-                                                        bportifaceobj.name)
-
-            if not running_vids and not running_pvid:
-                # There cannot be a no running pvid.
-                # It might just not be in our cache:
-                # this can happen if at the time we were
-                # creating the bridge vlan cache, the port
-                # was not part of the bridge. And we need
-                # to make sure both vids and pvid is not in
-                # the cache, to declare that our cache may
-                # be stale.
-                running_pvid = 1
-                running_vids = [1]
-
-            if running_vids:
-                (vids_to_del, vids_to_add) = \
-                    self._diff_vids(vids_to_add, running_vids)
-
-            if running_pvid:
-                if running_pvid != pvid_int and running_pvid != 0:
-                    pvid_to_del = running_pvid
-
-            if (pvid_to_del and (pvid_to_del in vids_int) and
-                (pvid_to_del not in vids_to_add)):
-                # kernel deletes dont take into account
-                # bridge vid flags and its possible that
-                # the pvid deletes we do end up deleting
-                # the vids. Be proactive and add the pvid
-                # to the vid add list if it is in the vids
-                # and not already part of vids_to_add.
-                # This helps with a small corner case:
-                #   - running
-                #       pvid 100
-                #       vid 101 102
-                #   - new change is going to move the state to
-                #       pvid 101
-                #       vid 100 102
-                vids_to_add.add(pvid_to_del)
-        except Exception, e:
-            self.log_error('%s: failed to process vids/pvids'
-                           %bportifaceobj.name + ' vids = %s' %str(vids) +
-                           'pvid = %s ' %pvid + '(%s)' %str(e),
-                           bportifaceobj, raise_error=False)
-        try:
-            if vids_to_del:
-               if pvid_to_add in vids_to_del:
-                   vids_to_del.remove(pvid_to_add)
-               self.ipcmd.bridge_vids_del(bportifaceobj.name,
-                                          self._compress_into_ranges(
-                                          vids_to_del), isbridge)
-        except Exception, e:
-                self.log_warn('%s: failed to del vid `%s` (%s)'
-                        %(bportifaceobj.name, str(vids_to_del), str(e)))
-
-        try:
-            if pvid_to_del:
-               self.ipcmd.bridge_port_pvid_del(bportifaceobj.name,
-                                               pvid_to_del)
-        except Exception, e:
-                self.log_warn('%s: failed to del pvid `%s` (%s)'
-                        %(bportifaceobj.name, pvid_to_del, str(e)))
-
-        try:
-            if vids_to_add:
-               self.ipcmd.bridge_vids_add(bportifaceobj.name,
-                                          self._compress_into_ranges(
-                                          vids_to_add), isbridge)
-        except Exception, e:
-                self.log_error('%s: failed to set vid `%s` (%s)'
-                               %(bportifaceobj.name, str(vids_to_add),
-                                 str(e)), bportifaceobj, raise_error=False)
-
-        try:
-            if pvid_to_add and pvid_to_add != running_pvid:
-                self.ipcmd.bridge_port_pvid_add(bportifaceobj.name,
-                                                pvid_to_add)
-        except Exception, e:
-                self.log_error('%s: failed to set pvid `%s` (%s)'
-                               %(bportifaceobj.name, pvid_to_add, str(e)),
-                               bportifaceobj)
-
-    def _apply_bridge_vlan_aware_port_settings_all(self, bportifaceobj,
-                                                   bridge_vids=None,
-                                                   bridge_pvid=None):
-        vids = None
-        pvids = None
-        vids_final = []
-        pvid_final = None
-        bport_access = bportifaceobj.get_attr_value_first('bridge-access')
-        if bport_access:
-            vids = re.split(r'[\s\t]\s*', bport_access)
-            pvids = vids
-            allow_untagged = 'yes'
-            self.check_bridge_port_vid_attrs(bportifaceobj)
-        else:
-            allow_untagged = bportifaceobj.get_attr_value_first('bridge-allow-untagged') or 'yes'
-
-            bport_vids = self.get_ifaceobj_bridge_vids_value(bportifaceobj)
-            if bport_vids:
-                vids = re.split(r'[\s\t,]\s*', bport_vids)
-
-            bport_pvids = bportifaceobj.get_attr_value_first('bridge-pvid')
-            if bport_pvids:
-                pvids = re.split(r'[\s\t]\s*', bport_pvids)
-
-        if vids:
-            vids_final =  vids
-        elif bridge_vids:
-            vids_final = bridge_vids
-
-        if allow_untagged == 'yes':
-            if pvids:
-                pvid_final = pvids[0]
-            elif bridge_pvid:
-                pvid_final = bridge_pvid
-            else:
-                pvid_final = '1'
-        else:
-            pvid_final = None
-
-        self._apply_bridge_vids_and_pvid(bportifaceobj, vids_final,
-                                         pvid_final, False)
-
-    def _apply_bridge_port_settings(self, bportifaceobj, bridgename=None,
-                                    bridgeifaceobj=None):
-        if not bridgename and bridgeifaceobj:
-            bridgename = bridgeifaceobj.name
-        # Set other stp and igmp attributes
-        portattrs = {}
-        for attrname, dstattrname in {
-            'bridge-pathcosts' : 'pathcost',
-            'bridge-portprios' : 'portprio'}.items():
-            attrval = bportifaceobj.get_attr_value_first(attrname)
-            if not attrval:
-                # Check if bridge has that attribute
-                #if bridgeifaceobj:
-                #    attrval = bridgeifaceobj.get_attr_value_first(attrname)
-                #    if not attrval:
-                #        continue
-                #else:
-                continue
-            portattrs[dstattrname] = attrval
-
-        portmcrouter = bportifaceobj.get_attr_value_first('bridge-portmcrouter')
-        if portmcrouter:
-            portattrs['portmcrouter'] = utils.boolean_support_binary(portmcrouter)
-
-        portmcfl = bportifaceobj.get_attr_value_first('bridge-portmcfl')
-        if portmcfl:
-            portattrs['portmcfl'] = utils.boolean_support_binary(portmcfl)
-
-        try:
-            self.brctlcmd.set_bridgeport_attrs(bridgename,
-                            bportifaceobj.name, portattrs)
-        except Exception, e:
-            self.log_error(str(e), bportifaceobj)
-
-        # special handling for learning:
-        # bridge ports may have learning capability, in which case
-        # we need to keep the bridge ports internal learning in sync
-        # with bridge learning capability. This is true for vxlan ports
-        # hence special case this for vxlan ports.
-        try:
-            config_learn = bportifaceobj.get_attr_value_first('bridge-learning')
-            if not config_learn:
-                config_learn = self.get_mod_subattr('bridge-learning', 'default')
-            config_learn = utils.get_onoff_bool(config_learn)
-            running_learn = self.ipcmd.get_brport_learning(bportifaceobj.name)
-            if config_learn != running_learn:
-                self.ipcmd.set_brport_learning(bportifaceobj.name, config_learn)
-        except Exception, e:
-            self.log_error(str(e), bportifaceobj)
-
-        if not (bportifaceobj.link_kind & ifaceLinkKind.VXLAN):
-            return
-        try:
-            vxlan_learn = self.ipcmd.get_vxlandev_learning(bportifaceobj.name)
-            if vxlan_learn:
-                vxlan_learn = utils.get_onoff_bool(vxlan_learn)
-            else:
-                vxlan_learn = 'on'
-            if vxlan_learn != config_learn:
-                self.ipcmd.set_vxlandev_learning(bportifaceobj.name, config_learn)
-        except Exception, e:
-            self.log_warn('%s: unable to propagate learning to vxlan dev (%s)'
-                          %(bportifaceobj.name, str(e)))
-
-    def _apply_bridge_port_settings_all(self, ifaceobj,
-                                        ifaceobj_getfunc=None,
-                                        bridge_vlan_aware=False):
-        err = False
-
-        if (ifaceobj.get_attr_value_first('bridge-port-vids') and
-                ifaceobj.get_attr_value_first('bridge-port-pvids')):
-            # Old style bridge port vid info
-            # skip new style setting on ports
-            return
-        self.logger.info('%s: applying bridge configuration '
-                         %ifaceobj.name + 'specific to ports')
-
-        bridge_vids = self.get_ifaceobj_bridge_vids_value(ifaceobj)
-        if bridge_vids:
-           bridge_vids = re.split(r'[\s\t,]\s*', bridge_vids)
-        else:
-           bridge_vids = None
-
-        bridge_pvid = ifaceobj.get_attr_value_first('bridge-pvid')
-        if bridge_pvid:
-           bridge_pvid = re.split(r'[\s\t]\s*', bridge_pvid)[0]
-        else:
-           bridge_pvid = None
-
-        if (ifaceobj.module_flags.get(self.name, 0x0) &
-                bridgeFlags.PORT_PROCESSED_OVERRIDE):
-            port_processed_override = True
-        else:
-            port_processed_override = False
-
-        bridgeports = self._get_bridge_port_list(ifaceobj)
-        if not bridgeports:
-           self.logger.debug('%s: cannot find bridgeports' %ifaceobj.name)
-           return
-        self.ipcmd.batch_start()
-        for bport in bridgeports:
-            # Use the brctlcmd bulk set method: first build a dictionary
-            # and then call set
-            if not self.ipcmd.bridge_port_exists(ifaceobj.name, bport):
-                self.logger.info('%s: skipping bridge config' %ifaceobj.name +
-                        ' for port %s (missing port)' %bport)
-                continue
-            self.logger.info('%s: processing bridge config for port %s'
-                             %(ifaceobj.name, bport))
-            bportifaceobjlist = ifaceobj_getfunc(bport)
-            if not bportifaceobjlist:
-               continue
-            for bportifaceobj in bportifaceobjlist:
-                # Dont process bridge port if it already has been processed
-                # and there is no override on port_processed
-                if (not port_processed_override and
-                    (bportifaceobj.module_flags.get(self.name,0x0) & 
-                     bridgeFlags.PORT_PROCESSED)):
-                    continue
-                try:
-                    # Add attributes specific to the vlan aware bridge
-                    if bridge_vlan_aware:
-                        self._apply_bridge_vlan_aware_port_settings_all(
-                                bportifaceobj, bridge_vids, bridge_pvid)
-                        self._apply_bridge_port_settings(bportifaceobj,
-                                                 bridgeifaceobj=ifaceobj)
-                    elif self.warn_on_untagged_bridge_absence:
-                        self._check_untagged_bridge(ifaceobj.name, bportifaceobj, ifaceobj_getfunc)
-                except Exception, e:
-                    err = True
-                    self.logger.warn('%s: %s' %(ifaceobj.name, str(e)))
-                    pass
-        self.ipcmd.bridge_batch_commit()
-        if err:
-           raise Exception('%s: errors applying port settings' %ifaceobj.name)
-
-    def _check_untagged_bridge(self, bridgename, bridgeportifaceobj, ifaceobj_getfunc):
-        if bridgeportifaceobj.link_kind & ifaceLinkKind.VLAN:
-            lower_ifaceobj_list = ifaceobj_getfunc(bridgeportifaceobj.lowerifaces[0])
-            if lower_ifaceobj_list and lower_ifaceobj_list[0] and \
-                    not lower_ifaceobj_list[0].link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
-                self.logger.warn('%s: untagged bridge not found. Please configure a bridge with untagged bridge ports to avoid Spanning Tree Interoperability issue.' % bridgename)
-                self.warn_on_untagged_bridge_absence = False
-
-    def _get_bridgename(self, ifaceobj):
-        for u in ifaceobj.upperifaces:
-            if self.ipcmd.is_bridge(u):
-                return u
-        return None
-
-    def _up(self, ifaceobj, ifaceobj_getfunc=None):
-        # Check if bridge port and see if we need to add it to the bridge
-        add_port = False
-        bridgename = self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name)
-        if (not bridgename and
-            (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT)):
-            # get bridgename and add port to bridge
-            bridgename = self._get_bridgename(ifaceobj)
-            add_port = True
-        if bridgename:
-            if self.ipcmd.bridge_is_vlan_aware(bridgename):
-                if add_port:
-                    # add ifaceobj to bridge
-                    netlink.link_set_master(ifaceobj.name, bridgename)
-                bridge_vids = self._get_bridge_vids(bridgename,
-                                                    ifaceobj_getfunc)
-                bridge_pvid = self._get_bridge_pvid(bridgename,
-                                                    ifaceobj_getfunc)
-                try:
-                    self._apply_bridge_vlan_aware_port_settings_all(ifaceobj,
-                                                                    bridge_vids,
-                                                                    bridge_pvid)
-                except Exception as e:
-                    self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj)
-                    return
-            self._apply_bridge_port_settings(ifaceobj, bridgename=bridgename)
-            ifaceobj.module_flags[self.name] = ifaceobj.module_flags.setdefault(self.name,0) | \
-                                              bridgeFlags.PORT_PROCESSED
-            return
-        if not self._is_bridge(ifaceobj):
-            return
-        err = False
-        errstr = ''
-        running_ports = ''
-        bridge_just_created = False
-        try:
-            if not ifupdownflags.flags.PERFMODE:
-                if not self.ipcmd.link_exists(ifaceobj.name):
-                   self.ipcmd.link_create(ifaceobj.name, 'bridge')
-                   bridge_just_created = True
-            else:
-                self.ipcmd.link_create(ifaceobj.name, 'bridge')
-                bridge_just_created = True
-        except Exception, e:
-            raise Exception(str(e))
-
-        try:
-            bridge_vlan_aware = False
-            if ifaceobj.get_attr_value_first('bridge-vlan-aware') == 'yes':
-                if not self.check_bridge_vlan_aware_port(ifaceobj, ifaceobj_getfunc):
-                    return
-                bridge_vlan_aware = True
-                if (bridge_just_created or
-                        not self.ipcmd.bridge_is_vlan_aware(ifaceobj.name)):
-                    self.ipcmd.link_set(ifaceobj.name, 'vlan_filtering', '1',
-                                        False, "bridge")
-                    if not bridge_just_created:
-                        ifaceobj.module_flags[self.name] = ifaceobj.module_flags.setdefault(self.name,0) | bridgeFlags.PORT_PROCESSED_OVERRIDE
-            elif (not bridge_just_created and
-                  ifaceobj.get_attr_value_first('bridge-vlan-aware') == 'no'
-                  and self.ipcmd.bridge_is_vlan_aware(ifaceobj.name)):
-                  self.ipcmd.link_set(ifaceobj.name, 'vlan_filtering', '0',
-                                      False, "bridge")
-                  bridge_vlan_aware = False
-        except Exception, e:
-            raise Exception(str(e))
-
-        try:
-            self._add_ports(ifaceobj)
-        except Exception, e:
-            err = True
-            errstr = str(e)
-            pass
-
-        try:
-            self._apply_bridge_settings(ifaceobj)
-        except Exception, e:
-            err = True
-            errstr = str(e)
-            pass
-
-        try:
-            running_ports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
-            if not running_ports:
-               return
-            # disable ipv6 for ports that were added to bridge
-            self.handle_ipv6(running_ports, '1', ifaceobj=ifaceobj)
-            self._apply_bridge_port_settings_all(ifaceobj,
-                            ifaceobj_getfunc=ifaceobj_getfunc,
-                            bridge_vlan_aware=bridge_vlan_aware)
-        except Exception, e:
-            err = True
-            errstr = str(e)
-            pass
-        finally:
-            if ifaceobj.link_type != ifaceLinkType.LINK_NA:
-                for p in running_ports:
-                    try:
-                        netlink.link_set_updown(p, "up")
-                    except Exception, e:
-                        self.logger.debug('%s: %s: link set up (%s)'
-                                          %(ifaceobj.name, p, str(e)))
-                        pass
-        if err:
-            raise Exception(errstr)
-
-    def _down(self, ifaceobj, ifaceobj_getfunc=None):
-        try:
-            if self._get_ifaceobj_bridge_ports(ifaceobj, warn=False):
-                ports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
-                self.brctlcmd.delete_bridge(ifaceobj.name)
-                if ports:
-                    self.handle_ipv6(ports, '0')
-                    if ifaceobj.link_type != ifaceLinkType.LINK_NA:
-                        map(lambda p: netlink.link_set_updown(p, "down"),
-                            ports)
-        except Exception, e:
-            self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj)
-
-    def _query_running_vidinfo_compat(self, ifaceobjrunning, ports):
-        running_attrs = {}
-        if ports:
-            running_bridge_port_vids = ''
-            for p in ports:
-                try:
-                    running_vids = self._get_runing_vids(p)
-                    if running_vids:
-                        running_bridge_port_vids += ' %s=%s' %(p,
-                                                      ','.join(running_vids))
-                except Exception:
-                    pass
-            running_attrs['bridge-port-vids'] = running_bridge_port_vids
-
-            running_bridge_port_pvid = ''
-            for p in ports:
-                try:
-                    running_pvid = self._get_runing_pvid(p)
-                    if running_pvid:
-                        running_bridge_port_pvid += ' %s=%s' %(p,
-                                                        running_pvid)
-                except Exception:
-                    pass
-            running_attrs['bridge-port-pvids'] = running_bridge_port_pvid
-
-        running_bridge_vids = self._get_running_vids(ifaceobjrunning.name)
-        if running_bridge_vids:
-            running_attrs['bridge-vids'] = ','.join(self._compress_into_ranges(running_bridge_vids))
-        return running_attrs
-
-    def _query_running_vidinfo(self, ifaceobjrunning, ifaceobj_getfunc,
-                               bridgeports=None):
-        running_attrs = {}
-
-        # 'bridge-vids' under the bridge is all about 'vids' on the port.
-        # so query the ports
-        running_bridgeport_vids = []
-        running_bridgeport_pvids = []
-        for bport in bridgeports:
-            (vids, pvid) = self._get_running_vids_n_pvid_str(bport)
-            if vids:
-                running_bridgeport_vids.append(' '.join(vids))
-            if pvid:
-                running_bridgeport_pvids.append(pvid)
-
-        bridge_vids = None
-        if running_bridgeport_vids: 
-           (vidval, freq) = Counter(running_bridgeport_vids).most_common()[0]
-           if freq == len(bridgeports):
-              running_attrs['bridge-vids'] = vidval
-              bridge_vids = vidval.split()
-
-        bridge_pvid = None
-        if running_bridgeport_pvids:
-           (vidval, freq) = Counter(running_bridgeport_pvids).most_common()[0]
-           if freq == len(bridgeports) and vidval != '1':
-              running_attrs['bridge-pvid'] = vidval
-              bridge_pvid = vidval.split()[0]
-
-        # Go through all bridge ports and find their vids
-        for bport in bridgeports:
-            bportifaceobj = ifaceobj_getfunc(bport)
-            if not bportifaceobj:
-               continue
-            bport_vids = []
-            bport_pvid = None
-            (vids, pvid) = self._get_running_vids_n_pvid_str(bport)
-            if vids and vids != bridge_vids:
-               bport_vids = vids
-            if pvid and pvid != bridge_pvid:
-               bport_pvid = pvid
-            if bport_vids and bport_pvid in bport_vids:
-                bport_vids.remove(bport_pvid)
-            if (not bport_vids and bport_pvid and bport_pvid != '1'):
-               bportifaceobj[0].replace_config('bridge-access', bport_pvid)
-               bportifaceobj[0].delete_config('bridge-pvid')
-               bportifaceobj[0].delete_config('bridge-vids')
-            else:
-               if bport_pvid and bport_pvid != '1':
-                  bportifaceobj[0].replace_config('bridge-pvid', bport_pvid)
-               else:
-                  # delete any stale bridge-vids under ports
-                  bportifaceobj[0].delete_config('bridge-pvid')
-               if bport_vids:
-                  bportifaceobj[0].replace_config('bridge-vids',
-                                                  ' '.join(bport_vids))
-               else:
-                  # delete any stale bridge-vids under ports
-                  bportifaceobj[0].delete_config('bridge-vids')
-        return running_attrs
-
-    def _query_running_mcqv4src(self, ifaceobjrunning):
-        running_mcqv4src = self.brctlcmd.get_mcqv4src(ifaceobjrunning.name)
-        mcqs = ['%s=%s' %(v, i) for v, i in running_mcqv4src.items()]
-        mcqs.sort()
-        mcq = ' '.join(mcqs)
-        return mcq
-
-    def _query_running_attrs(self, ifaceobjrunning, ifaceobj_getfunc,
-                             bridge_vlan_aware=False):
-        bridgeattrdict = {}
-        userspace_stp = 0
-        ports = None
-        skip_kernel_stp_attrs = 0
-
-        try:
-            if self.systcl_get_net_bridge_stp_user_space() == '1':
-                userspace_stp = 1
-        except Exception as e:
-            self.logger.info('%s: %s' % (ifaceobjrunning.name, str(e)))
-
-        tmpbridgeattrdict = self.brctlcmd.get_bridge_attrs(ifaceobjrunning.name)
-        if not tmpbridgeattrdict:
-            self.logger.warn('%s: unable to get bridge attrs'
-                    %ifaceobjrunning.name)
-            return bridgeattrdict
-
-        # Fill bridge_ports and bridge stp attributes first
-        ports = tmpbridgeattrdict.get('ports')
-        if ports:
-            bridgeattrdict['bridge-ports'] = [' '.join(ports.keys())]
-        stp = tmpbridgeattrdict.get('stp', 'no')
-        if stp != self.get_mod_subattr('bridge-stp', 'default'):
-            bridgeattrdict['bridge-stp'] = [stp]
-
-        if  stp == 'yes' and userspace_stp:
-            skip_kernel_stp_attrs = 1
-
-        bool2str = {'0': 'no', '1': 'yes'}
-        # pick all other attributes
-        for k,v in tmpbridgeattrdict.items():
-            if not v:
-                continue
-            if k == 'ports' or k == 'stp':
-                continue
-
-            if skip_kernel_stp_attrs and k[:2] != 'mc':
-                # only include igmp attributes if kernel stp is off
-                continue
-            attrname = 'bridge-' + k
-            mod_default = self.get_mod_subattr(attrname, 'default')
-            if v != mod_default:
-                # convert '0|1' running values to 'no|yes'
-                if v in bool2str.keys() and bool2str[v] == mod_default:
-                    continue
-                bridgeattrdict[attrname] = [v]
-
-        if bridge_vlan_aware:
-            bridgevidinfo = self._query_running_vidinfo(ifaceobjrunning,
-                                                        ifaceobj_getfunc,
-                                                        ports.keys())
-        else:
-            bridgevidinfo = self._query_running_vidinfo_compat(ifaceobjrunning,
-                                                               ports)
-        if bridgevidinfo:
-           bridgeattrdict.update({k : [v] for k, v in bridgevidinfo.items()
-                                  if v})
-
-        mcq = self._query_running_mcqv4src(ifaceobjrunning)
-        if mcq:
-            bridgeattrdict['bridge-mcqv4src'] = [mcq]
-
-        if skip_kernel_stp_attrs:
-            return bridgeattrdict
-
-        # Do this only for vlan-UNAWARE-bridge
-        if ports and not bridge_vlan_aware:
-            portconfig = {'bridge-pathcosts' : '',
-                          'bridge-portprios' : ''}
-            for p, v in ports.items():
-                v = self.brctlcmd.get_pathcost(ifaceobjrunning.name, p)
-                if v and v != self.get_mod_subattr('bridge-pathcosts',
-                                                   'default'):
-                    portconfig['bridge-pathcosts'] += ' %s=%s' %(p, v)
-
-                v = self.brctlcmd.get_portprio(ifaceobjrunning.name, p)
-                if v and v != self.get_mod_subattr('bridge-portprios',
-                                                   'default'):
-                    portconfig['bridge-portprios'] += ' %s=%s' %(p, v)
-
-            bridgeattrdict.update({k : [v] for k, v in portconfig.items()
-                                    if v})
-
-        return bridgeattrdict
-
-    def _query_check_mcqv4src(self, ifaceobj, ifaceobjcurr):
-        running_mcqs = self._query_running_mcqv4src(ifaceobj)
-        attrval = ifaceobj.get_attr_value_first('bridge-mcqv4src')
-        if attrval:
-            mcqs = attrval.split()
-            mcqs.sort()
-            mcqsout = ' '.join(mcqs)
-            ifaceobjcurr.update_config_with_status('bridge-mcqv4src',
-                         running_mcqs, 1 if running_mcqs != mcqsout else 0)
-
-    def _query_check_bridge_vidinfo(self, ifaceobj, ifaceobjcurr):
-        err = 0
-        attrval = ifaceobj.get_attr_value_first('bridge-port-vids')
-        if attrval:
-            running_bridge_port_vids = ''
-            portlist = self.parse_port_list(ifaceobj.name, attrval)
-            if not portlist:
-                self.log_warn('%s: could not parse \'bridge-port-vids %s\''
-                          %(ifaceobj.name, attrval))
-                return
-            err = 0
-            for p in portlist:
-                try:
-                    (port, val) = p.split('=')
-                    vids = val.split(',')
-                    running_vids = self._get_running_vids(port)
-                    if running_vids:
-                        if not self._compare_vids(vids, running_vids):
-                            err += 1
-                            running_bridge_port_vids += ' %s=%s' %(port,
-                                                      ','.join(running_vids))
-                        else:
-                            running_bridge_port_vids += ' %s' %p
-                    else:
-                        err += 1
-                except Exception, e:
-                    self.log_warn('%s: failure checking vid %s (%s)'
-                        %(ifaceobj.name, p, str(e)))
-            if err:
-                ifaceobjcurr.update_config_with_status('bridge-port-vids',
-                                                 running_bridge_port_vids, 1)
-            else:
-                ifaceobjcurr.update_config_with_status('bridge-port-vids',
-                                                 attrval, 0)
-
-        attrval = ifaceobj.get_attr_value_first('bridge-port-pvids')
-        if attrval:
-            portlist = self.parse_port_list(ifaceobj.name, attrval)
-            if not portlist:
-                self.log_warn('%s: could not parse \'bridge-port-pvids %s\''
-                              %(ifaceobj.name, attrval))
-                return
-            running_bridge_port_pvids = ''
-            err = 0
-            for p in portlist:
-                try:
-                    (port, pvid) = p.split('=')
-                    running_pvid = self._get_running_vids(port)
-                    if running_pvid and running_pvid == pvid:
-                        running_bridge_port_pvids += ' %s' %p
-                    else:
-                        err += 1
-                        running_bridge_port_pvids += ' %s=%s' %(port,
-                                                            running_pvid)
-                except Exception, e:
-                    self.log_warn('%s: failure checking pvid %s (%s)'
-                            %(ifaceobj.name, pvid, str(e)))
-            if err:
-                ifaceobjcurr.update_config_with_status('bridge-port-pvids',
-                                                 running_bridge_port_pvids, 1)
-            else:
-                ifaceobjcurr.update_config_with_status('bridge-port-pvids',
-                                                 running_bridge_port_pvids, 0)
-
-        vids = self.get_ifaceobj_bridge_vids(ifaceobj)
-        if vids[1]:
-            ifaceobjcurr.update_config_with_status(vids[0], vids[1], -1)
-
-    def _query_check_bridge(self, ifaceobj, ifaceobjcurr,
-                            ifaceobj_getfunc=None):
-        if not self._is_bridge(ifaceobj):
-            return
-        if not self.brctlcmd.bridge_exists(ifaceobj.name):
-            self.logger.info('%s: bridge: does not exist' %(ifaceobj.name))
-            return
-
-        ifaceattrs = self.dict_key_subset(ifaceobj.config,
-                                          self.get_mod_attrs())
-        #Add default attributes if --with-defaults is set
-        if ifupdownflags.flags.WITHDEFAULTS and 'bridge-stp' not in ifaceattrs:
-            ifaceattrs.append('bridge-stp')
-        if not ifaceattrs:
-            return
-        try:
-            runningattrs = self.brctlcmd.get_bridge_attrs(ifaceobj.name)
-            if not runningattrs:
-               self.logger.debug('%s: bridge: unable to get bridge attrs'
-                                 %ifaceobj.name)
-               runningattrs = {}
-        except Exception, e:
-            self.logger.warn(str(e))
-            runningattrs = {}
-
-        self._query_check_support_yesno_attrs(runningattrs, ifaceobj)
-
-        filterattrs = ['bridge-vids', 'bridge-trunk', 'bridge-port-vids',
-                       'bridge-port-pvids']
-        for k in Set(ifaceattrs).difference(filterattrs):
-            # get the corresponding ifaceobj attr
-            v = ifaceobj.get_attr_value_first(k)
-            if not v:
-                if ifupdownflags.flags.WITHDEFAULTS and k == 'bridge-stp':
-                    v = 'on' if self.default_stp_on else 'off'
-                else:
-                    continue
-            rv = runningattrs.get(k[7:])
-            if k == 'bridge-mcqv4src':
-               continue
-            if k == 'bridge-maxwait' or k == 'bridge-waitport':
-                ifaceobjcurr.update_config_with_status(k, v, 0)
-                continue
-            if k == 'bridge-vlan-aware':
-                rv = self.ipcmd.bridge_is_vlan_aware(ifaceobj.name)
-                if (rv and v == 'yes') or (not rv and v == 'no'):
-                    ifaceobjcurr.update_config_with_status('bridge-vlan-aware',
-                               v, 0)
-                else:
-                    ifaceobjcurr.update_config_with_status('bridge-vlan-aware',
-                               v, 1)
-            elif k == 'bridge-stp':
-               # special case stp compare because it may
-               # contain more than one valid values
-               stp_on_vals = ['on', 'yes']
-               stp_off_vals = ['off', 'no']
-               if ((v in stp_on_vals and rv in stp_on_vals) or
-                   (v in stp_off_vals and rv in stp_off_vals)):
-                    ifaceobjcurr.update_config_with_status('bridge-stp',
-                               rv, 0)
-               else:
-                    ifaceobjcurr.update_config_with_status('bridge-stp',
-                               rv, 1)
-            elif k == 'bridge-ports':
-               # special case ports because it can contain regex or glob
-               running_port_list = rv.keys() if rv else []
-               bridge_port_list = self._get_bridge_port_list(ifaceobj)
-               if not running_port_list and not bridge_port_list:
-                  continue
-               portliststatus = 1
-               if running_port_list and bridge_port_list:
-                  difference = set(running_port_list
-                                 ).symmetric_difference(bridge_port_list)
-                  if not difference:
-                     portliststatus = 0
-                  try:
-                    port_list = self._get_ifaceobj_bridge_ports(ifaceobj,
-                                                                warn=False).split()
-                    # we want to display the same bridge-ports list as provided
-                    # in the interfaces file but if this list contains regexes or
-                    # globs, for now, we won't try to change it.
-                    if 'regex' in port_list or 'glob' in port_list:
-                        port_list = running_port_list
-                    else:
-                        ordered = []
-                        for i in range(0, len(port_list)):
-                            if port_list[i] in running_port_list:
-                                ordered.append(port_list[i])
-                        port_list = ordered
-                  except:
-                    port_list = running_port_list
-                  ifaceobjcurr.update_config_with_status('bridge-ports',
-                                                         (' '.join(port_list)
-                                                          if port_list else ''),
-                                                         portliststatus)
-            elif (k == 'bridge-pathcosts' or
-                  k == 'bridge-portprios' or k == 'bridge-portmcrouter'
-                  or k == 'bridge-portmcfl'):
-               brctlcmdattrname = k[7:].rstrip('s')
-               # for port attributes, the attributes are in a list
-               # <portname>=<portattrvalue>
-               status = 0
-               currstr = ''
-               vlist = self.parse_port_list(ifaceobj.name, v)
-               if not vlist:
-                  continue
-               for vlistitem in vlist:
-                   try:
-                      (p, v) = vlistitem.split('=')
-                      currv = self.brctlcmd.get_bridgeport_attr(
-                                         ifaceobj.name, p,
-                                         brctlcmdattrname)
-                      if currv:
-                          currstr += ' %s=%s' %(p, currv)
-                      else:
-                          currstr += ' %s=%s' %(p, 'None')
-                      if currv != v:
-                          status = 1
-                   except Exception, e:
-                      self.log_warn(str(e))
-                   pass
-               ifaceobjcurr.update_config_with_status(k, currstr, status)
-            elif not rv:
-               if k == 'bridge-pvid' or k == 'bridge-vids' or k == 'bridge-trunk' or k == 'bridge-allow-untagged':
-                   # bridge-pvid and bridge-vids on a bridge does
-                   # not correspond directly to a running config
-                   # on the bridge. They correspond to default
-                   # values for the bridge ports. And they are
-                   # already checked against running config of the
-                   # bridge port and reported against a bridge port.
-                   # So, ignore these attributes under the bridge.
-                   # Use '2' for ignore today. XXX: '2' will be
-                   # mapped to a defined value in subsequent patches.
-                   ifaceobjcurr.update_config_with_status(k, v, 2)
-               else:
-                   ifaceobjcurr.update_config_with_status(k, 'notfound', 1)
-               continue
-            elif v != rv:
-               ifaceobjcurr.update_config_with_status(k, rv, 1)
-            else:
-               ifaceobjcurr.update_config_with_status(k, rv, 0)
-
-        self._query_check_bridge_vidinfo(ifaceobj, ifaceobjcurr)
-
-        self._query_check_mcqv4src(ifaceobj, ifaceobjcurr)
-
-    def get_ifaceobj_bridge_vids(self, ifaceobj):
-        vids = ('bridge-vids', ifaceobj.get_attr_value_first('bridge-vids'))
-        if not vids[1]:
-            vids = ('bridge-trunk', ifaceobj.get_attr_value_first('bridge-trunk'))
-        return vids
-
-    def get_ifaceobj_bridge_vids_value(self, ifaceobj):
-        return self.get_ifaceobj_bridge_vids(ifaceobj)[1]
-
-    def _get_bridge_vids(self, bridgename, ifaceobj_getfunc):
-        ifaceobjs = ifaceobj_getfunc(bridgename)
-        for ifaceobj in ifaceobjs:
-            vids = self.get_ifaceobj_bridge_vids_value(ifaceobj)
-            if vids: return re.split(r'[\s\t,]\s*', vids)
-        return None
-
-    def _get_bridge_pvid(self, bridgename, ifaceobj_getfunc):
-        ifaceobjs = ifaceobj_getfunc(bridgename)
-        pvid = None
-        for ifaceobj in ifaceobjs:
-            pvid = ifaceobj.get_attr_value_first('bridge-pvid')
-            if pvid:
-                break
-        return pvid
-
-    def _get_bridge_name(self, ifaceobj):
-        return self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name)
-
-    def _query_check_bridge_port_vidinfo(self, ifaceobj, ifaceobjcurr,
-                                         ifaceobj_getfunc, bridgename):
-        attr_name = 'bridge-access'
-        vid = ifaceobj.get_attr_value_first(attr_name)
-        if vid:
-            (running_vids, running_pvid) = self._get_running_vids_n_pvid_str(
-                                                        ifaceobj.name)
-            if (not running_pvid or running_pvid != vid or
-                (running_vids and running_vids[0] != vid)):
-               ifaceobjcurr.update_config_with_status(attr_name,
-                                running_pvid, 1)
-            else:
-               ifaceobjcurr.update_config_with_status(attr_name, vid, 0)
-            return
-
-        (running_vids, running_pvid) = self._get_running_vids_n_pvid_str(
-                                                        ifaceobj.name)
-        attr_name = 'bridge-pvid'
-        pvid = ifaceobj.get_attr_value_first('bridge-pvid')
-        if pvid:
-            if running_pvid and running_pvid == pvid:
-                ifaceobjcurr.update_config_with_status(attr_name,
-                                                       running_pvid, 0)
-            else:
-                ifaceobjcurr.update_config_with_status(attr_name,
-                                                       running_pvid, 1)
-        elif (not (ifaceobj.flags & iface.HAS_SIBLINGS) or
-              ((ifaceobj.flags & iface.HAS_SIBLINGS) and
-               (ifaceobj.flags & iface.OLDEST_SIBLING))):
-            # if the interface has multiple iface sections,
-            # we check the below only for the oldest sibling
-            # or the last iface section
-            pvid = self._get_bridge_pvid(bridgename, ifaceobj_getfunc)
-            if pvid:
-                if not running_pvid or running_pvid != pvid:
-                    ifaceobjcurr.status = ifaceStatus.ERROR
-                    ifaceobjcurr.status_str = 'bridge pvid error'
-            elif not running_pvid or running_pvid != '1':
-                ifaceobjcurr.status = ifaceStatus.ERROR
-                ifaceobjcurr.status_str = 'bridge pvid error'
-
-        attr_name, vids = self.get_ifaceobj_bridge_vids(ifaceobj)
-        if vids:
-           vids = re.split(r'[\s\t]\s*', vids)
-           if not running_vids or not self._compare_vids(vids, running_vids,
-                                                         running_pvid):
-               ifaceobjcurr.update_config_with_status(attr_name,
-                                            ' '.join(running_vids), 1)
-           else:
-               ifaceobjcurr.update_config_with_status(attr_name,
-                                            ' '.join(vids), 0)
-        elif (not (ifaceobj.flags & iface.HAS_SIBLINGS) or
-              ((ifaceobj.flags & iface.HAS_SIBLINGS) and
-               (ifaceobj.flags & iface.OLDEST_SIBLING))):
-           # if the interface has multiple iface sections,
-           # we check the below only for the oldest sibling
-           # or the last iface section
-
-           # check if it matches the bridge vids
-           bridge_vids = self._get_bridge_vids(bridgename, ifaceobj_getfunc)
-           if (bridge_vids and (not running_vids  or
-                   not self._compare_vids(bridge_vids, running_vids, running_pvid))):
-              ifaceobjcurr.status = ifaceStatus.ERROR
-              ifaceobjcurr.status_str = 'bridge vid error'
-
-    def _query_check_bridge_port(self, ifaceobj, ifaceobjcurr,
-                                 ifaceobj_getfunc):
-        if not self._is_bridge_port(ifaceobj):
-            # Mark all bridge attributes as failed
-            ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj,
-                    ['bridge-vids', 'bridge-trunk', 'bridge-pvid', 'bridge-access',
-                     'bridge-pathcosts', 'bridge-portprios',
-                     'bridge-portmcrouter',
-                     'bridge-portmcfl'], 1)
-            return
-        bridgename = self._get_bridge_name(ifaceobj)
-        if not bridgename:
-            self.logger.warn('%s: unable to determine bridge name'
-                             %ifaceobj.name)
-            return
-
-        if self.ipcmd.bridge_is_vlan_aware(bridgename):
-            self._query_check_bridge_port_vidinfo(ifaceobj, ifaceobjcurr,
-                                                  ifaceobj_getfunc,
-                                                  bridgename)
-        for attr, dstattr in {'bridge-pathcosts' : 'pathcost',
-                              'bridge-portprios' : 'portprio',
-                              'bridge-portmcrouter' : 'portmcrouter',
-                              'bridge-portmcfl' : 'portmcfl' }.items():
-            attrval = ifaceobj.get_attr_value_first(attr)
-            if not attrval:
-                continue
-
-            try:
-                running_attrval = self.brctlcmd.get_bridgeport_attr(
-                                       bridgename, ifaceobj.name, dstattr)
-
-                if dstattr == 'portmcrouter' or dstattr == 'portmcfl':
-                    if not utils.is_binary_bool(attrval) and running_attrval:
-                        running_attrval = utils.get_yesno_boolean(
-                            utils.get_boolean_from_string(running_attrval))
-
-                if running_attrval != attrval:
-                    ifaceobjcurr.update_config_with_status(attr,
-                                            running_attrval, 1)
-                else:
-                    ifaceobjcurr.update_config_with_status(attr,
-                                            running_attrval, 0)
-            except Exception, e:
-                self.log_warn('%s: %s' %(ifaceobj.name, str(e)))
-
-            try:
-                config_learn = ifaceobj.get_attr_value_first('bridge-learning')
-                if not config_learn:
-                    return
-                config_learn = utils.get_onoff_bool(config_learn)
-                running_learn = self.ipcmd.get_brport_learning(ifaceobj.name)
-                if config_learn == running_learn:
-                    ifaceobjcurr.update_config_with_status('bridge-learning',
-                                                            running_learn, 0)
-                else:
-                    ifaceobjcurr.update_config_with_status('bridge-learning',
-                                                            running_learn, 1)
-                
-            except Exception, e:
-                self.log_warn('%s: %s' %(ifaceobj.name, str(e)))
-
-    def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
-        if self._is_bridge(ifaceobj):
-            self._query_check_bridge(ifaceobj, ifaceobjcurr)
-        else:
-            self._query_check_bridge_port(ifaceobj, ifaceobjcurr,
-                                          ifaceobj_getfunc)
-
-    def _query_running_bridge(self, ifaceobjrunning, ifaceobj_getfunc):
-        if self.ipcmd.bridge_is_vlan_aware(ifaceobjrunning.name):
-            ifaceobjrunning.update_config('bridge-vlan-aware', 'yes')
-            ifaceobjrunning.update_config_dict(self._query_running_attrs(
-                                               ifaceobjrunning,
-                                               ifaceobj_getfunc,
-                                               bridge_vlan_aware=True))
-        else: 
-            ifaceobjrunning.update_config_dict(self._query_running_attrs(
-                                               ifaceobjrunning, None))
-
-    def _query_running_bridge_port_attrs(self, ifaceobjrunning, bridgename):
-        if self.systcl_get_net_bridge_stp_user_space() == '1':
-            return
-
-        v = self.brctlcmd.get_pathcost(bridgename, ifaceobjrunning.name)
-        if v and v != self.get_mod_subattr('bridge-pathcosts', 'default'):
-            ifaceobjrunning.update_config('bridge-pathcosts', v)
-
-        v = self.brctlcmd.get_pathcost(bridgename, ifaceobjrunning.name)
-        if v and v != self.get_mod_subattr('bridge-portprios', 'default'):
-            ifaceobjrunning.update_config('bridge-portprios', v)
-
-    def _query_running_bridge_port(self, ifaceobjrunning,
-                                   ifaceobj_getfunc=None):
-
-        bridgename = self.ipcmd.bridge_port_get_bridge_name(
-                                                ifaceobjrunning.name)
-        bridge_vids = None
-        bridge_pvid = None
-        if not bridgename:
-            self.logger.warn('%s: unable to find bridgename'
-                             %ifaceobjrunning.name)
-            return
-        if not self.ipcmd.bridge_is_vlan_aware(bridgename):
-            return
-
-        (bridge_port_vids, bridge_port_pvid) = self._get_running_vids_n_pvid_str(
-                                                           ifaceobjrunning.name)
-        if bridge_port_vids and bridge_port_pvid in bridge_port_vids:
-                bridge_port_vids.remove(bridge_port_pvid)
-
-        bridgeifaceobjlist = ifaceobj_getfunc(bridgename)
-        if bridgeifaceobjlist:
-           bridge_vids = bridgeifaceobjlist[0].get_attr_value('bridge-vids')
-           bridge_pvid = bridgeifaceobjlist[0].get_attr_value_first('bridge-pvid')
-
-        if not bridge_port_vids and bridge_port_pvid:
-            # must be an access port
-            if bridge_port_pvid != '1':
-               ifaceobjrunning.update_config('bridge-access',
-                                          bridge_port_pvid)
-        else:
-            if bridge_port_vids:
-                if (not bridge_vids or bridge_port_vids != bridge_vids):
-                   ifaceobjrunning.update_config('bridge-vids',
-                                        ' '.join(bridge_port_vids))
-            if bridge_port_pvid and bridge_port_pvid != '1':
-                if (not bridge_pvid or (bridge_port_pvid != bridge_pvid)):
-                    ifaceobjrunning.update_config('bridge-pvid',
-                                        bridge_port_pvid)
-        self._query_running_bridge_port_attrs(ifaceobjrunning, bridgename)
-
-    def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
-        if self.brctlcmd.bridge_exists(ifaceobjrunning.name):
-            self._query_running_bridge(ifaceobjrunning, ifaceobj_getfunc)
-        elif self.brctlcmd.is_bridge_port(ifaceobjrunning.name):
-            self._query_running_bridge_port(ifaceobjrunning, ifaceobj_getfunc)
-
-    def _query(self, ifaceobj, **kwargs):
-        """ add default policy attributes supported by the module """
-        if (not (ifaceobj.link_kind & ifaceLinkKind.BRIDGE) or
-            ifaceobj.get_attr_value_first('bridge-stp')):
-            return
-        if self.default_stp_on:
-            ifaceobj.update_config('bridge-stp', 'yes')
-
-    def _query_check_support_yesno_attrs(self, runningattrs, ifaceobj):
-        for attrl in [['mcqifaddr', 'bridge-mcqifaddr'],
-                     ['mcquerier', 'bridge-mcquerier'],
-                     ['mcrouter', 'bridge-mcrouter'],
-                     ['mcsnoop', 'bridge-mcsnoop']]:
-            value = ifaceobj.get_attr_value_first(attrl[1])
-            if value and not utils.is_binary_bool(value):
-                if attrl[0] in runningattrs:
-                    bool = utils.get_boolean_from_string(runningattrs[attrl[0]])
-                    runningattrs[attrl[0]] = utils.get_yesno_boolean(bool)
-        self._query_check_support_yesno_attr_port(runningattrs, ifaceobj, 'portmcrouter', ifaceobj.get_attr_value_first('bridge-portmcrouter'))
-        self._query_check_support_yesno_attr_port(runningattrs, ifaceobj, 'portmcfl', ifaceobj.get_attr_value_first('bridge-portmcfl'))
-
-    def _query_check_support_yesno_attr_port(self, runningattrs, ifaceobj, attr, attrval):
-        if attrval:
-            portlist = self.parse_port_list(ifaceobj.name, attrval)
-            if portlist:
-                to_convert = []
-                for p in portlist:
-                    (port, val) = p.split('=')
-                    if not utils.is_binary_bool(val):
-                        to_convert.append(port)
-                for port in to_convert:
-                    runningattrs['ports'][port][attr] = utils.get_yesno_boolean(
-                        utils.get_boolean_from_string(runningattrs['ports'][port][attr]))
-
-    _run_ops = {'pre-up' : _up,
-               'post-down' : _down,
-               'query-checkcurr' : _query_check,
-               'query-running' : _query_running,
-               'query' : _query}
-
-    def get_ops(self):
-        """ returns list of ops supported by this module """
-        return self._run_ops.keys()
-
-    def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = iproute2()
-        if not self.brctlcmd:
-            self.brctlcmd = brctl()
-
-    def run(self, ifaceobj, operation, query_ifaceobj=None,
-            ifaceobj_getfunc=None):
-        """ run bridge configuration on the interface object passed as
-            argument. Can create bridge interfaces if they dont exist already
-
-        Args:
-            **ifaceobj** (object): iface object
-
-            **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
-                                 'query-running'
-
-        Kwargs:
-            **query_ifaceobj** (object): query check ifaceobject. This is only
-                valid when op is 'query-checkcurr'. It is an object same as
-                ifaceobj, but contains running attribute values and its config
-                status. The modules can use it to return queried running state
-                of interfaces. status is success if the running state is same
-                as user required state in ifaceobj. error otherwise.
-        """
-        op_handler = self._run_ops.get(operation)
-        if not op_handler:
-           return
-        self._init_command_handlers()
-        #self._flush_running_vidinfo()
-        if operation == 'query-checkcurr':
-            op_handler(self, ifaceobj, query_ifaceobj,
-                       ifaceobj_getfunc=ifaceobj_getfunc)
-        else:
-            op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)
diff --git a/addons/bridgevlan.py b/addons/bridgevlan.py
deleted file mode 100644 (file)
index ef3cd02..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-
-from ifupdown.iface import *
-from ifupdownaddons.modulebase import moduleBase
-from ifupdownaddons.iproute2 import iproute2
-from ifupdownaddons.bridgeutils import brctl
-from ipaddr import IPv4Address
-import ifupdown.ifupdownflags as ifupdownflags
-import logging
-
-class bridgevlan(moduleBase):
-    """  ifupdown2 addon module to configure vlan attributes on a vlan
-         aware bridge """
-
-    _modinfo = {'mhelp' : 'bridgevlan module configures vlan attributes ' +
-                        'on a vlan aware bridge. This module only ' +
-                        'understands vlan interface name ' +
-                        'with dot notations. eg br0.100. where br0 is the ' +
-                        'vlan aware bridge this config is for',
-                'attrs' : {
-                        'bridge-igmp-querier-src' :
-                            { 'help' : 'bridge igmp querier src. Must be ' +
-                                   'specified under the vlan interface',
-                              'validvals' : ['<ipv4>', ],
-                              'example' : ['bridge-igmp-querier-src 172.16.101.1']}}}
-
-    def __init__(self, *args, **kargs):
-        moduleBase.__init__(self, *args, **kargs)
-        self.brctlcmd = None
-        self.ipcmd = None
-
-    def _is_bridge_vlan_device(self, ifaceobj):
-        if ifaceobj.type == ifaceType.BRIDGE_VLAN:
-            return True
-        return False
-
-    def _get_bridge_n_vlan(self, ifaceobj):
-        vlist = ifaceobj.name.split('.', 1)
-        if len(vlist) == 2:
-            return (vlist[0], vlist[1])
-        return None
-
-    def _get_bridgename(self, ifaceobj):
-        vlist = ifaceobj.name.split('.', 1)
-        if len(vlist) == 2:
-            return vlist[0]
-        return None
-
-    def get_dependent_ifacenames(self, ifaceobj, ifaceobjs_all=None):
-        if not self._is_bridge_vlan_device(ifaceobj):
-            return None
-        return [self._get_bridgename(ifaceobj)]
-
-    def _up(self, ifaceobj):
-        try:
-            (bridgename, vlan) = self._get_bridge_n_vlan(ifaceobj)
-            vlanid = int(vlan, 10)
-        except:
-            self.log_error('%s: bridge vlan interface name ' %ifaceobj.name +
-                    'does not correspond to format (eg. br0.100)', ifaceobj)
-            raise
-
-        if not self.ipcmd.link_exists(bridgename):
-            #self.logger.warn('%s: bridge %s does not exist' %(ifaceobj.name,
-            #                 bridgename))
-            return
-
-        running_mcqv4src = {}
-        if not ifupdownflags.flags.PERFMODE:
-            running_mcqv4src = self.brctlcmd.get_mcqv4src(bridgename)
-        if running_mcqv4src:
-            r_mcqv4src = running_mcqv4src.get(vlan)
-        else:
-            r_mcqv4src = None
-        mcqv4src = ifaceobj.get_attr_value_first('bridge-igmp-querier-src')
-        if not mcqv4src:
-            if r_mcqv4src:
-                self.brctlcmd.del_mcqv4src(bridgename, vlanid)
-            return
-
-        if r_mcqv4src and r_mcqv4src != mcqv4src:
-            self.brctlcmd.del_mcqv4src(bridgename, vlanid)
-            self.brctlcmd.set_mcqv4src(bridgename, vlanid, mcqv4src)
-        else:
-            self.brctlcmd.set_mcqv4src(bridgename, vlanid, mcqv4src)
-
-    def _down(self, ifaceobj):
-        try:
-            (bridgename, vlan) = self._get_bridge_n_vlan(ifaceobj)
-            vlanid = int(vlan, 10)
-        except:
-            self.logger.warn('%s: bridge vlan interface name ' %ifaceobj.name +
-                    'does not correspond to format (eg. br0.100)')
-            raise
-
-        if not self.ipcmd.link_exists(bridgename):
-            #self.logger.warn('%s: bridge %s does not exist' %(ifaceobj.name,
-            #                 bridgename))
-            return
-        mcqv4src = ifaceobj.get_attr_value_first('bridge-igmp-querier-src')
-        if mcqv4src:
-           self.brctlcmd.del_mcqv4src(bridgename, vlanid)
-
-    def _query_running_bridge_igmp_querier_src(self, ifaceobj):
-        (bridgename, vlanid) = ifaceobj.name.split('.')
-        running_mcqv4src = self.brctlcmd.get_mcqv4src(bridgename)
-        if running_mcqv4src:
-           return running_mcqv4src.get(vlanid)
-        return None
-
-    def _query_check(self, ifaceobj, ifaceobjcurr):
-        attrval = ifaceobj.get_attr_value_first('bridge-igmp-querier-src')
-        if attrval:
-            running_mcq = self._query_running_bridge_igmp_querier_src(ifaceobj)
-            if not running_mcq or running_mcq != attrval:
-                ifaceobjcurr.update_config_with_status(
-                        'bridge-igmp-querier-src', running_mcq, 1)
-            else:
-                ifaceobjcurr.update_config_with_status(
-                        'bridge-igmp-querier-src', attrval, 0)
-                ifaceobjcurr.status = ifaceStatus.SUCCESS
-        return
-
-    def _query_running(self, ifaceobjrunning):
-        # XXX not supported
-        return
-
-    _run_ops = {'pre-up' : _up,
-               'post-down' : _down,
-               'query-checkcurr' : _query_check,
-               'query-running' : _query_running}
-
-    def get_ops(self):
-        """ returns list of ops supported by this module """
-        return self._run_ops.keys()
-
-    def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = iproute2()
-        if not self.brctlcmd:
-            self.brctlcmd = brctl()
-
-    def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
-        """ run vlan configuration on the interface object passed as argument
-
-        Args:
-            **ifaceobj** (object): iface object
-
-            **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
-                                 'query-running'
-        Kwargs:
-            **query_ifaceobj** (object): query check ifaceobject. This is only
-                valid when op is 'query-checkcurr'. It is an object same as
-                ifaceobj, but contains running attribute values and its config
-                status. The modules can use it to return queried running state
-                of interfaces. status is success if the running state is same
-                as user required state in ifaceobj. error otherwise.
-        """
-        op_handler = self._run_ops.get(operation)
-        if not op_handler:
-            return
-        if (operation != 'query-running' and
-                not self._is_bridge_vlan_device(ifaceobj)):
-            return
-        self._init_command_handlers()
-        if operation == 'query-checkcurr':
-            op_handler(self, ifaceobj, query_ifaceobj)
-        else:
-            op_handler(self, ifaceobj)
diff --git a/addons/dhcp.py b/addons/dhcp.py
deleted file mode 100644 (file)
index 5faa84b..0000000
+++ /dev/null
@@ -1,217 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-
-try:
-    import re
-    from ipaddr import IPNetwork
-    from sets import Set
-    from ifupdown.iface import *
-    import ifupdown.policymanager as policymanager
-    from ifupdownaddons.modulebase import moduleBase
-    from ifupdownaddons.dhclient import dhclient
-    from ifupdownaddons.iproute2 import iproute2
-    import ifupdown.ifupdownflags as ifupdownflags
-    from ifupdown.utils import utils
-    import time
-    from ifupdown.netlink import netlink
-except ImportError, e:
-    raise ImportError (str(e) + "- required module not found")
-
-class dhcp(moduleBase):
-    """ ifupdown2 addon module to configure dhcp on interface """
-
-    def __init__(self, *args, **kargs):
-        moduleBase.__init__(self, *args, **kargs)
-        self.dhclientcmd = dhclient(**kargs)
-        self.ipcmd = None
-
-    def syntax_check(self, ifaceobj, ifaceobj_getfunc):
-        return self.is_dhcp_allowed_on(ifaceobj, syntax_check=True)
-
-    def is_dhcp_allowed_on(self, ifaceobj, syntax_check):
-        if ifaceobj.addr_method and 'dhcp' in ifaceobj.addr_method:
-            return utils.is_addr_ip_allowed_on(ifaceobj, syntax_check=True)
-        return True
-
-    def _up(self, ifaceobj):
-        # if dhclient is already running do not stop and start it
-        dhclient4_running = self.dhclientcmd.is_running(ifaceobj.name)
-        dhclient6_running = self.dhclientcmd.is_running6(ifaceobj.name)
-
-        # today if we have an interface with both inet and inet6, if we
-        # remove the inet or inet6 or both then execute ifreload, we need
-        # to release/kill the appropriate dhclient(4/6) if they are running
-        self._down_stale_dhcp_config(ifaceobj, 'inet', dhclient4_running)
-        self._down_stale_dhcp_config(ifaceobj, 'inet6', dhclient6_running)
-
-        try:
-            dhclient_cmd_prefix = None
-            dhcp_wait = policymanager.policymanager_api.get_attr_default(
-                module_name=self.__class__.__name__, attr='dhcp-wait')
-            wait = not str(dhcp_wait).lower() == "no"
-            vrf = ifaceobj.get_attr_value_first('vrf')
-            if (vrf and self.vrf_exec_cmd_prefix and
-                self.ipcmd.link_exists(vrf)):
-                dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix, vrf)
-
-            if 'inet' in ifaceobj.addr_family:
-                if dhclient4_running:
-                    self.logger.info('dhclient4 already running on %s. '
-                                     'Not restarting.' % ifaceobj.name)
-                else:
-                    # First release any existing dhclient processes
-                    try:
-                        if not ifupdownflags.flags.PERFMODE:
-                            self.dhclientcmd.stop(ifaceobj.name)
-                    except:
-                        pass
-                    self.dhclientcmd.start(ifaceobj.name, wait=wait,
-                                           cmd_prefix=dhclient_cmd_prefix)
-            if 'inet6' in ifaceobj.addr_family:
-                if dhclient6_running:
-                    self.logger.info('dhclient6 already running on %s. '
-                                     'Not restarting.' % ifaceobj.name)
-                else:
-                    accept_ra = ifaceobj.get_attr_value_first('accept_ra')
-                    if accept_ra:
-                        # XXX: Validate value
-                        self.sysctl_set('net.ipv6.conf.%s' %ifaceobj.name +
-                                '.accept_ra', accept_ra)
-                    autoconf = ifaceobj.get_attr_value_first('autoconf')
-                    if autoconf:
-                        # XXX: Validate value
-                        self.sysctl_set('net.ipv6.conf.%s' %ifaceobj.name +
-                                '.autoconf', autoconf)
-                        try:
-                            self.dhclientcmd.stop6(ifaceobj.name)
-                        except:
-                            pass
-                    #add delay before starting IPv6 dhclient to
-                    #make sure the configured interface/link is up.
-                    time.sleep(2)
-                    timeout = 10
-                    while timeout:
-                        timeout -= 2
-                        addr_output = utils.exec_command('ip -6 addr show %s'
-                                                         % ifaceobj.name)
-                        r = re.search('inet6 .* scope link', addr_output)
-                        if r:
-                            self.dhclientcmd.start6(ifaceobj.name,
-                                                    wait=wait,
-                                                    cmd_prefix=dhclient_cmd_prefix)
-                            return
-                        time.sleep(2)
-
-        except Exception, e:
-            self.log_error(str(e), ifaceobj)
-
-    def _down_stale_dhcp_config(self, ifaceobj, family, dhclientX_running):
-        addr_family = ifaceobj.addr_family
-        try:
-            if not family in ifaceobj.addr_family and dhclientX_running:
-                ifaceobj.addr_family = [family]
-                self._dhcp_down(ifaceobj)
-        except:
-            pass
-        finally:
-            ifaceobj.addr_family = addr_family
-
-    def _dhcp_down(self, ifaceobj):
-        dhclient_cmd_prefix = None
-        vrf = ifaceobj.get_attr_value_first('vrf')
-        if (vrf and self.vrf_exec_cmd_prefix and
-            self.ipcmd.link_exists(vrf)):
-            dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix, vrf)
-        if 'inet6' in ifaceobj.addr_family:
-            self.dhclientcmd.release6(ifaceobj.name, dhclient_cmd_prefix)
-        if 'inet' in ifaceobj.addr_family:
-            self.dhclientcmd.release(ifaceobj.name, dhclient_cmd_prefix)
-
-    def _down(self, ifaceobj):
-        self._dhcp_down(ifaceobj)
-        self.ipcmd.link_down(ifaceobj.name)
-
-    def _query_check(self, ifaceobj, ifaceobjcurr):
-        status = ifaceStatus.SUCCESS
-        dhcp_running = False
-
-        dhcp_v4 = self.dhclientcmd.is_running(ifaceobjcurr.name)
-        dhcp_v6 = self.dhclientcmd.is_running6(ifaceobjcurr.name)
-
-        if dhcp_v4:
-            dhcp_running = True
-            if 'inet' not in ifaceobj.addr_family and not dhcp_v6:
-                status = ifaceStatus.ERROR
-            ifaceobjcurr.addr_method = 'dhcp'
-        if dhcp_v6:
-            dhcp_running = True
-            if 'inet6' not in ifaceobj.addr_family and not dhcp_v4:
-                status = ifaceStatus.ERROR
-            ifaceobjcurr.addr_method = 'dhcp'
-        ifaceobjcurr.addr_family = ifaceobj.addr_family
-        if not dhcp_running:
-            ifaceobjcurr.addr_family = []
-            status = ifaceStatus.ERROR
-        ifaceobjcurr.status = status
-
-    def _query_running(self, ifaceobjrunning):
-        if not self.ipcmd.link_exists(ifaceobjrunning.name):
-            return
-        if self.dhclientcmd.is_running(ifaceobjrunning.name):
-            ifaceobjrunning.addr_family.append('inet')
-            ifaceobjrunning.addr_method = 'dhcp'
-        if self.dhclientcmd.is_running6(ifaceobjrunning.name):
-            ifaceobjrunning.addr_family.append('inet6')
-            ifaceobjrunning.addr_method = 'dhcp6'
-
-    _run_ops = {'up' : _up,
-               'down' : _down,
-               'pre-down' : _down,
-               'query-checkcurr' : _query_check,
-               'query-running' : _query_running }
-
-    def get_ops(self):
-        """ returns list of ops supported by this module """
-        return self._run_ops.keys()
-
-    def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = iproute2()
-
-    def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
-        """ run dhcp configuration on the interface object passed as argument
-
-        Args:
-            **ifaceobj** (object): iface object
-
-            **operation** (str): any of 'up', 'down', 'query-checkcurr',
-                                 'query-running'
-
-        Kwargs:
-            **query_ifaceobj** (object): query check ifaceobject. This is only
-                valid when op is 'query-checkcurr'. It is an object same as
-                ifaceobj, but contains running attribute values and its config
-                status. The modules can use it to return queried running state
-                of interfaces. status is success if the running state is same
-                as user required state in ifaceobj. error otherwise.
-        """
-        op_handler = self._run_ops.get(operation)
-        if not op_handler:
-            return
-        try:
-            if (operation != 'query-running' and
-                   (ifaceobj.addr_method != 'dhcp' and 
-                       ifaceobj.addr_method != 'dhcp6')):
-                return
-        except:
-            return
-        if not self.is_dhcp_allowed_on(ifaceobj, syntax_check=False):
-            return
-        self._init_command_handlers()
-        if operation == 'query-checkcurr':
-            op_handler(self, ifaceobj, query_ifaceobj)
-        else:
-            op_handler(self, ifaceobj)
diff --git a/addons/ethtool.py b/addons/ethtool.py
deleted file mode 100644 (file)
index 7cb52c6..0000000
+++ /dev/null
@@ -1,373 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-import json
-import ifupdown.policymanager as policymanager
-
-try:
-    import os
-    from ipaddr import IPNetwork
-    from sets import Set
-    from ifupdown.iface import *
-    from ifupdown.exceptions import moduleNotSupported
-    from ifupdown.utils import utils
-    from ifupdownaddons.utilsbase import *
-    from ifupdownaddons.modulebase import moduleBase, NotSupported
-    from ifupdownaddons.iproute2 import iproute2
-    import ifupdown.ifupdownflags as ifupdownflags
-except ImportError, e:
-    raise ImportError (str(e) + "- required module not found")
-
-class ethtool(moduleBase,utilsBase):
-    """  ifupdown2 addon module to configure ethtool attributes """
-
-    _modinfo = {'mhelp' : 'ethtool configuration module for interfaces',
-                'attrs': {
-                      'link-speed' :
-                            {'help' : 'set link speed',
-                             'validvals' : ['100',
-                                            '1000',
-                                            '10000',
-                                            '25000',
-                                            '40000',
-                                            '50000',
-                                            '100000'],
-                             'example' : ['link-speed 1000'],
-                             'default' : 'varies by platform and port'},
-                      'link-duplex' :
-                            {'help': 'set link duplex',
-                             'example' : ['link-duplex full'],
-                             'validvals' : ['half', 'full'],
-                             'default' : 'full'},
-                      'link-autoneg' :
-                            {'help': 'set autonegotiation',
-                             'example' : ['link-autoneg on'],
-                             'validvals' : ['yes', 'no', 'on', 'off'],
-                             'default' : 'varies by platform and port'},
-                      'link-fec' :
-                            {'help': 'set forward error correction mode',
-                             'example' : ['link-fec rs'],
-                             'validvals' : ['rs', 'baser', 'on', 'off'],
-                             'default' : 'varies by platform and port'}}}
-
-    def __init__(self, *args, **kargs):
-        moduleBase.__init__(self, *args, **kargs)
-        if not os.path.exists('/sbin/ethtool'):
-            raise moduleNotSupported('module init failed: no /sbin/ethtool found')
-        self.ipcmd = None
-        # keep a list of iface objects who have modified link attributes
-        self.ifaceobjs_modified_configs = []
-
-    def _post_up(self, ifaceobj, operation='post_up'):
-        """
-        _post_up and _pre_down will reset the layer 2 attributes to default policy
-        settings.
-        """
-        if not self.ipcmd.link_exists(ifaceobj.name):
-            return
-        cmd = ''
-        feccmd = ''
-        for attr in ['speed', 'duplex', 'autoneg', 'fec']:
-            # attribute existed before but we must reset to default
-            config_val = ifaceobj.get_attr_value_first('link-%s'%attr)
-            default_val = policymanager.policymanager_api.get_iface_default(
-                module_name='ethtool',
-                ifname=ifaceobj.name,
-                attr='link-%s'%attr)
-
-            if not default_val and not config_val:
-                # there is no point in checking the running config
-                # if we have no default and the user did not have settings
-                continue
-            # check running values
-            running_val = self.get_running_attr(attr, ifaceobj)
-
-            if attr == 'autoneg':
-                config_val = utils.get_onoff_bool(config_val)
-
-            # we need to track if an interface has a configured value
-            # this will be used if there are duplicate iface stanza and
-            # the configured interface will always take precedence.
-            # so even if we do not change the settings because they match
-            # what is configured, we need to append it here so that later duplicate
-            # ifaces will see that we had a configured iface and not change things.
-            if config_val and config_val == running_val:
-                # running value is what is configured, do nothing
-                # this prevents unconfigured ifaces from resetting to default
-                self.ifaceobjs_modified_configs.append(ifaceobj.name)
-                continue
-
-            if not config_val and default_val and default_val == running_val:
-                # nothing configured but the default is running
-                continue
-            # if we are the oldest sibling, we have to reset to defaults
-            # unless a previous sibling had link attr configured and made changes
-            if ((ifaceobj.flags & iface.HAS_SIBLINGS) and
-                (ifaceobj.flags & iface.OLDEST_SIBLING) and
-                (ifaceobj.name in self.ifaceobjs_modified_configs)):
-                continue
-
-            # If we have siblings AND are not the oldest AND we have no configs,
-            # do not change anything. The only way a non-oldest sibling would
-            # change values is if it had configured settings. iface stanzas may
-            # not be squashed if addr_config_squash is not set so we still need this.
-            if ((ifaceobj.flags & iface.HAS_SIBLINGS) and
-                not (ifaceobj.flags & iface.OLDEST_SIBLING) and
-                not config_val):
-                continue
-
-            if attr == 'fec':
-                # if we got this far, we need to change it
-                if config_val and (config_val != running_val):
-                    # if the configured value is not set, set it
-                    feccmd = ' %s %s' % ("encoding", config_val)
-                elif default_val and (default_val != running_val):
-                    # or if it has a default not equal to running value, set it
-                    feccmd = ' %s %s' % ("encoding", default_val)
-                else:
-                    # no value set nor default, leave it alone
-                    pass
-            else:
-                # if we got this far, we need to change it
-                if config_val and (config_val != running_val):
-                    # if the configured value is not set, set it
-                    cmd += ' %s %s' % (attr, config_val)
-                elif default_val and (default_val != running_val):
-                    # or if it has a default not equal to running value, set it
-                    cmd += ' %s %s' % (attr, default_val)
-                else:
-                    # no value set nor default, leave it alone
-                    pass
-
-        if cmd:
-            try:
-                # we should only be calling ethtool if there
-                # is a speed set or we can find a default speed
-                # because we should only be calling ethtool on swp ports
-                # we also need to set this here in case we changed
-                # something.  this prevents unconfigured ifaces from resetting to default
-                self.ifaceobjs_modified_configs.append(ifaceobj.name)
-                cmd = 'ethtool -s %s %s' %(ifaceobj.name, cmd)
-                utils.exec_command(cmd)
-            except Exception, e:
-                self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
-        else:
-            pass
-
-        if feccmd:
-            try:
-                # we should only be calling ethtool if there
-                # is a speed set or we can find a default speed
-                # because we should only be calling ethtool on swp ports
-                # we also need to set this here in case we changed
-                # something.  this prevents unconfigured ifaces from resetting to default
-                self.ifaceobjs_modified_configs.append(ifaceobj.name)
-                feccmd = 'ethtool --set-fec %s %s' %(ifaceobj.name, feccmd)
-                utils.exec_command(feccmd)
-            except Exception, e:
-                self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
-        else:
-            pass
-
-    def _pre_down(self, ifaceobj):
-        pass #self._post_up(ifaceobj,operation="_pre_down")
-
-    def _query_check(self, ifaceobj, ifaceobjcurr):
-        """
-        _query_check() needs to compare the configured (or running)
-        attribute with the running attribute.
-
-        If there is nothing configured, we compare the default attribute with
-        the running attribute and FAIL if they are different.
-        This is because a reboot will lose their running attribute
-        (the default will get set).
-        """
-        for attr in ['speed', 'duplex', 'autoneg', 'fec']:
-            configured = ifaceobj.get_attr_value_first('link-%s'%attr)
-            # if there is nothing configured, do not check
-            if not configured:
-                if not ifupdownflags.flags.WITHDEFAULTS:
-                    continue
-            default = policymanager.policymanager_api.get_iface_default(
-                module_name='ethtool',
-                ifname=ifaceobj.name,
-                attr='link-%s'%attr)
-            # if we have no default, do not bother checking
-            # this avoids ethtool calls on virtual interfaces
-            if not default:
-                continue
-            # autoneg comes from ethtool whereas speed and duplex from /sys/class
-            running_attr = self.get_running_attr(attr, ifaceobj)
-            if not running_attr:
-                if not configured:
-                    continue
-                ifaceobjcurr.update_config_with_status('link-%s' % attr,
-                                                       'unknown', 1)
-                continue
-
-            if attr == 'autoneg':
-                if configured == 'yes' and running_attr == 'on':
-                    running_attr = 'yes'
-                elif configured == 'no' and running_attr == 'off':
-                    running_attr = 'no'
-
-            # we make sure we can get a running value first
-            if (running_attr and configured and running_attr == configured):
-                # PASS since running is what is configured
-                ifaceobjcurr.update_config_with_status('link-%s'%attr,
-                                                       running_attr, 0)
-            elif (running_attr and configured and running_attr != configured):
-                # We show a FAIL since it is not the configured or default
-                ifaceobjcurr.update_config_with_status('link-%s'%attr,
-                                                       running_attr, 1)
-            elif (running_attr and default and running_attr == default):
-                # PASS since running is default
-                ifaceobjcurr.update_config_with_status('link-%s'%attr,
-                                                       running_attr, 0)
-            elif (default or configured):
-                # We show a FAIL since it is not the configured or default
-                ifaceobjcurr.update_config_with_status('link-%s'%attr,
-                                                       running_attr, 1)
-        return
-
-    def get_autoneg(self,ethtool_output=None):
-        """
-        get_autoneg simply calls the ethtool command and parses out
-        the autoneg value.
-        """
-        ethtool_attrs = ethtool_output.split()
-        if ('Auto-negotiation:' in ethtool_attrs):
-            return(ethtool_attrs[ethtool_attrs.index('Auto-negotiation:')+1])
-        else:
-            return(None)
-
-    def get_fec_encoding(self,ethtool_output=None):
-        """
-        get_fec_encoding simply calls the ethtool show-fec command and parses out
-        the fec encoding value.
-        """
-        try:
-            for attr in ethtool_output.splitlines():
-                if attr.startswith('FEC encodings'):
-                    fec_attrs = attr.split()
-                    return(fec_attrs[fec_attrs.index(':')+1])
-        except Exception as e:
-            self.logger.debug('ethtool: problems in ethtool set-fec output'
-                               ' %s: %s' %(ethtool_output.splitlines(), str(e)))
-
-        return(None)
-
-    def get_running_attr(self,attr='',ifaceobj=None):
-        if not ifaceobj or not attr:
-            return
-        running_attr = None
-        try:
-            if attr == 'autoneg':
-                output = utils.exec_commandl(['ethtool', ifaceobj.name])
-                running_attr = self.get_autoneg(ethtool_output=output)
-            elif attr == 'fec':
-                output = utils.exec_command('ethtool --show-fec %s'%(ifaceobj.name))
-                running_attr = self.get_fec_encoding(ethtool_output=output)
-            else:
-                running_attr = self.read_file_oneline('/sys/class/net/%s/%s' % \
-                                                      (ifaceobj.name, attr))
-        except Exception as e:
-            # for nonexistent interfaces, we get an error (rc = 256 or 19200)
-            self.logger.debug('ethtool: problems calling ethtool or reading'
-                              ' /sys/class on iface %s for attr %s: %s' %
-                              (ifaceobj.name, attr, str(e)))
-        return running_attr
-
-
-    def _query_running(self, ifaceobj, ifaceobj_getfunc=None):
-        """
-        _query_running looks at the speed and duplex from /sys/class
-        and retreives autoneg from ethtool.  We do not report autoneg
-        if speed is not available because this usually means the link is
-        down and the autoneg value is not reliable when the link is down.
-        """
-        # do not bother showing swp ifaces that are not up for the speed
-        # duplex and autoneg are not reliable.
-        if not self.ipcmd.is_link_up(ifaceobj.name):
-            return
-        for attr in ['speed', 'duplex', 'autoneg']:
-            default_val = policymanager.policymanager_api.get_iface_default(
-                module_name='ethtool',
-                ifname=ifaceobj.name,
-                attr='link-%s'%attr)
-            # do not continue if we have no defaults
-            # this avoids ethtool calls on virtual interfaces
-            if not default_val:
-                continue
-            running_attr = self.get_running_attr(attr, ifaceobj)
-
-            # Only show the link attributes if they differ from defaults
-            # to see the defaults, we should implement another flag (--with-defaults)
-            if default_val == running_attr:
-                continue
-
-            # do not proceed if speed = 0
-            if attr == 'speed' and running_attr and running_attr == '0':
-                return
-            if running_attr:
-                ifaceobj.update_config('link-%s'%attr, running_attr)
-
-        return
-
-    def _query(self, ifaceobj, **kwargs):
-        """ add default policy attributes supported by the module """
-        for attr in ['speed', 'duplex', 'autoneg', 'fec']:
-            if ifaceobj.get_attr_value_first('link-%s'%attr):
-                continue
-            default = policymanager.policymanager_api.get_iface_default(
-                        module_name='ethtool',
-                        ifname=ifaceobj.name,
-                        attr='link-%s' %attr)
-            if not default:
-                continue
-            ifaceobj.update_config('link-%s' %attr, default)
-
-    _run_ops = {'pre-down' : _pre_down,
-                'post-up' : _post_up,
-                'query-checkcurr' : _query_check,
-                'query-running' : _query_running,
-                'query' : _query}
-
-    def get_ops(self):
-        """ returns list of ops supported by this module """
-        return self._run_ops.keys()
-
-    def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = iproute2()
-
-    def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
-        """ run ethtool configuration on the interface object passed as
-            argument
-
-        Args:
-            **ifaceobj** (object): iface object
-
-            **operation** (str): any of 'post-up', 'query-checkcurr',
-                'query-running'
-        Kwargs:
-            **query_ifaceobj** (object): query check ifaceobject. This is only
-                valid when op is 'query-checkcurr'. It is an object same as
-                ifaceobj, but contains running attribute values and its config
-                status. The modules can use it to return queried running state
-                of interfaces. status is success if the running state is same
-                as user required state in ifaceobj. error otherwise.
-        """
-        if (ifaceobj.link_kind or
-                    ifaceobj.link_privflags & ifaceLinkPrivFlags.LOOPBACK):
-            return
-        op_handler = self._run_ops.get(operation)
-        if not op_handler:
-            return
-        self._init_command_handlers()
-        if operation == 'query-checkcurr':
-            op_handler(self, ifaceobj, query_ifaceobj)
-        else:
-            op_handler(self, ifaceobj)
diff --git a/addons/link.py b/addons/link.py
deleted file mode 100644 (file)
index 0940a69..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-#!/usr/bin/python
-
-# This should be pretty simple and might not really even need to exist.
-# The key is that we need to call link_create with a type of "dummy"
-# since that will translate to 'ip link add loopbackX type dummy'
-# The config file should probably just indicate that the type is
-# loopback or dummy.
-
-from ifupdown.iface import *
-from ifupdownaddons.modulebase import moduleBase
-from ifupdownaddons.iproute2 import iproute2
-import ifupdown.ifupdownflags as ifupdownflags
-import logging
-
-class link(moduleBase):
-    _modinfo = {'mhelp' : 'create/configure link types. similar to ip-link',
-                'attrs' : {
-                   'link-type' :
-                        {'help' : 'type of link as in \'ip link\' command.',
-                         'validvals' : ['dummy', 'veth'],
-                         'example' : ['link-type <dummy|veth>']},
-                   'link-down' :
-                        {'help': 'keep link down',
-                         'example' : ['link-down yes/no'],
-                         'default' : 'no',
-                         'validvals' : ['yes', 'no']}}}
-
-    def __init__(self, *args, **kargs):
-        moduleBase.__init__(self, *args, **kargs)
-        self.ipcmd = None
-
-    def _is_my_interface(self, ifaceobj):
-        if ifaceobj.get_attr_value_first('link-type'):
-            return True
-        return False
-
-    def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
-        if ifaceobj.get_attr_value_first('link-down') == 'yes':
-            ifaceobj.link_privflags |= ifaceLinkPrivFlags.KEEP_LINK_DOWN
-
-    def _up(self, ifaceobj):
-        self.ipcmd.link_create(ifaceobj.name,
-                               ifaceobj.get_attr_value_first('link-type'))
-
-    def _down(self, ifaceobj):
-        if (not ifupdownflags.flags.PERFMODE and
-            not self.ipcmd.link_exists(ifaceobj.name)):
-           return
-        try:
-            self.ipcmd.link_delete(ifaceobj.name)
-        except Exception, e:
-            self.log_warn(str(e))
-
-    def _query_check(self, ifaceobj, ifaceobjcurr):
-        if not self.ipcmd.link_exists(ifaceobj.name):
-            ifaceobjcurr.update_config_with_status('link-type', 'None', 1)
-        else:
-            link_type = ifaceobj.get_attr_value_first('link-type')
-            if self.ipcmd.link_get_kind(ifaceobj.name) == link_type:
-                ifaceobjcurr.update_config_with_status('link-type',
-                                                        link_type, 0)
-            else:
-                ifaceobjcurr.update_config_with_status('link-type',
-                                                        link_type, 1)
-
-    _run_ops = {'pre-up' : _up,
-               'post-down' : _down,
-               'query-checkcurr' : _query_check}
-
-    def get_ops(self):
-        return self._run_ops.keys()
-
-    def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = iproute2()
-
-    def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
-        op_handler = self._run_ops.get(operation)
-        if not op_handler:
-            return
-        if (operation != 'query-running' and
-                not self._is_my_interface(ifaceobj)):
-            return
-        self._init_command_handlers()
-        if operation == 'query-checkcurr':
-            op_handler(self, ifaceobj, query_ifaceobj)
-        else:
-            op_handler(self, ifaceobj)
diff --git a/addons/loopback.py b/addons/loopback.py
deleted file mode 100644 (file)
index dd35917..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/usr/bin/python
-
-# This should be pretty simple and might not really even need to exist.
-# The key is that we need to call link_create with a type of "dummy"
-# since that will translate to 'ip link add loopbackX type dummy'
-# The config file should probably just indicate that the type is
-# loopback or dummy.
-
-from ifupdown.iface import *
-from ifupdownaddons.modulebase import moduleBase
-from ifupdownaddons.iproute2 import iproute2
-import logging
-
-class loopback(moduleBase):
-    _modinfo = {'mhelp' : 'configure extra loopback module based on ' +
-                         'dummy device' }
-
-    def __init__(self, *args, **kargs):
-        moduleBase.__init__(self, *args, **kargs)
-        self.ipcmd = None
-
-    def _is_loopback_by_name(self, ifacename):
-        return 'loop' in ifacename
-
-    def _up(self, ifaceobj):
-        if self._is_loopback_by_name(ifaceobj.name):
-           self.ipcmd.link_create(ifaceobj.name, 'dummy')
-
-    def _down(self, ifaceobj):
-        if not self.PERFMODE and not self.ipcmd.link_exists(ifaceobj.name):
-           return
-        try:
-            self.ipcmd.link_delete(ifaceobj.name)
-        except Exception, e:
-            self.log_warn(str(e))
-
-    def _query_check(self, ifaceobj, ifaceobjcurr):
-        if not self.ipcmd.link_exists(ifaceobj.name):
-           return
-
-    _run_ops = {'pre-up' : _up,
-               'post-down' : _down,
-               'query-checkcurr' : _query_check}
-
-    def get_ops(self):
-        return self._run_ops.keys()
-
-    def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = iproute2(**self.get_flags())
-
-    def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
-        op_handler = self._run_ops.get(operation)
-        if not op_handler:
-            return
-        if (operation != 'query-running' and
-                not self._is_loopback_by_name(ifaceobj.name)):
-            return
-        self._init_command_handlers()
-        if operation == 'query-checkcurr':
-            op_handler(self, ifaceobj, query_ifaceobj)
-        else:
-            op_handler(self, ifaceobj)
diff --git a/addons/mstpctl.py b/addons/mstpctl.py
deleted file mode 100644 (file)
index 4ad1157..0000000
+++ /dev/null
@@ -1,1194 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-
-import os
-from sets import Set
-from ifupdown.iface import *
-from ifupdown.utils import utils
-from ifupdownaddons.modulebase import moduleBase
-from ifupdownaddons.bridgeutils import brctl
-from ifupdownaddons.iproute2 import iproute2
-from ifupdown.netlink import netlink
-from ifupdownaddons.mstpctlutil import mstpctlutil
-from ifupdownaddons.systemutils import systemUtils
-import ifupdown.ifupdownflags as ifupdownflags
-import ifupdown.policymanager as policymanager
-
-class mstpctlFlags:
-    PORT_PROCESSED = 0x1
-
-class mstpctl(moduleBase):
-    """  ifupdown2 addon module to configure mstp attributes """
-
-    _modinfo = {'mhelp' : 'mstp configuration module for bridges',
-                'attrs' : {
-                   'mstpctl-ports' :
-                        {'help' : 'mstp ports',
-                         'compat' : True,
-                         'deprecated': True,
-                         'new-attribute': 'bridge-ports'},
-                   'mstpctl-stp' :
-                        {'help': 'bridge stp yes/no',
-                         'validvals' : ['yes', 'no', 'on', 'off'],
-                         'compat' : True,
-                         'default' : 'no',
-                         'deprecated': True,
-                         'new-attribute': 'bridge-stp'},
-                   'mstpctl-treeprio' :
-                        {'help': 'tree priority',
-                         'default' : '32768',
-                         'validvals' : ['0', '4096', '8192', '12288', '16384',
-                                        '20480', '24576', '28672', '32768',
-                                        '36864', '40960', '45056', '49152',
-                                        '53248', '57344', '61440'],
-                         'required' : False,
-                         'example' : ['mstpctl-treeprio 32768']},
-                   'mstpctl-ageing' :
-                        {'help': 'ageing time',
-                         'validrange' : ['0', '4096'],
-                         'default' : '300',
-                         'required' : False,
-                         'jsonAttr': 'ageingTime',
-                         'example' : ['mstpctl-ageing 300']},
-                    'mstpctl-maxage' :
-                        { 'help' : 'max message age',
-                          'validrange' : ['0', '255'],
-                          'default' : '20',
-                          'jsonAttr': 'maxAge',
-                          'required' : False,
-                          'example' : ['mstpctl-maxage 20']},
-                    'mstpctl-fdelay' :
-                        { 'help' : 'set forwarding delay',
-                          'validrange' : ['0', '255'],
-                          'default' : '15',
-                          'jsonAttr': 'fwdDelay',
-                          'required' : False,
-                          'example' : ['mstpctl-fdelay 15']},
-                    'mstpctl-maxhops' :
-                        { 'help' : 'bridge max hops',
-                          'validrange' : ['0', '255'],
-                          'default' : '15',
-                          'jsonAttr': 'maxHops',
-                          'required' : False,
-                          'example' : ['mstpctl-maxhops 15']},
-                    'mstpctl-txholdcount' :
-                        { 'help' : 'bridge transmit holdcount',
-                          'validrange' : ['0', '255'],
-                          'default' : '6',
-                          'jsonAttr': 'txHoldCounter',
-                          'required' : False,
-                          'example' : ['mstpctl-txholdcount 6']},
-                    'mstpctl-forcevers' :
-                        { 'help' : 'bridge force stp version',
-                          'validvals' : ['rstp', ],
-                          'default' : 'rstp',
-                          'required' : False,
-                          'jsonAttr': 'forceProtocolVersion',
-                          'example' : ['mstpctl-forcevers rstp']},
-                    'mstpctl-portpathcost' :
-                        { 'help' : 'bridge port path cost',
-                          'validvals': ['<interface-range-list>'],
-                          'validrange' : ['0', '65535'],
-                          'default' : '0',
-                          'jsonAttr' : 'adminExtPortCost',
-                          'required' : False,
-                          'example' : ['under the bridge: mstpctl-portpathcost swp1=0 swp2=1',
-                                       'under the port (recommended): mstpctl-portpathcost 0']},
-                    'mstpctl-portp2p' :
-                        { 'help' : 'bridge port p2p detection mode',
-                          'default' : 'auto',
-                          'jsonAttr' : 'adminPointToPoint',
-                          'validvals' : ['<interface-yes-no-auto-list>'],
-                          'required' : False,
-                          'example' : ['under the bridge: mstpctl-portp2p swp1=yes swp2=no',
-                                       'under the port (recommended): mstpctl-portp2p yes']},
-                    'mstpctl-portrestrrole' :
-                        { 'help' :
-                          'enable/disable port ability to take root role of the port',
-                          'default' : 'no',
-                          'jsonAttr' : 'restrictedRole',
-                          'validvals' : ['<interface-yes-no-list>'],
-                          'required' : False,
-                          'example' : ['under the bridge: mstpctl-portrestrrole swp1=yes swp2=no',
-                                       'under the port (recommended): mstpctl-portrestrrole yes']},
-                    'mstpctl-portrestrtcn' :
-                        { 'help' :
-                          'enable/disable port ability to propagate received topology change notification of the port',
-                          'default' : 'no',
-                          'jsonAttr' : 'restrictedTcn',
-                          'validvals' : ['<interface-yes-no-list>'],
-                          'required' : False,
-                          'example' : ['under the bridge: mstpctl-portrestrtcn swp1=yes swp2=no',
-                                       'under the port (recommended): mstpctl-portrestrtcn yes']},
-                    'mstpctl-bpduguard' :
-                        { 'help' :
-                          'enable/disable bpduguard',
-                          'default' : 'no',
-                          'jsonAttr' : 'bpduGuardPort',
-                          'validvals' : ['<interface-yes-no-list>'],
-                          'required' : False,
-                          'example' : ['under the bridge: mstpctl-bpduguard swp1=yes swp2=no',
-                                       'under the port (recommended): mstpctl-bpduguard yes']},
-                    'mstpctl-treeportprio' : 
-                        { 'help' :
-                          'port priority for MSTI instance',
-                          'default' : '128',
-                          'validvals': ['<interface-range-list>'],
-                          'validrange' : ['0', '240'],
-                          'jsonAttr': 'treeportprio',
-                          'required' : False,
-                          'example' : ['under the bridge: mstpctl-treeportprio swp1=128 swp2=128',
-                                       'under the port (recommended): mstpctl-treeportprio 128']},
-                    'mstpctl-hello' :
-                        { 'help' : 'set hello time',
-                          'validrange' : ['0', '255'],
-                          'default' : '2',
-                          'required' : False,
-                          'jsonAttr': 'helloTime',
-                          'example' : ['mstpctl-hello 2']},
-                    'mstpctl-portnetwork' : 
-                        { 'help' : 'enable/disable bridge assurance capability for a port',
-                          'validvals' : ['<interface-yes-no-list>'],
-                          'default' : 'no',
-                          'jsonAttr' : 'networkPort',
-                          'required' : False,
-                          'example' : ['under the bridge: mstpctl-portnetwork swp1=yes swp2=no',
-                                       'under the port (recommended): mstpctl-portnetwork yes']},
-                    'mstpctl-portadminedge' : 
-                        { 'help' : 'enable/disable initial edge state of the port',
-                          'validvals' : ['<interface-yes-no-list>'],
-                          'default' : 'no',
-                          'jsonAttr' : 'adminEdgePort',
-                          'required' : False,
-                          'example' : ['under the bridge: mstpctl-portadminedge swp1=yes swp2=no',
-                                       'under the port (recommended): mstpctl-portadminedge yes']},
-                    'mstpctl-portautoedge' : 
-                        { 'help' : 'enable/disable auto transition to/from edge state of the port',
-                          'validvals' : ['<interface-yes-no-list>'],
-                          'default' : 'yes',
-                          'jsonAttr' : 'autoEdgePort',
-                          'required' : False,
-                          'example' : ['under the bridge: mstpctl-portautoedge swp1=yes swp2=no',
-                                       'under the port (recommended): mstpctl-portautoedge yes']},
-                    'mstpctl-treeportcost' : 
-                        { 'help' : 'port tree cost',
-                          'validrange' : ['0', '255'],
-                          'required' : False,
-                          'jsonAttr': 'extPortCost',
-                          },
-                    'mstpctl-portbpdufilter' : 
-                        { 'help' : 'enable/disable bpdu filter on a port. ' +
-                                'syntax varies when defined under a bridge ' +
-                                'vs under a port',
-                          'validvals' : ['<interface-yes-no-list>'],
-                          'jsonAttr' : 'bpduFilterPort',
-                          'default' : 'no',
-                          'required' : False,
-                          'example' : ['under a bridge: mstpctl-portbpdufilter swp1=no swp2=no',
-                                       'under a port: mstpctl-portbpdufilter yes']},
-                        }}
-
-    # Maps mstp bridge attribute names to corresponding mstpctl commands
-    # XXX: This can be encoded in the modules dict above
-    _attrs_map = OrderedDict([('mstpctl-treeprio' , 'treeprio'),
-                  ('mstpctl-ageing' , 'ageing'),
-                  ('mstpctl-fdelay' , 'fdelay'),
-                  ('mstpctl-maxage' , 'maxage'),
-                  ('mstpctl-maxhops' , 'maxhops'),
-                  ('mstpctl-txholdcount' , 'txholdcount'),
-                  ('mstpctl-forcevers', 'forcevers'),
-                  ('mstpctl-hello' , 'hello')])
-
-    # Maps mstp port attribute names to corresponding mstpctl commands
-    # XXX: This can be encoded in the modules dict above
-    _port_attrs_map = {'mstpctl-portpathcost' : 'portpathcost',
-                 'mstpctl-portadminedge' : 'portadminedge',
-                 'mstpctl-portautoedge' : 'portautoedge' ,
-                 'mstpctl-portp2p' : 'portp2p',
-                 'mstpctl-portrestrrole' : 'portrestrrole',
-                 'mstpctl-portrestrtcn' : 'portrestrtcn',
-                 'mstpctl-bpduguard' : 'bpduguard',
-                 'mstpctl-treeportprio' : 'treeportprio',
-                 'mstpctl-treeportcost' : 'treeportcost',
-                 'mstpctl-portnetwork' : 'portnetwork',
-                 'mstpctl-portbpdufilter' : 'portbpdufilter'}
-
-    def __init__(self, *args, **kargs):
-        moduleBase.__init__(self, *args, **kargs)
-        self.ipcmd = None
-        self.name = self.__class__.__name__
-        self.brctlcmd = None
-        self.mstpctlcmd = None
-        self.mstpd_running = (True if systemUtils.is_process_running('mstpd')
-                             else False)
-        self.default_vxlan_ports_set_bpduparams = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='mstpctl-vxlan-always-set-bpdu-params')
-        if self.default_vxlan_ports_set_bpduparams == 'yes':
-            self.default_vxlan_ports_set_bpduparams = True
-        else:
-            self.default_vxlan_ports_set_bpduparams = False
-
-    def syntax_check(self, ifaceobj, ifaceobj_getfunc):
-        if self._is_bridge(ifaceobj):
-            if (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE
-                    and ifaceobj.get_attr_value_first('mstpctl-portadminedge')):
-                self.logger.error('%s: unsupported use of keyword '
-                                  '\'mstpctl-portadminedge\' when '
-                                  'bridge-vlan-aware is on'
-                                  % ifaceobj.name)
-                return False
-        return True
-
-    def _is_bridge(self, ifaceobj):
-        if (ifaceobj.get_attr_value_first('mstpctl-ports') or
-                ifaceobj.get_attr_value_first('bridge-ports')):
-            return True
-        return False
-
-    def _is_bridge_port(self, ifaceobj):
-        if self.brctlcmd.is_bridge_port(ifaceobj.name):
-            return True
-        return False
-
-    def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
-        if not self._is_bridge(ifaceobj):
-            return None
-        return self.parse_port_list(ifaceobj.name,
-                                    ifaceobj.get_attr_value_first(
-                                    'mstpctl-ports'), ifacenames_all)
-
-    def get_dependent_ifacenames_running(self, ifaceobj):
-        self._init_command_handlers()
-        if (self.brctlcmd.bridge_exists(ifaceobj.name) and
-                not self.mstpctlcmd.mstpbridge_exists(ifaceobj.name)):
-            return None
-        return self.brctlcmd.get_bridge_ports(ifaceobj.name)
-
-    def _get_bridge_port_attr_value(self, bridgename, portname, attr):
-        json_attr = self.get_mod_subattr(attr, 'jsonAttr')
-        return self.mstpctlcmd.get_bridge_port_attr(bridgename,
-                                                    portname,
-                                                    json_attr)
-
-    def _get_bridge_port_list(self, ifaceobj):
-
-        # port list is also available in the previously
-        # parsed dependent list. Use that if available, instead
-        # of parsing port expr again
-        port_list = ifaceobj.lowerifaces
-        if port_list:
-            return port_list
-        ports = ifaceobj.get_attr_value_first('mstpctl-ports')
-        if ports:
-            return self.parse_port_list(ifaceobj.name, ports)
-        else:
-            return None
-
-    def _ports_enable_disable_ipv6(self, ports, enable='1'):
-        for p in ports:
-            try:
-                self.write_file('/proc/sys/net/ipv6/conf/%s' %p +
-                                '/disable_ipv6', enable)
-            except Exception, e:
-                self.logger.info(str(e))
-                pass
-
-    def _add_ports(self, ifaceobj):
-        bridgeports = self._get_bridge_port_list(ifaceobj)
-
-        runningbridgeports = []
-        # Delete active ports not in the new port list
-        if not ifupdownflags.flags.PERFMODE:
-            runningbridgeports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
-            if runningbridgeports:
-                [netlink.link_set_nomaster(bport)
-                    for bport in runningbridgeports
-                        if not bridgeports or bport not in bridgeports]
-            else:
-                runningbridgeports = []
-        if not bridgeports:
-            return
-        err = 0
-        for bridgeport in Set(bridgeports).difference(Set(runningbridgeports)):
-            try:
-                if (not ifupdownflags.flags.DRYRUN and
-                    not self.ipcmd.link_exists(bridgeport)):
-                    self.log_warn('%s: bridge port %s does not exist'
-                            %(ifaceobj.name, bridgeport))
-                    err += 1
-                    continue
-                netlink.link_set_master(bridgeport, ifaceobj.name)
-                self.ipcmd.addr_flush(bridgeport)
-            except Exception, e:
-                self.log_error(str(e), ifaceobj)
-
-        if err:
-            self.log_error('error configuring bridge (missing ports)')
-
-    def _apply_bridge_settings(self, ifaceobj):
-        check = False if ifupdownflags.flags.PERFMODE else True
-        try:
-            # set bridge attributes
-            for attrname, dstattrname in self._attrs_map.items():
-                config_val = ifaceobj.get_attr_value_first(attrname)
-                default_val = policymanager.policymanager_api.get_iface_default(module_name=self.__class__.__name__, ifname=ifaceobj.name, attr=attrname)
-                if not default_val:
-                    default_val = self.get_mod_subattr(attrname,'default')
-                jsonAttr =  self.get_mod_subattr(attrname, 'jsonAttr')
-                try:
-                    running_val = self.mstpctlcmd.get_bridge_attr(
-                                    ifaceobj.name, jsonAttr)
-                except:
-                    self.logger.info('%s: could not get running %s value'
-                                     %(ifaceobj.name, attrname))
-                    running_val = None
-                if (not config_val and default_val and (running_val != default_val)):
-                    # this happens when users remove an attribute from a port
-                    # and expect the default to be restored with ifreload.
-                    config_val = default_val
-                elif not config_val:
-                    # there is nothing configured and no default to reset
-                    continue
-                try:
-                    if attrname == 'mstpctl-treeprio':
-                       self.mstpctlcmd.set_bridge_treeprio(ifaceobj.name,
-                                config_val, check)
-                    else:
-                       self.mstpctlcmd.set_bridge_attr(ifaceobj.name,
-                                dstattrname, config_val, check)
-                except Exception, e:
-                    self.logger.warn('%s' %str(e))
-                    pass
-
-            if self.ipcmd.bridge_is_vlan_aware(ifaceobj.name):
-                return
-            # set bridge port attributes
-            for attrname, dstattrname in self._port_attrs_map.items():
-                config_val = ifaceobj.get_attr_value_first(attrname)
-                default_val = self.get_mod_subattr(attrname,'default')
-                if not config_val:
-                    # nothing configured, we may need to reset all ports to defaults
-                    # if the default exists and jsonAttribute conversion exists
-                    try:
-                        jsonAttr =  self.get_mod_subattr(attrname, 'jsonAttr')
-                        if default_val and jsonAttr:
-                            bridgeports = self._get_bridge_port_list(ifaceobj)
-                            for port in bridgeports:
-                                if not self.brctlcmd.is_bridge_port(port):
-                                    continue
-                                self.mstpctlcmd.set_bridge_port_attr(ifaceobj.name,
-                                                                     port,
-                                                                     dstattrname,
-                                                                     default_val,
-                                                                     json_attr=jsonAttr)
-                    except:
-                        self.logger.info('%s: not resetting %s config'
-                                         %(ifaceobj.name, attrname))
-                    # leave the loop for this attribute
-                    continue
-
-                portlist = self.parse_port_list(ifaceobj.name, config_val)
-                if not portlist:
-                    self.log_error('%s: error parsing \'%s %s\''
-                         %(ifaceobj.name, attrname, config_val), ifaceobj)
-                    continue
-                # there was a configured value so we need to parse it
-                # and set the attribute for each port configured
-                for p in portlist:
-                    try:
-                        (port, val) = p.split('=')
-                        # if it is not bridge port, continue
-                        if not os.path.exists('/sys/class/net/%s/brport' %port):
-                            continue
-                        json_attr = self.get_mod_subattr(attrname, 'jsonAttr')
-                        self.mstpctlcmd.set_bridge_port_attr(ifaceobj.name,
-                                                             port,
-                                                             dstattrname,
-                                                             val,
-                                                             json_attr=json_attr)
-                    except Exception, e:
-                        self.log_error('%s: error setting %s (%s)'
-                                       %(ifaceobj.name, attrname, str(e)),
-                                       ifaceobj, raise_error=False)
-        except Exception, e:
-            self.log_warn(str(e))
-            pass
-
-    def _get_default_val(self, attr, ifaceobj, bridgeifaceobj):
-        if ((attr == 'mstpctl-portbpdufilter' or
-            attr == 'mstpctl-bpduguard') and
-            self.default_vxlan_ports_set_bpduparams and
-            (ifaceobj.link_kind & ifaceLinkKind.VXLAN)):
-            try:
-                config_val = bridgeifaceobj.get_attr_value_first(attr)
-            except Exception, e:
-                config_val = None
-            if config_val:
-                if ifaceobj.name not in [v.split('=')[0] for v in config_val.split()]:
-                    return 'yes'
-                else:
-                    index = [v.split('=')[0] for v in config_val.split()].index(ifaceobj.name)
-                    return [v.split('=')[1] for v in config_val.split()][index]
-            else:
-                return 'yes'
-        else:
-            default_val = policymanager.policymanager_api.get_iface_default(module_name=self.__class__.__name__, ifname=ifaceobj.name, attr=attr)
-            if not default_val:
-                return self.get_mod_subattr(attr,'default')
-            return default_val
-
-    def _apply_bridge_port_settings(self, ifaceobj, bridgename=None,
-                                    bridgeifaceobj=None,
-                                    stp_running_on=True,
-                                    mstpd_running=True):
-        check = False if ifupdownflags.flags.PERFMODE else True
-        applied = False
-        if not bridgename and bridgeifaceobj:
-            bridgename = bridgeifaceobj.name
-
-        if not stp_running_on:
-            # stp may get turned on at a later point
-            self.logger.info('%s: ignoring config'
-                             %(ifaceobj.name) +
-                             ' (stp on bridge %s is not on yet)' %bridgename)
-            return applied
-        bvlan_aware = self.ipcmd.bridge_is_vlan_aware(bridgename)
-        if (not mstpd_running or
-            not os.path.exists('/sys/class/net/%s/brport' %ifaceobj.name) or
-            not bvlan_aware):
-                if (not bvlan_aware and
-                    self.default_vxlan_ports_set_bpduparams and
-                    (ifaceobj.link_kind & ifaceLinkKind.VXLAN)):
-                    for attr in ['mstpctl-portbpdufilter',
-                                 'mstpctl-bpduguard']:
-                        json_attr = self.get_mod_subattr(attr, 'jsonAttr')
-                        config_val = self._get_default_val(attr, ifaceobj,
-                                                           bridgeifaceobj)
-                        try:
-                            self.mstpctlcmd.set_bridge_port_attr(bridgename,
-                                                                 ifaceobj.name,
-                                                                 self._port_attrs_map[attr],
-                                                                 config_val,
-                                                                 json_attr=json_attr)
-                        except Exception, e:
-                            self.log_warn('%s: error setting %s (%s)'
-                                          % (ifaceobj.name, attr, str(e)))
-                return applied
-        # set bridge port attributes
-        for attrname, dstattrname in self._port_attrs_map.items():
-            attrval = ifaceobj.get_attr_value_first(attrname)
-            config_val = ifaceobj.get_attr_value_first(attrname)
-            default_val = self._get_default_val(attrname, ifaceobj, bridgeifaceobj)
-            jsonAttr =  self.get_mod_subattr(attrname, 'jsonAttr')
-            # to see the running value, stp would have to be on
-            # so we would have parsed mstpctl showportdetail json output
-            try:
-                running_val = self.mstpctlcmd.get_bridge_port_attr(bridgename,
-                                                       ifaceobj.name, jsonAttr)
-            except:
-                self.logger.info('%s %s: could not get running %s value'
-                                 %(bridgename, ifaceobj.name, attrname))
-                running_val = None
-            if (not config_val and default_val and (running_val != default_val)):
-                # this happens when users remove an attribute from a port
-                # and expect the default to be restored with ifreload.
-                config_val = default_val
-            elif not config_val:
-                # there is nothing configured and no default to reset
-                continue
-
-            try:
-               self.mstpctlcmd.set_bridge_port_attr(bridgename,
-                           ifaceobj.name, dstattrname, config_val, json_attr=jsonAttr)
-               applied = True
-            except Exception, e:
-               self.log_error('%s: error setting %s (%s)'
-                              %(ifaceobj.name, attrname, str(e)), ifaceobj,
-                               raise_error=False)
-        return applied
-
-    def _apply_bridge_port_settings_all(self, ifaceobj,
-                                        ifaceobj_getfunc=None):
-        self.logger.info('%s: applying mstp configuration '
-                          %ifaceobj.name + 'specific to ports')
-        # Query running bridge ports. and only apply attributes on them
-        bridgeports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
-        if not bridgeports:
-           self.logger.debug('%s: cannot find bridgeports' %ifaceobj.name)
-           return
-        for bport in bridgeports:
-            self.logger.info('%s: processing mstp config for port %s'
-                             %(ifaceobj.name, bport))
-            if not self.ipcmd.link_exists(bport):
-               continue
-            if not os.path.exists('/sys/class/net/%s/brport' %bport):
-                continue
-            bportifaceobjlist = ifaceobj_getfunc(bport)
-            if not bportifaceobjlist:
-               continue
-            for bportifaceobj in bportifaceobjlist:
-                # Dont process bridge port if it already has been processed
-                if (bportifaceobj.module_flags.get(self.name,0x0) & \
-                    mstpctlFlags.PORT_PROCESSED):
-                    continue
-                try:
-                    self._apply_bridge_port_settings(bportifaceobj,
-                                            ifaceobj.name, ifaceobj)
-                except Exception, e:
-                    pass
-                    self.log_warn(str(e))
-
-    def _is_running_userspace_stp_state_on(self, bridgename):
-        stp_state_file = '/sys/class/net/%s/bridge/stp_state' %bridgename
-        if not stp_state_file:
-            return False
-        running_stp_state = self.read_file_oneline(stp_state_file)
-        if running_stp_state and running_stp_state == '2':
-            return True
-        return False
-
-    def _up(self, ifaceobj, ifaceobj_getfunc=None):
-        # Check if bridge port
-        bridgename = self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name)
-        if bridgename:
-            mstpd_running = self.mstpd_running
-            stp_running_on = self._is_running_userspace_stp_state_on(bridgename)
-            applied = self._apply_bridge_port_settings(ifaceobj, bridgename,
-                                                       None, stp_running_on,
-                                                       mstpd_running)
-            if applied:
-                ifaceobj.module_flags[self.name] = \
-                        ifaceobj.module_flags.setdefault(self.name,0) | \
-                        mstpctlFlags.PORT_PROCESSED
-            return
-        if not self._is_bridge(ifaceobj):
-            return
-        # we are now here because the ifaceobj is a bridge
-        stp = None
-        try:
-            porterr = False
-            porterrstr = ''
-            if ifaceobj.get_attr_value_first('mstpctl-ports'):
-                # If bridge ports specified with mstpctl attr, create the
-                # bridge and also add its ports
-                self.ipcmd.batch_start()
-                if not ifupdownflags.flags.PERFMODE:
-                    if not self.ipcmd.link_exists(ifaceobj.name):
-                        self.ipcmd.link_create(ifaceobj.name, 'bridge')
-                else:
-                    self.ipcmd.link_create(ifaceobj.name, 'bridge')
-                try:
-                    self._add_ports(ifaceobj)
-                except Exception, e:
-                    porterr = True
-                    porterrstr = str(e)
-                    pass
-                finally:
-                    self.ipcmd.batch_commit()
-                running_ports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
-                if running_ports:
-                    # disable ipv6 for ports that were added to bridge
-                    self._ports_enable_disable_ipv6(running_ports, '1')
-
-            stp = ifaceobj.get_attr_value_first('mstpctl-stp')
-            if stp:
-               self.set_iface_attr(ifaceobj, 'mstpctl-stp',
-                                    self.brctlcmd.set_stp)
-            else:
-               stp = self.brctlcmd.get_stp(ifaceobj.name)
-            if (self.mstpd_running and
-                    (stp == 'yes' or stp == 'on')):
-                self._apply_bridge_settings(ifaceobj)
-                self._apply_bridge_port_settings_all(ifaceobj,
-                            ifaceobj_getfunc=ifaceobj_getfunc)
-        except Exception, e:
-            self.log_error(str(e), ifaceobj)
-        if porterr:
-            raise Exception(porterrstr)
-
-    def _down(self, ifaceobj, ifaceobj_getfunc=None):
-        if not self._is_bridge(ifaceobj):
-            return
-        try:
-            if ifaceobj.get_attr_value_first('mstpctl-ports'):
-                # If bridge ports specified with mstpctl attr, delete the
-                # bridge
-                ports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
-                if ports:
-                    self._ports_enable_disable_ipv6(ports, '0')
-                self.brctlcmd.delete_bridge(ifaceobj.name)
-        except Exception, e:
-            self.log_error(str(e), ifaceobj)
-
-    def _query_running_attrs(self, ifaceobjrunning, bridge_vlan_aware=False):
-        bridgeattrdict = {}
-
-        tmpbridgeattrdict = self.mstpctlcmd.get_bridge_attrs(ifaceobjrunning.name)
-        #self.logger.info('A' + str(tmpbridgeattrdict))
-        if not tmpbridgeattrdict:
-            return bridgeattrdict
-
-        for k,v in tmpbridgeattrdict.items():
-            if k == 'stp' or not v:
-                continue
-            if k == 'ports':
-                ports = v.keys()
-                continue
-            attrname = 'mstpctl-' + k
-            if (v and v != self.get_mod_subattr(attrname, 'default')
-                and attrname != 'mstpctl-maxhops'):
-                bridgeattrdict[attrname] = [v]
-
-        ports = self.brctlcmd.get_bridge_ports(ifaceobjrunning.name)
-        # Do this only for vlan-UNAWARE-bridge
-        if ports and not bridge_vlan_aware:
-            portconfig = {'mstpctl-portautoedge' : '',
-                          'mstpctl-portbpdufilter' : '',
-                          'mstpctl-portnetwork' : '',
-                          'mstpctl-portpathcost' : '',
-                          'mstpctl-portadminedge' : '',
-                          'mstpctl-portp2p' : '',
-                          'mstpctl-portrestrrole' : '',
-                          'mstpctl-portrestrtcn' : '',
-                          'mstpctl-bpduguard' : '',
-                          'mstpctl-treeportprio' : '',
-                          'mstpctl-treeportcost' : ''}
-
-            for p in ports:
-
-                for attr in ['mstpctl-portautoedge',
-                             'mstpctl-portbpdufilter',
-                             'mstpctl-portnetwork',
-                             'mstpctl-portadminedge',
-                             'mstpctl-portp2p',
-                             'mstpctl-portrestrrole',
-                             'mstpctl-portrestrtcn',
-                             'mstpctl-bpduguard',
-                             '']:
-                    v = self._get_bridge_port_attr_value(ifaceobjrunning.name,
-                                                         p, attr)
-                    if v and v != 'no':
-                        portconfig[attr] += ' %s=%s' % (p, v)
-
-                for attr in ['mstpctl-portpathcost', 'mstpctl-treeportcost']:
-                    v = self._get_bridge_port_attr_value(ifaceobjrunning.name,
-                                                         p, attr)
-                    if v and v != self.get_mod_subattr(attr, 'default'):
-                        portconfig[attr] += ' %s=%s' % (p, v)
-
-            bridgeattrdict.update({k : [v] for k, v in portconfig.items()
-                                    if v})
-        return bridgeattrdict
-
-    def _get_config_stp(self, ifaceobj):
-        stp = (ifaceobj.get_attr_value_first('mstpctl-stp') or
-               ifaceobj.get_attr_value_first('bridge-stp') or
-               policymanager.policymanager_api.get_iface_default(module_name=self.__class__.__name__, ifname=ifaceobj.name, attr='mstpctl-stp') or
-               # this is a temporary method to access policy default value of bridge-stp
-               policymanager.policymanager_api.get_iface_default(module_name='bridge', ifname=ifaceobj.name, attr='bridge-stp'))
-        return utils.get_boolean_from_string(stp)
-
-    def _get_running_stp(self, ifaceobj):
-        stp = self.brctlcmd.get_stp(ifaceobj.name)
-        return utils.get_boolean_from_string(stp)
-
-    def _query_check_bridge(self, ifaceobj, ifaceobjcurr,
-                            ifaceobj_getfunc=None):
-        # list of attributes that are not supported currently
-        blacklistedattrs = ['mstpctl-portpathcost',
-                'mstpctl-treeportprio', 'mstpctl-treeportcost']
-        if not self.brctlcmd.bridge_exists(ifaceobj.name):
-            self.logger.debug('bridge %s does not exist' %ifaceobj.name)
-            return
-        ifaceattrs = self.dict_key_subset(ifaceobj.config,
-                                          self.get_mod_attrs())
-        if self.default_vxlan_ports_set_bpduparams:
-            for attr in ['mstpctl-portbpdufilter', 'mstpctl-bpduguard']:
-                if attr not in ifaceattrs:
-                    ifaceattrs.append(attr)
-        if not ifaceattrs:
-            return
-        runningattrs = self.mstpctlcmd.get_bridge_attrs(ifaceobj.name)
-        #self.logger.info('B' + str(runningattrs))
-        if not runningattrs:
-            runningattrs = {}
-        config_stp = self._get_config_stp(ifaceobj)
-        running_stp = self._get_running_stp(ifaceobj)
-        running_port_list = self.brctlcmd.get_bridge_ports(ifaceobj.name)
-        for k in ifaceattrs:
-            # for all mstpctl options
-            if k in blacklistedattrs:
-                continue
-            if ((k == 'mstpctl-portbpdufilter' or
-                 k == 'mstpctl-bpduguard')):
-                #special case, 'ifquery --check --with-defaults' on a VLAN
-                #unaware bridge
-                if not running_port_list:
-                    continue
-                if (not config_stp or not running_stp):
-                    continue
-                v = ifaceobj.get_attr_value_first(k)
-                config_val = {}
-                running_val = {}
-                result = 0
-                bridge_ports = {}
-                state = ''
-                if v:
-                    for bportval in v.split():
-                        config_val[bportval.split('=')[0]] = bportval.split('=')[1]
-                #for bport in bridgeports:
-                for bport in running_port_list:
-                    bportifaceobjlist = ifaceobj_getfunc(bport)
-                    if not bportifaceobjlist:
-                        continue
-                    for bportifaceobj in bportifaceobjlist:
-                        if (bport not in config_val):
-                            if (bportifaceobj.link_kind & ifaceLinkKind.VXLAN):
-                                if (not ifupdownflags.flags.WITHDEFAULTS or
-                                    (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE)):
-                                    continue
-                                conf = 'yes'
-                            else:
-                                continue
-                        else:
-                            if ((bportifaceobj.link_kind & ifaceLinkKind.VXLAN) and
-                                 (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE)):
-                                continue
-                            conf = config_val[bport]
-                        jsonAttr =  self.get_mod_subattr(k, 'jsonAttr')
-                        try:
-                            running_val = self.mstpctlcmd.get_bridge_port_attr(ifaceobj.name, bport, jsonAttr)
-                        except:
-                            self.logger.info('%s %s: could not get running %s value'
-                                    %(ifaceobj.name, bport, attr))
-                            running_val = None
-                        if conf != running_val:
-                            result = 1
-                        bridge_ports.update({bport : running_val})
-                for port, val in bridge_ports.items():
-                    #running state format
-                    #mstpctl-portbpdufilter swp2=yes swp1=yes vx-14567101=yes    [pass]
-                    #mstpctl-bpduguard swp2=yes swp1=yes vx-14567101=yes         [pass]
-                    state += port + '=' + val + ' '
-                if state:
-                    ifaceobjcurr.update_config_with_status(k, state, result)
-                continue
-
-            # get the corresponding ifaceobj attr
-            v = ifaceobj.get_attr_value_first(k)
-            if not v:
-                continue
-
-            # Get the running attribute
-            rv = runningattrs.get(k[8:])
-            if k == 'mstpctl-stp':
-                # special case stp compare because it may
-                # contain more than one valid values
-                stp_on_vals = ['on', 'yes']
-                stp_off_vals = ['off']
-                rv = self.brctlcmd.get_stp(ifaceobj.name)
-                if ((v in stp_on_vals and rv in stp_on_vals) or
-                    (v in stp_off_vals and rv in stp_off_vals)):
-                    ifaceobjcurr.update_config_with_status('mstpctl-stp', v, 0)
-                else:
-                    ifaceobjcurr.update_config_with_status('mstpctl-stp', v, 1)
-                continue
-
-            if k == 'mstpctl-ports':
-                # special case ports because it can contain regex or glob
-                # XXX: We get all info from mstputils, which means if
-                # mstpd is down, we will not be returning any bridge bridgeports
-                bridge_port_list = self._get_bridge_port_list(ifaceobj)
-                if not running_port_list and not bridge_port_list:
-                    continue
-                portliststatus = 1
-                if running_port_list and bridge_port_list:
-                    difference = Set(running_port_list).symmetric_difference(
-                                                        Set(bridge_port_list))
-                    if not difference:
-                        portliststatus = 0
-                ifaceobjcurr.update_config_with_status('mstpctl-ports',
-                    ' '.join(running_port_list)
-                    if running_port_list else '', portliststatus)
-            elif k[:12] == 'mstpctl-port' or k == 'mstpctl-bpduguard':
-                # Now, look at port attributes
-                # derive the mstpctlcmd attr name
-                #mstpctlcmdattrname = k[12:] if k[:12] == 'mstpctl-port' else k[8:]
-                mstpctlcmdattrname = k[8:]
-
-                # for port attributes, the attributes are in a list
-                # <portname>=<portattrvalue>
-                status = 0
-                currstr = ''
-                vlist = self.parse_port_list(ifaceobj.name, v)
-                if not vlist:
-                    continue
-                for vlistitem in vlist:
-                    try:
-                        (p, v) = vlistitem.split('=')
-                        currv = self._get_bridge_port_attr_value(ifaceobj.name, p, k)
-                        if currv:
-                            currstr += ' %s=%s' %(p, currv)
-                        else:
-                            currstr += ' %s=%s' %(p, 'None')
-                        if currv != v:
-                            status = 1
-                    except Exception, e:
-                        self.log_warn(str(e))
-                        pass
-                ifaceobjcurr.update_config_with_status(k, currstr, status)
-            elif not rv:
-                ifaceobjcurr.update_config_with_status(k, '', 1)
-            elif v != rv:
-                ifaceobjcurr.update_config_with_status(k, rv, 1)
-            else:
-                ifaceobjcurr.update_config_with_status(k, rv, 0)
-
-    def _query_check_bridge_vxlan_port(self, ifaceobj, ifaceobjcurr,
-                            ifaceobj_getfunc=None):
-        masters = ifaceobj.upperifaces
-        if not masters:
-            return
-        for bridge in masters:
-            bifaceobjlist = ifaceobj_getfunc(bridge)
-            for bifaceobj in bifaceobjlist:
-                if (self._is_bridge(bifaceobj) and
-                    self.default_vxlan_ports_set_bpduparams and
-                    (bifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE)):
-                        config_stp = self._get_config_stp(bifaceobj)
-                        running_stp = self._get_running_stp(bifaceobj)
-                        if (not config_stp or not running_stp):
-                            continue
-                        for attr in ['mstpctl-portbpdufilter',
-                                     'mstpctl-bpduguard']:
-                            jsonAttr =  self.get_mod_subattr(attr, 'jsonAttr')
-                            config_val = bifaceobj.get_attr_value_first(attr)
-                            if config_val:
-                                if ifaceobj.name not in [v.split('=')[0] for v in config_val.split()]:
-                                    if not ifupdownflags.flags.WITHDEFAULTS:
-                                        continue
-                                    config_val = 'yes'
-                                else:
-                                    index = [v.split('=')[0] for v in config_val.split()].index(ifaceobj.name)
-                                    config_val = [v.split('=')[1] for v in config_val.split()][index]
-                            else:
-                                if not ifupdownflags.flags.WITHDEFAULTS:
-                                    continue
-                                config_val = 'yes'
-                            try:
-                                running_val = self.mstpctlcmd.get_bridge_port_attr(bifaceobj.name,
-                                                    ifaceobj.name, jsonAttr)
-                            except:
-                                self.logger.info('%s %s: could not get running %s value'
-                                        %(bifaceobj.name, ifaceobj.name, attr))
-                                running_val = None
-                            ifaceobjcurr.update_config_with_status(attr,
-                                        running_val,
-                                        0 if running_val == config_val else 1)
-                        return
-
-
-    def _query_check_bridge_port(self, ifaceobj, ifaceobjcurr):
-        if not self.ipcmd.link_exists(ifaceobj.name):
-            #self.logger.debug('bridge port %s does not exist' %ifaceobj.name)
-            ifaceobjcurr.status = ifaceStatus.NOTFOUND
-            return
-        # Check if this is a bridge port
-        if not self._is_bridge_port(ifaceobj):
-            # mark all the bridge attributes as error
-            ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj,
-                            self._port_attrs_map.keys(), 0)
-            return
-        bridgename = self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name)
-        # list of attributes that are not supported currently
-        blacklistedattrs = ['mstpctl-portpathcost',
-                'mstpctl-treeportprio', 'mstpctl-treeportcost']
-        ifaceattrs = self.dict_key_subset(ifaceobj.config,
-                                          self._port_attrs_map.keys())
-        if not ifaceattrs:
-            return
-        runningattrs = self.mstpctlcmd.get_bridge_attrs(ifaceobj.name)
-        #self.logger.info('C' + str(runningattrs))
-        if not runningattrs:
-            runningattrs = {}
-        for k in ifaceattrs:
-            # for all mstpctl options
-            # get the corresponding ifaceobj attr
-            v = ifaceobj.get_attr_value_first(k)
-            if not v or k in blacklistedattrs:
-                ifaceobjcurr.update_config_with_status(k, v, -1)
-                continue
-            currv = self._get_bridge_port_attr_value(bridgename, ifaceobj.name, k)
-            if currv:
-                if currv != v:
-                    ifaceobjcurr.update_config_with_status(k, currv, 1)
-                else:
-                    ifaceobjcurr.update_config_with_status(k, currv, 0)
-            else:
-                ifaceobjcurr.update_config_with_status(k, None, 1)
-
-    def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
-        if self._is_bridge(ifaceobj):
-            self._query_check_bridge(ifaceobj, ifaceobjcurr, ifaceobj_getfunc)
-        elif ifaceobj.link_kind & ifaceLinkKind.VXLAN:
-            self._query_check_bridge_vxlan_port(ifaceobj, ifaceobjcurr,
-                                              ifaceobj_getfunc)
-        else:
-            self._query_check_bridge_port(ifaceobj, ifaceobjcurr)
-
-    def _query_bridge_port_attr(self, ifaceobjrunning, bridgename, attr, value_cmp):
-        v = self._get_bridge_port_attr_value(bridgename,
-                                             ifaceobjrunning.name,
-                                             attr)
-        if v and value_cmp and v != value_cmp:
-            ifaceobjrunning.update_config(attr, v)
-        elif v and not value_cmp:
-            ifaceobjrunning.update_config(attr, v)
-
-    def _query_running_bridge_port(self, ifaceobjrunning):
-        bridgename = self.ipcmd.bridge_port_get_bridge_name(
-                                ifaceobjrunning.name)
-        if not bridgename:
-            self.logger.warn('%s: unable to determine bridgename'
-                             %ifaceobjrunning.name)
-            return
-        if self.brctlcmd.get_stp(bridgename) == 'no':
-           # This bridge does not run stp, return
-           return
-        # if userspace stp not set, return
-        if self.systcl_get_net_bridge_stp_user_space() != '1':
-            return
-
-        self._query_bridge_port_attr(ifaceobjrunning, bridgename,
-                                     'mstpctl-portautoedge',
-                                     self.get_mod_subattr('mstpctl-portautoedge', 'default'))
-
-        self._query_bridge_port_attr(ifaceobjrunning, bridgename,
-                                     'mstpctl-portbpdufilter',
-                                     'no')
-
-        self._query_bridge_port_attr(ifaceobjrunning, bridgename,
-                                     'mstpctl-portnetwork',
-                                     'no')
-
-        # XXX: Can we really get path cost of a port ???
-        #v = self.mstpctlcmd.get_portpathcost(ifaceobjrunning.name, p)
-        #if v and v != self.get_mod_subattr('mstpctl-pathcost',
-        #                                   'default'):
-        #   ifaceobjrunning.update_config('mstpctl-network', v)
-
-        self._query_bridge_port_attr(ifaceobjrunning, bridgename,
-                                     'mstpctl-portadminedge',
-                                     'no')
-
-        self._query_bridge_port_attr(ifaceobjrunning, bridgename,
-                                     'mstpctl-portp2p',
-                                     'auto')
-
-        self._query_bridge_port_attr(ifaceobjrunning, bridgename,
-                                     'mstpctl-portrestrrole',
-                                     'no')
-
-        self._query_bridge_port_attr(ifaceobjrunning, bridgename,
-                                     'mstpctl-portrestrtcn',
-                                     'no')
-
-        self._query_bridge_port_attr(ifaceobjrunning, bridgename,
-                                     'mstpctl-bpduguard',
-                                     'no')
-
-        # XXX: Can we really get path cost of a port ???
-        #v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
-        #            p, 'treeprio')
-        #if v and v != self.get_mod_subattr('mstpctl-treeportprio',
-        #                                   'default'):
-        #    portconfig['mstpctl-treeportprio'] += ' %s=%s' %(p, v)
-
-        #v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
-        #               p, 'treecost')
-        #if v and v != self.get_mod_subattr('mstpctl-treeportcost',
-        #                                   'default'):
-        #    portconfig['mstpctl-treeportcost'] += ' %s=%s' %(p, v)
-
-    def _query_running_bridge(self, ifaceobjrunning):
-        if self.brctlcmd.get_stp(ifaceobjrunning.name) == 'no':
-           # This bridge does not run stp, return
-           return
-        # if userspace stp not set, return
-        if self.systcl_get_net_bridge_stp_user_space() != '1':
-            return
-        # Check if mstp really knows about this bridge
-        if not self.mstpctlcmd.mstpbridge_exists(ifaceobjrunning.name):
-            return
-        bridge_vlan_aware = False
-        if ifaceobjrunning.get_attr_value_first('bridge-vlan-aware') == 'yes':
-            bridge_vlan_aware = True
-        ifaceobjrunning.update_config_dict(self._query_running_attrs(
-                                           ifaceobjrunning,
-                                           bridge_vlan_aware))
-
-    def _query_running(self, ifaceobjrunning, **extra_args):
-        if self.brctlcmd.bridge_exists(ifaceobjrunning.name):
-            self._query_running_bridge(ifaceobjrunning)
-        elif self.brctlcmd.is_bridge_port(ifaceobjrunning.name):
-            self._query_running_bridge_port(ifaceobjrunning)
-
-    def _query_bridge_port(self, ifaceobj, ifaceobj_getfunc=None):
-        """
-        Example:
-        Configuration:
-            auto vxlan1wd
-            iface vxlan1wd
-                vxlan-id 1001
-
-            auto vxlan2wd
-            iface vxlan2wd
-                vxlan-id 1002
-
-            auto brwithdef2
-            iface brwithdef2
-                bridge_ports vxlan1wd vxlan2wd
-                bridge-vlan-aware yes
-
-        Output:
-        $ ifquery vxlan1wd
-            auto vxlan1wd
-            iface vxlan1wd
-                vxlan-id 1001
-
-        $ ifquery --with-defaults vxlan1wd
-            auto vxlan1wd
-            iface vxlan1wd
-                vxlan-id 1001
-                mstpctl-portbpdufilter yes
-                mstpctl-bpduguard yes
-        """
-        masters = ifaceobj.upperifaces
-        if not masters:
-            return
-        try:
-            for bridge in masters:
-                bifaceobj = ifaceobj_getfunc(bridge)[0]
-                if (self._is_bridge(bifaceobj) and
-                    self.default_vxlan_ports_set_bpduparams and
-                    (bifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE)):
-                        for attr in ['mstpctl-portbpdufilter',
-                                     'mstpctl-bpduguard']:
-                            jsonAttr =  self.get_mod_subattr(attr, 'jsonAttr')
-                            config_val = ifaceobj.get_attr_value_first(attr)
-                            if config_val or not ifupdownflags.flags.WITHDEFAULTS:
-                                continue
-                            config_val = 'yes'
-                            ifaceobj.replace_config(attr, config_val)
-                        return
-        except Exception, e:
-            self.logger.info("%s: %s" %(ifaceobj.name, str(e)))
-            pass
-
-    def _query(self, ifaceobj, ifaceobj_getfunc=None, **kwargs):
-        """ add default policy attributes supported by the module """
-        if not self._is_bridge(ifaceobj):
-            if (ifaceobj.module_flags.get(self.name,0x0) &
-                    mstpctlFlags.PORT_PROCESSED):
-                return
-            self._query_bridge_port(ifaceobj, ifaceobj_getfunc)
-            ifaceobj.module_flags[self.name] = (
-                        ifaceobj.module_flags.setdefault(self.name,0) |
-                        mstpctlFlags.PORT_PROCESSED)
-            return
-        lowerinfs = ifaceobj.lowerifaces
-        if not lowerinfs:
-            return
-        if ifaceobj.get_attr_value_first('bridge-vlan-aware') != 'yes':
-            for attr in ['mstpctl-portbpdufilter', 'mstpctl-bpduguard']:
-                state = ''
-                config = ifaceobj.get_attr_value_first(attr)
-                for port in lowerinfs:
-                    bportobjlist = ifaceobj_getfunc(port)
-                    for bportobj in bportobjlist:
-                        if bportobj.get_attr_value_first('vxlan-id'):
-                            if config:
-                                if port not in [v.split('=')[0] for v in config.split()]:
-                                    config += ' %s=yes' %port
-                            else:
-                                state += '%s=yes ' %port
-                ifaceobj.replace_config(attr, config if config else state)
-        else:
-            for attr in ['mstpctl-portbpdufilter', 'mstpctl-bpduguard']:
-                state = ''
-                config = ifaceobj.get_attr_value_first(attr)
-                for port in lowerinfs:
-                    bportobjlist = ifaceobj_getfunc(port)
-                    for bportobj in bportobjlist:
-                        if (bportobj.module_flags.get(self.name,0x0) &
-                            mstpctlFlags.PORT_PROCESSED):
-                            continue
-                        if bportobj.get_attr_value_first('vxlan-id'):
-                            if config:
-                                if port not in [v.split('=')[0] for v in config.split()]:
-                                    bportobj.update_config(attr, 'yes')
-                                else:
-                                    index = [v.split('=')[0] for v in config.split()].index(port)
-                                    state = [v.split('=')[1] for v in config.split()][index]
-                                    bportobj.update_config(attr, '%s' %state)
-                                    v = config.split()
-                                    del v[index]
-                                    config = ' '.join(v)
-                            else:
-                                bportobj.replace_config(attr, 'yes')
-                            bportobj.module_flags[self.name] = (
-                                bportobj.module_flags.setdefault(self.name,0) |
-                                mstpctlFlags.PORT_PROCESSED)
-                if config:
-                    ifaceobj.replace_config(attr, config)
-
-
-
-    _run_ops = {'pre-up' : _up,
-               'post-down' : _down,
-               'query-checkcurr' : _query_check,
-               'query-running' : _query_running,
-               'query' : _query}
-
-    def get_ops(self):
-        """ returns list of ops supported by this module """
-        return self._run_ops.keys()
-
-    def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = iproute2()
-        if not self.brctlcmd:
-            self.brctlcmd = brctl()
-        if not self.mstpctlcmd:
-            self.mstpctlcmd = mstpctlutil()
-
-    def run(self, ifaceobj, operation, query_ifaceobj=None,
-            ifaceobj_getfunc=None, **extra_args):
-        """ run mstp configuration on the interface object passed as argument
-
-        Args:
-            **ifaceobj** (object): iface object
-
-            **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
-                                 'query-running'
-        Kwargs:
-            **query_ifaceobj** (object): query check ifaceobject. This is only
-                valid when op is 'query-checkcurr'. It is an object same as
-                ifaceobj, but contains running attribute values and its config
-                status. The modules can use it to return queried running state
-                of interfaces. status is success if the running state is same
-                as user required state in ifaceobj. error otherwise.
-        """
-        if ifaceobj.type == ifaceType.BRIDGE_VLAN:
-           return
-        op_handler = self._run_ops.get(operation)
-        if not op_handler:
-           return
-        self._init_command_handlers()
-        if operation == 'query-checkcurr':
-            op_handler(self, ifaceobj, query_ifaceobj,
-                       ifaceobj_getfunc=ifaceobj_getfunc)
-        else:
-            op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)
diff --git a/addons/ppp.py b/addons/ppp.py
deleted file mode 100644 (file)
index fe983d0..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-#!/usr/bin/python
-
-try:
-    import os
-    import ifupdown.statemanager as statemanager
-    import ifupdown.ifupdownflags as ifupdownflags
-    import logging
-    import hashlib
-    from ifupdown.iface import *
-    from ifupdown.utils import utils
-    from ifupdownaddons.modulebase import moduleBase
-    from ifupdownaddons.iproute2 import iproute2
-    from ifupdown.netlink import netlink
-    from ifupdown.exceptions import moduleNotSupported
-except ImportError, e:
-    raise ImportError (str(e) + "- required module not found")
-
-class ppp (moduleBase):
-    _modinfo = { 'mhelp' : 'create/configure ppp interfaces',
-                 'attrs' : {
-                   'provider' :
-                        { 'help' : 'Provider file in ppp',
-                          'validvals' : ['<text>'],
-                          'required' : True,
-                          'example' : ['dsl-provider']},
-                   'ppp-physdev' :
-                        { 'help' : 'Physical underlay device to use for ppp if any',
-                          'validvals' : ['<interface>'],
-                          'required' : False,
-                          'example' : ['ppp-physdev eth1']},
-                }
-               }
-
-
-    def __init__ (self, *args, **kargs):
-        moduleBase.__init__ (self, *args, **kargs)
-        if not os.path.exists('/usr/bin/pon'):
-            raise moduleNotSupported('module init failed: no /usr/bin/pon found')
-        self.ipcmd = None
-
-    def _is_my_interface (self, ifaceobj):
-        if ifaceobj.addr_method == "ppp" and ifaceobj.get_attr_value_first ('provider'):
-            return True
-        return False
-
-    def _up (self, ifaceobj):
-        '''
-        Up the PPP connection
-        '''
-        provider = ifaceobj.get_attr_value_first ('provider')
-        old_config = None
-        old_provider = None
-
-        try:
-            ppp_file = os.path.join('/etc/ppp/peers', provider)
-            if not os.path.isfile(ppp_file):
-                self.log_warn('Invalid ppp provider file does not exist')
-                return
-
-            # Load state data
-            saved_ifaceobjs = statemanager.statemanager_api.get_ifaceobjs(ifaceobj.name)
-            if saved_ifaceobjs:
-                old_provider = saved_ifaceobjs[0].get_attr_value_first ('provider')
-                old_config = saved_ifaceobjs[0].get_attr_value_first ('provider_file')
-
-            config = hashlib.sha256(open(ppp_file, 'rb').read()).hexdigest()
-            # Always save the current config files hash
-            ifaceobj.update_config('provider_file', config)
-
-            if not self.ipcmd.link_exists(ifaceobj.name):
-                utils.exec_commandl(['/usr/bin/pon', provider], stdout=None, stderr=None)
-            elif old_config and old_config != config:
-                # Restart on config change
-                utils.exec_commandl(['/usr/bin/poff', provider], stdout=None, stderr=None)
-                utils.exec_commandl(['/usr/bin/pon', provider], stdout=None, stderr=None)
-            elif old_provider and old_provider != provider:
-                # Restart on provider change
-                utils.exec_commandl(['/usr/bin/poff', old_provider], stdout=None, stderr=None)
-                utils.exec_commandl(['/usr/bin/pon', provider], stdout=None, stderr=None)
-
-        except Exception, e:
-            self.log_warn (str (e))
-
-    def _down (self, ifaceobj):
-        if not ifupdownflags.flags.PERFMODE and not self.ipcmd.link_exists (ifaceobj.name):
-           return
-        try:
-            provider = ifaceobj.get_attr_value_first ('provider')
-            utils.exec_commandl(['/usr/bin/poff', provider], stdout=None, stderr=None)
-        except Exception, e:
-            self.log_warn (str (e))
-
-    def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
-        if not self._is_my_interface(ifaceobj):
-            return None
-        
-        device = ifaceobj.get_attr_value_first ('ppp-physdev')
-        if device:
-            return [device]
-
-        return None
-
-    def _query_check (self, ifaceobj, ifaceobjcurr):
-        if not self.ipcmd.link_exists(ifaceobj.name):
-           return
-
-        ifaceobjcurr.status = ifaceStatus.SUCCESS
-
-    def _query_running(self, ifaceobjrunning):
-        if not self.ipcmd.link_exists(ifaceobjrunning.name):
-            return
-
-    # Operations supported by this addon (yet).
-    _run_ops = {
-        'pre-up' : _up,
-        'post-down' : _down,
-        'query-checkcurr' : _query_check,
-        'query-running' : _query_running,
-    }
-
-    def get_ops (self):
-        return self._run_ops.keys()
-
-    def _init_command_handlers (self):
-        if not self.ipcmd:
-            self.ipcmd = iproute2 ()
-
-    def run (self, ifaceobj, operation, query_ifaceobj = None, **extra_args):
-        op_handler = self._run_ops.get (operation)
-        if not op_handler:
-            return
-
-        if operation != 'query-running' and not self._is_my_interface (ifaceobj):
-            return
-
-        self._init_command_handlers ()
-        if operation == 'query-checkcurr':
-            op_handler (self, ifaceobj, query_ifaceobj)
-        else:
-            op_handler (self, ifaceobj)
diff --git a/addons/tunnel.py b/addons/tunnel.py
deleted file mode 100644 (file)
index 4242b08..0000000
+++ /dev/null
@@ -1,192 +0,0 @@
-#!/usr/bin/python
-#
-# Maximilian Wilhelm <max@rfc2324.org>
-#  --  Mon 10 Oct 2016 10:53:13 PM CEST
-#
-
-from ifupdown.iface import *
-from ifupdownaddons.modulebase import moduleBase
-from ifupdownaddons.iproute2 import iproute2
-from ifupdown.netlink import netlink
-import ifupdown.ifupdownflags as ifupdownflags
-import logging
-
-#
-# TODO: Add checks for ipip tunnels.
-#
-class tunnel (moduleBase):
-    _modinfo = { 'mhelp' : 'create/configure GRE/IPIP/SIT and GRETAP tunnel interfaces',
-                 'attrs' : {
-                   'mode' :
-                        { 'help' : 'type of tunnel as in \'ip link\' command.',
-                          'validvals' : ['gre', 'gretap', 'ipip', 'sit', 'vti', 'ip6gre', 'ipip6', 'ip6ip6', 'vti6'],
-                          'required' : True,
-                          'example' : ['mode gre']},
-                   'local' :
-                        { 'help' : 'IP of local tunnel endpoint',
-                          'validvals' : ['<ipv4>', '<ipv6>'],
-                          'required' : True,
-                          'example' : ['local 192.2.0.42']},
-                   'endpoint' :
-                        { 'help' : 'IP of remote tunnel endpoint',
-                          'validvals' : ['<ipv4>', '<ipv6>'],
-                          'required' : True,
-                          'example' : ['endpoint 192.2.0.23']},
-                   'ttl' :
-                        { 'help' : 'TTL for tunnel packets',
-                          'validvals' : ['<number>'],
-                          'required' : False,
-                          'example' : ['ttl 64']},
-                   'tunnel-physdev' :
-                        { 'help' : 'Physical underlay device to use for tunnel packets',
-                          'validvals' : ['<interface>'],
-                          'required' : False,
-                          'example' : ['tunnel-physdev eth1']},
-                 }
-               }
-
-
-    def __init__ (self, *args, **kargs):
-        moduleBase.__init__ (self, *args, **kargs)
-        self.ipcmd = None
-
-
-    def _is_my_interface (self, ifaceobj):
-        if ifaceobj.addr_method == "tunnel" and ifaceobj.get_attr_value_first ('mode'):
-            return True
-        return False
-
-
-    def _has_config_changed (self, attrs_present, attrs_configured):
-        for attr in set (attrs_present.keys () + attrs_configured.keys ()):
-            # 'mode' is not present in attrs_configured and checked explicitly
-            if attr == "mode":
-                continue
-
-            if attrs_present.get (attr, None) != attrs_configured.get (attr, None):
-                return True
-
-        return False
-
-
-    def _up (self, ifaceobj):
-        attr_map = {
-            # attr_name -> ip route param name
-            'local' : 'local',
-            'endpoint' : 'remote',
-            'ttl' : 'ttl',
-            'tunnel-physdev' : 'dev',
-        }
-
-        mode = ifaceobj.get_attr_value_first ('mode')
-        attrs = {}
-        attrs_mapped = {}
-
-        # Only include attributes which have been set and map ifupdown2 names
-        # to attribute names expected by iproute
-        for attr, iproute_attr in attr_map.items ():
-            attr_val = ifaceobj.get_attr_value_first (attr)
-            if attr_val != None:
-                attrs_mapped[iproute_attr] = attr_val
-               attrs[attr] = attr_val
-
-        # Create the tunnel if it doesn't exist yet...
-        if not self.ipcmd.link_exists(ifaceobj.name):
-            self.ipcmd.tunnel_create (ifaceobj.name, mode, attrs_mapped)
-            return
-
-        # If it's present, check if there were changes
-        current_attrs = self.ipcmd.link_get_linkinfo_attrs (ifaceobj.name)
-
-        try:
-            if current_attrs and current_attrs['mode'] != mode or self._has_config_changed (current_attrs, attrs):
-                # Mode and some other changes are not possible without recreating the interface,
-                # so just recreate it IFF there have been changes.
-                self.ipcmd.link_delete (ifaceobj.name)
-                self.ipcmd.tunnel_create (ifaceobj.name, mode, attrs_mapped)
-        except Exception, e:
-            self.log_warn (str (e))
-
-
-    def _down (self, ifaceobj):
-        if not ifupdownflags.flags.PERFMODE and not self.ipcmd.link_exists (ifaceobj.name):
-           return
-        try:
-            self.ipcmd.link_delete (ifaceobj.name)
-        except Exception, e:
-            self.log_warn (str (e))
-
-
-    def get_dependent_ifacenames (self, ifaceobj, ifacenames_all=None):
-        if not self._is_my_interface (ifaceobj):
-            return None
-
-        device = ifaceobj.get_attr_value_first ('tunnel-physdev')
-        if device:
-            return [device]
-
-        return None
-
-
-    def _query_check_n_update (self, ifaceobj, ifaceobjcurr, attrname, attrval,
-                               running_attrval):
-        if not ifaceobj.get_attr_value_first (attrname):
-            return
-
-        if running_attrval and attrval == running_attrval:
-           ifaceobjcurr.update_config_with_status (attrname, attrval, 0)
-        else:
-           ifaceobjcurr.update_config_with_status (attrname, running_attrval, 1)
-
-
-    def _query_check (self, ifaceobj, ifaceobjcurr):
-        if not self.ipcmd.link_exists (ifaceobj.name):
-            return
-
-        tunattrs = self.ipcmd.link_get_linkinfo_attrs (ifaceobj.name)
-        if not tunattrs:
-            ifaceobjcurr.check_n_update_config_with_status_many (ifaceobj, self.get_mod_attrs (), -1)
-            return
-
-        for attr in self.get_mod_attrs ():
-            if not ifaceobj.get_attr_value_first (attr):
-                continue
-
-            # Validate all interface attributes set in the config.
-            # Remote any leading 'tunnel-' prefix in front of the attr name
-            # when accessing tunattrs parsed from 'ip -d link'.
-            self._query_check_n_update (ifaceobj, ifaceobjcurr, attr,
-                                        ifaceobj.get_attr_value_first (attr),
-                                        tunattrs.get (attr.replace ("tunnel-", "")))
-
-
-    # Operations supported by this addon (yet).
-    _run_ops = {
-        'pre-up' : _up,
-        'post-down' : _down,
-        'query-checkcurr' : _query_check
-    }
-
-
-    def get_ops (self):
-        return self._run_ops.keys()
-
-
-    def _init_command_handlers (self):
-        if not self.ipcmd:
-            self.ipcmd = iproute2 ()
-
-
-    def run (self, ifaceobj, operation, query_ifaceobj = None, **extra_args):
-        op_handler = self._run_ops.get (operation)
-        if not op_handler:
-            return
-
-        if operation != 'query-running' and not self._is_my_interface (ifaceobj):
-            return
-
-        self._init_command_handlers ()
-        if operation == 'query-checkcurr':
-            op_handler (self, ifaceobj, query_ifaceobj)
-        else:
-            op_handler (self, ifaceobj)
diff --git a/addons/usercmds.py b/addons/usercmds.py
deleted file mode 100644 (file)
index 212d661..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-
-import os
-import ifupdownaddons
-
-from ifupdown.utils import utils
-import ifupdown.ifupdownflags as ifupdownflags
-
-class usercmds(ifupdownaddons.modulebase.moduleBase):
-    """  ifupdown2 addon module to configure user specified commands """
-
-    _modinfo = {'mhelp' : 'user commands for interfaces',
-                'attrs' : {
-                   'pre-up' :
-                        {'help' : 'run command before bringing the interface up',
-                         'multiline' : True},
-                   'up' :
-                        {'help' : 'run command at interface bring up',
-                         'multiline' : True},
-                   'post-up' :
-                        {'help' : 'run command after interface bring up',
-                         'multiline' : True},
-                   'pre-down' :
-                        {'help' : 'run command before bringing the interface down',
-                         'multiline' : True},
-                   'down' :
-                        {'help' : 'run command at interface down',
-                         'multiline' : True},
-                   'post-down' :
-                        {'help' : 'run command after bringing interface down',
-                         'multiline' : True}}}
-
-    def _run_command(self, ifaceobj, op):
-        cmd_list = ifaceobj.get_attr_value(op)
-        if cmd_list:
-            os.environ['IFACE'] = ifaceobj.name if ifaceobj.name else ''
-            os.environ['LOGICAL'] = ifaceobj.name if ifaceobj.name else ''
-            os.environ['METHOD'] = ifaceobj.addr_method if ifaceobj.addr_method else ''
-            os.environ['ADDRFAM'] = ','.join(ifaceobj.addr_family) if ifaceobj.addr_family else ''
-            for cmd in cmd_list:
-                try:
-                    utils.exec_user_command(cmd)
-                except Exception, e:
-                    if not self.ignore_error(str(e)):
-                        self.logger.warn('%s: %s %s' % (ifaceobj.name, op,
-                                                        str(e).strip('\n')))
-                    pass
-
-    def _query_check(self, ifaceobj, ifaceobjcurr):
-        if ifaceobj.config:
-            for ops in ['pre-up',
-                        'up',
-                        'post-up',
-                        'pre-down',
-                        'down',
-                        'post-down']:
-                for cmd in ifaceobj.config.get(ops, []):
-                    ifaceobjcurr.update_config_with_status(ops, cmd, -1)
-
-    _run_ops = {'pre-up' : _run_command,
-               'pre-down' : _run_command,
-               'up' : _run_command,
-               'post-up' : _run_command,
-               'down' : _run_command,
-               'post-down' : _run_command,
-               'query-checkcurr': _query_check}
-
-    def get_ops(self):
-        """ returns list of ops supported by this module """
-        return self._run_ops.keys()
-
-    def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
-        """ run user commands
-
-        Args:
-            **ifaceobj** (object): iface object
-
-            **operation** (str): list of ops
-
-        Kwargs:
-            **query_ifaceobj** (object): query check ifaceobject. This is only
-                valid when op is 'query-checkcurr'. It is an object same as
-                ifaceobj, but contains running attribute values and its config
-                status. The modules can use it to return queried running state
-                of interfaces. status is success if the running state is same
-                as user required state in ifaceobj. error otherwise.
-        """
-        op_handler = self._run_ops.get(operation)
-        if not op_handler:
-            return
-        if operation == 'query-checkcurr':
-            op_handler(self, ifaceobj, query_ifaceobj)
-        else:
-            op_handler(self, ifaceobj, operation)
diff --git a/addons/vlan.py b/addons/vlan.py
deleted file mode 100644 (file)
index 0bdab46..0000000
+++ /dev/null
@@ -1,237 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-
-from ifupdown.iface import *
-from ifupdownaddons.modulebase import moduleBase
-from ifupdownaddons.iproute2 import iproute2
-import ifupdown.ifupdownconfig as ifupdownConfig
-
-from ifupdown.netlink import netlink
-import ifupdown.ifupdownflags as ifupdownflags
-import logging
-import re
-
-class vlan(moduleBase):
-    """  ifupdown2 addon module to configure vlans """
-
-    _modinfo = {'mhelp' : 'vlan module configures vlan interfaces.' +
-                        'This module understands vlan interfaces with dot ' +
-                        'notations. eg swp1.100. Vlan interfaces with any ' +
-                        'other names need to have raw device and vlan id ' +
-                        'attributes',
-                'attrs' : {
-                        'vlan-raw-device' :
-                            {'help' : 'vlan raw device',
-                             'validvals': ['<interface>']},
-                        'vlan-id' :
-                            {'help' : 'vlan id',
-                             'validrange' : ['0', '4096']}}}
-
-
-    def __init__(self, *args, **kargs):
-        moduleBase.__init__(self, *args, **kargs)
-        self.ipcmd = None
-        self._bridge_vids_query_cache = {}
-        self._resv_vlan_range =  self._get_reserved_vlan_range()
-        self.logger.debug('%s: using reserved vlan range %s'
-                  %(self.__class__.__name__, str(self._resv_vlan_range)))
-
-    def _is_vlan_device(self, ifaceobj):
-        vlan_raw_device = ifaceobj.get_attr_value_first('vlan-raw-device')
-        if vlan_raw_device:
-            return True
-        elif '.' in ifaceobj.name:
-            return True
-        return False
-
-    def _get_vlan_id(self, ifaceobj):
-        """ Derives vlanid from iface name
-        
-        Example:
-            Returns 1 for ifname vlan0001 returns 1
-            Returns 1 for ifname vlan1
-            Returns 1 for ifname eth0.1
-
-            Returns -1 if vlan id cannot be determined
-        """
-        vid_str = ifaceobj.get_attr_value_first('vlan-id')
-        try:
-            if vid_str: return int(vid_str)
-        except:
-            return -1
-
-        if '.' in ifaceobj.name:
-            vid_str = ifaceobj.name.split('.', 1)[1]
-        elif ifaceobj.name.startswith('vlan'):
-            vid_str = ifaceobj.name[4:]
-        else:
-            return -1
-        try:
-            vid = int(vid_str)
-        except:
-            return -1
-        return vid
-
-    def _is_vlan_by_name(self, ifacename):
-        return '.' in ifacename
-
-    def _get_vlan_raw_device_from_ifacename(self, ifacename):
-        """ Returns vlan raw device from ifname
-        Example:
-            Returns eth0 for ifname eth0.100
-
-            Returns None if vlan raw device name cannot
-            be determined
-        """
-        vlist = ifacename.split('.', 1)
-        if len(vlist) == 2:
-            return vlist[0]
-        return None
-
-    def _get_vlan_raw_device(self, ifaceobj):
-        vlan_raw_device = ifaceobj.get_attr_value_first('vlan-raw-device')
-        if vlan_raw_device:
-            return vlan_raw_device
-        return self._get_vlan_raw_device_from_ifacename(ifaceobj.name)
-
-    def get_dependent_ifacenames(self, ifaceobj, ifaceobjs_all=None):
-        if not self._is_vlan_device(ifaceobj):
-            return None
-        ifaceobj.link_kind |= ifaceLinkKind.VLAN
-        return [self._get_vlan_raw_device(ifaceobj)]
-
-    def _bridge_vid_add_del(self, ifaceobj, bridgename, vlanid,
-                            add=True):
-        """ If the lower device is a vlan aware bridge, add/del the vlanid
-        to the bridge """
-        if self.ipcmd.bridge_is_vlan_aware(bridgename):
-           if add:
-               netlink.link_add_bridge_vlan(bridgename, vlanid)
-           else:
-               netlink.link_del_bridge_vlan(bridgename, vlanid)
-
-    def _bridge_vid_check(self, ifaceobj, ifaceobjcurr, bridgename, vlanid):
-        """ If the lower device is a vlan aware bridge, check if the vlanid
-        is configured on the bridge """
-        if not self.ipcmd.bridge_is_vlan_aware(bridgename):
-            return
-        vids = self._bridge_vids_query_cache.get(bridgename)
-        if vids == None:
-           vids = self.ipcmd.bridge_port_vids_get(bridgename)
-           self._bridge_vids_query_cache[bridgename] = vids
-        if not vids or vlanid not in vids:
-            ifaceobjcurr.status = ifaceStatus.ERROR
-            ifaceobjcurr.status_str = 'bridge vid error'
-
-    def _up(self, ifaceobj):
-        vlanid = self._get_vlan_id(ifaceobj)
-        if vlanid == -1:
-            raise Exception('could not determine vlanid')
-        vlanrawdevice = self._get_vlan_raw_device(ifaceobj)
-        if not vlanrawdevice:
-            raise Exception('could not determine vlan raw device')
-        if not ifupdownflags.flags.PERFMODE:
-            if not self.ipcmd.link_exists(vlanrawdevice):
-                raise Exception('rawdevice %s not present' %vlanrawdevice)
-            if self.ipcmd.link_exists(ifaceobj.name):
-                self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid)
-                return
-        netlink.link_add_vlan(vlanrawdevice, ifaceobj.name, vlanid)
-        self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid)
-
-    def _down(self, ifaceobj):
-        vlanid = self._get_vlan_id(ifaceobj)
-        if vlanid == -1:
-            raise Exception('could not determine vlanid')
-        vlanrawdevice = self._get_vlan_raw_device(ifaceobj)
-        if not vlanrawdevice:
-            raise Exception('could not determine vlan raw device')
-        if (not ifupdownflags.flags.PERFMODE and
-            not self.ipcmd.link_exists(ifaceobj.name)):
-           return
-        try:
-            self.ipcmd.link_delete(ifaceobj.name)
-            self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid, add=False)
-        except Exception, e:
-            self.log_warn(str(e))
-
-    def _query_check(self, ifaceobj, ifaceobjcurr):
-        if not self.ipcmd.link_exists(ifaceobj.name):
-           return
-        if not '.' in ifaceobj.name:
-            # if vlan name is not in the dot format, check its running state
-            (vlanrawdev, vlanid) = self.ipcmd.get_vlandev_attrs(ifaceobj.name)
-            if vlanrawdev != ifaceobj.get_attr_value_first('vlan-raw-device'):
-                ifaceobjcurr.update_config_with_status('vlan-raw-device',
-                        vlanrawdev, 1)
-            else:
-                ifaceobjcurr.update_config_with_status('vlan-raw-device',
-                        vlanrawdev, 0)
-            vlanid_config = ifaceobj.get_attr_value_first('vlan-id')
-            if not vlanid_config:
-                vlanid_config = str(self._get_vlan_id(ifaceobj))
-            if vlanid != vlanid_config:
-                ifaceobjcurr.update_config_with_status('vlan-id', vlanid, 1)
-            else:
-                ifaceobjcurr.update_config_with_status('vlan-id', vlanid, 0)
-            self._bridge_vid_check(ifaceobj, ifaceobjcurr, vlanrawdev, vlanid)
-
-    def _query_running(self, ifaceobjrunning):
-        if not self.ipcmd.link_exists(ifaceobjrunning.name):
-            return
-        (vlanrawdev, vlanid) = self.ipcmd.get_vlandev_attrs(ifaceobjrunning.name)
-        if not vlanid:
-            return
-        # If vlan name is not in the dot format, get the
-        # vlan dev and vlan id
-        if not '.' in ifaceobjrunning.name:
-            ifaceobjrunning.update_config_dict({(k, v) for k, v in
-                                                {'vlan-raw-device' : vlanrawdev,
-                                                 'vlan-id' : vlanid}.items()
-                                                if v})
-
-    _run_ops = {'pre-up' : _up,
-               'post-down' : _down,
-               'query-checkcurr' : _query_check,
-               'query-running' : _query_running}
-
-    def get_ops(self):
-        """ returns list of ops supported by this module """
-        return self._run_ops.keys()
-
-    def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = iproute2()
-
-    def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
-        """ run vlan configuration on the interface object passed as argument
-
-        Args:
-            **ifaceobj** (object): iface object
-
-            **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
-                                 'query-running'
-        Kwargs:
-            **query_ifaceobj** (object): query check ifaceobject. This is only
-                valid when op is 'query-checkcurr'. It is an object same as
-                ifaceobj, but contains running attribute values and its config
-                status. The modules can use it to return queried running state
-                of interfaces. status is success if the running state is same
-                as user required state in ifaceobj. error otherwise.
-        """
-        if ifaceobj.type == ifaceType.BRIDGE_VLAN:
-            return
-        op_handler = self._run_ops.get(operation)
-        if not op_handler:
-            return
-        if (operation != 'query-running' and
-                not self._is_vlan_device(ifaceobj)):
-            return
-        self._init_command_handlers()
-        if operation == 'query-checkcurr':
-            op_handler(self, ifaceobj, query_ifaceobj)
-        else:
-            op_handler(self, ifaceobj)
diff --git a/addons/vrf.py b/addons/vrf.py
deleted file mode 100644 (file)
index 5e46aa3..0000000
+++ /dev/null
@@ -1,1036 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-
-import os
-import signal
-import errno
-import fcntl
-import atexit
-import re
-from sets import Set
-from ifupdown.iface import *
-from ifupdown.utils import utils
-import ifupdown.policymanager as policymanager
-import ifupdownaddons
-from ifupdown.netlink import netlink
-import ifupdown.ifupdownflags as ifupdownflags
-from ifupdownaddons.modulebase import moduleBase
-from ifupdownaddons.bondutil import bondutil
-from ifupdownaddons.iproute2 import iproute2
-from ifupdownaddons.dhclient import dhclient
-from ifupdownaddons.utilsbase import *
-
-class vrfPrivFlags:
-    PROCESSED = 0x1
-
-class vrf(moduleBase):
-    """  ifupdown2 addon module to configure vrfs """
-    _modinfo = { 'mhelp' : 'vrf configuration module',
-                    'attrs' : {
-                    'vrf-table':
-                         {'help' : 'vrf device routing table id. key to ' +
-                                   'creating a vrf device. ' +
-                                   'Table id is either \'auto\' or '+
-                                   '\'valid routing table id\'',
-                          'validvals': ['auto', '<number>'],
-                          'example': ['vrf-table auto', 'vrf-table 1001']},
-                    'vrf':
-                         {'help' : 'vrf the interface is part of.',
-                          'validvals': ['<text>'],
-                          'example': ['vrf blue']}}}
-
-    iproute2_vrf_filename = '/etc/iproute2/rt_tables.d/ifupdown2_vrf_map.conf'
-    iproute2_vrf_filehdr = '# This file is autogenerated by ifupdown2.\n' + \
-                           '# It contains the vrf name to table mapping.\n' + \
-                           '# Reserved table range %s %s\n'
-    VRF_TABLE_START = 1001
-    VRF_TABLE_END = 5000
-
-    system_reserved_rt_tables = {'255' : 'local', '254' : 'main', 
-                                 '253' : 'default', '0' : 'unspec'}
-
-    def __init__(self, *args, **kargs):
-        ifupdownaddons.modulebase.moduleBase.__init__(self, *args, **kargs)
-        self.ipcmd = None
-        self.bondcmd = None
-        self.dhclientcmd = None
-        self.name = self.__class__.__name__
-        self.vrf_mgmt_devname = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-mgmt-devname')
-
-        if (ifupdownflags.flags.PERFMODE and
-            not (self.vrf_mgmt_devname and os.path.exists('/sys/class/net/%s'
-            %self.vrf_mgmt_devname))):
-            # if perf mode is set (PERFMODE is set at boot), and this is the first
-            # time we are calling ifup at boot (check for mgmt vrf existance at
-            # boot, make sure this is really the first invocation at boot.
-            # ifup is called with PERFMODE at boot multiple times (once for mgmt vrf
-            # and the second time with all auto interfaces). We want to delete
-            # the map file only the first time. This is to avoid accidently
-            # deleting map file with a valid mgmt vrf entry
-            if os.path.exists(self.iproute2_vrf_filename):
-                try:
-                    self.logger.info('vrf: removing file %s'
-                                     %self.iproute2_vrf_filename)
-                    os.remove(self.iproute2_vrf_filename)
-                except Exception, e:
-                    self.logger.debug('vrf: removing file failed (%s)'
-                                      %str(e))
-        try:
-            ip_rules = utils.exec_command('/sbin/ip rule show').splitlines()
-            self.ip_rule_cache = [' '.join(r.split()) for r in ip_rules]
-        except Exception, e:
-            self.ip_rule_cache = []
-            self.logger.warn('vrf: cache v4: %s' % str(e))
-
-        try:
-            ip_rules = utils.exec_command('/sbin/ip -6 rule show').splitlines()
-            self.ip6_rule_cache = [' '.join(r.split()) for r in ip_rules]
-        except Exception, e:
-            self.ip6_rule_cache = []
-            self.logger.warn('vrf: cache v6: %s' % str(e))
-
-        #self.logger.debug("vrf: ip rule cache")
-        #self.logger.info(self.ip_rule_cache)
-
-        #self.logger.info("vrf: ip -6 rule cache")
-        #self.logger.info(self.ip6_rule_cache)
-
-        self.l3mdev_checked = False
-        self.l3mdev4_rule = False
-        if self._l3mdev_rule(self.ip_rule_cache):
-            self.l3mdev4_rule = True
-            self.l3mdev_checked = True
-        self.l3mdev6_rule = False
-        if self._l3mdev_rule(self.ip6_rule_cache):
-            self.l3mdev6_rule = True
-            self.l3mdev_checked = True
-        self._iproute2_vrf_map_initialized = False
-        self.iproute2_vrf_map = {}
-        self.iproute2_vrf_map_fd = None
-        self.iproute2_vrf_map_sync_to_disk = False
-
-        self.vrf_table_id_start = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-table-id-start')
-        if not self.vrf_table_id_start:
-            self.vrf_table_id_start = self.VRF_TABLE_START
-        self.vrf_table_id_end = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-table-id-end')
-        if not self.vrf_table_id_end:
-            self.vrf_table_id_end = self.VRF_TABLE_END
-        self.vrf_max_count = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-max-count')
-
-        self.vrf_fix_local_table = True
-        self.vrf_count = 0
-        self.vrf_helper = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-helper')
-        self.vrf_close_socks_on_down = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-close-socks-on-down')
-        self.warn_on_vrf_map_write_err = True
-
-    def _iproute2_vrf_map_initialize(self, writetodisk=True):
-        if self._iproute2_vrf_map_initialized:
-            return
-
-        # XXX: check for vrf reserved overlap in /etc/iproute2/rt_tables
-        self.iproute2_vrf_map = {}
-        iproute2_vrf_map_force_rewrite = False
-        # read or create /etc/iproute2/rt_tables.d/ifupdown2.vrf_map
-        if os.path.exists(self.iproute2_vrf_filename):
-            with open(self.iproute2_vrf_filename, 'r+') as vrf_map_fd:
-                lines = vrf_map_fd.readlines()
-                for l in lines:
-                    l = l.strip()
-                    if l[0] == '#':
-                        continue
-                    try:
-                        (table, vrf_name) = l.strip().split()
-                        if self.iproute2_vrf_map.get(int(table)):
-                            # looks like the existing file has
-                            # duplicate entries, force rewrite of the
-                            # file
-                            iproute2_vrf_map_force_rewrite = True
-                            continue
-                        self.iproute2_vrf_map[int(table)] = vrf_name
-                    except Exception, e:
-                        self.logger.info('vrf: iproute2_vrf_map: unable to parse %s (%s)' %(l, str(e)))
-                        pass
-
-        vrfs = self.ipcmd.link_get_vrfs()
-        running_vrf_map = {}
-        if vrfs:
-            for v, lattrs in vrfs.iteritems():
-                table = lattrs.get('table', None)
-                if table:
-                   running_vrf_map[int(table)] = v
-
-        if (not running_vrf_map or (running_vrf_map != self.iproute2_vrf_map)):
-            self.iproute2_vrf_map = running_vrf_map
-            iproute2_vrf_map_force_rewrite = True
-
-        self.iproute2_vrf_map_fd = None
-        if writetodisk:
-            if iproute2_vrf_map_force_rewrite:
-                # reopen the file and rewrite the map
-                self._iproute2_vrf_map_open(True, False)
-            else:
-                self._iproute2_vrf_map_open(False, True)
-
-        self.iproute2_vrf_map_sync_to_disk = False
-        atexit.register(self._iproute2_vrf_map_sync_to_disk)
-
-        self.logger.info("vrf: dumping iproute2_vrf_map")
-        self.logger.info(self.iproute2_vrf_map)
-
-        last_used_vrf_table = None
-        for t in range(self.vrf_table_id_start,
-                       self.vrf_table_id_end):
-            if not self.iproute2_vrf_map.get(t):
-                break
-            last_used_vrf_table = t
-        self.last_used_vrf_table = last_used_vrf_table
-        self._iproute2_vrf_map_initialized = True
-        self.vrf_count = len(self.iproute2_vrf_map)
-
-    def _iproute2_map_warn(self, errstr):
-        if self.warn_on_vrf_map_write_err:
-            if not os.path.exists('/etc/iproute2/rt_tables.d/'):
-                self.logger.info('unable to save iproute2 vrf to table ' +
-                                 'map (%s)\n' %errstr)
-                self.logger.info('cannot find /etc/iproute2/rt_tables.d.' +
-                                 ' pls check if your iproute2 version' +
-                                 ' supports rt_tables.d')
-            else:
-                self.logger.warn('unable to open iproute2 vrf to table ' +
-                                 'map (%s)\n' %errstr)
-            self.warn_on_vrf_map_write_err = False
-
-    def _iproute2_vrf_map_sync_to_disk(self):
-        if (ifupdownflags.flags.DRYRUN or
-            not self.iproute2_vrf_map_sync_to_disk):
-            return
-        self.logger.info('vrf: syncing table map to %s'
-                         %self.iproute2_vrf_filename)
-        try:
-            with open(self.iproute2_vrf_filename, 'w') as f:
-                f.write(self.iproute2_vrf_filehdr %(self.vrf_table_id_start,
-                        self.vrf_table_id_end))
-                for t, v in self.iproute2_vrf_map.iteritems():
-                    f.write('%s %s\n' %(t, v))
-                f.flush()
-        except Exception, e:
-            self._iproute2_map_warn(str(e))
-            pass
-
-    def _iproute2_vrf_map_open(self, sync_vrfs=False, append=False):
-        self.logger.info('vrf: syncing table map to %s'
-                         %self.iproute2_vrf_filename)
-        if ifupdownflags.flags.DRYRUN:
-            return
-        fmode = 'a+' if append else 'w'
-        try:
-            self.iproute2_vrf_map_fd = open(self.iproute2_vrf_filename,
-                                         '%s' %fmode)
-            fcntl.fcntl(self.iproute2_vrf_map_fd, fcntl.F_SETFD, fcntl.FD_CLOEXEC)
-        except Exception, e:
-            self._iproute2_map_warn(str(e))
-            return
-
-        if not append:
-            # write file header
-            self.iproute2_vrf_map_fd.write(self.iproute2_vrf_filehdr
-                                           %(self.vrf_table_id_start,
-                                             self.vrf_table_id_end))
-            for t, v in self.iproute2_vrf_map.iteritems():
-                self.iproute2_vrf_map_fd.write('%s %s\n' %(t, v))
-            self.iproute2_vrf_map_fd.flush()
-
-    def _is_vrf(self, ifaceobj):
-        if ifaceobj.get_attr_value_first('vrf-table'):
-            return True
-        return False
-
-    def get_upper_ifacenames(self, ifaceobj, ifacenames_all=None):
-        """ Returns list of interfaces dependent on ifaceobj """
-
-        vrf_table = ifaceobj.get_attr_value_first('vrf-table')
-        if vrf_table:
-            ifaceobj.link_type = ifaceLinkType.LINK_MASTER
-            ifaceobj.link_kind |= ifaceLinkKind.VRF
-            ifaceobj.role |= ifaceRole.MASTER
-        vrf_iface_name = ifaceobj.get_attr_value_first('vrf')
-        if not vrf_iface_name:
-            return None
-        ifaceobj.link_type = ifaceLinkType.LINK_SLAVE
-        ifaceobj.link_privflags |= ifaceLinkPrivFlags.VRF_SLAVE
-
-        return [vrf_iface_name]
-
-    def get_upper_ifacenames_running(self, ifaceobj):
-        return None
-
-    def _get_iproute2_vrf_table(self, vrf_dev_name):
-        for t, v in self.iproute2_vrf_map.iteritems():
-            if v == vrf_dev_name:
-                return str(t)
-        return None
-
-    def _get_avail_vrf_table_id(self):
-        if self.last_used_vrf_table == None:
-            table_id_start = self.vrf_table_id_start
-        else:
-            table_id_start = self.last_used_vrf_table + 1
-        for t in range(table_id_start,
-                       self.vrf_table_id_end):
-            if not self.iproute2_vrf_map.get(t):
-                self.last_used_vrf_table = t
-                return str(t)
-        return None
-
-    def _iproute2_is_vrf_tableid_inuse(self, vrfifaceobj, table_id):
-        old_vrf_name = self.iproute2_vrf_map.get(int(table_id))
-        if old_vrf_name and old_vrf_name != vrfifaceobj.name:
-            self.log_error('table id %s already assigned to vrf dev %s'
-                           %(table_id, old_vrf_name), vrfifaceobj)
-
-    def _iproute2_vrf_table_entry_add(self, vrfifaceobj, table_id):
-        old_vrf_name = self.iproute2_vrf_map.get(int(table_id))
-        if not old_vrf_name:
-            self.iproute2_vrf_map[int(table_id)] = vrfifaceobj.name
-            if self.iproute2_vrf_map_fd:
-                self.iproute2_vrf_map_fd.write('%s %s\n'
-                                               %(table_id, vrfifaceobj.name))
-                self.iproute2_vrf_map_fd.flush()
-                self.vrf_count += 1
-            return
-        if old_vrf_name != vrfifaceobj.name:
-            self.log_error('table id %d already assigned to vrf dev %s'
-                           %(table_id, old_vrf_name))
-
-    def _iproute2_vrf_table_entry_del(self, table_id):
-        try:
-            # with any del of vrf map, we need to force sync to disk
-            self.iproute2_vrf_map_sync_to_disk = True
-            del self.iproute2_vrf_map[int(table_id)]
-        except Exception, e:
-            self.logger.info('vrf: iproute2 vrf map del failed for %s (%s)'
-                             %(table_id, str(e)))
-            pass
-
-    def _is_vrf_dev(self, ifacename):
-        # Look at iproute2 map for now.
-        # If it was a master we knew about,
-        # it is definately there
-        if ifacename in self.iproute2_vrf_map.values():
-            return True
-        return False
-
-    def _is_dhcp_slave(self, ifaceobj):
-        if (not ifaceobj.addr_method or
-            (ifaceobj.addr_method != 'dhcp' and
-             ifaceobj.addr_method != 'dhcp6')):
-                return False
-        return True
-
-    def _up_vrf_slave_without_master(self, ifacename, vrfname, ifaceobj,
-                                     vrf_master_objs):
-        """ If we have a vrf slave that has dhcp configured, bring up the
-            vrf master now. This is needed because vrf has special handling
-            in dhclient hook which requires the vrf master to be present """
-
-        vrf_master = ifaceobj.upperifaces[0]
-        if not vrf_master:
-            self.logger.warn('%s: vrf master not found' %ifacename)
-            return
-        if os.path.exists('/sys/class/net/%s' %vrf_master):
-            self.logger.info('%s: vrf master %s exists returning'
-                             %(ifacename, vrf_master))
-            return
-        self.logger.info('%s: bringing up vrf master %s'
-                         %(ifacename, vrf_master))
-        for mobj in vrf_master_objs:
-            vrf_table = mobj.get_attr_value_first('vrf-table')
-            if vrf_table:
-                if vrf_table == 'auto':
-                    vrf_table = self._get_avail_vrf_table_id()
-                    if not vrf_table:
-                        self.log_error('%s: unable to get an auto table id'
-                                       %mobj.name, ifaceobj)
-                    self.logger.info('%s: table id auto: selected table id %s\n'
-                                     %(mobj.name, vrf_table))
-                try:
-                    self._up_vrf_dev(mobj, vrf_table, False)
-                except Exception:
-                    raise
-                break
-        self._handle_existing_connections(ifaceobj, vrfname)
-        netlink.link_set_master(ifacename, vrfname)
-        return
-
-    def _down_dhcp_slave(self, ifaceobj, vrfname):
-        try:
-            dhclient_cmd_prefix = None
-            if (vrfname and self.vrf_exec_cmd_prefix and
-                self.ipcmd.link_exists(vrfname)):
-                dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix,
-                                                vrfname)
-            self.dhclientcmd.release(ifaceobj.name, dhclient_cmd_prefix)
-        except:
-            # ignore any dhclient release errors
-            pass
-
-    def _handle_existing_connections(self, ifaceobj, vrfname):
-        if not ifaceobj or ifupdownflags.flags.PERFMODE:
-            return
-        if (self.vrf_mgmt_devname and
-            self.vrf_mgmt_devname == vrfname):
-            self._kill_ssh_connections(ifaceobj.name)
-        if self._is_dhcp_slave(ifaceobj):
-            self._down_dhcp_slave(ifaceobj, vrfname)
-
-    def _up_vrf_slave(self, ifacename, vrfname, ifaceobj=None,
-                      ifaceobj_getfunc=None, vrf_exists=False):
-        try:
-            master_exists = True
-            if vrf_exists or self.ipcmd.link_exists(vrfname):
-                uppers = self.ipcmd.link_get_uppers(ifacename)
-                if not uppers or vrfname not in uppers:
-                    self._handle_existing_connections(ifaceobj, vrfname)
-                    netlink.link_set_master(ifacename, vrfname)
-            elif ifaceobj:
-                vrf_master_objs = ifaceobj_getfunc(vrfname)
-                if not vrf_master_objs:
-                    # this is the case where vrf is assigned to an interface
-                    # but user has not provided a vrf interface.
-                    # people expect you to warn them but go ahead with the
-                    # rest of the config on that interface
-                    netlink.link_set_updown(ifacename, "up")
-                    self.log_error('vrf master ifaceobj %s not found'
-                                   %vrfname)
-                    return
-                if (ifupdownflags.flags.ALL or
-                    ifupdownflags.flags.WITH_DEPENDS or
-                    (ifupdownflags.flags.CLASS and
-                     ifaceobj.classes and vrf_master_objs[0].classes and
-                     Set(ifaceobj.classes).intersection(vrf_master_objs[0].classes))):
-                    self._up_vrf_slave_without_master(ifacename, vrfname,
-                                                      ifaceobj,
-                                                      vrf_master_objs)
-                else:
-                    master_exists = False
-            else:
-                master_exists = False
-            if master_exists:
-                netlink.link_set_updown(ifacename, "up")
-            else:
-                self.log_error('vrf %s not around, skipping vrf config'
-                               %(vrfname), ifaceobj)
-        except Exception, e:
-            self.log_error('%s: %s' %(ifacename, str(e)), ifaceobj)
-
-    def _del_vrf_rules(self, vrf_dev_name, vrf_table):
-        pref = 200
-        ip_rule_out_format = '%s: from all %s %s lookup %s'
-        ip_rule_cmd = 'ip %s rule del pref %s %s %s table %s' 
-
-        rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
-        if rule in self.ip_rule_cache:
-            rule_cmd = ip_rule_cmd %('', pref, 'oif', vrf_dev_name,
-                                     vrf_dev_name)
-            utils.exec_command(rule_cmd)
-
-        rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
-        if rule in self.ip_rule_cache:
-            rule_cmd = ip_rule_cmd %('', pref, 'iif', vrf_dev_name,
-                                     vrf_dev_name)
-            utils.exec_command(rule_cmd)
-
-        rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
-        if rule in self.ip6_rule_cache:
-            rule_cmd = ip_rule_cmd %('-6', pref, 'oif', vrf_dev_name,
-                                     vrf_dev_name)
-            utils.exec_command(rule_cmd)
-
-        rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
-        if rule in self.ip6_rule_cache:
-            rule_cmd = ip_rule_cmd %('-6', pref, 'iif', vrf_dev_name,
-                                     vrf_dev_name)
-            utils.exec_command(rule_cmd)
-
-    def _l3mdev_rule(self, ip_rules):
-        for rule in ip_rules:
-            if not re.search(r"\d.*from\s+all\s+lookup\s+\W?l3mdev-table\W?",
-                             rule):
-                continue
-            return True
-        return False
-
-    def _rule_cache_fill(self):
-        ip_rules = utils.exec_command('/sbin/ip rule show').splitlines()
-        self.ip_rule_cache = [' '.join(r.split()) for r in ip_rules]
-        self.l3mdev4_rule = self._l3mdev_rule(self.ip_rule_cache)
-        ip_rules = utils.exec_command('/sbin/ip -6 rule show').splitlines()
-        self.ip6_rule_cache = [' '.join(r.split()) for r in ip_rules]
-        self.l3mdev6_rule = self._l3mdev_rule(self.ip6_rule_cache)
-
-    def _add_vrf_rules(self, vrf_dev_name, vrf_table):
-        pref = 200
-        ip_rule_out_format = '%s: from all %s %s lookup %s'
-        ip_rule_cmd = 'ip %s rule add pref %s %s %s table %s' 
-        if self.vrf_fix_local_table:
-            self.vrf_fix_local_table = False
-            rule = '0: from all lookup local'
-            if rule in self.ip_rule_cache:
-                try:
-                    utils.exec_command('ip rule del pref 0')
-                    utils.exec_command('ip rule add pref 32765 table local')
-                except Exception, e:
-                    self.logger.info('%s: %s' % (vrf_dev_name, str(e)))
-                    pass
-            if rule in self.ip6_rule_cache:
-                try:
-                    utils.exec_command('ip -6 rule del pref 0')
-                    utils.exec_command('ip -6 rule add pref 32765 table local')
-                except Exception, e:
-                    self.logger.info('%s: %s' % (vrf_dev_name, str(e)))
-                    pass
-
-        if not self.l3mdev_checked:
-            self._rule_cache_fill()
-            self.l3mdev_checked = True
-        #Example ip rule
-        #200: from all oif blue lookup blue
-        #200: from all iif blue lookup blue
-
-        rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
-        if not self.l3mdev4_rule and rule not in self.ip_rule_cache:
-            rule_cmd = ip_rule_cmd %('', pref, 'oif', vrf_dev_name,
-                                     vrf_dev_name)
-            utils.exec_command(rule_cmd)
-
-        rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
-        if not self.l3mdev4_rule and rule not in self.ip_rule_cache:
-            rule_cmd = ip_rule_cmd %('', pref, 'iif', vrf_dev_name,
-                                     vrf_dev_name)
-            utils.exec_command(rule_cmd)
-
-        rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
-        if not self.l3mdev6_rule and rule not in self.ip6_rule_cache:
-            rule_cmd = ip_rule_cmd %('-6', pref, 'oif', vrf_dev_name,
-                                     vrf_dev_name)
-            utils.exec_command(rule_cmd)
-
-        rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
-        if not self.l3mdev6_rule and rule not in self.ip6_rule_cache:
-            rule_cmd = ip_rule_cmd %('-6', pref, 'iif', vrf_dev_name,
-                                     vrf_dev_name)
-            utils.exec_command(rule_cmd)
-
-    def _is_address_virtual_slaves(self, vrfobj, config_vrfslaves,
-                                   vrfslave):
-        # Address virtual lines on a vrf slave will create
-        # macvlan devices on the vrf slave and enslave them
-        # to the vrf master. This function checks if the
-        # vrf slave is such a macvlan interface.
-        # XXX: additional possible checks that can be done here
-        # are:
-        #  - check if it is also a macvlan device of the
-        #    format <vrf_slave>-v<int> created by the 
-        #    address virtual module
-        vrfslave_lowers = self.ipcmd.link_get_lowers(vrfslave)
-        if vrfslave_lowers:
-            if vrfslave_lowers[0] in config_vrfslaves:
-                return True
-        return False
-
-    def _add_vrf_slaves(self, ifaceobj, ifaceobj_getfunc=None):
-        running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
-        config_slaves = ifaceobj.lowerifaces
-        if not config_slaves and not running_slaves:
-            return
-
-        if not config_slaves: config_slaves = []
-        if not running_slaves: running_slaves = []
-        add_slaves = set(config_slaves).difference(set(running_slaves))
-        del_slaves = set(running_slaves).difference(set(config_slaves))
-        if add_slaves:
-            for s in add_slaves:
-                try:
-                    if not self.ipcmd.link_exists(s):
-                        continue
-                    sobj = None
-                    if ifaceobj_getfunc:
-                        sobj = ifaceobj_getfunc(s)
-                    self._up_vrf_slave(s, ifaceobj.name,
-                                       sobj[0] if sobj else None,
-                                       ifaceobj_getfunc, True)
-                except Exception, e:
-                    self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
-
-        if del_slaves:
-            for s in del_slaves:
-                try:
-                    if self._is_address_virtual_slaves(ifaceobj,
-                                                       config_slaves, s):
-                        continue
-                    sobj = None
-                    if ifaceobj_getfunc:
-                        sobj = ifaceobj_getfunc(s)
-                    self._down_vrf_slave(s, sobj[0] if sobj else None,
-                                         ifaceobj.name)
-                except Exception, e:
-                    self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
-
-        if ifaceobj.link_type == ifaceLinkType.LINK_MASTER:
-            for s in config_slaves:
-                try:
-                    netlink.link_set_updown(s, "up")
-                except Exception, e:
-                    self.logger.debug('%s: %s' % (ifaceobj.name, str(e)))
-                    pass
-
-    def _set_vrf_dev_processed_flag(self, ifaceobj):
-        ifaceobj.module_flags[self.name] = \
-                             ifaceobj.module_flags.setdefault(self.name, 0) | \
-                                        vrfPrivFlags.PROCESSED
-
-    def _check_vrf_dev_processed_flag(self, ifaceobj):
-        if (ifaceobj.module_flags.get(self.name, 0) & vrfPrivFlags.PROCESSED):
-            return True
-        return False
-
-    def _create_vrf_dev(self, ifaceobj, vrf_table):
-        if not self.ipcmd.link_exists(ifaceobj.name):
-            if ifaceobj.name in self.system_reserved_rt_tables.values():
-                self.log_error('cannot use system reserved %s vrf names'
-                                %(str(self.system_reserved_rt_tables.values())),
-                                ifaceobj)
-            if self.vrf_count == self.vrf_max_count:
-                self.log_error('max vrf count %d hit...not '
-                               'creating vrf' % self.vrf_count, ifaceobj)
-            if vrf_table == 'auto':
-                vrf_table = self._get_avail_vrf_table_id()
-                if not vrf_table:
-                    self.log_error('unable to get an auto table id', ifaceobj)
-                self.logger.info('%s: table id auto: selected table id %s\n'
-                                 %(ifaceobj.name, vrf_table))
-            else:
-                self._iproute2_is_vrf_tableid_inuse(ifaceobj, vrf_table)
-                if ifaceobj.name in self.system_reserved_rt_tables.keys():
-                    self.log_error('cannot use system reserved %s table ids'
-                                  %(str(self.system_reserved_rt_tables.keys())),
-                                  ifaceobj)
-
-            if not vrf_table.isdigit():
-                self.log_error('vrf-table must be an integer or \'auto\'', ifaceobj)
-
-            # XXX: If we decide to not allow vrf id usages out of
-            # the reserved ifupdown range, then uncomment this code.
-            else:
-                if (int(vrf_table) < self.vrf_table_id_start or
-                    int(vrf_table) > self.vrf_table_id_end):
-                    self.log_error('vrf table id %s out of reserved range [%d,%d]'
-                                   %(vrf_table,
-                                     self.vrf_table_id_start,
-                                     self.vrf_table_id_end), ifaceobj)
-            try:
-                self.ipcmd.link_create(ifaceobj.name, 'vrf',
-                                       {'table' : '%s' %vrf_table})
-            except Exception, e:
-                self.log_error('create failed (%s)\n' % str(e), ifaceobj)
-            if vrf_table != 'auto':
-                self._iproute2_vrf_table_entry_add(ifaceobj, vrf_table)
-        else:
-            if vrf_table == 'auto':
-                vrf_table = self._get_iproute2_vrf_table(ifaceobj.name)
-                if not vrf_table:
-                    self.log_error('unable to get vrf table id', ifaceobj)
-
-            # if the device exists, check if table id is same
-            vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
-            if vrfdev_attrs:
-                running_table = vrfdev_attrs.get('table', None)
-                if vrf_table != running_table:
-                    self.log_error('cannot change vrf table id,running table id'
-                                   ' %s is different from config id %s'
-                                   % (running_table, vrf_table), ifaceobj)
-        return vrf_table
-
-    def _up_vrf_helper(self, ifaceobj, vrf_table):
-        mode = ""
-        if ifupdownflags.flags.PERFMODE:
-            mode = "boot"
-        if self.vrf_helper:
-            utils.exec_command('%s create %s %s %s' %
-                               (self.vrf_helper,
-                                ifaceobj.name,
-                                vrf_table,
-                                mode))
-
-    def _up_vrf_dev(self, ifaceobj, vrf_table, add_slaves=True,
-                    ifaceobj_getfunc=None):
-
-        # if vrf dev is already processed return. This can happen
-        # if we the slave was configured before.
-        # see self._up_vrf_slave_without_master
-        if self._check_vrf_dev_processed_flag(ifaceobj):
-            return True
-
-        try:
-            vrf_table = self._create_vrf_dev(ifaceobj, vrf_table)
-        except Exception, e:
-            self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
-        
-        try:
-            self._add_vrf_rules(ifaceobj.name, vrf_table)
-            self._up_vrf_helper(ifaceobj, vrf_table)
-            if add_slaves:
-                self._add_vrf_slaves(ifaceobj, ifaceobj_getfunc)
-            self._set_vrf_dev_processed_flag(ifaceobj)
-            netlink.link_set_updown(ifaceobj.name, "up")
-        except Exception, e:
-            self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
-
-    def _kill_ssh_connections(self, ifacename):
-        try:
-            runningaddrsdict = self.ipcmd.addr_get(ifacename)
-            if not runningaddrsdict:
-                return
-            iplist = [i.split('/', 1)[0] for i in runningaddrsdict.keys()]
-            if not iplist:
-                return
-            proc=[]
-            #Example output:
-            #ESTAB      0      0      10.0.1.84:ssh       10.0.1.228:45186     
-            #users:(("sshd",pid=2528,fd=3))
-            cmdl = ['/bin/ss', '-t', '-p']
-            for line in utils.exec_commandl(cmdl).splitlines():
-                citems = line.split()
-                addr = None
-                if '%' in citems[3]:
-                    addr = citems[3].split('%')[0]
-                elif ':ssh' in citems[3]:
-                    addr = citems[3].split(':')[0]
-                if not addr:
-                    continue
-                if addr in iplist:
-                    if len(citems) == 6:
-                        proc.append(citems[5].split(',')[1].split('=')[1])
-
-            if not proc:
-                return
-            pid = None
-            # outpt of '/usr/bin/pstree -Aps <pid>':
-            # 'systemd(1)---sshd(990)---sshd(16112)---sshd(16126)---bash(16127)---sudo(16756)---ifreload(16761)---pstree(16842)\n'
-            # get the above output to following format
-            # ['systemd(1)', 'sshd(990)', 'sshd(16112)', 'sshd(16126)', 'bash(16127)', 'sudo(16756)', 'ifreload(16761)', 'pstree(16850)']
-            pstree = list(reversed(utils.exec_command('/usr/bin/pstree -Aps %s' %os.getpid()).strip().split('---')))
-            for index, process in enumerate(pstree):
-                # check the parent of SSH process to make sure
-                # we don't kill SSH server or systemd process
-                if 'sshd' in process and 'sshd' in pstree[index + 1]:
-                    pid = filter(lambda x: x.isdigit(), process)
-                    break
-            self.logger.info("%s: killing active ssh sessions: %s"
-                             %(ifacename, str(proc)))
-
-            if ifupdownflags.flags.DRYRUN:
-                return
-            for id in proc:
-                if id != pid:
-                    try:
-                        os.kill(int(id), signal.SIGINT)
-                    except OSError as e:
-                        continue
-
-            # Kill current SSH client
-            if pid in proc:
-                try:
-                    forkret = os.fork()
-                except OSError, e:
-                    self.logger.info("fork error : %s [%d]" % (e.strerror, e.errno))
-                if (forkret == 0):  # The first child.
-                    try:
-                        os.setsid()
-                        self.logger.info("%s: ifreload continuing in the background" %ifacename)
-                    except OSError, (err_no, err_message):
-                        self.logger.info("os.setsid failed: errno=%d: %s" % (err_no, err_message))
-                        self.logger.info("pid=%d  pgid=%d" % (os.getpid(), os.getpgid(0)))
-                try:
-                    self.logger.info("%s: killing our session: %s"
-                                     %(ifacename, str(proc)))
-                    os.kill(int(pid), signal.SIGINT)
-                    return
-                except OSError as e:
-                    return
-        except Exception, e:
-            self.logger.info('%s: %s' %(ifacename, str(e)))
-
-    def _up(self, ifaceobj, ifaceobj_getfunc=None):
-        try:
-            vrf_table = ifaceobj.get_attr_value_first('vrf-table')
-            if vrf_table:
-                self._iproute2_vrf_map_initialize()
-                # This is a vrf device
-                self._up_vrf_dev(ifaceobj, vrf_table, True, ifaceobj_getfunc)
-            else:
-                vrf = ifaceobj.get_attr_value_first('vrf')
-                if vrf:
-                    self._iproute2_vrf_map_initialize()
-                    # This is a vrf slave
-                    self._up_vrf_slave(ifaceobj.name, vrf, ifaceobj,
-                                       ifaceobj_getfunc)
-                else:
-                    # check if we were a slave before
-                    master = self.ipcmd.link_get_master(ifaceobj.name)
-                    if master:
-                        self._iproute2_vrf_map_initialize()
-                        if self._is_vrf_dev(master):
-                            self._down_vrf_slave(ifaceobj.name, ifaceobj,
-                                                 master)
-        except Exception, e:
-            self.log_error(str(e), ifaceobj)
-
-    def _down_vrf_helper(self, ifaceobj, vrf_table):
-        mode = ""
-        if ifupdownflags.flags.PERFMODE:
-            mode = "boot"
-        if self.vrf_helper:
-            utils.exec_command('%s delete %s %s %s' %
-                               (self.vrf_helper,
-                                ifaceobj.name,
-                                vrf_table,
-                                mode))
-
-    def _close_sockets(self, ifaceobj, ifindex):
-        if not self.vrf_close_socks_on_down:
-            return
-
-        try:
-            utils.exec_command('/bin/ss -aK \"dev == %s\"'
-                               %ifindex)
-        except Exception, e:
-            self.logger.info('%s: closing socks using ss'
-                             ' failed (%s)\n' %(ifaceobj.name, str(e)))
-            pass
-
-    def _down_vrf_dev(self, ifaceobj, vrf_table, ifaceobj_getfunc=None):
-
-        if not self.ipcmd.link_exists(ifaceobj.name):
-            return
-
-        if vrf_table == 'auto':
-            vrf_table = self._get_iproute2_vrf_table(ifaceobj.name)
-
-        running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
-        if running_slaves:
-            for s in running_slaves:
-                if ifaceobj_getfunc:
-                    sobj = ifaceobj_getfunc(s)
-                    try:
-                        self._handle_existing_connections(sobj[0]
-                                                          if sobj else None,
-                                                          ifaceobj.name)
-                    except Exception, e:
-                        self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
-                        pass
-                try:
-                    self.ipcmd.addr_flush(s)
-                    netlink.link_set_updown(s, "down")
-                except Exception, e:
-                    self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
-                    pass
-
-        try:
-            self._down_vrf_helper(ifaceobj, vrf_table)
-        except Exception, e:
-            self.logger.warn('%s: %s' %(ifaceobj.name, str(e)))
-            pass
-
-        try:
-            self._del_vrf_rules(ifaceobj.name, vrf_table)
-        except Exception, e:
-            self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
-            pass
-
-        ifindex = self.ipcmd.link_get_ifindex(ifaceobj.name)
-
-        try:
-            self.ipcmd.link_delete(ifaceobj.name)
-        except Exception, e:
-            self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
-            pass
-
-        self._close_sockets(ifaceobj, ifindex)
-
-        try:
-            self._iproute2_vrf_table_entry_del(vrf_table)
-        except Exception, e:
-            self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
-            pass
-
-
-    def _down_vrf_slave(self, ifacename, ifaceobj=None, vrfname=None):
-        try:
-            self._handle_existing_connections(ifaceobj, vrfname)
-            netlink.link_set_nomaster(ifacename)
-            # Down this slave only if it is a slave ifupdown2 manages.
-            # we dont want to down slaves that maybe up'ed by
-            # somebody else. One such example is a macvlan device
-            # which ifupdown2 addressvirtual addon module auto creates
-            if ifaceobj:
-                netlink.link_set_updown(ifacename, "down")
-        except Exception, e:
-            self.logger.warn('%s: %s' %(ifacename, str(e)))
-
-    def _down(self, ifaceobj, ifaceobj_getfunc=None):
-        try:
-            vrf_table = ifaceobj.get_attr_value_first('vrf-table')
-            if vrf_table:
-                self._iproute2_vrf_map_initialize()
-                self._down_vrf_dev(ifaceobj, vrf_table, ifaceobj_getfunc)
-            else:
-                vrf = ifaceobj.get_attr_value_first('vrf')
-                if vrf:
-                    self._iproute2_vrf_map_initialize()
-                    self._down_vrf_slave(ifaceobj.name, ifaceobj, None)
-        except Exception, e:
-            self.log_warn(str(e))
-
-    def _query_check_vrf_slave(self, ifaceobj, ifaceobjcurr, vrf):
-        try:
-            master = self.ipcmd.link_get_master(ifaceobj.name)
-            if not master or master != vrf:
-                ifaceobjcurr.update_config_with_status('vrf', str(master), 1)
-            else:
-                ifaceobjcurr.update_config_with_status('vrf', master, 0)
-        except Exception, e:
-            self.log_error(str(e), ifaceobjcurr)
-
-    def _query_check_vrf_dev(self, ifaceobj, ifaceobjcurr, vrf_table):
-        try:
-            if not self.ipcmd.link_exists(ifaceobj.name):
-                self.logger.info('%s: vrf: does not exist' %(ifaceobj.name))
-                return
-            if vrf_table == 'auto':
-                config_table = self._get_iproute2_vrf_table(ifaceobj.name)
-            else:
-                config_table = vrf_table
-            vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
-            if not vrfdev_attrs:
-                ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
-                return
-            running_table = vrfdev_attrs.get('table')
-            if not running_table:
-                ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
-                return
-            if config_table != running_table:
-                ifaceobjcurr.update_config_with_status('vrf-table',
-                                                       running_table, 1)
-            else:
-                ifaceobjcurr.update_config_with_status('vrf-table',
-                                                       running_table, 0)
-            if not ifupdownflags.flags.WITHDEFAULTS:
-                return
-            if self.vrf_helper:
-                try:
-                    utils.exec_command('%s verify %s %s'
-                                      %(self.vrf_helper,
-                                      ifaceobj.name, config_table))
-                    ifaceobjcurr.update_config_with_status('vrf-helper',
-                                                           '%s create %s %s'
-                                                           %(self.vrf_helper,
-                                                           ifaceobj.name,
-                                                           config_table), 0)
-                except Exception, e:
-                    ifaceobjcurr.update_config_with_status('vrf-helper',
-                                                           '%s create %s %s'
-                                                           %(self.vrf_helper,
-                                                           ifaceobj.name,
-                                                           config_table), 1)
-                    pass
-        except Exception, e:
-            self.log_warn(str(e))
-
-    def _query_check(self, ifaceobj, ifaceobjcurr):
-        try:
-            vrf_table = ifaceobj.get_attr_value_first('vrf-table')
-            if vrf_table:
-                self._iproute2_vrf_map_initialize(writetodisk=False)
-                self._query_check_vrf_dev(ifaceobj, ifaceobjcurr, vrf_table)
-            else:
-                vrf = ifaceobj.get_attr_value_first('vrf')
-                if vrf:
-                    self._iproute2_vrf_map_initialize(writetodisk=False)
-                    self._query_check_vrf_slave(ifaceobj, ifaceobjcurr, vrf)
-        except Exception, e:
-            self.log_warn(str(e))
-
-    def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
-        try:
-            kind = self.ipcmd.link_get_kind(ifaceobjrunning.name)
-            if kind == 'vrf':
-                vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobjrunning.name)
-                if vrfdev_attrs:
-                    running_table = vrfdev_attrs.get('table')
-                    if running_table:
-                        ifaceobjrunning.update_config('vrf-table',
-                                                      running_table)
-                        return
-            slave_kind = self.ipcmd.link_get_slave_kind(ifaceobjrunning.name)
-            if slave_kind == 'vrf_slave':
-                vrf = self.ipcmd.link_get_master(ifaceobjrunning.name)
-                if vrf:
-                    ifaceobjrunning.update_config('vrf', vrf)
-        except Exception, e:
-            self.log_warn(str(e))
-
-    def _query(self, ifaceobj, **kwargs):
-        if not self.vrf_helper:
-            return
-        if (ifaceobj.link_kind & ifaceLinkKind.VRF):
-            ifaceobj.update_config('vrf-helper', '%s %s' %(self.vrf_helper,
-                                   ifaceobj.name))
-
-    _run_ops = {'pre-up' : _up,
-               'post-down' : _down,
-               'query-running' : _query_running,
-               'query-checkcurr' : _query_check,
-               'query' : _query}
-
-    def get_ops(self):
-        """ returns list of ops supported by this module """
-        return self._run_ops.keys()
-
-    def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = iproute2()
-        if not self.bondcmd:
-            self.bondcmd = bondutil()
-        if not self.dhclientcmd:
-            self.dhclientcmd = dhclient()
-
-    def run(self, ifaceobj, operation, query_ifaceobj=None,
-            ifaceobj_getfunc=None, **extra_args):
-        """ run bond configuration on the interface object passed as argument
-
-        Args:
-            **ifaceobj** (object): iface object
-
-            **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
-                'query-running'
-
-        Kwargs:
-            **query_ifaceobj** (object): query check ifaceobject. This is only
-                valid when op is 'query-checkcurr'. It is an object same as
-                ifaceobj, but contains running attribute values and its config
-                status. The modules can use it to return queried running state
-                of interfaces. status is success if the running state is same
-                as user required state in ifaceobj. error otherwise.
-        """
-        op_handler = self._run_ops.get(operation)
-        if not op_handler:
-            return
-        self._init_command_handlers()
-        if operation == 'query-checkcurr':
-            op_handler(self, ifaceobj, query_ifaceobj)
-        else:
-            op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)
diff --git a/addons/vrrpd.py b/addons/vrrpd.py
deleted file mode 100644 (file)
index 6d1ee7d..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-
-try:
-    from ipaddr import IPNetwork
-    from sets import Set
-    from ifupdown.iface import *
-    from ifupdown.utils import utils
-    from ifupdownaddons.modulebase import moduleBase
-    from ifupdownaddons.iproute2 import iproute2
-    import ifupdown.ifupdownflags as ifupdownflags
-    import os
-    import glob
-    import logging
-    import signal
-    import re
-except ImportError, e:
-    raise ImportError (str(e) + "- required module not found")
-
-class vrrpd(moduleBase):
-    """  ifupdown2 addon module to configure vrrpd attributes """
-
-    _modinfo = {'mhelp' : 'ethtool configuration module for interfaces',
-                'attrs': {
-                      'vrrp-id' :
-                            {'help' : 'vrrp instance id',
-                             'validrange' : ['1', '4096'],
-                             'example' : ['vrrp-id 1']},
-                      'vrrp-priority' :
-                            {'help': 'set vrrp priority',
-                             'validrange' : ['0', '255'],
-                             'example' : ['vrrp-priority 20']},
-                      'vrrp-virtual-ip' :
-                            {'help': 'set vrrp virtual ip',
-                             'validvals' : ['<ipv4>', ],
-                             'example' : ['vrrp-virtual-ip 10.0.1.254']}}}
-
-    def __init__(self, *args, **kargs):
-        moduleBase.__init__(self, *args, **kargs)
-        self.ipcmd = None
-
-    def _check_if_process_is_running(self, cmdname, cmdline):
-        targetpids = []
-        pidstr = ''
-        try:
-            cmdl = ['/bin/pidof', cmdname]
-            pidstr = utils.exec_commandl(cmdl, stderr=None).strip('\n')
-        except:
-            pass
-        if not pidstr:
-           return []
-
-        pids = pidstr.split()
-        if not pids:
-           return targetpids
-        for pid in pids:
-            tmpcmdline = cmdline.replace(' ', '')
-            try:
-                pcmdline = self.read_file_oneline('/proc/%s/cmdline' %pid)
-                pcmdline = re.sub(r'\\(.)', r'\1', pcmdline)
-                self.logger.info('(%s)' %(pcmdline))
-                self.logger.info('(%s)' %(tmpcmdline))
-                self.logger.info('(%d) (%d)' %(len(pcmdline), len(tmpcmdline)))
-                if pcmdline and pcmdline == tmpcmdline:
-                   targetpids.append(pid)
-            except:
-                pass
-        return targetpids
-            
-    def _up(self, ifaceobj):
-        """ up vrrpd -n -D -i $IFACE -v 1 -p 20 10.0.1.254
-            up ifplugd -i $IFACE -b -f -u0 -d1 -I -p -q """
-
-        if (not ifupdownflags.flags.DRYRUN and
-            not os.path.exists('/sys/class/net/%s' %ifaceobj.name)):
-            return
-
-        cmd = ''
-        attrval = ifaceobj.get_attr_value_first('vrrp-id')
-        if attrval:
-            cmd += ' -v %s' %attrval
-        else:
-            return
-        attrval = ifaceobj.get_attr_value_first('vrrp-priority')
-        if attrval:
-            cmd += ' -p %s' %attrval
-        else:
-            self.logger.warn('%s: incomplete vrrp parameters ' %ifaceobj.name,
-                    '(priority not found)')
-        attrval = ifaceobj.get_attr_value_first('vrrp-virtual-ip')
-        if attrval:
-            cmd += ' %s' %attrval
-        else:
-            self.logger.warn('%s: incomplete vrrp arguments ' %ifaceobj.name,
-                    '(virtual ip not found)')
-            return
-        cmd = '/usr/sbin/vrrpd -n -D -i %s %s' %(ifaceobj.name, cmd)
-        utils.exec_command(cmd)
-
-        cmd = '/usr/sbin/ifplugd -i %s -b -f -u0 -d1 -I -p -q' %ifaceobj.name
-        if self._check_if_process_is_running('/usr/sbin/ifplugd', cmd):
-           self.logger.info('%s: ifplugd already running' %ifaceobj.name)
-           return
-        utils.exec_command(cmd)
-
-    def _kill_pid_from_file(self, pidfilename):
-        if os.path.exists(pidfilename):
-            pid = self.read_file_oneline(pidfilename)
-            if os.path.exists('/proc/%s' %pid):
-               os.kill(int(pid), signal.SIGTERM)
-
-    def _down(self, ifaceobj):
-        """ down ifplugd -k -i $IFACE
-             down kill $(cat /var/run/vrrpd_$IFACE_*.pid) """
-        attrval = ifaceobj.get_attr_value_first('vrrp-id')
-        if not attrval:
-            return
-        try:
-            utils.exec_command('/usr/sbin/ifplugd -k -i %s' % ifaceobj.name)
-        except Exception, e:
-            self.logger.debug('%s: ifplugd down error (%s)'
-                              %(ifaceobj.name, str(e)))
-            pass
-
-        for pidfile in glob.glob('/var/run/vrrpd_%s_*.pid' %ifaceobj.name):
-            try:
-                self._kill_pid_from_file(pidfile)
-            except Exception, e:
-                self.logger.debug('%s: vrrpd down error (%s)'
-                                  %(ifaceobj.name, str(e)))
-                pass
-
-    def _query_check(self, ifaceobj, ifaceobjcurr):
-        # XXX
-        return
-
-
-    _run_ops = {'post-up' : _up,
-                'pre-down' : _down}
-
-    def get_ops(self):
-        """ returns list of ops supported by this module """
-        return self._run_ops.keys()
-
-    def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
-        """ run ethtool configuration on the interface object passed as
-            argument
-
-        Args:
-            **ifaceobj** (object): iface object
-
-            **operation** (str): any of 'post-up', 'query-checkcurr',
-                'query-running'
-        Kwargs:
-            **query_ifaceobj** (object): query check ifaceobject. This is only
-                valid when op is 'query-checkcurr'. It is an object same as
-                ifaceobj, but contains running attribute values and its config
-                status. The modules can use it to return queried running state
-                of interfaces. status is success if the running state is same
-                as user required state in ifaceobj. error otherwise.
-        """
-        op_handler = self._run_ops.get(operation)
-        if not op_handler:
-            return
-        if operation == 'query-checkcurr':
-            op_handler(self, ifaceobj, query_ifaceobj)
-        else:
-            op_handler(self, ifaceobj)
diff --git a/addons/vxlan.py b/addons/vxlan.py
deleted file mode 100644 (file)
index c00fa82..0000000
+++ /dev/null
@@ -1,308 +0,0 @@
-#!/usr/bin/python
-
-from ifupdown.iface import *
-from ifupdown.utils import utils
-from ifupdownaddons.modulebase import moduleBase
-from ifupdownaddons.iproute2 import iproute2
-from ifupdownaddons.systemutils import systemUtils
-from ifupdown.netlink import netlink
-from ipaddr import IPv4Address
-import ifupdown.ifupdownflags as ifupdownflags
-import logging
-import ifupdown.policymanager as policymanager
-import os
-from sets import Set
-
-class vxlan(moduleBase):
-    _modinfo = {'mhelp' : 'vxlan module configures vxlan interfaces.',
-                'attrs' : {
-                        'vxlan-id' :
-                            {'help' : 'vxlan id',
-                             'validrange' : ['1', '16777214'],
-                             'required' : True,
-                             'example': ['vxlan-id 100']},
-                        'vxlan-local-tunnelip' :
-                            {'help' : 'vxlan local tunnel ip',
-                             'validvals' : ['<ipv4>'],
-                             'example': ['vxlan-local-tunnelip 172.16.20.103']},
-                        'vxlan-svcnodeip' :
-                            {'help' : 'vxlan id',
-                             'validvals' : ['<ipv4>'],
-                             'example': ['vxlan-svcnodeip 172.16.22.125']},
-                        'vxlan-remoteip' :
-                            {'help' : 'vxlan remote ip',
-                             'validvals' : ['<ipv4>'],
-                             'example': ['vxlan-remoteip 172.16.22.127']},
-                        'vxlan-physdev' :
-                            {'help' : 'vxlan physical device',
-                             'example': ['vxlan-physdev eth1']},
-                        'vxlan-learning' :
-                            {'help' : 'vxlan learning yes/no',
-                             'validvals' : ['yes', 'no', 'on', 'off'],
-                             'example': ['vxlan-learning no'],
-                             'default': 'yes'},
-                        'vxlan-ageing' :
-                            {'help' : 'vxlan aging timer',
-                             'validrange' : ['0', '4096'],
-                             'example': ['vxlan-ageing 300'],
-                             'default': '300'},
-                        'vxlan-purge-remotes' :
-                            {'help' : 'vxlan purge existing remote entries',
-                             'validvals' : ['yes', 'no'],
-                             'example': ['vxlan-purge-remotes yes']}
-                }}
-    _clagd_vxlan_anycast_ip = ""
-
-    def __init__(self, *args, **kargs):
-        moduleBase.__init__(self, *args, **kargs)
-        self.ipcmd = None
-        purge_remotes = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vxlan-purge-remotes')
-        if purge_remotes:
-            self._purge_remotes = utils.get_boolean_from_string(purge_remotes)
-        else:
-            self._purge_remotes = False
-
-    def get_dependent_ifacenames(self, ifaceobj, ifaceobjs_all=None):
-        if self._is_vxlan_device(ifaceobj):
-            ifaceobj.link_kind |= ifaceLinkKind.VXLAN
-        elif ifaceobj.name == 'lo':
-            clagd_vxlan_list = ifaceobj.get_attr_value('clagd-vxlan-anycast-ip')
-            if clagd_vxlan_list:
-                if len(clagd_vxlan_list) != 1:
-                    self.log_warn('%s: multiple clagd-vxlan-anycast-ip lines, using first one'
-                                  % (ifaceobj.name,))
-                vxlan._clagd_vxlan_anycast_ip = clagd_vxlan_list[0]
-
-
-        # If we should use a specific underlay device for the VXLAN
-        # tunnel make sure this device is set up before the VXLAN iface.
-        physdev = ifaceobj.get_attr_value_first('vxlan-physdev')
-        if physdev:
-            return [ physdev ]
-
-        return None
-
-    def _is_vxlan_device(self, ifaceobj):
-        if ifaceobj.get_attr_value_first('vxlan-id'):
-            return True
-        return False
-
-    def _get_purge_remotes(self, ifaceobj):
-        if not ifaceobj:
-            return self._purge_remotes
-        purge_remotes = ifaceobj.get_attr_value_first('vxlan-purge-remotes')
-        if purge_remotes:
-            purge_remotes = utils.get_boolean_from_string(purge_remotes)
-        else:
-            purge_remotes = self._purge_remotes
-        return purge_remotes
-
-    def _vxlan_create(self, ifaceobj):
-        vxlanid = ifaceobj.get_attr_value_first('vxlan-id')
-        if vxlanid:
-            anycastip = self._clagd_vxlan_anycast_ip
-            group = ifaceobj.get_attr_value_first('vxlan-svcnodeip')
-            local = ifaceobj.get_attr_value_first('vxlan-local-tunnelip')
-            physdev = ifaceobj.get_attr_value_first('vxlan-physdev')
-            ageing = ifaceobj.get_attr_value_first('vxlan-ageing')
-            learning = utils.get_onoff_bool(ifaceobj.get_attr_value_first('vxlan-learning'))
-            purge_remotes = self._get_purge_remotes(ifaceobj)
-            
-            if self.ipcmd.link_exists(ifaceobj.name):
-                vxlanattrs = self.ipcmd.get_vxlandev_attrs(ifaceobj.name)
-                # on ifreload do not overwrite anycast_ip to individual ip
-                # if clagd has modified
-                if vxlanattrs:
-                    running_localtunnelip = vxlanattrs.get('local')
-                    if (anycastip and running_localtunnelip and
-                                anycastip == running_localtunnelip):
-                        local = running_localtunnelip
-
-            netlink.link_add_vxlan(ifaceobj.name, vxlanid,
-                                   local=local,
-                                   learning=learning,
-                                   ageing=ageing,
-                                   group=group,
-                                   physdev=physdev)
-
-            remoteips = ifaceobj.get_attr_value('vxlan-remoteip')
-            if purge_remotes or remoteips:
-                # figure out the diff for remotes and do the bridge fdb updates
-                # only if provisioned by user and not by an vxlan external
-                # controller.
-                peers = self.ipcmd.get_vxlan_peers(ifaceobj.name, group)
-                if local and remoteips and local in remoteips:
-                    remoteips.remove(local)
-                cur_peers = set(peers)
-                if remoteips:
-                    new_peers = set(remoteips)
-                    del_list = cur_peers.difference(new_peers)
-                    add_list = new_peers.difference(cur_peers)
-                else:
-                    del_list = cur_peers
-                    add_list = []
-
-                for addr in del_list:
-                    try:
-                        self.ipcmd.bridge_fdb_del(ifaceobj.name,
-                                                  '00:00:00:00:00:00',
-                                                  None, True, addr)
-                    except:
-                        pass
-
-                for addr in add_list:
-                    try:
-                        self.ipcmd.bridge_fdb_append(ifaceobj.name,
-                                                     '00:00:00:00:00:00',
-                                                     None, True, addr)
-                    except:
-                        pass
-
-    def _up(self, ifaceobj):
-        self._vxlan_create(ifaceobj)
-
-    def _down(self, ifaceobj):
-        try:
-            self.ipcmd.link_delete(ifaceobj.name)
-        except Exception, e:
-            self.log_warn(str(e))
-
-    def _query_check_n_update(self, ifaceobj, ifaceobjcurr, attrname, attrval,
-                              running_attrval):
-        if not ifaceobj.get_attr_value_first(attrname):
-            return
-        if running_attrval and attrval == running_attrval:
-           ifaceobjcurr.update_config_with_status(attrname, attrval, 0)
-        else:
-           ifaceobjcurr.update_config_with_status(attrname, running_attrval, 1)
-
-    def _query_check_n_update_addresses(self, ifaceobjcurr, attrname,
-                                        addresses, running_addresses):
-        if addresses:
-            for a in addresses: 
-                if a in running_addresses:
-                    ifaceobjcurr.update_config_with_status(attrname, a, 0)
-                else:
-                    ifaceobjcurr.update_config_with_status(attrname, a, 1)
-            running_addresses = Set(running_addresses).difference(
-                                                    Set(addresses))
-        [ifaceobjcurr.update_config_with_status(attrname, a, 1)
-                    for a in running_addresses]
-
-    def _query_check(self, ifaceobj, ifaceobjcurr):
-        if not self.ipcmd.link_exists(ifaceobj.name):
-           return
-        # Update vxlan object
-        vxlanattrs = self.ipcmd.get_vxlandev_attrs(ifaceobj.name)
-        if not vxlanattrs:
-            ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj,
-                    self.get_mod_attrs(), -1)
-            return
-        self._query_check_n_update(ifaceobj, ifaceobjcurr, 'vxlan-id',
-                       ifaceobj.get_attr_value_first('vxlan-id'), 
-                       vxlanattrs.get('vxlanid'))
-
-        running_attrval = vxlanattrs.get('local')
-        attrval = ifaceobj.get_attr_value_first('vxlan-local-tunnelip')
-        if running_attrval == self._clagd_vxlan_anycast_ip:
-            # if local ip is anycast_ip, then let query_check to go through
-            attrval = self._clagd_vxlan_anycast_ip
-        self._query_check_n_update(ifaceobj, ifaceobjcurr, 'vxlan-local-tunnelip',
-                                   attrval, running_attrval)
-
-        self._query_check_n_update(ifaceobj, ifaceobjcurr, 'vxlan-svcnodeip',
-                       ifaceobj.get_attr_value_first('vxlan-svcnodeip'),
-                       vxlanattrs.get('svcnode'))
-
-        purge_remotes = self._get_purge_remotes(ifaceobj)
-        if purge_remotes or ifaceobj.get_attr_value('vxlan-remoteip'):
-            # If purge remotes or if vxlan-remoteip's are set
-            # in the config file, we are owners of the installed
-            # remote-ip's, lets check and report any remote ips we don't
-            # understand
-            self._query_check_n_update_addresses(ifaceobjcurr, 'vxlan-remoteip',
-                           ifaceobj.get_attr_value('vxlan-remoteip'),
-                           vxlanattrs.get('remote', []))
-
-        learning = ifaceobj.get_attr_value_first('vxlan-learning')
-        if not learning:
-            learning = 'on'
-
-        running_learning = vxlanattrs.get('learning')
-        if learning == 'yes' and running_learning == 'on':
-            running_learning = 'yes'
-        elif learning == 'no' and running_learning == 'off':
-            running_learning = 'no'
-
-        if learning == running_learning:
-           ifaceobjcurr.update_config_with_status('vxlan-learning',
-                                                  running_learning, 0)
-        else:
-           ifaceobjcurr.update_config_with_status('vxlan-learning',
-                                                  running_learning, 1)
-        ageing = ifaceobj.get_attr_value_first('vxlan-ageing')
-        if not ageing:
-            ageing = self.get_mod_subattr('vxlan-ageing', 'default')
-        self._query_check_n_update(ifaceobj, ifaceobjcurr, 'vxlan-ageing',
-                       ageing, vxlanattrs.get('ageing'))
-
-        physdev = ifaceobj.get_attr_value_first('vxlan-physdev')
-        if physdev:
-            self._query_check_n_update(ifaceobj, ifaceobjcurr, 'vxlan-physdev',
-                           physdev, vxlanattrs.get('physdev'))
-
-    def _query_running(self, ifaceobjrunning):
-        vxlanattrs = self.ipcmd.get_vxlandev_attrs(ifaceobjrunning.name)
-        if not vxlanattrs:
-            return
-        attrval = vxlanattrs.get('vxlanid')
-        if attrval:
-            ifaceobjrunning.update_config('vxlan-id', vxlanattrs.get('vxlanid'))
-        else:
-            # if there is no vxlan id, this is not a vxlan port
-            return
-        attrval = vxlanattrs.get('local')
-        if attrval:
-            ifaceobjrunning.update_config('vxlan-local-tunnelip', attrval)
-        attrval = vxlanattrs.get('svcnode')
-        if attrval:
-            ifaceobjrunning.update_config('vxlan-svcnode', attrval)
-        purge_remotes = self._get_purge_remotes(None)
-        if purge_remotes:
-            # if purge_remotes is on, it means we own the
-            # remote ips. Query them and add it to the running config
-            attrval = vxlanattrs.get('remote')
-            if attrval:
-                [ifaceobjrunning.update_config('vxlan-remoteip', a)
-                            for a in attrval]
-        attrval = vxlanattrs.get('learning')
-        if attrval and attrval == 'on':
-            ifaceobjrunning.update_config('vxlan-learning', 'on')
-        attrval = vxlanattrs.get('ageing')
-        if attrval:
-            ifaceobjrunning.update_config('vxlan-ageing', vxlanattrs.get('ageing'))
-
-    _run_ops = {'pre-up' : _up,
-               'post-down' : _down,
-               'query-checkcurr' : _query_check,
-               'query-running' : _query_running}
-
-    def get_ops(self):
-        return self._run_ops.keys()
-
-    def _init_command_handlers(self):
-        if not self.ipcmd:
-            self.ipcmd = iproute2()
-
-    def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
-        op_handler = self._run_ops.get(operation)
-        if not op_handler:
-            return
-        if (operation != 'query-running' and
-                not self._is_vxlan_device(ifaceobj)):
-            return
-        self._init_command_handlers()
-        if operation == 'query-checkcurr':
-            op_handler(self, ifaceobj, query_ifaceobj)
-        else:
-            op_handler(self, ifaceobj)
diff --git a/build.sh b/build.sh
deleted file mode 100755 (executable)
index 9fd4f2b..0000000
--- a/build.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-
-TOPDIR=.
-
-${TOPDIR}/scripts/genmanpages.sh ${TOPDIR}/man.rst ${TOPDIR}/man
-
-python setup.py --command-packages=stdeb.command sdist_dsc bdist_deb
-
diff --git a/completion/ifup b/completion/ifup
deleted file mode 100644 (file)
index 0be547f..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-
-_python_argcomplete() {
-    local IFS='\v'
-    COMPREPLY=( $(IFS="$IFS" COMP_LINE="$COMP_LINE" COMP_POINT="$COMP_POINT"                   _ARGCOMPLETE_COMP_WORDBREAKS="$COMP_WORDBREAKS"                   _ARGCOMPLETE=1  "$1" 8>&1 9>&2 1>/dev/null 2>/dev/null) )
-    if [[ $? != 0 ]]; then
-        unset COMPREPLY
-    fi
-}
-complete -F _python_argcomplete ifup ifdown ifquery ifreload
diff --git a/config/addons.conf b/config/addons.conf
deleted file mode 100644 (file)
index 2af9b73..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-pre-up,link
-pre-up,tunnel
-pre-up,ppp
-pre-up,bond
-pre-up,batman_adv
-pre-up,vlan
-pre-up,vxlan
-pre-up,clagd
-pre-up,usercmds
-pre-up,bridge
-pre-up,bridgevlan
-pre-up,mstpctl
-pre-up,vrf
-up,dhcp
-up,address
-up,addressvirtual
-up,usercmds
-post-up,ethtool
-post-up,usercmds
-post-up,clagd
-post-up,vxrd
-pre-down,usercmds
-pre-down,ethtool
-pre-down,vxrd
-pre-down,dhcp
-down,addressvirtual
-down,address
-down,usercmds
-post-down,vrf
-post-down,clagd
-post-down,mstpctl
-post-down,bridgevlan
-post-down,bridge
-post-down,vxlan
-post-down,vlan
-post-down,bond
-post-down,batman_adv
-post-down,usercmds
-post-down,link
-post-down,tunnel
-post-down,ppp
diff --git a/config/ifupdown2.conf b/config/ifupdown2.conf
deleted file mode 100644 (file)
index 88f1bc9..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-#
-# ifupdown2 configuration file
-#
-# This file contains default settings for ifupdown
-#
-
-# enable templates
-template_enable=1
-
-# default template engine (only mako is currently supported)
-template_engine=mako
-
-# default template lookup path during template rendering
-template_lookuppath=/etc/network/ifupdown2/templates
-
-# default network configuration filepath
-default_interfaces_configfile=/etc/network/interfaces
-
-# The -i interfacefile option is allowed by default but
-# can be disabled by setting the below option to 1 to
-# reduce security issues (due to the pre- and post- commands)
-disable_cli_interfacesfile=0
-
-
-# enable addon module syntax check:
-# Python addon modules register dictionary of supported attributes.
-# The syntax checker in ifupdown2 uses this dictionary for syntax
-# checks in the interfaces file. This works well, when only python modules
-# are used. But when a mix of scripts and modules are used (which is the
-# default case), you may get false warnings for attributes supported
-# by scripts
-addon_syntax_check=0
-
-# Support executing of ifupdown style scripts.
-# Note that by default python addon modules override scripts with the same
-# name
-addon_scripts_support=0
-
-# enable python addons
-addon_python_modules_support=1
-
-# By default ifupdown2 only supports a single vlan filtering bridge
-# on the system. Set this flag to 1 to support multiple vlan
-# filtering bridges
-multiple_vlan_aware_bridge_support=0
-
-# ifquery check status strings.
-# By default `ifquery --check` prints the check and
-# cross marks against interface attributes.
-# Use the below strings to modify the default behaviour.
-#
-ifquery_check_success_str=pass
-ifquery_check_error_str=fail
-ifquery_check_unknown_str=
-#
-
-# This attribute controls iface/vlan range expansions
-# in ifquery default output.
-ifquery_ifacename_expand_range=0
-
-# Let link master (bridges, bonds) own the link state of slaves
-link_master_slave=1
-
-# Delay admin state change till the end
-delay_admin_state_change=0
-
-# ifreload by default downs: 'all interfaces for which config changed' +
-# 'interfaces that were deleted'. With the below variable set to '0'
-# ifreload will only down 'interfaces that were deleted'
-ifreload_down_changed=0
-
-# squash all addr config when you process the first interface
-addr_config_squash=0
-
-# squash iface config into one when you have multiple
-# ifaces stanzas for an interface
-ifaceobj_squash=0
-
-# By default ifupdown2 will adjust logical devices MTU
-# based on the physical interface they are running on top of.
-# set this flag to 0 to disable this behaviour
-adjust_logical_dev_mtu=1
diff --git a/config/networking b/config/networking
deleted file mode 100644 (file)
index cc3d3ef..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#
-#
-# Parameters for the /etc/init.d/networking script
-#
-#
-
-# Change the below to yes if you want verbose logging to be enabled
-VERBOSE="no"
-
-# Change the below to yes if you want debug logging to be enabled
-DEBUG="no"
-
-# Change the below to yes if you want logging to go to syslog
-SYSLOG="no"
-
-# Exclude interfaces
-EXCLUDE_INTERFACES=
-
-# Set to 'yes' if you want to skip ifdown during system reboot
-# and shutdown. This is of interest in large scale interface
-# deployments where you dont want to wait for interface
-# deconfiguration to speed up shutdown/reboot
-SKIP_DOWN_AT_SYSRESET="yes"
index e0ae2f85dbea8453425c3329141f2d1556ad73f3..772b13c10af4b2b1b50ffe841ee0b13690684d42 100644 (file)
-ifupdown2 (1.1-cl3u10) UNRELEASED; urgency=medium
+ifupdown2 (2.0.0) UNRELEASED; urgency=medium
+
+  * Package architecture refactoring and cleanups
+  * Package can be build/install as debian, pip or rpm package
+  * Makefile to easily perform tasks (i.e.: install, build, test, upload..)
+  * New. Enabled: traditional bridge support for mstpctl attributes
+    (portautoedge, portrestrrole)
+
+ -- Julien Fortin <julien@cumulusnetworks.com>  Thu, 24 May 2018 15:42:42 +0200
+
+ifupdown2 (1.1.18) RELEASED; urgency=medium
+
+  * Fix: Link down does not work on SVI configured in a VRF
+  * Fix: ifreload causes MTU to drop on bridge SVIs
+  * Fix: addons: addressvirtual: check if SVI name is first in routing table
+  * Fix: ifreload error on deleting bond slaves from an already configured bond
+  * Fix: ifupdown2 error is confusing when netmask is specified for vxlan-local-tunnelip
+  * Fix: ifupdown2 syntax check needed for vxlan interfaces
+  * Fix: vxlan-ageing default timer doesn't align with bridge-ageing
+  * Fix: Error with "ifreload -a -n" when MGMT VRF is not Applied
+  * Fix: using reserved VLAN range reports error but ifreload returns 0
+  * Fix: unable to set bridge-portmcrouter to "2"
+  * Fix: vxlan syntax-check warn on missing vxlan-local-tunnelip
+  * Fix: traditional bridge svi breaks when extra bridge added
+  * Fix: github #39: addons: vrf: fix vrf slave link kind
+  * New. Enabled: addons: vxlan: add support for vxlan-port attribute
+
+ -- Julien Fortin <julien@cumulusnetworks.com>  Thu, 12 Apr 2018 11:10:04 +0200
+
+ifupdown2 (1.1.17) RELEASED; urgency=medium
+
+  * Fix: ip[6]-forward attributes not set at boot
+
+ -- Julien Fortin <julien@cumulusnetworks.com>  Thu, 08 Feb 2018 09:48:37 +0100
+
+ifupdown2 (1.1.16) RELEASED; urgency=medium
+
+  * Fix: python exception on macvlans address dump
+  * Fix: eth0 doesn't acquire DHCP address when mgmt VRF is enabled
+
+ -- Julien Fortin <julien@cumulusnetworks.com>  Tue, 09 Jan 2018 02:02:58 +0100
+
+ifupdown2 (1.1.15) RELEASED; urgency=medium
+
+  * New. Enabled: bridge: add support for bridge-l2protocol-tunnel
+  * New. Enabled: bridge attributes, when removed reset to default
+  * New. Enabled: vxlan attributes, when removed reset to default
+  * New. Enabled: improve handling of optional resources (if missing bridge-utils/ethtool)
+  * Fix: policy "iface_defaults" not supported for MTU
+  * Fix: address module: handling of ipv4 & ipv6 (add/remove)
+  * Fix: warning for vlan reserved range
+  * Fix: MTU handling on bridge SVIs
+
+ -- Julien Fortin <julien@cumulusnetworks.com>  Wed, 22 Nov 2017 19:07:43 +0100
+
+ifupdown2 (1.1.14) RELEASED; urgency=medium
+
+  * New. Enabled: default policy for bridge MAC address
+  * Fix: ethtool: don't set link speed and duplex if autoneg is on
+
+ -- Julien Fortin <julien@cumulusnetworks.com>  Wed, 25 Oct 2017 23:12:27 +0200
+
+ifupdown2 (1.1.13) RELEASED; urgency=medium
+
+  * Fix: VRF: ssh session not killed on ifreload
+
+ -- Julien Fortin <julien@cumulusnetworks.com>  Fri, 15 Sep 2017 22:43:12 +0200
+
+ifupdown2 (1.1.12) RELEASED; urgency=medium
+
+  * New. Enabled: mpls-enable attribute
+  * New. Enabled: bond and bridge module moved to netlink configuration
+  * New. Enabled: handle a mix of auto and specified vrf table ids
+  * Fix: igmp attribute reset to defaults when removed from config
+
+ -- Julien Fortin <julien@cumulusnetworks.com>  Mon, 07 Aug 2017 22:14:03 +0200
+
+ifupdown2 (1.1.11) RELEASED; urgency=medium
+
+  * Fix: link-down attribute not applied on slave ports
+  * Fix: bug that prevented config of mtu below 1280
+
+ -- Julien Fortin <julien@cumulusnetworks.com>  Thu, 18 May 2017 12:45:21 -0700
+
+ifupdown2 (1.1.10) RELEASED; urgency=medium
 
   * New. Enabled: drop the dependency to pkg_resources, hardcode version number (closes: #855401)
   * New. Enabled: adjust allow-hotplug behavior to ifupdown (closes: #855598)
-  * Closes: CM-8424. bond: adding attribute bond-(up|down)delay
+  * New. Enabled: bond-lacp-rate support slow/fast argument
+  * New. Enabled: ifquery --syntax-help: add support for json output
+  * New. Enabled: vlan: add new attribute 'vlan-protocol'
+  * New. Enabled: address: add new attribute 'ip6-forward'
+  * New. Enabled: bridge: add new attribute 'bridge-mcstats'
+  * New. Enabled: bridge: add new attribute 'bridge-vlan-stats'
+  * New. Enabled: bridge: add new attribute 'bridge-vlan-protocol'
+  * New. Enabled: bridge: add new attribute 'bridge-arp-nd-suppress'
+  * Fix: bond: add attribute bond-(up|down)delay
+  * Fix: bridge-vids: --syntax-check accepts legacy syntax
 
- -- Julien Fortin <julien@cumulusnetworks.com>  Thu, 23 Feb 2017 10:50:14 +0100
+ -- Julien Fortin <julien@cumulusnetworks.com>  Mon, 17 Apr 2017 06:18:04 +0200
 
-ifupdown2 (1.1-cl3u9) RELEASED; urgency=medium
+ifupdown2 (1.1.9) RELEASED; urgency=medium
 
   * New. Enabled: support for bridge-learning attribute
 
- -- Cumulus Networks <dev-support@cumulusnetworks.com>  Mon, 06 Feb 2017 13:22:51 -0800
+ -- Julien Fortin <julien@cumulusnetworks.com>  Mon, 06 Feb 2017 13:22:51 -0800
 
-ifupdown2 (1.1-cl3u8) RELEASED; urgency=medium
+ifupdown2 (1.1.8) RELEASED; urgency=medium
 
   * New. Enabled: update link-speed values: add 25G and 50G
   * New. Enabled: new 'link-down' [yes|no] attribute to keep link down
 
- -- dev-support <dev-support@cumulusnetworks.com>  Tue, 17 Jan 2017 08:39:29 +0100
+ -- Julien Fortin <julien@cumulusnetworks.com>  Tue, 17 Jan 2017 08:39:29 +0100
 
-ifupdown2 (1.1-cl3u7) RELEASED; urgency=medium
+ifupdown2 (1.1.7) RELEASED; urgency=medium
 
   * New. Enabled: fix for inet and inet6 dhcp on the same interface
   * New. Enabled: syntax check to warn on common configuration mistakes
   * New. Enabled: addons: bridge: disable ip fwding on a bridge with no ip
     and no upperifaces
-  * Closes: CM-13221: fixes for MTU handling
-  * Closes: CM-13248: dhcpv6 fails if interface doesn't have link-local addr
+  * Fix: fixes for MTU handling
+  * Fix: dhcpv6 fails if interface doesn't have link-local addr
 
- -- dev-support <dev-support@cumulusnetworks.com>  Wed, 07 Dec 2016 05:48:45 +0100
+ -- Julien Fortin <julien@cumulusnetworks.com>  Wed, 07 Dec 2016 05:48:45 +0100
 
-ifupdown2 (1.1-cl3u6) RELEASED; urgency=medium
+ifupdown2 (1.1.6) RELEASED; urgency=medium
 
   * Closes: github #14. add environment variables passed to user scripts
   * New. Enabled: addons may provide a list of ifupdown scripts to ignore
 
- -- dev-support <dev-support@cumulusnetworks.com>  Mon, 19 Sep 2016 16:37:36 -0700
+ -- Julien Fortin <julien@cumulusnetworks.com>  Mon, 19 Sep 2016 16:37:36 -0700
 
-ifupdown2 (1.1-cl3u5) RELEASED; urgency=medium
+ifupdown2 (1.1.5) RELEASED; urgency=medium
 
-  * Closes: CM-12798. fix handling of EXISTS errors on address add
-  * Closes: CM-11214. fix handling of mtu on addressvirtual macvlan devices
-  * Closes: CM-12884. fix mako namespace handling
+  * Fix: handling of EXISTS errors on address add
+  * Fix: handling of mtu on addressvirtual macvlan devices
+  * Fix: mako namespace handling
 
- -- dev-support <dev-support@cumulusnetworks.com>  Fri, 16 Sep 2016 12:48:04 -0700
+ -- Julien Fortin <julien@cumulusnetworks.com>  Fri, 16 Sep 2016 12:48:04 -0700
 
-ifupdown2 (1.1-cl3u4) RELEASED; urgency=medium
+ifupdown2 (1.1.4) RELEASED; urgency=medium
 
   * Performance improvements
   * New. Enabled: sbin: start-networking: support hotplug class from init script
@@ -53,24 +146,24 @@ ifupdown2 (1.1-cl3u4) RELEASED; urgency=medium
   * New. Enabled: extend ifquery support for mstpctl addons
   * New. Enabled: each addon may perform semantic and syntax checks by
     implementing a custom method
-  * Closes: CM-11745. Support for address-virtual lines under a vrf slave
-  * Closes: CM-11718. Defaults for link attributes were not applied
-  * Closes: CM-11511. Disable IPv6 duplicate address detection on VRR interfaces
-  * Closes: CM-11485. Fix ifquery to extract vlan-id from iface if not preset
-  * Closes: CM-8623. Fix for ifquery -c bridge pvid error on a valid config
+  * Fix: Support for address-virtual lines under a vrf slave
+  * Fix: Defaults for link attributes were not applied
+  * Fix: Disable IPv6 duplicate address detection on VRR interfaces
+  * Fix: ifquery to extract vlan-id from iface if not preset
+  * Fix: ifquery -c bridge pvid error on a valid config
 
- -- dev-support <dev-support@cumulusnetworks.com>  Fri, 29 Jul 2016 08:55:50 -0700
+ -- Julien Fortin <julien@cumulusnetworks.com>  Fri, 29 Jul 2016 08:55:50 -0700
 
-ifupdown2 (1.1-cl3u3) RELEASED; urgency=medium
+ifupdown2 (1.1.3) RELEASED; urgency=medium
 
-  * Closes: CM-11214. Interface configuration parsing error when keyword vlan
+  * Fix: Interface configuration parsing error when keyword vlan
     is the interface name.
 
- -- dev-support <dev-support@cumulusnetworks.com>  Sun, 05 Jun 2016 08:55:50 -0700
+ -- Julien Fortin <julien@cumulusnetworks.com>  Sun, 05 Jun 2016 08:55:50 -0700
 
-ifupdown2 (1.1-cl3u2) RELEASED; urgency=medium
+ifupdown2 (1.1.2) RELEASED; urgency=medium
 
-  * Closes: CM-10478. checks for invalid address-virtual attributes
+  * Fix: checks for invalid address-virtual attributes
   * New. Deprecated: `mstpctl-stp` attribute
   * New. Deprecated: lacp parameters: bond-ad-sys-priority, bond-ad-sys-mac-addr
   * New. Enabled: addon module for configuring vrf
@@ -83,10 +176,10 @@ ifupdown2 (1.1-cl3u2) RELEASED; urgency=medium
   * New. Enabled: bridge: disabling ipv6 on bridge if any VXLAN port
   * New. Enabled: vrf awareness in dhcp addon module
 
- -- dev-support <dev-support@cumulusnetworks.com>  Tue, 3 May 2016 14:42:42 -0700
+ -- Julien Fortin <julien@cumulusnetworks.com>  Tue, 3 May 2016 14:42:42 -0700
 
-ifupdown2 (1.1-cl3u1) unstable; urgency=low
+ifupdown2 (1.1.0) unstable; urgency=low
 
   * Initial release.
 
- -- dev-support <dev-support@cumulusnetworks.com>  Thu, 20 Aug 2015 06:14:24 -0700
+ -- Roopa Prabhu <roopa@cumulusnetworks.com>  Thu, 20 Aug 2015 06:14:24 -0700
diff --git a/debian/clean b/debian/clean
deleted file mode 100644 (file)
index 0723a0c..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-man/ifquery.8
-man/ifreload.8
-man/ifup.8
-man/ifupdown-addons-interfaces.5
index e0e68b6013be0b1965efd0a18ef649d6b69e6cc1..a6178e460ce4429875c7c9a2e9ea3688f6b871d1 100644 (file)
@@ -2,18 +2,23 @@ Source: ifupdown2
 Section: admin
 Priority: optional
 Maintainer: Julien Fortin <julien@cumulusnetworks.com>
+Build-Depends: debhelper (>=9),
+               dh-systemd,
+               dh-python,
+               python-all,
+               python-setuptools,
+               python-docutils
 Standards-Version: 3.9.8
-Build-Depends: python-setuptools, dh-python, python-all (>= 2.6.6-3), debhelper (>= 9~), python-docutils, dh-systemd
-Homepage: https://github.com/CumulusNetworks/ifupdown2
-X-Python-Version: >= 2.6
+Homepage: https://github.com/cumulusnetworks/ifupdown2
+X-Python-Version: >= 2.7
 
 Package: ifupdown2
 Architecture: all
-Suggests: python-gvgen, python-mako
-Replaces: ifupdown
-Conflicts: ifupdown
 Provides: ifupdown
-Depends: ${python:Depends}, ${misc:Depends}, python-argcomplete, python-ipaddr, iproute2, isc-dhcp-client
+Conflicts: ifupdown
+Replaces: ifupdown
+Depends: ${python:Depends}, ${misc:Depends}, iproute2, python-argcomplete, python-ipaddr
+Suggests: isc-dhcp-client, bridge-utils, ethtool, python-gvgen, python-mako
 Description: Network Interface Management tool similar to ifupdown
  ifupdown2 is ifupdown re-written in Python. It replaces ifupdown and provides
  the same user interface as ifupdown for network interface configuration.
index f52800ab2560809746d1e7a7504d16a835a83bfd..51d56044a6ee62f2bc3e15ef947041b0a71b8713 100644 (file)
@@ -1,14 +1,14 @@
 Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
 Upstream-Name: ifupdown2
-Upstream-Contact: Roopa Prabhu <roopa@cumulusnetworks.com>, Julien Fortin <julien@cumulusnetworks.com>
-Source: http://www.cumulusnetworks.com
+Upstream-Contact: Julien Fortin <julien@cumulusnetworks.com>
+Source: https://github.com/cumulusnetworks/ifupdown2
 
 Files: *
-Copyright: 2014 Cumulus Networks
+Copyright: 2014,2015,2016,2017,2018 Cumulus Networks, Inc.
 License: GPL-2
 
 Files: debian/*
-Copyright: 2014 Cumulus Networks
+Copyright: 2014,2015,2016,2017,2018 Cumulus Networks, Inc.
 License: GPL-2
 
 License: GPL-2
@@ -25,4 +25,4 @@ License: GPL-2
  along with this program. If not, see <http://www.gnu.org/licenses/>
  .
  On Debian systems, the complete text of the GNU General
- Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
\ No newline at end of file
+ Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
index cf6dbfbdb2873b3d44a4eb2e668c780c98dd8fe8..a136a3e989cffab9d4597b57844a3b8d37391058 100644 (file)
@@ -1 +1 @@
-completion/ifup
+ifupdown2/completion/ifup
index 675c5f6e4cd5821c51d65b1d5a05bf27519f51cf..edc63b038e25558360a9e8d6d90fda41f9d2173d 100644 (file)
@@ -1,3 +1,4 @@
-/etc/network/interfaces.d/
-/var/lib/ifupdown2/policy.d/
-/var/lib/ifupdown2/hooks/
+etc/network/interfaces.d/
+etc/network/ifupdown2/policy.d/
+var/lib/ifupdown2/hooks/
+var/lib/ifupdown2/policy.d/
index 446b2acf8f0cc4bd5f9730c4bf5a650b4b0542e9..7444031d3f511623654bf58112e13ee7e847c181 100644 (file)
@@ -1,3 +1,3 @@
-sbin/ifupdown2 /usr/share/ifupdown2/
-sbin/start-networking /usr/share/ifupdown2/sbin/
-debian/networking /etc/default/
+etc/default/networking /etc/default/
+etc/network/ifupdown2/addons.conf /etc/network/ifupdown2/
+etc/network/ifupdown2/ifupdown2.conf /etc/network/ifupdown2/
index 177c9d5ae9fc8d0c66292f8036e91ab6bedf4915..246d559950fb71dadaf0e9de0b3378a4e97ac387 100644 (file)
@@ -1,6 +1,5 @@
+usr/share/ifupdown2/__main__.py usr/share/ifupdown2/ifupdown2
 usr/share/ifupdown2/ifupdown2 sbin/ifup
 usr/share/ifupdown2/ifupdown2 sbin/ifdown
 usr/share/ifupdown2/ifupdown2 sbin/ifquery
 usr/share/ifupdown2/ifupdown2 sbin/ifreload
-usr/share/man/man8/ifup.8.gz usr/share/man/man8/ifdown.8.gz
-
index b7d0faba63a430317194e7c46c2a8ed396f231c3..743173064ca3c786977f6f846f94721e14933dc0 100644 (file)
@@ -1,4 +1,5 @@
 man/ifup.8
+man/ifdown.8
 man/ifquery.8
 man/ifreload.8
 man/ifupdown-addons-interfaces.5
index ecf5b918f0ea387b817a466298e6f3747e42cb5e..06e1545b15e794b86bca4b628b0330b4fd0e6a9c 100644 (file)
@@ -1,6 +1,9 @@
 [Unit]
 Description=ifupdown2 networking initialization
 Documentation=man:interfaces(5) man:ifup(8) man:ifdown(8)
+DefaultDependencies=no
+Before=shutdown.target
+Conflicts=shutdown.target
 
 [Service]
 Type=oneshot
@@ -12,4 +15,4 @@ ExecStop=/usr/share/ifupdown2/sbin/start-networking stop
 ExecReload=/usr/share/ifupdown2/sbin/start-networking reload
 
 [Install]
-WantedBy=basic.target network.target
+WantedBy=basic.target network.target shutdown.target
index 19365db9b24d06587e73deec2365332c6d74159b..09480356b8db884f9d0c40baf702468229229415 100644 (file)
@@ -1,21 +1,49 @@
 #!/bin/sh
-# postinst script for ifupdown2
-#
-# see: dh_installdeb(1)
-
 set -e
 
-# summary of how this script can be called:
-#        * <postinst> `configure' <most-recently-configured-version>
-#        * <old-postinst> `abort-upgrade' <new version>
-#        * <conflictor's-postinst> `abort-remove' `in-favour' <package>
-#          <new-version>
-#        * <postinst> `abort-remove'
-#        * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
-#          <failed-install-package> <version> `removing'
-#          <conflicting-package> <version>
-# for details, see http://www.debian.org/doc/debian-policy/ or
-# the debian-policy package
+MYNAME="${0##*/}"
+
+report() { echo "${MYNAME}: $*" ; }
+report_warn() { report "Warning: $*" >&2 ; }
+report_err() { report "Error: $*" >&2 ; }
+
+fix_dhclient_file_with_space()
+{
+    # because of a typo an older ifupdown2 version was creating lease file
+    # with trailing space. In case we still have users with such files we
+    # need to strip that trailing whitespace.
+    for filename in `find /var/lib/dhcp/ -name "dhclient.*.leases "`
+    do
+        if [ -f "$filename " ];
+        then
+            interface_name=`echo $filename | cut -d'.' -f2,3,4,5`
+            mv "$filename " /var/lib/dhcp/dhclient6.$interface_name
+        fi
+    done
+}
+
+process_etc_network_interfaces()
+{
+    # Generic stuff done on all configurations
+    if [ -f /etc/network/interfaces ] ; then
+        if ! grep -q -E "^[[:space:]]*iface[[:space:]]+l[o0]([[:space:]]+inet([[:space:]]+loopback)?)?[[:space:]]*$" /etc/network/interfaces ; then
+            report_warn "No 'iface lo' definition found in /etc/network/interfaces"
+        fi
+
+        if ! grep -q "^[[:space:]]*\(allow-\|\)auto[[:space:]]\+\(.*[[:space:]]\+\|\)lo0\?\([[:space:]]\+\|$\)" /etc/network/interfaces ; then
+            report_warn "No 'auto lo' statement found in /etc/network/interfaces"
+        fi
+    else  # ! -f /etc/network/interfaces
+        if [ -z "$2" ]; then
+            echo "Creating /etc/network/interfaces."
+            echo "# interfaces(5) file used by ifup(8) and ifdown(8)" > /etc/network/interfaces
+            echo "auto lo" >> /etc/network/interfaces
+            echo "iface lo inet loopback" >> /etc/network/interfaces
+        else
+            report_warn "/etc/network/interfaces does not exist"
+        fi
+    fi
+}
 
 process_udev()
 {
@@ -35,52 +63,24 @@ process_udev()
     fi
 }
 
-MYNAME="${0##*/}"
-
-report() { echo "${MYNAME}: $*" ; }
-report_warn() { report "Warning: $*" >&2 ; }
-report_err() { report "Error: $*" >&2 ; }
-
 case "$1" in
     configure)
-
-        # work around to rename the existing dhclient6 lease file containing a space
-        for filename in `find /var/lib/dhcp/ -name "dhclient.*.leases "`
-        do
-            if [ -f "$filename " ];
-            then
-                interface_name=`echo $filename | cut -d'.' -f2,3,4,5`
-                mv "$filename " /var/lib/dhcp/dhclient6.$interface_name
-            fi
-        done
-
-        # Generic stuff done on all configurations
-        if [ -f /etc/network/interfaces ] ; then
-            if ! grep -q -E "^[[:space:]]*iface[[:space:]]+l[o0]([[:space:]]+inet([[:space:]]+loopback)?)?[[:space:]]*$" /etc/network/interfaces ; then
-                report_warn "No 'iface lo' definition found in /etc/network/interfaces"
-            fi
-
-            if ! grep -q "^[[:space:]]*\(allow-\|\)auto[[:space:]]\+\(.*[[:space:]]\+\|\)lo0\?\([[:space:]]\+\|$\)" /etc/network/interfaces ; then
-                report_warn "No 'auto lo' statement found in /etc/network/interfaces"
-            fi
-        else  # ! -f /etc/network/interfaces
-            if [ -z "$2" ]; then
-                echo "Creating /etc/network/interfaces."
-                echo "# interfaces(5) file used by ifup(8) and ifdown(8)" > /etc/network/interfaces
-                echo "auto lo" >> /etc/network/interfaces
-                    echo "iface lo inet loopback" >> /etc/network/interfaces
-            else
-                    report_warn "/etc/network/interfaces does not exist"
-            fi
-        fi
-
+        fix_dhclient_file_with_space
+        process_etc_network_interfaces
         process_udev
-        ;;
+        chmod +x /usr/share/ifupdown2/__main__.py
+    ;;
+
+    abort-upgrade|abort-remove|abort-deconfigure)
+    ;;
 
-  purge)
+    *)
+        echo "postinst called with unknown argument \`$1'" >&2
+        exit 1
     ;;
 esac
 
+
 #DEBHELPER#
 
 exit 0
index b66b07e074ef5a7de354f988c2c51fbf1dc22644..5d90b21d6959c546d5db90c5bc50285e769a6495 100644 (file)
@@ -1,91 +1,45 @@
 #!/bin/sh
-# postrm script for ifupdown2
-#
-# see: dh_installdeb(1)
-
 set -e
 
-# summary of how this script can be called:
-#        * <postrm> `remove'
-#        * <postrm> `purge'
-#        * <old-postrm> `upgrade' <new-version>
-#        * <new-postrm> `failed-upgrade' <old-version>
-#        * <new-postrm> `abort-install'
-#        * <new-postrm> `abort-install' <old-version>
-#        * <new-postrm> `abort-upgrade' <old-version>
-#        * <disappearer's-postrm> `disappear' <overwriter>
-#          <overwriter-version>
-# for details, see http://www.debian.org/doc/debian-policy/ or
-# the debian-policy package
-
-process_udev()
-{
-    if [ -e /etc/udev/rules.d/80-networking.rules ]; then
-        udevlink=$(readlink /etc/udev/rules.d/80-networking.rules 2>/dev/null || true)
-        [ -n "$udevlink" -a "$udevlink" = "/dev/null" ] && rm -f /etc/udev/rules.d/80-networking.rules
-    fi
+MYNAME="${0##*/}"
 
-    if  [ -e /etc/udev/rules.d/60-bridge-network-interface.rules ]; then
-        udevlink=$(readlink /etc/udev/rules.d/60-bridge-network-interface.rules 2>/dev/null || true)
-        [ -n "$udevlink" -a "$udevlink" = "/dev/null" ] && rm -f /etc/udev/rules.d/60-bridge-network-interface.rules
-    fi
-}
-
-postrm_remove()
-{
-    process_udev
-}
+report() { echo "${MYNAME}: $*" ; }
+report_warn() { report "Warning: $*" >&2 ; }
+report_err() { report "Error: $*" >&2 ; }
 
-# Note: We don't remove /etc/network/interfaces
-postrm_purge()
+process_state_file()
 {
-       rm -f /run/network/ifstatenew
+    rm -f /run/network/ifstatenew
 }
 
-# restore file if we diverted it on install/upgrade
-_postrm_divert()
+process_udev()
 {
-    diversions=$(dpkg-divert --list | grep "$1" | grep 'by ifupdown2$'  | wc -l 2> /dev/null)
-    if [ $diversions -gt 0 ] ;
-    then
-        dpkg-divert --remove --package ifupdown2 --rename $1
+    if [ -e /etc/udev/rules.d/80-networking.rules ]; then
+       udevlink=$(readlink /etc/udev/rules.d/80-networking.rules 2>/dev/null || true)
+       [ -n "$udevlink" -a "$udevlink" = "/dev/null" ] && rm -f /etc/udev/rules.d/80-networking.rules
     fi
-}
 
-postrm_divert()
-{
-    for filename in ifup ifdown ifquery ifreload
-    do
-        _postrm_divert "/usr/share/bash-completion/completions/$filename"
-        _postrm_divert "/etc/bash_completion.d/$filename"
-    done
+    if  [ -e /etc/udev/rules.d/60-bridge-network-interface.rules ]; then
+       udevlink=$(readlink /etc/udev/rules.d/60-bridge-network-interface.rules 2>/dev/null || true)
+       [ -n "$udevlink" -a "$udevlink" = "/dev/null" ] && rm -f /etc/udev/rules.d/60-bridge-network-interface.rules
+    fi
 }
 
 case "$1" in
-       purge)
-               postrm_purge
-       ;;
-
-       remove)
-               postrm_remove
-               postrm_divert
-       ;;
-
-    abort-install|disappear|failed-upgrade|abort-upgrade)
-        postrm_divert
+    purge|remove|abort-install|disappear)
+        process_state_file
+        process_udev
     ;;
 
-       upgrade)
-       ;;
+    upgrade|failed-upgrade|abort-upgrade|disappear)
+    ;;
 
-       *)
-               echo "postrm called with unknown argument \`$1'" >&2
-               exit 1
-       ;;
+    *)
+        echo "postrm called with unknown argument \`$1'" >&2
+        exit 1
+    ;;
 esac
 
-# dh_installdeb will replace this with shell code automatically
-# generated by other debhelper scripts.
 
 #DEBHELPER#
 
diff --git a/debian/ifupdown2.preinst b/debian/ifupdown2.preinst
deleted file mode 100644 (file)
index 9f41e8e..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-#! /bin/sh
-
-MYNAME="${0##*/}"
-
-report() { echo "${MYNAME}: $*" ; }
-report_err() { report "Error: $*" >&2 ; }
-
-preinst_divert()
-{
-    diversions=$(dpkg-divert --list | grep "$1" | grep -v 'by ifupdown2$'  | wc -l 2> /dev/null)
-    if [ $diversions -gt 0 ] ;
-    then
-        report_err "existing diversion for $1"
-    else
-        if [ -f $1 ];
-        then
-            dpkg-divert --add --package ifupdown2 --rename --divert "$1.disabled" "$1"
-        fi
-    fi
-}
-
-set -e
-case "$1" in
-    install|upgrade)
-        for filename in ifup ifdown ifquery ifreload
-        do
-            preinst_divert "/usr/share/bash-completion/completions/$filename"
-            preinst_divert "/etc/bash_completion.d/$filename"
-        done
-
-        # workaround 3.0.0 internal install error.  This can be removed in a
-        # few weeks.
-        if [ -f /etc/default/networking/networking.default ]; then
-            dpkg-maintscript-helper rm_conffile /etc/default/networking/networking.default 1.1 -- $@
-            rm -rf /etc/default/networking
-        fi
-        ;;
-esac
-#DEBHELPER#
diff --git a/debian/networking b/debian/networking
deleted file mode 100644 (file)
index 5856199..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#
-#
-# Parameters for the networking service
-#
-#
-
-# Change the below to yes if you want verbose logging to be enabled
-VERBOSE="no"
-
-# Change the below to yes if you want debug logging to be enabled
-DEBUG="no"
-
-# Change the below to yes if you want logging to go to syslog
-SYSLOG="no"
-
-# Exclude interfaces
-EXCLUDE_INTERFACES=
-
-# Set to 'yes' if you want to skip ifdown during system reboot
-# and shutdown. This is of interest in large scale interface
-# deployments where you dont want to wait for interface
-# deconfiguration to speed up shutdown/reboot
-SKIP_DOWN_AT_SYSRESET="yes"
diff --git a/debian/python-ifupdown2.postinst b/debian/python-ifupdown2.postinst
deleted file mode 100644 (file)
index cf61919..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-#!/bin/sh
-# postinst script for ifupdown2
-#
-# see: dh_installdeb(1)
-
-set -e
-
-# summary of how this script can be called:
-#        * <postinst> `configure' <most-recently-configured-version>
-#        * <old-postinst> `abort-upgrade' <new version>
-#        * <conflictor's-postinst> `abort-remove' `in-favour' <package>
-#          <new-version>
-#        * <postinst> `abort-remove'
-#        * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
-#          <failed-install-package> <version> `removing'
-#          <conflicting-package> <version>
-# for details, see http://www.debian.org/doc/debian-policy/ or
-# the debian-policy package
-
-process_udev()
-{
-    # override default udev bridge and hotplug rules because they interfere with
-    # networking init script
-    udev_user_rulesdir=/etc/udev/rules.d/
-    udev_sys_rulesdir=/lib/udev/rules.d/
-    if [ -e $udev_user_rulesdir ]; then
-        udev_ifupdown2_overrides="80-networking.rules
-        60-bridge-network-interface.rules"
-        for u in ${udev_ifupdown2_overrides}
-        do
-            if [ -e ${udev_sys_rulesdir}/$u -a ! -e ${udev_user_rulesdir}/$u ]; then
-                (cd ${udev_user_rulesdir} && ln -sf /dev/null $u)
-            fi
-        done
-    fi
-}
-
-MYNAME="${0##*/}"
-
-report() { echo "${MYNAME}: $*" ; }
-report_warn() { report "Warning: $*" >&2 ; }
-report_err() { report "Error: $*" >&2 ; }
-
-case "$1" in
-    configure)
-        # Create /etc/network/run
-        [ -d /run/network ] || mkdir -p /run/network
-
-        # for backward compatibility
-        if [ ! -f /etc/network/run ]; then
-            ln -sf /run/network /etc/network/run
-        fi
-
-        ln -sf /usr/share/python-ifupdown2/generate_interfaces.py \
-            /usr/share/doc/python-ifupdown2/examples/generate_interfaces.py
-
-        [ -d /etc/network/if-pre-up.d ] || mkdir -p /etc/network/if-pre-up.d
-        [ -d /etc/network/if-up.d ] || mkdir -p /etc/network/if-up.d
-        [ -d /etc/network/if-post-up.d ] || mkdir -p /etc/network/if-post-up.d
-
-        [ -d /etc/network/if-pre-down.d ] || mkdir -p /etc/network/if-pre-down.d
-        [ -d /etc/network/if-down.d ] || mkdir -p /etc/network/if-down.d
-        [ -d /etc/network/if-post-down.d ] || mkdir -p /etc/network/if-post-down.d
-
-
-        # Generic stuff done on all configurations
-        if [ -f /etc/network/interfaces ] ; then
-            # TODO: This should be handled with debconf and the script
-            # could introduce the line there directly
-            if ! grep -q "^[[:space:]]*iface[[:space:]]\+lo0\?[[:space:]]\+inet[[:space:]]\+loopback\>" /etc/network/interfaces ; then
-                report_warn "No 'iface lo' definition found in /etc/network/interfaces"
-            fi
-
-            if ! grep -q "^[[:space:]]*\(allow-\|\)auto[[:space:]]\+\(.*[[:space:]]\+\|\)lo0\?\([[:space:]]\+\|$\)" /etc/network/interfaces ; then
-                report_warn "No 'auto lo' statement found in /etc/network/interfaces"
-            fi
-        else  # ! -f /etc/network/interfaces
-            if [ -z "$2" ]; then
-                echo "Creating /etc/network/interfaces."
-                echo "# interfaces(5) file used by ifup(8) and ifdown(8)" > /etc/network/interfaces
-                echo "auto lo" >> /etc/network/interfaces
-                    echo "iface lo inet loopback" >> /etc/network/interfaces
-            else
-                    report_warn "/etc/network/interfaces does not exist"
-            fi
-        fi
-
-        [ -e /sbin/ifup ] || ln -sf /sbin/ifupdown /sbin/ifup
-        [ -e /sbin/ifdown ] || ln -sf /sbin/ifupdown /sbin/ifdown
-        [ -e /sbin/ifquery ] || ln -sf /sbin/ifupdown /sbin/ifquery
-        [ -e /sbin/ifreload ] || ln -sf /sbin/ifupdown /sbin/ifreload
-
-        (cd /usr/share/man/man8/ && ln -sf /usr/share/man/man8/ifup.8.gz ifdown.8.gz)
-
-        mkdir -p /etc/network/interfaces.d/
-        process_udev
-        update-rc.d networking start 40 S . start 35 0 6 . >/dev/null
-        ;;
-
-    abort-upgrade|abort-remove|abort-deconfigure)
-        ;;
-
-    *)
-        echo "postinst called with unknown argument \`$1'" >&2
-        exit 1
-        ;;
-esac
-
-# dh_installdeb will replace this with shell code automatically
-# generated by other debhelper scripts.
-
-#DEBHELPER#
-
-exit 0
diff --git a/debian/python-ifupdown2.postrm b/debian/python-ifupdown2.postrm
deleted file mode 100644 (file)
index fe68eb4..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-#!/bin/sh
-# postrm script for ifupdown2
-#
-# see: dh_installdeb(1)
-
-set -e
-
-# summary of how this script can be called:
-#        * <postrm> `remove'
-#        * <postrm> `purge'
-#        * <old-postrm> `upgrade' <new-version>
-#        * <new-postrm> `failed-upgrade' <old-version>
-#        * <new-postrm> `abort-install'
-#        * <new-postrm> `abort-install' <old-version>
-#        * <new-postrm> `abort-upgrade' <old-version>
-#        * <disappearer's-postrm> `disappear' <overwriter>
-#          <overwriter-version>
-# for details, see http://www.debian.org/doc/debian-policy/ or
-# the debian-policy package
-
-process_udev()
-{
-    udevlink=$(readlink /etc/udev/rules.d/80-networking.rules 2>/dev/null || true)
-    [ -n "$udevlink" -a "$udevlink" == "/dev/null" ] && rm -f /etc/udev/rules.d/80-networking.rules
-    udevlink=$(readlink /etc/udev/rules.d/60-bridge-network-interface.rules 2>/dev/null || true)
-    [ -n "$udevlink" -a "$udevlink" == "/dev/null" ] && rm -f /etc/udev/rules.d/60-bridge-network-interface.rules
-}
-
-postrm_remove()
-{
-       rm -f /sbin/ifup /sbin/ifdown /sbin/ifquery
-    process_udev
-       update-rc.d networking remove >/dev/null
-}
-
-# Note: We don't remove /etc/network/interfaces
-postrm_purge()
-{
-       rm -f /var/tmp/network/ifstatenew
-       if [ -L /etc/network/run ] ; then
-               rm -f /etc/network/run
-       elif [ -d /etc/network/run ] ; then
-               rmdir --ignore-fail-on-non-empty /etc/network/run
-       fi
-}
-
-case "$1" in
-       purge)
-               postrm_purge
-       ;;
-
-       remove)
-               postrm_remove
-       ;;
-
-
-       upgrade|disappear|failed-upgrade|abort-install|abort-upgrade)
-       ;;
-
-       *)
-               echo "postrm called with unknown argument \`$1'" >&2
-               exit 1
-       ;;
-esac
-
-# dh_installdeb will replace this with shell code automatically
-# generated by other debhelper scripts.
-
-#DEBHELPER#
-
-exit 0
diff --git a/debian/python-ifupdown2.preinst b/debian/python-ifupdown2.preinst
deleted file mode 100755 (executable)
index 3fcaed1..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/bin/sh
-# preinst script for newpkg
-#
-# see: dh_installdeb(1)
-
-set -e
-
-# summary of how this script can be called:
-#        * <new-preinst> `install'
-#        * <new-preinst> `install' <old-version>
-#        * <new-preinst> `upgrade' <old-version>
-#        * <old-preinst> `abort-upgrade' <new-version>
-# for details, see http://www.debian.org/doc/debian-policy/ or
-# the debian-policy package
-
-preinst_upgrade()
-{
-       local oldver="$1"
-       local udev_user_rulesdir="/etc/udev/rules.d"
-
-       # we have to fixup the filesystem here as previous packages of
-       # ifupdown2 introduced a bug in the postrm script that require
-       # these files to exist, otherwise the postrm script will always
-       # fail.
-       local badver="0.1-cl2.5+2"
-       if dpkg --compare-versions "${oldver}" "lt" "${badver}"; then
-               local files="${udev_user_rulesdir}/80-networking.rules
-                       ${udev_user_rulesdir}/60-bridge-network-interface.rules"
-               for f in ${files}; do
-                       echo "touching udev rule: ${f}"
-                       test ! -e "${f}" && ln -s /dev/null "${f}" || \
-                               /bin/echo -e "\tudev rule exists leaving"
-               done
-       fi
-}
-
-case "$1" in
-       install|upgrade)
-               preinst_upgrade "$2"
-       ;;
-
-       abort-upgrade)
-       ;;
-
-       *)
-               echo "preinst called with unknown argument \`$1'" >&2
-               exit 1
-       ;;
-esac
-
-# dh_installdeb will replace this with shell code automatically
-# generated by other debhelper scripts.
-
-#DEBHELPER#
-
-exit 0
index db0fef37394a0b85e8ec7eb6659d6d3f22b87ac4..9560f7fc7678e7944b9ace5c88cf60373549919e 100755 (executable)
@@ -2,13 +2,13 @@
 
 #export DH_VERBOSE=1
 export PYBUILD_NAME=ifupdown2
-export PYBUILD_INSTALL_ARGS=--install-lib=/usr/share/ifupdown2 --install-scripts=/usr/share/ifupdown2
+export PYBUILD_INSTALL_ARGS=--install-lib=/usr/share/ --install-scripts=/usr/share/
 
 %:
        dh $@ --with python2 --with systemd --buildsystem=pybuild
 
 override_dh_installman:
-       ./scripts/genmanpages.sh ./man.rst ./man
+       ./ifupdown2/man/genmanpages.sh ./ifupdown2/man ./man
        dh_installman
 
 override_dh_systemd_start:
index af745b310bc8367d6a767864292bbf5dc5d05a60..89ae9db8f88b823b6a7eabf55e203658739da122 100644 (file)
@@ -1 +1 @@
-3.0 (git)
+3.0 (native)
diff --git a/debian/watch b/debian/watch
new file mode 100644 (file)
index 0000000..9e7c0da
--- /dev/null
@@ -0,0 +1 @@
+version=3
diff --git a/docs/examples/batman_adv/configure_batman_adv.sh b/docs/examples/batman_adv/configure_batman_adv.sh
deleted file mode 100755 (executable)
index eabfea8..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/usr/bin/env bash
-
-echo "Installing batman-adv module on debian/ubuntu"
-echo ""
-echo "Batman is a Layer2-Mesh protocol which uses Ethernet devices (like eth*,
-vlans, etc.) to communicate with peers and provides access to the L2-mesh via
-a batX interface. You can only create a batman instance if at least one batman-
--iface (read: an interface where the mesh protocol is spoken on) is present and
-added to the batman-mesh-instance."
-echo "More info: https://en.wikipedia.org/wiki/B.A.T.M.A.N."
-echo ""
-
-echo "installing batctl: apt-get install batctl"
-apt-get install batctl
-echo ""
-echo ""
-
-echo "loading batman-adv module: modprobe batman-adv"
-modprobe batman-adv
-echo ""
-
-echo "usefull commands:
-$ batctl if add \$IFACE
-$ batctl -m bat0 if add \$IFACE"
-echo "please read: man batctl"
-echo ""
-echo ""
-
-echo "configuration example:
-$ cat /etc/network/interfaces
-
-auto bat0
-iface bat0
-      batman-ifaces \$IFACE [\$IFACES...]
-      batman-ifaces-ignore-regex .*_nodes
-      batman-hop-penalty 23
-      address 192.0.2.42/24
-$
-$
-$ ifreload -a
-$ ifquery -a -c
-auto bat0
-iface bat0                                                          [pass]
-       batman-ifaces tap0 tap1                                     [pass]
-       batman-ifaces-ignore-regex .*_nodes                         [pass]
-       batman-hop-penalty 23                                       [pass]
-       address 192.0.2.42/24                                       [pass]
-
-$"
diff --git a/docs/examples/batman_adv/interfaces_batman b/docs/examples/batman_adv/interfaces_batman
deleted file mode 100644 (file)
index 14ced43..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-auto eth0
-iface eth0 inet dhcp
-
-auto lo
-iface lo inet
-
-auto bat0
-iface bat0
-      batman-ifaces tap0 tap1
-      batman-ifaces-ignore-regex .*_nodes
-      batman-hop-penalty 23
-      address 192.0.2.42/24
\ No newline at end of file
index d4b2d651fe5cb1c20357d9c1ff35546da185611d..ac711ea95a467d072f1237233b0e8d2c78bff831 100755 (executable)
@@ -3,10 +3,7 @@
 import argparse
 import sys
 import subprocess
-<<<<<<< HEAD
-=======
 import os
->>>>>>> cumulus/dev
 
 """ This script prints to stdout /etc/network/interfaces entries for
     requested interfaces.
@@ -34,21 +31,6 @@ import os
 
 """
 
-<<<<<<< HEAD
-def get_swp_interfaces():
-    porttab_path = '/var/lib/cumulus/porttab'
-    ports = []
-
-    ptfile = open(porttab_path, 'r')
-    for line in ptfile.readlines():
-        line = line.strip()
-        if '#' in line:
-            continue
-        try:
-            ports.append(line.split()[0])
-        except ValueError:
-            continue
-=======
 def get_pci_interfaces():
     ports = []
     FNULL = open(os.devnull, 'w')
@@ -90,12 +72,11 @@ def get_swp_interfaces():
     if not ports:
         print 'Error: No ports found in %s' % porttab_path
         exit(1)
->>>>>>> cumulus/dev
     return ports
 
 def print_swp_defaults_header():
     print '''
-# ** This file is autogenerated by /usr/share/doc/python-ifupdown2/generate_interfaces.py **
+# ** This file is autogenerated by /usr/share/doc/ifupdown2/generate_interfaces.py **
 #
 # This is /etc/network/interfaces section for all available swp
 # ports on the system.
@@ -112,7 +93,7 @@ def print_swp_defaults_header():
 
 def print_bridge_untagged_defaults_header():
     print '''
-# ** This file is autogenerated by /usr/share/doc/python-ifupdown2/generate_interfaces.py **
+# ** This file is autogenerated by /usr/share/doc/ifupdown2/generate_interfaces.py **
 #
 # This is /etc/network/interfaces section for a bridge device with all swp
 # ports in the system.
@@ -186,12 +167,6 @@ if args.bridgedefault and args.mergefile:
     exit(1)
 
 swp_intfs = get_swp_interfaces()
-<<<<<<< HEAD
-if not swp_intfs:
-    print 'error: no ports found'
-    exit(1)
-=======
->>>>>>> cumulus/dev
 
 if args.swpdefaults:
     interfaces_print_swp_defaults(swp_intfs)
index 692a1e6773ac4ded26443f37400686dff68f8188..b2b26c816b6a11ada835484d4226e4d21008b5a8 100644 (file)
@@ -4,13 +4,13 @@
 # uplink1, peerlink and downlink are bond interfaces.
 # 'bridge' is a vlan aware bridge with ports uplink1, peerlink
 # and downlink (swp2-20).
-#
+# 
 # native vlan is by default 1
 #
 # 'bridge-vids' attribute is used to declare vlans.
 # 'bridge-pvid' attribute is used to specify native vlans if other than 1
 # 'bridge-access' attribute is used to declare access port
-#
+# 
 auto lo
 iface lo
 
index 42cfd28bd72d1615208c6592c76fab31c85befbe..7e655f08683099a9e97dfa4b1fded994441b7ffb 100644 (file)
@@ -7,13 +7,13 @@
 #
 # All ports inherit 'vlans 10 20-23' from the 'bridge-vids' attribute
 # under the bridge
-#
+# 
 # native vlan is by default 1
 #
 # 'bridge-vids' attribute is used to declare vlans.
 # 'bridge-pvid' attribute is used to specify native vlans if other than 1
 # 'bridge-access' attribute is used to declare access port
-#
+# 
 # 'spine-bond host-bond-0[1-2]' are clag bonds and will be considered by
 # clagd for dual connection. clag-id has to be a non-zero and has to match
 # across the peer switches for the bonds to become dual connected.
index 9b114fd93c129aa6c52ff50b362708d002184587..82a89062fd8b98ff9a573235a51baf2dc7cc0e35 100644 (file)
@@ -32,21 +32,12 @@ ethtool
 
 .. autoclass:: ethtool
 
-<<<<<<< HEAD
-ifenslave
-=========
-
-.. automodule:: ifenslave
-
-.. autoclass:: ifenslave
-=======
 bond
 ====
 
 .. automodule:: bond
 
 .. autoclass:: bond
->>>>>>> cumulus/dev
 
 mstpctl
 =======
index 110d80acad6de6678b59f4b247798c46eed3d1ed..6eaa0efff9df99919fc467f471d48d2515ca1add 100644 (file)
@@ -5,36 +5,14 @@ This package contains modules that provide helper methods
 for ifupdown2 addon modules to interact directly with tools
 like iproute2, brctl etc.
 
-
-bridgeutils
+LinkUtils
 ===========
 
-Helper module to work with bridgeutil commands
-
-.. automodule:: bridgeutils
-
-.. autoclass:: brctl
-
-<<<<<<< HEAD
-ifenslaveutil
-=============
-=======
-bondutil
-========
->>>>>>> cumulus/dev
-
-Helper module to interact with linux api to create bonds.
-Currently this is via sysfs.
+Helper module to work with brctl, bonds, iproute2, mstpctl commands
 
-<<<<<<< HEAD
-.. automodule:: ifenslaveutil
+.. automodule:: LinkUtils
 
-.. autoclass:: ifenslaveutil
-=======
-.. automodule:: bondutil
-
-.. autoclass:: bondutil
->>>>>>> cumulus/dev
+.. autoclass:: LinkUtils
 
 dhclient
 ========
@@ -44,12 +22,3 @@ Helper module to interact with dhclient tools.
 .. automodule:: dhclient
 
 .. autoclass:: dhclient
-
-iproute2
-========
-
-Helper module to interact with iproute2 tools.
-
-.. automodule:: iproute2
-
-.. autoclass:: iproute2
index 818ce62fc0dc132c01e184e0087d836ff608d9b7..0d4377026324079e8d18a3c106daa9409de91c30 100644 (file)
@@ -10,7 +10,7 @@ Keep the following points in mind before you start configuring interfaces using
 ``ifupdown2``:
 
 * IPv4 and IPv6 addresses for an interface can be listed in the same ``iface`` 
-  section. For examples, see ``/usr/share/doc/python-ifupdown2/examples/``.
+  section. For examples, see ``/usr/share/doc/ifupdown2/examples/``.
 
 * Do not use a legacy interface alias. They are only supported for backward 
   compatibility with ``ifupdown``. They do get configured, but ``ifquery`` has 
@@ -66,11 +66,7 @@ Man Pages
 Configuration Files
 ===================
 
-<<<<<<< HEAD
-* /etc/network/interfaces
-=======
 * config file defined in ifupdown2.conf (default /etc/network/interfaces)
->>>>>>> cumulus/dev
 
     
 ifupdown Built-in Interfaces
@@ -113,11 +109,7 @@ following example configuration::
         bond-slaves swp29 swp30
         bond-mode 802.3ad
         bond-miimon 100
-<<<<<<< HEAD
-        bond-use-carrier 1
-=======
         bond-use-carrier yes
->>>>>>> cumulus/dev
         bond-lacp-rate 1
         bond-min-links 1
         bond-xmit-hash-policy layer3+4
@@ -128,11 +120,7 @@ following example configuration::
         bond-slaves swp31 swp32
         bond-mode 802.3ad
         bond-miimon 100
-<<<<<<< HEAD
-        bond-use-carrier 1
-=======
         bond-use-carrier yes
->>>>>>> cumulus/dev
         bond-lacp-rate 1
         bond-min-links 1
         bond-xmit-hash-policy layer3+4
@@ -310,11 +298,7 @@ The contents of the sourced file used above are::
         bond-slaves swp25 swp26
         bond-mode 802.3ad
         bond-miimon 100
-<<<<<<< HEAD
-        bond-use-carrier 1
-=======
         bond-use-carrier yes
->>>>>>> cumulus/dev
         bond-lacp-rate 1
         bond-min-links 1
         bond-xmit-hash-policy layer3+4
@@ -333,13 +317,10 @@ bridge ports and bond slaves::
     iface br1
         bridge-ports glob swp7-9.100  swp11.100 glob swp15-18.100
 
-<<<<<<< HEAD
-=======
     auto br2
     iface br2
         bridge-ports glob swp[1-6]s[0-3].100
 
->>>>>>> cumulus/dev
 Using Templates
 ===============
 
@@ -382,11 +363,7 @@ file, run::
         bond-slaves swp25 swp26
         bond-mode 802.3ad
         bond-miimon 100
-<<<<<<< HEAD
-        bond-use-carrier 1
-=======
         bond-use-carrier yes
->>>>>>> cumulus/dev
         bond-lacp-rate 1
         bond-min-links 1
         bond-xmit-hash-policy layer3+4
@@ -402,11 +379,7 @@ does not match::
     iface bond0
             bond-mode 802.3ad  (✓)
             bond-miimon 100  (✓)
-<<<<<<< HEAD
-            bond-use-carrier 1  (✓)
-=======
             bond-use-carrier yes  (✓)
->>>>>>> cumulus/dev
             bond-lacp-rate 1  (✓)
             bond-min-links 1  (✓)
             bond-xmit-hash-policy layer3+4  (✓)
@@ -444,17 +417,10 @@ the ``interfaces`` file. For complete syntax on the ``interfaces`` file, see
     {
         "auto": true, 
         "config": {
-<<<<<<< HEAD
-            "bond-use-carrier": "1", 
-            "bond-xmit-hash-policy": "layer3+4", 
-            "bond-miimon": "100", 
-            "bond-lacp-rate": "1", 
-=======
             "bond-use-carrier": "yes",
             "bond-xmit-hash-policy": "layer3+4", 
             "bond-miimon": "100", 
             "bond-lacp-rate": "1",
->>>>>>> cumulus/dev
             "bond-min-links": "1", 
             "bond-slaves": "swp25 swp26", 
             "bond-mode": "802.3ad", 
diff --git a/etc/default/networking b/etc/default/networking
new file mode 100644 (file)
index 0000000..cc3d3ef
--- /dev/null
@@ -0,0 +1,23 @@
+#
+#
+# Parameters for the /etc/init.d/networking script
+#
+#
+
+# Change the below to yes if you want verbose logging to be enabled
+VERBOSE="no"
+
+# Change the below to yes if you want debug logging to be enabled
+DEBUG="no"
+
+# Change the below to yes if you want logging to go to syslog
+SYSLOG="no"
+
+# Exclude interfaces
+EXCLUDE_INTERFACES=
+
+# Set to 'yes' if you want to skip ifdown during system reboot
+# and shutdown. This is of interest in large scale interface
+# deployments where you dont want to wait for interface
+# deconfiguration to speed up shutdown/reboot
+SKIP_DOWN_AT_SYSRESET="yes"
diff --git a/etc/init.d/networking b/etc/init.d/networking
new file mode 100644 (file)
index 0000000..b52bfdc
--- /dev/null
@@ -0,0 +1,222 @@
+#!/bin/bash
+### BEGIN INIT INFO
+# Provides:          networking ifupdown
+# Required-Start:    mountkernfs $local_fs urandom
+# Required-Stop:     $local_fs
+# Default-Start:     S
+# Default-Stop:      0 6
+# Short-Description: Raise network interfaces.
+# Description:       Prepare /run/network directory, ifstate file and raise network interfaces, or take them down.
+### END INIT INFO
+
+RUN_DIR="/run/network"
+IFSTATE_LOCKFILE="${RUN_DIR}/ifstatelock"
+
+STATE_DIR="/var/tmp/network"
+IFSTATE_FILE="${STATE_DIR}/ifstatenew"
+
+NAME=networking
+SCRIPTNAME=/etc/init.d/$NAME
+
+[ -x /sbin/ifup ] || exit 0
+[ -x /sbin/ifdown ] || exit 0
+
+. /lib/lsb/init-functions
+
+CONFIGURE_INTERFACES=yes
+
+EXTRA_ARGS=
+
+[ -f /etc/default/networking ] && . /etc/default/networking
+
+[ "$VERBOSE" = yes ] && EXTRA_ARGS=-v
+[ "$DEBUG" = yes ] && EXTRA_ARGS="$EXTRA_ARGS -d"
+[ "$SYSLOG" = yes ] && EXTRA_ARGS="$EXTRA_ARGS --syslog"
+
+gen_examples() {
+    # Generate sample interfaces file. The interfaces files are
+    # generated under /usr/share/doc/ifupdown2/examples/
+    #
+
+    # generate files only at boot
+    [ -f ${IFSTATE_LOCKFILE} ] && return
+
+    python_ifupdown2_docdir="/usr/share/doc/ifupdown2"
+    swpfile=${python_ifupdown2_docdir}"/examples/swp_defaults"
+    bridgedefaultfile=${python_ifupdown2_docdir}"/examples/bridge_untagged_default"
+    interfaces_gen_script=${python_ifupdown2_docdir}"/examples/generate_interfaces.py"
+
+    [ ! -e $interfaces_gen_script ] && return
+    ret=$($interfaces_gen_script -s 2>&1 >$swpfile)
+    ret=$($interfaces_gen_script -b 2>&1 >$bridgedefaultfile)
+    return
+}
+
+perf_options() {
+    # At bootup lets set perfmode
+    [ -f ${IFSTATE_LOCKFILE} ] && echo -n "" && return
+
+    echo -n "--perfmode"
+}
+
+process_exclusions() {
+    set -- $EXCLUDE_INTERFACES
+    exclusions=""
+    for d
+    do
+       exclusions="-X $d $exclusions"
+    done
+    echo $exclusions
+}
+
+check_network_file_systems() {
+    [ -e /proc/mounts ] || return 0
+
+    if [ -e /etc/iscsi/iscsi.initramfs ]; then
+       log_warning_msg "not deconfiguring network interfaces: iSCSI root is mounted."
+       exit 0
+    fi
+
+    while read DEV MTPT FSTYPE REST; do
+       case $DEV in
+       /dev/nbd*|/dev/nd[a-z]*|/dev/etherd/e*)
+           log_warning_msg "not deconfiguring network interfaces: network devices still mounted."
+           exit 0
+           ;;
+       esac
+       case $FSTYPE in
+       nfs|nfs4|smbfs|ncp|ncpfs|cifs|coda|ocfs2|gfs|pvfs|pvfs2|fuse.httpfs|fuse.curlftpfs)
+           log_warning_msg "not deconfiguring network interfaces: network file systems still mounted."
+           exit 0
+           ;;
+       esac
+    done < /proc/mounts
+}
+
+check_network_swap() {
+    [ -e /proc/swaps ] || return 0
+
+    while read DEV MTPT FSTYPE REST; do
+       case $DEV in
+       /dev/nbd*|/dev/nd[a-z]*|/dev/etherd/e*)
+           log_warning_msg "not deconfiguring network interfaces: network swap still mounted."
+           exit 0
+           ;;
+       esac
+    done < /proc/swaps
+}
+
+ifup_hotplug () {
+    if [ -d /sys/class/net ]
+    then
+           ifaces=$(for iface in $(ifquery --list --allow=hotplug 2>/dev/null)
+                           do
+                                   link=${iface##:*}
+                                   link=${link##.*}
+                                   if [ -e "/sys/class/net/$link" ] && [ "$(cat /sys/class/net/$link/operstate)" = up ]
+                                   then
+                                           echo "$iface"
+                                   fi
+                           done)
+           if [ -n "$ifaces" ]
+           then
+               ifup $ifaces "$@" || true
+           fi
+    fi
+}
+
+ifupdown_init() {
+       # remove state file at boot
+       [ ! -e ${IFSTATE_LOCKFILE} ] && rm -f ${IFSTATE_FILE}
+       [ ! -e /run/network ] && mkdir -p /run/network &>/dev/null
+       [ ! -e /etc/network/run ] && \
+               ln -sf /run/network /etc/network/run &>/dev/null
+}
+
+case "$1" in
+start)
+       gen_examples
+       ifupdown_init
+       if [ "$CONFIGURE_INTERFACES" = no ]
+       then
+           log_action_msg "Not configuring network interfaces, see /etc/default/networking"
+           exit 0
+       fi
+       set -f
+       exclusions=$(process_exclusions)
+       perfoptions=$(perf_options)
+       log_action_begin_msg "Configuring network interfaces"
+       ifup -a $EXTRA_ARGS $exclusions $perfoptions
+       log_action_end_msg $?
+       ;;
+
+stop)
+       if [ "$SKIP_DOWN_AT_SYSRESET" = "yes" ]; then
+               shutdown_or_reboot=$(runlevel 2>/dev/null | \
+                                    /usr/bin/tr -s " " | \
+                                    /usr/bin/cut -d " " \
+                                    -f1- --output-delimiter=$'\n' | \
+                                    /bin/grep -e "0" -e "6")
+               if [ -n "$shutdown_or_reboot" ]; then
+                       log_action_begin_msg "Deconfiguring network interfaces..skip"
+                       log_action_end_msg 0
+                       exit 0
+               fi
+       fi
+       ifupdown_init
+       check_network_file_systems
+       check_network_swap
+       exclusions=$(process_exclusions)
+
+       log_action_begin_msg "Deconfiguring network interfaces"
+       ifdown -a $EXTRA_ARGS $exclusions
+       log_action_end_msg $?
+       ;;
+
+reload)
+
+       ifupdown_init
+       log_action_begin_msg "Reloading network interfaces configuration"
+
+       ifreload -a $EXTRA_ARGS
+       log_action_end_msg $?
+       ;;
+
+reload-currently-up)
+
+       ifupdown_init
+       log_action_begin_msg "Reloading currently up network interfaces configuration"
+
+       ifreload --currently-up $EXTRA_ARGS
+       log_action_end_msg $?
+       ;;
+
+force-reload)
+
+       ifupdown_init
+
+       log_action_begin_msg "Reloading network interfaces configuration"
+       ifreload -f -a $EXTRA_ARGS
+       log_action_end_msg $?
+       ;;
+
+restart)
+       ifupdown_init
+
+       set -f
+       exclusions=$(process_exclusions)
+       log_action_begin_msg "Reconfiguring network interfaces"
+       ifdown -a $EXTRA_ARGS $exclusions || true
+       ifup -a $EXTRA_ARGS $exclusions
+       log_action_end_msg $?
+       ;;
+
+*)
+       echo "Usage: /etc/init.d/networking {start|stop|reload|restart|force-reload}"
+       exit 1
+       ;;
+esac
+
+exit 0
+
+# vim: noet ts=8
diff --git a/etc/network/ifupdown2/addons.conf b/etc/network/ifupdown2/addons.conf
new file mode 100644 (file)
index 0000000..c0d1ce9
--- /dev/null
@@ -0,0 +1,35 @@
+pre-up,link
+pre-up,bond
+pre-up,vlan
+pre-up,vxlan
+pre-up,clagd
+pre-up,usercmds
+pre-up,bridge
+pre-up,bridgevlan
+pre-up,mstpctl
+pre-up,vrf
+pre-up,ethtool
+up,dhcp
+up,address
+up,addressvirtual
+up,usercmds
+post-up,usercmds
+post-up,clagd
+post-up,vxrd
+pre-down,ethtool
+pre-down,usercmds
+pre-down,vxrd
+pre-down,dhcp
+down,addressvirtual
+down,address
+down,usercmds
+post-down,vrf
+post-down,clagd
+post-down,mstpctl
+post-down,bridgevlan
+post-down,bridge
+post-down,vxlan
+post-down,vlan
+post-down,bond
+post-down,usercmds
+post-down,link
diff --git a/etc/network/ifupdown2/ifupdown2.conf b/etc/network/ifupdown2/ifupdown2.conf
new file mode 100644 (file)
index 0000000..906fdea
--- /dev/null
@@ -0,0 +1,82 @@
+#
+# ifupdown2 configuration file
+#
+# This file contains default settings for ifupdown
+#
+
+# enable templates
+template_enable=1
+
+# default template engine (only mako is currently supported)
+template_engine=mako
+
+# default template lookup path during template rendering
+template_lookuppath=/etc/network/ifupdown2/templates
+
+# default network configuration filepath
+default_interfaces_configfile=/etc/network/interfaces
+
+# The -i interfacefile option is allowed by default but
+# can be disabled by setting the below option to 1 to
+# reduce security issues (due to the pre- and post- commands)
+disable_cli_interfacesfile=0
+
+
+# enable addon module syntax check:
+# Python addon modules register dictionary of supported attributes.
+# The syntax checker in ifupdown2 uses this dictionary for syntax
+# checks in the interfaces file. This works well, when only python modules
+# are used. But when a mix of scripts and modules are used (which is the
+# default case), you may get false warnings for attributes supported
+# by scripts
+addon_syntax_check=1
+
+# Support executing of ifupdown style scripts.
+# Note that by default python addon modules override scripts with the same
+# name
+addon_scripts_support=0
+
+# enable python addons
+addon_python_modules_support=1
+
+# By default ifupdown2 only supports a single vlan filtering bridge
+# on the system. Set this flag to 1 to support multiple vlan
+# filtering bridges
+multiple_vlan_aware_bridge_support=0
+
+# ifquery check status strings.
+# By default `ifquery --check` prints the check and
+# cross marks against interface attributes.
+# Use the below strings to modify the default behaviour.
+#
+ifquery_check_success_str=pass
+ifquery_check_error_str=fail
+ifquery_check_unknown_str=
+#
+
+# This attribute controls iface/vlan range expansions
+# in ifquery default output.
+ifquery_ifacename_expand_range=0
+
+# Let link master (bridges, bonds) own the link state of slaves
+link_master_slave=1
+
+# Delay admin state change till the end
+delay_admin_state_change=0
+
+# ifreload by default downs: 'all interfaces for which config changed' +
+# 'interfaces that were deleted'. With the below variable set to '0'
+# ifreload will only down 'interfaces that were deleted'
+ifreload_down_changed=0
+
+# squash all addr config when you process the first interface
+addr_config_squash=0
+
+# squash iface config into one when you have multiple
+# ifaces stanzas for an interface
+ifaceobj_squash=0
+
+# By default ifupdown2 will adjust logical devices MTU
+# based on the physical interface they are running on top of.
+# set this flag to 0 to disable this behaviour
+adjust_logical_dev_mtu=1
diff --git a/ifupdown/__init__.py b/ifupdown/__init__.py
deleted file mode 100644 (file)
index 5f7d90a..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-""" ifupdown2 package.
-
-.. moduleauthor:: Roopa Prabhu <roopa@cumulusnetworks.com>
-
-"""
diff --git a/ifupdown/exceptions.py b/ifupdown/exceptions.py
deleted file mode 100644 (file)
index 68d3b7f..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-# ifupdown --
-#    exceptions
-#
-
-class Error(Exception):
-    """Base class for exceptions in ifupdown"""
-
-    pass
-
-class ifaceNotFoundError(Error):
-    pass
-
-
-class invalidValueError(Error):
-    pass
-
-class errorReadingStateError(Error):
-    pass
-
-class moduleNotSupported(Error):
-    pass
diff --git a/ifupdown/graph.py b/ifupdown/graph.py
deleted file mode 100644 (file)
index e1729d0..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-# graph --
-#    graph helper module for ifupdown
-#
-
-import logging
-import copy
-from collections import deque
-try:
-    from gvgen import *
-except ImportError, e:
-    pass
-
-class graph():
-    """ graph functions to sort and print interface graph """
-
-    logger = logging.getLogger('ifupdown.graph')
-
-    @classmethod
-    def topological_sort_graphs_all(cls, dependency_graphs, indegrees_arg):
-        """ runs topological sort on interface list passed as dependency graph
-
-        Args:
-            **dependency_graphs** (dict): dependency graph with dependency
-                                          lists for interfaces 
-
-            **indegrees_arg** (dict): indegrees array for all interfaces
-        """
-        S = []
-        Q = deque()
-
-        indegrees = copy.deepcopy(indegrees_arg)
-        for ifname,indegree in indegrees.items():
-            if indegree == 0:
-                Q.append(ifname)
-
-        while len(Q):
-            # initialize queue
-            x = Q.popleft()
-
-            # Get dependents of x
-            dlist = dependency_graphs.get(x)
-            if not dlist:
-                S.append(x)
-                continue
-
-            for y in dlist:
-                try:
-                    indegrees[y] = indegrees.get(y) - 1
-                except:
-                    cls.logger.debug('topological_sort_graphs_all: did not find %s' %y)
-                    indegrees[y] = 0
-                    pass
-                if indegrees.get(y) == 0:
-                    Q.append(y)
-
-            S.append(x)
-
-        for ifname,indegree in indegrees.items():
-            if indegree != 0:
-                raise Exception('cycle found involving iface %s' %ifname +
-                                ' (indegree %d)' %indegree)
-
-        return S
-
-    @classmethod
-    def generate_dots(cls, dependency_graph, indegrees):
-        """ spits out interface dependency graph in dot format
-
-        Args:
-            **dependency_graphs** (dict): dependency graph with dependency
-                                          lists for interfaces 
-
-            **indegrees_arg** (dict): indegrees array for all interfaces
-        """
-
-        gvgraph = GvGen()
-        graphnodes = {}
-        for v in dependency_graph.keys():
-            graphnodes[v] = gvgraph.newItem(v)
-
-        for i, v in graphnodes.items():
-            dlist = dependency_graph.get(i, [])
-            if not dlist:
-                continue
-            for d in dlist:
-                gvgraph.newLink(v, graphnodes.get(d))
-        gvgraph.dot()
diff --git a/ifupdown/iface.py b/ifupdown/iface.py
deleted file mode 100644 (file)
index 8dce8a6..0000000
+++ /dev/null
@@ -1,782 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-# iface --
-#    interface object
-#
-
-"""ifupdown2 network interface object
-
-It is modeled based on the 'iface' section in /etc/network/interfaces
-file. But can be extended to include any other network interface format
-"""
-
-from collections import OrderedDict
-import logging
-import json
-
-class ifaceStatusUserStrs():
-    """ This class declares strings user can see during an ifquery --check
-    for example. These strings can be overridden by user defined strings from
-    config file """
-    SUCCESS = "success",
-    FAILURE = "error",
-    UNKNOWN = "unknown"
-
-class ifaceType():
-    UNKNOWN =     0x00
-    IFACE =       0x01
-    BRIDGE_VLAN = 0x10
-
-class ifaceRole():
-    """ ifaceRole is used to classify the ifaceobj.role of
-        MASTER or SLAVE where there is a bond or bridge
-        with bond-slaves or bridge-ports.  A bond in a bridge
-        is both a master and slave (0x3)
-    """
-    UNKNOWN = 0x00
-    SLAVE =   0x01
-    MASTER =  0x10
-
-class ifaceLinkKind():
-    """ ifaceLlinkKind is used to identify interfaces
-        in the ifaceobj.link_kind attribute. Dependents of the bridge or
-        bond have an ifaceobj.role attribute of SLAVE and the bridge or
-        bond itself has ifaceobj.role of MASTER.
-    """
-    UNKNOWN =    0x000000
-    BRIDGE =     0x000001
-    BOND =       0x000010
-    VLAN =       0x000100
-    VXLAN =      0x001000
-    VRF =        0x010000
-    BATMAN_ADV = 0x100000
-
-class ifaceLinkPrivFlags():
-    """ This corresponds to kernel netdev->priv_flags
-        and can be BRIDGE_PORT, BOND_SLAVE etc """
-    UNKNOWN =           0x00000
-    BRIDGE_PORT =       0x00001
-    BOND_SLAVE =        0x00010
-    VRF_SLAVE =         0x00100
-    BRIDGE_VLAN_AWARE = 0x01000
-    BRIDGE_VXLAN =      0x10000
-    ADDRESS_VIRTUAL_SLAVE = 0x100000
-    LOOPBACK = 0x1000000
-    KEEP_LINK_DOWN = 0x10000000
-
-    @classmethod
-    def get_str(cls, flag):
-        if flag == cls.UNKNOWN:
-            return 'unknown'
-        elif flag == cls.BRIDGE_PORT:
-            return 'bridge port'
-        elif flag == cls.BOND_SLAVE:
-            return 'bond slave'
-        elif flag == cls.VRF_SLAVE:
-            return 'vrf slave'
-        elif flag == cls.BRIDGE_VLAN_AWARE:
-            return 'vlan aware bridge'
-        elif flag == cls.BRIDGE_VXLAN:
-            return 'vxlan bridge'
-
-    @classmethod
-    def get_all_str(cls, flags):
-        str = ''
-        if flags & cls.BRIDGE_PORT:
-            str += 'bridgeport '
-        if flags & cls.BOND_SLAVE:
-            str += 'bondslave '
-        if flags & cls.VRF_SLAVE:
-            str += 'vrfslave '
-        if flags & cls.BRIDGE_VLAN_AWARE:
-            str += 'vlanawarebridge '
-        if flags & cls.BRIDGE_VXLAN:
-            str += 'vxlanbridge '
-        return str
-
-class ifaceLinkType():
-    LINK_UNKNOWN = 0x0
-    LINK_SLAVE = 0x1
-    LINK_MASTER = 0x2
-    LINK_NA = 0x3
-
-class ifaceDependencyType():
-    """ Indicates type of dependency.
-
-        This class enumerates types of dependency relationships
-        between interfaces.
-
-        iface dependency relationships can be classified
-        into:
-         - link
-         - master/slave
-
-        In a 'link' dependency relationship, dependency can be shared
-        between interfaces. example: swp1.100 and
-        swp1.200 can both have 'link' swp1. swp1 is also a dependency
-        of swp1.100 and swp1.200. As you can see dependency
-        swp1 is shared between swp1.100 and swp1.200.
-         
-        In a master/slave relationship like bridge and
-        its ports: eg: bridge br0 and its ports swp1 and swp2.
-        dependency swp1 and swp2 cannot be shared with any other
-        interface with the same dependency relationship.
-        ie, swp1 and swp2 cannot be in a slave relationship
-        with another interface. Understanding the dependency type is
-        required for any semantic checks between dependencies.
-
-    """
-    UNKNOWN = 0x0
-    LINK = 0x1
-    MASTER_SLAVE = 0x2
-
-class ifaceStatus():
-    """Enumerates iface status """
-
-    UNKNOWN = 0x1
-    SUCCESS = 0x2
-    WARNING = 0x3
-    ERROR = 0x4
-    NOTFOUND = 0x5
-
-    @classmethod
-    def to_str(cls, state):
-        if state == cls.UNKNOWN:
-            return 'unknown'
-        elif state == cls.SUCCESS:
-            return 'success'
-        elif state == cls.ERROR:
-            return 'error'
-        elif state == cls.NOTFOUND:
-            return 'notfound'
-    
-    @classmethod
-    def from_str(cls, state_str):
-        if state_str == 'unknown':
-            return cls.UNKNOWN
-        elif state_str == 'success':
-            return cls.SUCCESS
-        elif state_str == 'error':
-            return cls.ERROR
-
-class ifaceState():
-    """Enumerates iface state """
-
-    UNKNOWN = 0x1
-    NEW = 0x2
-    PRE_UP = 0x3
-    UP = 0x4
-    POST_UP = 0x5
-    PRE_DOWN = 0x6
-    DOWN = 0x7
-    POST_DOWN = 0x8
-
-    # Pseudo states
-    QUERY_CHECKCURR = 0x9
-    QUERY_RUNNING = 0xa
-
-    @classmethod
-    def to_str(cls, state):
-        if state == cls.UNKNOWN:
-            return 'unknown'
-        elif state == cls.NEW:
-            return 'new'
-        elif state == cls.PRE_UP:
-            return 'pre-up'
-        elif state == cls.UP:
-            return 'up'
-        elif state == cls.POST_UP:
-            return 'post-up'
-        elif state == cls.PRE_DOWN:
-            return 'pre-down'
-        elif state == cls.DOWN:
-            return 'down'
-        elif state == cls.POST_DOWN:
-            return 'post-down'
-        elif state == cls.QUERY_CHECKCURR:
-            return 'query-checkcurr'
-        elif state == cls.QUERY_RUNNING:
-            return 'query-running'
-
-    @classmethod
-    def from_str(cls, state_str):
-        if state_str == 'unknown':
-            return cls.UNKNOWN
-        elif state_str == 'new':
-            return cls.NEW
-        elif state_str == 'pre-up':
-            return cls.PRE_UP
-        elif state_str == 'up':
-            return cls.UP
-        elif state_str == 'post-up':
-            return cls.POST_UP
-        elif state_str == 'pre-down':
-            return cls.PRE_DOWN
-        elif state_str == 'down':
-            return cls.DOWN
-        elif state_str == 'post-down':
-            return cls.POST_DOWN
-        elif state_str == 'query-checkcurr':
-            return cls.QUERY_CHECKCURR
-        elif state_str == 'query-running':
-            return cls.QUERY_RUNNING
-
-class ifaceJsonEncoder(json.JSONEncoder):
-    def default(self, o):
-        retconfig = {}
-        retifacedict = OrderedDict([])
-        if o.config: 
-            retconfig = dict((k, (v[0] if len(v) == 1 else v))
-                             for k,v in o.config.items())
-        retifacedict['name'] = o.name
-        if o.addr_method:
-            if 'inet' in o.addr_family and 'dhcp' in o.addr_method:
-                retifacedict['addr_method'] = 'dhcp'
-            else:
-                retifacedict['addr_method'] = o.addr_method
-        if o.addr_family:
-            if len(o.addr_family) > 1:
-                retifacedict['addr_family'] = o.addr_family
-            else:
-                retifacedict['addr_family'] = ' '.join(o.addr_family)
-        retifacedict['auto'] = o.auto
-        retifacedict['config'] = retconfig
-
-        return retifacedict
-
-class ifaceJsonEncoderWithStatus(json.JSONEncoder):
-    def default(self, o):
-        retconfig = {}
-        retconfig_status = {}
-        retifacedict = OrderedDict([])
-        if o.config:
-            for k,v in o.config.items():
-                idx = 0
-                vitem_status = []
-                for vitem in v:
-                    s = o.get_config_attr_status(k, idx)
-                    if s == -1:
-                        status_str = ifaceStatusUserStrs.UNKNOWN
-                    elif s == 1:
-                        status_str = ifaceStatusUserStrs.ERROR
-                    elif s == 0:
-                        status_str = ifaceStatusUserStrs.SUCCESS
-                    vitem_status.append('%s' %status_str)
-                    idx += 1
-                retconfig[k] = v[0] if len(v) == 1 else v
-                retconfig_status[k] = vitem_status[0] if len(vitem_status) == 1 else vitem_status
-
-        if (o.status == ifaceStatus.NOTFOUND or
-                o.status == ifaceStatus.ERROR):
-            status =  ifaceStatusUserStrs.ERROR
-        else:
-            status =  ifaceStatusUserStrs.SUCCESS
-
-        retifacedict['name'] = o.name
-        if o.addr_method:
-            retifacedict['addr_method'] = o.addr_method
-        if o.addr_family:
-            if len(o.addr_family) > 1:
-                retifacedict['addr_family'] = o.addr_family
-            else:
-                retifacedict['addr_family'] = ' '.join(o.addr_family)
-        retifacedict['auto'] = o.auto
-        retifacedict['config'] = retconfig
-        retifacedict['config_status'] = retconfig_status
-        retifacedict['status'] = status
-
-        return retifacedict
-
-class ifaceJsonDecoder():
-    @classmethod
-    def json_to_ifaceobj(cls, ifaceattrdict):
-        ifaceattrdict['config'] = OrderedDict([(k, (v if isinstance(v, list)
-                                                else [v.strip()]))
-                                for k,v in ifaceattrdict.get('config',
-                                            OrderedDict()).items()])
-        return iface(attrsdict=ifaceattrdict)
-
-class iface():
-    """ ifupdown2 iface object class
-    Attributes:
-        **name**      Name of the interface 
-
-        **addr_family**     Address family eg, inet, inet6. Can be None to
-                            indicate both address families
-
-        **addr_method**     Address method eg, static, manual or None for
-                            static address method
-
-        **config**          dictionary of config lines for this interface
-
-        **state**           Configuration state of an interface as defined by
-                            ifaceState
-
-        **status**          Configuration status of an interface as defined by
-                            ifaceStatus
-
-        **flags**           Internal flags used by iface processing
-
-        **priv_flags**      private flags owned by module using this class
-
-        **module_flags**    module flags owned by module using this class
-
-        **refcnt**          reference count, indicating number of interfaces
-                            dependent on this iface
-
-        **lowerifaces**     list of interface names lower to this interface or
-                            this interface depends on
-
-        **upperifaces**     list of interface names upper to this interface or
-                            the interfaces that depend on this interface 
-
-        **auto**            True if interface belongs to the auto class
-
-        **classes**         List of classes the interface belongs to
-
-        **env**             shell environment the interface needs during
-                            execution
-
-        **raw_config**      raw interface config from file
-    """
-
-    # flag to indicate that the object was created from pickled state
-    # XXX: Move these flags into a separate iface flags class
-    _PICKLED         = 0x00000001
-    HAS_SIBLINGS     = 0x00000010
-    IFACERANGE_ENTRY = 0x00000100
-    IFACERANGE_START = 0x00001000
-    OLDEST_SIBLING   = 0x00010000
-    YOUNGEST_SIBLING   = 0x00100000
-
-    version = '0.1'
-
-    def __init__(self, attrsdict={}):
-        self.addr_family = []
-
-        self._set_attrs_from_dict(attrsdict)
-        self._config_status = {}
-        """dict with config status of iface attributes"""
-        self.state = ifaceState.NEW
-        """iface state (of type ifaceState) """
-        self.status = ifaceStatus.UNKNOWN
-        """iface status (of type ifaceStatus) """
-        self.status_str = None
-        """iface status str (string representing the status) """
-        self.flags = 0x0
-        """iface flags """
-        self.priv_flags = None
-        """iface module flags dictionary with module name: flags"""
-        self.module_flags = {}
-        """iface priv flags. can be used by the external object manager """
-        self.refcnt = 0
-        """iface refcnt (incremented for each dependent this interface has) """
-        self.lowerifaces = None 
-        """lower iface list (in other words: slaves of this interface """
-        self.upperifaces = None
-        """upper iface list (in other words: master of this interface """
-        self.classes = []
-        """interface classes this iface belongs to """
-        self.env = None
-        """environment variable dict required for this interface to run"""
-        self.raw_config = []
-        """interface config/attributes in raw format (eg: as it appeared in the interfaces file)"""
-        self.linkstate = None
-        """linkstate of the interface"""
-        self.type = ifaceType.UNKNOWN
-        """interface type"""
-        self.priv_data = None
-        self.role = ifaceRole.UNKNOWN
-        self.realname = None
-        self.link_type = ifaceLinkType.LINK_UNKNOWN
-        self.link_kind = ifaceLinkKind.UNKNOWN
-        self.link_privflags = ifaceLinkPrivFlags.UNKNOWN
-
-        # The below attribute is used to disambiguate between various
-        # types of dependencies
-        self.dependency_type = ifaceDependencyType.UNKNOWN
-        self.blacklisted = False
-
-    def _set_attrs_from_dict(self, attrdict):
-        self.auto = attrdict.get('auto', False)
-        self.name = attrdict.get('name')
-        self.addr_method = attrdict.get('addr_method')
-        self.config = attrdict.get('config', OrderedDict())
-
-        addr_family = attrdict.get('addr_family')
-        if addr_family:
-            self.addr_family.append(addr_family)
-
-    def inc_refcnt(self):
-        """ increment refcnt of the interface. Usually used to indicate that
-        it has dependents """
-        self.refcnt += 1
-
-    def dec_refcnt(self):
-        """ decrement refcnt of the interface. Usually used to indicate that
-        it has lost its dependent """
-        self.refcnt -= 1
-
-    def is_config_present(self):
-        """ returns true if the interface has user provided config,
-        false otherwise """
-        addr_method = self.addr_method
-        if addr_method and addr_method in ['dhcp', 'dhcp6', 'loopback']:
-            return True
-        if not self.config:
-            return False
-        else:
-            return True
-
-    def set_class(self, classname):
-        """ appends class to the interfaces class list """
-        self.classes.append(classname)
-
-    def set_state_n_status(self, state, status):
-        """ sets state and status of an interface """
-        self.state = state
-        if status > self.status:
-            self.status = status
-
-    def set_status(self, status):
-        """ sets status of an interface """
-        if status > self.status:
-            self.status = status
-
-    def set_flag(self, flag):
-        self.flags |= flag
-
-    def clear_flag(self, flag):
-        self.flags &= ~flag
-
-    def add_to_upperifaces(self, upperifacename):
-        """ add to the list of upperifaces """
-        if self.upperifaces:
-            if upperifacename not in self.upperifaces:
-                self.upperifaces.append(upperifacename)
-        else:
-            self.upperifaces = [upperifacename]
-
-    def add_to_lowerifaces(self, lowerifacename):
-        """ add to the list of lowerifaces """
-        if self.lowerifaces:
-            if lowerifacename not in self.lowerifaces:
-                self.lowerifaces.append(lowerifacename)
-        else:
-            self.lowerifaces = [lowerifacename]
-
-    def get_attr_value(self, attr_name):
-        """ add to the list of upperifaces """
-        return self.config.get(attr_name)
-    
-    def get_attr_value_first(self, attr_name):
-        """ get first value of the specified attr name """
-        attr_value_list = self.config.get(attr_name)
-        if attr_value_list:
-            return attr_value_list[0]
-        return None
-
-    def get_attrs_value_first(self, attrs):
-        """ get first value of the first attr in the list.
-            Useful when you have multiple attrs representing the
-            same thing.
-        """
-        for attr in attrs:
-            attr_value_list = self.config.get(attr)
-            if attr_value_list:
-                return attr_value_list[0]
-        return None
-
-    def get_attr_value_n(self, attr_name, attr_index):
-        """ get n'th value of the specified attr name """
-        attr_value_list = self.config.get(attr_name)
-        if attr_value_list:
-            try:
-                return attr_value_list[attr_index]
-            except:
-                return None
-        return None
-
-    def get_env(self):
-        """ get shell environment variables the interface must execute in """
-        if not self.env:
-            self.generate_env()
-        return self.env
-
-    def generate_env(self):
-        """ generate shell environment variables dict interface must execute
-        in. This is used to support legacy ifupdown scripts
-        """
-        env = {}
-        config = self.config
-        env['IFACE'] = self.name
-        for attr, attr_value in config.items():
-            attr_env_name = 'IF_%s' %attr.upper().replace("-", "_")
-            env[attr_env_name] = attr_value[0]
-        self.env = env
-
-    def update_config(self, attr_name, attr_value):
-        """ add attribute name and value to the interface config """
-        self.config.setdefault(attr_name, []).append(attr_value)
-
-    def replace_config(self, attr_name, attr_value):
-        """ add attribute name and value to the interface config """
-        self.config[attr_name] = [attr_value]
-
-    def delete_config(self, attr_name):
-        """ add attribute name and value to the interface config """
-        try:
-            del self.config[attr_name]
-        except:
-            pass
-
-    def update_config_dict(self, attrdict):
-        self.config.update(attrdict)
-
-    def update_config_with_status(self, attr_name, attr_value, attr_status=0):
-        """ add attribute name and value to the interface config and also
-        update the config_status dict with status of this attribute config """
-        if not attr_value:
-            attr_value = ''
-        self.config.setdefault(attr_name, []).append(attr_value)
-        self._config_status.setdefault(attr_name, []).append(attr_status)
-        # set global iface state
-        if attr_status == 1:
-            self.status = ifaceStatus.ERROR
-        elif self.status != ifaceStatus.ERROR:
-            # Not already error, mark success
-            self.status = ifaceStatus.SUCCESS
-
-    def check_n_update_config_with_status_many(self, ifaceobjorig, attr_names,
-                                               attr_status=0):
-        # set multiple attribute status to zero
-        # also updates status only if the attribute is present
-        for attr_name in attr_names:
-            if not ifaceobjorig.get_attr_value_first(attr_name):
-               continue
-            self.config.setdefault(attr_name, []).append('')
-            self._config_status.setdefault(attr_name, []).append(attr_status)
-
-    def get_config_attr_status(self, attr_name, idx=0):
-        """ get status of a attribute config on this interface.
-        
-        Looks at the iface _config_status dict"""
-        return self._config_status.get(attr_name, [])[idx]
-
-    def compare(self, dstiface):
-        """ compares iface object with iface object passed as argument
-
-        Returns True if object self is same as dstiface and False otherwise """
-
-        if self.name != dstiface.name: return False
-        if self.type != dstiface.type: return False
-        if self.addr_family != dstiface.addr_family: return False
-        if self.addr_method != dstiface.addr_method: return False
-        if self.auto != dstiface.auto: return False
-        if self.classes != dstiface.classes: return False
-        if len(self.config) != len(dstiface.config):
-            return False
-        if any(True for k in self.config if k not in dstiface.config):
-            return False
-        if any(True for k,v in self.config.items()
-                    if v != dstiface.config.get(k)): return False
-        return True
-
-    def squash(self, newifaceobj):
-        """ This squashes the iface object """
-        for attrname, attrlist in newifaceobj.config.iteritems():
-            # if allready present add it to the list
-            # else add it to the end of the dictionary
-            # We need to maintain order.
-            if self.config.get(attrname):
-                self.config[attrname].extend(attrlist)
-            else:
-                self.config.update([(attrname, attrlist)])
-        # we now support inet and inet6 together
-        self.addr_family.extend(newifaceobj.addr_family)
-        # if auto %ifacename is not part of the first stanza
-        # we need to squash it
-        if not self.auto and newifaceobj.auto:
-            self.auto = True
-
-    def __getstate__(self):
-        odict = self.__dict__.copy()
-        del odict['state']
-        del odict['status']
-        del odict['lowerifaces']
-        del odict['upperifaces']
-        del odict['refcnt']
-        del odict['_config_status']
-        del odict['flags']
-        del odict['priv_flags']
-        del odict['module_flags']
-        del odict['raw_config']
-        del odict['linkstate']
-        del odict['env']
-        del odict['link_type']
-        del odict['link_kind']
-        del odict['link_privflags']
-        del odict['role']
-        del odict['dependency_type']
-        del odict['blacklisted']
-        return odict
-
-    def __setstate__(self, dict):
-        self.__dict__.update(dict)
-        self._config_status = {}
-        self.state = ifaceState.NEW
-        self.status = ifaceStatus.UNKNOWN
-        self.refcnt = 0
-        self.flags = 0
-        self.lowerifaces = None
-        self.upperifaces = None
-        self.linkstate = None
-        self.env = None
-        self.role = ifaceRole.UNKNOWN
-        self.priv_flags = None
-        self.module_flags = {}
-        self.raw_config = []
-        self.flags |= self._PICKLED
-        self.link_type = ifaceLinkType.LINK_NA
-        self.link_kind = ifaceLinkKind.UNKNOWN
-        self.link_privflags = ifaceLinkPrivFlags.UNKNOWN
-        self.dependency_type = ifaceDependencyType.UNKNOWN
-        self.blacklisted = False
-
-    def dump_raw(self, logger):
-        indent = '  '
-        if self.auto:
-            print 'auto %s' %self.name
-        print (self.raw_config[0])
-        for i in range(1, len(self.raw_config)):
-            print(indent + self.raw_config[i])
-        
-    def dump(self, logger):
-        indent = '\t'
-        logger.info(self.name + ' : {')
-        logger.info(indent + 'family: %s' % ' '.join(self.addr_family))
-        logger.info(indent + 'method: %s' %self.addr_method)
-        logger.info(indent + 'flags: %x' %self.flags)
-        logger.info(indent + 'state: %s'
-                %ifaceState.to_str(self.state))
-        logger.info(indent + 'status: %s'
-                %ifaceStatus.to_str(self.status))
-        logger.info(indent + 'refcnt: %d' %self.refcnt)
-        d = self.lowerifaces
-        if d:
-            logger.info(indent + 'lowerdevs: %s' %str(d))
-        else:
-            logger.info(indent + 'lowerdevs: None')
-
-        d = self.upperifaces
-        if d:
-            logger.info(indent + 'upperdevs: %s' %str(d))
-        else:
-            logger.info(indent + 'upperdevs: None')
-
-        logger.info(indent + 'config: ')
-        config = self.config
-        if config:
-            logger.info(indent + indent + str(config))
-        logger.info('}')
-
-    def _dump_pretty(self, family, first, addr_method, with_status=False, use_realname=False):
-        indent = '\t'
-        outbuf = ''
-        if use_realname and self.realname:
-            name = '%s' %self.realname
-        else:
-            name = '%s' %self.name
-        if self.auto:
-            outbuf += 'auto %s\n' %name
-        ifaceline = ''
-        if self.type == ifaceType.BRIDGE_VLAN:
-            ifaceline += 'vlan %s' %name
-        else:
-            ifaceline += 'iface %s' %name
-        if family:
-            ifaceline += ' %s' % family
-        if addr_method:
-            ifaceline += ' %s' % addr_method
-        if with_status:
-            status_str = None
-            if (self.status == ifaceStatus.ERROR or
-                    self.status == ifaceStatus.NOTFOUND):
-                if self.status_str:
-                    ifaceline += ' (%s)' %self.status_str
-                status_str = '[%s]' %ifaceStatusUserStrs.ERROR
-            elif self.status == ifaceStatus.SUCCESS:
-                status_str = '[%s]' %ifaceStatusUserStrs.SUCCESS
-            if status_str:
-                outbuf += '{0:65} {1:>8}'.format(ifaceline, status_str) + '\n'
-            else:
-                outbuf += ifaceline + '\n'
-            if self.status == ifaceStatus.NOTFOUND:
-                outbuf = (outbuf.encode('utf8')
-                    if isinstance(outbuf, unicode) else outbuf)
-                print outbuf + '\n'
-                return
-        else:
-            outbuf += ifaceline + '\n'
-        config = self.config
-        if config and first:
-            for cname, cvaluelist in config.items():
-                idx = 0
-                for cv in cvaluelist:
-                    status_str = None
-                    if with_status:
-                        s = self.get_config_attr_status(cname, idx)
-                        if s == -1:
-                            status_str = '[%s]' %ifaceStatusUserStrs.UNKNOWN
-                        elif s == 1:
-                            status_str = '[%s]' %ifaceStatusUserStrs.ERROR
-                        elif s == 0:
-                            status_str = '[%s]' %ifaceStatusUserStrs.SUCCESS
-                    if status_str:
-                        outbuf += (indent + '{0:55} {1:>10}'.format(
-                              '%s %s' %(cname, cv), status_str)) + '\n'
-                    else:
-                        outbuf += indent + '%s %s\n' %(cname, cv)
-                    idx += 1
-        if with_status:
-            outbuf = (outbuf.encode('utf8')
-                        if isinstance(outbuf, unicode) else outbuf)
-        print outbuf
-
-    def dump_pretty(self, with_status=False, use_realname=False):
-        if not self.addr_family:
-            self._dump_pretty(None, True,
-                              self.addr_method,
-                              with_status=with_status,
-                              use_realname=use_realname)
-        else:
-            # To allow both inet and inet6 on an interface we changed the
-            # addr_family attribute, it's now a list. Depending on how
-            # stanzas were squashed (and what command was used ie. ifquery -r)
-            # we want to dump the ifaceobj as usual but we will output an
-            # empty stanza for each additional addr_family. The config will
-            # only be displayed once, in the first stanza. Example:
-            # $ ifquery eth0 -r
-            # auto etho
-            # iface eth0 inet dhcp
-            #     config...
-            #
-            # auto eth0
-            # iface eth0 inet6 dhcp
-            # $
-            first = True
-            for family in self.addr_family:
-                addr_method = self.addr_method
-                # We need to make sure we display 'dhcp' for inet family.
-                # In some cases it might take the value 'dhcp6' even if it has
-                # both inet and inet6 addr_family
-                if addr_method and family == 'inet' and 'dhcp' in addr_method:
-                    addr_method = 'dhcp'
-                self._dump_pretty(family, first,
-                                  addr_method=addr_method,
-                                  with_status=with_status,
-                                  use_realname=use_realname)
-                first = False
diff --git a/ifupdown/iff.py b/ifupdown/iff.py
deleted file mode 100644 (file)
index f191883..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-#
-# Author: Scott Feldman, sfeldma@cumulusnetworks.com
-#
-#
-# from /usr/include/linux/if.h
-#
-
-# Standard interface flags (netdevice->flags).
-
-IFF_UP = 0x1                  # interface is up
-IFF_BROADCAST = 0x2           # broadcast address valid
-IFF_DEBUG = 0x4               # turn on debugging
-IFF_LOOPBACK = 0x8            # is a loopback net
-IFF_POINTOPOINT = 0x10        # interface is has p-p link
-IFF_NOTRAILERS = 0x20         # avoid use of trailers
-IFF_RUNNING = 0x40            # interface RFC2863 OPER_UP
-IFF_NOARP = 0x80              # no ARP protocol
-IFF_PROMISC = 0x100           # receive all packets
-IFF_ALLMULTI = 0x200          # receive all multicast packets
-
-IFF_MASTER = 0x400            # master of a load balancer
-IFF_SLAVE = 0x800             # slave of a load balancer
-
-IFF_MULTICAST = 0x1000        # Supports multicast
-
-IFF_PORTSEL = 0x2000          # can set media type
-IFF_AUTOMEDIA = 0x4000        # auto media select active
-IFF_DYNAMIC = 0x8000          # dialup device with changing addresses
-
-IFF_LOWER_UP = 0x10000        # driver signals L1 up
-IFF_DORMANT = 0x20000         # driver signals dormant
-
-IFF_ECHO = 0x40000            # echo sent packets
diff --git a/ifupdown/ifupdownbase.py b/ifupdown/ifupdownbase.py
deleted file mode 100644 (file)
index dbac0ad..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-# ifupdownBase --
-#    base object for various ifupdown objects
-#
-
-import logging
-import re
-import os
-import traceback
-from ifupdown.netlink import netlink
-
-from iface import *
-import ifupdownflags as ifupdownflags
-
-class ifupdownBase(object):
-
-    def __init__(self):
-        modulename = self.__class__.__name__
-        self.logger = logging.getLogger('ifupdown.' + modulename)
-
-    def ignore_error(self, errmsg):
-        if (ifupdownflags.flags.FORCE == True or re.search(r'exists', errmsg,
-            re.IGNORECASE | re.MULTILINE) is not None):
-            return True
-        return False
-
-    def log_warn(self, str):
-        if self.ignore_error(str) == False:
-            if self.logger.getEffectiveLevel() == logging.DEBUG:
-                traceback.print_stack()
-            self.logger.warn(str)
-        pass
-
-    def log_error(self, str):
-        if self.ignore_error(str) == False:
-            raise
-            #raise Exception(str)
-        else:
-            pass
-
-    def link_exists(self, ifacename):
-        return os.path.exists('/sys/class/net/%s' %ifacename)
-
-    def link_up(self, ifacename):
-        netlink.link_set_updown(ifacename, "up")
-
-    def link_down(self, ifacename):
-        netlink.link_set_updown(ifacename, "down")
diff --git a/ifupdown/ifupdownconfig.py b/ifupdown/ifupdownconfig.py
deleted file mode 100644 (file)
index b156b6a..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2015 Cumulus Networks, Inc. All rights reserved.
-#
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-#
-
-class ifupdownConfig():
-
-       def __init__(self):
-               self.conf = {}
-
-config = ifupdownConfig()
diff --git a/ifupdown/ifupdownflags.py b/ifupdown/ifupdownflags.py
deleted file mode 100644 (file)
index d76e733..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2015 Cumulus Networks, Inc. All rights reserved.
-#
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-#
-
-class ifupdownFlags():
-
-       def __init__(self):
-                self.ALL = False
-                self.CLASS = False
-                self.FORCE = False
-                self.DRYRUN = False
-                self.NOWAIT = False
-                self.PERFMODE = False
-                self.CACHE = False
-                self.WITHDEFAULTS = False
-                self.IGNORE_ERRORS = False
-
-                # Flags
-                self.CACHE_FLAGS = 0x0
-
-flags = ifupdownFlags()
diff --git a/ifupdown/ifupdownmain.py b/ifupdown/ifupdownmain.py
deleted file mode 100644 (file)
index 951cf68..0000000
+++ /dev/null
@@ -1,2179 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-# ifupdownMain --
-#    ifupdown main module
-#
-
-import os
-import re
-import imp
-import pprint
-import logging
-import sys, traceback
-import copy
-import json
-import ifupdown.statemanager as statemanager
-import ifupdown.ifupdownconfig as ifupdownConfig
-import ifupdown.ifupdownflags as ifupdownflags
-from networkinterfaces import *
-from iface import *
-from scheduler import *
-from collections import deque
-from collections import OrderedDict
-from graph import *
-from exceptions import *
-from sets import Set
-
-from ipaddr import IPNetwork, IPv4Network, IPv6Network, IPAddress, IPv4Address, IPv6Address
-
-"""
-.. module:: ifupdownmain
-:synopsis: main module for ifupdown package
-
-.. moduleauthor:: Roopa Prabhu <roopa@cumulusnetworks.com>
-
-"""
-
-_tickmark = u'\u2713'
-_crossmark = u'\u2717'
-_success_sym = '(%s)' %_tickmark
-_error_sym = '(%s)' %_crossmark
-
-class ifupdownMainFlags():
-    COMPAT_EXEC_SCRIPTS = False
-    STATEMANAGER_ENABLE = True
-    STATEMANAGER_UPDATE = True
-    ADDONS_ENABLE = False
-    DELETE_DEPENDENT_IFACES_WITH_NOCONFIG = False
-    SCHED_SKIP_CHECK_UPPERIFACES = False
-    CHECK_SHARED_DEPENDENTS = True
-
-class ifacePrivFlags():
-    # priv flags to mark iface objects
-    BUILTIN = False
-    NOCONFIG = False
-
-    def __init__(self, builtin=False, noconfig=False):
-        self.BUILTIN = builtin
-        self.NOCONFIG = noconfig
-    
-class ifupdownMain(ifupdownBase):
-    """ ifupdown2 main class """
-
-    scripts_dir='/etc/network'
-    addon_modules_dir='/usr/share/ifupdown2/addons'
-    addon_modules_configfile='/etc/network/ifupdown2/addons.conf'
-
-    # iface dictionary in the below format:
-    # { '<ifacename>' : [<ifaceobject1>, <ifaceobject2> ..] }
-    # eg:
-    # { 'swp1' : [<iface swp1>, <iface swp2> ..] }
-    #
-    # Each ifaceobject corresponds to a configuration block for
-    # that interface
-    # The value in the dictionary is a list because the network
-    # interface configuration file supports more than one iface section
-    # in the interfaces file
-    ifaceobjdict = OrderedDict()
-
-    # iface dictionary representing the curr running state of an iface
-    # in the below format:
-    # {'<ifacename>' : <ifaceobject>}
-    ifaceobjcurrdict = OrderedDict()
-
-    # Dictionary representing operation and modules
-    # for every operation
-    module_ops = OrderedDict([('pre-up', []),
-                              ('up' , []),
-                              ('post-up' , []),
-                              ('query-checkcurr', []),
-                              ('query-running', []),
-                              ('query-dependency', []),
-                              ('query', []),
-                              ('query-raw', []),
-                              ('pre-down', []),
-                              ('down' , []),
-                              ('post-down' , [])])
-
-    # For old style /etc/network/ bash scripts
-    script_ops = OrderedDict([('pre-up', []),
-                                    ('up' , []),
-                                    ('post-up' , []),
-                                    ('pre-down', []),
-                                    ('down' , []),
-                                    ('post-down' , [])])
-
-    # Handlers for ops that ifupdown2 owns
-    def run_up(self, ifaceobj):
-        # Skip link sets on ifaceobjs of type 'vlan' (used for l2 attrs).
-        # there is no real interface behind it
-        if ifaceobj.type == ifaceType.BRIDGE_VLAN:
-            return
-        if ((ifaceobj.link_kind & ifaceLinkKind.VRF) or
-            (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE)):
-            return
-        # if not a logical interface and addr method is manual,
-        # ignore link admin state changes
-        if (ifaceobj.addr_method == 'manual' and
-            not ifaceobj.link_kind):
-            return
-        if self._delay_admin_state:
-            self._delay_admin_state_iface_queue.append(ifaceobj.name)
-            return
-        # If this object is a link slave, ie its link is controlled
-        # by its link master interface, then dont set the link state.
-        # But do allow user to change state of the link if the interface
-        # is already with its link master (hence the master check).
-        if ifaceobj.link_type == ifaceLinkType.LINK_SLAVE:
-            return
-        if not self.link_exists(ifaceobj.name):
-           return
-        if ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
-            # user has asked to explicitly keep the link down,
-            # so, force link down
-            self.logger.info('%s: keeping link down due to user config' %ifaceobj.name)
-            self.link_down(ifaceobj.name)
-            return
-        self.link_up(ifaceobj.name)
-
-    def run_down(self, ifaceobj):
-        if ((ifaceobj.link_kind & ifaceLinkKind.VRF) or
-            (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE)):
-            return
-        # Skip link sets on ifaceobjs of type 'vlan' (used for l2 attrs)
-        # there is no real interface behind it
-        if ifaceobj.type == ifaceType.BRIDGE_VLAN:
-            return
-        # if not a logical interface and addr method is manual,
-        # ignore link admin state changes
-        if (ifaceobj.addr_method == 'manual' and
-            not ifaceobj.link_kind):
-            return
-        if self._delay_admin_state:
-            self._delay_admin_state_iface_queue.append(ifaceobj.name)
-            return
-        # If this object is a link slave, ie its link is controlled
-        # by its link master interface, then dont set the link state.
-        # But do allow user to change state of the link if the interface
-        # is already with its link master (hence the master check).
-        if ifaceobj.link_type == ifaceLinkType.LINK_SLAVE:
-           return
-        if not self.link_exists(ifaceobj.name):
-           return
-        self.link_down(ifaceobj.name)
-
-    # ifupdown object interface operation handlers
-    ops_handlers = OrderedDict([('up', run_up),
-                                ('down', run_down)])
-
-    def run_sched_ifaceobj_posthook(self, ifaceobj, op):
-        if (ifaceobj.priv_flags and (ifaceobj.priv_flags.BUILTIN or
-            ifaceobj.priv_flags.NOCONFIG)):
-            return
-        if self.flags.STATEMANAGER_UPDATE:
-            self.statemanager.ifaceobj_sync(ifaceobj, op)
-
-    # ifupdown object interface scheduler pre and posthooks
-    sched_hooks = {'posthook' : run_sched_ifaceobj_posthook}
-
-    def __init__(self, config={},
-                 force=False, dryrun=False, nowait=False,
-                 perfmode=False, withdepends=False, njobs=1,
-                 cache=False, addons_enable=True, statemanager_enable=True,
-                 interfacesfile='/etc/network/interfaces',
-                 interfacesfileiobuf=None,
-                 interfacesfileformat='native',
-                 withdefaults=False):
-        """This member function initializes the ifupdownmain object.
-
-        Kwargs:
-            config (dict):  config dict from /etc/network/ifupdown2/ifupdown2.conf
-            force (bool): force interface configuration
-            dryrun (bool): dryrun interface configuration
-            withdepends (bool): apply interface configuration on all depends
-            interfacesfile (str): interfaces file. default is /etc/network/interfaces
-            interfacesfileformat (str): default is 'native'. Other choices are 'json'
-
-        Raises:
-            AttributeError, KeyError """
-
-        self.logger = logging.getLogger('ifupdown')
-        ifupdownflags.flags.FORCE = force
-        ifupdownflags.flags.DRYRUN = dryrun
-        ifupdownflags.flags.WITHDEFAULTS = withdefaults
-        ifupdownflags.flags.NOWAIT = nowait
-        ifupdownflags.flags.PERFMODE = perfmode
-        ifupdownflags.flags.CACHE = cache
-        ifupdownflags.flags.WITH_DEPENDS = withdepends
-
-        # Can be used to provide hints for caching
-        ifupdownflags.flags.CACHE_FLAGS = 0x0
-
-        self.flags = ifupdownMainFlags()
-
-        self.flags.STATEMANAGER_ENABLE = statemanager_enable
-        self.interfacesfile = interfacesfile
-        self.interfacesfileiobuf = interfacesfileiobuf
-        self.interfacesfileformat = interfacesfileformat
-        self.config = config
-        self.logger.debug(self.config)
-        self.blacklisted_ifaces_present = False
-
-        self.type = ifaceType.UNKNOWN
-
-        self.flags.DELETE_DEPENDENT_IFACES_WITH_NOCONFIG = False
-        self.flags.ADDONS_ENABLE = addons_enable
-
-        self.ifaces = OrderedDict()
-        self.njobs = njobs
-        self.pp = pprint.PrettyPrinter(indent=4)
-        self.modules = OrderedDict({})
-        self.module_attrs = {}
-        self.overridden_ifupdown_scripts = []
-
-        if self.config.get('addon_python_modules_support', '1') == '1':
-            self.load_addon_modules(self.addon_modules_dir)
-        if self.config.get('addon_scripts_support', '0') == '1':
-            self.load_scripts(self.scripts_dir)
-        self.dependency_graph = OrderedDict({})
-
-        self._cache_no_repeats = {}
-
-        if self.flags.STATEMANAGER_ENABLE:
-            try:
-                self.statemanager = statemanager.statemanager_api
-                self.statemanager.read_saved_state()
-            except Exception, e:
-                # XXX Maybe we should continue by ignoring old state
-                self.logger.warning('error reading state (%s)' %str(e))
-                raise
-        else:
-            self.flags.STATEMANAGER_UPDATE = False
-        self._delay_admin_state = True if self.config.get(
-                            'delay_admin_state_change', '0') == '1' else False
-        self._delay_admin_state_iface_queue = []
-        if self._delay_admin_state:
-            self.logger.info('\'delay_admin_state_change\' is set. admin ' +
-                             'state changes will be delayed till the end.')
-
-        self._link_master_slave = True if self.config.get(
-                      'link_master_slave', '0') == '1' else False
-        if self._link_master_slave:
-            self.logger.info('\'link_master_slave\' is set. slave admin ' +
-                             'state changes will be delayed till the ' +
-                             'masters admin state change.')
-
-        # squash iface objects for same interface both internal and
-        # external representation. It is off by default.
-        self._ifaceobj_squash = True if self.config.get(
-                            'ifaceobj_squash', '0') == '1' else False
-
-        # squash iface objects for same interface internal
-        # representation only. External representation as seen by ifquery
-        # will continue to see multiple iface stanzas if it was specified
-        # that way by the user. It is on by default.
-        self._ifaceobj_squash_internal = True if self.config.get(
-                            'ifaceobj_squash_internal', '1') == '1' else False
-
-        # initialize global config object with config passed by the user
-        # This makes config available to addon modules
-        ifupdownConfig.config = self.config
-
-        self.validate_keywords = {
-            '<mac>': self._keyword_mac,
-            '<text>': self._keyword_text,
-            '<ipv4>': self._keyword_ipv4,
-            '<ipv6>': self._keyword_ipv6,
-            '<ip>': self._keyword_ip,
-            '<number>': self._keyword_number,
-            '<interface>': self._keyword_interface,
-            '<ipv4-vrf-text>': self._keyword_ipv4_vrf_text,
-            '<number-ipv4-list>': self._keyword_number_ipv4_list,
-            '<interface-list>': self._keyword_interface_list,
-            '<ipv4/prefixlen>': self._keyword_ipv4_prefixlen,
-            '<ipv6/prefixlen>': self._keyword_ipv6_prefixlen,
-            '<ip/prefixlen>': self._keyword_ip_prefixlen,
-            '<number-range-list>': self._keyword_number_range_list,
-            '<interface-range-list>': self._keyword_interface_range_list,
-            '<mac-ip/prefixlen-list>': self._keyword_mac_ip_prefixlen_list,
-            '<number-interface-list>': self._keyword_number_interface_list,
-            '<interface-yes-no-list>': self._keyword_interface_yes_no_list,
-            '<interface-yes-no-0-1-list>': self._keyword_interface_yes_no_0_1_list,
-            '<interface-yes-no-auto-list>': self._keyword_interface_yes_no_auto_list,
-        }
-
-    def link_master_slave_ignore_error(self, errorstr):
-        # If link master slave flag is set, 
-        # there may be cases where the lowerdev may not be
-        # up resulting in 'Network is down' error
-        # This can happen if the lowerdev is a LINK_SLAVE
-        # of another interface which is not up yet
-        # example of such a case:
-        #   bringing up a vlan on a bond interface and the bond
-        #   is a LINK_SLAVE of a bridge (in other words the bond is
-        #   part of a bridge) which is not up yet
-        if self._link_master_slave:
-           if 'Network is down' in errorstr:
-              return True
-        return False
-
-    def get_ifaceobjs(self, ifacename):
-        return self.ifaceobjdict.get(ifacename)
-
-    def get_ifaceobjs_saved(self, ifacename):
-        """ Return ifaceobjects from statemanager """
-        if self.flags.STATEMANAGER_ENABLE:
-           return self.statemanager.get_ifaceobjs(ifacename)
-        else:
-           return None
-
-    def get_ifaceobj_first(self, ifacename):
-        ifaceobjs = self.get_ifaceobjs(ifacename)
-        if ifaceobjs:
-            return ifaceobjs[0]
-        return None
-
-    def get_ifacenames(self):
-        return self.ifaceobjdict.keys()
-
-    def get_iface_obj_last(self, ifacename):
-        return self.ifaceobjdict.get(ifacename)[-1]
-
-
-    def must_follow_upperifaces(self, ifacename):
-        #
-        # XXX: This bleeds the knowledge of iface
-        # types in the infrastructure module.
-        # Cant think of a better fix at the moment.
-        # In future maybe the module can set a flag
-        # to indicate if we should follow upperifaces
-        #
-        ifaceobj = self.get_ifaceobj_first(ifacename)
-        if ifaceobj.type == ifaceType.BRIDGE_VLAN:
-            return False
-        return True
-
-    def create_n_save_ifaceobj(self, ifacename, priv_flags=None,
-                               increfcnt=False):
-        """ creates a iface object and adds it to the iface dictionary """
-        ifaceobj = iface()
-        ifaceobj.name = ifacename
-        ifaceobj.priv_flags = priv_flags
-        ifaceobj.auto = True
-        if not self._link_master_slave:
-            ifaceobj.link_type = ifaceLinkType.LINK_NA
-        if increfcnt:
-            ifaceobj.inc_refcnt()
-        self.ifaceobjdict[ifacename] = [ifaceobj]
-        return ifaceobj
-
-    def create_n_save_ifaceobjcurr(self, ifaceobj):
-        """ creates a copy of iface object and adds it to the iface
-            dict containing current iface objects 
-        """
-        ifaceobjcurr = iface()
-        ifaceobjcurr.name = ifaceobj.name
-        ifaceobjcurr.type = ifaceobj.type
-        ifaceobjcurr.lowerifaces = ifaceobj.lowerifaces
-        ifaceobjcurr.priv_flags = copy.deepcopy(ifaceobj.priv_flags)
-        ifaceobjcurr.auto = ifaceobj.auto
-        self.ifaceobjcurrdict.setdefault(ifaceobj.name,
-                                     []).append(ifaceobjcurr)
-        return ifaceobjcurr
-
-    def get_ifaceobjcurr(self, ifacename, idx=0):
-        ifaceobjlist = self.ifaceobjcurrdict.get(ifacename)
-        if not ifaceobjlist:
-            return None
-        if not idx:
-            return ifaceobjlist
-        else:
-            return ifaceobjlist[idx]
-
-    def get_ifaceobjrunning(self, ifacename):
-        return self.ifaceobjrunningdict.get(ifacename)
-
-    def get_iface_refcnt(self, ifacename):
-        """ Return iface ref count """
-        max = 0
-        ifaceobjs = self.get_ifaceobjs(ifacename)
-        if not ifaceobjs:
-            return 0
-        for i in ifaceobjs:
-            if i.refcnt > max:
-                max = i.refcnt
-        return max
-
-    def is_iface_builtin_byname(self, ifacename):
-        """ Returns true if iface name is a builtin interface.
-        
-        A builtin interface is an interface which ifupdown understands.
-        The following are currently considered builtin ifaces:
-            - vlan interfaces in the format <ifacename>.<vlanid>
-        """
-        return '.' in ifacename
-
-    def is_ifaceobj_builtin(self, ifaceobj):
-        """ Returns true if iface name is a builtin interface.
-        
-        A builtin interface is an interface which ifupdown understands.
-        The following are currently considered builtin ifaces:
-            - vlan interfaces in the format <ifacename>.<vlanid>
-        """
-        if (ifaceobj.priv_flags and ifaceobj.priv_flags.BUILTIN):
-            return True
-        return False
-
-    def is_ifaceobj_noconfig(self, ifaceobj):
-        """ Returns true if iface object did not have a user defined config.
-       
-        These interfaces appear only when they are dependents of interfaces
-        which have user defined config
-        """
-        return (ifaceobj.priv_flags and ifaceobj.priv_flags.NOCONFIG)
-
-    def is_iface_noconfig(self, ifacename):
-        """ Returns true if iface has no config """
-
-        ifaceobj = self.get_ifaceobj_first(ifacename)
-        if not ifaceobj: return True
-        return self.is_ifaceobj_noconfig(ifaceobj)
-
-    def check_shared_dependents(self, ifaceobj, dlist):
-        """ ABSOLETE: Check if dlist intersects with any other
-            interface with slave dependents.
-            example: bond and bridges.
-            This function logs such errors """
-        setdlist = Set(dlist)
-        for ifacename, ifacedlist in self.dependency_graph.items():
-            if not ifacedlist:
-                continue
-            check_depends = False
-            iobjs = self.get_ifaceobjs(ifacename)
-            if not iobjs:
-                continue
-            for i in iobjs:
-                if (i.dependency_type == ifaceDependencyType.MASTER_SLAVE):
-                    check_depends = True
-            if check_depends:
-                common = Set(ifacedlist).intersection(setdlist)
-                if common:
-                    self.logger.error('misconfig..?. iface %s and %s '
-                            %(ifaceobj.name, ifacename) +
-                            'seem to share dependents/ports %s' %str(list(common)))
-
-    def _set_iface_role(self, ifaceobj, role, upperifaceobj):
-        if (self.flags.CHECK_SHARED_DEPENDENTS and
-            (ifaceobj.role & ifaceRole.SLAVE) and
-            (role == ifaceRole.SLAVE) and (upperifaceobj.role == ifaceRole.MASTER)):
-               self.logger.error("misconfig..? %s %s is enslaved to multiple interfaces %s"
-                                  %(ifaceobj.name,
-                                    ifaceLinkPrivFlags.get_all_str(ifaceobj.link_privflags), str(ifaceobj.upperifaces)))
-                ifaceobj.set_status(ifaceStatus.ERROR)
-                return
-        ifaceobj.role = role
-
-    def _set_iface_role_n_kind(self, ifaceobj, upperifaceobj):
-
-        # If addr_method is set and link is not a logical interface,
-        # set flag KEEP_LINK_DOWN. addr_method == 'manual' only applies to
-        # logical interfaces.
-        if (ifaceobj.addr_method == 'manual' and not ifaceobj.link_kind):
-            ifaceobj.link_privflags |= ifaceLinkPrivFlags.KEEP_LINK_DOWN
-
-        if (upperifaceobj.link_kind & ifaceLinkKind.BOND):
-            self._set_iface_role(ifaceobj, ifaceRole.SLAVE, upperifaceobj)
-            ifaceobj.link_privflags |= ifaceLinkPrivFlags.BOND_SLAVE
-
-        if (upperifaceobj.link_kind & ifaceLinkKind.BRIDGE):
-            self._set_iface_role(ifaceobj, ifaceRole.SLAVE, upperifaceobj)
-            ifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_PORT
-
-        if (ifaceobj.link_kind & ifaceLinkKind.VXLAN) \
-                and (upperifaceobj.link_kind & ifaceLinkKind.BRIDGE):
-            upperifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_VXLAN
-
-        # vrf masters get processed after slaves, which means
-        # check both link_kind vrf and vrf slave
-        if ((upperifaceobj.link_kind & ifaceLinkKind.VRF) or
-            (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE)):
-            self._set_iface_role(ifaceobj, ifaceRole.SLAVE, upperifaceobj)
-            ifaceobj.link_privflags |= ifaceLinkPrivFlags.VRF_SLAVE
-        if self._link_master_slave:
-            if upperifaceobj.link_type == ifaceLinkType.LINK_MASTER:
-                ifaceobj.link_type = ifaceLinkType.LINK_SLAVE
-        else:
-            upperifaceobj.link_type = ifaceLinkType.LINK_NA
-            ifaceobj.link_type = ifaceLinkType.LINK_NA
-
-    def dump_iface_dependency_info(self):
-        """ debug funtion to print raw dependency 
-        info - lower and upper devices"""
-
-        for ifacename, ifaceobjs in self.ifaceobjdict.iteritems():
-            iobj = ifaceobjs[0]
-            self.logger.info("%s: refcnt: %d, lower: %s, upper: %s" %(ifacename,
-                             self.get_iface_refcnt(ifacename),
-                             str(iobj.lowerifaces) if iobj.lowerifaces else [],
-                             str(iobj.upperifaces) if iobj.upperifaces else []))
-
-
-    def preprocess_dependency_list(self, upperifaceobj, dlist, ops):
-        """ We go through the dependency list and
-            delete or add interfaces from the interfaces dict by
-            applying the following rules:
-                if flag DELETE_DEPENDENT_IFACES_WITH_NOCONFIG is True:
-                    we only consider devices whose configuration was
-                    specified in the network interfaces file. We delete
-                    any interface whose config was not specified except
-                    for vlan devices. vlan devices get special treatment.
-                    Even if they are not present they are created and added
-                    to the ifacesdict
-                elif flag DELETE_DEPENDENT_IFACES_WITH_NOCONFIG is False:
-                    we create objects for all dependent devices that are not
-                    present in the ifacesdict
-        """
-        del_list = []
-
-        for d in dlist:
-            dilist = self.get_ifaceobjs(d)
-            if not dilist:
-                ni = None
-                if self.is_iface_builtin_byname(d):
-                    ni = self.create_n_save_ifaceobj(d,
-                            ifacePrivFlags(True, True), True)
-                elif not self.flags.DELETE_DEPENDENT_IFACES_WITH_NOCONFIG:
-                    ni = self.create_n_save_ifaceobj(d,
-                                    ifacePrivFlags(False, True), True)
-                else:
-                    del_list.append(d)
-                if ni:
-                    ni.add_to_upperifaces(upperifaceobj.name)
-                    self._set_iface_role_n_kind(ni, upperifaceobj)
-            else:
-                for di in dilist:
-                    di.inc_refcnt()
-                    di.add_to_upperifaces(upperifaceobj.name)
-                    self._set_iface_role_n_kind(di, upperifaceobj)
-        for d in del_list:
-            dlist.remove(d)
-
-    def preprocess_upperiface(self, lowerifaceobj, ulist, ops):
-        for u in ulist:
-            if (lowerifaceobj.upperifaces and
-                u in lowerifaceobj.upperifaces):
-                continue
-            lowerifaceobj.add_to_upperifaces(u)
-            uifacelist = self.get_ifaceobjs(u)
-            if uifacelist:
-                for ui in uifacelist:
-                    lowerifaceobj.inc_refcnt()
-                    self._set_iface_role_n_kind(lowerifaceobj, ui)
-                    ui.add_to_lowerifaces(lowerifaceobj.name)
-
-    def query_lowerifaces(self, ifaceobj, ops, ifacenames, type=None):
-        """ Gets iface dependents by calling into respective modules """
-        ret_dlist = []
-
-        # Get dependents for interface by querying respective modules
-        for module in self.modules.values():
-            try:
-                if ops[0] == 'query-running':
-                    if (not hasattr(module,
-                        'get_dependent_ifacenames_running')):
-                        continue
-                    dlist = module.get_dependent_ifacenames_running(ifaceobj)
-                else:
-                    if (not hasattr(module, 'get_dependent_ifacenames')):
-                        continue
-                    dlist = module.get_dependent_ifacenames(ifaceobj,
-                                        ifacenames)
-            except Exception, e:
-                self.logger.warn('%s: error getting dependent interfaces (%s)'
-                        %(ifaceobj.name, str(e)))
-                dlist = None
-                pass
-            if dlist: ret_dlist.extend(dlist)
-        return list(set(ret_dlist))
-
-    def query_upperifaces(self, ifaceobj, ops, ifacenames, type=None):
-        """ Gets iface upperifaces by calling into respective modules """
-        ret_ulist = []
-
-        # Get upperifaces for interface by querying respective modules
-        for module in self.modules.values():
-            try:
-                if ops[0] == 'query-running':
-                    if (not hasattr(module,
-                        'get_upper_ifacenames_running')):
-                        continue
-                    ulist = module.get_upper_ifacenames_running(ifaceobj)
-                else:
-                    if (not hasattr(module, 'get_upper_ifacenames')):
-                        continue
-                    ulist = module.get_upper_ifacenames(ifaceobj, ifacenames)
-            except Exception, e:
-                self.logger.warn('%s: error getting upper interfaces (%s)'
-                                 %(ifaceobj.name, str(e)))
-                ulist = None
-                pass
-            if ulist: ret_ulist.extend(ulist)
-        return list(set(ret_ulist))
-
-    def populate_dependency_info(self, ops, ifacenames=None):
-        """ recursive function to generate iface dependency info """
-
-        if not ifacenames:
-            ifacenames = self.ifaceobjdict.keys()
-
-        iqueue = deque(ifacenames)
-        while iqueue:
-            i = iqueue.popleft()
-            # Go through all modules and find dependent ifaces
-            dlist = None
-            ulist = None
-            ifaceobjs = self.get_ifaceobjs(i)
-            if not ifaceobjs:
-                continue
-            dependents_processed = False
-
-            # Store all dependency info in the first ifaceobj
-            # but get dependency info from all ifaceobjs
-            ifaceobj = ifaceobjs[0]
-            for iobj in ifaceobjs:
-                ulist = self.query_upperifaces(iobj, ops, ifacenames)
-                if iobj.lowerifaces:
-                    dependents_processed = True
-                    break
-                dlist = self.query_lowerifaces(iobj, ops, ifacenames)
-                if dlist:
-                   break
-            if ulist:
-                self.preprocess_upperiface(ifaceobj, ulist, ops)
-            if dependents_processed:
-                continue
-            if dlist:
-                self.preprocess_dependency_list(ifaceobj,
-                                                dlist, ops)
-                ifaceobj.lowerifaces = dlist
-                [iqueue.append(d) for d in dlist]
-            #if not self.dependency_graph.get(i):
-            #    self.dependency_graph[i] = dlist
-
-        for i in self.ifaceobjdict.keys():
-            iobj = self.get_ifaceobj_first(i)
-            if (not iobj.link_kind and
-               not (iobj.link_privflags & ifaceLinkPrivFlags.LOOPBACK) and
-               iobj.name == 'lo'):
-               iobj.link_privflags |= ifaceLinkPrivFlags.LOOPBACK
-            if iobj.lowerifaces:
-                self.dependency_graph[i] = iobj.lowerifaces
-            else:
-                self.dependency_graph[i] = []
-
-        if not self.blacklisted_ifaces_present:
-            return
-
-        # Walk through the dependency graph and remove blacklisted
-        # interfaces that were picked up as dependents
-        for i in self.dependency_graph.keys():
-            ifaceobj = self.get_ifaceobj_first(i)
-            if not ifaceobj:
-                continue
-
-            if ifaceobj.blacklisted and not ifaceobj.upperifaces:
-                # if blacklisted and was not picked up as a
-                # dependent of a upper interface, delete the
-                # interface from the dependency graph
-                dlist = ifaceobj.lowerifaces
-                if dlist:
-                    for d in dlist:
-                        difaceobjs = self.get_ifaceobjs(d)
-                        if not difaceobjs:
-                            continue
-                        try:
-                            for d in difaceobjs:
-                                d.dec_refcnt()
-                                d.upperifaces.remove(i)
-                        except:
-                            self.logger.debug('error removing %s from %s upperifaces' %(i, d))
-                            pass
-                self.logger.debug("populate_dependency_info: deleting blacklisted interface %s" %i)
-                del self.dependency_graph[i]
-                continue
-
-    def _check_config_no_repeats(self, ifaceobj):
-        """ check if object has an attribute that is
-        restricted to a single object in the system.
-        if yes, warn and return """
-        for k,v in self._cache_no_repeats.items():
-            iv = ifaceobj.config.get(k)
-            if iv and iv[0] == v:
-                self.logger.error('ignoring interface %s. ' %ifaceobj.name +
-                        'Only one object with attribute ' +
-                        '\'%s %s\' allowed.' %(k, v))
-                return True
-        for k, v in self.config.get('no_repeats', {}).items():
-            iv = ifaceobj.config.get(k)
-            if iv and iv[0] == v:
-                self._cache_no_repeats[k] = v
-        return False
-
-    def _save_iface_squash(self, ifaceobj):
-        """ squash ifaceobjects belonging to same iface
-        into a single object """
-        if self._check_config_no_repeats(ifaceobj):
-           return
-        ifaceobj.priv_flags = ifacePrivFlags()
-        if not self._link_master_slave:
-           ifaceobj.link_type = ifaceLinkType.LINK_NA
-        currentifaceobjlist = self.ifaceobjdict.get(ifaceobj.name)
-        if not currentifaceobjlist:
-            self.ifaceobjdict[ifaceobj.name] = [ifaceobj]
-            return
-        if ifaceobj.compare(currentifaceobjlist[0]):
-            self.logger.warn('duplicate interface %s found' %ifaceobj.name)
-            return
-        for obj in self.ifaceobjdict[ifaceobj.name]:
-            if obj.type == ifaceobj.type:
-                obj.squash(ifaceobj)
-                return
-        self.ifaceobjdict[ifaceobj.name].append(ifaceobj)
-
-    def _save_iface(self, ifaceobj):
-        if self._check_config_no_repeats(ifaceobj):
-           return
-        ifaceobj.priv_flags = ifacePrivFlags()
-        if not self._link_master_slave:
-           ifaceobj.link_type = ifaceLinkType.LINK_NA
-        currentifaceobjlist = self.ifaceobjdict.get(ifaceobj.name)
-        if not currentifaceobjlist:
-            self.ifaceobjdict[ifaceobj.name]= [ifaceobj]
-            if not self._ifaceobj_squash:
-                ifaceobj.flags |= ifaceobj.YOUNGEST_SIBLING
-            return
-        if ifaceobj.compare(currentifaceobjlist[0]):
-            self.logger.warn('duplicate interface %s found' %ifaceobj.name)
-            return
-        if currentifaceobjlist[0].type == ifaceobj.type:
-            currentifaceobjlist[0].flags |= ifaceobj.HAS_SIBLINGS
-            ifaceobj.flags |= ifaceobj.HAS_SIBLINGS
-        # clear the OLDEST_SIBLING from all the siblings
-        for iface in self.ifaceobjdict[ifaceobj.name]:
-            iface.flags &= ~ifaceobj.OLDEST_SIBLING
-        # current sibling is the oldest
-        ifaceobj.flags |= ifaceobj.OLDEST_SIBLING
-        self.ifaceobjdict[ifaceobj.name].append(ifaceobj)
-
-    def _keyword_text(self, value, validrange=None):
-        return isinstance(value, str) and len(value) > 0
-
-    def _keyword_mac(self, value, validrange=None):
-        if value.strip().startswith('ether'):
-            value = value.strip()[6:]
-        return re.match('[0-9a-f]{1,2}([-:])[0-9a-f]{1,2}(\\1[0-9a-f]{1,2}){4}$',
-                        value.lower())
-
-    def _keyword_check_list(self, _list, obj, limit=None):
-        try:
-            if limit and limit > 0:
-                for i in xrange(0, limit):
-                    obj(_list[i])
-                return len(_list) == limit
-            else:
-                for elem in _list:
-                    obj(elem)
-            return True
-        except Exception as e:
-            self.logger.debug('keyword: check list: %s' % str(e))
-            return False
-
-    def _keyword_ipv4(self, value, validrange=None):
-        return self._keyword_check_list(value.split(), IPv4Address, limit=1)
-
-    def _keyword_ipv4_prefixlen(self, value, validrange=None):
-        return self._keyword_check_list(value.split(), IPv4Network, limit=1)
-
-    def _keyword_ipv6(self, value, validrange=None):
-        return self._keyword_check_list(value.split(), IPv6Address, limit=1)
-
-    def _keyword_ipv6_prefixlen(self, value, validrange=None):
-        return self._keyword_check_list(value.split(), IPv6Network, limit=1)
-
-    def _keyword_ip(self, value, validrange=None):
-        return self._keyword_check_list(value.split(), IPAddress, limit=1)
-
-    def _keyword_ip_prefixlen(self, value, validrange=None):
-        return self._keyword_check_list(value.split(), IPNetwork, limit=1)
-
-    def _keyword_mac_ip_prefixlen_list(self, value, validrange=None):
-        """
-            <mac> <ip> [<ip> ...]
-            ex: address-virtual 00:11:22:33:44:01 11.0.1.1/24 11.0.1.2/24
-        """
-        try:
-            res = value.split()
-            if len(res) < 2:
-                return False
-            if not self._keyword_mac(res[0]):
-                return False
-            for ip in res[1:]:
-                if not self._keyword_ip_prefixlen(ip):
-                    return False
-            return True
-        except Exception as e:
-            self.logger.debug('keyword: mac ipaddr prefixlen: %s' % str(e))
-            return False
-
-    def _keyword_number_ipv4_list(self, value, validrange=None):
-        """
-            <number>=<ipv4> [<number>=<ipv4> ...]
-            ex: bridge-mcqv4src 100=172.16.100.1 101=172.16.101.1
-        """
-        try:
-            elements = value.split(' ')
-            if not elements:
-                return False
-            for elem in elements:
-                v = elem.split('=')
-                int(v[0])
-                IPv4Address(v[1])
-            return True
-        except Exception as e:
-            self.logger.debug('keyword: number ipv4: %s' % str(e))
-            return False
-
-    def _keyword_interface(self, ifacename, validrange=None):
-        return self.get_ifaceobjs(ifacename)
-
-    def _keyword_ipv4_vrf_text(self, value, validrange=None):
-        """
-            <ipv4> "vrf" <text>
-            ex: clagd-backup-ip 10.10.10.42 vrf blue
-        """
-        values = value.split()
-        size = len(values)
-
-        if size > 3 or size < 1:
-            return False
-        try:
-            IPv4Address(values[0])
-            if size > 1:
-                if values[1] != 'vrf':
-                    return False
-                if size > 2:
-                    if not self._keyword_text(values[2]):
-                        return False
-            return True
-        except Exception as e:
-            self.logger.debug('keyword: ipv4 vrf text: %s' % str(e))
-            return False
-
-    def _keyword_interface_list_with_value(self, value, validvals):
-        values = value.split()
-        try:
-            if len(values) == 1:
-                if values[0] in validvals:
-                    return True
-            for v in values:
-                iface_value = v.split('=')
-                size = len(iface_value)
-                if size != 2:
-                    if iface_value[0] == 'glob' or iface_value[0] == 'regex':
-                        continue
-                    return False
-                if not iface_value[1] in validvals:
-                    return False
-            return True
-        except Exception as e:
-            self.logger.debug('keyword: interface list with value: %s' % str(e))
-            return False
-
-    def _keyword_interface_yes_no_list(self, value, validrange=None):
-        """
-            <yes|no> | ( <interface>=<yes|no> [<interface>=<yes|no> ...] )
-            ex: mstpctl-portrestrrole swp1=yes swp2=no
-        """
-        return self._keyword_interface_list_with_value(value, ['yes', 'no'])
-
-    def _keyword_interface_yes_no_auto_list(self, value, validrange=None):
-        """
-            <yes|no|auto> |
-                ( <interface>=<yes|no|auto> [<interface>=<yes|no|auto> ...] )
-            ex: mstpctl-portp2p swp1=yes swp2=no swp3=auto
-        """
-        return self._keyword_interface_list_with_value(value,
-                                                        ['yes', 'no', 'auto'])
-
-    def _keyword_interface_yes_no_0_1_list(self, value, validrange=None):
-        """
-            <yes|no|0|1> |
-                ( <interface>=<yes|no|0|1> [<interface>=<yes|no|0|1> ...] )
-            ex: bridge-portmcrouter swp1=yes swp2=yes swp3=1
-        """
-        return self._keyword_interface_list_with_value(value,
-                                                       ['yes', 'no', '1', '0'])
-
-    def _keyword_interface_range_list(self, value, validrange):
-        """
-            <number> | ( <interface>=<number> [ <interface>=number> ...] )
-            ex: mstpctl-portpathcost swp1=0 swp2=1
-        """
-        values = value.split()
-        try:
-            if len(values) == 1:
-                try:
-                    n = int(values[0])
-                    if n < int(validrange[0]) or n > int(
-                        validrange[1]):
-                        raise invalidValueError('value of out range "%s":'
-                                                ' valid attribute range: %s'
-                                                % (values[0],
-                                                   '-'.join(validrange)))
-                    return True
-                except invalidValueError as e:
-                    raise e
-                except Exception as e:
-                    self.logger.debug('keyword: interface range list: %s'
-                                      % str(e))
-                    return False
-            for v in values:
-                iface_value = v.split('=')
-                size = len(iface_value)
-                if size != 2:
-                    return False
-                number = int(iface_value[1])
-                if number < int(validrange[0]) or number > int(
-                        validrange[1]):
-                    raise invalidValueError(
-                        'value of out range "%s" for iface "%s":'
-                        ' valid attribute range: %s'
-                        % (iface_value[1],
-                           iface_value[0],
-                           '-'.join(validrange)))
-            return True
-        except invalidValueError as e:
-            raise e
-        except Exception as e:
-            self.logger.debug('keyword: interface range list: %s' % str(e))
-            return False
-
-    def _keyword_interface_list(self, value, validrange=None):
-        """
-            [glob|regex] <interface> [ [glob|regex] <interface> ...]
-            ex: bridge-ports swp1 swp2 glob swp3-5.100 regex (swp[6|7|8].100)
-        """
-        interface_list = value.split()
-        size = len(interface_list)
-        i = 0
-        while i < size:
-            if interface_list[i] == 'glob' or interface_list[i] == 'regex':
-                i += 1
-            else:
-                if not self._keyword_interface(interface_list[i]):
-                    return False
-            i += 1
-        return True
-
-    def _keyword_number_range_list(self, value, validrange=None):
-        """
-            <number> [<number>-<number>]
-            ex: bridge-vids 42 100-200
-        """
-        number_list = value.split()
-        try:
-            i = 0
-            while i < len(number_list):
-                if '-' in number_list[i]:
-                    range = number_list[i].split('-')
-                    a = int(range[0])
-                    b = int(range[1])
-                    if a > b:
-                        return False
-                else:
-                    int(number_list[i])
-                i += 1
-            return True
-        except Exception as e:
-            self.logger.debug('keyword: number range list: %s' % str(e))
-            return False
-
-    def _keyword_number_interface_list(self, value, validrange=None):
-        """
-            <number> <interface> [<interface>... [<number> <interface> ... ]]
-            bridge-waitport 42 swp1 swp2 swp3 9 swp4
-        """
-        interface_list = value.split()
-        if not interface_list:
-            return False
-        try:
-            int(interface_list[0])
-            prev = True
-            for elem in interface_list[1:]:
-                try:
-                    int(elem)
-                    if prev:
-                        return False
-                    prev = True
-                except:
-                    prev = False
-            return not prev
-        except Exception as e:
-            self.logger.debug('keyword: number interface list: %s' % str(e))
-            return False
-
-    def _keyword_number(self, value, validrange=None):
-        try:
-            int(value)
-            return True
-        except Exception as e:
-            self.logger.debug('keyword: number: %s' % str(e))
-            return False
-
-    def _is_keyword(self, value):
-        if isinstance(value, tuple):
-            return True
-        keyword_found = value in self.validate_keywords
-        if value.startswith('<') and value.endswith('>') and not keyword_found:
-            raise Exception('%s: invalid keyword, please make sure to use'
-                            ' a valid keyword see `ifquery -s`' % value)
-        return keyword_found
-
-    def _check_validvals_value(self, attrname, value, validvals, validrange):
-        if validvals and value not in validvals:
-            is_valid = False
-            for keyword in validvals:
-                if self._is_keyword(keyword):
-                    if validrange:
-                        if self.validate_keywords[keyword](value, validrange):
-                            return {'result': True}
-                    else:
-                        if self.validate_keywords[keyword](value):
-                            return {'result': True}
-            if not is_valid:
-                return {
-                    'result': False,
-                    'message': 'invalid value "%s": valid attribute values: %s'
-                               % (value, validvals)
-                }
-        elif validrange:
-            if len(validrange) != 2:
-                raise Exception('%s: invalid range in addon configuration'
-                                % '-'.join(validrange))
-            _value = int(value)
-            if _value < int(validrange[0]) or _value > int(validrange[1]):
-                return {
-                    'result': False,
-                    'message': 'value of out range "%s": '
-                               'valid attribute range: %s'
-                               % (value, '-'.join(validrange))
-                }
-        return {'result': True}
-
-    def _check_validvals(self, ifacename, module_name, attrs):
-        ifaceobj = self.get_ifaceobjs(ifacename)
-        if not ifaceobj:
-            return
-        success = True
-        for attrname, attrvalue in ifaceobj[0].config.items():
-            try:
-                attrname_dict = attrs.get(attrname, {})
-                validvals = attrname_dict.get('validvals', [])
-                validrange = attrname_dict.get('validrange', [])
-                for value in attrvalue:
-                    res = self._check_validvals_value(attrname,
-                                                      value,
-                                                      validvals,
-                                                      validrange)
-                    if not res['result']:
-                        self.logger.warn('%s: %s: %s' %
-                                         (ifacename, attrname, res['message']))
-                        success = False
-            except Exception as e:
-                self.logger.warn('addon \'%s\': %s: %s' % (module_name,
-                                                           attrname,
-                                                           str(e)))
-                success = False
-        return success
-
-    def _module_syntax_check(self, filtered_ifacenames):
-        result = True
-        for ifacename in filtered_ifacenames:
-            for module in self.modules.values():
-                try:
-                    if hasattr(module, '_modinfo'):
-                        if not self._check_validvals(ifacename,
-                                                     module.__class__.__name__,
-                                                     module._modinfo.get('attrs', {})):
-                            result = False
-                    if hasattr(module, 'syntax_check') and callable(module.syntax_check):
-                        if not module.syntax_check(self.get_ifaceobjs(ifacename)[0],
-                                                   self.get_ifaceobjs):
-                            result = False
-                except Exception, e:
-                    self.logger.warn('%s: %s' % (ifacename, str(e)))
-                    result = False
-        return result
-
-    def _iface_configattr_syntax_checker(self, attrname, attrval):
-        for m, mdict in self.module_attrs.items():
-            if not mdict:
-                continue
-            attrsdict = mdict.get('attrs')
-            try:
-                a = attrsdict.get(attrname)
-                if a:
-                    if a.get('deprecated'):
-                        newa = a.get('new-attribute')
-                        if newa:
-                            self.logger.warn('attribute %s is deprecated. use %s instead.' %(attrname, newa))
-                        else:
-                            self.logger.warn('attribute %s is deprecated.'
-                                             %attrname)
-                    return True
-                else:
-                    for key in attrsdict:
-                        if 'aliases' in attrsdict[key]:
-                            if attrname in attrsdict[key]['aliases']:
-                                return True
-            except AttributeError:
-                pass
-        return False
-
-    def _ifaceobj_syntax_checker(self, ifaceobj):
-        ret = True
-        for attrname, attrvalue in ifaceobj.config.items():
-            found = False
-            for k, v in self.module_attrs.items():
-                if v and v.get('attrs', {}).get(attrname):
-                    found = True
-                    break
-            if not found:
-                ret = False
-                self.logger.warn('%s: unsupported attribute \'%s\'' \
-                                 % (ifaceobj.name, attrname))
-                continue
-        return ret
-
-    def read_iface_config(self):
-        """ Reads default network interface config /etc/network/interfaces. """
-        ret = True
-        nifaces = networkInterfaces(self.interfacesfile,
-                        self.interfacesfileiobuf,
-                        self.interfacesfileformat,
-                        template_enable=self.config.get('template_enable', 0),
-                        template_engine=self.config.get('template_engine'),
-                template_lookuppath=self.config.get('template_lookuppath'))
-        if self._ifaceobj_squash or self._ifaceobj_squash_internal:
-            nifaces.subscribe('iface_found', self._save_iface_squash)
-        else:
-            nifaces.subscribe('iface_found', self._save_iface)
-        if self.config.get('addon_syntax_check', '1') == '1':
-            nifaces.subscribe('validateifaceattr',
-                              self._iface_configattr_syntax_checker)
-            nifaces.subscribe('validateifaceobj', self._ifaceobj_syntax_checker)
-        nifaces.load()
-        if nifaces.errors or nifaces.warns:
-            ret = False
-        return ret
-
-    def read_old_iface_config(self):
-        """ Reads the saved iface config instead of default iface config.
-        And saved iface config is already read by the statemanager """
-        self.ifaceobjdict = copy.deepcopy(self.statemanager.ifaceobjdict)
-
-    def _load_addon_modules_config(self):
-        """ Load addon modules config file """
-
-        with open(self.addon_modules_configfile, 'r') as f:
-            lines = f.readlines()
-            for l in lines:
-                try:
-                    litems = l.strip(' \n\t\r').split(',')
-                    if not litems or len(litems) < 2:
-                        continue
-                    operation = litems[0]
-                    mname = litems[1]
-                    self.module_ops[operation].append(mname)
-                except Exception, e:
-                    self.logger.warn('error reading line \'%s\' %s:' %(l, str(e)))
-                    continue
-
-    def load_addon_modules(self, modules_dir):
-        """ load python modules from modules_dir
-
-        Default modules_dir is /usr/share/ifupdownmodules
-
-        """
-        self.logger.info('loading builtin modules from %s' %modules_dir)
-        self._load_addon_modules_config()
-        if not modules_dir in sys.path:
-            sys.path.append(modules_dir)
-        try:
-            for op, mlist in self.module_ops.items():
-                for mname in mlist:
-                    if self.modules.get(mname):
-                        continue
-                    mpath = modules_dir + '/' + mname + '.py'
-                    if os.path.exists(mpath):
-                        try:
-                            m = __import__(mname)
-                            mclass = getattr(m, mname)
-                        except:
-                            raise
-                        try:
-                            minstance = mclass()
-                            script_override = minstance.get_overrides_ifupdown_scripts()
-                            self.overridden_ifupdown_scripts.extend(script_override)
-                        except moduleNotSupported, e:
-                            self.logger.info('module %s not loaded (%s)\n'
-                                             %(mname, str(e)))
-                            continue
-                        except:
-                            raise
-                        self.modules[mname] = minstance
-                        try:
-                            self.module_attrs[mname] = minstance.get_modinfo()
-                        except:
-                            pass
-        except: 
-            raise
-
-        # Assign all modules to query operations
-        self.module_ops['query-checkcurr'] = self.modules.keys()
-        self.module_ops['query-running'] = self.modules.keys()
-        self.module_ops['query-dependency'] = self.modules.keys()
-        self.module_ops['query'] = self.modules.keys()
-        self.module_ops['query-raw'] = self.modules.keys()
-
-
-    def _modules_help(self):
-        """ Prints addon modules supported syntax """
-
-        indent = '  '
-        for m, mdict in self.module_attrs.items():
-            if not mdict:
-                continue
-            print('%s: %s' %(m, mdict.get('mhelp')))
-            attrdict = mdict.get('attrs')
-            if not attrdict:
-                continue
-            try:
-                for attrname, attrvaldict in attrdict.items():
-                    if attrvaldict.get('compat', False):
-                        continue
-                    print('%s%s' %(indent, attrname))
-                    print('%shelp: %s' %(indent + '  ',
-                          attrvaldict.get('help', '')))
-                    print ('%srequired: %s' %(indent + '  ',
-                            attrvaldict.get('required', False)))
-                    default = attrvaldict.get('default')
-                    if default:
-                        print('%sdefault: %s' %(indent + '  ', default))
-
-                    validrange = attrvaldict.get('validrange')
-                    if validrange:
-                        print('%svalidrange: %s-%s'
-                              %(indent + '  ', validrange[0], validrange[1]))
-
-                    validvals = attrvaldict.get('validvals')
-                    if validvals:
-                        print('%svalidvals: %s'
-                              %(indent + '  ', ','.join(validvals)))
-
-                    examples = attrvaldict.get('example')
-                    if not examples:
-                        continue
-
-                    print '%sexample:' %(indent + '  ')
-                    for e in examples:
-                        print '%s%s' %(indent + '    ', e)
-            except:
-                pass
-            print ''
-            
-    def load_scripts(self, modules_dir):
-        """ loading user modules from /etc/network/.
-
-        Note that previously loaded python modules override modules found
-        under /etc/network if any
-
-        """
-
-        self.logger.info('looking for user scripts under %s' %modules_dir)
-        for op, mlist in self.script_ops.items():
-            msubdir = modules_dir + '/if-%s.d' %op
-            self.logger.info('loading scripts under %s ...' %msubdir)
-            try:
-                module_list = os.listdir(msubdir)
-                for module in module_list:
-                    if self.modules.get(module) or module in self.overridden_ifupdown_scripts:
-                        continue
-                    self.script_ops[op].append(msubdir + '/' + module)
-            except: 
-                # continue reading
-                pass
-
-    def _sched_ifaces(self, ifacenames, ops, skipupperifaces=False,
-                      followdependents=True, sort=False):
-        self.logger.debug('scheduling \'%s\' for %s'
-                          %(str(ops), str(ifacenames)))
-        self._pretty_print_ordered_dict('dependency graph',
-                    self.dependency_graph)
-        ifaceScheduler.sched_ifaces(self, ifacenames, ops,
-                        dependency_graph=self.dependency_graph,
-                        order=ifaceSchedulerFlags.INORDER
-                            if 'down' in ops[0]
-                                else ifaceSchedulerFlags.POSTORDER,
-                        followdependents=followdependents,
-                        skipupperifaces=skipupperifaces,
-                        sort=True if (sort or ifupdownflags.flags.CLASS) else False)
-        return ifaceScheduler.get_sched_status()
-
-    def _render_ifacename(self, ifacename):
-        new_ifacenames = []
-        vlan_match = re.match("^([\d]+)-([\d]+)", ifacename)
-        if vlan_match:
-            vlan_groups = vlan_match.groups()
-            if vlan_groups[0] and vlan_groups[1]:
-                [new_ifacenames.append('%d' %v)
-                    for v in range(int(vlan_groups[0]),
-                            int(vlan_groups[1])+1)]
-        return new_ifacenames
-
-    def _preprocess_ifacenames(self, ifacenames):
-        """ validates interface list for config existance.
-       
-        returns -1 if one or more interface not found. else, returns 0
-
-        """
-        new_ifacenames = []
-        err_iface = ''
-        for i in ifacenames:
-            ifaceobjs = self.get_ifaceobjs(i)
-            if not ifaceobjs:
-                # if name not available, render interface name and check again
-                rendered_ifacenames = utils.expand_iface_range(i)
-                if rendered_ifacenames:
-                    for ri in rendered_ifacenames:
-                        ifaceobjs = self.get_ifaceobjs(ri)
-                        if not ifaceobjs:
-                            err_iface += ' ' + ri
-                        else:
-                            new_ifacenames.append(ri)
-                else:
-                    err_iface += ' ' + i
-            else:
-                new_ifacenames.append(i)
-        if err_iface:
-            raise Exception('cannot find interfaces:%s' %err_iface)
-        return new_ifacenames 
-
-    def _iface_whitelisted(self, auto, allow_classes, excludepats, ifacename):
-        """ Checks if interface is whitelisted depending on set of parameters.
-
-        interfaces are checked against the allow_classes and auto lists.
-
-        """
-
-        ret = True
-
-           # Check if interface matches the exclude patter
-        if excludepats:
-            for e in excludepats:
-                if re.search(e, ifacename):
-                    ret = False
-        ifaceobjs = self.get_ifaceobjs(ifacename)
-        if not ifaceobjs:
-            if ret:
-                self.logger.debug('iface %s' %ifacename + ' not found')
-            return ret
-        # If matched exclude pattern, return false
-        if not ret:
-            for i in ifaceobjs:
-                i.blacklisted = True
-                self.blacklisted_ifaces_present = True
-            return ret
-        # Check if interface belongs to the class
-        # the user is interested in, if not return false
-        if allow_classes:
-            ret = False
-            for i in ifaceobjs:
-                if i.classes:
-                    common = Set([allow_classes]).intersection(
-                                Set(i.classes))
-                    if common:
-                        ret = True
-            if not ret:
-                # If a class was requested and interface does not belong
-                # to the class, only then mark the ifaceobjs as blacklisted
-                self.blacklisted_ifaces_present = True
-                for i in ifaceobjs:
-                    i.blacklisted = True
-            return ret
-        # If the user has requested auto class, check if the interface
-        # is marked auto
-        if auto:
-            ret = False
-            for i in ifaceobjs:
-                if i.auto:
-                    ret = True
-            if not ret:
-                # If auto was requested and interface was not marked auto,
-                # only then mark all of them as blacklisted
-                self.blacklisted_ifaces_present = True
-                for i in ifaceobjs:
-                    i.blacklisted = True
-        return ret
-
-    def _compat_conv_op_to_mode(self, op):
-        """ Returns old op name to work with existing scripts """
-        if 'up' in op:
-            return 'start'
-        elif 'down' in op:
-            return 'stop'
-        else:
-            return op
-
-    def generate_running_env(self, ifaceobj, op):
-        """ Generates a dictionary with env variables required for
-        an interface. Used to support script execution for interfaces.
-        """
-
-        cenv = None
-        iface_env = ifaceobj.get_env()
-        if iface_env:
-            cenv = os.environ
-            if cenv:
-                cenv.update(iface_env)
-            else:
-                cenv = iface_env
-        else:
-            cenv = {}
-        cenv['MODE'] = self._compat_conv_op_to_mode(op)
-        cenv['PHASE'] = op
-
-        return cenv
-
-    def _save_state(self):
-        if (not self.flags.STATEMANAGER_ENABLE or
-            not self.flags.STATEMANAGER_UPDATE):
-            return
-        try:
-            # Update persistant iface states
-            self.statemanager.save_state()
-        except Exception, e:
-            if self.logger.isEnabledFor(logging.DEBUG):
-                t = sys.exc_info()[2]
-                traceback.print_tb(t)
-                self.logger.warning('error saving state (%s)' %str(e))
-
-    def set_type(self, type):
-        if type == 'iface':
-            self.type = ifaceType.IFACE
-        elif type == 'vlan':
-            self.type = ifaceType.BRIDGE_VLAN
-        else:
-            self.type = ifaceType.UNKNOWN
-
-    def _process_delay_admin_state_queue(self, op):
-        if not self._delay_admin_state_iface_queue:
-           return
-        if op == 'up':
-           func = self.link_up
-        elif op == 'down':
-           func = self.link_down
-        else:
-           return
-        for i in self._delay_admin_state_iface_queue:
-            try:
-                if self.link_exists(i):
-                   func(i)
-            except Exception, e:
-                self.logger.warn(str(e))
-                pass
-
-    def up(self, ops, auto=False, allow_classes=None, ifacenames=None,
-           excludepats=None, printdependency=None, syntaxcheck=False,
-           type=None, skipupperifaces=False):
-        """This brings the interface(s) up
-        
-        Args:
-            ops (list): list of ops to perform on the interface(s).
-            Eg: ['pre-up', 'up', 'post-up'
-
-        Kwargs:
-            auto (bool): act on interfaces marked auto
-            allow_classes (list): act on interfaces belonging to classes in the list
-            ifacenames (list): act on interfaces specified in this list
-            excludepats (list): list of patterns of interfaces to exclude
-            syntaxcheck (bool): only perform syntax check
-        """
-
-        self.set_type(type)
-
-        if allow_classes:
-            ifupdownflags.flags.CLASS = True
-        if not self.flags.ADDONS_ENABLE:
-            self.flags.STATEMANAGER_UPDATE = False
-        if auto:
-            ifupdownflags.flags.ALL = True
-            ifupdownflags.flags.WITH_DEPENDS = True
-        try:
-            iface_read_ret = self.read_iface_config()
-        except Exception:
-            raise
-
-        if ifacenames:
-            ifacenames = self._preprocess_ifacenames(ifacenames)
-
-        # if iface list not given by user, assume all from config file
-        if not ifacenames: ifacenames = self.ifaceobjdict.keys()
-
-        # filter interfaces based on auto and allow classes
-        filtered_ifacenames = [i for i in ifacenames
-                               if self._iface_whitelisted(auto, allow_classes,
-                                                excludepats, i)]
-        if not filtered_ifacenames:
-            raise Exception('no ifaces found matching given allow lists')
-
-        if printdependency:
-            self.populate_dependency_info(ops, filtered_ifacenames)
-            self.print_dependency(filtered_ifacenames, printdependency)
-            return
-        else:
-            self.populate_dependency_info(ops)
-
-        # If only syntax check was requested, return here.
-        # return here because we want to make sure most
-        # errors above are caught and reported.
-        if syntaxcheck:
-            if not self._module_syntax_check(filtered_ifacenames):
-                raise Exception()
-            if not iface_read_ret:
-                raise Exception()
-            elif self._any_iface_errors(filtered_ifacenames):
-                raise Exception()
-            return
-
-        ret = None
-        try:
-            ret = self._sched_ifaces(filtered_ifacenames, ops,
-                                     skipupperifaces=skipupperifaces,
-                                     followdependents=True
-                                     if ifupdownflags.flags.WITH_DEPENDS
-                                     else False)
-        finally:
-            self._process_delay_admin_state_queue('up')
-            if not ifupdownflags.flags.DRYRUN and self.flags.ADDONS_ENABLE:
-                self._save_state()
-
-        if not iface_read_ret or not ret:
-            raise Exception()
-
-    def down(self, ops, auto=False, allow_classes=None, ifacenames=None,
-             excludepats=None, printdependency=None, usecurrentconfig=False,
-             type=None):
-        """ down an interface """
-
-        self.set_type(type)
-
-        if allow_classes:
-            ifupdownflags.flags.CLASS = True
-        if not self.flags.ADDONS_ENABLE:
-            self.flags.STATEMANAGER_UPDATE = False
-        if auto:
-            ifupdownflags.flags.ALL = True
-            ifupdownflags.flags.WITH_DEPENDS = True
-        # For down we need to look at old state, unless usecurrentconfig
-        # is set
-        if (not usecurrentconfig and self.flags.STATEMANAGER_ENABLE and
-                    self.statemanager.ifaceobjdict):
-            # Since we are using state manager objects,
-            # skip the updating of state manager objects
-            self.logger.debug('Looking at old state ..')
-            self.read_old_iface_config()
-        else:
-            # If no old state available 
-            try:
-                self.read_iface_config()
-            except Exception, e:
-                raise Exception('error reading iface config (%s)' %str(e))
-        if ifacenames:
-            # If iface list is given by the caller, always check if iface
-            # is present
-            try:
-               ifacenames = self._preprocess_ifacenames(ifacenames)
-            except Exception, e:
-               raise Exception('%s' %str(e) +
-                       ' (interface was probably never up ?)')
-
-        # if iface list not given by user, assume all from config file
-        if not ifacenames: ifacenames = self.ifaceobjdict.keys()
-
-        # filter interfaces based on auto and allow classes
-        filtered_ifacenames = [i for i in ifacenames
-                               if self._iface_whitelisted(auto, allow_classes,
-                                                excludepats, i)]
-        if not filtered_ifacenames:
-            raise Exception('no ifaces found matching given allow lists ' +
-                    '(or interfaces were probably never up ?)')
-
-        if printdependency:
-            self.populate_dependency_info(ops, filtered_ifacenames)
-            self.print_dependency(filtered_ifacenames, printdependency)
-            return
-        else:
-            self.populate_dependency_info(ops)
-
-        try:
-            self._sched_ifaces(filtered_ifacenames, ops,
-                               followdependents=True
-                               if ifupdownflags.flags.WITH_DEPENDS else False)
-        finally:
-            self._process_delay_admin_state_queue('down')
-            if not ifupdownflags.flags.DRYRUN and self.flags.ADDONS_ENABLE:
-                self._save_state()
-
-    def query(self, ops, auto=False, format_list=False, allow_classes=None,
-              ifacenames=None,
-              excludepats=None, printdependency=None,
-              format='native', type=None):
-        """ query an interface """
-
-        self.set_type(type)
-
-        # Let us forget internal squashing when it comes to 
-        # ifquery. It can surprise people relying of ifquery
-        # output
-        self._ifaceobj_squash_internal = False
-
-        if allow_classes:
-            ifupdownflags.flags.CLASS = True
-        if self.flags.STATEMANAGER_ENABLE and ops[0] == 'query-savedstate':
-            return self.statemanager.dump_pretty(ifacenames)
-        self.flags.STATEMANAGER_UPDATE = False
-        if auto:
-            self.logger.debug('setting flag ALL')
-            ifupdownflags.flags.ALL = True
-            ifupdownflags.flags.WITH_DEPENDS = True
-
-        if ops[0] == 'query-syntax':
-            self._modules_help()
-            return
-        elif ops[0] == 'query-running':
-            # create fake devices to all dependents that dont have config
-            map(lambda i: self.create_n_save_ifaceobj(i,
-                                ifacePrivFlags(False, True)), ifacenames)
-        else:
-            try:
-                self.read_iface_config()
-            except Exception:
-                raise
-
-        if ifacenames and ops[0] != 'query-running':
-           # If iface list is given, always check if iface is present
-           ifacenames = self._preprocess_ifacenames(ifacenames)
-
-        # if iface list not given by user, assume all from config file
-        if not ifacenames: ifacenames = self.ifaceobjdict.keys()
-
-        # filter interfaces based on auto and allow classes
-        if ops[0] == 'query-running':
-            filtered_ifacenames = ifacenames
-        else:
-            filtered_ifacenames = [i for i in ifacenames
-                if self._iface_whitelisted(auto, allow_classes,
-                        excludepats, i)]
-        if not filtered_ifacenames:
-                raise Exception('no ifaces found matching ' +
-                        'given allow lists')
-
-        self.populate_dependency_info(ops)
-        if ops[0] == 'query-dependency' and printdependency:
-            self.print_dependency(filtered_ifacenames, printdependency)
-            return
-
-        if format_list and (ops[0] == 'query' or ops[0] == 'query-raw'):
-            return self.print_ifaceobjs_list(filtered_ifacenames)
-
-        if ops[0] == 'query' and not ifupdownflags.flags.WITHDEFAULTS:
-            return self.print_ifaceobjs_pretty(filtered_ifacenames, format)
-        elif ops[0] == 'query-raw':
-            return self.print_ifaceobjs_raw(filtered_ifacenames)
-
-        ret = self._sched_ifaces(filtered_ifacenames, ops,
-                           followdependents=True
-                           if ifupdownflags.flags.WITH_DEPENDS else False)
-
-        if ops[0] == 'query' and ifupdownflags.flags.WITHDEFAULTS:
-            return self.print_ifaceobjs_pretty(filtered_ifacenames, format)
-        elif ops[0] == 'query-checkcurr':
-            ret = self.print_ifaceobjscurr_pretty(filtered_ifacenames, format)
-            if ret != 0:
-                # if any of the object has an error, signal that silently
-                raise Exception('')
-        elif ops[0] == 'query-running':
-            self.print_ifaceobjsrunning_pretty(filtered_ifacenames, format)
-            return
-
-    def _reload_currentlyup(self, upops, downops, auto=False, allow=None,
-            ifacenames=None, excludepats=None, usecurrentconfig=False,
-            syntaxcheck=False, **extra_args):
-        """ reload currently up interfaces """
-        new_ifaceobjdict = {}
-
-        self.logger.info('reloading interfaces that are currently up ..')
-
-        try:
-            iface_read_ret = self.read_iface_config()
-        except:
-            raise
-        if not self.ifaceobjdict:
-            self.logger.warn("nothing to reload ..exiting.")
-            return
-        already_up_ifacenames = []
-        if not ifacenames: ifacenames = self.ifaceobjdict.keys()
-
-        if (not usecurrentconfig and self.flags.STATEMANAGER_ENABLE
-                and self.statemanager.ifaceobjdict):
-            already_up_ifacenames = self.statemanager.ifaceobjdict.keys()
-
-        # Get already up interfaces that still exist in the interfaces file
-        already_up_ifacenames_not_present = Set(
-                        already_up_ifacenames).difference(ifacenames)
-        already_up_ifacenames_still_present = Set(
-                        already_up_ifacenames).difference(
-                        already_up_ifacenames_not_present)
-
-        interfaces_to_up = already_up_ifacenames_still_present
-
-        # generate dependency graph of interfaces
-        self.populate_dependency_info(upops, interfaces_to_up)
-
-        # If only syntax check was requested, return here.
-        # return here because we want to make sure most
-        # errors above are caught and reported.
-        if syntaxcheck:
-            if not self._module_syntax_check(interfaces_to_up):
-                raise Exception()
-            if not iface_read_ret:
-                raise Exception()
-            elif self._any_iface_errors(interfaces_to_up):
-                raise Exception()
-            return
-
-        if (already_up_ifacenames_not_present and
-                self.config.get('ifreload_currentlyup_down_notpresent') == '1'):
-           self.logger.info('reload: schedule down on interfaces: %s'
-                            %str(already_up_ifacenames_not_present))
-
-           # Save a copy of new iface objects and dependency_graph
-           new_ifaceobjdict = dict(self.ifaceobjdict)
-           new_dependency_graph = dict(self.dependency_graph)
-
-           # old interface config is read into self.ifaceobjdict
-           self.read_old_iface_config()
-
-           # reinitialize dependency graph 
-           self.dependency_graph = OrderedDict({})
-           falready_up_ifacenames_not_present = [i for i in
-                                    already_up_ifacenames_not_present
-                                    if self._iface_whitelisted(auto, allow,
-                                    excludepats, i)]
-           self.populate_dependency_info(downops,
-                                         falready_up_ifacenames_not_present)
-           self._sched_ifaces(falready_up_ifacenames_not_present, downops,
-                              followdependents=False, sort=True)
-        else:
-           self.logger.info('no interfaces to down ..')
-
-        # Now, run 'up' with new config dict
-        # reset statemanager update flag to default
-        if auto:
-            ifupdownflags.flags.ALL = True
-            ifupdownflags.flags.WITH_DEPENDS = True
-        if new_ifaceobjdict:
-            # and now, ifaceobjdict is back to current config
-            self.ifaceobjdict = new_ifaceobjdict
-            self.dependency_graph = new_dependency_graph
-
-        if not self.ifaceobjdict:
-            self.logger.info('no interfaces to up')
-            return
-        self.logger.info('reload: scheduling up on interfaces: %s'
-                         %str(interfaces_to_up))
-        ret = self._sched_ifaces(interfaces_to_up, upops,
-                                 followdependents=True
-                                 if ifupdownflags.flags.WITH_DEPENDS else False)
-        if ifupdownflags.flags.DRYRUN:
-            return
-        self._save_state()
-
-        if not iface_read_ret or not ret:
-            raise Exception()
-
-    def _reload_default(self, upops, downops, auto=False, allow=None,
-            ifacenames=None, excludepats=None, usecurrentconfig=False,
-            syntaxcheck=False, **extra_args):
-        """ reload interface config """
-        new_ifaceobjdict = {}
-
-        try:
-            iface_read_ret = self.read_iface_config()
-        except:
-            raise
-
-        if not self.ifaceobjdict:
-            self.logger.warn("nothing to reload ..exiting.")
-            return
-
-        if not ifacenames: ifacenames = self.ifaceobjdict.keys()
-        new_filtered_ifacenames = [i for i in ifacenames
-                               if self._iface_whitelisted(auto, allow,
-                               excludepats, i)]
-        # generate dependency graph of interfaces
-        self.populate_dependency_info(upops)
-
-        # If only syntax check was requested, return here.
-        # return here because we want to make sure most
-        # errors above are caught and reported.
-        if syntaxcheck:
-            if not self._module_syntax_check(new_filtered_ifacenames):
-                raise Exception()
-            if not iface_read_ret:
-                raise Exception()
-            elif self._any_iface_errors(new_filtered_ifacenames):
-                raise Exception()
-            return
-
-        if (not usecurrentconfig and self.flags.STATEMANAGER_ENABLE
-                and self.statemanager.ifaceobjdict):
-            # Save a copy of new iface objects and dependency_graph
-            new_ifaceobjdict = dict(self.ifaceobjdict)
-            new_dependency_graph = dict(self.dependency_graph)
-
-            self.ifaceobjdict = OrderedDict({})
-            self.dependency_graph = OrderedDict({})
-
-            # if old state is present, read old state and mark op for 'down'
-            # followed by 'up' aka: reload
-            # old interface config is read into self.ifaceobjdict
-            self.read_old_iface_config()
-            op = 'reload'
-        else:
-            # oldconfig not available, continue with 'up' with new config
-            op = 'up'
-            new_ifaceobjdict = self.ifaceobjdict
-            new_dependency_graph = self.dependency_graph
-
-        if op == 'reload' and ifacenames:
-            ifacenames = self.ifaceobjdict.keys()
-            old_filtered_ifacenames = [i for i in ifacenames
-                               if self._iface_whitelisted(auto, allow,
-                               excludepats, i)]
-
-            # generate dependency graph of old interfaces,
-            # This should make sure built in interfaces are
-            # populated. disable check shared dependents as an optimization.
-            # these are saved interfaces and dependency for these
-            # have been checked before they became part of saved state.
-            try:
-                self.flags.CHECK_SHARED_DEPENDENTS = False 
-                self.populate_dependency_info(upops)
-                self.flags.CHECK_SHARED_DEPENDENTS = True
-            except Exception, e:
-                self.logger.info("error generating dependency graph for "
-                                 "saved interfaces (%s)" %str(e))
-                pass
-            
-            # make sure we pick up built-in interfaces
-            # if config file had 'ifreload_down_changed' variable
-            # set, also look for interfaces that changed to down them
-            down_changed = int(self.config.get('ifreload_down_changed', '1'))
-
-            # Generate the interface down list
-            # Interfaces that go into the down list:
-            #   - interfaces that were present in last config and are not
-            #     present in the new config
-            #   - interfaces that were changed between the last and current
-            #     config
-            ifacedownlist = []
-            for ifname in self.ifaceobjdict.keys():
-                lastifaceobjlist = self.ifaceobjdict.get(ifname)
-                if not self.is_ifaceobj_builtin(lastifaceobjlist[0]):
-                    # if interface is not built-in and is not in
-                    # old filtered ifacenames
-                    if ifname not in old_filtered_ifacenames:
-                        continue
-                objidx = 0
-                # If interface is not present in the new file
-                # append it to the down list
-                newifaceobjlist = new_ifaceobjdict.get(ifname)
-                if not newifaceobjlist:
-                    ifacedownlist.append(ifname)
-                    continue
-                # If ifaceobj was present in the old interfaces file,
-                # and does not have a config in the new interfaces file
-                # but has been picked up as a dependent of another
-                # interface, catch it here. This catches a common error
-                # for example: remove a bond section from the interfaces
-                # file, but leave it around as a bridge port
-                # XXX: Ideally its better to just add it to the
-                # ifacedownlist. But we will be cautious here 
-                # and just print a warning
-                if (self.is_ifaceobj_noconfig(newifaceobjlist[0]) and
-                    not self.is_ifaceobj_builtin(newifaceobjlist[0]) and
-                    lastifaceobjlist[0].is_config_present() and
-                    lastifaceobjlist[0].link_kind):
-                    self.logger.warn('%s: misconfig ? removed but still exists '
-                                     'as a dependency of %s.\nPlease remove '
-                                     'the dependency manually `ifdown %s` if '
-                                     'it is being picked up as part of a regex'
-                                     % (newifaceobjlist[objidx].name,
-                                        str(newifaceobjlist[objidx].upperifaces),
-                                        newifaceobjlist[objidx].name))
-                if not down_changed:
-                    continue
-                if len(newifaceobjlist) != len(lastifaceobjlist):
-                    ifacedownlist.append(ifname)
-                    continue
-
-                # If interface has changed between the current file
-                # and the last installed append it to the down list
-                # compare object list
-                for objidx in range(0, len(lastifaceobjlist)):
-                    oldobj = lastifaceobjlist[objidx]
-                    newobj = newifaceobjlist[objidx]
-                    if not newobj.compare(oldobj):
-                        ifacedownlist.append(ifname)
-                        continue
-
-            if ifacedownlist:
-                self.logger.info('reload: scheduling down on interfaces: %s'
-                                  %str(ifacedownlist))
-                # reinitialize dependency graph 
-                self.dependency_graph = OrderedDict({})
-
-                # Generate dependency info for old config
-                self.flags.CHECK_SHARED_DEPENDENTS = False
-                self.populate_dependency_info(downops, ifacedownlist)
-                self.flags.CHECK_SHARED_DEPENDENTS = True
-
-                try:
-                    # XXX: Hack to skip checking upperifaces during down.
-                    # the dependency list is not complete here
-                    # and we dont want to down the upperiface.
-                    # Hence during reload, set  this to true.
-                    # This is being added to avoid a failure in
-                    # scheduler._check_upperifaces when we are dowing
-                    # a builtin bridge port 
-                    self.flags.SCHED_SKIP_CHECK_UPPERIFACES = True
-                    self._sched_ifaces(ifacedownlist, downops,
-                                       followdependents=False,
-                                       sort=True)
-                except Exception, e:
-                    self.logger.error(str(e))
-                    pass
-                finally:
-                    self.flags.SCHED_SKIP_CHECK_UPPERIFACES = False
-                    self._process_delay_admin_state_queue('down')
-            else:
-                self.logger.info('no interfaces to down ..')
-
-        # Now, run 'up' with new config dict
-        # reset statemanager update flag to default
-        if not new_ifaceobjdict:
-            self.logger.debug('no interfaces to up')
-            return
-
-        if auto:
-            ifupdownflags.flags.ALL = True
-            ifupdownflags.flags.WITH_DEPENDS = True
-        # and now, we are back to the current config in ifaceobjdict
-        self.ifaceobjdict = new_ifaceobjdict
-        self.dependency_graph = new_dependency_graph
-
-        self.logger.info('reload: scheduling up on interfaces: %s'
-                         %str(new_filtered_ifacenames))
-        ifupdownflags.flags.CACHE = True
-        try:
-            ret = self._sched_ifaces(new_filtered_ifacenames, upops,
-                                     followdependents=True
-                                     if ifupdownflags.flags.WITH_DEPENDS
-                                     else False)
-        except Exception, e:
-            ret = None
-            self.logger.error(str(e))
-        finally:
-            self._process_delay_admin_state_queue('up')
-        if ifupdownflags.flags.DRYRUN:
-            return
-        self._save_state()
-
-        if not iface_read_ret or not ret:
-            raise Exception()
-
-    def reload(self, *args, **kargs):
-        """ reload interface config """
-        self.logger.debug('reloading interface config ..')
-        if kargs.get('currentlyup', False):
-            self._reload_currentlyup(*args, **kargs)
-        else:
-            self._reload_default(*args, **kargs)
-
-    def _any_iface_errors(self, ifacenames):
-        for i in ifacenames:
-            ifaceobjs = self.get_ifaceobjs(i)
-            if not ifaceobjs: continue
-            for ifaceobj in ifaceobjs:
-                if (ifaceobj.status == ifaceStatus.NOTFOUND or
-                    ifaceobj.status == ifaceStatus.ERROR):
-                    return True
-        return False
-
-    def _pretty_print_ordered_dict(self, prefix, argdict):
-        outbuf = prefix + ' {\n'
-        for k, vlist in argdict.items():
-            outbuf += '\t%s : %s\n' %(k, str(vlist))
-        self.logger.debug(outbuf + '}')
-
-    def print_dependency(self, ifacenames, format):
-        """ prints iface dependency information """
-
-        if not ifacenames:
-            ifacenames = self.ifaceobjdict.keys()
-        if format == 'list':
-            for k,v in self.dependency_graph.items():
-                print '%s : %s' %(k, str(v))
-        elif format == 'dot':
-            indegrees = {}
-            map(lambda i: indegrees.update({i :
-                self.get_iface_refcnt(i)}),
-                self.dependency_graph.keys())
-            graph.generate_dots(self.dependency_graph, indegrees)
-
-    def print_ifaceobjs_list(self, ifacenames):
-        for i in ifacenames:
-            print i
-
-    def print_ifaceobjs_raw(self, ifacenames):
-        """ prints raw lines for ifaces from config file """
-
-        for i in ifacenames:
-            for ifaceobj in self.get_ifaceobjs(i):
-                if (self.is_ifaceobj_builtin(ifaceobj) or 
-                    not ifaceobj.is_config_present()):
-                    continue
-                ifaceobj.dump_raw(self.logger)
-                print '\n'
-                if (ifupdownflags.flags.WITH_DEPENDS and
-                    not ifupdownflags.flags.ALL):
-                    dlist = ifaceobj.lowerifaces
-                    if not dlist: continue
-                    self.print_ifaceobjs_raw(dlist)
-
-    def _get_ifaceobjs_pretty(self, ifacenames, ifaceobjs, running=False):
-        """ returns iface obj list """
-
-        for i in ifacenames:
-            for ifaceobj in self.get_ifaceobjs(i):
-                if ((not running and self.is_ifaceobj_noconfig(ifaceobj)) or
-                    (running and not ifaceobj.is_config_present() and
-                     not self.is_iface_builtin_byname(i) and
-                     not ifaceobj.upperifaces)):
-                    continue
-                ifaceobjs.append(ifaceobj)
-                if (ifupdownflags.flags.WITH_DEPENDS and
-                    not ifupdownflags.flags.ALL):
-                    dlist = ifaceobj.lowerifaces
-                    if not dlist: continue
-                    self._get_ifaceobjs_pretty(dlist, ifaceobjs, running)
-
-    def print_ifaceobjs_pretty(self, ifacenames, format='native'):
-        """ pretty prints iface in format given by keyword arg format """
-
-        ifaceobjs = []
-        self._get_ifaceobjs_pretty(ifacenames, ifaceobjs)
-        if not ifaceobjs: return
-        if format == 'json':
-            print json.dumps(ifaceobjs, cls=ifaceJsonEncoder,
-                             indent=4, separators=(',', ': '))
-        else:
-            expand = int(self.config.get('ifquery_ifacename_expand_range', '0'))
-            for i in ifaceobjs:
-                if not expand and (i.flags & iface.IFACERANGE_ENTRY):
-                    # print only the first one
-                    if i.flags & iface.IFACERANGE_START:
-                       i.dump_pretty(use_realname=True)
-                else:
-                    i.dump_pretty()
-
-    def _get_ifaceobjscurr_pretty(self, ifacenames, ifaceobjs):
-        ret = 0
-        for i in ifacenames:
-            ifaceobjscurr = self.get_ifaceobjcurr(i)
-            if not ifaceobjscurr: continue
-            for ifaceobj in ifaceobjscurr:
-                if (ifaceobj.status == ifaceStatus.NOTFOUND or
-                    ifaceobj.status == ifaceStatus.ERROR):
-                    ret = 1
-                if self.is_ifaceobj_noconfig(ifaceobj):
-                    continue
-                ifaceobjs.append(ifaceobj)
-                if (ifupdownflags.flags.WITH_DEPENDS and
-                    not ifupdownflags.flags.ALL):
-                    dlist = ifaceobj.lowerifaces
-                    if not dlist: continue
-                    dret = self._get_ifaceobjscurr_pretty(dlist, ifaceobjs)
-                    if dret: ret = 1
-        return ret
-
-    def print_ifaceobjscurr_pretty(self, ifacenames, format='native'):
-        """ pretty prints current running state of interfaces with status.
-
-        returns 1 if any of the interface has an error,
-        else returns 0
-        """
-
-        ifaceobjs = []
-        ret = self._get_ifaceobjscurr_pretty(ifacenames, ifaceobjs)
-        if not ifaceobjs: return
-
-        # override ifaceStatusUserStrs
-        ifaceStatusUserStrs.SUCCESS = self.config.get('ifquery_check_success_str', _success_sym)
-        ifaceStatusUserStrs.ERROR = self.config.get('ifquery_check_error_str', _error_sym)
-        ifaceStatusUserStrs.UNKNOWN = self.config.get('ifquery_check_unknown_str', '')
-        if format == 'json':
-            print json.dumps(ifaceobjs, cls=ifaceJsonEncoderWithStatus,
-                             indent=2, separators=(',', ': '))
-        else:
-            map(lambda i: i.dump_pretty(with_status=True), ifaceobjs)
-        return ret
-
-    def print_ifaceobjsrunning_pretty(self, ifacenames, format='native'):
-        """ pretty prints iface running state """
-
-        ifaceobjs = []
-        self._get_ifaceobjs_pretty(ifacenames, ifaceobjs, running=True)
-        if not ifaceobjs: return
-        if format == 'json':
-            print json.dumps(ifaceobjs, cls=ifaceJsonEncoder, indent=2,
-                       separators=(',', ': '))
-        else:
-            map(lambda i: i.dump_pretty(), ifaceobjs)
-
-    def _dump(self):
-        print 'ifupdown main object dump'
-        print self.pp.pprint(self.modules)
-        print self.pp.pprint(self.ifaceobjdict)
-
-    def _dump_ifaceobjs(self, ifacenames):
-        for i in ifacenames:
-            ifaceobjs = self.get_ifaceobjs(i)
-            for i in ifaceobjs:
-                i.dump(self.logger)
-                print '\n'
diff --git a/ifupdown/netlink.py b/ifupdown/netlink.py
deleted file mode 100644 (file)
index 01a4df2..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2016 Cumulus Networks, Inc. All rights reserved.
-# Author: Julien Fortin, julien@cumulusnetworks.com
-#
-
-try:
-    from ifupdownaddons.utilsbase import utilsBase
-    import ifupdown.ifupdownflags as ifupdownflags
-except ImportError, e:
-    raise ImportError(str(e) + "- required module not found")
-
-
-class Netlink(utilsBase):
-    VXLAN_UDP_PORT = 4789
-
-    def __init__(self, *args, **kargs):
-        utilsBase.__init__(self, *args, **kargs)
-        try:
-            import sys
-            sys.path.insert(0, '/usr/share/ifupdown2/')
-            from nlmanager.nlmanager import NetlinkManager
-            # this should force the use of the local nlmanager
-            self._nlmanager_api = NetlinkManager(extra_debug=False)
-        except Exception as e:
-            self.logger.error('cannot initialize ifupdown2\'s '
-                              'netlink manager: %s' % str(e))
-            raise
-
-    def get_iface_index(self, ifacename):
-        if ifupdownflags.flags.DRYRUN: return
-        try:
-            return self._nlmanager_api.get_iface_index(ifacename)
-        except Exception as e:
-            raise Exception('%s: netlink: %s: cannot get ifindex: %s'
-                            % (ifacename, ifacename, str(e)))
-
-    def link_add_vlan(self, vlanrawdevice, ifacename, vlanid):
-        self.logger.info('%s: netlink: ip link add link %s name %s type vlan id %s'
-                         % (ifacename, vlanrawdevice, ifacename, vlanid))
-        if ifupdownflags.flags.DRYRUN: return
-        ifindex = self.get_iface_index(vlanrawdevice)
-        try:
-            return self._nlmanager_api.link_add_vlan(ifindex, ifacename, vlanid)
-        except Exception as e:
-            raise Exception('netlink: %s: cannot create vlan %s: %s'
-                            % (vlanrawdevice, vlanid, str(e)))
-
-    def link_add_macvlan(self, ifacename, macvlan_ifacename):
-        self.logger.info('%s: netlink: ip link add link %s name %s type macvlan mode private'
-                         % (ifacename, ifacename, macvlan_ifacename))
-        if ifupdownflags.flags.DRYRUN: return
-        ifindex = self.get_iface_index(ifacename)
-        try:
-            return self._nlmanager_api.link_add_macvlan(ifindex, macvlan_ifacename)
-        except Exception as e:
-            raise Exception('netlink: %s: cannot create macvlan %s: %s'
-                            % (ifacename, macvlan_ifacename, str(e)))
-
-    def link_set_updown(self, ifacename, state):
-        self.logger.info('%s: netlink: ip link set dev %s %s'
-                         % (ifacename, ifacename, state))
-        if ifupdownflags.flags.DRYRUN: return
-        try:
-            return self._nlmanager_api.link_set_updown(ifacename, state)
-        except Exception as e:
-            raise Exception('netlink: cannot set link %s %s: %s'
-                            % (ifacename, state, str(e)))
-
-    def link_set_protodown(self, ifacename, state):
-        self.logger.info('%s: netlink: set link %s protodown %s'
-                         % (ifacename, ifacename, state))
-        if ifupdownflags.flags.DRYRUN: return
-        try:
-            return self._nlmanager_api.link_set_protodown(ifacename, state)
-        except Exception as e:
-            raise Exception('netlink: cannot set link %s protodown %s: %s'
-                            % (ifacename, state, str(e)))
-
-    def link_set_master(self, ifacename, master_dev, state=None):
-        self.logger.info('%s: netlink: ip link set dev %s master %s %s'
-                         % (ifacename, ifacename, master_dev,
-                            state if state else ''))
-        if ifupdownflags.flags.DRYRUN: return
-        try:
-            master = 0 if not master_dev else self.get_iface_index(master_dev)
-            return self._nlmanager_api.link_set_master(ifacename, master,
-                                                       state=state)
-        except Exception as e:
-            raise Exception('netlink: %s: cannot set %s master %s: %s'
-                            % (ifacename, ifacename, master_dev, str(e)))
-
-    def link_set_nomaster(self, ifacename, state=None):
-        self.logger.info('%s: netlink: ip link set dev %s nomaster %s'
-                         % (ifacename, ifacename, state if state else ''))
-        if ifupdownflags.flags.DRYRUN: return
-        try:
-            return self._nlmanager_api.link_set_master(ifacename, 0,
-                                                       state=state)
-        except Exception as e:
-            raise Exception('netlink: %s: cannot set %s nomaster: %s'
-                            % (ifacename, ifacename, str(e)))
-
-    def link_add_bridge_vlan(self, ifacename, vlanid):
-        self.logger.info('%s: netlink: bridge vlan add vid %s dev %s'
-                         % (ifacename, vlanid, ifacename))
-        if ifupdownflags.flags.DRYRUN: return
-        ifindex = self.get_iface_index(ifacename)
-        try:
-            return self._nlmanager_api.link_add_bridge_vlan(ifindex, vlanid)
-        except Exception as e:
-            raise Exception('netlink: %s: cannot create bridge vlan %s: %s'
-                            % (ifacename, vlanid, str(e)))
-
-    def link_del_bridge_vlan(self, ifacename, vlanid):
-        self.logger.info('%s: netlink: bridge vlan del vid %s dev %s'
-                         % (ifacename, vlanid, ifacename))
-        if ifupdownflags.flags.DRYRUN: return
-        ifindex = self.get_iface_index(ifacename)
-        try:
-            return self._nlmanager_api.link_del_bridge_vlan(ifindex, vlanid)
-        except Exception as e:
-            raise Exception('netlink: %s: cannot remove bridge vlan %s: %s'
-                            % (ifacename, vlanid, str(e)))
-
-    def link_add_vxlan(self, ifacename, vxlanid, local=None, dstport=VXLAN_UDP_PORT,
-                       group=None, learning='on', ageing=None, physdev=None):
-        cmd = 'ip link add %s type vxlan id %s dstport %s' % (ifacename,
-                                                              vxlanid,
-                                                              dstport)
-
-
-        cmd += ' local %s' % local if local else ''
-        cmd += ' ageing %s' % ageing if ageing else ''
-        cmd += ' remote %s' % group if group else ' noremote'
-        cmd += ' nolearning' if learning == 'off' else ''
-        cmd += ' dev %s' % physdev if physdev else ''
-        self.logger.info('%s: netlink: %s' % (ifacename, cmd))
-        if ifupdownflags.flags.DRYRUN: return
-        try:
-            if physdev:
-                physdev = self.get_iface_index (physdev)
-            return self._nlmanager_api.link_add_vxlan(ifacename,
-                                                      vxlanid,
-                                                      dstport=dstport,
-                                                      local=local,
-                                                      group=group,
-                                                      learning=learning,
-                                                      ageing=ageing,
-                                                      physdev=physdev)
-        except Exception as e:
-            raise Exception('netlink: %s: cannot create vxlan %s: %s'
-                            % (ifacename, vxlanid, str(e)))
-
-netlink = Netlink()
diff --git a/ifupdown/networkinterfaces.py b/ifupdown/networkinterfaces.py
deleted file mode 100644 (file)
index 22058d7..0000000
+++ /dev/null
@@ -1,514 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-# networkInterfaces --
-#    ifupdown network interfaces file parser
-#
-
-import collections
-import logging
-import glob
-import re
-import os
-import copy
-from utils import utils
-from iface import *
-from template import templateEngine
-
-whitespaces = '\n\t\r '
-
-class networkInterfaces():
-    """ debian ifupdown /etc/network/interfaces file parser """
-
-    hotplugs = {}
-    auto_ifaces = []
-    callbacks = {}
-    auto_all = False
-
-    _addrfams = {'inet' : ['static', 'manual', 'loopback', 'dhcp', 'dhcp6', 'tunnel', 'ppp'],
-                 'inet6' : ['static', 'manual', 'loopback', 'dhcp', 'dhcp6', 'tunnel', 'ppp']}
-
-    def __init__(self, interfacesfile='/etc/network/interfaces',
-                 interfacesfileiobuf=None, interfacesfileformat='native',
-                 template_enable='0', template_engine=None,
-                 template_lookuppath=None):
-        """This member function initializes the networkinterfaces parser object.
-
-        Kwargs:
-            **interfacesfile** (str):  path to the interfaces file (default is /etc/network/interfaces)
-
-            **interfacesfileiobuf** (object): interfaces file io stream
-
-            **interfacesfileformat** (str): format of interfaces file (choices are 'native' and 'json'. 'native' being the default)
-
-            **template_engine** (str): template engine name
-
-            **template_lookuppath** (str): template lookup path
-
-        Raises:
-            AttributeError, KeyError """
-
-        self.logger = logging.getLogger('ifupdown.' +
-                    self.__class__.__name__)
-        self.callbacks = {'iface_found' : None,
-                          'validateifaceattr' : None,
-                          'validateifaceobj' : None}
-        self.allow_classes = {}
-        self.interfacesfile = interfacesfile
-        self.interfacesfileiobuf = interfacesfileiobuf
-        self.interfacesfileformat = interfacesfileformat
-        self._filestack = [self.interfacesfile]
-
-        self._template_engine = None
-        self._template_enable = template_enable
-        self._template_engine_name = template_engine
-        self._template_engine_path = template_lookuppath
-
-        self._currentfile_has_template = False
-        self._ws_split_regex = re.compile(r'[\s\t]\s*')
-
-        self.errors = 0
-        self.warns = 0
-
-    @property
-    def _currentfile(self):
-        try:
-            return self._filestack[-1]
-        except:
-            return self.interfacesfile
-
-    def _parse_error(self, filename, lineno, msg):
-        if lineno == -1 or self._currentfile_has_template:
-            self.logger.error('%s: %s' %(filename, msg))
-        else:
-            self.logger.error('%s: line%d: %s' %(filename, lineno, msg))
-        self.errors += 1
-
-    def _parse_warn(self, filename, lineno, msg):
-        if lineno == -1 or self._currentfile_has_template:
-            self.logger.warn('%s: %s' %(filename, msg))
-        else:
-            self.logger.warn('%s: line%d: %s' %(filename, lineno, msg))
-        self.warns += 1
-
-    def _validate_addr_family(self, ifaceobj, lineno=-1):
-        for family in ifaceobj.addr_family:
-            if not self._addrfams.get(family):
-                self._parse_error(self._currentfile, lineno,
-                                  'iface %s: unsupported address family \'%s\''
-                                  % (ifaceobj.name, family))
-                ifaceobj.addr_family = []
-                ifaceobj.addr_method = None
-                return
-            if ifaceobj.addr_method:
-                if ifaceobj.addr_method not in self._addrfams.get(family):
-                    self._parse_error(self._currentfile, lineno,
-                                      'iface %s: unsupported '
-                                      'address method \'%s\''
-                                      % (ifaceobj.name, ifaceobj.addr_method))
-            else:
-                ifaceobj.addr_method = 'static'
-
-    def subscribe(self, callback_name, callback_func):
-        """This member function registers callback functions.
-
-        Args:
-            **callback_name** (str): callback function name (supported names: 'iface_found', 'validateifaceattr', 'validateifaceobj')
-
-            **callback_func** (function pointer): callback function pointer
-
-        Warns on error
-        """
-
-        if callback_name not in self.callbacks.keys():
-            print 'warning: invalid callback ' + callback_name
-            return -1
-
-        self.callbacks[callback_name] = callback_func
-
-    def ignore_line(self, line):
-        l = line.strip(whitespaces)
-        if not l or l[0] == '#':
-            return 1
-        return 0
-
-    def process_allow(self, lines, cur_idx, lineno):
-        allow_line = lines[cur_idx]
-
-        words = re.split(self._ws_split_regex, allow_line)
-        if len(words) <= 1:
-            raise Exception('invalid allow line \'%s\' at line %d'
-                            %(allow_line, lineno))
-
-        allow_class = words[0].split('-')[1]
-        ifacenames = words[1:]
-
-        if self.allow_classes.get(allow_class):
-            for i in ifacenames:
-                self.allow_classes[allow_class].append(i)
-        else:
-                self.allow_classes[allow_class] = ifacenames
-        return 0
-
-    def process_source(self, lines, cur_idx, lineno):
-        # Support regex
-        self.logger.debug('processing sourced line ..\'%s\'' %lines[cur_idx])
-        sourced_file = re.split(self._ws_split_regex, lines[cur_idx], 2)[1]
-        if sourced_file:
-            filenames = glob.glob(sourced_file)
-            if not filenames:
-                if '*' not in sourced_file:
-                    self._parse_warn(self._currentfile, lineno,
-                            'cannot find source file %s' %sourced_file)
-                return 0
-            for f in filenames:
-                self.read_file(f)
-        else:
-            self._parse_error(self._currentfile, lineno,
-                    'unable to read source line')
-        return 0
-
-    def process_auto(self, lines, cur_idx, lineno):
-        auto_ifaces = re.split(self._ws_split_regex, lines[cur_idx])[1:]
-        if not auto_ifaces:
-            self._parse_error(self._currentfile, lineno,
-                    'invalid auto line \'%s\''%lines[cur_idx])
-            return 0
-        for a in auto_ifaces:
-            if a == 'all':
-                self.auto_all = True
-                break
-            r = utils.parse_iface_range(a)
-            if r:
-                if len(r) == 3:
-                    # eg swp1.[2-4], r = "swp1.", 2, 4)
-                    for i in range(r[1], r[2]+1):
-                        self.auto_ifaces.append('%s%d' %(r[0], i))
-                elif len(r) == 4:
-                    for i in range(r[1], r[2]+1):
-                        # eg swp[2-4].100, r = ("swp", 2, 4, ".100")
-                        self.auto_ifaces.append('%s%d%s' %(r[0], i, r[3]))
-            self.auto_ifaces.append(a)
-        return 0
-
-    def _add_to_iface_config(self, ifacename, iface_config, attrname,
-                             attrval, lineno):
-        newattrname = attrname.replace("_", "-")
-        try:
-            if not self.callbacks.get('validateifaceattr')(newattrname,
-                                      attrval):
-                self._parse_error(self._currentfile, lineno,
-                        'iface %s: unsupported keyword (%s)'
-                        %(ifacename, attrname))
-                return
-        except:
-            pass
-        attrvallist = iface_config.get(newattrname, [])
-        if newattrname in ['scope', 'netmask', 'broadcast', 'preferred-lifetime']:
-            # For attributes that are related and that can have multiple
-            # entries, store them at the same index as their parent attribute.
-            # The example of such attributes is 'address' and its related
-            # attributes. since the related attributes can be optional, 
-            # we add null string '' in places where they are optional.
-            # XXX: this introduces awareness of attribute names in
-            # this class which is a violation.
-
-            # get the index corresponding to the 'address'
-            addrlist = iface_config.get('address')
-            if addrlist:
-                # find the index of last address element
-                for i in range(0, len(addrlist) - len(attrvallist) -1):
-                    attrvallist.append('')
-                attrvallist.append(attrval)
-                iface_config[newattrname] = attrvallist
-        elif not attrvallist:
-            iface_config[newattrname] = [attrval]
-        else:
-            iface_config[newattrname].append(attrval)
-
-    def parse_iface(self, lines, cur_idx, lineno, ifaceobj):
-        lines_consumed = 0
-        line_idx = cur_idx
-
-        iface_line = lines[cur_idx].strip(whitespaces)
-        iface_attrs = re.split(self._ws_split_regex, iface_line)
-        ifacename = iface_attrs[1]
-
-        if (not utils.is_ifname_range(ifacename) and
-            utils.check_ifname_size_invalid(ifacename)):
-            self._parse_warn(self._currentfile, lineno,
-                             '%s: interface name too long' %ifacename)
-
-        # in cases where mako is unable to render the template
-        # or incorrectly renders it due to user template
-        # errors, we maybe left with interface names with
-        # mako variables in them. There is no easy way to
-        # recognize and warn about these. In the below check
-        # we try to warn the user of such cases by looking for
-        # variable patterns ('$') in interface names.
-        if '$' in ifacename:
-           self._parse_warn(self._currentfile, lineno,
-                    '%s: unexpected characters in interface name' %ifacename)
-
-        ifaceobj.raw_config.append(iface_line)
-        iface_config = collections.OrderedDict()
-        for line_idx in range(cur_idx + 1, len(lines)):
-            l = lines[line_idx].strip(whitespaces)
-            if self.ignore_line(l) == 1:
-                continue
-            attrs = re.split(self._ws_split_regex, l, 1)
-            if self._is_keyword(attrs[0]):
-                line_idx -= 1
-                break
-            # if not a keyword, every line must have at least a key and value
-            if len(attrs) < 2:
-                self._parse_error(self._currentfile, line_idx,
-                        'iface %s: invalid syntax \'%s\'' %(ifacename, l))
-                continue
-            ifaceobj.raw_config.append(l)
-            attrname = attrs[0]
-            # preprocess vars (XXX: only preprocesses $IFACE for now)
-            attrval = re.sub(r'\$IFACE', ifacename, attrs[1])
-            self._add_to_iface_config(ifacename, iface_config, attrname,
-                                      attrval, line_idx+1)
-        lines_consumed = line_idx - cur_idx
-
-        # Create iface object
-        if ifacename.find(':') != -1:
-            ifaceobj.name = ifacename.split(':')[0]
-        else:
-            ifaceobj.name = ifacename
-
-        ifaceobj.config = iface_config
-        ifaceobj.generate_env()
-
-        try:
-            if iface_attrs[2]:
-                ifaceobj.addr_family.append(iface_attrs[2])
-            ifaceobj.addr_method = iface_attrs[3]
-        except IndexError:
-            # ignore
-            pass
-        self._validate_addr_family(ifaceobj, lineno)
-
-        if self.auto_all or (ifaceobj.name in self.auto_ifaces):
-            ifaceobj.auto = True
-
-        classes = self.get_allow_classes_for_iface(ifaceobj.name)
-        if classes:
-            [ifaceobj.set_class(c) for c in classes]
-        
-        return lines_consumed       # Return next index
-
-    def _create_ifaceobj_clone(self, ifaceobj, newifaceobjname,
-                               newifaceobjtype, newifaceobjflags):
-        ifaceobj_new = copy.deepcopy(ifaceobj)
-        ifaceobj_new.realname = '%s' %ifaceobj.name
-        ifaceobj_new.name = newifaceobjname
-        ifaceobj_new.type = newifaceobjtype
-        ifaceobj_new.flags = newifaceobjflags
-
-        return ifaceobj_new
-
-    def process_iface(self, lines, cur_idx, lineno):
-        ifaceobj = iface()
-        lines_consumed = self.parse_iface(lines, cur_idx, lineno, ifaceobj)
-
-        range_val = utils.parse_iface_range(ifaceobj.name)
-        if range_val:
-            if len(range_val) == 3:
-                for v in range(range_val[1], range_val[2]+1):
-                    ifacename = '%s%d' %(range_val[0], v)
-                    if utils.check_ifname_size_invalid(ifacename):
-                        self._parse_warn(self._currentfile, lineno,
-                                         '%s: interface name too long' %ifacename)
-                    flags = iface.IFACERANGE_ENTRY
-                    if v == range_val[1]:
-                        flags |= iface.IFACERANGE_START
-                    ifaceobj_new = self._create_ifaceobj_clone(ifaceobj,
-                                        ifacename, ifaceobj.type, flags)
-                    self.callbacks.get('iface_found')(ifaceobj_new)
-            elif len(range_val) == 4:
-                for v in range(range_val[1], range_val[2]+1):
-                    ifacename = '%s%d%s' %(range_val[0], v, range_val[3])
-                    if utils.check_ifname_size_invalid(ifacename):
-                        self._parse_warn(self._currentfile, lineno,
-                                         '%s: interface name too long' %ifacename)
-                    flags = iface.IFACERANGE_ENTRY
-                    if v == range_val[1]:
-                        flags |= iface.IFACERANGE_START
-                    ifaceobj_new = self._create_ifaceobj_clone(ifaceobj,
-                                        ifacename, ifaceobj.type, flags)
-                    self.callbacks.get('iface_found')(ifaceobj_new)
-        else:
-            self.callbacks.get('iface_found')(ifaceobj)
-
-        return lines_consumed       # Return next index
-
-    def process_vlan(self, lines, cur_idx, lineno):
-        ifaceobj = iface()
-        lines_consumed = self.parse_iface(lines, cur_idx, lineno, ifaceobj)
-
-        range_val = utils.parse_iface_range(ifaceobj.name)
-        if range_val:
-            if len(range_val) == 3:
-                for v in range(range_val[1], range_val[2]+1):
-                    flags = iface.IFACERANGE_ENTRY
-                    if v == range_val[1]:
-                        flags |= iface.IFACERANGE_START
-                    ifaceobj_new = self._create_ifaceobj_clone(ifaceobj,
-                                        '%s%d' %(range_val[0], v),
-                                        ifaceType.BRIDGE_VLAN, flags)
-                    self.callbacks.get('iface_found')(ifaceobj_new)
-            elif len(range_val) == 4:
-                for v in range(range_val[1], range_val[2]+1):
-                    flags = iface.IFACERANGE_ENTRY
-                    if v == range_val[1]:
-                        flags |= iface.IFACERANGE_START
-                    ifaceobj_new = self._create_ifaceobj_clone(ifaceobj,
-                                        '%s%d%s' %(range_val[0], v, range_val[3]),
-                                        ifaceType.BRIDGE_VLAN,
-                                        flags)
-                    self.callbacks.get('iface_found')(ifaceobj_new)
-        else:
-            ifaceobj.type = ifaceType.BRIDGE_VLAN
-            self.callbacks.get('iface_found')(ifaceobj)
-
-        return lines_consumed       # Return next index
-
-    network_elems = { 'source'      : process_source,
-                      'allow'      : process_allow,
-                      'auto'        : process_auto,
-                      'iface'       : process_iface,
-                      'vlan'       : process_vlan}
-
-    def _is_keyword(self, str):
-        # The additional split here is for allow- keyword
-        if (str in self.network_elems.keys() or
-            str.split('-')[0] == 'allow'):
-            return 1
-        return 0
-
-    def _get_keyword_func(self, str):
-        tmp_str = str.split('-')[0]
-        return self.network_elems.get(tmp_str)
-
-    def get_allow_classes_for_iface(self, ifacename):
-        classes = []
-        for class_name, ifacenames in self.allow_classes.items():
-            if ifacename in ifacenames:
-                classes.append(class_name)
-        return classes
-
-    def process_interfaces(self, filedata):
-
-        # process line continuations
-        filedata = ' '.join(d.strip() for d in filedata.split('\\'))
-
-        line_idx = 0
-        lines_consumed = 0
-        raw_config = filedata.split('\n')
-        lines = [l.strip(whitespaces) for l in raw_config]
-        while (line_idx < len(lines)):
-            if self.ignore_line(lines[line_idx]):
-                line_idx += 1
-                continue
-            words = re.split(self._ws_split_regex, lines[line_idx])
-            if not words:
-                line_idx += 1
-                continue
-            # Check if first element is a supported keyword
-            if self._is_keyword(words[0]):
-                keyword_func = self._get_keyword_func(words[0])
-                lines_consumed = keyword_func(self, lines, line_idx, line_idx+1)
-                line_idx += lines_consumed
-            else:
-                self._parse_error(self._currentfile, line_idx + 1,
-                        'error processing line \'%s\'' %lines[line_idx])
-            line_idx += 1
-        return 0
-
-    def read_filedata(self, filedata):
-        self._currentfile_has_template = False
-        # run through template engine
-        if filedata and '%' in filedata:
-            try:
-                if not self._template_engine:
-                    self._template_engine = templateEngine(
-                        template_engine=self._template_engine_name,
-                        template_enable=self._template_enable,
-                        template_lookuppath=self._template_engine_path)
-                rendered_filedata = self._template_engine.render(filedata)
-                if rendered_filedata is filedata:
-                    self._currentfile_has_template = False
-                else:
-                    self._currentfile_has_template = True
-            except Exception, e:
-                self._parse_error(self._currentfile, -1,
-                                  'failed to render template (%s). Continue without template rendering ...'
-                                  % str(e))
-                rendered_filedata = None
-            if rendered_filedata:
-                self.process_interfaces(rendered_filedata)
-                return
-        self.process_interfaces(filedata)
-
-    def read_file(self, filename, fileiobuf=None):
-        if fileiobuf:
-            self.read_filedata(fileiobuf)
-            return
-        self._filestack.append(filename)
-        self.logger.info('processing interfaces file %s' %filename)
-        try:
-            with open(filename) as f:
-                filedata = f.read()
-        except Exception, e:
-            self.logger.warn('error processing file %s (%s)',
-                             filename, str(e))
-            return
-        self.read_filedata(filedata)
-        self._filestack.pop()
-
-    def read_file_json(self, filename, fileiobuf=None):
-        if fileiobuf:
-            ifacedicts = json.loads(fileiobuf, encoding="utf-8")
-                              #object_hook=ifaceJsonDecoder.json_object_hook)
-        elif filename:
-            self.logger.info('processing interfaces file %s' %filename)
-            with open(filename) as fp:
-                ifacedicts = json.load(fp)
-                            #object_hook=ifaceJsonDecoder.json_object_hook)
-
-        # we need to handle both lists and non lists formats (e.g. {{}})
-        if not isinstance(ifacedicts,list):
-            ifacedicts = [ifacedicts]
-
-        errors = 0
-        for ifacedict in ifacedicts:
-            ifaceobj = ifaceJsonDecoder.json_to_ifaceobj(ifacedict)
-            if ifaceobj:
-                self._validate_addr_family(ifaceobj)
-                if not self.callbacks.get('validateifaceobj')(ifaceobj):
-                    errors += 1
-                self.callbacks.get('iface_found')(ifaceobj)
-        self.errors += errors
-        
-    def load(self):
-        """ This member function loads the networkinterfaces file.
-
-        Assumes networkinterfaces parser object is initialized with the
-        parser arguments
-        """
-        if not self.interfacesfile and not self.interfacesfileiobuf:
-            self.logger.warn('no terminal line stdin used or ')
-            self.logger.warn('no network interfaces file defined.')
-            self.warns += 1
-            return
-
-        if self.interfacesfileformat == 'json':
-            return self.read_file_json(self.interfacesfile,
-                                       self.interfacesfileiobuf)
-        return self.read_file(self.interfacesfile,
-                              self.interfacesfileiobuf)
diff --git a/ifupdown/policymanager.py b/ifupdown/policymanager.py
deleted file mode 100644 (file)
index c624c18..0000000
+++ /dev/null
@@ -1,214 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2015 Cumulus Networks, Inc. All rights reserved.
-#
-#
-'''
-The PolicyManager should be subclassed by addon modules
-to read a JSON policy config file that is later used to
-set defaults:
-
-Initialize: This module defines a list of config file location based
-          on module.  There are defined in the __init__():  All the
-          addon modules need to do is import the policymanager module.
-
-          import ifupdown.policymanager as policymanager
-
-
-Provides: an API to retrieve link attributes based on addon module name,
-          interface name, and attribute.
-
-        The ifupdown.policymanager module provides a global object policymanager_api
-        that can be called like so:
-
-        speed_default = policymanager.policymanager_api.get_default(
-            module_name='ethtool',
-            ifname=ifaceobj.name,
-            attr='link-speed'
-            )
-'''
-
-import json
-import logging
-import glob
-
-class policymanager():
-    def __init__(self):
-        # we should check for these files in order
-        # so that customers can override the /var/lib file settings
-        self.logger = logging.getLogger('ifupdown.' +
-                            self.__class__.__name__)
-
-        # we grab the json files from a known location and make sure that
-        # the defaults_policy is checked first
-        user_files = glob.glob('/etc/network/ifupdown2/policy.d/*.json')
-        # grab the default module files
-        default_files = glob.glob('/var/lib/ifupdown2/policy.d/*.json')
-        # keep an array of defaults indexed by module name
-        self.system_policy_array = {}
-        for filename in default_files:
-            system_array  = {}
-            try:
-                with open(filename, 'r') as fd:
-                    system_array = json.load(fd)
-                self.logger.debug('reading %s system policy defaults config' \
-                                  % filename)
-            except Exception, e:
-                self.logger.info('could not read %s system policy defaults config' \
-                                  % filename)
-                self.logger.info('    exception is %s' % str(e))
-
-            for module in system_array.keys():
-                if self.system_policy_array.has_key(module):
-                    self.logger.debug('warning: overwriting system module %s from file %s' \
-                                      % (module,filename))
-                self.system_policy_array[module] = system_array[module]
-
-        # take care of user defined policy defaults
-        self.user_policy_array = {}
-        for filename in user_files:
-            user_array  = {}
-            try:
-                with open(filename, 'r') as fd:
-                    user_array = json.load(fd)
-                self.logger.debug('reading %s policy user defaults config' \
-                                  % filename)
-            except Exception, e:
-                self.logger.debug('could not read %s user policy defaults config' \
-                                  % filename)
-                self.logger.debug('    exception is %s' % str(e))
-            # customer added module attributes
-            for module in user_array.keys():
-                if self.system_policy_array.has_key(module):
-                    # warn user that we are overriding the system module setting
-                    self.logger.debug('warning: overwriting system with user module %s from file %s' \
-                                      % (module,filename))
-                self.user_policy_array[module] = user_array[module]
-        return
-
-    def get_iface_default(self,module_name=None,ifname=None,attr=None):
-        '''
-        get_iface_default: Addon modules must use one of two types of access methods to
-        the default configs.   In this method, we expect the default to be
-        either in
-            [module]['iface_defaults'][ifname][attr] or
-            [module]['defaults'][attr]
-        We first check the user_policy_array and return that value. But if
-        the user did not specify an override, we use the system_policy_array.
-        '''
-        # make sure we have an index
-        if (not ifname or not attr or not module_name):
-            return None
-
-        val = None
-        # users can specify defaults to override the systemwide settings
-        # look for user specific interface attribute iface_defaults first
-        try:
-            # looks for user specified value
-            val = self.user_policy_array[module_name]['iface_defaults'][ifname][attr]
-            return val
-        except (TypeError, KeyError, IndexError):
-            pass
-        try:
-            # failing that, there may be a user default for all intefaces
-            val = self.user_policy_array[module_name]['defaults'][attr]
-            return val
-        except (TypeError, KeyError, IndexError):
-            pass
-        try:
-            # failing that, look for  system setting for the interface
-            val = self.system_policy_array[module_name]['iface_defaults'][ifname][attr]
-            return val
-        except (TypeError, KeyError, IndexError):
-            pass
-        try:
-            # failing that, look for  system setting for all interfaces
-            val = self.system_policy_array[module_name]['defaults'][attr]
-            return val
-        except (TypeError, KeyError, IndexError):
-            pass
-
-        # could not find any system or user default so return Non
-        return val
-
-    def get_attr_default(self,module_name=None,attr=None):
-        '''
-        get_attr_default: Addon modules must use one of two types of access methods to
-        the default configs.   In this method, we expect the default to be in
-
-        [module]['defaults'][attr] 
-
-        We first check the user_policy_array and return that value. But if
-        the user did not specify an override, we use the system_policy_array.
-        '''
-        if (not attr or not module_name):
-            return None
-        # users can specify defaults to override the systemwide settings
-        # look for user specific attribute defaults first
-        val = None
-        try:
-            # looks for user specified value
-            val = self.user_policy_array[module_name]['defaults'][attr]
-            return val
-        except (TypeError, KeyError, IndexError):
-            pass
-        try:
-            # failing that, look for system setting
-            val = self.system_policy_array[module_name]['defaults'][attr]
-            return val
-        except (TypeError, KeyError, IndexError):
-            pass
-
-        return val
-
-    def get_module_globals(self,module_name=None,attr=None):
-        '''
-        get_module_globals: Addon modules must use one of two types of access methods to
-        the default configs.   In this method, we expect the default to be in
-
-        [module]['module_globals'][attr] 
-
-        We first check the user_policy_array and return that value. But if
-        the user did not specify an override, we use the system_policy_array.
-        '''
-
-        if (not attr or not module_name):
-            return None
-        # users can specify defaults to override the systemwide settings
-        # look for user specific attribute defaults first
-        val = None
-        try:
-            # looks for user specified value
-            val = self.user_policy_array[module_name]['module_globals'][attr]
-            return val
-        except (TypeError, KeyError, IndexError):
-            pass
-        try:
-            # failing that, look for system setting
-            val = self.system_policy_array[module_name]['module_globals'][attr]
-            return val
-        except (TypeError, KeyError, IndexError):
-            pass
-
-        return val
-
-    def get_module_default(self,module_name=None):
-        '''
-        get_module_default: Addon modules can also access the entire config
-        This method returns indexed by "system" and "user": these are the
-        system-wide and user-defined policy arrays for a specific module.
-        '''
-        if not module_name:
-            return None
-        if self.system_policy_array.get(module_name) and \
-           self.user_policy_array.get(module_name):
-            mod_array = {"system":self.system_policy_array[module_name],
-                         "user":self.user_policy_array[module_name]}
-        else:
-            # the module must not have these defined, return None
-            mod_array = None
-
-        return mod_array
-
-
-policymanager_api = policymanager()
diff --git a/ifupdown/rtnetlink.py b/ifupdown/rtnetlink.py
deleted file mode 100644 (file)
index 9b13ad5..0000000
+++ /dev/null
@@ -1,860 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-#
-# Author: Scott Feldman, sfeldma@cumulusnetworks.com
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-#
-from socket import NETLINK_ROUTE, AF_INET, AF_INET6
-from string import printable
-from ipaddr import *
-from ctypes import *
-from netlink import *
-import logging
-
-logger = logging.getLogger(__name__)
-
-#
-# from /usr/include/linux/rtnetlink.h
-#
-
-RTMGRP_LINK = 0x1
-RTMGRP_IPV4_IFADDR = 0x10
-RTMGRP_IPV4_ROUTE = 0x40
-RTMGRP_IPV6_IFADDR = 0x100
-RTMGRP_IPV6_ROUTE = 0x400
-
-RTM_NEWLINK = 16
-RTM_DELLINK = 17
-RTM_GETLINK = 18
-RTM_SETLINK = 19
-RTM_NEWADDR = 20
-RTM_DELADDR = 21
-RTM_GETADDR = 22
-RTM_NEWROUTE = 24
-RTM_DELROUTE = 25
-RTM_GETROUTE = 26
-
-# Definitions used in routing table administration.
-
-class Nlmsg(Structure):
-
-    def _stringify(self):
-        return string_at(addressof(self), sizeof(self))
-
-    def __eq__(self, other):
-        return self._stringify() == other._stringify() and \
-            self.__dict__ == other.__dict__
-
-    def to_rta(self):
-        return Rtattr.from_address(addressof(self) + NLMSG_ALIGN(sizeof(self)))
-
-    def pack_extra(self, extra, addr):
-        memmove(addr, addressof(extra), sizeof(extra))
-        return NLMSG_ALIGN(sizeof(extra))
-
-    def pack_rtas(self, rtas, addr):
-        total_len = 0
-        for rta_type, value in rtas.iteritems():
-            rta = Rtattr.from_address(addr)
-            rta.rta_type = rta_type
-            pack_fn = self.rta_fn(rta_type)
-            rta_len = NLMSG_ALIGN(pack_fn(rta, value))
-            total_len += rta_len
-            addr += rta_len
-        return total_len
-
-    def pack_rtas_new(self, rtas, addr, policy):
-        total_len = 0
-                
-        for rta_type, value in rtas.iteritems():
-            if type(value) == dict:
-               rta = Rtattr.from_address(addr)
-               rta.rta_type = rta_type
-                rta.rta_len = RTA_LENGTH(0)
-                rta_len = NLMSG_ALIGN(rta.rta_len)
-                total_len += rta_len
-                addr += rta_len
-               pack_fn = policy.get(rta_type)
-                rta_len = NLMSG_ALIGN(pack_fn(addr, value))
-
-                rta.rta_len += rta_len
-            else:
-                rta = Rtattr.from_address(addr)
-                rta.rta_type = rta_type
-                pack_fn = policy.get(rta_type)
-                rta_len = NLMSG_ALIGN(pack_fn(rta, value))
-            total_len += rta_len
-            addr += rta_len
-        return total_len
-
-    def rta_linkinfo(self, addr, rtas):
-        total_len = 0
-
-        # Check interface kind 
-        kind = rtas.get(IFLA_INFO_KIND)
-        if kind == 'vlan':
-            data_policy = self.rta_linkinfo_data_vlan_policy()
-        else:
-            data_policy = self.rta_linkinfo_data_macvlan_policy()
-
-        # Pack info kind
-        rta = Rtattr.from_address(addr)
-        rta.rta_type = IFLA_INFO_KIND
-        rta_len = NLMSG_ALIGN(self.rta_string(rta, kind))
-        total_len += rta_len
-        addr += rta_len
-
-        # nest start link info data
-        rta = Rtattr.from_address(addr)
-        rta.rta_type = IFLA_INFO_DATA
-        rta.rta_len = RTA_LENGTH(0)
-        rta_len = NLMSG_ALIGN(rta.rta_len)
-        total_len += rta_len
-        addr += rta_len
-        rta_len = self.pack_rtas_new(rtas.get(IFLA_INFO_DATA), addr,
-                                     data_policy)
-        rta.rta_len += rta_len
-
-        total_len += rta_len
-        addr += rta_len
-
-        return total_len
-
-    def rta_bridge_vlan_info(self, rta, value):
-        if value:
-           data = RTA_DATA(rta)
-           memmove(data, addressof(value), sizeof(value))
-           rta.rta_len = RTA_LENGTH(sizeof(value))
-           return rta.rta_len
-
-    def rta_af_spec(self, addr, rtas):
-        total_len = 0
-
-        # XXX: Check family (Assumes bridge family for now)
-        rta_len = self.pack_rtas_new(rtas, addr,
-                                     self.rta_bridge_af_spec_policy())
-        total_len += rta_len
-        return total_len
-
-    def unpack_rtas(self, which_ones=[]):
-        len = self.nlh.nlmsg_len - NLMSG_LENGTH(sizeof(self))
-        rta = self.to_rta()
-        rtas = {}
-        while RTA_OK(rta, len):
-            rta_type = rta.rta_type
-            if not which_ones or rta_type in which_ones:
-                unpack_fn = self.rta_fn(rta_type)
-                rtas[rta_type] = unpack_fn(rta)
-            len, rta = RTA_NEXT(rta, len)
-        return rtas
-
-    def dump_rtas(self):
-        rtas = self.unpack_rtas()
-        for type, value in rtas.iteritems():
-            print "rta", type, ":", value
-
-    class _IPv6Addr(BigEndianStructure):
-        _fields_ = [
-            ('upper', c_uint64),
-            ('lower', c_uint64),
-        ]
-
-    class _IPv4Addr(BigEndianStructure):
-        _fields_ = [
-            ('addr', c_uint32),
-        ]
-
-    def rta_uint8(self, rta, value=None):
-        data = RTA_DATA(rta)
-        if value:
-            c_uint8.from_address(data).value = value
-            rta.rta_len = RTA_LENGTH(sizeof(c_uint8))
-            return rta.rta_len
-       else:
-            return c_uint8.from_address(data).value
-
-    def rta_uint16(self, rta, value=None):
-        data = RTA_DATA(rta)
-        if value:
-            c_uint16.from_address(data).value = value
-            rta.rta_len = RTA_LENGTH(sizeof(c_uint16))
-            return rta.rta_len
-       else:
-            return c_uint16.from_address(data).value
-
-    def rta_uint32(self, rta, value=None):
-        data = RTA_DATA(rta)
-        if value:
-            c_uint32.from_address(data).value = value
-            rta.rta_len = RTA_LENGTH(sizeof(c_uint32))
-            return rta.rta_len
-        else:
-            return c_uint32.from_address(data).value
-
-    def rta_string(self, rta, value=None):
-        data = RTA_DATA(rta)
-        if value:
-            s = create_string_buffer(value)
-            memmove(data, addressof(s), len(value))
-            rta.rta_len = RTA_LENGTH(len(value))
-            return rta.rta_len
-        else:
-            return c_char_p(data).value
-
-    def rta_addr(self, rta, value=None):
-        data = RTA_DATA(rta)
-        if value:
-            if isinstance(value, IPv4Address):
-                self._IPv4Addr.from_address(data).addr = value._ip
-                rta.rta_len = RTA_LENGTH(sizeof(self._IPv4Addr))
-            elif isinstance(value, IPv6Address):
-                addr = self._IPv6Addr.from_address(data)
-                addr.upper = value._ip >> 64
-                addr.lower = value._ip & 0xffffffffffffffff
-                rta.rta_len = RTA_LENGTH(sizeof(self._IPv6Addr))
-            else:
-                assert(False)
-            return rta.rta_len
-        else:
-            if RTA_PAYLOAD(rta) == 4:
-                addr = c_uint32.__ctype_be__.from_address(data).value
-                addr = IPv4Address(addr)
-            else:
-                addr = self._IPv6Addr.from_address(data)
-                addr = IPv6Address((addr.upper << 64) + addr.lower)
-            return addr
-
-    def rta_uint8_array(self, rta, value=None):
-        data = RTA_DATA(rta)
-        if value:
-            s = (c_uint8 * len(value)).from_buffer_copy(value)
-            memmove(data, addressof(s), len(value))
-            rta.rta_len = RTA_LENGTH(len(value))
-            return rta.rta_len
-        else:
-            array = (c_uint8 * RTA_PAYLOAD(rta))()
-            memmove(array, data, RTA_PAYLOAD(rta))
-            return array
-
-    def rta_uint32_array(self, rta, value=None):
-        if value:
-            assert(False)
-        else:
-            data = RTA_DATA(rta)
-            size = RTA_PAYLOAD(rta) / sizeof(c_uint32)
-            array = (c_uint32 * size)()
-            memmove(array, data, RTA_PAYLOAD(rta))
-            return array
-
-    def rta_multipath(self, rta, value=None):
-        # XXX implement this
-        return None
-
-    def rta_wtf(self, rta, value=None):
-        return None
-
-    def rta_none(self, rta, value=None):
-        return None
-
-    def rta_fn(self, rta_type):
-        return None
-
-
-# rtm_type
-
-RTN_UNSPEC = 0
-RTN_UNICAST = 1            # Gateway or direct route
-RTN_LOCAL = 2              # Accept locally
-RTN_BROADCAST = 3          # Accept locally as broadcast,
-                           # send as broadcast
-RTN_ANYCAST = 4            # Accept locally as broadcast,
-                           # but send as unicast
-RTN_MULTICAST = 5          # Multicast route
-RTN_BLACKHOLE = 6          # Drop
-RTN_UNREACHABLE = 7        # Destination is unreachable
-RTN_PROHIBIT = 8           # Administratively prohibited
-RTN_THROW = 9              # Not in this table
-RTN_NAT = 10               # Translate this address
-RTN_XRESOLVE = 11          # Use external resolver
-RTN_MAX = 11
-
-# rtm_protocol
-
-RTPROT_UNSPEC = 0
-RTPROT_REDIRECT = 1     # Route installed by ICMP redirects;
-                        # not used by current IPv4
-RTPROT_KERNEL = 2       # Route installed by kernel
-RTPROT_BOOT = 3         # Route installed during boot
-RTPROT_STATIC = 4       # Route installed by administrator
-
-# Values of protocol >= RTPROT_STATIC are not interpreted by kernel;
-# they are just passed from user and back as is.
-# It will be used by hypothetical multiple routing daemons.
-# Note that protocol values should be standardized in order to
-# avoid conflicts.
-
-RTPROT_GATED = 8       # Apparently, GateD
-RTPROT_RA = 9          # RDISC/ND router advertisements
-RTPROT_MRT = 10        # Merit MRT
-RTPROT_ZEBRA = 11      # Zebra
-RTPROT_BIRD = 12       # BIRD
-RTPROT_DNROUTED = 13   # DECnet routing daemon
-RTPROT_XORP = 14       # XORP
-RTPROT_NTK = 15        # Netsukuku
-RTPROT_DHCP = 16       # DHCP client
-
-# rtm_scope
-
-# Really it is not scope, but sort of distance to the destination.
-# NOWHERE are reserved for not existing destinations, HOST is our
-# local addresses, LINK are destinations, located on directly attached
-# link and UNIVERSE is everywhere in the Universe.
-
-# Intermediate values are also possible f.e. interior routes
-# could be assigned a value between UNIVERSE and LINK.
-
-RT_SCOPE_UNIVERSE = 0
-# User defined values
-RT_SCOPE_SITE = 200
-RT_SCOPE_LINK = 253
-RT_SCOPE_HOST = 254
-RT_SCOPE_NOWHERE=255
-
-# rtm_flags
-
-RTM_F_NOTIFY = 0x100   # Notify user of route change
-RTM_F_CLONED = 0x200   # This route is cloned
-RTM_F_EQUALIZE = 0x400 # Multipath equalizer: NI
-RTM_F_PREFIX = 0x800   # Prefix addresses
-
-# Reserved table identifiers
-
-RT_TABLE_UNSPEC = 0
-# User defined values
-RT_TABLE_COMPAT = 252
-RT_TABLE_DEFAULT = 253
-RT_TABLE_MAIN = 254
-RT_TABLE_LOCAL = 255
-RT_TABLE_MAX = 0xFFFFFFFF
-
-# Generic structure for encapsulation of optional route information.
-# It is reminiscent of sockaddr, but with sa_family replaced
-# with attribute type.
-
-class Rtattr(Structure):
-
-    _fields_ = [
-        ('rta_len', c_uint16),
-        ('rta_type', c_uint16),
-    ]
-
-# Routing message attributes
-
-RTA_UNSPEC = 0
-RTA_DST = 1
-RTA_SRC = 2
-RTA_IIF = 3
-RTA_OIF = 4
-RTA_GATEWAY = 5
-RTA_PRIORITY = 6
-RTA_PREFSRC = 7
-RTA_METRICS = 8
-RTA_MULTIPATH = 9
-RTA_PROTOINFO = 10        # no longer used
-RTA_FLOW = 11
-RTA_CACHEINFO = 12
-RTA_SESSION = 13          # no longer used
-RTA_MP_ALGO = 14          # no longer used 
-RTA_TABLE = 15
-RTA_MAX = 15
-
-# Macros to handle rtattributes
-
-RTA_ALIGNTO = 4
-def RTA_ALIGN(len):
-    return (len + RTA_ALIGNTO - 1) & ~(RTA_ALIGNTO - 1)
-def RTA_OK(rta, len):
-    return len >= sizeof(Rtattr) and \
-        rta.rta_len >= sizeof(Rtattr) and \
-        rta.rta_len <= len
-def RTA_NEXT(rta, len):
-    cur = RTA_ALIGN(rta.rta_len)
-    rta = Rtattr.from_address(addressof(rta) + cur)
-    return len - cur, rta
-def RTA_LENGTH(len):
-    return len + RTA_ALIGN(sizeof(Rtattr))
-def RTA_SPACE(len):
-    return RTA_ALIGN(RTA_LENGTH(len))
-def RTA_DATA(rta):
-    return addressof(rta) + RTA_LENGTH(0)
-def RTA_PAYLOAD(rta):
-    return rta.rta_len - RTA_LENGTH(0)
-
-RTNH_F_DEAD = 1         # Nexthop is dead (used by multipath)
-RTNH_F_PERVASIVE = 2    # Do recursive gateway lookup
-RTNH_F_ONLINK = 4       # Gateway is forced on link
-
-# Reserved table identifiers
-
-RT_TABLE_UNSPEC = 0
-# User defined values
-RT_TABLE_COMPAT = 252
-RT_TABLE_DEFAULT = 253
-RT_TABLE_MAIN = 254
-RT_TABLE_LOCAL = 255
-RT_TABLE_MAX = 0xFFFFFFFF
-
-class Rtmsg(Nlmsg):
-
-    _fields_ = [
-        ('rtm_family', c_uint8),
-        ('rtm_dst_len', c_uint8),
-        ('rtm_src_len', c_uint8),
-        ('rtm_tos', c_uint8),
-        ('rtm_table', c_uint8),
-        ('rtm_protocol', c_uint8),
-        ('rtm_scope', c_uint8),
-        ('rtm_type', c_uint8),
-        ('rtm_flags', c_uint32),
-    ]
-
-    _table_str = {
-        RT_TABLE_UNSPEC: "unspecified",
-        RT_TABLE_COMPAT: "compat",
-        RT_TABLE_DEFAULT: "default",
-        RT_TABLE_MAIN: "main",
-        RT_TABLE_LOCAL: "local",
-    }
-
-    _proto_str = {
-        RTPROT_UNSPEC: "none",
-        RTPROT_REDIRECT: "redirect",
-        RTPROT_KERNEL: "kernel",
-        RTPROT_BOOT: "boot",
-        RTPROT_STATIC: "static",
-        RTPROT_GATED: "gated",
-        RTPROT_RA: "ra",
-        RTPROT_MRT: "mrtmrt",
-        RTPROT_ZEBRA: "zebra",
-        RTPROT_BIRD: "bird",
-        RTPROT_DNROUTED: "dnrouted",
-        RTPROT_XORP: "xorp",
-        RTPROT_NTK: "ntk",
-        RTPROT_DHCP: "dhcp",
-    }
-
-    _scope_str = {
-        RT_SCOPE_UNIVERSE: "universe",
-        RT_SCOPE_SITE: "site",
-        RT_SCOPE_LINK: "link",
-        RT_SCOPE_HOST: "host",
-        RT_SCOPE_NOWHERE: "nowhere",
-    }
-
-    _type_str = {
-        RTN_UNSPEC: "unspecified",
-        RTN_UNICAST: "unicast",
-        RTN_LOCAL: "local",
-        RTN_BROADCAST: "broadcast",
-        RTN_ANYCAST: "anycast",
-        RTN_MULTICAST: "multicast",
-        RTN_BLACKHOLE: "blackhole",
-        RTN_UNREACHABLE: "unreachable",
-        RTN_PROHIBIT: "prohibit",
-        RTN_THROW: "throw",
-        RTN_NAT: "nat",
-        RTN_XRESOLVE: "xresolve",
-    }
-
-    def dump(self):
-        print 'rtm_family', self.rtm_family
-        print 'rtm_dst_len', self.rtm_dst_len
-        print 'rtm_src_len', self.rtm_src_len
-        print 'rtm_tos', self.rtm_tos
-        print 'rtm_table', self._table_str.get(self.rtm_table, self.rtm_table)
-        print 'rtm_protocol', self._proto_str.get(self.rtm_protocol)
-        print 'rtm_scope', self._scope_str.get(self.rtm_scope)
-        print 'rtm_type', self._type_str.get(self.rtm_type)
-        print 'rtm_flags 0x%08x' % self.rtm_flags
-
-    def rta_fn(self, rta_type):
-        fns = {
-            RTA_DST: self.rta_addr,
-            RTA_SRC: self.rta_addr,
-            RTA_IIF: self.rta_uint32,
-            RTA_OIF: self.rta_uint32,
-            RTA_GATEWAY: self.rta_addr,
-            RTA_PRIORITY: self.rta_uint32,
-            RTA_PREFSRC: self.rta_addr,
-            RTA_METRICS: self.rta_uint32_array,
-            RTA_MULTIPATH: self.rta_multipath,
-            RTA_PROTOINFO: self.rta_none,
-            RTA_FLOW: self.rta_uint32,
-            RTA_CACHEINFO: self.rta_none,
-            RTA_SESSION: self.rta_none,
-            RTA_MP_ALGO: self.rta_none,
-            RTA_TABLE: self.rta_uint32,
-        }
-
-        return fns.get(rta_type)
-
-class Rtgenmsg(Nlmsg):
-
-    _fields_ = [
-        ('rtgen_family', c_uint8),
-    ]
-
-    def dump(self):
-        print 'rtgen_family', self.rtgen_family
-
-# New extended info filters for IFLA_EXT_MASK
-RTEXT_FILTER_VF = (1 << 0)
-
-# passes link level specific information, not dependent
-# on network protocol.
-
-IFLA_UNSPEC = 0
-IFLA_ADDRESS = 1
-IFLA_BROADCAST = 2
-IFLA_IFNAME = 3
-IFLA_MTU = 4
-IFLA_LINK = 5
-IFLA_QDISC = 6
-IFLA_STATS = 7
-IFLA_COST = 8
-IFLA_PRIORITY = 9
-IFLA_MASTER = 10
-IFLA_WIRELESS = 11          # Wireless Extension event - see wireless.h
-IFLA_PROTINFO = 12          # Protocol specific information for a link
-IFLA_TXQLEN = 13
-IFLA_MAP = 14
-IFLA_WEIGHT = 15
-IFLA_OPERSTATE = 16
-IFLA_LINKMODE = 17
-IFLA_LINKINFO = 18
-IFLA_NET_NS_PID = 19
-IFLA_IFALIAS = 20
-IFLA_NUM_VF = 21            # Number of VFs if device is SR-IOV PF
-IFLA_VFINFO_LIST = 22
-IFLA_STATS64 = 23
-IFLA_VF_PORTS = 24
-IFLA_PORT_SELF = 25
-IFLA_AF_SPEC = 26
-IFLA_GROUP = 27             # Group the device belongs to
-IFLA_NET_NS_FD = 28
-IFLA_EXT_MASK = 29          # Extended info mask, VFs, etc
-IFLA_MAX = 29
-
-
-# IFLA_LINKINFO attributes
-IFLA_INFO_UNSPEC = 0
-IFLA_INFO_KIND = 1
-IFLA_INFO_DATA = 2
-IFLA_INFO_XSTATS = 3
-IFLA_INFO_MAX = 4
-
-# IFLA_LINKINFO_DATA attributes for vlan
-IFLA_VLAN_UNSPEC = 0
-IFLA_VLAN_ID = 1
-
-# IFLA_LINKINFO_DATA attributes for macvlan
-IFLA_MACVLAN_UNSPEC = 0
-IFLA_MACVLAN_MODE = 1
-
-# macvlan modes
-MACVLAN_MODE_PRIVATE = 1
-MACVLAN_MODE_VEPA = 2
-MACVLAN_MODE_BRIDGE = 3
-MACVLAN_MODE_PASSTHRU = 4
-
-# BRIDGE IFLA_AF_SPEC attributes
-IFLA_BRIDGE_FLAGS = 0
-IFLA_BRIDGE_MODE = 1
-IFLA_BRIDGE_VLAN_INFO = 2
-
-# BRIDGE_VLAN_INFO flags
-BRIDGE_VLAN_INFO_MASTER = 1
-BRIDGE_VLAN_INFO_PVID = 2
-BRIDGE_VLAN_INFO_UNTAGGED = 4
-
-# Bridge flags
-BRIDGE_FLAGS_MASTER = 1
-BRIDGE_FLAGS_SELF = 2
-
-class BridgeVlanInfo(Structure):
-    _fields_ = [
-        ('flags', c_uint16),
-        ('vid', c_uint16),
-        ('vid_end', c_uint16),
-    ]
-
-class Ifinfomsg(Nlmsg):
-
-    _fields_ = [
-        ('ifi_family', c_uint8),
-        ('__ifi_pad', c_uint8),
-        ('ifi_type', c_uint16),      # ARPHRD_*
-        ('ifi_index', c_int32),      # Link index
-        ('ifi_flags', c_uint32),     # IFF_* flags
-        ('ifi_change', c_uint32),    # IFF_* change mask
-    ]
-
-    def dump(self):
-        print 'ifi_family', self.ifi_family
-        print 'ifi_type', self.ifi_type
-        print 'ifi_index', self.ifi_index
-        print 'ifi_flags 0x%08x' % self.ifi_flags
-        print 'ifi_change 0x%08x' % self.ifi_change
-
-    def rta_linkinfo_data_vlan_policy(self):
-        fns = {
-            IFLA_VLAN_ID : self.rta_uint16,
-        }
-        return fns
-
-    def rta_linkinfo_data_macvlan_policy(self):
-        fns = {
-            IFLA_MACVLAN_MODE : self.rta_uint32,
-        }
-        return fns
-
-    def rta_linkinfo_policy(self):
-        fns = {
-            IFLA_INFO_KIND : self.rta_string,
-            IFLA_INFO_DATA : self.rta_linkinfo_data,
-        }
-        return fns
-
-    def rta_bridge_af_spec_policy(self):
-        # Assume bridge family for now
-        fns = {
-            IFLA_BRIDGE_FLAGS : self.rta_uint16,
-            IFLA_BRIDGE_VLAN_INFO : self.rta_bridge_vlan_info,
-        }
-        return fns
-
-    def rta_policy(self):
-        fns = {
-            IFLA_UNSPEC: self.rta_wtf,
-            IFLA_ADDRESS: self.rta_uint8_array,
-            IFLA_BROADCAST: self.rta_uint8_array,
-            IFLA_IFNAME: self.rta_string,
-            IFLA_MTU: self.rta_uint32,
-            IFLA_LINK: self.rta_uint32,
-            IFLA_QDISC: self.rta_string,
-            IFLA_STATS: self.rta_none,
-            IFLA_COST: self.rta_none,
-            IFLA_PRIORITY: self.rta_none,
-            IFLA_MASTER: self.rta_uint32,
-            IFLA_WIRELESS: self.rta_none,
-            IFLA_PROTINFO: self.rta_none,
-            IFLA_TXQLEN: self.rta_uint32,
-            IFLA_MAP: self.rta_none,
-            IFLA_WEIGHT: self.rta_uint32,
-            IFLA_OPERSTATE: self.rta_uint8,
-            IFLA_LINKMODE: self.rta_uint8,
-            IFLA_LINKINFO: self.rta_linkinfo,
-            IFLA_NET_NS_PID: self.rta_uint32,
-            IFLA_IFALIAS: self.rta_string,
-            IFLA_NUM_VF: self.rta_uint32,
-            IFLA_VFINFO_LIST: self.rta_none,
-            IFLA_STATS64: self.rta_none,
-            IFLA_VF_PORTS: self.rta_none,
-            IFLA_PORT_SELF: self.rta_none,
-            IFLA_AF_SPEC: self.rta_af_spec,
-            IFLA_GROUP: self.rta_none,
-            IFLA_NET_NS_FD: self.rta_none,
-            IFLA_EXT_MASK: self.rta_none,
-        }
-        return fns;
-
-    def rta_fn(self, rta_type):
-        fns = {
-            IFLA_UNSPEC: self.rta_wtf,
-            IFLA_ADDRESS: self.rta_uint8_array,
-            IFLA_BROADCAST: self.rta_uint8_array,
-            IFLA_IFNAME: self.rta_string,
-            IFLA_MTU: self.rta_uint32,
-            IFLA_LINK: self.rta_uint32,
-            IFLA_QDISC: self.rta_string,
-            IFLA_STATS: self.rta_none,
-            IFLA_COST: self.rta_none,
-            IFLA_PRIORITY: self.rta_none,
-            IFLA_MASTER: self.rta_uint32,
-            IFLA_WIRELESS: self.rta_none,
-            IFLA_PROTINFO: self.rta_none,
-            IFLA_TXQLEN: self.rta_uint32,
-            IFLA_MAP: self.rta_none,
-            IFLA_WEIGHT: self.rta_uint32,
-            IFLA_OPERSTATE: self.rta_uint8,
-            IFLA_LINKMODE: self.rta_uint8,
-            IFLA_LINKINFO: self.rta_linkinfo,
-            IFLA_NET_NS_PID: self.rta_uint32,
-            IFLA_IFALIAS: self.rta_string,
-            IFLA_NUM_VF: self.rta_uint32,
-            IFLA_VFINFO_LIST: self.rta_none,
-            IFLA_STATS64: self.rta_none,
-            IFLA_VF_PORTS: self.rta_none,
-            IFLA_PORT_SELF: self.rta_none,
-            IFLA_AF_SPEC: self.rta_af_spec,
-            IFLA_GROUP: self.rta_none,
-            IFLA_NET_NS_FD: self.rta_none,
-            IFLA_EXT_MASK: self.rta_none,
-        }
-        return fns.get(rta_type)
-
-# passes address specific information
-
-# Important comment:
-# IFA_ADDRESS is prefix address, rather than local interface address.
-# It makes no difference for normally configured broadcast interfaces,
-# but for point-to-point IFA_ADDRESS is DESTINATION address,
-# local address is supplied in IFA_LOCAL attribute.
-
-IFA_UNSPEC = 0
-IFA_ADDRESS = 1
-IFA_LOCAL = 2
-IFA_LABEL = 3
-IFA_BROADCAST = 4
-IFA_ANYCAST = 5
-IFA_CACHEINFO = 6
-IFA_MULTICAST = 7
-IFA_MAX = 7
-
-class Ifaddrmsg(Nlmsg):
-
-    _fields_ = [
-        ('ifa_family', c_uint8),
-        ('ifa_prefixlen', c_uint8), # The prefix length
-        ('ifa_flags', c_uint8),     # Flags
-        ('ifa_scope', c_uint8),     # Address scope
-        ('ifa_index', c_uint32),    # Link index
-    ]
-
-    _family_str = {
-        AF_INET: "inet",
-        AF_INET6: "inet6",
-    }
-
-    def dump(self):
-        print 'ifa_family', self.ifa_family
-        print 'ifa_prefixlen', self.ifa_prefixlen
-        print 'ifa_flags 0x%02x' % self.ifa_flags
-        print 'ifa_scope', self.ifa_scope
-        print 'ifa_index', self.ifa_index
-
-    def rta_fn(self, rta_type):
-        fns = {
-            IFA_ADDRESS: self.rta_addr,
-            IFA_LOCAL: self.rta_addr,
-            IFA_LABEL: self.rta_string,
-            IFA_BROADCAST: self.rta_addr,
-            IFA_ANYCAST: self.rta_addr,
-            IFA_CACHEINFO: self.rta_none,
-            IFA_MULTICAST: self.rta_addr,
-        }
-        return fns.get(rta_type)
-
-class RtNetlinkError(Exception):
-
-    def __init__(self, message):
-        Exception.__init__(self, message)
-        logger.error(message)
-
-class RtNetlink(Netlink):
-
-    def __init__(self, pid):
-        Netlink.__init__(self, pid, NETLINK_ROUTE)
-
-    _rt_nlmsg_type_str = {
-        RTM_NEWROUTE: "RTM_NEWROUTE",
-        RTM_DELROUTE: "RTM_DELROUTE",
-        RTM_NEWLINK: "RTM_NEWLINK",
-        RTM_SETLINK: "RTM_SETLINK",
-        RTM_DELLINK: "RTM_DELLINK",
-        RTM_GETLINK: "RTM_GETLINK",
-        RTM_NEWADDR: "RTM_NEWADDR",
-        RTM_DELADDR: "RTM_DELADDR",
-    }
-
-    def _hexdump(self, buf):
-        while buf:
-            chunk = buf[:16]
-            buf = buf[16:]
-            nums = ["%02x" % c for c in chunk]
-            txt = [chr(c) if chr(c) in printable[:-5] else '.' for c in chunk]
-            print " ".join(nums).ljust(48), "".join(txt)
-
-    def dump(self, nlh):
-        nlmsg = self.nlmsg(nlh)
-        print
-        self._hexdump(self.sendbuf[:nlh.nlmsg_len])
-        print
-        nlh.dump()
-        print
-        nlmsg.dump()
-        print
-        nlmsg.dump_rtas()
-
-    def nlmsg(self, nlh):
-        nlmsg_struct = {
-            RTM_NEWROUTE: Rtmsg,
-            RTM_DELROUTE: Rtmsg,
-            RTM_GETROUTE: Rtmsg,
-            RTM_NEWLINK: Ifinfomsg,
-            RTM_SETLINK: Ifinfomsg,
-            RTM_DELLINK: Ifinfomsg,
-            RTM_GETLINK: Rtgenmsg,
-            RTM_NEWADDR: Ifaddrmsg,
-            RTM_DELADDR: Ifaddrmsg,
-            RTM_GETADDR: Rtgenmsg,
-        }
-        nldata = NLMSG_DATA(nlh)
-        nlmsg = nlmsg_struct[nlh.nlmsg_type].from_address(nldata)
-        nlmsg.nlh = nlh
-        return nlmsg
-
-    def _nl_cb(self, nlh):
-#        print "nl cb", self._rt_nlmsg_type_str[nlh.nlmsg_type]
-
-        if nlh.nlmsg_type in self._cbs:
-
-            nlmsg = self.nlmsg(nlh)
-
-            # validate nl length
-            if nlh.nlmsg_len - NLMSG_LENGTH(sizeof(nlmsg)) < 0:
-                raise RtNetlinkError("invalid nl length")
-
-            self._cbs[nlh.nlmsg_type](nlh, nlmsg)
-
-    def bind(self, groups, cbs):
-        self._cbs = cbs
-        Netlink.bind(self, groups, self._nl_cb)
-
-    def request(self, nlmsg_type, flags, extra, rtas={}):
-
-        nlh = Nlmsghdr.from_buffer(self.sendbuf)
-        nlh_p = addressof(nlh)
-
-        seq = self.seq
-        pid = self.pid
-
-        nlh.nlmsg_len = NLMSG_HDRLEN()
-        nlh.nlmsg_type = nlmsg_type
-        nlh.nlmsg_flags = flags
-        nlh.nlmsg_pid = pid
-        nlh.nlmsg_seq = seq
-
-        nlmsg = self.nlmsg(nlh)
-
-        nlh.nlmsg_len += nlmsg.pack_extra(extra, nlh_p + nlh.nlmsg_len)
-        nlh.nlmsg_len += nlmsg.pack_rtas_new(rtas, nlh_p + nlh.nlmsg_len,
-                                             nlmsg.rta_policy())
-        #self.dump(nlh)
-        self.sendall(string_at(nlh_p, nlh.nlmsg_len))
-        self.seq += 1
-
-        token = (pid, seq)
-        return token
diff --git a/ifupdown/rtnetlink_api.py b/ifupdown/rtnetlink_api.py
deleted file mode 100644 (file)
index f9bba10..0000000
+++ /dev/null
@@ -1,237 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-#
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-#
-
-from os import getpid
-from socket import AF_UNSPEC
-from socket import AF_BRIDGE
-from iff import IFF_UP
-from rtnetlink import *
-import os
-import ifupdownmain
-
-class rtnetlinkApi(RtNetlink):
-
-    bind_done = False
-
-    def __init__(self, pid):
-        RtNetlink.__init__(self, pid)
-        self.logger = logging.getLogger('ifupdown.' +
-                            self.__class__.__name__)
-        self.bind(0, None)
-        self.bind_done = True
-        self.ifindexmap = {}
-
-    def do_bind(self):
-        if self.bind_done:
-            return True
-        self.bind(0, None)
-        self.bind_done = True
-
-    def get_ifindex(self, ifname):
-        ifindex = self.ifindexmap.get(ifname)
-        if not ifindex:
-            with open('/sys/class/net/%s/ifindex' %ifname, 'r') as f:
-                ifindex = int(f.read())
-                self.ifindexmap[ifname] = ifindex
-        return ifindex
-
-    def create_vlan(self, link, ifname, vlanid):
-        self.logger.info('rtnetlink: creating vlan interface %s' %ifname)
-        if ifupdownmain.ifupdownFlags.DRYRUN:
-            return
-        try:
-            ifindex = self.get_ifindex(link)
-        except Exception, e:
-            raise Exception('cannot determine ifindex for link %s (%s)'
-                            %(link, str(e)))
-
-        ifm = Ifinfomsg(AF_UNSPEC)
-        rtas = {IFLA_IFNAME: ifname,
-                    IFLA_LINK : ifindex,
-                           IFLA_LINKINFO : {
-                                   IFLA_INFO_KIND : 'vlan',
-                                   IFLA_INFO_DATA : {
-                            IFLA_VLAN_ID : vlanid,
-                        }
-                    }
-               }
-        token = self.request(RTM_NEWLINK,
-                        NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK, ifm, rtas)
-        self.process_wait([token])
-
-    def create_macvlan(self, ifname, link, mode='private'):
-        self.logger.info('rtnetlink: creating macvlan interface %s' %ifname)
-        if ifupdownmain.ifupdownFlags.DRYRUN:
-            return
-        try:
-            ifindex = self.get_ifindex(link)
-        except Exception, e:
-            raise Exception('cannot determine ifindex for link %s (%s)'
-                            %(link, str(e)))
-
-        ifm = Ifinfomsg(AF_UNSPEC)
-        rtas = {IFLA_IFNAME: ifname,
-                    IFLA_LINK : ifindex,
-                           IFLA_LINKINFO : {
-                                   IFLA_INFO_KIND : 'macvlan',
-                           IFLA_INFO_DATA : {
-                           IFLA_MACVLAN_MODE : MACVLAN_MODE_PRIVATE,
-                        }
-                    }
-               }
-        token = self.request(RTM_NEWLINK, NLM_F_CREATE | NLM_F_REQUEST |
-                             NLM_F_ACK, ifm, rtas)
-        self.process_wait([token])
-
-    def link_set(self, ifname, state):
-        flags = 0
-        self.logger.info('rtnetlink: setting link %s %s' %(ifname, state))
-        if ifupdownmain.ifupdownFlags.DRYRUN:
-            return
-
-        if state == "up":
-            flags |= IFF_UP
-        else:
-            flags &= ~IFF_UP
-
-        ifm = Ifinfomsg(AF_UNSPEC, ifi_change=IFF_UP, ifi_flags=flags)
-        rtas = {IFLA_IFNAME: ifname}
-
-        token = self.request(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK, ifm, rtas)
-        self.process_wait([token])
-
-    def link_set_hwaddress(self, ifname, hwaddress):
-        flags = 0
-        self.logger.info('rtnetlink: setting link hwaddress %s %s' %(ifname, hwaddress))
-        if ifupdownmain.ifupdownFlags.DRYRUN:
-            return
-
-        flags &= ~IFF_UP
-        ifm = Ifinfomsg(AF_UNSPEC, ifi_change=IFF_UP)
-        rtas = {IFLA_IFNAME: ifname,
-                IFLA_ADDRESS : str(bytearray([int(a,16) for a in hwaddress.split(':')]))}
-
-        self.logger.info(rtas)
-
-        token = self.request(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK, ifm, rtas)
-        self.process_wait([token])
-
-    def addr_add(self, ifname, address, broadcast=None, peer=None, scope=None,
-                 preferred_lifetime=None):
-        self.logger.info('rtnetlink: setting address')
-        if ifupdownmain.ifupdownFlags.DRYRUN:
-            return
-
-        try:
-            ifindex = self.get_ifindex(link)
-        except Exception, e:
-            raise Exception('cannot determine ifindex for link %s (%s)'
-                            %(link, str(e)))
-        ifa_scope = RT_SCOPE_
-        if scope:
-            if scope == "universe":
-                ifa_scope = RT_SCOPE_UNIVERSE
-            elif scope == "site":
-                ifa_scope = RT_SCOPE_SITE
-            elif scope == "link":
-                ifa_scope = RT_SCOPE_LINK
-            elif scope == "host":
-                ifa_scope = RT_SCOPE_HOST
-            elif scope == "nowhere":
-                ifa_scope = RT_SCOPE_NOWHERE
-        rtas = {IFLA_ADDRESS: ifname}
-
-        ifa = Ifaddrmsg(AF_UNSPEC, ifa_scope=ifa_scope, ifa_index=ifindex)
-
-        token = self.request(RTM_NEWADDR, NLM_F_REQUEST | NLM_F_ACK, ifa, rtas)
-        self.process_wait([token])
-
-    def link_set_many(self, ifname, ifattrs):
-        _ifattr_to_rta_map = {'dev' : IFLA_NAME,
-                              'address' : IFLA_ADDRESS,
-                              'broadcast' : IFLA_BROADCAST,
-                              'mtu' : IFLA_MTU,
-                              'master' : IFLA_MASTER}
-        flags = 0
-        ifi_change = IFF_UP
-        rtas = {}
-        self.logger.info('rtnetlink: setting link %s %s' %(ifname, state))
-        if ifupdownmain.ifupdownFlags.DRYRUN:
-            return
-        if not ifattrs:
-           return
-        state = ifattrs.get('state')
-        if state == 'up':
-            flags |= IFF_UP
-        elif state == 'down':
-            flags &= ~IFF_UP
-        else:
-            ifi_change = 0
-
-        if ifi_change:
-           ifm = Ifinfomsg(AF_UNSPEC, ifi_change=IFF_UP, ifi_flags=flags)
-        else:
-           ifm = Ifinfomsg(AF_UNSPEC)
-
-        for attr, attrval in ifattrs.items():
-            rta_attr = _ifattr_to_rta_map.get(attr)
-            if rta_attr:
-               if attr == 'hwaddress':
-                  rtas[rta_attr] = str(bytearray([int(a,16) for a in attrval.split(':')]))
-               else:
-                  rtas[rta_attr] = attrval
-
-        token = self.request(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK, ifm, rtas)
-        self.process_wait([token])
-
-    def bridge_vlan(self, add=True, vid=None, dev=None, pvid=False,
-                    untagged=False, master=True):
-        flags = 0
-        vflags = 0
-        if not vid or not dev:
-           return
-        self.logger.info('rtnetlink: bridge vlan add vid %s %s %s dev %s %s'
-                         %(vid, 'untagged' if untagged else '',
-                           'pvid' if pvid else '', dev,
-                           'self' if self else ''))
-        if ifupdownmain.ifupdownFlags.DRYRUN:
-            return
-        try:
-            ifindex = self.get_ifindex(dev)
-        except Exception, e:
-            raise Exception('cannot determine ifindex for dev %s (%s)'
-                            %(dev, str(e)))
-        if not master:
-            flags = BRIDGE_FLAGS_SELF
-
-        if pvid:
-           vflags = BRIDGE_VLAN_INFO_PVID
-           vflags |= BRIDGE_VLAN_INFO_UNTAGGED
-        elif untagged:
-           vflags |= BRIDGE_VLAN_INFO_UNTAGGED
-
-        ifm = Ifinfomsg(AF_BRIDGE, ifi_index=ifindex)
-        rtas = {IFLA_AF_SPEC: {
-                    IFLA_BRIDGE_FLAGS: flags,
-                    IFLA_BRIDGE_VLAN_INFO : BridgeVlanInfo(vflags, int(vid), int(vid))
-                  }
-               }
-        if add:
-            token = self.request(RTM_SETLINK,
-                        NLM_F_REQUEST | NLM_F_ACK, ifm, rtas)
-        else:
-            token = self.request(RTM_DELLINK,
-                        NLM_F_REQUEST | NLM_F_ACK, ifm, rtas)
-        self.process_wait([token])
-
-    def bridge_vlan_many(self, add=True, vids=[], dev=None, pvid=False,
-                         untagged=False, master=True):
-        for v in vids:
-            self.bridge_vlan_add(add, v, dev, ispvid, isuntagged, master)
-
-rtnl_api = rtnetlinkApi(os.getpid())
diff --git a/ifupdown/scheduler.py b/ifupdown/scheduler.py
deleted file mode 100644 (file)
index cb81287..0000000
+++ /dev/null
@@ -1,591 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-# ifaceScheduler --
-#    interface scheduler
-#
-
-from statemanager import *
-import ifupdown.ifupdownflags as ifupdownflags
-from iface import *
-from graph import *
-from collections import deque
-from collections import OrderedDict
-import logging
-import traceback
-import sys
-from graph import *
-from collections import deque
-from threading import *
-from ifupdownbase import *
-from ifupdown.utils import utils
-from sets import Set
-
-class ifaceSchedulerFlags():
-    """ Enumerates scheduler flags """
-
-    INORDER = 0x1
-    POSTORDER = 0x2
-
-class ifaceScheduler():
-    """ scheduler functions to schedule configuration of interfaces.
-
-    supports scheduling of interfaces serially in plain interface list
-    or dependency graph format.
-
-    """
-
-    _STATE_CHECK = True
-
-    _SCHED_STATUS = True
-
-    @classmethod
-    def get_sched_status(cls):
-        return cls._SCHED_STATUS
-
-    @classmethod
-    def set_sched_status(cls, state):
-        cls._SCHED_STATUS = state
-
-    @classmethod
-    def run_iface_op(cls, ifupdownobj, ifaceobj, op, cenv=None):
-        """ Runs sub operation on an interface """
-        ifacename = ifaceobj.name
-
-        if ifupdownobj.type and ifupdownobj.type != ifaceobj.type:
-            return
-
-        if not ifupdownobj.flags.ADDONS_ENABLE: return
-        if op == 'query-checkcurr':
-            query_ifaceobj=ifupdownobj.create_n_save_ifaceobjcurr(ifaceobj)
-            # If not type bridge vlan and the object does not exist,
-            # mark not found and return
-            if (not ifupdownobj.link_exists(ifaceobj.name) and
-                ifaceobj.type != ifaceType.BRIDGE_VLAN):
-                query_ifaceobj.set_state_n_status(ifaceState.from_str(op),
-                                                  ifaceStatus.NOTFOUND)
-                return
-        for mname in ifupdownobj.module_ops.get(op):
-            m = ifupdownobj.modules.get(mname)
-            err = 0
-            try:
-                if hasattr(m, 'run'):
-                    msg = ('%s: %s : running module %s' %(ifacename, op, mname))
-                    if op == 'query-checkcurr':
-                        # Dont check curr if the interface object was 
-                        # auto generated
-                        if (ifaceobj.priv_flags and
-                            ifaceobj.priv_flags.NOCONFIG):
-                            continue
-                        ifupdownobj.logger.debug(msg)
-                        m.run(ifaceobj, op, query_ifaceobj,
-                              ifaceobj_getfunc=ifupdownobj.get_ifaceobjs)
-                    else:
-                        ifupdownobj.logger.debug(msg)
-                        m.run(ifaceobj, op,
-                              ifaceobj_getfunc=ifupdownobj.get_ifaceobjs)
-            except Exception, e:
-                if not ifupdownobj.ignore_error(str(e)):
-                   err = 1
-                   ifupdownobj.logger.error(str(e))
-                # Continue with rest of the modules
-                pass
-            finally:
-                if err or ifaceobj.status == ifaceStatus.ERROR:
-                    ifaceobj.set_state_n_status(ifaceState.from_str(op),
-                                                ifaceStatus.ERROR)
-                    if 'up' in  op or 'down' in op or 'query-checkcurr' in op:
-                        cls.set_sched_status(False)
-                else:
-                    # Mark success only if the interface was not already
-                    # marked with error
-                    status = (ifaceobj.status
-                              if ifaceobj.status == ifaceStatus.ERROR
-                              else ifaceStatus.SUCCESS)
-                    ifaceobj.set_state_n_status(ifaceState.from_str(op),
-                                                status)
-
-        if ifupdownobj.config.get('addon_scripts_support', '0') == '1':
-            # execute /etc/network/ scripts 
-            os.environ['IFACE'] = ifaceobj.name if ifaceobj.name else ''
-            os.environ['LOGICAL'] = ifaceobj.name if ifaceobj.name else ''
-            os.environ['METHOD'] = ifaceobj.addr_method if ifaceobj.addr_method else ''
-            os.environ['ADDRFAM'] = ','.join(ifaceobj.addr_family) if ifaceobj.addr_family else ''
-            for mname in ifupdownobj.script_ops.get(op, []):
-                ifupdownobj.logger.debug('%s: %s : running script %s'
-                    %(ifacename, op, mname))
-                try:
-                    utils.exec_command(mname, env=cenv)
-                except Exception, e:
-                    ifupdownobj.log_error('%s: %s %s' % (ifacename, op, str(e)))
-
-    @classmethod
-    def run_iface_list_ops(cls, ifupdownobj, ifaceobjs, ops):
-        """ Runs all operations on a list of interface
-            configurations for the same interface
-        """
-
-        # minor optimization. If operation is 'down', proceed only
-        # if interface exists in the system
-        ifacename = ifaceobjs[0].name
-        ifupdownobj.logger.info('%s: running ops ...' %ifacename)
-        if ('down' in ops[0] and
-                ifaceobjs[0].type != ifaceType.BRIDGE_VLAN and
-                not ifupdownobj.link_exists(ifacename)):
-            ifupdownobj.logger.debug('%s: does not exist' %ifacename)
-            # run posthook before you get out of here, so that
-            # appropriate cleanup is done
-            posthookfunc = ifupdownobj.sched_hooks.get('posthook')
-            if posthookfunc:
-                for ifaceobj in ifaceobjs:
-                    ifaceobj.status = ifaceStatus.SUCCESS
-                    posthookfunc(ifupdownobj, ifaceobj, 'down')
-            return 
-        for op in ops:
-            # first run ifupdownobj handlers. This is good enough
-            # for the first object in the list
-            handler = ifupdownobj.ops_handlers.get(op)
-            if handler:
-                try:
-                    handler(ifupdownobj, ifaceobjs[0])
-                except Exception, e:
-                    if not ifupdownobj.link_master_slave_ignore_error(str(e)):
-                       ifupdownobj.logger.warn('%s: %s'
-                                   %(ifaceobjs[0].name, str(e)))
-                    pass
-            for ifaceobj in ifaceobjs:
-                cls.run_iface_op(ifupdownobj, ifaceobj, op,
-                    cenv=ifupdownobj.generate_running_env(ifaceobj, op)
-                        if ifupdownobj.config.get('addon_scripts_support',
-                            '0') == '1' else None)
-        posthookfunc = ifupdownobj.sched_hooks.get('posthook')
-        if posthookfunc:
-            try:
-                [posthookfunc(ifupdownobj, ifaceobj, ops[0])
-                    for ifaceobj in ifaceobjs]
-            except Exception, e:
-                ifupdownobj.logger.warn('%s' %str(e))
-                pass
-
-    @classmethod
-    def _check_upperifaces(cls, ifupdownobj, ifaceobj, ops, parent,
-                           followdependents=False):
-        """ Check if upperifaces are hanging off us and help caller decide
-        if he can proceed with the ops on this device
-
-        Returns True or False indicating the caller to proceed with the
-        operation.
-        """
-        # proceed only for down operation
-        if 'down' not in ops[0]:
-            return True
-
-        if (ifupdownobj.flags.SCHED_SKIP_CHECK_UPPERIFACES):
-            return True
-
-        if (ifupdownflags.flags.FORCE or
-                not ifupdownobj.flags.ADDONS_ENABLE or
-                (not ifupdownobj.is_ifaceobj_noconfig(ifaceobj) and
-                ifupdownobj.config.get('warn_on_ifdown', '0') == '0' and
-                not ifupdownflags.flags.ALL)):
-            return True
-
-        ulist = ifaceobj.upperifaces
-        if not ulist:
-            return True
-
-        # Get the list of upper ifaces other than the parent
-        tmpulist = ([u for u in ulist if u != parent] if parent
-                    else ulist)
-        if not tmpulist:
-            return True
-        # XXX: This is expensive. Find a cheaper way to do this.
-        # if any of the upperdevs are present,
-        # return false to the caller to skip this interface
-        for u in tmpulist:
-            if ifupdownobj.link_exists(u):
-                if not ifupdownflags.flags.ALL:
-                    if ifupdownobj.is_ifaceobj_noconfig(ifaceobj):
-                        ifupdownobj.logger.info('%s: skipping interface down,'
-                            %ifaceobj.name + ' upperiface %s still around ' %u)
-                    else:
-                        ifupdownobj.logger.warn('%s: skipping interface down,'
-                            %ifaceobj.name + ' upperiface %s still around ' %u)
-                return False
-        return True
-
-    @classmethod
-    def run_iface_graph(cls, ifupdownobj, ifacename, ops, parent=None,
-                        order=ifaceSchedulerFlags.POSTORDER,
-                        followdependents=True):
-        """ runs interface by traversing all nodes rooted at itself """
-
-        # Each ifacename can have a list of iface objects
-        ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename)
-        if not ifaceobjs:
-            raise Exception('%s: not found' %ifacename)
-
-        # Check state of the dependent. If it is already brought up, return
-        if (cls._STATE_CHECK and
-            (ifaceobjs[0].state == ifaceState.from_str(ops[-1]))):
-            ifupdownobj.logger.debug('%s: already processed' %ifacename)
-            return
-
-        for ifaceobj in ifaceobjs:
-            if not cls._check_upperifaces(ifupdownobj, ifaceobj,
-                                          ops, parent, followdependents):
-               return
-
-        # If inorder, run the iface first and then its dependents
-        if order == ifaceSchedulerFlags.INORDER:
-            cls.run_iface_list_ops(ifupdownobj, ifaceobjs, ops)
-
-        for ifaceobj in ifaceobjs:
-            # Run lowerifaces or dependents
-            dlist = ifaceobj.lowerifaces
-            if dlist:
-                ifupdownobj.logger.debug('%s: found dependents %s'
-                            %(ifacename, str(dlist)))
-                try:
-                    if not followdependents:
-                        # XXX: this is yet another extra step,
-                        # but is needed for interfaces that are
-                        # implicit dependents. even though we are asked to
-                        # not follow dependents, we must follow the ones
-                        # that dont have user given config. Because we own them
-                        new_dlist = [d for d in dlist
-                                    if ifupdownobj.is_iface_noconfig(d)]
-                        if new_dlist:
-                            cls.run_iface_list(ifupdownobj, new_dlist, ops,
-                                           ifacename, order, followdependents,
-                                           continueonfailure=False)
-                    else:
-                        cls.run_iface_list(ifupdownobj, dlist, ops,
-                                            ifacename, order,
-                                            followdependents,
-                                            continueonfailure=False)
-                except Exception, e:
-                    if (ifupdownobj.ignore_error(str(e))):
-                        pass
-                    else:
-                        # Dont bring the iface up if children did not come up
-                        ifaceobj.set_state_n_status(ifaceState.NEW,
-                                                ifaceStatus.ERROR)
-                        raise
-        if order == ifaceSchedulerFlags.POSTORDER:
-            cls.run_iface_list_ops(ifupdownobj, ifaceobjs, ops)
-
-    @classmethod
-    def run_iface_list(cls, ifupdownobj, ifacenames,
-                       ops, parent=None, order=ifaceSchedulerFlags.POSTORDER,
-                       followdependents=True, continueonfailure=True):
-        """ Runs interface list """
-
-        for ifacename in ifacenames:
-            try:
-              cls.run_iface_graph(ifupdownobj, ifacename, ops, parent,
-                      order, followdependents)
-            except Exception, e:
-                if continueonfailure:
-                    if ifupdownobj.logger.isEnabledFor(logging.DEBUG):
-                        traceback.print_tb(sys.exc_info()[2])
-                    ifupdownobj.logger.error('%s : %s' %(ifacename, str(e)))
-                    pass
-                else:
-                    if (ifupdownobj.ignore_error(str(e))):
-                        pass
-                    else:
-                        raise Exception('%s : (%s)' %(ifacename, str(e)))
-
-    @classmethod
-    def run_iface_graph_upper(cls, ifupdownobj, ifacename, ops, parent=None,
-                        followdependents=True, skip_root=False):
-        """ runs interface by traversing all nodes rooted at itself """
-
-        # Each ifacename can have a list of iface objects
-        ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename)
-        if not ifaceobjs:
-            raise Exception('%s: not found' %ifacename)
-
-        if (cls._STATE_CHECK and
-            (ifaceobjs[0].state == ifaceState.from_str(ops[-1]))):
-            ifupdownobj.logger.debug('%s: already processed' %ifacename)
-            return
-
-        if not skip_root:
-            # run the iface first and then its upperifaces
-            cls.run_iface_list_ops(ifupdownobj, ifaceobjs, ops)
-        for ifaceobj in ifaceobjs:
-            # Run upperifaces
-            ulist = ifaceobj.upperifaces
-            if ulist:
-                ifupdownobj.logger.debug('%s: found upperifaces %s'
-                                            %(ifacename, str(ulist)))
-                try:
-                    cls.run_iface_list_upper(ifupdownobj, ulist, ops,
-                                            ifacename,
-                                            followdependents,
-                                            continueonfailure=True)
-                except Exception, e:
-                    if (ifupdownobj.ignore_error(str(e))):
-                        pass
-                    else:
-                        raise
-
-    @classmethod
-    def run_iface_list_upper(cls, ifupdownobj, ifacenames,
-                       ops, parent=None, followdependents=True,
-                       continueonfailure=True, skip_root=False):
-        """ Runs interface list """
-
-        for ifacename in ifacenames:
-            try:
-              cls.run_iface_graph_upper(ifupdownobj, ifacename, ops, parent,
-                      followdependents, skip_root)
-            except Exception, e:
-                if ifupdownobj.logger.isEnabledFor(logging.DEBUG):
-                    traceback.print_tb(sys.exc_info()[2])
-                ifupdownobj.logger.warn('%s : %s' %(ifacename, str(e)))
-                pass
-
-    @classmethod
-    def _get_valid_upperifaces(cls, ifupdownobj, ifacenames,
-                               allupperifacenames):
-        """ Recursively find valid upperifaces
-
-        valid upperifaces are:
-            - An upperiface which had no user config (example builtin
-              interfaces. usually vlan interfaces.)
-            - or had config and previously up
-            - and interface currently does not exist
-            - or is a bridge (because if your upperiface was a bridge
-            - u will have to execute up on the bridge
-              to enslave the port and apply bridge attributes to the port) """
-
-        upperifacenames = []
-        for ifacename in ifacenames:
-            # get upperifaces
-            ifaceobj = ifupdownobj.get_ifaceobj_first(ifacename)
-            if not ifaceobj:
-               continue
-            ulist = Set(ifaceobj.upperifaces).difference(upperifacenames)
-            nulist = []
-            for u in ulist:
-                uifaceobj = ifupdownobj.get_ifaceobj_first(u)
-                if not uifaceobj:
-                   continue
-                has_config = not (uifaceobj.priv_flags and
-                                  uifaceobj.priv_flags.NOCONFIG)
-                if (((has_config and ifupdownobj.get_ifaceobjs_saved(u)) or
-                     not has_config) and (not ifupdownobj.link_exists(u)
-                         # Do this always for a bridge. Note that this is
-                         # not done for a vlan aware bridge because,
-                         # in the vlan aware bridge case, the bridge module
-                         # applies the bridge port configuration on the port
-                         # when up is scheduled on the port.
-                         or (uifaceobj.link_kind == ifaceLinkKind.BRIDGE))):
-                     nulist.append(u)
-            upperifacenames.extend(nulist)
-        allupperifacenames.extend(upperifacenames)
-        if upperifacenames:
-            cls._get_valid_upperifaces(ifupdownobj, upperifacenames,
-                                       allupperifacenames)
-        return
-
-    @classmethod
-    def run_upperifaces(cls, ifupdownobj, ifacenames, ops,
-                        continueonfailure=True):
-        """ Run through valid upperifaces """ 
-        upperifaces = []
-
-        cls._get_valid_upperifaces(ifupdownobj, ifacenames, upperifaces)
-        if not upperifaces:
-           return
-        # dump valid upperifaces
-        ifupdownobj.logger.debug(upperifaces)
-        for u in upperifaces:
-            try:
-                ifaceobjs = ifupdownobj.get_ifaceobjs(u)
-                if not ifaceobjs:
-                   continue
-                cls.run_iface_list_ops(ifupdownobj, ifaceobjs, ops)
-            except Exception, e:
-                if continueonfailure:
-                    ifupdownobj.logger.warn('%s' %str(e))
-
-    @classmethod
-    def _dump_dependency_info(cls, ifupdownobj, ifacenames,
-                              dependency_graph=None, indegrees=None):
-        ifupdownobj.logger.info('{\n')
-        ifupdownobj.logger.info('\nifaceobjs:')
-        for iname in ifacenames:
-               iobjs = ifupdownobj.get_ifaceobjs(iname)
-               for iobj in iobjs:
-                       iobj.dump(ifupdownobj.logger)
-        if (dependency_graph):
-            ifupdownobj.logger.info('\nDependency Graph:')
-            ifupdownobj.logger.info(dependency_graph)
-           if (indegrees):
-                   ifupdownobj.logger.info('\nIndegrees:')
-                   ifupdownobj.logger.info(indegrees)
-           ifupdownobj.logger.info('}\n')
-
-    @classmethod
-    def get_sorted_iface_list(cls, ifupdownobj, ifacenames, ops,
-                              dependency_graph, indegrees=None):
-        if len(ifacenames) == 1:
-            return ifacenames
-        # Get a sorted list of all interfaces
-        if not indegrees:
-            indegrees = OrderedDict()
-            for ifacename in dependency_graph.keys():
-                indegrees[ifacename] = ifupdownobj.get_iface_refcnt(ifacename)
-
-        #cls._dump_dependency_info(ifupdownobj, ifacenames,
-        #                          dependency_graph, indegrees)
-
-        ifacenames_all_sorted = graph.topological_sort_graphs_all(
-                                        dependency_graph, indegrees)
-        # if ALL was set, return all interfaces
-        if ifupdownflags.flags.ALL:
-            return ifacenames_all_sorted
-
-        # else return ifacenames passed as argument in sorted order
-        ifacenames_sorted = []
-        [ifacenames_sorted.append(ifacename)
-                        for ifacename in ifacenames_all_sorted
-                            if ifacename in ifacenames]
-        return ifacenames_sorted
-
-    @classmethod
-    def sched_ifaces(cls, ifupdownobj, ifacenames, ops,
-                dependency_graph=None, indegrees=None,
-                order=ifaceSchedulerFlags.POSTORDER,
-                followdependents=True, skipupperifaces=False, sort=False):
-        """ runs interface configuration modules on interfaces passed as
-            argument. Runs topological sort on interface dependency graph.
-
-        Args:
-            **ifupdownobj** (object): ifupdownMain object 
-
-            **ifacenames** (list): list of interface names
-
-            **ops** : list of operations to perform eg ['pre-up', 'up', 'post-up']
-
-            **dependency_graph** (dict): dependency graph in adjacency list format
-
-        Kwargs:
-            **indegrees** (dict): indegree array of the dependency graph
-
-            **order** (int): ifaceSchedulerFlags (POSTORDER, INORDER)
-
-            **followdependents** (bool): follow dependent interfaces if true
-
-            **sort** (bool): sort ifacelist in the case where ALL is not set
-
-        """
-        #
-        # Algo:
-        # if ALL/auto interfaces are specified,
-        #   - walk the dependency tree in postorder or inorder depending
-        #     on the operation.
-        #     (This is to run interfaces correctly in order)
-        # else:
-        #   - sort iface list if the ifaces belong to a "class"
-        #   - else just run iface list in the order they were specified
-        #
-        # Run any upperifaces if available
-        #
-        followupperifaces = False
-        run_queue = []
-        skip_ifacesort = int(ifupdownobj.config.get('skip_ifacesort', '0'))
-        if not skip_ifacesort and not indegrees:
-            indegrees = OrderedDict()
-            for ifacename in dependency_graph.keys():
-                indegrees[ifacename] = ifupdownobj.get_iface_refcnt(ifacename)
-
-        if not ifupdownflags.flags.ALL:
-            if 'up' in ops[0]:
-                # If there is any interface that does not exist, maybe it
-                # is a logical interface and we have to followupperifaces
-                # when it comes up, so lets get that list.
-                if any([True for i in ifacenames
-                        if ifupdownobj.must_follow_upperifaces(i)]):
-                    followupperifaces = (True if
-                                    [i for i in ifacenames
-                                        if not ifupdownobj.link_exists(i)]
-                                        else False)
-            # sort interfaces only if the caller asked to sort
-            # and skip_ifacesort is not on.
-            if not skip_ifacesort and sort:
-                run_queue = cls.get_sorted_iface_list(ifupdownobj, ifacenames,
-                                    ops, dependency_graph, indegrees)
-                if run_queue and 'up' in ops[0]:
-                    run_queue.reverse()
-        else:
-            # if -a is set, we pick the interfaces
-            # that have no parents and use a sorted list of those
-            if not skip_ifacesort:
-                sorted_ifacenames = cls.get_sorted_iface_list(ifupdownobj,
-                                            ifacenames, ops, dependency_graph,
-                                            indegrees)
-                if sorted_ifacenames:
-                    # pick interfaces that user asked
-                    # and those that dont have any dependents first
-                    [run_queue.append(ifacename)
-                        for ifacename in sorted_ifacenames
-                            if ifacename in ifacenames and
-                            not indegrees.get(ifacename)]
-                    ifupdownobj.logger.debug('graph roots (interfaces that ' +
-                            'dont have dependents):' + ' %s' %str(run_queue))
-                else:
-                    ifupdownobj.logger.warn('interface sort returned None')
-
-        # If queue not present, just run interfaces that were asked by the
-        # user
-        if not run_queue:
-            run_queue = list(ifacenames)
-            # if we are taking the order of interfaces as specified
-            # in the interfaces file, we should reverse the list if we
-            # want to down. This can happen if 'skip_ifacesort'
-            # is been specified.
-            if 'down' in ops[0]:
-                run_queue.reverse()
-
-        # run interface list
-        cls.run_iface_list(ifupdownobj, run_queue, ops,
-                           parent=None, order=order,
-                           followdependents=followdependents)
-        if not cls.get_sched_status():
-            return
-
-        if (not skipupperifaces and
-                ifupdownobj.config.get('skip_upperifaces', '0') == '0' and
-                ((not ifupdownflags.flags.ALL and followdependents) or
-                followupperifaces) and
-                'up' in ops[0]):
-            # If user had given a set of interfaces to bring up
-            # try and execute 'up' on the upperifaces
-            ifupdownobj.logger.info('running upperifaces (parent interfaces) ' +
-                                    'if available ..')
-            try:
-                # upperiface bring up is best effort.
-                # eg case: if we are bringing up a bridge port
-                # this section does an 'ifup on the bridge'
-                # so that the recently up'ed bridge port gets enslaved
-                # to the bridge. But the up on the bridge may
-                # throw out more errors if the bridge is not
-                # in the correct state. Lets not surprise
-                # the user with such errors when he has
-                # only requested to bring up the bridge port.
-                cls._STATE_CHECK = False
-                ifupdownflags.flags.IGNORE_ERRORS = True
-                cls.run_upperifaces(ifupdownobj, ifacenames, ops)
-            finally:
-                ifupdownflags.flags.IGNORE_ERRORS = False
-                cls._STATE_CHECK = True
-                # upperiface bringup is best effort, so dont propagate errors
-                # reset scheduler status to True
-                cls.set_sched_status(True)
diff --git a/ifupdown/statemanager.py b/ifupdown/statemanager.py
deleted file mode 100644 (file)
index 9eee24f..0000000
+++ /dev/null
@@ -1,188 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-# stateManager --
-#    interface state manager
-#
-import cPickle
-from collections import OrderedDict
-import logging
-import exceptions
-import os
-from iface import *
-
-class pickling():
-    """ class with helper methods for pickling/unpickling iface objects """
-
-    @classmethod
-    def save(cls, filename, list_of_objects):
-        """ pickle a list of iface objects """
-        try:
-            with open(filename, 'w') as f:
-                for obj in list_of_objects:
-                    cPickle.dump(obj, f, cPickle.HIGHEST_PROTOCOL)
-        except:
-            raise
-
-    @classmethod
-    def save_obj(cls, f, obj):
-        """ pickle iface object """
-        try:
-            cPickle.dump(obj, f, cPickle.HIGHEST_PROTOCOL)
-        except:
-            raise
-
-    @classmethod
-    def load(cls, filename):
-        """ load picked iface object """
-        with open(filename, 'r') as f:
-            while True:
-                try: yield cPickle.load(f)
-                except EOFError: break
-                except: raise
-
-class stateManager():
-    """ state manager for managing ifupdown iface obj state
-
-    ifupdown2 has to maitain old objects for down operation on
-    interfaces. ie to down or delete old configuration.
-
-    This class uses pickle to store iface objects.
-
-    """
-
-    state_dir = '/var/tmp/network/'
-    """directory where the state file is stored """
-
-    state_filename = 'ifstatenew'
-    """name of the satefile """
-
-    state_rundir = '/run/network/'
-    """name of the state run dir """
-
-    state_runlockfile = 'ifstatelock'
-    """name of the state run lock file """
-
-    def __init__(self):
-        """ Initializes statemanager internal state
-
-        which includes a dictionary of last pickled iface objects
-        """
-        self.ifaceobjdict = OrderedDict()
-        self.logger = logging.getLogger('ifupdown.' +
-                    self.__class__.__name__)
-        if not os.path.exists(self.state_dir):
-            os.mkdir(self.state_dir)
-        if not os.path.exists(self.state_rundir):
-            os.mkdir(self.state_rundir)
-        self.state_file = self.state_dir + self.state_filename
-
-    def save_ifaceobj(self, ifaceobj):
-        self.ifaceobjdict.setdefault(ifaceobj.name,
-                            []).append(ifaceobj)
-
-    def read_saved_state(self, filename=None):
-        """This member function reads saved iface objects
-
-        Kwargs:
-            filename (str): name of the state file
-        """
-
-        pickle_filename = filename
-        if not pickle_filename:
-            pickle_filename = self.state_file
-        if not os.path.exists(pickle_filename):
-            return
-        for ifaceobj in pickling.load(pickle_filename):
-            self.save_ifaceobj(ifaceobj)
-
-    def get_ifaceobjs(self, ifacename):
-        return self.ifaceobjdict.get(ifacename)
-
-    def ifaceobj_sync(self, ifaceobj, op):
-        """This member function sync's new obj state to old statemanager state
-
-        Args:
-            ifaceobj (object): new iface object
-            op (str): ifupdown operation
-        """
-
-        self.logger.debug('%s: statemanager sync state %s'
-                          %(ifaceobj.name, op))
-        old_ifaceobjs = self.ifaceobjdict.get(ifaceobj.name)
-        if 'up' in op:
-            if not old_ifaceobjs:
-                self.ifaceobjdict[ifaceobj.name] = [ifaceobj]
-            else:
-                # If it matches any of the object, return
-                if any(o.compare(ifaceobj) for o in old_ifaceobjs):
-                    return
-                # If it does not match any of the objects, and if
-                # all objs in the list came from the pickled file,
-                # then reset the list and add this object as a fresh one,
-                # else append to the list
-                if old_ifaceobjs[0].flags & iface._PICKLED:
-                    del self.ifaceobjdict[ifaceobj.name]
-                    self.ifaceobjdict[ifaceobj.name] = [ifaceobj]
-                else:
-                    self.ifaceobjdict[ifaceobj.name].append(ifaceobj)
-        elif 'down' in op:
-            # If down of object successfull, delete object from state manager
-            if not old_ifaceobjs:
-                return
-            if ifaceobj.status != ifaceStatus.SUCCESS:
-                return
-            # If it matches any of the object, return
-            oidx = 0
-            for o in old_ifaceobjs:
-                if o.compare(ifaceobj):
-                    old_ifaceobjs.pop(oidx)
-                    if not len(old_ifaceobjs):
-                        del self.ifaceobjdict[ifaceobj.name]
-                    return
-                oidx += 1
-
-    def save_state(self):
-        """ saves state (ifaceobjects) to persistent state file """
-
-        try:
-            with open(self.state_file, 'w') as f:
-                if not len(self.ifaceobjdict):
-                    f.truncate(0)
-                    return
-                self.logger.debug('saving state ..')
-                for ifaceobjs in self.ifaceobjdict.values():
-                    [pickling.save_obj(f, i) for i in ifaceobjs]
-            open('%s/%s' %(self.state_rundir, self.state_runlockfile), 'w').close()
-        except:
-            raise
-
-    def dump_pretty(self, ifacenames, format='native'):
-        if not ifacenames:
-            ifacenames = self.ifaceobjdict.keys()
-        for i in ifacenames:
-            ifaceobjs = self.get_ifaceobjs(i)
-            if not ifaceobjs:
-                continue
-            for ifaceobj in ifaceobjs:
-                if format == 'json':
-                    ifaceobj.dump_json()
-                else:
-                    ifaceobj.dump_pretty()
-
-    def dump(self, ifacenames=None):
-        self.logger.debug('statemanager iface state:')
-        if ifacenames:
-            for i in ifacenames:
-                ifaceobj = self.ifaces.get(i)
-                if ifaceobj is None:
-                    raise exceptions.ifaceNotFoundError('ifname %s'
-                        %i + ' not found')
-                ifaceobj.dump(self.logger)
-        else:
-            for ifacename, ifaceobjs in self.ifaceobjdict.items():
-                [i.dump(self.logger) for i in ifaceobjs]
-
-statemanager_api = stateManager()
diff --git a/ifupdown/template.py b/ifupdown/template.py
deleted file mode 100644 (file)
index 4397834..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-# template --
-#    helper class to render templates
-#
-
-import logging
-import traceback
-from utils import *
-
-class templateEngine():
-    """ provides template rendering methods """
-
-    def __init__(self, template_engine, template_enable='0',
-                 template_lookuppath=None):
-        self.logger = logging.getLogger('ifupdown.' +
-                    self.__class__.__name__)
-        self.tclass = None
-        self.tclassargs = {}
-        self.render = self._render_default
-        if template_enable == '0':
-            return
-        if template_engine == 'mako':
-            try:
-                self.tclass = utils.importName('mako.template', 'Template')
-            except Exception, e:
-                self.logger.warn('unable to load template engine %s (%s)'
-                        %(template_engine, str(e)))
-                pass
-            if template_lookuppath:
-                try:
-                    self.logger.debug('setting template lookuppath to %s'
-                            %template_lookuppath)
-                    lc = utils.importName('mako.lookup', 'TemplateLookup')
-                    self.tclassargs['lookup'] = lc(
-                                directories=template_lookuppath.split(':'))
-                except Exception, e:
-                    self.logger.warn('unable to set template lookup path'
-                                     ' %s (%s): are you sure \'python-mako\''
-                                     'is installed?'
-                                     % (template_lookuppath, str(e)))
-            self.render = self._render_mako
-        else:
-            self.logger.info('skip template processing.., ' +
-                    'template engine not found')
-
-    def _render_default(self, textdata):
-        return textdata
-
-    def _render_mako(self, textdata):
-        """ render textdata passed as argument using mako
-
-        Returns rendered textdata """
-
-        if not self.tclass:
-            return textdata
-        self.logger.info('template processing on interfaces file ...')
-        t = self.tclass(text=textdata, output_encoding='utf-8',
-                     lookup=self.tclassargs.get('lookup'))
-        return t.render()
diff --git a/ifupdown/utils.py b/ifupdown/utils.py
deleted file mode 100644 (file)
index 82eafa1..0000000
+++ /dev/null
@@ -1,318 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-# utils --
-#    helper class
-#
-
-import os
-import re
-import shlex
-import fcntl
-import signal
-import logging
-import subprocess
-import ifupdownflags
-
-from functools import partial
-from ipaddr import IPNetwork, IPAddress
-
-from ifupdown.iface import *
-
-def signal_handler_f(ps, sig, frame):
-    if ps:
-        ps.send_signal(sig)
-    if sig == signal.SIGINT:
-        raise KeyboardInterrupt
-
-class utils():
-    logger = logging.getLogger('ifupdown')
-    DEVNULL = open(os.devnull, 'w')
-
-    _string_values = {
-        "on": True,
-        "yes": True,
-        "1": True,
-        "off": False,
-        "no": False,
-        "0": False,
-    }
-
-    _binary_bool = {
-        True: "1",
-        False: "0",
-    }
-
-    _yesno_bool = {
-        True: 'yes',
-        False: 'no'
-    }
-
-    _onoff_bool = {
-        'yes': 'on',
-        'no': 'off'
-    }
-
-    @staticmethod
-    def get_onoff_bool(value):
-        if value in utils._onoff_bool:
-            return utils._onoff_bool[value]
-        return value
-
-    @staticmethod
-    def get_boolean_from_string(value):
-        if value in utils._string_values:
-            return utils._string_values[value]
-        return False
-
-    @staticmethod
-    def get_yesno_boolean(bool):
-        return utils._yesno_bool[bool]
-
-    @staticmethod
-    def boolean_support_binary(value):
-        return utils._binary_bool[utils.get_boolean_from_string(value)]
-
-    @staticmethod
-    def is_binary_bool(value):
-        return value == '0' or value == '1'
-
-    @staticmethod
-    def support_yesno_attrs(attrsdict, attrslist, ifaceobj=None):
-        if ifaceobj:
-            for attr in attrslist:
-                value = ifaceobj.get_attr_value_first(attr)
-                if value and not utils.is_binary_bool(value):
-                    if attr in attrsdict:
-                        bool = utils.get_boolean_from_string(attrsdict[attr])
-                        attrsdict[attr] = utils.get_yesno_boolean(bool)
-        else:
-            for attr in attrslist:
-                if attr in attrsdict:
-                    attrsdict[attr] = utils.boolean_support_binary(attrsdict[attr])
-
-    @classmethod
-    def importName(cls, modulename, name):
-        """ Import a named object """
-        try:
-            module = __import__(modulename, globals(), locals(), [name])
-        except ImportError:
-            return None
-        return getattr(module, name)
-
-    @classmethod
-    def lockFile(cls, lockfile):
-        try:
-            fp = os.open(lockfile, os.O_CREAT | os.O_TRUNC | os.O_WRONLY)
-            fcntl.flock(fp, fcntl.LOCK_EX | fcntl.LOCK_NB)
-            fcntl.fcntl(fp, fcntl.F_SETFD, fcntl.FD_CLOEXEC)
-        except IOError:
-            return False
-        return True
-
-    @classmethod
-    def parse_iface_range(cls, name):
-        # eg: swp1.[2-100]
-        # return (prefix, range-start, range-end)
-        # eg return ("swp1.", 1, 20, ".100")
-        range_match = re.match("^([\w]+)\[([\d]+)-([\d]+)\]([\.\w]+)", name)
-        if range_match:
-            range_groups = range_match.groups()
-            if range_groups[1] and range_groups[2]:
-                return (range_groups[0], int(range_groups[1], 10),
-                        int(range_groups[2], 10), range_groups[3])
-        else:
-            # eg: swp[1-20].100
-            # return (prefix, range-start, range-end, suffix)
-            # eg return ("swp", 1, 20, ".100")
-            range_match = re.match("^([\w\.]+)\[([\d]+)-([\d]+)\]", name)
-            if range_match:
-                range_groups = range_match.groups()
-                if range_groups[1] and range_groups[2]:
-                    return (range_groups[0], int(range_groups[1], 10),
-                            int(range_groups[2], 10))
-        return None
-
-    @classmethod
-    def expand_iface_range(cls, name):
-        ifacenames = []
-        irange = cls.parse_iface_range(name)
-        if irange:
-            if len(irange) == 3:
-                # eg swp1.[2-4], r = "swp1.", 2, 4)
-                for i in range(irange[1], irange[2]):
-                    ifacenames.append('%s%d' %(irange[0], i))
-            elif len(irange) == 4:
-                for i in range(irange[1], irange[2]):
-                    # eg swp[2-4].100, r = ("swp", 2, 4, ".100")
-                    ifacenames.append('%s%d%s' %(irange[0], i, irange[3]))
-        return ifacenames
-
-    @classmethod
-    def is_ifname_range(cls, name):
-        if '[' in name or ']' in name:
-            return True
-        return False
-
-    @classmethod
-    def check_ifname_size_invalid(cls, name=''):
-        """ IFNAMSIZ in include/linux/if.h is 16 so we check this """
-        IFNAMSIZ = 16
-        if len(name) > IFNAMSIZ - 1:
-            return True
-        else:
-            return False
-
-    @classmethod
-    def enable_subprocess_signal_forwarding(cls, ps, sig):
-        signal.signal(sig, partial(signal_handler_f, ps))
-
-    @classmethod
-    def disable_subprocess_signal_forwarding(cls, sig):
-        signal.signal(sig, signal.SIG_DFL)
-
-    @classmethod
-    def _log_command_exec(cls, cmd, stdin):
-        if stdin:
-            cls.logger.info('executing %s [%s]' % (cmd, stdin))
-        else:
-            cls.logger.info('executing %s' % cmd)
-
-    @classmethod
-    def _format_error(cls, cmd, cmd_returncode, cmd_output, stdin):
-        if type(cmd) is list:
-            cmd = ' '.join(cmd)
-        if stdin:
-            cmd = '%s [%s]' % (cmd, stdin)
-        if cmd_output:
-            return 'cmd \'%s\' failed: returned %d (%s)' % \
-                   (cmd, cmd_returncode, cmd_output)
-        else:
-            return 'cmd \'%s\' failed: returned %d' % (cmd, cmd_returncode)
-
-    @classmethod
-    def get_normalized_ip_addr(cls, ifacename, ipaddrs):
-        if not ipaddrs: return None
-        if isinstance(ipaddrs, list):
-                addrs = []
-                for ip in ipaddrs:
-                    if not ip:
-                        continue
-                    try:
-                        addrs.append(str(IPNetwork(ip)) if '/' in ip else str(IPAddress(ip)))
-                    except Exception as e:
-                        cls.logger.warning('%s: %s' % (ifacename, e))
-                return addrs
-        else:
-            try:
-                return str(IPNetwork(ipaddrs)) if '/' in ipaddrs else str(IPAddress(ipaddrs))
-            except Exception as e:
-                cls.logger.warning('%s: %s' % (ifacename, e))
-            return ipaddrs
-
-    @classmethod
-    def is_addr_ip_allowed_on(cls, ifaceobj, syntax_check=False):
-        msg = ('%s: ignoring ip address. Assigning an IP '
-               'address is not allowed on' % ifaceobj.name)
-        if (ifaceobj.role & ifaceRole.SLAVE
-                and not (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE)):
-            up = None
-            if ifaceobj.upperifaces:
-                up = ifaceobj.upperifaces[0]
-            msg = ('%s enslaved interfaces. %s'
-                   % (msg, ('%s is enslaved to %s'
-                            % (ifaceobj.name, up)) if up else '')).strip()
-            if syntax_check:
-                cls.logger.warning(msg)
-            else:
-                cls.logger.info(msg)
-            return False
-        elif (ifaceobj.link_kind & ifaceLinkKind.BRIDGE
-                and ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE):
-            msg = '%s bridge vlan aware interfaces'
-            if syntax_check:
-                cls.logger.warning(msg)
-            else:
-                cls.logger.info(msg)
-            return False
-        return True
-
-    @classmethod
-    def _execute_subprocess(cls, cmd,
-                            env=None,
-                            shell=False,
-                            close_fds=False,
-                            stdout=True,
-                            stdin=None,
-                            stderr=subprocess.STDOUT):
-        """
-        exec's commands using subprocess Popen
-            Args:
-                cmd, should be shlex.split if not shell
-        returns: output
-
-        Note: close_fds=True is affecting performance (2~3 times slower)
-        """
-        if ifupdownflags.flags.DRYRUN:
-            return ''
-
-        cmd_output = None
-        try:
-            ch = subprocess.Popen(cmd,
-                                  env=env,
-                                  shell=shell,
-                                  close_fds=close_fds,
-                                  stdin=subprocess.PIPE if stdin else None,
-                                  stdout=subprocess.PIPE if stdout else cls.DEVNULL,
-                                  stderr=stderr)
-            utils.enable_subprocess_signal_forwarding(ch, signal.SIGINT)
-            if stdout or stdin:
-                cmd_output = ch.communicate(input=stdin)[0]
-            cmd_returncode = ch.wait()
-        except Exception as e:
-            raise Exception('cmd \'%s\' failed (%s)' % (' '.join(cmd), str(e)))
-        finally:
-            utils.disable_subprocess_signal_forwarding(signal.SIGINT)
-        if cmd_returncode != 0:
-            raise Exception(cls._format_error(cmd,
-                                              cmd_returncode,
-                                              cmd_output,
-                                              stdin))
-        return cmd_output
-
-    @classmethod
-    def exec_user_command(cls, cmd, close_fds=False, stdout=True,
-                          stdin=None, stderr=subprocess.STDOUT):
-        cls._log_command_exec(cmd, stdin)
-        return cls._execute_subprocess(cmd,
-                                       shell=True,
-                                       close_fds=close_fds,
-                                       stdout=stdout,
-                                       stdin=stdin,
-                                       stderr=stderr)
-
-    @classmethod
-    def exec_command(cls, cmd, env=None, close_fds=False, stdout=True,
-                     stdin=None, stderr=subprocess.STDOUT):
-        cls._log_command_exec(cmd, stdin)
-        return cls._execute_subprocess(shlex.split(cmd),
-                                       env=env,
-                                       close_fds=close_fds,
-                                       stdout=stdout,
-                                       stdin=stdin,
-                                       stderr=stderr)
-
-    @classmethod
-    def exec_commandl(cls, cmdl, env=None, close_fds=False, stdout=True,
-                      stdin=None, stderr=subprocess.STDOUT):
-        cls._log_command_exec(' '.join(cmdl), stdin)
-        return cls._execute_subprocess(cmdl,
-                                       env=env,
-                                       close_fds=close_fds,
-                                       stdout=stdout,
-                                       stdin=stdin,
-                                       stderr=stderr)
-
-fcntl.fcntl(utils.DEVNULL, fcntl.F_SETFD, fcntl.FD_CLOEXEC)
diff --git a/ifupdown2/__init__.py b/ifupdown2/__init__.py
new file mode 100644 (file)
index 0000000..c720dc0
--- /dev/null
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+__version__ = '2.0.0'
+
+# Copyright (C) 2014,2015,2016,2017,2018 Cumulus Networks, Inc. All rights reserved
+#
+# 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 the Free Software Foundation; version 2.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# https://www.gnu.org/licenses/gpl-2.0-standalone.html
+#
+# Authors:
+#       Roopa Prabhu, roopa@cumulusnetworks.com
+#       Julien Fortin, julien@cumulusnetworks.com
+
+__license__ = 'GPL-2'
+__status__ = 'Production'
+__copyright__ = 'Copyright (C) 2014,2015,2016,2017,2018 Cumulus Networks, Inc.'
+
+
+__authors__ = [
+    'Roopa Prabhu',
+    'Julien Fortin'
+]
+__credits__ = [
+    'Roopa Prabhu',
+    'Julien Fortin'
+]
+__emails__ = [
+    'roopa@cumulusnetworks.com',
+    'julien@cumulusnetworks.com'
+]
+__maintainer__ = 'Julien Fortin'
+__email__ = 'julien@cumulusnetworks.com'
diff --git a/ifupdown2/__main__.py b/ifupdown2/__main__.py
new file mode 100755 (executable)
index 0000000..4dd4270
--- /dev/null
@@ -0,0 +1,205 @@
+#!/usr/bin/python
+#
+# Copyright 2016 Cumulus Networks, Inc. All rights reserved.
+# Author: Julien Fortin, julien@cumulusnetworks.com
+#
+#
+
+import os
+import re
+import sys
+import json
+import errno
+import struct
+import select
+import socket
+import signal
+
+try:
+    import ifupdown2.ifupdown.config as core_config
+    from ifupdown2.ifupdown.log import log
+    from ifupdown2 import __version__
+
+    core_config.__version__ = __version__
+except:
+    import ifupdown.config as core_config
+    from ifupdown.log import log
+
+    core_config.__version__ = __import__('__init__').__version__
+
+
+class Ifupdown2Complete(Exception):
+    def __init__(self, status):
+        self.status = status
+
+
+class Ifupdown2Client:
+    def __init__(self, argv):
+
+        self.stdin = None
+        self.argv = argv
+        self.data = ''
+        self.HEADER_SIZE = 4
+        self.daemon_pid = -1
+
+        self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+        try:
+            self.socket.connect('/var/run/ifupdown2d/uds')
+
+            signal.signal(signal.SIGINT, self.signal_handler)
+            signal.signal(signal.SIGTERM, self.signal_handler)
+            signal.signal(signal.SIGQUIT, self.signal_handler)
+
+            try:
+                self.SO_PEERCRED = socket.SO_PEERCRED
+            except AttributeError:
+                # powerpc is the only non-generic we care about. alpha, mips,
+                # sparc, and parisc also have non-generic values.
+                machine = os.uname()[4]
+                if re.search(r'^(ppc|powerpc)', machine):
+                    self.SO_PASSCRED = 20
+                    self.SO_PEERCRED = 21
+                else:
+                    self.SO_PASSCRED = 16
+                    self.SO_PEERCRED = 17
+            try:
+                self.socket.setsockopt(socket.SOL_SOCKET, self.SO_PASSCRED, 1)
+            except Exception as e:
+                raise Exception('setsockopt: %s' % str(e))
+
+        except socket.error:
+            self.socket.close()
+            self.socket = None
+            sys.stderr.write("""
+    ERROR: %s could not connect to ifupdown2d
+
+    Try starting ifupdown2d with:
+    sudo systemctl start ifupdown2d
+
+    To configure ifupdown2d to start when the box boots:
+    sudo systemctl enable ifupdown2d
+    """ % argv[0])
+
+    def __del__(self):
+        if self.socket:
+            self.socket.close()
+
+    def signal_handler(self, sig, frame):
+        if self.daemon_pid > 0:
+            os.kill(self.daemon_pid, sig)
+
+    def read_data(self):
+        ready = select.select([self.socket], [], [])
+        if ready and ready[0] and ready[0][0] == self.socket:
+            d = self.socket.recv(65536)
+            if self.data:
+                self.data += d
+            else:
+                self.data = d
+            return True
+        return False
+
+    def get_packets(self):
+        """
+            ifupdown2 output is divided into "json packets"
+            the first 4 bytes is the size of the next json
+            object to etract
+
+        """
+        data_size = len(self.data)
+        if not data_size:
+            raise Ifupdown2Complete(status=1)
+
+        packets = []
+        try:
+            while data_size > 0:
+                packet_len = struct.unpack('=I', self.data[:self.HEADER_SIZE])[0]
+                packet_data = self.data[self.HEADER_SIZE:packet_len + self.HEADER_SIZE]
+
+                fmt = "=%ds" % packet_len
+
+                packets.append(json.loads(struct.unpack(fmt, packet_data)[0]))
+
+                self.data = self.data[self.HEADER_SIZE + packet_len:]
+                data_size -= self.HEADER_SIZE + packet_len
+        except:
+            pass
+        return packets
+
+    def process_packets(self, packets):
+        for packet in packets:
+            if 'pid' in packet:
+                self.daemon_pid = packet['pid']
+            if 'stdout' in packet:
+                sys.stdout.write(packet['stdout'])
+            if 'stderr' in packet:
+                sys.stderr.write(packet['stderr'])
+            if 'status' in packet:
+                raise Ifupdown2Complete(packet['status'])
+
+    def run(self):
+        status = 1
+        if self.socket:
+            for arg in ['-i', '--interfaces']:
+                try:
+                    if self.argv[self.argv.index(arg) + 1] == '-':
+                        self.stdin = sys.stdin.read()
+                        continue
+                except (ValueError, IndexError):
+                    pass
+
+            self.socket.send(json.dumps({
+                'argv': self.argv,
+                'stdin': self.stdin
+            }))
+
+            try:
+                while True:
+                    try:
+                        self.read_data()
+                        self.process_packets(self.get_packets())
+                    except Ifupdown2Complete as e:
+                        status = e.status
+                        break
+                    except Exception as e:
+                        if ((isinstance(e.args, tuple) and e[0] == 4)
+                            or (hasattr(e, 'errno') and e.errno == errno.EINTR)):
+                            pass
+                        else:
+                            raise
+            except Exception as e:
+                sys.stderr.write(str(e))
+            finally:
+                self.socket.close()
+        return status if status != None else 1
+
+
+def ifupdown2_standalone():
+    try:
+        import ifupdown2.ifupdown.main as main_ifupdown2
+    except:
+        import ifupdown.main as main_ifupdown2
+    ifupdown2 = main_ifupdown2.Ifupdown2(daemon=False, uid=os.geteuid())
+    ifupdown2.parse_argv(sys.argv)
+    ifupdown2.update_logger()
+    return ifupdown2.main()
+
+
+def main():
+    try:
+        if 'use_daemon=yes' in open(core_config.IFUPDOWN2_CONF_PATH).read():
+            return Ifupdown2Client(sys.argv).run()
+        else:
+            return ifupdown2_standalone()
+    except KeyboardInterrupt:
+        return 1
+    except Exception as e:
+        log.error(str(e))
+        return 1
+
+
+if __name__ == '__main__':
+    try:
+        sys.exit(main())
+    except KeyboardInterrupt:
+        sys.exit(1)
diff --git a/ifupdown2/addons/__init__.py b/ifupdown2/addons/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/ifupdown2/addons/address.py b/ifupdown2/addons/address.py
new file mode 100644 (file)
index 0000000..dc43f77
--- /dev/null
@@ -0,0 +1,1099 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+from ipaddr import IPNetwork, IPv4Network, IPv6Network, _BaseV6
+
+try:
+    from ifupdown2.ifupdown.iface import *
+    from ifupdown2.ifupdown.utils import utils
+    from ifupdown2.ifupdown.netlink import netlink
+
+    from ifupdown2.ifupdownaddons.dhclient import dhclient
+    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdown2.ifupdownaddons.modulebase import moduleBase
+
+    import ifupdown2.ifupdown.statemanager as statemanager
+    import ifupdown2.ifupdown.policymanager as policymanager
+    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
+    import ifupdown2.ifupdown.ifupdownconfig as ifupdownconfig
+except ImportError:
+    from ifupdown.iface import *
+    from ifupdown.utils import utils
+    from ifupdown.netlink import netlink
+
+    from ifupdownaddons.dhclient import dhclient
+    from ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdownaddons.modulebase import moduleBase
+
+    import ifupdown.statemanager as statemanager
+    import ifupdown.policymanager as policymanager
+    import ifupdown.ifupdownflags as ifupdownflags
+    import ifupdown.ifupdownconfig as ifupdownconfig
+
+
+class address(moduleBase):
+    """  ifupdown2 addon module to configure address, mtu, hwaddress, alias
+    (description) on an interface """
+
+    _modinfo = {'mhelp' : 'address configuration module for interfaces',
+                'attrs': {
+                      'address' :
+                            {'help' : 'ipv4 or ipv6 addresses',
+                             'validvals' : ['<ipv4/prefixlen>', '<ipv6/prefixlen>'],
+                             'multiline' : True,
+                             'example' : ['address 10.0.12.3/24',
+                             'address 2000:1000:1000:1000:3::5/128']},
+                      'netmask' :
+                            {'help': 'netmask',
+                             'example' : ['netmask 255.255.255.0'],
+                             'compat' : True},
+                      'broadcast' :
+                            {'help': 'broadcast address',
+                             'validvals' : ['<ipv4>', ],
+                             'example' : ['broadcast 10.0.1.255']},
+                      'scope' :
+                            {'help': 'scope',
+                             'validvals' : ['universe', 'site', 'link', 'host', 'nowhere'],
+                             'example' : ['scope host']},
+                      'preferred-lifetime' :
+                            {'help': 'preferred lifetime',
+                              'validrange' : ['0', '65535'],
+                             'example' : ['preferred-lifetime forever',
+                                          'preferred-lifetime 10']},
+                      'gateway' :
+                            {'help': 'default gateway',
+                             'validvals' : ['<ipv4>', '<ipv6>'],
+                             'multiline' : True,
+                             'example' : ['gateway 255.255.255.0']},
+                      'mtu' :
+                            { 'help': 'interface mtu',
+                              'validrange' : ['552', '9216'],
+                              'example' : ['mtu 1600'],
+                              'default' : '1500'},
+                      'hwaddress' :
+                            {'help' : 'hw address',
+                             'validvals' : ['<mac>',],
+                             'example': ['hwaddress 44:38:39:00:27:b8']},
+                      'alias' :
+                            { 'help': 'description/alias',
+                              'example' : ['alias testnetwork']},
+                      'address-purge' :
+                            { 'help': 'purge existing addresses. By default ' +
+                              'any existing ip addresses on an interface are ' +
+                              'purged to match persistant addresses in the ' +
+                              'interfaces file. Set this attribute to \'no\'' +
+                              'if you want to preserve existing addresses',
+                              'validvals' : ['yes', 'no'],
+                              'default' : 'yes',
+                              'example' : ['address-purge yes/no']},
+                      'clagd-vxlan-anycast-ip' :
+                            { 'help'     : 'Anycast local IP address for ' +
+                              'dual connected VxLANs',
+                              'validvals' : ['<ipv4>', ],
+                              'example'  : ['clagd-vxlan-anycast-ip 36.0.0.11']},
+                      'ip-forward' :
+                            { 'help': 'ip forwarding flag',
+                              'validvals': ['on', 'off'],
+                              'default' : 'off',
+                              'example' : ['ip-forward off']},
+                      'ip6-forward' :
+                            { 'help': 'ipv6 forwarding flag',
+                              'validvals': ['on', 'off'],
+                              'default' : 'off',
+                              'example' : ['ip6-forward off']},
+                      'mpls-enable' :
+                            { 'help': 'mpls enable flag',
+                              'validvals': ['yes', 'no'],
+                              'default' : 'no',
+                              'example' : ['mpls-enable yes']},
+                }}
+
+    def __init__(self, *args, **kargs):
+        moduleBase.__init__(self, *args, **kargs)
+        self.ipcmd = None
+        self._bridge_fdb_query_cache = {}
+        self.default_mtu = policymanager.policymanager_api.get_attr_default(module_name=self.__class__.__name__, attr='mtu')
+        self.max_mtu = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='max_mtu')
+        self.ipforward = policymanager.policymanager_api.get_attr_default(module_name=self.__class__.__name__, attr='ip-forward')
+        self.ip6forward = policymanager.policymanager_api.get_attr_default(module_name=self.__class__.__name__, attr='ip6-forward')
+        self.ifaces_defaults = policymanager.policymanager_api.get_iface_defaults(module_name=self.__class__.__name__)
+        self.enable_l3_iface_forwarding_checks = utils.get_boolean_from_string(
+            policymanager.policymanager_api.get_module_globals(
+                self.__class__.__name__,
+                'enable_l3_iface_forwarding_checks'
+            )
+        )
+
+        if not self.default_mtu:
+            self.default_mtu = '1500'
+
+        self.logger.info('address: using default mtu %s' %self.default_mtu)
+
+        if self.max_mtu:
+            self.logger.info('address: using max mtu %s' %self.max_mtu)
+
+        self.lower_iface_mtu_checked_list = list()
+
+    def syntax_check(self, ifaceobj, ifaceobj_getfunc=None):
+        return (self.syntax_check_multiple_gateway(ifaceobj)
+                and self.syntax_check_addr_allowed_on(ifaceobj, True)
+                and self.syntax_check_mtu(ifaceobj, ifaceobj_getfunc)
+                and self.syntax_check_sysctls(ifaceobj)
+                and self.syntax_check_enable_l3_iface_forwardings(ifaceobj, ifaceobj_getfunc, syntax_check=True))
+
+    def syntax_check_enable_l3_iface_forwardings(self, ifaceobj, ifaceobj_getfunc, syntax_check=False):
+        if (self.enable_l3_iface_forwarding_checks
+                and (ifaceobj.link_kind & ifaceLinkKind.VLAN
+                     or ifaceobj.link_kind & ifaceLinkKind.BRIDGE)
+                and not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT):
+
+            ifname = ifaceobj.name
+            vlan_addr = None
+            vlan_ipforward_off = None
+
+            for obj in ifaceobj_getfunc(ifname) or [ifaceobj]:
+                if not vlan_addr:
+                    vlan_addr = obj.get_attr_value('address')
+
+                if not vlan_ipforward_off:
+                    ip_forward_value = obj.get_attr_value_first('ip-forward')
+
+                    if ip_forward_value and not utils.get_boolean_from_string(ip_forward_value):
+                        vlan_ipforward_off = True
+
+                if vlan_addr and vlan_ipforward_off:
+                    if syntax_check:
+                        raise Exception(
+                            'configuring ip-forward off and ip address(es) (%s) is not compatible'
+                            % (', '.join(vlan_addr))
+                        )
+                    else:
+                        raise Exception(
+                            '%s: configuring ip-forward off and ip address(es) (%s) is not compatible'
+                            % (ifname, ', '.join(vlan_addr))
+                        )
+
+        return True
+
+    def syntax_check_sysctls(self, ifaceobj):
+        result = True
+        bridge_port = (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT)
+        ipforward = ifaceobj.get_attr_value_first('ip-forward')
+        if bridge_port and ipforward:
+            result = False
+            self.log_error('%s: \'ip-forward\' is not supported for '
+                           'bridge port' %ifaceobj.name)
+        ip6forward = ifaceobj.get_attr_value_first('ip6-forward')
+        if bridge_port and ip6forward:
+            result = False
+            self.log_error('%s: \'ip6-forward\' is not supported for '
+                           'bridge port' %ifaceobj.name)
+        return result
+
+    def syntax_check_mtu(self, ifaceobj, ifaceobj_getfunc):
+        mtu = ifaceobj.get_attr_value_first('mtu')
+        if mtu:
+            return self._check_mtu_config(ifaceobj, mtu, ifaceobj_getfunc,
+                                          syntaxcheck=True)
+        return True
+
+    def syntax_check_addr_allowed_on(self, ifaceobj, syntax_check=False):
+        if ifaceobj.get_attr_value('address'):
+            return utils.is_addr_ip_allowed_on(ifaceobj, syntax_check=syntax_check)
+        return True
+
+    def _syntax_check_multiple_gateway(self, family, found, addr, type_obj):
+        if type(IPNetwork(addr)) == type_obj:
+            if found:
+                raise Exception('%s: multiple gateways for %s family'
+                                % (addr, family))
+            return True
+        return False
+
+    def syntax_check_multiple_gateway(self, ifaceobj):
+        result = True
+        inet = False
+        inet6 = False
+        gateways = ifaceobj.get_attr_value('gateway')
+        for addr in gateways if gateways else []:
+            try:
+                if self._syntax_check_multiple_gateway('inet', inet, addr,
+                                                       IPv4Network):
+                    inet = True
+                if self._syntax_check_multiple_gateway('inet6', inet6, addr,
+                                                       IPv6Network):
+                    inet6 = True
+            except Exception as e:
+                self.logger.warning('%s: address: %s' % (ifaceobj.name, str(e)))
+                result = False
+        return result
+
+    def _address_valid(self, addrs):
+        if not addrs:
+           return False
+        if any(map(lambda a: True if a[:7] != '0.0.0.0'
+                else False, addrs)):
+           return True
+        return False
+
+    def _get_hwaddress(self, ifaceobj):
+        return utils.strip_hwaddress(ifaceobj.get_attr_value_first('hwaddress'))
+
+    def _process_bridge(self, ifaceobj, up):
+        hwaddress = self._get_hwaddress(ifaceobj)
+        addrs = ifaceobj.get_attr_value_first('address')
+        is_vlan_dev_on_vlan_aware_bridge = False
+        is_bridge = self.ipcmd.is_bridge(ifaceobj.name)
+        if not is_bridge:
+            if ifaceobj.link_kind & ifaceLinkKind.VLAN:
+                bridgename = ifaceobj.lowerifaces[0]
+                vlan = self._get_vlan_id(ifaceobj)
+                is_vlan_dev_on_vlan_aware_bridge = self.ipcmd.bridge_is_vlan_aware(bridgename)
+        if ((is_bridge and not self.ipcmd.bridge_is_vlan_aware(ifaceobj.name))
+                        or is_vlan_dev_on_vlan_aware_bridge):
+           if self._address_valid(addrs):
+              if up:
+                self.write_file('/proc/sys/net/ipv4/conf/%s' %ifaceobj.name +
+                                '/arp_accept', '1')
+              else:
+                self.write_file('/proc/sys/net/ipv4/conf/%s' %ifaceobj.name +
+                                '/arp_accept', '0')
+        if hwaddress and is_vlan_dev_on_vlan_aware_bridge:
+           if up:
+              self.ipcmd.bridge_fdb_add(bridgename, hwaddress, vlan)
+           else:
+              self.ipcmd.bridge_fdb_del(bridgename, hwaddress, vlan)
+
+    def _get_anycast_addr(self, ifaceobjlist):
+        for ifaceobj in ifaceobjlist:
+            anycast_addr = ifaceobj.get_attr_value_first('clagd-vxlan-anycast-ip')
+            if anycast_addr:
+                anycast_addr = anycast_addr+'/32'
+                return anycast_addr
+        return None
+
+    def _inet_address_convert_to_cidr(self, ifaceobjlist):
+        newaddrs = []
+        newaddr_attrs = {}
+
+        for ifaceobj in ifaceobjlist:
+            addrs = ifaceobj.get_attr_value('address')
+            if not addrs:
+                continue
+
+            if not self.syntax_check_addr_allowed_on(ifaceobj,
+                                                     syntax_check=False):
+                return (False, newaddrs, newaddr_attrs)
+            # If user address is not in CIDR notation, convert them to CIDR
+            for addr_index in range(0, len(addrs)):
+                addr = addrs[addr_index]
+                newaddr = addr
+                if '/' in addr:
+                    newaddrs.append(addr)
+                else:
+                    netmask = ifaceobj.get_attr_value_n('netmask', addr_index)
+                    if netmask:
+                        prefixlen = IPNetwork('%s' %addr +
+                                    '/%s' %netmask).prefixlen
+                        newaddr = addr + '/%s' %prefixlen
+                    else:
+                        # we are here because there is no slash (/xx) and no netmask
+                        # just let IPNetwork handle the ipv4 or ipv6 address mask
+                        prefixlen = IPNetwork(addr).prefixlen
+                        newaddr = addr + '/%s' %prefixlen
+                    newaddrs.append(newaddr)
+
+                attrs = {}
+                for a in ['broadcast', 'pointopoint', 'scope',
+                        'preferred-lifetime']:
+                    aval = ifaceobj.get_attr_value_n(a, addr_index)
+                    if aval:
+                        attrs[a] = aval
+
+                if attrs:
+                    newaddr_attrs[newaddr]= attrs
+        return (True, newaddrs, newaddr_attrs)
+
+    def _inet_address_list_config(self, ifaceobj, newaddrs, newaddr_attrs):
+        for addr_index in range(0, len(newaddrs)):
+            try:
+                if newaddr_attrs:
+                    self.ipcmd.addr_add(ifaceobj.name, newaddrs[addr_index],
+                        newaddr_attrs.get(newaddrs[addr_index],
+                                          {}).get('broadcast'),
+                        newaddr_attrs.get(newaddrs[addr_index],
+                                          {}).get('pointopoint'),
+                        newaddr_attrs.get(newaddrs[addr_index],
+                                          {}).get('scope'),
+                        newaddr_attrs.get(newaddrs[addr_index],
+                                          {}).get('preferred-lifetime'))
+                else:
+                    self.ipcmd.addr_add(ifaceobj.name, newaddrs[addr_index])
+            except Exception, e:
+                self.log_error(str(e), ifaceobj)
+
+    def _inet_address_config(self, ifaceobj, ifaceobj_getfunc=None,
+                             force_reapply=False):
+        squash_addr_config = (True if \
+                                  ifupdownconfig.config.get('addr_config_squash', \
+                              '0') == '1' else False)
+
+        if (squash_addr_config and
+            not (ifaceobj.flags & ifaceobj.YOUNGEST_SIBLING)):
+            return
+
+        purge_addresses = ifaceobj.get_attr_value_first('address-purge')
+        if not purge_addresses:
+           purge_addresses = 'yes'
+
+        if squash_addr_config and ifaceobj.flags & iface.HAS_SIBLINGS:
+            ifaceobjlist = ifaceobj_getfunc(ifaceobj.name)
+        else:
+            ifaceobjlist = [ifaceobj]
+
+        module_name = self.__class__.__name__
+        ifname = ifaceobj.name
+
+        (addr_supported, newaddrs, newaddr_attrs) = self._inet_address_convert_to_cidr(ifaceobjlist)
+        newaddrs = utils.get_ip_objs(module_name, ifname, newaddrs)
+
+        if not addr_supported:
+            return
+        if (not squash_addr_config and (ifaceobj.flags & iface.HAS_SIBLINGS)):
+            # if youngest sibling and squash addr is not set
+            # print a warning that addresses will not be purged
+            if (ifaceobj.flags & iface.YOUNGEST_SIBLING):
+                self.logger.warn('%s: interface has multiple ' %ifaceobj.name +
+                               'iface stanzas, skip purging existing addresses')
+            purge_addresses = 'no'
+
+        if not ifupdownflags.flags.PERFMODE and purge_addresses == 'yes':
+            # if perfmode is not set and purge addresses is not set to 'no'
+            # lets purge addresses not in the config
+            runningaddrs = self.ipcmd.get_running_addrs(ifaceobj, details=False)
+
+            # if anycast address is configured on 'lo' and is in running config
+            # add it to newaddrs so that ifreload doesn't wipe it out
+            anycast_addr = utils.get_normalized_ip_addr(ifaceobj.name, self._get_anycast_addr(ifaceobjlist))
+
+            if runningaddrs and anycast_addr and anycast_addr in runningaddrs:
+                newaddrs.append(anycast_addr)
+
+            user_ip4, user_ip6, newaddrs = self.order_user_configured_addrs(newaddrs)
+
+            if newaddrs == runningaddrs or self.compare_running_ips_and_user_config(user_ip4, user_ip6, runningaddrs):
+                if force_reapply:
+                    self._inet_address_list_config(ifaceobj, newaddrs, newaddr_attrs)
+                return
+            try:
+                # if primary address is not same, there is no need to keep any.
+                # reset all addresses
+                if newaddrs and runningaddrs and newaddrs[0] != runningaddrs[0]:
+                    skip_addrs = []
+                else:
+                    skip_addrs = newaddrs or []
+                for addr in runningaddrs or []:
+                    if addr in skip_addrs:
+                        continue
+                    self.ipcmd.addr_del(ifaceobj.name, addr)
+            except Exception, e:
+                self.log_warn(str(e))
+        if not newaddrs:
+            return
+        self._inet_address_list_config(ifaceobj, newaddrs, newaddr_attrs)
+
+    def compare_running_ips_and_user_config(self, user_ip4, user_ip6, running_addrs):
+        """
+            We need to compare the user config ips and the running ips.
+            ip4 ordering matters (primary etc) but ip6 order doesn't matter
+
+            this function replaces the strict comparison previously in place
+                if newaddrs == running_addrs ?
+
+            We will compare if the ip4 ordering is correct, then check if all
+            ip6 are present in the list (without checking the ordering)
+        """
+        if (user_ip4 or user_ip6) and not running_addrs:
+            return False
+        elif running_addrs and not user_ip4 and not user_ip6:
+            return False
+        elif not running_addrs and not user_ip4 and not user_ip6:
+            return True
+
+        len_ip4 = len(user_ip4)
+        len_running_addrs = len(running_addrs)
+
+        if len_ip4 > len_running_addrs:
+            return False
+
+        i = 0
+        while i < len_ip4:
+            if user_ip4[i] != running_addrs[i]:
+                return False
+            i += 1
+
+        if len_ip4 > 0:
+            running_ip6 = running_addrs[len_ip4:]
+        else:
+            running_ip6 = running_addrs
+
+        i = 0
+        len_ip6 = len(user_ip6)
+
+        for ip6 in running_ip6:
+            if ip6 not in user_ip6:
+                return False
+            i += 1
+
+        return i == len_ip6
+
+    def order_user_configured_addrs(self, user_config_addrs):
+        ip4 = []
+        ip6 = []
+
+        for a in user_config_addrs:
+            if isinstance(a, _BaseV6):
+                ip6.append(str(a))
+            else:
+                ip4.append(str(a))
+
+        return ip4, ip6, ip4 + ip6
+
+    def _delete_gateway(self, ifaceobj, gateways, vrf, metric):
+        for del_gw in gateways:
+            try:
+                self.ipcmd.route_del_gateway(ifaceobj.name, del_gw, vrf, metric)
+            except Exception as e:
+                self.logger.debug('%s: %s' % (ifaceobj.name, str(e)))
+
+    def _add_delete_gateway(self, ifaceobj, gateways=[], prev_gw=[]):
+        vrf = ifaceobj.get_attr_value_first('vrf')
+        metric = ifaceobj.get_attr_value_first('metric')
+        self._delete_gateway(ifaceobj, list(set(prev_gw) - set(gateways)),
+                             vrf, metric)
+        for add_gw in gateways:
+            try:
+                self.ipcmd.route_add_gateway(ifaceobj.name, add_gw, vrf, metric)
+            except Exception as e:
+                self.log_error('%s: %s' % (ifaceobj.name, str(e)))
+
+    def _get_prev_gateway(self, ifaceobj, gateways):
+        ipv = []
+        saved_ifaceobjs = statemanager.statemanager_api.get_ifaceobjs(ifaceobj.name)
+        if not saved_ifaceobjs:
+            return ipv
+        prev_gateways = saved_ifaceobjs[0].get_attr_value('gateway')
+        if not prev_gateways:
+            return ipv
+        return prev_gateways
+
+    def _check_mtu_config(self, ifaceobj, mtu, ifaceobj_getfunc, syntaxcheck=False):
+        retval = True
+        if (ifaceobj.link_kind & ifaceLinkKind.BRIDGE):
+            if syntaxcheck:
+                self.logger.warn('%s: bridge inherits mtu from its ports. There is no need to assign mtu on a bridge' %ifaceobj.name)
+                retval = False
+            else:
+                self.logger.info('%s: bridge inherits mtu from its ports. There is no need to assign mtu on a bridge' %ifaceobj.name)
+        elif ifaceobj_getfunc:
+            if ((ifaceobj.link_privflags & ifaceLinkPrivFlags.BOND_SLAVE) and
+                ifaceobj.upperifaces):
+                masterobj = ifaceobj_getfunc(ifaceobj.upperifaces[0])
+                if masterobj:
+                    master_mtu = masterobj[0].get_attr_value_first('mtu')
+                    if master_mtu and master_mtu != mtu:
+                        if syntaxcheck:
+                            self.logger.warn('%s: bond slave mtu %s is different from bond master %s mtu %s. There is no need to configure mtu on a bond slave.' %(ifaceobj.name, mtu, masterobj[0].name, master_mtu))
+                            retval = False
+                        else:
+                            self.logger.info('%s: bond slave mtu %s is different from bond master %s mtu %s. There is no need to configure mtu on a bond slave.' %(ifaceobj.name, mtu, masterobj[0].name, master_mtu))
+            elif ((ifaceobj.link_kind & ifaceLinkKind.VLAN) and
+                  ifaceobj.lowerifaces):
+                lowerobj = ifaceobj_getfunc(ifaceobj.lowerifaces[0])
+                if lowerobj:
+                    if syntaxcheck:
+                        lowerdev_mtu = lowerobj[0].get_attr_value_first('mtu')
+                    else:
+                        lowerdev_mtu = self.ipcmd.link_get_mtu_sysfs(lowerobj[0].name)
+                    if lowerdev_mtu and int(mtu) > int(lowerdev_mtu):
+                        self.logger.warn('%s: vlan dev mtu %s is greater than lower realdev %s mtu %s'
+                                         %(ifaceobj.name, mtu, lowerobj[0].name, lowerdev_mtu))
+                        retval = False
+                    elif (not lowerobj[0].link_kind and
+                          not (lowerobj[0].link_privflags & ifaceLinkPrivFlags.LOOPBACK) and
+                          not lowerdev_mtu and self.default_mtu and
+                          (int(mtu) > int(self.default_mtu))):
+                        # only check default mtu on lower device which is a physical interface
+                        self.logger.warn('%s: vlan dev mtu %s is greater than lower realdev %s mtu %s'
+                                         %(ifaceobj.name, mtu, lowerobj[0].name, self.default_mtu))
+                        retval = False
+            if self.max_mtu and mtu > self.max_mtu:
+                self.logger.warn('%s: specified mtu %s is greater than max mtu %s'
+                                 %(ifaceobj.name, mtu, self.max_mtu))
+                retval = False
+        return retval
+
+    def _propagate_mtu_to_upper_devs(self, ifaceobj, mtu, ifaceobj_getfunc):
+        if (not ifaceobj.upperifaces or
+            (ifaceobj.link_privflags & ifaceLinkPrivFlags.BOND_SLAVE) or
+            (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE) or
+            (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT)):
+            return
+        for u in ifaceobj.upperifaces:
+            upperobjs = ifaceobj_getfunc(u)
+            if (not upperobjs or
+                not (upperobjs[0].link_kind & ifaceLinkKind.VLAN)):
+                continue
+            # only adjust mtu for vlan devices on ifaceobj
+            umtu = upperobjs[0].get_attr_value_first('mtu')
+            if not umtu:
+                running_mtu = self.ipcmd.link_get_mtu(upperobjs[0].name)
+                if not running_mtu or (running_mtu != mtu):
+                    self.ipcmd.link_set(u, 'mtu', mtu)
+
+    def _process_mtu_config(self, ifaceobj, ifaceobj_getfunc, mtu):
+        if mtu:
+            if not self._check_mtu_config(ifaceobj, mtu, ifaceobj_getfunc):
+                return
+            cached_running_mtu = self.ipcmd.link_get_mtu(ifaceobj.name)
+            running_mtu = self.ipcmd.link_get_mtu_sysfs(ifaceobj.name)
+            if not running_mtu or (running_mtu and running_mtu != mtu):
+                force = cached_running_mtu != running_mtu
+                self.ipcmd.link_set(ifaceobj.name, 'mtu', mtu, force=force)
+                if (not ifupdownflags.flags.ALL and
+                    not ifaceobj.link_kind and
+                    ifupdownconfig.config.get('adjust_logical_dev_mtu', '1') != '0'):
+                    # This is additional cost to us, so do it only when
+                    # ifupdown2 is called on a particular interface and
+                    # it is a physical interface
+                    self._propagate_mtu_to_upper_devs(ifaceobj, mtu, ifaceobj_getfunc)
+            return
+
+        if ifaceobj.link_kind:
+            # bonds and vxlan devices need an explicit set of mtu.
+            # bridges don't need mtu set
+            if (ifaceobj.link_kind & ifaceLinkKind.BOND or
+                ifaceobj.link_kind & ifaceLinkKind.VXLAN):
+                running_mtu = self.ipcmd.link_get_mtu(ifaceobj.name)
+                if (self.default_mtu and running_mtu != self.default_mtu):
+                    self.ipcmd.link_set(ifaceobj.name, 'mtu', self.default_mtu)
+                return
+            if (ifupdownconfig.config.get('adjust_logical_dev_mtu', '1') != '0'
+                and ifaceobj.lowerifaces):
+                # set vlan interface mtu to lower device mtu
+                if (ifaceobj.link_kind & ifaceLinkKind.VLAN):
+                    lower_iface = ifaceobj.lowerifaces[0]
+                    if lower_iface not in self.lower_iface_mtu_checked_list:
+                        lower_iface_mtu = self.ipcmd.link_get_mtu_sysfs(lower_iface)
+                        self.ipcmd.cache_update([lower_iface, 'mtu'], lower_iface_mtu)
+                        self.lower_iface_mtu_checked_list.append(lower_iface)
+                    else:
+                        lower_iface_mtu = self.ipcmd.link_get_mtu(lower_iface)
+
+                    if lower_iface_mtu != self.ipcmd.link_get_mtu_sysfs(ifaceobj.name):
+                        self.ipcmd.link_set_mtu(ifaceobj.name, lower_iface_mtu)
+
+        elif (not (ifaceobj.name == 'lo') and not ifaceobj.link_kind and
+              not (ifaceobj.link_privflags & ifaceLinkPrivFlags.BOND_SLAVE) and
+              self.default_mtu):
+            # logical devices like bridges and vlan devices rely on mtu
+            # from their lower devices. ie mtu travels from
+            # lower devices to upper devices. For bonds mtu travels from
+            # upper to lower devices. running mtu depends on upper and
+            # lower device mtu. With all this implicit mtu
+            # config by the kernel in play, we try to be cautious here
+            # on which devices we want to reset mtu to default.
+            # essentially only physical interfaces which are not bond slaves
+            running_mtu = self.ipcmd.link_get_mtu(ifaceobj.name)
+            if running_mtu != self.default_mtu:
+                self.ipcmd.link_set(ifaceobj.name, 'mtu', self.default_mtu)
+
+    def _set_bridge_forwarding(self, ifaceobj):
+        """ set ip forwarding to 0 if bridge interface does not have a
+        ip nor svi """
+        if not ifaceobj.upperifaces and not ifaceobj.get_attr_value('address'):
+            # set forwarding = 0
+            if self.sysctl_get('net.ipv4.conf.%s.forwarding' %ifaceobj.name) == '1':
+                self.sysctl_set('net.ipv4.conf.%s.forwarding' %ifaceobj.name, 0)
+            if self.sysctl_get('net.ipv6.conf.%s.forwarding' %ifaceobj.name) == '1':
+                self.sysctl_set('net.ipv6.conf.%s.forwarding' %ifaceobj.name, 0)
+        else:
+            if self.sysctl_get('net.ipv4.conf.%s.forwarding' %ifaceobj.name) == '0':
+                self.sysctl_set('net.ipv4.conf.%s.forwarding' %ifaceobj.name, 1)
+            if self.sysctl_get('net.ipv6.conf.%s.forwarding' %ifaceobj.name) == '0':
+                self.sysctl_set('net.ipv6.conf.%s.forwarding' %ifaceobj.name, 1)
+
+    def _sysctl_config(self, ifaceobj):
+        setting_default_value = False
+        mpls_enable = ifaceobj.get_attr_value_first('mpls-enable');
+        if not mpls_enable:
+            setting_default_value = True
+            mpls_enable = self.get_mod_subattr('mpls-enable', 'default')
+        mpls_enable = utils.boolean_support_binary(mpls_enable)
+        # File read has been used for better performance
+        # instead of using sysctl command
+        if ifupdownflags.flags.PERFMODE:
+            running_mpls_enable = '0'
+        else:
+            running_mpls_enable = self.read_file_oneline(
+                '/proc/sys/net/mpls/conf/%s/input'
+                % ifaceobj.name
+            )
+
+        if mpls_enable != running_mpls_enable:
+            try:
+                self.sysctl_set('net.mpls.conf.%s.input'
+                                %('/'.join(ifaceobj.name.split("."))),
+                                mpls_enable)
+            except Exception as e:
+                if not setting_default_value:
+                    ifaceobj.status = ifaceStatus.ERROR
+                    self.logger.error('%s: %s' %(ifaceobj.name, str(e)))
+
+        if (ifaceobj.link_kind & ifaceLinkKind.BRIDGE):
+            self._set_bridge_forwarding(ifaceobj)
+            return
+        if not self.syntax_check_sysctls(ifaceobj):
+            return
+        ipforward = ifaceobj.get_attr_value_first('ip-forward')
+        ip6forward = ifaceobj.get_attr_value_first('ip6-forward')
+        if ifupdownflags.flags.PERFMODE:
+            if ipforward:
+                self.sysctl_set('net.ipv4.conf.%s.forwarding'
+                                 %('/'.join(ifaceobj.name.split("."))),
+                                utils.boolean_support_binary(ipforward))
+            if ip6forward:
+                self.sysctl_set('net.ipv6.conf.%s.forwarding'
+                                %('/'.join(ifaceobj.name.split("."))),
+                                utils.boolean_support_binary(ip6forward))
+            return
+        bridge_port = ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT
+        if bridge_port:
+            if ipforward:
+                self.log_error('%s: \'ip-forward\' is not supported for '
+                               'bridge port' %ifaceobj.name)
+            if ip6forward:
+                self.log_error('%s: \'ip6-forward\' is not supported for '
+                               'bridge port' %ifaceobj.name)
+            return
+        setting_default_value = False
+        if not ipforward:
+            setting_default_value = True
+            ipforward = (self.ipforward or
+                         self.get_mod_subattr('ip-forward', 'default'))
+        ipforward = utils.boolean_support_binary(ipforward)
+        # File read has been used for better performance
+        # instead of using sysctl command
+        running_ipforward = self.read_file_oneline(
+                                '/proc/sys/net/ipv4/conf/%s/forwarding'
+                                %ifaceobj.name)
+        if ipforward != running_ipforward:
+            try:
+                self.sysctl_set('net.ipv4.conf.%s.forwarding'
+                                %('/'.join(ifaceobj.name.split("."))),
+                                ipforward)
+            except Exception as e:
+                if not setting_default_value:
+                    ifaceobj.status = ifaceStatus.ERROR
+                    self.logger.error('%s: %s' %(ifaceobj.name, str(e)))
+
+        setting_default_value = False
+        if not ip6forward:
+            setting_default_value = True
+            ip6forward = (self.ip6forward or
+                          self.get_mod_subattr('ip6-forward', 'default'))
+        ip6forward = utils.boolean_support_binary(ip6forward)
+        # File read has been used for better performance
+        # instead of using sysctl command
+        running_ip6forward = self.read_file_oneline(
+                                '/proc/sys/net/ipv6/conf/%s/forwarding'
+                                %ifaceobj.name)
+        if ip6forward != running_ip6forward:
+            try:
+                self.sysctl_set('net.ipv6.conf.%s.forwarding'
+                                %('/'.join(ifaceobj.name.split("."))),
+                                ip6forward)
+            except Exception as e:
+                # There is chance of ipv6 being removed because of,
+                # for example, setting mtu < 1280
+                # In such cases, log error only if user has configured
+                # ip6-forward
+                if not setting_default_value:
+                    ifaceobj.status = ifaceStatus.ERROR
+                    self.logger.error('%s: %s' %(ifaceobj.name, str(e)))
+
+    def process_mtu(self, ifaceobj, ifaceobj_getfunc):
+        mtu = ifaceobj.get_attr_value_first('mtu')
+
+        if not mtu:
+            default_iface_mtu = self.ifaces_defaults.get(ifaceobj.name, {}).get('mtu')
+
+            if default_iface_mtu:
+                try:
+                    mtu = default_iface_mtu
+                    int(default_iface_mtu)
+                except Exception as e:
+                    self.logger.warning('%s: MTU value from policy file: %s' % (ifaceobj.name, str(e)))
+                    return
+
+        self._process_mtu_config(ifaceobj, ifaceobj_getfunc, mtu)
+
+    def _up(self, ifaceobj, ifaceobj_getfunc=None):
+        if not self.ipcmd.link_exists(ifaceobj.name):
+            return
+
+        if not self.syntax_check_enable_l3_iface_forwardings(ifaceobj, ifaceobj_getfunc):
+            return
+
+        alias = ifaceobj.get_attr_value_first('alias')
+        current_alias = self.ipcmd.link_get_alias(ifaceobj.name)
+        if alias and alias != current_alias:
+            self.ipcmd.link_set_alias(ifaceobj.name, alias)
+        elif not alias and current_alias:
+            self.ipcmd.link_set_alias(ifaceobj.name, '')
+
+        self._sysctl_config(ifaceobj)
+
+        addr_method = ifaceobj.addr_method
+        force_reapply = False
+        try:
+            # release any stale dhcp addresses if present
+            if (addr_method != "dhcp" and not ifupdownflags.flags.PERFMODE and
+                    not (ifaceobj.flags & iface.HAS_SIBLINGS)):
+                # if not running in perf mode and ifaceobj does not have
+                # any sibling iface objects, kill any stale dhclient
+                # processes
+                dhclientcmd = dhclient()
+                if dhclientcmd.is_running(ifaceobj.name):
+                    # release any dhcp leases
+                    dhclientcmd.release(ifaceobj.name)
+                    force_reapply = True
+                elif dhclientcmd.is_running6(ifaceobj.name):
+                    dhclientcmd.release6(ifaceobj.name)
+                    force_reapply = True
+        except:
+            pass
+
+        self.ipcmd.batch_start()
+        if addr_method != "dhcp":
+            self._inet_address_config(ifaceobj, ifaceobj_getfunc,
+                                      force_reapply)
+
+        self.process_mtu(ifaceobj, ifaceobj_getfunc)
+
+        try:
+            self.ipcmd.batch_commit()
+        except Exception as e:
+            self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj, raise_error=False)
+
+        try:
+            hwaddress = self._get_hwaddress(ifaceobj)
+            if hwaddress:
+                running_hwaddress = None
+                if not ifupdownflags.flags.PERFMODE: # system is clean
+                    running_hwaddress = self.ipcmd.link_get_hwaddress(ifaceobj.name)
+                if hwaddress != running_hwaddress:
+                    slave_down = False
+                    netlink.link_set_updown(ifaceobj.name, "down")
+                    if ifaceobj.link_kind & ifaceLinkKind.BOND:
+                        # if bond, down all the slaves
+                        if ifaceobj.lowerifaces:
+                            for l in ifaceobj.lowerifaces:
+                                netlink.link_set_updown(l, "down")
+                            slave_down = True
+                    try:
+                        self.ipcmd.link_set(ifaceobj.name, 'address', hwaddress)
+                    finally:
+                        netlink.link_set_updown(ifaceobj.name, "up")
+                        if slave_down:
+                            for l in ifaceobj.lowerifaces:
+                                netlink.link_set_updown(l, "up")
+
+            # Handle special things on a bridge
+            self._process_bridge(ifaceobj, True)
+        except Exception, e:
+            self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
+
+        gateways = ifaceobj.get_attr_value('gateway')
+        if not gateways:
+            gateways = []
+        prev_gw = self._get_prev_gateway(ifaceobj, gateways)
+        self._add_delete_gateway(ifaceobj, gateways, prev_gw)
+
+    def _down(self, ifaceobj, ifaceobj_getfunc=None):
+        try:
+            if not self.ipcmd.link_exists(ifaceobj.name):
+                return
+            addr_method = ifaceobj.addr_method
+            if addr_method != "dhcp":
+                if ifaceobj.get_attr_value_first('address-purge')=='no':
+                    addrlist = ifaceobj.get_attr_value('address')
+                    for addr in addrlist:
+                        self.ipcmd.addr_del(ifaceobj.name, addr)
+                    #self.ipcmd.addr_del(ifaceobj.name, ifaceobj.get_attr_value('address')[0])
+                elif not ifaceobj.link_kind:
+                    # for logical interfaces we don't need to remove the ip addresses
+                    # kernel will do it for us on 'ip link del'
+                    self.ipcmd.del_addr_all(ifaceobj.name)
+            gateways = ifaceobj.get_attr_value('gateway')
+            if gateways:
+                self._delete_gateway(ifaceobj, gateways,
+                                     ifaceobj.get_attr_value_first('vrf'),
+                                     ifaceobj.get_attr_value_first('metric'))
+            mtu = ifaceobj.get_attr_value_first('mtu')
+            if (not ifaceobj.link_kind and mtu and
+                self.default_mtu and (mtu != self.default_mtu)):
+                self.ipcmd.link_set(ifaceobj.name, 'mtu', self.default_mtu)
+            alias = ifaceobj.get_attr_value_first('alias')
+            if alias:
+                self.write_file('/sys/class/net/%s/ifalias' % ifaceobj.name, '\n')
+            # XXX hwaddress reset cannot happen because we dont know last
+            # address.
+
+            # Handle special things on a bridge
+            self._process_bridge(ifaceobj, False)
+        except Exception, e:
+            self.logger.debug('%s : %s' %(ifaceobj.name, str(e)))
+            pass
+
+    def _get_iface_addresses(self, ifaceobj):
+        addrlist = ifaceobj.get_attr_value('address')
+        outaddrlist = []
+
+        if not addrlist: return None
+        for addrindex in range(0, len(addrlist)):
+            addr = addrlist[addrindex]
+            netmask = ifaceobj.get_attr_value_n('netmask', addrindex)
+            if netmask:
+                prefixlen = IPNetwork('%s' %addr +
+                                '/%s' %netmask).prefixlen
+                addr = addr + '/%s' %prefixlen
+            outaddrlist.append(addr)
+        return outaddrlist
+
+    def _get_bridge_fdbs(self, bridgename, vlan):
+        fdbs = self._bridge_fdb_query_cache.get(bridgename)
+        if not fdbs:
+           fdbs = self.ipcmd.bridge_fdb_show_dev(bridgename)
+           if not fdbs:
+              return
+           self._bridge_fdb_query_cache[bridgename] = fdbs
+        return fdbs.get(vlan)
+
+    def _check_addresses_in_bridge(self, ifaceobj, hwaddress):
+        """ If the device is a bridge, make sure the addresses
+        are in the bridge """
+        if ifaceobj.link_kind & ifaceLinkKind.VLAN:
+            bridgename = ifaceobj.lowerifaces[0]
+            vlan = self._get_vlan_id(ifaceobj)
+            if self.ipcmd.bridge_is_vlan_aware(bridgename):
+                fdb_addrs = self._get_bridge_fdbs(bridgename, str(vlan))
+                if not fdb_addrs or hwaddress not in fdb_addrs:
+                   return False
+        return True
+
+    def _query_sysctl(self, ifaceobj, ifaceobjcurr):
+        bridge_port = ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT
+        ipforward = ifaceobj.get_attr_value_first('ip-forward')
+        if ipforward:
+            if bridge_port:
+                ifaceobjcurr.status = ifaceStatus.ERROR
+                ifaceobjcurr.status_str = ('\'ip-forward\' not supported ' +
+                                           'for bridge port')
+                ifaceobjcurr.update_config_with_status('ip-forward', 1, None)
+            else:
+                running_ipforward = self.read_file_oneline(
+                                        '/proc/sys/net/ipv4/conf/%s/forwarding'
+                                        %ifaceobj.name)
+                running_ipforward = utils.get_onff_from_onezero(
+                                            running_ipforward)
+                ifaceobjcurr.update_config_with_status('ip-forward',
+                                                       running_ipforward,
+                                                 ipforward != running_ipforward)
+
+        ip6forward = ifaceobj.get_attr_value_first('ip6-forward')
+        if ip6forward:
+            if bridge_port:
+                ifaceobjcurr.status = ifaceStatus.ERROR
+                ifaceobjcurr.status_str = ('\'ip6-forward\' not supported ' +
+                                           'for bridge port')
+                ifaceobjcurr.update_config_with_status('ip6-forward', 1, None)
+            else:
+                running_ip6forward = self.read_file_oneline(
+                                        '/proc/sys/net/ipv6/conf/%s/forwarding'
+                                        %ifaceobj.name)
+                running_ip6forward = utils.get_onff_from_onezero(
+                                            running_ip6forward)
+                ifaceobjcurr.update_config_with_status('ip6-forward',
+                                                       running_ip6forward,
+                                                 ip6forward != running_ip6forward)
+        mpls_enable = ifaceobj.get_attr_value_first('mpls-enable');
+        if mpls_enable:
+            running_mpls_enable = self.read_file_oneline(
+                                    '/proc/sys/net/mpls/conf/%s/input'
+                                    %ifaceobj.name)
+            running_mpls_enable = utils.get_yesno_from_onezero(
+                                            running_mpls_enable)
+            ifaceobjcurr.update_config_with_status('mpls-enable',
+                                                   running_mpls_enable,
+                                            mpls_enable != running_mpls_enable)
+        return
+
+    def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
+        runningaddrsdict = None
+        if not self.ipcmd.link_exists(ifaceobj.name):
+            self.logger.debug('iface %s not found' %ifaceobj.name)
+            return
+        addr_method = ifaceobj.addr_method
+        self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr,
+                'mtu', self.ipcmd.link_get_mtu)
+        hwaddress = self._get_hwaddress(ifaceobj)
+        if hwaddress:
+            rhwaddress = self.ipcmd.link_get_hwaddress(ifaceobj.name)
+            if not rhwaddress  or rhwaddress != hwaddress:
+               ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress,
+                       1)
+            elif not self._check_addresses_in_bridge(ifaceobj, hwaddress):
+               # XXX: hw address is not in bridge
+               ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress,
+                       1)
+               ifaceobjcurr.status_str = 'bridge fdb error'
+            else:
+               ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress,
+                       0)
+        self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr,
+                    'alias', self.ipcmd.link_get_alias)
+        self._query_sysctl(ifaceobj, ifaceobjcurr)
+        # compare addresses
+        if addr_method == 'dhcp':
+           return
+        addrs = utils.get_normalized_ip_addr(ifaceobj.name,
+                                             self._get_iface_addresses(ifaceobj))
+        runningaddrsdict = self.ipcmd.get_running_addrs(ifaceobj)
+        # if anycast address is configured on 'lo' and is in running config
+        # add it to addrs so that query_check doesn't fail
+        anycast_addr = utils.get_normalized_ip_addr(ifaceobj.name, ifaceobj.get_attr_value_first('clagd-vxlan-anycast-ip'))
+        if anycast_addr:
+            anycast_addr = anycast_addr+'/32'
+        if runningaddrsdict and anycast_addr and runningaddrsdict.get(anycast_addr):
+            addrs.append(anycast_addr)
+
+        # Set ifaceobjcurr method and family
+        ifaceobjcurr.addr_method = ifaceobj.addr_method
+        ifaceobjcurr.addr_family = ifaceobj.addr_family
+        if not runningaddrsdict and not addrs:
+            return
+        runningaddrs = runningaddrsdict.keys() if runningaddrsdict else []
+        # Add /32 netmask to configured address without netmask.
+        # This may happen on interfaces where pointopoint is used.
+        runningaddrs = [ addr if '/' in addr else addr + '/32' for addr in runningaddrs]
+        if runningaddrs != addrs:
+            runningaddrsset = set(runningaddrs) if runningaddrs else set([])
+            addrsset = set(addrs) if addrs else set([])
+            if (ifaceobj.flags & iface.HAS_SIBLINGS):
+                if not addrsset:
+                    return
+                # only check for addresses present in running config
+                addrsdiff = addrsset.difference(runningaddrsset)
+                for addr in addrs:
+                    if addr in addrsdiff:
+                        ifaceobjcurr.update_config_with_status('address',
+                                    addr, 1)
+                    else:
+                        ifaceobjcurr.update_config_with_status('address',
+                                    addr, 0)
+            else:
+                addrsdiff = addrsset.symmetric_difference(runningaddrsset)
+                for addr in addrsset.union(runningaddrsset):
+                    if addr in addrsdiff:
+                        ifaceobjcurr.update_config_with_status('address',
+                                                               addr, 1)
+                    else:
+                        ifaceobjcurr.update_config_with_status('address',
+                                                               addr, 0)
+        elif addrs:
+            [ifaceobjcurr.update_config_with_status('address',
+                       addr, 0) for addr in addrs]
+        #XXXX Check broadcast address, scope, etc
+        return
+
+    def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
+        if not self.ipcmd.link_exists(ifaceobjrunning.name):
+            self.logger.debug('iface %s not found' %ifaceobjrunning.name)
+            return
+        dhclientcmd = dhclient()
+        if (dhclientcmd.is_running(ifaceobjrunning.name) or
+                dhclientcmd.is_running6(ifaceobjrunning.name)):
+            # If dhcp is configured on the interface, we skip it
+            return
+        isloopback = self.ipcmd.link_isloopback(ifaceobjrunning.name)
+        if isloopback:
+            default_addrs = ['127.0.0.1/8', '::1/128']
+            ifaceobjrunning.addr_family.append('inet')
+            ifaceobjrunning.addr_method = 'loopback'
+        else:
+            default_addrs = []
+        runningaddrsdict = self.ipcmd.get_running_addrs(ifaceobjrunning)
+        if runningaddrsdict:
+            [ifaceobjrunning.update_config('address', addr)
+                for addr, addrattrs in runningaddrsdict.items()
+                if addr not in default_addrs]
+        mtu = self.ipcmd.link_get_mtu(ifaceobjrunning.name)
+        if (mtu and
+                (ifaceobjrunning.name == 'lo' and mtu != '16436') or
+                (ifaceobjrunning.name != 'lo' and
+                    mtu != self.get_mod_subattr('mtu', 'default'))):
+                ifaceobjrunning.update_config('mtu', mtu)
+        alias = self.ipcmd.link_get_alias(ifaceobjrunning.name)
+        if alias:
+            ifaceobjrunning.update_config('alias', alias)
+
+        ipforward = self.read_file_oneline(
+                        '/proc/sys/net/ipv4/conf/%s/forwarding'
+                        %ifaceobjrunning.name)
+
+
+    _run_ops = {'up' : _up,
+               'down' : _down,
+               'query-checkcurr' : _query_check,
+               'query-running' : _query_running }
+
+    def get_ops(self):
+        """ returns list of ops supported by this module """
+        return self._run_ops.keys()
+
+    def _init_command_handlers(self):
+        if not self.ipcmd:
+            self.ipcmd = LinkUtils()
+
+    def run(self, ifaceobj, operation, query_ifaceobj=None, ifaceobj_getfunc=None):
+        """ run address configuration on the interface object passed as argument
+
+        Args:
+            **ifaceobj** (object): iface object
+
+            **operation** (str): any of 'up', 'down', 'query-checkcurr',
+                                 'query-running'
+        Kwargs:
+            query_ifaceobj (object): query check ifaceobject. This is only
+                valid when op is 'query-checkcurr'. It is an object same as
+                ifaceobj, but contains running attribute values and its config
+                status. The modules can use it to return queried running state
+                of interfaces. status is success if the running state is same
+                as user required state in ifaceobj. error otherwise.
+        """
+        if ifaceobj.type == ifaceType.BRIDGE_VLAN:
+           return
+        op_handler = self._run_ops.get(operation)
+        if not op_handler:
+            return
+        self._init_command_handlers()
+        if operation == 'query-checkcurr':
+            op_handler(self, ifaceobj, query_ifaceobj,
+                       ifaceobj_getfunc=ifaceobj_getfunc)
+        else:
+            op_handler(self, ifaceobj,
+                       ifaceobj_getfunc=ifaceobj_getfunc)
diff --git a/ifupdown2/addons/addressvirtual.py b/ifupdown2/addons/addressvirtual.py
new file mode 100644 (file)
index 0000000..80541b2
--- /dev/null
@@ -0,0 +1,530 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+import os
+import glob
+
+from ipaddr import IPNetwork, IPv6Network
+
+try:
+    from ifupdown2.ifupdown.iface import *
+    from ifupdown2.ifupdown.netlink import netlink
+
+    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdown2.ifupdownaddons.modulebase import moduleBase
+
+    import ifupdown2.ifupdown.statemanager as statemanager
+    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
+    import ifupdown2.ifupdown.ifupdownconfig as ifupdownconfig
+except ImportError:
+    from ifupdown.iface import *
+    from ifupdown.netlink import netlink
+
+    from ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdownaddons.modulebase import moduleBase
+
+    import ifupdown.statemanager as statemanager
+    import ifupdown.ifupdownflags as ifupdownflags
+    import ifupdown.ifupdownconfig as ifupdownconfig
+
+
+class addressvirtual(moduleBase):
+    """  ifupdown2 addon module to configure virtual addresses """
+
+    _modinfo = {'mhelp' : 'address module configures virtual addresses for ' +
+                          'interfaces. It creates a macvlan interface for ' +
+                          'every mac ip address-virtual line',
+                'attrs' : {
+                    'address-virtual' :
+                        { 'help' : 'bridge router virtual mac and ips',
+                          'multivalue' : True,
+                          'validvals' : ['<mac-ip/prefixlen-list>',],
+                          'example' : ['address-virtual 00:11:22:33:44:01 11.0.1.1/24 11.0.1.2/24']}
+                 }}
+
+
+    def __init__(self, *args, **kargs):
+        moduleBase.__init__(self, *args, **kargs)
+        self.ipcmd = None
+        self._bridge_fdb_query_cache = {}
+
+    def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
+        if ifaceobj.get_attr_value('address-virtual'):
+            ifaceobj.link_privflags |= ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE
+
+    def _get_macvlan_prefix(self, ifaceobj):
+        return '%s-v' %ifaceobj.name[0:13].replace('.', '-')
+
+    def _add_addresses_to_bridge(self, ifaceobj, hwaddress):
+        # XXX: batch the addresses
+        if ifaceobj.link_kind & ifaceLinkKind.VLAN:
+            bridgename = ifaceobj.lowerifaces[0]
+            vlan = self._get_vlan_id(ifaceobj)
+            if self.ipcmd.bridge_is_vlan_aware(bridgename):
+                [self.ipcmd.bridge_fdb_add(bridgename, addr,
+                    vlan) for addr in hwaddress]
+        elif self.ipcmd.is_bridge(ifaceobj.name):
+            [self.ipcmd.bridge_fdb_add(ifaceobj.name, addr)
+                    for addr in hwaddress]
+
+    def _remove_addresses_from_bridge(self, ifaceobj, hwaddress):
+        # XXX: batch the addresses
+        if ifaceobj.link_kind & ifaceLinkKind.VLAN:
+            bridgename = ifaceobj.lowerifaces[0]
+            vlan = self._get_vlan_id(ifaceobj)
+            if self.ipcmd.bridge_is_vlan_aware(bridgename):
+                for addr in hwaddress:
+                    try:
+                        self.ipcmd.bridge_fdb_del(bridgename, addr, vlan)
+                    except Exception, e:
+                        self.logger.debug("%s: %s" %(ifaceobj.name, str(e)))
+                        pass
+        elif self.ipcmd.is_bridge(ifaceobj.name):
+            for addr in hwaddress:
+                try:
+                    self.ipcmd.bridge_fdb_del(ifaceobj.name, addr)
+                except Exception, e:
+                    self.logger.debug("%s: %s" %(ifaceobj.name, str(e)))
+                    pass
+
+    def _get_bridge_fdbs(self, bridgename, vlan):
+        fdbs = self._bridge_fdb_query_cache.get(bridgename)
+        if not fdbs:
+           fdbs = self.ipcmd.bridge_fdb_show_dev(bridgename)
+           if not fdbs:
+              return
+           self._bridge_fdb_query_cache[bridgename] = fdbs
+        return fdbs.get(vlan)
+
+    def _check_addresses_in_bridge(self, ifaceobj, hwaddress):
+        """ If the device is a bridge, make sure the addresses
+        are in the bridge """
+        if ifaceobj.link_kind & ifaceLinkKind.VLAN:
+            bridgename = ifaceobj.lowerifaces[0]
+            vlan = self._get_vlan_id(ifaceobj)
+            if self.ipcmd.bridge_is_vlan_aware(bridgename):
+                fdb_addrs = self._get_bridge_fdbs(bridgename, str(vlan))
+                if not fdb_addrs or hwaddress not in fdb_addrs:
+                   return False
+        return True
+
+    def _fix_connected_route(self, ifaceobj, vifacename, addr):
+        #
+        # XXX: Hack to make sure the primary address
+        # is the first in the routing table.
+        #
+        # We use `ip route get` on the vrr network to see which
+        # device the kernel returns. if it is the mac vlan device,
+        # flap the macvlan device to adjust the routing table entry.
+        #
+        # flapping the macvlan device makes sure the macvlan
+        # connected route goes through delete + add, hence adjusting
+        # the order in the routing table.
+        #
+        try:
+            self.logger.info('%s: checking route entry ...' %ifaceobj.name)
+            ip = IPNetwork(addr)
+
+            # we don't support ip6 route fix yet
+            if type(ip) == IPv6Network:
+                return
+
+            route_prefix = '%s/%d' %(ip.network, ip.prefixlen)
+
+            dev = self.ipcmd.ip_route_get_dev(route_prefix)
+            if dev and dev != ifaceobj.name:
+                self.logger.info('%s: preferred routing entry ' %ifaceobj.name +
+                                 'seems to be of the macvlan dev %s'
+                                 %vifacename +
+                                 ' .. flapping macvlan dev to fix entry.')
+                self.ipcmd.link_down(vifacename)
+                self.ipcmd.link_up(vifacename)
+        except Exception, e:
+            self.logger.debug('%s: fixing route entry failed (%s)'
+                              % (ifaceobj.name, str(e)))
+            pass
+
+    def _handle_vrf_slaves(self, macvlan_ifacename, ifaceobj):
+        vrfname = self.ipcmd.link_get_master(ifaceobj.name)
+        if vrfname:
+            self.ipcmd.link_set(macvlan_ifacename, 'master', vrfname)
+
+    def _get_macs_from_old_config(self, ifaceobj=None):
+        """ This method returns a list of the mac addresses
+        in the address-virtual attribute for the bridge. """
+        maclist = []
+        saved_ifaceobjs = statemanager.statemanager_api.get_ifaceobjs(ifaceobj.name)
+        if not saved_ifaceobjs:
+            return maclist
+        # we need the old saved configs from the statemanager
+        for oldifaceobj in saved_ifaceobjs:
+            if not oldifaceobj.get_attr_value('address-virtual'):
+                continue
+            for av in oldifaceobj.get_attr_value('address-virtual'):
+                macip = av.split()
+                if len(macip) < 2:
+                    self.logger.debug("%s: incorrect old address-virtual attrs '%s'"
+                                      %(oldifaceobj.name,  av))
+                    continue
+                maclist.append(macip[0])
+        return maclist
+
+    def _apply_address_config(self, ifaceobj, address_virtual_list):
+        purge_existing = False if ifupdownflags.flags.PERFMODE else True
+
+        lower_iface_mtu = update_mtu = None
+        if ifupdownconfig.config.get('adjust_logical_dev_mtu', '1') != '0':
+            if ifaceobj.lowerifaces and address_virtual_list:
+                update_mtu = True
+
+        hwaddress = []
+        self.ipcmd.batch_start()
+        av_idx = 0
+        macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
+        for av in address_virtual_list:
+            av_attrs = av.split()
+            if len(av_attrs) < 2:
+                self.log_error("%s: incorrect address-virtual attrs '%s'"
+                               %(ifaceobj.name,  av), ifaceobj,
+                               raise_error=False)
+                av_idx += 1
+                continue
+
+            mac = av_attrs[0]
+            if not self.check_mac_address(ifaceobj, mac):
+                continue
+            # Create a macvlan device on this device and set the virtual
+            # router mac and ip on it
+            link_created = False
+            macvlan_ifacename = '%s%d' %(macvlan_prefix, av_idx)
+            if not self.ipcmd.link_exists(macvlan_ifacename):
+                try:
+                    netlink.link_add_macvlan(ifaceobj.name, macvlan_ifacename)
+                except:
+                    self.ipcmd.link_add_macvlan(ifaceobj.name, macvlan_ifacename)
+                link_created = True
+
+            # first thing we need to handle vrf enslavement
+            if (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE):
+                self._handle_vrf_slaves(macvlan_ifacename, ifaceobj)
+
+            ips = av_attrs[1:]
+            if mac != 'None':
+                mac = mac.lower()
+                # customer could have used UPPERCASE for MAC
+                self.ipcmd.link_set_hwaddress(macvlan_ifacename, mac)
+                hwaddress.append(mac)
+            self.ipcmd.addr_add_multiple(ifaceobj, macvlan_ifacename, ips,
+                                         purge_existing)
+
+            # If link existed before, flap the link
+            if not link_created:
+                self._fix_connected_route(ifaceobj, macvlan_ifacename,
+                                          ips[0])
+                if update_mtu:
+                    lower_iface_mtu = self.ipcmd.link_get_mtu(ifaceobj.name, refresh=True)
+                    update_mtu = False
+
+                if lower_iface_mtu and lower_iface_mtu != self.ipcmd.link_get_mtu(macvlan_ifacename, refresh=True):
+                    try:
+                        self.ipcmd.link_set_mtu(macvlan_ifacename,
+                                                lower_iface_mtu)
+                    except Exception as e:
+                        self.logger.info('%s: failed to set mtu %s: %s' %
+                                         (macvlan_ifacename, lower_iface_mtu, e))
+
+                # set macvlan device to up in anycase.
+                # since we auto create them here..we are responsible
+                # to bring them up here in the case they were brought down
+                # by some other entity in the system.
+                netlink.link_set_updown(macvlan_ifacename, "up")
+            else:
+                try:
+                    self.ipcmd.fix_ipv6_route_metric(ifaceobj, macvlan_ifacename, ips)
+                except Exception as e:
+                    self.logger.debug('fix_vrf_slave_ipv6_route_metric: failed: %s' % e)
+
+            # Disable IPv6 duplicate address detection on VRR interfaces
+            for key, sysval in { 'accept_dad' : '0', 'dad_transmits' : '0' }.iteritems():
+                syskey = 'net.ipv6.conf.%s.%s' % (macvlan_ifacename, key)
+                if self.sysctl_get(syskey) != sysval:
+                    self.sysctl_set(syskey, sysval)
+
+            av_idx += 1
+        self.ipcmd.batch_commit()
+
+        # check the statemanager for old configs.
+        # We need to remove only the previously configured FDB entries
+        oldmacs = self._get_macs_from_old_config(ifaceobj)
+        # get a list of fdbs in old that are not in new config meaning they should
+        # be removed since they are gone from the config
+        removed_macs = [mac for mac in oldmacs if mac.lower() not in hwaddress]
+        self._remove_addresses_from_bridge(ifaceobj, removed_macs)
+        # if ifaceobj is a bridge and bridge is a vlan aware bridge
+        # add the vid to the bridge
+        self._add_addresses_to_bridge(ifaceobj, hwaddress)
+
+    def _remove_running_address_config(self, ifaceobj):
+        if not self.ipcmd.link_exists(ifaceobj.name):
+            return
+        hwaddress = []
+        self.ipcmd.batch_start()
+        macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
+        for macvlan_ifacename in glob.glob("/sys/class/net/%s*" %macvlan_prefix):
+            macvlan_ifacename = os.path.basename(macvlan_ifacename)
+            if not self.ipcmd.link_exists(macvlan_ifacename):
+                continue
+            hwaddress.append(self.ipcmd.link_get_hwaddress(macvlan_ifacename))
+            self.ipcmd.link_delete(os.path.basename(macvlan_ifacename))
+            # XXX: Also delete any fdb addresses. This requires, checking mac address
+            # on individual macvlan interfaces and deleting the vlan from that.
+        self.ipcmd.batch_commit()
+        if any(hwaddress):
+            self._remove_addresses_from_bridge(ifaceobj, hwaddress)
+
+    def _remove_address_config(self, ifaceobj, address_virtual_list=None):
+        if not address_virtual_list:
+            self._remove_running_address_config(ifaceobj)
+            return
+
+        if not self.ipcmd.link_exists(ifaceobj.name):
+            return
+        hwaddress = []
+        self.ipcmd.batch_start()
+        av_idx = 0
+        macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
+        for av in address_virtual_list:
+            av_attrs = av.split()
+            if len(av_attrs) < 2:
+                self.log_error("%s: incorrect address-virtual attrs '%s'"
+                               %(ifaceobj.name,  av), ifaceobj,
+                               raise_error=False)
+                av_idx += 1
+                continue
+
+            # Delete the macvlan device on this device
+            macvlan_ifacename = '%s%d' %(macvlan_prefix, av_idx)
+            self.ipcmd.link_delete(os.path.basename(macvlan_ifacename))
+            if av_attrs[0] != 'None':
+                hwaddress.append(av_attrs[0])
+            av_idx += 1
+        self.ipcmd.batch_commit()
+        self._remove_addresses_from_bridge(ifaceobj, hwaddress)
+
+    def check_mac_address(self, ifaceobj, mac):
+        if mac == 'None':
+            return True
+        mac = mac.lower()
+        try:
+            if int(mac.split(":")[0], 16) & 1 :
+                self.logger.error("%s: Multicast bit is set in the virtual mac address '%s'" %(ifaceobj.name, mac))
+                return False
+            return True
+        except Exception:
+            return False
+
+    def _fixup_vrf_enslavements(self, ifaceobj, ifaceobj_getfunc=None):
+        """ This function fixes up address virtual interfaces
+        (macvlans) on vrf slaves. Since this fixup is an overhead,
+        this must be called only in cases when ifupdown2 is
+        called on the vrf device or its slave and not when
+        ifupdown2 is called for all devices. When all
+        interfaces are brought up, the expectation is that
+        the normal path will fix up a vrf device or its slaves"""
+
+        if not ifaceobj_getfunc:
+            return
+        if ((ifaceobj.link_kind & ifaceLinkKind.VRF) and
+            self.ipcmd.link_exists(ifaceobj.name)):
+            # if I am a vrf device and I have slaves
+            # that have address virtual config,
+            # enslave the slaves 'address virtual
+            # interfaces (macvlans)' to myself:
+            running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
+            if running_slaves:
+                # pick up any existing slaves of a vrf device and
+                # look for their upperdevices and enslave them to the
+                # vrf device:
+                for s in running_slaves:
+                    sobjs = ifaceobj_getfunc(s)
+                    if (sobjs and
+                        (sobjs[0].link_privflags & ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE)):
+                        # enslave all its upper devices to
+                        # the vrf device
+                        upperdevs = self.ipcmd.link_get_uppers(sobjs[0].name)
+                        if not upperdevs:
+                            continue
+                        for u in upperdevs:
+                            # skip vrf device which
+                            # will also show up in the
+                            # upper device list
+                            if u == ifaceobj.name:
+                                continue
+                            self.ipcmd.link_set(u, 'master', ifaceobj.name,
+                                                state='up')
+        elif ((ifaceobj.link_privflags & ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE) and
+              (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE) and
+              self.ipcmd.link_exists(ifaceobj.name)):
+            # If I am a vrf slave and I have 'address virtual'
+            # config, make sure my addrress virtual interfaces
+            # (macvlans) are also enslaved to the vrf device
+            vrfname = ifaceobj.get_attr_value_first('vrf')
+            if not vrfname or not self.ipcmd.link_exists(vrfname):
+                return
+            running_uppers = self.ipcmd.link_get_uppers(ifaceobj.name)
+            if not running_uppers:
+                return
+            macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
+            if not macvlan_prefix:
+                return
+            for u in running_uppers:
+                if u == vrfname:
+                    continue
+                if u.startswith(macvlan_prefix):
+                    self.ipcmd.link_set(u, 'master', vrfname,
+                                        state='up')
+
+    def _up(self, ifaceobj, ifaceobj_getfunc=None):
+        if not ifupdownflags.flags.ALL:
+            self._fixup_vrf_enslavements(ifaceobj, ifaceobj_getfunc)
+        address_virtual_list = ifaceobj.get_attr_value('address-virtual')
+        if not address_virtual_list:
+            # XXX: address virtual is not present. In which case,
+            # delete stale macvlan devices.
+            self._remove_address_config(ifaceobj, address_virtual_list)
+            return
+
+        if (ifaceobj.upperifaces and
+            not ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE):
+            self.log_error('%s: invalid placement of address-virtual lines (must be configured under an interface with no upper interfaces or parent interfaces)'
+                % (ifaceobj.name), ifaceobj)
+            return
+
+        if not self.ipcmd.link_exists(ifaceobj.name):
+            return
+        self._apply_address_config(ifaceobj, address_virtual_list)
+
+    def _down(self, ifaceobj, ifaceobj_getfunc=None):
+        try:
+            self._remove_address_config(ifaceobj,
+                         ifaceobj.get_attr_value('address-virtual'))
+        except Exception, e:
+            self.log_warn(str(e))
+
+    def _query_check(self, ifaceobj, ifaceobjcurr):
+        address_virtual_list = ifaceobj.get_attr_value('address-virtual')
+        if not address_virtual_list:
+            return
+        if not self.ipcmd.link_exists(ifaceobj.name):
+            return
+        av_idx = 0
+        macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
+        for address_virtual in address_virtual_list:
+            av_attrs = address_virtual.split()
+            if len(av_attrs) < 2:
+                self.logger.warn("%s: incorrect address-virtual attrs '%s'"
+                             %(ifaceobj.name,  address_virtual))
+                av_idx += 1
+                continue
+
+            # Check if the macvlan device on this interface
+            macvlan_ifacename = '%s%d' %(macvlan_prefix, av_idx)
+            if not self.ipcmd.link_exists(macvlan_ifacename):
+                ifaceobjcurr.update_config_with_status('address-virtual',
+                            '', 1)
+                av_idx += 1
+                continue
+            # Check mac and ip address
+            rhwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename)
+            raddrs = self.ipcmd.get_running_addrs(
+                ifname=macvlan_ifacename,
+                details=False,
+                addr_virtual_ifaceobj=ifaceobj
+            )
+            if not raddrs or not rhwaddress:
+               ifaceobjcurr.update_config_with_status('address-virtual', '', 1)
+               av_idx += 1
+               continue
+            try:
+                av_attrs[0] = ':'.join([i if len(i) == 2 else '0%s' % i
+                                        for i in av_attrs[0].split(':')])
+            except:
+                self.logger.info('%s: %s: invalid value for address-virtual (%s)'
+                                 % (ifaceobj.name,
+                                    macvlan_ifacename,
+                                    ' '.join(av_attrs)))
+            try:
+                if (rhwaddress == av_attrs[0].lower() and
+                    self.ipcmd.compare_user_config_vs_running_state(raddrs, av_attrs[1:]) and
+                    self._check_addresses_in_bridge(ifaceobj, av_attrs[0].lower())):
+                    ifaceobjcurr.update_config_with_status('address-virtual',
+                                                           address_virtual, 0)
+                else:
+                    raddress_virtual = '%s %s' % (rhwaddress, ' '.join(raddrs))
+                    ifaceobjcurr.update_config_with_status('address-virtual',
+                                                           raddress_virtual, 1)
+            except:
+                raddress_virtual = '%s %s' % (rhwaddress, ' '.join(raddrs))
+                ifaceobjcurr.update_config_with_status('address-virtual',
+                                                       raddress_virtual, 1)
+            av_idx += 1
+        return
+
+    def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
+        macvlan_prefix = self._get_macvlan_prefix(ifaceobjrunning)
+        address_virtuals = glob.glob("/sys/class/net/%s*" %macvlan_prefix)
+        for av in address_virtuals:
+            macvlan_ifacename = os.path.basename(av)
+            rhwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename)
+            raddress = self.ipcmd.get_running_addrs(None, macvlan_ifacename)
+            if not raddress:
+                self.logger.warn('%s: no running addresses'
+                                 %ifaceobjrunning.name)
+                raddress = []
+            ifaceobjrunning.update_config('address-virtual',
+                            '%s %s' %(rhwaddress, ''.join(raddress)))
+        return
+
+    _run_ops = {'up' : _up,
+               'down' : _down,
+               'query-checkcurr' : _query_check,
+               'query-running' : _query_running}
+
+    def get_ops(self):
+        """ returns list of ops supported by this module """
+        return self._run_ops.keys()
+
+    def _init_command_handlers(self):
+        if not self.ipcmd:
+            self.ipcmd = LinkUtils()
+
+    def run(self, ifaceobj, operation, query_ifaceobj=None,
+            ifaceobj_getfunc=None, **extra_args):
+        """ run vlan configuration on the interface object passed as argument
+
+        Args:
+            **ifaceobj** (object): iface object
+
+            **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
+                                 'query-running'
+        Kwargs:
+            **query_ifaceobj** (object): query check ifaceobject. This is only
+                valid when op is 'query-checkcurr'. It is an object same as
+                ifaceobj, but contains running attribute values and its config
+                status. The modules can use it to return queried running state
+                of interfaces. status is success if the running state is same
+                as user required state in ifaceobj. error otherwise.
+        """
+        if ifaceobj.type == ifaceType.BRIDGE_VLAN:
+            return
+        op_handler = self._run_ops.get(operation)
+        if not op_handler:
+            return
+        self._init_command_handlers()
+        if operation == 'query-checkcurr':
+            op_handler(self, ifaceobj, query_ifaceobj)
+        else:
+            op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)
diff --git a/ifupdown2/addons/bond.py b/ifupdown2/addons/bond.py
new file mode 100644 (file)
index 0000000..3650a10
--- /dev/null
@@ -0,0 +1,773 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Authors:
+#           Roopa Prabhu, roopa@cumulusnetworks.com
+#           Julien Fortin, julien@cumulusnetworks.com
+#
+
+import os
+
+from sets import Set
+
+try:
+    from ifupdown2.nlmanager.nlmanager import Link
+
+    from ifupdown2.ifupdown.iface import *
+    from ifupdown2.ifupdown.utils import utils
+    from ifupdown2.ifupdown.netlink import netlink
+    from ifupdown2.ifupdown.statemanager import statemanager_api as statemanager
+
+    import ifupdown2.ifupdown.policymanager as policymanager
+    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
+
+    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdown2.ifupdownaddons.modulebase import moduleBase
+except ImportError:
+    from nlmanager.nlmanager import Link
+
+    from ifupdown.iface import *
+    from ifupdown.utils import utils
+    from ifupdown.netlink import netlink
+    from ifupdown.statemanager import statemanager_api as statemanager
+
+    from ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdownaddons.modulebase import moduleBase
+
+    import ifupdown.policymanager as policymanager
+    import ifupdown.ifupdownflags as ifupdownflags
+
+class bond(moduleBase):
+    """  ifupdown2 addon module to configure bond interfaces """
+
+    overrides_ifupdown_scripts = ['ifenslave', ]
+
+    _modinfo = { 'mhelp' : 'bond configuration module',
+                    'attrs' : {
+                    'bond-use-carrier':
+                         {'help' : 'bond use carrier',
+                          'validvals' : ['yes', 'no', '0', '1'],
+                          'default' : 'yes',
+                          'example': ['bond-use-carrier yes']},
+                     'bond-num-grat-arp':
+                         {'help' : 'bond use carrier',
+                          'validrange' : ['0', '255'],
+                          'default' : '1',
+                          'example' : ['bond-num-grat-arp 1']},
+                     'bond-num-unsol-na' :
+                         {'help' : 'bond slave devices',
+                          'validrange' : ['0', '255'],
+                          'default' : '1',
+                          'example' : ['bond-num-unsol-na 1']},
+                     'bond-xmit-hash-policy' :
+                         {'help' : 'bond slave devices',
+                          'validvals' : ['0', 'layer2',
+                                         '1', 'layer3+4',
+                                         '2', 'layer2+3',
+                                         '3', 'encap2+3',
+                                         '4', 'encap3+4'],
+                          'default' : 'layer2',
+                          'example' : ['bond-xmit-hash-policy layer2']},
+                     'bond-miimon' :
+                         {'help' : 'bond miimon',
+                          'validrange' : ['0', '255'],
+                          'default' : '0',
+                          'example' : ['bond-miimon 0']},
+                     'bond-mode' :
+                         {'help': 'bond mode',
+                          'validvals': ['0', 'balance-rr',
+                                        '1', 'active-backup',
+                                        '2', 'balance-xor',
+                                        '3', 'broadcast',
+                                        '4', '802.3ad',
+                                        '5', 'balance-tlb',
+                                        '6', 'balance-alb'],
+                          'default': 'balance-rr',
+                          'example': ['bond-mode 802.3ad']},
+                     'bond-lacp-rate':
+                         {'help' : 'bond lacp rate',
+                          'validvals' : ['0', 'slow', '1', 'fast'],
+                          'default' : '0',
+                          'example' : ['bond-lacp-rate 0']},
+                     'bond-min-links':
+                         {'help' : 'bond min links',
+                          'default' : '0',
+                          'validrange' : ['0', '255'],
+                          'example' : ['bond-min-links 0']},
+                     'bond-ad-sys-priority':
+                         {'help' : '802.3ad system priority',
+                          'default' : '65535',
+                          'validrange' : ['0', '65535'],
+                          'example' : ['bond-ad-sys-priority 65535'],
+                          'deprecated' : True,
+                          'new-attribute' : 'bond-ad-actor-sys-prio'},
+                     'bond-ad-actor-sys-prio':
+                         {'help' : '802.3ad system priority',
+                          'default' : '65535',
+                          'validrange' : ['0', '65535'],
+                          'example' : ['bond-ad-actor-sys-prio 65535']},
+                     'bond-ad-sys-mac-addr':
+                         {'help' : '802.3ad system mac address',
+                          'validvals': ['<mac>', ],
+                         'example' : ['bond-ad-sys-mac-addr 00:00:00:00:00:00'],
+                         'deprecated' : True,
+                         'new-attribute' : 'bond-ad-actor-system'},
+                     'bond-ad-actor-system':
+                         {'help' : '802.3ad system mac address',
+                          'validvals': ['<mac>', ],
+                         'example' : ['bond-ad-actor-system 00:00:00:00:00:00'],},
+                     'bond-lacp-bypass-allow':
+                         {'help' : 'allow lacp bypass',
+                          'validvals' : ['yes', 'no', '0', '1'],
+                          'default' : 'no',
+                          'example' : ['bond-lacp-bypass-allow no']},
+                     'bond-slaves' :
+                        {'help' : 'bond slaves',
+                         'required' : True,
+                         'multivalue' : True,
+                         'validvals': ['<interface-list>'],
+                         'example' : ['bond-slaves swp1 swp2',
+                                      'bond-slaves glob swp1-2',
+                                      'bond-slaves regex (swp[1|2)'],
+                         'aliases': ['bond-ports']},
+                     'bond-updelay' :
+                        {'help' : 'bond updelay',
+                         'default' : '0',
+                         'validrange' : ['0', '65535'],
+                         'example' : ['bond-updelay 100']},
+                     'bond-downdelay':
+                        {'help' : 'bond downdelay',
+                         'default' : '0',
+                         'validrange' : ['0', '65535'],
+                         'example' : ['bond-downdelay 100']}
+                    }}
+
+    _bond_attr_netlink_map = {
+        'bond-mode': Link.IFLA_BOND_MODE,
+        'bond-miimon': Link.IFLA_BOND_MIIMON,
+        'bond-use-carrier': Link.IFLA_BOND_USE_CARRIER,
+        'bond-lacp-rate': Link.IFLA_BOND_AD_LACP_RATE,
+        'bond-xmit-hash-policy': Link.IFLA_BOND_XMIT_HASH_POLICY,
+        'bond-min-links': Link.IFLA_BOND_MIN_LINKS,
+        'bond-num-grat-arp': Link.IFLA_BOND_NUM_PEER_NOTIF,
+        'bond-num-unsol-na': Link.IFLA_BOND_NUM_PEER_NOTIF,
+        'bond-ad-sys-mac-addr': Link.IFLA_BOND_AD_ACTOR_SYSTEM,
+        'bond-ad-actor-system': Link.IFLA_BOND_AD_ACTOR_SYSTEM,
+        'bond-ad-sys-priority': Link.IFLA_BOND_AD_ACTOR_SYS_PRIO,
+        'bond-ad-actor-sys-prio': Link.IFLA_BOND_AD_ACTOR_SYS_PRIO,
+        'bond-lacp-bypass-allow': Link.IFLA_BOND_AD_LACP_BYPASS,
+        'bond-updelay': Link.IFLA_BOND_UPDELAY,
+        'bond-downdelay': Link.IFLA_BOND_DOWNDELAY
+    }
+
+    # ifquery-check attr dictionary with callable object to translate user data to netlink format
+    _bond_attr_ifquery_check_translate_func = {
+        Link.IFLA_BOND_MODE: lambda x: Link.ifla_bond_mode_tbl[x],
+        Link.IFLA_BOND_MIIMON: int,
+        Link.IFLA_BOND_USE_CARRIER: utils.get_boolean_from_string,
+        Link.IFLA_BOND_AD_LACP_RATE: lambda x: int(utils.get_boolean_from_string(x)),
+        Link.IFLA_BOND_XMIT_HASH_POLICY: lambda x: Link.ifla_bond_xmit_hash_policy_tbl[x],
+        Link.IFLA_BOND_MIN_LINKS: int,
+        Link.IFLA_BOND_NUM_PEER_NOTIF: int,
+        Link.IFLA_BOND_AD_ACTOR_SYSTEM: str,
+        Link.IFLA_BOND_AD_ACTOR_SYS_PRIO: int,
+        Link.IFLA_BOND_AD_LACP_BYPASS: lambda x: int(utils.get_boolean_from_string(x)),
+        Link.IFLA_BOND_UPDELAY: int,
+        Link.IFLA_BOND_DOWNDELAY: int
+    }
+
+    # ifup attr list with callable object to translate user data to netlink format
+    # in the future this can be moved to a dictionary, whenever we detect that some
+    # netlink capabilities are missing we can dynamically remove them from the dict.
+    _bond_attr_set_list = (
+        ('bond-mode', Link.IFLA_BOND_MODE, lambda x: Link.ifla_bond_mode_tbl[x]),
+        ('bond-xmit-hash-policy', Link.IFLA_BOND_XMIT_HASH_POLICY, lambda x: Link.ifla_bond_xmit_hash_policy_tbl[x]),
+        ('bond-miimon', Link.IFLA_BOND_MIIMON, int),
+        ('bond-min-links', Link.IFLA_BOND_MIN_LINKS, int),
+        ('bond-num-grat-arp', Link.IFLA_BOND_NUM_PEER_NOTIF, int),
+        ('bond-num-unsol-na', Link.IFLA_BOND_NUM_PEER_NOTIF, int),
+        ('bond-ad-sys-priority', Link.IFLA_BOND_AD_ACTOR_SYS_PRIO, int),
+        ('bond-ad-actor-sys-prio', Link.IFLA_BOND_AD_ACTOR_SYS_PRIO, int),
+        ('bond-updelay', Link.IFLA_BOND_UPDELAY, int),
+        ('bond-downdelay', Link.IFLA_BOND_DOWNDELAY, int),
+        ('bond-use-carrier', Link.IFLA_BOND_USE_CARRIER, lambda x: int(utils.get_boolean_from_string(x))),
+        ('bond-lacp-rate', Link.IFLA_BOND_AD_LACP_RATE, lambda x: int(utils.get_boolean_from_string(x))),
+        ('bond-lacp-bypass-allow', Link.IFLA_BOND_AD_LACP_BYPASS, lambda x: int(utils.get_boolean_from_string(x))),
+        ('bond-ad-sys-mac-addr', Link.IFLA_BOND_AD_ACTOR_SYSTEM, str),
+        ('bond-ad-actor-system', Link.IFLA_BOND_AD_ACTOR_SYSTEM, str),
+    )
+
+    def __init__(self, *args, **kargs):
+        moduleBase.__init__(self, *args, **kargs)
+        self.ipcmd = None
+        self.bondcmd = None
+
+        if not os.path.exists('/sys/class/net/bonding_masters'):
+            utils.exec_command('modprobe -q bonding')
+
+    @staticmethod
+    def get_bond_slaves(ifaceobj):
+        slaves = ifaceobj.get_attr_value_first('bond-slaves')
+        if not slaves:
+            slaves = ifaceobj.get_attr_value_first('bond-ports')
+        return slaves
+
+    def _is_bond(self, ifaceobj):
+        # at first link_kind is not set but once ifupdownmain
+        # calls get_dependent_ifacenames link_kind is set to BOND
+        if ifaceobj.link_kind & ifaceLinkKind.BOND or self.get_bond_slaves(ifaceobj):
+            return True
+        return False
+
+    def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
+        """ Returns list of interfaces dependent on ifaceobj """
+
+        if not self._is_bond(ifaceobj):
+            return None
+        slave_list = self.parse_port_list(ifaceobj.name,
+                                          self.get_bond_slaves(ifaceobj),
+                                          ifacenames_all)
+        ifaceobj.dependency_type = ifaceDependencyType.MASTER_SLAVE
+        # Also save a copy for future use
+        ifaceobj.priv_data = list(slave_list)
+        if ifaceobj.link_type != ifaceLinkType.LINK_NA:
+           ifaceobj.link_type = ifaceLinkType.LINK_MASTER
+        ifaceobj.link_kind |= ifaceLinkKind.BOND
+        ifaceobj.role |= ifaceRole.MASTER
+
+        return slave_list
+
+    def syntax_check(self, ifaceobj, ifaceobj_getfunc):
+        return self.syntax_check_updown_delay(ifaceobj)
+
+    def get_dependent_ifacenames_running(self, ifaceobj):
+        self._init_command_handlers()
+        return self.bondcmd.bond_get_slaves(ifaceobj.name)
+
+    def _get_slave_list(self, ifaceobj):
+        """ Returns slave list present in ifaceobj config """
+
+        # If priv data already has slave list use that first.
+        if ifaceobj.priv_data:
+            return ifaceobj.priv_data
+        slaves = self.get_bond_slaves(ifaceobj)
+        if slaves:
+            return self.parse_port_list(ifaceobj.name, slaves)
+        else:
+            return None
+
+    def _is_clag_bond(self, ifaceobj):
+        if self.get_bond_slaves(ifaceobj):
+            attrval = ifaceobj.get_attr_value_first('clag-id')
+            if attrval and attrval != '0':
+                return True
+        return False
+
+    def _add_slaves(self, ifaceobj, ifaceobj_getfunc=None):
+        runningslaves = []
+
+        slaves = self._get_slave_list(ifaceobj)
+        if not slaves:
+            self.logger.debug('%s: no slaves found' %ifaceobj.name)
+            return
+
+        if not ifupdownflags.flags.PERFMODE:
+            runningslaves = self.bondcmd.bond_get_slaves(ifaceobj.name)
+
+        clag_bond = self._is_clag_bond(ifaceobj)
+
+        for slave in Set(slaves).difference(Set(runningslaves)):
+            if (not ifupdownflags.flags.PERFMODE and
+                not self.ipcmd.link_exists(slave)):
+                    self.log_error('%s: skipping slave %s, does not exist'
+                                   %(ifaceobj.name, slave), ifaceobj,
+                                     raise_error=False)
+                    continue
+            link_up = False
+            if self.ipcmd.is_link_up(slave):
+                netlink.link_set_updown(slave, "down")
+                link_up = True
+            # If clag bond place the slave in a protodown state; clagd
+            # will protoup it when it is ready
+            if clag_bond:
+                try:
+                    netlink.link_set_protodown(slave, "on")
+                except Exception, e:
+                    self.logger.error('%s: %s' % (ifaceobj.name, str(e)))
+            netlink.link_set_master(slave, ifaceobj.name)
+            if link_up or ifaceobj.link_type != ifaceLinkType.LINK_NA:
+               try:
+                    if (ifaceobj_getfunc(slave)[0].link_privflags &
+                        ifaceLinkPrivFlags.KEEP_LINK_DOWN):
+                        netlink.link_set_updown(slave, "down")
+                    else:
+                        netlink.link_set_updown(slave, "up")
+               except Exception, e:
+                    self.logger.debug('%s: %s' % (ifaceobj.name, str(e)))
+                    pass
+
+        if runningslaves:
+            for s in runningslaves:
+                if s not in slaves:
+                    self.bondcmd.bond_remove_slave(ifaceobj.name, s)
+                    if clag_bond:
+                        try:
+                            netlink.link_set_protodown(s, "off")
+                        except Exception, e:
+                            self.logger.error('%s: %s' % (ifaceobj.name, str(e)))
+                else:
+                    # apply link-down config changes on running slaves
+                    try:
+                        link_up = self.ipcmd.is_link_up(s)
+                        config_link_down = (ifaceobj_getfunc(s)[0].link_privflags &
+                                            ifaceLinkPrivFlags.KEEP_LINK_DOWN)
+                        if (config_link_down and link_up):
+                            netlink.link_set_updown(s, "down")
+                        elif (not config_link_down and not link_up):
+                            netlink.link_set_updown(s, "up")
+                    except Exception, e:
+                        self.logger.warn('%s: %s' % (ifaceobj.name, str(e)))
+
+    def _check_updown_delay_log(self, ifaceobj, attr_name, value):
+        ifaceobj.status = ifaceStatus.ERROR
+        self.logger.error('%s: unable to set %s %s as MII link monitoring is '
+                          'disabled' % (ifaceobj.name, attr_name, value))
+        # return False to notify syntax_check that an error has been logged
+        return False
+
+    def syntax_check_updown_delay(self, ifaceobj):
+        result      = True
+        updelay     = ifaceobj.get_attr_value_first('bond-updelay')
+        downdelay   = ifaceobj.get_attr_value_first('bond-downdelay')
+
+        if not updelay and not downdelay:
+            return True
+
+        try:
+            miimon = int(ifaceobj.get_attr_value_first('bond-miimon'))
+        except:
+            try:
+                miimon = int(policymanager.policymanager_api.get_iface_default(
+                    module_name=self.__class__.__name__,
+                    ifname=ifaceobj.name,
+                    attr='bond-miimon'))
+            except:
+                miimon = 0
+
+        if not miimon:
+            # self._check_updown_delay_log returns False no matter what
+            if updelay and int(updelay):
+                result = self._check_updown_delay_log(ifaceobj, 'bond-updelay', updelay)
+            if downdelay and int(downdelay):
+                result = self._check_updown_delay_log(ifaceobj, 'bond-downdelay', downdelay)
+
+        return result
+
+    _bond_updown_delay_nl_list = (
+        (Link.IFLA_BOND_UPDELAY, 'bond-updelay'),
+        (Link.IFLA_BOND_DOWNDELAY, 'bond-downdelay')
+    )
+
+    def check_updown_delay_nl(self, link_exists, ifaceobj, ifla_info_data):
+        """
+            IFLA_BOND_MIIMON
+            Specifies the time, in milliseconds, to wait before enabling a slave
+            after a link recovery has been detected. This option is only valid
+            for the miimon link monitor. The updelay value should be a multiple
+            of the miimon value; if not, it will be rounded down to the nearest
+            multiple. The default value is 0.
+
+            This ifla_bond_miimon code should be move to get_ifla_bond_attr_from_user_config
+            but we need to know if the operation was successful to update the cache accordingly
+        """
+        ifla_bond_miimon = ifla_info_data.get(Link.IFLA_BOND_MIIMON)
+        if link_exists and ifla_bond_miimon is None:
+            ifla_bond_miimon = self.bondcmd.link_cache_get([ifaceobj.name, 'linkinfo', Link.IFLA_BOND_MIIMON])
+
+        if ifla_bond_miimon == 0:
+            for nl_attr, attr_name in self._bond_updown_delay_nl_list:
+                delay = ifla_info_data.get(nl_attr)
+                # if up-down-delay exists we need to remove it, if non zero log error
+                if delay is not None:
+                    if delay > 0:
+                        self._check_updown_delay_log(ifaceobj, attr_name, delay)
+                    del ifla_info_data[nl_attr]
+            return True
+        return False
+
+    _bond_lacp_attrs = (
+        (Link.IFLA_BOND_AD_LACP_RATE, 'bond-lacp-rate'),
+        (Link.IFLA_BOND_AD_LACP_BYPASS, 'bond-lacp-bypass')
+    )
+
+    def _check_bond_mode_user_config(self, ifname, link_exists, ifla_info_data):
+        ifla_bond_mode = ifla_info_data.get(Link.IFLA_BOND_MODE)
+        if ifla_bond_mode is None and link_exists:
+            ifla_bond_mode = self.bondcmd.link_cache_get([ifname, 'linkinfo', Link.IFLA_BOND_MODE])
+            # in this case the link already exists (we have a cached value):
+            # if IFLA_BOND_MODE is not present in ifla_info_data it means:
+            #   - that bond-mode was present in the user config and didn't change
+            #   - never was in the user config so bond mode should be the system default value
+            #   - was removed from the stanza so we might have to reset it to default value
+            # nevertheless we need to add it back to the ifla_info_data dict to check
+            # if we need to reset the mode to system default
+            ifla_info_data[Link.IFLA_BOND_MODE] = ifla_bond_mode
+
+        if ifla_bond_mode == 4:  # 802.3ad
+            min_links = ifla_info_data.get(Link.IFLA_BOND_MIN_LINKS)
+            if min_links is None:
+                min_links = self.bondcmd.link_cache_get([ifname, 'linkinfo', Link.IFLA_BOND_MIN_LINKS])
+            # get_min_links_nl may return None so we need to strictly check 0
+            if min_links == 0:
+                self.logger.warn('%s: attribute bond-min-links is set to \'0\'' % ifname)
+        else:
+            # IFLA_BOND_AD_LACP_RATE and IFLA_BOND_AD_LACP_BYPASS only for 802.3ad mode (4)
+            for nl_attr, attr_name in self._bond_lacp_attrs:
+                if nl_attr in ifla_info_data:
+                    self.logger.info('%s: ignoring %s: only available for 802.3ad mode (4)' % (ifname, attr_name))
+                    del ifla_info_data[nl_attr]
+
+    @staticmethod
+    def get_saved_ifaceobj(link_exists, ifname):
+        if link_exists:
+            old_config = statemanager.get_ifaceobjs(ifname)
+            if old_config:
+                return old_config[0]
+        return None
+
+    def get_ifla_bond_attr_from_user_config(self, ifaceobj, link_exists):
+        """
+            Potential issue: if a user load the bond driver with custom
+            default values (say bond-mode 3), ifupdown2 has no knowledge
+            of these default values.
+            At bond creation everything should work, bonds will be created
+            with mode 3 (even if not specified under the stanza).
+            But, for example: if the user specifies a value under bond-mode
+            and later on the user removes the bond-mode line from the stanza
+            we will detect it and reset to MODINFO: BOND-MODE: DEFAULT aka 0
+            which is not the real default value that the user may expect.
+        """
+        ifname          = ifaceobj.name
+        ifla_info_data  = OrderedDict()
+        old_config      = self.get_saved_ifaceobj(link_exists, ifname)
+
+        # for each bond attribute we fetch the user configuration
+        # if no configuration is provided we look for a config in policy files
+        for attr_name, netlink_attr, func_ptr in self._bond_attr_set_list:
+            cached_value        = None
+            user_config         = ifaceobj.get_attr_value_first(attr_name)
+
+            if not user_config:
+                user_config = policymanager.policymanager_api.get_iface_default(
+                    module_name=self.__class__.__name__,
+                    ifname=ifname,
+                    attr=attr_name)
+                if user_config:
+                    self.logger.debug('%s: %s %s: extracted from policy files'
+                                      % (ifname, attr_name, user_config))
+
+            # no policy override, do we need to reset an attr to default value?
+            if not user_config and old_config and old_config.get_attr_value_first(attr_name):
+                # if the link already exists but the value is set
+                # (potentially removed from the stanza, we need to reset it to default)
+                # might not work for specific cases, see explanation at the top of this function :)
+                user_config = self.get_attr_default_value(attr_name)
+                if user_config:
+                    self.logger.debug('%s: %s: removed from stanza, resetting to default value: %s'
+                                      % (ifname, attr_name, user_config))
+
+            if user_config:
+                try:
+                    nl_value = func_ptr(user_config.lower())
+
+                    if link_exists:
+                        cached_value = self.bondcmd.link_cache_get([ifname, 'linkinfo', netlink_attr])
+
+                    if link_exists and cached_value is None:
+                        # the link already exists but we don't have any value
+                        # cached for this attr, it probably means that the
+                        # capability is not available on this system (i.e old kernel)
+                        self.logger.debug('%s: ignoring %s %s: capability '
+                                          'probably not supported on this system'
+                                          % (ifname, attr_name, user_config))
+                        continue
+                    elif link_exists:
+                        # there should be a cached value if the link already exists
+                        if cached_value == nl_value:
+                            # if the user value is already cached: continue
+                            continue
+
+                    # else: the link doesn't exist so we create the bond with
+                    # all the user/policy defined values without extra checks
+                    ifla_info_data[netlink_attr] = nl_value
+
+                    if cached_value is not None:
+                        self.logger.info('%s: set %s %s (cache %s)' % (ifname, attr_name, user_config, cached_value))
+                    else:
+                        self.logger.info('%s: set %s %s' % (ifname, attr_name, user_config))
+
+                except KeyError:
+                    self.logger.warning('%s: invalid %s value %s' % (ifname, attr_name, user_config))
+
+        self._check_bond_mode_user_config(ifname, link_exists, ifla_info_data)
+        return ifla_info_data
+
+    _bond_down_nl_attributes_list = (
+        Link.IFLA_BOND_MODE,
+        Link.IFLA_BOND_XMIT_HASH_POLICY,
+        Link.IFLA_BOND_AD_LACP_RATE,
+        Link.IFLA_BOND_MIN_LINKS
+    )
+
+    def _should_down_bond(self, ifla_info_data):
+        for nl_attr in self._bond_down_nl_attributes_list:
+            if nl_attr in ifla_info_data:
+                return True
+        return False
+
+    def should_update_bond_mode(self, ifaceobj, ifname, is_link_up, ifla_info_data):
+        # if bond-mode was changed the bond needs to be brought
+        # down and slaves un-slaved before bond mode is changed.
+        cached_bond_mode = self.bondcmd.link_cache_get([ifname, 'linkinfo', Link.IFLA_BOND_MODE])
+        ifla_bond_mode = ifla_info_data.get(Link.IFLA_BOND_MODE)
+
+        # bond-mode was changed or is not specified
+        if ifla_bond_mode is not None:
+            if ifla_bond_mode != cached_bond_mode:
+                self.logger.info('%s: bond mode changed to %s: running ops on bond and slaves'
+                                 % (ifname, ifla_bond_mode))
+                if is_link_up:
+                    netlink.link_set_updown(ifname, 'down')
+                    is_link_up = False
+
+                for lower_dev in ifaceobj.lowerifaces:
+                    netlink.link_set_nomaster(lower_dev)
+
+                self.bondcmd.cache_delete([ifname, 'linkinfo', 'slaves'])
+            else:
+                # bond-mode user config value is the current running(cached) value
+                # no need to reset it again we can ignore this attribute
+                del ifla_info_data[Link.IFLA_BOND_MODE]
+
+        return is_link_up
+
+    def create_or_set_bond_config(self, ifaceobj):
+        ifname          = ifaceobj.name
+        link_exists     = self.ipcmd.link_exists(ifname)
+        is_link_up      = self.ipcmd.is_link_up(ifname) if link_exists else False
+        ifla_info_data  = self.get_ifla_bond_attr_from_user_config(ifaceobj, link_exists)
+
+        remove_delay_from_cache = self.check_updown_delay_nl(link_exists, ifaceobj, ifla_info_data)
+
+        # if link exists: down link if specific attributes are specified
+        if link_exists:
+            # did bond-mode changed?
+            is_link_up = self.should_update_bond_mode(ifaceobj, ifname, is_link_up, ifla_info_data)
+
+            # if specific attributes need to be set we need to down the bond first
+            if ifla_info_data and is_link_up:
+                if self._should_down_bond(ifla_info_data):
+                    netlink.link_set_updown(ifname, 'down')
+                    is_link_up = False
+
+        if link_exists and not ifla_info_data:
+            # if the bond already exists and no attrs need to be set
+            # ignore the netlink call
+            self.logger.info('%s: already exists, no change detected' % ifname)
+        else:
+            try:
+                netlink.link_add_set(kind='bond', ifname=ifname, ifla_info_data=ifla_info_data)
+            except Exception as e:
+                # defensive code
+                # if anything happens, we try to set up the bond with the sysfs api
+                self.logger.debug('%s: bond setup: %s' % (ifname, str(e)))
+                self.create_or_set_bond_config_sysfs(ifaceobj, ifla_info_data)
+
+            if remove_delay_from_cache:
+                # making sure up/down delay attributes are set to 0 before caching
+                # this can be removed when moving to a nllistener/live cache
+                ifla_info_data[Link.IFLA_BOND_UPDELAY] = 0
+                ifla_info_data[Link.IFLA_BOND_DOWNDELAY] = 0
+
+            # if link_add doesn't raise we can update the cache, the future
+            # netlink listener will update the cache based on the kernel response
+            for key, value in ifla_info_data.items():
+                self.bondcmd.cache_update([ifname, 'linkinfo', key], value)
+
+        if link_exists and ifla_info_data and not is_link_up:
+            netlink.link_set_updown(ifname, 'up')
+
+    def create_or_set_bond_config_sysfs(self, ifaceobj, ifla_info_data):
+        if not self.ipcmd.link_exists(ifaceobj.name):
+            self.bondcmd.create_bond(ifaceobj.name)
+        self.bondcmd.bond_set_attrs_nl(ifaceobj.name, ifla_info_data)
+
+    def _up(self, ifaceobj, ifaceobj_getfunc=None):
+        try:
+            self.create_or_set_bond_config(ifaceobj)
+            self._add_slaves(ifaceobj, ifaceobj_getfunc)
+        except Exception, e:
+            self.log_error(str(e), ifaceobj)
+
+    def _down(self, ifaceobj, ifaceobj_getfunc=None):
+        try:
+            netlink.link_del(ifaceobj.name)
+            self.bondcmd.cache_delete([ifaceobj.name])
+        except Exception as e:
+            self.log_warn('%s: %s' % (ifaceobj.name, str(e)))
+
+    def _query_check_bond_slaves(self, ifaceobjcurr, attr, user_bond_slaves, running_bond_slaves):
+        query = 1
+
+        if user_bond_slaves and running_bond_slaves:
+            if not set(user_bond_slaves).symmetric_difference(running_bond_slaves):
+                query = 0
+
+        # we want to display the same bond-slaves list as provided
+        # in the interfaces file but if this list contains regexes or
+        # globs, for now, we won't try to change it.
+        if 'regex' in user_bond_slaves or 'glob' in user_bond_slaves:
+            user_bond_slaves = running_bond_slaves
+        else:
+            ordered = []
+            for slave in user_bond_slaves:
+                if slave in running_bond_slaves:
+                    ordered.append(slave)
+            user_bond_slaves = ordered
+        ifaceobjcurr.update_config_with_status(attr, ' '.join(user_bond_slaves) if user_bond_slaves else 'None', query)
+
+    def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
+        if not self.bondcmd.bond_exists(ifaceobj.name):
+            self.logger.debug('bond iface %s does not exist' % ifaceobj.name)
+            return
+
+        iface_attrs = self.dict_key_subset(ifaceobj.config, self.get_mod_attrs())
+        if not iface_attrs:
+            return
+
+        # remove bond-slaves and bond-ports from the list,
+        # because there aren't any ifla_info_data netlink attr for slaves
+        # an exception is raised when index is not found, so query_slaves will stay False
+        query_slaves = False
+
+        user_bond_slaves    = None
+        running_bond_slaves = None
+        try:
+            del iface_attrs[iface_attrs.index('bond-slaves')]
+
+            # if user specified bond-slaves we need to display it
+            query_slaves = True
+            if not user_bond_slaves:
+                user_bond_slaves = self._get_slave_list(ifaceobj)
+                running_bond_slaves = self.bondcmd.bond_get_slaves(ifaceobj.name)
+
+            self._query_check_bond_slaves(ifaceobjcurr, 'bond-slaves', user_bond_slaves, running_bond_slaves)
+        except:
+            pass
+        try:
+            del iface_attrs[iface_attrs.index('bond-ports')]
+
+            # if user specified bond-ports we need to display it
+            if not query_slaves and not user_bond_slaves: # if get_slave_list was already called for slaves
+                user_bond_slaves = self._get_slave_list(ifaceobj)
+                running_bond_slaves = self.bondcmd.bond_get_slaves(ifaceobj.name)
+
+            self._query_check_bond_slaves(ifaceobjcurr, 'bond-ports', user_bond_slaves, running_bond_slaves)
+        except:
+            pass
+
+        for attr in iface_attrs:
+            nl_attr         = self._bond_attr_netlink_map[attr]
+            translate_func  = self._bond_attr_ifquery_check_translate_func[nl_attr]
+            current_config  = self.bondcmd.link_cache_get([ifaceobj.name, 'linkinfo', nl_attr])
+            user_config     = ifaceobj.get_attr_value_first(attr)
+
+            if current_config == translate_func(user_config):
+                ifaceobjcurr.update_config_with_status(attr, user_config, 0)
+            else:
+                ifaceobjcurr.update_config_with_status(attr, str(current_config), 1)
+
+    @staticmethod
+    def translate_nl_value_yesno(value):
+        return 'yes' if value else 'no'
+
+    @staticmethod
+    def translate_nl_value_slowfast(value):
+        return 'fast' if value else 'slow'
+
+    def _query_running_attrs(self, bondname):
+        bond_attrs = {
+            'bond-mode': Link.ifla_bond_mode_pretty_tbl.get(self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MODE])),
+            'bond-miimon': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MIIMON]),
+            'bond-use-carrier': self.translate_nl_value_yesno(self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_USE_CARRIER])),
+            'bond-lacp-rate': self.translate_nl_value_slowfast(self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_RATE])),
+            'bond-min-links': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MIN_LINKS]),
+            'bond-ad-actor-system': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYSTEM]),
+            'bond-ad-actor-sys-prio': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYS_PRIO]),
+            'bond-xmit-hash-policy': Link.ifla_bond_xmit_hash_policy_pretty_tbl.get(self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_XMIT_HASH_POLICY])),
+            'bond-lacp-bypass-allow': self.translate_nl_value_yesno(self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_BYPASS])),
+            'bond-num-unsol-na': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF]),
+            'bond-num-grat-arp': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF]),
+            'bond-updelay': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_UPDELAY]),
+            'bond-downdelay': self.bondcmd.link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_DOWNDELAY])
+        }
+        slaves = self.bondcmd.bond_get_slaves(bondname)
+        if slaves:
+            bond_attrs['bond-slaves'] = slaves
+        return bond_attrs
+
+    def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
+        if not self.bondcmd.bond_exists(ifaceobjrunning.name):
+            return
+        bond_attrs = self._query_running_attrs(ifaceobjrunning.name)
+        if bond_attrs.get('bond-slaves'):
+            bond_attrs['bond-slaves'] = ' '.join(bond_attrs.get('bond-slaves'))
+
+        [ifaceobjrunning.update_config(k, str(v))
+         for k, v in bond_attrs.items()
+         if v is not None]
+
+    _run_ops = {
+        'pre-up': _up,
+        'post-down': _down,
+        'query-running': _query_running,
+        'query-checkcurr': _query_check
+    }
+
+    def get_ops(self):
+        """ returns list of ops supported by this module """
+        return self._run_ops.keys()
+
+    def _init_command_handlers(self):
+        if not self.ipcmd:
+            self.ipcmd = self.bondcmd = LinkUtils()
+
+    def run(self, ifaceobj, operation, query_ifaceobj=None,
+            ifaceobj_getfunc=None):
+        """ run bond configuration on the interface object passed as argument
+
+        Args:
+            **ifaceobj** (object): iface object
+
+            **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
+                'query-running'
+
+        Kwargs:
+            **query_ifaceobj** (object): query check ifaceobject. This is only
+                valid when op is 'query-checkcurr'. It is an object same as
+                ifaceobj, but contains running attribute values and its config
+                status. The modules can use it to return queried running state
+                of interfaces. status is success if the running state is same
+                as user required state in ifaceobj. error otherwise.
+        """
+        op_handler = self._run_ops.get(operation)
+        if not op_handler:
+            return
+        if operation != 'query-running' and not self._is_bond(ifaceobj):
+            return
+        self._init_command_handlers()
+        if operation == 'query-checkcurr':
+            op_handler(self, ifaceobj, query_ifaceobj,
+                       ifaceobj_getfunc=ifaceobj_getfunc)
+        else:
+            op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)
diff --git a/ifupdown2/addons/bridge.py b/ifupdown2/addons/bridge.py
new file mode 100644 (file)
index 0000000..02cbebf
--- /dev/null
@@ -0,0 +1,3146 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+import re
+import time
+import itertools
+
+from sets import Set
+from collections import Counter
+
+try:
+    import ifupdown2.ifupdown.exceptions as exceptions
+    import ifupdown2.ifupdown.policymanager as policymanager
+    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
+
+    from ifupdown2.nlmanager.nlmanager import Link
+
+    from ifupdown2.ifupdown.iface import *
+    from ifupdown2.ifupdown.utils import utils
+    from ifupdown2.ifupdown.netlink import netlink
+
+    from ifupdown2.ifupdownaddons.cache import *
+    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdown2.ifupdownaddons.modulebase import moduleBase
+except ImportError:
+    import ifupdown.exceptions as exceptions
+    import ifupdown.policymanager as policymanager
+    import ifupdown.ifupdownflags as ifupdownflags
+
+    from nlmanager.nlmanager import Link
+
+    from ifupdown.iface import *
+    from ifupdown.utils import utils
+    from ifupdown.netlink import netlink
+
+    from ifupdownaddons.cache import *
+    from ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdownaddons.modulebase import moduleBase
+
+
+class bridgeFlags:
+    PORT_PROCESSED = 0x1
+    PORT_PROCESSED_OVERRIDE = 0x2
+
+
+class bridge(moduleBase):
+    """  ifupdown2 addon module to configure linux bridges """
+
+    _modinfo = { 'mhelp' : 'Bridge configuration module. Supports both ' +
+                    'vlan aware and non vlan aware bridges. For the vlan ' +
+                    'aware bridge, the port specific attributes must be ' +
+                    'specified under the port. And for vlan unaware bridge ' +
+                    'port specific attributes must be specified under the ' +
+                    'bridge.',
+                 'attrs' : {
+                   'bridge-vlan-aware' :
+                        {'help' : 'vlan aware bridge. Setting this ' +
+                                  'attribute to yes enables vlan filtering' +
+                                  ' on the bridge',
+                         'validvals' : ['yes', 'no'],
+                         'example' : ['bridge-vlan-aware yes/no'],
+                         'default': 'no'
+                         },
+                   'bridge-ports' :
+                        {'help' : 'bridge ports',
+                         'multivalue' : True,
+                         'required' : True,
+                         'validvals': ['<interface-list>'],
+                         'example' : ['bridge-ports swp1.100 swp2.100 swp3.100',
+                                      'bridge-ports glob swp1-3.100',
+                                      'bridge-ports regex (swp[1|2|3].100)']},
+                   'bridge-stp' :
+                        {'help': 'bridge-stp yes/no',
+                         'example' : ['bridge-stp no'],
+                         'validvals' : ['yes', 'on', 'off', 'no'],
+                         'default' : 'no'},
+                   'bridge-bridgeprio' :
+                        {'help': 'bridge priority',
+                         'validrange' : ['0', '65535'],
+                         'example' : ['bridge-bridgeprio 32768'],
+                         'default' : '32768'},
+                   'bridge-ageing' :
+                       {'help': 'bridge ageing',
+                         'validrange' : ['0', '65535'],
+                         'example' : ['bridge-ageing 300'],
+                         'default' : '300'},
+                   'bridge-fd' :
+                        { 'help' : 'bridge forward delay',
+                          'validrange' : ['0', '255'],
+                          'example' : ['bridge-fd 15'],
+                          'default' : '15'},
+                   'bridge-gcint' :
+                        # XXX: recheck values
+                        { 'help' : 'bridge garbage collection interval in secs',
+                          'validrange' : ['0', '255'],
+                          'example' : ['bridge-gcint 4'],
+                          'default' : '4',
+                          'compat' : True,
+                          'deprecated': True},
+                   'bridge-hello' :
+                        { 'help' : 'bridge set hello time',
+                          'validrange' : ['0', '255'],
+                          'example' : ['bridge-hello 2'],
+                          'default' : '2'},
+                   'bridge-maxage' :
+                        { 'help' : 'bridge set maxage',
+                          'validrange' : ['0', '255'],
+                          'example' : ['bridge-maxage 20'],
+                          'default' : '20'},
+                   'bridge-pathcosts' :
+                        { 'help' : 'bridge set port path costs',
+                          'validvals': ['<interface-range-list>'],
+                          'validrange' : ['0', '65535'],
+                          'example' : ['under the port (for vlan aware bridge): bridge-pathcosts 100',
+                                       'under the bridge (for vlan unaware bridge): bridge-pathcosts swp1=100 swp2=100'],
+                          'default' : '100'},
+                   'bridge-portprios' :
+                        { 'help' : 'bridge port prios',
+                          'validvals': ['<interface-range-list>'],
+                          'validrange' : ['0', '65535'],
+                          'example' : ['under the port (for vlan aware bridge): bridge-portprios 32',
+                                       'under the bridge (for vlan unaware bridge): bridge-portprios swp1=32 swp2=32'],
+                          'default' : '32'},
+                   'bridge-mclmc' :
+                        { 'help' : 'set multicast last member count',
+                          'validrange' : ['0', '255'],
+                          'example' : ['bridge-mclmc 2'],
+                          'default' : '2'},
+                    'bridge-mcrouter' :
+                        { 'help' : 'set multicast router',
+                          'validvals' : ['yes', 'no', '0', '1', '2'],
+                          'example' : ['bridge-mcrouter 1'],
+                          'default': 'yes'
+                          },
+                    'bridge-mcsnoop' :
+                        { 'help' : 'set multicast snooping',
+                          'validvals' : ['yes', 'no', '0', '1'],
+                          'default' : 'yes',
+                          'example' : ['bridge-mcsnoop yes']},
+                    'bridge-mcsqc' :
+                        { 'help' : 'set multicast startup query count',
+                          'validrange' : ['0', '255'],
+                          'default' : '2',
+                          'example' : ['bridge-mcsqc 2']},
+                    'bridge-mcqifaddr' :
+                        { 'help' : 'set multicast query to use ifaddr',
+                          'validvals' : ['yes', 'no', '0', '1'],
+                          'default' : 'no',
+                          'example' : ['bridge-mcqifaddr no']},
+                    'bridge-mcquerier' :
+                        { 'help' : 'set multicast querier',
+                          'validvals' : ['yes', 'no', '0', '1'],
+                          'default' : 'no',
+                          'example' : ['bridge-mcquerier no']},
+                    'bridge-hashel' :
+                        { 'help' : 'set hash elasticity',
+                          'validrange' : ['0', '4096'],
+                          'default' : '4',
+                          'example' : ['bridge-hashel 4096']},
+                    'bridge-hashmax' :
+                        { 'help' : 'set hash max',
+                          'validrange' : ['0', '4096'],
+                          'default' : '512',
+                          'example' : ['bridge-hashmax 4096']},
+                    'bridge-mclmi' :
+                        { 'help' : 'set multicast last member interval (in secs)',
+                          'validrange' : ['0', '255'],
+                          'default' : '1',
+                          'example' : ['bridge-mclmi 1']},
+                    'bridge-mcmi' :
+                        { 'help' : 'set multicast membership interval (in secs)',
+                          'validrange' : ['0', '255'],
+                          'default' : '260',
+                          'example' : ['bridge-mcmi 260']},
+                    'bridge-mcqpi' :
+                        { 'help' : 'set multicast querier interval (in secs)',
+                          'validrange' : ['0', '255'],
+                          'default' : '255',
+                          'example' : ['bridge-mcqpi 255']},
+                    'bridge-mcqi' :
+                        { 'help' : 'set multicast query interval (in secs)',
+                          'validrange' : ['0', '255'],
+                          'default' : '125',
+                          'example' : ['bridge-mcqi 125']},
+                    'bridge-mcqri' :
+                        { 'help' : 'set multicast query response interval (in secs)',
+                          'validrange' : ['0', '255'],
+                          'default' : '10',
+                          'example' : ['bridge-mcqri 10']},
+                    'bridge-mcsqi' :
+                        { 'help' : 'set multicast startup query interval (in secs)',
+                          'validrange' : ['0', '255'],
+                          'default' : '31',
+                          'example' : ['bridge-mcsqi 31']},
+                    'bridge-mcqv4src' :
+                        { 'help' : 'set per VLAN v4 multicast querier source address',
+                          'validvals' : ['<number-ipv4-list>', ],
+                          'multivalue' : True,
+                          'compat' : True,
+                          'example' : ['bridge-mcqv4src 100=172.16.100.1 101=172.16.101.1']},
+                     'bridge-portmcrouter':
+                         {
+                             'help': 'set port multicast routers',
+                             'validvals': ['<interface-disabled-automatic-enabled>'],
+                             'example': [
+                                 'under the port (for vlan aware bridge): bridge-portmcrouter 0',
+                                 'under the port (for vlan aware bridge): bridge-portmcrouter 1',
+                                 'under the port (for vlan aware bridge): bridge-portmcrouter 2',
+                                 'under the port (for vlan aware bridge): bridge-portmcrouter disabled',
+                                 'under the port (for vlan aware bridge): bridge-portmcrouter automatic',
+                                 'under the port (for vlan aware bridge): bridge-portmcrouter enabled',
+                                 'under the bridge (for vlan unaware bridge): bridge-portmcrouter swp1=0 swp2=1 swp2=2',
+                                 'under the bridge (for vlan unaware bridge): bridge-portmcrouter swp1=disabled swp2=automatic swp3=enabled',
+                                 'under the bridge (for vlan unaware bridge): bridge-portmcrouter swp1=2 swp2=disabled swp3=1',
+                             ]
+                         },
+                    'bridge-portmcfl' :
+                        { 'help' : 'port multicast fast leave.',
+                          'validvals': ['<interface-yes-no-0-1-list>'],
+                          'validrange' : ['yes', 'no', '0', '1'],
+                          'default' : 'no',
+                          'example' : ['under the port (for vlan aware bridge): bridge-portmcfl no',
+                                       'under the bridge (for vlan unaware bridge): bridge-portmcfl swp1=no swp2=no']},
+                    'bridge-waitport' :
+                        { 'help' : 'wait for a max of time secs for the' +
+                                ' specified ports to become available,' +
+                                'if no ports are specified then those' +
+                                ' specified on bridge-ports will be' +
+                                ' used here. Specifying no ports here ' +
+                                'should not be used if we are using ' +
+                                'regex or \"all\" on bridge_ports,' +
+                                'as it wouldnt work.',
+                          'default' : '0',
+                          'validvals': ['<number-interface-list>'],
+                          'example' : ['bridge-waitport 4 swp1 swp2']},
+                    'bridge-maxwait' :
+                        { 'help' : 'forces to time seconds the maximum time ' +
+                                'that the Debian bridge setup scripts will ' +
+                                'wait for the bridge ports to get to the ' +
+                                'forwarding status, doesn\'t allow factional ' +
+                                'part. If it is equal to 0 then no waiting' +
+                                ' is done',
+                          'validrange' : ['0', '255'],
+                          'default' : '0',
+                          'example' : ['bridge-maxwait 3']},
+                    'bridge-vids' :
+                        { 'help' : 'bridge port vids. Can be specified ' +
+                                   'under the bridge or under the port. ' +
+                                   'If specified under the bridge the ports ' +
+                                   'inherit it unless overridden by a ' +
+                                   'bridge-vids attribute under the port',
+                          'multivalue' : True,
+                          'validvals': ['<number-comma-range-list>'],
+                          'example' : ['bridge-vids 4000',
+                                       'bridge-vids 2000 2200-3000'],
+                          'aliases': ['bridge-trunk']},
+                    'bridge-pvid' :
+                        { 'help' : 'bridge port pvid. Must be specified under' +
+                                   ' the bridge port',
+                          'validrange' : ['0', '4096'],
+                          'example' : ['bridge-pvid 1']},
+                    'bridge-access' :
+                        { 'help' : 'bridge port access vlan. Must be ' +
+                                   'specified under the bridge port',
+                          'validrange' : ['1', '4094'],
+                          'example' : ['bridge-access 300']},
+                    'bridge-allow-untagged' :
+                        { 'help' : 'indicate if the bridge port accepts ' +
+                                   'untagged packets or not.  Must be ' +
+                                   'specified under the bridge port. ' +
+                                   'Default is \'yes\'',
+                          'validvals' : ['yes', 'no'],
+                          'example' : ['bridge-allow-untagged yes'],
+                          'default' : 'yes'},
+                    'bridge-port-vids' :
+                        { 'help' : 'bridge vlans',
+                          'compat': True,
+                          'example' : ['bridge-port-vids bond0=1-1000,1010-1020']},
+                    'bridge-port-pvids' :
+                        { 'help' : 'bridge port vlans',
+                          'compat': True,
+                          'example' : ['bridge-port-pvids bond0=100 bond1=200']},
+                    'bridge-learning' :
+                        { 'help' : 'bridge port learning flag',
+                          'validvals': ['on', 'off', '<interface-on-off-list>'],
+                          'default': 'on',
+                          'example' : ['bridge-learning off']},
+                    'bridge-igmp-version' :
+                        { 'help' : 'mcast igmp version',
+                          'validvals': ['2', '3'],
+                          'default' : '2',
+                          'example' : ['bridge-igmp-version 2']},
+                    'bridge-mld-version':
+                        { 'help' : 'mcast mld version',
+                          'validvals': ['1', '2'],
+                          'default' : '1',
+                          'example' : ['bridge-mld-version 1']},
+                    'bridge-unicast-flood' :
+                        { 'help' : 'bridge port unicast flood flag',
+                          'validvals': ['on', 'off', '<interface-on-off-list>'],
+                          'default': 'on',
+                          'example' : ['under the port (for vlan aware bridge): bridge-unicast-flood on',
+                                       'under the bridge (for vlan unaware bridge): bridge-unicast-flood swp1=on swp2=on']},
+                    'bridge-multicast-flood' :
+                        { 'help' : 'bridge port multicast flood flag',
+                          'validvals': ['on', 'off', '<interface-on-off-list>'],
+                          'default': 'on',
+                          'example' : ['under the port (for vlan aware bridge): bridge-multicast-flood on',
+                                       'under the bridge (for vlan unaware bridge): bridge-multicast-flood swp1=on swp2=on']},
+                    'bridge-vlan-protocol' :
+                        { 'help' : 'bridge vlan protocol',
+                          'default' : '802.1q',
+                          'validvals': ['802.1q', '802.1ad'],
+                          'example' : ['bridge-vlan-protocol 802.1q']},
+                    'bridge-vlan-stats' :
+                        { 'help' : 'bridge vlan stats',
+                          'default' : 'off',
+                          'validvals': ['on', 'off'],
+                          'example' : ['bridge-vlan-stats off']},
+                    'bridge-arp-nd-suppress' :
+                        { 'help' : 'bridge port arp nd suppress flag',
+                          'validvals': ['on', 'off', '<interface-on-off-list>'],
+                          'default': 'off',
+                          'example' : ['under the port (for vlan aware bridge): bridge-arp-nd-suppress on',
+                                       'under the bridge (for vlan unaware bridge): bridge-arp-nd-suppress swp1=on swp2=on']},
+                    'bridge-mcstats' :
+                        { 'help' : 'bridge multicast stats',
+                          'default' : 'off',
+                          'validvals': ['on', 'off'],
+                          'example' : ['bridge-mcstats off']},
+                     'bridge-l2protocol-tunnel': {
+                         'help': 'layer 2 protocol tunneling',
+                         'validvals': [ # XXX: lists all combinations, should move to
+                                        # a better representation
+                                        'all',
+                                        'cdp',
+                                        'cdp lacp',
+                                        'cdp lacp lldp',
+                                        'cdp lacp lldp pvst',
+                                        'cdp lacp lldp stp',
+                                        'cdp lacp pvst',
+                                        'cdp lacp pvst stp',
+                                        'cdp lacp stp',
+                                        'cdp lldp',
+                                        'cdp lldp pvst',
+                                        'cdp lldp pvst stp',
+                                        'cdp lldp stp',
+                                        'cdp pvst',
+                                        'cdp pvst stp',
+                                        'cdp stp',
+                                        'lacp',
+                                        'lacp lldp',
+                                        'lacp lldp pvst',
+                                        'lacp lldp pvst stp',
+                                        'lacp lldp stp',
+                                        'lacp pvst',
+                                        'lacp pvst stp',
+                                        'lacp stp',
+                                        'lldp',
+                                        'lldp pvst',
+                                        'lldp pvst stp',
+                                        'lldp stp',
+                                        'pvst',
+                                        'pvst stp',
+                                        'stp',
+                                        '<interface-l2protocol-tunnel-list>'],
+                         'example': [
+                             'under the bridge (for vlan unaware bridge): bridge-l2protocol-tunnel swpX=lacp,stp swpY=cdp swpZ=all',
+                             'under the port (for vlan aware bridge): bridge-l2protocol-tunnel lacp stp lldp cdp pvst',
+                             'under the port (for vlan aware bridge): bridge-l2protocol-tunnel lldp pvst',
+                             'under the port (for vlan aware bridge): bridge-l2protocol-tunnel stp',
+                             'under the port (for vlan aware bridge): bridge-l2protocol-tunnel all'
+                         ]
+                     }
+                     }}
+
+    # Netlink attributes not associated with ifupdown2
+    # attributes are left commented-out for a future use
+    # and kept in order :)
+    _ifla_br_attributes_map = (
+        # Link.IFLA_BR_UNSPEC,
+        ('bridge-fd', Link.IFLA_BR_FORWARD_DELAY),
+        ('bridge-hello', Link.IFLA_BR_HELLO_TIME),
+        ('bridge-maxage', Link.IFLA_BR_MAX_AGE),
+        ('bridge-ageing', Link.IFLA_BR_AGEING_TIME),
+        ('bridge-stp', Link.IFLA_BR_STP_STATE),
+        ('bridge-bridgeprio', Link.IFLA_BR_PRIORITY),
+        ('bridge-vlan-aware', Link.IFLA_BR_VLAN_FILTERING),
+        ('bridge-vlan-protocol', Link.IFLA_BR_VLAN_PROTOCOL),
+        # Link.IFLA_BR_GROUP_FWD_MASK,
+        # Link.IFLA_BR_ROOT_ID,
+        # Link.IFLA_BR_BRIDGE_ID,
+        # Link.IFLA_BR_ROOT_PORT,
+        # (Link.IFLA_BR_ROOT_PATH_COST,,
+        # Link.IFLA_BR_TOPOLOGY_CHANGE,
+        # Link.IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
+        # Link.IFLA_BR_HELLO_TIMER,
+        # Link.IFLA_BR_TCN_TIMER,
+        # Link.IFLA_BR_TOPOLOGY_CHANGE_TIMER,
+        # Link.IFLA_BR_GC_TIMER,
+        # Link.IFLA_BR_GROUP_ADDR,
+        # Link.IFLA_BR_FDB_FLUSH,
+        ('bridge-mcrouter', Link.IFLA_BR_MCAST_ROUTER),
+        #('bridge-mcsnoop', Link.IFLA_BR_MCAST_SNOOPING), # requires special handling so we won't loop on this attr
+        ('bridge-mcqifaddr', Link.IFLA_BR_MCAST_QUERY_USE_IFADDR),
+        ('bridge-mcquerier', Link.IFLA_BR_MCAST_QUERIER),
+        ('bridge-hashel', Link.IFLA_BR_MCAST_HASH_ELASTICITY),
+        ('bridge-hashmax', Link.IFLA_BR_MCAST_HASH_MAX),
+        ('bridge-mclmc', Link.IFLA_BR_MCAST_LAST_MEMBER_CNT),
+        ('bridge-mcsqc', Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT),
+        ('bridge-mclmi', Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL),
+        ('bridge-mcmi', Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL),
+        ('bridge-mcqpi', Link.IFLA_BR_MCAST_QUERIER_INTVL),
+        ('bridge-mcqi', Link.IFLA_BR_MCAST_QUERY_INTVL),
+        ('bridge-mcqri', Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL),
+        ('bridge-mcsqi', Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL),
+        # Link.IFLA_BR_NF_CALL_IPTABLES,
+        # Link.IFLA_BR_NF_CALL_IP6TABLES,
+        # Link.IFLA_BR_NF_CALL_ARPTABLES,
+        # Link.IFLA_BR_VLAN_DEFAULT_PVID,
+        # Link.IFLA_BR_PAD,
+        # (Link.IFLA_BR_VLAN_STATS_ENABLED, 'bridge-vlan-stats'), #  already dealt with, in a separate loop
+        ('bridge-igmp-version', Link.IFLA_BR_MCAST_IGMP_VERSION, ),
+        ('bridge-mcstats', Link.IFLA_BR_MCAST_STATS_ENABLED),
+        ('bridge-mld-version', Link.IFLA_BR_MCAST_MLD_VERSION)
+    )
+    # 'bridge-vlan-stats & bridge-mcstat are commented out even though, today
+    # they are supported. It is done this way because this dictionary is used
+    # in a loop, but these attributes require additional work. Thus they are
+    # excluded from this loop without overhead.
+
+    # we are still using the old linkCache we need an easy way
+    # to use this cache with the new full-netlink approach
+    _ifla_br_attributes_old_cache_key_map = dict(
+        (
+            (Link.IFLA_BR_FORWARD_DELAY, 'fd'),
+            (Link.IFLA_BR_HELLO_TIME, 'hello'),
+            (Link.IFLA_BR_MAX_AGE, 'maxage'),
+            (Link.IFLA_BR_AGEING_TIME, 'ageing'),
+            (Link.IFLA_BR_STP_STATE, 'stp'),
+            (Link.IFLA_BR_PRIORITY, 'bridgeprio'),
+            (Link.IFLA_BR_VLAN_FILTERING, 'vlan_filtering'),
+            (Link.IFLA_BR_VLAN_PROTOCOL, 'vlan-protocol'),
+            (Link.IFLA_BR_MCAST_ROUTER, 'mcrouter'),
+            (Link.IFLA_BR_MCAST_SNOOPING, 'mcsnoop'),
+            (Link.IFLA_BR_MCAST_QUERY_USE_IFADDR, 'mcqifaddr'),
+            (Link.IFLA_BR_MCAST_QUERIER, 'mcquerier'),
+            (Link.IFLA_BR_MCAST_HASH_ELASTICITY, 'hashel'),
+            (Link.IFLA_BR_MCAST_HASH_MAX, 'hashmax'),
+            (Link.IFLA_BR_MCAST_LAST_MEMBER_CNT, 'mclmc'),
+            (Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT, 'mcsqc'),
+            (Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL, 'mclmi'),
+            (Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL, 'mcmi'),
+            (Link.IFLA_BR_MCAST_QUERIER_INTVL, 'mcqpi'),
+            (Link.IFLA_BR_MCAST_QUERY_INTVL, 'mcqi'),
+            (Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, 'mcqri'),
+            (Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL, 'mcsqi'),
+            (Link.IFLA_BR_VLAN_STATS_ENABLED, 'vlan-stats'),
+            (Link.IFLA_BR_MCAST_STATS_ENABLED, 'mcstats'),
+            (Link.IFLA_BR_MCAST_IGMP_VERSION, 'igmp-version'),
+            (Link.IFLA_BR_MCAST_MLD_VERSION, 'mld-version')
+        )
+    )
+
+    _ifla_br_attributes_translate_user_config_to_netlink_map = dict(
+        (
+            # Link.IFLA_BR_UNSPEC,
+            (Link.IFLA_BR_FORWARD_DELAY, lambda x: int(x) * 100),
+            (Link.IFLA_BR_HELLO_TIME, lambda x: int(x) * 100),
+            (Link.IFLA_BR_MAX_AGE, lambda x: int(x) * 100),
+            (Link.IFLA_BR_AGEING_TIME, lambda x: int(x) * 100),
+            # Link.IFLA_BR_STP_STATE, #  STP is treated outside the loop
+            (Link.IFLA_BR_PRIORITY, int),
+            (Link.IFLA_BR_VLAN_FILTERING, utils.get_boolean_from_string),
+            (Link.IFLA_BR_VLAN_PROTOCOL, str),
+            # Link.IFLA_BR_GROUP_FWD_MASK,
+            # Link.IFLA_BR_ROOT_ID,
+            # Link.IFLA_BR_BRIDGE_ID,
+            # Link.IFLA_BR_ROOT_PORT,
+            # Link.IFLA_BR_ROOT_PATH_COST,
+            # Link.IFLA_BR_TOPOLOGY_CHANGE,
+            # Link.IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
+            # Link.IFLA_BR_HELLO_TIMER,
+            # Link.IFLA_BR_TCN_TIMER,
+            # Link.IFLA_BR_TOPOLOGY_CHANGE_TIMER,
+            # Link.IFLA_BR_GC_TIMER,
+            # Link.IFLA_BR_GROUP_ADDR,
+            # Link.IFLA_BR_FDB_FLUSH,
+            (Link.IFLA_BR_MCAST_ROUTER, utils.get_int_from_boolean_and_string),
+            (Link.IFLA_BR_MCAST_SNOOPING, utils.get_boolean_from_string),
+            (Link.IFLA_BR_MCAST_QUERY_USE_IFADDR, utils.get_boolean_from_string),
+            (Link.IFLA_BR_MCAST_QUERIER, utils.get_boolean_from_string),
+            (Link.IFLA_BR_MCAST_HASH_ELASTICITY, int),
+            (Link.IFLA_BR_MCAST_HASH_MAX, int),
+            (Link.IFLA_BR_MCAST_LAST_MEMBER_CNT, int),
+            (Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT, int),
+            (Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL, lambda x: int(x) * 100),
+            (Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL, lambda x: int(x) * 100),
+            (Link.IFLA_BR_MCAST_QUERIER_INTVL, lambda x: int(x) * 100),
+            (Link.IFLA_BR_MCAST_QUERY_INTVL, lambda x: int(x) * 100),
+            (Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, lambda x: int(x) * 100),
+            (Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL, lambda x: int(x) * 100),
+            # Link.IFLA_BR_NF_CALL_IPTABLES,
+            # Link.IFLA_BR_NF_CALL_IP6TABLES,
+            # Link.IFLA_BR_NF_CALL_ARPTABLES,
+            # Link.IFLA_BR_VLAN_DEFAULT_PVID,
+            # Link.IFLA_BR_PAD,
+            (Link.IFLA_BR_VLAN_STATS_ENABLED, utils.get_boolean_from_string),
+            (Link.IFLA_BR_MCAST_IGMP_VERSION, int),
+            (Link.IFLA_BR_MCAST_STATS_ENABLED, utils.get_boolean_from_string),
+            (Link.IFLA_BR_MCAST_MLD_VERSION, int)
+        )
+    )
+
+    _ifla_brport_attributes_map = (
+        # Link.IFLA_BRPORT_UNSPEC,
+        # Link.IFLA_BRPORT_STATE,
+        ('bridge-portprios', Link.IFLA_BRPORT_PRIORITY),
+        ('bridge-pathcosts', Link.IFLA_BRPORT_COST),
+        # Link.IFLA_BRPORT_MODE,
+        # Link.IFLA_BRPORT_GUARD,
+        # Link.IFLA_BRPORT_PROTECT,
+        ('bridge-portmcfl', Link.IFLA_BRPORT_FAST_LEAVE),
+        ('bridge-learning', Link.IFLA_BRPORT_LEARNING),
+        ('bridge-unicast-flood', Link.IFLA_BRPORT_UNICAST_FLOOD),
+        # Link.IFLA_BRPORT_PROXYARP,
+        # Link.IFLA_BRPORT_LEARNING_SYNC,
+        # Link.IFLA_BRPORT_PROXYARP_WIFI,
+        # Link.IFLA_BRPORT_ROOT_ID,
+        # Link.IFLA_BRPORT_BRIDGE_ID,
+        # Link.IFLA_BRPORT_DESIGNATED_PORT,
+        # Link.IFLA_BRPORT_DESIGNATED_COST,
+        # Link.IFLA_BRPORT_ID,
+        # Link.IFLA_BRPORT_NO,
+        # Link.IFLA_BRPORT_TOPOLOGY_CHANGE_ACK,
+        # Link.IFLA_BRPORT_CONFIG_PENDING,
+        # Link.IFLA_BRPORT_MESSAGE_AGE_TIMER,
+        # Link.IFLA_BRPORT_FORWARD_DELAY_TIMER,
+        # Link.IFLA_BRPORT_HOLD_TIMER,
+        # Link.IFLA_BRPORT_FLUSH,
+        ('bridge-portmcrouter', Link.IFLA_BRPORT_MULTICAST_ROUTER),
+        # Link.IFLA_BRPORT_PAD,
+        ('bridge-multicast-flood', Link.IFLA_BRPORT_MCAST_FLOOD),
+        # Link.IFLA_BRPORT_MCAST_TO_UCAST,
+        # Link.IFLA_BRPORT_VLAN_TUNNEL,
+        # Link.IFLA_BRPORT_BCAST_FLOOD
+        ('bridge-l2protocol-tunnel', Link.IFLA_BRPORT_GROUP_FWD_MASK),
+        # Link.IFLA_BRPORT_PEER_LINK,
+        # Link.IFLA_BRPORT_DUAL_LINK,
+        ('bridge-arp-nd-suppress', Link.IFLA_BRPORT_ARP_SUPPRESS),
+    )
+
+    _ifla_brport_multicast_router_dict_to_int = {
+        'disabled': 0,
+        '0': 0,
+        'no': 0,
+        'automatic': 1,
+        '1': 1,
+        'yes': 1,
+        'enabled': 2,
+        '2': 2,
+    }
+
+    # callable to translate <interface-yes-no-0-1-list> to netlink value
+    _ifla_brport_attributes_translate_user_config_to_netlink_map = dict(
+        (
+            (Link.IFLA_BRPORT_PRIORITY, int),
+            (Link.IFLA_BRPORT_COST, int),
+            (Link.IFLA_BRPORT_MULTICAST_ROUTER, lambda x: bridge._ifla_brport_multicast_router_dict_to_int.get(x, 0)),
+            (Link.IFLA_BRPORT_FAST_LEAVE, utils.get_boolean_from_string),
+            (Link.IFLA_BRPORT_LEARNING, utils.get_boolean_from_string),
+            (Link.IFLA_BRPORT_UNICAST_FLOOD, utils.get_boolean_from_string),
+            (Link.IFLA_BRPORT_MCAST_FLOOD, utils.get_boolean_from_string),
+            (Link.IFLA_BRPORT_GROUP_FWD_MASK, lambda x: x),
+            (Link.IFLA_BRPORT_ARP_SUPPRESS, utils.get_boolean_from_string)
+        )
+    )
+
+    def __init__(self, *args, **kargs):
+        moduleBase.__init__(self, *args, **kargs)
+        self.ipcmd = None
+        self.name = self.__class__.__name__
+        self.brctlcmd = None
+        self._running_vidinfo = {}
+        self._running_vidinfo_valid = False
+        self._resv_vlan_range =  self._get_reserved_vlan_range()
+        self.logger.debug('%s: using reserved vlan range %s' % (self.__class__.__name__, str(self._resv_vlan_range)))
+
+        self.default_stp_on = utils.get_boolean_from_string(
+            policymanager.policymanager_api.get_attr_default(
+                module_name=self.__class__.__name__,
+                attr='bridge-stp'
+            )
+        )
+
+        self.default_vlan_stats = policymanager.policymanager_api.get_attr_default(
+            module_name=self.__class__.__name__,
+            attr='bridge-vlan-stats'
+        )
+
+        self.warn_on_untagged_bridge_absence = utils.get_boolean_from_string(
+            policymanager.policymanager_api.get_module_globals(
+                module_name=self.__class__.__name__,
+                attr='warn_on_untagged_bridge_absence'
+            )
+        )
+        self.logger.debug('bridge: init: warn_on_untagged_bridge_absence=%s'
+                         % self.warn_on_untagged_bridge_absence)
+
+        self._vxlan_bridge_default_igmp_snooping = policymanager.policymanager_api.get_module_globals(
+            self.__class__.__name__,
+            'vxlan_bridge_default_igmp_snooping'
+        )
+        self.logger.debug('bridge: init: vxlan_bridge_default_igmp_snooping=%s'
+                          % self._vxlan_bridge_default_igmp_snooping)
+
+        self.arp_nd_suppress_only_on_vxlan = utils.get_boolean_from_string(
+            policymanager.policymanager_api.get_module_globals(
+                module_name=self.__class__.__name__,
+                attr='allow_arp_nd_suppress_only_on_vxlan'
+            )
+        )
+        self.logger.debug('bridge: init: arp_nd_suppress_only_on_vxlan=%s' % self.arp_nd_suppress_only_on_vxlan)
+
+        try:
+            self.bridge_allow_multiple_vlans = utils.get_boolean_from_string(
+                self.sysctl_get('net.bridge.bridge-allow-multiple-vlans')
+            )
+        except:
+            # Cumulus Linux specific variable. Failure probably means that
+            # ifupdown2 is running a a different system.
+            self.bridge_allow_multiple_vlans = True
+        self.logger.debug('bridge: init: multiple vlans allowed %s' % self.bridge_allow_multiple_vlans)
+
+        self.bridge_mac_iface_list = policymanager.policymanager_api.get_module_globals(self.__class__.__name__, 'bridge_mac_iface') or []
+        self.bridge_mac_iface = None, None  # ifname, mac
+
+        self.bridge_set_static_mac_from_port = utils.get_boolean_from_string(
+            policymanager.policymanager_api.get_module_globals(
+                self.__class__.__name__, 'bridge_set_static_mac_from_port'
+            )
+        )
+
+        self.l2protocol_tunnel_callback = {
+            'all': self._l2protocol_tunnel_set_all,
+            'stp': self._l2protocol_tunnel_set_stp,
+            'cdp': self._l2protocol_tunnel_set_cdp,
+            'pvst': self._l2protocol_tunnel_set_pvst,
+            'lldp': self._l2protocol_tunnel_set_lldp,
+            'lacp': self._l2protocol_tunnel_set_lacp
+        }
+
+        self.query_check_l2protocol_tunnel_callback = {
+            'all': self._query_check_l2protocol_tunnel_all,
+            'stp': self._query_check_l2protocol_tunnel_stp,
+            'cdp': self._query_check_l2protocol_tunnel_cdp,
+            'pvst': self._query_check_l2protocol_tunnel_pvst,
+            'lldp': self._query_check_l2protocol_tunnel_lldp,
+            'lacp': self._query_check_l2protocol_tunnel_lacp
+        }
+
+    @staticmethod
+    def _l2protocol_tunnel_set_pvst(ifla_brport_group_mask, ifla_brport_group_maskhi):
+        ifla_brport_group_maskhi |= 0x1
+        return ifla_brport_group_mask, ifla_brport_group_maskhi
+
+    @staticmethod
+    def _l2protocol_tunnel_set_cdp(ifla_brport_group_mask, ifla_brport_group_maskhi):
+        ifla_brport_group_maskhi |= 0x2
+        return ifla_brport_group_mask, ifla_brport_group_maskhi
+
+    @staticmethod
+    def _l2protocol_tunnel_set_stp(ifla_brport_group_mask, ifla_brport_group_maskhi):
+        ifla_brport_group_mask |= 0x1
+        return ifla_brport_group_mask, ifla_brport_group_maskhi
+
+    @staticmethod
+    def _l2protocol_tunnel_set_lacp(ifla_brport_group_mask, ifla_brport_group_maskhi):
+        ifla_brport_group_mask |= 0x4
+        return ifla_brport_group_mask, ifla_brport_group_maskhi
+
+    @staticmethod
+    def _l2protocol_tunnel_set_lldp(ifla_brport_group_mask, ifla_brport_group_maskhi):
+        ifla_brport_group_mask |= 0x4000
+        return ifla_brport_group_mask, ifla_brport_group_maskhi
+
+    @staticmethod
+    def _l2protocol_tunnel_set_all(ifla_brport_group_mask, ifla_brport_group_maskhi):
+        # returns new values for ifla_brport_group_mask and ifla_brport_group_maskhi
+        return 0x1 | 0x4 | 0x4000, 0x1 | 0x2
+
+    @staticmethod
+    def _query_check_l2protocol_tunnel_stp(ifla_brport_group_mask, ifla_brport_group_maskhi):
+        return ifla_brport_group_mask & 0x1
+
+    @staticmethod
+    def _query_check_l2protocol_tunnel_cdp(ifla_brport_group_mask, ifla_brport_group_maskhi):
+        return ifla_brport_group_maskhi & 0x2
+
+    @staticmethod
+    def _query_check_l2protocol_tunnel_pvst(ifla_brport_group_mask, ifla_brport_group_maskhi):
+        return ifla_brport_group_maskhi & 0x1
+
+    @staticmethod
+    def _query_check_l2protocol_tunnel_lldp(ifla_brport_group_mask, ifla_brport_group_maskhi):
+        return ifla_brport_group_mask & 0x4000
+
+    @staticmethod
+    def _query_check_l2protocol_tunnel_lacp(ifla_brport_group_mask, ifla_brport_group_maskhi):
+        return ifla_brport_group_mask & 0x4
+
+    @staticmethod
+    def _query_check_l2protocol_tunnel_all(ifla_brport_group_mask, ifla_brport_group_maskhi):
+        return ifla_brport_group_mask == (0x1 | 0x4 | 0x4000) and ifla_brport_group_maskhi == (0x1 | 0x2)
+
+    def syntax_check(self, ifaceobj, ifaceobj_getfunc):
+        retval = self.check_bridge_vlan_aware_port(ifaceobj, ifaceobj_getfunc)
+        if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
+            if not self.check_bridge_port_vid_attrs(ifaceobj):
+                retval = False
+        c1 = self.syntax_check_vxlan_in_vlan_aware_br(ifaceobj, ifaceobj_getfunc)
+        c2 = self.syntax_check_bridge_allow_multiple_vlans(ifaceobj, ifaceobj_getfunc)
+        return retval and c1 #and c2
+
+    def syntax_check_bridge_allow_multiple_vlans(self, ifaceobj, ifaceobj_getfunc):
+        result = True
+        if not self.bridge_allow_multiple_vlans and ifaceobj.link_kind & ifaceLinkKind.BRIDGE and ifaceobj.lowerifaces:
+            vlan_id = None
+            for brport_name in ifaceobj.lowerifaces:
+                for obj in ifaceobj_getfunc(brport_name) or []:
+                    if obj.link_kind & ifaceLinkKind.VLAN:
+                        sub_intf_vlan_id = self._get_vlan_id(obj)
+                        if vlan_id and vlan_id != sub_intf_vlan_id:
+                            self.logger.error('%s: ignore %s: multiple vlans not allowed under bridge '
+                                              '(sysctl net.bridge.bridge-allow-multiple-vlans not set)'
+                                              % (ifaceobj.name, brport_name))
+                            result = False
+                            continue
+                        vlan_id = sub_intf_vlan_id
+        return result
+
+    def check_bridge_port_vid_attrs(self, ifaceobj):
+        if (ifaceobj.get_attr_value('bridge-access') and
+            (self.get_ifaceobj_bridge_vids_value(ifaceobj) or
+             ifaceobj.get_attr_value('bridge-pvid'))):
+            self.logger.warn('%s: bridge-access given, bridge-vids and bridge-pvid '
+                             'will be ignored' % ifaceobj.name)
+            return False
+        return True
+
+    def check_bridge_vlan_aware_port(self, ifaceobj, ifaceobj_getfunc):
+        if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE:
+            ports = self._get_bridge_port_list(ifaceobj)
+            if not ports:
+                return True
+            result = True
+            for port_name in ports:
+                port_obj_l = ifaceobj_getfunc(port_name)
+                if port_obj_l and port_obj_l[0].link_kind & ifaceLinkKind.VLAN:
+                    self.logger.error('%s: %s: vlan sub-interface is not '
+                                      'supported in a vlan-aware bridge'
+                                      % (ifaceobj.name, port_name))
+                    result = False
+                if (port_obj_l and
+                    port_obj_l[0].get_attr_value('bridge-arp-nd-suppress') and
+                    self.arp_nd_suppress_only_on_vxlan and
+                    not port_obj_l[0].link_kind & ifaceLinkKind.VXLAN):
+                    self.log_error('\'bridge-arp-nd-suppress\' is not '
+                                   'supported on a non-vxlan port %s'
+                                   %port_obj_l[0].name)
+                    result = False
+            return result
+        return True
+
+    def _error_vxlan_in_vlan_aware_br(self, ifaceobj, bridgename):
+        self.log_error('`bridge-access` attribute is mandatory when vxlan '
+                       'device (%s) is part of vlan aware bridge (%s)'
+                       % (ifaceobj.name, bridgename), ifaceobj)
+
+    def syntax_check_vxlan_in_vlan_aware_br(self, ifaceobj, ifaceobj_getfunc):
+        if not ifaceobj_getfunc:
+            return True
+        if (ifaceobj.link_kind & ifaceLinkKind.VXLAN
+                and ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT):
+            if ifaceobj.get_attr_value('bridge-access'):
+                return True
+            for iface in ifaceobj.upperifaces if ifaceobj.upperifaces else []:
+                ifaceobj_upper_list = ifaceobj_getfunc(iface)
+                if not ifaceobj_upper_list:
+                    continue
+                ifaceobj_upper = ifaceobj_upper_list[0]
+                bridge_vids = self._get_bridge_vids(iface, ifaceobj_getfunc)
+                if ifaceobj_upper.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE:
+                    vids = self.get_ifaceobj_bridge_vids_value(ifaceobj)
+                    pvid = ifaceobj.get_attr_value_first('bridge-pvid')
+                    if (not vids
+                        or not pvid
+                        or not self._compare_vids(bridge_vids,
+                                                  vids,
+                                                  pvid=pvid)):
+                        self._error_vxlan_in_vlan_aware_br(ifaceobj,
+                                                           ifaceobj_upper.name)
+                        return False
+        return True
+
+    @staticmethod
+    def _is_bridge(ifaceobj):
+        return (ifaceobj.link_kind & ifaceLinkKind.BRIDGE or
+                ifaceobj.get_attr_value_first('bridge-ports') or
+                ifaceobj.get_attr_value_first('bridge-vlan-aware'))
+
+    def _get_ifaceobj_bridge_ports(self, ifaceobj, warn=True):
+        ports = ifaceobj.get_attr_value('bridge-ports')
+        if warn and ports and len(ports) > 1:
+            self.log_warn('%s: ignoring duplicate bridge-ports lines: %s'
+                          %(ifaceobj.name, ports[1:]))
+        if ports:
+            if 'none' in ports[0]:
+                return []
+            return ports[0]
+        return None
+
+    def _is_bridge_port(self, ifaceobj):
+        if self.brctlcmd.is_bridge_port(ifaceobj.name):
+            return True
+        return False
+
+    def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
+        if not self._is_bridge(ifaceobj):
+            return None
+        if ifaceobj.link_type != ifaceLinkType.LINK_NA:
+           ifaceobj.link_type = ifaceLinkType.LINK_MASTER
+        ifaceobj.link_kind |= ifaceLinkKind.BRIDGE
+        # for special vlan aware bridges, we need to add another bit
+        if utils.get_boolean_from_string(ifaceobj.get_attr_value_first('bridge-vlan-aware')):
+            ifaceobj.link_kind |= ifaceLinkKind.BRIDGE
+            ifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE
+        ifaceobj.role |= ifaceRole.MASTER
+        ifaceobj.dependency_type = ifaceDependencyType.MASTER_SLAVE
+        return self.parse_port_list(ifaceobj.name,
+                                    self._get_ifaceobj_bridge_ports(ifaceobj),
+                                    ifacenames_all)
+
+    def get_dependent_ifacenames_running(self, ifaceobj):
+        self._init_command_handlers()
+        if not self.brctlcmd.bridge_exists(ifaceobj.name):
+            return None
+        return self.brctlcmd.get_bridge_ports(ifaceobj.name)
+
+    def _get_bridge_port_list(self, ifaceobj):
+
+        # port list is also available in the previously
+        # parsed dependent list. Use that if available, instead
+        # of parsing port expr again
+        port_list = ifaceobj.lowerifaces
+        if port_list:
+            return port_list
+        ports = self._get_ifaceobj_bridge_ports(ifaceobj)
+        if ports:
+            return self.parse_port_list(ifaceobj.name, ports)
+        else:
+            return None
+
+    def _get_bridge_port_list_user_ordered(self, ifaceobj):
+        # When enslaving bridge-ports we need to return the exact user
+        # configured bridge ports list (bridge will inherit the mac of the
+        # first device.
+        ports = self._get_ifaceobj_bridge_ports(ifaceobj)
+        return self.parse_port_list(ifaceobj.name, ports) if ports else None
+
+    def _process_bridge_waitport(self, ifaceobj, portlist):
+        waitport_value = ifaceobj.get_attr_value_first('bridge-waitport')
+        if not waitport_value: return
+        try:
+            waitportvals = re.split(r'[\s\t]\s*', waitport_value, 1)
+            if not waitportvals: return
+            try:
+                waitporttime = int(waitportvals[0])
+            except:
+                self.log_warn('%s: invalid waitport value \'%s\''
+                        %(ifaceobj.name, waitportvals[0]))
+                return
+            if waitporttime <= 0: return
+            try:
+                waitportlist = self.parse_port_list(ifaceobj.name,
+                                                    waitportvals[1])
+            except IndexError, e:
+                # ignore error and use all bridge ports
+                waitportlist = portlist
+                pass
+            if not waitportlist: return
+            self.logger.info('%s: waiting for ports %s to exist ...'
+                    %(ifaceobj.name, str(waitportlist)))
+            starttime = time.time()
+            while ((time.time() - starttime) < waitporttime):
+                if all([False for p in waitportlist
+                        if not self.ipcmd.link_exists(p)]):
+                    break;
+                time.sleep(1)
+        except Exception, e:
+            self.log_warn('%s: unable to process waitport: %s'
+                    %(ifaceobj.name, str(e)))
+
+    def _enable_disable_ipv6(self, port, enable='1'):
+        try:
+            self.write_file('/proc/sys/net/ipv6/conf/%s/disable_ipv6' % port, enable)
+        except Exception, e:
+            self.logger.info(str(e))
+
+    def handle_ipv6(self, ports, state, ifaceobj=None):
+        if (ifaceobj and
+                (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN) and
+                not ifaceobj.get_attr_value('address')):
+            self._enable_disable_ipv6(ifaceobj.name, state)
+        for p in ports:
+            self._enable_disable_ipv6(p, state)
+
+    def _pretty_print_add_ports_error(self, errstr, bridgeifaceobj, bridgeports):
+        """ pretty print bridge port add errors.
+            since the commands are batched and the kernel only returns error
+            codes, this function tries to interpret some error codes
+            and prints clearer errors """
+
+        if re.search('RTNETLINK answers: Invalid argument', errstr):
+            # Cumulus Linux specific error checks
+            try:
+                if self.sysctl_get('net.bridge.bridge-allow-multiple-vlans') == '0':
+                    vlanid = None
+                    for bport in bridgeports:
+                        currvlanid = self._get_vlan_id_from_ifacename(bport)
+                        if vlanid:
+                            if currvlanid != vlanid:
+                                self.log_error('%s: ' %bridgeifaceobj.name +
+                                               'net.bridge.bridge-allow-multiple-vlans not set, multiple vlans not allowed', bridgeifaceobj)
+                                break
+                        if currvlanid:
+                            vlanid = currvlanid
+            except Exception as e:
+                errstr += '\n%s' % str(e)
+        self.log_error(bridgeifaceobj.name + ': ' + errstr, bridgeifaceobj)
+
+    def _add_ports(self, ifaceobj, ifaceobj_getfunc):
+        bridgeports = self._get_bridge_port_list(ifaceobj)
+        runningbridgeports = []
+
+        self.ipcmd.batch_start()
+        self._process_bridge_waitport(ifaceobj, bridgeports)
+        self.ipcmd.batch_start()
+        # Delete active ports not in the new port list
+        if not ifupdownflags.flags.PERFMODE:
+            runningbridgeports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
+            if runningbridgeports:
+                for bport in runningbridgeports:
+                    if not bridgeports or bport not in bridgeports:
+                        self.ipcmd.link_set(bport, 'nomaster')
+                        # set admin DOWN on all removed ports
+                        # that don't have config outside bridge
+                        if not ifaceobj_getfunc(bport):
+                            netlink.link_set_updown(bport, "down")
+                        # enable ipv6 for ports that were removed
+                        self.handle_ipv6([bport], '0')
+            else:
+                runningbridgeports = []
+        if not bridgeports:
+            self.ipcmd.batch_commit()
+            return []
+        err = 0
+        ports = 0
+        newbridgeports = Set(bridgeports).difference(Set(runningbridgeports))
+        newly_enslaved_ports = []
+
+        newbridgeports_ordered = []
+        for br_port in self._get_bridge_port_list_user_ordered(ifaceobj):
+            if br_port in newbridgeports:
+                newbridgeports_ordered.append(br_port)
+
+        for bridgeport in newbridgeports_ordered:
+            try:
+                if (not ifupdownflags.flags.DRYRUN and
+                    not self.ipcmd.link_exists(bridgeport)):
+                    self.log_error('%s: bridge port %s does not exist'
+                                   %(ifaceobj.name, bridgeport), ifaceobj)
+                    err += 1
+                    continue
+                hwaddress = self.ipcmd.link_get_hwaddress(bridgeport)
+                if not self._valid_ethaddr(hwaddress):
+                    self.log_warn('%s: skipping port %s, ' %(ifaceobj.name,
+                                  bridgeport) + 'invalid ether addr %s'
+                                  %hwaddress)
+                    continue
+                self.ipcmd.link_set(bridgeport, 'master', ifaceobj.name)
+                newly_enslaved_ports.append(bridgeport)
+                self.handle_ipv6([bridgeport], '1')
+                self.ipcmd.addr_flush(bridgeport)
+                ports += 1
+                if ports == 250:
+                    ports = 0
+                    self.ipcmd.batch_commit()
+                    self.ipcmd.batch_start()
+            except Exception, e:
+                self.logger.error(str(e))
+                pass
+        try:
+            self.ipcmd.batch_commit()
+        except Exception, e:
+            self._pretty_print_add_ports_error(str(e), ifaceobj,
+                                               bridgeports)
+            pass
+
+        if err:
+            self.log_error('bridge configuration failed (missing ports)')
+
+        return newly_enslaved_ports
+
+    def _process_bridge_maxwait(self, ifaceobj, portlist):
+        maxwait = ifaceobj.get_attr_value_first('bridge-maxwait')
+        if not maxwait: return
+        try:
+            maxwait = int(maxwait)
+        except:
+            self.log_warn('%s: invalid maxwait value \'%s\'' %(ifaceobj.name,
+                    maxwait))
+            return
+        if not maxwait: return
+        self.logger.info('%s: waiting for ports to go to fowarding state ..'
+                %ifaceobj.name)
+        try:
+            starttime = time.time()
+            while ((time.time() - starttime) < maxwait):
+                if all([False for p in portlist
+                    if self.read_file_oneline(
+                            '/sys/class/net/%s/brif/%s/state'
+                            %(ifaceobj.name, p)) != '3']):
+                    break;
+                time.sleep(1)
+        except Exception, e:
+            self.log_warn('%s: unable to process maxwait: %s'
+                    %(ifaceobj.name, str(e)))
+
+    def _ints_to_ranges(self, ints):
+        for a, b in itertools.groupby(enumerate(ints), lambda (x, y): y - x):
+            b = list(b)
+            yield b[0][1], b[-1][1]
+
+    def _ranges_to_ints(self, rangelist):
+        """ returns expanded list of integers given set of string ranges
+        example: ['1', '2-4', '6'] returns [1, 2, 3, 4, 6]
+        """
+        result = []
+        try:
+            for part in rangelist:
+                if '-' in part:
+                    a, b = part.split('-')
+                    a, b = int(a), int(b)
+                    result.extend(range(a, b + 1))
+                else:
+                    a = int(part)
+                    result.append(a)
+        except:
+            self.logger.warn('unable to parse vids \'%s\''
+                             %''.join(rangelist))
+            pass
+        return result
+
+    def _compress_into_ranges(self, vids_ints):
+        return ['%d' %start if start == end else '%d-%d' %(start, end)
+                       for start, end in self._ints_to_ranges(vids_ints)]
+
+    def _diff_vids(self, vids1_ints, vids2_ints):
+        return Set(vids2_ints).difference(vids1_ints), Set(vids1_ints).difference(vids2_ints)
+
+    def _compare_vids(self, vids1, vids2, pvid=None):
+        """ Returns true if the vids are same else return false """
+
+        vids1_ints = self._ranges_to_ints(vids1)
+        vids2_ints = self._ranges_to_ints(vids2)
+        set_diff = Set(vids1_ints).symmetric_difference(vids2_ints)
+        if pvid and int(pvid) in set_diff:
+            set_diff.remove(int(pvid))
+        if set_diff:
+            return False
+        else:
+            return True
+
+    def _set_bridge_mcqv4src_compat(self, ifaceobj):
+        #
+        # Sets old style igmp querier
+        #
+        attrval = ifaceobj.get_attr_value_first('bridge-mcqv4src')
+        if attrval:
+            running_mcqv4src = {}
+            if not ifupdownflags.flags.PERFMODE:
+                running_mcqv4src = self.brctlcmd.bridge_get_mcqv4src(ifaceobj.name)
+            mcqs = {}
+            srclist = attrval.split()
+            for s in srclist:
+                k, v = s.split('=')
+                mcqs[k] = v
+
+            k_to_del = Set(running_mcqv4src.keys()).difference(mcqs.keys())
+            for v in k_to_del:
+                self.brctlcmd.bridge_del_mcqv4src(ifaceobj.name, v)
+            for v in mcqs.keys():
+                self.brctlcmd.bridge_set_mcqv4src(ifaceobj.name, v, mcqs[v])
+        elif not ifupdownflags.flags.PERFMODE:
+            running_mcqv4src = self.brctlcmd.bridge_get_mcqv4src(ifaceobj.name)
+            if running_mcqv4src:
+                for v in running_mcqv4src.keys():
+                    self.brctlcmd.bridge_del_mcqv4src(ifaceobj.name, v)
+
+    def _get_running_vidinfo(self):
+        if self._running_vidinfo_valid:
+            return self._running_vidinfo
+        self._running_vidinfo = {}
+
+        # Removed check for PERFMODE.  Need the get in all cases
+        # including reboot, so that we can configure the pvid correctly.
+        self._running_vidinfo = self.ipcmd.bridge_port_vids_get_all_json()
+        self._running_vidinfo_valid = True
+        return self._running_vidinfo
+
+    def _set_bridge_vidinfo_compat(self, ifaceobj):
+        #
+        # Supports old style vlan vid info format
+        # for compatibility
+        #
+        bridge_port_pvids = ifaceobj.get_attr_value_first('bridge-port-pvids')
+        bridge_port_vids = ifaceobj.get_attr_value_first('bridge-port-vids')
+        if not bridge_port_pvids and not bridge_port_vids:
+            return
+
+        # Handle bridge vlan attrs
+        # Install pvids
+        if bridge_port_pvids:
+            portlist = self.parse_port_list(ifaceobj.name, bridge_port_pvids)
+            if not portlist:
+                self.log_warn('%s: could not parse \'%s %s\''
+                              %(ifaceobj.name, 'bridge-port-pvids',
+                                bridge_port_pvids))
+                return
+            for p in portlist:
+                try:
+                    (port, pvid) = p.split('=')
+                    pvid = int(pvid)
+                    running_pvid = self._get_running_pvid(port)
+                    if running_pvid:
+                        if running_pvid == pvid:
+                            continue
+                        else:
+                            self.ipcmd.bridge_port_pvid_del(port, running_pvid)
+                    self.ipcmd.bridge_port_pvid_add(port, pvid)
+                except Exception, e:
+                    self.log_warn('%s: failed to set pvid `%s` (%s)'
+                            %(ifaceobj.name, p, str(e)))
+
+        # install port vids
+        if bridge_port_vids:
+            portlist = self.parse_port_list(ifaceobj.name, bridge_port_vids)
+            if not portlist:
+                self.log_warn('%s: could not parse \'%s %s\'' %(ifaceobj.name,
+                              'bridge-port-vids', bridge_port_vids))
+                return
+            for p in portlist:
+                try:
+                    (port, val) = p.split('=')
+                    vids = val.split(',')
+                    vids_int =  self._ranges_to_ints(vids)
+                    running_vids = self.ipcmd.bridge_vlan_get_vids(port)
+                    if running_vids:
+                        (vids_to_del, vids_to_add) = \
+                                self._diff_vids(vids_int, running_vids)
+                        if vids_to_del:
+                            self.ipcmd.bridge_port_vids_del(port,
+                                    self._compress_into_ranges(vids_to_del))
+                        if vids_to_add:
+                            self.ipcmd.bridge_port_vids_add(port,
+                                    self._compress_into_ranges(vids_to_add))
+                    else:
+                        self.ipcmd.bridge_port_vids_add(port, vids_int)
+                except Exception, e:
+                    self.log_warn('%s: failed to set vid `%s` (%s)'
+                        %(ifaceobj.name, p, str(e)))
+
+    def _is_running_stp_state_on(self, bridgename):
+        """ Returns True if running stp state is on, else False """
+
+        stp_state_file = '/sys/class/net/%s/bridge/stp_state' %bridgename
+        try:
+            running_stp_state = self.read_file_oneline(stp_state_file)
+            return running_stp_state and running_stp_state != '0'
+        except:
+            return False
+
+    def _is_config_stp_state_on(self, ifaceobj):
+        """ Returns true if user specified stp state is on, else False """
+
+        stp_attr = ifaceobj.get_attr_value_first('bridge-stp')
+        if not stp_attr:
+            return self.default_stp_on
+        return utils.get_boolean_from_string(stp_attr)
+
+    def get_bridge_mcsnoop_value(self, ifaceobj):
+        mcsnoop = ifaceobj.get_attr_value_first('bridge-mcsnoop')
+        if not mcsnoop and ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN:
+            return self._vxlan_bridge_default_igmp_snooping
+        return mcsnoop
+
+    def fill_ifla_info_data_with_ifla_br_attribute(self,
+                                                   ifla_info_data,
+                                                   link_just_created,
+                                                   ifname,
+                                                   nl_attr,
+                                                   attr_name,
+                                                   user_config):
+        try:
+            translate_func = self._ifla_br_attributes_translate_user_config_to_netlink_map.get(nl_attr)
+
+            if not callable(translate_func):
+                return
+
+            if not user_config:
+                user_config = policymanager.policymanager_api.get_iface_default(
+                    module_name=self.__class__.__name__,
+                    ifname=ifname,
+                    attr=attr_name
+                )
+
+            old_cache_key = self._ifla_br_attributes_old_cache_key_map.get(nl_attr)
+            if old_cache_key and not link_just_created:
+                cached_value = self.brctlcmd.link_cache_get([ifname, 'linkinfo', old_cache_key])
+                if not cached_value:
+                    # the link already exists but we don't have any value
+                    # cached for this attr, it probably means that the
+                    # capability is not available on this system (i.e old kernel)
+                    self.logger.debug('%s: ignoring %s %s: capability '
+                                      'probably not supported on this system'
+                                      % (ifname, attr_name, user_config))
+                    return
+                # we need to convert the cache value to "netlink" format
+                cached_value = translate_func(cached_value.lower())
+            else:
+                cached_value = None
+
+            if not user_config and not link_just_created and cached_value is not None:
+                # there is no user configuration for this attribute
+                # if the bridge existed before we need to check if
+                # this attribute needs to be reset to default value
+                default_value = self.get_attr_default_value(attr_name)
+
+                if default_value:
+                    # the attribute has a default value, we need to convert it to
+                    # netlink format to compare it with the cache value
+                    default_value_nl = translate_func(default_value)  # default_value.lower()
+
+                    if default_value_nl != cached_value:
+                        # the running value difers from the default value
+                        # but the user didn't specify any config
+                        # resetting attribute to default
+                        ifla_info_data[nl_attr] = default_value_nl
+                        self.logger.info('%s: reset %s to default: %s' % (ifname, attr_name, default_value))
+            elif user_config:
+                user_config_nl = translate_func(user_config)  # user_config.lower()
+
+                if user_config_nl != cached_value:
+                    ifla_info_data[nl_attr] = user_config_nl
+
+                    if cached_value is not None:
+                        self.logger.info('%s: set %s %s (cache %s)' % (ifname, attr_name, user_config, cached_value))
+                    else:
+                        self.logger.info('%s: set %s %s' % (ifname, attr_name, user_config))
+        except Exception as e:
+            self.logger.warning('%s: %s: %s' % (ifname, attr_name, str(e)))
+
+    def up_apply_bridge_settings(self, ifaceobj, link_just_created, bridge_vlan_aware):
+        ifla_info_data = dict()
+        ifname = ifaceobj.name
+
+        self.logger.info('%s: apply bridge settings' % ifname)
+
+        for attr_name, nl_attr in self._ifla_br_attributes_map:
+            self.fill_ifla_info_data_with_ifla_br_attribute(
+                ifla_info_data=ifla_info_data,
+                link_just_created=link_just_created,
+                ifname=ifname,
+                nl_attr=nl_attr,
+                attr_name=attr_name,
+                user_config=ifaceobj.get_attr_value_first(attr_name)
+            )
+
+        # bridge-mcsnoop
+        self.fill_ifla_info_data_with_ifla_br_attribute(
+            ifla_info_data=ifla_info_data,
+            link_just_created=link_just_created,
+            ifname=ifname,
+            nl_attr=Link.IFLA_BR_MCAST_SNOOPING,
+            attr_name='bridge-mcsnoop',
+            user_config=self.get_bridge_mcsnoop_value(ifaceobj)
+        )
+
+        # bridge-vlan-stats
+        if bridge_vlan_aware:
+            self.fill_ifla_info_data_with_ifla_br_attribute(
+                ifla_info_data=ifla_info_data,
+                link_just_created=link_just_created,
+                ifname=ifname,
+                nl_attr=Link.IFLA_BR_VLAN_STATS_ENABLED,
+                attr_name='bridge-vlan-stats',
+                user_config=ifaceobj.get_attr_value_first('bridge-vlan-stats') or self.default_vlan_stats
+            )
+
+        try:
+            if self._is_config_stp_state_on(ifaceobj):
+                if not self._is_running_stp_state_on(ifname):
+                    ifla_info_data[Link.IFLA_BR_STP_STATE] = 1
+                    self.logger.info('%s: stp state reset, reapplying port settings' % ifname)
+                    ifaceobj.module_flags[ifaceobj.name] = \
+                        ifaceobj.module_flags.setdefault(self.name, 0) | \
+                        bridgeFlags.PORT_PROCESSED_OVERRIDE
+            else:
+                # If stp not specified and running stp state on, set it to off
+                if self._is_running_stp_state_on(ifname):
+                    self.logger.info('%s: bridge-stp not specified but running: turning stp off')
+                    ifla_info_data[Link.IFLA_BR_STP_STATE] = 0
+        except Exception as e:
+            self.logger.warning('%s: bridge stp: %s' % (ifname, str(e)))
+
+        if ifla_info_data:
+            netlink.link_add_set(ifname=ifname, kind='bridge', ifla_info_data=ifla_info_data, link_exists=True)
+
+    def _check_vids(self, ifaceobj, vids):
+        ret = True
+        for v in vids:
+            try:
+                if '-' in v:
+                    va, vb = v.split('-')
+                    va, vb = int(va), int(vb)
+                    self._handle_reserved_vlan(va, ifaceobj.name, end=vb)
+                else:
+                    va = int(v)
+                    self._handle_reserved_vlan(va, ifaceobj.name)
+            except exceptions.ReservedVlanException as e:
+                raise e
+            except Exception:
+                self.logger.warn('%s: unable to parse vid \'%s\''
+                                 %(ifaceobj.name, v))
+        return ret
+
+    def _get_running_pvid(self, ifacename):
+        pvid = 0
+
+        running_vidinfo = self._get_running_vidinfo()
+        for vinfo in running_vidinfo.get(ifacename, {}):
+            v = vinfo.get('vlan')
+            pvid = v if 'PVID' in vinfo.get('flags', []) else 0
+            if pvid:
+                return pvid
+        return pvid
+
+    def _get_running_vids_n_pvid_str(self, ifacename):
+        vids = []
+        pvid = None
+
+        (vids, pvid) = self.ipcmd.bridge_vlan_get_vids_n_pvid(ifacename)
+
+        if vids:
+            ret_vids = self._compress_into_ranges(vids)
+        else:
+            ret_vids = None
+
+        if pvid:
+            ret_pvid = '%s' %pvid
+        else:
+            ret_pvid = None
+        return (ret_vids, ret_pvid)
+
+    def _apply_bridge_vids_and_pvid(self, bportifaceobj, vids, pvid,
+                                    isbridge):
+        """ This method is a combination of methods _apply_bridge_vids and
+            _apply_bridge_port_pvids above. A combined function is
+            found necessary to do the deletes first and the adds later
+            because kernel does honor vid info flags during deletes.
+
+        """
+        if not isbridge and bportifaceobj.link_kind & ifaceLinkKind.VXLAN:
+            if not vids or not pvid or len(vids) > 1 or vids[0] != pvid:
+                self._error_vxlan_in_vlan_aware_br(bportifaceobj,
+                                                   bportifaceobj.upperifaces[0])
+                return
+
+        vids_int =  self._ranges_to_ints(vids)
+        try:
+            pvid_int = int(pvid) if pvid else 0
+        except Exception:
+            self.logger.warn('%s: unable to parse pvid \'%s\''
+                             %(bportifaceobj.name, pvid))
+            pvid_int = 0
+            pass
+
+        vids_to_del = []
+        vids_to_add = vids_int
+        pvid_to_del = None
+        pvid_to_add = pvid_int
+
+        try:
+            if not self._check_vids(bportifaceobj, vids):
+               return
+
+            (running_vids, running_pvid) = self.ipcmd.bridge_vlan_get_vids_n_pvid(
+                                                        bportifaceobj.name)
+
+            if not running_vids and not running_pvid:
+                # There cannot be a no running pvid.
+                # It might just not be in our cache:
+                # this can happen if at the time we were
+                # creating the bridge vlan cache, the port
+                # was not part of the bridge. And we need
+                # to make sure both vids and pvid is not in
+                # the cache, to declare that our cache may
+                # be stale.
+                running_pvid = 1
+                running_vids = [1]
+
+            if running_vids:
+                (vids_to_del, vids_to_add) = \
+                    self._diff_vids(vids_to_add, running_vids)
+
+            if running_pvid:
+                if running_pvid != pvid_int and running_pvid != 0:
+                    pvid_to_del = running_pvid
+
+            if (pvid_to_del and (pvid_to_del in vids_int) and
+                (pvid_to_del not in vids_to_add)):
+                # kernel deletes dont take into account
+                # bridge vid flags and its possible that
+                # the pvid deletes we do end up deleting
+                # the vids. Be proactive and add the pvid
+                # to the vid add list if it is in the vids
+                # and not already part of vids_to_add.
+                # This helps with a small corner case:
+                #   - running
+                #       pvid 100
+                #       vid 101 102
+                #   - new change is going to move the state to
+                #       pvid 101
+                #       vid 100 102
+                vids_to_add.add(pvid_to_del)
+        except exceptions.ReservedVlanException as e:
+            raise e
+        except Exception, e:
+            self.log_error('%s: failed to process vids/pvids'
+                           %bportifaceobj.name + ' vids = %s' %str(vids) +
+                           'pvid = %s ' %pvid + '(%s)' %str(e),
+                           bportifaceobj, raise_error=False)
+        try:
+            if vids_to_del:
+               if pvid_to_add in vids_to_del:
+                   vids_to_del.remove(pvid_to_add)
+               self.ipcmd.bridge_vids_del(bportifaceobj.name,
+                                          self._compress_into_ranges(
+                                          vids_to_del), isbridge)
+        except Exception, e:
+                self.log_warn('%s: failed to del vid `%s` (%s)'
+                        %(bportifaceobj.name, str(vids_to_del), str(e)))
+
+        try:
+            if pvid_to_del:
+               self.ipcmd.bridge_port_pvid_del(bportifaceobj.name,
+                                               pvid_to_del)
+        except Exception, e:
+                self.log_warn('%s: failed to del pvid `%s` (%s)'
+                        %(bportifaceobj.name, pvid_to_del, str(e)))
+
+        try:
+            if vids_to_add:
+               self.ipcmd.bridge_vids_add(bportifaceobj.name,
+                                          self._compress_into_ranges(
+                                          vids_to_add), isbridge)
+        except Exception, e:
+                self.log_error('%s: failed to set vid `%s` (%s)'
+                               %(bportifaceobj.name, str(vids_to_add),
+                                 str(e)), bportifaceobj, raise_error=False)
+
+        try:
+            if pvid_to_add and pvid_to_add != running_pvid:
+                self.ipcmd.bridge_port_pvid_add(bportifaceobj.name,
+                                                pvid_to_add)
+        except Exception, e:
+                self.log_error('%s: failed to set pvid `%s` (%s)'
+                               %(bportifaceobj.name, pvid_to_add, str(e)),
+                               bportifaceobj)
+
+    def _apply_bridge_vlan_aware_port_settings_all(self, bportifaceobj,
+                                                   bridge_vids=None,
+                                                   bridge_pvid=None):
+        vids = None
+        pvids = None
+        vids_final = []
+        pvid_final = None
+        bport_access = bportifaceobj.get_attr_value_first('bridge-access')
+        if bport_access:
+            vids = re.split(r'[\s\t]\s*', bport_access)
+            pvids = vids
+            allow_untagged = 'yes'
+            self.check_bridge_port_vid_attrs(bportifaceobj)
+        else:
+            allow_untagged = bportifaceobj.get_attr_value_first('bridge-allow-untagged') or 'yes'
+
+            bport_vids = self.get_ifaceobj_bridge_vids_value(bportifaceobj)
+            if bport_vids:
+                vids = re.split(r'[\s\t,]\s*', bport_vids)
+
+            bport_pvids = bportifaceobj.get_attr_value_first('bridge-pvid')
+            if bport_pvids:
+                pvids = re.split(r'[\s\t]\s*', bport_pvids)
+
+        if vids:
+            vids_final =  vids
+        elif bridge_vids:
+            vids_final = bridge_vids
+
+        if allow_untagged == 'yes':
+            if pvids:
+                pvid_final = pvids[0]
+            elif bridge_pvid:
+                pvid_final = bridge_pvid
+            else:
+                pvid_final = '1'
+        else:
+            pvid_final = None
+
+        self._apply_bridge_vids_and_pvid(bportifaceobj, vids_final,
+                                         pvid_final, False)
+
+    def _apply_bridge_port_settings_all(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aware):
+        err = False
+
+        if (ifaceobj.get_attr_value_first('bridge-port-vids') and
+                ifaceobj.get_attr_value_first('bridge-port-pvids')):
+            # Old style bridge port vid info
+            # skip new style setting on ports
+            return
+        self.logger.info('%s: applying bridge configuration '
+                         %ifaceobj.name + 'specific to ports')
+
+        bridge_vids = self.get_ifaceobj_bridge_vids_value(ifaceobj)
+        if bridge_vids:
+           bridge_vids = re.split(r'[\s\t,]\s*', bridge_vids)
+        else:
+           bridge_vids = None
+
+        bridge_pvid = ifaceobj.get_attr_value_first('bridge-pvid')
+        if bridge_pvid:
+           bridge_pvid = re.split(r'[\s\t]\s*', bridge_pvid)[0]
+        else:
+           bridge_pvid = None
+
+        if (ifaceobj.module_flags.get(self.name, 0x0) &
+                bridgeFlags.PORT_PROCESSED_OVERRIDE):
+            port_processed_override = True
+        else:
+            port_processed_override = False
+
+        bridgeports = self._get_bridge_port_list(ifaceobj)
+        if not bridgeports:
+           self.logger.debug('%s: cannot find bridgeports' %ifaceobj.name)
+           return
+        self.ipcmd.batch_start()
+        for bport in bridgeports:
+            # Use the brctlcmd bulk set method: first build a dictionary
+            # and then call set
+            if not self.ipcmd.bridge_port_exists(ifaceobj.name, bport):
+                self.logger.info('%s: skipping bridge config' %ifaceobj.name +
+                        ' for port %s (missing port)' %bport)
+                continue
+            self.logger.info('%s: processing bridge config for port %s'
+                             %(ifaceobj.name, bport))
+            bportifaceobjlist = ifaceobj_getfunc(bport)
+            if not bportifaceobjlist:
+               continue
+            for bportifaceobj in bportifaceobjlist:
+                # Dont process bridge port if it already has been processed
+                # and there is no override on port_processed
+                if (not port_processed_override and
+                    (bportifaceobj.module_flags.get(self.name,0x0) &
+                     bridgeFlags.PORT_PROCESSED)):
+                    continue
+                try:
+                    # Add attributes specific to the vlan aware bridge
+                    if bridge_vlan_aware:
+                        self._apply_bridge_vlan_aware_port_settings_all(
+                                bportifaceobj, bridge_vids, bridge_pvid)
+                    elif self.warn_on_untagged_bridge_absence:
+                        self._check_untagged_bridge(ifaceobj.name, bportifaceobj, ifaceobj_getfunc)
+                except exceptions.ReservedVlanException as e:
+                    raise e
+                except Exception, e:
+                    err = True
+                    self.logger.warn('%s: %s' %(ifaceobj.name, str(e)))
+                    pass
+        self.ipcmd.bridge_batch_commit()
+        if err:
+           raise Exception('%s: errors applying port settings' %ifaceobj.name)
+
+    def _check_untagged_bridge(self, bridgename, bridgeportifaceobj, ifaceobj_getfunc):
+        if bridgeportifaceobj.link_kind & ifaceLinkKind.VLAN:
+            lower_ifaceobj_list = ifaceobj_getfunc(bridgeportifaceobj.lowerifaces[0])
+            if lower_ifaceobj_list and lower_ifaceobj_list[0] and \
+                    not lower_ifaceobj_list[0].link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
+                self.logger.warn('%s: untagged bridge not found. Please configure a bridge with untagged bridge ports to avoid Spanning Tree Interoperability issue.' % bridgename)
+                self.warn_on_untagged_bridge_absence = False
+
+    def bridge_port_get_bridge_name(self, ifaceobj):
+        bridgename = self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name)
+        if not bridgename:
+            # bridge port is not enslaved to a bridge we need to find
+            # the bridge in it's upper ifaces then enslave it
+            for u in ifaceobj.upperifaces:
+                if self.ipcmd.is_bridge(u):
+                    return True, u
+            return False, None
+        # return should_enslave port, bridgename
+        return False, bridgename
+
+    def up_bridge_port_vlan_aware_bridge(self, ifaceobj, ifaceobj_getfunc, bridge_name, should_enslave_port):
+        if should_enslave_port:
+            netlink.link_set_master(ifaceobj.name, bridge_name)
+            self.handle_ipv6([ifaceobj.name], '1')
+
+        bridge_vids = self._get_bridge_vids(bridge_name, ifaceobj_getfunc)
+        bridge_pvid = self._get_bridge_pvid(bridge_name, ifaceobj_getfunc)
+        try:
+            self._apply_bridge_vlan_aware_port_settings_all(ifaceobj, bridge_vids, bridge_pvid)
+        except Exception as e:
+            self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj)
+            return
+
+    def up_bridge_port(self, ifaceobj, ifaceobj_getfunc):
+        should_enslave_port, bridge_name = self.bridge_port_get_bridge_name(ifaceobj)
+
+        if not bridge_name:
+            # bridge doesn't exist
+            return
+
+        vlan_aware_bridge = self.ipcmd.bridge_is_vlan_aware(bridge_name)
+        if vlan_aware_bridge:
+            self.up_bridge_port_vlan_aware_bridge(ifaceobj,
+                                                  ifaceobj_getfunc,
+                                                  bridge_name,
+                                                  should_enslave_port)
+
+        bridge_ifaceobj = ifaceobj_getfunc(bridge_name)[0]
+
+        self.up_apply_brports_attributes(target_ports=[ifaceobj.name],
+                                         ifaceobj=bridge_ifaceobj,
+                                         ifaceobj_getfunc=ifaceobj_getfunc,
+                                         bridge_vlan_aware=vlan_aware_bridge)
+
+        ifaceobj.module_flags[self.name] = ifaceobj.module_flags.setdefault(self.name, 0) | bridgeFlags.PORT_PROCESSED
+
+    def up_check_bridge_vlan_aware(self, ifaceobj, ifaceobj_getfunc, link_exists):
+        if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE:
+            if not self.check_bridge_vlan_aware_port(ifaceobj, ifaceobj_getfunc):
+                return False
+            if link_exists:
+                ifaceobj.module_flags[self.name] = ifaceobj.module_flags.setdefault(self.name, 0) | bridgeFlags.PORT_PROCESSED_OVERRIDE
+            return True
+        return False
+
+    @staticmethod
+    def parse_interface_list_value(user_config):
+        config = dict()
+        for entry in user_config.split():
+            ifname, value = entry.split('=')
+            config[ifname] = value
+        return config
+
+    def sync_bridge_learning_to_vxlan_brport(self, bridge_name, bridge_vlan_aware, brport_ifaceobj,
+                                             brport_name, brport_ifla_info_slave_data, brport_learning):
+        """
+            brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN
+            and
+            brport_ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT
+
+            Checks are not performed in this function and must be verified
+             before. This is done this way to avoid calling this method on
+             non vlan & bridge port interfaces thus wasting a bit less time
+        """
+
+        kind = None
+        ifla_info_data = {}
+
+        brport_vxlan_learning_config = brport_ifaceobj.get_attr_value_first('vxlan-learning')
+        # if the user explicitly defined vxlan-learning we need to honor his config
+        # and not sync vxlan-learning with bridge-learning
+
+        brport_vxlan_learning = self.ipcmd.get_vxlandev_learning(brport_name)
+
+        # if BRIDGE_LEARNING is in the desired configuration
+        # and differs from the running vxlan configuration
+        if brport_learning is not None and brport_learning != brport_vxlan_learning and not brport_vxlan_learning_config:
+            kind = 'vxlan'
+            ifla_info_data = {Link.IFLA_VXLAN_LEARNING: brport_learning}
+            self.logger.info('%s: %s: vxlan learning and bridge learning out of sync: set %s'
+                             % (bridge_name, brport_name, brport_learning))
+
+        elif brport_learning is None and bridge_vlan_aware:
+            # is bridge-learning is not configured but the bridge is vlan-aware
+
+            running_value = self.ipcmd.get_brport_learning_bool(brport_name)
+            default_value = utils.get_boolean_from_string(self.get_mod_subattr('bridge-learning', 'default'))
+
+            if default_value != running_value:
+                brport_ifla_info_slave_data[Link.IFLA_BRPORT_LEARNING] = default_value
+
+                if not brport_vxlan_learning_config:
+                    kind = 'vxlan'
+                    ifla_info_data = {Link.IFLA_VXLAN_LEARNING: default_value}
+                    self.logger.info('%s: %s: reset brport learning to %s and sync vxlan learning'
+                                     % (bridge_name, brport_name, default_value))
+
+        # if kind and ifla_info_data are set they will be added to the
+        # netlink request on the VXLAN brport, to sync IFLA_VXLAN_LEARNING
+        return kind, ifla_info_data
+
+    def check_vxlan_brport_arp_suppress(self, ifaceobj, bridge_vlan_aware, brport_ifaceobj, brport_name, user_config):
+        if user_config:
+            if self.arp_nd_suppress_only_on_vxlan and not brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN:
+                self.logger.warning('%s: %s: \'bridge-arp-nd-suppress\' '
+                                    'is not supported on a non-vxlan port'
+                                    % (ifaceobj.name, brport_name))
+                raise Exception()
+        elif (bridge_vlan_aware and
+                  (not self.arp_nd_suppress_only_on_vxlan or
+                       (self.arp_nd_suppress_only_on_vxlan and
+                                brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN))):
+            return self.get_mod_subattr('bridge-arp-nd-suppress', 'default')
+        return None
+
+    def up_apply_brports_attributes(self, ifaceobj, ifaceobj_getfunc, bridge_vlan_aware, target_ports=[], newly_enslaved_ports=[]):
+        ifname = ifaceobj.name
+
+        try:
+            brports_ifla_info_slave_data    = dict()
+            brport_ifaceobj_dict            = dict()
+
+            running_brports = self.brctlcmd.get_bridge_ports(ifname)
+
+            if target_ports:
+                new_targets = []
+                for brport_name in target_ports:
+                    if brport_name not in running_brports:
+                        self.logger.info('%s: not enslaved to bridge %s: ignored for now' % (brport_name, ifname))
+                    else:
+                        new_targets.append(brport_name)
+                running_brports = new_targets
+
+            self.logger.info('%s: applying bridge port configuration: %s' % (ifname, running_brports))
+
+            # If target_ports is specified we want to configure only this
+            # sub-list of port we need to check if these ports are already
+            # enslaved, if not they will be ignored.
+            # If target_ports is not populated we will apply the brport
+            # attributes on all running brport.
+
+            for port in running_brports:
+                brport_list = ifaceobj_getfunc(port)
+                if brport_list:
+                    brport_ifaceobj_dict[port] = brport_list[0]
+                    brports_ifla_info_slave_data[port] = dict()
+
+            bridge_ports_learning = {}
+
+            # we iterate through all IFLA_BRPORT supported attributes
+            for attr_name, nl_attr in self._ifla_brport_attributes_map:
+                br_config = ifaceobj.get_attr_value_first(attr_name)
+                translate_func = self._ifla_brport_attributes_translate_user_config_to_netlink_map.get(nl_attr)
+
+                if not translate_func:
+                    # if no translation function is found,
+                    # we ignore this attribute and continue
+                    continue
+
+                if not br_config:
+                    # user didn't specify any value for this attribute
+                    # looking at policy overrides
+                    br_config = policymanager.policymanager_api.get_iface_default(
+                        module_name=self.__class__.__name__,
+                        ifname=ifname,
+                        attr=attr_name
+                    )
+
+                if br_config:
+                    #if bridge_vlan_aware:
+                    #    self.logger.info('%s: is a vlan-aware bridge, "%s %s" '
+                    #                     'should be configured under the ports'
+                    #                     % (ifname, attr_name, br_config))
+
+                    # convert the <interface-yes-no-0-1-list> and <interface-range-list> value to subdict
+                    # brport_name: { attr: value }
+                    # example:
+                    #   bridge-portprios swp1=5 swp2=32
+                    # swp1: { bridge-portprios: 5 } swp2: { bridge-portprios: 32}
+                    if '=' in br_config:
+                        try:
+                            br_config = self.parse_interface_list_value(br_config)
+                        except:
+                            self.log_error('error while parsing \'%s %s\'' % (attr_name, br_config))
+                            continue
+
+                for brport_ifaceobj in brport_ifaceobj_dict.values():
+                    brport_config = brport_ifaceobj.get_attr_value_first(attr_name)
+                    brport_name = brport_ifaceobj.name
+
+                    if not ifupdownflags.flags.PERFMODE and brport_name not in newly_enslaved_ports:
+                        # if the port has just been enslaved, info_slave_data is not cached yet
+                        cached_value = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', nl_attr])
+                    else:
+                        cached_value = None
+
+                    if not brport_config:
+                        # if a brport attribute was specified under the bridge and not under the port
+                        # we assign the bridge value to the port. If an attribute is both defined under
+                        # the bridge and the brport we keep the value of the port and ignore the br val.
+                        if type(br_config) == dict:
+                            # if the attribute value was in the format interface-list-value swp1=XX swp2=YY
+                            # br_config is a dictionary, example:
+                            # bridge-portprios swp1=5 swp2=32 = {swp1: 5, swp2: 32}
+                            brport_config = br_config.get(brport_name)
+                        else:
+                            brport_config = br_config
+
+                    if not brport_config:
+                        brport_config = policymanager.policymanager_api.get_iface_default(
+                            module_name=self.__class__.__name__,
+                            ifname=brport_name,
+                            attr=attr_name
+                        )
+
+                    user_config = brport_config
+
+                    # attribute specific work
+                    # This shouldn't be here but we don't really have a choice otherwise this
+                    # will require too much code duplication and will make the code very complex
+                    if nl_attr == Link.IFLA_BRPORT_ARP_SUPPRESS:
+                        try:
+                            arp_suppress = self.check_vxlan_brport_arp_suppress(ifaceobj,
+                                                                                bridge_vlan_aware,
+                                                                                brport_ifaceobj,
+                                                                                brport_name,
+                                                                                user_config)
+                            if arp_suppress:
+                                user_config = arp_suppress
+                        except:
+                            continue
+                    elif nl_attr == Link.IFLA_BRPORT_GROUP_FWD_MASK:
+                        # special handking for group_fwd_mask because Cisco proprietary
+                        # protocol needs to be set via a private netlink attribute
+                        self.ifla_brport_group_fwd_mask(ifname, brport_name,
+                                                        brports_ifla_info_slave_data,
+                                                        user_config, cached_value)
+                        continue
+
+                    #if brport_config:
+                    #    if not bridge_vlan_aware:
+                    #        self.logger.info('%s: %s: is not a vlan-aware bridge, "%s %s" '
+                    #                         'should be configured under the bridge'
+                    #                         % (ifname, brport_name,
+                    #                            attr_name, brport_config))
+
+                    if user_config:
+                        user_config_nl = translate_func(user_config)
+                        # check config value against running value
+                        if user_config_nl != cached_value:
+                            brports_ifla_info_slave_data[brport_name][nl_attr] = user_config_nl
+                            self.logger.info('%s: %s: set %s %s' % (ifname, brport_name, attr_name, user_config))
+                            self.logger.debug('(cache %s)' % cached_value)
+
+                        if nl_attr == Link.IFLA_BRPORT_LEARNING:
+                            # for vxlan-learning sync purposes we need to save the user config for each brports.
+                            # The dictionary 'brports_ifla_info_slave_data' might not contain any value for
+                            # IFLA_BRPORT_LEARNING if the user value is already configured and running
+                            # nevertheless we still need to check if the vxlan-learning is rightly synced with
+                            # the brport since it might go out of sync for X and Y reasons.
+                            bridge_ports_learning[brport_name] = user_config_nl
+
+                    elif cached_value is not None:
+                        # no config found, do we need to reset to default?
+                        default = self.get_attr_default_value(attr_name)
+                        if default:
+                            default_netlink = translate_func(default)
+
+                            if (nl_attr == Link.IFLA_BRPORT_LEARNING
+                                and not ifupdownflags.flags.PERFMODE
+                                    and brport_name not in newly_enslaved_ports):
+                                try:
+                                    if self.ipcmd.get_brport_peer_link(brport_name):
+                                        if default_netlink != cached_value:
+                                            self.logger.debug('%s: %s: bridge port peerlink: ignoring bridge-learning'
+                                                              % (ifname, brport_name))
+                                        continue
+                                    bridge_ports_learning[brport_name] = default_netlink
+                                except Exception as e:
+                                    self.logger.debug('%s: %s: peerlink check: %s' % (ifname, brport_name, str(e)))
+
+                            if default_netlink != cached_value:
+                                self.logger.info('%s: %s: %s: no configuration detected, resetting to default %s'
+                                                 % (ifname, brport_name, attr_name, default))
+                                self.logger.debug('(cache %s)' % cached_value)
+                                brports_ifla_info_slave_data[brport_name][nl_attr] = default_netlink
+
+            # applying bridge port configuration via netlink
+            for brport_name, brport_ifla_info_slave_data in brports_ifla_info_slave_data.items():
+
+                brport_ifaceobj = brport_ifaceobj_dict.get(brport_name)
+                if (brport_ifaceobj
+                    and brport_ifaceobj.link_kind & ifaceLinkKind.VXLAN
+                    and brport_ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT):
+                    # if the brport is a VXLAN, we might need to sync the VXLAN learning with the brport_learning val
+                    # we use the same netlink request, by specfying kind=vxlan and ifla_info_data={vxlan_learning=0/1}
+                    kind, ifla_info_data = self.sync_bridge_learning_to_vxlan_brport(ifaceobj.name,
+                                                                                     bridge_vlan_aware,
+                                                                                     brport_ifaceobj,
+                                                                                     brport_name,
+                                                                                     brport_ifla_info_slave_data,
+                                                                                     bridge_ports_learning.get(brport_name))
+                else:
+                    kind = None
+                    ifla_info_data = {}
+
+                if brport_ifla_info_slave_data or ifla_info_data:
+                    try:
+                        netlink.link_add_set(ifname=brport_name,
+                                             kind=kind,
+                                             ifla_info_data=ifla_info_data,
+                                             slave_kind='bridge',
+                                             ifla_info_slave_data=brport_ifla_info_slave_data)
+                    except Exception as e:
+                        self.logger.warning('%s: %s: %s' % (ifname, brport_name, str(e)))
+
+            self._set_bridge_vidinfo_compat(ifaceobj)
+            self._set_bridge_mcqv4src_compat(ifaceobj)
+            self._process_bridge_maxwait(ifaceobj, self._get_bridge_port_list(ifaceobj))
+
+        except Exception as e:
+            self.log_error(str(e), ifaceobj)
+
+    def ifla_brport_group_fwd_mask(self, ifname, brport_name, brports_ifla_info_slave_data, user_config, cached_ifla_brport_group_fwd_mask):
+        """
+            Support for IFLA_BRPORT_GROUP_FWD_MASK and IFLA_BRPORT_GROUP_FWD_MASKHI
+            Since this is the only ifupdown2 attribute dealing with more than 1 netlink
+            field we need to have special handling for that.
+        """
+        ifla_brport_group_fwd_mask = 0
+        ifla_brport_group_fwd_maskhi = 0
+        if user_config:
+            for group in re.split(',|\s*', user_config):
+                if not group:
+                    continue
+
+                callback = self.l2protocol_tunnel_callback.get(group)
+
+                if not callable(callback):
+                    self.logger.warning('%s: %s: bridge-l2protocol-tunnel ignoring invalid parameter \'%s\'' % (ifname, brport_name, group))
+                else:
+                    ifla_brport_group_fwd_mask, ifla_brport_group_fwd_maskhi = callback(ifla_brport_group_fwd_mask, ifla_brport_group_fwd_maskhi)
+
+        # cached_ifla_brport_group_fwd_mask is given as parameter because it was already pulled out from the cache in the functio above
+        cached_ifla_brport_group_fwd_maskhi = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', Link.IFLA_BRPORT_GROUP_FWD_MASKHI])
+
+        log_mask_change = True
+        # if user specify bridge-l2protocol-tunnel stp cdp
+        # we need to set both MASK and MASKHI but we only want to log once
+
+        if cached_ifla_brport_group_fwd_mask is None:
+            cached_ifla_brport_group_fwd_mask = 0
+        if cached_ifla_brport_group_fwd_maskhi is None:
+            cached_ifla_brport_group_fwd_maskhi = 0
+
+        # if the cache value is None it means that the kernel doesn't support this attribute
+        # or that the cache is stale, we dumped this intf before it was enslaved in the bridge
+
+        if ifla_brport_group_fwd_mask != cached_ifla_brport_group_fwd_mask:
+            if log_mask_change:
+                self.logger.info('%s: %s: set bridge-l2protocol-tunnel %s' % (ifname, brport_name, user_config))
+                self.logger.debug('(cache %s)' % cached_ifla_brport_group_fwd_mask)
+                log_mask_change = False
+            brports_ifla_info_slave_data[brport_name][Link.IFLA_BRPORT_GROUP_FWD_MASK] = ifla_brport_group_fwd_mask
+
+        if ifla_brport_group_fwd_maskhi != cached_ifla_brport_group_fwd_maskhi:
+            if log_mask_change:
+                self.logger.info('%s: %s: set bridge-l2protocol-tunnel %s' % (ifname, brport_name, user_config))
+                self.logger.debug('(cache %s)' % cached_ifla_brport_group_fwd_maskhi)
+            brports_ifla_info_slave_data[brport_name][Link.IFLA_BRPORT_GROUP_FWD_MASKHI] = ifla_brport_group_fwd_maskhi
+
+    def up_bridge(self, ifaceobj, ifaceobj_getfunc):
+        ifname = ifaceobj.name
+
+        if ifupdownflags.flags.PERFMODE:
+            link_just_created = True
+            link_exists = False
+        else:
+            link_exists = self.ipcmd.link_exists(ifaceobj.name)
+            link_just_created = not link_exists
+
+        if not link_exists:
+            netlink.link_add_bridge(ifname)
+        else:
+            self.logger.info('%s: bridge already exists' % ifname)
+
+        bridge_vlan_aware = self.up_check_bridge_vlan_aware(ifaceobj, ifaceobj_getfunc, not link_just_created)
+
+        self.up_apply_bridge_settings(ifaceobj, link_just_created, bridge_vlan_aware)
+
+        try:
+            newly_enslaved_ports = self._add_ports(ifaceobj, ifaceobj_getfunc)
+            self.up_apply_brports_attributes(ifaceobj, ifaceobj_getfunc, bridge_vlan_aware,
+                                             newly_enslaved_ports=newly_enslaved_ports)
+        except Exception as e:
+            self.logger.warning('%s: apply bridge ports settings: %s' % (ifname, str(e)))
+
+        running_ports = ''
+        try:
+            running_ports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
+            if not running_ports:
+                return
+            self.handle_ipv6([], '1', ifaceobj=ifaceobj)
+            self._apply_bridge_port_settings_all(ifaceobj,
+                                                 ifaceobj_getfunc=ifaceobj_getfunc,
+                                                 bridge_vlan_aware=bridge_vlan_aware)
+        except exceptions.ReservedVlanException as e:
+            raise e
+        except Exception as e:
+            self.logger.warning('%s: apply bridge settings: %s' % (ifname, str(e)))
+        finally:
+            if ifaceobj.link_type != ifaceLinkType.LINK_NA:
+                for p in running_ports:
+                    if (ifaceobj_getfunc(p)[0].link_privflags &
+                            ifaceLinkPrivFlags.KEEP_LINK_DOWN):
+                        netlink.link_set_updown(p, "down")
+                        continue
+                    try:
+                        netlink.link_set_updown(p, "up")
+                    except Exception, e:
+                        self.logger.debug('%s: %s: link set up (%s)'
+                                          % (ifaceobj.name, p, str(e)))
+                        pass
+
+        try:
+            self._up_bridge_mac(ifaceobj, ifaceobj_getfunc)
+        except Exception as e:
+            self.logger.warning('%s: setting bridge mac address: %s' % (ifaceobj.name, str(e)))
+
+    def _get_bridge_mac(self, ifaceobj, ifname, ifaceobj_getfunc):
+        if self.bridge_mac_iface and self.bridge_mac_iface[0] and self.bridge_mac_iface[1]:
+            return self.bridge_mac_iface
+
+        if self.bridge_mac_iface_list:
+            self.logger.debug('bridge mac iface list: %s' % self.bridge_mac_iface_list)
+
+            for bridge_mac_intf in self.bridge_mac_iface_list:
+                ifaceobj_list = ifaceobj_getfunc(bridge_mac_intf)
+                iface_mac = None
+
+                if ifaceobj_list:
+                    for obj in ifaceobj_list:
+                        iface_user_configured_hwaddress = utils.strip_hwaddress(obj.get_attr_value_first('hwaddress'))
+                        # if user did configured 'hwaddress' we need to use this value instead of the cached value.
+                        if iface_user_configured_hwaddress:
+                            iface_mac = iface_user_configured_hwaddress
+
+                if not iface_mac and not self.ipcmd.link_exists(bridge_mac_intf):
+                    continue
+
+                if not iface_mac:
+                    iface_mac = self.ipcmd.cache_get('link', [bridge_mac_intf, 'hwaddress'])
+                    # if hwaddress attribute is not configured we use the running mac addr
+
+                self.bridge_mac_iface = (bridge_mac_intf, iface_mac)
+                return self.bridge_mac_iface
+        elif self.bridge_set_static_mac_from_port:
+            # no policy was provided, we need to get the first physdev or bond ports
+            # and use its hwaddress to set the bridge mac
+            for port in self._get_bridge_port_list_user_ordered(ifaceobj) or []:
+                # iterate through the bridge-port list
+                for port_obj in ifaceobj_getfunc(port) or []:
+                    # check if the port is a physdev (link_kind is null) or a bon
+                    if port_obj.link_kind != ifaceLinkKind.VXLAN:
+                        iface_user_configured_hwaddress = utils.strip_hwaddress(port_obj.get_attr_value_first('hwaddress'))
+                        # if user did configured 'hwaddress' we need to use this value instead of the cached value.
+                        if iface_user_configured_hwaddress:
+                            iface_mac = iface_user_configured_hwaddress.lower()
+                            # we need to "normalize" the user provided MAC so it can match with
+                            # what we have in the cache (data retrieved via a netlink dump by
+                            # nlmanager). nlmanager return all macs in lower-case
+                        else:
+                            iface_mac = self.ipcmd.link_get_hwaddress(port)
+
+                        if iface_mac:
+                            self.bridge_mac_iface = (port, iface_mac)
+                            return self.bridge_mac_iface
+
+        return None, None
+
+    def _add_bridge_mac_to_fdb(self, ifaceobj, bridge_mac):
+        if not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE and bridge_mac and ifaceobj.get_attr_value('address'):
+            self.ipcmd.bridge_fdb_add(ifaceobj.name, bridge_mac, vlan=None, bridge=True, remote=None)
+
+    def _up_bridge_mac(self, ifaceobj, ifaceobj_getfunc):
+        """
+        We have a day one bridge mac changing problem with changing ports
+        (basically bridge mac changes when the port it inherited the mac from
+        gets de-enslaved).
+
+        We have discussed this problem many times before and tabled it.
+        The issue has aggravated with vxlan bridge ports having auto-generated
+        random macs...which change on every reboot.
+
+        ifupdown2 extract from policy files an iface to select a mac from and
+        configure it automatically.
+        """
+        if ifaceobj.get_attr_value('hwaddress'):
+            # if the user configured a static hwaddress
+            # there is no need to assign one
+            return
+
+        ifname = ifaceobj.name
+        mac_intf, bridge_mac = self._get_bridge_mac(ifaceobj, ifname, ifaceobj_getfunc)
+        self.logger.debug("%s: _get_bridge_mac returned (%s, %s)"
+                          %(ifname, mac_intf, bridge_mac))
+
+        if bridge_mac:
+            # if an interface is configured with the following attribute:
+            # hwaddress 08:00:27:42:42:4
+            # the cache_check won't match because nlmanager return "08:00:27:42:42:04"
+            # from the kernel. The only way to counter that is to convert all mac to int
+            # and compare the ints, it will increase perfs and be safer.
+            cached_value = self.ipcmd.cache_get('link', [ifname, 'hwaddress'])
+            self.logger.debug('%s: cached hwaddress value: %s' % (ifname, cached_value))
+            if cached_value and cached_value == bridge_mac:
+                # the bridge mac is already set to the bridge_mac_intf's mac
+                return
+
+            self.logger.info('%s: setting bridge mac to port %s mac' % (ifname, mac_intf))
+            try:
+                self.ipcmd.link_set(ifname, 'address', value=bridge_mac, force=True)
+            except Exception as e:
+                self.logger.info('%s: %s' % (ifname, str(e)))
+                # log info this error because the user didn't explicitly configured this
+        else:
+            self._add_bridge_mac_to_fdb(ifaceobj, self.ipcmd.link_get_hwaddress(ifname))
+
+    def _up(self, ifaceobj, ifaceobj_getfunc=None):
+        if ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
+            self.up_bridge_port(ifaceobj, ifaceobj_getfunc)
+
+        elif ifaceobj.link_kind & ifaceLinkKind.BRIDGE:
+            self.up_bridge(ifaceobj, ifaceobj_getfunc)
+
+    def _down(self, ifaceobj, ifaceobj_getfunc=None):
+        if not self._is_bridge(ifaceobj):
+            return
+        ifname = ifaceobj.name
+        if not self.ipcmd.link_exists(ifname):
+            return
+        try:
+            running_ports = self.brctlcmd.get_bridge_ports(ifname)
+            if running_ports:
+                self.handle_ipv6(running_ports, '0')
+                if ifaceobj.link_type != ifaceLinkType.LINK_NA:
+                    map(lambda p: netlink.link_set_updown(p, 'down'), running_ports)
+        except Exception as e:
+            self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj)
+        try:
+            netlink.link_del(ifname)
+        except Exception as e:
+            ifaceobj.set_status(ifaceStatus.ERROR)
+            self.logger.error(str(e))
+            # netlink exception already contains the ifname
+
+    def _query_running_vidinfo_compat(self, ifaceobjrunning, ports):
+        running_attrs = {}
+        if ports:
+            running_bridge_port_vids = ''
+            for p in ports:
+                try:
+                    running_vids = self._get_runing_vids(p)
+                    if running_vids:
+                        running_bridge_port_vids += ' %s=%s' %(p,
+                                                      ','.join(running_vids))
+                except Exception:
+                    pass
+            running_attrs['bridge-port-vids'] = running_bridge_port_vids
+
+            running_bridge_port_pvid = ''
+            for p in ports:
+                try:
+                    running_pvid = self._get_runing_pvid(p)
+                    if running_pvid:
+                        running_bridge_port_pvid += ' %s=%s' %(p,
+                                                        running_pvid)
+                except Exception:
+                    pass
+            running_attrs['bridge-port-pvids'] = running_bridge_port_pvid
+
+        running_bridge_vids = self.ipcmd.bridge_vlan_get_vids(ifaceobjrunning.name)
+        if running_bridge_vids:
+            running_attrs['bridge-vids'] = ','.join(self._compress_into_ranges(running_bridge_vids))
+        return running_attrs
+
+    def _query_running_vidinfo(self, ifaceobjrunning, ifaceobj_getfunc,
+                               bridgeports=None):
+        running_attrs = {}
+
+        # 'bridge-vids' under the bridge is all about 'vids' on the port.
+        # so query the ports
+        running_bridgeport_vids = []
+        running_bridgeport_pvids = []
+        for bport in bridgeports:
+            (vids, pvid) = self._get_running_vids_n_pvid_str(bport)
+            if vids:
+                running_bridgeport_vids.append(' '.join(vids))
+            if pvid:
+                running_bridgeport_pvids.append(pvid)
+
+        bridge_vids = None
+        if running_bridgeport_vids:
+           (vidval, freq) = Counter(running_bridgeport_vids).most_common()[0]
+           if freq == len(bridgeports):
+              running_attrs['bridge-vids'] = vidval
+              bridge_vids = vidval.split()
+
+        bridge_pvid = None
+        if running_bridgeport_pvids:
+           (vidval, freq) = Counter(running_bridgeport_pvids).most_common()[0]
+           if freq == len(bridgeports) and vidval != '1':
+              running_attrs['bridge-pvid'] = vidval
+              bridge_pvid = vidval.split()[0]
+
+        # Go through all bridge ports and find their vids
+        for bport in bridgeports:
+            bportifaceobj = ifaceobj_getfunc(bport)
+            if not bportifaceobj:
+               continue
+            bport_vids = []
+            bport_pvid = None
+            (vids, pvid) = self._get_running_vids_n_pvid_str(bport)
+            if vids and vids != bridge_vids:
+               bport_vids = vids
+            if pvid and pvid != bridge_pvid:
+               bport_pvid = pvid
+            if bport_vids and bport_pvid in bport_vids:
+                bport_vids.remove(bport_pvid)
+            if (not bport_vids and bport_pvid and bport_pvid != '1'):
+               bportifaceobj[0].replace_config('bridge-access', bport_pvid)
+               bportifaceobj[0].delete_config('bridge-pvid')
+               bportifaceobj[0].delete_config('bridge-vids')
+            else:
+               if bport_pvid and bport_pvid != '1':
+                  bportifaceobj[0].replace_config('bridge-pvid', bport_pvid)
+               else:
+                  # delete any stale bridge-vids under ports
+                  bportifaceobj[0].delete_config('bridge-pvid')
+               if bport_vids:
+                  bportifaceobj[0].replace_config('bridge-vids',
+                                                  ' '.join(bport_vids))
+               else:
+                  # delete any stale bridge-vids under ports
+                  bportifaceobj[0].delete_config('bridge-vids')
+        return running_attrs
+
+    def _query_running_mcqv4src(self, ifaceobjrunning):
+        running_mcqv4src = self.brctlcmd.bridge_get_mcqv4src(ifaceobjrunning.name)
+        mcqs = ['%s=%s' %(v, i) for v, i in running_mcqv4src.items()]
+        mcqs.sort()
+        mcq = ' '.join(mcqs)
+        return mcq
+
+    def _query_running_attrs(self, ifaceobjrunning, ifaceobj_getfunc,
+                             bridge_vlan_aware=False):
+        bridgeattrdict = {}
+        userspace_stp = 0
+        ports = None
+        skip_kernel_stp_attrs = 0
+
+        try:
+            if self.systcl_get_net_bridge_stp_user_space() == '1':
+                userspace_stp = 1
+        except Exception as e:
+            self.logger.info('%s: %s' % (ifaceobjrunning.name, str(e)))
+
+        tmpbridgeattrdict = self.brctlcmd.get_bridge_attrs(ifaceobjrunning.name)
+        if not tmpbridgeattrdict:
+            self.logger.warn('%s: unable to get bridge attrs'
+                    %ifaceobjrunning.name)
+            return bridgeattrdict
+
+        # Fill bridge_ports and bridge stp attributes first
+        ports = tmpbridgeattrdict.get('ports')
+        if ports:
+            bridgeattrdict['bridge-ports'] = [' '.join(ports.keys())]
+        stp = tmpbridgeattrdict.get('stp', 'no')
+        if stp != self.get_mod_subattr('bridge-stp', 'default'):
+            bridgeattrdict['bridge-stp'] = [stp]
+
+        if  stp == 'yes' and userspace_stp:
+            skip_kernel_stp_attrs = 1
+
+        vlan_stats = utils.get_onff_from_onezero(
+                            tmpbridgeattrdict.get('vlan-stats', None))
+        if (vlan_stats and
+            vlan_stats != self.get_mod_subattr('bridge-vlan-stats', 'default')):
+            bridgeattrdict['bridge-vlan-stats'] = [vlan_stats]
+
+        bool2str = {'0': 'no', '1': 'yes'}
+        # pick all other attributes
+        for k,v in tmpbridgeattrdict.items():
+            if not v:
+                continue
+            if k == 'ports' or k == 'stp':
+                continue
+
+            if skip_kernel_stp_attrs and k[:2] != 'mc':
+                # only include igmp attributes if kernel stp is off
+                continue
+            attrname = 'bridge-' + k
+            mod_default = self.get_mod_subattr(attrname, 'default')
+            if v != mod_default:
+                # convert '0|1' running values to 'no|yes'
+                if v in bool2str.keys() and bool2str[v] == mod_default:
+                    continue
+                bridgeattrdict[attrname] = [v]
+
+        if bridge_vlan_aware:
+            if not ports:
+                ports = {}
+            bridgevidinfo = self._query_running_vidinfo(ifaceobjrunning,
+                                                        ifaceobj_getfunc,
+                                                        ports.keys())
+        else:
+            bridgevidinfo = self._query_running_vidinfo_compat(ifaceobjrunning,
+                                                               ports)
+        if bridgevidinfo:
+           bridgeattrdict.update({k : [v] for k, v in bridgevidinfo.items()
+                                  if v})
+
+        mcq = self._query_running_mcqv4src(ifaceobjrunning)
+        if mcq:
+            bridgeattrdict['bridge-mcqv4src'] = [mcq]
+
+        if skip_kernel_stp_attrs:
+            return bridgeattrdict
+
+        # Do this only for vlan-UNAWARE-bridge
+        if ports and not bridge_vlan_aware:
+            portconfig = {'bridge-pathcosts' : '',
+                          'bridge-portprios' : '',
+                          'bridge-learning' : '',
+                          'bridge-unicast-flood' : '',
+                          'bridge-multicast-flood' : '',
+                          'bridge-arp-nd-suppress' : '',
+                         }
+            for p, v in ports.items():
+                v = self.brctlcmd.bridge_get_pathcost(ifaceobjrunning.name, p)
+                if v and v != self.get_mod_subattr('bridge-pathcosts',
+                                                   'default'):
+                    portconfig['bridge-pathcosts'] += ' %s=%s' %(p, v)
+
+                v = self.brctlcmd.bridge_get_portprio(ifaceobjrunning.name, p)
+                if v and v != self.get_mod_subattr('bridge-portprios',
+                                                   'default'):
+                    portconfig['bridge-portprios'] += ' %s=%s' %(p, v)
+
+                v = utils.get_onff_from_onezero(
+                        self.brctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
+                                                          p, 'learning'))
+                if (v and
+                    v != self.get_mod_subattr('bridge-learning', 'default')):
+                    portconfig['bridge-learning'] += ' %s=%s' %(p, v)
+
+                v = utils.get_onff_from_onezero(
+                        self.brctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
+                                                          p, 'unicast-flood'))
+                if (v and
+                    v != self.get_mod_subattr('bridge-unicast-flood',
+                                              'default')):
+                    portconfig['bridge-unicast-flood'] += ' %s=%s' %(p, v)
+
+                v = utils.get_onff_from_onezero(
+                        self.brctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
+                                                          p, 'multicast-flood'))
+                if (v and
+                    v != self.get_mod_subattr('bridge-multicast-flood',
+                                              'default')):
+                    portconfig['bridge-multicast-flood'] += ' %s=%s' %(p, v)
+
+                v = utils.get_onff_from_onezero(
+                        self.brctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
+                                                          p, 'arp-nd-suppress'))
+                if (v and
+                    v != self.get_mod_subattr('bridge-arp-nd-suppress',
+                                              'default')):
+                    portconfig['bridge-arp-nd-suppress'] += ' %s=%s' %(p, v)
+
+            bridgeattrdict.update({k : [v] for k, v in portconfig.items()
+                                    if v})
+
+        return bridgeattrdict
+
+    def _query_check_mcqv4src(self, ifaceobj, ifaceobjcurr):
+        running_mcqs = self._query_running_mcqv4src(ifaceobj)
+        attrval = ifaceobj.get_attr_value_first('bridge-mcqv4src')
+        if attrval:
+            mcqs = attrval.split()
+            mcqs.sort()
+            mcqsout = ' '.join(mcqs)
+            ifaceobjcurr.update_config_with_status('bridge-mcqv4src',
+                         running_mcqs, 1 if running_mcqs != mcqsout else 0)
+
+    def _query_check_bridge_vidinfo(self, ifaceobj, ifaceobjcurr):
+        err = 0
+        attrval = ifaceobj.get_attr_value_first('bridge-port-vids')
+        if attrval:
+            running_bridge_port_vids = ''
+            portlist = self.parse_port_list(ifaceobj.name, attrval)
+            if not portlist:
+                self.log_warn('%s: could not parse \'bridge-port-vids %s\''
+                          %(ifaceobj.name, attrval))
+                return
+            err = 0
+            for p in portlist:
+                try:
+                    (port, val) = p.split('=')
+                    vids = val.split(',')
+                    running_vids = self.ipcmd.bridge_vlan_get_vids(port)
+                    if running_vids:
+                        if not self._compare_vids(vids, running_vids):
+                            err += 1
+                            running_bridge_port_vids += ' %s=%s' %(port,
+                                                      ','.join(running_vids))
+                        else:
+                            running_bridge_port_vids += ' %s' %p
+                    else:
+                        err += 1
+                except Exception, e:
+                    self.log_warn('%s: failure checking vid %s (%s)'
+                        %(ifaceobj.name, p, str(e)))
+            if err:
+                ifaceobjcurr.update_config_with_status('bridge-port-vids',
+                                                 running_bridge_port_vids, 1)
+            else:
+                ifaceobjcurr.update_config_with_status('bridge-port-vids',
+                                                 attrval, 0)
+
+        attrval = ifaceobj.get_attr_value_first('bridge-port-pvids')
+        if attrval:
+            portlist = self.parse_port_list(ifaceobj.name, attrval)
+            if not portlist:
+                self.log_warn('%s: could not parse \'bridge-port-pvids %s\''
+                              %(ifaceobj.name, attrval))
+                return
+            running_bridge_port_pvids = ''
+            err = 0
+            for p in portlist:
+                try:
+                    (port, pvid) = p.split('=')
+                    running_pvid = self.ipcmd.bridge_vlan_get_vids(port)
+                    if running_pvid and running_pvid == pvid:
+                        running_bridge_port_pvids += ' %s' %p
+                    else:
+                        err += 1
+                        running_bridge_port_pvids += ' %s=%s' %(port,
+                                                            running_pvid)
+                except Exception, e:
+                    self.log_warn('%s: failure checking pvid %s (%s)'
+                            %(ifaceobj.name, pvid, str(e)))
+            if err:
+                ifaceobjcurr.update_config_with_status('bridge-port-pvids',
+                                                 running_bridge_port_pvids, 1)
+            else:
+                ifaceobjcurr.update_config_with_status('bridge-port-pvids',
+                                                 running_bridge_port_pvids, 0)
+
+        vids = self.get_ifaceobj_bridge_vids(ifaceobj)
+        if vids[1]:
+            ifaceobjcurr.update_config_with_status(vids[0], vids[1], -1)
+
+    def _query_check_snooping_wdefault(self, ifaceobj):
+        if (ifupdownflags.flags.WITHDEFAULTS
+            and not self._vxlan_bridge_default_igmp_snooping
+                and ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VXLAN):
+            ifaceobj.replace_config('bridge-mcsnoop', 'no')
+
+    def _query_check_bridge(self, ifaceobj, ifaceobjcurr,
+                            ifaceobj_getfunc=None):
+        if not self._is_bridge(ifaceobj):
+            return
+        if not self.brctlcmd.bridge_exists(ifaceobj.name):
+            self.logger.info('%s: bridge: does not exist' %(ifaceobj.name))
+            return
+
+        self._query_check_snooping_wdefault(ifaceobj)
+
+        ifaceattrs = self.dict_key_subset(ifaceobj.config,
+                                          self.get_mod_attrs())
+        #Add default attributes if --with-defaults is set
+        if ifupdownflags.flags.WITHDEFAULTS and 'bridge-stp' not in ifaceattrs:
+            ifaceattrs.append('bridge-stp')
+        if not ifaceattrs:
+            return
+        try:
+            runningattrs = self.brctlcmd.get_bridge_attrs(ifaceobj.name)
+            if not runningattrs:
+               self.logger.debug('%s: bridge: unable to get bridge attrs'
+                                 %ifaceobj.name)
+               runningattrs = {}
+        except Exception, e:
+            self.logger.warn(str(e))
+            runningattrs = {}
+
+        self._query_check_support_yesno_attrs(runningattrs, ifaceobj)
+
+        filterattrs = ['bridge-vids', 'bridge-trunk', 'bridge-port-vids',
+                       'bridge-port-pvids']
+
+        diff = Set(ifaceattrs).difference(filterattrs)
+
+        if 'bridge-l2protocol-tunnel' in diff:
+            diff.remove('bridge-l2protocol-tunnel')
+            # bridge-l2protocol-tunnel requires separate handling
+
+        for k in diff:
+            # get the corresponding ifaceobj attr
+            v = ifaceobj.get_attr_value_first(k)
+            if not v:
+                if ifupdownflags.flags.WITHDEFAULTS and k == 'bridge-stp':
+                    v = 'on' if self.default_stp_on else 'off'
+                else:
+                    continue
+            rv = runningattrs.get(k[7:])
+            if k == 'bridge-mcqv4src':
+               continue
+            if k == 'bridge-maxwait' or k == 'bridge-waitport':
+                ifaceobjcurr.update_config_with_status(k, v, 0)
+                continue
+            if k == 'bridge-vlan-aware':
+                rv = self.ipcmd.bridge_is_vlan_aware(ifaceobj.name)
+                if (rv and v == 'yes') or (not rv and v == 'no'):
+                    ifaceobjcurr.update_config_with_status('bridge-vlan-aware',
+                               v, 0)
+                else:
+                    ifaceobjcurr.update_config_with_status('bridge-vlan-aware',
+                               v, 1)
+            elif k == 'bridge-stp':
+               # special case stp compare because it may
+               # contain more than one valid values
+               stp_on_vals = ['on', 'yes']
+               stp_off_vals = ['off', 'no']
+               if ((v in stp_on_vals and rv in stp_on_vals) or
+                   (v in stp_off_vals and rv in stp_off_vals)):
+                    ifaceobjcurr.update_config_with_status('bridge-stp',
+                               rv, 0)
+               else:
+                    ifaceobjcurr.update_config_with_status('bridge-stp',
+                               rv, 1)
+            elif k == 'bridge-ports':
+               # special case ports because it can contain regex or glob
+               running_port_list = rv.keys() if rv else []
+               bridge_port_list = self._get_bridge_port_list(ifaceobj)
+               if not running_port_list and not bridge_port_list:
+                  continue
+               portliststatus = 1
+               if running_port_list and bridge_port_list:
+                  difference = set(running_port_list
+                                 ).symmetric_difference(bridge_port_list)
+                  if not difference:
+                     portliststatus = 0
+                  try:
+                    port_list = self._get_ifaceobj_bridge_ports(ifaceobj,
+                                                                warn=False).split()
+                    # we want to display the same bridge-ports list as provided
+                    # in the interfaces file but if this list contains regexes or
+                    # globs, for now, we won't try to change it.
+                    if 'regex' in port_list or 'glob' in port_list:
+                        port_list = running_port_list
+                    else:
+                        ordered = []
+                        for i in range(0, len(port_list)):
+                            if port_list[i] in running_port_list:
+                                ordered.append(port_list[i])
+                        port_list = ordered
+                  except:
+                    port_list = running_port_list
+                  ifaceobjcurr.update_config_with_status('bridge-ports',
+                                                         (' '.join(port_list)
+                                                          if port_list else ''),
+                                                         portliststatus)
+            elif k in ['bridge-pathcosts',
+                       'bridge-portprios',
+                       'bridge-portmcrouter',
+                       'bridge-portmcfl',
+                       'bridge-learning',
+                       'bridge-unicast-flood',
+                       'bridge-multicast-flood',
+                       'bridge-arp-nd-suppress',
+                      ]:
+               if k == 'bridge-arp-nd-suppress':
+                  brctlcmdattrname = k[7:]
+               else:
+                  brctlcmdattrname = k[7:].rstrip('s')
+               # for port attributes, the attributes are in a list
+               # <portname>=<portattrvalue>
+               status = 0
+               currstr = ''
+               vlist = self.parse_port_list(ifaceobj.name, v)
+               if not vlist:
+                  continue
+               for vlistitem in vlist:
+                   try:
+                      (p, v) = vlistitem.split('=')
+                      if k in ['bridge-learning',
+                               'bridge-unicast-flood',
+                               'bridge-multicast-flood',
+                               'bridge-arp-nd-suppress',
+                              ]:
+                         currv = utils.get_onoff_bool(
+                                    self.brctlcmd.get_bridgeport_attr(
+                                         ifaceobj.name, p,
+                                         brctlcmdattrname))
+                      else:
+                         currv = self.brctlcmd.get_bridgeport_attr(
+                                         ifaceobj.name, p,
+                                         brctlcmdattrname)
+                      if currv:
+                          currstr += ' %s=%s' %(p, currv)
+                      else:
+                          currstr += ' %s=%s' %(p, 'None')
+
+                      if k == 'bridge-portmcrouter':
+                          if self._ifla_brport_multicast_router_dict_to_int.get(v) != int(currv):
+                              status = 1
+                      elif currv != v:
+                          status = 1
+                   except Exception, e:
+                      self.log_warn(str(e))
+                   pass
+               ifaceobjcurr.update_config_with_status(k, currstr, status)
+            elif k == 'bridge-vlan-stats' or k == 'bridge-mcstats':
+                rv = utils.get_onff_from_onezero(rv)
+                if v != rv:
+                    ifaceobjcurr.update_config_with_status(k, rv, 1)
+                else:
+                    ifaceobjcurr.update_config_with_status(k, rv, 0)
+            elif not rv:
+               if k == 'bridge-pvid' or k == 'bridge-vids' or k == 'bridge-trunk' or k == 'bridge-allow-untagged':
+                   # bridge-pvid and bridge-vids on a bridge does
+                   # not correspond directly to a running config
+                   # on the bridge. They correspond to default
+                   # values for the bridge ports. And they are
+                   # already checked against running config of the
+                   # bridge port and reported against a bridge port.
+                   # So, ignore these attributes under the bridge.
+                   # Use '2' for ignore today. XXX: '2' will be
+                   # mapped to a defined value in subsequent patches.
+                   ifaceobjcurr.update_config_with_status(k, v, 2)
+               else:
+                   ifaceobjcurr.update_config_with_status(k, 'notfound', 1)
+               continue
+            elif v.upper() != rv.upper():
+               ifaceobjcurr.update_config_with_status(k, rv, 1)
+            else:
+               ifaceobjcurr.update_config_with_status(k, rv, 0)
+
+        self._query_check_bridge_vidinfo(ifaceobj, ifaceobjcurr)
+
+        self._query_check_mcqv4src(ifaceobj, ifaceobjcurr)
+        self._query_check_l2protocol_tunnel_on_bridge(ifaceobj, ifaceobjcurr, runningattrs)
+
+    def get_ifaceobj_bridge_vids(self, ifaceobj):
+        vids = ('bridge-vids', ifaceobj.get_attr_value_first('bridge-vids'))
+        if not vids[1]:
+            vids = ('bridge-trunk', ifaceobj.get_attr_value_first('bridge-trunk'))
+        return vids
+
+    def get_ifaceobj_bridge_vids_value(self, ifaceobj):
+        return self.get_ifaceobj_bridge_vids(ifaceobj)[1]
+
+    def _get_bridge_vids(self, bridgename, ifaceobj_getfunc):
+        ifaceobjs = ifaceobj_getfunc(bridgename)
+        for ifaceobj in ifaceobjs:
+            vids = self.get_ifaceobj_bridge_vids_value(ifaceobj)
+            if vids: return re.split(r'[\s\t,]\s*', vids)
+        return None
+
+    def _get_bridge_pvid(self, bridgename, ifaceobj_getfunc):
+        ifaceobjs = ifaceobj_getfunc(bridgename)
+        pvid = None
+        for ifaceobj in ifaceobjs:
+            pvid = ifaceobj.get_attr_value_first('bridge-pvid')
+            if pvid:
+                break
+        return pvid
+
+    def _get_bridge_name(self, ifaceobj):
+        return self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name)
+
+    def _query_check_bridge_port_vidinfo(self, ifaceobj, ifaceobjcurr,
+                                         ifaceobj_getfunc, bridgename):
+        attr_name = 'bridge-access'
+        vid = ifaceobj.get_attr_value_first(attr_name)
+        if vid:
+            (running_vids, running_pvid) = self._get_running_vids_n_pvid_str(
+                                                        ifaceobj.name)
+            if (not running_pvid or running_pvid != vid or
+                (running_vids and running_vids[0] != vid)):
+               ifaceobjcurr.update_config_with_status(attr_name,
+                                running_pvid, 1)
+            else:
+               ifaceobjcurr.update_config_with_status(attr_name, vid, 0)
+            return
+
+        (running_vids, running_pvid) = self._get_running_vids_n_pvid_str(
+                                                        ifaceobj.name)
+        attr_name = 'bridge-pvid'
+        pvid = ifaceobj.get_attr_value_first('bridge-pvid')
+        if pvid:
+            if running_pvid and running_pvid == pvid:
+                ifaceobjcurr.update_config_with_status(attr_name,
+                                                       running_pvid, 0)
+            else:
+                ifaceobjcurr.update_config_with_status(attr_name,
+                                                       running_pvid, 1)
+        elif (not (ifaceobj.flags & iface.HAS_SIBLINGS) or
+              ((ifaceobj.flags & iface.HAS_SIBLINGS) and
+               (ifaceobj.flags & iface.OLDEST_SIBLING))):
+            # if the interface has multiple iface sections,
+            # we check the below only for the oldest sibling
+            # or the last iface section
+            pvid = self._get_bridge_pvid(bridgename, ifaceobj_getfunc)
+            if pvid:
+                if not running_pvid or running_pvid != pvid:
+                    ifaceobjcurr.status = ifaceStatus.ERROR
+                    ifaceobjcurr.status_str = 'bridge pvid error'
+            elif not running_pvid or running_pvid != '1':
+                ifaceobjcurr.status = ifaceStatus.ERROR
+                ifaceobjcurr.status_str = 'bridge pvid error'
+
+        attr_name, vids = self.get_ifaceobj_bridge_vids(ifaceobj)
+        if vids:
+           vids = re.split(r'[\s\t]\s*', vids)
+           if not running_vids or not self._compare_vids(vids, running_vids,
+                                                         running_pvid):
+               ifaceobjcurr.update_config_with_status(attr_name,
+                                            ' '.join(running_vids), 1)
+           else:
+               ifaceobjcurr.update_config_with_status(attr_name,
+                                            ' '.join(vids), 0)
+        elif (not (ifaceobj.flags & iface.HAS_SIBLINGS) or
+              ((ifaceobj.flags & iface.HAS_SIBLINGS) and
+               (ifaceobj.flags & iface.OLDEST_SIBLING))):
+           # if the interface has multiple iface sections,
+           # we check the below only for the oldest sibling
+           # or the last iface section
+
+           # check if it matches the bridge vids
+           bridge_vids = self._get_bridge_vids(bridgename, ifaceobj_getfunc)
+           if (bridge_vids and (not running_vids  or
+                   not self._compare_vids(bridge_vids, running_vids, running_pvid))):
+              ifaceobjcurr.status = ifaceStatus.ERROR
+              ifaceobjcurr.status_str = 'bridge vid error'
+
+    def _query_check_bridge_port(self, ifaceobj, ifaceobjcurr,
+                                 ifaceobj_getfunc):
+        if not self._is_bridge_port(ifaceobj):
+            # Mark all bridge attributes as failed
+            ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj,
+                    ['bridge-vids', 'bridge-trunk', 'bridge-pvid', 'bridge-access',
+                     'bridge-pathcosts', 'bridge-portprios',
+                     'bridge-portmcrouter',
+                     'bridge-learning',
+                     'bridge-portmcfl', 'bridge-unicast-flood',
+                     'bridge-multicast-flood',
+                     'bridge-arp-nd-suppress', 'bridge-l2protocol-tunnel'
+                    ], 1)
+            return
+        bridgename = self._get_bridge_name(ifaceobj)
+        if not bridgename:
+            self.logger.warn('%s: unable to determine bridge name'
+                             %ifaceobj.name)
+            return
+
+        if self.ipcmd.bridge_is_vlan_aware(bridgename):
+            self._query_check_bridge_port_vidinfo(ifaceobj, ifaceobjcurr,
+                                                  ifaceobj_getfunc,
+                                                  bridgename)
+        for attr, dstattr in {'bridge-pathcosts' : 'pathcost',
+                              'bridge-portprios' : 'portprio',
+                              'bridge-portmcrouter' : 'portmcrouter',
+                              'bridge-portmcfl' : 'portmcfl',
+                              'bridge-learning' : 'learning',
+                              'bridge-unicast-flood' : 'unicast-flood',
+                              'bridge-multicast-flood' : 'multicast-flood',
+                              'bridge-arp-nd-suppress' : 'arp-nd-suppress',
+                             }.items():
+            attrval = ifaceobj.get_attr_value_first(attr)
+            if not attrval:
+                continue
+
+            try:
+                running_attrval = self.brctlcmd.get_bridgeport_attr(
+                                       bridgename, ifaceobj.name, dstattr)
+
+                if dstattr == 'portmcfl':
+                    if not utils.is_binary_bool(attrval) and running_attrval:
+                        running_attrval = utils.get_yesno_boolean(
+                            utils.get_boolean_from_string(running_attrval))
+                elif dstattr == 'portmcrouter':
+                    if self._ifla_brport_multicast_router_dict_to_int.get(attrval) == int(running_attrval):
+                        ifaceobjcurr.update_config_with_status(attr, attrval, 0)
+                    else:
+                        ifaceobjcurr.update_config_with_status(attr, attrval, 1)
+                    continue
+                elif dstattr in ['learning',
+                                 'unicast-flood',
+                                 'multicast-flood',
+                                 'arp-nd-suppress',
+                                ]:
+                    if not utils.is_binary_bool(attrval) and running_attrval:
+                        running_attrval = utils.get_onff_from_onezero(
+                                                running_attrval)
+
+                if running_attrval != attrval:
+                    ifaceobjcurr.update_config_with_status(attr,
+                                            running_attrval, 1)
+                else:
+                    ifaceobjcurr.update_config_with_status(attr,
+                                            running_attrval, 0)
+            except Exception, e:
+                self.log_warn('%s: %s' %(ifaceobj.name, str(e)))
+
+        self._query_check_l2protocol_tunnel_on_port(ifaceobj, ifaceobjcurr)
+
+    def _query_check_l2protocol_tunnel_on_port(self, ifaceobj, ifaceobjcurr):
+        user_config_l2protocol_tunnel = ifaceobj.get_attr_value_first('bridge-l2protocol-tunnel')
+
+        if user_config_l2protocol_tunnel:
+            result = 0
+            try:
+                self._query_check_l2protocol_tunnel(ifaceobj.name, user_config_l2protocol_tunnel)
+            except Exception as e:
+                self.logger.debug('query: %s: %s' % (ifaceobj.name, str(e)))
+                result = 1
+            ifaceobjcurr.update_config_with_status('bridge-l2protocol-tunnel', user_config_l2protocol_tunnel, result)
+
+    def _query_check_l2protocol_tunnel_on_bridge(self, ifaceobj, ifaceobjcurr, bridge_running_attrs):
+        """
+            In case the bridge-l2protocol-tunnel is specified under the bridge and not the brport
+            We need to make sure that all ports comply with the mask given under the bridge
+        """
+        user_config_l2protocol_tunnel = ifaceobj.get_attr_value_first('bridge-l2protocol-tunnel')
+
+        if user_config_l2protocol_tunnel:
+            if '=' in user_config_l2protocol_tunnel:
+                try:
+                    config_per_port_dict = self.parse_interface_list_value(user_config_l2protocol_tunnel)
+                    brport_list = config_per_port_dict.keys()
+                except:
+                    ifaceobjcurr.update_config_with_status('bridge-l2protocol-tunnel', user_config_l2protocol_tunnel, 1)
+                    return
+            else:
+                config_per_port_dict = {}
+                brport_list = bridge_running_attrs.get('ports', {}).keys()
+            result = 1
+            try:
+                for brport_name in brport_list:
+                    self._query_check_l2protocol_tunnel(
+                        brport_name,
+                        config_per_port_dict.get(brport_name) if config_per_port_dict else user_config_l2protocol_tunnel
+                    )
+                result = 0
+            except Exception as e:
+                self.logger.debug('query: %s: %s' % (ifaceobj.name, str(e)))
+                result = 1
+            ifaceobjcurr.update_config_with_status('bridge-l2protocol-tunnel', user_config_l2protocol_tunnel, result)
+
+    def _query_check_l2protocol_tunnel(self, brport_name, user_config_l2protocol_tunnel):
+        cached_ifla_brport_group_maskhi = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', Link.IFLA_BRPORT_GROUP_FWD_MASKHI])
+        cached_ifla_brport_group_mask = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', Link.IFLA_BRPORT_GROUP_FWD_MASK])
+
+        for protocol in re.split(',|\s*', user_config_l2protocol_tunnel):
+            callback = self.query_check_l2protocol_tunnel_callback.get(protocol)
+
+            if callable(callback):
+                if not callback(cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi):
+                    raise Exception('%s: bridge-l2protocol-tunnel: protocol \'%s\' not present (cached value: %d | %d)'
+                                    % (brport_name, protocol, cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi))
+
+    def _query_running_bridge_l2protocol_tunnel(self, brport_name, brport_ifaceobj=None, bridge_ifaceobj=None):
+        cached_ifla_brport_group_maskhi = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', Link.IFLA_BRPORT_GROUP_FWD_MASKHI])
+        cached_ifla_brport_group_mask = self.ipcmd.cache_get_info_slave([brport_name, 'info_slave_data', Link.IFLA_BRPORT_GROUP_FWD_MASK])
+        running_protocols = []
+        for protocol_name, callback in self.query_check_l2protocol_tunnel_callback.items():
+            if protocol_name == 'all' and callback(cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi):
+                running_protocols = self.query_check_l2protocol_tunnel_callback.keys()
+                running_protocols.remove('all')
+                break
+            elif callback(cached_ifla_brport_group_mask, cached_ifla_brport_group_maskhi):
+                running_protocols.append(protocol_name)
+        if running_protocols:
+            if brport_ifaceobj:
+                brport_ifaceobj.update_config('bridge-l2protocol-tunnel', ' '.join(running_protocols))
+            elif bridge_ifaceobj:
+                current_config = bridge_ifaceobj.get_attr_value_first('bridge-l2protocol-tunnel')
+
+                if current_config:
+                    bridge_ifaceobj.replace_config('bridge-l2protocol-tunnel', '%s %s=%s' % (current_config, brport_name, ','.join(running_protocols)))
+                else:
+                    bridge_ifaceobj.replace_config('bridge-l2protocol-tunnel', '%s=%s' % (brport_name, ','.join(running_protocols)))
+
+    def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
+        if self._is_bridge(ifaceobj):
+            self._query_check_bridge(ifaceobj, ifaceobjcurr)
+        else:
+            self._query_check_bridge_port(ifaceobj, ifaceobjcurr,
+                                          ifaceobj_getfunc)
+
+    def _query_running_bridge(self, ifaceobjrunning, ifaceobj_getfunc):
+        if self.ipcmd.bridge_is_vlan_aware(ifaceobjrunning.name):
+            ifaceobjrunning.update_config('bridge-vlan-aware', 'yes')
+            ifaceobjrunning.update_config_dict(self._query_running_attrs(
+                                               ifaceobjrunning,
+                                               ifaceobj_getfunc,
+                                               bridge_vlan_aware=True))
+        else:
+            ifaceobjrunning.update_config_dict(self._query_running_attrs(
+                                               ifaceobjrunning, None))
+
+    def _query_running_bridge_port_attrs(self, ifaceobjrunning, bridgename):
+        if self.systcl_get_net_bridge_stp_user_space() == '1':
+            return
+
+        v = self.brctlcmd.bridge_get_pathcost(bridgename, ifaceobjrunning.name)
+        if v and v != self.get_mod_subattr('bridge-pathcosts', 'default'):
+            ifaceobjrunning.update_config('bridge-pathcosts', v)
+
+        v = self.brctlcmd.bridge_get_pathcost(bridgename, ifaceobjrunning.name)
+        if v and v != self.get_mod_subattr('bridge-portprios', 'default'):
+            ifaceobjrunning.update_config('bridge-portprios', v)
+
+    def _query_running_bridge_port(self, ifaceobjrunning,
+                                   ifaceobj_getfunc=None):
+
+        bridgename = self.ipcmd.bridge_port_get_bridge_name(
+                                                ifaceobjrunning.name)
+        bridge_vids = None
+        bridge_pvid = None
+        if not bridgename:
+            self.logger.warn('%s: unable to find bridgename'
+                             %ifaceobjrunning.name)
+            return
+
+        if not self.ipcmd.bridge_is_vlan_aware(bridgename):
+            try:
+                self._query_running_bridge_l2protocol_tunnel(ifaceobjrunning.name, bridge_ifaceobj=ifaceobj_getfunc(bridgename)[0])
+            except Exception as e:
+                self.logger.debug('%s: q_query_running_bridge_l2protocol_tunnel: %s' % (ifaceobjrunning.name, str(e)))
+            return
+
+        self._query_running_bridge_l2protocol_tunnel(ifaceobjrunning.name, brport_ifaceobj=ifaceobjrunning)
+
+        (bridge_port_vids, bridge_port_pvid) = self._get_running_vids_n_pvid_str(
+                                                           ifaceobjrunning.name)
+        if bridge_port_vids and bridge_port_pvid in bridge_port_vids:
+                bridge_port_vids.remove(bridge_port_pvid)
+
+        bridgeifaceobjlist = ifaceobj_getfunc(bridgename)
+        if bridgeifaceobjlist:
+           bridge_vids = bridgeifaceobjlist[0].get_attr_value('bridge-vids')
+           bridge_pvid = bridgeifaceobjlist[0].get_attr_value_first('bridge-pvid')
+
+        if not bridge_port_vids and bridge_port_pvid:
+            # must be an access port
+            if bridge_port_pvid != '1':
+               ifaceobjrunning.update_config('bridge-access',
+                                          bridge_port_pvid)
+        else:
+            if bridge_port_vids:
+                if (not bridge_vids or bridge_port_vids != bridge_vids):
+                   ifaceobjrunning.update_config('bridge-vids',
+                                        ' '.join(bridge_port_vids))
+            if bridge_port_pvid and bridge_port_pvid != '1':
+                if (not bridge_pvid or (bridge_port_pvid != bridge_pvid)):
+                    ifaceobjrunning.update_config('bridge-pvid',
+                                        bridge_port_pvid)
+
+        v = utils.get_onff_from_onezero(
+                self.brctlcmd.get_bridgeport_attr(bridgename,
+                                                  ifaceobjrunning.name,
+                                                  'learning'))
+        if v and v != self.get_mod_subattr('bridge-learning', 'default'):
+            ifaceobjrunning.update_config('bridge-learning', v)
+
+        v = utils.get_onff_from_onezero(
+                self.brctlcmd.get_bridgeport_attr(bridgename,
+                                                  ifaceobjrunning.name,
+                                                  'unicast-flood'))
+        if v and v != self.get_mod_subattr('bridge-unicast-flood', 'default'):
+            ifaceobjrunning.update_config('bridge-unicast-flood', v)
+
+        v = utils.get_onff_from_onezero(
+                self.brctlcmd.get_bridgeport_attr(bridgename,
+                                                  ifaceobjrunning.name,
+                                                  'multicast-flood'))
+        if v and v != self.get_mod_subattr('bridge-multicast-flood', 'default'):
+            ifaceobjrunning.update_config('bridge-multicast-flood', v)
+
+        v = utils.get_onff_from_onezero(
+                self.brctlcmd.get_bridgeport_attr(bridgename,
+                                                  ifaceobjrunning.name,
+                                                  'arp-nd-suppress'))
+        # Display running 'arp-nd-suppress' only on vxlan ports
+        # if 'allow_arp_nd_suppress_only_on_vxlan' is set to 'yes'
+        # otherwise, display on all bridge-ports
+
+        bportifaceobj = ifaceobj_getfunc(ifaceobjrunning.name)[0]
+        if (v and
+            v != self.get_mod_subattr('bridge-arp-nd-suppress', 'default') and
+            (not self.arp_nd_suppress_only_on_vxlan or
+             (self.arp_nd_suppress_only_on_vxlan and
+              bportifaceobj.link_kind & ifaceLinkKind.VXLAN))):
+            ifaceobjrunning.update_config('bridge-arp-nd-suppress', v)
+
+        self._query_running_bridge_port_attrs(ifaceobjrunning, bridgename)
+
+    def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
+        try:
+            if self.brctlcmd.bridge_exists(ifaceobjrunning.name):
+                self._query_running_bridge(ifaceobjrunning, ifaceobj_getfunc)
+            elif self.brctlcmd.is_bridge_port(ifaceobjrunning.name):
+                self._query_running_bridge_port(ifaceobjrunning, ifaceobj_getfunc)
+        except Exception as e:
+            raise Exception('%s: %s' % (ifaceobjrunning.name, str(e)))
+
+    def _query(self, ifaceobj, **kwargs):
+        """ add default policy attributes supported by the module """
+        if (not (ifaceobj.link_kind & ifaceLinkKind.BRIDGE) or
+            ifaceobj.get_attr_value_first('bridge-stp')):
+            return
+        if self.default_stp_on:
+            ifaceobj.update_config('bridge-stp', 'yes')
+
+    def _query_check_support_yesno_attrs(self, runningattrs, ifaceobj):
+        for attrl in [['mcqifaddr', 'bridge-mcqifaddr'],
+                     ['mcquerier', 'bridge-mcquerier'],
+                     ['mcsnoop', 'bridge-mcsnoop']]:
+            value = ifaceobj.get_attr_value_first(attrl[1])
+            if value and not utils.is_binary_bool(value):
+                if attrl[0] in runningattrs:
+                    bool = utils.get_boolean_from_string(runningattrs[attrl[0]])
+                    runningattrs[attrl[0]] = utils.get_yesno_boolean(bool)
+
+        self._query_check_mcrouter(ifaceobj, runningattrs)
+        self._query_check_support_yesno_attr_port(runningattrs, ifaceobj, 'portmcfl', ifaceobj.get_attr_value_first('bridge-portmcfl'))
+        self._query_check_support_yesno_attr_port(runningattrs, ifaceobj, 'learning', ifaceobj.get_attr_value_first('bridge-learning'))
+        self._query_check_support_yesno_attr_port(runningattrs, ifaceobj, 'unicast-flood', ifaceobj.get_attr_value_first('bridge-unicast-flood'))
+        self._query_check_support_yesno_attr_port(runningattrs, ifaceobj, 'multicast-flood', ifaceobj.get_attr_value_first('bridge-multicast-flood'))
+        self._query_check_support_yesno_attr_port(runningattrs, ifaceobj, 'arp-nd-suppress', ifaceobj.get_attr_value_first('bridge-arp-nd-suppress'))
+
+    def _query_check_mcrouter(self, ifaceobj, running_attrs):
+        """
+        bridge-mcrouter and bridge-portmcrouter supports: yes-no-0-1-2
+        """
+        if 'mcrouter' in running_attrs:
+            value = ifaceobj.get_attr_value_first('bridge-mcrouter')
+            if value:
+                try:
+                    int(value)
+                except:
+                    running_attrs['mcrouter'] = 'yes' if utils.get_boolean_from_string(running_attrs['mcrouter']) else 'no'
+
+    def _query_check_support_yesno_attr_port(self, runningattrs, ifaceobj, attr, attrval):
+        if attrval:
+            portlist = self.parse_port_list(ifaceobj.name, attrval)
+            if portlist:
+                to_convert = []
+                for p in portlist:
+                    (port, val) = p.split('=')
+                    if not utils.is_binary_bool(val):
+                        to_convert.append(port)
+                for port in to_convert:
+                    runningattrs['ports'][port][attr] = utils.get_yesno_boolean(
+                        utils.get_boolean_from_string(runningattrs['ports'][port][attr]))
+
+    _run_ops = {
+        'pre-up': _up,
+        'post-down': _down,
+        'query-checkcurr': _query_check,
+        'query-running': _query_running,
+        'query': _query
+    }
+
+    def get_ops(self):
+        """ returns list of ops supported by this module """
+        return self._run_ops.keys()
+
+    def _init_command_handlers(self):
+        if not self.ipcmd:
+            self.ipcmd = self.brctlcmd = LinkUtils()
+
+    def run(self, ifaceobj, operation, query_ifaceobj=None, ifaceobj_getfunc=None):
+        """ run bridge configuration on the interface object passed as
+            argument. Can create bridge interfaces if they dont exist already
+
+        Args:
+            **ifaceobj** (object): iface object
+
+            **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
+                                 'query-running'
+
+        Kwargs:
+            **query_ifaceobj** (object): query check ifaceobject. This is only
+                valid when op is 'query-checkcurr'. It is an object same as
+                ifaceobj, but contains running attribute values and its config
+                status. The modules can use it to return queried running state
+                of interfaces. status is success if the running state is same
+                as user required state in ifaceobj. error otherwise.
+        """
+        op_handler = self._run_ops.get(operation)
+        if not op_handler:
+           return
+        self._init_command_handlers()
+
+        if (not LinkUtils.bridge_utils_is_installed
+                and (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT or ifaceobj.link_kind & ifaceLinkKind.BRIDGE)
+                    and LinkUtils.bridge_utils_missing_warning):
+            self.logger.warning('%s: missing - bridge operation may not work as expected. '
+                                'Please check if \'bridge-utils\' package is installed' % utils.brctl_cmd)
+            LinkUtils.bridge_utils_missing_warning = False
+
+        if operation == 'query-checkcurr':
+            op_handler(self, ifaceobj, query_ifaceobj,
+                       ifaceobj_getfunc=ifaceobj_getfunc)
+        else:
+            op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)
diff --git a/ifupdown2/addons/bridgevlan.py b/ifupdown2/addons/bridgevlan.py
new file mode 100644 (file)
index 0000000..aadecc8
--- /dev/null
@@ -0,0 +1,196 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+try:
+    from ifupdown2.ifupdown.iface import *
+
+    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdown2.ifupdownaddons.modulebase import moduleBase
+
+    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
+except ImportError:
+    from ifupdown.iface import *
+
+    from ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdownaddons.modulebase import moduleBase
+
+    import ifupdown.ifupdownflags as ifupdownflags
+
+
+class bridgevlan(moduleBase):
+    """  ifupdown2 addon module to configure vlan attributes on a vlan
+         aware bridge """
+
+    _modinfo = {'mhelp' : 'bridgevlan module configures vlan attributes ' +
+                        'on a vlan aware bridge. This module only ' +
+                        'understands vlan interface name ' +
+                        'with dot notations. eg br0.100. where br0 is the ' +
+                        'vlan aware bridge this config is for',
+                'attrs' : {
+                        'bridge-igmp-querier-src' :
+                            { 'help' : 'bridge igmp querier src. Must be ' +
+                                   'specified under the vlan interface',
+                              'validvals' : ['<ipv4>', ],
+                              'example' : ['bridge-igmp-querier-src 172.16.101.1']}}}
+
+    def __init__(self, *args, **kargs):
+        moduleBase.__init__(self, *args, **kargs)
+        self.brctlcmd = None
+        self.ipcmd = None
+
+    def _is_bridge_vlan_device(self, ifaceobj):
+        if ifaceobj.type == ifaceType.BRIDGE_VLAN:
+            return True
+        return False
+
+    def _get_bridge_n_vlan(self, ifaceobj):
+        vlist = ifaceobj.name.split('.', 1)
+        if len(vlist) == 2:
+            return (vlist[0], vlist[1])
+        return None
+
+    def _get_bridgename(self, ifaceobj):
+        vlist = ifaceobj.name.split('.', 1)
+        if len(vlist) == 2:
+            return vlist[0]
+        return None
+
+    def get_dependent_ifacenames(self, ifaceobj, ifaceobjs_all=None):
+        if not self._is_bridge_vlan_device(ifaceobj):
+            return None
+        return [self._get_bridgename(ifaceobj)]
+
+    def _up(self, ifaceobj):
+        try:
+            (bridgename, vlan) = self._get_bridge_n_vlan(ifaceobj)
+            vlanid = int(vlan, 10)
+        except:
+            self.log_error('%s: bridge vlan interface name ' %ifaceobj.name +
+                    'does not correspond to format (eg. br0.100)', ifaceobj)
+            raise
+
+        if not self.ipcmd.link_exists(bridgename):
+            #self.logger.warn('%s: bridge %s does not exist' %(ifaceobj.name,
+            #                 bridgename))
+            return
+
+        running_mcqv4src = {}
+        if not ifupdownflags.flags.PERFMODE:
+            running_mcqv4src = self.brctlcmd.bridge_get_mcqv4src(bridgename)
+        if running_mcqv4src:
+            r_mcqv4src = running_mcqv4src.get(vlan)
+        else:
+            r_mcqv4src = None
+        mcqv4src = ifaceobj.get_attr_value_first('bridge-igmp-querier-src')
+        if not mcqv4src:
+            if r_mcqv4src:
+                self.brctlcmd.bridge_del_mcqv4src(bridgename, vlanid)
+            return
+
+        if r_mcqv4src and r_mcqv4src != mcqv4src:
+            self.brctlcmd.bridge_del_mcqv4src(bridgename, vlanid)
+            self.brctlcmd.bridge_set_mcqv4src(bridgename, vlanid, mcqv4src)
+        else:
+            self.brctlcmd.bridge_set_mcqv4src(bridgename, vlanid, mcqv4src)
+
+    def _down(self, ifaceobj):
+        try:
+            (bridgename, vlan) = self._get_bridge_n_vlan(ifaceobj)
+            vlanid = int(vlan, 10)
+        except:
+            self.logger.warn('%s: bridge vlan interface name ' %ifaceobj.name +
+                    'does not correspond to format (eg. br0.100)')
+            raise
+
+        if not self.ipcmd.link_exists(bridgename):
+            #self.logger.warn('%s: bridge %s does not exist' %(ifaceobj.name,
+            #                 bridgename))
+            return
+        mcqv4src = ifaceobj.get_attr_value_first('bridge-igmp-querier-src')
+        if mcqv4src:
+           self.brctlcmd.bridge_del_mcqv4src(bridgename, vlanid)
+
+    def _query_running_bridge_igmp_querier_src(self, ifaceobj):
+        (bridgename, vlanid) = ifaceobj.name.split('.')
+        running_mcqv4src = self.brctlcmd.bridge_get_mcqv4src(bridgename)
+        if running_mcqv4src:
+           return running_mcqv4src.get(vlanid)
+        return None
+
+    def _query_check(self, ifaceobj, ifaceobjcurr):
+        attrval = ifaceobj.get_attr_value_first('bridge-igmp-querier-src')
+        if attrval:
+            running_mcq = self._query_running_bridge_igmp_querier_src(ifaceobj)
+            if not running_mcq or running_mcq != attrval:
+                ifaceobjcurr.update_config_with_status(
+                        'bridge-igmp-querier-src', running_mcq, 1)
+            else:
+                ifaceobjcurr.update_config_with_status(
+                        'bridge-igmp-querier-src', attrval, 0)
+                ifaceobjcurr.status = ifaceStatus.SUCCESS
+        return
+
+    def _query_running(self, ifaceobjrunning):
+        # XXX not supported
+        return
+
+    def syntax_check(self, ifaceobj, ifaceobj_getfunc):
+        ret = True
+        bvlan_intf = self._is_bridge_vlan_device(ifaceobj)
+        if (ifaceobj.get_attr_value_first('bridge-igmp-querier-src') and
+            not bvlan_intf):
+            self.logger.error('%s: bridge-igmp-querier-src only allowed under vlan stanza' %ifaceobj.name)
+            ret = False
+        return ret
+
+    _run_ops = {'pre-up' : _up,
+               'post-down' : _down,
+               'query-checkcurr' : _query_check,
+               'query-running' : _query_running}
+
+    def get_ops(self):
+        """ returns list of ops supported by this module """
+        return self._run_ops.keys()
+
+    def _init_command_handlers(self):
+        if not self.ipcmd:
+            self.ipcmd = self.brctlcmd = LinkUtils()
+
+    def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
+        """ run vlan configuration on the interface object passed as argument
+
+        Args:
+            **ifaceobj** (object): iface object
+
+            **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
+                                 'query-running'
+        Kwargs:
+            **query_ifaceobj** (object): query check ifaceobject. This is only
+                valid when op is 'query-checkcurr'. It is an object same as
+                ifaceobj, but contains running attribute values and its config
+                status. The modules can use it to return queried running state
+                of interfaces. status is success if the running state is same
+                as user required state in ifaceobj. error otherwise.
+        """
+        op_handler = self._run_ops.get(operation)
+        if not op_handler:
+            return
+        if (operation != 'query-running' and
+                not self._is_bridge_vlan_device(ifaceobj)):
+            # most common problem is people specify BRIDGE_VLAN
+            # attribute on a bridge or a vlan device, which
+            # is incorrect. So, catch them here and warn before
+            # giving up processing the interface
+            if ((ifaceobj.link_kind & ifaceLinkKind.BRIDGE or
+                ifaceobj.link_kind & ifaceLinkKind.VLAN) and
+                not self.syntax_check(ifaceobj, None)):
+                ifaceobj.status = ifaceStatus.ERROR
+            return
+        self._init_command_handlers()
+        if operation == 'query-checkcurr':
+            op_handler(self, ifaceobj, query_ifaceobj)
+        else:
+            op_handler(self, ifaceobj)
diff --git a/ifupdown2/addons/dhcp.py b/ifupdown2/addons/dhcp.py
new file mode 100644 (file)
index 0000000..09251a9
--- /dev/null
@@ -0,0 +1,226 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+import re
+import time
+
+try:
+    import ifupdown2.ifupdown.policymanager as policymanager
+    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
+
+    from ifupdown2.ifupdown.iface import *
+    from ifupdown2.ifupdown.utils import utils
+
+    from ifupdown2.ifupdownaddons.dhclient import dhclient
+    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdown2.ifupdownaddons.modulebase import moduleBase
+except ImportError:
+    import ifupdown.policymanager as policymanager
+    import ifupdown.ifupdownflags as ifupdownflags
+
+    from ifupdown.iface import *
+    from ifupdown.utils import utils
+
+    from ifupdownaddons.dhclient import dhclient
+    from ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdownaddons.modulebase import moduleBase
+
+
+class dhcp(moduleBase):
+    """ ifupdown2 addon module to configure dhcp on interface """
+
+    def __init__(self, *args, **kargs):
+        moduleBase.__init__(self, *args, **kargs)
+        self.dhclientcmd = dhclient(**kargs)
+        self.ipcmd = None
+
+    def syntax_check(self, ifaceobj, ifaceobj_getfunc):
+        return self.is_dhcp_allowed_on(ifaceobj, syntax_check=True)
+
+    def is_dhcp_allowed_on(self, ifaceobj, syntax_check):
+        if ifaceobj.addr_method and 'dhcp' in ifaceobj.addr_method:
+            return utils.is_addr_ip_allowed_on(ifaceobj, syntax_check=True)
+        return True
+
+    def _up(self, ifaceobj):
+        # if dhclient is already running do not stop and start it
+        dhclient4_running = self.dhclientcmd.is_running(ifaceobj.name)
+        dhclient6_running = self.dhclientcmd.is_running6(ifaceobj.name)
+
+        # today if we have an interface with both inet and inet6, if we
+        # remove the inet or inet6 or both then execute ifreload, we need
+        # to release/kill the appropriate dhclient(4/6) if they are running
+        self._down_stale_dhcp_config(ifaceobj, 'inet', dhclient4_running)
+        self._down_stale_dhcp_config(ifaceobj, 'inet6', dhclient6_running)
+
+        try:
+            dhclient_cmd_prefix = None
+            dhcp_wait = policymanager.policymanager_api.get_attr_default(
+                module_name=self.__class__.__name__, attr='dhcp-wait')
+            wait = not str(dhcp_wait).lower() == "no"
+            vrf = ifaceobj.get_attr_value_first('vrf')
+            if (vrf and self.vrf_exec_cmd_prefix and
+                self.ipcmd.link_exists(vrf)):
+                dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix, vrf)
+
+            if 'inet' in ifaceobj.addr_family:
+                if dhclient4_running:
+                    self.logger.info('dhclient4 already running on %s. '
+                                     'Not restarting.' % ifaceobj.name)
+                else:
+                    # First release any existing dhclient processes
+                    try:
+                        if not ifupdownflags.flags.PERFMODE:
+                            self.dhclientcmd.stop(ifaceobj.name)
+                    except:
+                        pass
+                    self.dhclientcmd.start(ifaceobj.name, wait=wait,
+                                           cmd_prefix=dhclient_cmd_prefix)
+            if 'inet6' in ifaceobj.addr_family:
+                if dhclient6_running:
+                    self.logger.info('dhclient6 already running on %s. '
+                                     'Not restarting.' % ifaceobj.name)
+                else:
+                    accept_ra = ifaceobj.get_attr_value_first('accept_ra')
+                    if accept_ra:
+                        # XXX: Validate value
+                        self.sysctl_set('net.ipv6.conf.%s' %ifaceobj.name +
+                                '.accept_ra', accept_ra)
+                    autoconf = ifaceobj.get_attr_value_first('autoconf')
+                    if autoconf:
+                        # XXX: Validate value
+                        self.sysctl_set('net.ipv6.conf.%s' %ifaceobj.name +
+                                '.autoconf', autoconf)
+                        try:
+                            self.dhclientcmd.stop6(ifaceobj.name)
+                        except:
+                            pass
+                    #add delay before starting IPv6 dhclient to
+                    #make sure the configured interface/link is up.
+                    time.sleep(2)
+                    timeout = 10
+                    while timeout:
+                        timeout -= 2
+                        addr_output = utils.exec_command('%s -6 addr show %s'
+                                                         %(utils.ip_cmd, ifaceobj.name))
+                        r = re.search('inet6 .* scope link', addr_output)
+                        if r:
+                            self.dhclientcmd.start6(ifaceobj.name,
+                                                    wait=wait,
+                                                    cmd_prefix=dhclient_cmd_prefix)
+                            return
+                        time.sleep(2)
+
+        except Exception, e:
+            self.log_error(str(e), ifaceobj)
+
+    def _down_stale_dhcp_config(self, ifaceobj, family, dhclientX_running):
+        addr_family = ifaceobj.addr_family
+        try:
+            if not family in ifaceobj.addr_family and dhclientX_running:
+                ifaceobj.addr_family = [family]
+                self._dhcp_down(ifaceobj)
+        except:
+            pass
+        finally:
+            ifaceobj.addr_family = addr_family
+
+    def _dhcp_down(self, ifaceobj):
+        dhclient_cmd_prefix = None
+        vrf = ifaceobj.get_attr_value_first('vrf')
+        if (vrf and self.vrf_exec_cmd_prefix and
+            self.ipcmd.link_exists(vrf)):
+            dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix, vrf)
+        if 'inet6' in ifaceobj.addr_family:
+            self.dhclientcmd.release6(ifaceobj.name, dhclient_cmd_prefix)
+        if 'inet' in ifaceobj.addr_family:
+            self.dhclientcmd.release(ifaceobj.name, dhclient_cmd_prefix)
+
+    def _down(self, ifaceobj):
+        self._dhcp_down(ifaceobj)
+        self.ipcmd.link_down(ifaceobj.name)
+
+    def _query_check(self, ifaceobj, ifaceobjcurr):
+        status = ifaceStatus.SUCCESS
+        dhcp_running = False
+
+        dhcp_v4 = self.dhclientcmd.is_running(ifaceobjcurr.name)
+        dhcp_v6 = self.dhclientcmd.is_running6(ifaceobjcurr.name)
+
+        if dhcp_v4:
+            dhcp_running = True
+            if 'inet' not in ifaceobj.addr_family and not dhcp_v6:
+                status = ifaceStatus.ERROR
+            ifaceobjcurr.addr_method = 'dhcp'
+        if dhcp_v6:
+            dhcp_running = True
+            if 'inet6' not in ifaceobj.addr_family and not dhcp_v4:
+                status = ifaceStatus.ERROR
+            ifaceobjcurr.addr_method = 'dhcp'
+        ifaceobjcurr.addr_family = ifaceobj.addr_family
+        if not dhcp_running:
+            ifaceobjcurr.addr_family = []
+            status = ifaceStatus.ERROR
+        ifaceobjcurr.status = status
+
+    def _query_running(self, ifaceobjrunning):
+        if not self.ipcmd.link_exists(ifaceobjrunning.name):
+            return
+        if self.dhclientcmd.is_running(ifaceobjrunning.name):
+            ifaceobjrunning.addr_family.append('inet')
+            ifaceobjrunning.addr_method = 'dhcp'
+        if self.dhclientcmd.is_running6(ifaceobjrunning.name):
+            ifaceobjrunning.addr_family.append('inet6')
+            ifaceobjrunning.addr_method = 'dhcp6'
+
+    _run_ops = {'up' : _up,
+               'down' : _down,
+               'pre-down' : _down,
+               'query-checkcurr' : _query_check,
+               'query-running' : _query_running }
+
+    def get_ops(self):
+        """ returns list of ops supported by this module """
+        return self._run_ops.keys()
+
+    def _init_command_handlers(self):
+        if not self.ipcmd:
+            self.ipcmd = LinkUtils()
+
+    def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
+        """ run dhcp configuration on the interface object passed as argument
+
+        Args:
+            **ifaceobj** (object): iface object
+
+            **operation** (str): any of 'up', 'down', 'query-checkcurr',
+                                 'query-running'
+
+        Kwargs:
+            **query_ifaceobj** (object): query check ifaceobject. This is only
+                valid when op is 'query-checkcurr'. It is an object same as
+                ifaceobj, but contains running attribute values and its config
+                status. The modules can use it to return queried running state
+                of interfaces. status is success if the running state is same
+                as user required state in ifaceobj. error otherwise.
+        """
+        op_handler = self._run_ops.get(operation)
+        if not op_handler:
+            return
+        try:
+            if (operation != 'query-running' and
+                   (ifaceobj.addr_method != 'dhcp' and
+                       ifaceobj.addr_method != 'dhcp6')):
+                return
+        except:
+            return
+        if not self.is_dhcp_allowed_on(ifaceobj, syntax_check=False):
+            return
+        self._init_command_handlers()
+        if operation == 'query-checkcurr':
+            op_handler(self, ifaceobj, query_ifaceobj)
+        else:
+            op_handler(self, ifaceobj)
diff --git a/ifupdown2/addons/ethtool.py b/ifupdown2/addons/ethtool.py
new file mode 100644 (file)
index 0000000..3c8be20
--- /dev/null
@@ -0,0 +1,418 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+import os
+
+try:
+    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
+    import ifupdown2.ifupdown.policymanager as policymanager
+
+    from ifupdown2.ifupdown.iface import *
+    from ifupdown2.ifupdown.utils import utils
+    from ifupdown2.ifupdown.exceptions import moduleNotSupported
+
+    from ifupdown2.ifupdownaddons.utilsbase import *
+    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdown2.ifupdownaddons.modulebase import moduleBase
+except ImportError:
+    import ifupdown.ifupdownflags as ifupdownflags
+    import ifupdown.policymanager as policymanager
+
+    from ifupdown.iface import *
+    from ifupdown.utils import utils
+    from ifupdown.exceptions import moduleNotSupported
+
+    from ifupdownaddons.utilsbase import *
+    from ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdownaddons.modulebase import moduleBase
+
+
+class ethtool(moduleBase,utilsBase):
+    """  ifupdown2 addon module to configure ethtool attributes """
+
+    _modinfo = {'mhelp' : 'ethtool configuration module for interfaces',
+                'attrs': {
+                      'link-speed' :
+                            {'help' : 'set link speed',
+                             'validvals' : ['100',
+                                            '1000',
+                                            '10000',
+                                            '25000',
+                                            '40000',
+                                            '50000',
+                                            '100000'],
+                             'example' : ['link-speed 1000'],
+                             'default' : 'varies by platform and port'},
+                      'link-duplex' :
+                            {'help': 'set link duplex',
+                             'example' : ['link-duplex full'],
+                             'validvals' : ['half', 'full'],
+                             'default' : 'full'},
+                      'link-autoneg' :
+                            {'help': 'set autonegotiation',
+                             'example' : ['link-autoneg on'],
+                             'validvals' : ['yes', 'no', 'on', 'off'],
+                             'default' : 'varies by platform and port'},
+                      'link-fec' :
+                            {'help': 'set forward error correction mode',
+                             'example' : ['link-fec rs'],
+                             'validvals' : ['rs', 'baser', 'auto', 'off'],
+                             'default' : 'varies by platform and port'}}}
+
+    def __init__(self, *args, **kargs):
+        moduleBase.__init__(self, *args, **kargs)
+        if not os.path.exists(utils.ethtool_cmd):
+            raise moduleNotSupported('module init failed: %s: not found' % utils.ethtool_cmd)
+        self.ipcmd = None
+        # keep a list of iface objects who have modified link attributes
+        self.ifaceobjs_modified_configs = []
+
+    def do_fec_settings(self, ifaceobj):
+        feccmd = ''
+
+        # attribute existed before but we must reset to default
+        config_val = ifaceobj.get_attr_value_first('link-fec')
+        default_val = policymanager.policymanager_api.get_iface_default(
+                            module_name='ethtool',
+                            ifname=ifaceobj.name,
+                            attr='link-fec')
+
+        if not default_val and not config_val:
+            # there is no point in checking the running config
+            # if we have no default and the user did not have settings
+            return
+
+        # check running values
+        running_val = self.get_running_attr('fec', ifaceobj)
+        if config_val and config_val == running_val:
+            return
+
+        if not config_val and default_val and default_val == running_val:
+            # nothing configured but the default is running
+            return
+
+        # if we got this far, we need to change it
+        if config_val and (config_val != running_val):
+            # if the configured value is not set, set it
+            feccmd = ' %s %s' % ("encoding", config_val)
+        elif default_val and (default_val != running_val):
+            # or if it has a default not equal to running value, set it
+            feccmd = ' %s %s' % ("encoding", default_val)
+
+        if feccmd:
+            try:
+                feccmd = ('%s --set-fec %s %s' %
+                           (utils.ethtool_cmd, ifaceobj.name, feccmd))
+                utils.exec_command(feccmd)
+            except Exception, e:
+                self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
+        else:
+            pass
+
+    def do_speed_settings(self, ifaceobj, operation='post_up'):
+        cmd = ''
+
+        autoneg_to_configure = None
+        speed_to_configure = None
+        duplex_to_configure = None
+
+        config_speed = ifaceobj.get_attr_value_first('link-speed')
+        config_duplex = ifaceobj.get_attr_value_first('link-duplex')
+        config_autoneg = ifaceobj.get_attr_value_first('link-autoneg')
+
+        default_speed = policymanager.policymanager_api.get_iface_default(
+                module_name='ethtool',
+                ifname=ifaceobj.name,
+                attr='link-speed'
+            )
+
+        default_duplex = policymanager.policymanager_api.get_iface_default(
+                module_name='ethtool',
+                ifname=ifaceobj.name,
+                attr='link-duplex'
+            )
+
+        default_autoneg = policymanager.policymanager_api.get_iface_default(
+                module_name='ethtool',
+                ifname=ifaceobj.name,
+                attr='link-autoneg'
+        )
+
+        # autoneg wins if provided by user and is on
+        if config_autoneg and utils.get_boolean_from_string(config_autoneg):
+            autoneg_to_configure = config_autoneg
+            speed_to_configure = None
+            duplex_to_configure = None
+        elif config_speed:
+            # Any speed settings configured by the user wins
+            autoneg_to_configure = None
+            speed_to_configure = config_speed
+            duplex_to_configure = config_duplex
+            if not config_duplex:
+                duplex_to_configure = default_duplex
+        else:
+            # if user given autoneg config is off, we must respect that and
+            # override any default autoneg config
+            if config_autoneg and not utils.get_boolean_from_string(config_autoneg):
+                default_autoneg = 'off'
+
+            if default_autoneg and utils.get_boolean_from_string(default_autoneg):
+                autoneg_to_configure = utils.get_onoff_bool(default_autoneg)
+                speed_to_configure = None
+                duplex_to_configure = None
+            else:
+                autoneg_to_configure = None
+                speed_to_configure = default_speed
+                duplex_to_configure = default_duplex
+
+        if autoneg_to_configure:
+            autoneg_to_configure = utils.get_onoff_bool(autoneg_to_configure)
+            # check running values
+            running_val = self.get_running_attr('autoneg', ifaceobj)
+            if autoneg_to_configure != running_val:
+                # if the configured value is not set, set it
+                cmd += ' autoneg %s' % autoneg_to_configure
+        else:
+            force_set = False
+            if speed_to_configure:
+                # check running values
+                if utils.get_boolean_from_string(self.get_running_attr('autoneg', ifaceobj) or 'off'):
+                    cmd = 'autoneg off'
+                    # if we are transitioning from autoneg 'on' to 'off'
+                    # don't check running speed
+                    force_set = True
+
+                running_val = self.get_running_attr('speed', ifaceobj)
+                if force_set or (speed_to_configure != running_val):
+                    # if the configured value is not set, set it
+                    cmd += ' speed %s' % speed_to_configure
+
+            if duplex_to_configure:
+                # check running values
+                running_val = self.get_running_attr('duplex', ifaceobj)
+                if force_set or (duplex_to_configure != running_val):
+                    # if the configured value is not set, set it
+                    cmd += ' duplex %s' % duplex_to_configure
+
+        if cmd:
+            try:
+                cmd = ('%s -s %s %s' % (utils.ethtool_cmd, ifaceobj.name, cmd))
+                utils.exec_command(cmd)
+            except Exception, e:
+                self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj, raise_error=False)
+
+    def _pre_up(self, ifaceobj, operation='post_up'):
+        """
+        _pre_up and _pre_down will reset the layer 2 attributes to default policy
+        settings.
+        """
+        if not self.ipcmd.link_exists(ifaceobj.name):
+            return
+
+        self.do_speed_settings(ifaceobj)
+        self.do_fec_settings(ifaceobj)
+
+    def _pre_down(self, ifaceobj):
+        pass #self._post_up(ifaceobj,operation="_pre_down")
+
+    def _query_check(self, ifaceobj, ifaceobjcurr):
+        """
+        _query_check() needs to compare the configured (or running)
+        attribute with the running attribute.
+
+        If there is nothing configured, we compare the default attribute with
+        the running attribute and FAIL if they are different.
+        This is because a reboot will lose their running attribute
+        (the default will get set).
+        """
+        for attr in ['speed', 'duplex', 'autoneg', 'fec']:
+            configured = ifaceobj.get_attr_value_first('link-%s'%attr)
+            # if there is nothing configured, do not check
+            if not configured:
+                if not ifupdownflags.flags.WITHDEFAULTS:
+                    continue
+            default = policymanager.policymanager_api.get_iface_default(
+                module_name='ethtool',
+                ifname=ifaceobj.name,
+                attr='link-%s'%attr)
+            # if we have no default, do not bother checking
+            # this avoids ethtool calls on virtual interfaces
+            if not default:
+                continue
+            # autoneg comes from ethtool whereas speed and duplex from /sys/class
+            running_attr = self.get_running_attr(attr, ifaceobj)
+            if not running_attr:
+                if not configured:
+                    continue
+                ifaceobjcurr.update_config_with_status('link-%s' % attr,
+                                                       'unknown', 1)
+                continue
+
+            if attr == 'autoneg':
+                if configured == 'yes' and running_attr == 'on':
+                    running_attr = 'yes'
+                elif configured == 'no' and running_attr == 'off':
+                    running_attr = 'no'
+
+            # we make sure we can get a running value first
+            if (running_attr and configured and running_attr == configured):
+                # PASS since running is what is configured
+                ifaceobjcurr.update_config_with_status('link-%s'%attr,
+                                                       running_attr, 0)
+            elif (running_attr and configured and running_attr != configured):
+                # We show a FAIL since it is not the configured or default
+                ifaceobjcurr.update_config_with_status('link-%s'%attr,
+                                                       running_attr, 1)
+            elif (running_attr and default and running_attr == default):
+                # PASS since running is default
+                ifaceobjcurr.update_config_with_status('link-%s'%attr,
+                                                       running_attr, 0)
+            elif (default or configured):
+                # We show a FAIL since it is not the configured or default
+                ifaceobjcurr.update_config_with_status('link-%s'%attr,
+                                                       running_attr, 1)
+        return
+
+    def get_autoneg(self,ethtool_output=None):
+        """
+        get_autoneg simply calls the ethtool command and parses out
+        the autoneg value.
+        """
+        ethtool_attrs = ethtool_output.split()
+        if ('Auto-negotiation:' in ethtool_attrs):
+            return(ethtool_attrs[ethtool_attrs.index('Auto-negotiation:')+1])
+        else:
+            return(None)
+
+    def get_fec_encoding(self,ethtool_output=None):
+        """
+        get_fec_encoding simply calls the ethtool show-fec command and parses out
+        the fec encoding value.
+        """
+        try:
+            for attr in ethtool_output.splitlines():
+                if attr.startswith('FEC encodings'):
+                    fec_attrs = attr.split()
+                return(fec_attrs[fec_attrs.index(':')+1])
+        except Exception as e:
+            self.logger.debug('ethtool: problems in ethtool set-fec output'
+                               ' %s: %s' %(ethtool_output.splitlines(), str(e)))
+
+        return(None)
+
+    def get_running_attr(self,attr='',ifaceobj=None):
+        if not ifaceobj or not attr:
+            return
+        running_attr = None
+        try:
+            if attr == 'autoneg':
+                output = utils.exec_commandl([utils.ethtool_cmd, ifaceobj.name])
+                running_attr = self.get_autoneg(ethtool_output=output)
+            elif attr == 'fec':
+                output = utils.exec_command('%s --show-fec %s'%
+                                            (utils.ethtool_cmd, ifaceobj.name))
+                running_attr = self.get_fec_encoding(ethtool_output=output)
+            else:
+                running_attr = self.read_file_oneline('/sys/class/net/%s/%s' % \
+                                                      (ifaceobj.name, attr))
+        except Exception as e:
+            # for nonexistent interfaces, we get an error (rc = 256 or 19200)
+            self.logger.debug('ethtool: problems calling ethtool or reading'
+                              ' /sys/class on iface %s for attr %s: %s' %
+                              (ifaceobj.name, attr, str(e)))
+        return running_attr
+
+
+    def _query_running(self, ifaceobj, ifaceobj_getfunc=None):
+        """
+        _query_running looks at the speed and duplex from /sys/class
+        and retreives autoneg from ethtool.  We do not report autoneg
+        if speed is not available because this usually means the link is
+        down and the autoneg value is not reliable when the link is down.
+        """
+        # do not bother showing swp ifaces that are not up for the speed
+        # duplex and autoneg are not reliable.
+        if not self.ipcmd.is_link_up(ifaceobj.name):
+            return
+        for attr in ['speed', 'duplex', 'autoneg']:
+            default_val = policymanager.policymanager_api.get_iface_default(
+                module_name='ethtool',
+                ifname=ifaceobj.name,
+                attr='link-%s'%attr)
+            # do not continue if we have no defaults
+            # this avoids ethtool calls on virtual interfaces
+            if not default_val:
+                continue
+            running_attr = self.get_running_attr(attr, ifaceobj)
+
+            # Only show the link attributes if they differ from defaults
+            # to see the defaults, we should implement another flag (--with-defaults)
+            if default_val == running_attr:
+                continue
+
+            # do not proceed if speed = 0
+            if attr == 'speed' and running_attr and running_attr == '0':
+                return
+            if running_attr:
+                ifaceobj.update_config('link-%s'%attr, running_attr)
+
+        return
+
+    def _query(self, ifaceobj, **kwargs):
+        """ add default policy attributes supported by the module """
+        for attr in ['speed', 'duplex', 'autoneg', 'fec']:
+            if ifaceobj.get_attr_value_first('link-%s'%attr):
+                continue
+            default = policymanager.policymanager_api.get_iface_default(
+                        module_name='ethtool',
+                        ifname=ifaceobj.name,
+                        attr='link-%s' %attr)
+            if not default:
+                continue
+            ifaceobj.update_config('link-%s' %attr, default)
+
+    _run_ops = {'pre-down' : _pre_down,
+                'pre-up' : _pre_up,
+                'query-checkcurr' : _query_check,
+                'query-running' : _query_running,
+                'query' : _query}
+
+    def get_ops(self):
+        """ returns list of ops supported by this module """
+        return self._run_ops.keys()
+
+    def _init_command_handlers(self):
+        if not self.ipcmd:
+            self.ipcmd = LinkUtils()
+
+    def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
+        """ run ethtool configuration on the interface object passed as
+            argument
+
+        Args:
+            **ifaceobj** (object): iface object
+
+            **operation** (str): any of 'post-up', 'query-checkcurr',
+                'query-running'
+        Kwargs:
+            **query_ifaceobj** (object): query check ifaceobject. This is only
+                valid when op is 'query-checkcurr'. It is an object same as
+                ifaceobj, but contains running attribute values and its config
+                status. The modules can use it to return queried running state
+                of interfaces. status is success if the running state is same
+                as user required state in ifaceobj. error otherwise.
+        """
+        if (ifaceobj.link_kind or
+                    ifaceobj.link_privflags & ifaceLinkPrivFlags.LOOPBACK):
+            return
+        op_handler = self._run_ops.get(operation)
+        if not op_handler:
+            return
+        self._init_command_handlers()
+        if operation == 'query-checkcurr':
+            op_handler(self, ifaceobj, query_ifaceobj)
+        else:
+            op_handler(self, ifaceobj)
diff --git a/ifupdown2/addons/link.py b/ifupdown2/addons/link.py
new file mode 100644 (file)
index 0000000..3aaa969
--- /dev/null
@@ -0,0 +1,142 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+# This should be pretty simple and might not really even need to exist.
+# The key is that we need to call link_create with a type of "dummy"
+# since that will translate to 'ip link add loopbackX type dummy'
+# The config file should probably just indicate that the type is
+# loopback or dummy.
+
+try:
+    from ifupdown2.ifupdown.iface import *
+    from ifupdown2.ifupdown.utils import utils
+
+    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdown2.ifupdownaddons.modulebase import moduleBase
+
+    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
+    import ifupdown2.ifupdown.policymanager as policymanager
+except ImportError:
+    from ifupdown.iface import *
+    from ifupdown.utils import utils
+
+    from ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdownaddons.modulebase import moduleBase
+
+    import ifupdown.ifupdownflags as ifupdownflags
+    import ifupdown.policymanager as policymanager
+
+
+class link(moduleBase):
+    _modinfo = {'mhelp' : 'create/configure link types. similar to ip-link',
+                'attrs' : {
+                   'link-type' :
+                        {'help' : 'type of link as in \'ip link\' command.',
+                         'validvals' : ['dummy', 'veth'],
+                         'example' : ['link-type <dummy|veth>']},
+                   'link-down' :
+                        {'help': 'keep link down',
+                         'example' : ['link-down yes/no'],
+                         'default' : 'no',
+                         'validvals' : ['yes', 'no']}}}
+
+    def __init__(self, *args, **kargs):
+        moduleBase.__init__(self, *args, **kargs)
+        self.ipcmd = None
+
+        self.check_physical_port_existance = utils.get_boolean_from_string(policymanager.policymanager_api.get_module_globals(
+            self.__class__.__name__,
+            'warn_on_physdev_not_present'
+        ))
+
+    def syntax_check(self, ifaceobj, ifaceobj_getfunc):
+        if self.check_physical_port_existance:
+            if not ifaceobj.link_kind and not LinkUtils.link_exists(ifaceobj.name):
+                self.logger.warning('%s: interface does not exist' % ifaceobj.name)
+                return False
+        return True
+
+    def _is_my_interface(self, ifaceobj):
+        if (ifaceobj.get_attr_value_first('link-type')
+                or ifaceobj.get_attr_value_first('link-down')):
+            return True
+        return False
+
+    def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
+        if ifaceobj.get_attr_value_first('link-down') == 'yes':
+            ifaceobj.link_privflags |= ifaceLinkPrivFlags.KEEP_LINK_DOWN
+        if ifaceobj.get_attr_value_first('link-type'):
+            ifaceobj.link_kind = ifaceLinkKind.OTHER
+
+    def _up(self, ifaceobj):
+        link_type = ifaceobj.get_attr_value_first('link-type')
+        if link_type:
+            self.ipcmd.link_create(ifaceobj.name,
+                                   ifaceobj.get_attr_value_first('link-type'))
+
+    def _down(self, ifaceobj):
+        if not ifaceobj.get_attr_value_first('link-type'):
+            return
+        if (not ifupdownflags.flags.PERFMODE and
+            not self.ipcmd.link_exists(ifaceobj.name)):
+           return
+        try:
+            self.ipcmd.link_delete(ifaceobj.name)
+        except Exception, e:
+            self.log_warn(str(e))
+
+    def _query_check(self, ifaceobj, ifaceobjcurr):
+        if ifaceobj.get_attr_value('link-type'):
+            if not self.ipcmd.link_exists(ifaceobj.name):
+                ifaceobjcurr.update_config_with_status('link-type', 'None', 1)
+            else:
+                link_type = ifaceobj.get_attr_value_first('link-type')
+                if self.ipcmd.link_get_kind(ifaceobj.name) == link_type:
+                    ifaceobjcurr.update_config_with_status('link-type',
+                                                            link_type, 0)
+                else:
+                    ifaceobjcurr.update_config_with_status('link-type',
+                                                            link_type, 1)
+
+        link_down = ifaceobj.get_attr_value_first('link-down')
+        if link_down:
+            link_up = self.ipcmd.is_link_up(ifaceobj.name)
+            link_should_be_down = utils.get_boolean_from_string(link_down)
+
+            if link_should_be_down and link_up:
+                status = 1
+                link_down = 'no'
+            elif link_should_be_down and not link_up:
+                status = 0
+            elif not link_should_be_down and link_up:
+                status = 0
+            else:
+                status = 1
+
+            ifaceobjcurr.update_config_with_status('link-down', link_down, status)
+
+    _run_ops = {'pre-up' : _up,
+               'post-down' : _down,
+               'query-checkcurr' : _query_check}
+
+    def get_ops(self):
+        return self._run_ops.keys()
+
+    def _init_command_handlers(self):
+        if not self.ipcmd:
+            self.ipcmd = LinkUtils()
+
+    def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
+        op_handler = self._run_ops.get(operation)
+        if not op_handler:
+            return
+        if (operation != 'query-running' and
+                not self._is_my_interface(ifaceobj)):
+            return
+        self._init_command_handlers()
+        if operation == 'query-checkcurr':
+            op_handler(self, ifaceobj, query_ifaceobj)
+        else:
+            op_handler(self, ifaceobj)
diff --git a/ifupdown2/addons/mstpctl.py b/ifupdown2/addons/mstpctl.py
new file mode 100644 (file)
index 0000000..a13e8b6
--- /dev/null
@@ -0,0 +1,1274 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+import os
+
+from sets import Set
+
+try:
+    from ifupdown2.ifupdown.iface import *
+    from ifupdown2.ifupdown.utils import utils
+    from ifupdown2.ifupdown.netlink import netlink
+
+    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
+    import ifupdown2.ifupdown.policymanager as policymanager
+
+    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdown2.ifupdownaddons.modulebase import moduleBase
+    from ifupdown2.ifupdownaddons.mstpctlutil import mstpctlutil
+    from ifupdown2.ifupdownaddons.systemutils import systemUtils
+except ImportError:
+    from ifupdown.iface import *
+    from ifupdown.utils import utils
+    from ifupdown.netlink import netlink
+
+    import ifupdown.ifupdownflags as ifupdownflags
+    import ifupdown.policymanager as policymanager
+
+    from ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdownaddons.modulebase import moduleBase
+    from ifupdownaddons.mstpctlutil import mstpctlutil
+    from ifupdownaddons.systemutils import systemUtils
+
+
+class mstpctlFlags:
+    PORT_PROCESSED = 0x1
+
+class mstpctl(moduleBase):
+    """  ifupdown2 addon module to configure mstp attributes """
+
+    _modinfo = {'mhelp' : 'mstp configuration module for bridges',
+                'attrs' : {
+                   'mstpctl-ports' :
+                        {'help' : 'mstp ports',
+                         'compat' : True,
+                         'deprecated': True,
+                         'new-attribute': 'bridge-ports'},
+                   'mstpctl-stp' :
+                        {'help': 'bridge stp yes/no',
+                         'validvals' : ['yes', 'no', 'on', 'off'],
+                         'compat' : True,
+                         'default' : 'no',
+                         'deprecated': True,
+                         'new-attribute': 'bridge-stp'},
+                   'mstpctl-treeprio' :
+                        {'help': 'tree priority',
+                         'default' : '32768',
+                         'validvals' : ['0', '4096', '8192', '12288', '16384',
+                                        '20480', '24576', '28672', '32768',
+                                        '36864', '40960', '45056', '49152',
+                                        '53248', '57344', '61440'],
+                         'required' : False,
+                         'example' : ['mstpctl-treeprio 32768']},
+                   'mstpctl-ageing' :
+                        {'help': 'ageing time',
+                         'validrange' : ['0', '4096'],
+                         'default' : '300',
+                         'required' : False,
+                         'jsonAttr': 'ageingTime',
+                         'example' : ['mstpctl-ageing 300']},
+                    'mstpctl-maxage' :
+                        { 'help' : 'max message age',
+                          'validrange' : ['0', '255'],
+                          'default' : '20',
+                          'jsonAttr': 'bridgeMaxAge',
+                          'required' : False,
+                          'example' : ['mstpctl-maxage 20']},
+                    'mstpctl-fdelay' :
+                        { 'help' : 'set forwarding delay',
+                          'validrange' : ['0', '255'],
+                          'default' : '15',
+                          'jsonAttr': 'bridgeFwdDelay',
+                          'required' : False,
+                          'example' : ['mstpctl-fdelay 15']},
+                    'mstpctl-maxhops' :
+                        { 'help' : 'bridge max hops',
+                          'validrange' : ['0', '255'],
+                          'default' : '20',
+                          'jsonAttr': 'maxHops',
+                          'required' : False,
+                          'example' : ['mstpctl-maxhops 15']},
+                    'mstpctl-txholdcount' :
+                        { 'help' : 'bridge transmit holdcount',
+                          'validrange' : ['0', '255'],
+                          'default' : '6',
+                          'jsonAttr': 'txHoldCounter',
+                          'required' : False,
+                          'example' : ['mstpctl-txholdcount 6']},
+                    'mstpctl-forcevers' :
+                        { 'help' : 'bridge force stp version',
+                          'validvals' : ['rstp', ],
+                          'default' : 'rstp',
+                          'required' : False,
+                          'jsonAttr': 'forceProtocolVersion',
+                          'example' : ['mstpctl-forcevers rstp']},
+                    'mstpctl-portpathcost' :
+                        { 'help' : 'bridge port path cost',
+                          'validvals': ['<interface-range-list>'],
+                          'validrange' : ['0', '65535'],
+                          'default' : '0',
+                          'jsonAttr' : 'adminExtPortCost',
+                          'required' : False,
+                          'example' : ['under the bridge: mstpctl-portpathcost swp1=0 swp2=1',
+                                       'under the port (recommended): mstpctl-portpathcost 0']},
+                    'mstpctl-portp2p' :
+                        { 'help' : 'bridge port p2p detection mode',
+                          'default' : 'auto',
+                          'jsonAttr' : 'adminPointToPoint',
+                          'validvals' : ['<interface-yes-no-auto-list>'],
+                          'required' : False,
+                          'example' : ['under the bridge: mstpctl-portp2p swp1=yes swp2=no',
+                                       'under the port (recommended): mstpctl-portp2p yes']},
+                    'mstpctl-portrestrrole' :
+                        { 'help' :
+                          'enable/disable port ability to take root role of the port',
+                          'default' : 'no',
+                          'jsonAttr' : 'restrictedRole',
+                          'validvals' : ['<interface-yes-no-list>'],
+                          'required' : False,
+                          'example' : ['under the bridge: mstpctl-portrestrrole swp1=yes swp2=no',
+                                       'under the port (recommended): mstpctl-portrestrrole yes']},
+                    'mstpctl-portrestrtcn' :
+                        { 'help' :
+                          'enable/disable port ability to propagate received topology change notification of the port',
+                          'default' : 'no',
+                          'jsonAttr' : 'restrictedTcn',
+                          'validvals' : ['<interface-yes-no-list>'],
+                          'required' : False,
+                          'example' : ['under the bridge: mstpctl-portrestrtcn swp1=yes swp2=no',
+                                       'under the port (recommended): mstpctl-portrestrtcn yes']},
+                    'mstpctl-bpduguard' :
+                        { 'help' :
+                          'enable/disable bpduguard',
+                          'default' : 'no',
+                          'jsonAttr' : 'bpduGuardPort',
+                          'validvals' : ['<interface-yes-no-list>'],
+                          'required' : False,
+                          'example' : ['under the bridge: mstpctl-bpduguard swp1=yes swp2=no',
+                                       'under the port (recommended): mstpctl-bpduguard yes']},
+                    'mstpctl-treeportprio' :
+                        { 'help': 'Sets the <port>\'s priority MSTI instance. '
+                                  'The priority value must be a number between 0 and 240 and a multiple of 16.',
+                          'default' : '128',
+                          'validvals': ['<interface-range-list-multiple-of-16>'],
+                          'validrange' : ['0', '240'],
+                          'jsonAttr': 'treeportprio',
+                          'required' : False,
+                          'example' : ['under the bridge: mstpctl-treeportprio swp1=128 swp2=128',
+                                       'under the port (recommended): mstpctl-treeportprio 128']},
+                    'mstpctl-hello' :
+                        { 'help' : 'set hello time',
+                          'validrange' : ['0', '255'],
+                          'default' : '2',
+                          'required' : False,
+                          'jsonAttr': 'helloTime',
+                          'example' : ['mstpctl-hello 2']},
+                    'mstpctl-portnetwork' :
+                        { 'help' : 'enable/disable bridge assurance capability for a port',
+                          'validvals' : ['<interface-yes-no-list>'],
+                          'default' : 'no',
+                          'jsonAttr' : 'networkPort',
+                          'required' : False,
+                          'example' : ['under the bridge: mstpctl-portnetwork swp1=yes swp2=no',
+                                       'under the port (recommended): mstpctl-portnetwork yes']},
+                    'mstpctl-portadminedge' :
+                        { 'help' : 'enable/disable initial edge state of the port',
+                          'validvals' : ['<interface-yes-no-list>'],
+                          'default' : 'no',
+                          'jsonAttr' : 'adminEdgePort',
+                          'required' : False,
+                          'example' : ['under the bridge: mstpctl-portadminedge swp1=yes swp2=no',
+                                       'under the port (recommended): mstpctl-portadminedge yes']},
+                    'mstpctl-portautoedge' :
+                        { 'help' : 'enable/disable auto transition to/from edge state of the port',
+                          'validvals' : ['<interface-yes-no-list>'],
+                          'default' : 'yes',
+                          'jsonAttr' : 'autoEdgePort',
+                          'required' : False,
+                          'example' : ['under the bridge: mstpctl-portautoedge swp1=yes swp2=no',
+                                       'under the port (recommended): mstpctl-portautoedge yes']},
+                    'mstpctl-treeportcost' :
+                        { 'help' : 'port tree cost',
+                          'validrange' : ['0', '255'],
+                          'required' : False,
+                          'jsonAttr': 'extPortCost',
+                          },
+                    'mstpctl-portbpdufilter' :
+                        { 'help' : 'enable/disable bpdu filter on a port. ' +
+                                'syntax varies when defined under a bridge ' +
+                                'vs under a port',
+                          'validvals' : ['<interface-yes-no-list>'],
+                          'jsonAttr' : 'bpduFilterPort',
+                          'default' : 'no',
+                          'required' : False,
+                          'example' : ['under a bridge: mstpctl-portbpdufilter swp1=no swp2=no',
+                                       'under a port: mstpctl-portbpdufilter yes']},
+                        }}
+
+    # Maps mstp bridge attribute names to corresponding mstpctl commands
+    # XXX: This can be encoded in the modules dict above
+    _attrs_map = OrderedDict([('mstpctl-treeprio' , 'treeprio'),
+                  ('mstpctl-ageing' , 'ageing'),
+                  ('mstpctl-fdelay' , 'fdelay'),
+                  ('mstpctl-maxage' , 'maxage'),
+                  ('mstpctl-maxhops' , 'maxhops'),
+                  ('mstpctl-txholdcount' , 'txholdcount'),
+                  ('mstpctl-forcevers', 'forcevers'),
+                  ('mstpctl-hello' , 'hello')])
+
+    # Maps mstp port attribute names to corresponding mstpctl commands
+    # XXX: This can be encoded in the modules dict above
+    _port_attrs_map = {'mstpctl-portpathcost' : 'portpathcost',
+                 'mstpctl-portadminedge' : 'portadminedge',
+                 'mstpctl-portautoedge' : 'portautoedge' ,
+                 'mstpctl-portp2p' : 'portp2p',
+                 'mstpctl-portrestrrole' : 'portrestrrole',
+                 'mstpctl-portrestrtcn' : 'portrestrtcn',
+                 'mstpctl-bpduguard' : 'bpduguard',
+                 'mstpctl-treeportprio' : 'treeportprio',
+                 'mstpctl-treeportcost' : 'treeportcost',
+                 'mstpctl-portnetwork' : 'portnetwork',
+                 'mstpctl-portbpdufilter' : 'portbpdufilter'}
+
+    def __init__(self, *args, **kargs):
+        moduleBase.__init__(self, *args, **kargs)
+        self.ipcmd = None
+        self.name = self.__class__.__name__
+        self.brctlcmd = None
+        self.mstpctlcmd = None
+        self.mstpd_running = (True if systemUtils.is_process_running('mstpd')
+                             else False)
+
+        # Background -
+        # The ask is to make "mstpctl-portadminedge yes" part of the default ifupdown2
+        # policy for all vxlan interfaces. In the absence of this, the mstp work flow
+        # is flawed in the event of vxlan flap.
+        # Details -
+        # As of today, for vxlan interfaces "oper edge port" is set to 'yes' and also
+        # "bpdufilter port" is also set to 'yes'. So, in a case where bridge has multiple
+        # vxlan interfaces, if one vxlan interface is flapped, this would trigger mstp
+        # re-evaluation of states on other vxlan interfaces, creating momentary traffic
+        # glitch on those vxlans. Setting "admin edge port" to yes (in addition to the
+        # defaults we already have) prevents this.
+        #
+        # We use to only support 'mstpctl-vxlan-always-set-bpdu-params' but introducing a
+        # separate policy attribute doesn't make sense, we should have one single
+        # attribute to handle the whole thing (and deprecate mstpctl-vxlan-always-set-bpdu-params)
+        # mstpctl-set-default-vxlan-bridge-attrs=yes will set
+        #   mstpctl-portbpdufilter
+        #   mstpctl-bpduguard
+        #   mstpctl-portadminedge
+        #
+        self.set_default_mstp_vxlan_bridge_config = utils.get_boolean_from_string(
+            policymanager.policymanager_api.get_module_globals(
+                module_name=self.__class__.__name__,
+                attr='mstpctl-vxlan-always-set-bpdu-params'
+            )
+        ) or utils.get_boolean_from_string(
+            policymanager.policymanager_api.get_module_globals(
+                module_name=self.__class__.__name__,
+                attr='mstpctl-set-default-vxlan-bridge-attrs'
+            )
+        )
+
+    def syntax_check(self, ifaceobj, ifaceobj_getfunc):
+        if self._is_bridge(ifaceobj):
+            if (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE
+                    and ifaceobj.get_attr_value_first('mstpctl-portadminedge')):
+                self.logger.error('%s: unsupported use of keyword '
+                                  '\'mstpctl-portadminedge\' when '
+                                  'bridge-vlan-aware is on'
+                                  % ifaceobj.name)
+                return False
+        return True
+
+    def _is_bridge(self, ifaceobj):
+        if (ifaceobj.get_attr_value_first('mstpctl-ports') or
+                ifaceobj.get_attr_value_first('bridge-ports')):
+            return True
+        return False
+
+    def _is_bridge_port(self, ifaceobj):
+        if self.brctlcmd.is_bridge_port(ifaceobj.name):
+            return True
+        return False
+
+    def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
+        if not self._is_bridge(ifaceobj):
+            return None
+        return self.parse_port_list(ifaceobj.name,
+                                    ifaceobj.get_attr_value_first(
+                                    'mstpctl-ports'), ifacenames_all)
+
+    def get_dependent_ifacenames_running(self, ifaceobj):
+        self._init_command_handlers()
+        if (self.brctlcmd.bridge_exists(ifaceobj.name) and
+                not self.mstpctlcmd.mstpbridge_exists(ifaceobj.name)):
+            return None
+        return self.brctlcmd.get_bridge_ports(ifaceobj.name)
+
+    def _get_bridge_port_attr_value(self, bridgename, portname, attr):
+        json_attr = self.get_mod_subattr(attr, 'jsonAttr')
+        return self.mstpctlcmd.get_bridge_port_attr(bridgename,
+                                                    portname,
+                                                    json_attr)
+
+    def _get_bridge_port_list(self, ifaceobj):
+
+        # port list is also available in the previously
+        # parsed dependent list. Use that if available, instead
+        # of parsing port expr again
+        port_list = ifaceobj.lowerifaces
+        if port_list:
+            return port_list
+        ports = ifaceobj.get_attr_value_first('mstpctl-ports')
+        if ports:
+            return self.parse_port_list(ifaceobj.name, ports)
+        else:
+            return None
+
+    def _ports_enable_disable_ipv6(self, ports, enable='1'):
+        for p in ports:
+            try:
+                self.write_file('/proc/sys/net/ipv6/conf/%s' %p +
+                                '/disable_ipv6', enable)
+            except Exception, e:
+                self.logger.info(str(e))
+                pass
+
+    def _add_ports(self, ifaceobj):
+        bridgeports = self._get_bridge_port_list(ifaceobj)
+
+        runningbridgeports = []
+        # Delete active ports not in the new port list
+        if not ifupdownflags.flags.PERFMODE:
+            runningbridgeports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
+            if runningbridgeports:
+                [self.ipcmd.link_set(bport, 'nomaster')
+                    for bport in runningbridgeports
+                        if not bridgeports or bport not in bridgeports]
+            else:
+                runningbridgeports = []
+        if not bridgeports:
+            return
+        err = 0
+        for bridgeport in Set(bridgeports).difference(Set(runningbridgeports)):
+            try:
+                if (not ifupdownflags.flags.DRYRUN and
+                    not self.ipcmd.link_exists(bridgeport)):
+                    self.log_warn('%s: bridge port %s does not exist'
+                            %(ifaceobj.name, bridgeport))
+                    err += 1
+                    continue
+                self.ipcmd.link_set(bridgeport, 'master', ifaceobj.name)
+                self.ipcmd.addr_flush(bridgeport)
+            except Exception, e:
+                self.log_error(str(e), ifaceobj)
+
+        if err:
+            self.log_error('error configuring bridge (missing ports)')
+
+    def _apply_bridge_settings(self, ifaceobj, ifaceobj_getfunc):
+        check = False if ifupdownflags.flags.PERFMODE else True
+        try:
+            # set bridge attributes
+            for attrname, dstattrname in self._attrs_map.items():
+                config_val = ifaceobj.get_attr_value_first(attrname)
+                default_val = policymanager.policymanager_api.get_iface_default(module_name=self.__class__.__name__, ifname=ifaceobj.name, attr=attrname)
+                if not default_val:
+                    default_val = self.get_mod_subattr(attrname, 'default')
+                jsonAttr = self.get_mod_subattr(attrname, 'jsonAttr')
+                try:
+                    running_val = self.mstpctlcmd.get_bridge_attr(
+                                    ifaceobj.name, jsonAttr)
+                except:
+                    self.logger.info('%s: could not get running %s value'
+                                     %(ifaceobj.name, attrname))
+                    running_val = None
+                if (not config_val and default_val and (running_val != default_val)):
+                    # this happens when users remove an attribute from a port
+                    # and expect the default to be restored with ifreload.
+                    config_val = default_val
+                elif not config_val:
+                    # there is nothing configured and no default to reset
+                    continue
+                try:
+                    if attrname == 'mstpctl-treeprio':
+                       self.mstpctlcmd.set_bridge_treeprio(ifaceobj.name,
+                                config_val, check)
+                    else:
+                       self.mstpctlcmd.set_bridge_attr(ifaceobj.name,
+                                dstattrname, config_val, check)
+                except Exception, e:
+                    self.logger.warn('%s' %str(e))
+                    pass
+
+            if self.ipcmd.bridge_is_vlan_aware(ifaceobj.name):
+                return
+            # set bridge port attributes
+            for attrname, dstattrname in self._port_attrs_map.items():
+                config_val = ifaceobj.get_attr_value_first(attrname)
+                default_val = self.get_mod_subattr(attrname,'default')
+                if not config_val:
+                    # nothing configured, we may need to reset all ports to defaults
+                    # if the default exists and jsonAttribute conversion exists
+                    try:
+                        jsonAttr =  self.get_mod_subattr(attrname, 'jsonAttr')
+                        if default_val and jsonAttr:
+                            bridgeports = self._get_bridge_port_list(ifaceobj)
+                            for port in bridgeports:
+                                if not self.brctlcmd.is_bridge_port(port):
+                                    continue
+
+                                bport_ifaceobj = ifaceobj_getfunc(port)
+                                if bport_ifaceobj:
+                                    default_val = self._get_default_val(attrname, bport_ifaceobj[0], ifaceobj)
+
+                                self.mstpctlcmd.set_bridge_port_attr(ifaceobj.name,
+                                                                     port,
+                                                                     dstattrname,
+                                                                     default_val,
+                                                                     json_attr=jsonAttr)
+                    except Exception as e:
+                        self.logger.debug('%s' % str(e))
+                        self.logger.info('%s: not resetting %s config'
+                                         %(ifaceobj.name, attrname))
+                    # leave the loop for this attribute
+                    continue
+
+                portlist = self.parse_port_list(ifaceobj.name, config_val)
+                if not portlist:
+                    self.log_error('%s: error parsing \'%s %s\''
+                         %(ifaceobj.name, attrname, config_val), ifaceobj)
+                    continue
+                # there was a configured value so we need to parse it
+                # and set the attribute for each port configured
+                for p in portlist:
+                    try:
+                        (port, val) = p.split('=')
+                        # if it is not bridge port, continue
+                        if not os.path.exists('/sys/class/net/%s/brport' %port):
+                            continue
+                        json_attr = self.get_mod_subattr(attrname, 'jsonAttr')
+                        self.mstpctlcmd.set_bridge_port_attr(ifaceobj.name,
+                                                             port,
+                                                             dstattrname,
+                                                             val,
+                                                             json_attr=json_attr)
+                    except Exception, e:
+                        self.log_error('%s: error setting %s (%s)'
+                                       %(ifaceobj.name, attrname, str(e)),
+                                       ifaceobj, raise_error=False)
+        except Exception, e:
+            self.log_warn(str(e))
+            pass
+
+    def _get_default_val(self, attr, ifaceobj, bridgeifaceobj):
+        if (self.set_default_mstp_vxlan_bridge_config
+            and ifaceobj.link_kind & ifaceLinkKind.VXLAN
+                and attr in (
+                        'mstpctl-portbpdufilter',
+                        'mstpctl-bpduguard',
+                        'mstpctl-portadminedge',
+                )
+        ):
+            try:
+                config_val = bridgeifaceobj.get_attr_value_first(attr)
+            except Exception, e:
+                config_val = None
+            if config_val:
+                if ifaceobj.name not in [v.split('=')[0] for v in config_val.split()]:
+                    return 'yes'
+                else:
+                    index = [v.split('=')[0] for v in config_val.split()].index(ifaceobj.name)
+                    return [v.split('=')[1] for v in config_val.split()][index]
+            else:
+                return 'yes'
+        else:
+            default_val = policymanager.policymanager_api.get_iface_default(module_name=self.__class__.__name__, ifname=ifaceobj.name, attr=attr)
+            if not default_val:
+                return self.get_mod_subattr(attr,'default')
+            return default_val
+
+    def _apply_bridge_port_settings(self, ifaceobj, bridgename=None,
+                                    bridgeifaceobj=None,
+                                    stp_running_on=True,
+                                    mstpd_running=True):
+        check = False if ifupdownflags.flags.PERFMODE else True
+        applied = False
+        if not bridgename and bridgeifaceobj:
+            bridgename = bridgeifaceobj.name
+
+        if not stp_running_on:
+            # stp may get turned on at a later point
+            self.logger.info('%s: ignoring config'
+                             %(ifaceobj.name) +
+                             ' (stp on bridge %s is not on yet)' %bridgename)
+            return applied
+        bvlan_aware = self.ipcmd.bridge_is_vlan_aware(bridgename)
+        if (not mstpd_running or
+            not os.path.exists('/sys/class/net/%s/brport' %ifaceobj.name) or
+            not bvlan_aware):
+                if (not bvlan_aware and
+                    self.set_default_mstp_vxlan_bridge_config and
+                    (ifaceobj.link_kind & ifaceLinkKind.VXLAN)):
+                    for attr in (
+                            'mstpctl-portbpdufilter',
+                            'mstpctl-bpduguard',
+                            'mstpctl-portadminedge'
+                    ):
+                        json_attr = self.get_mod_subattr(attr, 'jsonAttr')
+                        config_val = self._get_default_val(attr, ifaceobj,
+                                                           bridgeifaceobj)
+                        try:
+                            self.mstpctlcmd.set_bridge_port_attr(bridgename,
+                                                                 ifaceobj.name,
+                                                                 self._port_attrs_map[attr],
+                                                                 config_val,
+                                                                 json_attr=json_attr)
+                        except Exception, e:
+                            self.log_warn('%s: error setting %s (%s)'
+                                          % (ifaceobj.name, attr, str(e)))
+
+                if not bvlan_aware:
+                    # for "traditional" bridges we also want to let the user configure
+                    # some attributes (possibly all of them in the future)
+                    applied = self._apply_bridge_port_settings_attributes_list(
+                        (
+                            ('mstpctl-portrestrrole', 'portrestrrole'),
+                            ('mstpctl-portautoedge', 'portautoedge')
+                        ),
+                        ifaceobj,
+                        bridgeifaceobj,
+                        bridgename,
+                        applied
+                    )
+                return applied
+        # set bridge port attributes
+        return self._apply_bridge_port_settings_attributes_list(
+            self._port_attrs_map.items(),
+            ifaceobj,
+            bridgeifaceobj,
+            bridgename,
+            applied
+        )
+
+    def _apply_bridge_port_settings_attributes_list(self, attributes_list, ifaceobj, bridgeifaceobj, bridgename, applied):
+        for attrname, dstattrname in attributes_list:
+            config_val = ifaceobj.get_attr_value_first(attrname)
+            default_val = self._get_default_val(attrname, ifaceobj, bridgeifaceobj)
+            jsonAttr =  self.get_mod_subattr(attrname, 'jsonAttr')
+            # to see the running value, stp would have to be on
+            # so we would have parsed mstpctl showportdetail json output
+            try:
+                running_val = self.mstpctlcmd.get_bridge_port_attr(bridgename,
+                                                       ifaceobj.name, jsonAttr)
+            except:
+                self.logger.info('%s %s: could not get running %s value'
+                                 %(bridgename, ifaceobj.name, attrname))
+                running_val = None
+            if (not config_val and default_val and (running_val != default_val)):
+                # this happens when users remove an attribute from a port
+                # and expect the default to be restored with ifreload.
+                config_val = default_val
+            elif not config_val:
+                # there is nothing configured and no default to reset
+                continue
+
+            try:
+               self.mstpctlcmd.set_bridge_port_attr(bridgename,
+                           ifaceobj.name, dstattrname, config_val, json_attr=jsonAttr)
+               applied = True
+            except Exception, e:
+               self.log_error('%s: error setting %s (%s)'
+                              %(ifaceobj.name, attrname, str(e)), ifaceobj,
+                               raise_error=False)
+        return applied
+
+    def _apply_bridge_port_settings_all(self, ifaceobj,
+                                        ifaceobj_getfunc=None):
+        self.logger.info('%s: applying mstp configuration '
+                          %ifaceobj.name + 'specific to ports')
+        # Query running bridge ports. and only apply attributes on them
+        bridgeports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
+        if not bridgeports:
+           self.logger.debug('%s: cannot find bridgeports' %ifaceobj.name)
+           return
+        for bport in bridgeports:
+            self.logger.info('%s: processing mstp config for port %s'
+                             %(ifaceobj.name, bport))
+            if not self.ipcmd.link_exists(bport):
+               continue
+            if not os.path.exists('/sys/class/net/%s/brport' %bport):
+                continue
+            bportifaceobjlist = ifaceobj_getfunc(bport)
+            if not bportifaceobjlist:
+               continue
+            for bportifaceobj in bportifaceobjlist:
+                # Dont process bridge port if it already has been processed
+                if (bportifaceobj.module_flags.get(self.name,0x0) & \
+                    mstpctlFlags.PORT_PROCESSED):
+                    continue
+                try:
+                    self._apply_bridge_port_settings(bportifaceobj,
+                                            ifaceobj.name, ifaceobj)
+                except Exception, e:
+                    pass
+                    self.log_warn(str(e))
+
+    def _is_running_userspace_stp_state_on(self, bridgename):
+        stp_state_file = '/sys/class/net/%s/bridge/stp_state' %bridgename
+        if not stp_state_file:
+            return False
+        running_stp_state = self.read_file_oneline(stp_state_file)
+        if running_stp_state and running_stp_state == '2':
+            return True
+        return False
+
+    def _up(self, ifaceobj, ifaceobj_getfunc=None):
+        # Check if bridge port
+        bridgename = self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name)
+        if bridgename:
+            mstpd_running = self.mstpd_running
+            stp_running_on = self._is_running_userspace_stp_state_on(bridgename)
+            applied = self._apply_bridge_port_settings(ifaceobj, bridgename,
+                                                       None, stp_running_on,
+                                                       mstpd_running)
+            if applied:
+                ifaceobj.module_flags[self.name] = \
+                        ifaceobj.module_flags.setdefault(self.name,0) | \
+                        mstpctlFlags.PORT_PROCESSED
+            return
+        if not self._is_bridge(ifaceobj):
+            return
+        # we are now here because the ifaceobj is a bridge
+        stp = None
+        try:
+            porterr = False
+            porterrstr = ''
+            if ifaceobj.get_attr_value_first('mstpctl-ports'):
+                # If bridge ports specified with mstpctl attr, create the
+                # bridge and also add its ports
+                self.ipcmd.batch_start()
+                if not ifupdownflags.flags.PERFMODE:
+                    if not self.ipcmd.link_exists(ifaceobj.name):
+                        self.ipcmd.link_create(ifaceobj.name, 'bridge')
+                else:
+                    self.ipcmd.link_create(ifaceobj.name, 'bridge')
+                try:
+                    self._add_ports(ifaceobj)
+                except Exception, e:
+                    porterr = True
+                    porterrstr = str(e)
+                    pass
+                finally:
+                    self.ipcmd.batch_commit()
+                running_ports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
+                if running_ports:
+                    # disable ipv6 for ports that were added to bridge
+                    self._ports_enable_disable_ipv6(running_ports, '1')
+
+            stp = ifaceobj.get_attr_value_first('mstpctl-stp')
+            if stp:
+               self.set_iface_attr(ifaceobj, 'mstpctl-stp',
+                                   self.brctlcmd.bridge_set_stp)
+            else:
+               stp = self.brctlcmd.bridge_get_stp(ifaceobj.name)
+            if (self.mstpd_running and
+                    (stp == 'yes' or stp == 'on')):
+                self._apply_bridge_settings(ifaceobj, ifaceobj_getfunc)
+                self._apply_bridge_port_settings_all(ifaceobj,
+                            ifaceobj_getfunc=ifaceobj_getfunc)
+        except Exception, e:
+            self.log_error(str(e), ifaceobj)
+        if porterr:
+            raise Exception(porterrstr)
+
+    def _down(self, ifaceobj, ifaceobj_getfunc=None):
+        if not self._is_bridge(ifaceobj):
+            return
+        try:
+            if ifaceobj.get_attr_value_first('mstpctl-ports'):
+                # If bridge ports specified with mstpctl attr, delete the
+                # bridge
+                ports = self.brctlcmd.get_bridge_ports(ifaceobj.name)
+                if ports:
+                    self._ports_enable_disable_ipv6(ports, '0')
+                self.brctlcmd.delete_bridge(ifaceobj.name)
+        except Exception, e:
+            self.log_error(str(e), ifaceobj)
+
+    def _query_running_attrs(self, ifaceobjrunning, bridge_vlan_aware=False):
+        bridgeattrdict = {}
+
+        tmpbridgeattrdict = self.mstpctlcmd.get_bridge_attrs(ifaceobjrunning.name)
+        #self.logger.info('A' + str(tmpbridgeattrdict))
+        if not tmpbridgeattrdict:
+            return bridgeattrdict
+
+        for k,v in tmpbridgeattrdict.items():
+            if k == 'stp' or not v:
+                continue
+            if k == 'ports':
+                ports = v.keys()
+                continue
+            attrname = 'mstpctl-' + k
+            if (v and v != self.get_mod_subattr(attrname, 'default')
+                and attrname != 'mstpctl-maxhops'):
+                bridgeattrdict[attrname] = [v]
+
+        ports = self.brctlcmd.get_bridge_ports(ifaceobjrunning.name)
+        # Do this only for vlan-UNAWARE-bridge
+        if ports and not bridge_vlan_aware:
+            portconfig = {'mstpctl-portautoedge' : '',
+                          'mstpctl-portbpdufilter' : '',
+                          'mstpctl-portnetwork' : '',
+                          'mstpctl-portpathcost' : '',
+                          'mstpctl-portadminedge' : '',
+                          'mstpctl-portp2p' : '',
+                          'mstpctl-portrestrrole' : '',
+                          'mstpctl-portrestrtcn' : '',
+                          'mstpctl-bpduguard' : '',
+                          'mstpctl-treeportprio' : '',
+                          'mstpctl-treeportcost' : ''}
+
+            for p in ports:
+
+                for attr in ['mstpctl-portautoedge',
+                             'mstpctl-portbpdufilter',
+                             'mstpctl-portnetwork',
+                             'mstpctl-portadminedge',
+                             'mstpctl-portp2p',
+                             'mstpctl-portrestrrole',
+                             'mstpctl-portrestrtcn',
+                             'mstpctl-bpduguard',
+                             '']:
+                    v = self._get_bridge_port_attr_value(ifaceobjrunning.name,
+                                                         p, attr)
+                    if v and v != 'no':
+                        portconfig[attr] += ' %s=%s' % (p, v)
+
+                for attr in ['mstpctl-portpathcost', 'mstpctl-treeportcost']:
+                    v = self._get_bridge_port_attr_value(ifaceobjrunning.name,
+                                                         p, attr)
+                    if v and v != self.get_mod_subattr(attr, 'default'):
+                        portconfig[attr] += ' %s=%s' % (p, v)
+
+            bridgeattrdict.update({k : [v] for k, v in portconfig.items()
+                                    if v})
+        return bridgeattrdict
+
+    def _get_config_stp(self, ifaceobj):
+        stp = (ifaceobj.get_attr_value_first('mstpctl-stp') or
+               ifaceobj.get_attr_value_first('bridge-stp') or
+               policymanager.policymanager_api.get_iface_default(module_name=self.__class__.__name__, ifname=ifaceobj.name, attr='mstpctl-stp') or
+               # this is a temporary method to access policy default value of bridge-stp
+               policymanager.policymanager_api.get_iface_default(module_name='bridge', ifname=ifaceobj.name, attr='bridge-stp'))
+        return utils.get_boolean_from_string(stp)
+
+    def _get_running_stp(self, ifaceobj):
+        stp = self.brctlcmd.bridge_get_stp(ifaceobj.name)
+        return utils.get_boolean_from_string(stp)
+
+    def _query_check_bridge(self, ifaceobj, ifaceobjcurr,
+                            ifaceobj_getfunc=None):
+        # list of attributes that are not supported currently
+        blacklistedattrs = ['mstpctl-portpathcost',
+                'mstpctl-treeportprio', 'mstpctl-treeportcost']
+        if not self.brctlcmd.bridge_exists(ifaceobj.name):
+            self.logger.debug('bridge %s does not exist' %ifaceobj.name)
+            return
+        ifaceattrs = self.dict_key_subset(ifaceobj.config,
+                                          self.get_mod_attrs())
+        if self.set_default_mstp_vxlan_bridge_config:
+            for attr in ('mstpctl-portbpdufilter', 'mstpctl-bpduguard', 'mstpctl-portadminedge'):
+                if attr not in ifaceattrs:
+                    ifaceattrs.append(attr)
+        if not ifaceattrs:
+            return
+        runningattrs = self.mstpctlcmd.get_bridge_attrs(ifaceobj.name)
+        #self.logger.info('B' + str(runningattrs))
+        if not runningattrs:
+            runningattrs = {}
+        config_stp = self._get_config_stp(ifaceobj)
+        running_stp = self._get_running_stp(ifaceobj)
+        running_port_list = self.brctlcmd.get_bridge_ports(ifaceobj.name)
+        for k in ifaceattrs:
+            # for all mstpctl options
+            if k in blacklistedattrs:
+                continue
+            if k in ('mstpctl-portbpdufilter', 'mstpctl-bpduguard', 'mstpctl-portadminedge'):
+                #special case, 'ifquery --check --with-defaults' on a VLAN
+                #unaware bridge
+                if not running_port_list:
+                    continue
+                if (not config_stp or not running_stp):
+                    continue
+                v = ifaceobj.get_attr_value_first(k)
+                config_val = {}
+                running_val = {}
+                result = 0
+                bridge_ports = {}
+                state = ''
+                if v:
+                    for bportval in v.split():
+                        config_val[bportval.split('=')[0]] = bportval.split('=')[1]
+                #for bport in bridgeports:
+                for bport in running_port_list:
+                    bportifaceobjlist = ifaceobj_getfunc(bport)
+                    if not bportifaceobjlist:
+                        continue
+                    for bportifaceobj in bportifaceobjlist:
+                        if (bport not in config_val):
+                            if (bportifaceobj.link_kind & ifaceLinkKind.VXLAN):
+                                if (not ifupdownflags.flags.WITHDEFAULTS or
+                                    (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE)):
+                                    continue
+                                conf = 'yes'
+                            else:
+                                continue
+                        else:
+                            if ((bportifaceobj.link_kind & ifaceLinkKind.VXLAN) and
+                                 (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE)):
+                                continue
+                            conf = config_val[bport]
+                        jsonAttr =  self.get_mod_subattr(k, 'jsonAttr')
+                        try:
+                            running_val = self.mstpctlcmd.get_bridge_port_attr(ifaceobj.name, bport, jsonAttr)
+                        except:
+                            self.logger.info('%s %s: could not get running %s value'
+                                    %(ifaceobj.name, bport, attr))
+                            running_val = None
+                        if conf != running_val:
+                            result = 1
+                        bridge_ports.update({bport : running_val})
+                for port, val in bridge_ports.items():
+                    #running state format
+                    #mstpctl-portbpdufilter swp2=yes swp1=yes vx-14567101=yes    [pass]
+                    #mstpctl-bpduguard swp2=yes swp1=yes vx-14567101=yes         [pass]
+                    state += port + '=' + val + ' '
+                if state:
+                    ifaceobjcurr.update_config_with_status(k, state, result)
+                continue
+
+            # get the corresponding ifaceobj attr
+            v = ifaceobj.get_attr_value_first(k)
+            if not v:
+                continue
+
+            # Get the running attribute
+            rv = runningattrs.get(k[8:])
+            if k == 'mstpctl-stp':
+                # special case stp compare because it may
+                # contain more than one valid values
+                stp_on_vals = ['on', 'yes']
+                stp_off_vals = ['off']
+                rv = self.brctlcmd.bridge_get_stp(ifaceobj.name)
+                if ((v in stp_on_vals and rv in stp_on_vals) or
+                    (v in stp_off_vals and rv in stp_off_vals)):
+                    ifaceobjcurr.update_config_with_status('mstpctl-stp', v, 0)
+                else:
+                    ifaceobjcurr.update_config_with_status('mstpctl-stp', v, 1)
+                continue
+
+            if k == 'mstpctl-ports':
+                # special case ports because it can contain regex or glob
+                # XXX: We get all info from mstputils, which means if
+                # mstpd is down, we will not be returning any bridge bridgeports
+                bridge_port_list = self._get_bridge_port_list(ifaceobj)
+                if not running_port_list and not bridge_port_list:
+                    continue
+                portliststatus = 1
+                if running_port_list and bridge_port_list:
+                    difference = Set(running_port_list).symmetric_difference(
+                                                        Set(bridge_port_list))
+                    if not difference:
+                        portliststatus = 0
+                ifaceobjcurr.update_config_with_status('mstpctl-ports',
+                    ' '.join(running_port_list)
+                    if running_port_list else '', portliststatus)
+            elif k[:12] == 'mstpctl-port' or k == 'mstpctl-bpduguard':
+                # Now, look at port attributes
+                # derive the mstpctlcmd attr name
+                #mstpctlcmdattrname = k[12:] if k[:12] == 'mstpctl-port' else k[8:]
+                mstpctlcmdattrname = k[8:]
+
+                # for port attributes, the attributes are in a list
+                # <portname>=<portattrvalue>
+                status = 0
+                currstr = ''
+                vlist = self.parse_port_list(ifaceobj.name, v)
+                if not vlist:
+                    continue
+                for vlistitem in vlist:
+                    try:
+                        (p, v) = vlistitem.split('=')
+                        currv = self._get_bridge_port_attr_value(ifaceobj.name, p, k)
+                        if currv:
+                            currstr += ' %s=%s' %(p, currv)
+                        else:
+                            currstr += ' %s=%s' %(p, 'None')
+                        if currv != v:
+                            status = 1
+                    except Exception, e:
+                        self.log_warn(str(e))
+                        pass
+                ifaceobjcurr.update_config_with_status(k, currstr, status)
+            elif not rv:
+                ifaceobjcurr.update_config_with_status(k, '', 1)
+            elif v != rv:
+                ifaceobjcurr.update_config_with_status(k, rv, 1)
+            else:
+                ifaceobjcurr.update_config_with_status(k, rv, 0)
+
+    def _query_check_bridge_vxlan_port(self, ifaceobj, ifaceobjcurr,
+                            ifaceobj_getfunc=None):
+        masters = ifaceobj.upperifaces
+        if not masters:
+            return
+        for bridge in masters:
+            bifaceobjlist = ifaceobj_getfunc(bridge)
+            for bifaceobj in bifaceobjlist:
+                if (self._is_bridge(bifaceobj) and
+                    self.set_default_mstp_vxlan_bridge_config and
+                    (bifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE)):
+                        config_stp = self._get_config_stp(bifaceobj)
+                        running_stp = self._get_running_stp(bifaceobj)
+                        if (not config_stp or not running_stp):
+                            continue
+                        for attr in (
+                                'mstpctl-portbpdufilter',
+                                'mstpctl-bpduguard',
+                                'mstpctl-portadminedge'
+                        ):
+                            jsonAttr =  self.get_mod_subattr(attr, 'jsonAttr')
+                            config_val = bifaceobj.get_attr_value_first(attr)
+                            if config_val:
+                                if ifaceobj.name not in [v.split('=')[0] for v in config_val.split()]:
+                                    if not ifupdownflags.flags.WITHDEFAULTS:
+                                        continue
+                                    config_val = 'yes'
+                                else:
+                                    index = [v.split('=')[0] for v in config_val.split()].index(ifaceobj.name)
+                                    config_val = [v.split('=')[1] for v in config_val.split()][index]
+                            else:
+                                if not ifupdownflags.flags.WITHDEFAULTS:
+                                    continue
+                                config_val = 'yes'
+                            try:
+                                running_val = self.mstpctlcmd.get_bridge_port_attr(bifaceobj.name,
+                                                    ifaceobj.name, jsonAttr)
+                            except:
+                                self.logger.info('%s %s: could not get running %s value'
+                                        %(bifaceobj.name, ifaceobj.name, attr))
+                                running_val = None
+                            ifaceobjcurr.update_config_with_status(attr,
+                                        running_val,
+                                        0 if running_val == config_val else 1)
+                        return
+
+
+    def _query_check_bridge_port(self, ifaceobj, ifaceobjcurr):
+        if not self.ipcmd.link_exists(ifaceobj.name):
+            #self.logger.debug('bridge port %s does not exist' %ifaceobj.name)
+            ifaceobjcurr.status = ifaceStatus.NOTFOUND
+            return
+        # Check if this is a bridge port
+        if not self._is_bridge_port(ifaceobj):
+            # mark all the bridge attributes as error
+            ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj,
+                            self._port_attrs_map.keys(), 0)
+            return
+        bridgename = self.ipcmd.bridge_port_get_bridge_name(ifaceobj.name)
+        # list of attributes that are not supported currently
+        blacklistedattrs = ['mstpctl-portpathcost',
+                'mstpctl-treeportprio', 'mstpctl-treeportcost']
+        ifaceattrs = self.dict_key_subset(ifaceobj.config,
+                                          self._port_attrs_map.keys())
+        if not ifaceattrs:
+            return
+        runningattrs = self.mstpctlcmd.get_bridge_attrs(ifaceobj.name)
+        #self.logger.info('C' + str(runningattrs))
+        if not runningattrs:
+            runningattrs = {}
+        for k in ifaceattrs:
+            # for all mstpctl options
+            # get the corresponding ifaceobj attr
+            v = ifaceobj.get_attr_value_first(k)
+            if not v or k in blacklistedattrs:
+                ifaceobjcurr.update_config_with_status(k, v, -1)
+                continue
+            currv = self._get_bridge_port_attr_value(bridgename, ifaceobj.name, k)
+            if currv:
+                if currv != v:
+                    ifaceobjcurr.update_config_with_status(k, currv, 1)
+                else:
+                    ifaceobjcurr.update_config_with_status(k, currv, 0)
+            else:
+                ifaceobjcurr.update_config_with_status(k, None, 1)
+
+    def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
+        if self._is_bridge(ifaceobj):
+            self._query_check_bridge(ifaceobj, ifaceobjcurr, ifaceobj_getfunc)
+        elif ifaceobj.link_kind & ifaceLinkKind.VXLAN:
+            self._query_check_bridge_vxlan_port(ifaceobj, ifaceobjcurr,
+                                              ifaceobj_getfunc)
+        else:
+            self._query_check_bridge_port(ifaceobj, ifaceobjcurr)
+
+    def _query_bridge_port_attr(self, ifaceobjrunning, bridgename, attr, value_cmp):
+        v = self._get_bridge_port_attr_value(bridgename,
+                                             ifaceobjrunning.name,
+                                             attr)
+        if v and value_cmp and v != value_cmp:
+            ifaceobjrunning.update_config(attr, v)
+        elif v and not value_cmp:
+            ifaceobjrunning.update_config(attr, v)
+
+    def _query_running_bridge_port(self, ifaceobjrunning):
+        bridgename = self.ipcmd.bridge_port_get_bridge_name(
+                                ifaceobjrunning.name)
+        if not bridgename:
+            self.logger.warn('%s: unable to determine bridgename'
+                             %ifaceobjrunning.name)
+            return
+        if self.brctlcmd.bridge_get_stp(bridgename) == 'no':
+           # This bridge does not run stp, return
+           return
+        # if userspace stp not set, return
+        if self.systcl_get_net_bridge_stp_user_space() != '1':
+            return
+
+        self._query_bridge_port_attr(ifaceobjrunning, bridgename,
+                                     'mstpctl-portautoedge',
+                                     self.get_mod_subattr('mstpctl-portautoedge', 'default'))
+
+        self._query_bridge_port_attr(ifaceobjrunning, bridgename,
+                                     'mstpctl-portbpdufilter',
+                                     'no')
+
+        self._query_bridge_port_attr(ifaceobjrunning, bridgename,
+                                     'mstpctl-portnetwork',
+                                     'no')
+
+        # XXX: Can we really get path cost of a port ???
+        #v = self.mstpctlcmd.get_portpathcost(ifaceobjrunning.name, p)
+        #if v and v != self.get_mod_subattr('mstpctl-pathcost',
+        #                                   'default'):
+        #   ifaceobjrunning.update_config('mstpctl-network', v)
+
+        self._query_bridge_port_attr(ifaceobjrunning, bridgename,
+                                     'mstpctl-portadminedge',
+                                     'no')
+
+        self._query_bridge_port_attr(ifaceobjrunning, bridgename,
+                                     'mstpctl-portp2p',
+                                     'auto')
+
+        self._query_bridge_port_attr(ifaceobjrunning, bridgename,
+                                     'mstpctl-portrestrrole',
+                                     'no')
+
+        self._query_bridge_port_attr(ifaceobjrunning, bridgename,
+                                     'mstpctl-portrestrtcn',
+                                     'no')
+
+        self._query_bridge_port_attr(ifaceobjrunning, bridgename,
+                                     'mstpctl-bpduguard',
+                                     'no')
+
+        # XXX: Can we really get path cost of a port ???
+        #v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
+        #            p, 'treeprio')
+        #if v and v != self.get_mod_subattr('mstpctl-treeportprio',
+        #                                   'default'):
+        #    portconfig['mstpctl-treeportprio'] += ' %s=%s' %(p, v)
+
+        #v = self.mstpctlcmd.get_bridgeport_attr(ifaceobjrunning.name,
+        #               p, 'treecost')
+        #if v and v != self.get_mod_subattr('mstpctl-treeportcost',
+        #                                   'default'):
+        #    portconfig['mstpctl-treeportcost'] += ' %s=%s' %(p, v)
+
+    def _query_running_bridge(self, ifaceobjrunning):
+        if self.brctlcmd.bridge_get_stp(ifaceobjrunning.name) == 'no':
+           # This bridge does not run stp, return
+           return
+        # if userspace stp not set, return
+        if self.systcl_get_net_bridge_stp_user_space() != '1':
+            return
+        # Check if mstp really knows about this bridge
+        if not self.mstpctlcmd.mstpbridge_exists(ifaceobjrunning.name):
+            return
+        bridge_vlan_aware = False
+        if ifaceobjrunning.get_attr_value_first('bridge-vlan-aware') == 'yes':
+            bridge_vlan_aware = True
+        ifaceobjrunning.update_config_dict(self._query_running_attrs(
+                                           ifaceobjrunning,
+                                           bridge_vlan_aware))
+
+    def _query_running(self, ifaceobjrunning, **extra_args):
+        if self.brctlcmd.bridge_exists(ifaceobjrunning.name):
+            self._query_running_bridge(ifaceobjrunning)
+        elif self.brctlcmd.is_bridge_port(ifaceobjrunning.name):
+            self._query_running_bridge_port(ifaceobjrunning)
+
+    def _query_bridge_port(self, ifaceobj, ifaceobj_getfunc=None):
+        """
+        Example:
+        Configuration:
+            auto vxlan1wd
+            iface vxlan1wd
+                vxlan-id 1001
+
+            auto vxlan2wd
+            iface vxlan2wd
+                vxlan-id 1002
+
+            auto brwithdef2
+            iface brwithdef2
+                bridge_ports vxlan1wd vxlan2wd
+                bridge-vlan-aware yes
+
+        Output:
+        $ ifquery vxlan1wd
+            auto vxlan1wd
+            iface vxlan1wd
+                vxlan-id 1001
+
+        $ ifquery --with-defaults vxlan1wd
+            auto vxlan1wd
+            iface vxlan1wd
+                vxlan-id 1001
+                mstpctl-portbpdufilter yes
+                mstpctl-bpduguard yes
+        """
+        masters = ifaceobj.upperifaces
+        if not masters:
+            return
+        try:
+            for bridge in masters:
+                bifaceobj = ifaceobj_getfunc(bridge)[0]
+                if (self._is_bridge(bifaceobj) and
+                    self.set_default_mstp_vxlan_bridge_config and
+                    (bifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE)):
+                        for attr in ('mstpctl-portbpdufilter',
+                                     'mstpctl-bpduguard',
+                                     'mstpctl-portadminedge'):
+                            jsonAttr =  self.get_mod_subattr(attr, 'jsonAttr')
+                            config_val = ifaceobj.get_attr_value_first(attr)
+                            if config_val or not ifupdownflags.flags.WITHDEFAULTS:
+                                continue
+                            config_val = 'yes'
+                            ifaceobj.replace_config(attr, config_val)
+                        return
+        except Exception, e:
+            self.logger.info("%s: %s" %(ifaceobj.name, str(e)))
+            pass
+
+    def _query(self, ifaceobj, ifaceobj_getfunc=None, **kwargs):
+        """ add default policy attributes supported by the module """
+        if not self._is_bridge(ifaceobj):
+            if (ifaceobj.module_flags.get(self.name,0x0) &
+                    mstpctlFlags.PORT_PROCESSED):
+                return
+            self._query_bridge_port(ifaceobj, ifaceobj_getfunc)
+            ifaceobj.module_flags[self.name] = (
+                        ifaceobj.module_flags.setdefault(self.name,0) |
+                        mstpctlFlags.PORT_PROCESSED)
+            return
+        lowerinfs = ifaceobj.lowerifaces
+        if not lowerinfs:
+            return
+        if ifaceobj.get_attr_value_first('bridge-vlan-aware') != 'yes':
+            for attr in ('mstpctl-portbpdufilter', 'mstpctl-bpduguard', 'mstpctl-portadminedge'):
+                state = ''
+                config = ifaceobj.get_attr_value_first(attr)
+                for port in lowerinfs:
+                    bportobjlist = ifaceobj_getfunc(port)
+                    for bportobj in bportobjlist:
+                        if bportobj.get_attr_value_first('vxlan-id'):
+                            if config:
+                                if port not in [v.split('=')[0] for v in config.split()]:
+                                    config += ' %s=yes' %port
+                            else:
+                                state += '%s=yes ' %port
+                ifaceobj.replace_config(attr, config if config else state)
+        else:
+            for attr in ('mstpctl-portbpdufilter', 'mstpctl-bpduguard', 'mstpctl-portadminedge'):
+                state = ''
+                config = ifaceobj.get_attr_value_first(attr)
+                for port in lowerinfs:
+                    bportobjlist = ifaceobj_getfunc(port)
+                    for bportobj in bportobjlist:
+                        if (bportobj.module_flags.get(self.name,0x0) &
+                            mstpctlFlags.PORT_PROCESSED):
+                            continue
+                        if bportobj.get_attr_value_first('vxlan-id'):
+                            if config:
+                                if port not in [v.split('=')[0] for v in config.split()]:
+                                    bportobj.update_config(attr, 'yes')
+                                else:
+                                    index = [v.split('=')[0] for v in config.split()].index(port)
+                                    state = [v.split('=')[1] for v in config.split()][index]
+                                    bportobj.update_config(attr, '%s' %state)
+                                    v = config.split()
+                                    del v[index]
+                                    config = ' '.join(v)
+                            else:
+                                bportobj.replace_config(attr, 'yes')
+                            bportobj.module_flags[self.name] = (
+                                bportobj.module_flags.setdefault(self.name,0) |
+                                mstpctlFlags.PORT_PROCESSED)
+                if config:
+                    ifaceobj.replace_config(attr, config)
+
+
+
+    _run_ops = {'pre-up' : _up,
+               'post-down' : _down,
+               'query-checkcurr' : _query_check,
+               'query-running' : _query_running,
+               'query' : _query}
+
+    def get_ops(self):
+        """ returns list of ops supported by this module """
+        return self._run_ops.keys()
+
+    def _init_command_handlers(self):
+        if not self.ipcmd:
+            self.ipcmd = self.brctlcmd = LinkUtils()
+        if not self.mstpctlcmd:
+            self.mstpctlcmd = mstpctlutil()
+
+    def run(self, ifaceobj, operation, query_ifaceobj=None,
+            ifaceobj_getfunc=None, **extra_args):
+        """ run mstp configuration on the interface object passed as argument
+
+        Args:
+            **ifaceobj** (object): iface object
+
+            **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
+                                 'query-running'
+        Kwargs:
+            **query_ifaceobj** (object): query check ifaceobject. This is only
+                valid when op is 'query-checkcurr'. It is an object same as
+                ifaceobj, but contains running attribute values and its config
+                status. The modules can use it to return queried running state
+                of interfaces. status is success if the running state is same
+                as user required state in ifaceobj. error otherwise.
+        """
+        if ifaceobj.type == ifaceType.BRIDGE_VLAN:
+           return
+        op_handler = self._run_ops.get(operation)
+        if not op_handler:
+           return
+        self._init_command_handlers()
+        if operation == 'query-checkcurr':
+            op_handler(self, ifaceobj, query_ifaceobj,
+                       ifaceobj_getfunc=ifaceobj_getfunc)
+        else:
+            op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)
diff --git a/ifupdown2/addons/usercmds.py b/ifupdown2/addons/usercmds.py
new file mode 100644 (file)
index 0000000..863f57f
--- /dev/null
@@ -0,0 +1,104 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+import os
+
+try:
+    from ifupdown2.ifupdown.utils import utils
+
+    from ifupdown2.ifupdownaddons.modulebase import moduleBase
+except ImportError:
+    from ifupdown.utils import utils
+
+    from ifupdownaddons.modulebase import moduleBase
+
+
+class usercmds(moduleBase):
+    """  ifupdown2 addon module to configure user specified commands """
+
+    _modinfo = {'mhelp' : 'user commands for interfaces',
+                'attrs' : {
+                   'pre-up' :
+                        {'help' : 'run command before bringing the interface up',
+                         'multiline' : True},
+                   'up' :
+                        {'help' : 'run command at interface bring up',
+                         'multiline' : True},
+                   'post-up' :
+                        {'help' : 'run command after interface bring up',
+                         'multiline' : True},
+                   'pre-down' :
+                        {'help' : 'run command before bringing the interface down',
+                         'multiline' : True},
+                   'down' :
+                        {'help' : 'run command at interface down',
+                         'multiline' : True},
+                   'post-down' :
+                        {'help' : 'run command after bringing interface down',
+                         'multiline' : True}}}
+
+    def _run_command(self, ifaceobj, op):
+        cmd_list = ifaceobj.get_attr_value(op)
+        if cmd_list:
+            os.environ['IFACE'] = ifaceobj.name if ifaceobj.name else ''
+            os.environ['LOGICAL'] = ifaceobj.name if ifaceobj.name else ''
+            os.environ['METHOD'] = ifaceobj.addr_method if ifaceobj.addr_method else ''
+            os.environ['ADDRFAM'] = ','.join(ifaceobj.addr_family) if ifaceobj.addr_family else ''
+            for cmd in cmd_list:
+                try:
+                    utils.exec_user_command(cmd)
+                except Exception, e:
+                    if not self.ignore_error(str(e)):
+                        self.logger.warn('%s: %s %s' % (ifaceobj.name, op,
+                                                        str(e).strip('\n')))
+                    pass
+
+    def _query_check(self, ifaceobj, ifaceobjcurr):
+        if ifaceobj.config:
+            for ops in ['pre-up',
+                        'up',
+                        'post-up',
+                        'pre-down',
+                        'down',
+                        'post-down']:
+                for cmd in ifaceobj.config.get(ops, []):
+                    ifaceobjcurr.update_config_with_status(ops, cmd, -1)
+
+    _run_ops = {'pre-up' : _run_command,
+               'pre-down' : _run_command,
+               'up' : _run_command,
+               'post-up' : _run_command,
+               'down' : _run_command,
+               'post-down' : _run_command,
+               'query-checkcurr': _query_check}
+
+    def get_ops(self):
+        """ returns list of ops supported by this module """
+        return self._run_ops.keys()
+
+    def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
+        """ run user commands
+
+        Args:
+            **ifaceobj** (object): iface object
+
+            **operation** (str): list of ops
+
+        Kwargs:
+            **query_ifaceobj** (object): query check ifaceobject. This is only
+                valid when op is 'query-checkcurr'. It is an object same as
+                ifaceobj, but contains running attribute values and its config
+                status. The modules can use it to return queried running state
+                of interfaces. status is success if the running state is same
+                as user required state in ifaceobj. error otherwise.
+        """
+        op_handler = self._run_ops.get(operation)
+        if not op_handler:
+            return
+        if operation == 'query-checkcurr':
+            op_handler(self, ifaceobj, query_ifaceobj)
+        else:
+            op_handler(self, ifaceobj, operation)
diff --git a/ifupdown2/addons/vlan.py b/ifupdown2/addons/vlan.py
new file mode 100644 (file)
index 0000000..c7b2c0f
--- /dev/null
@@ -0,0 +1,242 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+try:
+    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
+
+    from ifupdown2.ifupdown.iface import *
+    from ifupdown2.ifupdown.netlink import netlink
+
+    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdown2.ifupdownaddons.modulebase import moduleBase
+except ImportError:
+    import ifupdown.ifupdownflags as ifupdownflags
+
+    from ifupdown.iface import *
+    from ifupdown.netlink import netlink
+
+    from ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdownaddons.modulebase import moduleBase
+
+
+
+class vlan(moduleBase):
+    """  ifupdown2 addon module to configure vlans """
+
+    _modinfo = {'mhelp' : 'vlan module configures vlan interfaces.' +
+                        'This module understands vlan interfaces with dot ' +
+                        'notations. eg swp1.100. Vlan interfaces with any ' +
+                        'other names need to have raw device and vlan id ' +
+                        'attributes',
+                'attrs' : {
+                        'vlan-raw-device' :
+                            {'help' : 'vlan raw device',
+                             'validvals': ['<interface>']},
+                        'vlan-id' :
+                            {'help' : 'vlan id',
+                             'validrange' : ['0', '4096']},
+                        'vlan-protocol' :
+                            {'help' : 'vlan protocol',
+                             'default' : '802.1q',
+                             'validvals': ['802.1q', '802.1ad'],
+                             'example' : ['vlan-protocol 802.1q']},
+               }}
+
+
+    def __init__(self, *args, **kargs):
+        moduleBase.__init__(self, *args, **kargs)
+        self.ipcmd = None
+
+    def _is_vlan_device(self, ifaceobj):
+        vlan_raw_device = ifaceobj.get_attr_value_first('vlan-raw-device')
+        if vlan_raw_device:
+            return True
+        elif '.' in ifaceobj.name:
+            return True
+        return False
+
+    def _is_vlan_by_name(self, ifacename):
+        return '.' in ifacename
+
+    def _get_vlan_raw_device_from_ifacename(self, ifacename):
+        """ Returns vlan raw device from ifname
+        Example:
+            Returns eth0 for ifname eth0.100
+            Returns eth0.100 for ifname eth0.100.200
+            Returns None if vlan raw device name cannot
+            be determined
+        """
+        vlist = ifacename.split('.', 2)
+        if len(vlist) == 2:
+            return vlist[0]
+        elif len(vlist) == 3:
+            return vlist[0] + "." + vlist[1]
+        return None
+
+    def _get_vlan_raw_device(self, ifaceobj):
+        vlan_raw_device = ifaceobj.get_attr_value_first('vlan-raw-device')
+        if vlan_raw_device:
+            return vlan_raw_device
+        return self._get_vlan_raw_device_from_ifacename(ifaceobj.name)
+
+    def get_dependent_ifacenames(self, ifaceobj, ifaceobjs_all=None):
+        if not self._is_vlan_device(ifaceobj):
+            return None
+        ifaceobj.link_kind |= ifaceLinkKind.VLAN
+        return [self._get_vlan_raw_device(ifaceobj)]
+
+    def _bridge_vid_add_del(self, ifaceobj, bridgename, vlanid,
+                            add=True):
+        """ If the lower device is a vlan aware bridge, add/del the vlanid
+        to the bridge """
+        if self.ipcmd.bridge_is_vlan_aware(bridgename):
+           if add:
+               netlink.link_add_bridge_vlan(bridgename, vlanid)
+           else:
+               netlink.link_del_bridge_vlan(bridgename, vlanid)
+
+    def _bridge_vid_check(self, ifaceobj, ifaceobjcurr, bridgename, vlanid):
+        """ If the lower device is a vlan aware bridge, check if the vlanid
+        is configured on the bridge """
+        if not self.ipcmd.bridge_is_vlan_aware(bridgename):
+            return
+        vids = self.ipcmd.bridge_vlan_get_vids(bridgename)
+        if not vids or vlanid not in vids:
+            ifaceobjcurr.status = ifaceStatus.ERROR
+            ifaceobjcurr.status_str = 'bridge vid error'
+
+    def _up(self, ifaceobj):
+        vlanid = self._get_vlan_id(ifaceobj)
+        if vlanid == -1:
+            raise Exception('could not determine vlanid')
+        vlanrawdevice = self._get_vlan_raw_device(ifaceobj)
+        if not vlanrawdevice:
+            raise Exception('could not determine vlan raw device')
+
+        vlan_protocol           = ifaceobj.get_attr_value_first('vlan-protocol')
+        cached_vlan_protocol    = self.ipcmd.get_vlan_protocol(ifaceobj.name)
+
+        if not vlan_protocol:
+            vlan_protocol = self.get_attr_default_value('vlan-protocol')
+
+        if cached_vlan_protocol and vlan_protocol.lower() != cached_vlan_protocol.lower():
+            raise Exception('%s: cannot change vlan-protocol to %s: operation not supported. '
+                            'Please delete the device with \'ifdown %s\' and recreate it to '
+                            'apply the change.'
+                            % (ifaceobj.name, vlan_protocol, ifaceobj.name))
+
+        if not ifupdownflags.flags.PERFMODE:
+            if not self.ipcmd.link_exists(vlanrawdevice):
+                raise Exception('rawdevice %s not present' %vlanrawdevice)
+            if self.ipcmd.link_exists(ifaceobj.name):
+                self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid)
+                return
+
+        netlink.link_add_vlan(vlanrawdevice, ifaceobj.name, vlanid, vlan_protocol)
+        self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid)
+
+    def _down(self, ifaceobj):
+        vlanid = self._get_vlan_id(ifaceobj)
+        if vlanid == -1:
+            raise Exception('could not determine vlanid')
+        vlanrawdevice = self._get_vlan_raw_device(ifaceobj)
+        if not vlanrawdevice:
+            raise Exception('could not determine vlan raw device')
+        if (not ifupdownflags.flags.PERFMODE and
+            not self.ipcmd.link_exists(ifaceobj.name)):
+           return
+        try:
+            self.ipcmd.link_delete(ifaceobj.name)
+            self._bridge_vid_add_del(ifaceobj, vlanrawdevice, vlanid, add=False)
+        except Exception, e:
+            self.log_warn(str(e))
+
+    def _query_check(self, ifaceobj, ifaceobjcurr):
+        if not self.ipcmd.link_exists(ifaceobj.name):
+           return
+        if not '.' in ifaceobj.name:
+            # if vlan name is not in the dot format, check its running state
+            (vlanrawdev, vlanid, protocol) = self.ipcmd.get_vlandev_attrs(ifaceobj.name)
+            if vlanrawdev != ifaceobj.get_attr_value_first('vlan-raw-device'):
+                ifaceobjcurr.update_config_with_status('vlan-raw-device',
+                        vlanrawdev, 1)
+            else:
+                ifaceobjcurr.update_config_with_status('vlan-raw-device',
+                        vlanrawdev, 0)
+            vlanid_config = ifaceobj.get_attr_value_first('vlan-id')
+            if not vlanid_config:
+                vlanid_config = str(self._get_vlan_id(ifaceobj))
+            if vlanid != vlanid_config:
+                ifaceobjcurr.update_config_with_status('vlan-id', vlanid, 1)
+            else:
+                ifaceobjcurr.update_config_with_status('vlan-id', vlanid, 0)
+            protocol_config = ifaceobj.get_attr_value_first('vlan-protocol')
+            if protocol_config:
+                if protocol_config.upper() != protocol.upper():
+                    ifaceobjcurr.update_config_with_status('vlan-protocol',
+                                                           protocol, 1)
+                else:
+                    ifaceobjcurr.update_config_with_status('vlan-protocol',
+                                                           protocol, 0)
+            self._bridge_vid_check(ifaceobj, ifaceobjcurr, vlanrawdev, int(vlanid))
+
+    def _query_running(self, ifaceobjrunning):
+        if not self.ipcmd.link_exists(ifaceobjrunning.name):
+            return
+        (vlanrawdev, vlanid, protocol) = self.ipcmd.get_vlandev_attrs(ifaceobjrunning.name)
+        if not vlanid:
+            return
+        # If vlan name is not in the dot format, get the
+        # vlan dev and vlan id
+        if not '.' in ifaceobjrunning.name:
+            ifaceobjrunning.update_config_dict({k: [v] for k, v in
+                                                {'vlan-raw-device' : vlanrawdev,
+                                                 'vlan-id' : vlanid,
+                                                 'vlan-protocol' : protocol}.items()
+                                                if v})
+
+    _run_ops = {'pre-up' : _up,
+               'post-down' : _down,
+               'query-checkcurr' : _query_check,
+               'query-running' : _query_running}
+
+    def get_ops(self):
+        """ returns list of ops supported by this module """
+        return self._run_ops.keys()
+
+    def _init_command_handlers(self):
+        if not self.ipcmd:
+            self.ipcmd = LinkUtils()
+
+    def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
+        """ run vlan configuration on the interface object passed as argument
+
+        Args:
+            **ifaceobj** (object): iface object
+
+            **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
+                                 'query-running'
+        Kwargs:
+            **query_ifaceobj** (object): query check ifaceobject. This is only
+                valid when op is 'query-checkcurr'. It is an object same as
+                ifaceobj, but contains running attribute values and its config
+                status. The modules can use it to return queried running state
+                of interfaces. status is success if the running state is same
+                as user required state in ifaceobj. error otherwise.
+        """
+        if ifaceobj.type == ifaceType.BRIDGE_VLAN:
+            return
+        op_handler = self._run_ops.get(operation)
+        if not op_handler:
+            return
+        if (operation != 'query-running' and
+                not self._is_vlan_device(ifaceobj)):
+            return
+        self._init_command_handlers()
+        if operation == 'query-checkcurr':
+            op_handler(self, ifaceobj, query_ifaceobj)
+        else:
+            op_handler(self, ifaceobj)
diff --git a/ifupdown2/addons/vrf.py b/ifupdown2/addons/vrf.py
new file mode 100644 (file)
index 0000000..7c98d9a
--- /dev/null
@@ -0,0 +1,1125 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+import re
+import os
+import fcntl
+import atexit
+import signal
+
+from sets import Set
+
+try:
+    import ifupdown2.ifupdown.policymanager as policymanager
+    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
+
+    from ifupdown2.ifupdown.iface import *
+    from ifupdown2.ifupdown.utils import utils
+    from ifupdown2.ifupdown.netlink import netlink
+
+    from ifupdown2.ifupdownaddons.dhclient import dhclient
+    from ifupdown2.ifupdownaddons.utilsbase import *
+    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdown2.ifupdownaddons.modulebase import moduleBase
+except ImportError:
+    import ifupdown.policymanager as policymanager
+    import ifupdown.ifupdownflags as ifupdownflags
+
+    from ifupdown.iface import *
+    from ifupdown.utils import utils
+    from ifupdown.netlink import netlink
+
+    from ifupdownaddons.dhclient import dhclient
+    from ifupdownaddons.utilsbase import *
+    from ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdownaddons.modulebase import moduleBase
+
+
+class vrfPrivFlags:
+    PROCESSED = 0x1
+
+class vrf(moduleBase):
+    """  ifupdown2 addon module to configure vrfs """
+    _modinfo = { 'mhelp' : 'vrf configuration module',
+                    'attrs' : {
+                    'vrf-table':
+                         {'help' : 'vrf device routing table id. key to ' +
+                                   'creating a vrf device. ' +
+                                   'Table id is either \'auto\' or '+
+                                   '\'valid routing table id\'',
+                          'validvals': ['auto', '<number>'],
+                          'example': ['vrf-table auto', 'vrf-table 1001']},
+                    'vrf':
+                         {'help' : 'vrf the interface is part of.',
+                          'validvals': ['<text>'],
+                          'example': ['vrf blue']}}}
+
+    iproute2_vrf_filename = '/etc/iproute2/rt_tables.d/ifupdown2_vrf_map.conf'
+    iproute2_vrf_filehdr = '# This file is autogenerated by ifupdown2.\n' + \
+                           '# It contains the vrf name to table mapping.\n' + \
+                           '# Reserved table range %s %s\n'
+    VRF_TABLE_START = 1001
+    VRF_TABLE_END = 5000
+
+    system_reserved_rt_tables = {'255' : 'local', '254' : 'main',
+                                 '253' : 'default', '0' : 'unspec'}
+
+    def __init__(self, *args, **kargs):
+        moduleBase.__init__(self, *args, **kargs)
+        self.ipcmd = None
+        self.bondcmd = None
+        self.dhclientcmd = None
+        self.name = self.__class__.__name__
+        self.vrf_mgmt_devname = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-mgmt-devname')
+
+        self.user_reserved_vrf_table = []
+
+        if (ifupdownflags.flags.PERFMODE and
+            not (self.vrf_mgmt_devname and os.path.exists('/sys/class/net/%s'
+            %self.vrf_mgmt_devname))):
+            # if perf mode is set (PERFMODE is set at boot), and this is the first
+            # time we are calling ifup at boot (check for mgmt vrf existance at
+            # boot, make sure this is really the first invocation at boot.
+            # ifup is called with PERFMODE at boot multiple times (once for mgmt vrf
+            # and the second time with all auto interfaces). We want to delete
+            # the map file only the first time. This is to avoid accidently
+            # deleting map file with a valid mgmt vrf entry
+            if os.path.exists(self.iproute2_vrf_filename):
+                try:
+                    self.logger.info('vrf: removing file %s'
+                                     %self.iproute2_vrf_filename)
+                    os.remove(self.iproute2_vrf_filename)
+                except Exception, e:
+                    self.logger.debug('vrf: removing file failed (%s)'
+                                      %str(e))
+        try:
+            ip_rules = utils.exec_command('%s rule show'
+                                          %utils.ip_cmd).splitlines()
+            self.ip_rule_cache = [' '.join(r.split()) for r in ip_rules]
+        except Exception, e:
+            self.ip_rule_cache = []
+            self.logger.warn('vrf: cache v4: %s' % str(e))
+
+        try:
+            ip_rules = utils.exec_command('%s -6 rule show'
+                                          %utils.ip_cmd).splitlines()
+            self.ip6_rule_cache = [' '.join(r.split()) for r in ip_rules]
+        except Exception, e:
+            self.ip6_rule_cache = []
+            self.logger.warn('vrf: cache v6: %s' % str(e))
+
+        #self.logger.debug("vrf: ip rule cache")
+        #self.logger.info(self.ip_rule_cache)
+
+        #self.logger.info("vrf: ip -6 rule cache")
+        #self.logger.info(self.ip6_rule_cache)
+
+        self.l3mdev_checked = False
+        self.l3mdev4_rule = False
+        if self._l3mdev_rule(self.ip_rule_cache):
+            self.l3mdev4_rule = True
+            self.l3mdev_checked = True
+        self.l3mdev6_rule = False
+        if self._l3mdev_rule(self.ip6_rule_cache):
+            self.l3mdev6_rule = True
+            self.l3mdev_checked = True
+        self._iproute2_vrf_map_initialized = False
+        self.iproute2_vrf_map = {}
+        self.iproute2_vrf_map_fd = None
+        self.iproute2_vrf_map_sync_to_disk = False
+
+        self.vrf_table_id_start = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-table-id-start')
+        if not self.vrf_table_id_start:
+            self.vrf_table_id_start = self.VRF_TABLE_START
+        self.vrf_table_id_end = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-table-id-end')
+        if not self.vrf_table_id_end:
+            self.vrf_table_id_end = self.VRF_TABLE_END
+
+        self._modinfo['attrs']['vrf-table']['validrange'] = [
+            str(self.vrf_table_id_start),
+            str(self.vrf_table_id_end)
+        ]
+
+        self.vrf_max_count = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-max-count')
+
+        self.vrf_fix_local_table = True
+        self.vrf_count = 0
+        self.vrf_helper = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-helper')
+        self.vrf_close_socks_on_down = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-close-socks-on-down')
+        self.warn_on_vrf_map_write_err = True
+
+    def _check_vrf_table_id(self, ifaceobj):
+        vrf_table = ifaceobj.get_attr_value_first('vrf-table')
+        if not vrf_table:
+            return False
+        if (vrf_table != 'auto' and
+            (int(vrf_table) < self.vrf_table_id_start or
+             int(vrf_table) > self.vrf_table_id_end)):
+            self.logger.error('%s: vrf table id %s out of reserved range [%d,%d]'
+                             %(ifaceobj.name,
+                               vrf_table,
+                               self.vrf_table_id_start,
+                               self.vrf_table_id_end))
+            return False
+        return True
+
+    def syntax_check(self, ifaceobj, ifaceobj_getfunc):
+        if ifaceobj.link_kind & ifaceLinkKind.VRF:
+            try:
+                check_vrf_table_id  = self._check_vrf_table_id(ifaceobj)
+                check_vrf_sys_names = self._check_vrf_system_reserved_names(ifaceobj)
+                return check_vrf_table_id and check_vrf_sys_names
+            except Exception as e:
+                self.logger.error('%s: %s' % (ifaceobj.name, str(e)))
+                return False
+        return True
+
+    def _check_vrf_system_reserved_names(self, ifaceobj):
+        system_reserved_names = self.system_reserved_rt_tables.values()
+        if ifaceobj.name in system_reserved_names:
+            self.log_error('cannot use system reserved %s vrf names'
+                           % (str(system_reserved_names)), ifaceobj)
+            return False
+        return True
+
+    def _iproute2_vrf_map_initialize(self, writetodisk=True):
+        if self._iproute2_vrf_map_initialized:
+            return
+
+        # XXX: check for vrf reserved overlap in /etc/iproute2/rt_tables
+        self.iproute2_vrf_map = {}
+        iproute2_vrf_map_force_rewrite = False
+        # read or create /etc/iproute2/rt_tables.d/ifupdown2.vrf_map
+        if os.path.exists(self.iproute2_vrf_filename):
+            with open(self.iproute2_vrf_filename, 'r+') as vrf_map_fd:
+                lines = vrf_map_fd.readlines()
+                for l in lines:
+                    l = l.strip()
+                    if l[0] == '#':
+                        continue
+                    try:
+                        (table, vrf_name) = l.strip().split()
+                        if self.iproute2_vrf_map.get(int(table)):
+                            # looks like the existing file has
+                            # duplicate entries, force rewrite of the
+                            # file
+                            iproute2_vrf_map_force_rewrite = True
+                            continue
+                        self.iproute2_vrf_map[int(table)] = vrf_name
+                    except Exception, e:
+                        self.logger.info('vrf: iproute2_vrf_map: unable to parse %s (%s)' %(l, str(e)))
+                        pass
+
+        vrfs = self.ipcmd.link_get_vrfs()
+        running_vrf_map = {}
+        if vrfs:
+            for v, lattrs in vrfs.iteritems():
+                table = lattrs.get('table', None)
+                if table:
+                   running_vrf_map[int(table)] = v
+
+        if (not running_vrf_map or (running_vrf_map != self.iproute2_vrf_map)):
+            self.iproute2_vrf_map = running_vrf_map
+            iproute2_vrf_map_force_rewrite = True
+
+        self.iproute2_vrf_map_fd = None
+        if writetodisk:
+            if iproute2_vrf_map_force_rewrite:
+                # reopen the file and rewrite the map
+                self._iproute2_vrf_map_open(True, False)
+            else:
+                self._iproute2_vrf_map_open(False, True)
+
+        self.iproute2_vrf_map_sync_to_disk = False
+        atexit.register(self._iproute2_vrf_map_sync_to_disk)
+
+        self.logger.info("vrf: dumping iproute2_vrf_map")
+        self.logger.info(self.iproute2_vrf_map)
+
+        last_used_vrf_table = None
+        for t in range(self.vrf_table_id_start,
+                       self.vrf_table_id_end):
+            if not self.iproute2_vrf_map.get(t):
+                break
+            last_used_vrf_table = t
+        self.last_used_vrf_table = last_used_vrf_table
+        self._iproute2_vrf_map_initialized = True
+        self.vrf_count = len(self.iproute2_vrf_map)
+
+    def _iproute2_map_warn(self, errstr):
+        if self.warn_on_vrf_map_write_err:
+            if not os.path.exists('/etc/iproute2/rt_tables.d/'):
+                self.logger.info('unable to save iproute2 vrf to table ' +
+                                 'map (%s)\n' %errstr)
+                self.logger.info('cannot find /etc/iproute2/rt_tables.d.' +
+                                 ' pls check if your iproute2 version' +
+                                 ' supports rt_tables.d')
+            else:
+                self.logger.warn('unable to open iproute2 vrf to table ' +
+                                 'map (%s)\n' %errstr)
+            self.warn_on_vrf_map_write_err = False
+
+    def _iproute2_vrf_map_sync_to_disk(self):
+        if (ifupdownflags.flags.DRYRUN or
+            not self.iproute2_vrf_map_sync_to_disk):
+            return
+        self.logger.info('vrf: syncing table map to %s'
+                         %self.iproute2_vrf_filename)
+        try:
+            with open(self.iproute2_vrf_filename, 'w') as f:
+                f.write(self.iproute2_vrf_filehdr %(self.vrf_table_id_start,
+                        self.vrf_table_id_end))
+                for t, v in self.iproute2_vrf_map.iteritems():
+                    f.write('%s %s\n' %(t, v))
+                f.flush()
+        except Exception, e:
+            self._iproute2_map_warn(str(e))
+            pass
+
+    def _iproute2_vrf_map_open(self, sync_vrfs=False, append=False):
+        self.logger.info('vrf: syncing table map to %s'
+                         %self.iproute2_vrf_filename)
+        if ifupdownflags.flags.DRYRUN:
+            return
+        fmode = 'a+' if append else 'w'
+        try:
+            self.iproute2_vrf_map_fd = open(self.iproute2_vrf_filename,
+                                         '%s' %fmode)
+            fcntl.fcntl(self.iproute2_vrf_map_fd, fcntl.F_SETFD, fcntl.FD_CLOEXEC)
+        except Exception, e:
+            self._iproute2_map_warn(str(e))
+            return
+
+        if not append:
+            # write file header
+            self.iproute2_vrf_map_fd.write(self.iproute2_vrf_filehdr
+                                           %(self.vrf_table_id_start,
+                                             self.vrf_table_id_end))
+            for t, v in self.iproute2_vrf_map.iteritems():
+                self.iproute2_vrf_map_fd.write('%s %s\n' %(t, v))
+            self.iproute2_vrf_map_fd.flush()
+
+    def _is_vrf(self, ifaceobj):
+        if ifaceobj.get_attr_value_first('vrf-table'):
+            return True
+        return False
+
+    def get_upper_ifacenames(self, ifaceobj, ifacenames_all=None):
+        """ Returns list of interfaces dependent on ifaceobj """
+
+        vrf_table = ifaceobj.get_attr_value_first('vrf-table')
+        if vrf_table:
+            ifaceobj.link_type = ifaceLinkType.LINK_MASTER
+            ifaceobj.link_kind |= ifaceLinkKind.VRF
+            ifaceobj.role |= ifaceRole.MASTER
+
+            if vrf_table != 'auto':
+                # if the user didn't specify auto we need to store the desired
+                # vrf tables ids, in case the configuration has both auto and
+                # hardcoded vrf-table ids. We need to create them all without
+                # collisions.
+                self.user_reserved_vrf_table.append(int(vrf_table))
+
+        vrf_iface_name = ifaceobj.get_attr_value_first('vrf')
+        if not vrf_iface_name:
+            return None
+        ifaceobj.link_type = ifaceLinkType.LINK_SLAVE
+        ifaceobj.link_privflags |= ifaceLinkPrivFlags.VRF_SLAVE
+
+        return [vrf_iface_name]
+
+    def get_upper_ifacenames_running(self, ifaceobj):
+        return None
+
+    def _get_iproute2_vrf_table(self, vrf_dev_name):
+        for t, v in self.iproute2_vrf_map.iteritems():
+            if v == vrf_dev_name:
+                return str(t)
+        return None
+
+    def _get_avail_vrf_table_id(self):
+        if self.last_used_vrf_table == None:
+            table_id_start = self.vrf_table_id_start
+        else:
+            table_id_start = self.last_used_vrf_table + 1
+        for t in range(table_id_start, self.vrf_table_id_end):
+            if (not self.iproute2_vrf_map.get(t)
+                    and t not in self.user_reserved_vrf_table):
+                self.last_used_vrf_table = t
+                return str(t)
+        return None
+
+    def _iproute2_is_vrf_tableid_inuse(self, vrfifaceobj, table_id):
+        old_vrf_name = self.iproute2_vrf_map.get(int(table_id))
+        if old_vrf_name and old_vrf_name != vrfifaceobj.name:
+            self.log_error('table id %s already assigned to vrf dev %s'
+                           %(table_id, old_vrf_name), vrfifaceobj)
+
+    def _iproute2_vrf_table_entry_add(self, vrfifaceobj, table_id):
+        old_vrf_name = self.iproute2_vrf_map.get(int(table_id))
+        if not old_vrf_name:
+            self.iproute2_vrf_map[int(table_id)] = vrfifaceobj.name
+            if self.iproute2_vrf_map_fd:
+                self.iproute2_vrf_map_fd.write('%s %s\n'
+                                               %(table_id, vrfifaceobj.name))
+                self.iproute2_vrf_map_fd.flush()
+                self.vrf_count += 1
+            return
+        if old_vrf_name != vrfifaceobj.name:
+            self.log_error('table id %d already assigned to vrf dev %s'
+                           %(table_id, old_vrf_name))
+
+    def _iproute2_vrf_table_entry_del(self, table_id):
+        try:
+            # with any del of vrf map, we need to force sync to disk
+            self.iproute2_vrf_map_sync_to_disk = True
+            del self.iproute2_vrf_map[int(table_id)]
+        except Exception, e:
+            self.logger.info('vrf: iproute2 vrf map del failed for %s (%s)'
+                             %(table_id, str(e)))
+            pass
+
+    def _is_vrf_dev(self, ifacename):
+        # Look at iproute2 map for now.
+        # If it was a master we knew about,
+        # it is definately there
+        if ifacename in self.iproute2_vrf_map.values():
+            return True
+        return False
+
+    def _is_dhcp_slave(self, ifaceobj):
+        if (not ifaceobj.addr_method or
+            (ifaceobj.addr_method != 'dhcp' and
+             ifaceobj.addr_method != 'dhcp6')):
+                return False
+        return True
+
+    def _up_vrf_slave_without_master(self, ifacename, vrfname, ifaceobj, vrf_master_objs, ifaceobj_getfunc=None):
+        """ If we have a vrf slave that has dhcp configured, bring up the
+            vrf master now. This is needed because vrf has special handling
+            in dhclient hook which requires the vrf master to be present """
+        vrf_master = None
+        if len(ifaceobj.upperifaces) > 1 and ifaceobj_getfunc:
+            for upper_iface in ifaceobj.upperifaces:
+                upper_ifaceobjs = ifaceobj_getfunc(upper_iface)
+
+                if upper_ifaceobjs:
+                    for upper_obj in upper_ifaceobjs:
+                        if upper_obj.link_kind & ifaceLinkKind.VRF:
+                            vrf_master = upper_obj.name
+                            break
+        elif ifaceobj.upperifaces:
+            vrf_master = ifaceobj.upperifaces[0]
+        if not vrf_master:
+            self.logger.warn('%s: vrf master not found' %ifacename)
+            return
+        if os.path.exists('/sys/class/net/%s' %vrf_master):
+            self.logger.info('%s: vrf master %s exists returning'
+                             %(ifacename, vrf_master))
+            return
+        self.logger.info('%s: bringing up vrf master %s'
+                         %(ifacename, vrf_master))
+        for mobj in vrf_master_objs:
+            vrf_table = mobj.get_attr_value_first('vrf-table')
+            if vrf_table:
+                if vrf_table == 'auto':
+                    vrf_table = self._get_avail_vrf_table_id()
+                    if not vrf_table:
+                        self.log_error('%s: unable to get an auto table id'
+                                       %mobj.name, ifaceobj)
+                    self.logger.info('%s: table id auto: selected table id %s\n'
+                                     %(mobj.name, vrf_table))
+                try:
+                    self._up_vrf_dev(mobj, vrf_table, False)
+                except Exception:
+                    raise
+                break
+        self._handle_existing_connections(ifaceobj, vrfname)
+        self.ipcmd.link_set(ifacename, 'master', vrfname)
+        return
+
+    def _down_dhcp_slave(self, ifaceobj, vrfname):
+        try:
+            dhclient_cmd_prefix = None
+            if (vrfname and self.vrf_exec_cmd_prefix and
+                self.ipcmd.link_exists(vrfname)):
+                dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix,
+                                                vrfname)
+            self.dhclientcmd.release(ifaceobj.name, dhclient_cmd_prefix)
+        except:
+            # ignore any dhclient release errors
+            pass
+
+    def _handle_existing_connections(self, ifaceobj, vrfname):
+        if not ifaceobj or ifupdownflags.flags.PERFMODE:
+            return
+        if (self.vrf_mgmt_devname and
+            self.vrf_mgmt_devname == vrfname):
+            self._kill_ssh_connections(ifaceobj.name)
+        if self._is_dhcp_slave(ifaceobj):
+            self._down_dhcp_slave(ifaceobj, vrfname)
+
+    def _up_vrf_slave(self, ifacename, vrfname, ifaceobj=None,
+                      ifaceobj_getfunc=None, vrf_exists=False):
+        try:
+            master_exists = True
+            if vrf_exists or self.ipcmd.link_exists(vrfname):
+                uppers = self.ipcmd.link_get_uppers(ifacename)
+                if not uppers or vrfname not in uppers:
+                    self._handle_existing_connections(ifaceobj, vrfname)
+                    self.ipcmd.link_set(ifacename, 'master', vrfname)
+            elif ifaceobj:
+                vrf_master_objs = ifaceobj_getfunc(vrfname)
+                if not vrf_master_objs:
+                    # this is the case where vrf is assigned to an interface
+                    # but user has not provided a vrf interface.
+                    # people expect you to warn them but go ahead with the
+                    # rest of the config on that interface
+                    netlink.link_set_updown(ifacename, "up")
+                    self.log_error('vrf master ifaceobj %s not found'
+                                   %vrfname)
+                    return
+                if (ifupdownflags.flags.ALL or
+                    ifupdownflags.flags.WITH_DEPENDS or
+                    (ifupdownflags.flags.CLASS and
+                     ifaceobj.classes and vrf_master_objs[0].classes and
+                     Set(ifaceobj.classes).intersection(vrf_master_objs[0].classes))):
+                    self._up_vrf_slave_without_master(ifacename, vrfname,
+                                                      ifaceobj,
+                                                      vrf_master_objs,
+                                                      ifaceobj_getfunc)
+                else:
+                    master_exists = False
+            else:
+                master_exists = False
+            if master_exists:
+                netlink.link_set_updown(ifacename, "up")
+            else:
+                self.log_error('vrf %s not around, skipping vrf config'
+                               %(vrfname), ifaceobj)
+        except Exception, e:
+            self.log_error('%s: %s' %(ifacename, str(e)), ifaceobj)
+
+    def _del_vrf_rules(self, vrf_dev_name, vrf_table):
+        pref = 200
+        ip_rule_out_format = '%s: from all %s %s lookup %s'
+        ip_rule_cmd = '%s %s rule del pref %s %s %s table %s'
+
+        rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
+        if rule in self.ip_rule_cache:
+            rule_cmd = ip_rule_cmd %(utils.ip_cmd,
+                                     '', pref, 'oif', vrf_dev_name,
+                                     vrf_dev_name)
+            utils.exec_command(rule_cmd)
+
+        rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
+        if rule in self.ip_rule_cache:
+            rule_cmd = ip_rule_cmd %(utils.ip_cmd,
+                                     '', pref, 'iif', vrf_dev_name,
+                                     vrf_dev_name)
+            utils.exec_command(rule_cmd)
+
+        rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
+        if rule in self.ip6_rule_cache:
+            rule_cmd = ip_rule_cmd %(utils.ip_cmd,
+                                     '-6', pref, 'oif', vrf_dev_name,
+                                     vrf_dev_name)
+            utils.exec_command(rule_cmd)
+
+        rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
+        if rule in self.ip6_rule_cache:
+            rule_cmd = ip_rule_cmd %(utils.ip_cmd,
+                                     '-6', pref, 'iif', vrf_dev_name,
+                                     vrf_dev_name)
+            utils.exec_command(rule_cmd)
+
+    def _l3mdev_rule(self, ip_rules):
+        for rule in ip_rules:
+            if not re.search(r"\d.*from\s+all\s+lookup\s+\W?l3mdev-table\W?",
+                             rule):
+                continue
+            return True
+        return False
+
+    def _rule_cache_fill(self):
+        ip_rules = utils.exec_command('%s rule show'
+                                      %utils.ip_cmd).splitlines()
+        self.ip_rule_cache = [' '.join(r.split()) for r in ip_rules]
+        self.l3mdev4_rule = self._l3mdev_rule(self.ip_rule_cache)
+        ip_rules = utils.exec_command('%s -6 rule show'
+                                      %utils.ip_cmd).splitlines()
+        self.ip6_rule_cache = [' '.join(r.split()) for r in ip_rules]
+        self.l3mdev6_rule = self._l3mdev_rule(self.ip6_rule_cache)
+
+    def _add_vrf_rules(self, vrf_dev_name, vrf_table):
+        pref = 200
+        ip_rule_out_format = '%s: from all %s %s lookup %s'
+        ip_rule_cmd = '%s %s rule add pref %s %s %s table %s'
+        if self.vrf_fix_local_table:
+            self.vrf_fix_local_table = False
+            rule = '0: from all lookup local'
+            if rule in self.ip_rule_cache:
+                try:
+                    utils.exec_command('%s rule del pref 0'
+                                       %utils.ip_cmd)
+                    utils.exec_command('%s rule add pref 32765 table local'
+                                       %utils.ip_cmd)
+                except Exception, e:
+                    self.logger.info('%s: %s' % (vrf_dev_name, str(e)))
+                    pass
+            if rule in self.ip6_rule_cache:
+                try:
+                    utils.exec_command('%s -6 rule del pref 0'
+                                       %utils.ip_cmd)
+                    utils.exec_command('%s -6 rule add pref 32765 table local'
+                                       %utils.ip_cmd)
+                except Exception, e:
+                    self.logger.info('%s: %s' % (vrf_dev_name, str(e)))
+                    pass
+
+        if not self.l3mdev_checked:
+            self._rule_cache_fill()
+            self.l3mdev_checked = True
+        #Example ip rule
+        #200: from all oif blue lookup blue
+        #200: from all iif blue lookup blue
+
+        rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
+        if not self.l3mdev4_rule and rule not in self.ip_rule_cache:
+            rule_cmd = ip_rule_cmd %(utils.ip_cmd,
+                                     '', pref, 'oif', vrf_dev_name,
+                                     vrf_dev_name)
+            utils.exec_command(rule_cmd)
+
+        rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
+        if not self.l3mdev4_rule and rule not in self.ip_rule_cache:
+            rule_cmd = ip_rule_cmd %(utils.ip_cmd,
+                                     '', pref, 'iif', vrf_dev_name,
+                                     vrf_dev_name)
+            utils.exec_command(rule_cmd)
+
+        rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
+        if not self.l3mdev6_rule and rule not in self.ip6_rule_cache:
+            rule_cmd = ip_rule_cmd %(utils.ip_cmd,
+                                     '-6', pref, 'oif', vrf_dev_name,
+                                     vrf_dev_name)
+            utils.exec_command(rule_cmd)
+
+        rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
+        if not self.l3mdev6_rule and rule not in self.ip6_rule_cache:
+            rule_cmd = ip_rule_cmd %(utils.ip_cmd,
+                                     '-6', pref, 'iif', vrf_dev_name,
+                                     vrf_dev_name)
+            utils.exec_command(rule_cmd)
+
+    def _is_address_virtual_slaves(self, vrfobj, config_vrfslaves,
+                                   vrfslave):
+        # Address virtual lines on a vrf slave will create
+        # macvlan devices on the vrf slave and enslave them
+        # to the vrf master. This function checks if the
+        # vrf slave is such a macvlan interface.
+        # XXX: additional possible checks that can be done here
+        # are:
+        #  - check if it is also a macvlan device of the
+        #    format <vrf_slave>-v<int> created by the
+        #    address virtual module
+        vrfslave_lowers = self.ipcmd.link_get_lowers(vrfslave)
+        if vrfslave_lowers:
+            if vrfslave_lowers[0] in config_vrfslaves:
+                return True
+        return False
+
+    def _add_vrf_slaves(self, ifaceobj, ifaceobj_getfunc=None):
+        running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
+        config_slaves = ifaceobj.lowerifaces
+        if not config_slaves and not running_slaves:
+            return
+
+        if not config_slaves: config_slaves = []
+        if not running_slaves: running_slaves = []
+        add_slaves = set(config_slaves).difference(set(running_slaves))
+        del_slaves = set(running_slaves).difference(set(config_slaves))
+        if add_slaves:
+            for s in add_slaves:
+                try:
+                    if not self.ipcmd.link_exists(s):
+                        continue
+                    sobj = None
+                    if ifaceobj_getfunc:
+                        sobj = ifaceobj_getfunc(s)
+                    self._up_vrf_slave(s, ifaceobj.name,
+                                       sobj[0] if sobj else None,
+                                       ifaceobj_getfunc, True)
+                except Exception, e:
+                    self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
+
+        if del_slaves:
+            for s in del_slaves:
+                try:
+                    if self._is_address_virtual_slaves(ifaceobj,
+                                                       config_slaves, s):
+                        continue
+                    sobj = None
+                    if ifaceobj_getfunc:
+                        sobj = ifaceobj_getfunc(s)
+                    self._down_vrf_slave(s, sobj[0] if sobj else None,
+                                         ifaceobj.name)
+                except Exception, e:
+                    self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
+
+        if ifaceobj.link_type == ifaceLinkType.LINK_MASTER:
+            for s in config_slaves:
+                try:
+                    netlink.link_set_updown(s, "up")
+                except Exception, e:
+                    self.logger.debug('%s: %s' % (ifaceobj.name, str(e)))
+                    pass
+
+    def _set_vrf_dev_processed_flag(self, ifaceobj):
+        ifaceobj.module_flags[self.name] = \
+                             ifaceobj.module_flags.setdefault(self.name, 0) | \
+                                        vrfPrivFlags.PROCESSED
+
+    def _check_vrf_dev_processed_flag(self, ifaceobj):
+        if (ifaceobj.module_flags.get(self.name, 0) & vrfPrivFlags.PROCESSED):
+            return True
+        return False
+
+    def _create_vrf_dev(self, ifaceobj, vrf_table):
+        if not self.ipcmd.link_exists(ifaceobj.name):
+            self._check_vrf_system_reserved_names(ifaceobj)
+
+            if self.vrf_count == self.vrf_max_count:
+                self.log_error('max vrf count %d hit...not '
+                               'creating vrf' % self.vrf_count, ifaceobj)
+            if vrf_table == 'auto':
+                vrf_table = self._get_avail_vrf_table_id()
+                if not vrf_table:
+                    self.log_error('unable to get an auto table id', ifaceobj)
+                self.logger.info('%s: table id auto: selected table id %s\n'
+                                 %(ifaceobj.name, vrf_table))
+            else:
+                self._iproute2_is_vrf_tableid_inuse(ifaceobj, vrf_table)
+                if ifaceobj.name in self.system_reserved_rt_tables.keys():
+                    self.log_error('cannot use system reserved %s table ids'
+                                  %(str(self.system_reserved_rt_tables.keys())),
+                                  ifaceobj)
+
+            if not vrf_table.isdigit():
+                self.log_error('vrf-table must be an integer or \'auto\'', ifaceobj)
+
+            # XXX: If we decide to not allow vrf id usages out of
+            # the reserved ifupdown range, then uncomment this code.
+            else:
+                if (int(vrf_table) < self.vrf_table_id_start or
+                    int(vrf_table) > self.vrf_table_id_end):
+                    self.log_error('vrf table id %s out of reserved range [%d,%d]'
+                                   %(vrf_table,
+                                     self.vrf_table_id_start,
+                                     self.vrf_table_id_end), ifaceobj)
+            try:
+                self.ipcmd.link_create(ifaceobj.name, 'vrf',
+                                       {'table' : '%s' %vrf_table})
+            except Exception, e:
+                self.log_error('create failed (%s)\n' % str(e), ifaceobj)
+            if vrf_table != 'auto':
+                self._iproute2_vrf_table_entry_add(ifaceobj, vrf_table)
+        else:
+            if vrf_table == 'auto':
+                vrf_table = self._get_iproute2_vrf_table(ifaceobj.name)
+                if not vrf_table and not ifupdownflags.flags.DRYRUN:
+                    self.log_error('unable to get vrf table id', ifaceobj)
+
+            # if the device exists, check if table id is same
+            vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
+            if vrfdev_attrs:
+                running_table = vrfdev_attrs.get('table', None)
+                if vrf_table != running_table:
+                    self.log_error('cannot change vrf table id,running table id'
+                                   ' %s is different from config id %s'
+                                   % (running_table, vrf_table), ifaceobj)
+        return vrf_table
+
+    def _up_vrf_helper(self, ifaceobj, vrf_table):
+        mode = ""
+        if ifupdownflags.flags.PERFMODE:
+            mode = "boot"
+        if self.vrf_helper:
+            utils.exec_command('%s create %s %s %s' %
+                               (self.vrf_helper,
+                                ifaceobj.name,
+                                vrf_table,
+                                mode))
+
+    def _up_vrf_dev(self, ifaceobj, vrf_table, add_slaves=True,
+                    ifaceobj_getfunc=None):
+
+        # if vrf dev is already processed return. This can happen
+        # if we the slave was configured before.
+        # see self._up_vrf_slave_without_master
+        if self._check_vrf_dev_processed_flag(ifaceobj):
+            return True
+
+        try:
+            vrf_table = self._create_vrf_dev(ifaceobj, vrf_table)
+        except Exception, e:
+            self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
+
+        try:
+            self._add_vrf_rules(ifaceobj.name, vrf_table)
+            self._up_vrf_helper(ifaceobj, vrf_table)
+            if add_slaves:
+                self._add_vrf_slaves(ifaceobj, ifaceobj_getfunc)
+            self._set_vrf_dev_processed_flag(ifaceobj)
+            netlink.link_set_updown(ifaceobj.name, "up")
+        except Exception, e:
+            self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
+
+    def _kill_ssh_connections(self, ifacename):
+        try:
+            runningaddrsdict = self.ipcmd.get_running_addrs(None, ifacename)
+            if not runningaddrsdict:
+                return
+            iplist = [i.split('/', 1)[0] for i in runningaddrsdict.keys()]
+            if not iplist:
+                return
+            proc=[]
+            #Example output:
+            #ESTAB      0      0      10.0.1.84:ssh       10.0.1.228:45186
+            #users:(("sshd",pid=2528,fd=3))
+            cmdl = [utils.ss_cmd, '-t', '-p']
+            for line in utils.exec_commandl(cmdl).splitlines():
+                citems = line.split()
+                addr = None
+                if '%' in citems[3]:
+                    addr = citems[3].split('%')[0]
+                elif ':ssh' in citems[3]:
+                    addr = citems[3].split(':')[0]
+                if not addr:
+                    continue
+                if addr in iplist:
+                    if len(citems) == 6:
+                        proc.append(citems[5].split(',')[1].split('=')[1])
+
+            if not proc:
+                return
+            pid = None
+            # outpt of '/usr/bin/pstree -Aps <pid>':
+            # 'systemd(1)---sshd(990)---sshd(16112)---sshd(16126)---bash(16127)---sudo(16756)---ifreload(16761)---pstree(16842)\n'
+            # get the above output to following format
+            # ['systemd(1)', 'sshd(990)', 'sshd(16112)', 'sshd(16126)', 'bash(16127)', 'sudo(16756)', 'ifreload(16761)', 'pstree(16850)']
+            pstree = list(reversed(utils.exec_command('%s -Aps %s' %
+                                                       (utils.pstree_cmd, os.getpid())).strip().split('---')))
+            for index, process in enumerate(pstree):
+                # check the parent of SSH process to make sure
+                # we don't kill SSH server or systemd process
+                if 'sshd' in process and 'sshd' in pstree[index + 1]:
+                    pid = filter(lambda x: x.isdigit(), process)
+                    break
+            self.logger.info("%s: killing active ssh sessions: %s"
+                             %(ifacename, str(proc)))
+
+            if ifupdownflags.flags.DRYRUN:
+                return
+            for id in proc:
+                if id != pid:
+                    try:
+                        os.kill(int(id), signal.SIGINT)
+                    except OSError as e:
+                        continue
+
+            # Kill current SSH client
+            if pid in proc:
+                try:
+                    forkret = os.fork()
+                except OSError, e:
+                    self.logger.info("fork error : %s [%d]" % (e.strerror, e.errno))
+                if (forkret == 0):  # The first child.
+                    try:
+                        os.setsid()
+                        self.logger.info("%s: ifreload continuing in the background" %ifacename)
+                    except OSError, (err_no, err_message):
+                        self.logger.info("os.setsid failed: errno=%d: %s" % (err_no, err_message))
+                        self.logger.info("pid=%d  pgid=%d" % (os.getpid(), os.getpgid(0)))
+                try:
+                    self.logger.info("%s: killing our session: %s"
+                                     %(ifacename, str(proc)))
+                    os.kill(int(pid), signal.SIGINT)
+                    return
+                except OSError as e:
+                    return
+        except Exception, e:
+            self.logger.info('%s: %s' %(ifacename, str(e)))
+
+    def _up(self, ifaceobj, ifaceobj_getfunc=None):
+        try:
+            vrf_table = ifaceobj.get_attr_value_first('vrf-table')
+            if vrf_table:
+                self._iproute2_vrf_map_initialize()
+                # This is a vrf device
+                self._up_vrf_dev(ifaceobj, vrf_table, True, ifaceobj_getfunc)
+            else:
+                vrf = ifaceobj.get_attr_value_first('vrf')
+                if vrf:
+                    self._iproute2_vrf_map_initialize()
+                    # This is a vrf slave
+                    self._up_vrf_slave(ifaceobj.name, vrf, ifaceobj,
+                                       ifaceobj_getfunc)
+                elif not ifupdownflags.flags.PERFMODE:
+                    # check if we were a slave before
+                    master = self.ipcmd.link_get_master(ifaceobj.name)
+                    if master:
+                        self._iproute2_vrf_map_initialize()
+                        if self._is_vrf_dev(master):
+                            self._down_vrf_slave(ifaceobj.name, ifaceobj,
+                                                 master)
+        except Exception, e:
+            self.log_error(str(e), ifaceobj)
+
+    def _down_vrf_helper(self, ifaceobj, vrf_table):
+        mode = ""
+        if ifupdownflags.flags.PERFMODE:
+            mode = "boot"
+        if self.vrf_helper:
+            utils.exec_command('%s delete %s %s %s' %
+                               (self.vrf_helper,
+                                ifaceobj.name,
+                                vrf_table,
+                                mode))
+
+    def _close_sockets(self, ifaceobj, ifindex):
+        if not self.vrf_close_socks_on_down:
+            return
+
+        try:
+            utils.exec_command('%s -aK \"dev == %s\"'
+                               %(utils.ss_cmd, ifindex))
+        except Exception, e:
+            self.logger.info('%s: closing socks using ss'
+                             ' failed (%s)\n' %(ifaceobj.name, str(e)))
+            pass
+
+    def _down_vrf_dev(self, ifaceobj, vrf_table, ifaceobj_getfunc=None):
+
+        if not self.ipcmd.link_exists(ifaceobj.name):
+            return
+
+        if vrf_table == 'auto':
+            vrf_table = self._get_iproute2_vrf_table(ifaceobj.name)
+
+        running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
+        if running_slaves:
+            for s in running_slaves:
+                if ifaceobj_getfunc:
+                    sobj = ifaceobj_getfunc(s)
+                    try:
+                        self._handle_existing_connections(sobj[0]
+                                                          if sobj else None,
+                                                          ifaceobj.name)
+                    except Exception, e:
+                        self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
+                        pass
+                try:
+                    self.ipcmd.addr_flush(s)
+                    netlink.link_set_updown(s, "down")
+                except Exception, e:
+                    self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
+                    pass
+
+        try:
+            self._down_vrf_helper(ifaceobj, vrf_table)
+        except Exception, e:
+            self.logger.warn('%s: %s' %(ifaceobj.name, str(e)))
+            pass
+
+        try:
+            self._del_vrf_rules(ifaceobj.name, vrf_table)
+        except Exception, e:
+            self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
+            pass
+
+        ifindex = self.ipcmd.link_get_ifindex(ifaceobj.name)
+
+        if ifindex:
+            try:
+                self.ipcmd.link_delete(ifaceobj.name)
+            except Exception, e:
+                self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
+                pass
+
+        self._close_sockets(ifaceobj, ifindex)
+
+        try:
+            self._iproute2_vrf_table_entry_del(vrf_table)
+        except Exception, e:
+            self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
+            pass
+
+
+    def _down_vrf_slave(self, ifacename, ifaceobj=None, vrfname=None):
+        try:
+            self._handle_existing_connections(ifaceobj, vrfname)
+            self.ipcmd.link_set(ifacename, 'nomaster')
+            # Down this slave only if it is a slave ifupdown2 manages.
+            # we dont want to down slaves that maybe up'ed by
+            # somebody else. One such example is a macvlan device
+            # which ifupdown2 addressvirtual addon module auto creates
+            if ifaceobj:
+                netlink.link_set_updown(ifacename, "down")
+        except Exception, e:
+            self.logger.warn('%s: %s' %(ifacename, str(e)))
+
+    def _down(self, ifaceobj, ifaceobj_getfunc=None):
+        try:
+            vrf_table = ifaceobj.get_attr_value_first('vrf-table')
+            if vrf_table:
+                self._iproute2_vrf_map_initialize()
+                self._down_vrf_dev(ifaceobj, vrf_table, ifaceobj_getfunc)
+            else:
+                vrf = ifaceobj.get_attr_value_first('vrf')
+                if vrf:
+                    self._iproute2_vrf_map_initialize()
+                    self._down_vrf_slave(ifaceobj.name, ifaceobj, None)
+        except Exception, e:
+            self.log_warn(str(e))
+
+    def _query_check_vrf_slave(self, ifaceobj, ifaceobjcurr, vrf):
+        try:
+            master = self.ipcmd.link_get_master(ifaceobj.name)
+            if not master or master != vrf:
+                ifaceobjcurr.update_config_with_status('vrf', str(master), 1)
+            else:
+                ifaceobjcurr.update_config_with_status('vrf', master, 0)
+        except Exception, e:
+            self.log_error(str(e), ifaceobjcurr)
+
+    def _query_check_vrf_dev(self, ifaceobj, ifaceobjcurr, vrf_table):
+        try:
+            if not self.ipcmd.link_exists(ifaceobj.name):
+                self.logger.info('%s: vrf: does not exist' %(ifaceobj.name))
+                return
+            if vrf_table == 'auto':
+                config_table = self._get_iproute2_vrf_table(ifaceobj.name)
+            else:
+                config_table = vrf_table
+            vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
+            if not vrfdev_attrs:
+                ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
+                return
+            running_table = vrfdev_attrs.get('table')
+            if not running_table:
+                ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
+                return
+            if config_table != running_table:
+                ifaceobjcurr.update_config_with_status('vrf-table',
+                                                       running_table, 1)
+            else:
+                ifaceobjcurr.update_config_with_status('vrf-table',
+                                                       running_table, 0)
+            if not ifupdownflags.flags.WITHDEFAULTS:
+                return
+            if self.vrf_helper:
+                try:
+                    utils.exec_command('%s verify %s %s'
+                                      %(self.vrf_helper,
+                                      ifaceobj.name, config_table))
+                    ifaceobjcurr.update_config_with_status('vrf-helper',
+                                                           '%s create %s %s'
+                                                           %(self.vrf_helper,
+                                                           ifaceobj.name,
+                                                           config_table), 0)
+                except Exception, e:
+                    ifaceobjcurr.update_config_with_status('vrf-helper',
+                                                           '%s create %s %s'
+                                                           %(self.vrf_helper,
+                                                           ifaceobj.name,
+                                                           config_table), 1)
+                    pass
+        except Exception, e:
+            self.log_warn(str(e))
+
+    def _query_check(self, ifaceobj, ifaceobjcurr):
+        try:
+            vrf_table = ifaceobj.get_attr_value_first('vrf-table')
+            if vrf_table:
+                self._iproute2_vrf_map_initialize(writetodisk=False)
+                self._query_check_vrf_dev(ifaceobj, ifaceobjcurr, vrf_table)
+            else:
+                vrf = ifaceobj.get_attr_value_first('vrf')
+                if vrf:
+                    self._iproute2_vrf_map_initialize(writetodisk=False)
+                    self._query_check_vrf_slave(ifaceobj, ifaceobjcurr, vrf)
+        except Exception, e:
+            self.log_warn(str(e))
+
+    def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
+        try:
+            kind = self.ipcmd.link_get_kind(ifaceobjrunning.name)
+            if kind == 'vrf':
+                vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobjrunning.name)
+                if vrfdev_attrs:
+                    running_table = vrfdev_attrs.get('table')
+                    if running_table:
+                        ifaceobjrunning.update_config('vrf-table',
+                                                      running_table)
+                        return
+            slave_kind = self.ipcmd.link_get_slave_kind(ifaceobjrunning.name)
+            if slave_kind == 'vrf_slave':
+                vrf = self.ipcmd.link_get_master(ifaceobjrunning.name)
+                if vrf:
+                    ifaceobjrunning.update_config('vrf', vrf)
+        except Exception, e:
+            self.log_warn(str(e))
+
+    def _query(self, ifaceobj, **kwargs):
+        if not self.vrf_helper:
+            return
+        if (ifaceobj.link_kind & ifaceLinkKind.VRF):
+            ifaceobj.update_config('vrf-helper', '%s %s' %(self.vrf_helper,
+                                   ifaceobj.name))
+
+    _run_ops = {'pre-up' : _up,
+               'post-down' : _down,
+               'query-running' : _query_running,
+               'query-checkcurr' : _query_check,
+               'query' : _query}
+
+    def get_ops(self):
+        """ returns list of ops supported by this module """
+        return self._run_ops.keys()
+
+    def _init_command_handlers(self):
+        if not self.ipcmd:
+            self.ipcmd = self.bondcmd = LinkUtils()
+        if not self.dhclientcmd:
+            self.dhclientcmd = dhclient()
+
+    def run(self, ifaceobj, operation, query_ifaceobj=None,
+            ifaceobj_getfunc=None, **extra_args):
+        """ run bond configuration on the interface object passed as argument
+
+        Args:
+            **ifaceobj** (object): iface object
+
+            **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
+                'query-running'
+
+        Kwargs:
+            **query_ifaceobj** (object): query check ifaceobject. This is only
+                valid when op is 'query-checkcurr'. It is an object same as
+                ifaceobj, but contains running attribute values and its config
+                status. The modules can use it to return queried running state
+                of interfaces. status is success if the running state is same
+                as user required state in ifaceobj. error otherwise.
+        """
+        op_handler = self._run_ops.get(operation)
+        if not op_handler:
+            return
+        self._init_command_handlers()
+        if operation == 'query-checkcurr':
+            op_handler(self, ifaceobj, query_ifaceobj)
+        else:
+            op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)
diff --git a/ifupdown2/addons/vrrpd.py b/ifupdown2/addons/vrrpd.py
new file mode 100644 (file)
index 0000000..77ada84
--- /dev/null
@@ -0,0 +1,178 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+import os
+import re
+import glob
+import signal
+
+try:
+    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
+
+    from ifupdown2.ifupdownaddons.modulebase import moduleBase
+
+    from ifupdown2.ifupdown.iface import *
+    from ifupdown2.ifupdown.utils import utils
+except ImportError:
+    import ifupdown.ifupdownflags as ifupdownflags
+
+    from ifupdownaddons.modulebase import moduleBase
+
+    from ifupdown.iface import *
+    from ifupdown.utils import utils
+
+
+class vrrpd(moduleBase):
+    """  ifupdown2 addon module to configure vrrpd attributes """
+
+    _modinfo = {'mhelp' : 'ethtool configuration module for interfaces',
+                'attrs': {
+                      'vrrp-id' :
+                            {'help' : 'vrrp instance id',
+                             'validrange' : ['1', '4096'],
+                             'example' : ['vrrp-id 1']},
+                      'vrrp-priority' :
+                            {'help': 'set vrrp priority',
+                             'validrange' : ['0', '255'],
+                             'example' : ['vrrp-priority 20']},
+                      'vrrp-virtual-ip' :
+                            {'help': 'set vrrp virtual ip',
+                             'validvals' : ['<ipv4>', ],
+                             'example' : ['vrrp-virtual-ip 10.0.1.254']}}}
+
+    def __init__(self, *args, **kargs):
+        moduleBase.__init__(self, *args, **kargs)
+
+    def _check_if_process_is_running(self, cmdname, cmdline):
+        targetpids = []
+        pidstr = ''
+        try:
+            cmdl = [utils.pidof_cmd, cmdname]
+            pidstr = utils.exec_commandl(cmdl, stderr=None).strip('\n')
+        except:
+            pass
+        if not pidstr:
+           return []
+
+        pids = pidstr.split()
+        if not pids:
+           return targetpids
+        for pid in pids:
+            tmpcmdline = cmdline.replace(' ', '')
+            try:
+                pcmdline = self.read_file_oneline('/proc/%s/cmdline' %pid)
+                pcmdline = re.sub(r'\\(.)', r'\1', pcmdline)
+                self.logger.info('(%s)' %(pcmdline))
+                self.logger.info('(%s)' %(tmpcmdline))
+                self.logger.info('(%d) (%d)' %(len(pcmdline), len(tmpcmdline)))
+                if pcmdline and pcmdline == tmpcmdline:
+                   targetpids.append(pid)
+            except:
+                pass
+        return targetpids
+
+    def _up(self, ifaceobj):
+        """ up vrrpd -n -D -i $IFACE -v 1 -p 20 10.0.1.254
+            up ifplugd -i $IFACE -b -f -u0 -d1 -I -p -q """
+
+        if (not ifupdownflags.flags.DRYRUN and
+            not os.path.exists('/sys/class/net/%s' %ifaceobj.name)):
+            return
+
+        cmd = ''
+        attrval = ifaceobj.get_attr_value_first('vrrp-id')
+        if attrval:
+            cmd += ' -v %s' %attrval
+        else:
+            return
+        attrval = ifaceobj.get_attr_value_first('vrrp-priority')
+        if attrval:
+            cmd += ' -p %s' %attrval
+        else:
+            self.logger.warn('%s: incomplete vrrp parameters ' %ifaceobj.name,
+                    '(priority not found)')
+        attrval = ifaceobj.get_attr_value_first('vrrp-virtual-ip')
+        if attrval:
+            cmd += ' %s' %attrval
+        else:
+            self.logger.warn('%s: incomplete vrrp arguments ' %ifaceobj.name,
+                    '(virtual ip not found)')
+            return
+        cmd = ('%s -n -D -i %s %s' %
+               (utils.vrrpd_cmd, ifaceobj.name, cmd))
+        utils.exec_command(cmd)
+
+        cmd = ('%s -i %s -b -f -u0 -d1 -I -p -q' %
+               (utils.ifplugd_cmd, ifaceobj.name))
+        if self._check_if_process_is_running(utils.ifplugd_cmd, cmd):
+           self.logger.info('%s: ifplugd already running' %ifaceobj.name)
+           return
+        utils.exec_command(cmd)
+
+    def _kill_pid_from_file(self, pidfilename):
+        if os.path.exists(pidfilename):
+            pid = self.read_file_oneline(pidfilename)
+            if os.path.exists('/proc/%s' %pid):
+               os.kill(int(pid), signal.SIGTERM)
+
+    def _down(self, ifaceobj):
+        """ down ifplugd -k -i $IFACE
+             down kill $(cat /var/run/vrrpd_$IFACE_*.pid) """
+        attrval = ifaceobj.get_attr_value_first('vrrp-id')
+        if not attrval:
+            return
+        try:
+            utils.exec_command('%s -k -i %s' %
+                               (utils.ifplugd_cmd, ifaceobj.name))
+        except Exception, e:
+            self.logger.debug('%s: ifplugd down error (%s)'
+                              %(ifaceobj.name, str(e)))
+            pass
+
+        for pidfile in glob.glob('/var/run/vrrpd_%s_*.pid' %ifaceobj.name):
+            try:
+                self._kill_pid_from_file(pidfile)
+            except Exception, e:
+                self.logger.debug('%s: vrrpd down error (%s)'
+                                  %(ifaceobj.name, str(e)))
+                pass
+
+    def _query_check(self, ifaceobj, ifaceobjcurr):
+        # XXX
+        return
+
+
+    _run_ops = {'post-up' : _up,
+                'pre-down' : _down}
+
+    def get_ops(self):
+        """ returns list of ops supported by this module """
+        return self._run_ops.keys()
+
+    def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
+        """ run ethtool configuration on the interface object passed as
+            argument
+
+        Args:
+            **ifaceobj** (object): iface object
+
+            **operation** (str): any of 'post-up', 'query-checkcurr',
+                'query-running'
+        Kwargs:
+            **query_ifaceobj** (object): query check ifaceobject. This is only
+                valid when op is 'query-checkcurr'. It is an object same as
+                ifaceobj, but contains running attribute values and its config
+                status. The modules can use it to return queried running state
+                of interfaces. status is success if the running state is same
+                as user required state in ifaceobj. error otherwise.
+        """
+        op_handler = self._run_ops.get(operation)
+        if not op_handler:
+            return
+        if operation == 'query-checkcurr':
+            op_handler(self, ifaceobj, query_ifaceobj)
+        else:
+            op_handler(self, ifaceobj)
diff --git a/ifupdown2/addons/vxlan.py b/ifupdown2/addons/vxlan.py
new file mode 100644 (file)
index 0000000..3148e44
--- /dev/null
@@ -0,0 +1,523 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+
+from sets import Set
+from ipaddr import IPNetwork, IPv4Address, IPv4Network, AddressValueError
+
+try:
+    import ifupdown2.ifupdown.policymanager as policymanager
+
+    from ifupdown2.nlmanager.nlmanager import Link
+
+    from ifupdown2.ifupdown.iface import *
+    from ifupdown2.ifupdown.utils import utils
+    from ifupdown2.ifupdown.netlink import netlink
+    from ifupdown2.ifupdownaddons.cache import *
+    from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdown2.ifupdownaddons.modulebase import moduleBase
+except ImportError:
+    import ifupdown.policymanager as policymanager
+
+    from nlmanager.nlmanager import Link
+
+    from ifupdown.iface import *
+    from ifupdown.utils import utils
+    from ifupdown.netlink import netlink
+
+    from ifupdownaddons.cache import *
+    from ifupdownaddons.LinkUtils import LinkUtils
+    from ifupdownaddons.modulebase import moduleBase
+
+
+class vxlan(moduleBase):
+    _modinfo = {'mhelp' : 'vxlan module configures vxlan interfaces.',
+                'attrs' : {
+                        'vxlan-id' :
+                            {'help' : 'vxlan id',
+                             'validrange' : ['1', '16777214'],
+                             'required' : True,
+                             'example': ['vxlan-id 100']},
+                        'vxlan-local-tunnelip' :
+                            {'help' : 'vxlan local tunnel ip',
+                             'validvals' : ['<ipv4>'],
+                             'example': ['vxlan-local-tunnelip 172.16.20.103']},
+                        'vxlan-svcnodeip' :
+                            {'help' : 'vxlan id',
+                             'validvals' : ['<ipv4>'],
+                             'example': ['vxlan-svcnodeip 172.16.22.125']},
+                        'vxlan-remoteip' :
+                            {'help' : 'vxlan remote ip',
+                             'validvals' : ['<ipv4>'],
+                             'example': ['vxlan-remoteip 172.16.22.127'],
+                             'multiline': True},
+                        'vxlan-learning' :
+                            {'help' : 'vxlan learning yes/no',
+                             'validvals' : ['yes', 'no', 'on', 'off'],
+                             'example': ['vxlan-learning no'],
+                             'default': 'yes'},
+                        'vxlan-ageing' :
+                            {'help' : 'vxlan aging timer',
+                             'validrange' : ['0', '4096'],
+                             'example': ['vxlan-ageing 300'],
+                             'default': '300'},
+                        'vxlan-purge-remotes' :
+                            {'help' : 'vxlan purge existing remote entries',
+                             'validvals' : ['yes', 'no'],
+                             'example': ['vxlan-purge-remotes yes'],},
+                    'vxlan-port': {
+                        'help': 'vxlan UDP port (transmitted to vxlan driver)',
+                        'validvals': ['<number>'],
+                        'example': 'vxlan-port 4789',
+                        'validrange': ['1', '65536'],
+                        'default': '4789',
+                    }
+                }}
+    _clagd_vxlan_anycast_ip = ""
+    _vxlan_local_tunnelip = None
+
+    def __init__(self, *args, **kargs):
+        moduleBase.__init__(self, *args, **kargs)
+        self.ipcmd = None
+        purge_remotes = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vxlan-purge-remotes')
+        if purge_remotes:
+            self._purge_remotes = utils.get_boolean_from_string(purge_remotes)
+        else:
+            self._purge_remotes = False
+
+    def syntax_check(self, ifaceobj, ifaceobj_getfunc):
+        if self._is_vxlan_device(ifaceobj):
+            if not ifaceobj.get_attr_value_first('vxlan-local-tunnelip') and not vxlan._vxlan_local_tunnelip:
+                self.logger.warning('%s: missing vxlan-local-tunnelip' % ifaceobj.name)
+                return False
+            return self.syntax_check_localip_anycastip_equal(
+                ifaceobj.name,
+                ifaceobj.get_attr_value_first('vxlan-local-tunnelip') or vxlan._vxlan_local_tunnelip,
+                vxlan._clagd_vxlan_anycast_ip
+            )
+        return True
+
+    def syntax_check_localip_anycastip_equal(self, ifname, local_ip, anycast_ip):
+        try:
+            if IPNetwork(local_ip) == IPNetwork(anycast_ip):
+                self.logger.warning('%s: vxlan-local-tunnelip and clagd-vxlan-anycast-ip are identical (%s)'
+                                    % (ifname, local_ip))
+                return False
+        except:
+            pass
+        return True
+
+    def get_dependent_ifacenames(self, ifaceobj, ifaceobjs_all=None):
+        if self._is_vxlan_device(ifaceobj):
+            ifaceobj.link_kind |= ifaceLinkKind.VXLAN
+            self._set_global_local_ip(ifaceobj)
+        elif ifaceobj.name == 'lo':
+            clagd_vxlan_list = ifaceobj.get_attr_value('clagd-vxlan-anycast-ip')
+            if clagd_vxlan_list:
+                if len(clagd_vxlan_list) != 1:
+                    self.log_warn('%s: multiple clagd-vxlan-anycast-ip lines, using first one'
+                                  % (ifaceobj.name,))
+                vxlan._clagd_vxlan_anycast_ip = clagd_vxlan_list[0]
+
+            self._set_global_local_ip(ifaceobj)
+        return None
+
+    def _set_global_local_ip(self, ifaceobj):
+        vxlan_local_tunnel_ip = ifaceobj.get_attr_value_first('vxlan-local-tunnelip')
+        if vxlan_local_tunnel_ip and not vxlan._vxlan_local_tunnelip:
+            vxlan._vxlan_local_tunnelip = vxlan_local_tunnel_ip
+
+    def _is_vxlan_device(self, ifaceobj):
+        if ifaceobj.get_attr_value_first('vxlan-id'):
+            return True
+        return False
+
+    def _get_purge_remotes(self, ifaceobj):
+        if not ifaceobj:
+            return self._purge_remotes
+        purge_remotes = ifaceobj.get_attr_value_first('vxlan-purge-remotes')
+        if purge_remotes:
+            purge_remotes = utils.get_boolean_from_string(purge_remotes)
+        else:
+            purge_remotes = self._purge_remotes
+        return purge_remotes
+
+    def should_create_set_vxlan(self, link_exists, ifname, vxlan_id, local, learning, ageing, group):
+        """
+            should we issue a netlink: ip link add dev %ifname type vxlan ...?
+            checking each attribute against the cache
+        """
+        if not link_exists:
+            return True
+
+        try:
+            if ageing:
+                ageing = int(ageing)
+        except:
+            pass
+
+        for attr_list, value in (
+            ((ifname, 'linkinfo', Link.IFLA_VXLAN_ID), vxlan_id),
+            ((ifname, 'linkinfo', Link.IFLA_VXLAN_AGEING), ageing),
+            ((ifname, 'linkinfo', Link.IFLA_VXLAN_LOCAL), local),
+            ((ifname, 'linkinfo', Link.IFLA_VXLAN_LEARNING), learning),
+            ((ifname, 'linkinfo', Link.IFLA_VXLAN_GROUP), group),
+        ):
+            if value and not self.ipcmd.cache_check(attr_list, value):
+                return True
+        return False
+
+    def _vxlan_create(self, ifaceobj):
+        vxlanid = ifaceobj.get_attr_value_first('vxlan-id')
+        if vxlanid:
+            ifname = ifaceobj.name
+            anycastip = self._clagd_vxlan_anycast_ip
+            group = ifaceobj.get_attr_value_first('vxlan-svcnodeip')
+
+            local = ifaceobj.get_attr_value_first('vxlan-local-tunnelip')
+            if not local and vxlan._vxlan_local_tunnelip:
+                local = vxlan._vxlan_local_tunnelip
+
+            self.syntax_check_localip_anycastip_equal(ifname, local, anycastip)
+            # if both local-ip and anycast-ip are identical the function prints a warning
+
+            ageing = ifaceobj.get_attr_value_first('vxlan-ageing')
+            vxlan_port = ifaceobj.get_attr_value_first('vxlan-port')
+            purge_remotes = self._get_purge_remotes(ifaceobj)
+
+            link_exists = self.ipcmd.link_exists(ifname)
+
+            if (not link_exists or
+                not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT):
+                vxlan_learning = ifaceobj.get_attr_value_first('vxlan-learning')
+                if not vxlan_learning:
+                    vxlan_learning = self.get_attr_default_value('vxlan-learning')
+                learning = utils.get_boolean_from_string(vxlan_learning)
+            else:
+                learning = utils.get_boolean_from_string(
+                                self.ipcmd.get_vxlandev_learning(ifname))
+
+            if link_exists:
+                vxlanattrs = self.ipcmd.get_vxlandev_attrs(ifname)
+                # on ifreload do not overwrite anycast_ip to individual ip
+                # if clagd has modified
+                if vxlanattrs:
+                    running_localtunnelip = vxlanattrs.get('local')
+                    if (anycastip and running_localtunnelip and
+                                anycastip == running_localtunnelip):
+                        local = running_localtunnelip
+                    if vxlanattrs.get('vxlanid') != vxlanid:
+                        self.log_error('%s: Cannot change running vxlan id: '
+                                       'Operation not supported' % ifname, ifaceobj)
+            try:
+                vxlanid = int(vxlanid)
+            except:
+                self.log_error('%s: invalid vxlan-id \'%s\'' % (ifname, vxlanid), ifaceobj)
+
+            if not group:
+                group = policymanager.policymanager_api.get_attr_default(
+                    module_name=self.__class__.__name__,
+                    attr='vxlan-svcnodeip'
+                )
+
+            if group:
+                try:
+                    group = IPv4Address(group)
+                except AddressValueError:
+                    try:
+                        group_ip = IPv4Network(group).ip
+                        self.logger.warning('%s: vxlan-svcnodeip %s: netmask ignored' % (ifname, group))
+                        group = group_ip
+                    except:
+                        raise Exception('%s: invalid vxlan-svcnodeip %s: must be in ipv4 format' % (ifname, group))
+
+            if not local:
+                local = policymanager.policymanager_api.get_attr_default(
+                    module_name=self.__class__.__name__,
+                    attr='vxlan-local-tunnelip'
+                )
+
+            if local:
+                try:
+                    local = IPv4Address(local)
+                except AddressValueError:
+                    try:
+                        local_ip = IPv4Network(local).ip
+                        self.logger.warning('%s: vxlan-local-tunnelip %s: netmask ignored' % (ifname, local))
+                        local = local_ip
+                    except:
+                        raise Exception('%s: invalid vxlan-local-tunnelip %s: must be in ipv4 format' % (ifname, local))
+
+            if not ageing:
+                ageing = policymanager.policymanager_api.get_attr_default(
+                    module_name=self.__class__.__name__,
+                    attr='vxlan-ageing'
+                )
+
+                if not ageing and link_exists:
+                    # if link doesn't exist we let the kernel define ageing
+                    ageing = self.get_attr_default_value('vxlan-ageing')
+
+            if not vxlan_port:
+                vxlan_port = policymanager.policymanager_api.get_attr_default(
+                    module_name=self.__class__.__name__,
+                    attr='vxlan-port'
+                )
+
+            try:
+                vxlan_port = int(vxlan_port)
+            except TypeError:
+                # TypeError means vxlan_port was None
+                # ie: not provided by the user or the policy
+                vxlan_port = netlink.VXLAN_UDP_PORT
+            except ValueError as e:
+                self.logger.warning('%s: vxlan-port: using default %s: invalid configured value %s' % (ifname, netlink.VXLAN_UDP_PORT, str(e)))
+                vxlan_port = netlink.VXLAN_UDP_PORT
+
+            if link_exists:
+                cache_port = vxlanattrs.get(Link.IFLA_VXLAN_PORT)
+                if vxlan_port != cache_port:
+                    self.logger.warning('%s: vxlan-port (%s) cannot be changed - to apply the desired change please run: ifdown %s && ifup %s'
+                                        % (ifname, cache_port, ifname, ifname))
+                    vxlan_port = cache_port
+
+            if self.should_create_set_vxlan(link_exists, ifname, vxlanid, local, learning, ageing, group):
+                try:
+                    netlink.link_add_vxlan(ifname, vxlanid,
+                                           local=local,
+                                           learning=learning,
+                                           ageing=ageing,
+                                           group=group,
+                                           dstport=vxlan_port)
+                except Exception as e_netlink:
+                    self.logger.debug('%s: vxlan netlink: %s' % (ifname, str(e_netlink)))
+                    try:
+                        self.ipcmd.link_create_vxlan(ifname, vxlanid,
+                                                     localtunnelip=local,
+                                                     svcnodeip=group,
+                                                     remoteips=ifaceobj.get_attr_value('vxlan-remoteip'),
+                                                     learning='on' if learning else 'off',
+                                                     ageing=ageing)
+                    except Exception as e_iproute2:
+                        self.logger.warning('%s: vxlan add/set failed: %s' % (ifname, str(e_iproute2)))
+                        return
+
+                try:
+                    # manually adding an entry to the caching after creating/updating the vxlan
+                    if not ifname in linkCache.links:
+                        linkCache.links[ifname] = {'linkinfo': {}}
+                    linkCache.links[ifname]['linkinfo'].update({
+                        'learning': learning,
+                        Link.IFLA_VXLAN_LEARNING: learning,
+                        'vxlanid': str(vxlanid),
+                        Link.IFLA_VXLAN_ID: vxlanid
+                    })
+                    if ageing:
+                        linkCache.links[ifname]['linkinfo'].update({
+                            'ageing': ageing,
+                            Link.IFLA_VXLAN_AGEING: int(ageing)
+                        })
+                except:
+                    pass
+            else:
+                self.logger.info('%s: vxlan already exists' % ifname)
+                # if the vxlan already exists it's already cached
+
+            remoteips = ifaceobj.get_attr_value('vxlan-remoteip')
+            if remoteips:
+                try:
+                    for remoteip in remoteips:
+                        IPv4Address(remoteip)
+                except Exception as e:
+                    self.log_error('%s: vxlan-remoteip: %s' %(ifaceobj.name, str(e)))
+
+            if purge_remotes or remoteips:
+                # figure out the diff for remotes and do the bridge fdb updates
+                # only if provisioned by user and not by an vxlan external
+                # controller.
+                peers = self.ipcmd.get_vxlan_peers(ifaceobj.name, group)
+                if local and remoteips and local in remoteips:
+                    remoteips.remove(local)
+                cur_peers = set(peers)
+                if remoteips:
+                    new_peers = set(remoteips)
+                    del_list = cur_peers.difference(new_peers)
+                    add_list = new_peers.difference(cur_peers)
+                else:
+                    del_list = cur_peers
+                    add_list = []
+
+                for addr in del_list:
+                    try:
+                        self.ipcmd.bridge_fdb_del(ifaceobj.name,
+                                                  '00:00:00:00:00:00',
+                                                  None, True, addr)
+                    except:
+                        pass
+
+                for addr in add_list:
+                    try:
+                        self.ipcmd.bridge_fdb_append(ifaceobj.name,
+                                                     '00:00:00:00:00:00',
+                                                     None, True, addr)
+                    except:
+                        pass
+
+    def _up(self, ifaceobj):
+        self._vxlan_create(ifaceobj)
+
+    def _down(self, ifaceobj):
+        try:
+            self.ipcmd.link_delete(ifaceobj.name)
+        except Exception, e:
+            self.log_warn(str(e))
+
+    def _query_check_n_update(self, ifaceobj, ifaceobjcurr, attrname, attrval,
+                              running_attrval):
+        if not ifaceobj.get_attr_value_first(attrname):
+            return
+        if running_attrval and attrval == running_attrval:
+           ifaceobjcurr.update_config_with_status(attrname, attrval, 0)
+        else:
+           ifaceobjcurr.update_config_with_status(attrname, running_attrval, 1)
+
+    def _query_check_n_update_addresses(self, ifaceobjcurr, attrname,
+                                        addresses, running_addresses):
+        if addresses:
+            for a in addresses:
+                if a in running_addresses:
+                    ifaceobjcurr.update_config_with_status(attrname, a, 0)
+                else:
+                    ifaceobjcurr.update_config_with_status(attrname, a, 1)
+            running_addresses = Set(running_addresses).difference(
+                                                    Set(addresses))
+        [ifaceobjcurr.update_config_with_status(attrname, a, 1)
+                    for a in running_addresses]
+
+    def _query_check(self, ifaceobj, ifaceobjcurr):
+        if not self.ipcmd.link_exists(ifaceobj.name):
+           return
+        # Update vxlan object
+        vxlanattrs = self.ipcmd.get_vxlandev_attrs(ifaceobj.name)
+        if not vxlanattrs:
+            ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj,
+                    self.get_mod_attrs(), -1)
+            return
+        self._query_check_n_update(ifaceobj, ifaceobjcurr, 'vxlan-id',
+                       ifaceobj.get_attr_value_first('vxlan-id'),
+                       vxlanattrs.get('vxlanid'))
+
+        self._query_check_n_update(
+            ifaceobj,
+            ifaceobjcurr,
+            'vxlan-port',
+            ifaceobj.get_attr_value_first('vxlan-port'),
+            str(vxlanattrs.get(Link.IFLA_VXLAN_PORT))
+        )
+
+        running_attrval = vxlanattrs.get('local')
+        attrval = ifaceobj.get_attr_value_first('vxlan-local-tunnelip')
+        if not attrval:
+            attrval = vxlan._vxlan_local_tunnelip
+            ifaceobj.update_config('vxlan-local-tunnelip', attrval)
+
+        if running_attrval == self._clagd_vxlan_anycast_ip:
+            # if local ip is anycast_ip, then let query_check to go through
+            attrval = self._clagd_vxlan_anycast_ip
+        self._query_check_n_update(ifaceobj, ifaceobjcurr, 'vxlan-local-tunnelip',
+                                   attrval, running_attrval)
+
+        self._query_check_n_update(ifaceobj, ifaceobjcurr, 'vxlan-svcnodeip',
+                       ifaceobj.get_attr_value_first('vxlan-svcnodeip'),
+                       vxlanattrs.get('svcnode'))
+
+        purge_remotes = self._get_purge_remotes(ifaceobj)
+        if purge_remotes or ifaceobj.get_attr_value('vxlan-remoteip'):
+            # If purge remotes or if vxlan-remoteip's are set
+            # in the config file, we are owners of the installed
+            # remote-ip's, lets check and report any remote ips we don't
+            # understand
+            self._query_check_n_update_addresses(ifaceobjcurr, 'vxlan-remoteip',
+                           ifaceobj.get_attr_value('vxlan-remoteip'),
+                                                 self.ipcmd.get_vxlan_peers(ifaceobj.name, vxlanattrs.get('svcnode')))
+
+        learning = ifaceobj.get_attr_value_first('vxlan-learning')
+        if learning:
+            running_learning = vxlanattrs.get('learning')
+            if learning == 'yes' and running_learning == 'on':
+                running_learning = 'yes'
+            elif learning == 'no' and running_learning == 'off':
+                running_learning = 'no'
+            if learning == running_learning:
+                ifaceobjcurr.update_config_with_status('vxlan-learning',
+                                                        running_learning, 0)
+            else:
+                ifaceobjcurr.update_config_with_status('vxlan-learning',
+                                                        running_learning, 1)
+        ageing = ifaceobj.get_attr_value_first('vxlan-ageing')
+        if not ageing:
+            ageing = self.get_mod_subattr('vxlan-ageing', 'default')
+        self._query_check_n_update(ifaceobj, ifaceobjcurr, 'vxlan-ageing',
+                       ageing, vxlanattrs.get('ageing'))
+
+    def _query_running(self, ifaceobjrunning):
+        vxlanattrs = self.ipcmd.get_vxlandev_attrs(ifaceobjrunning.name)
+        if not vxlanattrs:
+            return
+        attrval = vxlanattrs.get('vxlanid')
+        if attrval:
+            ifaceobjrunning.update_config('vxlan-id', vxlanattrs.get('vxlanid'))
+        else:
+            # if there is no vxlan id, this is not a vxlan port
+            return
+
+        ifaceobjrunning.update_config('vxlan-port', vxlanattrs.get(Link.IFLA_VXLAN_PORT))
+
+        attrval = vxlanattrs.get('local')
+        if attrval:
+            ifaceobjrunning.update_config('vxlan-local-tunnelip', attrval)
+        attrval = vxlanattrs.get('svcnode')
+        if attrval:
+            ifaceobjrunning.update_config('vxlan-svcnode', attrval)
+        purge_remotes = self._get_purge_remotes(None)
+        if purge_remotes:
+            # if purge_remotes is on, it means we own the
+            # remote ips. Query them and add it to the running config
+            attrval = self.ipcmd.get_vxlan_peers(ifaceobjrunning.name, vxlanattrs.get('svcnode'))
+            if attrval:
+                [ifaceobjrunning.update_config('vxlan-remoteip', a)
+                            for a in attrval]
+        attrval = vxlanattrs.get('learning')
+        if attrval and attrval == 'on':
+            ifaceobjrunning.update_config('vxlan-learning', 'on')
+        attrval = vxlanattrs.get('ageing')
+        if attrval:
+            ifaceobjrunning.update_config('vxlan-ageing', vxlanattrs.get('ageing'))
+
+    _run_ops = {'pre-up' : _up,
+               'post-down' : _down,
+               'query-checkcurr' : _query_check,
+               'query-running' : _query_running}
+
+    def get_ops(self):
+        return self._run_ops.keys()
+
+    def _init_command_handlers(self):
+        if not self.ipcmd:
+            self.ipcmd = LinkUtils()
+
+    def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
+        op_handler = self._run_ops.get(operation)
+        if not op_handler:
+            return
+        if (operation != 'query-running' and
+                not self._is_vxlan_device(ifaceobj)):
+            return
+        self._init_command_handlers()
+        if operation == 'query-checkcurr':
+            op_handler(self, ifaceobj, query_ifaceobj)
+        else:
+            op_handler(self, ifaceobj)
diff --git a/ifupdown2/completion/ifup b/ifupdown2/completion/ifup
new file mode 100644 (file)
index 0000000..0be547f
--- /dev/null
@@ -0,0 +1,9 @@
+
+_python_argcomplete() {
+    local IFS='\v'
+    COMPREPLY=( $(IFS="$IFS" COMP_LINE="$COMP_LINE" COMP_POINT="$COMP_POINT"                   _ARGCOMPLETE_COMP_WORDBREAKS="$COMP_WORDBREAKS"                   _ARGCOMPLETE=1  "$1" 8>&1 9>&2 1>/dev/null 2>/dev/null) )
+    if [[ $? != 0 ]]; then
+        unset COMPREPLY
+    fi
+}
+complete -F _python_argcomplete ifup ifdown ifquery ifreload
diff --git a/ifupdown2/ifupdown/__init__.py b/ifupdown2/ifupdown/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/ifupdown2/ifupdown/argv.py b/ifupdown2/ifupdown/argv.py
new file mode 100644 (file)
index 0000000..5c44267
--- /dev/null
@@ -0,0 +1,231 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Authors:
+#           Roopa Prabhu, roopa@cumulusnetworks.com
+#           Julien Fortin, julien@cumulusnetworks.com
+#
+
+import sys
+import argparse
+import argcomplete
+
+try:
+    from ifupdown2.ifupdown.utils import utils
+    from ifupdown2.ifupdown.exceptions import ArgvParseError
+except:
+    from ifupdown.utils import utils
+    from ifupdown.exceptions import ArgvParseError
+
+
+class VersionAction(argparse.Action):
+    def __call__(self, parser, namespace, values, option_string=None):
+
+        try:
+            dpkg = utils.exec_commandl([utils.dpkg_cmd, '-l', 'ifupdown2'])
+
+            if not dpkg:
+                raise Exception('dpkg -l ifupdown2 returns without output')
+
+            dpkg = dpkg.split('\n')
+
+            if not dpkg:
+                raise Exception('dpkg -l ifupdown2 returns without output')
+
+            for line in dpkg:
+                if 'ifupdown2' in line:
+                    info = line.split()
+
+                    sys.stdout.write('ifupdown2:%s\n' % (info[2]))
+                    sys.exit(0)
+
+            raise Exception('ifupdown2 package not found using dpkg -l')
+
+        except Exception as e:
+            sys.stderr.write('error: cannot get current version using dpkg: %s\n' % str(e))
+            sys.exit(1)
+
+
+class Parse:
+    valid_ops = {
+        'ifup': 'up',
+        'ifdown': 'down',
+        'ifreload': 'reload',
+        'ifquery': 'query'
+    }
+
+    def __init__(self, argv):
+        self.executable_name = argv[0]
+        self.op = self.get_op()
+        self.argv = argv[1:]
+
+        if self.op == 'query':
+            descr = 'query interfaces (all or interface list)'
+        elif self.op == 'reload':
+            descr = 'reload interface configuration.'
+        else:
+            descr = 'interface management'
+        argparser = argparse.ArgumentParser(description=descr)
+        if self.op == 'reload':
+            self.update_ifreload_argparser(argparser)
+        else:
+            self.update_argparser(argparser)
+            if self.op == 'up':
+                self.update_ifup_argparser(argparser)
+            elif self.op == 'down':
+                self.update_ifdown_argparser(argparser)
+            elif self.op == 'query':
+                self.update_ifquery_argparser(argparser)
+        self.update_common_argparser(argparser)
+        argcomplete.autocomplete(argparser)
+        self.args = argparser.parse_args(self.argv)
+
+    def validate(self):
+        if self.op == 'query' and (self.args.syntaxhelp or self.args.list):
+            return True
+
+        if self.op == 'reload':
+            if not self.args.all and not self.args.currentlyup and not self.args.CLASS:
+                raise ArgvParseError("'-a' or '-c' or '-allow' option is required")
+        elif not self.args.iflist and not self.args.all and not self.args.CLASS:
+            raise ArgvParseError("'-a' option or interface list are required")
+
+        if self.args.iflist and self.args.all:
+            raise ArgvParseError("'-a' option and interface list are mutually exclusive")
+
+        if self.op != 'reload' and self.args.CLASS and self.args.all:
+            raise ArgvParseError("'--allow' option is mutually exclusive with '-a'")
+        return True
+
+    def get_op(self):
+        try:
+            for key, value in self.valid_ops.iteritems():
+                if self.executable_name.endswith(key):
+                    return value
+        except:
+            raise ArgvParseError("Unexpected executable. Should be 'ifup' or 'ifdown' or 'ifquery'")
+
+    def get_args(self):
+        return self.args
+
+    def update_argparser(self, argparser):
+        """ base parser, common to all commands """
+        argparser.add_argument('-a', '--all', action='store_true', required=False,
+                               help='process all interfaces marked "auto"')
+        argparser.add_argument('iflist', metavar='IFACE', nargs='*',
+                               help='interface list separated by spaces. '
+                                    'IFACE list is mutually exclusive with -a option.')
+        argparser.add_argument('-v', '--verbose', dest='verbose', action='store_true', help='verbose')
+        argparser.add_argument('-d', '--debug', dest='debug', action='store_true', help='output debug info')
+        argparser.add_argument('-q', '--quiet', dest='quiet', action='store_true', help=argparse.SUPPRESS)
+        argparser.add_argument('--allow', dest='CLASS', action='append', help='ignore non-"allow-CLASS" interfaces')
+        argparser.add_argument('-w', '--with-depends', dest='withdepends', action='store_true',
+                               help="run with all dependent interfaces. "
+                                    "This option is redundant when '-a' is specified. "
+                                    "With '-a' interfaces are always executed in dependency order")
+        argparser.add_argument('--perfmode', dest='perfmode', action='store_true', help=argparse.SUPPRESS)
+        argparser.add_argument('--nocache', dest='nocache', action='store_true', help=argparse.SUPPRESS)
+        argparser.add_argument('-X', '--exclude', dest='excludepats', action='append',
+                               help='Exclude interfaces from the list of interfaces to operate on. '
+                                    'Can be specified multiple times.')
+        argparser.add_argument('-i', '--interfaces', dest='interfacesfile', default=None,
+                               help='Specify interfaces file instead of file defined in ifupdown2.conf file')
+        argparser.add_argument('-t', '--interfaces-format', dest='interfacesfileformat', default='native',
+                               choices=['native', 'json'], help='interfaces file format')
+        argparser.add_argument('-T', '--type', dest='type', default=None, choices=['iface', 'vlan'],
+                               help='type of interface entry (iface or vlan). '
+                                    'This option can be used in case of ambiguity between '
+                                    'a vlan interface and an iface interface of the same name')
+
+    def update_ifupdown_argparser(self, argparser):
+        """ common arg parser for ifup and ifdown """
+        argparser.add_argument('-f', '--force', dest='force', action='store_true', help='force run all operations')
+        argparser.add_argument('-l', '--syslog', dest='syslog', action='store_true', help=argparse.SUPPRESS)
+        group = argparser.add_mutually_exclusive_group(required=False)
+        group.add_argument('-n', '--no-act', dest='noact', action='store_true',
+                           help="print out what would happen, but don't do it")
+        group.add_argument('-p', '--print-dependency', dest='printdependency',
+                           choices=['list', 'dot'], help='print iface dependency')
+        group.add_argument('--no-scripts', '--admin-state', dest='noaddons', action='store_true',
+                           help='dont run any addon modules/scripts. Only bring the interface administratively up/down')
+
+    def update_ifup_argparser(self, argparser):
+        argparser.add_argument('-s', '--syntax-check', dest='syntaxcheck',
+                               action='store_true', help='Only run the interfaces file parser')
+        argparser.add_argument('-k', '--skip-upperifaces', dest='skipupperifaces', action='store_true',
+                               help='ifup by default tries to add newly created interfaces into its upper/parent '
+                                    'interfaces. Eg. if a bridge port is created as a result of ifup on the port, '
+                                    'ifup automatically adds the port to the bridge. This option can be used to '
+                                    'disable this default behaviour')
+        self.update_ifupdown_argparser(argparser)
+
+    def update_ifdown_argparser(self, argparser):
+        self.update_ifupdown_argparser(argparser)
+        argparser.add_argument('-u', '--use-current-config',
+                               dest='usecurrentconfig', action='store_true',
+                               help='By default ifdown looks at the saved state for interfaces to bring down. '
+                                    'This option allows ifdown to look at the current interfaces file. '
+                                    'Useful when your state file is corrupted or you want down to use '
+                                    'the latest from the interfaces file')
+
+    def update_ifquery_argparser(self, argparser):
+        """ arg parser for ifquery options """
+
+        # -l is same as '-a', only here for backward compatibility
+        argparser.add_argument('-l', '--list', action='store_true', dest='list',
+                               help='list all matching known interfaces')
+        group = argparser.add_mutually_exclusive_group(required=False)
+        group.add_argument('-r', '--running', dest='running', action='store_true',
+                           help='query running state of an interface')
+        group.add_argument('-c', '--check', dest='checkcurr', action='store_true',
+                           help='check interface file contents against running state of an interface')
+        group.add_argument('-x', '--raw', action='store_true', dest='raw', help='print raw config file entries')
+        group.add_argument('--print-savedstate', action='store_true', dest='printsavedstate', help=argparse.SUPPRESS)
+        argparser.add_argument('-o', '--format', dest='format', default='native',
+                               choices=['native', 'json'], help='interface display format')
+        argparser.add_argument('-p', '--print-dependency', dest='printdependency',
+                               choices=['list', 'dot'], help='print interface dependency')
+        argparser.add_argument('-s', '--syntax-help', action='store_true', dest='syntaxhelp',
+                               help='print supported interface config syntax')
+        argparser.add_argument('--with-defaults', action='store_true', dest='withdefaults',
+                               help='check policy default file contents, for unconfigured attributes, '
+                                    'against running state of an interface')
+
+    def update_ifreload_argparser(self, argparser):
+        """ parser for ifreload """
+        group = argparser.add_mutually_exclusive_group(required=True)
+        group.add_argument('-a', '--all', action='store_true', help='process all interfaces marked "auto"')
+        group.add_argument('-c', '--currently-up', dest='currentlyup', action='store_true',
+                           help='Reload the configuration for all interfaces which are '
+                                'currently up regardless of whether an interface has '
+                                '"auto <interface>" configuration within the /etc/network/interfaces file.')
+        group.add_argument('--allow', dest='CLASS', action='append', help='ignore non-"allow-CLASS" interfaces')
+        argparser.add_argument('iflist', metavar='IFACE', nargs='*', help=argparse.SUPPRESS)
+        argparser.add_argument('-n', '--no-act', dest='noact', action='store_true',
+                               help='print out what would happen, but don\'t do it')
+        argparser.add_argument('-v', '--verbose', dest='verbose', action='store_true', help='verbose')
+        argparser.add_argument('-d', '--debug', dest='debug', action='store_true', help='output debug info')
+        argparser.add_argument('-w', '--with-depends', dest='withdepends', action='store_true', help=argparse.SUPPRESS)
+        argparser.add_argument('--perfmode', dest='perfmode', action='store_true', help=argparse.SUPPRESS)
+        argparser.add_argument('--nocache', dest='nocache', action='store_true', help=argparse.SUPPRESS)
+        argparser.add_argument('-X', '--exclude', dest='excludepats', action='append', help=argparse.SUPPRESS)
+        # argparser.add_argument('-j', '--jobs', dest='jobs', type=int,
+        #            default=-1, choices=range(1,12), help=argparse.SUPPRESS)
+        # argparser.add_argument('-i', '--interfaces', dest='interfacesfile',
+        #            default='/etc/network/interfaces',
+        #            help='use interfaces file instead of default ' +
+        #            '/etc/network/interfaces')
+        argparser.add_argument('-u', '--use-current-config', dest='usecurrentconfig', action='store_true',
+                               help='By default ifreload looks at saved state for interfaces to bring down. '
+                                    'With this option ifreload will only look at the current interfaces file. '
+                                    'Useful when your state file is corrupted or you want down to use the latest '
+                                    'from the interfaces file')
+        argparser.add_argument('-l', '--syslog', dest='syslog', action='store_true', help=argparse.SUPPRESS)
+        argparser.add_argument('-f', '--force', dest='force', action='store_true', help='force run all operations')
+        argparser.add_argument('-s', '--syntax-check', dest='syntaxcheck', action='store_true',
+                               help='Only run the interfaces file parser')
+
+    def update_common_argparser(self, argparser):
+        ''' general parsing rules '''
+
+        argparser.add_argument('-V', '--version', action=VersionAction, nargs=0)
diff --git a/ifupdown2/ifupdown/config.py b/ifupdown2/ifupdown/config.py
new file mode 100644 (file)
index 0000000..5e2696f
--- /dev/null
@@ -0,0 +1,57 @@
+#!/usr/bin/python
+#
+# Copyright 2017 Cumulus Networks, Inc. All rights reserved.
+# Authors:
+#           Julien Fortin, julien@cumulusnetworks.com
+#
+# ifupdown2 --
+#    tool to configure network interfaces
+#
+
+import os
+import resource
+
+IFUPDOWN2_ADDON_DROPIN_FOLDER = '/usr/share/ifupdown2/addons'
+
+# ifupdown2/core/config.py -> we need to use dirname twice.
+_ = {
+    IFUPDOWN2_ADDON_DROPIN_FOLDER,
+    '%s/addons' % (os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
+}
+
+try:
+    addon_module_dir_list = list(_)
+    if addon_module_dir_list[0] is not IFUPDOWN2_ADDON_DROPIN_FOLDER:
+        addon_module_dir_list.remove(IFUPDOWN2_ADDON_DROPIN_FOLDER)
+        ADDON_MODULES_DIR = [IFUPDOWN2_ADDON_DROPIN_FOLDER] + addon_module_dir_list
+    else:
+        ADDON_MODULES_DIR = addon_module_dir_list
+except:
+    ADDON_MODULES_DIR = [IFUPDOWN2_ADDON_DROPIN_FOLDER]
+
+__version__ = ''
+
+
+def get_configuration_file_real_path(path_to_file):
+    """
+    When install via pypi or `pip install .` ifupdown2 is install in a virtualenv
+    config file that should be installed in /etc/network/ifupdown2 end-up being
+    installed in /usr/local/lib/python2.7/dist-packages/etc/network/ifupdown2/
+    """
+    if not os.path.exists(path_to_file):
+        # we will try to resolve the location of our conf file
+        # otherwise default to the input argument
+        package_dir = os.path.dirname(os.path.realpath(__file__))
+        parent_dir = os.path.dirname(package_dir)
+        resolved_path = '%s%s' % (parent_dir, path_to_file)
+
+        if os.path.exists(resolved_path):
+            return resolved_path
+
+    return path_to_file
+
+
+IFUPDOWN2_CONF_PATH = get_configuration_file_real_path('/etc/network/ifupdown2/ifupdown2.conf')
+ADDONS_CONF_PATH = get_configuration_file_real_path('/etc/network/ifupdown2/addons.conf')
+
+resource.setrlimit(resource.RLIMIT_CORE, (0, 0))
diff --git a/ifupdown2/ifupdown/exceptions.py b/ifupdown2/ifupdown/exceptions.py
new file mode 100644 (file)
index 0000000..9537f21
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Authors:
+#           Roopa Prabhu, roopa@cumulusnetworks.com
+#           Julien Fortin, julien@cumulusnetworks.com
+#
+# ifupdown --
+#    exceptions
+#
+
+try:
+    from ifupdown2.ifupdown.log import log
+except:
+    from ifupdown.log import log
+
+
+class Error(Exception):
+    """Base class for exceptions in ifupdown"""
+
+    def log_error(self):
+        log.error(self.message)
+
+    def log_warning(self):
+        log.warning(self.message)
+
+    def log_info(self):
+        log.info(self.message)
+
+    def log_debug(self):
+        log.debug(self.message)
+
+
+class ArgvParseError(Error):
+    """
+        Exception coming from argv parsing
+    """
+    pass
+
+
+class ifaceNotFoundError(Error):
+    pass
+
+
+class invalidValueError(Error):
+    pass
+
+
+class errorReadingStateError(Error):
+    pass
+
+
+class moduleNotSupported(Error):
+    pass
+
+
+class ReservedVlanException(Error):
+    pass
diff --git a/ifupdown2/ifupdown/graph.py b/ifupdown2/ifupdown/graph.py
new file mode 100644 (file)
index 0000000..22101ec
--- /dev/null
@@ -0,0 +1,95 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+# graph --
+#    graph helper module for ifupdown
+#
+
+import copy
+import logging
+
+from collections import deque
+
+try:
+    from gvgen import *
+except ImportError, e:
+    pass
+
+
+class graph():
+    """ graph functions to sort and print interface graph """
+
+    logger = logging.getLogger('ifupdown.graph')
+
+    @classmethod
+    def topological_sort_graphs_all(cls, dependency_graphs, indegrees_arg):
+        """ runs topological sort on interface list passed as dependency graph
+
+        Args:
+            **dependency_graphs** (dict): dependency graph with dependency
+                                          lists for interfaces
+
+            **indegrees_arg** (dict): indegrees array for all interfaces
+        """
+        S = []
+        Q = deque()
+
+        indegrees = copy.deepcopy(indegrees_arg)
+        for ifname,indegree in indegrees.items():
+            if indegree == 0:
+                Q.append(ifname)
+
+        while len(Q):
+            # initialize queue
+            x = Q.popleft()
+
+            # Get dependents of x
+            dlist = dependency_graphs.get(x)
+            if not dlist:
+                S.append(x)
+                continue
+
+            for y in dlist:
+                try:
+                    indegrees[y] = indegrees.get(y) - 1
+                except:
+                    cls.logger.debug('topological_sort_graphs_all: did not find %s' %y)
+                    indegrees[y] = 0
+                    pass
+                if indegrees.get(y) == 0:
+                    Q.append(y)
+
+            S.append(x)
+
+        for ifname,indegree in indegrees.items():
+            if indegree != 0:
+                raise Exception('cycle found involving iface %s' %ifname +
+                                ' (indegree %d)' %indegree)
+
+        return S
+
+    @classmethod
+    def generate_dots(cls, dependency_graph, indegrees):
+        """ spits out interface dependency graph in dot format
+
+        Args:
+            **dependency_graphs** (dict): dependency graph with dependency
+                                          lists for interfaces
+
+            **indegrees_arg** (dict): indegrees array for all interfaces
+        """
+
+        gvgraph = GvGen()
+        graphnodes = {}
+        for v in dependency_graph.keys():
+            graphnodes[v] = gvgraph.newItem(v)
+
+        for i, v in graphnodes.items():
+            dlist = dependency_graph.get(i, [])
+            if not dlist:
+                continue
+            for d in dlist:
+                gvgraph.newLink(v, graphnodes.get(d))
+        gvgraph.dot()
diff --git a/ifupdown2/ifupdown/iface.py b/ifupdown2/ifupdown/iface.py
new file mode 100644 (file)
index 0000000..75cc37a
--- /dev/null
@@ -0,0 +1,810 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+# iface --
+#    interface object
+#
+
+"""ifupdown2 network interface object
+
+It is modeled based on the 'iface' section in /etc/network/interfaces
+file. But can be extended to include any other network interface format
+"""
+
+import json
+
+from collections import OrderedDict
+
+
+class ifaceStatusUserStrs():
+    """ This class declares strings user can see during an ifquery --check
+    for example. These strings can be overridden by user defined strings from
+    config file """
+    SUCCESS = "success",
+    FAILURE = "error",
+    UNKNOWN = "unknown"
+
+class ifaceType():
+    UNKNOWN =     0x00
+    IFACE =       0x01
+    BRIDGE_VLAN = 0x10
+
+class ifaceRole():
+    """ ifaceRole is used to classify the ifaceobj.role of
+        MASTER or SLAVE where there is a bond or bridge
+        with bond-slaves or bridge-ports.  A bond in a bridge
+        is both a master and slave (0x3)
+    """
+    UNKNOWN = 0x00
+    SLAVE =   0x01
+    MASTER =  0x10
+
+class ifaceLinkKind():
+    """ ifaceLlinkKind is used to identify interfaces
+        in the ifaceobj.link_kind attribute. Dependents of the bridge or
+        bond have an ifaceobj.role attribute of SLAVE and the bridge or
+        bond itself has ifaceobj.role of MASTER.
+    """
+    UNKNOWN = 0x000000
+    BRIDGE =  0x000001
+    BOND =    0x000010
+    VLAN =    0x000100
+    VXLAN =   0x001000
+    VRF =     0x010000
+    # to indicate logical interface created by an external entity.
+    # the 'kind' of which ifupdown2 does not really understand
+    OTHER =     0x100000
+
+    @classmethod
+    def to_str(cls, kind):
+        if kind == cls.BRIDGE:
+            return "bridge"
+        elif kind == cls.BOND:
+            return "bond"
+        elif kind == cls.VLAN:
+            return "vlan"
+        elif kind == cls.VXLAN:
+            return "vxlan"
+        elif kind == cls.VRF:
+            return "vrf"
+
+class ifaceLinkPrivFlags():
+    """ This corresponds to kernel netdev->priv_flags
+        and can be BRIDGE_PORT, BOND_SLAVE etc """
+    UNKNOWN =           0x00000
+    BRIDGE_PORT =       0x00001
+    BOND_SLAVE =        0x00010
+    VRF_SLAVE =         0x00100
+    BRIDGE_VLAN_AWARE = 0x01000
+    BRIDGE_VXLAN =      0x10000
+    ADDRESS_VIRTUAL_SLAVE = 0x100000
+    LOOPBACK = 0x1000000
+    KEEP_LINK_DOWN = 0x10000000
+
+    @classmethod
+    def get_str(cls, flag):
+        if flag == cls.UNKNOWN:
+            return 'unknown'
+        elif flag == cls.BRIDGE_PORT:
+            return 'bridge port'
+        elif flag == cls.BOND_SLAVE:
+            return 'bond slave'
+        elif flag == cls.VRF_SLAVE:
+            return 'vrf slave'
+        elif flag == cls.BRIDGE_VLAN_AWARE:
+            return 'vlan aware bridge'
+        elif flag == cls.BRIDGE_VXLAN:
+            return 'vxlan bridge'
+
+    @classmethod
+    def get_all_str(cls, flags):
+        str = ''
+        if flags & cls.BRIDGE_PORT:
+            str += 'bridgeport '
+        if flags & cls.BOND_SLAVE:
+            str += 'bondslave '
+        if flags & cls.VRF_SLAVE:
+            str += 'vrfslave '
+        if flags & cls.BRIDGE_VLAN_AWARE:
+            str += 'vlanawarebridge '
+        if flags & cls.BRIDGE_VXLAN:
+            str += 'vxlanbridge '
+        return str
+
+class ifaceLinkType():
+    LINK_UNKNOWN = 0x0
+    LINK_SLAVE = 0x1
+    LINK_MASTER = 0x2
+    LINK_NA = 0x3
+
+class VlanProtocols():
+    # Picked ID values from
+    # http://www.microhowto.info/howto/configure_an_ethernet_interface_as_a_qinq_vlan_trunk.html
+    ETHERTYPES_TO_ID = {
+                        '802.1Q'  : '0x8100',
+                        '802.1AD' : '0x88a8',
+                       }
+    ID_TO_ETHERTYPES = {
+                        '0x8100'  : '802.1Q',
+                        '0x88a8'  : '802.1AD',
+                       }
+
+class ifaceDependencyType():
+    """ Indicates type of dependency.
+
+        This class enumerates types of dependency relationships
+        between interfaces.
+
+        iface dependency relationships can be classified
+        into:
+         - link
+         - master/slave
+
+        In a 'link' dependency relationship, dependency can be shared
+        between interfaces. example: swp1.100 and
+        swp1.200 can both have 'link' swp1. swp1 is also a dependency
+        of swp1.100 and swp1.200. As you can see dependency
+        swp1 is shared between swp1.100 and swp1.200.
+
+        In a master/slave relationship like bridge and
+        its ports: eg: bridge br0 and its ports swp1 and swp2.
+        dependency swp1 and swp2 cannot be shared with any other
+        interface with the same dependency relationship.
+        ie, swp1 and swp2 cannot be in a slave relationship
+        with another interface. Understanding the dependency type is
+        required for any semantic checks between dependencies.
+
+    """
+    UNKNOWN = 0x0
+    LINK = 0x1
+    MASTER_SLAVE = 0x2
+
+class ifaceStatus():
+    """Enumerates iface status """
+
+    UNKNOWN = 0x1
+    SUCCESS = 0x2
+    WARNING = 0x3
+    ERROR = 0x4
+    NOTFOUND = 0x5
+
+    @classmethod
+    def to_str(cls, state):
+        if state == cls.UNKNOWN:
+            return 'unknown'
+        elif state == cls.SUCCESS:
+            return 'success'
+        elif state == cls.ERROR:
+            return 'error'
+        elif state == cls.NOTFOUND:
+            return 'notfound'
+
+    @classmethod
+    def from_str(cls, state_str):
+        if state_str == 'unknown':
+            return cls.UNKNOWN
+        elif state_str == 'success':
+            return cls.SUCCESS
+        elif state_str == 'error':
+            return cls.ERROR
+
+class ifaceState():
+    """Enumerates iface state """
+
+    UNKNOWN = 0x1
+    NEW = 0x2
+    PRE_UP = 0x3
+    UP = 0x4
+    POST_UP = 0x5
+    PRE_DOWN = 0x6
+    DOWN = 0x7
+    POST_DOWN = 0x8
+
+    # Pseudo states
+    QUERY_CHECKCURR = 0x9
+    QUERY_RUNNING = 0xa
+
+    @classmethod
+    def to_str(cls, state):
+        if state == cls.UNKNOWN:
+            return 'unknown'
+        elif state == cls.NEW:
+            return 'new'
+        elif state == cls.PRE_UP:
+            return 'pre-up'
+        elif state == cls.UP:
+            return 'up'
+        elif state == cls.POST_UP:
+            return 'post-up'
+        elif state == cls.PRE_DOWN:
+            return 'pre-down'
+        elif state == cls.DOWN:
+            return 'down'
+        elif state == cls.POST_DOWN:
+            return 'post-down'
+        elif state == cls.QUERY_CHECKCURR:
+            return 'query-checkcurr'
+        elif state == cls.QUERY_RUNNING:
+            return 'query-running'
+
+    @classmethod
+    def from_str(cls, state_str):
+        if state_str == 'unknown':
+            return cls.UNKNOWN
+        elif state_str == 'new':
+            return cls.NEW
+        elif state_str == 'pre-up':
+            return cls.PRE_UP
+        elif state_str == 'up':
+            return cls.UP
+        elif state_str == 'post-up':
+            return cls.POST_UP
+        elif state_str == 'pre-down':
+            return cls.PRE_DOWN
+        elif state_str == 'down':
+            return cls.DOWN
+        elif state_str == 'post-down':
+            return cls.POST_DOWN
+        elif state_str == 'query-checkcurr':
+            return cls.QUERY_CHECKCURR
+        elif state_str == 'query-running':
+            return cls.QUERY_RUNNING
+
+class ifaceJsonEncoder(json.JSONEncoder):
+    def default(self, o):
+        retconfig = {}
+        retifacedict = OrderedDict([])
+        if o.config:
+            retconfig = dict((k, (v[0] if len(v) == 1 else v))
+                             for k,v in o.config.items())
+        retifacedict['name'] = o.name
+        if o.addr_method:
+            if 'inet' in o.addr_family and 'dhcp' in o.addr_method:
+                retifacedict['addr_method'] = 'dhcp'
+            else:
+                retifacedict['addr_method'] = o.addr_method
+        if o.addr_family:
+            if len(o.addr_family) > 1:
+                retifacedict['addr_family'] = o.addr_family
+            else:
+                retifacedict['addr_family'] = ' '.join(o.addr_family)
+        retifacedict['auto'] = o.auto
+        retifacedict['config'] = retconfig
+
+        return retifacedict
+
+class ifaceJsonEncoderWithStatus(json.JSONEncoder):
+    def default(self, o):
+        retconfig = {}
+        retconfig_status = {}
+        retifacedict = OrderedDict([])
+        if o.config:
+            for k,v in o.config.items():
+                idx = 0
+                vitem_status = []
+                for vitem in v:
+                    s = o.get_config_attr_status(k, idx)
+                    if s == 1:
+                        status_str = ifaceStatusUserStrs.ERROR
+                    elif s == 0:
+                        status_str = ifaceStatusUserStrs.SUCCESS
+                    else:
+                        status_str = ifaceStatusUserStrs.UNKNOWN
+                    vitem_status.append('%s' %status_str)
+                    idx += 1
+                retconfig[k] = v[0] if len(v) == 1 else v
+                retconfig_status[k] = vitem_status[0] if len(vitem_status) == 1 else vitem_status
+
+        if (o.status == ifaceStatus.NOTFOUND or
+                o.status == ifaceStatus.ERROR):
+            status =  ifaceStatusUserStrs.ERROR
+        else:
+            status =  ifaceStatusUserStrs.SUCCESS
+
+        retifacedict['name'] = o.name
+        if o.addr_method:
+            retifacedict['addr_method'] = o.addr_method
+        if o.addr_family:
+            if len(o.addr_family) > 1:
+                retifacedict['addr_family'] = o.addr_family
+            else:
+                retifacedict['addr_family'] = ' '.join(o.addr_family)
+        retifacedict['auto'] = o.auto
+        retifacedict['config'] = retconfig
+        retifacedict['config_status'] = retconfig_status
+        retifacedict['status'] = status
+
+        return retifacedict
+
+class ifaceJsonDecoder():
+    @classmethod
+    def json_to_ifaceobj(cls, ifaceattrdict):
+        ifaceattrdict['config'] = OrderedDict([(k, (v if isinstance(v, list)
+                                                else [v.strip()]))
+                                for k,v in ifaceattrdict.get('config',
+                                            OrderedDict()).items()])
+        return iface(attrsdict=ifaceattrdict)
+
+class iface():
+    """ ifupdown2 iface object class
+    Attributes:
+        **name**      Name of the interface
+
+        **addr_family**     Address family eg, inet, inet6. Can be None to
+                            indicate both address families
+
+        **addr_method**     Address method eg, static, manual or None for
+                            static address method
+
+        **config**          dictionary of config lines for this interface
+
+        **state**           Configuration state of an interface as defined by
+                            ifaceState
+
+        **status**          Configuration status of an interface as defined by
+                            ifaceStatus
+
+        **flags**           Internal flags used by iface processing
+
+        **priv_flags**      private flags owned by module using this class
+
+        **module_flags**    module flags owned by module using this class
+
+        **refcnt**          reference count, indicating number of interfaces
+                            dependent on this iface
+
+        **lowerifaces**     list of interface names lower to this interface or
+                            this interface depends on
+
+        **upperifaces**     list of interface names upper to this interface or
+                            the interfaces that depend on this interface
+
+        **auto**            True if interface belongs to the auto class
+
+        **classes**         List of classes the interface belongs to
+
+        **env**             shell environment the interface needs during
+                            execution
+
+        **raw_config**      raw interface config from file
+    """
+
+    # flag to indicate that the object was created from pickled state
+    # XXX: Move these flags into a separate iface flags class
+    _PICKLED         = 0x00000001
+    HAS_SIBLINGS     = 0x00000010
+    IFACERANGE_ENTRY = 0x00000100
+    IFACERANGE_START = 0x00001000
+    OLDEST_SIBLING   = 0x00010000
+    YOUNGEST_SIBLING   = 0x00100000
+
+    version = '0.1'
+
+    def __init__(self, attrsdict={}):
+        self.addr_family = []
+
+        self._set_attrs_from_dict(attrsdict)
+        self._config_status = {}
+        """dict with config status of iface attributes"""
+        self.state = ifaceState.NEW
+        """iface state (of type ifaceState) """
+        self.status = ifaceStatus.UNKNOWN
+        """iface status (of type ifaceStatus) """
+        self.status_str = None
+        """iface status str (string representing the status) """
+        self.flags = 0x0
+        """iface flags """
+        self.priv_flags = None
+        """iface module flags dictionary with module name: flags"""
+        self.module_flags = {}
+        """iface priv flags. can be used by the external object manager """
+        self.refcnt = 0
+        """iface refcnt (incremented for each dependent this interface has) """
+        self.lowerifaces = None
+        """lower iface list (in other words: slaves of this interface """
+        self.upperifaces = None
+        """upper iface list (in other words: master of this interface """
+        self.classes = []
+        """interface classes this iface belongs to """
+        self.env = None
+        """environment variable dict required for this interface to run"""
+        self.raw_config = []
+        """interface config/attributes in raw format (eg: as it appeared in the interfaces file)"""
+        self.linkstate = None
+        """linkstate of the interface"""
+        self.type = ifaceType.UNKNOWN
+        """interface type"""
+        self.priv_data = None
+        self.role = ifaceRole.UNKNOWN
+        self.realname = None
+        self.link_type = ifaceLinkType.LINK_UNKNOWN
+        self.link_kind = ifaceLinkKind.UNKNOWN
+        self.link_privflags = ifaceLinkPrivFlags.UNKNOWN
+
+        # The below attribute is used to disambiguate between various
+        # types of dependencies
+        self.dependency_type = ifaceDependencyType.UNKNOWN
+        self.blacklisted = False
+
+    def _set_attrs_from_dict(self, attrdict):
+        self.auto = attrdict.get('auto', False)
+        self.name = attrdict.get('name')
+        self.addr_method = attrdict.get('addr_method')
+        self.config = attrdict.get('config', OrderedDict())
+
+        addr_family = attrdict.get('addr_family')
+        if addr_family:
+            self.addr_family.append(addr_family)
+
+    def inc_refcnt(self):
+        """ increment refcnt of the interface. Usually used to indicate that
+        it has dependents """
+        self.refcnt += 1
+
+    def dec_refcnt(self):
+        """ decrement refcnt of the interface. Usually used to indicate that
+        it has lost its dependent """
+        self.refcnt -= 1
+
+    def is_config_present(self):
+        """ returns true if the interface has user provided config,
+        false otherwise """
+        addr_method = self.addr_method
+        if addr_method and addr_method in ['dhcp', 'dhcp6', 'loopback']:
+            return True
+        if not self.config:
+            return False
+        else:
+            return True
+
+    def set_class(self, classname):
+        """ appends class to the interfaces class list """
+        self.classes.append(classname)
+
+    def set_state_n_status(self, state, status):
+        """ sets state and status of an interface """
+        self.state = state
+        if status > self.status:
+            self.status = status
+
+    def set_status(self, status):
+        """ sets status of an interface """
+        if status > self.status:
+            self.status = status
+
+    def set_flag(self, flag):
+        self.flags |= flag
+
+    def clear_flag(self, flag):
+        self.flags &= ~flag
+
+    def add_to_upperifaces(self, upperifacename):
+        """ add to the list of upperifaces """
+        if self.upperifaces:
+            if upperifacename not in self.upperifaces:
+                self.upperifaces.append(upperifacename)
+        else:
+            self.upperifaces = [upperifacename]
+
+    def add_to_lowerifaces(self, lowerifacename):
+        """ add to the list of lowerifaces """
+        if self.lowerifaces:
+            if lowerifacename not in self.lowerifaces:
+                self.lowerifaces.append(lowerifacename)
+        else:
+            self.lowerifaces = [lowerifacename]
+
+    def get_attr_value(self, attr_name):
+        """ add to the list of upperifaces """
+        return self.config.get(attr_name)
+
+    def get_attr_value_first(self, attr_name):
+        """ get first value of the specified attr name """
+        attr_value_list = self.config.get(attr_name)
+        if attr_value_list:
+            return attr_value_list[0]
+        return None
+
+    def get_attrs_value_first(self, attrs):
+        """ get first value of the first attr in the list.
+            Useful when you have multiple attrs representing the
+            same thing.
+        """
+        for attr in attrs:
+            attr_value_list = self.config.get(attr)
+            if attr_value_list:
+                return attr_value_list[0]
+        return None
+
+    def get_attr_value_n(self, attr_name, attr_index):
+        """ get n'th value of the specified attr name """
+        attr_value_list = self.config.get(attr_name)
+        if attr_value_list:
+            try:
+                return attr_value_list[attr_index]
+            except:
+                return None
+        return None
+
+    def get_env(self):
+        """ get shell environment variables the interface must execute in """
+        if not self.env:
+            self.generate_env()
+        return self.env
+
+    def generate_env(self):
+        """ generate shell environment variables dict interface must execute
+        in. This is used to support legacy ifupdown scripts
+        """
+        env = {}
+        config = self.config
+        env['IFACE'] = self.name
+        for attr, attr_value in config.items():
+            attr_env_name = 'IF_%s' %attr.upper().replace("-", "_")
+            env[attr_env_name] = attr_value[0]
+        self.env = env
+
+    def update_config(self, attr_name, attr_value):
+        """ add attribute name and value to the interface config """
+        self.config.setdefault(attr_name, []).append(attr_value)
+
+    def replace_config(self, attr_name, attr_value):
+        """ add attribute name and value to the interface config """
+        self.config[attr_name] = [attr_value]
+
+    def delete_config(self, attr_name):
+        """ add attribute name and value to the interface config """
+        try:
+            del self.config[attr_name]
+        except:
+            pass
+
+    def update_config_dict(self, attrdict):
+        self.config.update(attrdict)
+
+    def update_config_with_status(self, attr_name, attr_value, attr_status=0):
+        """ add attribute name and value to the interface config and also
+        update the config_status dict with status of this attribute config """
+        if not attr_value:
+            attr_value = ''
+        self.config.setdefault(attr_name, []).append(attr_value)
+        self._config_status.setdefault(attr_name, []).append(attr_status)
+        # set global iface state
+        if attr_status == 1:
+            self.status = ifaceStatus.ERROR
+        elif self.status != ifaceStatus.ERROR:
+            # Not already error, mark success
+            self.status = ifaceStatus.SUCCESS
+
+    def check_n_update_config_with_status_many(self, ifaceobjorig, attr_names,
+                                               attr_status=0):
+        # set multiple attribute status to zero
+        # also updates status only if the attribute is present
+        for attr_name in attr_names:
+            if not ifaceobjorig.get_attr_value_first(attr_name):
+               continue
+            self.config.setdefault(attr_name, []).append('')
+            self._config_status.setdefault(attr_name, []).append(attr_status)
+
+    def get_config_attr_status(self, attr_name, idx=0):
+        """ get status of a attribute config on this interface.
+
+        Looks at the iface _config_status dict"""
+        return self._config_status.get(attr_name, [])[idx]
+
+    def compare(self, dstiface):
+        """ compares iface object with iface object passed as argument
+
+        Returns True if object self is same as dstiface and False otherwise """
+
+        if self.name != dstiface.name: return False
+        if self.type != dstiface.type: return False
+        if self.addr_family != dstiface.addr_family: return False
+        if self.addr_method != dstiface.addr_method: return False
+        if self.auto != dstiface.auto: return False
+        if self.classes != dstiface.classes: return False
+        if len(self.config) != len(dstiface.config):
+            return False
+        if any(True for k in self.config if k not in dstiface.config):
+            return False
+        if any(True for k,v in self.config.items()
+                    if v != dstiface.config.get(k)): return False
+        return True
+
+    def squash(self, newifaceobj):
+        """ This squashes the iface object """
+        for attrname, attrlist in newifaceobj.config.iteritems():
+            # if allready present add it to the list
+            # else add it to the end of the dictionary
+            # We need to maintain order.
+            if self.config.get(attrname):
+                self.config[attrname].extend(attrlist)
+            else:
+                self.config.update([(attrname, attrlist)])
+        # we now support inet and inet6 together
+        self.addr_family.extend(newifaceobj.addr_family)
+        # if auto %ifacename is not part of the first stanza
+        # we need to squash it
+        if not self.auto and newifaceobj.auto:
+            self.auto = True
+
+    def __getstate__(self):
+        odict = self.__dict__.copy()
+        del odict['state']
+        del odict['status']
+        del odict['lowerifaces']
+        del odict['upperifaces']
+        del odict['refcnt']
+        del odict['_config_status']
+        del odict['flags']
+        del odict['priv_flags']
+        del odict['module_flags']
+        del odict['raw_config']
+        del odict['linkstate']
+        del odict['env']
+        del odict['link_type']
+        del odict['link_kind']
+        del odict['link_privflags']
+        del odict['role']
+        del odict['dependency_type']
+        del odict['blacklisted']
+        return odict
+
+    def __setstate__(self, dict):
+        self.__dict__.update(dict)
+        self._config_status = {}
+        self.state = ifaceState.NEW
+        self.status = ifaceStatus.UNKNOWN
+        self.refcnt = 0
+        self.flags = 0
+        self.lowerifaces = None
+        self.upperifaces = None
+        self.linkstate = None
+        self.env = None
+        self.role = ifaceRole.UNKNOWN
+        self.priv_flags = None
+        self.module_flags = {}
+        self.raw_config = []
+        self.flags |= self._PICKLED
+        self.link_type = ifaceLinkType.LINK_NA
+        self.link_kind = ifaceLinkKind.UNKNOWN
+        self.link_privflags = ifaceLinkPrivFlags.UNKNOWN
+        self.dependency_type = ifaceDependencyType.UNKNOWN
+        self.blacklisted = False
+
+    def dump_raw(self, logger):
+        indent = '  '
+        if self.auto:
+            print 'auto %s' %self.name
+        print (self.raw_config[0])
+        for i in range(1, len(self.raw_config)):
+            print(indent + self.raw_config[i])
+
+    def dump(self, logger):
+        indent = '\t'
+        logger.info(self.name + ' : {')
+        logger.info(indent + 'family: %s' % ' '.join(self.addr_family))
+        logger.info(indent + 'method: %s' %self.addr_method)
+        logger.info(indent + 'flags: %x' %self.flags)
+        logger.info(indent + 'state: %s'
+                %ifaceState.to_str(self.state))
+        logger.info(indent + 'status: %s'
+                %ifaceStatus.to_str(self.status))
+        logger.info(indent + 'refcnt: %d' %self.refcnt)
+        d = self.lowerifaces
+        if d:
+            logger.info(indent + 'lowerdevs: %s' %str(d))
+        else:
+            logger.info(indent + 'lowerdevs: None')
+
+        d = self.upperifaces
+        if d:
+            logger.info(indent + 'upperdevs: %s' %str(d))
+        else:
+            logger.info(indent + 'upperdevs: None')
+
+        logger.info(indent + 'config: ')
+        config = self.config
+        if config:
+            logger.info(indent + indent + str(config))
+        logger.info('}')
+
+    def _dump_pretty(self, family, first, addr_method, with_status=False, use_realname=False):
+        indent = '\t'
+        outbuf = ''
+        if use_realname and self.realname:
+            name = '%s' %self.realname
+        else:
+            name = '%s' %self.name
+        if self.auto:
+            outbuf += 'auto %s\n' %name
+        ifaceline = ''
+        if self.type == ifaceType.BRIDGE_VLAN:
+            ifaceline += 'vlan %s' %name
+        else:
+            ifaceline += 'iface %s' %name
+        if family:
+            ifaceline += ' %s' % family
+        if addr_method:
+            ifaceline += ' %s' % addr_method
+        if with_status:
+            status_str = None
+            if (self.status == ifaceStatus.ERROR or
+                    self.status == ifaceStatus.NOTFOUND):
+                if self.status_str:
+                    ifaceline += ' (%s)' %self.status_str
+                status_str = '[%s]' %ifaceStatusUserStrs.ERROR
+            elif self.status == ifaceStatus.SUCCESS:
+                status_str = '[%s]' %ifaceStatusUserStrs.SUCCESS
+            if status_str:
+                outbuf += '{0:65} {1:>8}'.format(ifaceline, status_str) + '\n'
+            else:
+                outbuf += ifaceline + '\n'
+            if self.status == ifaceStatus.NOTFOUND:
+                outbuf = (outbuf.encode('utf8')
+                    if isinstance(outbuf, unicode) else outbuf)
+                print outbuf + '\n'
+                return
+        else:
+            outbuf += ifaceline + '\n'
+        config = self.config
+        if config and first:
+            for cname, cvaluelist in config.items():
+                idx = 0
+                for cv in cvaluelist:
+                    status_str = None
+                    if with_status:
+                        s = self.get_config_attr_status(cname, idx)
+                        if s == -1:
+                            status_str = '[%s]' %ifaceStatusUserStrs.UNKNOWN
+                        elif s == 1:
+                            status_str = '[%s]' %ifaceStatusUserStrs.ERROR
+                        elif s == 0:
+                            status_str = '[%s]' %ifaceStatusUserStrs.SUCCESS
+                    if status_str:
+                        outbuf += (indent + '{0:55} {1:>10}'.format(
+                              '%s %s' %(cname, cv), status_str)) + '\n'
+                    else:
+                        outbuf += indent + '%s %s\n' %(cname, cv)
+                    idx += 1
+        if with_status:
+            outbuf = (outbuf.encode('utf8')
+                        if isinstance(outbuf, unicode) else outbuf)
+        print outbuf
+
+    def dump_pretty(self, with_status=False, use_realname=False):
+        if not self.addr_family:
+            self._dump_pretty(None, True,
+                              self.addr_method,
+                              with_status=with_status,
+                              use_realname=use_realname)
+        else:
+            # To allow both inet and inet6 on an interface we changed the
+            # addr_family attribute, it's now a list. Depending on how
+            # stanzas were squashed (and what command was used ie. ifquery -r)
+            # we want to dump the ifaceobj as usual but we will output an
+            # empty stanza for each additional addr_family. The config will
+            # only be displayed once, in the first stanza. Example:
+            # $ ifquery eth0 -r
+            # auto etho
+            # iface eth0 inet dhcp
+            #     config...
+            #
+            # auto eth0
+            # iface eth0 inet6 dhcp
+            # $
+            first = True
+            for family in self.addr_family:
+                addr_method = self.addr_method
+                # We need to make sure we display 'dhcp' for inet family.
+                # In some cases it might take the value 'dhcp6' even if it has
+                # both inet and inet6 addr_family
+                if addr_method and family == 'inet' and 'dhcp' in addr_method:
+                    addr_method = 'dhcp'
+                self._dump_pretty(family, first,
+                                  addr_method=addr_method,
+                                  with_status=with_status,
+                                  use_realname=use_realname)
+                first = False
diff --git a/ifupdown2/ifupdown/iff.py b/ifupdown2/ifupdown/iff.py
new file mode 100644 (file)
index 0000000..c7c5d65
--- /dev/null
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+#
+# Author: Scott Feldman, sfeldma@cumulusnetworks.com
+#
+#
+# from /usr/include/linux/if.h
+#
+
+# Standard interface flags (netdevice->flags).
+
+IFF_UP = 0x1                  # interface is up
+IFF_BROADCAST = 0x2           # broadcast address valid
+IFF_DEBUG = 0x4               # turn on debugging
+IFF_LOOPBACK = 0x8            # is a loopback net
+IFF_POINTOPOINT = 0x10        # interface is has p-p link
+IFF_NOTRAILERS = 0x20         # avoid use of trailers
+IFF_RUNNING = 0x40            # interface RFC2863 OPER_UP
+IFF_NOARP = 0x80              # no ARP protocol
+IFF_PROMISC = 0x100           # receive all packets
+IFF_ALLMULTI = 0x200          # receive all multicast packets
+
+IFF_MASTER = 0x400            # master of a load balancer
+IFF_SLAVE = 0x800             # slave of a load balancer
+
+IFF_MULTICAST = 0x1000        # Supports multicast
+
+IFF_PORTSEL = 0x2000          # can set media type
+IFF_AUTOMEDIA = 0x4000        # auto media select active
+IFF_DYNAMIC = 0x8000          # dialup device with changing addresses
+
+IFF_LOWER_UP = 0x10000        # driver signals L1 up
+IFF_DORMANT = 0x20000         # driver signals dormant
+
+IFF_ECHO = 0x40000            # echo sent packets
diff --git a/ifupdown2/ifupdown/ifupdownbase.py b/ifupdown2/ifupdown/ifupdownbase.py
new file mode 100644 (file)
index 0000000..fd7c730
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+# ifupdownBase --
+#    base object for various ifupdown objects
+#
+
+import re
+import os
+import logging
+import traceback
+
+try:
+    from ifupdown2.ifupdown.netlink import netlink
+
+    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
+except ImportError:
+    from ifupdown.netlink import netlink
+
+    import ifupdown.ifupdownflags as ifupdownflags
+
+
+class ifupdownBase(object):
+
+    def __init__(self):
+        modulename = self.__class__.__name__
+        self.logger = logging.getLogger('ifupdown.' + modulename)
+
+    def ignore_error(self, errmsg):
+        if (ifupdownflags.flags.FORCE == True or re.search(r'exists', errmsg,
+            re.IGNORECASE | re.MULTILINE) is not None):
+            return True
+        return False
+
+    def log_warn(self, str):
+        if self.ignore_error(str) == False:
+            if self.logger.getEffectiveLevel() == logging.DEBUG:
+                traceback.print_stack()
+                traceback.print_exc()
+            self.logger.warn(str)
+        pass
+
+    def log_error(self, str):
+        if self.ignore_error(str) == False:
+            raise Exception(str)
+        else:
+            pass
+
+    def link_exists(self, ifacename):
+        return os.path.exists('/sys/class/net/%s' %ifacename)
+
+    def link_up(self, ifacename):
+        netlink.link_set_updown(ifacename, "up")
+
+    def link_down(self, ifacename):
+        netlink.link_set_updown(ifacename, "down")
diff --git a/ifupdown2/ifupdown/ifupdownconfig.py b/ifupdown2/ifupdown/ifupdownconfig.py
new file mode 100644 (file)
index 0000000..f4f6bf2
--- /dev/null
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+#
+# Copyright 2015-2017 Cumulus Networks, Inc. All rights reserved.
+#
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+class ifupdownConfig():
+
+       def __init__(self):
+               self.conf = {}
+
+config = ifupdownConfig()
+
+def reset():
+       global config
+       config = ifupdownConfig()
diff --git a/ifupdown2/ifupdown/ifupdownflags.py b/ifupdown2/ifupdown/ifupdownflags.py
new file mode 100644 (file)
index 0000000..e5efbe1
--- /dev/null
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+#
+# Copyright 2015-2017 Cumulus Networks, Inc. All rights reserved.
+#
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+class ifupdownFlags():
+
+       def __init__(self):
+                self.ALL = False
+                self.CLASS = False
+                self.FORCE = False
+                self.DRYRUN = False
+                self.NOWAIT = False
+                self.PERFMODE = False
+                self.CACHE = False
+                self.WITHDEFAULTS = False
+                self.IGNORE_ERRORS = False
+
+                # Flags
+                self.CACHE_FLAGS = 0x0
+
+flags = ifupdownFlags()
+
+
+def reset():
+    global flags
+    flags = ifupdownFlags()
diff --git a/ifupdown2/ifupdown/ifupdownmain.py b/ifupdown2/ifupdown/ifupdownmain.py
new file mode 100644 (file)
index 0000000..45391ee
--- /dev/null
@@ -0,0 +1,2344 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+# ifupdownMain --
+#    ifupdown main module
+#
+
+import pprint
+
+from collections import OrderedDict
+
+from ipaddr import IPNetwork, IPv4Network, IPv6Network, IPAddress, IPv4Address, IPv6Address
+
+try:
+    import ifupdown2.ifupdownaddons.cache
+    import ifupdown2.ifupdownaddons.LinkUtils
+    import ifupdown2.ifupdownaddons.mstpctlutil
+
+    import ifupdown2.ifupdown.policymanager
+    import ifupdown2.ifupdown.ifupdownflags
+    import ifupdown2.ifupdown.statemanager as statemanager
+    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
+    import ifupdown2.ifupdown.ifupdownconfig as ifupdownConfig
+
+    from ifupdown2.ifupdown.graph import *
+    from ifupdown2.ifupdown.iface import *
+    from ifupdown2.ifupdown.scheduler import *
+    from ifupdown2.ifupdown.exceptions import *
+    from ifupdown2.ifupdown.networkinterfaces import *
+    from ifupdown2.ifupdown.config import ADDON_MODULES_DIR, ADDONS_CONF_PATH, IFUPDOWN2_ADDON_DROPIN_FOLDER
+except ImportError:
+    import ifupdownaddons.cache
+    import ifupdownaddons.LinkUtils
+    import ifupdownaddons.mstpctlutil
+
+    import ifupdown.ifupdownflags
+    import ifupdown.policymanager
+    import ifupdown.statemanager as statemanager
+    import ifupdown.ifupdownflags as ifupdownflags
+    import ifupdown.ifupdownconfig as ifupdownConfig
+
+    from ifupdown.graph import *
+    from ifupdown.iface import *
+    from ifupdown.scheduler import *
+    from ifupdown.exceptions import *
+    from ifupdown.networkinterfaces import *
+    from ifupdown.config import ADDON_MODULES_DIR, ADDONS_CONF_PATH, IFUPDOWN2_ADDON_DROPIN_FOLDER
+
+
+"""
+.. module:: ifupdownmain
+:synopsis: main module for ifupdown package
+
+.. moduleauthor:: Roopa Prabhu <roopa@cumulusnetworks.com>
+
+"""
+
+_tickmark = u'\u2713'
+_crossmark = u'\u2717'
+_success_sym = '(%s)' %_tickmark
+_error_sym = '(%s)' %_crossmark
+
+class ifupdownMainFlags():
+    COMPAT_EXEC_SCRIPTS = False
+    STATEMANAGER_ENABLE = True
+    STATEMANAGER_UPDATE = True
+    ADDONS_ENABLE = False
+    DELETE_DEPENDENT_IFACES_WITH_NOCONFIG = False
+    SCHED_SKIP_CHECK_UPPERIFACES = False
+    CHECK_SHARED_DEPENDENTS = True
+
+class ifacePrivFlags():
+    # priv flags to mark iface objects
+    BUILTIN = False
+    NOCONFIG = False
+
+    def __init__(self, builtin=False, noconfig=False):
+        self.BUILTIN = builtin
+        self.NOCONFIG = noconfig
+
+class ifupdownMain(ifupdownBase):
+    """ ifupdown2 main class """
+
+    scripts_dir = '/etc/network'
+    addon_modules_dir = ADDON_MODULES_DIR
+    addon_modules_configfile = ADDONS_CONF_PATH
+
+    # Handlers for ops that ifupdown2 owns
+    def run_up(self, ifaceobj):
+        # Skip link sets on ifaceobjs of type 'vlan' (used for l2 attrs).
+        # there is no real interface behind it
+        if ifaceobj.type == ifaceType.BRIDGE_VLAN:
+            return
+        if ((ifaceobj.link_kind & ifaceLinkKind.VRF) or
+            (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE)):
+            self._keep_link_down(ifaceobj)
+            return
+        # if not a logical interface and addr method is manual,
+        # ignore link admin state changes
+        if (ifaceobj.addr_method == 'manual' and
+            not ifaceobj.link_kind):
+            return
+        if self._delay_admin_state:
+            self._delay_admin_state_iface_queue.append(ifaceobj.name)
+            return
+        # If this object is a link slave, ie its link is controlled
+        # by its link master interface, then dont set the link state.
+        # But do allow user to change state of the link if the interface
+        # is already with its link master (hence the master check).
+        if ifaceobj.link_type == ifaceLinkType.LINK_SLAVE:
+            return
+        if not self.link_exists(ifaceobj.name):
+            return
+        if self._keep_link_down(ifaceobj):
+            return
+        self.link_up(ifaceobj.name)
+
+    def _keep_link_down(self, ifaceobj):
+        if ifaceobj.link_privflags & ifaceLinkPrivFlags.KEEP_LINK_DOWN:
+            # user has asked to explicitly keep the link down,
+            # so, force link down
+            self.logger.info('%s: keeping link down due to user config' %ifaceobj.name)
+            self.link_down(ifaceobj.name)
+            return True
+        return False
+
+    def run_down(self, ifaceobj):
+        if ((ifaceobj.link_kind & ifaceLinkKind.VRF) or
+            (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE)):
+            return
+        # Skip link sets on ifaceobjs of type 'vlan' (used for l2 attrs)
+        # there is no real interface behind it
+        if ifaceobj.type == ifaceType.BRIDGE_VLAN:
+            return
+        # if not a logical interface and addr method is manual,
+        # ignore link admin state changes
+        if (ifaceobj.addr_method == 'manual' and
+            not ifaceobj.link_kind):
+            return
+        if self._delay_admin_state:
+            self._delay_admin_state_iface_queue.append(ifaceobj.name)
+            return
+        # If this object is a link slave, ie its link is controlled
+        # by its link master interface, then dont set the link state.
+        # But do allow user to change state of the link if the interface
+        # is already with its link master (hence the master check).
+        if ifaceobj.link_type == ifaceLinkType.LINK_SLAVE:
+           return
+        if not self.link_exists(ifaceobj.name):
+           return
+        self.link_down(ifaceobj.name)
+
+    # ifupdown object interface operation handlers
+    ops_handlers = OrderedDict([('up', run_up),
+                                ('down', run_down)])
+
+    def run_sched_ifaceobj_posthook(self, ifaceobj, op):
+        if (ifaceobj.priv_flags and (ifaceobj.priv_flags.BUILTIN or
+            ifaceobj.priv_flags.NOCONFIG)):
+            return
+        if self.flags.STATEMANAGER_UPDATE:
+            self.statemanager.ifaceobj_sync(ifaceobj, op)
+
+    # ifupdown object interface scheduler pre and posthooks
+    sched_hooks = {'posthook' : run_sched_ifaceobj_posthook}
+
+    def reset_ifupdown2(self):
+        ifaceScheduler.reset()
+
+        ifupdown2.ifupdown.statemanager.reset()
+        ifupdown2.ifupdown.policymanager.reset()
+        ifupdown2.ifupdown.ifupdownflags.reset()
+        ifupdownConfig.reset()
+        ifupdown2.ifupdownaddons.mstpctlutil.mstpctlutil.reset()
+        ifupdown2.ifupdownaddons.LinkUtils.LinkUtils.reset()
+
+        ifupdown2.ifupdownaddons.cache.linkCache.reset()
+        ifupdown2.ifupdownaddons.cache.MSTPAttrsCache.invalidate()
+
+    def __init__(self, config={},
+                 daemon=False, force=False, dryrun=False, nowait=False,
+                 perfmode=False, withdepends=False, njobs=1,
+                 cache=False, addons_enable=True, statemanager_enable=True,
+                 interfacesfile='/etc/network/interfaces',
+                 interfacesfileiobuf=None,
+                 interfacesfileformat='native',
+                 withdefaults=False):
+        """This member function initializes the ifupdownmain object.
+
+        Kwargs:
+            config (dict):  config dict from /etc/network/ifupdown2/ifupdown2.conf
+            force (bool): force interface configuration
+            dryrun (bool): dryrun interface configuration
+            withdepends (bool): apply interface configuration on all depends
+            interfacesfile (str): interfaces file. default is /etc/network/interfaces
+            interfacesfileformat (str): default is 'native'. Other choices are 'json'
+
+        Raises:
+            AttributeError, KeyError """
+
+        if daemon:
+            self.reset_ifupdown2()
+
+        # iface dictionary in the below format:
+        # { '<ifacename>' : [<ifaceobject1>, <ifaceobject2> ..] }
+        # eg:
+        # { 'swp1' : [<iface swp1>, <iface swp2> ..] }
+        #
+        # Each ifaceobject corresponds to a configuration block for
+        # that interface
+        # The value in the dictionary is a list because the network
+        # interface configuration file supports more than one iface section
+        # in the interfaces file
+        self.ifaceobjdict = OrderedDict()
+
+        # iface dictionary representing the curr running state of an iface
+        # in the below format:
+        # {'<ifacename>' : <ifaceobject>}
+        self.ifaceobjcurrdict = OrderedDict()
+
+        # Dictionary representing operation and modules
+        # for every operation
+        self.module_ops = OrderedDict([('pre-up', []),
+                                  ('up', []),
+                                  ('post-up', []),
+                                  ('query-checkcurr', []),
+                                  ('query-running', []),
+                                  ('query-dependency', []),
+                                  ('query', []),
+                                  ('query-raw', []),
+                                  ('pre-down', []),
+                                  ('down', []),
+                                  ('post-down', [])])
+
+        # For old style /etc/network/ bash scripts
+        self.script_ops = OrderedDict([('pre-up', []),
+                                  ('up', []),
+                                  ('post-up', []),
+                                  ('pre-down', []),
+                                  ('down', []),
+                                  ('post-down', [])])
+
+
+        self.logger = logging.getLogger('ifupdown')
+        ifupdownflags.flags.FORCE = force
+        ifupdownflags.flags.DRYRUN = dryrun
+        ifupdownflags.flags.WITHDEFAULTS = withdefaults
+        ifupdownflags.flags.NOWAIT = nowait
+        ifupdownflags.flags.PERFMODE = perfmode
+        ifupdownflags.flags.CACHE = cache
+        ifupdownflags.flags.WITH_DEPENDS = withdepends
+
+        # Can be used to provide hints for caching
+        ifupdownflags.flags.CACHE_FLAGS = 0x0
+
+        self.flags = ifupdownMainFlags()
+
+        self.flags.STATEMANAGER_ENABLE = statemanager_enable
+        self.interfacesfile = interfacesfile
+        self.interfacesfileiobuf = interfacesfileiobuf
+        self.interfacesfileformat = interfacesfileformat
+        self.config = config
+        self.logger.debug(self.config)
+        self.blacklisted_ifaces_present = False
+
+        self.type = ifaceType.UNKNOWN
+
+        self.flags.DELETE_DEPENDENT_IFACES_WITH_NOCONFIG = False
+        self.flags.ADDONS_ENABLE = addons_enable
+
+        self.ifaces = OrderedDict()
+        self.njobs = njobs
+        self.pp = pprint.PrettyPrinter(indent=4)
+        self.modules = OrderedDict({})
+        self.module_attrs = {}
+        self.overridden_ifupdown_scripts = []
+
+        if self.config.get('addon_python_modules_support', '1') == '1':
+            self.load_addon_modules(self.addon_modules_dir)
+        if self.config.get('addon_scripts_support', '0') == '1':
+            self.load_scripts(self.scripts_dir)
+        self.dependency_graph = OrderedDict({})
+
+        self._cache_no_repeats = {}
+
+        if self.flags.STATEMANAGER_ENABLE:
+            self.statemanager = statemanager.statemanager_api
+            try:
+                self.statemanager.read_saved_state()
+            except Exception, e:
+                # if read_saved_state fails, state file might be corrupt.
+                # Ignore old state and continue
+                self.logger.warning('error reading state (%s)' %str(e))
+        else:
+            self.flags.STATEMANAGER_UPDATE = False
+        self._delay_admin_state = True if self.config.get(
+                            'delay_admin_state_change', '0') == '1' else False
+        self._delay_admin_state_iface_queue = []
+        if self._delay_admin_state:
+            self.logger.info('\'delay_admin_state_change\' is set. admin ' +
+                             'state changes will be delayed till the end.')
+
+        self._link_master_slave = True if self.config.get(
+                      'link_master_slave', '0') == '1' else False
+        if self._link_master_slave:
+            self.logger.info('\'link_master_slave\' is set. slave admin ' +
+                             'state changes will be delayed till the ' +
+                             'masters admin state change.')
+
+        # squash iface objects for same interface both internal and
+        # external representation. It is off by default.
+        self._ifaceobj_squash = True if self.config.get(
+                            'ifaceobj_squash', '0') == '1' else False
+
+        # squash iface objects for same interface internal
+        # representation only. External representation as seen by ifquery
+        # will continue to see multiple iface stanzas if it was specified
+        # that way by the user. It is on by default.
+        self._ifaceobj_squash_internal = True if self.config.get(
+                            'ifaceobj_squash_internal', '1') == '1' else False
+
+        # initialize global config object with config passed by the user
+        # This makes config available to addon modules
+        ifupdownConfig.config = self.config
+
+        self.validate_keywords = {
+            '<mac>': self._keyword_mac,
+            '<text>': self._keyword_text,
+            '<ipv4>': self._keyword_ipv4,
+            '<ipv6>': self._keyword_ipv6,
+            '<ip>': self._keyword_ip,
+            '<number>': self._keyword_number,
+            '<interface>': self._keyword_interface,
+            '<ipv4-vrf-text>': self._keyword_ipv4_vrf_text,
+            '<number-ipv4-list>': self._keyword_number_ipv4_list,
+            '<interface-list>': self._keyword_interface_list,
+            '<ipv4/prefixlen>': self._keyword_ipv4_prefixlen,
+            '<ipv6/prefixlen>': self._keyword_ipv6_prefixlen,
+            '<ip/prefixlen>': self._keyword_ip_prefixlen,
+            '<number-range-list>': self._keyword_number_range_list,
+            '<number-comma-range-list>': self._keyword_number_comma_range_list,
+            '<interface-range-list>': self._keyword_interface_range_list,
+            '<interface-range-list-multiple-of-16>': self._keyword_interface_range_list_multiple_of_16,
+            '<mac-ip/prefixlen-list>': self._keyword_mac_ip_prefixlen_list,
+            '<number-interface-list>': self._keyword_number_interface_list,
+            '<interface-yes-no-list>': self._keyword_interface_yes_no_list,
+            '<interface-on-off-list>': self._keyword_interface_on_off_list,
+            '<interface-yes-no-0-1-list>': self._keyword_interface_yes_no_0_1_list,
+            '<interface-disabled-automatic-enabled>': self._keyword_interface_disabled_automatic_enabled_list,
+            '<interface-yes-no-auto-list>': self._keyword_interface_yes_no_auto_list,
+            '<interface-l2protocol-tunnel-list>': self._keyword_interface_l2protocol_tunnel_list
+        }
+
+    def link_master_slave_ignore_error(self, errorstr):
+        # If link master slave flag is set,
+        # there may be cases where the lowerdev may not be
+        # up resulting in 'Network is down' error
+        # This can happen if the lowerdev is a LINK_SLAVE
+        # of another interface which is not up yet
+        # example of such a case:
+        #   bringing up a vlan on a bond interface and the bond
+        #   is a LINK_SLAVE of a bridge (in other words the bond is
+        #   part of a bridge) which is not up yet
+        if self._link_master_slave:
+           if 'Network is down' in errorstr:
+              return True
+        return False
+
+    def get_ifaceobjs(self, ifacename):
+        return self.ifaceobjdict.get(ifacename)
+
+    def get_ifaceobjs_saved(self, ifacename):
+        """ Return ifaceobjects from statemanager """
+        if self.flags.STATEMANAGER_ENABLE:
+           return self.statemanager.get_ifaceobjs(ifacename)
+        else:
+           return None
+
+    def get_ifaceobj_first(self, ifacename):
+        ifaceobjs = self.get_ifaceobjs(ifacename)
+        if ifaceobjs:
+            return ifaceobjs[0]
+        return None
+
+    def get_ifacenames(self):
+        return self.ifaceobjdict.keys()
+
+    def get_iface_obj_last(self, ifacename):
+        return self.ifaceobjdict.get(ifacename)[-1]
+
+
+    def must_follow_upperifaces(self, ifacename):
+        #
+        # XXX: This bleeds the knowledge of iface
+        # types in the infrastructure module.
+        # Cant think of a better fix at the moment.
+        # In future maybe the module can set a flag
+        # to indicate if we should follow upperifaces
+        #
+        ifaceobj = self.get_ifaceobj_first(ifacename)
+        if ifaceobj.type == ifaceType.BRIDGE_VLAN:
+            return False
+        return True
+
+    def create_n_save_ifaceobj(self, ifacename, priv_flags=None,
+                               increfcnt=False):
+        """ creates a iface object and adds it to the iface dictionary """
+        ifaceobj = iface()
+        ifaceobj.name = ifacename
+        ifaceobj.priv_flags = priv_flags
+        ifaceobj.auto = True
+        if not self._link_master_slave:
+            ifaceobj.link_type = ifaceLinkType.LINK_NA
+        if increfcnt:
+            ifaceobj.inc_refcnt()
+        self.ifaceobjdict[ifacename] = [ifaceobj]
+        return ifaceobj
+
+    def create_n_save_ifaceobjcurr(self, ifaceobj):
+        """ creates a copy of iface object and adds it to the iface
+            dict containing current iface objects
+        """
+        ifaceobjcurr = iface()
+        ifaceobjcurr.name = ifaceobj.name
+        ifaceobjcurr.type = ifaceobj.type
+        ifaceobjcurr.lowerifaces = ifaceobj.lowerifaces
+        ifaceobjcurr.priv_flags = copy.deepcopy(ifaceobj.priv_flags)
+        ifaceobjcurr.auto = ifaceobj.auto
+        self.ifaceobjcurrdict.setdefault(ifaceobj.name,
+                                     []).append(ifaceobjcurr)
+        return ifaceobjcurr
+
+    def get_ifaceobjcurr(self, ifacename, idx=0):
+        ifaceobjlist = self.ifaceobjcurrdict.get(ifacename)
+        if not ifaceobjlist:
+            return None
+        if not idx:
+            return ifaceobjlist
+        else:
+            return ifaceobjlist[idx]
+
+    def get_ifaceobjrunning(self, ifacename):
+        return self.ifaceobjrunningdict.get(ifacename)
+
+    def get_iface_refcnt(self, ifacename):
+        """ Return iface ref count """
+        max = 0
+        ifaceobjs = self.get_ifaceobjs(ifacename)
+        if not ifaceobjs:
+            return 0
+        for i in ifaceobjs:
+            if i.refcnt > max:
+                max = i.refcnt
+        return max
+
+    def is_iface_builtin_byname(self, ifacename):
+        """ Returns true if iface name is a builtin interface.
+
+        A builtin interface is an interface which ifupdown understands.
+        The following are currently considered builtin ifaces:
+            - vlan interfaces in the format <ifacename>.<vlanid>
+        """
+        return '.' in ifacename
+
+    def is_ifaceobj_builtin(self, ifaceobj):
+        """ Returns true if iface name is a builtin interface.
+
+        A builtin interface is an interface which ifupdown understands.
+        The following are currently considered builtin ifaces:
+            - vlan interfaces in the format <ifacename>.<vlanid>
+        """
+        if (ifaceobj.priv_flags and ifaceobj.priv_flags.BUILTIN):
+            return True
+        return False
+
+    def is_ifaceobj_noconfig(self, ifaceobj):
+        """ Returns true if iface object did not have a user defined config.
+
+        These interfaces appear only when they are dependents of interfaces
+        which have user defined config
+        """
+        return (ifaceobj.priv_flags and ifaceobj.priv_flags.NOCONFIG)
+
+    def is_iface_noconfig(self, ifacename):
+        """ Returns true if iface has no config """
+
+        ifaceobj = self.get_ifaceobj_first(ifacename)
+        if not ifaceobj: return True
+        return self.is_ifaceobj_noconfig(ifaceobj)
+
+    def check_shared_dependents(self, ifaceobj, dlist):
+        """ ABSOLETE: Check if dlist intersects with any other
+            interface with slave dependents.
+            example: bond and bridges.
+            This function logs such errors """
+        setdlist = Set(dlist)
+        for ifacename, ifacedlist in self.dependency_graph.items():
+            if not ifacedlist:
+                continue
+            check_depends = False
+            iobjs = self.get_ifaceobjs(ifacename)
+            if not iobjs:
+                continue
+            for i in iobjs:
+                if (i.dependency_type == ifaceDependencyType.MASTER_SLAVE):
+                    check_depends = True
+            if check_depends:
+                common = Set(ifacedlist).intersection(setdlist)
+                if common:
+                    self.logger.error('misconfig..?. iface %s and %s '
+                            %(ifaceobj.name, ifacename) +
+                            'seem to share dependents/ports %s' %str(list(common)))
+
+    def _set_iface_role(self, ifaceobj, role, upperifaceobj):
+        if (self.flags.CHECK_SHARED_DEPENDENTS and
+            (ifaceobj.role & ifaceRole.SLAVE) and
+            (role == ifaceRole.SLAVE) and (upperifaceobj.role & ifaceRole.MASTER)):
+               self.logger.error("misconfig..? %s %s is enslaved to multiple interfaces %s"
+                                  %(ifaceobj.name,
+                                    ifaceLinkPrivFlags.get_all_str(ifaceobj.link_privflags), str(ifaceobj.upperifaces)))
+                ifaceobj.set_status(ifaceStatus.ERROR)
+                return
+        ifaceobj.role = role
+
+    def _set_iface_role_n_kind(self, ifaceobj, upperifaceobj):
+
+        # If addr_method is set and link is not a logical interface,
+        # set flag KEEP_LINK_DOWN. addr_method == 'manual' only applies to
+        # logical interfaces.
+        if (ifaceobj.addr_method == 'manual' and not ifaceobj.link_kind):
+            ifaceobj.link_privflags |= ifaceLinkPrivFlags.KEEP_LINK_DOWN
+
+        if (upperifaceobj.link_kind & ifaceLinkKind.BOND):
+            self._set_iface_role(ifaceobj, ifaceRole.SLAVE, upperifaceobj)
+            ifaceobj.link_privflags |= ifaceLinkPrivFlags.BOND_SLAVE
+
+        if (upperifaceobj.link_kind & ifaceLinkKind.BRIDGE):
+            self._set_iface_role(ifaceobj, ifaceRole.SLAVE, upperifaceobj)
+            ifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_PORT
+
+        if (ifaceobj.link_kind & ifaceLinkKind.VXLAN) \
+                and (upperifaceobj.link_kind & ifaceLinkKind.BRIDGE):
+            upperifaceobj.link_privflags |= ifaceLinkPrivFlags.BRIDGE_VXLAN
+
+        # vrf masters get processed after slaves, which means
+        # check both link_kind vrf and vrf slave
+        if ((upperifaceobj.link_kind & ifaceLinkKind.VRF) or
+            (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE)):
+            self._set_iface_role(ifaceobj, ifaceRole.SLAVE, upperifaceobj)
+            ifaceobj.link_privflags |= ifaceLinkPrivFlags.VRF_SLAVE
+        if self._link_master_slave:
+            if upperifaceobj.link_type == ifaceLinkType.LINK_MASTER:
+                ifaceobj.link_type = ifaceLinkType.LINK_SLAVE
+        else:
+            upperifaceobj.link_type = ifaceLinkType.LINK_NA
+            ifaceobj.link_type = ifaceLinkType.LINK_NA
+
+    def dump_iface_dependency_info(self):
+        """ debug funtion to print raw dependency
+        info - lower and upper devices"""
+
+        for ifacename, ifaceobjs in self.ifaceobjdict.iteritems():
+            iobj = ifaceobjs[0]
+            self.logger.info("%s: refcnt: %d, lower: %s, upper: %s" %(ifacename,
+                             self.get_iface_refcnt(ifacename),
+                             str(iobj.lowerifaces) if iobj.lowerifaces else [],
+                             str(iobj.upperifaces) if iobj.upperifaces else []))
+
+
+    def preprocess_dependency_list(self, upperifaceobj, dlist, ops):
+        """ We go through the dependency list and
+            delete or add interfaces from the interfaces dict by
+            applying the following rules:
+                if flag DELETE_DEPENDENT_IFACES_WITH_NOCONFIG is True:
+                    we only consider devices whose configuration was
+                    specified in the network interfaces file. We delete
+                    any interface whose config was not specified except
+                    for vlan devices. vlan devices get special treatment.
+                    Even if they are not present they are created and added
+                    to the ifacesdict
+                elif flag DELETE_DEPENDENT_IFACES_WITH_NOCONFIG is False:
+                    we create objects for all dependent devices that are not
+                    present in the ifacesdict
+        """
+        del_list = []
+
+        for d in dlist:
+            dilist = self.get_ifaceobjs(d)
+            if not dilist:
+                ni = None
+                if self.is_iface_builtin_byname(d):
+                    ni = self.create_n_save_ifaceobj(d,
+                            ifacePrivFlags(True, True), True)
+                elif not self.flags.DELETE_DEPENDENT_IFACES_WITH_NOCONFIG:
+                    ni = self.create_n_save_ifaceobj(d,
+                                    ifacePrivFlags(False, True), True)
+                else:
+                    del_list.append(d)
+                if ni:
+                    ni.add_to_upperifaces(upperifaceobj.name)
+                    self._set_iface_role_n_kind(ni, upperifaceobj)
+            else:
+                for di in dilist:
+                    di.inc_refcnt()
+                    di.add_to_upperifaces(upperifaceobj.name)
+                    self._set_iface_role_n_kind(di, upperifaceobj)
+        for d in del_list:
+            dlist.remove(d)
+
+    def preprocess_upperiface(self, lowerifaceobj, ulist, ops):
+        for u in ulist:
+            if (lowerifaceobj.upperifaces and
+                u in lowerifaceobj.upperifaces):
+                continue
+            lowerifaceobj.add_to_upperifaces(u)
+            uifacelist = self.get_ifaceobjs(u)
+            if uifacelist:
+                for ui in uifacelist:
+                    lowerifaceobj.inc_refcnt()
+                    self._set_iface_role_n_kind(lowerifaceobj, ui)
+                    ui.add_to_lowerifaces(lowerifaceobj.name)
+
+    def query_lowerifaces(self, ifaceobj, ops, ifacenames, type=None):
+        """ Gets iface dependents by calling into respective modules """
+        ret_dlist = []
+
+        # Get dependents for interface by querying respective modules
+        for module in self.modules.values():
+            try:
+                if ops[0] == 'query-running':
+                    if (not hasattr(module,
+                        'get_dependent_ifacenames_running')):
+                        continue
+                    dlist = module.get_dependent_ifacenames_running(ifaceobj)
+                else:
+                    if (not hasattr(module, 'get_dependent_ifacenames')):
+                        continue
+                    dlist = module.get_dependent_ifacenames(ifaceobj,
+                                        ifacenames)
+            except Exception, e:
+                self.logger.warn('%s: error getting dependent interfaces (%s)'
+                        %(ifaceobj.name, str(e)))
+                dlist = None
+                pass
+            if dlist: ret_dlist.extend(dlist)
+        return list(set(ret_dlist))
+
+    def query_upperifaces(self, ifaceobj, ops, ifacenames, type=None):
+        """ Gets iface upperifaces by calling into respective modules """
+        ret_ulist = []
+
+        # Get upperifaces for interface by querying respective modules
+        for module in self.modules.values():
+            try:
+                if ops[0] == 'query-running':
+                    if (not hasattr(module,
+                        'get_upper_ifacenames_running')):
+                        continue
+                    ulist = module.get_upper_ifacenames_running(ifaceobj)
+                else:
+                    if (not hasattr(module, 'get_upper_ifacenames')):
+                        continue
+                    ulist = module.get_upper_ifacenames(ifaceobj, ifacenames)
+            except Exception, e:
+                self.logger.warn('%s: error getting upper interfaces (%s)'
+                                 %(ifaceobj.name, str(e)))
+                ulist = None
+                pass
+            if ulist: ret_ulist.extend(ulist)
+        return list(set(ret_ulist))
+
+    def populate_dependency_info(self, ops, ifacenames=None):
+        """ recursive function to generate iface dependency info """
+
+        if not ifacenames:
+            ifacenames = self.ifaceobjdict.keys()
+
+        iqueue = deque(ifacenames)
+        while iqueue:
+            i = iqueue.popleft()
+            # Go through all modules and find dependent ifaces
+            dlist = None
+            ulist = None
+            ifaceobjs = self.get_ifaceobjs(i)
+            if not ifaceobjs:
+                continue
+            dependents_processed = False
+
+            # Store all dependency info in the first ifaceobj
+            # but get dependency info from all ifaceobjs
+            ifaceobj = ifaceobjs[0]
+            for iobj in ifaceobjs:
+                ulist = self.query_upperifaces(iobj, ops, ifacenames)
+                if iobj.lowerifaces:
+                    dependents_processed = True
+                    break
+                dlist = self.query_lowerifaces(iobj, ops, ifacenames)
+                if dlist:
+                   break
+            if ulist:
+                self.preprocess_upperiface(ifaceobj, ulist, ops)
+            if dependents_processed:
+                continue
+            if dlist:
+                self.preprocess_dependency_list(ifaceobj,
+                                                dlist, ops)
+                ifaceobj.lowerifaces = dlist
+                [iqueue.append(d) for d in dlist]
+            #if not self.dependency_graph.get(i):
+            #    self.dependency_graph[i] = dlist
+
+        for i in self.ifaceobjdict.keys():
+            iobj = self.get_ifaceobj_first(i)
+            if (not iobj.link_kind and
+               not (iobj.link_privflags & ifaceLinkPrivFlags.LOOPBACK) and
+               iobj.name == 'lo'):
+               iobj.link_privflags |= ifaceLinkPrivFlags.LOOPBACK
+            if iobj.lowerifaces:
+                self.dependency_graph[i] = iobj.lowerifaces
+            else:
+                self.dependency_graph[i] = []
+
+        if not self.blacklisted_ifaces_present:
+            return
+
+        # Walk through the dependency graph and remove blacklisted
+        # interfaces that were picked up as dependents
+        for i in self.dependency_graph.keys():
+            ifaceobj = self.get_ifaceobj_first(i)
+            if not ifaceobj:
+                continue
+
+            if ifaceobj.blacklisted and not ifaceobj.upperifaces:
+                # if blacklisted and was not picked up as a
+                # dependent of a upper interface, delete the
+                # interface from the dependency graph
+                dlist = ifaceobj.lowerifaces
+                if dlist:
+                    for d in dlist:
+                        difaceobjs = self.get_ifaceobjs(d)
+                        if not difaceobjs:
+                            continue
+                        try:
+                            for d in difaceobjs:
+                                d.dec_refcnt()
+                                d.upperifaces.remove(i)
+                        except:
+                            self.logger.debug('error removing %s from %s upperifaces' %(i, d))
+                            pass
+                self.logger.debug("populate_dependency_info: deleting blacklisted interface %s" %i)
+                del self.dependency_graph[i]
+                continue
+
+    def _check_config_no_repeats(self, ifaceobj):
+        """ check if object has an attribute that is
+        restricted to a single object in the system.
+        if yes, warn and return """
+        for k,v in self._cache_no_repeats.items():
+            iv = ifaceobj.config.get(k)
+            if iv and iv[0] == v:
+                self.logger.error('ignoring interface %s. ' %ifaceobj.name +
+                        'Only one object with attribute ' +
+                        '\'%s %s\' allowed.' %(k, v))
+                return True
+        for k, v in self.config.get('no_repeats', {}).items():
+            iv = ifaceobj.config.get(k)
+            if iv and iv[0] == v:
+                self._cache_no_repeats[k] = v
+        return False
+
+    def _save_iface_squash(self, ifaceobj):
+        """ squash ifaceobjects belonging to same iface
+        into a single object """
+        if self._check_config_no_repeats(ifaceobj):
+           return
+        ifaceobj.priv_flags = ifacePrivFlags()
+        if not self._link_master_slave:
+           ifaceobj.link_type = ifaceLinkType.LINK_NA
+        currentifaceobjlist = self.ifaceobjdict.get(ifaceobj.name)
+        if not currentifaceobjlist:
+            self.ifaceobjdict[ifaceobj.name] = [ifaceobj]
+            return
+        if ifaceobj.compare(currentifaceobjlist[0]):
+            self.logger.warn('duplicate interface %s found' %ifaceobj.name)
+            return
+        for obj in self.ifaceobjdict[ifaceobj.name]:
+            if obj.type == ifaceobj.type:
+                obj.squash(ifaceobj)
+                return
+        self.ifaceobjdict[ifaceobj.name].append(ifaceobj)
+
+    def _save_iface(self, ifaceobj):
+        if self._check_config_no_repeats(ifaceobj):
+           return
+        ifaceobj.priv_flags = ifacePrivFlags()
+        if not self._link_master_slave:
+           ifaceobj.link_type = ifaceLinkType.LINK_NA
+        currentifaceobjlist = self.ifaceobjdict.get(ifaceobj.name)
+        if not currentifaceobjlist:
+            self.ifaceobjdict[ifaceobj.name]= [ifaceobj]
+            if not self._ifaceobj_squash:
+                ifaceobj.flags |= ifaceobj.YOUNGEST_SIBLING
+            return
+        if ifaceobj.compare(currentifaceobjlist[0]):
+            self.logger.warn('duplicate interface %s found' %ifaceobj.name)
+            return
+        if currentifaceobjlist[0].type == ifaceobj.type:
+            currentifaceobjlist[0].flags |= ifaceobj.HAS_SIBLINGS
+            ifaceobj.flags |= ifaceobj.HAS_SIBLINGS
+        # clear the OLDEST_SIBLING from all the siblings
+        for iface in self.ifaceobjdict[ifaceobj.name]:
+            iface.flags &= ~ifaceobj.OLDEST_SIBLING
+        # current sibling is the oldest
+        ifaceobj.flags |= ifaceobj.OLDEST_SIBLING
+        self.ifaceobjdict[ifaceobj.name].append(ifaceobj)
+
+    def _keyword_text(self, value, validrange=None):
+        return isinstance(value, str) and len(value) > 0
+
+    def _keyword_mac(self, value, validrange=None):
+        if value.strip().startswith('ether'):
+            value = value.strip()[6:]
+        return re.match('[0-9a-f]{1,2}([-:])[0-9a-f]{1,2}(\\1[0-9a-f]{1,2}){4}$',
+                        value.lower())
+
+    def _keyword_check_list(self, _list, obj, limit=None):
+        try:
+            if limit and limit > 0:
+                for i in xrange(0, limit):
+                    obj(_list[i])
+                return len(_list) == limit
+            else:
+                for elem in _list:
+                    obj(elem)
+            return True
+        except Exception as e:
+            self.logger.debug('keyword: check list: %s' % str(e))
+            return False
+
+    def _keyword_ipv4(self, value, validrange=None):
+        return self._keyword_check_list(value.split(), IPv4Address, limit=1)
+
+    def _keyword_ipv4_prefixlen(self, value, validrange=None):
+        return self._keyword_check_list(value.split(), IPv4Network, limit=1)
+
+    def _keyword_ipv6(self, value, validrange=None):
+        return self._keyword_check_list(value.split(), IPv6Address, limit=1)
+
+    def _keyword_ipv6_prefixlen(self, value, validrange=None):
+        return self._keyword_check_list(value.split(), IPv6Network, limit=1)
+
+    def _keyword_ip(self, value, validrange=None):
+        return self._keyword_check_list(value.split(), IPAddress, limit=1)
+
+    def _keyword_ip_prefixlen(self, value, validrange=None):
+        return self._keyword_check_list(value.split(), IPNetwork, limit=1)
+
+    def _keyword_mac_ip_prefixlen_list(self, value, validrange=None):
+        """
+            <mac> <ip> [<ip> ...]
+            ex: address-virtual 00:11:22:33:44:01 11.0.1.1/24 11.0.1.2/24
+        """
+        try:
+            res = value.split()
+            if len(res) < 2:
+                return False
+            if not self._keyword_mac(res[0]):
+                return False
+            for ip in res[1:]:
+                if not self._keyword_ip_prefixlen(ip):
+                    return False
+            return True
+        except Exception as e:
+            self.logger.debug('keyword: mac ipaddr prefixlen: %s' % str(e))
+            return False
+
+    def _keyword_number_ipv4_list(self, value, validrange=None):
+        """
+            <number>=<ipv4> [<number>=<ipv4> ...]
+            ex: bridge-mcqv4src 100=172.16.100.1 101=172.16.101.1
+        """
+        try:
+            elements = value.split(' ')
+            if not elements:
+                return False
+            for elem in elements:
+                v = elem.split('=')
+                int(v[0])
+                IPv4Address(v[1])
+            return True
+        except Exception as e:
+            self.logger.debug('keyword: number ipv4: %s' % str(e))
+            return False
+
+    def _keyword_interface(self, ifacename, validrange=None):
+        return self.get_ifaceobjs(ifacename)
+
+    def _keyword_ipv4_vrf_text(self, value, validrange=None):
+        """
+            <ipv4> "vrf" <text>
+            ex: clagd-backup-ip 10.10.10.42 vrf blue
+        """
+        values = value.split()
+        size = len(values)
+
+        if size > 3 or size < 1:
+            return False
+        try:
+            IPv4Address(values[0])
+            if size > 1:
+                if values[1] != 'vrf':
+                    return False
+                if size > 2:
+                    if not self._keyword_text(values[2]):
+                        return False
+            return True
+        except Exception as e:
+            self.logger.debug('keyword: ipv4 vrf text: %s' % str(e))
+            return False
+
+    def _keyword_interface_list_with_value(self, value, validvals):
+        values = value.split()
+        try:
+            if len(values) == 1:
+                if values[0] in validvals:
+                    return True
+            for v in values:
+                iface_value = v.split('=')
+                size = len(iface_value)
+                if size != 2:
+                    if iface_value[0] == 'glob' or iface_value[0] == 'regex':
+                        continue
+                    return False
+                if not iface_value[1] in validvals:
+                    return False
+            return True
+        except Exception as e:
+            self.logger.debug('keyword: interface list with value: %s' % str(e))
+            return False
+
+    def _keyword_interface_on_off_list(self, value, validrange=None):
+        """
+            <yes|no> | ( <interface>=<on|off> [<interface>=<on|off> ...] )
+            ex: bridge-learning swp1=on swp2=off
+        """
+        return self._keyword_interface_list_with_value(value, ['on', 'off'])
+
+    def _keyword_interface_yes_no_list(self, value, validrange=None):
+        """
+            <yes|no> | ( <interface>=<yes|no> [<interface>=<yes|no> ...] )
+            ex: mstpctl-portrestrrole swp1=yes swp2=no
+        """
+        return self._keyword_interface_list_with_value(value, ['yes', 'no'])
+
+    def _keyword_interface_yes_no_auto_list(self, value, validrange=None):
+        """
+            <yes|no|auto> |
+                ( <interface>=<yes|no|auto> [<interface>=<yes|no|auto> ...] )
+            ex: mstpctl-portp2p swp1=yes swp2=no swp3=auto
+        """
+        return self._keyword_interface_list_with_value(value,
+                                                        ['yes', 'no', 'auto'])
+
+    def _keyword_interface_l2protocol_tunnel_list(self, value, validrange=None):
+        """
+            bridge-l2protocol-tunnel swpX=lacp,stp swpY=cdp swpZ=all
+            bridge-l2protocol-tunnel lacp stp,lldp,cdp
+            bridge-l2protocol-tunnel stp lacp cdp
+            bridge-l2protocol-tunnel lldp pvst
+            bridge-l2protocol-tunnel stp
+            bridge-l2protocol-tunnel all
+        """
+        try:
+            if '=' in value:
+                for intf_arg in value.split():
+                    intf_arg_split = intf_arg.split('=')
+                    for arg in re.split(',|\s*', intf_arg_split[1]):
+                        if arg not in ['all', 'stp', 'lldp', 'lacp', 'cdp', 'pvst']:
+                            return False
+            else:
+                for arg in re.split(',|\s*', value):
+                    if arg not in ['all', 'stp', 'lldp', 'lacp', 'cdp', 'pvst']:
+                        return False
+        except:
+            return False
+        return True
+
+    def _keyword_interface_yes_no_0_1_list(self, value, validrange=None):
+        """
+            <yes|no|0|1> |
+                ( <interface>=<yes|no|0|1> [<interface>=<yes|no|0|1> ...] )
+            ex: bridge-portmcrouter swp1=yes swp2=yes swp3=1
+        """
+        return self._keyword_interface_list_with_value(value,
+                                                       ['yes', 'no', '1', '0', '2'])
+
+    def _keyword_interface_disabled_automatic_enabled_list(self, value, validrange=None):
+        return self._keyword_interface_list_with_value(value, [
+            '0', 'disabled', 'no',
+            '1', 'automatic', 'yes',
+            '2', 'enabled'])
+
+    def _keyword_interface_range_list_multiple_of_16(self, value, validrange):
+        return self._keyword_interface_range_list(value, validrange, multiple=16)
+
+    def _keyword_interface_range_list(self, value, validrange, multiple=None):
+        """
+            <number> | ( <interface>=<number> [ <interface>=number> ...] )
+            ex: mstpctl-portpathcost swp1=0 swp2=1
+        """
+        values = value.split()
+        try:
+            if len(values) == 1 and '=' not in values[0]:
+                try:
+                    n = int(values[0])
+                    if n < int(validrange[0]) or n > int(
+                        validrange[1]):
+                        raise invalidValueError('value of out range "%s":'
+                                                ' valid attribute range: %s'
+                                                % (values[0],
+                                                   '-'.join(validrange)))
+
+                    if multiple is not None:
+                        if not (n % multiple == 0):
+                            raise invalidValueError('invalid value %s: must be a multiple of %s' % (n, multiple))
+
+                    return True
+                except invalidValueError as e:
+                    raise e
+                except Exception as e:
+                    self.logger.debug('keyword: interface range list: %s'
+                                      % str(e))
+                    return False
+            for v in values:
+                iface_value = v.split('=')
+                size = len(iface_value)
+                if size != 2:
+                    return False
+                number = int(iface_value[1])
+                if number < int(validrange[0]) or number > int(
+                        validrange[1]):
+                    raise invalidValueError(
+                        'value of out range "%s" for iface "%s":'
+                        ' valid attribute range: %s'
+                        % (iface_value[1],
+                           iface_value[0],
+                           '-'.join(validrange)))
+
+                if multiple is not None:
+                    if not (number % multiple == 0):
+                        raise invalidValueError('invalid value %s: must be a multiple of %s' % (number, multiple))
+
+            return True
+        except invalidValueError as e:
+            raise e
+        except Exception as e:
+            self.logger.debug('keyword: interface range list: %s' % str(e))
+            return False
+
+    def _keyword_interface_list(self, value, validrange=None):
+        """
+            [glob|regex] <interface> [ [glob|regex] <interface> ...]
+            ex: bridge-ports swp1 swp2 glob swp3-5.100 regex (swp[6|7|8].100)
+        """
+        interface_list = value.split()
+        size = len(interface_list)
+        i = 0
+        while i < size:
+            if interface_list[i] == 'glob' or interface_list[i] == 'regex':
+                i += 1
+            else:
+                if not self._keyword_interface(interface_list[i]):
+                    return False
+            i += 1
+        return True
+
+    def _keyword_number_range_list(self, value, validrange=None):
+        """
+            <number> [<number>-<number>]
+            ex: bridge-vids 42 100-200
+        """
+        number_list = value.split()
+        try:
+            i = 0
+            while i < len(number_list):
+                if '-' in number_list[i]:
+                    range = number_list[i].split('-')
+                    a = int(range[0])
+                    b = int(range[1])
+                    if a > b:
+                        return False
+                else:
+                    int(number_list[i])
+                i += 1
+            return True
+        except Exception as e:
+            self.logger.debug('keyword: number range list: %s' % str(e))
+            return False
+
+    def _keyword_number_interface_list(self, value, validrange=None):
+        """
+            <number> <interface> [<interface>... [<number> <interface> ... ]]
+            bridge-waitport 42 swp1 swp2 swp3 9 swp4
+        """
+        interface_list = value.split()
+        if not interface_list:
+            return False
+        try:
+            int(interface_list[0])
+            prev = True
+            for elem in interface_list[1:]:
+                try:
+                    int(elem)
+                    if prev:
+                        return False
+                    prev = True
+                except:
+                    prev = False
+            return not prev
+        except Exception as e:
+            self.logger.debug('keyword: number interface list: %s' % str(e))
+            return False
+
+    def _keyword_number(self, value, validrange=None):
+        try:
+            int(value)
+            return True
+        except Exception as e:
+            self.logger.debug('keyword: number: %s' % str(e))
+            return False
+
+    def _is_keyword(self, value):
+        if isinstance(value, tuple):
+            return True
+        keyword_found = value in self.validate_keywords
+        if value.startswith('<') and value.endswith('>') and not keyword_found:
+            raise Exception('%s: invalid keyword, please make sure to use'
+                            ' a valid keyword see `ifquery -s`' % value)
+        return keyword_found
+
+    def _check_validvals_value(self, attrname, value, validvals, validrange):
+        if validvals and value not in validvals:
+            is_valid = False
+            for keyword in validvals:
+                if self._is_keyword(keyword):
+                    if validrange:
+                        if self.validate_keywords[keyword](value, validrange):
+                            return {'result': True}
+                    else:
+                        if self.validate_keywords[keyword](value):
+                            return {'result': True}
+            if not is_valid:
+                return {
+                    'result': False,
+                    'message': 'invalid value "%s": valid attribute values: %s'
+                               % (value, validvals)
+                }
+        elif validvals and value in validvals:
+            pass
+        elif validrange:
+            if len(validrange) != 2:
+                raise Exception('%s: invalid range in addon configuration'
+                                % '-'.join(validrange))
+            _value = int(value)
+            if _value < int(validrange[0]) or _value > int(validrange[1]):
+                return {
+                    'result': False,
+                    'message': 'value of out range "%s": '
+                               'valid attribute range: %s'
+                               % (value, '-'.join(validrange))
+                }
+        return {'result': True}
+
+    def _check_validvals(self, ifacename, module_name, attrs):
+        ifaceobj = self.get_ifaceobjs(ifacename)
+        if not ifaceobj:
+            return
+        success = True
+        for attrname, attrvalue in ifaceobj[0].config.items():
+            try:
+                attrname_dict = attrs.get(attrname, {})
+                validvals = attrname_dict.get('validvals', [])
+                validrange = attrname_dict.get('validrange', [])
+                for value in attrvalue:
+                    res = self._check_validvals_value(attrname,
+                                                      value,
+                                                      validvals,
+                                                      validrange)
+                    if not res['result']:
+                        self.logger.warn('%s: %s: %s' %
+                                         (ifacename, attrname, res['message']))
+                        success = False
+            except Exception as e:
+                self.logger.warn('addon \'%s\': %s: %s' % (module_name,
+                                                           attrname,
+                                                           str(e)))
+                success = False
+        return success
+
+    def _module_syntax_check(self, filtered_ifacenames):
+        result = True
+        for ifacename in filtered_ifacenames:
+            for module in self.modules.values():
+                try:
+                    if hasattr(module, '_modinfo'):
+                        if not self._check_validvals(ifacename,
+                                                     module.__class__.__name__,
+                                                     module._modinfo.get('attrs', {})):
+                            result = False
+                    if hasattr(module, 'syntax_check') and callable(module.syntax_check):
+                        if not module.syntax_check(self.get_ifaceobjs(ifacename)[0],
+                                                   self.get_ifaceobjs):
+                            result = False
+                except Exception, e:
+                    self.logger.warn('%s: %s' % (ifacename, str(e)))
+                    result = False
+        return result
+
+    def _iface_configattr_syntax_checker(self, attrname, attrval):
+        for m, mdict in self.module_attrs.items():
+            if not mdict:
+                continue
+            attrsdict = mdict.get('attrs')
+            try:
+                a = attrsdict.get(attrname)
+                if a:
+                    if a.get('deprecated'):
+                        newa = a.get('new-attribute')
+                        if newa:
+                            self.logger.warn('attribute %s is deprecated. use %s instead.' %(attrname, newa))
+                        else:
+                            self.logger.warn('attribute %s is deprecated.'
+                                             %attrname)
+                    return True
+                else:
+                    for key in attrsdict:
+                        if 'aliases' in attrsdict[key]:
+                            if attrname in attrsdict[key]['aliases']:
+                                return True
+            except AttributeError:
+                pass
+        return False
+
+    def _ifaceobj_syntax_checker(self, ifaceobj):
+        ret = True
+        for attrname, attrvalue in ifaceobj.config.items():
+            found = False
+            for k, v in self.module_attrs.items():
+                if v and v.get('attrs', {}).get(attrname):
+                    found = True
+                    break
+            if not found:
+                ret = False
+                self.logger.warn('%s: unsupported attribute \'%s\'' \
+                                 % (ifaceobj.name, attrname))
+                continue
+        return ret
+
+    def read_iface_config(self):
+        """ Reads default network interface config /etc/network/interfaces. """
+        ret = True
+        nifaces = networkInterfaces(self.interfacesfile,
+                        self.interfacesfileiobuf,
+                        self.interfacesfileformat,
+                        template_enable=self.config.get('template_enable', 0),
+                        template_engine=self.config.get('template_engine'),
+                template_lookuppath=self.config.get('template_lookuppath'))
+        if self._ifaceobj_squash or self._ifaceobj_squash_internal:
+            nifaces.subscribe('iface_found', self._save_iface_squash)
+        else:
+            nifaces.subscribe('iface_found', self._save_iface)
+        if self.config.get('addon_syntax_check', '1') == '1':
+            nifaces.subscribe('validateifaceattr',
+                              self._iface_configattr_syntax_checker)
+            nifaces.subscribe('validateifaceobj', self._ifaceobj_syntax_checker)
+        nifaces.load()
+        if nifaces.errors or nifaces.warns:
+            ret = False
+        return ret
+
+    def read_old_iface_config(self):
+        """ Reads the saved iface config instead of default iface config.
+        And saved iface config is already read by the statemanager """
+        self.ifaceobjdict = copy.deepcopy(self.statemanager.ifaceobjdict)
+
+    def _load_addon_modules_config(self):
+        """ Load addon modules config file """
+
+        with open(self.addon_modules_configfile, 'r') as f:
+            lines = f.readlines()
+            for l in lines:
+                try:
+                    litems = l.strip(' \n\t\r').split(',')
+                    if not litems or len(litems) < 2:
+                        continue
+                    operation = litems[0]
+                    mname = litems[1]
+                    self.module_ops[operation].append(mname)
+                except Exception, e:
+                    self.logger.warn('error reading line \'%s\' %s:' %(l, str(e)))
+                    continue
+
+    def load_addon_modules(self, modules_dir_list):
+        """ load python modules from modules_dir
+
+        Default modules_dir is /usr/share/ifupdownmodules
+
+        """
+        failed_import = list()
+
+        self.logger.info('loading builtin modules from %s' % str(modules_dir_list))
+        self._load_addon_modules_config()
+
+        for modules_dir in modules_dir_list:
+            if not modules_dir in sys.path:
+                sys.path.insert(1, modules_dir)
+            try:
+                for op, mlist in self.module_ops.items():
+                    for mname in mlist:
+                        if self.modules.get(mname):
+                            continue
+                        mpath = modules_dir + '/' + mname + '.py'
+                        if os.path.exists(mpath) and mpath not in failed_import:
+                            try:
+                                m = __import__(mname)
+                                mclass = getattr(m, mname)
+                            except Exception as e:
+                                self.logger.warning('cannot load "%s" module: %s' % (mname, str(e)))
+                                failed_import.append(mpath)
+                                continue
+                            try:
+                                minstance = mclass()
+                                script_override = minstance.get_overrides_ifupdown_scripts()
+                                self.overridden_ifupdown_scripts.extend(script_override)
+                            except moduleNotSupported, e:
+                                self.logger.info('module %s not loaded (%s)\n'
+                                                 %(mname, str(e)))
+                                continue
+                            except:
+                                raise
+                            self.modules[mname] = minstance
+                            try:
+                                self.module_attrs[mname] = minstance.get_modinfo()
+                            except:
+                                pass
+            except:
+                raise
+
+        # Assign all modules to query operations
+        self.module_ops['query-checkcurr'] = self.modules.keys()
+        self.module_ops['query-running'] = self.modules.keys()
+        self.module_ops['query-dependency'] = self.modules.keys()
+        self.module_ops['query'] = self.modules.keys()
+        self.module_ops['query-raw'] = self.modules.keys()
+
+    def _keyword_number_comma_range_list(self, value, validrange=None):
+        return self._keyword_number_range_list(value.replace(',', ' '), validrange=validrange)
+
+
+    def _modules_help(self, fmt):
+        """ Prints addon modules supported syntax """
+
+        if fmt == 'json':
+            modinfos = {}
+            for key, value in self.modules.items():
+                if hasattr(value, '_modinfo'):
+                    modinfos[key] = {
+                        'mhelp': value._modinfo['mhelp'],
+                        'attrs': value.merge_modinfo_with_policy_files()
+                    }
+            print json.dumps(modinfos)
+        else:
+            indent = '  '
+            for m, mdict in self.module_attrs.items():
+                if not mdict:
+                    continue
+                print('%s: %s' %(m, mdict.get('mhelp')))
+                attrdict = self.modules[m].merge_modinfo_with_policy_files()
+                if not attrdict:
+                    continue
+                try:
+                    for attrname, attrvaldict in attrdict.items():
+                        if attrvaldict.get('compat', False):
+                            continue
+                        print('%s%s' %(indent, attrname))
+                        print('%shelp: %s' %(indent + '  ',
+                              attrvaldict.get('help', '')))
+                        print ('%srequired: %s' %(indent + '  ',
+                                attrvaldict.get('required', False)))
+                        default = attrvaldict.get('default')
+                        if default:
+                            print('%sdefault: %s' %(indent + '  ', default))
+
+                        validrange = attrvaldict.get('validrange')
+                        if validrange:
+                            print('%svalidrange: %s-%s'
+                                  %(indent + '  ', validrange[0], validrange[1]))
+
+                        validvals = attrvaldict.get('validvals')
+                        if validvals:
+                            print('%svalidvals: %s'
+                                  %(indent + '  ', ','.join(validvals)))
+
+                        examples = attrvaldict.get('example')
+                        if not examples:
+                            continue
+
+                        print '%sexample:' %(indent + '  ')
+                        for e in examples:
+                            print '%s%s' %(indent + '    ', e)
+                except:
+                    pass
+                print ''
+
+    def load_scripts(self, modules_dir):
+        """ loading user modules from /etc/network/.
+
+        Note that previously loaded python modules override modules found
+        under /etc/network if any
+
+        """
+
+        self.logger.info('looking for user scripts under %s' %modules_dir)
+        for op, mlist in self.script_ops.items():
+            msubdir = modules_dir + '/if-%s.d' %op
+            self.logger.info('loading scripts under %s ...' %msubdir)
+            try:
+                module_list = os.listdir(msubdir)
+                for module in module_list:
+                    if self.modules.get(module) or module in self.overridden_ifupdown_scripts:
+                        continue
+                    self.script_ops[op].append(msubdir + '/' + module)
+            except:
+                # continue reading
+                pass
+
+    def _sched_ifaces(self, ifacenames, ops, skipupperifaces=False,
+                      followdependents=True, sort=False):
+        self.logger.debug('scheduling \'%s\' for %s'
+                          %(str(ops), str(ifacenames)))
+        self._pretty_print_ordered_dict('dependency graph',
+                    self.dependency_graph)
+        ifaceScheduler.sched_ifaces(self, ifacenames, ops,
+                                    dependency_graph=self.dependency_graph,
+                                    order=ifaceSchedulerFlags.INORDER
+                            if 'down' in ops[0]
+                                else ifaceSchedulerFlags.POSTORDER,
+                                    followdependents=followdependents,
+                                    skipupperifaces=skipupperifaces,
+                                    sort=True if (sort or ifupdownflags.flags.CLASS) else False)
+        return ifaceScheduler.get_sched_status()
+
+    def _render_ifacename(self, ifacename):
+        new_ifacenames = []
+        vlan_match = re.match("^([\d]+)-([\d]+)", ifacename)
+        if vlan_match:
+            vlan_groups = vlan_match.groups()
+            if vlan_groups[0] and vlan_groups[1]:
+                [new_ifacenames.append('%d' %v)
+                    for v in range(int(vlan_groups[0]),
+                            int(vlan_groups[1])+1)]
+        return new_ifacenames
+
+    def _preprocess_ifacenames(self, ifacenames):
+        """ validates interface list for config existance.
+
+        returns -1 if one or more interface not found. else, returns 0
+
+        """
+        new_ifacenames = []
+        err_iface = ''
+        for i in ifacenames:
+            ifaceobjs = self.get_ifaceobjs(i)
+            if not ifaceobjs:
+                # if name not available, render interface name and check again
+                rendered_ifacenames = utils.expand_iface_range(i)
+                if rendered_ifacenames:
+                    for ri in rendered_ifacenames:
+                        ifaceobjs = self.get_ifaceobjs(ri)
+                        if not ifaceobjs:
+                            err_iface += ' ' + ri
+                        else:
+                            new_ifacenames.append(ri)
+                else:
+                    err_iface += ' ' + i
+            else:
+                new_ifacenames.append(i)
+        if err_iface:
+            raise Exception('cannot find interfaces:%s' %err_iface)
+        return new_ifacenames
+
+    def _iface_whitelisted(self, auto, allow_classes, excludepats, ifacename):
+        """ Checks if interface is whitelisted depending on set of parameters.
+
+        interfaces are checked against the allow_classes and auto lists.
+
+        """
+
+        ret = True
+
+           # Check if interface matches the exclude patter
+        if excludepats:
+            for e in excludepats:
+                if re.search(e, ifacename):
+                    ret = False
+        ifaceobjs = self.get_ifaceobjs(ifacename)
+        if not ifaceobjs:
+            if ret:
+                self.logger.debug('iface %s' %ifacename + ' not found')
+            return ret
+        # If matched exclude pattern, return false
+        if not ret:
+            for i in ifaceobjs:
+                i.blacklisted = True
+                self.blacklisted_ifaces_present = True
+            return ret
+        # Check if interface belongs to the class
+        # the user is interested in, if not return false
+        if allow_classes:
+            ret = False
+            for i in ifaceobjs:
+                if i.classes:
+                    common = Set(allow_classes).intersection(
+                                Set(i.classes))
+                    if common:
+                        ret = True
+            if not ret:
+                # If a class was requested and interface does not belong
+                # to the class, only then mark the ifaceobjs as blacklisted
+                self.blacklisted_ifaces_present = True
+                for i in ifaceobjs:
+                    i.blacklisted = True
+            return ret
+        # If the user has requested auto class, check if the interface
+        # is marked auto
+        if auto:
+            ret = False
+            for i in ifaceobjs:
+                if i.auto:
+                    ret = True
+            if not ret:
+                # If auto was requested and interface was not marked auto,
+                # only then mark all of them as blacklisted
+                self.blacklisted_ifaces_present = True
+                for i in ifaceobjs:
+                    i.blacklisted = True
+        return ret
+
+    def _compat_conv_op_to_mode(self, op):
+        """ Returns old op name to work with existing scripts """
+        if 'up' in op:
+            return 'start'
+        elif 'down' in op:
+            return 'stop'
+        else:
+            return op
+
+    def generate_running_env(self, ifaceobj, op):
+        """ Generates a dictionary with env variables required for
+        an interface. Used to support script execution for interfaces.
+        """
+
+        cenv = None
+        iface_env = ifaceobj.get_env()
+        if iface_env:
+            cenv = os.environ
+            if cenv:
+                cenv.update(iface_env)
+            else:
+                cenv = iface_env
+        else:
+            cenv = {}
+        cenv['MODE'] = self._compat_conv_op_to_mode(op)
+        cenv['PHASE'] = op
+
+        return cenv
+
+    def _save_state(self):
+        if (not self.flags.STATEMANAGER_ENABLE or
+            not self.flags.STATEMANAGER_UPDATE):
+            return
+        try:
+            # Update persistant iface states
+            self.statemanager.save_state()
+        except Exception, e:
+            if self.logger.isEnabledFor(logging.DEBUG):
+                t = sys.exc_info()[2]
+                traceback.print_tb(t)
+                self.logger.warning('error saving state (%s)' %str(e))
+
+    def set_type(self, type):
+        if type == 'iface':
+            self.type = ifaceType.IFACE
+        elif type == 'vlan':
+            self.type = ifaceType.BRIDGE_VLAN
+        else:
+            self.type = ifaceType.UNKNOWN
+
+    def _process_delay_admin_state_queue(self, op):
+        if not self._delay_admin_state_iface_queue:
+           return
+        if op == 'up':
+           func = self.link_up
+        elif op == 'down':
+           func = self.link_down
+        else:
+           return
+        for i in self._delay_admin_state_iface_queue:
+            try:
+                if self.link_exists(i):
+                   func(i)
+            except Exception, e:
+                self.logger.warn(str(e))
+                pass
+
+    def up(self, ops, auto=False, allow_classes=None, ifacenames=None,
+           excludepats=None, printdependency=None, syntaxcheck=False,
+           type=None, skipupperifaces=False):
+        """This brings the interface(s) up
+
+        Args:
+            ops (list): list of ops to perform on the interface(s).
+            Eg: ['pre-up', 'up', 'post-up'
+
+        Kwargs:
+            auto (bool): act on interfaces marked auto
+            allow_classes (list): act on interfaces belonging to classes in the list
+            ifacenames (list): act on interfaces specified in this list
+            excludepats (list): list of patterns of interfaces to exclude
+            syntaxcheck (bool): only perform syntax check
+        """
+
+        self.set_type(type)
+
+        if allow_classes:
+            ifupdownflags.flags.CLASS = True
+        if not self.flags.ADDONS_ENABLE:
+            self.flags.STATEMANAGER_UPDATE = False
+        if auto:
+            ifupdownflags.flags.ALL = True
+            ifupdownflags.flags.WITH_DEPENDS = True
+        try:
+            iface_read_ret = self.read_iface_config()
+        except Exception:
+            raise
+
+        filtered_ifacenames = None
+        if ifacenames:
+            ifacenames = self._preprocess_ifacenames(ifacenames)
+
+            if allow_classes:
+                filtered_ifacenames = self._get_filtered_ifacenames_with_classes(auto, allow_classes, excludepats, ifacenames)
+
+        # if iface list not given by user, assume all from config file
+        if not ifacenames: ifacenames = self.ifaceobjdict.keys()
+
+        if not filtered_ifacenames:
+            # filter interfaces based on auto and allow classes
+            filtered_ifacenames = [i for i in ifacenames
+                                    if self._iface_whitelisted(auto, allow_classes,
+                                                excludepats, i)]
+
+        if not filtered_ifacenames:
+            raise Exception('no ifaces found matching given allow lists')
+
+        if printdependency:
+            self.populate_dependency_info(ops, filtered_ifacenames)
+            self.print_dependency(filtered_ifacenames, printdependency)
+            return
+        else:
+            self.populate_dependency_info(ops)
+
+        # If only syntax check was requested, return here.
+        # return here because we want to make sure most
+        # errors above are caught and reported.
+        if syntaxcheck:
+            if not self._module_syntax_check(filtered_ifacenames):
+                raise Exception()
+            if not iface_read_ret:
+                raise Exception()
+            elif self._any_iface_errors(filtered_ifacenames):
+                raise Exception()
+            return
+
+        ret = None
+        try:
+            ret = self._sched_ifaces(filtered_ifacenames, ops,
+                                     skipupperifaces=skipupperifaces,
+                                     followdependents=True
+                                     if ifupdownflags.flags.WITH_DEPENDS
+                                     else False)
+        finally:
+            self._process_delay_admin_state_queue('up')
+            if not ifupdownflags.flags.DRYRUN and self.flags.ADDONS_ENABLE:
+                self._save_state()
+
+        if not iface_read_ret or not ret:
+            raise Exception()
+
+    def _get_filtered_ifacenames_with_classes(self, auto, allow_classes, excludepats, ifacenames):
+        # if user has specified ifacelist and allow_classes
+        # append the allow_classes interfaces to user
+        # ifacelist
+        filtered_ifacenames = [i for i in self.ifaceobjdict.keys()
+                               if self._iface_whitelisted(auto, allow_classes,
+                                                          excludepats, i)]
+        filtered_ifacenames += ifacenames
+
+        for intf in ifacenames:
+            for obj in self.get_ifaceobjs(intf) or []:
+                obj.blacklisted = False
+
+        return filtered_ifacenames
+
+    def down(self, ops, auto=False, allow_classes=None, ifacenames=None,
+             excludepats=None, printdependency=None, usecurrentconfig=False,
+             type=None):
+        """ down an interface """
+
+        self.set_type(type)
+
+        if allow_classes:
+            ifupdownflags.flags.CLASS = True
+        if not self.flags.ADDONS_ENABLE:
+            self.flags.STATEMANAGER_UPDATE = False
+        if auto:
+            ifupdownflags.flags.ALL = True
+            ifupdownflags.flags.WITH_DEPENDS = True
+        # For down we need to look at old state, unless usecurrentconfig
+        # is set
+        if (not usecurrentconfig and self.flags.STATEMANAGER_ENABLE and
+                    self.statemanager.ifaceobjdict):
+            # Since we are using state manager objects,
+            # skip the updating of state manager objects
+            self.logger.debug('Looking at old state ..')
+            self.read_old_iface_config()
+        else:
+            # If no old state available
+            try:
+                self.read_iface_config()
+            except Exception, e:
+                raise Exception('error reading iface config (%s)' %str(e))
+        filtered_ifacenames = None
+        if ifacenames:
+            # If iface list is given by the caller, always check if iface
+            # is present
+            try:
+               ifacenames = self._preprocess_ifacenames(ifacenames)
+
+               if allow_classes:
+                   filtered_ifacenames = self._get_filtered_ifacenames_with_classes(auto, allow_classes, excludepats, ifacenames)
+
+            except Exception, e:
+               raise Exception('%s' %str(e) +
+                       ' (interface was probably never up ?)')
+
+
+        # if iface list not given by user, assume all from config file
+        if not ifacenames: ifacenames = self.ifaceobjdict.keys()
+
+        if not filtered_ifacenames:
+            # filter interfaces based on auto and allow classes
+            filtered_ifacenames = [i for i in ifacenames
+                                   if self._iface_whitelisted(auto, allow_classes,
+                                                    excludepats, i)]
+
+        if not filtered_ifacenames:
+            raise Exception('no ifaces found matching given allow lists ' +
+                    '(or interfaces were probably never up ?)')
+
+        if printdependency:
+            self.populate_dependency_info(ops, filtered_ifacenames)
+            self.print_dependency(filtered_ifacenames, printdependency)
+            return
+        else:
+            self.populate_dependency_info(ops)
+
+        try:
+            self._sched_ifaces(filtered_ifacenames, ops,
+                               followdependents=True
+                               if ifupdownflags.flags.WITH_DEPENDS else False)
+        finally:
+            self._process_delay_admin_state_queue('down')
+            if not ifupdownflags.flags.DRYRUN and self.flags.ADDONS_ENABLE:
+                self._save_state()
+
+    def query(self, ops, auto=False, format_list=False, allow_classes=None,
+              ifacenames=None,
+              excludepats=None, printdependency=None,
+              format='native', type=None):
+        """ query an interface """
+
+        self.set_type(type)
+
+        # Let us forget internal squashing when it comes to
+        # ifquery. It can surprise people relying of ifquery
+        # output
+        self._ifaceobj_squash_internal = False
+
+        if allow_classes:
+            ifupdownflags.flags.CLASS = True
+        if self.flags.STATEMANAGER_ENABLE and ops[0] == 'query-savedstate':
+            return self.statemanager.dump_pretty(ifacenames)
+        self.flags.STATEMANAGER_UPDATE = False
+        if auto:
+            self.logger.debug('setting flag ALL')
+            ifupdownflags.flags.ALL = True
+            ifupdownflags.flags.WITH_DEPENDS = True
+
+        if ops[0] == 'query-syntax':
+            self._modules_help(format)
+            return
+        elif ops[0] == 'query-running':
+            # create fake devices to all dependents that dont have config
+            map(lambda i: self.create_n_save_ifaceobj(i,
+                                ifacePrivFlags(False, True)), ifacenames)
+        else:
+            try:
+                self.read_iface_config()
+            except Exception:
+                raise
+
+        if ifacenames and ops[0] != 'query-running':
+            # If iface list is given, always check if iface is present
+            ifacenames = self._preprocess_ifacenames(ifacenames)
+
+        if allow_classes:
+            filtered_ifacenames = self._get_filtered_ifacenames_with_classes(auto, allow_classes, excludepats, ifacenames)
+
+        # if iface list not given by user, assume all from config file
+        if not ifacenames: ifacenames = self.ifaceobjdict.keys()
+
+        # filter interfaces based on auto and allow classes
+        if ops[0] == 'query-running':
+            filtered_ifacenames = ifacenames
+        elif not allow_classes:
+            filtered_ifacenames = [
+                i for i in ifacenames
+                if self._iface_whitelisted(
+                    auto,
+                    allow_classes,
+                    excludepats, i
+                )
+            ]
+
+        if not filtered_ifacenames:
+                raise Exception('no ifaces found matching ' +
+                        'given allow lists')
+
+        self.populate_dependency_info(ops)
+        if ops[0] == 'query-dependency' and printdependency:
+            self.print_dependency(filtered_ifacenames, printdependency)
+            return
+
+        if format_list and (ops[0] == 'query' or ops[0] == 'query-raw'):
+            return self.print_ifaceobjs_list(filtered_ifacenames)
+
+        if ops[0] == 'query' and not ifupdownflags.flags.WITHDEFAULTS:
+            return self.print_ifaceobjs_pretty(filtered_ifacenames, format)
+        elif ops[0] == 'query-raw':
+            return self.print_ifaceobjs_raw(filtered_ifacenames)
+
+        ret = self._sched_ifaces(filtered_ifacenames, ops,
+                                 followdependents=True
+                           if ifupdownflags.flags.WITH_DEPENDS else False)
+
+        if ops[0] == 'query' and ifupdownflags.flags.WITHDEFAULTS:
+            return self.print_ifaceobjs_pretty(filtered_ifacenames, format)
+        elif ops[0] == 'query-checkcurr':
+            ret = self.print_ifaceobjscurr_pretty(filtered_ifacenames, format)
+            if ret != 0:
+                # if any of the object has an error, signal that silently
+                raise Exception('')
+        elif ops[0] == 'query-running':
+            self.print_ifaceobjsrunning_pretty(filtered_ifacenames, format)
+            return
+
+    def _reload_currentlyup(self, upops, downops, auto=False, allow=None,
+            ifacenames=None, excludepats=None, usecurrentconfig=False,
+            syntaxcheck=False, **extra_args):
+        """ reload currently up interfaces """
+        new_ifaceobjdict = {}
+
+        self.logger.info('reloading interfaces that are currently up ..')
+
+        try:
+            iface_read_ret = self.read_iface_config()
+        except:
+            raise
+        if not self.ifaceobjdict:
+            self.logger.warn("nothing to reload ..exiting.")
+            return
+        already_up_ifacenames = []
+        if not ifacenames: ifacenames = self.ifaceobjdict.keys()
+
+        if (not usecurrentconfig and self.flags.STATEMANAGER_ENABLE
+                and self.statemanager.ifaceobjdict):
+            already_up_ifacenames = self.statemanager.ifaceobjdict.keys()
+
+        # Get already up interfaces that still exist in the interfaces file
+        already_up_ifacenames_not_present = Set(
+                        already_up_ifacenames).difference(ifacenames)
+        already_up_ifacenames_still_present = Set(
+                        already_up_ifacenames).difference(
+                        already_up_ifacenames_not_present)
+
+        interfaces_to_up = already_up_ifacenames_still_present
+
+        # generate dependency graph of interfaces
+        self.populate_dependency_info(upops, interfaces_to_up)
+
+        # If only syntax check was requested, return here.
+        # return here because we want to make sure most
+        # errors above are caught and reported.
+        if syntaxcheck:
+            if not self._module_syntax_check(interfaces_to_up):
+                raise Exception()
+            if not iface_read_ret:
+                raise Exception()
+            elif self._any_iface_errors(interfaces_to_up):
+                raise Exception()
+            return
+
+        if (already_up_ifacenames_not_present and
+                self.config.get('ifreload_currentlyup_down_notpresent') == '1'):
+           self.logger.info('reload: schedule down on interfaces: %s'
+                            %str(already_up_ifacenames_not_present))
+
+           # Save a copy of new iface objects and dependency_graph
+           new_ifaceobjdict = dict(self.ifaceobjdict)
+           new_dependency_graph = dict(self.dependency_graph)
+
+           # old interface config is read into self.ifaceobjdict
+           self.read_old_iface_config()
+
+           # reinitialize dependency graph
+           self.dependency_graph = OrderedDict({})
+           falready_up_ifacenames_not_present = [i for i in
+                                    already_up_ifacenames_not_present
+                                    if self._iface_whitelisted(auto, allow,
+                                    excludepats, i)]
+           self.populate_dependency_info(downops,
+                                         falready_up_ifacenames_not_present)
+           self._sched_ifaces(falready_up_ifacenames_not_present, downops,
+                              followdependents=False, sort=True)
+        else:
+           self.logger.info('no interfaces to down ..')
+
+        # Now, run 'up' with new config dict
+        # reset statemanager update flag to default
+        if auto:
+            ifupdownflags.flags.ALL = True
+            ifupdownflags.flags.WITH_DEPENDS = True
+        if new_ifaceobjdict:
+            # and now, ifaceobjdict is back to current config
+            self.ifaceobjdict = new_ifaceobjdict
+            self.dependency_graph = new_dependency_graph
+
+        if not self.ifaceobjdict:
+            self.logger.info('no interfaces to up')
+            return
+        self.logger.info('reload: scheduling up on interfaces: %s'
+                         %str(interfaces_to_up))
+        ret = self._sched_ifaces(interfaces_to_up, upops,
+                                 followdependents=True
+                                 if ifupdownflags.flags.WITH_DEPENDS else False)
+        if ifupdownflags.flags.DRYRUN:
+            return
+        self._save_state()
+
+        if not iface_read_ret or not ret:
+            raise Exception()
+
+    def _reload_default(self, upops, downops, auto=False, allow=None,
+            ifacenames=None, excludepats=None, usecurrentconfig=False,
+            syntaxcheck=False, **extra_args):
+        """ reload interface config """
+        new_ifaceobjdict = {}
+
+        try:
+            iface_read_ret = self.read_iface_config()
+        except:
+            raise
+
+        if not self.ifaceobjdict:
+            self.logger.warn("nothing to reload ..exiting.")
+            return
+
+        if not ifacenames: ifacenames = self.ifaceobjdict.keys()
+        new_filtered_ifacenames = [i for i in ifacenames
+                               if self._iface_whitelisted(auto, allow,
+                               excludepats, i)]
+        # generate dependency graph of interfaces
+        self.populate_dependency_info(upops)
+
+        # If only syntax check was requested, return here.
+        # return here because we want to make sure most
+        # errors above are caught and reported.
+        if syntaxcheck:
+            if not self._module_syntax_check(new_filtered_ifacenames):
+                raise Exception()
+            if not iface_read_ret:
+                raise Exception()
+            elif self._any_iface_errors(new_filtered_ifacenames):
+                raise Exception()
+            return
+
+        if (not usecurrentconfig and self.flags.STATEMANAGER_ENABLE
+                and self.statemanager.ifaceobjdict):
+            # Save a copy of new iface objects and dependency_graph
+            new_ifaceobjdict = dict(self.ifaceobjdict)
+            new_dependency_graph = dict(self.dependency_graph)
+
+            self.ifaceobjdict = OrderedDict({})
+            self.dependency_graph = OrderedDict({})
+
+            # if old state is present, read old state and mark op for 'down'
+            # followed by 'up' aka: reload
+            # old interface config is read into self.ifaceobjdict
+            self.read_old_iface_config()
+            op = 'reload'
+        else:
+            # oldconfig not available, continue with 'up' with new config
+            op = 'up'
+            new_ifaceobjdict = self.ifaceobjdict
+            new_dependency_graph = self.dependency_graph
+
+        if op == 'reload' and ifacenames:
+            ifacenames = self.ifaceobjdict.keys()
+            old_filtered_ifacenames = [i for i in ifacenames
+                               if self._iface_whitelisted(auto, allow,
+                               excludepats, i)]
+
+            # generate dependency graph of old interfaces,
+            # This should make sure built in interfaces are
+            # populated. disable check shared dependents as an optimization.
+            # these are saved interfaces and dependency for these
+            # have been checked before they became part of saved state.
+            try:
+                self.flags.CHECK_SHARED_DEPENDENTS = False
+                self.populate_dependency_info(upops)
+                self.flags.CHECK_SHARED_DEPENDENTS = True
+            except Exception, e:
+                self.logger.info("error generating dependency graph for "
+                                 "saved interfaces (%s)" %str(e))
+                pass
+
+            # make sure we pick up built-in interfaces
+            # if config file had 'ifreload_down_changed' variable
+            # set, also look for interfaces that changed to down them
+            down_changed = int(self.config.get('ifreload_down_changed', '1'))
+
+            # Generate the interface down list
+            # Interfaces that go into the down list:
+            #   - interfaces that were present in last config and are not
+            #     present in the new config
+            #   - interfaces that were changed between the last and current
+            #     config
+            ifacedownlist = []
+            for ifname in self.ifaceobjdict.keys():
+                lastifaceobjlist = self.ifaceobjdict.get(ifname)
+                if not self.is_ifaceobj_builtin(lastifaceobjlist[0]):
+                    # if interface is not built-in and is not in
+                    # old filtered ifacenames
+                    if ifname not in old_filtered_ifacenames:
+                        continue
+                objidx = 0
+                # If interface is not present in the new file
+                # append it to the down list
+                newifaceobjlist = new_ifaceobjdict.get(ifname)
+                if not newifaceobjlist:
+                    ifacedownlist.append(ifname)
+                    continue
+                # If ifaceobj was present in the old interfaces file,
+                # and does not have a config in the new interfaces file
+                # but has been picked up as a dependent of another
+                # interface, catch it here. This catches a common error
+                # for example: remove a bond section from the interfaces
+                # file, but leave it around as a bridge port
+                # XXX: Ideally its better to just add it to the
+                # ifacedownlist. But we will be cautious here
+                # and just print a warning
+                if (self.is_ifaceobj_noconfig(newifaceobjlist[0]) and
+                    not self.is_ifaceobj_builtin(newifaceobjlist[0]) and
+                    lastifaceobjlist[0].is_config_present() and
+                    lastifaceobjlist[0].link_kind):
+                    self.logger.warn('%s: misconfig ? removed but still exists '
+                                     'as a dependency of %s.\nPlease remove '
+                                     'the dependency manually `ifdown %s` if '
+                                     'it is being picked up as part of a regex'
+                                     % (newifaceobjlist[objidx].name,
+                                        str(newifaceobjlist[objidx].upperifaces),
+                                        newifaceobjlist[objidx].name))
+                if (lastifaceobjlist[0].link_kind and
+                    not newifaceobjlist[0].link_kind):
+                    self.logger.warn('%s: moved from being a %s to a'
+                                     ' physical interface (non-logical interface).'
+                                     'This interface will be downed.\n'
+                                     ' If this was not intentional, please restore the'
+                                     ' original interface definition and execute ifreload'
+                                     % (newifaceobjlist[objidx].name,
+                                        ifaceLinkKind.to_str(lastifaceobjlist[0].link_kind)))
+                    ifacedownlist.append(newifaceobjlist[objidx].name)
+                if not down_changed:
+                    continue
+                if len(newifaceobjlist) != len(lastifaceobjlist):
+                    ifacedownlist.append(ifname)
+                    continue
+
+                # If interface has changed between the current file
+                # and the last installed append it to the down list
+                # compare object list
+                for objidx in range(0, len(lastifaceobjlist)):
+                    oldobj = lastifaceobjlist[objidx]
+                    newobj = newifaceobjlist[objidx]
+                    if not newobj.compare(oldobj):
+                        ifacedownlist.append(ifname)
+                        continue
+
+            if ifacedownlist:
+                self.logger.info('reload: scheduling down on interfaces: %s'
+                                  %str(ifacedownlist))
+                # reinitialize dependency graph
+                self.dependency_graph = OrderedDict({})
+
+                # Generate dependency info for old config
+                self.flags.CHECK_SHARED_DEPENDENTS = False
+                self.populate_dependency_info(downops, ifacedownlist)
+                self.flags.CHECK_SHARED_DEPENDENTS = True
+
+                try:
+                    # XXX: Hack to skip checking upperifaces during down.
+                    # the dependency list is not complete here
+                    # and we dont want to down the upperiface.
+                    # Hence during reload, set  this to true.
+                    # This is being added to avoid a failure in
+                    # scheduler._check_upperifaces when we are dowing
+                    # a builtin bridge port
+                    self.flags.SCHED_SKIP_CHECK_UPPERIFACES = True
+                    self._sched_ifaces(ifacedownlist, downops,
+                                       followdependents=False,
+                                       sort=True)
+                except Exception, e:
+                    self.logger.error(str(e))
+                    pass
+                finally:
+                    self.flags.SCHED_SKIP_CHECK_UPPERIFACES = False
+                    self._process_delay_admin_state_queue('down')
+            else:
+                self.logger.info('no interfaces to down ..')
+
+        # Now, run 'up' with new config dict
+        # reset statemanager update flag to default
+        if not new_ifaceobjdict:
+            self.logger.debug('no interfaces to up')
+            return
+
+        if auto:
+            ifupdownflags.flags.ALL = True
+            ifupdownflags.flags.WITH_DEPENDS = True
+        # and now, we are back to the current config in ifaceobjdict
+        self.ifaceobjdict = new_ifaceobjdict
+        self.dependency_graph = new_dependency_graph
+
+        self.logger.info('reload: scheduling up on interfaces: %s'
+                         %str(new_filtered_ifacenames))
+        ifupdownflags.flags.CACHE = True
+        try:
+            ret = self._sched_ifaces(new_filtered_ifacenames, upops,
+                                     followdependents=True
+                                     if ifupdownflags.flags.WITH_DEPENDS
+                                     else False)
+        except Exception, e:
+            ret = None
+            self.logger.error(str(e))
+        finally:
+            self._process_delay_admin_state_queue('up')
+        if ifupdownflags.flags.DRYRUN:
+            return
+        self._save_state()
+
+        if not iface_read_ret or not ret:
+            raise Exception()
+
+    def reload(self, *args, **kargs):
+        """ reload interface config """
+        self.logger.debug('reloading interface config ..')
+        if kargs.get('currentlyup', False):
+            self._reload_currentlyup(*args, **kargs)
+        else:
+            self._reload_default(*args, **kargs)
+
+    def _any_iface_errors(self, ifacenames):
+        for i in ifacenames:
+            ifaceobjs = self.get_ifaceobjs(i)
+            if not ifaceobjs: continue
+            for ifaceobj in ifaceobjs:
+                if (ifaceobj.status == ifaceStatus.NOTFOUND or
+                    ifaceobj.status == ifaceStatus.ERROR):
+                    return True
+        return False
+
+    def _pretty_print_ordered_dict(self, prefix, argdict):
+        outbuf = prefix + ' {\n'
+        for k, vlist in argdict.items():
+            outbuf += '\t%s : %s\n' %(k, str(vlist))
+        self.logger.debug(outbuf + '}')
+
+    def print_dependency(self, ifacenames, format):
+        """ prints iface dependency information """
+
+        if not ifacenames:
+            ifacenames = self.ifaceobjdict.keys()
+        if format == 'list':
+            for k,v in self.dependency_graph.items():
+                print '%s : %s' %(k, str(v))
+        elif format == 'dot':
+            indegrees = {}
+            map(lambda i: indegrees.update({i :
+                self.get_iface_refcnt(i)}),
+                self.dependency_graph.keys())
+            graph.generate_dots(self.dependency_graph, indegrees)
+
+    def print_ifaceobjs_list(self, ifacenames):
+        for i in ifacenames:
+            print i
+
+    def print_ifaceobjs_raw(self, ifacenames):
+        """ prints raw lines for ifaces from config file """
+
+        for i in ifacenames:
+            for ifaceobj in self.get_ifaceobjs(i):
+                if self.is_ifaceobj_builtin(ifaceobj):
+                    continue
+                ifaceobj.dump_raw(self.logger)
+                if (ifupdownflags.flags.WITH_DEPENDS and
+                    not ifupdownflags.flags.ALL):
+                    dlist = ifaceobj.lowerifaces
+                    if not dlist: continue
+                    self.print_ifaceobjs_raw(dlist)
+
+    def _get_ifaceobjs_pretty(self, ifacenames, ifaceobjs, running=False):
+        """ returns iface obj list """
+
+        for i in ifacenames:
+            for ifaceobj in self.get_ifaceobjs(i):
+                if ((not running and self.is_ifaceobj_noconfig(ifaceobj)) or
+                    (running and not ifaceobj.is_config_present() and
+                     not self.is_iface_builtin_byname(i) and
+                     not ifaceobj.upperifaces)):
+                    continue
+                ifaceobjs.append(ifaceobj)
+                if (ifupdownflags.flags.WITH_DEPENDS and
+                    not ifupdownflags.flags.ALL):
+                    dlist = ifaceobj.lowerifaces
+                    if not dlist: continue
+                    self._get_ifaceobjs_pretty(dlist, ifaceobjs, running)
+
+    def print_ifaceobjs_pretty(self, ifacenames, format='native'):
+        """ pretty prints iface in format given by keyword arg format """
+
+        ifaceobjs = []
+        self._get_ifaceobjs_pretty(ifacenames, ifaceobjs)
+        if not ifaceobjs: return
+        if format == 'json':
+            print json.dumps(ifaceobjs, cls=ifaceJsonEncoder,
+                             indent=4, separators=(',', ': '))
+        else:
+            expand = int(self.config.get('ifquery_ifacename_expand_range', '0'))
+            for i in ifaceobjs:
+                if not expand and (i.flags & iface.IFACERANGE_ENTRY):
+                    # print only the first one
+                    if i.flags & iface.IFACERANGE_START:
+                       i.dump_pretty(use_realname=True)
+                else:
+                    i.dump_pretty()
+
+    def _get_ifaceobjscurr_pretty(self, ifacenames, ifaceobjs):
+        ret = 0
+        for i in ifacenames:
+            ifaceobjscurr = self.get_ifaceobjcurr(i)
+            if not ifaceobjscurr: continue
+            for ifaceobj in ifaceobjscurr:
+                if (ifaceobj.status == ifaceStatus.NOTFOUND or
+                    ifaceobj.status == ifaceStatus.ERROR):
+                    ret = 1
+                if self.is_ifaceobj_noconfig(ifaceobj):
+                    continue
+                ifaceobjs.append(ifaceobj)
+                if (ifupdownflags.flags.WITH_DEPENDS and
+                    not ifupdownflags.flags.ALL):
+                    dlist = ifaceobj.lowerifaces
+                    if not dlist: continue
+                    dret = self._get_ifaceobjscurr_pretty(dlist, ifaceobjs)
+                    if dret: ret = 1
+        return ret
+
+    def print_ifaceobjscurr_pretty(self, ifacenames, format='native'):
+        """ pretty prints current running state of interfaces with status.
+
+        returns 1 if any of the interface has an error,
+        else returns 0
+        """
+
+        ifaceobjs = []
+        ret = self._get_ifaceobjscurr_pretty(ifacenames, ifaceobjs)
+        if not ifaceobjs: return
+
+        # override ifaceStatusUserStrs
+        ifaceStatusUserStrs.SUCCESS = self.config.get('ifquery_check_success_str', _success_sym)
+        ifaceStatusUserStrs.ERROR = self.config.get('ifquery_check_error_str', _error_sym)
+        ifaceStatusUserStrs.UNKNOWN = self.config.get('ifquery_check_unknown_str', '')
+        if format == 'json':
+            print json.dumps(ifaceobjs, cls=ifaceJsonEncoderWithStatus,
+                             indent=2, separators=(',', ': '))
+        else:
+            map(lambda i: i.dump_pretty(with_status=True), ifaceobjs)
+        return ret
+
+    def print_ifaceobjsrunning_pretty(self, ifacenames, format='native'):
+        """ pretty prints iface running state """
+
+        ifaceobjs = []
+        self._get_ifaceobjs_pretty(ifacenames, ifaceobjs, running=True)
+        if not ifaceobjs: return
+        if format == 'json':
+            print json.dumps(ifaceobjs, cls=ifaceJsonEncoder, indent=2,
+                       separators=(',', ': '))
+        else:
+            map(lambda i: i.dump_pretty(), ifaceobjs)
+
+    def _dump(self):
+        print 'ifupdown main object dump'
+        print self.pp.pprint(self.modules)
+        print self.pp.pprint(self.ifaceobjdict)
+
+    def _dump_ifaceobjs(self, ifacenames):
+        for i in ifacenames:
+            ifaceobjs = self.get_ifaceobjs(i)
+            for i in ifaceobjs:
+                i.dump(self.logger)
+                print '\n'
diff --git a/ifupdown2/ifupdown/log.py b/ifupdown2/ifupdown/log.py
new file mode 100644 (file)
index 0000000..732c68a
--- /dev/null
@@ -0,0 +1,183 @@
+#!/usr/bin/env python
+#
+# Copyright 2016-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Julien Fortin, julien@cumulusnetworks.com
+#
+
+import sys
+import json
+import struct
+import select
+import logging
+import logging.handlers
+
+from cStringIO import StringIO
+
+
+class Log:
+    LOGGER_NAME = sys.argv[0].split('/')[-1]
+
+    def __init__(self):
+        """
+            - On start the daemon will log on syslog.
+            - For each client commands we might need to adjust the target
+            (stderr/stdout):
+                if -v --verbose or -d --debug are provided we override
+                sys.stdout and sys.stderr with string buffers to be able to send
+                back the content of these buffer on the UNIX socket back to the
+                client.
+                if -l or --syslog we make sure to use syslog.
+        """
+
+        self.stdout_buffer = None
+        self.stderr_buffer = None
+
+        self.root = logging.getLogger()
+        self.root.name = Log.LOGGER_NAME
+        self.root.setLevel(logging.INFO)
+
+        self.root_info = self.root.info
+        self.root_debug = self.root.debug
+        self.root_error = self.root.error
+        self.root_warning = self.root.warning
+        self.root_critical = self.root.critical
+
+        self.root.info = self.info
+        self.root.debug = self.debug
+        self.root.error = self.error
+        self.root.warning = self.warning
+        self.root.critical = self.critical
+
+        logging.addLevelName(logging.CRITICAL, 'critical')
+        logging.addLevelName(logging.WARNING, 'warning')
+        logging.addLevelName(logging.ERROR, 'error')
+        logging.addLevelName(logging.DEBUG, 'debug')
+        logging.addLevelName(logging.INFO, 'info')
+
+        self.syslog = True
+        self.socket = None
+
+        # syslog
+        facility = logging.handlers.SysLogHandler.LOG_DAEMON
+        address = '/dev/log'
+        format = '%(name)s: %(levelname)s: %(message)s'
+
+        self.syslog_handler = logging.handlers.SysLogHandler(address=address, facility=facility)
+        self.syslog_handler.setFormatter(logging.Formatter(format))
+
+        # console
+        format = '%(levelname)s: %(message)s'
+        self.console_handler = logging.StreamHandler(sys.stderr)
+        self.console_handler.setFormatter(logging.Formatter(format))
+
+        if self.LOGGER_NAME[-1] == 'd':
+            self.update_current_logger(syslog=True, verbose=True, debug=False)
+        else:
+            self.update_current_logger(syslog=False, verbose=False, debug=False)
+
+    def update_current_logger(self, syslog, verbose, debug):
+        self.syslog = syslog
+        self.root.setLevel(self.get_log_level(verbose=verbose, debug=debug))
+        self.root.handlers = [self.syslog_handler if self.syslog else self.console_handler]
+        self.flush()
+
+    def flush(self):
+        if self.socket:
+            result = dict()
+            stdout = self._flush_buffer('stdout', self.stdout_buffer, result)
+            stderr = self._flush_buffer('stderr', self.stderr_buffer, result)
+            if stdout or stderr:
+                try:
+                    self.tx_data(json.dumps(result))
+                    self.redirect_stdouput()
+                except select.error as e:
+                    # haven't seen the case yet
+                    self.socket = None
+                    self.update_current_logger(syslog=True, verbose=True)
+                    self.critical(str(e))
+                    exit(84)
+        self.console_handler.flush()
+        self.syslog_handler.flush()
+
+    def tx_data(self, data, socket=None):
+        socket_obj = socket if socket else self.socket
+        ready = select.select([], [socket_obj], [])
+        if ready and ready[1] and ready[1][0] == socket_obj:
+            frmt = "=%ds" % len(data)
+            packed_msg = struct.pack(frmt, data)
+            packed_hdr = struct.pack('=I', len(packed_msg))
+            socket_obj.sendall(packed_hdr)
+            socket_obj.sendall(packed_msg)
+
+    def set_socket(self, socket):
+        self.socket = socket
+        self.redirect_stdouput()
+
+    def redirect_stdouput(self):
+        self.stdout_buffer = sys.stdout = StringIO()
+        self.stderr_buffer = self.console_handler.stream = sys.stderr = StringIO()
+
+    def error(self, msg, *args, **kwargs):
+        self.root_error(msg, *args, **kwargs)
+        self.flush()
+
+    def critical(self, msg, *args, **kwargs):
+        self.root_critical(msg, *args, **kwargs)
+        self.flush()
+
+    def warning(self, msg, *args, **kwargs):
+        self.root_warning(msg, *args, **kwargs)
+        self.flush()
+
+    def info(self, msg, *args, **kwargs):
+        self.root_info(msg, *args, **kwargs)
+        self.flush()
+
+    def debug(self, msg, *args, **kwargs):
+        self.root_debug(msg, *args, **kwargs)
+        self.flush()
+
+    def get_current_log_level(self):
+        return self.root.level
+
+    def is_syslog(self): return self.syslog
+
+    @staticmethod
+    def get_log_level(verbose=False, debug=False):
+        log_level = logging.WARNING
+        if debug:
+            log_level = logging.DEBUG
+        elif verbose:
+            log_level = logging.INFO
+        return log_level
+
+    @staticmethod
+    def _flush_buffer(stream, buff, dictionary):
+        if buff:
+            data = buff.getvalue()
+            if data:
+                dictionary[stream] = data
+                return True
+
+
+log = Log()
+
+
+"""
+
+#logging.basicConfig( format="%(filename)s: %(username)s says '%(message)s' in %(funcname)s" )
+
+Logger.debug(msg, *args, **kwargs)
+Logs a message with level DEBUG on this logger. The msg is the message format string, and the args are the arguments which are merged into msg using the string formatting operator. (Note that this means that you can use keywords in the format string, together with a single dictionary argument.)
+
+There are two keyword arguments in kwargs which are inspected: exc_info which, if it does not evaluate as false, causes exception information to be added to the logging message. If an exception tuple (in the format returned by sys.exc_info()) is provided, it is used; otherwise, sys.exc_info() is called to get the exception information.
+
+"""
+
+"""
+USE FILTER TO IGNORE "EXITS" MESSAGES
+Now that you know the basic plot, let me introduce one more character - the Filter.
+Filter as the name suggests, allows you to filter a message before you log it. Yes, messages are filtered based on the level setting, but adding a Filter gives you more fine grained control of messages you log.
+Both Loggers and Handlers can have multiple Filters. You can add Filters using addFilter and removeFilter methods.
+When a Logger/Handler receives a message, it consults all of its filters. If the filter(record) method on any of the Filters attached returns False (or 0) the message is dropped.
+The official documentation, though detailed, is actually pretty confusing about the role of Filters. This is a pity; because Filters can be handy when you want to drop a message based on a regular expression, error code, contextual information and pretty much anything else. The default Filter is pretty much useless (and the doc string is very confusing too). Just inherit from the default filter and override the filter method according to what you want to filter out. (Be sure to download the source for logging module and check out the unit tests which have some good examples. See the references at the end of this post.)"""
diff --git a/ifupdown2/ifupdown/main.py b/ifupdown2/ifupdown/main.py
new file mode 100644 (file)
index 0000000..e54ace8
--- /dev/null
@@ -0,0 +1,277 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Authors:
+#           Roopa Prabhu, roopa@cumulusnetworks.com
+#           Julien Fortin, julien@cumulusnetworks.com
+# ifupdown2 --
+#    tool to configure network interfaces
+#
+
+import os
+import sys
+import signal
+import StringIO
+import ConfigParser
+
+try:
+    from ifupdown2.ifupdown.log import log
+    from ifupdown2.ifupdown.argv import Parse
+    from ifupdown2.ifupdown.config import IFUPDOWN2_CONF_PATH
+    from ifupdown2.ifupdown.ifupdownmain import ifupdownMain
+except ImportError:
+    from ifupdown.log import log
+    from ifupdown.argv import Parse
+    from ifupdown.config import IFUPDOWN2_CONF_PATH
+    from ifupdown.ifupdownmain import ifupdownMain
+
+
+_SIGINT = signal.getsignal(signal.SIGINT)
+_SIGTERM = signal.getsignal(signal.SIGTERM)
+_SIGQUIT = signal.getsignal(signal.SIGQUIT)
+
+configmap_g = None
+
+
+class Ifupdown2:
+    def __init__(self, daemon, uid):
+        self.daemon = daemon
+        self.uid = uid
+        self.args = None
+        self.op = None
+
+        self.interfaces_filename = None
+        self.interfaces_file_iobuf = None
+
+        self.handlers = {
+            'up': self.run_up,
+            'down': self.run_down,
+            'query': self.run_query,
+            'reload': self.run_reload
+        }
+
+    def parse_argv(self, argv):
+        args_parse = Parse(argv)
+        args_parse.validate()
+
+        self.args = args_parse.get_args()
+        self.op = args_parse.get_op()
+
+    def update_logger(self, socket=None):
+        syslog = self.args.syslog if hasattr(self.args, 'syslog') else False
+        log.update_current_logger(syslog=syslog,
+                                  verbose=self.args.verbose,
+                                  debug=self.args.debug)
+        if socket:
+            log.set_socket(socket)
+
+    def main(self, stdin_buffer=None):
+        if self.op != 'query' and self.uid != 0:
+            raise Exception('must be root to run this command')
+
+        try:
+            self.read_config()
+            self.init(stdin_buffer)
+            self.handlers.get(self.op)(self.args)
+        except Exception, e:
+            if not str(e):
+                return 1
+                # if args and args.debug:
+                raise
+            # else:
+            if log:
+                log.error(str(e))
+            else:
+                print str(e)
+                # if args and not args.debug:
+                #    print '\nrerun the command with \'-d\' for a detailed errormsg'
+            return 1
+        return 0
+
+    def init(self, stdin_buffer):
+        if hasattr(self.args, 'interfacesfile') and self.args.interfacesfile != None:
+            # Check to see if -i option is allowed by config file
+            # But for ifquery, we will not check this
+            if (not self.op == 'query' and
+                        configmap_g.get('disable_cli_interfacesfile', '0') == '1'):
+                log.error('disable_cli_interfacesfile is set so users '
+                          'not allowed to specify interfaces file on cli.')
+                raise Exception("")
+            if self.args.interfacesfile == '-':
+                # If interfaces file is stdin, read
+                if self.daemon:
+                    self.interfaces_file_iobuf = stdin_buffer
+                else:
+                    self.interfaces_file_iobuf = sys.stdin.read()
+            else:
+                self.interfaces_filename = self.args.interfacesfile
+        else:
+            # if the ifupdown2 config file does not have it, default to standard
+            self.interfaces_filename = configmap_g.get('default_interfaces_configfile',
+                                                       '/etc/network/interfaces')
+
+    def read_config(self):
+        global configmap_g
+
+        with open(IFUPDOWN2_CONF_PATH, 'r') as f:
+            config = f.read()
+        configStr = '[ifupdown2]\n' + config
+        configFP = StringIO.StringIO(configStr)
+        parser = ConfigParser.RawConfigParser()
+        parser.readfp(configFP)
+        configmap_g = dict(parser.items('ifupdown2'))
+
+        # Preprocess config map
+        configval = configmap_g.get('multiple_vlan_aware_bridge_support', '0')
+        if configval == '0':
+            # if multiple bridges not allowed, set the bridge-vlan-aware
+            # attribute in the 'no_repeats' config, so that the ifupdownmain
+            # module can catch it appropriately
+            configmap_g['no_repeats'] = {'bridge-vlan-aware': 'yes'}
+
+        configval = configmap_g.get('link_master_slave', '0')
+        if configval == '1':
+            # link_master_slave is only valid when all is set
+            if hasattr(self.args, 'all') and not self.args.all:
+                configmap_g['link_master_slave'] = '0'
+
+        configval = configmap_g.get('delay_admin_state_change', '0')
+        if configval == '1':
+            # reset link_master_slave if delay_admin_state_change is on
+            configmap_g['link_master_slave'] = '0'
+
+    def run_up(self, args):
+        log.debug('args = %s' % str(args))
+
+        try:
+            iflist = args.iflist
+            if len(args.iflist) == 0:
+                iflist = None
+            log.debug('creating ifupdown object ..')
+            cachearg = (False if (iflist or args.nocache or args.noact)
+                        else True)
+            ifupdown_handle = ifupdownMain(daemon=self.daemon,
+                                                                 config=configmap_g,
+                                                                 force=args.force,
+                                                                 withdepends=args.withdepends,
+                                                                 perfmode=args.perfmode,
+                                                                 dryrun=args.noact,
+                                                                 cache=cachearg,
+                                                                 addons_enable=not args.noaddons,
+                                                                 statemanager_enable=not args.noaddons,
+                                                                 interfacesfile=self.interfaces_filename,
+                                                                 interfacesfileiobuf=self.interfaces_file_iobuf,
+                                                                 interfacesfileformat=args.interfacesfileformat)
+            if args.noaddons:
+                ifupdown_handle.up(['up'], args.all, args.CLASS, iflist,
+                                   excludepats=args.excludepats,
+                                   printdependency=args.printdependency,
+                                   syntaxcheck=args.syntaxcheck, type=args.type,
+                                   skipupperifaces=args.skipupperifaces)
+            else:
+                ifupdown_handle.up(['pre-up', 'up', 'post-up'],
+                                   args.all, args.CLASS, iflist,
+                                   excludepats=args.excludepats,
+                                   printdependency=args.printdependency,
+                                   syntaxcheck=args.syntaxcheck, type=args.type,
+                                   skipupperifaces=args.skipupperifaces)
+        except:
+            raise
+
+    def run_down(self, args):
+        log.debug('args = %s' % str(args))
+
+        try:
+            iflist = args.iflist
+            log.debug('creating ifupdown object ..')
+            ifupdown_handle = ifupdownMain(daemon=self.daemon,
+                                                                 config=configmap_g, force=args.force,
+                                                                 withdepends=args.withdepends,
+                                                                 perfmode=args.perfmode,
+                                                                 dryrun=args.noact,
+                                                                 addons_enable=not args.noaddons,
+                                                                 statemanager_enable=not args.noaddons,
+                                                                 interfacesfile=self.interfaces_filename,
+                                                                 interfacesfileiobuf=self.interfaces_file_iobuf,
+                                                                 interfacesfileformat=args.interfacesfileformat)
+
+            ifupdown_handle.down(['pre-down', 'down', 'post-down'],
+                                 args.all, args.CLASS, iflist,
+                                 excludepats=args.excludepats,
+                                 printdependency=args.printdependency,
+                                 usecurrentconfig=args.usecurrentconfig,
+                                 type=args.type)
+        except:
+            raise
+
+    def run_query(self, args):
+        log.debug('args = %s' % str(args))
+
+        try:
+            iflist = args.iflist
+            if args.checkcurr:
+                qop = 'query-checkcurr'
+            elif args.running:
+                qop = 'query-running'
+            elif args.raw:
+                qop = 'query-raw'
+            elif args.syntaxhelp:
+                qop = 'query-syntax'
+            elif args.printdependency:
+                qop = 'query-dependency'
+            elif args.printsavedstate:
+                qop = 'query-savedstate'
+            else:
+                qop = 'query'
+            cachearg = (False if (iflist or args.nocache or args.syntaxhelp or
+                                  (qop != 'query-checkcurr' and
+                                   qop != 'query-running')) else True)
+            if not iflist and qop == 'query-running':
+                iflist = [i for i in os.listdir('/sys/class/net/')
+                          if os.path.isdir('/sys/class/net/%s' % i)]
+            log.debug('creating ifupdown object ..')
+            ifupdown_handle = ifupdownMain(daemon=self.daemon,
+                                                                 config=configmap_g,
+                                                                 withdepends=args.withdepends,
+                                                                 perfmode=args.perfmode,
+                                                                 cache=cachearg,
+                                                                 interfacesfile=self.interfaces_filename,
+                                                                 interfacesfileiobuf=self.interfaces_file_iobuf,
+                                                                 interfacesfileformat=args.interfacesfileformat,
+                                                                 withdefaults=args.withdefaults)
+            # list implies all auto interfaces (this is how ifupdown behaves)
+            if args.list:
+                args.all = True
+            ifupdown_handle.query([qop], args.all, args.list, args.CLASS, iflist,
+                                  excludepats=args.excludepats,
+                                  printdependency=args.printdependency,
+                                  format=args.format, type=args.type)
+        except:
+            raise
+
+    def run_reload(self, args):
+        log.debug('args = %s' % str(args))
+
+        try:
+            log.debug('creating ifupdown object ..')
+            ifupdown_handle = ifupdownMain(daemon=self.daemon,
+                                                                 config=configmap_g,
+                                                                 interfacesfile=self.interfaces_filename,
+                                                                 withdepends=args.withdepends,
+                                                                 perfmode=args.perfmode,
+                                                                 dryrun=args.noact)
+            ifupdown_handle.reload(['pre-up', 'up', 'post-up'],
+                                   ['pre-down', 'down', 'post-down'],
+                                   auto=args.all, allow=args.CLASS, ifacenames=None,
+                                   excludepats=args.excludepats,
+                                   usecurrentconfig=args.usecurrentconfig,
+                                   syntaxcheck=args.syntaxcheck,
+                                   currentlyup=args.currentlyup)
+        except:
+            raise
+
+    @staticmethod
+    def set_signal_handlers():
+        signal.signal(signal.SIGQUIT, _SIGQUIT)
+        signal.signal(signal.SIGTERM, _SIGTERM)
+        signal.signal(signal.SIGINT, _SIGINT)
diff --git a/ifupdown2/ifupdown/netlink.py b/ifupdown2/ifupdown/netlink.py
new file mode 100644 (file)
index 0000000..6a29902
--- /dev/null
@@ -0,0 +1,627 @@
+#!/usr/bin/python
+#
+# Copyright 2016-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Julien Fortin, julien@cumulusnetworks.com
+#
+
+import sys
+import socket
+import logging
+
+from collections import OrderedDict
+
+try:
+    import ifupdown2.nlmanager.nlpacket
+    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
+
+    from ifupdown2.ifupdownaddons.cache import *
+    from ifupdown2.ifupdownaddons.utilsbase import utilsBase
+
+    from ifupdown2.nlmanager.nlmanager import Link, Address, Route, NetlinkPacket
+except ImportError:
+    import nlmanager.nlpacket
+    import ifupdown.ifupdownflags as ifupdownflags
+
+    from ifupdownaddons.cache import *
+    from ifupdownaddons.utilsbase import utilsBase
+
+    from nlmanager.nlmanager import Link, Address, Route, NetlinkPacket
+
+
+class Netlink(utilsBase):
+    VXLAN_UDP_PORT = 4789
+
+    def __init__(self, *args, **kargs):
+        utilsBase.__init__(self, *args, **kargs)
+        try:
+            sys.path.insert(0, '/usr/share/ifupdown2/')
+            try:
+                from ifupdown2.nlmanager.nlmanager import NetlinkManager
+                # Override the nlmanager's mac_int_to_str function to print the MACs
+                # like xx:xx:xx:xx:xx:xx instead of xxxx.xxxx.xxxx
+                ifupdown2.nlmanager.nlpacket.mac_int_to_str = self.mac_int_to_str
+            except ImportError:
+                from nlmanager.nlmanager import NetlinkManager
+                # Override the nlmanager's mac_int_to_str function to print the MACs
+                # like xx:xx:xx:xx:xx:xx instead of xxxx.xxxx.xxxx
+                nlmanager.nlpacket.mac_int_to_str = self.mac_int_to_str
+
+            # this should force the use of the local nlmanager
+            self._nlmanager_api = NetlinkManager(log_level=logging.WARNING)
+
+            self.link_kind_handlers = {
+                'vlan': self._link_dump_info_data_vlan,
+                'vrf': self._link_dump_info_data_vrf,
+                'vxlan': self._link_dump_info_data_vxlan,
+                'bond': self._link_dump_info_data_bond,
+                'bridge': self._link_dump_info_data_bridge
+            }
+
+        except Exception as e:
+            self.logger.error('cannot initialize ifupdown2\'s '
+                              'netlink manager: %s' % str(e))
+            raise
+
+    @staticmethod
+    def IN_MULTICAST(a):
+        """
+            /include/uapi/linux/in.h
+
+            #define IN_CLASSD(a)            ((((long int) (a)) & 0xf0000000) == 0xe0000000)
+            #define IN_MULTICAST(a)         IN_CLASSD(a)
+        """
+        return (int(a) & 0xf0000000) == 0xe0000000
+
+    @staticmethod
+    def mac_int_to_str(mac_int):
+        """
+        Return an integer in MAC string format: xx:xx:xx:xx:xx:xx
+        """
+        return ':'.join(("%012x" % mac_int)[i:i + 2] for i in range(0, 12, 2))
+
+    def get_iface_index(self, ifacename):
+        if ifupdownflags.flags.DRYRUN: return
+        try:
+            return self._nlmanager_api.get_iface_index(ifacename)
+        except Exception as e:
+            raise Exception('%s: netlink: %s: cannot get ifindex: %s'
+                            % (ifacename, ifacename, str(e)))
+
+    def get_iface_name(self, ifindex):
+        if ifupdownflags.flags.DRYRUN: return
+        try:
+            return self._nlmanager_api.get_iface_name(ifindex)
+        except Exception as e:
+            raise Exception('netlink: cannot get ifname for index %s: %s' % (ifindex, str(e)))
+
+    def get_bridge_vlan(self, ifname):
+        self.logger.info('%s: netlink: /sbin/bridge -d -c -json vlan show' % ifname)
+        if ifupdownflags.flags.DRYRUN: return
+        try:
+            return self._nlmanager_api.vlan_get()
+        except Exception as e:
+            raise Exception('netlink: get bridge vlan: %s' % str(e))
+
+    def bridge_set_vlan_filtering(self, ifname, vlan_filtering):
+        self.logger.info('%s: netlink: ip link set dev %s type bridge vlan_filtering %s'
+                         % (ifname, ifname, vlan_filtering))
+        if ifupdownflags.flags.DRYRUN: return
+        try:
+            ifla_info_data = {Link.IFLA_BR_VLAN_FILTERING: int(vlan_filtering)}
+            return self._nlmanager_api.link_set_attrs(ifname, 'bridge', ifla_info_data=ifla_info_data)
+        except Exception as e:
+            raise Exception('%s: cannot set %s vlan_filtering %s' % (ifname, ifname, vlan_filtering))
+
+    def link_add_set(self,
+                     ifname=None, ifindex=0,
+                     kind=None, slave_kind=None,
+                     ifla={},
+                     ifla_info_data={},
+                     ifla_info_slave_data={},
+                     link_exists=False):
+        action = 'set' if ifindex or link_exists else 'add'
+
+        if slave_kind:
+            self.logger.info('%s: netlink: ip link set dev %s: %s slave attributes' % (ifname, ifname, slave_kind))
+        else:
+            self.logger.info('%s: netlink: ip link %s %s type %s with attributes' % (ifname, action, ifname, kind))
+        if ifla:
+            self.logger.debug('%s: ifla attributes a %s' % (ifname, ifla))
+        if ifla_info_data:
+            self.logger.debug('%s: ifla_info_data %s' % (ifname, ifla_info_data))
+        if ifla_info_slave_data:
+            self.logger.debug('%s: ifla_info_slave_data %s' % (ifname, ifla_info_slave_data))
+
+        if ifupdownflags.flags.DRYRUN: return
+        try:
+            self._nlmanager_api.link_add_set(ifname=ifname,
+                                             ifindex=ifindex,
+                                             kind=kind,
+                                             slave_kind=slave_kind,
+                                             ifla=ifla,
+                                             ifla_info_data=ifla_info_data,
+                                             ifla_info_slave_data=ifla_info_slave_data)
+        except Exception as e:
+            if kind and not slave_kind:
+                kind_str = kind
+            elif kind and slave_kind:
+                kind_str = '%s (%s slave)' % (kind, slave_kind)
+            else:
+                kind_str = '(%s slave)' % slave_kind
+
+            raise Exception('netlink: cannot %s %s %s with options: %s' % (action, kind_str, ifname, str(e)))
+
+    def link_del(self, ifname):
+        self.logger.info('%s: netlink: ip link del %s' % (ifname, ifname))
+        if ifupdownflags.flags.DRYRUN: return
+        try:
+            self._nlmanager_api.link_del(ifname=ifname)
+        except Exception as e:
+            raise Exception('netlink: cannot delete link %s: %s' % (ifname, str(e)))
+
+    def link_set_master(self, ifacename, master_dev, state=None):
+        self.logger.info('%s: netlink: ip link set dev %s master %s %s'
+                         % (ifacename, ifacename, master_dev,
+                            state if state else ''))
+        if ifupdownflags.flags.DRYRUN: return
+        try:
+            master = 0 if not master_dev else self.get_iface_index(master_dev)
+            return self._nlmanager_api.link_set_master(ifacename,
+                                                       master_ifindex=master,
+                                                       state=state)
+        except Exception as e:
+            raise Exception('netlink: %s: cannot set %s master %s: %s'
+                            % (ifacename, ifacename, master_dev, str(e)))
+
+    def link_set_nomaster(self, ifacename, state=None):
+        self.logger.info('%s: netlink: ip link set dev %s nomaster %s'
+                         % (ifacename, ifacename, state if state else ''))
+        if ifupdownflags.flags.DRYRUN: return
+        try:
+            return self._nlmanager_api.link_set_master(ifacename,
+                                                       master_ifindex=0,
+                                                       state=state)
+        except Exception as e:
+            raise Exception('netlink: %s: cannot set %s nomaster: %s'
+                            % (ifacename, ifacename, str(e)))
+
+    def link_add_vlan(self, vlanrawdevice, ifacename, vlanid, vlan_protocol):
+        if vlan_protocol:
+            self.logger.info('%s: netlink: ip link add link %s name %s type vlan id %s protocol %s'
+                             % (ifacename, vlanrawdevice, ifacename, vlanid, vlan_protocol))
+
+        else:
+            self.logger.info('%s: netlink: ip link add link %s name %s type vlan id %s'
+                             % (ifacename, vlanrawdevice, ifacename, vlanid))
+        if ifupdownflags.flags.DRYRUN: return
+        ifindex = self.get_iface_index(vlanrawdevice)
+        try:
+            return self._nlmanager_api.link_add_vlan(ifindex, ifacename, vlanid, vlan_protocol)
+        except Exception as e:
+            raise Exception('netlink: %s: cannot create vlan %s: %s'
+                            % (vlanrawdevice, vlanid, str(e)))
+
+    def link_add_macvlan(self, ifacename, macvlan_ifacename):
+        self.logger.info('%s: netlink: ip link add link %s name %s type macvlan mode private'
+                         % (ifacename, ifacename, macvlan_ifacename))
+        if ifupdownflags.flags.DRYRUN: return
+        ifindex = self.get_iface_index(ifacename)
+        try:
+            return self._nlmanager_api.link_add_macvlan(ifindex, macvlan_ifacename)
+        except Exception as e:
+            raise Exception('netlink: %s: cannot create macvlan %s: %s'
+                            % (ifacename, macvlan_ifacename, str(e)))
+
+    def link_set_updown(self, ifacename, state):
+        self.logger.info('%s: netlink: ip link set dev %s %s'
+                         % (ifacename, ifacename, state))
+        if ifupdownflags.flags.DRYRUN: return
+        try:
+            return self._nlmanager_api.link_set_updown(ifacename, state)
+        except Exception as e:
+            raise Exception('netlink: cannot set link %s %s: %s'
+                            % (ifacename, state, str(e)))
+
+    def link_set_protodown(self, ifacename, state):
+        self.logger.info('%s: netlink: set link %s protodown %s'
+                         % (ifacename, ifacename, state))
+        if ifupdownflags.flags.DRYRUN: return
+        try:
+            return self._nlmanager_api.link_set_protodown(ifacename, state)
+        except Exception as e:
+            raise Exception('netlink: cannot set link %s protodown %s: %s'
+                            % (ifacename, state, str(e)))
+
+    def link_add_bridge(self, ifname):
+        self.logger.info('%s: netlink: ip link add %s type bridge' % (ifname, ifname))
+        if ifupdownflags.flags.DRYRUN: return
+        try:
+            return self._nlmanager_api.link_add_bridge(ifname)
+        except Exception as e:
+            raise Exception('netlink: cannot create bridge %s: %s' % (ifname, str(e)))
+
+    def link_add_bridge_vlan(self, ifacename, vlanid):
+        self.logger.info('%s: netlink: bridge vlan add vid %s dev %s'
+                         % (ifacename, vlanid, ifacename))
+        if ifupdownflags.flags.DRYRUN: return
+        ifindex = self.get_iface_index(ifacename)
+        try:
+            return self._nlmanager_api.link_add_bridge_vlan(ifindex, vlanid)
+        except Exception as e:
+            raise Exception('netlink: %s: cannot create bridge vlan %s: %s'
+                            % (ifacename, vlanid, str(e)))
+
+    def link_del_bridge_vlan(self, ifacename, vlanid):
+        self.logger.info('%s: netlink: bridge vlan del vid %s dev %s'
+                         % (ifacename, vlanid, ifacename))
+        if ifupdownflags.flags.DRYRUN: return
+        ifindex = self.get_iface_index(ifacename)
+        try:
+            return self._nlmanager_api.link_del_bridge_vlan(ifindex, vlanid)
+        except Exception as e:
+            raise Exception('netlink: %s: cannot remove bridge vlan %s: %s'
+                            % (ifacename, vlanid, str(e)))
+
+    def link_add_vxlan(self, ifacename, vxlanid, local=None, dstport=VXLAN_UDP_PORT,
+                       group=None, learning=True, ageing=None):
+        cmd = 'ip link add %s type vxlan id %s dstport %s' % (ifacename,
+                                                              vxlanid,
+                                                              dstport)
+        cmd += ' local %s' % local if local else ''
+        cmd += ' ageing %s' % ageing if ageing else ''
+        cmd += ' remote %s' % group if group else ' noremote'
+        cmd += ' nolearning' if not learning else ''
+        self.logger.info('%s: netlink: %s' % (ifacename, cmd))
+        if ifupdownflags.flags.DRYRUN: return
+        try:
+            return self._nlmanager_api.link_add_vxlan(ifacename,
+                                                      vxlanid,
+                                                      dstport=dstport,
+                                                      local=local,
+                                                      group=group,
+                                                      learning=learning,
+                                                      ageing=ageing)
+        except Exception as e:
+            raise Exception('netlink: %s: cannot create vxlan %s: %s'
+                            % (ifacename, vxlanid, str(e)))
+
+    @staticmethod
+    def _link_dump_attr(link, ifla_attributes, dump):
+        for obj in ifla_attributes:
+            attr = link.attributes.get(obj['attr'])
+            if attr:
+                dump[obj['name']] = attr.get_pretty_value(obj=obj.get('func'))
+
+    @staticmethod
+    def _link_dump_linkdata_attr(linkdata, ifla_linkdata_attr, dump):
+        for obj in ifla_linkdata_attr:
+            attr = obj['attr']
+            if attr in linkdata:
+                func    = obj.get('func')
+                value   = linkdata.get(attr)
+
+                if func:
+                    value = func(value)
+
+                if value or obj['accept_none']:
+                    dump[obj['name']] = value
+
+    ifla_attributes = [
+        {
+            'attr': Link.IFLA_LINK,
+            'name': 'link',
+            'func': lambda x: netlink.get_iface_name(x) if x > 0 else None
+        },
+        {
+            'attr': Link.IFLA_MASTER,
+            'name': 'master',
+            'func': lambda x: netlink.get_iface_name(x) if x > 0 else None
+        },
+        {
+            'attr': Link.IFLA_IFNAME,
+            'name': 'ifname',
+            'func': str,
+        },
+        {
+            'attr': Link.IFLA_MTU,
+            'name': 'mtu',
+            'func': str
+        },
+        {
+            'attr': Link.IFLA_OPERSTATE,
+            'name': 'state',
+            'func': lambda x: '0%x' % int(x) if x > len(Link.oper_to_string) else Link.oper_to_string[x][8:]
+        }
+    ]
+
+    ifla_address = {'attr': Link.IFLA_ADDRESS, 'name': 'hwaddress', 'func': str}
+
+    ifla_vxlan_attributes = [
+        {
+            'attr': Link.IFLA_VXLAN_LOCAL,
+            'name': 'local',
+            'func': str,
+            'accept_none': True
+        },
+        {
+            'attr': Link.IFLA_VXLAN_LOCAL6,
+            'name': 'local',
+            'func': str,
+            'accept_none': True
+        },
+        {
+            'attr': Link.IFLA_VXLAN_GROUP,
+            'name': 'svcnode',
+            'func': lambda x: str(x) if not Netlink.IN_MULTICAST(x) else None,
+            'accept_none': False
+        },
+        {
+            'attr': Link.IFLA_VXLAN_GROUP6,
+            'name': 'svcnode',
+            'func': lambda x: str(x) if not Netlink.IN_MULTICAST(x) else None,
+            'accept_none': False
+        },
+        {
+            'attr': Link.IFLA_VXLAN_LEARNING,
+            'name': 'learning',
+            'func': lambda x: 'on' if x else 'off',
+            'accept_none': True
+        }
+    ]
+
+    def _link_dump_info_data_vlan(self, ifname, linkdata):
+        return {
+            'vlanid': str(linkdata.get(Link.IFLA_VLAN_ID, '')),
+            'vlan_protocol': linkdata.get(Link.IFLA_VLAN_PROTOCOL)
+        }
+
+    def _link_dump_info_data_vrf(self, ifname, linkdata):
+        vrf_info = {'table': str(linkdata.get(Link.IFLA_VRF_TABLE, ''))}
+
+        # to remove later when moved to a true netlink cache
+        linkCache.vrfs[ifname] = vrf_info
+        return vrf_info
+
+    def _link_dump_info_data_vxlan(self, ifname, linkdata):
+        for attr, value in (
+                ('learning', 'on'),
+                ('svcnode', None),
+                ('vxlanid', str(linkdata.get(Link.IFLA_VXLAN_ID, ''))),
+                ('ageing', str(linkdata.get(Link.IFLA_VXLAN_AGEING, ''))),
+                (Link.IFLA_VXLAN_PORT, linkdata.get(Link.IFLA_VXLAN_PORT))
+        ):
+            linkdata[attr] = value
+        self._link_dump_linkdata_attr(linkdata, self.ifla_vxlan_attributes, linkdata)
+        return linkdata
+
+    ifla_bond_attributes = (
+        Link.IFLA_BOND_MODE,
+        Link.IFLA_BOND_MIIMON,
+        Link.IFLA_BOND_USE_CARRIER,
+        Link.IFLA_BOND_AD_LACP_RATE,
+        Link.IFLA_BOND_XMIT_HASH_POLICY,
+        Link.IFLA_BOND_MIN_LINKS,
+        Link.IFLA_BOND_NUM_PEER_NOTIF,
+        Link.IFLA_BOND_AD_ACTOR_SYSTEM,
+        Link.IFLA_BOND_AD_ACTOR_SYS_PRIO,
+        Link.IFLA_BOND_AD_LACP_BYPASS,
+        Link.IFLA_BOND_UPDELAY,
+        Link.IFLA_BOND_DOWNDELAY,
+    )
+
+    def _link_dump_info_data_bond(self, ifname, linkdata):
+        linkinfo = {}
+        for nl_attr in self.ifla_bond_attributes:
+            try:
+                linkinfo[nl_attr] = linkdata.get(nl_attr)
+            except Exception as e:
+                self.logger.debug('%s: parsing bond IFLA_INFO_DATA (%s): %s'
+                                  % (ifname, nl_attr, str(e)))
+        return linkinfo
+
+    # this dict contains the netlink attribute, cache key,
+    # and a callable to translate the netlink value into
+    # whatever value we need to store in the old cache to
+    # make sure we don't break anything
+    ifla_bridge_attributes = (
+        (Link.IFLA_BR_UNSPEC, Link.IFLA_BR_UNSPEC, None),
+        (Link.IFLA_BR_FORWARD_DELAY, "fd", lambda x: str(x / 100)),
+        (Link.IFLA_BR_HELLO_TIME, "hello", lambda x: str(x / 100)),
+        (Link.IFLA_BR_MAX_AGE, "maxage", lambda x: str(x / 100)),
+        (Link.IFLA_BR_AGEING_TIME, "ageing", lambda x: str(x / 100)),
+        (Link.IFLA_BR_STP_STATE, "stp", lambda x: 'yes' if x else 'no'),
+        (Link.IFLA_BR_PRIORITY, "bridgeprio", str),
+        (Link.IFLA_BR_VLAN_FILTERING, 'vlan_filtering', str),
+        (Link.IFLA_BR_VLAN_PROTOCOL, "vlan-protocol", str),
+        (Link.IFLA_BR_GROUP_FWD_MASK, Link.IFLA_BR_GROUP_FWD_MASK, None),
+        (Link.IFLA_BR_ROOT_ID, Link.IFLA_BR_ROOT_ID, None),
+        (Link.IFLA_BR_BRIDGE_ID, Link.IFLA_BR_BRIDGE_ID, None),
+        (Link.IFLA_BR_ROOT_PORT, Link.IFLA_BR_ROOT_PORT, None),
+        (Link.IFLA_BR_ROOT_PATH_COST, Link.IFLA_BR_ROOT_PATH_COST, None),
+        (Link.IFLA_BR_TOPOLOGY_CHANGE, Link.IFLA_BR_TOPOLOGY_CHANGE, None),
+        (Link.IFLA_BR_TOPOLOGY_CHANGE_DETECTED, Link.IFLA_BR_TOPOLOGY_CHANGE_DETECTED, None),
+        (Link.IFLA_BR_HELLO_TIMER, Link.IFLA_BR_HELLO_TIMER, None),
+        (Link.IFLA_BR_TCN_TIMER, Link.IFLA_BR_TCN_TIMER, None),
+        (Link.IFLA_BR_TOPOLOGY_CHANGE_TIMER, Link.IFLA_BR_TOPOLOGY_CHANGE_TIMER, None),
+        (Link.IFLA_BR_GC_TIMER, Link.IFLA_BR_GC_TIMER, None),
+        (Link.IFLA_BR_GROUP_ADDR, Link.IFLA_BR_GROUP_ADDR, None),
+        (Link.IFLA_BR_FDB_FLUSH, Link.IFLA_BR_FDB_FLUSH, None),
+        (Link.IFLA_BR_MCAST_ROUTER, "mcrouter", str),
+        (Link.IFLA_BR_MCAST_SNOOPING, "mcsnoop", str),
+        (Link.IFLA_BR_MCAST_QUERY_USE_IFADDR, "mcqifaddr", str),
+        (Link.IFLA_BR_MCAST_QUERIER, "mcquerier", str),
+        (Link.IFLA_BR_MCAST_HASH_ELASTICITY, "hashel", str),
+        (Link.IFLA_BR_MCAST_HASH_MAX, "hashmax", str),
+        (Link.IFLA_BR_MCAST_LAST_MEMBER_CNT, "mclmc", str),
+        (Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT, "mcsqc", str),
+        (Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL, "mclmi", lambda x: str(x / 100)),
+        (Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL, "mcmi", lambda x: str(x / 100)),
+        (Link.IFLA_BR_MCAST_QUERIER_INTVL, "mcqpi", lambda x: str(x / 100)),
+        (Link.IFLA_BR_MCAST_QUERY_INTVL, "mcqi", lambda x: str(x / 100)),
+        (Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, "mcqri", lambda x: str(x / 100)),
+        (Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL, "mcsqi", lambda x: str(x / 100)),
+        (Link.IFLA_BR_NF_CALL_IPTABLES, Link.IFLA_BR_NF_CALL_IPTABLES, None),
+        (Link.IFLA_BR_NF_CALL_IP6TABLES, Link.IFLA_BR_NF_CALL_IP6TABLES, None),
+        (Link.IFLA_BR_NF_CALL_ARPTABLES, Link.IFLA_BR_NF_CALL_ARPTABLES, None),
+        (Link.IFLA_BR_VLAN_DEFAULT_PVID, Link.IFLA_BR_VLAN_DEFAULT_PVID, None),
+        (Link.IFLA_BR_PAD, Link.IFLA_BR_PAD, None),
+        (Link.IFLA_BR_VLAN_STATS_ENABLED, "vlan-stats", str),
+        (Link.IFLA_BR_MCAST_STATS_ENABLED, "mcstats", str),
+        (Link.IFLA_BR_MCAST_IGMP_VERSION, "igmp-version", str),
+        (Link.IFLA_BR_MCAST_MLD_VERSION, "mld-version", str)
+    )
+
+    def _link_dump_info_data_bridge(self, ifname, linkdata):
+        linkinfo = {}
+        for nl_attr, cache_key, func in self.ifla_bridge_attributes:
+            try:
+                if func:
+                    linkinfo[cache_key] = func(linkdata.get(nl_attr))
+                else:
+                    linkinfo[cache_key] = linkdata.get(nl_attr)
+
+                # we also store the value in pure netlink,
+                # to make the transition easier in the future
+                linkinfo[nl_attr] = linkdata.get(nl_attr)
+            except Exception as e:
+                self.logger.error('%s: parsing birdge IFLA_INFO_DATA %s: %s'
+                                  % (ifname, nl_attr, str(e)))
+        return linkinfo
+
+    def _link_dump_info_slave_data_bridge(self, ifname, info_slave_data):
+        return info_slave_data
+
+    def _link_dump_linkinfo(self, link, dump):
+        linkinfo = link.attributes[Link.IFLA_LINKINFO].get_pretty_value(dict)
+
+        if linkinfo:
+            info_kind = linkinfo.get(Link.IFLA_INFO_KIND)
+            info_data = linkinfo.get(Link.IFLA_INFO_DATA)
+
+            info_slave_kind = linkinfo.get(Link.IFLA_INFO_SLAVE_KIND)
+            info_slave_data = linkinfo.get(Link.IFLA_INFO_SLAVE_DATA)
+
+            dump['kind']        = info_kind
+            dump['slave_kind']  = info_slave_kind
+
+            if info_data:
+                link_kind_handler = self.link_kind_handlers.get(info_kind)
+                if callable(link_kind_handler):
+                    dump['linkinfo'] = link_kind_handler(dump['ifname'], info_data)
+
+            if info_slave_data:
+                dump['info_slave_data'] = info_slave_data
+
+    def link_dump(self, ifname=None):
+        if ifname:
+            self.logger.info('netlink: ip link show dev %s' % ifname)
+        else:
+            self.logger.info('netlink: ip link show')
+
+        if ifupdownflags.flags.DRYRUN: return {}
+
+        links = dict()
+
+        try:
+            links_dump = self._nlmanager_api.link_dump(ifname)
+        except Exception as e:
+            raise Exception('netlink: link dump failed: %s' % str(e))
+
+        for link in links_dump:
+            try:
+                dump = dict()
+
+                flags = []
+                for flag, string in Link.flag_to_string.items():
+                    if link.flags & flag:
+                        flags.append(string[4:])
+
+                dump['flags'] = flags
+                dump['ifflag'] = 'UP' if 'UP' in flags else 'DOWN'
+                dump['ifindex'] = str(link.ifindex)
+
+                if link.device_type == Link.ARPHRD_ETHER:
+                    self._link_dump_attr(link, [self.ifla_address], dump)
+
+                self._link_dump_attr(link, self.ifla_attributes, dump)
+
+                if Link.IFLA_LINKINFO in link.attributes:
+                    self._link_dump_linkinfo(link, dump)
+
+                links[dump['ifname']] = dump
+            except Exception as e:
+                self.logger.warning('netlink: ip link show: %s' % str(e))
+        return links
+
+    def _addr_dump_extract_ifname(self, addr_packet):
+        addr_ifname_attr = addr_packet.attributes.get(Address.IFA_LABEL)
+
+        if addr_ifname_attr:
+            return addr_ifname_attr.get_pretty_value(str)
+        else:
+            return self.get_iface_name(addr_packet.ifindex)
+
+    @staticmethod
+    def _addr_filter(addr_ifname, addr):
+        return addr_ifname == 'lo' and addr in ['127.0.0.1/8', '::1/128', '0.0.0.0']
+
+    def _addr_dump_entry(self, ifaces, addr_packet, addr_ifname, ifa_attr):
+        attribute = addr_packet.attributes.get(ifa_attr)
+
+        if attribute:
+            address = attribute.get_pretty_value(str)
+
+            if hasattr(addr_packet, 'prefixlen'):
+                address = '%s/%d' % (address, addr_packet.prefixlen)
+
+            if self._addr_filter(addr_ifname, address):
+                return
+
+            addr_family = NetlinkPacket.af_family_to_string.get(addr_packet.family)
+            if not addr_family:
+                return
+
+            ifaces[addr_ifname]['addrs'][address] = {
+                'type': addr_family,
+                'scope': addr_packet.scope
+            }
+
+    ifa_address_attributes = [
+        Address.IFA_ADDRESS,
+        Address.IFA_LOCAL,
+        Address.IFA_BROADCAST,
+        Address.IFA_ANYCAST,
+        Address.IFA_MULTICAST
+    ]
+
+    def addr_dump(self, ifname=None):
+        if ifname:
+            self.logger.info('netlink: ip addr show dev %s' % ifname)
+        else:
+            self.logger.info('netlink: ip addr show')
+
+        ifaces = dict()
+        addr_dump = self._nlmanager_api.addr_dump()
+
+        for addr_packet in addr_dump:
+            addr_ifname = self._addr_dump_extract_ifname(addr_packet)
+
+            if addr_packet.family not in [socket.AF_INET, socket.AF_INET6]:
+                continue
+
+            if ifname and ifname != addr_ifname:
+                continue
+
+            if addr_ifname not in ifaces:
+                ifaces[addr_ifname] = {'addrs': OrderedDict({})}
+
+            for ifa_attr in self.ifa_address_attributes:
+                self._addr_dump_entry(ifaces, addr_packet, addr_ifname, ifa_attr)
+
+        if ifname:
+            return {ifname: ifaces.get(ifname, {})}
+
+        return ifaces
+
+
+netlink = Netlink()
diff --git a/ifupdown2/ifupdown/networkinterfaces.py b/ifupdown2/ifupdown/networkinterfaces.py
new file mode 100644 (file)
index 0000000..c79d2b8
--- /dev/null
@@ -0,0 +1,520 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+# networkInterfaces --
+#    ifupdown network interfaces file parser
+#
+
+import re
+import copy
+import glob
+import logging
+import collections
+
+try:
+    from ifupdown2.ifupdown.iface import *
+    from ifupdown2.ifupdown.utils import utils
+    from ifupdown2.ifupdown.template import templateEngine
+except ImportError:
+    from ifupdown.iface import *
+    from ifupdown.utils import utils
+    from ifupdown.template import templateEngine
+
+
+whitespaces = '\n\t\r '
+
+class networkInterfaces():
+    """ debian ifupdown /etc/network/interfaces file parser """
+
+    _addrfams = {'inet' : ['static', 'manual', 'loopback', 'dhcp', 'dhcp6'],
+                 'inet6' : ['static', 'manual', 'loopback', 'dhcp', 'dhcp6']}
+
+    def __init__(self, interfacesfile='/etc/network/interfaces',
+                 interfacesfileiobuf=None, interfacesfileformat='native',
+                 template_enable='0', template_engine=None,
+                 template_lookuppath=None):
+        """This member function initializes the networkinterfaces parser object.
+
+        Kwargs:
+            **interfacesfile** (str):  path to the interfaces file (default is /etc/network/interfaces)
+
+            **interfacesfileiobuf** (object): interfaces file io stream
+
+            **interfacesfileformat** (str): format of interfaces file (choices are 'native' and 'json'. 'native' being the default)
+
+            **template_engine** (str): template engine name
+
+            **template_lookuppath** (str): template lookup path
+
+        Raises:
+            AttributeError, KeyError """
+
+        self.auto_ifaces = []
+        self.callbacks = {}
+        self.auto_all = False
+
+        self.logger = logging.getLogger('ifupdown.' +
+                    self.__class__.__name__)
+        self.callbacks = {'iface_found' : None,
+                          'validateifaceattr' : None,
+                          'validateifaceobj' : None}
+        self.allow_classes = {}
+        self.interfacesfile = interfacesfile
+        self.interfacesfileiobuf = interfacesfileiobuf
+        self.interfacesfileformat = interfacesfileformat
+        self._filestack = [self.interfacesfile]
+
+        self._template_engine = None
+        self._template_enable = template_enable
+        self._template_engine_name = template_engine
+        self._template_engine_path = template_lookuppath
+
+        self._currentfile_has_template = False
+        self._ws_split_regex = re.compile(r'[\s\t]\s*')
+
+        self.errors = 0
+        self.warns = 0
+
+    @property
+    def _currentfile(self):
+        try:
+            return self._filestack[-1]
+        except:
+            return self.interfacesfile
+
+    def _parse_error(self, filename, lineno, msg):
+        if lineno == -1 or self._currentfile_has_template:
+            self.logger.error('%s: %s' %(filename, msg))
+        else:
+            self.logger.error('%s: line%d: %s' %(filename, lineno, msg))
+        self.errors += 1
+
+    def _parse_warn(self, filename, lineno, msg):
+        if lineno == -1 or self._currentfile_has_template:
+            self.logger.warn('%s: %s' %(filename, msg))
+        else:
+            self.logger.warn('%s: line%d: %s' %(filename, lineno, msg))
+        self.warns += 1
+
+    def _validate_addr_family(self, ifaceobj, lineno=-1):
+        for family in ifaceobj.addr_family:
+            if not self._addrfams.get(family):
+                self._parse_error(self._currentfile, lineno,
+                                  'iface %s: unsupported address family \'%s\''
+                                  % (ifaceobj.name, family))
+                ifaceobj.addr_family = []
+                ifaceobj.addr_method = None
+                return
+            if ifaceobj.addr_method:
+                if ifaceobj.addr_method not in self._addrfams.get(family):
+                    self._parse_error(self._currentfile, lineno,
+                                      'iface %s: unsupported '
+                                      'address method \'%s\''
+                                      % (ifaceobj.name, ifaceobj.addr_method))
+            else:
+                ifaceobj.addr_method = 'static'
+
+    def subscribe(self, callback_name, callback_func):
+        """This member function registers callback functions.
+
+        Args:
+            **callback_name** (str): callback function name (supported names: 'iface_found', 'validateifaceattr', 'validateifaceobj')
+
+            **callback_func** (function pointer): callback function pointer
+
+        Warns on error
+        """
+
+        if callback_name not in self.callbacks.keys():
+            print 'warning: invalid callback ' + callback_name
+            return -1
+
+        self.callbacks[callback_name] = callback_func
+
+    def ignore_line(self, line):
+        l = line.strip(whitespaces)
+        if not l or l[0] == '#':
+            return 1
+        return 0
+
+    def process_allow(self, lines, cur_idx, lineno):
+        allow_line = lines[cur_idx]
+
+        words = re.split(self._ws_split_regex, allow_line)
+        if len(words) <= 1:
+            raise Exception('invalid allow line \'%s\' at line %d'
+                            %(allow_line, lineno))
+
+        allow_class = words[0].split('-')[1]
+        ifacenames = words[1:]
+
+        if self.allow_classes.get(allow_class):
+            for i in ifacenames:
+                self.allow_classes[allow_class].append(i)
+        else:
+                self.allow_classes[allow_class] = ifacenames
+        return 0
+
+    def process_source(self, lines, cur_idx, lineno):
+        # Support regex
+        self.logger.debug('processing sourced line ..\'%s\'' %lines[cur_idx])
+        sourced_file = re.split(self._ws_split_regex, lines[cur_idx], 2)[1]
+        if sourced_file:
+            filenames = glob.glob(sourced_file)
+            if not filenames:
+                if '*' not in sourced_file:
+                    self._parse_warn(self._currentfile, lineno,
+                            'cannot find source file %s' %sourced_file)
+                return 0
+            for f in filenames:
+                self.read_file(f)
+        else:
+            self._parse_error(self._currentfile, lineno,
+                    'unable to read source line')
+        return 0
+
+    def process_auto(self, lines, cur_idx, lineno):
+        auto_ifaces = re.split(self._ws_split_regex, lines[cur_idx])[1:]
+        if not auto_ifaces:
+            self._parse_error(self._currentfile, lineno,
+                    'invalid auto line \'%s\''%lines[cur_idx])
+            return 0
+        for a in auto_ifaces:
+            if a == 'all':
+                self.auto_all = True
+                break
+            r = utils.parse_iface_range(a)
+            if r:
+                if len(r) == 3:
+                    # eg swp1.[2-4], r = "swp1.", 2, 4)
+                    for i in range(r[1], r[2]+1):
+                        self.auto_ifaces.append('%s%d' %(r[0], i))
+                elif len(r) == 4:
+                    for i in range(r[1], r[2]+1):
+                        # eg swp[2-4].100, r = ("swp", 2, 4, ".100")
+                        self.auto_ifaces.append('%s%d%s' %(r[0], i, r[3]))
+            self.auto_ifaces.append(a)
+        return 0
+
+    def _add_to_iface_config(self, ifacename, iface_config, attrname,
+                             attrval, lineno):
+        newattrname = attrname.replace("_", "-")
+        try:
+            if not self.callbacks.get('validateifaceattr')(newattrname,
+                                      attrval):
+                self._parse_error(self._currentfile, lineno,
+                        'iface %s: unsupported keyword (%s)'
+                        %(ifacename, attrname))
+                return
+        except:
+            pass
+        attrvallist = iface_config.get(newattrname, [])
+        if newattrname in ['scope', 'netmask', 'broadcast', 'preferred-lifetime']:
+            # For attributes that are related and that can have multiple
+            # entries, store them at the same index as their parent attribute.
+            # The example of such attributes is 'address' and its related
+            # attributes. since the related attributes can be optional,
+            # we add null string '' in places where they are optional.
+            # XXX: this introduces awareness of attribute names in
+            # this class which is a violation.
+
+            # get the index corresponding to the 'address'
+            addrlist = iface_config.get('address')
+            if addrlist:
+                # find the index of last address element
+                for i in range(0, len(addrlist) - len(attrvallist) -1):
+                    attrvallist.append('')
+                attrvallist.append(attrval)
+                iface_config[newattrname] = attrvallist
+        elif not attrvallist:
+            iface_config[newattrname] = [attrval]
+        else:
+            iface_config[newattrname].append(attrval)
+
+    def parse_iface(self, lines, cur_idx, lineno, ifaceobj):
+        lines_consumed = 0
+        line_idx = cur_idx
+
+        iface_line = lines[cur_idx].strip(whitespaces)
+        iface_attrs = re.split(self._ws_split_regex, iface_line)
+        ifacename = iface_attrs[1]
+
+        if (not utils.is_ifname_range(ifacename) and
+            utils.check_ifname_size_invalid(ifacename)):
+            self._parse_warn(self._currentfile, lineno,
+                             '%s: interface name too long' %ifacename)
+
+        # in cases where mako is unable to render the template
+        # or incorrectly renders it due to user template
+        # errors, we maybe left with interface names with
+        # mako variables in them. There is no easy way to
+        # recognize and warn about these. In the below check
+        # we try to warn the user of such cases by looking for
+        # variable patterns ('$') in interface names.
+        if '$' in ifacename:
+           self._parse_warn(self._currentfile, lineno,
+                    '%s: unexpected characters in interface name' %ifacename)
+
+        ifaceobj.raw_config.append(iface_line)
+        iface_config = collections.OrderedDict()
+        for line_idx in range(cur_idx + 1, len(lines)):
+            l = lines[line_idx].strip(whitespaces)
+            if self.ignore_line(l) == 1:
+                ifaceobj.raw_config.append(l)
+                continue
+            attrs = re.split(self._ws_split_regex, l, 1)
+            if self._is_keyword(attrs[0]):
+                line_idx -= 1
+                break
+            # if not a keyword, every line must have at least a key and value
+            if len(attrs) < 2:
+                self._parse_error(self._currentfile, line_idx,
+                        'iface %s: invalid syntax \'%s\'' %(ifacename, l))
+                continue
+            ifaceobj.raw_config.append(l)
+            attrname = attrs[0]
+            # preprocess vars (XXX: only preprocesses $IFACE for now)
+            attrval = re.sub(r'\$IFACE', ifacename, attrs[1])
+            self._add_to_iface_config(ifacename, iface_config, attrname,
+                                      attrval, line_idx+1)
+        lines_consumed = line_idx - cur_idx
+
+        # Create iface object
+        if ifacename.find(':') != -1:
+            ifaceobj.name = ifacename.split(':')[0]
+        else:
+            ifaceobj.name = ifacename
+
+        ifaceobj.config = iface_config
+        ifaceobj.generate_env()
+
+        try:
+            if iface_attrs[2]:
+                ifaceobj.addr_family.append(iface_attrs[2])
+            ifaceobj.addr_method = iface_attrs[3]
+        except IndexError:
+            # ignore
+            pass
+        self._validate_addr_family(ifaceobj, lineno)
+
+        if self.auto_all or (ifaceobj.name in self.auto_ifaces):
+            ifaceobj.auto = True
+
+        classes = self.get_allow_classes_for_iface(ifaceobj.name)
+        if classes:
+            [ifaceobj.set_class(c) for c in classes]
+
+        return lines_consumed       # Return next index
+
+    def _create_ifaceobj_clone(self, ifaceobj, newifaceobjname,
+                               newifaceobjtype, newifaceobjflags):
+        ifaceobj_new = copy.deepcopy(ifaceobj)
+        ifaceobj_new.realname = '%s' %ifaceobj.name
+        ifaceobj_new.name = newifaceobjname
+        ifaceobj_new.type = newifaceobjtype
+        ifaceobj_new.flags = newifaceobjflags
+
+        return ifaceobj_new
+
+    def process_iface(self, lines, cur_idx, lineno):
+        ifaceobj = iface()
+        lines_consumed = self.parse_iface(lines, cur_idx, lineno, ifaceobj)
+
+        range_val = utils.parse_iface_range(ifaceobj.name)
+        if range_val:
+            if len(range_val) == 3:
+                for v in range(range_val[1], range_val[2]+1):
+                    ifacename = '%s%d' %(range_val[0], v)
+                    if utils.check_ifname_size_invalid(ifacename):
+                        self._parse_warn(self._currentfile, lineno,
+                                         '%s: interface name too long' %ifacename)
+                    flags = iface.IFACERANGE_ENTRY
+                    if v == range_val[1]:
+                        flags |= iface.IFACERANGE_START
+                    ifaceobj_new = self._create_ifaceobj_clone(ifaceobj,
+                                        ifacename, ifaceobj.type, flags)
+                    self.callbacks.get('iface_found')(ifaceobj_new)
+            elif len(range_val) == 4:
+                for v in range(range_val[1], range_val[2]+1):
+                    ifacename = '%s%d%s' %(range_val[0], v, range_val[3])
+                    if utils.check_ifname_size_invalid(ifacename):
+                        self._parse_warn(self._currentfile, lineno,
+                                         '%s: interface name too long' %ifacename)
+                    flags = iface.IFACERANGE_ENTRY
+                    if v == range_val[1]:
+                        flags |= iface.IFACERANGE_START
+                    ifaceobj_new = self._create_ifaceobj_clone(ifaceobj,
+                                        ifacename, ifaceobj.type, flags)
+                    self.callbacks.get('iface_found')(ifaceobj_new)
+        else:
+            self.callbacks.get('iface_found')(ifaceobj)
+
+        return lines_consumed       # Return next index
+
+    def process_vlan(self, lines, cur_idx, lineno):
+        ifaceobj = iface()
+        lines_consumed = self.parse_iface(lines, cur_idx, lineno, ifaceobj)
+
+        range_val = utils.parse_iface_range(ifaceobj.name)
+        if range_val:
+            if len(range_val) == 3:
+                for v in range(range_val[1], range_val[2]+1):
+                    flags = iface.IFACERANGE_ENTRY
+                    if v == range_val[1]:
+                        flags |= iface.IFACERANGE_START
+                    ifaceobj_new = self._create_ifaceobj_clone(ifaceobj,
+                                        '%s%d' %(range_val[0], v),
+                                        ifaceType.BRIDGE_VLAN, flags)
+                    self.callbacks.get('iface_found')(ifaceobj_new)
+            elif len(range_val) == 4:
+                for v in range(range_val[1], range_val[2]+1):
+                    flags = iface.IFACERANGE_ENTRY
+                    if v == range_val[1]:
+                        flags |= iface.IFACERANGE_START
+                    ifaceobj_new = self._create_ifaceobj_clone(ifaceobj,
+                                        '%s%d%s' %(range_val[0], v, range_val[3]),
+                                        ifaceType.BRIDGE_VLAN,
+                                        flags)
+                    self.callbacks.get('iface_found')(ifaceobj_new)
+        else:
+            ifaceobj.type = ifaceType.BRIDGE_VLAN
+            self.callbacks.get('iface_found')(ifaceobj)
+
+        return lines_consumed       # Return next index
+
+    network_elems = { 'source'      : process_source,
+                      'allow'      : process_allow,
+                      'auto'        : process_auto,
+                      'iface'       : process_iface,
+                      'vlan'       : process_vlan}
+
+    def _is_keyword(self, str):
+        # The additional split here is for allow- keyword
+        if (str in self.network_elems.keys() or
+            str.split('-')[0] == 'allow'):
+            return 1
+        return 0
+
+    def _get_keyword_func(self, str):
+        tmp_str = str.split('-')[0]
+        return self.network_elems.get(tmp_str)
+
+    def get_allow_classes_for_iface(self, ifacename):
+        classes = []
+        for class_name, ifacenames in self.allow_classes.items():
+            if ifacename in ifacenames:
+                classes.append(class_name)
+        return classes
+
+    def process_interfaces(self, filedata):
+
+        # process line continuations
+        filedata = ' '.join(d.strip() for d in filedata.split('\\'))
+
+        line_idx = 0
+        lines_consumed = 0
+        raw_config = filedata.split('\n')
+        lines = [l.strip(whitespaces) for l in raw_config]
+        while (line_idx < len(lines)):
+            if self.ignore_line(lines[line_idx]):
+                line_idx += 1
+                continue
+            words = re.split(self._ws_split_regex, lines[line_idx])
+            if not words:
+                line_idx += 1
+                continue
+            # Check if first element is a supported keyword
+            if self._is_keyword(words[0]):
+                keyword_func = self._get_keyword_func(words[0])
+                lines_consumed = keyword_func(self, lines, line_idx, line_idx+1)
+                line_idx += lines_consumed
+            else:
+                self._parse_error(self._currentfile, line_idx + 1,
+                        'error processing line \'%s\'' %lines[line_idx])
+            line_idx += 1
+        return 0
+
+    def read_filedata(self, filedata):
+        self._currentfile_has_template = False
+        # run through template engine
+        if filedata and '%' in filedata:
+            try:
+                if not self._template_engine:
+                    self._template_engine = templateEngine(
+                        template_engine=self._template_engine_name,
+                        template_enable=self._template_enable,
+                        template_lookuppath=self._template_engine_path)
+                rendered_filedata = self._template_engine.render(filedata)
+                if rendered_filedata is filedata:
+                    self._currentfile_has_template = False
+                else:
+                    self._currentfile_has_template = True
+            except Exception, e:
+                self._parse_error(self._currentfile, -1,
+                                  'failed to render template (%s). Continue without template rendering ...'
+                                  % str(e))
+                rendered_filedata = None
+            if rendered_filedata:
+                self.process_interfaces(rendered_filedata)
+                return
+        self.process_interfaces(filedata)
+
+    def read_file(self, filename, fileiobuf=None):
+        if fileiobuf:
+            self.read_filedata(fileiobuf)
+            return
+        self._filestack.append(filename)
+        self.logger.info('processing interfaces file %s' %filename)
+        try:
+            with open(filename) as f:
+                filedata = f.read()
+        except Exception, e:
+            self.logger.warn('error processing file %s (%s)',
+                             filename, str(e))
+            return
+        self.read_filedata(filedata)
+        self._filestack.pop()
+
+    def read_file_json(self, filename, fileiobuf=None):
+        if fileiobuf:
+            ifacedicts = json.loads(fileiobuf, encoding="utf-8")
+                              #object_hook=ifaceJsonDecoder.json_object_hook)
+        elif filename:
+            self.logger.info('processing interfaces file %s' %filename)
+            with open(filename) as fp:
+                ifacedicts = json.load(fp)
+                            #object_hook=ifaceJsonDecoder.json_object_hook)
+
+        # we need to handle both lists and non lists formats (e.g. {{}})
+        if not isinstance(ifacedicts,list):
+            ifacedicts = [ifacedicts]
+
+        errors = 0
+        for ifacedict in ifacedicts:
+            ifaceobj = ifaceJsonDecoder.json_to_ifaceobj(ifacedict)
+            if ifaceobj:
+                self._validate_addr_family(ifaceobj)
+                if not self.callbacks.get('validateifaceobj')(ifaceobj):
+                    errors += 1
+                self.callbacks.get('iface_found')(ifaceobj)
+        self.errors += errors
+
+    def load(self):
+        """ This member function loads the networkinterfaces file.
+
+        Assumes networkinterfaces parser object is initialized with the
+        parser arguments
+        """
+        if not self.interfacesfile and not self.interfacesfileiobuf:
+            self.logger.warn('no terminal line stdin used or ')
+            self.logger.warn('no network interfaces file defined.')
+            self.warns += 1
+            return
+
+        if self.interfacesfileformat == 'json':
+            return self.read_file_json(self.interfacesfile,
+                                       self.interfacesfileiobuf)
+        return self.read_file(self.interfacesfile,
+                              self.interfacesfileiobuf)
diff --git a/ifupdown2/ifupdown/policymanager.py b/ifupdown2/ifupdown/policymanager.py
new file mode 100644 (file)
index 0000000..d6de8c2
--- /dev/null
@@ -0,0 +1,226 @@
+#!/usr/bin/python
+#
+# Copyright 2015-2017 Cumulus Networks, Inc. All rights reserved.
+#
+#
+'''
+The PolicyManager should be subclassed by addon modules
+to read a JSON policy config file that is later used to
+set defaults:
+
+Initialize: This module defines a list of config file location based
+          on module.  There are defined in the __init__():  All the
+          addon modules need to do is import the policymanager module.
+
+          import ifupdown2.ifupdown.policymanager as policymanager
+
+
+Provides: an API to retrieve link attributes based on addon module name,
+          interface name, and attribute.
+
+        The ifupdown.policymanager module provides a global object policymanager_api
+        that can be called like so:
+
+        speed_default = policymanager.policymanager_api.get_default(
+            module_name='ethtool',
+            ifname=ifaceobj.name,
+            attr='link-speed'
+            )
+'''
+
+import json
+import glob
+import logging
+
+
+class policymanager():
+    def __init__(self):
+        # we should check for these files in order
+        # so that customers can override the /var/lib file settings
+        self.logger = logging.getLogger('ifupdown.' +
+                            self.__class__.__name__)
+
+        # we grab the json files from a known location and make sure that
+        # the defaults_policy is checked first
+        user_files = glob.glob('/etc/network/ifupdown2/policy.d/*.json')
+        # grab the default module files
+        default_files = glob.glob('/var/lib/ifupdown2/policy.d/*.json')
+        # keep an array of defaults indexed by module name
+        self.system_policy_array = {}
+        for filename in default_files:
+            system_array  = {}
+            try:
+                with open(filename, 'r') as fd:
+                    system_array = json.load(fd)
+                self.logger.debug('reading %s system policy defaults config' \
+                                  % filename)
+            except Exception, e:
+                self.logger.warning('could not read %s system policy defaults config' \
+                                  % filename)
+                self.logger.warning('    exception is %s' % str(e))
+
+            for module in system_array.keys():
+                if self.system_policy_array.has_key(module):
+                    self.logger.debug('warning: overwriting system module %s from file %s' \
+                                      % (module,filename))
+                self.system_policy_array[module] = system_array[module]
+
+        # take care of user defined policy defaults
+        self.user_policy_array = {}
+        for filename in user_files:
+            user_array  = {}
+            try:
+                with open(filename, 'r') as fd:
+                    user_array = json.load(fd)
+                self.logger.debug('reading %s policy user defaults config' \
+                                  % filename)
+            except Exception, e:
+                self.logger.warning('could not read %s user policy defaults config' \
+                                  % filename)
+                self.logger.warning('    exception is %s' % str(e))
+            # customer added module attributes
+            for module in user_array.keys():
+                if self.system_policy_array.has_key(module):
+                    # warn user that we are overriding the system module setting
+                    self.logger.debug('warning: overwriting system with user module %s from file %s' \
+                                      % (module,filename))
+                self.user_policy_array[module] = user_array[module]
+        return
+
+    def get_iface_default(self,module_name=None,ifname=None,attr=None):
+        '''
+        get_iface_default: Addon modules must use one of two types of access methods to
+        the default configs.   In this method, we expect the default to be
+        either in
+            [module]['iface_defaults'][ifname][attr] or
+            [module]['defaults'][attr]
+        We first check the user_policy_array and return that value. But if
+        the user did not specify an override, we use the system_policy_array.
+        '''
+        # make sure we have an index
+        if (not ifname or not attr or not module_name):
+            return None
+
+        val = None
+        # users can specify defaults to override the systemwide settings
+        # look for user specific interface attribute iface_defaults first
+        try:
+            # looks for user specified value
+            val = self.user_policy_array[module_name]['iface_defaults'][ifname][attr]
+            return val
+        except (TypeError, KeyError, IndexError):
+            pass
+        try:
+            # failing that, there may be a user default for all intefaces
+            val = self.user_policy_array[module_name]['defaults'][attr]
+            return val
+        except (TypeError, KeyError, IndexError):
+            pass
+        try:
+            # failing that, look for  system setting for the interface
+            val = self.system_policy_array[module_name]['iface_defaults'][ifname][attr]
+            return val
+        except (TypeError, KeyError, IndexError):
+            pass
+        try:
+            # failing that, look for  system setting for all interfaces
+            val = self.system_policy_array[module_name]['defaults'][attr]
+            return val
+        except (TypeError, KeyError, IndexError):
+            pass
+
+        # could not find any system or user default so return Non
+        return val
+
+    def get_attr_default(self,module_name=None,attr=None):
+        '''
+        get_attr_default: Addon modules must use one of two types of access methods to
+        the default configs.   In this method, we expect the default to be in
+
+        [module]['defaults'][attr]
+
+        We first check the user_policy_array and return that value. But if
+        the user did not specify an override, we use the system_policy_array.
+        '''
+        if (not attr or not module_name):
+            return None
+        # users can specify defaults to override the systemwide settings
+        # look for user specific attribute defaults first
+        val = None
+        try:
+            # looks for user specified value
+            val = self.user_policy_array[module_name]['defaults'][attr]
+            return val
+        except (TypeError, KeyError, IndexError):
+            pass
+        try:
+            # failing that, look for system setting
+            val = self.system_policy_array[module_name]['defaults'][attr]
+            return val
+        except (TypeError, KeyError, IndexError):
+            pass
+
+        return val
+
+    def get_module_globals(self,module_name=None,attr=None):
+        '''
+        get_module_globals: Addon modules must use one of two types of access methods to
+        the default configs.   In this method, we expect the default to be in
+
+        [module]['module_globals'][attr]
+
+        We first check the user_policy_array and return that value. But if
+        the user did not specify an override, we use the system_policy_array.
+        '''
+
+        if (not attr or not module_name):
+            return None
+        # users can specify defaults to override the systemwide settings
+        # look for user specific attribute defaults first
+        val = None
+        try:
+            # looks for user specified value
+            val = self.user_policy_array[module_name]['module_globals'][attr]
+            return val
+        except (TypeError, KeyError, IndexError):
+            pass
+        try:
+            # failing that, look for system setting
+            val = self.system_policy_array[module_name]['module_globals'][attr]
+            return val
+        except (TypeError, KeyError, IndexError):
+            pass
+
+        return val
+
+    def get_module_defaults(self, module_name):
+        """
+            get_module_defaults: returns a merged dictionary of default values
+            specified in policy files. Users provided values override system
+            values.
+        """
+
+        if not module_name:
+            raise NotImplementedError('get_module_defaults: module name can\'t be None')
+
+        defaults = dict()
+        defaults.update(self.system_policy_array.get(module_name, {}).get('defaults', {}))
+        defaults.update(self.user_policy_array.get(module_name, {}).get('defaults', {}))
+        return defaults
+
+    def get_iface_defaults(self, module_name):
+        defaults = dict()
+
+        if not module_name:
+            self.logger.info('get_iface_defaults: module name can\'t be None')
+        else:
+            defaults.update(self.system_policy_array.get(module_name, {}).get('iface_defaults', {}))
+            defaults.update(self.user_policy_array.get(module_name, {}).get('iface_defaults', {}))
+        return defaults
+
+
+policymanager_api = policymanager()
+
+def reset():
+    global policymanager_api
+    policymanager_api = policymanager()
diff --git a/ifupdown2/ifupdown/scheduler.py b/ifupdown2/ifupdown/scheduler.py
new file mode 100644 (file)
index 0000000..6cbe785
--- /dev/null
@@ -0,0 +1,604 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+# ifaceScheduler --
+#    interface scheduler
+#
+
+import sys
+
+from sets import Set
+
+try:
+    from ifupdown2.ifupdown.graph import *
+    from ifupdown2.ifupdown.ifupdownbase import *
+
+    from ifupdown2.ifupdown.iface import *
+    from ifupdown2.ifupdown.utils import utils
+    from ifupdown2.ifupdown.statemanager import *
+
+    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
+except ImportError:
+    from ifupdown.graph import *
+    from ifupdown.ifupdownbase import *
+
+    from ifupdown.iface import *
+    from ifupdown.utils import utils
+    from ifupdown.statemanager import *
+
+    import ifupdown.ifupdownflags as ifupdownflags
+
+
+class ifaceSchedulerFlags():
+    """ Enumerates scheduler flags """
+
+    INORDER = 0x1
+    POSTORDER = 0x2
+
+class ifaceScheduler():
+    """ scheduler functions to schedule configuration of interfaces.
+
+    supports scheduling of interfaces serially in plain interface list
+    or dependency graph format.
+
+    """
+
+    _STATE_CHECK = True
+
+    _SCHED_STATUS = True
+
+    @classmethod
+    def reset(cls):
+        cls._STATE_CHECK = True
+        cls._SCHED_STATUS = True
+
+    @classmethod
+    def get_sched_status(cls):
+        return cls._SCHED_STATUS
+
+    @classmethod
+    def set_sched_status(cls, state):
+        cls._SCHED_STATUS = state
+
+    @classmethod
+    def run_iface_op(cls, ifupdownobj, ifaceobj, op, cenv=None):
+        """ Runs sub operation on an interface """
+        ifacename = ifaceobj.name
+
+        if ifupdownobj.type and ifupdownobj.type != ifaceobj.type:
+            return
+
+        if not ifupdownobj.flags.ADDONS_ENABLE: return
+        if op == 'query-checkcurr':
+            query_ifaceobj=ifupdownobj.create_n_save_ifaceobjcurr(ifaceobj)
+            # If not type bridge vlan and the object does not exist,
+            # mark not found and return
+            if (not ifupdownobj.link_exists(ifaceobj.name) and
+                ifaceobj.type != ifaceType.BRIDGE_VLAN):
+                query_ifaceobj.set_state_n_status(ifaceState.from_str(op),
+                                                  ifaceStatus.NOTFOUND)
+                return
+        for mname in ifupdownobj.module_ops.get(op):
+            m = ifupdownobj.modules.get(mname)
+            err = 0
+            try:
+                if hasattr(m, 'run'):
+                    msg = ('%s: %s : running module %s' %(ifacename, op, mname))
+                    if op == 'query-checkcurr':
+                        # Dont check curr if the interface object was
+                        # auto generated
+                        if (ifaceobj.priv_flags and
+                            ifaceobj.priv_flags.NOCONFIG):
+                            continue
+                        ifupdownobj.logger.debug(msg)
+                        m.run(ifaceobj, op, query_ifaceobj,
+                              ifaceobj_getfunc=ifupdownobj.get_ifaceobjs)
+                    else:
+                        ifupdownobj.logger.debug(msg)
+                        m.run(ifaceobj, op,
+                              ifaceobj_getfunc=ifupdownobj.get_ifaceobjs)
+            except Exception, e:
+                if not ifupdownobj.ignore_error(str(e)):
+                   err = 1
+                   ifupdownobj.logger.error(str(e))
+                # Continue with rest of the modules
+                pass
+            finally:
+                if err or ifaceobj.status == ifaceStatus.ERROR:
+                    ifaceobj.set_state_n_status(ifaceState.from_str(op),
+                                                ifaceStatus.ERROR)
+                    if 'up' in  op or 'down' in op or 'query-checkcurr' in op:
+                        cls.set_sched_status(False)
+                else:
+                    # Mark success only if the interface was not already
+                    # marked with error
+                    status = (ifaceobj.status
+                              if ifaceobj.status == ifaceStatus.ERROR
+                              else ifaceStatus.SUCCESS)
+                    ifaceobj.set_state_n_status(ifaceState.from_str(op),
+                                                status)
+
+        if ifupdownobj.config.get('addon_scripts_support', '0') == '1':
+            # execute /etc/network/ scripts
+            os.environ['IFACE'] = ifaceobj.name if ifaceobj.name else ''
+            os.environ['LOGICAL'] = ifaceobj.name if ifaceobj.name else ''
+            os.environ['METHOD'] = ifaceobj.addr_method if ifaceobj.addr_method else ''
+            os.environ['ADDRFAM'] = ','.join(ifaceobj.addr_family) if ifaceobj.addr_family else ''
+            for mname in ifupdownobj.script_ops.get(op, []):
+                ifupdownobj.logger.debug('%s: %s : running script %s'
+                    %(ifacename, op, mname))
+                try:
+                    utils.exec_command(mname, env=cenv)
+                except Exception, e:
+                    ifupdownobj.log_error('%s: %s %s' % (ifacename, op, str(e)))
+
+    @classmethod
+    def run_iface_list_ops(cls, ifupdownobj, ifaceobjs, ops):
+        """ Runs all operations on a list of interface
+            configurations for the same interface
+        """
+
+        # minor optimization. If operation is 'down', proceed only
+        # if interface exists in the system
+        ifacename = ifaceobjs[0].name
+        ifupdownobj.logger.info('%s: running ops ...' %ifacename)
+        if ('down' in ops[0] and
+                ifaceobjs[0].type != ifaceType.BRIDGE_VLAN and
+                not ifupdownobj.link_exists(ifacename)):
+            ifupdownobj.logger.debug('%s: does not exist' %ifacename)
+            # run posthook before you get out of here, so that
+            # appropriate cleanup is done
+            posthookfunc = ifupdownobj.sched_hooks.get('posthook')
+            if posthookfunc:
+                for ifaceobj in ifaceobjs:
+                    ifaceobj.status = ifaceStatus.SUCCESS
+                    posthookfunc(ifupdownobj, ifaceobj, 'down')
+            return
+        for op in ops:
+            # first run ifupdownobj handlers. This is good enough
+            # for the first object in the list
+            handler = ifupdownobj.ops_handlers.get(op)
+            if handler:
+                try:
+                    handler(ifupdownobj, ifaceobjs[0])
+                except Exception, e:
+                    if not ifupdownobj.link_master_slave_ignore_error(str(e)):
+                       ifupdownobj.logger.warn('%s: %s'
+                                   %(ifaceobjs[0].name, str(e)))
+                    pass
+            for ifaceobj in ifaceobjs:
+                cls.run_iface_op(ifupdownobj, ifaceobj, op,
+                    cenv=ifupdownobj.generate_running_env(ifaceobj, op)
+                        if ifupdownobj.config.get('addon_scripts_support',
+                            '0') == '1' else None)
+        posthookfunc = ifupdownobj.sched_hooks.get('posthook')
+        if posthookfunc:
+            try:
+                [posthookfunc(ifupdownobj, ifaceobj, ops[0])
+                    for ifaceobj in ifaceobjs]
+            except Exception, e:
+                ifupdownobj.logger.warn('%s' %str(e))
+                pass
+
+    @classmethod
+    def _check_upperifaces(cls, ifupdownobj, ifaceobj, ops, parent,
+                           followdependents=False):
+        """ Check if upperifaces are hanging off us and help caller decide
+        if he can proceed with the ops on this device
+
+        Returns True or False indicating the caller to proceed with the
+        operation.
+        """
+        # proceed only for down operation
+        if 'down' not in ops[0]:
+            return True
+
+        if (ifupdownobj.flags.SCHED_SKIP_CHECK_UPPERIFACES):
+            return True
+
+        if (ifupdownflags.flags.FORCE or
+                not ifupdownobj.flags.ADDONS_ENABLE or
+                (not ifupdownobj.is_ifaceobj_noconfig(ifaceobj) and
+                ifupdownobj.config.get('warn_on_ifdown', '0') == '0' and
+                not ifupdownflags.flags.ALL)):
+            return True
+
+        ulist = ifaceobj.upperifaces
+        if not ulist:
+            return True
+
+        # Get the list of upper ifaces other than the parent
+        tmpulist = ([u for u in ulist if u != parent] if parent
+                    else ulist)
+        if not tmpulist:
+            return True
+        # XXX: This is expensive. Find a cheaper way to do this.
+        # if any of the upperdevs are present,
+        # return false to the caller to skip this interface
+        for u in tmpulist:
+            if ifupdownobj.link_exists(u):
+                if not ifupdownflags.flags.ALL:
+                    if ifupdownobj.is_ifaceobj_noconfig(ifaceobj):
+                        ifupdownobj.logger.info('%s: skipping interface down,'
+                            %ifaceobj.name + ' upperiface %s still around ' %u)
+                    else:
+                        ifupdownobj.logger.warn('%s: skipping interface down,'
+                            %ifaceobj.name + ' upperiface %s still around ' %u)
+                return False
+        return True
+
+    @classmethod
+    def run_iface_graph(cls, ifupdownobj, ifacename, ops, parent=None,
+                        order=ifaceSchedulerFlags.POSTORDER,
+                        followdependents=True):
+        """ runs interface by traversing all nodes rooted at itself """
+
+        # Each ifacename can have a list of iface objects
+        ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename)
+        if not ifaceobjs:
+            raise Exception('%s: not found' %ifacename)
+
+        # Check state of the dependent. If it is already brought up, return
+        if (cls._STATE_CHECK and
+            (ifaceobjs[0].state == ifaceState.from_str(ops[-1]))):
+            ifupdownobj.logger.debug('%s: already processed' %ifacename)
+            return
+
+        for ifaceobj in ifaceobjs:
+            if not cls._check_upperifaces(ifupdownobj, ifaceobj,
+                                          ops, parent, followdependents):
+               return
+
+        # If inorder, run the iface first and then its dependents
+        if order == ifaceSchedulerFlags.INORDER:
+            cls.run_iface_list_ops(ifupdownobj, ifaceobjs, ops)
+
+        for ifaceobj in ifaceobjs:
+            # Run lowerifaces or dependents
+            dlist = ifaceobj.lowerifaces
+            if dlist:
+                ifupdownobj.logger.debug('%s: found dependents %s'
+                            %(ifacename, str(dlist)))
+                try:
+                    if not followdependents:
+                        # XXX: this is yet another extra step,
+                        # but is needed for interfaces that are
+                        # implicit dependents. even though we are asked to
+                        # not follow dependents, we must follow the ones
+                        # that dont have user given config. Because we own them
+                        new_dlist = [d for d in dlist
+                                    if ifupdownobj.is_iface_noconfig(d)]
+                        if new_dlist:
+                            cls.run_iface_list(ifupdownobj, new_dlist, ops,
+                                           ifacename, order, followdependents,
+                                           continueonfailure=False)
+                    else:
+                        cls.run_iface_list(ifupdownobj, dlist, ops,
+                                            ifacename, order,
+                                            followdependents,
+                                            continueonfailure=False)
+                except Exception, e:
+                    if (ifupdownobj.ignore_error(str(e))):
+                        pass
+                    else:
+                        # Dont bring the iface up if children did not come up
+                        ifaceobj.set_state_n_status(ifaceState.NEW,
+                                                ifaceStatus.ERROR)
+                        raise
+        if order == ifaceSchedulerFlags.POSTORDER:
+            cls.run_iface_list_ops(ifupdownobj, ifaceobjs, ops)
+
+    @classmethod
+    def run_iface_list(cls, ifupdownobj, ifacenames,
+                       ops, parent=None, order=ifaceSchedulerFlags.POSTORDER,
+                       followdependents=True, continueonfailure=True):
+        """ Runs interface list """
+
+        for ifacename in ifacenames:
+            try:
+              cls.run_iface_graph(ifupdownobj, ifacename, ops, parent,
+                      order, followdependents)
+            except Exception, e:
+                if continueonfailure:
+                    if ifupdownobj.logger.isEnabledFor(logging.DEBUG):
+                        traceback.print_tb(sys.exc_info()[2])
+                    ifupdownobj.logger.error('%s : %s' %(ifacename, str(e)))
+                    pass
+                else:
+                    if (ifupdownobj.ignore_error(str(e))):
+                        pass
+                    else:
+                        raise Exception('%s : (%s)' %(ifacename, str(e)))
+
+    @classmethod
+    def run_iface_graph_upper(cls, ifupdownobj, ifacename, ops, parent=None,
+                        followdependents=True, skip_root=False):
+        """ runs interface by traversing all nodes rooted at itself """
+
+        # Each ifacename can have a list of iface objects
+        ifaceobjs = ifupdownobj.get_ifaceobjs(ifacename)
+        if not ifaceobjs:
+            raise Exception('%s: not found' %ifacename)
+
+        if (cls._STATE_CHECK and
+            (ifaceobjs[0].state == ifaceState.from_str(ops[-1]))):
+            ifupdownobj.logger.debug('%s: already processed' %ifacename)
+            return
+
+        if not skip_root:
+            # run the iface first and then its upperifaces
+            cls.run_iface_list_ops(ifupdownobj, ifaceobjs, ops)
+        for ifaceobj in ifaceobjs:
+            # Run upperifaces
+            ulist = ifaceobj.upperifaces
+            if ulist:
+                ifupdownobj.logger.debug('%s: found upperifaces %s'
+                                            %(ifacename, str(ulist)))
+                try:
+                    cls.run_iface_list_upper(ifupdownobj, ulist, ops,
+                                            ifacename,
+                                            followdependents,
+                                            continueonfailure=True)
+                except Exception, e:
+                    if (ifupdownobj.ignore_error(str(e))):
+                        pass
+                    else:
+                        raise
+
+    @classmethod
+    def run_iface_list_upper(cls, ifupdownobj, ifacenames,
+                       ops, parent=None, followdependents=True,
+                       continueonfailure=True, skip_root=False):
+        """ Runs interface list """
+
+        for ifacename in ifacenames:
+            try:
+              cls.run_iface_graph_upper(ifupdownobj, ifacename, ops, parent,
+                      followdependents, skip_root)
+            except Exception, e:
+                if ifupdownobj.logger.isEnabledFor(logging.DEBUG):
+                    traceback.print_tb(sys.exc_info()[2])
+                ifupdownobj.logger.warn('%s : %s' %(ifacename, str(e)))
+                pass
+
+    @classmethod
+    def _get_valid_upperifaces(cls, ifupdownobj, ifacenames,
+                               allupperifacenames):
+        """ Recursively find valid upperifaces
+
+        valid upperifaces are:
+            - An upperiface which had no user config (example builtin
+              interfaces. usually vlan interfaces.)
+            - or had config and previously up
+            - and interface currently does not exist
+            - or is a bridge (because if your upperiface was a bridge
+            - u will have to execute up on the bridge
+              to enslave the port and apply bridge attributes to the port) """
+
+        upperifacenames = []
+        for ifacename in ifacenames:
+            # get upperifaces
+            ifaceobj = ifupdownobj.get_ifaceobj_first(ifacename)
+            if not ifaceobj:
+               continue
+            ulist = Set(ifaceobj.upperifaces).difference(upperifacenames)
+            nulist = []
+            for u in ulist:
+                uifaceobj = ifupdownobj.get_ifaceobj_first(u)
+                if not uifaceobj:
+                   continue
+                has_config = not (uifaceobj.priv_flags and
+                                  uifaceobj.priv_flags.NOCONFIG)
+                if (((has_config and ifupdownobj.get_ifaceobjs_saved(u)) or
+                     not has_config) and (not ifupdownobj.link_exists(u)
+                         # Do this always for a bridge. Note that this is
+                         # not done for a vlan aware bridge because,
+                         # in the vlan aware bridge case, the bridge module
+                         # applies the bridge port configuration on the port
+                         # when up is scheduled on the port.
+                         or (uifaceobj.link_kind == ifaceLinkKind.BRIDGE))):
+                     nulist.append(u)
+            upperifacenames.extend(nulist)
+        allupperifacenames.extend(upperifacenames)
+        if upperifacenames:
+            cls._get_valid_upperifaces(ifupdownobj, upperifacenames,
+                                       allupperifacenames)
+        return
+
+    @classmethod
+    def run_upperifaces(cls, ifupdownobj, ifacenames, ops,
+                        continueonfailure=True):
+        """ Run through valid upperifaces """
+        upperifaces = []
+
+        cls._get_valid_upperifaces(ifupdownobj, ifacenames, upperifaces)
+        if not upperifaces:
+           return
+        # dump valid upperifaces
+        ifupdownobj.logger.debug(upperifaces)
+        for u in upperifaces:
+            try:
+                ifaceobjs = ifupdownobj.get_ifaceobjs(u)
+                if not ifaceobjs:
+                   continue
+                cls.run_iface_list_ops(ifupdownobj, ifaceobjs, ops)
+            except Exception, e:
+                if continueonfailure:
+                    ifupdownobj.logger.warn('%s' %str(e))
+
+    @classmethod
+    def _dump_dependency_info(cls, ifupdownobj, ifacenames,
+                              dependency_graph=None, indegrees=None):
+        ifupdownobj.logger.info('{\n')
+        ifupdownobj.logger.info('\nifaceobjs:')
+        for iname in ifacenames:
+               iobjs = ifupdownobj.get_ifaceobjs(iname)
+               for iobj in iobjs:
+                       iobj.dump(ifupdownobj.logger)
+        if (dependency_graph):
+            ifupdownobj.logger.info('\nDependency Graph:')
+            ifupdownobj.logger.info(dependency_graph)
+           if (indegrees):
+                   ifupdownobj.logger.info('\nIndegrees:')
+                   ifupdownobj.logger.info(indegrees)
+           ifupdownobj.logger.info('}\n')
+
+    @classmethod
+    def get_sorted_iface_list(cls, ifupdownobj, ifacenames, ops,
+                              dependency_graph, indegrees=None):
+        if len(ifacenames) == 1:
+            return ifacenames
+        # Get a sorted list of all interfaces
+        if not indegrees:
+            indegrees = OrderedDict()
+            for ifacename in dependency_graph.keys():
+                indegrees[ifacename] = ifupdownobj.get_iface_refcnt(ifacename)
+
+        #cls._dump_dependency_info(ifupdownobj, ifacenames,
+        #                          dependency_graph, indegrees)
+
+        ifacenames_all_sorted = graph.topological_sort_graphs_all(
+                                        dependency_graph, indegrees)
+        # if ALL was set, return all interfaces
+        if ifupdownflags.flags.ALL:
+            return ifacenames_all_sorted
+
+        # else return ifacenames passed as argument in sorted order
+        ifacenames_sorted = []
+        [ifacenames_sorted.append(ifacename)
+                        for ifacename in ifacenames_all_sorted
+                            if ifacename in ifacenames]
+        return ifacenames_sorted
+
+    @classmethod
+    def sched_ifaces(cls, ifupdownobj, ifacenames, ops,
+                dependency_graph=None, indegrees=None,
+                order=ifaceSchedulerFlags.POSTORDER,
+                followdependents=True, skipupperifaces=False, sort=False):
+        """ runs interface configuration modules on interfaces passed as
+            argument. Runs topological sort on interface dependency graph.
+
+        Args:
+            **ifupdownobj** (object): ifupdownMain object
+
+            **ifacenames** (list): list of interface names
+
+            **ops** : list of operations to perform eg ['pre-up', 'up', 'post-up']
+
+            **dependency_graph** (dict): dependency graph in adjacency list format
+
+        Kwargs:
+            **indegrees** (dict): indegree array of the dependency graph
+
+            **order** (int): ifaceSchedulerFlags (POSTORDER, INORDER)
+
+            **followdependents** (bool): follow dependent interfaces if true
+
+            **sort** (bool): sort ifacelist in the case where ALL is not set
+
+        """
+        #
+        # Algo:
+        # if ALL/auto interfaces are specified,
+        #   - walk the dependency tree in postorder or inorder depending
+        #     on the operation.
+        #     (This is to run interfaces correctly in order)
+        # else:
+        #   - sort iface list if the ifaces belong to a "class"
+        #   - else just run iface list in the order they were specified
+        #
+        # Run any upperifaces if available
+        #
+        followupperifaces = False
+        run_queue = []
+        skip_ifacesort = int(ifupdownobj.config.get('skip_ifacesort', '0'))
+        if not skip_ifacesort and not indegrees:
+            indegrees = OrderedDict()
+            for ifacename in dependency_graph.keys():
+                indegrees[ifacename] = ifupdownobj.get_iface_refcnt(ifacename)
+
+        if not ifupdownflags.flags.ALL:
+            if 'up' in ops[0]:
+                # If there is any interface that does not exist, maybe it
+                # is a logical interface and we have to followupperifaces
+                # when it comes up, so lets get that list.
+                if any([True for i in ifacenames
+                        if ifupdownobj.must_follow_upperifaces(i)]):
+                    followupperifaces = (True if
+                                    [i for i in ifacenames
+                                        if not ifupdownobj.link_exists(i)]
+                                        else False)
+            # sort interfaces only if the caller asked to sort
+            # and skip_ifacesort is not on.
+            if not skip_ifacesort and sort:
+                run_queue = cls.get_sorted_iface_list(ifupdownobj, ifacenames,
+                                    ops, dependency_graph, indegrees)
+                if run_queue and 'up' in ops[0]:
+                    run_queue.reverse()
+        else:
+            # if -a is set, we pick the interfaces
+            # that have no parents and use a sorted list of those
+            if not skip_ifacesort:
+                sorted_ifacenames = cls.get_sorted_iface_list(ifupdownobj,
+                                            ifacenames, ops, dependency_graph,
+                                            indegrees)
+                if sorted_ifacenames:
+                    # pick interfaces that user asked
+                    # and those that dont have any dependents first
+                    [run_queue.append(ifacename)
+                        for ifacename in sorted_ifacenames
+                            if ifacename in ifacenames and
+                            not indegrees.get(ifacename)]
+                    ifupdownobj.logger.debug('graph roots (interfaces that ' +
+                            'dont have dependents):' + ' %s' %str(run_queue))
+                else:
+                    ifupdownobj.logger.warn('interface sort returned None')
+
+        # If queue not present, just run interfaces that were asked by the
+        # user
+        if not run_queue:
+            run_queue = list(ifacenames)
+            # if we are taking the order of interfaces as specified
+            # in the interfaces file, we should reverse the list if we
+            # want to down. This can happen if 'skip_ifacesort'
+            # is been specified.
+            if 'down' in ops[0]:
+                run_queue.reverse()
+
+        # run interface list
+        cls.run_iface_list(ifupdownobj, run_queue, ops,
+                           parent=None, order=order,
+                           followdependents=followdependents)
+        if not cls.get_sched_status():
+            return
+
+        if (not skipupperifaces and
+                ifupdownobj.config.get('skip_upperifaces', '0') == '0' and
+                ((not ifupdownflags.flags.ALL and followdependents) or
+                 followupperifaces) and
+                'up' in ops[0]):
+            # If user had given a set of interfaces to bring up
+            # try and execute 'up' on the upperifaces
+            ifupdownobj.logger.info('running upperifaces (parent interfaces) ' +
+                                    'if available ..')
+            try:
+                # upperiface bring up is best effort.
+                # eg case: if we are bringing up a bridge port
+                # this section does an 'ifup on the bridge'
+                # so that the recently up'ed bridge port gets enslaved
+                # to the bridge. But the up on the bridge may
+                # throw out more errors if the bridge is not
+                # in the correct state. Lets not surprise
+                # the user with such errors when he has
+                # only requested to bring up the bridge port.
+                cls._STATE_CHECK = False
+                ifupdownflags.flags.IGNORE_ERRORS = True
+                cls.run_upperifaces(ifupdownobj, ifacenames, ops)
+            finally:
+                ifupdownflags.flags.IGNORE_ERRORS = False
+                cls._STATE_CHECK = True
+                # upperiface bringup is best effort, so dont propagate errors
+                # reset scheduler status to True
+                cls.set_sched_status(True)
diff --git a/ifupdown2/ifupdown/statemanager.py b/ifupdown2/ifupdown/statemanager.py
new file mode 100644 (file)
index 0000000..0615456
--- /dev/null
@@ -0,0 +1,200 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+# stateManager --
+#    interface state manager
+#
+
+import os
+import cPickle
+import logging
+
+try:
+    from ifupdown2.ifupdown.iface import *
+
+    import ifupdown2.ifupdown.exceptions as exceptions
+except ImportError:
+    from ifupdown.iface import *
+
+    import ifupdown.exceptions as exceptions
+
+
+class pickling():
+    """ class with helper methods for pickling/unpickling iface objects """
+
+    @classmethod
+    def save(cls, filename, list_of_objects):
+        """ pickle a list of iface objects """
+        try:
+            with open(filename, 'w') as f:
+                for obj in list_of_objects:
+                    cPickle.dump(obj, f, cPickle.HIGHEST_PROTOCOL)
+        except:
+            raise
+
+    @classmethod
+    def save_obj(cls, f, obj):
+        """ pickle iface object """
+        try:
+            cPickle.dump(obj, f, cPickle.HIGHEST_PROTOCOL)
+        except:
+            raise
+
+    @classmethod
+    def load(cls, filename):
+        """ load picked iface object """
+        with open(filename, 'r') as f:
+            while True:
+                try: yield cPickle.load(f)
+                except EOFError: break
+                except: raise
+
+class stateManager():
+    """ state manager for managing ifupdown iface obj state
+
+    ifupdown2 has to maitain old objects for down operation on
+    interfaces. ie to down or delete old configuration.
+
+    This class uses pickle to store iface objects.
+
+    """
+
+    state_dir = '/var/tmp/network/'
+    """directory where the state file is stored """
+
+    state_filename = 'ifstatenew'
+    """name of the satefile """
+
+    state_rundir = '/run/network/'
+    """name of the state run dir """
+
+    state_runlockfile = 'ifstatelock'
+    """name of the state run lock file """
+
+    def __init__(self):
+        """ Initializes statemanager internal state
+
+        which includes a dictionary of last pickled iface objects
+        """
+        self.ifaceobjdict = OrderedDict()
+        self.logger = logging.getLogger('ifupdown.' +
+                    self.__class__.__name__)
+        if not os.path.exists(self.state_dir):
+            os.mkdir(self.state_dir)
+        if not os.path.exists(self.state_rundir):
+            os.mkdir(self.state_rundir)
+        self.state_file = self.state_dir + self.state_filename
+
+    def save_ifaceobj(self, ifaceobj):
+        self.ifaceobjdict.setdefault(ifaceobj.name,
+                            []).append(ifaceobj)
+
+    def read_saved_state(self, filename=None):
+        """This member function reads saved iface objects
+
+        Kwargs:
+            filename (str): name of the state file
+        """
+
+        pickle_filename = filename
+        if not pickle_filename:
+            pickle_filename = self.state_file
+        if not os.path.exists(pickle_filename):
+            return
+        for ifaceobj in pickling.load(pickle_filename):
+            self.save_ifaceobj(ifaceobj)
+
+    def get_ifaceobjs(self, ifacename):
+        return self.ifaceobjdict.get(ifacename)
+
+    def ifaceobj_sync(self, ifaceobj, op):
+        """This member function sync's new obj state to old statemanager state
+
+        Args:
+            ifaceobj (object): new iface object
+            op (str): ifupdown operation
+        """
+
+        self.logger.debug('%s: statemanager sync state %s'
+                          %(ifaceobj.name, op))
+        old_ifaceobjs = self.ifaceobjdict.get(ifaceobj.name)
+        if 'up' in op:
+            if not old_ifaceobjs:
+                self.ifaceobjdict[ifaceobj.name] = [ifaceobj]
+            else:
+                # If it matches any of the object, return
+                if any(o.compare(ifaceobj) for o in old_ifaceobjs):
+                    return
+                # If it does not match any of the objects, and if
+                # all objs in the list came from the pickled file,
+                # then reset the list and add this object as a fresh one,
+                # else append to the list
+                if old_ifaceobjs[0].flags & iface._PICKLED:
+                    del self.ifaceobjdict[ifaceobj.name]
+                    self.ifaceobjdict[ifaceobj.name] = [ifaceobj]
+                else:
+                    self.ifaceobjdict[ifaceobj.name].append(ifaceobj)
+        elif 'down' in op:
+            # If down of object successfull, delete object from state manager
+            if not old_ifaceobjs:
+                return
+            if ifaceobj.status != ifaceStatus.SUCCESS:
+                return
+            # If it matches any of the object, return
+            oidx = 0
+            for o in old_ifaceobjs:
+                if o.compare(ifaceobj):
+                    old_ifaceobjs.pop(oidx)
+                    if not len(old_ifaceobjs):
+                        del self.ifaceobjdict[ifaceobj.name]
+                    return
+                oidx += 1
+
+    def save_state(self):
+        """ saves state (ifaceobjects) to persistent state file """
+
+        try:
+            with open(self.state_file, 'w') as f:
+                if not len(self.ifaceobjdict):
+                    f.truncate(0)
+                    return
+                self.logger.debug('saving state ..')
+                for ifaceobjs in self.ifaceobjdict.values():
+                    [pickling.save_obj(f, i) for i in ifaceobjs]
+            open('%s/%s' %(self.state_rundir, self.state_runlockfile), 'w').close()
+        except:
+            raise
+
+    def dump_pretty(self, ifacenames, format='native'):
+        if not ifacenames:
+            ifacenames = self.ifaceobjdict.keys()
+        for i in ifacenames:
+            ifaceobjs = self.get_ifaceobjs(i)
+            if not ifaceobjs:
+                continue
+            for ifaceobj in ifaceobjs:
+                if format == 'json':
+                    ifaceobj.dump_json()
+                else:
+                    ifaceobj.dump_pretty()
+
+    def dump(self, ifacenames=None):
+        self.logger.debug('statemanager iface state:')
+        if ifacenames:
+            for i in ifacenames:
+                ifaceobj = self.ifaces.get(i)
+                if ifaceobj is None:
+                    raise exceptions.ifaceNotFoundError('ifname %s'
+                                                        % i + ' not found')
+                ifaceobj.dump(self.logger)
+        else:
+            for ifacename, ifaceobjs in self.ifaceobjdict.items():
+                [i.dump(self.logger) for i in ifaceobjs]
+
+statemanager_api = stateManager()
+
+def reset():
+    global statemanager_api
+    statemanager_api = stateManager()
diff --git a/ifupdown2/ifupdown/template.py b/ifupdown2/ifupdown/template.py
new file mode 100644 (file)
index 0000000..6560180
--- /dev/null
@@ -0,0 +1,65 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+# template --
+#    helper class to render templates
+#
+
+try:
+    from ifupdown2.ifupdown.utils import *
+except ImportError:
+    from ifupdown.utils import *
+
+
+class templateEngine():
+    """ provides template rendering methods """
+
+    def __init__(self, template_engine, template_enable='0',
+                 template_lookuppath=None):
+        self.logger = logging.getLogger('ifupdown.' +
+                    self.__class__.__name__)
+        self.tclass = None
+        self.tclassargs = {}
+        self.render = self._render_default
+        if template_enable == '0':
+            return
+        if template_engine == 'mako':
+            try:
+                self.tclass = utils.importName('mako.template', 'Template')
+            except Exception, e:
+                self.logger.warn('unable to load template engine %s (%s)'
+                        %(template_engine, str(e)))
+                pass
+            if template_lookuppath:
+                try:
+                    self.logger.debug('setting template lookuppath to %s'
+                            %template_lookuppath)
+                    lc = utils.importName('mako.lookup', 'TemplateLookup')
+                    self.tclassargs['lookup'] = lc(
+                                directories=template_lookuppath.split(':'))
+                except Exception, e:
+                    self.logger.warn('unable to set template lookup path'
+                                     ' %s (%s): are you sure \'python-mako\''
+                                     'is installed?'
+                                     % (template_lookuppath, str(e)))
+            self.render = self._render_mako
+        else:
+            self.logger.info('skip template processing.., ' +
+                    'template engine not found')
+
+    def _render_default(self, textdata):
+        return textdata
+
+    def _render_mako(self, textdata):
+        """ render textdata passed as argument using mako
+
+        Returns rendered textdata """
+
+        if not self.tclass:
+            return textdata
+        self.logger.info('template processing on interfaces file ...')
+        t = self.tclass(text=textdata, output_encoding='utf-8',
+                     lookup=self.tclassargs.get('lookup'))
+        return t.render()
diff --git a/ifupdown2/ifupdown/utils.py b/ifupdown2/ifupdown/utils.py
new file mode 100644 (file)
index 0000000..f7a89a7
--- /dev/null
@@ -0,0 +1,435 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+# utils --
+#    helper class
+#
+
+import os
+import re
+import shlex
+import fcntl
+import signal
+import logging
+import subprocess
+
+from functools import partial
+from ipaddr import IPNetwork, IPAddress
+
+try:
+    from ifupdown2.ifupdown.iface import *
+
+    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
+except ImportError:
+    from ifupdown.iface import *
+
+    import ifupdown.ifupdownflags as ifupdownflags
+
+
+def signal_handler_f(ps, sig, frame):
+    if ps:
+        ps.send_signal(sig)
+    if sig == signal.SIGINT:
+        raise KeyboardInterrupt
+
+class utils():
+    logger = logging.getLogger('ifupdown')
+    DEVNULL = open(os.devnull, 'w')
+
+    _string_values = {
+        "on": True,
+        "yes": True,
+        "1": True,
+        "fast": True,
+        "off": False,
+        "no": False,
+        "0": False,
+        "slow": False
+    }
+
+    _binary_bool = {
+        True: "1",
+        False: "0",
+    }
+
+    _yesno_bool = {
+        True: 'yes',
+        False: 'no'
+    }
+
+    _onoff_bool = {
+        'yes': 'on',
+        'no': 'off'
+    }
+
+    _onoff_onezero = {
+        '1' : 'on',
+        '0' : 'off'
+    }
+
+    _yesno_onezero = {
+        '1' : 'yes',
+        '0' : 'no'
+    }
+
+    """
+    Set debian path as default path for all the commands.
+    If command not present in debian path, search for the
+    commands in the other system directories.
+    This search is carried out to handle different locations
+    on different distros.
+    If the command is not found in any of the system
+    directories, command execution will fail because we have
+    set default path same as debian path.
+    """
+    bridge_cmd      = '/sbin/bridge'
+    ip_cmd          = '/bin/ip'
+    brctl_cmd       = '/sbin/brctl'
+    pidof_cmd       = '/bin/pidof'
+    service_cmd     = '/usr/sbin/service'
+    sysctl_cmd      = '/sbin/sysctl'
+    modprobe_cmd    = '/sbin/modprobe'
+    pstree_cmd      = '/usr/bin/pstree'
+    ss_cmd          = '/bin/ss'
+    vrrpd_cmd       = '/usr/sbin/vrrpd'
+    ifplugd_cmd     = '/usr/sbin/ifplugd'
+    mstpctl_cmd     = '/sbin/mstpctl'
+    ethtool_cmd     = '/sbin/ethtool'
+    systemctl_cmd   = '/bin/systemctl'
+    dpkg_cmd        = '/usr/bin/dpkg'
+
+    for cmd in ['bridge',
+                'ip',
+                'brctl',
+                'pidof',
+                'service',
+                'sysctl',
+                'modprobe',
+                'pstree',
+                'ss',
+                'vrrpd',
+                'ifplugd',
+                'mstpctl',
+                'ethtool',
+                'systemctl',
+                'dpkg'
+                ]:
+        if os.path.exists(vars()[cmd + '_cmd']):
+            continue
+        for path in ['/bin/',
+                     '/sbin/',
+                     '/usr/bin/',
+                     '/usr/sbin/',]:
+            if os.path.exists(path + cmd):
+                vars()[cmd + '_cmd'] = path + cmd
+            else:
+                logger.debug('warning: path %s not found: %s won\'t be usable' % (path + cmd, cmd))
+
+    @staticmethod
+    def get_onff_from_onezero(value):
+        if value in utils._onoff_onezero:
+            return utils._onoff_onezero[value]
+        return value
+
+    @staticmethod
+    def get_yesno_from_onezero(value):
+        if value in utils._yesno_onezero:
+            return utils._yesno_onezero[value]
+        return value
+
+    @staticmethod
+    def get_onoff_bool(value):
+        if value in utils._onoff_bool:
+            return utils._onoff_bool[value]
+        return value
+
+    @staticmethod
+    def get_boolean_from_string(value):
+        return utils._string_values.get(value, False)
+
+    @staticmethod
+    def get_yesno_boolean(bool):
+        return utils._yesno_bool[bool]
+
+    @staticmethod
+    def boolean_support_binary(value):
+        return utils._binary_bool[utils.get_boolean_from_string(value)]
+
+    @staticmethod
+    def is_binary_bool(value):
+        return value == '0' or value == '1'
+
+    @staticmethod
+    def support_yesno_attrs(attrsdict, attrslist, ifaceobj=None):
+        if ifaceobj:
+            for attr in attrslist:
+                value = ifaceobj.get_attr_value_first(attr)
+                if value and not utils.is_binary_bool(value):
+                    if attr in attrsdict:
+                        bool = utils.get_boolean_from_string(attrsdict[attr])
+                        attrsdict[attr] = utils.get_yesno_boolean(bool)
+        else:
+            for attr in attrslist:
+                if attr in attrsdict:
+                    attrsdict[attr] = utils.boolean_support_binary(attrsdict[attr])
+
+    @staticmethod
+    def get_int_from_boolean_and_string(value):
+        try:
+            return int(value)
+        except:
+            return int(utils.get_boolean_from_string(value))
+
+    @staticmethod
+    def strip_hwaddress(hwaddress):
+        if hwaddress and hwaddress.startswith("ether"):
+            hwaddress = hwaddress[5:].strip()
+        return hwaddress.lower() if hwaddress else hwaddress
+        # we need to "normalize" the user provided MAC so it can match with
+        # what we have in the cache (data retrieved via a netlink dump by
+        # nlmanager). nlmanager return all macs in lower-case
+
+    @classmethod
+    def importName(cls, modulename, name):
+        """ Import a named object """
+        try:
+            module = __import__(modulename, globals(), locals(), [name])
+        except ImportError:
+            return None
+        return getattr(module, name)
+
+    @classmethod
+    def lockFile(cls, lockfile):
+        try:
+            fp = os.open(lockfile, os.O_CREAT | os.O_TRUNC | os.O_WRONLY)
+            fcntl.flock(fp, fcntl.LOCK_EX | fcntl.LOCK_NB)
+            fcntl.fcntl(fp, fcntl.F_SETFD, fcntl.FD_CLOEXEC)
+        except IOError:
+            return False
+        return True
+
+    @classmethod
+    def parse_iface_range(cls, name):
+        # eg: swp1.[2-100]
+        # return (prefix, range-start, range-end)
+        # eg return ("swp1.", 1, 20, ".100")
+        range_match = re.match("^([\w]+)\[([\d]+)-([\d]+)\]([\.\w]+)", name)
+        if range_match:
+            range_groups = range_match.groups()
+            if range_groups[1] and range_groups[2]:
+                return (range_groups[0], int(range_groups[1], 10),
+                        int(range_groups[2], 10), range_groups[3])
+        else:
+            # eg: swp[1-20].100
+            # return (prefix, range-start, range-end, suffix)
+            # eg return ("swp", 1, 20, ".100")
+            range_match = re.match("^([\w\.]+)\[([\d]+)-([\d]+)\]", name)
+            if range_match:
+                range_groups = range_match.groups()
+                if range_groups[1] and range_groups[2]:
+                    return (range_groups[0], int(range_groups[1], 10),
+                            int(range_groups[2], 10))
+        return None
+
+    @classmethod
+    def expand_iface_range(cls, name):
+        ifacenames = []
+        irange = cls.parse_iface_range(name)
+        if irange:
+            if len(irange) == 3:
+                # eg swp1.[2-4], r = "swp1.", 2, 4)
+                for i in range(irange[1], irange[2]):
+                    ifacenames.append('%s%d' %(irange[0], i))
+            elif len(irange) == 4:
+                for i in range(irange[1], irange[2]):
+                    # eg swp[2-4].100, r = ("swp", 2, 4, ".100")
+                    ifacenames.append('%s%d%s' %(irange[0], i, irange[3]))
+        return ifacenames
+
+    @classmethod
+    def is_ifname_range(cls, name):
+        if '[' in name or ']' in name:
+            return True
+        return False
+
+    @classmethod
+    def check_ifname_size_invalid(cls, name=''):
+        """ IFNAMSIZ in include/linux/if.h is 16 so we check this """
+        IFNAMSIZ = 16
+        if len(name) > IFNAMSIZ - 1:
+            return True
+        else:
+            return False
+
+    @classmethod
+    def enable_subprocess_signal_forwarding(cls, ps, sig):
+        signal.signal(sig, partial(signal_handler_f, ps))
+
+    @classmethod
+    def disable_subprocess_signal_forwarding(cls, sig):
+        signal.signal(sig, signal.SIG_DFL)
+
+    @classmethod
+    def _log_command_exec(cls, cmd, stdin):
+        if stdin:
+            cls.logger.info('executing %s [%s]' % (cmd, stdin))
+        else:
+            cls.logger.info('executing %s' % cmd)
+
+    @classmethod
+    def _format_error(cls, cmd, cmd_returncode, cmd_output, stdin):
+        if type(cmd) is list:
+            cmd = ' '.join(cmd)
+        if stdin:
+            cmd = '%s [%s]' % (cmd, stdin)
+        if cmd_output:
+            return 'cmd \'%s\' failed: returned %d (%s)' % \
+                   (cmd, cmd_returncode, cmd_output)
+        else:
+            return 'cmd \'%s\' failed: returned %d' % (cmd, cmd_returncode)
+
+    @classmethod
+    def get_normalized_ip_addr(cls, ifacename, ipaddrs):
+        if not ipaddrs: return None
+        if isinstance(ipaddrs, list):
+                addrs = []
+                for ip in ipaddrs:
+                    if not ip:
+                        continue
+                    try:
+                        addrs.append(str(IPNetwork(ip)) if '/' in ip else str(IPAddress(ip)))
+                    except Exception as e:
+                        cls.logger.warning('%s: %s' % (ifacename, e))
+                return addrs
+        else:
+            try:
+                return str(IPNetwork(ipaddrs)) if '/' in ipaddrs else str(IPAddress(ipaddrs))
+            except Exception as e:
+                cls.logger.warning('%s: %s' % (ifacename, e))
+            return ipaddrs
+
+    @classmethod
+    def get_ip_objs(cls, module_name, ifname, addrs_list):
+        addrs_obj_list = []
+        for a in addrs_list or []:
+            try:
+                addrs_obj_list.append(IPNetwork(a) if '/' in a else IPAddress(a))
+            except Exception as e:
+                cls.logger.warning('%s: %s: %s' % (module_name, ifname, str(e)))
+        return addrs_obj_list
+
+    @classmethod
+    def get_ip_obj(cls, module_name, ifname, addr):
+        if addr:
+            try:
+                return IPNetwork(addr) if '/' in addr else IPAddress(addr)
+            except Exception as e:
+                cls.logger.warning('%s: %s: %s' % (module_name, ifname, str(e)))
+        return None
+
+    @classmethod
+    def is_addr_ip_allowed_on(cls, ifaceobj, syntax_check=False):
+        msg = ('%s: ignoring ip address. Assigning an IP '
+               'address is not allowed on' % ifaceobj.name)
+        if (ifaceobj.role & ifaceRole.SLAVE
+                and not (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE)):
+            up = None
+            if ifaceobj.upperifaces:
+                up = ifaceobj.upperifaces[0]
+            msg = ('%s enslaved interfaces. %s'
+                   % (msg, ('%s is enslaved to %s'
+                            % (ifaceobj.name, up)) if up else '')).strip()
+            if syntax_check:
+                cls.logger.warning(msg)
+            else:
+                cls.logger.info(msg)
+            return False
+        elif (ifaceobj.link_kind & ifaceLinkKind.BRIDGE
+                and ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE):
+            msg = '%s bridge vlan aware interfaces'
+            if syntax_check:
+                cls.logger.warning(msg)
+            else:
+                cls.logger.info(msg)
+            return False
+        return True
+
+    @classmethod
+    def _execute_subprocess(cls, cmd,
+                            env=None,
+                            shell=False,
+                            close_fds=False,
+                            stdout=True,
+                            stdin=None,
+                            stderr=subprocess.STDOUT):
+        """
+        exec's commands using subprocess Popen
+            Args:
+                cmd, should be shlex.split if not shell
+        returns: output
+
+        Note: close_fds=True is affecting performance (2~3 times slower)
+        """
+        if ifupdownflags.flags.DRYRUN:
+            return ''
+
+        cmd_output = None
+        try:
+            ch = subprocess.Popen(cmd,
+                                  env=env,
+                                  shell=shell,
+                                  close_fds=close_fds,
+                                  stdin=subprocess.PIPE if stdin else None,
+                                  stdout=subprocess.PIPE if stdout else cls.DEVNULL,
+                                  stderr=stderr)
+            utils.enable_subprocess_signal_forwarding(ch, signal.SIGINT)
+            if stdout or stdin:
+                cmd_output = ch.communicate(input=stdin)[0]
+            cmd_returncode = ch.wait()
+        except Exception as e:
+            raise Exception('cmd \'%s\' failed (%s)' % (' '.join(cmd), str(e)))
+        finally:
+            utils.disable_subprocess_signal_forwarding(signal.SIGINT)
+        if cmd_returncode != 0:
+            raise Exception(cls._format_error(cmd,
+                                              cmd_returncode,
+                                              cmd_output,
+                                              stdin))
+        return cmd_output
+
+    @classmethod
+    def exec_user_command(cls, cmd, close_fds=False, stdout=True,
+                          stdin=None, stderr=subprocess.STDOUT):
+        cls._log_command_exec(cmd, stdin)
+        return cls._execute_subprocess(cmd,
+                                       shell=True,
+                                       close_fds=close_fds,
+                                       stdout=stdout,
+                                       stdin=stdin,
+                                       stderr=stderr)
+
+    @classmethod
+    def exec_command(cls, cmd, env=None, close_fds=False, stdout=True,
+                     stdin=None, stderr=subprocess.STDOUT):
+        cls._log_command_exec(cmd, stdin)
+        return cls._execute_subprocess(shlex.split(cmd),
+                                       env=env,
+                                       close_fds=close_fds,
+                                       stdout=stdout,
+                                       stdin=stdin,
+                                       stderr=stderr)
+
+    @classmethod
+    def exec_commandl(cls, cmdl, env=None, close_fds=False, stdout=True,
+                      stdin=None, stderr=subprocess.STDOUT):
+        cls._log_command_exec(' '.join(cmdl), stdin)
+        return cls._execute_subprocess(cmdl,
+                                       env=env,
+                                       close_fds=close_fds,
+                                       stdout=stdout,
+                                       stdin=stdin,
+                                       stderr=stderr)
+
+fcntl.fcntl(utils.DEVNULL, fcntl.F_SETFD, fcntl.FD_CLOEXEC)
diff --git a/ifupdown2/ifupdownaddons/LinkUtils.py b/ifupdown2/ifupdownaddons/LinkUtils.py
new file mode 100644 (file)
index 0000000..ab93f77
--- /dev/null
@@ -0,0 +1,2542 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#         Julien Fortin, julien@cumulusnetworks.com
+#
+
+import os
+import re
+import glob
+import shlex
+import signal
+import subprocess
+
+from ipaddr import IPNetwork, IPv6Network
+
+try:
+    import ifupdown2.ifupdown.statemanager as statemanager
+    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
+
+    from ifupdown2.nlmanager.nlmanager import Link, Route
+
+    from ifupdown2.ifupdown.iface import *
+    from ifupdown2.ifupdown.utils import utils
+    from ifupdown2.ifupdown.netlink import netlink
+
+    from ifupdown2.ifupdownaddons.utilsbase import utilsBase
+    from ifupdown2.ifupdownaddons.cache import linkCache, MSTPAttrsCache
+except ImportError:
+    import ifupdown.ifupdownflags as ifupdownflags
+    import ifupdown.statemanager as statemanager
+
+    from nlmanager.nlmanager import Link, Route
+
+    from ifupdown.iface import *
+    from ifupdown.utils import utils
+    from ifupdown.netlink import netlink
+
+    from ifupdownaddons.utilsbase import utilsBase
+    from ifupdownaddons.cache import linkCache, MSTPAttrsCache
+
+
+class LinkUtils(utilsBase):
+    """
+    This class contains helper methods to cache and manipulate interfaces through
+    non-netlink APIs (sysfs, iproute2, brctl...)
+    """
+    _CACHE_FILL_DONE = False
+    VXLAN_UDP_PORT = 4789
+
+    ipbatchbuf = ''
+    ipbatch = False
+    ipbatch_pause = False
+
+    bridge_utils_is_installed = os.path.exists(utils.brctl_cmd)
+    bridge_utils_missing_warning = True
+
+    def __init__(self, *args, **kargs):
+        utilsBase.__init__(self, *args, **kargs)
+
+        self.supported_command = {
+            '%s -c -json vlan show' % utils.bridge_cmd: True,
+            'showmcqv4src': True
+        }
+        self.bridge_vlan_cache = {}
+        self.bridge_vlan_cache_fill_done = False
+
+        if not ifupdownflags.flags.PERFMODE and not LinkUtils._CACHE_FILL_DONE:
+            self._fill_cache()
+
+    @classmethod
+    def reset(cls):
+        LinkUtils._CACHE_FILL_DONE = False
+        LinkUtils.ipbatchbuf = ''
+        LinkUtils.ipbatch = False
+        LinkUtils.ipbatch_pause = False
+
+    def _fill_cache(self):
+        if not LinkUtils._CACHE_FILL_DONE:
+            self._link_fill()
+            self._addr_fill()
+            LinkUtils._CACHE_FILL_DONE = True
+            return True
+        return False
+
+    @staticmethod
+    def _get_vland_id(citems, i, warn):
+        try:
+            sub = citems[i:]
+            index = sub.index('id')
+            int(sub[index + 1])
+            return sub[index + 1]
+        except:
+            if warn:
+                raise Exception('invalid use of \'vlan\' keyword')
+            return None
+
+    def _link_fill(self, ifacename=None, refresh=False):
+        """ fills cache with link information
+
+        if ifacename argument given, fill cache for ifacename, else
+        fill cache for all interfaces in the system
+        """
+
+        if LinkUtils._CACHE_FILL_DONE and not refresh:
+            return
+        try:
+            # if ifacename already present, return
+            if (ifacename and not refresh and
+                    linkCache.get_attr([ifacename, 'ifflag'])):
+                return
+        except:
+            pass
+
+        if True:
+            try:
+                [linkCache.update_attrdict([ifname], linkattrs)
+                 for ifname, linkattrs in netlink.link_dump(ifacename).items()]
+            except Exception as e:
+                self.logger.info('%s' % str(e))
+                # this netlink call replaces the call to _link_fill_iproute2_cmd()
+                # We shouldn't have netlink calls in the iproute2 module, this will
+                # be removed in the future. We plan to release, a flexible backend
+                # (netlink+iproute2) by default we will use netlink backend but with
+                # a CLI arg we can switch to iproute2 backend.
+                # Until we decide to create this "backend" switch capability,
+                # we have to put the netlink call inside the iproute2 module.
+        else:
+            self._link_fill_iproute2_cmd(ifacename, refresh)
+
+        self._fill_bond_info(ifacename)
+        self._fill_bridge_info(ifacename)
+
+    def _fill_bridge_info(self, ifacename):
+
+        if True:  # netlink
+            brports = {}
+
+            if ifacename:
+                cache_dict = {ifacename: linkCache.links.get(ifacename, {})}
+            else:
+                cache_dict = linkCache.links
+
+            for ifname, obj in cache_dict.items():
+                slave_kind = obj.get('slave_kind')
+                if not slave_kind and slave_kind != 'bridge':
+                    continue
+
+                info_slave_data = obj.get('info_slave_data')
+                if not info_slave_data:
+                    continue
+
+                ifla_master = obj.get('master')
+                if not ifla_master:
+                    raise Exception('No master associated with bridge port %s' % ifname)
+
+                for nl_attr in [
+                    Link.IFLA_BRPORT_STATE,
+                    Link.IFLA_BRPORT_COST,
+                    Link.IFLA_BRPORT_PRIORITY,
+                ]:
+                    if nl_attr not in info_slave_data and LinkUtils.bridge_utils_is_installed:
+                        self._fill_bridge_info_brctl()
+                        return
+
+                brport_attrs = {
+                    'pathcost': str(info_slave_data.get(Link.IFLA_BRPORT_COST, 0)),
+                    'fdelay': format(float(info_slave_data.get(Link.IFLA_BRPORT_FORWARD_DELAY_TIMER, 0) / 100), '.2f'),
+                    'portmcrouter': str(info_slave_data.get(Link.IFLA_BRPORT_MULTICAST_ROUTER, 0)),
+                    'portmcfl': str(info_slave_data.get(Link.IFLA_BRPORT_FAST_LEAVE, 0)),
+                    'portprio': str(info_slave_data.get(Link.IFLA_BRPORT_PRIORITY, 0)),
+                    'unicast-flood': str(info_slave_data.get(Link.IFLA_BRPORT_UNICAST_FLOOD, 0)),
+                    'multicast-flood': str(info_slave_data.get(Link.IFLA_BRPORT_MCAST_FLOOD, 0)),
+                    'learning': str(info_slave_data.get(Link.IFLA_BRPORT_LEARNING, 0)),
+                    'arp-nd-suppress': str(info_slave_data.get(Link.IFLA_BRPORT_ARP_SUPPRESS, 0))
+                }
+
+                if ifla_master in brports:
+                    brports[ifla_master][ifname] = brport_attrs
+                else:
+                    brports[ifla_master] = {ifname: brport_attrs}
+
+                linkCache.update_attrdict([ifla_master, 'linkinfo', 'ports'], brports[ifla_master])
+        else:
+            if LinkUtils.bridge_utils_is_installed:
+                self._fill_bridge_info_brctl()
+
+    def _fill_bridge_info_brctl(self):
+        brctlout = utils.exec_command('%s show' % utils.brctl_cmd)
+        if not brctlout:
+            return
+
+        for bline in brctlout.splitlines()[1:]:
+            bitems = bline.split()
+            if len(bitems) < 2:
+                continue
+            try:
+                linkCache.update_attrdict([bitems[0], 'linkinfo'],
+                                          {'stp': bitems[2]})
+            except KeyError:
+                linkCache.update_attrdict([bitems[0]],
+                                          {'linkinfo': {'stp': bitems[2]}})
+            self._bridge_attrs_fill(bitems[0])
+
+    def _bridge_attrs_fill(self, bridgename):
+        battrs = {}
+        bports = {}
+
+        brout = utils.exec_command('%s showstp %s' % (utils.brctl_cmd, bridgename))
+        chunks = re.split(r'\n\n', brout, maxsplit=0, flags=re.MULTILINE)
+
+        try:
+            # Get all bridge attributes
+            broutlines = chunks[0].splitlines()
+            # battrs['pathcost'] = broutlines[3].split('path cost')[1].strip()
+
+            try:
+                battrs['maxage'] = broutlines[4].split('bridge max age')[
+                    1].strip().replace('.00', '')
+            except:
+                pass
+
+            try:
+                battrs['hello'] = broutlines[5].split('bridge hello time')[
+                    1].strip().replace('.00', '')
+            except:
+                pass
+
+            try:
+                battrs['fd'] = broutlines[6].split('bridge forward delay')[
+                    1].strip().replace('.00', '')
+            except:
+                pass
+
+            try:
+                battrs['ageing'] = broutlines[7].split('ageing time')[
+                    1].strip().replace('.00', '')
+            except:
+                pass
+
+            try:
+                battrs['mcrouter'] = broutlines[12].split('mc router')[
+                    1].strip().split('\t\t\t')[0]
+            except:
+                pass
+
+            try:
+                battrs['bridgeprio'] = self.read_file_oneline(
+                    '/sys/class/net/%s/bridge/priority' % bridgename)
+            except:
+                pass
+
+            try:
+                battrs['vlan-protocol'] = VlanProtocols.ID_TO_ETHERTYPES[
+                    self.read_file_oneline(
+                        '/sys/class/net/%s/bridge/vlan_protocol' % bridgename)]
+            except:
+                pass
+
+            try:
+                battrs.update(self._bridge_get_mcattrs_from_sysfs(bridgename))
+            except:
+                pass
+
+                # XXX: comment this out until mc attributes become available
+                # with brctl again
+                # battrs['hashel'] = broutlines[10].split('hash elasticity')[1].split()[0].strip()
+                # battrs['hashmax'] = broutlines[10].split('hash max')[1].strip()
+                # battrs['mclmc'] = broutlines[11].split('mc last member count')[1].split()[0].strip()
+                # battrs['mciqc'] = broutlines[11].split('mc init query count')[1].strip()
+                # battrs['mcrouter'] = broutlines[12].split('mc router')[1].split()[0].strip()
+                ##battrs['mcsnoop'] = broutlines[12].split('mc snooping')[1].strip()
+                # battrs['mclmt'] = broutlines[13].split('mc last member timer')[1].split()[0].strip()
+        except Exception, e:
+            self.logger.warn('%s: error while processing bridge attributes: %s' % (bridgename, str(e)))
+            pass
+
+        linkCache.update_attrdict([bridgename, 'linkinfo'], battrs)
+        for cidx in range(1, len(chunks)):
+            bpout = chunks[cidx].lstrip('\n')
+            if not bpout or bpout[0] == ' ':
+                continue
+            bplines = bpout.splitlines()
+            pname = bplines[0].split()[0]
+            bportattrs = {}
+            try:
+                bportattrs['pathcost'] = bplines[2].split(
+                    'path cost')[1].strip()
+                bportattrs['fdelay'] = bplines[4].split(
+                    'forward delay timer')[1].strip()
+                bportattrs['portmcrouter'] = self.read_file_oneline(
+                    '/sys/class/net/%s/brport/multicast_router' % pname)
+                bportattrs['portmcfl'] = self.read_file_oneline(
+                    '/sys/class/net/%s/brport/multicast_fast_leave' % pname)
+                bportattrs['portprio'] = self.read_file_oneline(
+                    '/sys/class/net/%s/brport/priority' % pname)
+                bportattrs['unicast-flood'] = self.read_file_oneline(
+                    '/sys/class/net/%s/brport/unicast_flood' % pname)
+                bportattrs['multicast-flood'] = self.read_file_oneline(
+                    '/sys/class/net/%s/brport/multicast_flood' % pname)
+                bportattrs['learning'] = self.read_file_oneline(
+                    '/sys/class/net/%s/brport/learning' % pname)
+                bportattrs['arp-nd-suppress'] = self.read_file_oneline(
+                    '/sys/class/net/%s/brport/neigh_suppress' % pname)
+                # bportattrs['mcrouters'] = bplines[6].split('mc router')[1].split()[0].strip()
+                # bportattrs['mc fast leave'] = bplines[6].split('mc fast leave')[1].strip()
+            except Exception, e:
+                self.logger.warn('%s: error while processing bridge attributes: %s' % (bridgename, str(e)))
+            bports[pname] = bportattrs
+            linkCache.update_attrdict([bridgename, 'linkinfo', 'ports'], bports)
+
+    _bridge_sysfs_mcattrs = {
+        'mclmc': 'multicast_last_member_count',
+        'mcrouter': 'multicast_router',
+        'mcsnoop': 'multicast_snooping',
+        'mcsqc': 'multicast_startup_query_count',
+        'mcqifaddr': 'multicast_query_use_ifaddr',
+        'mcquerier': 'multicast_querier',
+        'hashel': 'hash_elasticity',
+        'hashmax': 'hash_max',
+        'mclmi': 'multicast_last_member_interval',
+        'mcmi': 'multicast_membership_interval',
+        'mcqpi': 'multicast_querier_interval',
+        'mcqi': 'multicast_query_interval',
+        'mcqri': 'multicast_query_response_interval',
+        'mcsqi': 'multicast_startup_query_interval',
+        'igmp-version': 'multicast_igmp_version',
+        'mld-version': 'multicast_mld_version',
+        'vlan-stats': 'vlan_stats_enabled',
+        'mcstats': 'multicast_stats_enabled',
+    }
+
+    def _bridge_get_mcattrs_from_sysfs(self, bridgename):
+        mcattrsdivby100 = ['mclmi', 'mcmi', 'mcqpi', 'mcqi', 'mcqri', 'mcsqi']
+        mcattrs = {}
+
+        for m, s in self._bridge_sysfs_mcattrs.items():
+            n = self.read_file_oneline('/sys/class/net/%s/bridge/%s' % (bridgename, s))
+            if m in mcattrsdivby100:
+                try:
+                    v = int(n) / 100
+                    mcattrs[m] = str(v)
+                except Exception, e:
+                    self.logger.warn('error getting mc attr %s (%s)' % (m, str(e)))
+                    pass
+            else:
+                mcattrs[m] = n
+        return mcattrs
+
+    def _fill_bond_info(self, ifacename):
+        bonding_masters = self.read_file_oneline('/sys/class/net/bonding_masters')
+        if not bonding_masters:
+            return
+
+        bond_masters_list = bonding_masters.split()
+
+        if ifacename:
+            if ifacename in bond_masters_list:
+                bond_masters_list = [ifacename]
+            else:
+                # we want to refresh this interface only if it's a bond master
+                return
+
+        for bondname in bond_masters_list:
+            try:
+                if bondname not in linkCache.links:
+                    linkCache.set_attr([bondname], {'linkinfo': {}})
+                linkCache.set_attr([bondname, 'linkinfo', 'slaves'],
+                                   self.read_file_oneline('/sys/class/net/%s/bonding/slaves'
+                                                          % bondname).split())
+                try:
+                    # if some attribute are missing we try to get the bond attributes via sysfs
+                    bond_linkinfo = linkCache.links[bondname]['linkinfo']
+                    for attr in [Link.IFLA_BOND_MODE, Link.IFLA_BOND_XMIT_HASH_POLICY, Link.IFLA_BOND_MIN_LINKS]:
+                        if attr not in bond_linkinfo:
+                            self._fill_bond_info_sysfs(bondname)
+                            # after we fill in the cache we can continue to the next bond
+                            break
+                except:
+                    self._fill_bond_info_sysfs(bondname)
+
+            except Exception as e:
+                self.logger.debug('LinkUtils: bond cache error: %s' % str(e))
+
+    def _fill_bond_info_sysfs(self, bondname):
+        try:
+            linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_MIN_LINKS],
+                               self.read_file_oneline(
+                                   '/sys/class/net/%s/bonding/min_links'
+                                   % bondname))
+        except Exception as e:
+            self.logger.debug(str(e))
+
+        try:
+            linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_MODE],
+                               self.read_file_oneline('/sys/class/net/%s/bonding/mode'
+                                                      % bondname).split()[0])
+        except Exception as e:
+            self.logger.debug(str(e))
+        try:
+            linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_XMIT_HASH_POLICY],
+                               self.read_file_oneline(
+                                   '/sys/class/net/%s/bonding/xmit_hash_policy'
+                                   % bondname).split()[0])
+        except Exception as e:
+            self.logger.debug(str(e))
+        try:
+            linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_RATE],
+                               self.read_file_oneline('/sys/class/net/%s/bonding/lacp_rate'
+                                                      % bondname).split()[1])
+        except Exception as e:
+            self.logger.debug(str(e))
+        try:
+            linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYS_PRIO],
+                               self.read_file_oneline('/sys/class/net/%s/bonding/ad_actor_sys_prio'
+                                                      % bondname))
+        except Exception as e:
+            self.logger.debug(str(e))
+        try:
+            linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYSTEM],
+                               self.read_file_oneline('/sys/class/net/%s/bonding/ad_actor_system'
+                                                      % bondname))
+        except Exception as e:
+            self.logger.debug(str(e))
+        try:
+            linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_BYPASS],
+                               self.read_file_oneline('/sys/class/net/%s/bonding/lacp_bypass'
+                                                      % bondname).split()[1])
+        except Exception as e:
+            self.logger.debug(str(e))
+        try:
+            linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_UPDELAY],
+                               self.read_file_oneline('/sys/class/net/%s/bonding/updelay'
+                                                      % bondname))
+        except Exception as e:
+            self.logger.debug(str(e))
+        try:
+            linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_DOWNDELAY],
+                               self.read_file_oneline('/sys/class/net/%s/bonding/downdelay'
+                                                      % bondname))
+        except Exception as e:
+            self.logger.debug(str(e))
+
+        try:
+            linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_USE_CARRIER],
+                               self.read_file_oneline('/sys/class/net/%s/bonding/use_carrier' % bondname))
+        except Exception as e:
+            self.logger.debug(str(e))
+
+        try:
+            linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_MIIMON],
+                               self.read_file_oneline('/sys/class/net/%s/bonding/miimon' % bondname))
+        except Exception as e:
+            self.logger.debug(str(e))
+
+        try:
+            linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF],
+                               self.read_file_oneline('/sys/class/net/%s/bonding/num_unsol_na' % bondname))
+        except Exception as e:
+            self.logger.debug(str(e))
+
+        try:
+            linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF],
+                               self.read_file_oneline('/sys/class/net/%s/bonding/num_grat_arp' % bondname))
+        except Exception as e:
+            self.logger.debug(str(e))
+
+
+    def _link_fill_iproute2_cmd(self, ifacename=None, refresh=False):
+        warn = True
+        linkout = {}
+        if LinkUtils._CACHE_FILL_DONE and not refresh:
+            return
+        try:
+            # if ifacename already present, return
+            if (ifacename and not refresh and
+                    linkCache.get_attr([ifacename, 'ifflag'])):
+                return
+        except:
+            pass
+        cmdout = self.link_show(ifacename=ifacename)
+        if not cmdout:
+            return
+        for c in cmdout.splitlines():
+            citems = c.split()
+            ifnamenlink = citems[1].split('@')
+            if len(ifnamenlink) > 1:
+                ifname = ifnamenlink[0]
+                iflink = ifnamenlink[1].strip(':')
+            else:
+                ifname = ifnamenlink[0].strip(':')
+                iflink = None
+            linkattrs = dict()
+            linkattrs['link'] = iflink
+            linkattrs['ifindex'] = citems[0].strip(':')
+            flags = citems[2].strip('<>').split(',')
+            linkattrs['flags'] = flags
+            linkattrs['ifflag'] = 'UP' if 'UP' in flags else 'DOWN'
+            for i in range(0, len(citems)):
+                try:
+                    if citems[i] == 'mtu':
+                        linkattrs['mtu'] = citems[i + 1]
+                    elif citems[i] == 'state':
+                        linkattrs['state'] = citems[i + 1]
+                    elif citems[i] == 'link/ether':
+                        linkattrs['hwaddress'] = citems[i + 1]
+                    elif citems[i] == 'vlan':
+                        vlanid = self._get_vland_id(citems, i, warn)
+                        if vlanid:
+                            linkattrs['linkinfo'] = {'vlanid': vlanid}
+                            linkattrs['kind'] = 'vlan'
+                    elif citems[i] == 'dummy':
+                        linkattrs['kind'] = 'dummy'
+                    elif citems[i] == 'vxlan' and citems[i + 1] == 'id':
+                        linkattrs['kind'] = 'vxlan'
+                        vattrs = {'vxlanid': citems[i + 2],
+                                  'svcnode': None,
+                                  'remote': [],
+                                  'ageing': citems[i + 2],
+                                  'learning': 'on'}
+                        for j in range(i + 2, len(citems)):
+                            if citems[j] == 'local':
+                                vattrs['local'] = citems[j + 1]
+                            elif citems[j] == 'remote':
+                                vattrs['svcnode'] = citems[j + 1]
+                            elif citems[j] == 'ageing':
+                                vattrs['ageing'] = citems[j + 1]
+                            elif citems[j] == 'nolearning':
+                                vattrs['learning'] = 'off'
+                        linkattrs['linkinfo'] = vattrs
+                        break
+                    elif citems[i] == 'vrf' and citems[i + 1] == 'table':
+                        vattrs = {'table': citems[i + 2]}
+                        linkattrs['linkinfo'] = vattrs
+                        linkattrs['kind'] = 'vrf'
+                        linkCache.vrfs[ifname] = vattrs
+                        break
+                    elif citems[i] == 'veth':
+                        linkattrs['kind'] = 'veth'
+                    elif citems[i] == 'vrf_slave':
+                        linkattrs['slave_kind'] = 'vrf_slave'
+                        break
+                    elif citems[i] == 'macvlan' and citems[i + 1] == 'mode':
+                        linkattrs['kind'] = 'macvlan'
+                except Exception as e:
+                    if warn:
+                        self.logger.debug('%s: parsing error: id, mtu, state, '
+                                          'link/ether, vlan, dummy, vxlan, local, '
+                                          'remote, ageing, nolearning, vrf, table, '
+                                          'vrf_slave are reserved keywords: %s' %
+                                          (ifname, str(e)))
+                        warn = False
+            # linkattrs['alias'] = self.read_file_oneline(
+            #            '/sys/class/net/%s/ifalias' %ifname)
+            linkout[ifname] = linkattrs
+        [linkCache.update_attrdict([ifname], linkattrs)
+         for ifname, linkattrs in linkout.items()]
+
+    @staticmethod
+    def _addr_filter(ifname, addr, scope=None):
+        default_addrs = ['127.0.0.1/8', '::1/128', '0.0.0.0']
+        if ifname == 'lo' and addr in default_addrs:
+            return True
+        if scope and scope == 'link':
+            return True
+        return False
+
+    def _addr_fill(self, ifacename=None, refresh=False):
+        """ fills cache with address information
+
+        if ifacename argument given, fill cache for ifacename, else
+        fill cache for all interfaces in the system
+        """
+        if LinkUtils._CACHE_FILL_DONE and not refresh:
+            return
+        try:
+            # Check if ifacename is already full, in which case, return
+            if ifacename and not refresh:
+                linkCache.get_attr([ifacename, 'addrs'])
+                return
+        except:
+            pass
+
+        if True:
+            try:
+                [linkCache.update_attrdict([ifname], linkattrs)
+                 for ifname, linkattrs in netlink.addr_dump(ifname=ifacename).items()]
+            except Exception as e:
+                self.logger.info(str(e))
+
+                # this netlink call replaces the call to _addr_fill_iproute2_cmd()
+                # We shouldn't have netlink calls in the iproute2 module, this will
+                # be removed in the future. We plan to release, a flexible backend
+                # (netlink+iproute2) by default we will use netlink backend but with
+                # a CLI arg we can switch to iproute2 backend.
+                # Until we decide to create this "backend" switch capability,
+                # we have to put the netlink call inside the iproute2 module.
+
+        else:
+            self._addr_fill_iproute2_cmd(ifacename, refresh)
+
+    def _addr_fill_iproute2_cmd(self, ifacename=None, refresh=False):
+        """ fills cache with address information
+
+        if ifacename argument given, fill cache for ifacename, else
+        fill cache for all interfaces in the system
+        """
+        linkout = {}
+        if LinkUtils._CACHE_FILL_DONE and not refresh:
+            return
+        try:
+            # Check if ifacename is already full, in which case, return
+            if ifacename and not refresh:
+                linkCache.get_attr([ifacename, 'addrs'])
+                return
+        except:
+            pass
+        cmdout = self.addr_show(ifacename=ifacename)
+        if not cmdout:
+            return
+        for c in cmdout.splitlines():
+            citems = c.split()
+            ifnamenlink = citems[1].split('@')
+            if len(ifnamenlink) > 1:
+                ifname = ifnamenlink[0]
+            else:
+                ifname = ifnamenlink[0].strip(':')
+            if not linkout.get(ifname):
+                linkattrs = dict()
+                linkattrs['addrs'] = OrderedDict({})
+                try:
+                    linkout[ifname].update(linkattrs)
+                except KeyError:
+                    linkout[ifname] = linkattrs
+            if citems[2] == 'inet':
+                if self._addr_filter(ifname, citems[3], scope=citems[5]):
+                    continue
+                addrattrs = dict()
+                addrattrs['scope'] = citems[5]
+                addrattrs['type'] = 'inet'
+                linkout[ifname]['addrs'][citems[3]] = addrattrs
+            elif citems[2] == 'inet6':
+                if self._addr_filter(ifname, citems[3], scope=citems[5]):
+                    continue
+                if citems[5] == 'link':
+                    continue  # skip 'link' addresses
+                addrattrs = dict()
+                addrattrs['scope'] = citems[5]
+                addrattrs['type'] = 'inet6'
+                linkout[ifname]['addrs'][citems[3]] = addrattrs
+        [linkCache.update_attrdict([ifname], linkattrs)
+         for ifname, linkattrs in linkout.items()]
+
+    def cache_get(self, t, attrlist, refresh=False):
+        return self._cache_get(t, attrlist, refresh)
+
+    def _cache_get(self, t, attrlist, refresh=False):
+        try:
+            if ifupdownflags.flags.DRYRUN:
+                return False
+            if ifupdownflags.flags.CACHE:
+                if self._fill_cache():
+                    # if we filled the cache, return new data
+                    return linkCache.get_attr(attrlist)
+                if not refresh:
+                    return linkCache.get_attr(attrlist)
+            if t == 'link':
+                self._link_fill(attrlist[0], refresh)
+            elif t == 'addr':
+                self._addr_fill(attrlist[0], refresh)
+            else:
+                self._link_fill(attrlist[0], refresh)
+                self._addr_fill(attrlist[0], refresh)
+            return linkCache.get_attr(attrlist)
+        except Exception, e:
+            self.logger.debug('_cache_get(%s) : [%s]' % (str(attrlist), str(e)))
+        return None
+
+    def cache_check(self, attrlist, value, refresh=False):
+        return self._cache_check('link', attrlist, value, refresh=refresh)
+
+    def _cache_check(self, t, attrlist, value, refresh=False):
+        try:
+            return self._cache_get(t, attrlist, refresh) == value
+        except Exception, e:
+            self.logger.debug('_cache_check(%s) : [%s]'
+                              % (str(attrlist), str(e)))
+        return False
+
+    def cache_update(self, attrlist, value):
+        return self._cache_update(attrlist, value)
+
+    @staticmethod
+    def _cache_update(attrlist, value):
+        if ifupdownflags.flags.DRYRUN:
+            return
+        try:
+            if attrlist[-1] == 'slaves':
+                linkCache.append_to_attrlist(attrlist, value)
+                return
+            linkCache.set_attr(attrlist, value)
+        except:
+            pass
+
+    @staticmethod
+    def _cache_delete(attrlist, value=None):
+        if ifupdownflags.flags.DRYRUN:
+            return
+        try:
+            if value:
+                linkCache.remove_from_attrlist(attrlist, value)
+            else:
+                linkCache.del_attr(attrlist)
+        except:
+            pass
+
+    @staticmethod
+    def _cache_invalidate():
+        linkCache.invalidate()
+        LinkUtils._CACHE_FILL_DONE = False
+
+    @staticmethod
+    def batch_start():
+        LinkUtils.ipbatcbuf = ''
+        LinkUtils.ipbatch = True
+        LinkUtils.ipbatch_pause = False
+
+    @staticmethod
+    def add_to_batch(cmd):
+        LinkUtils.ipbatchbuf += cmd + '\n'
+
+    @staticmethod
+    def batch_pause():
+        LinkUtils.ipbatch_pause = True
+
+    @staticmethod
+    def batch_resume():
+        LinkUtils.ipbatch_pause = False
+
+    def batch_commit(self):
+        if not LinkUtils.ipbatchbuf:
+            LinkUtils.ipbatchbuf = ''
+            LinkUtils.ipbatch = False
+            LinkUtils.ipbatch_pause = False
+            return
+        try:
+            utils.exec_command('%s -force -batch -' % utils.ip_cmd,
+                               stdin=self.ipbatchbuf)
+        except:
+            raise
+        finally:
+            LinkUtils.ipbatchbuf = ''
+            LinkUtils.ipbatch = False
+            LinkUtils.ipbatch_pause = False
+
+    def bridge_batch_commit(self):
+        if not LinkUtils.ipbatchbuf:
+            LinkUtils.ipbatchbuf = ''
+            LinkUtils.ipbatch = False
+            LinkUtils.ipbatch_pause = False
+            return
+        try:
+            utils.exec_command('%s -force -batch -'
+                               % utils.bridge_cmd, stdin=self.ipbatchbuf)
+        except:
+            raise
+        finally:
+            LinkUtils.ipbatchbuf = ''
+            LinkUtils.ipbatch = False
+            LinkUtils.ipbatch_pause = False
+
+    def addr_show(self, ifacename=None):
+        if ifacename:
+            if not self.link_exists(ifacename):
+                return
+            return utils.exec_commandl([utils.ip_cmd,
+                                        '-o', 'addr', 'show', 'dev', ifacename])
+        else:
+            return utils.exec_commandl([utils.ip_cmd,
+                                        '-o', 'addr', 'show'])
+
+    @staticmethod
+    def link_show(ifacename=None):
+        if ifacename:
+            return utils.exec_commandl([utils.ip_cmd,
+                                        '-o', '-d', 'link', 'show', 'dev', ifacename])
+        else:
+            return utils.exec_commandl([utils.ip_cmd,
+                                        '-o', '-d', 'link', 'show'])
+
+    def addr_add(self, ifacename, address, broadcast=None,
+                 peer=None, scope=None, preferred_lifetime=None):
+        if not address:
+            return
+        cmd = 'addr add %s' % address
+        if broadcast:
+            cmd += ' broadcast %s' % broadcast
+        if peer:
+            cmd += ' peer %s' % peer
+        if scope:
+            cmd += ' scope %s' % scope
+        if preferred_lifetime:
+            cmd += ' preferred_lft %s' % preferred_lifetime
+        cmd += ' dev %s' % ifacename
+        if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
+            self.add_to_batch(cmd)
+        else:
+            utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
+        self._cache_update([ifacename, 'addrs', address], {})
+
+    def addr_del(self, ifacename, address, broadcast=None,
+                 peer=None, scope=None):
+        """ Delete ipv4 address """
+        if not address:
+            return
+        if not self._cache_get('addr', [ifacename, 'addrs', address]):
+            return
+        cmd = 'addr del %s' % address
+        if broadcast:
+            cmd += 'broadcast %s' % broadcast
+        if peer:
+            cmd += 'peer %s' % peer
+        if scope:
+            cmd += 'scope %s' % scope
+        cmd += ' dev %s' % ifacename
+        utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
+        self._cache_delete([ifacename, 'addrs', address])
+
+    def addr_flush(self, ifacename):
+        cmd = 'addr flush dev %s' % ifacename
+        if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
+            self.add_to_batch(cmd)
+        else:
+            utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
+        self._cache_delete([ifacename, 'addrs'])
+
+    def del_addr_all(self, ifacename, skip_addrs=[]):
+        if not skip_addrs:
+            skip_addrs = []
+        runningaddrsdict = self.get_running_addrs(ifname=ifacename)
+        try:
+            # XXX: ignore errors. Fix this to delete secondary addresses
+            # first
+            [self.addr_del(ifacename, a) for a in
+             set(runningaddrsdict.keys()).difference(skip_addrs)]
+        except:
+            # ignore errors
+            pass
+
+    def addr_get(self, ifacename, details=True, refresh=False):
+        addrs = self._cache_get('addr', [ifacename, 'addrs'], refresh=refresh)
+        if not addrs:
+            return None
+        if details:
+            return addrs
+        return addrs.keys()
+
+    def get_running_addrs(self, ifaceobj=None, ifname=None, details=True, addr_virtual_ifaceobj=None):
+        """
+            We now support addr with link scope. Since the kernel may add it's
+            own link address to some interfaces we need to filter them out and
+            make sure we only deal with the addresses set by ifupdown2.
+
+            To do so we look at the previous configuration made by ifupdown2
+            (with the help of the statemanager) together with the addresses
+            specified by the user in /etc/network/interfaces, these addresses
+            are then compared to the running state of the intf (ip addr show)
+            made via a netlink addr dump.
+            For each configured addresses of scope link, we check if it was
+            previously configured by ifupdown2 to create a final set of the
+            addresses watched by ifupdown2
+        """
+        if not ifaceobj and not ifname:
+            return None
+
+        config_addrs = set()
+
+        if ifaceobj:
+            interface_name = ifaceobj.name
+        else:
+            interface_name = ifname
+
+        if addr_virtual_ifaceobj:
+            for virtual in addr_virtual_ifaceobj.get_attr_value('address-virtual') or []:
+                for ip in virtual.split():
+                    try:
+                        IPNetwork(ip)
+                        config_addrs.add(ip)
+                    except:
+                        pass
+
+            saved_ifaceobjs = statemanager.statemanager_api.get_ifaceobjs(addr_virtual_ifaceobj.name)
+            for saved_ifaceobj in saved_ifaceobjs or []:
+                for virtual in saved_ifaceobj.get_attr_value('address-virtual') or []:
+                    for ip in virtual.split():
+                        try:
+                            IPNetwork(ip)
+                            config_addrs.add(ip)
+                        except:
+                            pass
+        else:
+            if ifaceobj:
+                for addr in ifaceobj.get_attr_value('address') or []:
+                    config_addrs.add(addr)
+
+            saved_ifaceobjs = statemanager.statemanager_api.get_ifaceobjs(interface_name)
+            for saved_ifaceobj in saved_ifaceobjs or []:
+                for addr in saved_ifaceobj.get_attr_value('address') or []:
+                    config_addrs.add(addr)
+
+        running_addrs = OrderedDict()
+        cached_addrs = self.addr_get(interface_name)
+        if cached_addrs:
+            for addr, addr_details in cached_addrs.items():
+                try:
+                    scope = int(addr_details['scope'])
+                except Exception:
+                    try:
+                        d = {}
+                        addr_obj = IPNetwork(addr)
+                        if isinstance(addr_obj, IPv6Network):
+                            d['family'] = 'inet6'
+                        else:
+                            d['family'] = 'inet'
+                        running_addrs[addr] = d
+                    except:
+                        running_addrs[addr] = {}
+                    continue
+                if (scope & Route.RT_SCOPE_LINK and addr in config_addrs) or not scope & Route.RT_SCOPE_LINK:
+                    running_addrs[addr] = addr_details
+        else:
+            return None
+
+        if details:
+            return running_addrs
+        return running_addrs.keys()
+
+    @staticmethod
+    def compare_user_config_vs_running_state(running_addrs, user_addrs):
+        ip4 = []
+        ip6 = []
+
+        for ip in user_addrs or []:
+            obj = IPNetwork(ip)
+
+            if type(obj) == IPv6Network:
+                ip6.append(obj)
+            else:
+                ip4.append(obj)
+
+        running_ipobj = []
+        for ip in running_addrs or []:
+            running_ipobj.append(IPNetwork(ip))
+
+        return running_ipobj == (ip4 + ip6)
+
+    def addr_add_multiple(self, ifaceobj, ifacename, addrs, purge_existing=False):
+        # purges address
+        if purge_existing:
+            # if perfmode is not set and also if iface has no sibling
+            # objects, purge addresses that are not present in the new
+            # config
+            runningaddrs = self.get_running_addrs(
+                ifname=ifacename,
+                details=False,
+                addr_virtual_ifaceobj=ifaceobj
+            )
+            addrs = utils.get_normalized_ip_addr(ifacename, addrs)
+
+            if self.compare_user_config_vs_running_state(runningaddrs, addrs):
+                return
+            try:
+                # if primary address is not same, there is no need to keep any.
+                # reset all addresses
+                if (addrs and runningaddrs and
+                        (addrs[0] != runningaddrs[0])):
+                    self.del_addr_all(ifacename)
+                else:
+                    self.del_addr_all(ifacename, addrs)
+            except Exception, e:
+                self.logger.warning('%s: %s' % (ifacename, str(e)))
+        for a in addrs:
+            try:
+                self.addr_add(ifacename, a)
+            except Exception, e:
+                self.logger.error(str(e))
+
+    def _link_set_ifflag(self, ifacename, value):
+        # Dont look at the cache, the cache may have stale value
+        # because link status can be changed by external
+        # entity (One such entity is ifupdown main program)
+        cmd = 'link set dev %s %s' % (ifacename, value.lower())
+        if LinkUtils.ipbatch:
+            self.add_to_batch(cmd)
+        else:
+            utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
+
+    def link_up(self, ifacename):
+        self._link_set_ifflag(ifacename, 'UP')
+
+    def link_down(self, ifacename):
+        self._link_set_ifflag(ifacename, 'DOWN')
+
+    def link_set(self, ifacename, key, value=None,
+                 force=False, t=None, state=None):
+        if not force:
+            if (key not in ['master', 'nomaster'] and
+                    self._cache_check('link', [ifacename, key], value)):
+                return
+        cmd = 'link set dev %s' % ifacename
+        if t:
+            cmd += ' type %s' % t
+        cmd += ' %s' % key
+        if value:
+            cmd += ' %s' % value
+        if state:
+            cmd += ' %s' % state
+        if LinkUtils.ipbatch:
+            self.add_to_batch(cmd)
+        else:
+            utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
+        if key not in ['master', 'nomaster']:
+            self._cache_update([ifacename, key], value)
+
+    def link_set_hwaddress(self, ifacename, hwaddress, force=False):
+        if not force:
+            if self._cache_check('link', [ifacename, 'hwaddress'], hwaddress):
+                return
+        self.link_down(ifacename)
+        cmd = 'link set dev %s address %s' % (ifacename, hwaddress)
+        if LinkUtils.ipbatch:
+            self.add_to_batch(cmd)
+        else:
+            utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
+        self.link_up(ifacename)
+        self._cache_update([ifacename, 'hwaddress'], hwaddress)
+
+    def link_set_mtu(self, ifacename, mtu):
+        if ifupdownflags.flags.DRYRUN:
+            return True
+        if not mtu or not ifacename: return
+        self.write_file('/sys/class/net/%s/mtu' % ifacename, mtu)
+        self._cache_update([ifacename, 'mtu'], mtu)
+
+    def link_set_alias(self, ifacename, alias):
+        self.write_file('/sys/class/net/%s/ifalias' % ifacename,
+                        '\n' if not alias else alias)
+
+    def link_get_alias(self, ifacename):
+        return self.read_file_oneline('/sys/class/net/%s/ifalias'
+                                      % ifacename)
+
+    def link_isloopback(self, ifacename):
+        flags = self._cache_get('link', [ifacename, 'flags'])
+        if not flags:
+            return
+        if 'LOOPBACK' in flags:
+            return True
+        return False
+
+    def link_get_status(self, ifacename):
+        return self._cache_get('link', [ifacename, 'ifflag'], refresh=True)
+
+    @staticmethod
+    def route_add_gateway(ifacename, gateway, vrf=None, metric=None):
+        if not gateway:
+            return
+        if not vrf:
+            cmd = '%s route add default via %s proto kernel' % (utils.ip_cmd,
+                                                   gateway)
+        else:
+            cmd = ('%s route add table %s default via %s proto kernel' %
+                   (utils.ip_cmd, vrf, gateway))
+        # Add metric
+        if metric:
+            cmd += 'metric %s' % metric
+        cmd += ' dev %s' % ifacename
+        utils.exec_command(cmd)
+
+    @staticmethod
+    def route_del_gateway(ifacename, gateway, vrf=None, metric=None):
+        # delete default gw
+        if not gateway:
+            return
+        if not vrf:
+            cmd = ('%s route del default via %s proto kernel' %
+                   (utils.ip_cmd, gateway))
+        else:
+            cmd = ('%s route del table %s default via %s proto kernel' %
+                   (utils.ip_cmd, vrf, gateway))
+        if metric:
+            cmd += ' metric %s' % metric
+        cmd += ' dev %s' % ifacename
+        utils.exec_command(cmd)
+
+    @staticmethod
+    def _get_vrf_id(ifacename):
+        try:
+            return linkCache.vrfs[ifacename]['table']
+        except KeyError:
+            dump = netlink.link_dump(ifacename)
+
+            [linkCache.update_attrdict([ifname], linkattrs)
+             for ifname, linkattrs in dump.items()]
+
+            if dump and dump.get(ifacename, {}).get('kind') == 'vrf':
+                vrf_table = dump.get(ifacename, {}).get('linkinfo', {}).get('table')
+                linkCache.vrfs[ifacename] = {'table': vrf_table}
+                return vrf_table
+
+            return None
+
+    def fix_ipv6_route_metric(self, ifaceobj, macvlan_ifacename, ips):
+        vrf_table = None
+
+        if ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE:
+            try:
+                for upper_iface in ifaceobj.upperifaces:
+                    vrf_table = self._get_vrf_id(upper_iface)
+                    if vrf_table:
+                        break
+            except:
+                pass
+
+        ip_route_del = []
+        for ip in ips:
+            ip_network_obj = IPNetwork(ip)
+
+            if type(ip_network_obj) == IPv6Network:
+                route_prefix = '%s/%d' % (ip_network_obj.network, ip_network_obj.prefixlen)
+
+                if vrf_table:
+                    if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
+                        LinkUtils.add_to_batch('route del %s table %s dev %s' % (route_prefix, vrf_table, macvlan_ifacename))
+                    else:
+                        utils.exec_commandl([utils.ip_cmd, 'route', 'del', route_prefix, 'table', vrf_table, 'dev', macvlan_ifacename])
+                else:
+                    if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
+                        LinkUtils.add_to_batch('route del %s dev %s' % (route_prefix, macvlan_ifacename))
+                    else:
+                        utils.exec_commandl([utils.ip_cmd, 'route', 'del', route_prefix, 'dev', macvlan_ifacename])
+                ip_route_del.append((route_prefix, vrf_table))
+
+        for ip, vrf_table in ip_route_del:
+            if vrf_table:
+                if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
+                    LinkUtils.add_to_batch('route add %s table %s dev %s proto kernel metric 9999' % (ip, vrf_table, macvlan_ifacename))
+                else:
+                    utils.exec_commandl([utils.ip_cmd, 'route', 'add', ip, 'table', vrf_table, 'dev', macvlan_ifacename, 'proto', 'kernel' 'metric', '9999'])
+            else:
+                if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
+                    LinkUtils.add_to_batch('route add %s dev %s proto kernel metric 9999' % (ip, macvlan_ifacename))
+                else:
+                    utils.exec_commandl([utils.ip_cmd, 'route', 'add', ip, 'dev', macvlan_ifacename, 'proto', 'kernel' 'metric', '9999'])
+
+    def link_create_vlan(self, vlan_device_name, vlan_raw_device, vlanid):
+        if self.link_exists(vlan_device_name):
+            return
+        utils.exec_command('%s link add link %s name %s type vlan id %d' %
+                           (utils.ip_cmd,
+                            vlan_raw_device, vlan_device_name, vlanid))
+        self._cache_update([vlan_device_name], {})
+
+    def link_create_vlan_from_name(self, vlan_device_name):
+        v = vlan_device_name.split('.')
+        if len(v) != 2:
+            self.logger.warn('invalid vlan device name %s' % vlan_device_name)
+            return
+        self.link_create_vlan(vlan_device_name, v[0], v[1])
+
+    def link_create_macvlan(self, name, linkdev, mode='private'):
+        if self.link_exists(name):
+            return
+        cmd = ('link add link %s' % linkdev +
+               ' name %s' % name +
+               ' type macvlan mode %s' % mode)
+        if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
+            self.add_to_batch(cmd)
+        else:
+            utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
+        self._cache_update([name], {})
+
+    def get_vxlan_peers(self, dev, svcnodeip):
+        cmd = '%s fdb show brport %s' % (utils.bridge_cmd,
+                                         dev)
+        cur_peers = []
+        try:
+            ps = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, close_fds=False)
+            utils.enable_subprocess_signal_forwarding(ps, signal.SIGINT)
+            output = subprocess.check_output(('grep', '00:00:00:00:00:00'), stdin=ps.stdout)
+            ps.wait()
+            utils.disable_subprocess_signal_forwarding(signal.SIGINT)
+            try:
+                ppat = re.compile('\s+dst\s+(\d+.\d+.\d+.\d+)\s+')
+                for l in output.split('\n'):
+                    m = ppat.search(l)
+                    if m and m.group(1) != svcnodeip:
+                        cur_peers.append(m.group(1))
+            except:
+                self.logger.warn('error parsing ip link output')
+        except subprocess.CalledProcessError as e:
+            if e.returncode != 1:
+                self.logger.error(str(e))
+        finally:
+            utils.disable_subprocess_signal_forwarding(signal.SIGINT)
+
+        return cur_peers
+
+    def link_create_vxlan(self, name, vxlanid,
+                          localtunnelip=None,
+                          svcnodeip=None,
+                          remoteips=None,
+                          learning='on',
+                          ageing=None,
+                          anycastip=None):
+        if svcnodeip and remoteips:
+            raise Exception("svcnodeip and remoteip is mutually exclusive")
+        args = ''
+        if svcnodeip:
+            args += ' remote %s' % svcnodeip
+        if ageing:
+            args += ' ageing %s' % ageing
+        if learning == 'off':
+            args += ' nolearning'
+
+        if self.link_exists(name):
+            cmd = 'link set dev %s type vxlan dstport %d' % (name, LinkUtils.VXLAN_UDP_PORT)
+            vxlanattrs = self.get_vxlandev_attrs(name)
+            # on ifreload do not overwrite anycast_ip to individual ip if clagd
+            # has modified
+            if vxlanattrs:
+                running_localtunnelip = vxlanattrs.get('local')
+                if anycastip and running_localtunnelip and anycastip == running_localtunnelip:
+                    localtunnelip = running_localtunnelip
+                running_svcnode = vxlanattrs.get('svcnode')
+                if running_svcnode and not svcnodeip:
+                    args += ' noremote'
+        else:
+            cmd = 'link add dev %s type vxlan id %s dstport %d' % (name, vxlanid, LinkUtils.VXLAN_UDP_PORT)
+
+        if localtunnelip:
+            args += ' local %s' % localtunnelip
+        cmd += args
+
+        if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
+            self.add_to_batch(cmd)
+        else:
+            utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
+
+        # XXX: update linkinfo correctly
+        #self._cache_update([name], {})
+
+    @staticmethod
+    def link_exists(ifacename):
+        if ifupdownflags.flags.DRYRUN:
+            return True
+        return os.path.exists('/sys/class/net/%s' % ifacename)
+
+    def link_get_ifindex(self, ifacename):
+        if ifupdownflags.flags.DRYRUN:
+            return True
+        return self.read_file_oneline('/sys/class/net/%s/ifindex' % ifacename)
+
+    def is_vlan_device_by_name(self, ifacename):
+        if re.search(r'\.', ifacename):
+            return True
+        return False
+
+    @staticmethod
+    def link_add_macvlan(ifname, macvlan_ifacename):
+        utils.exec_commandl(['ip', 'link', 'add',  'link', ifname, 'name', macvlan_ifacename, 'type', 'macvlan', 'mode', 'private'])
+
+    @staticmethod
+    def route_add(route):
+        utils.exec_command('%s route add %s' % (utils.ip_cmd,
+                                                route))
+
+    @staticmethod
+    def route6_add(route):
+        utils.exec_command('%s -6 route add %s' % (utils.ip_cmd,
+                                                   route))
+
+    def get_vlandev_attrs(self, ifacename):
+        return (self._cache_get('link', [ifacename, 'link']),
+                self._cache_get('link', [ifacename, 'linkinfo', 'vlanid']),
+                self._cache_get('link', [ifacename, 'linkinfo', 'vlan_protocol']))
+
+    def get_vlan_protocol(self, ifacename):
+        return self._cache_get('link', [ifacename, 'linkinfo', 'vlan_protocol'])
+
+    def get_vxlandev_attrs(self, ifacename):
+        return self._cache_get('link', [ifacename, 'linkinfo'])
+
+    def get_vxlandev_learning(self, ifacename):
+        return self._cache_get('link', [ifacename, 'linkinfo', Link.IFLA_VXLAN_LEARNING])
+
+    def set_vxlandev_learning(self, ifacename, learn):
+        if learn == 'on':
+            utils.exec_command('%s link set dev %s type vxlan learning' %
+                               (utils.ip_cmd, ifacename))
+            self._cache_update([ifacename, 'linkinfo', 'learning'], 'on')
+        else:
+            utils.exec_command('%s link set dev %s type vxlan nolearning' %
+                               (utils.ip_cmd, ifacename))
+            self._cache_update([ifacename, 'linkinfo', 'learning'], 'off')
+
+    def link_get_linkinfo_attrs(self, ifacename):
+        return self._cache_get('link', [ifacename, 'linkinfo'])
+
+    def link_get_mtu(self, ifacename, refresh=False):
+        return self._cache_get('link', [ifacename, 'mtu'], refresh=refresh)
+
+    def link_get_mtu_sysfs(self, ifacename):
+        return self.read_file_oneline('/sys/class/net/%s/mtu'
+                                      % ifacename)
+
+    def link_get_kind(self, ifacename):
+        return self._cache_get('link', [ifacename, 'kind'])
+
+    def link_get_slave_kind(self, ifacename):
+        return self._cache_get('link', [ifacename, 'slave_kind'])
+
+    def link_get_hwaddress(self, ifacename):
+        address = self._cache_get('link', [ifacename, 'hwaddress'])
+        # newly created logical interface addresses dont end up in the cache
+        # read hwaddress from sysfs file for these interfaces
+        if not address:
+            address = self.read_file_oneline('/sys/class/net/%s/address'
+                                             % ifacename)
+        return address
+
+    def link_create(self, ifacename, t, attrs={}):
+        """ generic link_create function """
+        if self.link_exists(ifacename):
+            return
+        cmd = 'link add'
+        cmd += ' name %s type %s' % (ifacename, t)
+        if attrs:
+            for k, v in attrs.iteritems():
+                cmd += ' %s' % k
+                if v:
+                    cmd += ' %s' % v
+        if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
+            self.add_to_batch(cmd)
+        else:
+            utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
+        self._cache_update([ifacename], {})
+
+    def link_delete(self, ifacename):
+        if not self.link_exists(ifacename):
+            return
+        cmd = 'link del %s' % ifacename
+        if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
+            self.add_to_batch(cmd)
+        else:
+            utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
+        self._cache_invalidate()
+
+    def link_get_master(self, ifacename):
+        sysfs_master_path = '/sys/class/net/%s/master' % ifacename
+        if os.path.exists(sysfs_master_path):
+            link_path = os.readlink(sysfs_master_path)
+            if link_path:
+                return os.path.basename(link_path)
+            else:
+                return None
+        else:
+            return self._cache_get('link', [ifacename, 'master'])
+
+    def get_brport_peer_link(self, bridgename):
+        try:
+            return self._cache_get('link', [bridgename, 'info_slave_data', Link.IFLA_BRPORT_PEER_LINK])
+        except:
+            return None
+
+    @staticmethod
+    def bridge_port_vids_add(bridgeportname, vids):
+        [utils.exec_command('%s vlan add vid %s dev %s' %
+                            (utils.bridge_cmd,
+                             v, bridgeportname)) for v in vids]
+
+    @staticmethod
+    def bridge_port_vids_del(bridgeportname, vids):
+        if not vids:
+            return
+        [utils.exec_command('%s vlan del vid %s dev %s' %
+                            (utils.bridge_cmd,
+                             v, bridgeportname)) for v in vids]
+
+    @staticmethod
+    def bridge_port_vids_flush(bridgeportname, vid):
+        utils.exec_command('%s vlan del vid %s dev %s' %
+                           (utils.bridge_cmd,
+                            vid, bridgeportname))
+
+    @staticmethod
+    def bridge_port_vids_get(bridgeportname):
+        bridgeout = utils.exec_command('%s vlan show dev %s' %
+                                       (utils.bridge_cmd,
+                                        bridgeportname))
+        if not bridgeout:
+            return []
+        brvlanlines = bridgeout.readlines()[2:]
+        vids = [l.strip() for l in brvlanlines]
+        return [v for v in vids if v]
+
+    @staticmethod
+    def bridge_port_vids_get_all():
+        brvlaninfo = {}
+        bridgeout = utils.exec_command('%s -c vlan show'
+                                       % utils.bridge_cmd)
+        if not bridgeout:
+            return brvlaninfo
+        brvlanlines = bridgeout.splitlines()
+        brportname = None
+        for l in brvlanlines[1:]:
+            if l and not l.startswith(' ') and not l.startswith('\t'):
+                attrs = l.split()
+                brportname = attrs[0].strip()
+                brvlaninfo[brportname] = {'pvid': None, 'vlan': []}
+                l = ' '.join(attrs[1:])
+            if not brportname or not l:
+                continue
+            l = l.strip()
+            if 'PVID' in l:
+                brvlaninfo[brportname]['pvid'] = l.split()[0]
+            elif 'Egress Untagged' not in l:
+                brvlaninfo[brportname]['vlan'].append(l)
+        return brvlaninfo
+
+    def bridge_port_vids_get_all_json(self):
+        if not self.supported_command['%s -c -json vlan show'
+                % utils.bridge_cmd]:
+            return {}
+        brvlaninfo = {}
+        try:
+            bridgeout = utils.exec_command('%s -c -json vlan show'
+                                           % utils.bridge_cmd)
+        except:
+            self.supported_command['%s -c -json vlan show'
+                                   % utils.bridge_cmd] = False
+            self.logger.info('%s -c -json vlan show: skipping unsupported command'
+                             % utils.bridge_cmd)
+            try:
+                return self.get_bridge_vlan_nojson()
+            except Exception as e:
+                self.logger.info('bridge: get_bridge_vlan_nojson: %s' % str(e))
+                return {}
+
+        if not bridgeout: return brvlaninfo
+        try:
+            vlan_json_dict = json.loads(bridgeout, encoding="utf-8")
+        except Exception, e:
+            self.logger.info('json loads failed with (%s)' % str(e))
+            return {}
+        return vlan_json_dict
+
+    @staticmethod
+    def get_bridge_vlan_nojson():
+        vlan_json = {}
+        bridgeout = utils.exec_commandl([utils.bridge_cmd, '-c', 'vlan', 'show'])
+        if bridgeout:
+            output = [line.split('\n') for line in bridgeout.split('\n\n')]
+            output[0] = output[0][1:]
+            for line in output:
+                current_swp = None
+                if not line:
+                    continue
+                for entry in line:
+                    if not entry:
+                        continue
+                    prefix, vlan = entry.split('\t')
+                    if prefix:
+                        current_swp = prefix
+                        vlan_json[prefix] = []
+                    v = {}
+                    vlan = vlan[1:]
+                    try:
+                        v['vlan'] = int(vlan)
+                    except:
+                        try:
+                            if '-' in vlan:
+                                start, end = vlan.split('-')
+                                if ' ' in end:
+                                    end = end[0:end.index(' ')]
+                                v['vlan'] = int(start)
+                                v['vlanEnd'] = int(end)
+                            else:
+                                v['vlan'] = int(vlan[0:vlan.index(' ')])
+                            flags = []
+                            if 'PVID' in vlan:
+                                flags.append('PVID')
+                            if 'Egress Untagged' in vlan:
+                                flags.append('Egress Untagged')
+                            v['flags'] = flags
+                        except:
+                            continue
+                    vlan_json[current_swp].append(v)
+        return vlan_json
+
+    def bridge_vlan_cache_get(self, ifacename, refresh=False):
+        if not self.bridge_vlan_cache_fill_done or refresh:
+            self.bridge_vlan_cache = self.bridge_port_vids_get_all_json()
+            self.bridge_vlan_cache_fill_done = True
+        return self.bridge_vlan_cache.get(ifacename, {})
+
+    def bridge_vlan_get_pvid(self, ifacename, refresh=False):
+        pvid = 0
+
+        for vinfo in self.bridge_vlan_cache_get(ifacename, refresh):
+            v = vinfo.get('vlan')
+            pvid = v if 'PVID' in vinfo.get('flags', []) else 0
+            if pvid:
+                return pvid
+        return pvid
+
+    def bridge_vlan_get_vids(self, ifacename, refresh=False):
+        vids = []
+
+        for vinfo in self.bridge_vlan_cache_get(ifacename, refresh):
+            v = vinfo.get('vlan')
+            ispvid = True if 'PVID' in vinfo.get('flags', []) else False
+            if ispvid:
+                pvid = v if 'PVID' in vinfo.get('flags', []) else 0
+                if pvid == 1:
+                    continue
+            vEnd = vinfo.get('vlanEnd')
+            if vEnd:
+                vids.extend(range(v, vEnd + 1))
+            else:
+                vids.append(v)
+        return vids
+
+    def bridge_vlan_get_vids_n_pvid(self, ifacename, refresh=False):
+        vids = []
+        pvid = 0
+
+        for vinfo in self.bridge_vlan_cache_get(ifacename, refresh):
+            v = vinfo.get('vlan')
+            ispvid = True if 'PVID' in vinfo.get('flags', []) else False
+            if ispvid:
+                pvid = v if 'PVID' in vinfo.get('flags', []) else 0
+            vEnd = vinfo.get('vlanEnd')
+            if vEnd:
+                vids.extend(range(v, vEnd + 1))
+            else:
+                vids.append(v)
+        return vids, pvid
+
+    def bridge_port_pvid_add(self, bridgeportname, pvid):
+        if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
+            self.add_to_batch('vlan add vid %s untagged pvid dev %s' %
+                              (pvid, bridgeportname))
+        else:
+            utils.exec_command('%s vlan add vid %s untagged pvid dev %s' %
+                               (utils.bridge_cmd,
+                                pvid, bridgeportname))
+
+    def bridge_port_pvid_del(self, bridgeportname, pvid):
+        if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
+            self.add_to_batch('vlan del vid %s untagged pvid dev %s' %
+                              (pvid, bridgeportname))
+        else:
+            utils.exec_command('%s vlan del vid %s untagged pvid dev %s' %
+                               (utils.bridge_cmd,
+                                pvid, bridgeportname))
+
+    def bridge_port_pvids_get(self, bridgeportname):
+        return self.read_file_oneline('/sys/class/net/%s/brport/pvid'
+                                      % bridgeportname)
+
+    def bridge_vids_add(self, bridgeportname, vids, bridge=True):
+        target = 'self' if bridge else ''
+        if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
+            [self.add_to_batch('vlan add vid %s dev %s %s' %
+                               (v, bridgeportname, target)) for v in vids]
+        else:
+            [utils.exec_command('%s vlan add vid %s dev %s %s' %
+                                (utils.bridge_cmd,
+                                 v, bridgeportname, target)) for v in vids]
+
+    def bridge_vids_del(self, bridgeportname, vids, bridge=True):
+        target = 'self' if bridge else ''
+        if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
+            [self.add_to_batch('vlan del vid %s dev %s %s' %
+                               (v, bridgeportname, target)) for v in vids]
+        else:
+            [utils.exec_command('%s vlan del vid %s dev %s %s' %
+                                (utils.bridge_cmd,
+                                 v, bridgeportname, target)) for v in vids]
+
+    @staticmethod
+    def bridge_fdb_add(dev, address, vlan=None, bridge=True, remote=None):
+        target = 'self' if bridge else ''
+        vlan_str = ''
+        if vlan:
+            vlan_str = 'vlan %s ' % vlan
+
+        dst_str = ''
+        if remote:
+            dst_str = 'dst %s ' % remote
+
+        utils.exec_command('%s fdb replace %s dev %s %s %s %s' %
+                           (utils.bridge_cmd,
+                            address, dev, vlan_str, target, dst_str))
+
+    @staticmethod
+    def bridge_fdb_append(dev, address, vlan=None, bridge=True, remote=None):
+        target = 'self' if bridge else ''
+        vlan_str = ''
+        if vlan:
+            vlan_str = 'vlan %s ' % vlan
+
+        dst_str = ''
+        if remote:
+            dst_str = 'dst %s ' % remote
+
+        utils.exec_command('%s fdb append %s dev %s %s %s %s' %
+                           (utils.bridge_cmd,
+                            address, dev, vlan_str, target, dst_str))
+
+    @staticmethod
+    def bridge_fdb_del(dev, address, vlan=None, bridge=True, remote=None):
+        target = 'self' if bridge else ''
+        vlan_str = ''
+        if vlan:
+            vlan_str = 'vlan %s ' % vlan
+
+        dst_str = ''
+        if remote:
+            dst_str = 'dst %s ' % remote
+        utils.exec_command('%s fdb del %s dev %s %s %s %s' %
+                           (utils.bridge_cmd,
+                            address, dev, vlan_str, target, dst_str))
+
+    def bridge_is_vlan_aware(self, bridgename):
+        filename = '/sys/class/net/%s/bridge/vlan_filtering' % bridgename
+        if os.path.exists(filename) and self.read_file_oneline(filename) == '1':
+            return True
+        return False
+
+    @staticmethod
+    def bridge_port_get_bridge_name(bridgeport):
+        filename = '/sys/class/net/%s/brport/bridge' % bridgeport
+        try:
+            return os.path.basename(os.readlink(filename))
+        except:
+            return None
+
+    @staticmethod
+    def bridge_port_exists(bridge, bridgeportname):
+        try:
+            return os.path.exists('/sys/class/net/%s/brif/%s'
+                                  % (bridge, bridgeportname))
+        except Exception:
+            return False
+
+    def bridge_fdb_show_dev(self, dev):
+        try:
+            fdbs = {}
+            output = utils.exec_command('%s fdb show dev %s'
+                                        % (utils.bridge_cmd, dev))
+            if output:
+                for fdb_entry in output.splitlines():
+                    try:
+                        entries = fdb_entry.split()
+                        fdbs.setdefault(entries[2], []).append(entries[0])
+                    except:
+                        self.logger.debug('%s: invalid fdb line \'%s\''
+                                          % (dev, fdb_entry))
+            return fdbs
+        except Exception:
+            return None
+
+    @staticmethod
+    def is_bridge(bridge):
+        return os.path.exists('/sys/class/net/%s/bridge' % bridge)
+
+    def is_link_up(self, ifacename):
+        ret = False
+        try:
+            flags = self.read_file_oneline('/sys/class/net/%s/flags' % ifacename)
+            iflags = int(flags, 16)
+            if iflags & 0x0001:
+                ret = True
+        except:
+            ret = False
+        return ret
+
+    def ip_route_get_dev(self, prefix):
+        try:
+            output = utils.exec_command('%s route get %s' %
+                                        (utils.ip_cmd, prefix))
+            if output:
+                rline = output.splitlines()[0]
+                if rline:
+                    rattrs = rline.split()
+                    return rattrs[rattrs.index('dev') + 1]
+        except Exception, e:
+            self.logger.debug('ip_route_get_dev: failed .. %s' % str(e))
+        return None
+
+    @staticmethod
+    def link_get_lowers(ifacename):
+        try:
+            lowers = glob.glob("/sys/class/net/%s/lower_*" % ifacename)
+            if not lowers:
+                return []
+            return [os.path.basename(l)[6:] for l in lowers]
+        except:
+            return []
+
+    @staticmethod
+    def link_get_uppers(ifacename):
+        try:
+            uppers = glob.glob("/sys/class/net/%s/upper_*" % ifacename)
+            if not uppers:
+                return None
+            return [os.path.basename(u)[6:] for u in uppers]
+        except Exception:
+            return None
+
+    def link_get_vrfs(self):
+        if not LinkUtils._CACHE_FILL_DONE:
+            self._fill_cache()
+        return linkCache.vrfs
+
+    @staticmethod
+    def cache_get_info_slave(attrlist):
+        try:
+            return linkCache.get_attr(attrlist)
+        except:
+            return None
+
+    def get_brport_learning(self, ifacename):
+        learn = self.read_file_oneline('/sys/class/net/%s/brport/learning'
+                                       % ifacename)
+        if learn and learn == '1':
+            return 'on'
+        else:
+            return 'off'
+
+    def get_brport_learning_bool(self, ifacename):
+        return utils.get_boolean_from_string(self.read_file_oneline('/sys/class/net/%s/brport/learning' % ifacename))
+
+    def set_brport_learning(self, ifacename, learn):
+        if learn == 'off':
+            return self.write_file('/sys/class/net/%s/brport/learning'
+                                   % ifacename, '0')
+        else:
+            return self.write_file('/sys/class/net/%s/brport/learning'
+                                   % ifacename, '1')
+
+    #################################################################################
+    ################################### BOND UTILS ##################################
+    #################################################################################
+
+    def _link_cache_get(self, attrlist, refresh=False):
+        return self._cache_get('link', attrlist, refresh)
+
+    def cache_delete(self, attrlist, value=None):
+        return self._cache_delete(attrlist, value)
+
+    def link_cache_get(self, attrlist, refresh=False):
+        return self._link_cache_get(attrlist, refresh)
+
+    def link_cache_check(self, attrlist, value, refresh=False):
+        return self._link_cache_check(attrlist, value, refresh)
+
+    def _link_cache_check(self, attrlist, value, refresh=False):
+        try:
+            return self._link_cache_get(attrlist, refresh) == value
+        except Exception, e:
+            self.logger.debug('_cache_check(%s) : [%s]'
+                              % (str(attrlist), str(e)))
+            pass
+        return False
+
+    bondcmd_attrmap = {
+        Link.IFLA_BOND_MODE: 'mode',
+        Link.IFLA_BOND_MIIMON: 'miimon',
+        Link.IFLA_BOND_USE_CARRIER: 'use_carrier',
+        Link.IFLA_BOND_AD_LACP_RATE: 'lacp_rate',
+        Link.IFLA_BOND_XMIT_HASH_POLICY: 'xmit_hash_policy',
+        Link.IFLA_BOND_MIN_LINKS: 'min_links',
+        Link.IFLA_BOND_NUM_PEER_NOTIF: 'num_grat_arp',
+        Link.IFLA_BOND_AD_ACTOR_SYSTEM: 'ad_actor_system',
+        Link.IFLA_BOND_AD_ACTOR_SYS_PRIO: 'ad_actor_sys_prio',
+        Link.IFLA_BOND_AD_LACP_BYPASS: 'lacp_bypass',
+        Link.IFLA_BOND_UPDELAY: 'updelay',
+        Link.IFLA_BOND_DOWNDELAY: 'downdelay',
+    }
+
+    def bond_set_attrs_nl(self, bondname, ifla_info_data):
+        bond_attr_name = 'None'  # for log purpose (in case an exception raised)
+        for nl_attr, value in ifla_info_data.items():
+            try:
+                bond_attr_name = self.bondcmd_attrmap[nl_attr]
+                file_path = '/sys/class/net/%s/bonding/%s' % (bondname, bond_attr_name)
+                if os.path.exists(file_path):
+                    self.write_file(file_path, str(value))
+            except Exception as e:
+                exception_str = '%s: %s %s: %s' % (bondname, bond_attr_name, value, str(e))
+                if ifupdownflags.flags.FORCE:
+                    self.logger.warning(exception_str)
+                else:
+                    self.logger.debug(exception_str)
+
+    def bond_set_attrs(self, bondname, attrdict, prehook):
+        for attrname, attrval in attrdict.items():
+            if (self._link_cache_check([bondname, 'linkinfo',
+                                        attrname], attrval)):
+                continue
+            if (attrname == 'mode'
+                or attrname == 'xmit_hash_policy'
+                or attrname == 'lacp_rate' or attrname == 'min_links'):
+                if prehook:
+                    prehook(bondname)
+            try:
+                if ((attrname not in ['lacp_rate',
+                                      'lacp_bypass']) or
+                        self._link_cache_check([bondname, 'linkinfo', 'mode'], '802.3ad',
+                                               True)):
+                    self.write_file('/sys/class/net/%s/bonding/%s'
+                                    % (bondname, attrname), attrval)
+            except Exception, e:
+                if ifupdownflags.flags.FORCE:
+                    self.logger.warn(str(e))
+                    pass
+                else:
+                    raise
+
+    def bond_set_use_carrier(self, bondname, use_carrier):
+        if not use_carrier or (use_carrier != '0' and use_carrier != '1'):
+            return
+        if (self._link_cache_check([bondname, 'linkinfo', 'use_carrier'],
+                                   use_carrier)):
+            return
+        self.write_file('/sys/class/net/%s' % bondname +
+                        '/bonding/use_carrier', use_carrier)
+        self._cache_update([bondname, 'linkinfo',
+                            'use_carrier'], use_carrier)
+
+    def bond_get_use_carrier(self, bondname):
+        return self._link_cache_get([bondname, 'linkinfo', 'use_carrier'])
+
+    def bond_get_use_carrier_nl(self, bondname):
+        return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_USE_CARRIER])
+
+    def bond_set_xmit_hash_policy(self, bondname, hash_policy, prehook=None):
+        valid_values = ['layer2', 'layer3+4', 'layer2+3']
+        if not hash_policy:
+            return
+        if hash_policy not in valid_values:
+            raise Exception('invalid hash policy value %s' % hash_policy)
+        if (self._link_cache_check([bondname, 'linkinfo', 'xmit_hash_policy'],
+                                   hash_policy)):
+            return
+        if prehook:
+            prehook(bondname)
+        self.write_file('/sys/class/net/%s' % bondname +
+                        '/bonding/xmit_hash_policy', hash_policy)
+        self._cache_update([bondname, 'linkinfo', 'xmit_hash_policy'],
+                           hash_policy)
+
+    def bond_get_xmit_hash_policy(self, bondname):
+        return self._link_cache_get([bondname, 'linkinfo', 'xmit_hash_policy'])
+
+    def bond_get_xmit_hash_policy_nl(self, bondname):
+        return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_XMIT_HASH_POLICY])
+
+    def bond_set_miimon(self, bondname, miimon):
+        if (self._link_cache_check([bondname, 'linkinfo', 'miimon'],
+                                   miimon)):
+            return
+        self.write_file('/sys/class/net/%s' % bondname +
+                        '/bonding/miimon', miimon)
+        self._cache_update([bondname, 'linkinfo', 'miimon'], miimon)
+
+    def bond_get_miimon(self, bondname):
+        return self._link_cache_get([bondname, 'linkinfo', 'miimon'])
+
+    def bond_get_miimon_nl(self, bondname):
+        return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MIIMON])
+
+    def bond_set_mode(self, bondname, mode, prehook=None):
+        valid_modes = ['balance-rr', 'active-backup', 'balance-xor',
+                       'broadcast', '802.3ad', 'balance-tlb', 'balance-alb']
+        if not mode:
+            return
+        if mode not in valid_modes:
+            raise Exception('invalid mode %s' % mode)
+        if (self._link_cache_check([bondname, 'linkinfo', 'mode'],
+                                   mode)):
+            return
+        if prehook:
+            prehook(bondname)
+        self.write_file('/sys/class/net/%s' % bondname + '/bonding/mode', mode)
+        self._cache_update([bondname, 'linkinfo', 'mode'], mode)
+
+    def bond_get_mode(self, bondname):
+        return self._link_cache_get([bondname, 'linkinfo', 'mode'])
+
+    def bond_get_mode_nl(self, bondname):
+        return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MODE])
+
+    def bond_set_lacp_rate(self, bondname, lacp_rate, prehook=None, posthook=None):
+        if not lacp_rate or (lacp_rate != '0' and lacp_rate != '1'):
+            return
+        if (self._link_cache_check([bondname, 'linkinfo', 'lacp_rate'],
+                                   lacp_rate)):
+            return
+        if prehook:
+            prehook(bondname)
+        try:
+            self.write_file('/sys/class/net/%s' % bondname +
+                            '/bonding/lacp_rate', lacp_rate)
+        except:
+            raise
+        finally:
+            if posthook:
+                prehook(bondname)
+            self._cache_update([bondname, 'linkinfo',
+                                'lacp_rate'], lacp_rate)
+
+    def bond_get_lacp_rate(self, bondname):
+        return self._link_cache_get([bondname, 'linkinfo', 'lacp_rate'])
+
+    def bond_get_lacp_rate_nl(self, bondname):
+        return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_RATE])
+
+    def bond_set_lacp_bypass_allow(self, bondname, allow, prehook=None, posthook=None):
+        if self._link_cache_check([bondname, 'linkinfo', 'lacp_bypass'], allow):
+            return
+        if prehook:
+            prehook(bondname)
+        try:
+            self.write_file('/sys/class/net/%s' % bondname +
+                            '/bonding/lacp_bypass', allow)
+        except:
+            raise
+        finally:
+            if posthook:
+                posthook(bondname)
+            self._cache_update([bondname, 'linkinfo',
+                                'lacp_bypass'], allow)
+
+    def bond_get_lacp_bypass_allow(self, bondname):
+        return self._link_cache_get([bondname, 'linkinfo', 'lacp_bypass'])
+
+    def bond_get_lacp_bypass_allow_nl(self, bondname):
+        return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_BYPASS])
+
+    def bond_set_min_links(self, bondname, min_links, prehook=None):
+        if (self._link_cache_check([bondname, 'linkinfo', 'min_links'],
+                                   min_links)):
+            return
+        if prehook:
+            prehook(bondname)
+        self.write_file('/sys/class/net/%s/bonding/min_links' % bondname,
+                        min_links)
+        self._cache_update([bondname, 'linkinfo', 'min_links'], min_links)
+
+    def bond_get_min_links(self, bondname):
+        return self._link_cache_get([bondname, 'linkinfo', 'min_links'])
+
+    def get_min_links_nl(self, bondname):
+        return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MIN_LINKS])
+
+    def bond_get_ad_actor_system(self, bondname):
+        return self._link_cache_get([bondname, 'linkinfo', 'ad_actor_system'])
+
+    def bond_get_ad_actor_system_nl(self, bondname):
+        return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYSTEM])
+
+    def bond_get_ad_actor_sys_prio(self, bondname):
+        return self._link_cache_get([bondname, 'linkinfo', 'ad_actor_sys_prio'])
+
+    def bond_get_ad_actor_sys_prio_nl(self, bondname):
+        return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYS_PRIO])
+
+    def bond_get_num_unsol_na(self, bondname):
+        return self._link_cache_get([bondname, 'linkinfo', 'num_unsol_na'])
+
+    def bond_get_num_unsol_na_nl(self, bondname):
+        return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF])
+
+    def bond_get_num_grat_arp(self, bondname):
+        return self._link_cache_get([bondname, 'linkinfo', 'num_grat_arp'])
+
+    def bond_get_num_grat_arp_nl(self, bondname):
+        return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF])
+
+    def bond_get_updelay(self, bondname):
+        return self._link_cache_get([bondname, 'linkinfo', 'updelay'])
+
+    def bond_get_updelay_nl(self, bondname):
+        return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_UPDELAY])
+
+    def bond_get_downdelay(self, bondname):
+        return self._link_cache_get([bondname, 'linkinfo', 'downdelay'])
+
+    def bond_get_downdelay_nl(self, bondname):
+        return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_DOWNDELAY])
+
+    def bond_enslave_slave(self, bondname, slave, prehook=None, posthook=None):
+        slaves = self._link_cache_get([bondname, 'linkinfo', 'slaves'])
+        if slaves and slave in slaves:
+            return
+        if prehook:
+            prehook(slave)
+        self.write_file('/sys/class/net/%s' % bondname +
+                        '/bonding/slaves', '+' + slave)
+        if posthook:
+            posthook(slave)
+        self._cache_update([bondname, 'linkinfo', 'slaves'], slave)
+
+    def bond_remove_slave(self, bondname, slave):
+        slaves = self._link_cache_get([bondname, 'linkinfo', 'slaves'])
+        if not slaves or slave not in slaves:
+            return
+        sysfs_bond_path = ('/sys/class/net/%s' % bondname +
+                           '/bonding/slaves')
+        if not os.path.exists(sysfs_bond_path):
+            return
+        self.write_file(sysfs_bond_path, '-' + slave)
+        self._cache_delete([bondname, 'linkinfo', 'slaves'], slave)
+
+    def bond_remove_slaves_all(self, bondname):
+        if not self._link_cache_get([bondname, 'linkinfo', 'slaves']):
+            return
+        slaves = None
+        sysfs_bond_path = ('/sys/class/net/%s' % bondname +
+                           '/bonding/slaves')
+        try:
+            with open(sysfs_bond_path, 'r') as f:
+                slaves = f.readline().strip().split()
+        except IOError, e:
+            raise Exception('error reading slaves of bond %s (%s)' % (bondname, str(e)))
+        for slave in slaves:
+            self.link_down(slave)
+            try:
+                self.bond_remove_slave(bondname, slave)
+            except Exception, e:
+                if not ifupdownflags.flags.FORCE:
+                    raise Exception('error removing slave %s from bond %s (%s)' % (slave, bondname, str(e)))
+                else:
+                    pass
+        self._cache_delete([bondname, 'linkinfo', 'slaves'])
+
+    @staticmethod
+    def bond_load_bonding_module():
+        return utils.exec_command('%s -q bonding' % utils.modprobe_cmd)
+
+    def create_bond(self, bondname):
+        if self.bond_exists(bondname):
+            return
+        # load_bonding_module() has already been run
+        self.write_file('/sys/class/net/bonding_masters', '+' + bondname)
+        self._cache_update([bondname], {})
+
+    def delete_bond(self, bondname):
+        if not os.path.exists('/sys/class/net/%s' % bondname):
+            return
+        self.write_file('/sys/class/net/bonding_masters', '-' + bondname)
+        self._cache_delete([bondname])
+
+    def bond_get_slaves(self, bondname):
+        slaves = self._link_cache_get([bondname, 'linkinfo', 'slaves'])
+        if slaves:
+            return list(slaves)
+        slavefile = '/sys/class/net/%s/bonding/slaves' % bondname
+        if os.path.exists(slavefile):
+            buf = self.read_file_oneline(slavefile)
+            if buf:
+                slaves = buf.split()
+        if not slaves:
+            return []
+        self._cache_update([bondname, 'linkinfo', 'slaves'], slaves)
+        return list(slaves)
+
+    def bond_slave_exists(self, bond, slave):
+        slaves = self.bond_get_slaves(bond)
+        if not slaves:
+            return False
+        return slave in slaves
+
+    @staticmethod
+    def bond_exists(bondname):
+        return os.path.exists('/sys/class/net/%s/bonding' % bondname)
+
+    #################################################################################
+    ################################## BRIDGE UTILS #################################
+    #################################################################################
+
+    def create_bridge(self, bridgename):
+        if not LinkUtils.bridge_utils_is_installed:
+            return
+        if self.bridge_exists(bridgename):
+            return
+        utils.exec_command('%s addbr %s' % (utils.brctl_cmd, bridgename))
+        self._cache_update([bridgename], {})
+
+    def delete_bridge(self, bridgename):
+        if not LinkUtils.bridge_utils_is_installed:
+            return
+        if not self.bridge_exists(bridgename):
+            return
+        utils.exec_command('%s delbr %s' % (utils.brctl_cmd, bridgename))
+        self._cache_invalidate()
+
+    def add_bridge_port(self, bridgename, bridgeportname):
+        """ Add port to bridge """
+        if not LinkUtils.bridge_utils_is_installed:
+            return
+        ports = self._link_cache_get([bridgename, 'linkinfo', 'ports'])
+        if ports and ports.get(bridgeportname):
+            return
+        utils.exec_command('%s addif %s %s' % (utils.brctl_cmd, bridgename, bridgeportname))
+        self._cache_update([bridgename, 'linkinfo', 'ports', bridgeportname], {})
+
+    def delete_bridge_port(self, bridgename, bridgeportname):
+        """ Delete port from bridge """
+        if not LinkUtils.bridge_utils_is_installed:
+            return
+        ports = self._link_cache_get([bridgename, 'linkinfo', 'ports'])
+        if not ports or not ports.get(bridgeportname):
+            return
+        utils.exec_command('%s delif %s %s' % (utils.brctl_cmd, bridgename, bridgeportname))
+        self._cache_delete([bridgename, 'linkinfo', 'ports', 'bridgeportname'])
+
+    def set_bridgeport_attrs(self, bridgename, bridgeportname, attrdict):
+        portattrs = self._link_cache_get([bridgename, 'linkinfo', 'ports', bridgeportname])
+        if portattrs == None:
+            portattrs = {}
+        for k, v in attrdict.iteritems():
+            if ifupdownflags.flags.CACHE:
+                curval = portattrs.get(k)
+                if curval and curval == v:
+                    continue
+            if k == 'unicast-flood':
+                self.write_file('/sys/class/net/%s/brport/unicast_flood' % bridgeportname, v)
+            elif k == 'multicast-flood':
+                self.write_file('/sys/class/net/%s/brport/multicast_flood' % bridgeportname, v)
+            elif k == 'learning':
+                self.write_file('/sys/class/net/%s/brport/learning' % bridgeportname, v)
+            elif k == 'arp-nd-suppress':
+                self.write_file('/sys/class/net/%s/brport/neigh_suppress' % bridgeportname, v)
+            else:
+                if not LinkUtils.bridge_utils_is_installed:
+                    continue
+                utils.exec_command('%s set%s %s %s %s' % (utils.brctl_cmd, k, bridgename, bridgeportname, v))
+
+    def set_bridgeport_attr(self, bridgename, bridgeportname,
+                            attrname, attrval):
+        if not LinkUtils.bridge_utils_is_installed:
+            return
+        if self._link_cache_check([bridgename, 'linkinfo', 'ports', bridgeportname, attrname], attrval):
+            return
+        utils.exec_command('%s set%s %s %s %s' %
+                           (utils.brctl_cmd,
+                            attrname,
+                            bridgename,
+                            bridgeportname,
+                            attrval))
+
+    def set_bridge_attrs(self, bridgename, attrdict):
+        for k, v in attrdict.iteritems():
+            if not v:
+                continue
+            if self._link_cache_check([bridgename, 'linkinfo', k], v):
+                continue
+            try:
+                if k == 'igmp-version':
+                    self.write_file('/sys/class/net/%s/bridge/'
+                                    'multicast_igmp_version' % bridgename, v)
+                elif k == 'mld-version':
+                    self.write_file('/sys/class/net/%s/bridge/'
+                                    'multicast_mld_version' % bridgename, v)
+                elif k == 'vlan-protocol':
+                    self.write_file('/sys/class/net/%s/bridge/'
+                                    'vlan_protocol' % bridgename,
+                                    VlanProtocols.ETHERTYPES_TO_ID.get(v.upper(),
+                                                                       None))
+                elif k == 'vlan-stats':
+                    self.write_file('/sys/class/net/%s/bridge/'
+                                    'vlan_stats_enabled' % bridgename, v)
+                elif k == 'mcstats':
+                    self.write_file('/sys/class/net/%s/bridge/'
+                                    'multicast_stats_enabled' % bridgename, v)
+                else:
+                    if not LinkUtils.bridge_utils_is_installed:
+                        continue
+                    cmd = ('%s set%s %s %s' %
+                           (utils.brctl_cmd, k, bridgename, v))
+                    utils.exec_command(cmd)
+            except Exception, e:
+                self.logger.warn('%s: %s' % (bridgename, str(e)))
+                pass
+
+    def set_bridge_attr(self, bridgename, attrname, attrval):
+        if self._link_cache_check([bridgename, 'linkinfo', attrname], attrval):
+            return
+        if attrname == 'igmp-version':
+            self.write_file('/sys/class/net/%s/bridge/multicast_igmp_version'
+                            % bridgename, attrval)
+        elif attrname == 'mld-version':
+            self.write_file('/sys/class/net/%s/bridge/multicast_mld_version'
+                            % bridgename, attrval)
+        elif attrname == 'vlan-protocol':
+            self.write_file('/sys/class/net/%s/bridge/vlan_protocol'
+                            % bridgename, VlanProtocols.ETHERTYPES_TO_ID[attrval.upper()])
+        elif attrname == 'vlan-stats':
+            self.write_file('/sys/class/net/%s/bridge/vlan_stats_enabled'
+                            % bridgename, attrval)
+        elif attrname == 'mcstats':
+            self.write_file('/sys/class/net/%s/bridge/multicast_stats_enabled'
+                            % bridgename, attrval)
+        else:
+            if not LinkUtils.bridge_utils_is_installed:
+                return
+            cmd = '%s set%s %s %s' % (utils.brctl_cmd,
+                                      attrname, bridgename, attrval)
+            utils.exec_command(cmd)
+
+    def get_bridge_attrs(self, bridgename):
+        attrs = self._link_cache_get([bridgename, 'linkinfo'])
+        no_ints_attrs = {}
+        for key, value in attrs.items():
+            if type(key) == str:
+                no_ints_attrs[key] = value
+        return no_ints_attrs
+
+    def get_bridgeport_attrs(self, bridgename, bridgeportname):
+        return self._link_cache_get([bridgename, 'linkinfo', 'ports',
+                                     bridgeportname])
+
+    def get_bridgeport_attr(self, bridgename, bridgeportname, attrname):
+        return self._link_cache_get([bridgename, 'linkinfo', 'ports',
+                                     bridgeportname, attrname])
+
+    @staticmethod
+    def bridge_set_stp(bridge, stp_state):
+        if not LinkUtils.bridge_utils_is_installed:
+            return
+        utils.exec_command('%s stp %s %s' % (utils.brctl_cmd, bridge, stp_state))
+
+    def bridge_get_stp(self, bridge):
+        sysfs_stpstate = '/sys/class/net/%s/bridge/stp_state' % bridge
+        if not os.path.exists(sysfs_stpstate):
+            return 'error'
+        stpstate = self.read_file_oneline(sysfs_stpstate)
+        if not stpstate:
+            return 'error'
+        try:
+            if int(stpstate) > 0:
+                return 'yes'
+            elif int(stpstate) == 0:
+                return 'no'
+        except:
+            return 'unknown'
+
+    @staticmethod
+    def _conv_value_to_user(s):
+        try:
+            ret = int(s) / 100
+            return '%d' % ret
+        except:
+            return None
+
+    def read_value_from_sysfs(self, filename, preprocess_func):
+        value = self.read_file_oneline(filename)
+        if not value:
+            return None
+        return preprocess_func(value)
+
+    @staticmethod
+    def bridge_set_ageing(bridge, ageing):
+        if not LinkUtils.bridge_utils_is_installed:
+            return
+        utils.exec_command('%s setageing %s %s' % (utils.brctl_cmd, bridge, ageing))
+
+    def bridge_get_ageing(self, bridge):
+        return self.read_value_from_sysfs('/sys/class/net/%s/bridge/ageing_time'
+                                          % bridge, self._conv_value_to_user)
+
+    @staticmethod
+    def set_bridgeprio(bridge, prio):
+        if not LinkUtils.bridge_utils_is_installed:
+            return
+        utils.exec_command('%s setbridgeprio %s %s' % (utils.brctl_cmd, bridge, prio))
+
+    def get_bridgeprio(self, bridge):
+        return self.read_file_oneline(
+            '/sys/class/net/%s/bridge/priority' % bridge)
+
+    @staticmethod
+    def bridge_set_fd(bridge, fd):
+        if not LinkUtils.bridge_utils_is_installed:
+            return
+        utils.exec_command('%s setfd %s %s' % (utils.brctl_cmd, bridge, fd))
+
+    def bridge_get_fd(self, bridge):
+        return self.read_value_from_sysfs(
+            '/sys/class/net/%s/bridge/forward_delay'
+            % bridge, self._conv_value_to_user)
+
+    def bridge_set_gcint(self, bridge, gcint):
+        raise Exception('set_gcint not implemented')
+
+    @staticmethod
+    def bridge_set_hello(bridge, hello):
+        if not LinkUtils.bridge_utils_is_installed:
+            return
+        utils.exec_command('%s sethello %s %s' % (utils.brctl_cmd, bridge, hello))
+
+    def bridge_get_hello(self, bridge):
+        return self.read_value_from_sysfs('/sys/class/net/%s/bridge/hello_time'
+                                          % bridge, self._conv_value_to_user)
+
+    @staticmethod
+    def bridge_set_maxage(bridge, maxage):
+        if not LinkUtils.bridge_utils_is_installed:
+            return
+        utils.exec_command('%s setmaxage %s %s' % (utils.brctl_cmd, bridge, maxage))
+
+    def bridge_get_maxage(self, bridge):
+        return self.read_value_from_sysfs('/sys/class/net/%s/bridge/max_age'
+                                          % bridge, self._conv_value_to_user)
+
+    @staticmethod
+    def bridge_set_pathcost(bridge, port, pathcost):
+        if not LinkUtils.bridge_utils_is_installed:
+            return
+        utils.exec_command('%s setpathcost %s %s %s' % (utils.brctl_cmd, bridge, port, pathcost))
+
+    def bridge_get_pathcost(self, bridge, port):
+        return self.read_file_oneline('/sys/class/net/%s/brport/path_cost'
+                                      % port)
+
+    @staticmethod
+    def bridge_set_portprio(bridge, port, prio):
+        if not LinkUtils.bridge_utils_is_installed:
+            return
+        utils.exec_command('%s setportprio %s %s %s' % (utils.brctl_cmd, bridge, port, prio))
+
+    def bridge_get_portprio(self, bridge, port):
+        return self.read_file_oneline('/sys/class/net/%s/brport/priority'
+                                      % port)
+
+    @staticmethod
+    def bridge_set_hashmax(bridge, hashmax):
+        if not LinkUtils.bridge_utils_is_installed:
+            return
+        utils.exec_command('%s sethashmax %s %s' % (utils.brctl_cmd, bridge, hashmax))
+
+    def bridge_get_hashmax(self, bridge):
+        return self.read_file_oneline('/sys/class/net/%s/bridge/hash_max'
+                                      % bridge)
+
+    @staticmethod
+    def bridge_set_hashel(bridge, hashel):
+        if not LinkUtils.bridge_utils_is_installed:
+            return
+        utils.exec_command('%s sethashel %s %s' % (utils.brctl_cmd, bridge, hashel))
+
+    def bridge_get_hashel(self, bridge):
+        return self.read_file_oneline('/sys/class/net/%s/bridge/hash_elasticity'
+                                      % bridge)
+
+    @staticmethod
+    def bridge_set_mclmc(bridge, mclmc):
+        if not LinkUtils.bridge_utils_is_installed:
+            return
+        utils.exec_command('%s setmclmc %s %s' % (utils.brctl_cmd, bridge, mclmc))
+
+    def bridge_get_mclmc(self, bridge):
+        return self.read_file_oneline(
+            '/sys/class/net/%s/bridge/multicast_last_member_count'
+            % bridge)
+
+    @staticmethod
+    def bridge_set_mcrouter(bridge, mcrouter):
+        if not LinkUtils.bridge_utils_is_installed:
+            return
+        utils.exec_command('%s setmcrouter %s %s' % (utils.brctl_cmd, bridge, mcrouter))
+
+    def bridge_get_mcrouter(self, bridge):
+        return self.read_file_oneline(
+            '/sys/class/net/%s/bridge/multicast_router' % bridge)
+
+    @staticmethod
+    def bridge_set_mcsnoop(bridge, mcsnoop):
+        if not LinkUtils.bridge_utils_is_installed:
+            return
+        utils.exec_command('%s setmcsnoop %s %s' % (utils.brctl_cmd, bridge, mcsnoop))
+
+    def bridge_get_mcsnoop(self, bridge):
+        return self.read_file_oneline(
+            '/sys/class/net/%s/bridge/multicast_snooping' % bridge)
+
+    @staticmethod
+    def bridge_set_mcsqc(bridge, mcsqc):
+        if not LinkUtils.bridge_utils_is_installed:
+            return
+        utils.exec_command('%s setmcsqc %s %s' % (utils.brctl_cmd, bridge, mcsqc))
+
+    def bridge_get_mcsqc(self, bridge):
+        return self.read_file_oneline(
+            '/sys/class/net/%s/bridge/multicast_startup_query_count'
+            % bridge)
+
+    @staticmethod
+    def bridge_set_mcqifaddr(bridge, mcqifaddr):
+        if not LinkUtils.bridge_utils_is_installed:
+            return
+        utils.exec_command('%s setmcqifaddr %s %s' % (utils.brctl_cmd, bridge, mcqifaddr))
+
+    def bridge_get_mcqifaddr(self, bridge):
+        return self.read_file_oneline(
+            '/sys/class/net/%s/bridge/multicast_startup_query_use_ifaddr'
+            % bridge)
+
+    @staticmethod
+    def bridge_set_mcquerier(bridge, mcquerier):
+        if not LinkUtils.bridge_utils_is_installed:
+            return
+        utils.exec_command('%s setmcquerier %s %s' % (utils.brctl_cmd, bridge, mcquerier))
+
+    def bridge_get_mcquerier(self, bridge):
+        return self.read_file_oneline(
+            '/sys/class/net/%s/bridge/multicast_querier' % bridge)
+
+    def bridge_set_mcqv4src(self, bridge, vlan, mcquerier):
+        try:
+            vlan = int(vlan)
+        except:
+            self.logger.info('%s: set mcqv4src vlan: invalid parameter %s: %s' %(bridge, vlan, str(e)))
+            return
+        if vlan == 0 or vlan > 4095:
+            self.logger.warn('mcqv4src vlan \'%d\' invalid range' % vlan)
+            return
+
+        ip = mcquerier.split('.')
+        if len(ip) != 4:
+            self.logger.warn('mcqv4src \'%s\' invalid IPv4 address' % mcquerier)
+            return
+        for k in ip:
+            if not k.isdigit() or int(k, 10) < 0 or int(k, 10) > 255:
+                self.logger.warn('mcqv4src \'%s\' invalid IPv4 address' % mcquerier)
+                return
+
+        if not LinkUtils.bridge_utils_is_installed:
+            return
+
+        utils.exec_command('%s setmcqv4src %s %d %s' %
+                           (utils.brctl_cmd, bridge, vlan, mcquerier))
+
+    def bridge_del_mcqv4src(self, bridge, vlan):
+        if not LinkUtils.bridge_utils_is_installed:
+            return
+        try:
+            vlan = int(vlan)
+        except:
+            self.logger.info('%s: del mcqv4src vlan: invalid parameter %s: %s' %(bridge, vlan, str(e)))
+            return
+        utils.exec_command('%s delmcqv4src %s %d' % (utils.brctl_cmd, bridge, vlan))
+
+    def bridge_get_mcqv4src(self, bridge, vlan=None):
+        if not LinkUtils.bridge_utils_is_installed:
+            return {}
+        if not self.supported_command['showmcqv4src']:
+            return {}
+        mcqv4src = {}
+        try:
+            mcqout = utils.exec_command('%s showmcqv4src %s' %
+                                        (utils.brctl_cmd, bridge))
+        except Exception as e:
+            s = str(e).lower()
+            if 'never heard' in s:
+                msg = ('%s showmcqv4src: skipping unsupported command'
+                       % utils.brctl_cmd)
+                self.logger.info(msg)
+                self.supported_command['showmcqv4src'] = False
+                return {}
+            raise
+        if not mcqout:
+            return {}
+        mcqlines = mcqout.splitlines()
+        for l in mcqlines[1:]:
+            l = l.strip()
+            k, d, v = l.split('\t')
+            if not k or not v:
+                continue
+            mcqv4src[k] = v
+        if vlan:
+            return mcqv4src.get(vlan)
+        return mcqv4src
+
+    @staticmethod
+    def bridge_set_mclmi(bridge, mclmi):
+        if not LinkUtils.bridge_utils_is_installed:
+            return
+        utils.exec_command('%s setmclmi %s %s' % (utils.brctl_cmd, bridge, mclmi))
+
+    def bridge_get_mclmi(self, bridge):
+        return self.read_file_oneline(
+            '/sys/class/net/%s/bridge/multicast_last_member_interval'
+            % bridge)
+
+    @staticmethod
+    def bridge_set_mcmi(bridge, mcmi):
+        if not LinkUtils.bridge_utils_is_installed:
+            return
+        utils.exec_command('%s setmcmi %s %s' % (utils.brctl_cmd, bridge, mcmi))
+
+    def bridge_get_mcmi(self, bridge):
+        return self.read_file_oneline(
+            '/sys/class/net/%s/bridge/multicast_membership_interval'
+            % bridge)
+
+    @staticmethod
+    def bridge_exists(bridge):
+        return os.path.exists('/sys/class/net/%s/bridge' % bridge)
+
+    @staticmethod
+    def is_bridge_port(ifacename):
+        return os.path.exists('/sys/class/net/%s/brport' % ifacename)
+
+    @staticmethod
+    def bridge_port_exists(bridge, bridgeportname):
+        try:
+            return os.path.exists('/sys/class/net/%s/brif/%s' % (bridge, bridgeportname))
+        except:
+            return False
+
+    @staticmethod
+    def get_bridge_ports(bridgename):
+        try:
+            return os.listdir('/sys/class/net/%s/brif/' % bridgename)
+        except:
+            return []
diff --git a/ifupdown2/ifupdownaddons/__init__.py b/ifupdown2/ifupdownaddons/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/ifupdown2/ifupdownaddons/cache.py b/ifupdown2/ifupdownaddons/cache.py
new file mode 100644 (file)
index 0000000..b74f327
--- /dev/null
@@ -0,0 +1,114 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+import pprint
+
+
+class MSTPAttrsCache():
+    bridges = {}
+
+    @classmethod
+    def get(cls, bridgename, default=None):
+        if bridgename in MSTPAttrsCache.bridges:
+            return MSTPAttrsCache.bridges[bridgename]
+        else:
+            return default
+
+    @classmethod
+    def set(cls, bridgename, attrs):
+        MSTPAttrsCache.bridges[bridgename] = attrs
+
+    @classmethod
+    def invalidate(cls):
+        MSTPAttrsCache.bridges = {}
+
+
+class linkCache():
+    """ This class contains methods and instance variables to cache
+    link info """
+
+    """ { <ifacename> : { 'ifindex': <index>,
+                          'mtu': <mtu>,
+                          'state' : <state>',
+                          'flags' : <flags>,
+                          'kind' : <kind: bridge, bond, vlan>,
+                          'linkinfo' : {<attr1> : <attrval1>,
+                                        <attr2> : <attrval2>,
+                                        <ports> : {
+                                                  } """
+    links = {}
+    vrfs = {}
+
+    @classmethod
+    def get_attr(cls, mapList):
+        return reduce(lambda d, k: d[k], mapList, linkCache.links)
+
+    @classmethod
+    def set_attr(cls, mapList, value):
+        cls.get_attr(mapList[:-1])[mapList[-1]] = value
+
+    @classmethod
+    def del_attr(cls, mapList):
+        try:
+            del cls.get_attr(mapList[:-1])[mapList[-1]]
+        except:
+            pass
+
+    @classmethod
+    def update_attrdict(cls, mapList, valuedict):
+        try:
+            cls.get_attr(mapList[:-1])[mapList[-1]].update(valuedict)
+        except:
+            cls.get_attr(mapList[:-1])[mapList[-1]] = valuedict
+            pass
+
+    @classmethod
+    def append_to_attrlist(cls, mapList, value):
+        cls.get_attr(mapList[:-1])[mapList[-1]].append(value)
+
+    @classmethod
+    def remove_from_attrlist(cls, mapList, value):
+        try:
+            cls.get_attr(mapList[:-1])[mapList[-1]].remove(value)
+        except:
+            pass
+
+    @classmethod
+    def check_attr(cls, attrlist, value=None):
+        try:
+            cachedvalue = cls.get_attr(attrlist)
+            if value:
+                if cachedvalue == value:
+                    return True
+                else:
+                    return False
+            elif cachedvalue:
+                return True
+            else:
+                return False
+        except:
+            return False
+
+    @classmethod
+    def invalidate(cls):
+        cls.links = {}
+
+    @classmethod
+    def reset(cls):
+        cls.invalidate()
+        cls.vrfs = {}
+
+    @classmethod
+    def dump(cls):
+        print 'Dumping link cache'
+        pp = pprint.PrettyPrinter(indent=4)
+        pp.pprint(cls.links)
+
+    @classmethod
+    def dump_link(cls, linkname):
+        print 'Dumping link %s' % linkname
+        pp = pprint.PrettyPrinter(indent=4)
+        pp.pprint(cls.links.get(linkname))
diff --git a/ifupdown2/ifupdownaddons/dhclient.py b/ifupdown2/ifupdownaddons/dhclient.py
new file mode 100644 (file)
index 0000000..d8f8924
--- /dev/null
@@ -0,0 +1,109 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+import os
+
+try:
+    from ifupdown2.ifupdown.utils import utils
+    from ifupdown2.ifupdownaddons.utilsbase import *
+except ImportError:
+    from ifupdown.utils import utils
+    from ifupdownaddons.utilsbase import *
+
+
+class dhclient(utilsBase):
+    """ This class contains helper methods to interact with the dhclient
+    utility """
+
+    def _pid_exists(self, pidfilename):
+        if os.path.exists(pidfilename):
+            pid = self.read_file_oneline(pidfilename)
+            if not os.path.exists('/proc/%s' %pid):
+                return False
+        else:
+            return False
+        return True
+
+    def is_running(self, ifacename):
+        return self._pid_exists('/run/dhclient.%s.pid' %ifacename)
+
+    def is_running6(self, ifacename):
+        return self._pid_exists('/run/dhclient6.%s.pid' %ifacename)
+
+    def _run_dhclient_cmd(self, cmd, cmd_prefix=None):
+        if not cmd_prefix:
+            cmd_aslist = []
+        else:
+            cmd_aslist = cmd_prefix.split()
+        if cmd_aslist:
+            cmd_aslist.extend(cmd)
+        else:
+            cmd_aslist = cmd
+        utils.exec_commandl(cmd_aslist, stdout=None, stderr=None)
+
+    def stop(self, ifacename, cmd_prefix=None):
+        if os.path.exists('/sbin/dhclient3'):
+            cmd = ['/sbin/dhclient3', '-x', '-pf',
+                   '/run/dhclient.%s.pid' %ifacename, '-lf',
+                   '/var/lib/dhcp3/dhclient.%s.leases' %ifacename,
+                   '%s' %ifacename]
+        else:
+            cmd = ['/sbin/dhclient', '-x', '-pf',
+                   '/run/dhclient.%s.pid' %ifacename,
+                   '-lf', '/var/lib/dhcp/dhclient.%s.leases' %ifacename,
+                   '%s' %ifacename]
+        self._run_dhclient_cmd(cmd, cmd_prefix)
+
+    def start(self, ifacename, wait=True, cmd_prefix=None):
+        if os.path.exists('/sbin/dhclient3'):
+            cmd = ['/sbin/dhclient3', '-pf',
+                   '/run/dhclient.%s.pid' %ifacename,
+                   '-lf', '/var/lib/dhcp3/dhclient.%s.leases' %ifacename,
+                   '%s' %ifacename]
+        else:
+            cmd = ['/sbin/dhclient', '-pf',
+                   '/run/dhclient.%s.pid' %ifacename, '-lf',
+                   '/var/lib/dhcp/dhclient.%s.leases' %ifacename,
+                   '%s' %ifacename]
+        if not wait:
+            cmd.append('-nw')
+        self._run_dhclient_cmd(cmd, cmd_prefix)
+
+    def release(self, ifacename, cmd_prefix=None):
+        if os.path.exists('/sbin/dhclient3'):
+            cmd = ['/sbin/dhclient3', '-r', '-pf',
+                   '/run/dhclient.%s.pid' %ifacename, '-lf',
+                   '/var/lib/dhcp3/dhclient.%s.leases' %ifacename,
+                   '%s' %ifacename]
+        else:
+            cmd = ['/sbin/dhclient', '-r', '-pf',
+                   '/run/dhclient.%s.pid' %ifacename,
+                   '-lf', '/var/lib/dhcp/dhclient.%s.leases' %ifacename,
+                   '%s' %ifacename]
+        self._run_dhclient_cmd(cmd, cmd_prefix)
+
+    def start6(self, ifacename, wait=True, cmd_prefix=None):
+        cmd = ['/sbin/dhclient', '-6', '-pf',
+                '/run/dhclient6.%s.pid' %ifacename, '-lf',
+                '/var/lib/dhcp/dhclient6.%s.leases' % ifacename,
+                '%s' %ifacename]
+        if not wait:
+            cmd.append('-nw')
+        self._run_dhclient_cmd(cmd, cmd_prefix)
+
+    def stop6(self, ifacename, cmd_prefix=None):
+        cmd = ['/sbin/dhclient', '-6', '-x', '-pf',
+               '/run/dhclient6.%s.pid' % ifacename, '-lf',
+               '/var/lib/dhcp/dhclient6.%s.leases' % ifacename,
+               '%s' %ifacename]
+        self._run_dhclient_cmd(cmd, cmd_prefix)
+
+    def release6(self, ifacename, cmd_prefix=None):
+        cmd = ['/sbin/dhclient', '-6', '-r', '-pf',
+               '/run/dhclient6.%s.pid' %ifacename,
+              '-lf', '/var/lib/dhcp/dhclient6.%s.leases' % ifacename,
+               '%s' %ifacename]
+        self._run_dhclient_cmd(cmd, cmd_prefix)
diff --git a/ifupdown2/ifupdownaddons/modulebase.py b/ifupdown2/ifupdownaddons/modulebase.py
new file mode 100644 (file)
index 0000000..d0169f3
--- /dev/null
@@ -0,0 +1,489 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+import os
+import re
+import logging
+import traceback
+
+try:
+    from ifupdown2.ifupdown.iface import *
+    from ifupdown2.ifupdown.utils import utils
+
+    import ifupdown2.ifupdown.exceptions as exceptions
+    import ifupdown2.ifupdown.policymanager as policymanager
+    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
+except ImportError:
+    from ifupdown.iface import *
+    from ifupdown.utils import utils
+
+    import ifupdown.exceptions as exceptions
+    import ifupdown.policymanager as policymanager
+    import ifupdown.ifupdownflags as ifupdownflags
+
+
+class NotSupported(Exception):
+    pass
+
+class moduleBase(object):
+    """ Base class for ifupdown addon modules
+
+    Provides common infrastructure methods for all addon modules """
+
+    def __init__(self, *args, **kargs):
+        self.modulename = self.__class__.__name__
+        self.logger = logging.getLogger('ifupdown.' + self.modulename)
+
+        # vrfs are a global concept and a vrf context can be applicable
+        # to all global vrf commands. Get the default vrf-exec-cmd-prefix
+        # here so that all modules can use it
+        self.vrf_exec_cmd_prefix = policymanager.policymanager_api.get_module_globals('vrf', attr='vrf-exec-cmd-prefix')
+
+        # explanations are shown in parse_glob
+        self.glob_regexs = [re.compile(r"([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\]([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\](.*)"),
+                            re.compile(r"([A-Za-z0-9\-]+[A-Za-z])(\d+)\-(\d+)(.*)"),
+                            re.compile(r"([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\](.*)")]
+
+        self._bridge_stp_user_space = None
+
+        self.merge_modinfo_with_policy_files()
+
+    def merge_modinfo_with_policy_files(self):
+        """
+            update addons modinfo dictionary with system/user defined values in policy files
+            Any value can be updated except the module help "mhelp"
+
+            We also check if the policy attributes really exist to make sure someone is not
+            trying to "inject" new attributes to prevent breakages and security issue
+        """
+        attrs = dict(self.get_modinfo().get('attrs', {}))
+
+        if not attrs:
+            return
+
+        error_msg = 'this attribute doesn\'t exist or isn\'t supported'
+
+        # first check module_defaults
+        for key, value in policymanager.policymanager_api.get_module_defaults(self.modulename).items():
+            if not key in attrs:
+                self.logger.warning('%s: %s: %s' % (self.modulename, key, error_msg))
+                continue
+            attrs[key]['default'] = value
+
+        # then check module_globals (overrides module_defaults)
+        policy_modinfo = policymanager.policymanager_api.get_module_globals(self.modulename, '_modinfo')
+        if policy_modinfo:
+            policy_attrs = policy_modinfo.get('attrs', {})
+            update_attrs = dict()
+
+            for attr_name, attr_description in policy_attrs.items():
+                if attr_name not in attrs:
+                    self.logger.warning('%s: %s: %s' % (self.modulename, attr_name, error_msg))
+                else:
+                    update_attrs[attr_name] = attr_description
+
+            attrs.update(update_attrs)
+
+        return attrs
+
+    def log_warn(self, str, ifaceobj=None):
+        """ log a warning if err str is not one of which we should ignore """
+        if not self.ignore_error(str) and not ifupdownflags.flags.IGNORE_ERRORS:
+            if self.logger.getEffectiveLevel() == logging.DEBUG:
+                traceback.print_stack()
+                traceback.print_exc()
+            self.logger.warn(str)
+            if ifaceobj:
+                ifaceobj.set_status(ifaceStatus.WARNING)
+        pass
+
+    def log_error(self, str, ifaceobj=None, raise_error=True):
+        """ log an err if err str is not one of which we should ignore and raise an exception """
+        if not self.ignore_error(str) and not ifupdownflags.flags.IGNORE_ERRORS:
+            if self.logger.getEffectiveLevel() == logging.DEBUG:
+                traceback.print_stack()
+                traceback.print_exc()
+            if raise_error:
+                if ifaceobj:
+                    ifaceobj.set_status(ifaceStatus.ERROR)
+                raise Exception(str)
+            else:
+                self.logger.error(str)
+        else:
+            pass
+
+    def is_process_running(self, procName):
+        try:
+            utils.exec_command('%s -x %s' %
+                               (utils.pidof_cmd, procName))
+        except:
+            return False
+        else:
+            return True
+
+    def get_ifaces_from_proc(self):
+        ifacenames = []
+        with open('/proc/net/dev') as f:
+            try:
+                lines = f.readlines()
+                for line in lines[2:]:
+                    ifacenames.append(line.split()[0].strip(': '))
+            except:
+                raise
+        return ifacenames
+
+    def parse_regex(self, ifacename, expr, ifacenames=None):
+        try:
+            proc_ifacenames = self.get_ifaces_from_proc()
+        except:
+            self.logger.warn('%s: error reading ifaces from proc' %ifacename)
+
+        for proc_ifacename in proc_ifacenames:
+            try:
+                if re.search(expr + '$', proc_ifacename):
+                    yield proc_ifacename
+            except Exception, e:
+                raise Exception('%s: error searching regex \'%s\' in %s (%s)'
+                                %(ifacename, expr, proc_ifacename, str(e)))
+        if not ifacenames:
+            return
+        for ifacename in ifacenames:
+            try:
+                if re.search(expr + '$', ifacename):
+                    yield ifacename
+            except Exception, e:
+                raise Exception('%s: error searching regex \'%s\' in %s (%s)'
+                                %(ifacename, expr, ifacename, str(e)))
+
+    def ifname_is_glob(self, ifname):
+        """
+        Used by iface where ifname could be swp7 or swp[1-10].300
+        """
+        if (self.glob_regexs[0].match(ifname) or
+            self.glob_regexs[1].match(ifname) or
+            self.glob_regexs[2].match(ifname)):
+            return True
+        return False
+
+    def parse_glob(self, ifacename, expr):
+        errmsg = ('error parsing glob expression \'%s\'' %expr +
+                    ' (supported glob syntax: swp1-10.300 or swp[1-10].300' +
+                    '  or swp[1-10]sub[0-4].300')
+
+        if ',' in expr:
+            self.logger.warn('%s: comma are not supported in glob: %s' % (ifacename, errmsg))
+            yield expr
+            return
+
+        regexs = self.glob_regexs
+
+        if regexs[0].match(expr):
+            # the first regex checks for exactly two levels of ranges defined only with square brackets
+            # (e.g. swpxyz[10-23]subqwe[0-4].100) to handle naming with two levels of port names.
+            m = regexs[0].match(expr)
+            mlist = m.groups()
+            if len(mlist) < 7:
+                # we have problems and should not continue
+                raise Exception('%s: error: unhandled glob expression %s\n%s' % (ifacename, expr,errmsg))
+
+            prefix = mlist[0]
+            suffix = mlist[6]
+            start_index = int(mlist[1])
+            end_index = int(mlist[2])
+            sub_string = mlist[3]
+            start_sub = int(mlist[4])
+            end_sub = int(mlist[5])
+            for i in range(start_index, end_index + 1):
+                for j in range(start_sub, end_sub + 1):
+                    yield prefix + '%d%s%d' % (i,sub_string,j) + suffix
+
+        elif regexs[1].match(expr) or regexs[2].match(expr):
+            # the second regex for 1 level with a range (e.g. swp10-14.100
+            # the third regex checks for 1 level with [] (e.g. swp[10-14].100)
+            start_index = 0
+            end_index = 0
+            if regexs[1].match(expr):
+                m = regexs[1].match(expr)
+            else:
+                m = regexs[2].match(expr)
+            mlist = m.groups()
+            if len(mlist) != 4:
+                raise Exception('%s: ' %ifacename + errmsg + '(unexpected len)')
+            prefix = mlist[0]
+            suffix = mlist[3]
+            start_index = int(mlist[1])
+            end_index = int(mlist[2])
+            for i in range(start_index, end_index + 1):
+                yield prefix + '%d' %i + suffix
+
+        else:
+            # Could not match anything.
+            self.logger.warn('%s: %s' %(ifacename, errmsg))
+            yield expr
+
+    def parse_port_list(self, ifacename, port_expr, ifacenames=None):
+        """ parse port list containing glob and regex
+
+        Args:
+            port_expr (str): expression
+            ifacenames (list): list of interface names. This needs to be specified if the expression has a regular expression
+        """
+        regex = 0
+        glob = 0
+        portlist = []
+
+        if not port_expr:
+            return None
+        exprs = re.split(r'[\s\t]\s*', port_expr)
+        self.logger.debug('%s: evaluating port expr \'%s\''
+                         %(ifacename, str(exprs)))
+        for expr in exprs:
+            if expr == 'noregex':
+                regex = 0
+            elif expr == 'noglob':
+                glob = 0
+            elif expr == 'regex':
+                regex = 1
+            elif expr == 'glob':
+                glob = 1
+            elif regex:
+                for port in self.parse_regex(ifacename, expr, ifacenames):
+                    if port not in portlist:
+                        portlist.append(port)
+                regex = 0
+            elif glob:
+                for port in self.parse_glob(ifacename, expr):
+                    portlist.append(port)
+                glob = 0
+            else:
+                portlist.append(expr)
+        if not portlist:
+            return None
+        return portlist
+
+    def ignore_error(self, errmsg):
+        if (ifupdownflags.flags.FORCE or re.search(r'exists', errmsg,
+            re.IGNORECASE | re.MULTILINE)):
+            return True
+        return False
+
+    def write_file(self, filename, strexpr):
+        """ writes string to a file """
+        try:
+            self.logger.info('writing \'%s\'' %strexpr +
+                ' to file %s' %filename)
+            if ifupdownflags.flags.DRYRUN:
+                return 0
+            with open(filename, 'w') as f:
+                f.write(strexpr)
+        except IOError, e:
+            self.logger.warn('error writing to file %s'
+                %filename + '(' + str(e) + ')')
+            return -1
+        return 0
+
+    def read_file(self, filename):
+        """ read file and return lines from the file """
+        try:
+            self.logger.info('reading \'%s\'' %filename)
+            with open(filename, 'r') as f:
+                return f.readlines()
+        except:
+            return None
+        return None
+
+    def read_file_oneline(self, filename):
+        """ reads and returns first line from the file """
+        try:
+            self.logger.info('reading \'%s\'' %filename)
+            with open(filename, 'r') as f:
+                return f.readline().strip('\n')
+        except:
+            return None
+        return None
+
+    def sysctl_set(self, variable, value):
+        """ set sysctl variable to value passed as argument """
+        utils.exec_command('%s %s=%s' %
+                           (utils.sysctl_cmd, variable, value))
+
+    def sysctl_get(self, variable):
+        """ get value of sysctl variable """
+        output = utils.exec_command('%s %s' %
+                                    (utils.sysctl_cmd, variable))
+        split = output.split('=')
+        if len(split) > 1:
+            return split[1].strip()
+        return None
+
+    def systcl_get_net_bridge_stp_user_space(self):
+        if self._bridge_stp_user_space:
+            return self._bridge_stp_user_space
+        self._bridge_stp_user_space = self.sysctl_get('net.bridge.bridge-stp-user-space')
+        return self._bridge_stp_user_space
+
+    def set_iface_attr(self, ifaceobj, attr_name, attr_valsetfunc,
+                       prehook=None, prehookargs=None):
+        ifacename = ifaceobj.name
+        attrvalue = ifaceobj.get_attr_value_first(attr_name)
+        if attrvalue:
+            if prehook:
+                if prehookargs:
+                    prehook(prehookargs)
+                else:
+                    prehook(ifacename)
+            attr_valsetfunc(ifacename, attrvalue)
+
+    def query_n_update_ifaceobjcurr_attr(self, ifaceobj, ifaceobjcurr,
+                                       attr_name, attr_valgetfunc,
+                                       attr_valgetextraarg=None):
+        attrvalue = ifaceobj.get_attr_value_first(attr_name)
+        if not attrvalue:
+            return
+        if attr_valgetextraarg:
+            runningattrvalue = attr_valgetfunc(ifaceobj.name,
+                                             attr_valgetextraarg)
+        else:
+            runningattrvalue = attr_valgetfunc(ifaceobj.name)
+        if (not runningattrvalue or
+            (runningattrvalue != attrvalue)):
+            ifaceobjcurr.update_config_with_status(attr_name,
+                runningattrvalue, 1)
+        else:
+            ifaceobjcurr.update_config_with_status(attr_name,
+                runningattrvalue, 0)
+
+    def dict_key_subset(self, a, b):
+        """ returns a list of differing keys """
+        return [x for x in a if x in b]
+
+    def get_mod_attrs(self):
+        """ returns list of all module attrs defined in the module _modinfo
+            dict
+        """
+        try:
+            retattrs = []
+            attrsdict = self._modinfo.get('attrs')
+            for attrname, attrvals in attrsdict.iteritems():
+                if not attrvals or attrvals.get('deprecated'):
+                    continue
+                retattrs.append(attrname)
+                if 'aliases' in attrvals:
+                    retattrs.extend(attrvals['aliases'])
+            return retattrs
+        except:
+            return None
+
+    def get_mod_attr(self, attrname):
+        """ returns module attr info """
+        try:
+            return self._modinfo.get('attrs', {}).get(attrname)
+        except:
+            return None
+
+    def get_mod_subattr(self, attrname, subattrname):
+        """ returns module attrs defined in the module _modinfo dict"""
+        try:
+            return reduce(lambda d, k: d[k], ['attrs', attrname, subattrname],
+                         self._modinfo)
+        except:
+            return None
+
+    def get_modinfo(self):
+        """ return module info """
+        try:
+            return self._modinfo
+        except:
+            return {}
+
+    def get_attr_default_value(self, attrname):
+        return self.get_modinfo().get('attrs', {}).get(attrname, {}).get('default')
+
+    def get_overrides_ifupdown_scripts(self):
+        """ return the ifupdown scripts replaced by the current module """
+        try:
+            return self.overrides_ifupdown_scripts
+        except:
+            return []
+
+    def _get_reserved_vlan_range(self):
+        start = end = 0
+        get_resvvlan = '/var/lib/ifupdown2/hooks/get_reserved_vlan_range.sh'
+        if not os.path.exists(get_resvvlan):
+            return (start, end)
+        try:
+            (s, e) = utils.exec_command(get_resvvlan).strip('\n').split('-')
+            start = int(s)
+            end = int(e)
+        except Exception, e:
+            self.logger.debug('%s failed (%s)' %(get_resvvlan, str(e)))
+            # ignore errors
+            pass
+        return (start, end)
+
+    def _handle_reserved_vlan(self, vlanid, logprefix='', end=-1):
+        """ Helper function to check and warn if the vlanid falls in the
+        reserved vlan range """
+        error = False
+        invalid_vlan = vlanid
+
+        if self._resv_vlan_range[0] <= vlanid <= self._resv_vlan_range[1]:
+            error = True
+        elif end > 0:
+            if self._resv_vlan_range[0] <= end <= self._resv_vlan_range[1]:
+                error = True
+                invalid_vlan = end
+            elif vlanid < self._resv_vlan_range[0] and end > self._resv_vlan_range[1]:
+                error = True
+                invalid_vlan = self._resv_vlan_range[0]
+
+        if error:
+            raise exceptions.ReservedVlanException('%s: reserved vlan %d being used (reserved vlan range %d-%d)'
+                                                   % (logprefix, invalid_vlan, self._resv_vlan_range[0], self._resv_vlan_range[1]))
+
+        return error
+
+    def _valid_ethaddr(self, ethaddr):
+        """ Check if address is 00:00:00:00:00:00 """
+        if not ethaddr or re.match('00:00:00:00:00:00', ethaddr):
+            return False
+        return True
+
+    def _get_vlan_id_from_ifacename(self, ifacename):
+        if '.' in ifacename:
+            vid_str = ifacename.split('.', 2)
+            vlen = len(vid_str)
+            if vlen == 2:
+                vid_str = vid_str[1]
+            elif vlen == 3:
+                vid_str = vid_str[2]
+        elif ifacename.startswith('vlan'):
+            vid_str = ifacename[4:]
+        else:
+            return -1
+        try:
+            vid = int(vid_str)
+        except:
+            return -1
+        return vid
+
+    def _get_vlan_id(self, ifaceobj):
+        """ Derives vlanid from iface name
+
+        Example:
+            Returns 1 for ifname vlan0001 returns 1
+            Returns 1 for ifname vlan1
+            Returns 1 for ifname eth0.1
+            Returns 100 for ifname eth0.1.100
+            Returns -1 if vlan id cannot be determined
+        """
+        vid_str = ifaceobj.get_attr_value_first('vlan-id')
+        try:
+            if vid_str: return int(vid_str)
+        except:
+            return -1
+
+        return self._get_vlan_id_from_ifacename(ifaceobj.name)
diff --git a/ifupdown2/ifupdownaddons/mstpctlutil.py b/ifupdown2/ifupdownaddons/mstpctlutil.py
new file mode 100644 (file)
index 0000000..e601e12
--- /dev/null
@@ -0,0 +1,247 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+try:
+    from ifupdown2.ifupdown.iface import *
+    from ifupdown2.ifupdown.utils import utils
+
+    from ifupdown2.ifupdownaddons.cache import *
+    from ifupdown2.ifupdownaddons.utilsbase import *
+except ImportError:
+    from ifupdown.iface import *
+    from ifupdown.utils import utils
+
+    from ifupdownaddons.cache import *
+    from ifupdownaddons.utilsbase import *
+
+
+class mstpctlutil(utilsBase):
+    """ This class contains helper methods to interact with mstpd using
+    mstputils commands """
+
+    _DEFAULT_PORT_PRIO = '128'
+
+    _cache_fill_done = False
+
+    _bridgeattrmap = {'bridgeid' : 'bridge-id',
+                     'maxage' : 'max-age',
+                     'fdelay' : 'forward-delay',
+                     'txholdcount' : 'tx-hold-count',
+                     'maxhops' : 'max-hops',
+                     'ageing' : 'ageing-time',
+                     'hello' : 'hello-time',
+                     'forcevers' : 'force-protocol-version'}
+
+    _bridge_jsonAttr_map = {
+                            'treeprio': 'bridgeId',
+                            'maxage': 'maxAge',
+                            'fdelay': 'fwdDelay',
+                            'txholdcount': 'txHoldCounter',
+                            'maxhops': 'maxHops',
+                            'ageing': 'ageingTime',
+                            'hello': 'helloTime',
+                            'forcevers': 'forceProtocolVersion',
+                            }
+
+    _bridgeportattrmap = {'portadminedge' : 'admin-edge-port',
+                     'portp2p' : 'admin-point-to-point',
+                     'portrestrrole' : 'restricted-role',
+                     'portrestrtcn' : 'restricted-TCN',
+                     'bpduguard' : 'bpdu-guard-port',
+                     'portautoedge' : 'auto-edge-port',
+                     'portnetwork' : 'network-port',
+                     'portbpdufilter' : 'bpdufilter-port',
+                     'portpathcost' : 'external-port-cost',
+                     'treeportcost' : 'internal-port-cost'}
+
+    def __init__(self, *args, **kargs):
+        utilsBase.__init__(self, *args, **kargs)
+
+    @classmethod
+    def reset(cls):
+        cls._cache_fill_done = False
+
+    def is_mstpd_running(self):
+        try:
+            utils.exec_command('%s mstpd'%utils.pidof_cmd)
+        except:
+            return False
+        else:
+            return True
+
+    def _extract_bridge_port_prio(self, portid):
+        try:
+            return str(int(portid[0], 16) * 16)
+        except:
+            return mstpctlutil._DEFAULT_PORT_PRIO
+
+    def _get_bridge_and_port_attrs_from_cache(self, bridgename):
+        attrs = MSTPAttrsCache.get(bridgename)
+        if attrs:
+            return attrs
+        mstpctl_bridgeport_attrs_dict = {}
+        try:
+            cmd = [utils.mstpctl_cmd,
+                   'showportdetail', bridgename, 'json']
+            output = utils.exec_commandl(cmd)
+            if not output:
+                return mstpctl_bridgeport_attrs_dict
+        except Exception as e:
+            self.logger.info(str(e))
+            return mstpctl_bridgeport_attrs_dict
+        try:
+            mstpctl_bridge_cache = json.loads(output.strip('\n'))
+            for portname in mstpctl_bridge_cache.keys():
+                for portid in mstpctl_bridge_cache[portname].keys():
+                    mstpctl_bridgeport_attrs_dict[portname] = {}
+                    mstpctl_bridgeport_attrs_dict[portname]['treeportprio'] = self._extract_bridge_port_prio(portid)
+                    for jsonAttr in mstpctl_bridge_cache[portname][portid].keys():
+                        jsonVal = mstpctl_bridge_cache[portname][portid][jsonAttr]
+                        mstpctl_bridgeport_attrs_dict[portname][jsonAttr] = str(jsonVal)
+            MSTPAttrsCache.set(bridgename, mstpctl_bridgeport_attrs_dict)
+        except Exception as e:
+            self.logger.info('%s: cannot fetch mstpctl bridge port attributes: %s' % str(e))
+
+        mstpctl_bridge_attrs_dict = {}
+        try:
+            cmd = [utils.mstpctl_cmd,
+                   'showbridge', 'json', bridgename]
+            output = utils.exec_commandl(cmd)
+            if not output:
+                return mstpctl_bridge_attrs_dict
+        except Exception as e:
+            self.logger.info(str(e))
+            return mstpctl_bridge_attrs_dict
+        try:
+            mstpctl_bridge_cache = json.loads(output.strip('\n'))
+            for jsonAttr in mstpctl_bridge_cache[bridgename].keys():
+                mstpctl_bridge_attrs_dict[jsonAttr] = (
+                    str(mstpctl_bridge_cache[bridgename][jsonAttr]))
+            mstpctl_bridge_attrs_dict['treeprio'] = '%d' %(
+                                   int(mstpctl_bridge_attrs_dict.get('bridgeId',
+                                   '').split('.')[0], base=16) * 4096)
+            del mstpctl_bridge_attrs_dict['bridgeId']
+            MSTPAttrsCache.bridges[bridgename].update(mstpctl_bridge_attrs_dict)
+        except Exception as e:
+            self.logger.info('%s: cannot fetch mstpctl bridge attributes: %s' % str(e))
+        return MSTPAttrsCache.get(bridgename)
+
+    def get_bridge_ports_attrs(self, bridgename):
+        return self._get_bridge_and_port_attrs_from_cache(bridgename)
+
+    def get_bridge_port_attr(self, bridgename, portname, attrname):
+        attrs = self._get_bridge_and_port_attrs_from_cache(bridgename)
+        value = attrs.get(portname, {}).get(attrname, 'no')
+        if value == 'True' or value == 'true':
+            return 'yes'
+        return str(value)
+
+    def update_bridge_port_cache(self, bridgename, portname, attrname, value):
+        attrs = self.get_bridge_ports_attrs(bridgename)
+        if not attrs:
+            attrs = {}
+        if not portname in attrs:
+            attrs[portname] = {}
+        attrs[portname][attrname] = value
+        MSTPAttrsCache.set(bridgename, attrs)
+
+    def update_bridge_cache(self, bridgename, attrname, value):
+        attrs = self.get_bridge_ports_attrs(bridgename)
+        if not attrs:
+            attrs = {}
+        attrs[attrname] = value
+        MSTPAttrsCache.set(bridgename, attrs)
+
+    def set_bridge_port_attr(self, bridgename, portname, attrname, value, json_attr=None):
+        cache_value = self.get_bridge_port_attr(bridgename, portname, json_attr)
+        if cache_value and cache_value == value:
+            return
+        if attrname == 'treeportcost' or attrname == 'treeportprio':
+            utils.exec_commandl([utils.mstpctl_cmd, 'set%s' % attrname,
+                                 bridgename, portname, '0', value])
+        else:
+            utils.exec_commandl([utils.mstpctl_cmd, 'set%s' % attrname,
+                                 bridgename, portname, value])
+        if json_attr:
+            self.update_bridge_port_cache(bridgename, portname, json_attr, value)
+
+    def get_bridge_attrs(self, bridgename):
+        bridgeattrs = {}
+        try:
+            bridgeattrs = dict((k, self.get_bridge_attr(bridgename, v))
+                                 for k,v in self._bridge_jsonAttr_map.items())
+        except Exception, e:
+            self.logger.debug(bridgeattrs)
+            self.logger.debug(str(e))
+            pass
+        return bridgeattrs
+
+    def get_bridge_attr(self, bridgename, attrname):
+        if attrname == 'bridgeId':
+            attrname = 'treeprio'
+        return self._get_bridge_and_port_attrs_from_cache(bridgename).get(attrname)
+
+    def set_bridge_attr(self, bridgename, attrname, attrvalue, check=True):
+
+        if check:
+            if attrname == 'treeprio':
+                attrvalue_curr = self.get_bridge_attr(bridgename, attrname)
+            else:
+                attrvalue_curr = self.get_bridge_attr(bridgename,
+                                        self._bridge_jsonAttr_map[attrname])
+            if attrvalue_curr and attrvalue_curr == attrvalue:
+                return
+        if attrname == 'treeprio':
+            utils.exec_commandl([utils.mstpctl_cmd, 'set%s' % attrname,
+                                 '%s' % bridgename, '0', '%s' % attrvalue], stderr=None)
+            self.update_bridge_cache(bridgename, attrname, str(attrvalue))
+        else:
+            utils.exec_commandl([utils.mstpctl_cmd, 'set%s' % attrname,
+                                 '%s' % bridgename, '%s' % attrvalue], stderr=None)
+            self.update_bridge_cache(bridgename,
+                                     self._bridge_jsonAttr_map[attrname],
+                                     str(attrvalue))
+
+    def set_bridge_attrs(self, bridgename, attrdict, check=True):
+        for k, v in attrdict.iteritems():
+            if not v:
+                continue
+            try:
+                self.set_bridge_attr(bridgename, k, v, check)
+            except Exception, e:
+                self.logger.warn('%s: %s' %(bridgename, str(e)))
+
+    def get_bridge_treeprio(self, bridgename):
+        return self.get_bridge_attr(bridgename, 'treeprio')
+
+    def set_bridge_treeprio(self, bridgename, attrvalue, check=True):
+        if check:
+            attrvalue_curr = self.get_bridge_treeprio(bridgename)
+            if attrvalue_curr and attrvalue_curr == attrvalue:
+                return
+        utils.exec_commandl([utils.mstpctl_cmd,
+                             'settreeprio', bridgename, '0',
+                             str(attrvalue)])
+        self.update_bridge_cache(bridgename, 'treeprio', str(attrvalue))
+
+    def showbridge(self, bridgename=None):
+        if bridgename:
+            return utils.exec_command('%s showbridge %s' %
+                                       (utils.mstpctl_cmd, bridgename))
+        else:
+            return utils.exec_command('%s showbridge' %utils.mstpctl_cmd)
+
+    def showportdetail(self, bridgename):
+        return utils.exec_command('%s showportdetail %s' %
+                                  (utils.mstpctl_cmd, bridgename))
+
+    def mstpbridge_exists(self, bridgename):
+        try:
+            utils.exec_command('%s showbridge %s' %
+                               (utils.mstpctl_cmd, bridgename))
+            return True
+        except:
+            return False
diff --git a/ifupdown2/ifupdownaddons/systemutils.py b/ifupdown2/ifupdownaddons/systemutils.py
new file mode 100644 (file)
index 0000000..5e2812a
--- /dev/null
@@ -0,0 +1,63 @@
+#!/usr/bin/python
+#
+# Copyright 2015-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+import os
+
+try:
+    from ifupdown2.ifupdown.utils import utils
+    from ifupdown2.ifupdownaddons.utilsbase import *
+except ImportError:
+    from ifupdown.utils import utils
+    from ifupdownaddons.utilsbase import *
+
+
+class systemUtils():
+    @classmethod
+    def is_service_running(cls, procname=None, pidfile=None):
+        utilsobj = utilsBase()
+        if pidfile:
+            if os.path.exists(pidfile):
+                pid = utilsobj.read_file_oneline(pidfile)
+                if not os.path.exists('/proc/%s' %pid):
+                    return False
+            else:
+                return False
+            return True
+
+        if procname:
+            try:
+                utils.exec_command('%s %s' %
+                                    (utils.pidof_cmd, procname))
+            except:
+                return False
+            else:
+                return True
+
+        return False
+
+    @classmethod
+    def check_service_status(cls, servicename=None):
+        if not servicename:
+            return False
+        try:
+            utils.exec_commandl([utils.service_cmd,
+                                 servicename, 'status'])
+        except Exception:
+            # XXX: check for subprocess errors vs os error
+            return False
+        return True
+
+    @classmethod
+    def is_process_running(self, processname):
+        if not processname:
+            return False
+        try:
+            utils.exec_command('%s %s' %
+                               (utils.pidof_cmd, processname))
+        except:
+            return False
+        else:
+            return True
diff --git a/ifupdown2/ifupdownaddons/utilsbase.py b/ifupdown2/ifupdownaddons/utilsbase.py
new file mode 100644 (file)
index 0000000..f467087
--- /dev/null
@@ -0,0 +1,79 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+import time
+import logging
+
+try:
+    from ifupdown2.ifupdown.iface import *
+    from ifupdown2.ifupdown.utils import utils
+    from ifupdown2.ifupdownaddons.cache import *
+
+    import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
+except ImportError:
+    from ifupdown.iface import *
+    from ifupdown.utils import utils
+    from ifupdownaddons.cache import *
+
+    import ifupdown.ifupdownflags as ifupdownflags
+
+
+def profile(func):
+    def wrap(*args, **kwargs):
+        started_at = time.time()
+        result = func(*args, **kwargs)
+        print str(func)
+        print (time.time() - started_at)
+        return result
+    return wrap
+
+class utilsBase(object):
+    """ Base class for ifupdown addon utilities """
+
+    def __init__(self, *args, **kargs):
+        modulename = self.__class__.__name__
+        self.logger = logging.getLogger('ifupdown.' + modulename)
+
+    def write_file(self, filename, strexpr):
+        try:
+            self.logger.info('writing \'%s\'' %strexpr +
+                ' to file %s' %filename)
+            if ifupdownflags.flags.DRYRUN:
+                return 0
+            with open(filename, 'w') as f:
+                f.write(strexpr)
+        except IOError, e:
+            self.logger.warn('error writing to file %s'
+                %filename + '(' + str(e) + ')')
+            return -1
+        return 0
+
+    def read_file(self, filename):
+        try:
+            self.logger.debug('reading \'%s\'' %filename)
+            with open(filename, 'r') as f:
+                return f.readlines()
+        except:
+            return None
+        return None
+
+    def read_file_oneline(self, filename):
+        try:
+            self.logger.debug('reading \'%s\'' %filename)
+            with open(filename, 'r') as f:
+                return f.readline().strip('\n')
+        except:
+            return None
+        return None
+
+    def sysctl_set(self, variable, value):
+        utils.exec_command('%s %s=%s' %
+                           (utils.sysctl_cmd, variable, value))
+
+    def sysctl_get(self, variable):
+        return utils.exec_command('%s %s' %
+                                  (utils.sysctl_cmd,
+                                   variable)).split('=')[1].strip()
diff --git a/ifupdown2/man/genmanpages.sh b/ifupdown2/man/genmanpages.sh
new file mode 100755 (executable)
index 0000000..2553dc7
--- /dev/null
@@ -0,0 +1,28 @@
+#!/bin/sh
+#
+# Copyright 2013 Cumulus Networks, Inc.
+# All rights reserved.
+#
+
+# Install the man pages into the sysroot
+SRC_MAN_DIR=$1
+DST_MAN_DIR=$2
+
+echo -n "Generating man pages .."
+# Loop over all the man directories
+mkdir -p $DST_MAN_DIR
+for p in $(ls $SRC_MAN_DIR/*.rst) ; do
+    # strip src man path
+    src_file=$p
+    dst_file=${p##.*\/}
+    dst_file="${DST_MAN_DIR}/${dst_file%.rst}"
+    # treat warnings as errors
+    rst2man --halt=2 "$p" > $dst_file || {
+        echo
+        echo "Error: problems generating man page: $p"
+        rm -f $dst_file &>/dev/null
+        exit 1
+    }
+    echo -n "."
+done
+echo " done."
diff --git a/ifupdown2/man/ifdown.8.rst b/ifupdown2/man/ifdown.8.rst
new file mode 100644 (file)
index 0000000..6fc202c
--- /dev/null
@@ -0,0 +1,183 @@
+====
+ifup
+====
+
+-------------------------------------
+network interface management commands 
+-------------------------------------
+
+:Author: Roopa Prabhu <roopa@cumulusnetworks.com>
+:Date:   2014-02-05
+:Copyright: Copyright 2014 Cumulus Networks, Inc.  All rights reserved.
+:Version: 0.1
+:Manual section: 8
+
+NAME
+====
+    **ifup** - bring a network interface up
+
+    **ifdown** - take a network interface down
+
+SYNOPSIS
+========
+
+    ifup [-h] [-a] [-v] [-d] [--allow CLASS] [--with-depends]
+       **[-X EXCLUDEPATS] [-f] [-n] [-s] [--print-dependency {list,dot}]**
+       **[IFACE [IFACE ...]]**
+
+    ifdown [-h] [-a] [-v] [-d] [--allow CLASS] [--with-depends]
+           **[-X EXCLUDEPATS] [-f] [-n] [--print-dependency {list,dot}]**
+           **[IFACE [IFACE ...]]**
+
+DESCRIPTION
+===========
+    **ifup** and **ifdown** commands can be used to configure (or, respectively,
+    deconfigure) network interfaces based on interface definitions in the
+    config file ifupdown2.conf (defaults to **/etc/network/interfaces/** file).
+
+    **ifquery(8)** maybe used in conjunction with **ifup** and **ifdown**
+    commands to query and validate applied/running configuration.
+
+    **ifup** always works on the current **interfaces(5)** file defined in ifupdown2.conf
+    (default **/etc/network/interfaces**). **ifdown** works on the last applied interface
+    configuration.
+
+    **ifup** on an already ifup'ed interface will re-apply the configuration,
+    skipping already applied configuration wherever possible. In many cases
+    where config commands are idempotent, you will see that ifup/ifdown will
+    reapply the config even if the interface already has that config.
+
+    **ifup** and **ifdown** understands interface dependency order.
+
+    For logical interfaces like vlans, bridges, bonds, **ifup** creates the
+    interface and **ifdown** deletes the interface. Use **--admin-state**
+    option if you only want to administratively bring the interface up/down.
+
+    When **ifup** and **ifdown** are used with interfaces on command line,
+    they must be have a **iface** section in the **interfaces(5)** file.
+
+OPTIONS
+=======
+    positional arguments:
+
+    **IFACE**  interface list separated by spaces. **IFACE** list and **'-a'**
+    argument are mutually exclusive.
+
+    optional arguments:
+
+    -h, --help            show this help message and exit
+
+    -a, --all             process all interfaces marked "auto"
+
+    -v, --verbose         verbose
+
+    -d, --debug           output debug info
+    --allow CLASS         ignore non-"allow-CLASS" interfaces
+
+    -w, --with-depends        run with all dependent interfaces. This option
+                          is redundant when -a is specified. When '-a' is
+                          specified, interfaces are always executed in
+                          dependency order.
+                        
+    -X EXCLUDEPATS, --exclude EXCLUDEPATS
+                          Exclude interfaces from the list of interfaces to
+                          operate on. Can be specified multiple times
+                          If the excluded interface has dependent interfaces,
+                          (e.g. a bridge or a bond with multiple enslaved interfaces)
+                          then each dependent interface must be specified in order
+                          to be excluded.
+
+    -i INTERFACESFILE, --interfaces INTERFACESFILE
+                          Uses interfaces file instead of default defined in 
+                          ifupdown2.conf (default /etc/network/interfaces).
+                         Also in ifupdown2.conf, users are not allowed to specify their own
+                         interfaces file unless disable_cli_interfacesfile is set to 0
+                         (default is 1).
+
+    -t {native,json}, --interfaces-format {native,json}
+                          interfaces file format
+
+    -f, --force           force run all operations
+
+    -n, --no-act          print out what would happen, but don't do it
+
+    -p, --print-dependency {list,dot}
+                          print iface dependency in list or dot format
+
+    -m, --admin-state, --no-scripts
+                          don't run any addon modules/scripts. Only bring
+                          the interface administratively up/down
+
+    -u, --use-current-config
+                          By default ifdown looks at the saved state for
+                          interfaces to bring down. This option allows ifdown
+                          to look at the current interfaces file. Useful when
+                          your state file is corrupted or you want down to use
+                          the latest from the interfaces file
+
+    -s, --syntax-check    Only run the interfaces file parser
+
+EXAMPLES
+========
+    # bringing up all interfaces
+
+        **ifup -a**
+
+    # bringing up interface list
+
+        **ifup swp1 swp2**
+
+    # bringing up interface with its dependents
+
+        **ifup br0 --with-depends**
+
+    # bringing down all interfaces
+
+        **ifdown -a**
+
+    # bringing down a single interface
+
+        **ifdown swp1**
+
+    # excluding interfaces using -X option
+
+        **ifdown -X eth0 -a**
+
+        **ifup -X eth0 -a**
+
+        **ifdown -X eth0 -X lo -a**
+
+    # using verbose -v option to see what is going on
+
+        **ifup -v -a**
+
+    # using debug -d option to see more of what is going on
+
+        **ifup -d -a**
+
+    # ignore errors
+
+        **ifup -a -f**
+
+        **ifdown -a -f**
+
+    # run ifdown and ifup on all interfaces using service command/init script
+
+        **service networking restart**
+
+    # run ifup on all interfaces using service command/init script
+
+        **service networking start**
+
+    # ifdown on all interfaces using service command/init script
+
+        **service networking stop**
+
+    # To run ifup/ifdown on only interfaces that changed see **ifreload(8)**
+
+SEE ALSO
+========
+    ifquery(8),
+    ifreload(8),
+    interfaces(5),
+    ifupdown-addons-interfaces(5)
diff --git a/ifupdown2/man/ifquery.8.rst b/ifupdown2/man/ifquery.8.rst
new file mode 100644 (file)
index 0000000..ed808ca
--- /dev/null
@@ -0,0 +1,156 @@
+=======
+ifquery
+=======
+
+-------------------------------------
+query network interface configuration
+-------------------------------------
+
+:Author: Roopa Prabhu <roopa@cumulusnetworks.com>
+:Date:   2014-02-05
+:Copyright: Copyright 2014 Cumulus Networks, Inc.  All rights reserved.
+:Version: 0.1
+:Manual section: 8
+
+SYNOPSIS
+========
+
+    **ifquery [-v] [--allow CLASS] [--with-depends] -a|IFACE...**
+
+    **ifquery [-v] [-r|--running] [--allow CLASS] [--with-depends] -a|IFACE...**
+
+    **ifquery [-v] [-c|--check] [--allow CLASS] [--with-depends] -a|IFACE...**
+
+    **ifquery [-v] [-p|--print-dependency {list,dot}] [--allow CLASS] [--with-depends] -a|IFACE...**
+
+    **ifquery [-v] -s|--syntax-help**
+
+DESCRIPTION
+===========
+    **ifquery** can be used to parse interface configuration file, query
+    running state or check running state of the interface with configuration
+    in **/etc/network/interfaces** file.
+
+    **ifquery** always works on the current **interfaces(5)** file
+    **/etc/network/interfaces** unless an alternate interfaces file is
+    defined in ifupdown2.conf or provided with the **-i** option.
+    Note: the -i option is disabled by default in ifupdown2.conf.
+
+OPTIONS
+=======
+    positional arguments:
+
+    **IFACE**   interface list separated by spaces. **IFACE** list and **'-a'** argument are mutually exclusive.
+
+    optional arguments:
+
+    -h, --help            show this help message and exit
+
+    -a, --all             process all interfaces marked "auto"
+
+    -v, --verbose         verbose
+
+    -d, --debug           output debug info
+    --allow CLASS         ignore non-"allow-CLASS" interfaces
+
+    -w, --with-depends    run with all dependent interfaces. This option
+                          is redundant when -a is specified. When '-a' is
+                          specified, interfaces are always executed in
+                          dependency order.
+
+    -X EXCLUDEPATS, --exclude EXCLUDEPATS
+                          Exclude interfaces from the list of interfaces to
+                          operate on. Can be specified multiple times
+                          If the excluded interface has dependent interfaces,
+                          (e.g. a bridge or a bond with multiple enslaved interfaces)
+                          then each dependent interface must be specified in order
+                          to be excluded.
+
+    -i INTERFACESFILE, --interfaces INTERFACESFILE
+                          Use interfaces file instead of default
+                          defined in ifupdown2.conf (default /etc/network/interfaces)
+
+    -t {native,json}, --interfaces-format {native,json}
+                          interfaces file format
+
+    -r, --running         print raw interfaces file entries
+
+    -c, --check           check interface file contents against running state
+                          of an interface. Returns exit code 0 on success and
+                          1 on error
+
+    -x, --raw             print raw config file entries
+
+    -o {native,json}, --format {native,json}
+                          interface display format
+
+    -p, --print-dependency {list,dot}
+                          print iface dependency in list or dot format
+
+    -s, --syntax-help     print supported interface config syntax. Scans all
+                          addon modules and dumps supported syntax from them
+                          if provided by the module.
+
+EXAMPLES
+========
+    # dump all or some interfaces config file entries
+    # (pretty prints user provided entries)
+
+        **ifquery -a**
+
+        **ifquery br0**
+
+    # Same as above but dump with dependencies
+
+        **ifquery br0 --with-depends**
+
+    # Check running state with the config in /etc/network/interfaces
+
+        **ifquery --check br0**
+
+        **ifquery --check --with-depends br0**
+
+        **ifquery --check -a** 
+
+    # dump running state of all interfaces in /etc/network/interfaces format
+
+        **ifquery --running br0**
+
+        **ifquery --running --with-depends br0**
+
+        **ifquery --running -a**
+
+    # print dependency info in list format
+
+        **ifquery --print-dependency=list -a**
+
+        **ifquery --print-dependency=list  br2000**
+
+    # print dependency info in dot format
+
+        **ifquery --print-dependency=dot -a**
+
+        **ifquery --print-dependency=dot br2000**
+
+    # Create an image (png) from the dot format
+
+        **ifquery --print-dependency=dot -a > interfaces.dot**
+
+        **dot -Tpng interfaces.dot > interfaces.png**
+
+        (The above command only works on a system with dot installed)
+
+KNOWN_ISSUES
+============
+    **ifquery --check** is currently experimental
+
+    **ifquery --check** cannot validate usercommands given under pre-up, post-up etc
+    There is currently no support to check/validate ethtool iface attributes
+
+SEE ALSO
+========
+    ifup(8),
+    ifdown(8),
+    ifreload(8),
+    interfaces(5),
+    ifupdown-addons-interfaces(5)
diff --git a/ifupdown2/man/ifreload.8.rst b/ifupdown2/man/ifreload.8.rst
new file mode 100644 (file)
index 0000000..ccb1806
--- /dev/null
@@ -0,0 +1,93 @@
+========
+ifreload
+========
+
+--------------------------------------
+reload network interface configuration
+--------------------------------------
+
+:Author: Roopa Prabhu <roopa@cumulusnetworks.com>
+:Date:   2014-02-05
+:Copyright: Copyright 2014 Cumulus Networks, Inc.  All rights reserved.
+:Version: 0.1
+:Manual section: 8
+
+SYNOPSIS
+========
+    ifreload [-h] (-a|-c) [-v] [-d] [-f] [-n] [-s]
+
+DESCRIPTION
+===========
+    reloads network **interfaces(5)** file **/etc/network/interfaces**
+    or config file defined in ifupdown2.conf file.
+
+    Runs **ifdown** on interfaces that were removed from the file and
+    subsequently runs **ifup** on all interfaces.
+
+    ifreload is non-disruptive. It will fix running config to match what
+    is configured in the interfaces file without bringing the interface
+    down. There are some cases where on linux an interface config cannot
+    be applied unless the interface is brought down...eg: change of mac
+    address and a few bond attributes. For such attribute changes, it may
+    flap the interface only if the linux kernel requires it to.
+
+    When removing an interface (iface section) from the interfaces file
+    please make sure all its references are removed as well. Similarly
+    when renaming an interface, please make sure all references to the
+    interface are changed to the new name. Renaming an interface
+    in the interfaces file results in ifdown of the old and ifup
+    of the interface with the new name.
+
+    If you do not wish to execute **down** on any interfaces, but only **up** on
+    interfaces that were already **up**, please see the **--currently-up**
+    option below.
+
+OPTIONS
+=======
+    -h, --help            show this help message and exit
+
+    -a, --all             process all interfaces marked "auto"
+
+    -v, --verbose         verbose
+
+    -d, --debug           output debug info
+
+    -f, --force           force run all operations
+
+    -c, --currently-up    Reload the configuration for all interfaces which
+                          are currently up regardless of whether an interface
+                          has "auto <interface>" configuration within the
+                          /etc/network/interfaces file.
+
+    -X EXCLUDEPATS, --exclude EXCLUDEPATS
+                          Exclude interfaces from the list of interfaces to
+                          operate on. Can be specified multiple times
+                          If the excluded interface has dependent interfaces,
+                          (e.g. a bridge or a bond with multiple enslaved interfaces)
+                          then each dependent interface must be specified in order
+                          to be excluded.
+
+    -s, --syntax-check    Only run the interfaces file parser
+
+
+EXAMPLES
+========
+    # reload all auto interfaces in **interfaces(5)** file
+
+    **ifreload -a**
+
+    # reload all interfaces using service command
+
+    **service networking reload**
+
+    # reload all currently up interfaces without bringing any interfaces down
+
+    **service networking reload-currently-up**
+
+SEE ALSO
+========
+    ifup(8),
+    ifdown(8),
+    ifquery(8),
+    interfaces(5),
+    ifupdown-addons-interfaces(5)
diff --git a/ifupdown2/man/ifup.8.rst b/ifupdown2/man/ifup.8.rst
new file mode 100644 (file)
index 0000000..6fc202c
--- /dev/null
@@ -0,0 +1,183 @@
+====
+ifup
+====
+
+-------------------------------------
+network interface management commands 
+-------------------------------------
+
+:Author: Roopa Prabhu <roopa@cumulusnetworks.com>
+:Date:   2014-02-05
+:Copyright: Copyright 2014 Cumulus Networks, Inc.  All rights reserved.
+:Version: 0.1
+:Manual section: 8
+
+NAME
+====
+    **ifup** - bring a network interface up
+
+    **ifdown** - take a network interface down
+
+SYNOPSIS
+========
+
+    ifup [-h] [-a] [-v] [-d] [--allow CLASS] [--with-depends]
+       **[-X EXCLUDEPATS] [-f] [-n] [-s] [--print-dependency {list,dot}]**
+       **[IFACE [IFACE ...]]**
+
+    ifdown [-h] [-a] [-v] [-d] [--allow CLASS] [--with-depends]
+           **[-X EXCLUDEPATS] [-f] [-n] [--print-dependency {list,dot}]**
+           **[IFACE [IFACE ...]]**
+
+DESCRIPTION
+===========
+    **ifup** and **ifdown** commands can be used to configure (or, respectively,
+    deconfigure) network interfaces based on interface definitions in the
+    config file ifupdown2.conf (defaults to **/etc/network/interfaces/** file).
+
+    **ifquery(8)** maybe used in conjunction with **ifup** and **ifdown**
+    commands to query and validate applied/running configuration.
+
+    **ifup** always works on the current **interfaces(5)** file defined in ifupdown2.conf
+    (default **/etc/network/interfaces**). **ifdown** works on the last applied interface
+    configuration.
+
+    **ifup** on an already ifup'ed interface will re-apply the configuration,
+    skipping already applied configuration wherever possible. In many cases
+    where config commands are idempotent, you will see that ifup/ifdown will
+    reapply the config even if the interface already has that config.
+
+    **ifup** and **ifdown** understands interface dependency order.
+
+    For logical interfaces like vlans, bridges, bonds, **ifup** creates the
+    interface and **ifdown** deletes the interface. Use **--admin-state**
+    option if you only want to administratively bring the interface up/down.
+
+    When **ifup** and **ifdown** are used with interfaces on command line,
+    they must be have a **iface** section in the **interfaces(5)** file.
+
+OPTIONS
+=======
+    positional arguments:
+
+    **IFACE**  interface list separated by spaces. **IFACE** list and **'-a'**
+    argument are mutually exclusive.
+
+    optional arguments:
+
+    -h, --help            show this help message and exit
+
+    -a, --all             process all interfaces marked "auto"
+
+    -v, --verbose         verbose
+
+    -d, --debug           output debug info
+    --allow CLASS         ignore non-"allow-CLASS" interfaces
+
+    -w, --with-depends        run with all dependent interfaces. This option
+                          is redundant when -a is specified. When '-a' is
+                          specified, interfaces are always executed in
+                          dependency order.
+                        
+    -X EXCLUDEPATS, --exclude EXCLUDEPATS
+                          Exclude interfaces from the list of interfaces to
+                          operate on. Can be specified multiple times
+                          If the excluded interface has dependent interfaces,
+                          (e.g. a bridge or a bond with multiple enslaved interfaces)
+                          then each dependent interface must be specified in order
+                          to be excluded.
+
+    -i INTERFACESFILE, --interfaces INTERFACESFILE
+                          Uses interfaces file instead of default defined in 
+                          ifupdown2.conf (default /etc/network/interfaces).
+                         Also in ifupdown2.conf, users are not allowed to specify their own
+                         interfaces file unless disable_cli_interfacesfile is set to 0
+                         (default is 1).
+
+    -t {native,json}, --interfaces-format {native,json}
+                          interfaces file format
+
+    -f, --force           force run all operations
+
+    -n, --no-act          print out what would happen, but don't do it
+
+    -p, --print-dependency {list,dot}
+                          print iface dependency in list or dot format
+
+    -m, --admin-state, --no-scripts
+                          don't run any addon modules/scripts. Only bring
+                          the interface administratively up/down
+
+    -u, --use-current-config
+                          By default ifdown looks at the saved state for
+                          interfaces to bring down. This option allows ifdown
+                          to look at the current interfaces file. Useful when
+                          your state file is corrupted or you want down to use
+                          the latest from the interfaces file
+
+    -s, --syntax-check    Only run the interfaces file parser
+
+EXAMPLES
+========
+    # bringing up all interfaces
+
+        **ifup -a**
+
+    # bringing up interface list
+
+        **ifup swp1 swp2**
+
+    # bringing up interface with its dependents
+
+        **ifup br0 --with-depends**
+
+    # bringing down all interfaces
+
+        **ifdown -a**
+
+    # bringing down a single interface
+
+        **ifdown swp1**
+
+    # excluding interfaces using -X option
+
+        **ifdown -X eth0 -a**
+
+        **ifup -X eth0 -a**
+
+        **ifdown -X eth0 -X lo -a**
+
+    # using verbose -v option to see what is going on
+
+        **ifup -v -a**
+
+    # using debug -d option to see more of what is going on
+
+        **ifup -d -a**
+
+    # ignore errors
+
+        **ifup -a -f**
+
+        **ifdown -a -f**
+
+    # run ifdown and ifup on all interfaces using service command/init script
+
+        **service networking restart**
+
+    # run ifup on all interfaces using service command/init script
+
+        **service networking start**
+
+    # ifdown on all interfaces using service command/init script
+
+        **service networking stop**
+
+    # To run ifup/ifdown on only interfaces that changed see **ifreload(8)**
+
+SEE ALSO
+========
+    ifquery(8),
+    ifreload(8),
+    interfaces(5),
+    ifupdown-addons-interfaces(5)
diff --git a/ifupdown2/man/ifupdown-addons-interfaces.5.rst b/ifupdown2/man/ifupdown-addons-interfaces.5.rst
new file mode 100644 (file)
index 0000000..1858dac
--- /dev/null
@@ -0,0 +1,1385 @@
+==========================
+ifupdown-addons-interfaces
+==========================
+---------------------------------------------------------
+ifupdown2 addon modules interface configuration
+---------------------------------------------------------
+:Author: roopa@cumulusnetworks.com
+:Date:   2013-09-25
+:Copyright: Copyright 2013 Cumulus Networks, Inc.  All rights reserved.
+:Version: 0.1
+:Manual section: 5
+
+
+DESCRIPTION
+===========
+    ifupdown2 addon modules add incremental functionality to
+    core ifupdown2 tool.
+           
+    All installed addon modules are executed on every interface
+    listed in the interfaces file. Addon modules are installed under
+    /usr/share/ifupdownaddons. To see the list of active addon
+    modules, see ifaddon(8).
+
+    Addon modules add new attributes to the interfaces(5) file.
+    Below is a list of attribute options provided by each module.
+    These can be listed under each iface section in the interfaces(5)
+    file.  
+
+
+EXAMPLES
+========
+    Listed below are addon modules and their supported attributes.
+    The attributes if applicable go under the iface section in the
+    interfaces(5) file.
+
+    **ethtool**: ethtool configuration module for interfaces
+
+
+      **link-duplex**
+
+        **help**: set link duplex
+
+
+        **required**: False
+
+        **default**: half
+
+        **validvals**: half,full
+
+        **example**:
+            link-duplex full
+
+
+      **link-autoneg**
+
+        **help**: set autonegotiation
+
+
+        **required**: False
+
+        **default**: no
+
+        **validvals**: yes,no
+
+        **example**:
+            link-autoneg yes
+
+
+      **link-speed**
+
+        **help**: set link speed
+
+
+        **required**: False
+
+        **example**:
+            link-speed 1000
+
+
+
+    **bridge**: Bridge configuration module. Supports both vlan aware 
+    and non vlan aware bridges. For the vlan aware bridge, the port sp
+    ecific attributes must be specified under the port. And for vlan u
+    naware bridge port specific attributes must be specified under the
+    bridge.
+
+
+      **bridge-vlan-aware**
+
+        **help**: vlan aware bridge. Setting this attribute to yes ena
+        bles vlan filtering on the bridge
+
+
+        **required**: False
+
+        **example**:
+            bridge-vlan-aware yes/no
+
+
+      **bridge-pathcosts**
+
+        **help**: bridge set port path costs
+
+
+        **required**: False
+
+        **default**: 100
+
+        **example**:
+            bridge-pathcosts swp1=100 swp2=100
+
+
+      **bridge-portprios**
+
+        **help**: bridge port prios
+
+
+        **required**: False
+
+        **default**: 32
+
+        **example**:
+            bridge-portprios swp1=32 swp2=32
+
+
+      **bridge-fd**
+
+        **help**: bridge forward delay
+
+
+        **required**: False
+
+        **default**: 15
+
+        **example**:
+            bridge-fd 15
+
+
+      **bridge-ageing**
+
+        **help**: bridge ageing
+
+
+        **required**: False
+
+        **default**: 300
+
+        **example**:
+            bridge-ageing 300
+
+
+      **bridge-hello**
+
+        **help**: bridge set hello time
+
+
+        **required**: False
+
+        **default**: 2
+
+        **example**:
+            bridge-hello 2
+
+
+      **bridge-gcint**
+
+        **help**: bridge garbage collection interval in secs
+
+
+        **required**: False
+
+        **default**: 4
+
+        **example**:
+            bridge-gcint 4
+
+
+      **bridge-mcquerier**
+
+        **help**: set multicast querier
+
+
+        **required**: False
+
+        **default**: no
+
+        **validvals**: yes,no
+
+        **example**:
+            bridge-mcquerier no
+
+
+      **bridge-mclmc**
+
+        **help**: set multicast last member count
+
+
+        **required**: False
+
+        **default**: 2
+
+        **example**:
+            bridge-mclmc 2
+
+
+      **bridge-mcsqc**
+
+        **help**: set multicast startup query count
+
+
+        **required**: False
+
+        **default**: 2
+
+        **example**:
+            bridge-mcsqc 2
+
+
+      **bridge-mcrouter**
+
+        **help**: set multicast router
+
+
+        **required**: False
+
+        **default**: yes
+
+        **validvals**: yes,no
+
+        **example**:
+            bridge-mcrouter yes
+
+
+      **bridge-stp**
+
+        **help**: bridge-stp yes/no
+
+
+        **required**: False
+
+        **default**: no
+
+        **validvals**: yes,on,off,no
+
+        **example**:
+            bridge-stp no
+
+
+      **bridge-pvid**
+
+        **help**: bridge port pvid. Must be specified under the bridge
+        port
+
+
+        **required**: False
+
+        **example**:
+            bridge-pvid 1
+
+
+      **bridge-mcsqi**
+
+        **help**: set multicast startup query interval (in secs)
+
+
+        **required**: False
+
+        **default**: 31
+
+        **example**:
+            bridge-mcsqi 31
+
+
+      **bridge-mcmi**
+
+        **help**: set multicast membership interval (in secs)
+
+
+        **required**: False
+
+        **default**: 260
+
+        **example**:
+            bridge-mcmi 260
+
+
+      **bridge-mclmi**
+
+        **help**: set multicast last member interval (in secs)
+
+
+        **required**: False
+
+        **default**: 1
+
+        **example**:
+            bridge-mclmi 1
+
+
+      **bridge-vids**
+
+        **help**: bridge port vids. Can be specified under the bridge 
+        or under the port. If specified under the bridge the ports inh
+        erit it unless overridden by a bridge-vids attribuet under the
+        port
+
+
+        **required**: False
+
+        **example**:
+            bridge-vids 4000
+
+            bridge-vids 2000 2200-3000
+
+
+      **bridge-ports**
+
+        **help**: bridge ports
+
+
+        **required**: True
+
+        **example**:
+            bridge-ports swp1.100 swp2.100 swp3.100
+
+            bridge-ports glob swp1-3.100
+
+            bridge-ports glob swp[1-3]s[0-4].100
+
+            bridge-ports regex (swp[1|2|3].100)
+
+
+      **bridge-mcqifaddr**
+
+        **help**: set multicast query to use ifaddr
+
+
+        **required**: False
+
+        **default**: no
+
+        **validvals**: yes,no
+
+        **example**:
+            bridge-mcqifaddr no
+
+
+      **bridge-waitport**
+
+        **help**: wait for a max of time secs for the specified ports 
+        to become available,if no ports are specified then those speci
+        fied on bridge-ports will be used here. Specifying no ports he
+        re should not be used if we are using regex or "all" on bridge
+        _ports,as it wouldnt work.
+
+
+        **required**: False
+
+        **default**: 0
+
+        **example**:
+            bridge-waitport 4 swp1 swp2
+
+
+      **bridge-mcqri**
+
+        **help**: set multicast query response interval (in secs)
+
+
+        **required**: False
+
+        **default**: 10
+
+        **example**:
+            bridge-mcqri 10
+
+
+      **bridge-hashel**
+
+        **help**: set hash elasticity
+
+
+        **required**: False
+
+        **default**: 4096
+
+        **example**:
+            bridge-hashel 4096
+
+
+      **bridge-mcqpi**
+
+        **help**: set multicast querier interval (in secs)
+
+
+        **required**: False
+
+        **default**: 255
+
+        **example**:
+            bridge-mcqpi 255
+
+
+      **bridge-hashmax**
+
+        **help**: set hash max
+
+
+        **required**: False
+
+        **default**: 4096
+
+        **example**:
+            bridge-hashmax 4096
+
+
+      **bridge-bridgeprio**
+
+        **help**: bridge priority
+
+
+        **required**: False
+
+        **default**: 32768
+
+        **example**:
+            bridge-bridgeprio 32768
+
+
+      **bridge-maxage**
+
+        **help**: bridge set maxage
+
+
+        **required**: False
+
+        **default**: 20
+
+        **example**:
+            bridge-maxage 20
+
+
+      **bridge-mcsnoop**
+
+        **help**: set multicast snooping
+
+
+        **required**: False
+
+        **default**: yes
+
+        **validvals**: yes,no
+
+        **example**:
+            bridge-mcsnoop yes
+
+
+      **bridge-access**
+
+        **help**: bridge port access vlan. Must be specified under the
+        bridge port
+
+
+        **required**: False
+
+        **example**:
+            bridge-access 300
+
+
+      **bridge-maxwait**
+
+        **help**: forces to time seconds the maximum time that the Deb
+        ian bridge setup  scripts will wait for the bridge ports to ge
+        t to the forwarding status, doesn't allow factional part. If i
+        t is equal to 0 then no waiting is done
+
+
+        **required**: False
+
+        **default**: 0
+
+        **example**:
+            bridge-maxwait 3
+
+
+      **bridge-portmcrouter**
+
+        **help**: set port multicast routers
+
+
+        **required**: False
+
+        **default**: 1
+
+        **example**:
+            under the bridge: bridge-portmcrouter swp1=1 swp2=1
+
+            under the port: bridge-portmcrouter 1
+
+
+      **bridge-portmcfl**
+
+        **help**: port multicast fast leave.
+
+
+        **required**: False
+
+        **default**: 0
+
+        **example**:
+            under the bridge: bridge-portmcfl swp1=0 swp2=0
+
+            under the port: bridge-portmcfl 0
+
+
+      **bridge-mcqi**
+
+        **help**: set multicast query interval (in secs)
+
+
+        **required**: False
+
+        **default**: 125
+
+        **example**:
+            bridge-mcqi 125
+
+
+
+    **usercmds**: user commands for interfaces
+
+
+      **down**
+
+        **help**: run command at interface down
+
+
+        **required**: False
+
+      **post-up**
+
+        **help**: run command after interface bring up
+
+
+        **required**: False
+
+      **up**
+
+        **help**: run command at interface bring up
+
+
+        **required**: False
+
+      **pre-down**
+
+        **help**: run command before bringing the interface down
+
+
+        **required**: False
+
+      **pre-up**
+
+        **help**: run command before bringing the interface up
+
+
+        **required**: False
+
+      **post-down**
+
+        **help**: run command after bringing interface down
+
+
+        **required**: False
+
+
+    **mstpctl**: mstp configuration module for bridges
+
+
+      **mstpctl-portadminedge**
+
+        **help**: enable/disable initial edge state of the port
+
+
+        **required**: False
+
+        **default**: no
+
+        **validvals**: yes,no
+
+        **example**:
+            mstpctl-portadminedge swp1=no swp2=no
+
+
+      **mstpctl-portbpdufilter**
+
+        **help**: enable/disable bpdu filter on a port. syntax varies 
+        when defined under a bridge vs under a port
+
+
+        **required**: False
+
+        **default**: no
+
+        **validvals**: yes,no
+
+        **example**:
+            under a bridge: mstpctl-portbpdufilter swp1=no swp2=no
+
+            under a port: mstpctl-portbpdufilter yes
+
+
+      **mstpctl-fdelay**
+
+        **help**: set forwarding delay
+
+
+        **required**: False
+
+        **default**: 15
+
+        **example**:
+            mstpctl-fdelay 15
+
+
+      **mstpctl-portnetwork**
+
+        **help**: enable/disable bridge assurance capability for a por
+        t
+
+
+        **required**: False
+
+        **default**: no
+
+        **validvals**: yes,no
+
+        **example**:
+            mstpctl-portnetwork swp1=no swp2=no
+
+
+      **mstpctl-txholdcount**
+
+        **help**: bridge transmit holdcount
+
+
+        **required**: False
+
+        **default**: 6
+
+        **example**:
+            mstpctl-txholdcount 6
+
+
+      **mstpctl-forcevers**
+
+        **help**: bridge force stp version
+
+
+        **required**: False
+
+        **default**: rstp
+
+        **example**:
+            mstpctl-forcevers rstp
+
+
+      **mstpctl-portautoedge**
+
+        **help**: enable/disable auto transition to/from edge state of
+        the port
+
+
+        **required**: False
+
+        **default**: yes
+
+        **validvals**: yes,no
+
+        **example**:
+            mstpctl-portautoedge swp1=yes swp2=yes
+
+
+      **mstpctl-maxhops**
+
+        **help**: bridge max hops
+
+
+        **required**: False
+
+        **default**: 15
+
+        **example**:
+            mstpctl-maxhops 15
+
+
+      **mstpctl-treeprio**
+
+        **help**: tree priority
+
+
+        **required**: False
+
+        **default**: 32768
+
+        validrange: 0-65535
+
+        **example**:
+            mstpctl-treeprio 32768
+
+
+      **mstpctl-treeportprio**
+
+        **help**: port priority for MSTI instance
+
+
+        **required**: False
+
+        **default**: 128
+
+        validrange: 0-240
+
+        **example**:
+            mstpctl-treeportprio swp1=128 swp2=128
+
+
+      **mstpctl-portpathcost**
+
+        **help**: bridge port path cost
+
+
+        **required**: False
+
+        **default**: 0
+
+        **example**:
+            mstpctl-portpathcost swp1=0 swp2=1
+
+
+      **mstpctl-portrestrtcn**
+
+        **help**: enable/disable port ability to propagate received to
+        pology change notification of the port
+
+
+        **required**: False
+
+        **default**: no
+
+        **validvals**: yes,no
+
+        **example**:
+            mstpctl-portrestrtcn swp1=no swp2=no
+
+
+      **mstpctl-maxage**
+
+        **help**: max message age
+
+
+        **required**: False
+
+        **default**: 20
+
+        **example**:
+            mstpctl-maxage 20
+
+
+      **mstpctl-hello**
+
+        **help**: set hello time
+
+
+        **required**: False
+
+        **default**: 2
+
+        **example**:
+            mstpctl-hello 2
+
+
+      **mstpctl-portrestrrole**
+
+        **help**: enable/disable port ability to take root role of the
+        port
+
+
+        **required**: False
+
+        **default**: no
+
+        **validvals**: yes,no
+
+        **example**:
+            mstpctl-portrestrrole swp1=no swp2=no
+
+
+      **mstpctl-bpduguard**
+
+        **help**: enable/disable bpduguard
+
+
+        **required**: False
+
+        **default**: no
+
+        **validvals**: yes,no
+
+        **example**:
+            mstpctl-bpduguard swp1=no swp2=no
+
+
+      **mstpctl-ageing**
+
+        **help**: ageing time
+
+
+        **required**: False
+
+        **default**: 300
+
+        **example**:
+            mstpctl-ageing 300
+
+
+      **mstpctl-treeportcost**
+
+        **help**: port tree cost
+
+
+        **required**: False
+
+      **mstpctl-portp2p**
+
+        **help**: bridge port p2p detection mode
+
+
+        **required**: False
+
+        **default**: auto
+
+        **validvals**: yes,no,auto
+
+        **example**:
+            mstpctl-portp2p swp1=no swp2=no
+
+
+
+    **clagd**: This module generates the clagd defaults file.
+
+
+      **clagd-priority**
+
+        **help**: The priority of this clagd switch
+
+
+        **required**: False
+
+        **example**:
+            clagd-priority 30000
+
+
+      **clagd-backup-ip**
+
+        **help**: Backup IP address of the clagd peer
+
+
+        **required**: False
+
+        **example**:
+            clagd-backup-ip 192.1.1.1
+
+
+      **clagd-enable**
+
+        **help**: enable clagd
+
+
+        **required**: False
+
+        **validvals**: yes,no
+
+        **example**:
+            clagd-enable yes
+
+
+      **clag-id**
+
+        **help**: multi-chassis lag id
+
+
+        **required**: False
+
+        **default**: 0
+
+        validrange: 0-65535
+
+        **example**:
+            clag-id 1
+
+
+      **clagd-peer-ip**
+
+        **help**: The IP address of the clagd peer
+
+
+        **required**: True
+
+        **example**:
+            clagd-peer 10.10.10.2
+
+
+      **clagd-sys-mac**
+
+        **help**: The system ID of the CLAG pair
+
+
+        **required**: True
+
+        **example**:
+            clagd-sys-mac 44:38:39:ff:00:00
+
+
+      **clagd-args**
+
+        **help**: Additional command line arguments for clagd
+
+
+        **required**: False
+
+        **example**:
+            clagd-args --log /var/log/clagd.log
+
+            clagd-args --verbose --lacpPoll 10
+
+            clagd-args --debug 0x4
+
+
+
+    **vlan**: vlan module configures vlan interfaces.This module under
+    stands vlan interfaces with dot notations. eg swp1.100. Vlan inter
+    faces with any other names need to have raw device and vlan id att
+    ributes
+
+
+      **vlan-id**
+
+        **help**: vlan id
+
+
+        **required**: False
+
+      **vlan-raw-device**
+
+        **help**: vlan raw device
+
+
+        **required**: False
+
+
+    **bridgevlan**: bridgevlan module configures vlan attributes on a 
+    vlan aware bridge. This module only understands vlan interface name
+    with dot notations. eg br0.100. where br0 is the vlan aware bridge
+    this config is for.
+
+
+      **bridge-igmp-querier-src**
+
+        **help**: bridge igmp querier src. Must be specified under the
+        vlan interface
+
+
+        **required**: False
+
+        **example**:
+            bridge-igmp-querier-src 172.16.101.1
+
+
+
+    **bond**: bond configuration module
+
+
+      **bond-use-carrier**
+
+        **help**: bond use carrier
+
+
+        **required**: False
+
+        **default**: yes
+
+        **validvals**: yes,no
+
+        **example**:
+            bond-use-carrier yes
+
+
+      **bond-lacp-bypass-period**
+
+        **help**: grace period (seconds) for lacp bypass
+
+
+        **required**: False
+
+        **default**: 0
+
+        validrange: 0-900
+
+        **example**:
+            bond-lacp-bypass-period 100
+
+
+      **bond-miimon**
+
+        **help**: bond miimon
+
+
+        **required**: False
+
+        **default**: 0
+
+        validrange: 0-255
+
+        **example**:
+            bond-miimon 0
+
+
+      **bond-lacp-rate**
+
+        **help**: bond lacp rate
+
+
+        **required**: False
+
+        **default**: 0
+
+        **validvals**: 0,1
+
+        **example**:
+            bond-lacp-rate 0
+
+
+      **bond-lacp-bypass-priority**
+
+        **help**: slave priority for lacp bypass
+
+
+        **required**: False
+
+        **example**:
+            bond-lacp-bypass-priority swp1=1 swp2=1 swp3=2
+
+
+      **bond-min-links**
+
+        **help**: bond min links
+
+
+        **required**: False
+
+        **default**: 0
+
+        **example**:
+            bond-min-links 0
+
+
+      **bond-slaves**
+
+        **help**: bond slaves
+
+
+        **required**: True
+
+        **example**:
+            bond-slaves swp1 swp2
+
+            bond-slaves glob swp1-2
+
+            bond-slaves regex (swp[1|2)
+
+
+      **bond-lacp-bypass-allow**
+
+        **help**: allow lacp bypass
+
+
+        **required**: False
+
+        **default**: no
+
+        **validvals**: yes,no
+
+        **example**:
+            bond-lacp-bypass-allow no
+
+
+      **bond-mode**
+
+        **help**: bond mode
+
+
+        **required**: False
+
+        **default**: balance-rr
+
+        **validvals**: balance-rr,active-backup,balance-xor,broadcast,802.3ad,balance-tlb,balance-alb
+
+        **example**:
+            bond-mode 802.3ad
+
+
+      **bond-num-unsol-na**
+
+        **help**: bond slave devices
+
+
+        **required**: False
+
+        **default**: 1
+
+        validrange: 0-255
+
+        **example**:
+            bond-num-unsol-na 1
+
+
+      **bond-ad-sys-priority**
+
+        **help**: 802.3ad system priority
+
+
+        **required**: False
+
+        **default**: 65535
+
+        **example**:
+            bond-ad-sys-priority 65535
+
+
+      **bond-xmit-hash-policy**
+
+        **help**: bond slave devices
+
+
+        **required**: False
+
+        **default**: layer2
+
+        **validvals**: layer2,layer3+4,layer2+3
+
+        **example**:
+            bond-xmit-hash-policy layer2
+
+
+      **bond-num-grat-arp**
+
+        **help**: bond use carrier
+
+
+        **required**: False
+
+        **default**: 1
+
+        validrange: 0-255
+
+        **example**:
+            bond-num-grat-arp 1
+
+
+      **bond-ad-sys-mac-addr**
+
+        **help**: 802.3ad system mac address
+
+
+        **required**: False
+
+        **default**: 00:00:00:00:00:00
+
+        **example**:
+            bond-ad-sys-mac-addr 00:00:00:00:00:00
+
+
+
+    **address**: address configuration module for interfaces
+
+
+      **broadcast**
+
+        **help**: broadcast address
+
+
+        **required**: False
+
+        **example**:
+            broadcast 10.0.1.255
+
+
+      **hwaddress**
+
+        **help**: hw address
+
+
+        **required**: False
+
+        **example**:
+            hwaddress 44:38:39:00:27:b8
+
+
+      **alias**
+
+        **help**: description/alias
+
+
+        **required**: False
+
+        **example**:
+            alias testnetwork
+
+
+      **address**
+
+        **help**: ipv4 or ipv6 addresses
+
+
+        **required**: False
+
+        **example**:
+            address 10.0.12.3/24
+
+            address 2000:1000:1000:1000:3::5/128
+
+
+      **scope**
+
+        **help**: scope
+
+
+        **required**: False
+
+        **example**:
+            scope host
+
+
+      **address-purge**
+
+        **help**: purge existing addresses. By default any existing ip
+        addresses on an interface are purged to match persistent addre
+        sses in the interfaces file. Set this attribute to 'no'if you 
+        want to preserve existing addresses
+
+
+        **required**: False
+
+        **default**: yes
+
+        **example**:
+            address-purge yes/no
+
+
+      **preferred-lifetime**
+
+        **help**: preferred lifetime
+
+
+        **required**: False
+
+        **example**:
+            preferred-lifetime forever
+
+            preferred-lifetime 10
+
+
+      **gateway**
+
+        **help**: default gateway
+
+
+        **required**: False
+
+        **example**:
+            gateway 255.255.255.0
+
+
+      **mtu**
+
+        **help**: interface mtu
+
+
+        **required**: False
+
+        **default**: 1500
+
+        **example**:
+            mtu 1600
+
+
+
+    **addressvirtual**: address module configures virtual addresses fo
+    r interfaces. It creates a macvlan interface for every mac ip addr
+    ess-virtual line
+
+
+      **address-virtual**
+
+        **help**: bridge router virtual mac and ip
+
+
+        **required**: False
+
+        **example**:
+            address-virtual 00:11:22:33:44:01 11.0.1.254/24 11.0.1.254/24
+
+
+
+    **vxlan**: vxlan module configures vxlan interfaces.
+
+
+      **vxlan-learning**
+
+        **help**: vxlan learning yes/no
+
+
+        **required**: False
+
+        **default**: yes
+
+        **validvals**: yes,no
+
+        **example**:
+            vxlan-learning no
+
+
+      **vxlan-id**
+
+        **help**: vxlan id
+
+
+        **required**: True
+
+        **example**:
+            vxlan-id 100
+
+
+      **vxlan-remoteip**
+
+        **help**: vxlan remote ip
+
+
+        **required**: False
+
+        **example**:
+            vxlan-remoteip 172.16.22.127
+
+
+      **vxlan-svcnodeip**
+
+        **help**: vxlan id
+
+
+        **required**: False
+
+        **example**:
+            vxlan-svcnodeip 172.16.22.125
+
+
+      **vxlan-local-tunnelip**
+
+        **help**: vxlan local tunnel ip
+
+
+        **required**: False
+
+        **example**:
+            vxlan-local-tunnelip 172.16.20.103
+
+
+
+SEE ALSO
+========
+    interfaces(5),
+    ifup(8),
+    ip(8),
+    mstpctl(8),
+    brctl(8),
+    ethtool(8),
+    clagctl(8)
diff --git a/ifupdown2/man/interfaces.5.rst b/ifupdown2/man/interfaces.5.rst
new file mode 100644 (file)
index 0000000..262d726
--- /dev/null
@@ -0,0 +1,155 @@
+==========
+interfaces
+==========
+
+--------------------------------------------
+network interface configuration for ifupdown
+--------------------------------------------
+
+:Author: Roopa Prabhu <roopa@cumulusnetworks.com>
+:Date:   2014-02-05
+:Copyright: Copyright 2014 Cumulus Networks, Inc.  All rights reserved.
+:Version: 0.1
+:Manual section: 5
+
+DESCRIPTION
+===========
+    By default, ifupdown2.conf sets **/etc/network/interfaces** as the
+    network interface configuration file.  This file contains information
+    for the **ifup(8)**, **ifdown(8)** and **ifquery(8)** commands.
+
+    This is where you configure how your system is connected to the network.
+
+    Lines starting with # are ignored. Note that end-of-line comments are
+    NOT supported, comments must be on a line of their own.
+
+    A line may be extended across multiple lines by making the last character
+    a backslash.
+
+    The file consists of zero or more "iface", "auto", "allow-"
+    and "source" stanzas. Here is an example::
+
+        auto lo eth0
+        allow-hotplug eth1
+
+        iface lo inet loopback
+
+        source /etc/network/interfaces.d/bridges
+
+        iface eth0 inet static
+            address 192.168.1.1/24
+            up flush-mail
+
+        iface eth1 inet dhcp
+
+    Lines beginning with the word "auto" are used to identify the physical
+    interfaces to be brought up when ifup is run with the -a option.
+    (This option is used by the system boot scripts.) Physical interface names
+    should follow the word "auto" on the same line. There can be multiple
+    "auto" stanzas.
+
+    Lines beginning with "allow-" are used to identify interfaces that
+    should be brought up automatically by various subsystems. This may be
+    done using a command such as "ifup --allow=hotplug  eth0 eth1", which
+    will only bring up eth0 or eth1 if it is listed in an "allow-hotplug"
+    line. Note that "allow-auto" and "auto" are synonyms.
+
+    Lines beginning with "source" are used to include stanzas from other
+    files, so configuration can be split into many files. The word "source"
+    is followed by the path of the file to be sourced. Shell wildcards can
+    be used. Currently only supports absolute path names.
+
+    iface is normally given a interface name as its first non-option
+    argument.
+
+    The interface name is followed by the name of the address family that the
+    interface uses. This will be "inet" for TCP/IP networking and inet6 for
+    ipv6. Following that is the name of the method used to configure the
+    interface.
+
+    ifupdown supports iface stanzas without a family or a method. This enables
+    using the same stanza for inet and inet6 family addresses. And the method
+    defaults to "static"
+
+    Additional interface options/attributes can be given on subsequent lines
+    in the iface stanza. These options come from addon modules. see
+    **ifupdown-addons-interfaces(5)** for these options.
+
+    example bridge interface with additional attributes listed in the
+    **ifupdown-addons-interfaces(5)** man page::
+
+        auto br0
+        iface br0
+            address 12.0.0.4/24
+            address 2000:1000:1000:1000:3::5/128
+            bridge-ports swp1 swp2 swp3
+            bridge-stp on
+
+    ifupdown supports python-mako style templates in the interfaces file.
+    See examples section for details.
+
+    See **/usr/share/doc/ifupdown2/examples/** for **interfaces(5)**
+    file examples and interfaces file generation scripts.
+
+METHODS
+=======
+    Both **inet** and **inet6** address family interfaces can use the following
+    methods (However they are not required):
+
+    The loopback Method
+           This method may be used to define the loopback interface.
+
+    The static Method
+           This method may be used to define ethernet interfaces with
+           statically allocated addresses.
+
+    The dhcp Method
+           This method may be used to obtain an address via DHCP.
+
+BUILTIN INTERFACES
+==================
+    **iface** sections for some interfaces like physical interfaces or vlan
+    interfaces in dot notation (like eth1.100) are understood by ifupdown.
+    These interfaces do not need an entry in the interfaces file if
+    they are dependents of other interfaces and don't need any specific
+    configurations like addresses etc.
+
+EXAMPLES
+========
+    Sample /etc/network/interfaces file::
+
+        auto lo
+        iface lo
+            address 192.168.2.0/24
+            address 2001:dee:eeee:1::4/128
+
+        auto eth0
+        iface eth0 inet dhcp
+
+        auto eth1
+        iface eth1 inet manual
+            address 192.168.2.0/24
+            address 2001:dee:eeee:1::4/128
+
+        # source files from a directory /etc/network/interfaces.d
+        source /etc/network/interfaces.d/*
+
+        # Using mako style templates
+        % for v in [11,12]:
+            auto vlan${v}
+            iface vlan${v} inet static
+                address 10.20.${v}.3/24
+        % endfor
+
+    For additional syntax and examples see **ifupdown-addons-interfaces(5)**
+
+FILES
+=====
+    configuration file defined in ifupdown2.conf (default /etc/network/interfaces)
+
+SEE ALSO
+========
+    ifupdown-addons-interfaces(5),
+    ifup(8),
+    ifquery(8),
+    ifreload(8)
diff --git a/ifupdown2/nlmanager/README b/ifupdown2/nlmanager/README
new file mode 100644 (file)
index 0000000..8adac90
--- /dev/null
@@ -0,0 +1,6 @@
+DO NOT EDIT NLMANAGER SOURCES.
+This is a mirror copy of python-nlmanager sources.
+It was extracted and directly included here to support some usecases where
+user don't have python-nlmanager already installed on their system. So we
+decided to have local copy and build with it. It is the mainter responsability
+to keep an updated version of nlmanager.
\ No newline at end of file
diff --git a/ifupdown2/nlmanager/__init__.py b/ifupdown2/nlmanager/__init__.py
new file mode 100644 (file)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
diff --git a/ifupdown2/nlmanager/nllistener.py b/ifupdown2/nlmanager/nllistener.py
new file mode 100644 (file)
index 0000000..e9c877e
--- /dev/null
@@ -0,0 +1,644 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2015, 2017 Cumulus Networks, Inc. all rights reserved
+#
+# 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 the Free Software Foundation; version 2.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# https://www.gnu.org/licenses/gpl-2.0-standalone.html
+#
+# Authors:
+#       Daniel Walton, dwalton@cumulusnetworks.com
+#       Julien Fortin, julien@cumulusnetworks.com
+#
+# Netlink Listener --
+#
+
+from nlpacket import *
+from nlmanager import NetlinkManager
+from select import select
+from struct import pack, unpack, calcsize
+from threading import Thread, Event, Lock
+from Queue import Queue
+import logging
+import socket
+import errno
+
+log = logging.getLogger(__name__)
+
+
+class NetlinkListener(Thread):
+
+    def __init__(self, manager, groups, pid_offset=1, error_notification=False, rcvbuf_sz=10000000):
+        """
+        groups controls what types of messages we are interested in hearing
+        To get everything pass:
+            RTMGRP_LINK | \
+            RTMGRP_IPV4_IFADDR | \
+            RTMGRP_IPV4_ROUTE | \
+            RTMGRP_IPV6_IFADDR | \
+            RTMGRP_IPV6_ROUTE
+        """
+        Thread.__init__(self)
+        self.manager = manager
+        self.shutdown_event = Event()
+        self.groups = groups
+        self.pid_offset = pid_offset
+        self.rcvbuf_sz = rcvbuf_sz
+
+        # if the app has requested for error notification socket errors will
+        # be sent via the SERVICE_ERROR event
+        self.error_notification = error_notification
+
+        self.supported_messages = [RTM_NEWLINK, RTM_DELLINK, RTM_NEWADDR,
+                                   RTM_DELADDR, RTM_NEWNEIGH, RTM_DELNEIGH,
+                                   RTM_NEWROUTE, RTM_DELROUTE]
+        self.ignore_messages = [RTM_GETLINK, RTM_GETADDR, RTM_GETNEIGH,
+                                RTM_GETROUTE, RTM_GETQDISC, NLMSG_ERROR, NLMSG_DONE]
+
+    def __str__(self):
+        return 'NetlinkListener'
+
+    def supported_messages_add(self, msgtype):
+
+        if msgtype not in self.supported_messages:
+            self.supported_messages.append(msgtype)
+
+        if msgtype in self.ignore_messages:
+            self.ignore_messages.remove(msgtype)
+
+    def supported_messages_del(self, msgtype):
+
+        if msgtype in self.supported_messages:
+            self.supported_messages.remove(msgtype)
+
+        if msgtype not in self.ignore_messages:
+            self.ignore_messages.append(msgtype)
+
+    def run(self):
+        manager = self.manager
+        header_PACK = 'IHHII'
+        header_LEN = calcsize(header_PACK)
+
+        # The RX socket is used to listen to all netlink messages that fly by
+        # as things change in the kernel. We need a very large SO_RCVBUF here
+        # else we tend to miss messages.
+        # PID_MAX_LIMIT is 2^22 allowing 1024 sockets per-pid. We default to 
+        # use 2 in the upper space (top 10 bits) instead of 0 to avoid conflicts
+        # with the netlink manager which always attempts to bind with the pid.
+        self.rx_socket = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, 0)
+        _SO_RCVBUFFORCE = socket.SO_RCVBUFFORCE if hasattr(socket, 'SO_RCVBUFFORCE') else 33
+        self.rx_socket.setsockopt(socket.SOL_SOCKET, _SO_RCVBUFFORCE, self.rcvbuf_sz)
+        self.rx_socket.bind((manager.pid | (self.pid_offset << 22), self.groups))
+        self.rx_socket_prev_seq = {}
+
+        manager.target_lock.acquire()
+        if not manager.tx_socket:
+            manager.tx_socket_allocate()
+        manager.target_lock.release()
+
+        my_sockets = (manager.tx_socket, self.rx_socket)
+
+        socket_string = {
+            manager.tx_socket: "TX",
+            self.rx_socket: "RX"
+        }
+
+        while True:
+
+            if self.shutdown_event.is_set():
+                log.info("%s: shutting down" % self)
+                break
+
+            # Only block for 1 second so we can wake up to see if shutdown_event is set
+            try:
+                (readable, writeable, exceptional) = select(my_sockets, [], my_sockets, 1)
+            except Exception as e:
+                log.error('select() error: ' + str(e))
+                continue
+
+            if not readable:
+                continue
+
+            set_alarm = False
+            set_overrun = False
+            set_tx_socket_rxed_ack_alarm = False
+
+            for s in readable:
+                data = []
+
+                try:
+                    data = s.recv(4096)
+                except socket.error, e:
+                    log.error('recv() error: ' + str(e))
+                    data = []
+                    if e.errno is errno.ENOBUFS and self.error_notification:
+                        set_overrun = True
+                except Exception as e:
+                    log.error('recv() error: ' + str(e))
+                    data = []
+
+                total_length = len(data)
+                while data:
+
+                    # Extract the length, etc from the header
+                    (length, msgtype, flags, seq, pid) = unpack(header_PACK, data[:header_LEN])
+
+                    log.debug('%s %s: RXed %s seq %d, pid %d, %d bytes (%d total)' %
+                              (self, socket_string[s], NetlinkPacket.type_to_string[msgtype],
+                               seq, pid, length, total_length))
+                    possible_ack = False
+
+                    if msgtype == NLMSG_DONE:
+                        possible_ack = True
+
+                    elif msgtype == NLMSG_ERROR:
+                        possible_ack = True
+
+                        # The error code is a signed negative number.
+                        error_code = abs(unpack('=i', data[header_LEN:header_LEN+4])[0])
+                        msg = Error(msgtype, True)
+                        msg.decode_packet(length, flags, seq, pid, data)
+
+                        if error_code:
+                            log.debug("%s %s: RXed NLMSG_ERROR code %s (%d)" % (self, socket_string[s], msg.error_to_string.get(error_code), error_code))
+
+                    if possible_ack and seq == manager.target_seq and pid == manager.target_pid:
+                        log.debug("%s %s: Setting RXed ACK alarm for seq %d, pid %d" %
+                                  (self, socket_string[s], seq, pid))
+                        set_tx_socket_rxed_ack_alarm = True
+
+                    # Put the message on the manager's netlinkq
+                    if msgtype in self.supported_messages:
+                        set_alarm = True
+                        manager.netlinkq.append((msgtype, length, flags, seq, pid, data[0:length]))
+
+                    # There are certain message types we do not care about
+                    # (RTM_GETs for example)
+                    elif msgtype in self.ignore_messages:
+                        pass
+
+                    # And there are certain message types we have not added
+                    # support for yet (QDISC). Log an error for these just
+                    # as a reminder to add support for them.
+                    else:
+                        if msgtype in NetlinkPacket.type_to_string:
+                            log.warning('%s %s: RXed unsupported message %s (type %d)' %
+                                        (self, socket_string[s], NetlinkPacket.type_to_string[msgtype], msgtype))
+                        else:
+                            log.warning('%s %s: RXed unknown message type %d' %
+                                        (self, socket_string[s], msgtype))
+
+                    # Track the previous PID sequence number for RX and TX sockets
+                    if s == self.rx_socket:
+                        prev_seq = self.rx_socket_prev_seq
+                    elif s == manager.tx_socket:
+                        prev_seq = manager.tx_socket_prev_seq
+
+                    if pid in prev_seq and prev_seq[pid] and prev_seq[pid] != seq and (prev_seq[pid]+1 != seq):
+                        log.debug('%s %s: went from seq %d to %d' % (self, socket_string[s], prev_seq[pid], seq))
+                    prev_seq[pid] = seq
+
+                    data = data[length:]
+
+            if set_tx_socket_rxed_ack_alarm:
+                manager.target_lock.acquire()
+                manager.target_seq = None
+                manager.target_pid = None
+                manager.target_lock.release()
+                manager.tx_socket_rxed_ack.set()
+
+            if set_alarm:
+                manager.workq.put(('SERVICE_NETLINK_QUEUE', None))
+
+            if set_overrun:
+                manager.workq.put(('SERVICE_ERROR', "OVERFLOW"))
+
+            if set_alarm or set_overrun:
+                manager.alarm.set()
+
+        self.rx_socket.close()
+
+
+class NetlinkManagerWithListener(NetlinkManager):
+
+    def __init__(self, groups, start_listener=True, use_color=True, pid_offset=0, error_notification=False, rcvbuf_sz=10000000):
+        NetlinkManager.__init__(self, use_color=use_color, pid_offset=pid_offset)
+        self.groups = groups
+        self.workq = Queue()
+        self.netlinkq = []
+        self.alarm = Event()
+        self.shutdown_event = Event()
+        self.tx_socket_rxed_ack = Event()
+        self.tx_socket_rxed_ack.clear()
+        self.target_seq = None
+        self.target_pid = None
+        self.target_seq_pid_debug = False
+        self.target_lock = Lock()
+        self.tx_socket_prev_seq = {}
+        self.debug_listener = False
+        self.debug_seq_pid = {}
+        self.ifname_by_index = {}
+        self.blacklist_filter = {}
+        self.whitelist_filter = {}
+        self.rcvbuf_sz = rcvbuf_sz
+        self.error_notification = error_notification
+        self.pid_offset = pid_offset
+
+        # Listen to netlink messages
+        if start_listener:
+            self.restart_listener()
+        else:
+            self.listener = None
+
+    def __str__(self):
+        return 'NetlinkManagerWithListener'
+
+    def restart_listener(self):
+        self.listener = NetlinkListener(self, self.groups, self.pid_offset+1, self.error_notification, self.rcvbuf_sz)
+        self.listener.start()
+
+    def signal_term_handler(self, signal, frame):
+        log.info("NetlinkManagerWithListener: Caught SIGTERM")
+
+        if self.listener:
+            self.listener.shutdown_event.set()
+
+        self.shutdown_flag = True  # For NetlinkManager shutdown
+        self.shutdown_event.set()
+        self.alarm.set()
+
+    def signal_int_handler(self, signal, frame):
+        log.info("NetlinkManagerWithListener: Caught SIGINT")
+
+        if self.listener:
+            self.listener.shutdown_event.set()
+
+        self.shutdown_flag = True  # For NetlinkManager shutdown
+        self.shutdown_event.set()
+        self.alarm.set()
+
+    def tx_nlpacket_get_response(self, nlpacket):
+        """
+        TX the message and wait for an ack
+        """
+
+        # NetlinkListener looks at the manager's target_seq and target_pid
+        # to know when we've RXed the ack that we want
+        self.target_lock.acquire()
+        self.target_seq = nlpacket.seq
+        self.target_pid = nlpacket.pid
+
+        if not self.tx_socket:
+            self.tx_socket_allocate()
+        self.target_lock.release()
+
+        log.debug('%s TX: TXed %s seq %d, pid %d, %d bytes' %
+                   (self,  NetlinkPacket.type_to_string[nlpacket.msgtype],
+                    nlpacket.seq, nlpacket.pid, nlpacket.length))
+
+        self.tx_socket.sendall(nlpacket.message)
+
+        # Wait for NetlinkListener to RX an ACK or DONE for this (seq, pid)
+        self.tx_socket_rxed_ack.wait()
+        self.tx_socket_rxed_ack.clear()
+
+    # These are here to show some basic examples of how one might react to RXing
+    # various netlink message types. Odds are our child class will redefine these
+    # to do more than log a message.
+    def rx_rtm_newlink(self, msg):
+        log.debug("RXed RTM_NEWLINK seq %d, pid %d, %d bytes, for %s, state %s" %
+                  (msg.seq, msg.pid, msg.length, msg.get_attribute_value(msg.IFLA_IFNAME), "up" if msg.is_up() else "down"))
+
+    def rx_rtm_dellink(self, msg):
+        log.debug("RXed RTM_DELLINK seq %d, pid %d, %d bytes, for %s, state %s" %
+                  (msg.seq, msg.pid, msg.length, msg.get_attribute_value(msg.IFLA_IFNAME), "up" if msg.is_up() else "down"))
+
+    def rx_rtm_newaddr(self, msg):
+        log.debug("RXed RTM_NEWADDR seq %d, pid %d, %d bytes, for %s/%d on %s" %
+                  (msg.seq, msg.pid, msg.length, msg.get_attribute_value(msg.IFA_ADDRESS), msg.prefixlen, self.ifname_by_index.get(msg.ifindex)))
+
+    def rx_rtm_deladdr(self, msg):
+        log.debug("RXed RTM_DELADDR seq %d, pid %d, %d bytes, for %s/%d on %s" %
+                  (msg.seq, msg.pid, msg.length, msg.get_attribute_value(msg.IFA_ADDRESS), msg.prefixlen, self.ifname_by_index.get(msg.ifindex)))
+
+    def rx_rtm_newneigh(self, msg):
+        log.debug("RXed RTM_NEWNEIGH seq %d, pid %d, %d bytes, for %s on %s" %
+                  (msg.seq, msg.pid, msg.length, msg.get_attribute_value(msg.NDA_DST), self.ifname_by_index.get(msg.ifindex)))
+
+    def rx_rtm_delneigh(self, msg):
+        log.debug("RXed RTM_DELNEIGH seq %d, pid %d, %d bytes, for %s on %s" %
+                  (msg.seq, msg.pid, msg.length, msg.get_attribute_value(msg.NDA_DST), self.ifname_by_index.get(msg.ifindex)))
+
+    def rx_rtm_newroute(self, msg):
+        log.debug("RXed RTM_NEWROUTE seq %d, pid %d, %d bytes, for %s%s" %
+                  (msg.seq, msg.pid, msg.length, msg.get_prefix_string(), msg.get_nexthops_string(self.ifname_by_index)))
+
+    def rx_rtm_delroute(self, msg):
+        log.debug("RXed RTM_DELROUTE seq %d, pid %d, %d bytes, for %s%s" %
+                  (msg.seq, msg.pid, msg.length, msg.get_prefix_string(), msg.get_nexthops_string(self.ifname_by_index)))
+
+    def rx_nlmsg_done(self, msg):
+        log.debug("RXed NLMSG_DONE seq %d, pid %d, %d bytes" % (msg.seq, msg.pid, msg.length))
+
+    # Note that tx_nlpacket_get_response will block until NetlinkListener has RXed
+    # an Ack/DONE for the message we TXed
+    def get_all_addresses(self):
+        family = socket.AF_UNSPEC
+        debug = RTM_GETADDR in self.debug
+
+        addr = Address(RTM_GETADDR, debug, use_color=self.use_color)
+        addr.flags = NLM_F_REQUEST | NLM_F_DUMP
+        addr.body = pack('Bxxxi', family, 0)
+        addr.build_message(self.sequence.next(), self.pid)
+
+        if debug:
+            self.debug_seq_pid[(addr.seq, addr.pid)] = True
+
+        self.tx_nlpacket_get_response(addr)
+
+    def get_all_links(self):
+        family = socket.AF_UNSPEC
+        debug = RTM_GETLINK in self.debug
+
+        link = Link(RTM_GETLINK, debug, use_color=self.use_color)
+        link.flags = NLM_F_REQUEST | NLM_F_DUMP
+        link.body = pack('Bxxxiii', family, 0, 0, 0)
+        link.build_message(self.sequence.next(), self.pid)
+
+        if debug:
+            self.debug_seq_pid[(link.seq, link.pid)] = True
+
+        self.tx_nlpacket_get_response(link)
+
+    def get_all_br_links(self, compress_vlans=True):
+        family = socket.AF_BRIDGE
+        debug = RTM_GETLINK in self.debug
+
+        link = Link(RTM_GETLINK, debug, use_color=self.use_color)
+        link.flags = NLM_F_REQUEST | NLM_F_DUMP
+        link.body = pack('Bxxxiii', family, 0, 0, 0)
+        if compress_vlans:
+            link.add_attribute(Link.IFLA_EXT_MASK, Link.RTEXT_FILTER_BRVLAN_COMPRESSED)
+        else:
+            link.add_attribute(Link.IFLA_EXT_MASK, Link.RTEXT_FILTER_BRVLAN)
+        link.build_message(self.sequence.next(), self.pid)
+
+        if debug:
+            self.debug_seq_pid[(link.seq, link.pid)] = True
+
+        self.tx_nlpacket_get_response(link)
+
+    def get_all_neighbors(self):
+        family = socket.AF_UNSPEC
+        debug = RTM_GETNEIGH in self.debug
+
+        neighbor = Neighbor(RTM_GETNEIGH, debug, use_color=self.use_color)
+        neighbor.flags = NLM_F_REQUEST | NLM_F_DUMP
+        neighbor.body = pack('Bxxxii', family, 0, 0)
+        neighbor.build_message(self.sequence.next(), self.pid)
+
+        if debug:
+            self.debug_seq_pid[(neighbor.seq, neighbor.pid)] = True
+
+        self.tx_nlpacket_get_response(neighbor)
+
+    def get_all_routes(self):
+        family = socket.AF_UNSPEC
+        debug = RTM_GETROUTE in self.debug
+
+        route = Route(RTM_GETROUTE, debug, use_color=self.use_color)
+        route.flags = NLM_F_REQUEST | NLM_F_DUMP
+        route.body = pack('Bxxxii', family, 0, 0)
+        route.build_message(self.sequence.next(), self.pid)
+
+        if debug:
+            self.debug_seq_pid[(route.seq, route.pid)] = True
+
+        self.tx_nlpacket_get_response(route)
+
+    def nested_attributes_match(self, msg, attr_filter):
+        """
+        attr_filter will be a dictionary such as:
+        attr_filter = {
+            Link.IFLA_LINKINFO: {
+                Link.IFLA_INFO_KIND: 'vlan'
+            }
+        }
+        """
+        for (key, value) in attr_filter.items():
+            if type(value) is dict:
+                if not self.nested_attributes_match(msg, value):
+                    return False
+            else:
+                attr_value = msg.get_attribute_value(key)
+                if attr_value != value:
+                    return False
+        return True
+
+    def filter_rule_matches(self, msg, rule):
+        field = rule[0]
+        options = rule[1:]
+
+        if field == 'IFINDEX':
+            ifindex = options[0]
+
+            if msg.ifindex == ifindex:
+                return True
+
+        elif field == 'ATTRIBUTE':
+            (attr_type, target_value) = options[0:2]
+            attr_value = msg.get_attribute_value(attr_type)
+
+            if attr_value == target_value:
+                return True
+
+        elif field == 'NESTED_ATTRIBUTE':
+            if self.nested_attributes_match(msg, options[0]):
+                return True
+
+        elif field == 'FAMILY':
+            family = options[0]
+
+            if msg.family == family:
+                return True
+        else:
+            raise Exception("Add support to filter based on %s" % field)
+
+        return False
+
+    def filter_permit(self, msg):
+        """
+        Return True if our whitelist/blacklist filters permit this netlink msg
+        """
+        if msg.msgtype in self.whitelist_filter:
+            found_it = False
+
+            for rule in self.whitelist_filter[msg.msgtype]:
+                if self.filter_rule_matches(msg, rule):
+                    found_it = True
+                    break
+
+            return found_it
+
+        elif msg.msgtype in self.blacklist_filter:
+            for rule in self.blacklist_filter[msg.msgtype]:
+                if self.filter_rule_matches(msg, rule):
+                    return False
+            return True
+
+        else:
+            return True
+
+    def _filter_update(self, add, filter_type, msgtype, filter_guts):
+        assert filter_type in ('whitelist', 'blacklist'), "whitelist and blacklist are the only supported filter options"
+
+        if add:
+            if filter_type == 'whitelist':
+
+                # Keep things simple, do not allow both whitelist and blacklist
+                if self.blacklist_filter and self.blacklist_filter.get(msgtype):
+                    raise Exception("whitelist and blacklist filters cannot be used at the same time")
+
+                if msgtype not in self.whitelist_filter:
+                    self.whitelist_filter[msgtype] = []
+                self.whitelist_filter[msgtype].append(filter_guts)
+
+            elif filter_type == 'blacklist':
+
+                # Keep things simple, do not allow both whitelist and blacklist
+                if self.whitelist_filter and self.whitelist_filter.get(msgtype):
+                    raise Exception("whitelist and blacklist filters cannot be used at the same time")
+
+                if msgtype not in self.blacklist_filter:
+                    self.blacklist_filter[msgtype] = []
+                self.blacklist_filter[msgtype].append(filter_guts)
+
+        else:
+            if filter_type == 'whitelist':
+                if msgtype in self.whitelist_filter:
+                    self.whitelist_filter[msgtype].remove(filter_guts)
+
+                    if not self.whitelist_filter[msgtype]:
+                        del self.whitelist_filter[msgtype]
+
+            elif filter_type == 'blacklist':
+                if msgtype in self.blacklist_filter:
+                    self.blacklist_filter[msgtype].remove(filter_guts)
+
+                    if not self.blacklist_filter[msgtype]:
+                        del self.blacklist_filter[msgtype]
+
+    def filter_by_address_family(self, add, filter_type, msgtype, family):
+        self._filter_update(add, filter_type, msgtype, ('FAMILY', family))
+
+    def filter_by_ifindex(self, add, filter_type, msgtype, ifindex):
+        self._filter_update(add, filter_type, msgtype, ('IFINDEX', ifindex))
+
+    def filter_by_attribute(self, add, filter_type, msgtype, attribute, attribute_value):
+        self._filter_update(add, filter_type, msgtype, ('ATTRIBUTE', attribute, attribute_value))
+
+    def filter_by_nested_attribute(self, add, filter_type, msgtype, attr_filter):
+        self._filter_update(add, filter_type, msgtype, ('NESTED_ATTRIBUTE', attr_filter))
+
+    def service_netlinkq(self):
+        msg_count = {}
+        processed = 0
+
+        for (msgtype, length, flags, seq, pid, data) in self.netlinkq:
+            processed += 1
+
+            # If this is a reply to a TX message that debugs were enabled for then debug the reply
+            if (seq, pid) in self.debug_seq_pid:
+                debug = True
+            else:
+                debug = self.debug_this_packet(msgtype)
+
+            if msgtype == RTM_NEWLINK or msgtype == RTM_DELLINK:
+                msg = Link(msgtype, debug, use_color=self.use_color)
+
+            elif msgtype == RTM_NEWADDR or msgtype == RTM_DELADDR:
+                msg = Address(msgtype, debug, use_color=self.use_color)
+
+            elif msgtype == RTM_NEWNEIGH or msgtype == RTM_DELNEIGH:
+                msg = Neighbor(msgtype, debug, use_color=self.use_color)
+
+            elif msgtype == RTM_NEWROUTE or msgtype == RTM_DELROUTE:
+                msg = Route(msgtype, debug, use_color=self.use_color)
+
+            elif msgtype == NLMSG_DONE:
+                msg = Done(msgtype, debug, use_color=self.use_color)
+
+            else:
+                log.warning('RXed unknown netlink message type %s' % msgtype)
+                continue
+
+            msg.decode_packet(length, flags, seq, pid, data)
+
+            if not self.filter_permit(msg):
+                continue
+
+            if debug:
+                msg.dump()
+
+            # Only used for printing debugs about how many we RXed of each type
+            if msg.msgtype not in msg_count:
+                msg_count[msg.msgtype] = 0
+            msg_count[msg.msgtype] += 1
+
+            # Call the appropriate handler method based on the msgtype.  The handler
+            # functions are defined in our child class.
+            if msg.msgtype == RTM_NEWLINK:
+
+                # We will use ifname_by_index to display the interface name in debug output
+                self.ifname_by_index[msg.ifindex] = msg.get_attribute_value(msg.IFLA_IFNAME)
+                self.rx_rtm_newlink(msg)
+
+            elif msg.msgtype == RTM_DELLINK:
+
+                # We will use ifname_by_index to display the interface name in debug output
+                if msg.ifindex in self.ifname_by_index:
+                    del self.ifname_by_index[msg.ifindex]
+                self.rx_rtm_dellink(msg)
+
+            elif msg.msgtype == RTM_NEWADDR:
+                self.rx_rtm_newaddr(msg)
+
+            elif msg.msgtype == RTM_DELADDR:
+                self.rx_rtm_deladdr(msg)
+
+            elif msg.msgtype == RTM_NEWNEIGH:
+                self.rx_rtm_newneigh(msg)
+
+            elif msg.msgtype == RTM_DELNEIGH:
+                self.rx_rtm_delneigh(msg)
+
+            elif msg.msgtype == RTM_NEWROUTE:
+                self.rx_rtm_newroute(msg)
+
+            elif msg.msgtype == RTM_DELROUTE:
+                self.rx_rtm_delroute(msg)
+
+            elif msg.msgtype == NLMSG_DONE:
+                self.rx_nlmsg_done(msg)
+
+            else:
+                log.warning('RXed unknown netlink message type %s' % msgtype)
+
+        if processed:
+            self.netlinkq = self.netlinkq[processed:]
+
+        # too chatty
+        # for msgtype in msg_count:
+        #     log.debug('RXed %d %s messages' % (msg_count[msgtype], NetlinkPacket.type_to_string[msgtype]))
diff --git a/ifupdown2/nlmanager/nlmanager.py b/ifupdown2/nlmanager/nlmanager.py
new file mode 100644 (file)
index 0000000..e4a5892
--- /dev/null
@@ -0,0 +1,1017 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2015, 2017 Cumulus Networks, Inc. all rights reserved
+#
+# 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 the Free Software Foundation; version 2.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+# https://www.gnu.org/licenses/gpl-2.0-standalone.html
+#
+# Authors:
+#       Daniel Walton, dwalton@cumulusnetworks.com
+#       Julien Fortin, julien@cumulusnetworks.com
+#
+# Netlink Manager --
+#
+
+from collections import OrderedDict
+from ipaddr import IPv4Address, IPv6Address
+from nlpacket import *
+from select import select
+from struct import pack, unpack
+import logging
+import os
+import socket
+
+log = logging.getLogger(__name__)
+
+
+class NetlinkError(Exception):
+    pass
+
+
+class NetlinkNoAddressError(NetlinkError):
+    pass
+
+
+class NetlinkInterruptedSystemCall(NetlinkError):
+    pass
+
+
+class InvalidInterfaceNameVlanCombo(Exception):
+    pass
+
+
+class Sequence(object):
+
+    def __init__(self):
+        self._next = 0
+
+    def next(self):
+        self._next += 1
+        return self._next
+
+
+class NetlinkManager(object):
+
+    def __init__(self, pid_offset=0, use_color=True, log_level=None):
+        # PID_MAX_LIMIT is 2^22 allowing 1024 sockets per-pid. We default to 0
+        # in the upper space (top 10 bits), which will simply be the PID. Other
+        # NetlinkManager instantiations in the same process can choose other
+        # offsets to avoid conflicts with each other.
+        self.pid = os.getpid() | (pid_offset << 22)
+        self.sequence = Sequence()
+        self.shutdown_flag = False
+        self.ifindexmap = {}
+        self.tx_socket = None
+        self.use_color = use_color
+
+        # debugs
+        self.debug = {}
+        self.debug_link(False)
+        self.debug_address(False)
+        self.debug_neighbor(False)
+        self.debug_route(False)
+
+        if log_level:
+            log.setLevel(log_level)
+            set_log_level(log_level)
+
+    def __str__(self):
+        return 'NetlinkManager'
+
+    def signal_term_handler(self, signal, frame):
+        log.info("NetlinkManager: Caught SIGTERM")
+        self.shutdown_flag = True
+
+    def signal_int_handler(self, signal, frame):
+        log.info("NetlinkManager: Caught SIGINT")
+        self.shutdown_flag = True
+
+    def shutdown(self):
+        if self.tx_socket:
+            self.tx_socket.close()
+            self.tx_socket = None
+        log.info("NetlinkManager: shutdown complete")
+
+    def _debug_set_clear(self, msg_types, enabled):
+        """
+        Enable or disable debugs for all msgs_types messages
+        """
+
+        for x in msg_types:
+            if enabled:
+                self.debug[x] = True
+            else:
+                if x in self.debug:
+                    del self.debug[x]
+
+    def debug_link(self, enabled):
+        self._debug_set_clear((RTM_NEWLINK, RTM_DELLINK, RTM_GETLINK, RTM_SETLINK), enabled)
+
+    def debug_address(self, enabled):
+        self._debug_set_clear((RTM_NEWADDR, RTM_DELADDR, RTM_GETADDR), enabled)
+
+    def debug_neighbor(self, enabled):
+        self._debug_set_clear((RTM_NEWNEIGH, RTM_DELNEIGH, RTM_GETNEIGH), enabled)
+
+    def debug_route(self, enabled):
+        self._debug_set_clear((RTM_NEWROUTE, RTM_DELROUTE, RTM_GETROUTE), enabled)
+
+    def debug_this_packet(self, mtype):
+        if mtype in self.debug:
+            return True
+        return False
+
+    def tx_socket_allocate(self):
+        """
+        The TX socket is used for install requests, sending RTM_GETXXXX
+        requests, etc
+        """
+        self.tx_socket = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, 0)
+        self.tx_socket.bind((self.pid, 0))
+
+    def tx_nlpacket_raw(self, message):
+        """
+        TX a bunch of concatenated nlpacket.messages....do NOT wait for an ACK
+        """
+        if not self.tx_socket:
+            self.tx_socket_allocate()
+        self.tx_socket.sendall(message)
+
+    def tx_nlpacket(self, nlpacket):
+        """
+        TX a netlink packet but do NOT wait for an ACK
+        """
+        if not nlpacket.message:
+            log.error('You must first call build_message() to create the packet')
+            return
+
+        if not self.tx_socket:
+            self.tx_socket_allocate()
+        self.tx_socket.sendall(nlpacket.message)
+
+    def tx_nlpacket_get_response(self, nlpacket):
+
+        if not nlpacket.message:
+            log.error('You must first call build_message() to create the packet')
+            return
+
+        if not self.tx_socket:
+            self.tx_socket_allocate()
+        self.tx_socket.sendall(nlpacket.message)
+
+        # If nlpacket.debug is True we already printed the following in the
+        # build_message() call...so avoid printing two messages for one packet.
+        if not nlpacket.debug:
+            log.debug("TXed %12s, pid %d, seq %d, %d bytes" %
+                     (nlpacket.get_type_string(), nlpacket.pid, nlpacket.seq, nlpacket.length))
+
+        header_PACK = NetlinkPacket.header_PACK
+        header_LEN = NetlinkPacket.header_LEN
+        null_read = 0
+        nle_intr_count = 0
+        MAX_NULL_READS = 3
+        MAX_ERROR_NLE_INTR = 3
+        msgs = []
+
+        # Now listen to our socket and wait for the reply
+        while True:
+
+            if self.shutdown_flag:
+                log.info('shutdown flag is True, exiting')
+                return msgs
+
+            # Only block for 1 second so we can wake up to see if self.shutdown_flag is True
+            try:
+                (readable, writeable, exceptional) = select([self.tx_socket, ], [], [self.tx_socket, ], 1)
+            except Exception as e:
+                # 4 is Interrupted system call
+                if isinstance(e.args, tuple) and e[0] == 4:
+                    nle_intr_count += 1
+                    log.info("select() Interrupted system call %d/%d" % (nle_intr_count, MAX_ERROR_NLE_INTR))
+
+                    if nle_intr_count >= MAX_ERROR_NLE_INTR:
+                        raise NetlinkInterruptedSystemCall(error_str)
+                    else:
+                        continue
+                else:
+                    raise
+
+            if readable:
+                null_read = 0
+            else:
+                null_read += 1
+
+                # Safety net to make sure we do not spend too much time in
+                # this while True loop
+                if null_read >= MAX_NULL_READS:
+                    log.info('Socket was not readable for %d attempts' % null_read)
+                    return msgs
+                else:
+                    continue
+
+            for s in readable:
+                data = []
+
+                try:
+                    data = s.recv(4096)
+                except Exception as e:
+                    # 4 is Interrupted system call
+                    if isinstance(e.args, tuple) and e[0] == 4:
+                        nle_intr_count += 1
+                        log.info("%s: recv() Interrupted system call %d/%d" % (s, nle_intr_count, MAX_ERROR_NLE_INTR))
+
+                        if nle_intr_count >= MAX_ERROR_NLE_INTR:
+                            raise NetlinkInterruptedSystemCall(error_str)
+                        else:
+                            continue
+                    else:
+                        raise
+
+                if not data:
+                    log.info('RXed zero length data, the socket is closed')
+                    return msgs
+
+                while data:
+
+                    # Extract the length, etc from the header
+                    (length, msgtype, flags, seq, pid) = unpack(header_PACK, data[:header_LEN])
+
+                    debug_str = "RXed %12s, pid %d, seq %d, %d bytes" % (NetlinkPacket.type_to_string[msgtype], pid, seq, length)
+
+                    # This shouldn't happen but it would be nice to be aware of it if it does
+                    if pid != nlpacket.pid:
+                        log.debug(debug_str + '...we are not interested in this pid %s since ours is %s' %
+                                    (pid, nlpacket.pid))
+                        data = data[length:]
+                        continue
+
+                    if seq != nlpacket.seq:
+                        log.debug(debug_str + '...we are not interested in this seq %s since ours is %s' %
+                                    (seq, nlpacket.seq))
+                        data = data[length:]
+                        continue
+
+                    # See if we RXed an ACK for our RTM_GETXXXX
+                    if msgtype == NLMSG_DONE:
+                        log.debug(debug_str + '...this is an ACK')
+                        return msgs
+
+                    elif msgtype == NLMSG_ERROR:
+
+                        # The error code is a signed negative number.
+                        error_code = abs(unpack('=i', data[header_LEN:header_LEN+4])[0])
+                        msg = Error(msgtype, nlpacket.debug)
+                        msg.decode_packet(length, flags, seq, pid, data)
+
+                        # 0 is NLE_SUCCESS...everything else is a true error
+                        if error_code:
+                            error_code_str = msg.error_to_string.get(error_code)
+                            if error_code_str:
+                                error_str = 'Operation failed with \'%s\'' % error_code_str
+                            else:
+                                error_str = 'Operation failed with code %s' % error_code
+
+                            log.debug(debug_str)
+
+                            if error_code == Error.NLE_NOADDR:
+                                raise NetlinkNoAddressError(error_str)
+                            elif error_code == Error.NLE_INTR:
+                                nle_intr_count += 1
+                                log.debug("%s: RXed NLE_INTR Interrupted system call %d/%d" % (s, nle_intr_count, MAX_ERROR_NLE_INTR))
+
+                                if nle_intr_count >= MAX_ERROR_NLE_INTR:
+                                    raise NetlinkInterruptedSystemCall(error_str)
+
+                            else:
+                                msg.dump()
+                                if not error_code_str:
+                                    try:
+                                        # os.strerror might raise ValueError
+                                        strerror = os.strerror(error_code)
+                                        if strerror:
+                                            raise NetlinkError('Operation failed with \'%s\'' % strerror)
+                                        else:
+                                            raise NetlinkError(error_str)
+                                    except ValueError:
+                                        pass
+                                raise NetlinkError(error_str)
+                        else:
+                            log.debug('%s code NLE_SUCCESS...this is an ACK' % debug_str)
+                            return msgs
+
+                    # No ACK...create a nlpacket object and append it to msgs
+                    else:
+                        nle_intr_count = 0
+
+                        if msgtype == RTM_NEWLINK or msgtype == RTM_DELLINK:
+                            msg = Link(msgtype, nlpacket.debug, use_color=self.use_color)
+
+                        elif msgtype == RTM_NEWADDR or msgtype == RTM_DELADDR:
+                            msg = Address(msgtype, nlpacket.debug, use_color=self.use_color)
+
+                        elif msgtype == RTM_NEWNEIGH or msgtype == RTM_DELNEIGH:
+                            msg = Neighbor(msgtype, nlpacket.debug, use_color=self.use_color)
+
+                        elif msgtype == RTM_NEWROUTE or msgtype == RTM_DELROUTE:
+                            msg = Route(msgtype, nlpacket.debug, use_color=self.use_color)
+
+                        else:
+                            raise Exception("RXed unknown netlink message type %s" % msgtype)
+
+                        msg.decode_packet(length, flags, seq, pid, data)
+                        msgs.append(msg)
+
+                        if nlpacket.debug:
+                            msg.dump()
+
+                    data = data[length:]
+
+    def ip_to_afi(self, ip):
+        type_ip = type(ip)
+
+        if type_ip == IPv4Address:
+            return socket.AF_INET
+        elif type_ip == IPv6Address:
+            return socket.AF_INET6
+        else:
+            raise Exception("%s is an invalid IP type" % type_ip)
+
+    def request_dump(self, rtm_type, family, debug):
+        """
+        Issue a RTM_GETROUTE, etc with the NLM_F_DUMP flag
+        set and return the results
+        """
+
+        if rtm_type == RTM_GETADDR:
+            msg = Address(rtm_type, debug, use_color=self.use_color)
+            msg.body = pack('Bxxxi', family, 0)
+
+        elif rtm_type == RTM_GETLINK:
+            msg = Link(rtm_type, debug, use_color=self.use_color)
+            msg.body = pack('Bxxxiii', family, 0, 0, 0)
+
+        elif rtm_type == RTM_GETNEIGH:
+            msg = Neighbor(rtm_type, debug, use_color=self.use_color)
+            msg.body = pack('Bxxxii', family, 0, 0)
+
+        elif rtm_type == RTM_GETROUTE:
+            msg = Route(rtm_type, debug, use_color=self.use_color)
+            msg.body = pack('Bxxxii', family, 0, 0)
+
+        else:
+            log.error("request_dump RTM_GET %s is not supported" % rtm_type)
+            return None
+
+        msg.flags = NLM_F_REQUEST | NLM_F_DUMP
+        msg.attributes = {}
+        msg.build_message(self.sequence.next(), self.pid)
+        return self.tx_nlpacket_get_response(msg)
+
+    # ======
+    # Routes
+    # ======
+    def _routes_add_or_delete(self, add_route, routes, ecmp_routes, table, protocol, route_scope, route_type):
+
+        def tx_or_concat_message(total_message, route):
+            """
+            Adding an ipv4 route only takes 60 bytes, if we are adding thousands
+            of them this can add up to a lot of send calls.  Concat several of
+            them together before TXing.
+            """
+
+            if not total_message:
+                total_message = route.message
+            else:
+                total_message += route.message
+
+            if len(total_message) >= PACKET_CONCAT_SIZE:
+                self.tx_nlpacket_raw(total_message)
+                total_message = None
+
+            return total_message
+
+        if add_route:
+            rtm_command = RTM_NEWROUTE
+        else:
+            rtm_command = RTM_DELROUTE
+
+        total_message = None
+        PACKET_CONCAT_SIZE = 16384
+        debug = rtm_command in self.debug
+
+        if routes:
+            for (afi, ip, mask, nexthop, interface_index) in routes:
+                route = Route(rtm_command, debug, use_color=self.use_color)
+                route.flags = NLM_F_REQUEST | NLM_F_CREATE
+                route.body = pack('BBBBBBBBi', afi, mask, 0, 0, table, protocol,
+                                  route_scope, route_type, 0)
+                route.family = afi
+                route.add_attribute(Route.RTA_DST, ip)
+                if nexthop:
+                    route.add_attribute(Route.RTA_GATEWAY, nexthop)
+                route.add_attribute(Route.RTA_OIF, interface_index)
+                route.build_message(self.sequence.next(), self.pid)
+                total_message = tx_or_concat_message(total_message, route)
+
+            if total_message:
+                self.tx_nlpacket_raw(total_message)
+
+        if ecmp_routes:
+
+            for (route_key, value) in ecmp_routes.iteritems():
+                (afi, ip, mask) = route_key
+
+                route = Route(rtm_command, debug, use_color=self.use_color)
+                route.flags = NLM_F_REQUEST | NLM_F_CREATE
+                route.body = pack('BBBBBBBBi', afi, mask, 0, 0, table, protocol,
+                                  route_scope, route_type, 0)
+                route.family = afi
+                route.add_attribute(Route.RTA_DST, ip)
+                route.add_attribute(Route.RTA_MULTIPATH, value)
+                route.build_message(self.sequence.next(), self.pid)
+                total_message = tx_or_concat_message(total_message, route)
+
+            if total_message:
+                self.tx_nlpacket_raw(total_message)
+
+    def routes_add(self, routes, ecmp_routes,
+                   table=Route.RT_TABLE_MAIN,
+                   protocol=Route.RT_PROT_XORP,
+                   route_scope=Route.RT_SCOPE_UNIVERSE,
+                   route_type=Route.RTN_UNICAST):
+        self._routes_add_or_delete(True, routes, ecmp_routes, table, protocol, route_scope, route_type)
+
+    def routes_del(self, routes, ecmp_routes,
+                   table=Route.RT_TABLE_MAIN,
+                   protocol=Route.RT_PROT_XORP,
+                   route_scope=Route.RT_SCOPE_UNIVERSE,
+                   route_type=Route.RTN_UNICAST):
+        self._routes_add_or_delete(False, routes, ecmp_routes, table, protocol, route_scope, route_type)
+
+    def route_get(self, ip, debug=False):
+        """
+        ip must be one of the following:
+        - IPv4Address
+        - IPv6Address
+        """
+        # Transmit a RTM_GETROUTE to query for the route we want
+        route = Route(RTM_GETROUTE, debug, use_color=self.use_color)
+        route.flags = NLM_F_REQUEST | NLM_F_ACK
+
+        # Set everything in the service header as 0 other than the afi
+        afi = self.ip_to_afi(ip)
+        route.body = pack('Bxxxxxxxi', afi, 0)
+        route.family = afi
+        route.add_attribute(Route.RTA_DST, ip)
+        route.build_message(self.sequence.next(), self.pid)
+        return self.tx_nlpacket_get_response(route)
+
+    def routes_dump(self, family=socket.AF_UNSPEC, debug=True):
+        return self.request_dump(RTM_GETROUTE, family, debug)
+
+    def routes_print(self, routes):
+        """
+        Print a table of 'routes'
+        """
+        print "Prefix            Nexthop           ifindex"
+
+        for x in routes:
+            if Route.RTA_DST not in x.attributes:
+                log.warning("Route is missing RTA_DST")
+                continue
+
+            ip = "%s/%d" % (x.attributes[Route.RTA_DST].value, x.src_len)
+            print "%-15s   %-15s   %s" %\
+                (ip,
+                 str(x.attributes[Route.RTA_GATEWAY].value) if Route.RTA_GATEWAY in x.attributes else None,
+                 x.attributes[Route.RTA_OIF].value)
+
+    # =====
+    # Links
+    # =====
+    def _get_iface_by_name(self, ifname):
+        """
+        Return a Link object for ifname
+        """
+        debug = RTM_GETLINK in self.debug
+
+        link = Link(RTM_GETLINK, debug, use_color=self.use_color)
+        link.flags = NLM_F_REQUEST | NLM_F_ACK
+        link.body = pack('=Bxxxiii', socket.AF_UNSPEC, 0, 0, 0)
+        link.add_attribute(Link.IFLA_IFNAME, ifname)
+        link.build_message(self.sequence.next(), self.pid)
+
+        try:
+            return self.tx_nlpacket_get_response(link)[0]
+
+        except NetlinkNoAddressError:
+            log.info("Netlink did not find interface %s" % ifname)
+            return None
+
+    def _get_iface_by_index(self, ifindex):
+        """
+        Return a Link object for ifindex
+        """
+        debug = RTM_GETLINK in self.debug
+
+        link = Link(RTM_GETLINK, debug, use_color=self.use_color)
+        link.flags = NLM_F_REQUEST | NLM_F_ACK
+        link.body = pack('=Bxxxiii', socket.AF_UNSPEC, ifindex, 0, 0)
+        link.build_message(self.sequence.next(), self.pid)
+        try:
+            return self.tx_nlpacket_get_response(link)[0]
+        except NetlinkNoAddressError:
+            log.info("Netlink did not find interface %s" % ifindex)
+            return None
+
+    def get_iface_index(self, ifname):
+        """
+        Return the interface index for ifname
+        """
+        iface = self._get_iface_by_name(ifname)
+
+        if iface:
+            return iface.ifindex
+        return None
+
+    def get_iface_name(self, ifindex):
+        iface = self._get_iface_by_index(ifindex)
+
+        if iface:
+            return iface.attributes[Link.IFLA_IFNAME].get_pretty_value(str)
+        return None
+
+    def link_dump(self, ifname=None):
+        msg = Link(RTM_GETLINK, False, use_color=self.use_color)
+        msg.body = pack('Bxxxiii', socket.AF_UNSPEC, 0, 0, 0)
+        msg.flags = NLM_F_REQUEST | NLM_F_ACK
+
+        if ifname:
+            msg.add_attribute(Link.IFLA_IFNAME, ifname)
+        else:
+            msg.flags |= NLM_F_DUMP
+
+        msg.build_message(self.sequence.next(), self.pid)
+        return self.tx_nlpacket_get_response(msg)
+
+    def link_set_attrs(self, ifname, kind=None, slave_kind=None, ifindex=0, ifla={}, ifla_info_data={}, ifla_info_slave_data={}):
+        debug = RTM_NEWLINK in self.debug
+
+        link = Link(RTM_NEWLINK, debug, use_color=self.use_color)
+        link.flags = NLM_F_REQUEST | NLM_F_ACK
+        link.body = pack('Bxxxiii', socket.AF_UNSPEC, ifindex, 0, 0)
+
+        for nl_attr, value in ifla.items():
+            link.add_attribute(nl_attr, value)
+
+        if ifname:
+            link.add_attribute(Link.IFLA_IFNAME, ifname)
+
+        linkinfo = dict()
+
+        if kind:
+            linkinfo[Link.IFLA_INFO_KIND] = kind
+            linkinfo[Link.IFLA_INFO_DATA] = ifla_info_data
+        elif slave_kind:
+            linkinfo[Link.IFLA_INFO_SLAVE_KIND] = slave_kind,
+            linkinfo[Link.IFLA_INFO_SLAVE_DATA] = ifla_info_slave_data
+
+        link.add_attribute(Link.IFLA_LINKINFO, linkinfo)
+        link.build_message(self.sequence.next(), self.pid)
+        return self.tx_nlpacket_get_response(link)
+
+    def link_add_set(self, kind,
+                     ifname=None,
+                     ifindex=0,
+                     slave_kind=None,
+                     ifla={},
+                     ifla_info_data={},
+                     ifla_info_slave_data={}):
+        """
+        Build and TX a RTM_NEWLINK message to add the desired interface
+        """
+        debug = RTM_NEWLINK in self.debug
+
+        link = Link(RTM_NEWLINK, debug, use_color=self.use_color)
+        link.flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK
+        link.body = pack('Bxxxiii', socket.AF_UNSPEC, ifindex, 0, 0)
+
+        for nl_attr, value in ifla.items():
+            link.add_attribute(nl_attr, value)
+
+        if ifname:
+            link.add_attribute(Link.IFLA_IFNAME, ifname)
+
+        linkinfo = dict()
+        if kind:
+            linkinfo[Link.IFLA_INFO_KIND] = kind
+            linkinfo[Link.IFLA_INFO_DATA] = ifla_info_data
+        if slave_kind:
+            linkinfo[Link.IFLA_INFO_SLAVE_KIND] = slave_kind
+            linkinfo[Link.IFLA_INFO_SLAVE_DATA] = ifla_info_slave_data
+        link.add_attribute(Link.IFLA_LINKINFO, linkinfo)
+
+        link.build_message(self.sequence.next(), self.pid)
+        return self.tx_nlpacket_get_response(link)
+
+    def link_del(self, ifindex=None, ifname=None):
+        if not ifindex and not ifname:
+            raise ValueError('invalid ifindex and/or ifname')
+
+        if not ifindex:
+            ifindex = self.get_iface_index(ifname)
+
+        debug = RTM_DELLINK in self.debug
+
+        link = Link(RTM_DELLINK, debug, use_color=self.use_color)
+        link.flags = NLM_F_REQUEST | NLM_F_ACK
+        link.body = pack('Bxxxiii', socket.AF_UNSPEC, ifindex, 0, 0)
+        link.build_message(self.sequence.next(), self.pid)
+        return self.tx_nlpacket_get_response(link)
+
+    def _link_add(self, ifindex, ifname, kind, ifla_info_data):
+        """
+        Build and TX a RTM_NEWLINK message to add the desired interface
+        """
+        debug = RTM_NEWLINK in self.debug
+
+        link = Link(RTM_NEWLINK, debug, use_color=self.use_color)
+        link.flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK
+        link.body = pack('Bxxxiii', socket.AF_UNSPEC, 0, 0, 0)
+        link.add_attribute(Link.IFLA_IFNAME, ifname)
+
+        if ifindex:
+            link.add_attribute(Link.IFLA_LINK, ifindex)
+
+        link.add_attribute(Link.IFLA_LINKINFO, {
+            Link.IFLA_INFO_KIND: kind,
+            Link.IFLA_INFO_DATA: ifla_info_data
+        })
+        link.build_message(self.sequence.next(), self.pid)
+        return self.tx_nlpacket_get_response(link)
+
+    def link_add_bridge(self, ifname, ifla_info_data={}):
+        return self._link_add(ifindex=None, ifname=ifname, kind='bridge', ifla_info_data=ifla_info_data)
+
+    def link_add_vlan(self, ifindex, ifname, vlanid, vlan_protocol=None):
+        """
+        ifindex is the index of the parent interface that this sub-interface
+        is being added to
+        """
+
+        '''
+        If you name an interface swp2.17 but assign it to vlan 12, the kernel
+        will return a very misleading NLE_MSG_OVERFLOW error.  It only does
+        this check if the ifname uses dot notation.
+
+        Do this check here so we can provide a more intuitive error
+        '''
+        if '.' in ifname:
+            ifname_vlanid = int(ifname.split('.')[-1])
+
+            if ifname_vlanid != vlanid:
+                raise InvalidInterfaceNameVlanCombo("Interface %s must belong "
+                                                    "to VLAN %d (VLAN %d was requested)" %
+                                                    (ifname, ifname_vlanid, vlanid))
+
+        ifla_info_data = {Link.IFLA_VLAN_ID: vlanid}
+
+        if vlan_protocol:
+            ifla_info_data[Link.IFLA_VLAN_PROTOCOL] = vlan_protocol
+
+        return self._link_add(ifindex, ifname, 'vlan', ifla_info_data)
+
+    def link_add_macvlan(self, ifindex, ifname):
+        """
+        ifindex is the index of the parent interface that this sub-interface
+        is being added to
+        """
+        return self._link_add(ifindex, ifname, 'macvlan', {Link.IFLA_MACVLAN_MODE: Link.MACVLAN_MODE_PRIVATE})
+
+    def vlan_get(self, filter_ifindex=None, filter_vlanid=None, compress_vlans=True):
+        """
+        filter_ifindex should be a tuple if interface indexes, this is a whitelist filter
+        filter_vlandid should be a tuple if VLAN IDs, this is a whitelist filter
+        """
+        debug = RTM_GETLINK in self.debug
+
+        link = Link(RTM_GETLINK, debug, use_color=self.use_color)
+        link.family = AF_BRIDGE
+        link.flags = NLM_F_DUMP | NLM_F_REQUEST
+        link.body = pack('Bxxxiii', socket.AF_BRIDGE, 0, 0, 0)
+
+        if compress_vlans:
+            link.add_attribute(Link.IFLA_EXT_MASK, Link.RTEXT_FILTER_BRVLAN_COMPRESSED)
+        else:
+            link.add_attribute(Link.IFLA_EXT_MASK, Link.RTEXT_FILTER_BRVLAN)
+
+        link.build_message(self.sequence.next(), self.pid)
+        reply = self.tx_nlpacket_get_response(link)
+
+        iface_vlans = {}
+
+        for msg in reply:
+            if msg.family != socket.AF_BRIDGE:
+                continue
+
+            if filter_ifindex and msg.ifindex not in filter_ifindex:
+                continue
+
+            ifla_af_spec = msg.get_attribute_value(Link.IFLA_AF_SPEC)
+
+            if not ifla_af_spec:
+                continue
+
+            ifname = msg.get_attribute_value(Link.IFLA_IFNAME)
+
+            '''
+            Example IFLA_AF_SPEC
+
+              20: 0x1c001a00  ....  Length 0x001c (28), Type 0x001a (26) IFLA_AF_SPEC
+              21: 0x08000200  ....  Nested Attribute - Length 0x0008 (8),  Type 0x0002 (2) IFLA_BRIDGE_VLAN_INFO
+              22: 0x00000a00  ....
+              23: 0x08000200  ....  Nested Attribute - Length 0x0008 (8),  Type 0x0002 (2) IFLA_BRIDGE_VLAN_INFO
+              24: 0x00001000  ....
+              25: 0x08000200  ....  Nested Attribute - Length 0x0008 (8),  Type 0x0002 (2) IFLA_BRIDGE_VLAN_INFO
+              26: 0x00001400  ....
+            '''
+            for (x_type, x_value) in ifla_af_spec.iteritems():
+                if x_type == Link.IFLA_BRIDGE_VLAN_INFO:
+                    for (vlan_flag, vlan_id) in x_value:
+                        if filter_vlanid is None or vlan_id in filter_vlanid:
+
+                            if ifname not in iface_vlans:
+                                iface_vlans[ifname] = []
+
+                            # We store these in the tuple as (vlan, flag) instead (flag, vlan)
+                            # so that we can sort the list of tuples
+                            iface_vlans[ifname].append((vlan_id, vlan_flag))
+
+        return iface_vlans
+
+    def vlan_show(self, filter_ifindex=None, filter_vlanid=None, compress_vlans=True):
+
+        def vlan_flag_to_string(vlan_flag):
+            flag_str = []
+            if vlan_flag & Link.BRIDGE_VLAN_INFO_PVID:
+                flag_str.append('PVID')
+
+            if vlan_flag & Link.BRIDGE_VLAN_INFO_UNTAGGED:
+                flag_str.append('Egress Untagged')
+
+            return ', '.join(flag_str)
+
+        iface_vlans = self.vlan_get(filter_ifindex, filter_vlanid, compress_vlans)
+        log.debug("iface_vlans:\n%s\n" % pformat(iface_vlans))
+        range_begin_vlan_id = None
+        range_flag = None
+
+        print "   Interface  VLAN  Flags"
+        print "  ==========  ====  ====="
+
+        for (ifname, vlan_tuples) in sorted(iface_vlans.iteritems()):
+            for (vlan_id, vlan_flag) in sorted(vlan_tuples):
+
+                if vlan_flag & Link.BRIDGE_VLAN_INFO_RANGE_BEGIN:
+                    range_begin_vlan_id = vlan_id
+                    range_flag = vlan_flag
+
+                elif vlan_flag & Link.BRIDGE_VLAN_INFO_RANGE_END:
+                    range_flag |= vlan_flag
+
+                    if not range_begin_vlan_id:
+                        log.warning("BRIDGE_VLAN_INFO_RANGE_END is %d but we never saw a BRIDGE_VLAN_INFO_RANGE_BEGIN" % vlan_id)
+                        range_begin_vlan_id = vlan_id
+
+                    for x in xrange(range_begin_vlan_id, vlan_id + 1):
+                        print "  %10s  %4d  %s" % (ifname, x, vlan_flag_to_string(vlan_flag))
+                        ifname = ''
+
+                    range_begin_vlan_id = None
+                    range_flag = None
+
+                else:
+                    print "  %10s  %4d  %s" % (ifname, vlan_id, vlan_flag_to_string(vlan_flag))
+                    ifname = ''
+
+
+    def vlan_modify(self, msgtype, ifindex, vlanid_start, vlanid_end=None, bridge_self=False, bridge_master=False, pvid=False, untagged=False):
+        """
+        iproute2 bridge/vlan.c vlan_modify()
+        """
+        assert msgtype in (RTM_SETLINK, RTM_DELLINK), "Invalid msgtype %s, must be RTM_SETLINK or RTM_DELLINK" % msgtype
+        assert vlanid_start >= 1 and vlanid_start <= 4096, "Invalid VLAN start %s" % vlanid_start
+
+        if vlanid_end is None:
+            vlanid_end = vlanid_start
+
+        assert vlanid_end >= 1 and vlanid_end <= 4096, "Invalid VLAN end %s" % vlanid_end
+        assert vlanid_start <= vlanid_end, "Invalid VLAN range %s-%s, start must be <= end" % (vlanid_start, vlanid_end)
+
+        debug = msgtype in self.debug
+        bridge_flags = 0
+        vlan_info_flags = 0
+
+        link = Link(msgtype, debug, use_color=self.use_color)
+        link.flags = NLM_F_REQUEST | NLM_F_ACK
+        link.body = pack('Bxxxiii', socket.AF_BRIDGE, ifindex, 0, 0)
+
+        if bridge_self:
+            bridge_flags |= Link.BRIDGE_FLAGS_SELF
+
+        if bridge_master:
+            bridge_flags |= Link.BRIDGE_FLAGS_MASTER
+
+        if pvid:
+            vlan_info_flags |= Link.BRIDGE_VLAN_INFO_PVID
+
+        if untagged:
+            vlan_info_flags |= Link.BRIDGE_VLAN_INFO_UNTAGGED
+
+        ifla_af_spec = OrderedDict()
+
+        if bridge_flags:
+            ifla_af_spec[Link.IFLA_BRIDGE_FLAGS] = bridge_flags
+
+        # just one VLAN
+        if vlanid_start == vlanid_end:
+            ifla_af_spec[Link.IFLA_BRIDGE_VLAN_INFO] = [(vlan_info_flags, vlanid_start), ]
+
+        # a range of VLANs
+        else:
+            ifla_af_spec[Link.IFLA_BRIDGE_VLAN_INFO] = [
+                (vlan_info_flags | Link.BRIDGE_VLAN_INFO_RANGE_BEGIN, vlanid_start),
+                (vlan_info_flags | Link.BRIDGE_VLAN_INFO_RANGE_END, vlanid_end)
+            ]
+
+        link.add_attribute(Link.IFLA_AF_SPEC, ifla_af_spec)
+        link.build_message(self.sequence.next(), self.pid)
+        return self.tx_nlpacket_get_response(link)
+
+    def link_add_bridge_vlan(self, ifindex, vlanid_start, vlanid_end=None, pvid=False, untagged=False, master=False):
+        """
+        Add VLAN(s) to a bridge interface
+        """
+        bridge_self = False if master else True
+        self.vlan_modify(RTM_SETLINK, ifindex, vlanid_start, vlanid_end, bridge_self, master, pvid, untagged)
+
+    def link_del_bridge_vlan(self, ifindex, vlanid_start, vlanid_end=None, pvid=False, untagged=False, master=False):
+        """
+        Delete VLAN(s) from a bridge interface
+        """
+        bridge_self = False if master else True
+        self.vlan_modify(RTM_DELLINK, ifindex, vlanid_start, vlanid_end, bridge_self, master, pvid, untagged)
+
+    def link_set_updown(self, ifname, state):
+        """
+        Either bring ifname up or take it down
+        """
+
+        if state == 'up':
+            if_flags = Link.IFF_UP
+        elif state == 'down':
+            if_flags = 0
+        else:
+            raise Exception('Unsupported state %s, valid options are "up" and "down"' % state)
+
+        debug = RTM_NEWLINK in self.debug
+        if_change = Link.IFF_UP
+
+        link = Link(RTM_NEWLINK, debug, use_color=self.use_color)
+        link.flags = NLM_F_REQUEST | NLM_F_ACK
+        link.body = pack('=BxxxiLL', socket.AF_UNSPEC, 0, if_flags, if_change)
+        link.add_attribute(Link.IFLA_IFNAME, ifname)
+        link.build_message(self.sequence.next(), self.pid)
+        return self.tx_nlpacket_get_response(link)
+
+    def link_set_protodown(self, ifname, state):
+        """
+        Either bring ifname up or take it down by setting IFLA_PROTO_DOWN on or off
+        """
+        flags = 0
+        protodown = 1 if state == "on" else 0
+
+        debug = RTM_NEWLINK in self.debug
+
+        link = Link(RTM_NEWLINK, debug, use_color=self.use_color)
+        link.flags = NLM_F_REQUEST | NLM_F_ACK
+        link.body = pack('=BxxxiLL', socket.AF_UNSPEC, 0, 0, 0)
+        link.add_attribute(Link.IFLA_IFNAME, ifname)
+        link.add_attribute(Link.IFLA_PROTO_DOWN, protodown)
+        link.build_message(self.sequence.next(), self.pid)
+        return self.tx_nlpacket_get_response(link)
+
+    def link_set_master(self, ifname, master_ifindex=0, state=None):
+        """
+            ip link set %ifname master %master_ifindex %state
+            use master_ifindex=0 for nomaster
+        """
+        if state == 'up':
+            if_change = Link.IFF_UP
+            if_flags = Link.IFF_UP
+        elif state == 'down':
+            if_change = Link.IFF_UP
+            if_flags = 0
+        else:
+            if_change = 0
+            if_flags = 0
+
+        debug = RTM_NEWLINK in self.debug
+
+        link = Link(RTM_NEWLINK, debug, use_color=self.use_color)
+        link.flags = NLM_F_REQUEST | NLM_F_ACK
+        link.body = pack('=BxxxiLL', socket.AF_UNSPEC, 0, if_flags, if_change)
+        link.add_attribute(Link.IFLA_IFNAME, ifname)
+        link.add_attribute(Link.IFLA_MASTER, master_ifindex)
+        link.build_message(self.sequence.next(), self.pid)
+        return self.tx_nlpacket_get_response(link)
+
+    # =========
+    # Neighbors
+    # =========
+    def neighbor_add(self, afi, ifindex, ip, mac):
+        debug = RTM_NEWNEIGH in self.debug
+        service_hdr_flags = 0
+
+        nbr = Neighbor(RTM_NEWNEIGH, debug, use_color=self.use_color)
+        nbr.flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK
+        nbr.family = afi
+        nbr.body = pack('=BxxxiHBB', afi, ifindex, Neighbor.NUD_REACHABLE, service_hdr_flags, Route.RTN_UNICAST)
+        nbr.add_attribute(Neighbor.NDA_DST, ip)
+        nbr.add_attribute(Neighbor.NDA_LLADDR, mac)
+        nbr.build_message(self.sequence.next(), self.pid)
+        return self.tx_nlpacket_get_response(nbr)
+
+    def neighbor_del(self, afi, ifindex, ip, mac):
+        debug = RTM_DELNEIGH in self.debug
+        service_hdr_flags = 0
+
+        nbr = Neighbor(RTM_DELNEIGH, debug, use_color=self.use_color)
+        nbr.flags = NLM_F_REQUEST | NLM_F_ACK
+        nbr.family = afi
+        nbr.body = pack('=BxxxiHBB', afi, ifindex, Neighbor.NUD_REACHABLE, service_hdr_flags, Route.RTN_UNICAST)
+        nbr.add_attribute(Neighbor.NDA_DST, ip)
+        nbr.add_attribute(Neighbor.NDA_LLADDR, mac)
+        nbr.build_message(self.sequence.next(), self.pid)
+        return self.tx_nlpacket_get_response(nbr)
+
+    def link_add_vxlan(self, ifname, vxlanid, dstport=None, local=None,
+                       group=None, learning=True, ageing=None):
+
+        debug = RTM_NEWLINK in self.debug
+
+        info_data = {Link.IFLA_VXLAN_ID: int(vxlanid)}
+        if dstport:
+            info_data[Link.IFLA_VXLAN_PORT] = int(dstport)
+        if local:
+            info_data[Link.IFLA_VXLAN_LOCAL] = local
+        if group:
+            info_data[Link.IFLA_VXLAN_GROUP] = group
+
+        info_data[Link.IFLA_VXLAN_LEARNING] = int(learning)
+
+        if ageing:
+            info_data[Link.IFLA_VXLAN_AGEING] = int(ageing)
+
+        link = Link(RTM_NEWLINK, debug, use_color=self.use_color)
+        link.flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK
+        link.body = pack('Bxxxiii', socket.AF_UNSPEC, 0, 0, 0)
+        link.add_attribute(Link.IFLA_IFNAME, ifname)
+        link.add_attribute(Link.IFLA_LINKINFO, {
+            Link.IFLA_INFO_KIND: "vxlan",
+            Link.IFLA_INFO_DATA: info_data
+        })
+
+        link.build_message(self.sequence.next(), self.pid)
+        return self.tx_nlpacket_get_response(link)
+
+    # =========
+    # Addresses
+    # =========
+    def addr_dump(self):
+        """
+            TODO: add ifname/ifindex filtering:
+                        - via the RTM_GETADDR request packet
+                        - or in python if kernel doesn't support per intf dump
+        """
+        debug = RTM_GETADDR in self.debug
+
+        msg = Address(RTM_GETADDR, debug, use_color=self.use_color)
+        msg.body = pack('=Bxxxi', socket.AF_UNSPEC, 0)
+        msg.flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP
+
+        msg.build_message(self.sequence.next(), self.pid)
+        return self.tx_nlpacket_get_response(msg)
diff --git a/ifupdown2/nlmanager/nlpacket.py b/ifupdown2/nlmanager/nlpacket.py
new file mode 100644 (file)
index 0000000..d6f93fe
--- /dev/null
@@ -0,0 +1,4111 @@
+# Copyright (c) 2009-2013, Exa Networks Limited
+# Copyright (c) 2009-2013, Thomas Mangin
+# Copyright (c) 2015-2017 Cumulus Networks, Inc.
+#
+# All rights reserved.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# The names of the Exa Networks Limited, Cumulus Networks, Inc. nor the names
+# of its contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import logging
+import struct
+from ipaddr import IPv4Address, IPv6Address, IPAddress
+from binascii import hexlify
+from pprint import pformat
+from socket import AF_UNSPEC, AF_INET, AF_INET6, AF_BRIDGE, htons
+from string import printable
+from struct import pack, unpack, calcsize
+
+log = logging.getLogger(__name__)
+SYSLOG_EXTRA_DEBUG = 5
+
+
+# Interface name buffer size #define IFNAMSIZ 16 (kernel source)
+IF_NAME_SIZE = 15 # 15 because python doesn't have \0
+
+# Netlink message types
+NLMSG_NOOP    = 0x01
+NLMSG_ERROR   = 0x02
+NLMSG_DONE    = 0x03
+NLMSG_OVERRUN = 0x04
+
+RTM_NEWLINK   = 0x10  # Create a new network interface
+RTM_DELLINK   = 0x11  # Destroy a network interface
+RTM_GETLINK   = 0x12  # Retrieve information about a network interface(ifinfomsg)
+RTM_SETLINK   = 0x13  #
+
+RTM_NEWADDR   = 0x14
+RTM_DELADDR   = 0x15
+RTM_GETADDR   = 0x16
+
+RTM_NEWNEIGH  = 0x1C
+RTM_DELNEIGH  = 0x1D
+RTM_GETNEIGH  = 0x1E
+
+RTM_NEWROUTE  = 0x18
+RTM_DELROUTE  = 0x19
+RTM_GETROUTE  = 0x1A
+
+RTM_NEWQDISC  = 0x24
+RTM_DELQDISC  = 0x25
+RTM_GETQDISC  = 0x26
+
+# Netlink message flags
+NLM_F_REQUEST = 0x01  # It is query message.
+NLM_F_MULTI   = 0x02  # Multipart message, terminated by NLMSG_DONE
+NLM_F_ACK     = 0x04  # Reply with ack, with zero or error code
+NLM_F_ECHO    = 0x08  # Echo this query
+
+# Modifiers to GET query
+NLM_F_ROOT   = 0x100  # specify tree root
+NLM_F_MATCH  = 0x200  # return all matching
+NLM_F_DUMP   = NLM_F_ROOT | NLM_F_MATCH
+NLM_F_ATOMIC = 0x400  # atomic GET
+
+# Modifiers to NEW query
+NLM_F_REPLACE = 0x100  # Override existing
+NLM_F_EXCL    = 0x200  # Do not touch, if it exists
+NLM_F_CREATE  = 0x400  # Create, if it does not exist
+NLM_F_APPEND  = 0x800  # Add to end of list
+
+NLA_F_NESTED        = 0x8000
+NLA_F_NET_BYTEORDER = 0x4000
+NLA_TYPE_MASK       = ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER)
+
+# Groups
+RTMGRP_LINK          = 0x1
+RTMGRP_NOTIFY        = 0x2
+RTMGRP_NEIGH         = 0x4
+RTMGRP_TC            = 0x8
+RTMGRP_IPV4_IFADDR   = 0x10
+RTMGRP_IPV4_MROUTE   = 0x20
+RTMGRP_IPV4_ROUTE    = 0x40
+RTMGRP_IPV4_RULE     = 0x80
+RTMGRP_IPV6_IFADDR   = 0x100
+RTMGRP_IPV6_MROUTE   = 0x200
+RTMGRP_IPV6_ROUTE    = 0x400
+RTMGRP_IPV6_IFINFO   = 0x800
+RTMGRP_DECnet_IFADDR = 0x1000
+RTMGRP_DECnet_ROUTE  = 0x4000
+RTMGRP_IPV6_PREFIX   = 0x20000
+
+RTMGRP_ALL = (RTMGRP_LINK | RTMGRP_NOTIFY | RTMGRP_NEIGH | RTMGRP_TC |
+              RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_MROUTE | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_RULE |
+              RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_MROUTE | RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFINFO |
+              RTMGRP_DECnet_IFADDR | RTMGRP_DECnet_ROUTE |
+              RTMGRP_IPV6_PREFIX)
+
+AF_MPLS = 28
+
+# Colors for logging
+red    = 91
+green  = 92
+yellow = 93
+blue   = 94
+
+value_to_bool_dict = {
+    False: False,
+    None: False,
+    0: False,
+    '0': False,
+    'no': False,
+    'off': False,
+    'slow': False,
+    'None': False,
+    True: True,
+    1: True,
+    '1': True,
+    'on': True,
+    'yes': True,
+    'fast': True
+}
+
+
+def set_log_level(level):
+    log.setLevel(level)
+
+
+def zfilled_hex(value, digits):
+    return '0x' + hex(value)[2:].zfill(digits)
+
+
+def remove_trailing_null(line):
+    """
+    Remove the last character if it is a NULL...having that NULL
+    causes python to print a garbage character
+    """
+
+    if ord(line[-1]) == 0:
+        line = line[:-1]
+
+    return line
+
+
+def mac_int_to_str(mac_int):
+    """
+    Return an integer in MAC string format
+    """
+
+    # [2:] to remove the leading 0x, then fill out to 12 zeroes, then uppercase
+    all_caps = hex(int(mac_int))[2:].zfill(12).upper()
+
+    if all_caps[-1] == 'L':
+        all_caps = all_caps[:-1]
+        all_caps = all_caps.zfill(12).upper()
+
+    return "%s.%s.%s" % (all_caps[0:4], all_caps[4:8], all_caps[8:12])
+
+
+def data_to_color_text(line_number, color, data, extra=''):
+    (c1, c2, c3, c4) = unpack('BBBB', data[0:4])
+    in_ascii = []
+
+    for c in (c1, c2, c3, c4):
+        char_c = chr(c)
+
+        if char_c in printable[:-5]:
+            in_ascii.append(char_c)
+        else:
+            in_ascii.append('.')
+
+    if color:
+        return '  %2d: \033[%dm0x%02x%02x%02x%02x\033[0m  %s  %s' % (line_number, color, c1, c2, c3, c4, ''.join(in_ascii), extra)
+
+    return '  %2d: 0x%02x%02x%02x%02x  %s  %s' % (line_number, c1, c2, c3, c4, ''.join(in_ascii), extra)
+
+
+def padded_length(length):
+    return int((length + 3) / 4) * 4
+
+
+class Attribute(object):
+
+    def __init__(self, atype, string, logger):
+        self.atype = atype
+        self.string = string
+        self.HEADER_PACK = '=HH'
+        self.HEADER_LEN = calcsize(self.HEADER_PACK)
+        self.PACK = None
+        self.LEN = None
+        self.value = None
+        self.nested = False
+        self.net_byteorder = False
+        self.log = logger
+
+    def __str__(self):
+        return self.string
+
+    def set_value(self, value):
+        self.value = value
+
+    def set_nested(self, nested):
+        self.nested = nested
+
+    def set_net_byteorder(self, net_byteorder):
+        self.net_byteorder = net_byteorder
+
+    def pad_bytes_needed(self, length):
+        """
+        Return the number of bytes that should be added to align on a 4-byte boundry
+        """
+        remainder = length % 4
+
+        if remainder:
+            return 4 - remainder
+
+        return 0
+
+    def pad(self, length, raw):
+        pad = self.pad_bytes_needed(length)
+
+        if pad:
+            raw += '\0' * pad
+
+        return raw
+
+    def encode(self):
+
+        if not self.LEN:
+            raise Exception('Please define an encode() method in your child attribute class, or do not use AttributeGeneric')
+
+        length = self.HEADER_LEN + self.LEN
+        attr_type_with_flags = self.atype
+
+        if self.nested:
+            attr_type_with_flags = attr_type_with_flags | NLA_F_NESTED
+
+        if self.net_byteorder:
+            attr_type_with_flags = attr_type_with_flags | NLA_F_NET_BYTEORDER
+
+        raw = pack(self.HEADER_PACK, length, attr_type_with_flags) + pack(self.PACK, self.value)
+        raw = self.pad(length, raw)
+        return raw
+
+    def decode_length_type(self, data):
+        """
+        The first two bytes of an attribute are the length, the next two bytes are the type
+        """
+        self.data = data
+        prev_atype = self.atype
+        (data1, data2) = unpack(self.HEADER_PACK, data[:self.HEADER_LEN])
+        self.length = int(data1)
+        self.atype = int(data2)
+        self.attr_end = padded_length(self.length)
+
+        self.nested = True if self.atype & NLA_F_NESTED else False
+        self.net_byteorder = True if self.atype & NLA_F_NET_BYTEORDER else False
+        self.atype = self.atype & NLA_TYPE_MASK
+
+        # Should never happen
+        assert self.atype == prev_atype, "This object changes attribute type from %d to %d, this is bad" % (prev_atype, self.atype)
+
+    def dump_first_line(self, dump_buffer, line_number, color):
+        """
+        Add the "Length....Type..." line to the dump buffer
+        """
+        if self.attr_end == self.length:
+            padded_to = ', '
+        else:
+            padded_to = ' padded to %d, ' % self.attr_end
+
+        extra = 'Length %s (%d)%sType %s%s%s (%d) %s' % \
+                 (zfilled_hex(self.length, 4), self.length,
+                  padded_to,
+                  zfilled_hex(self.atype, 4),
+                  " (NLA_F_NESTED set)" if self.nested else "",
+                  " (NLA_F_NET_BYTEORDER set)" if self.net_byteorder else "",
+                  self.atype,
+                  self)
+
+        dump_buffer.append(data_to_color_text(line_number, color, self.data[0:4], extra))
+        return line_number + 1
+
+    def dump_lines(self, dump_buffer, line_number, color):
+        line_number = self.dump_first_line(dump_buffer, line_number, color)
+
+        for x in xrange(1, self.attr_end/4):
+            start = x * 4
+            end = start + 4
+            dump_buffer.append(data_to_color_text(line_number, color, self.data[start:end], ''))
+            line_number += 1
+
+        return line_number
+
+    def get_pretty_value(self, obj=None):
+        if obj and callable(obj):
+            return obj(self.value)
+        return self.value
+
+
+class AttributeFourByteList(Attribute):
+
+    def __init__(self, atype, string, family, logger):
+        Attribute.__init__(self, atype, string, logger)
+
+    def decode(self, parent_msg, data):
+        self.decode_length_type(data)
+        wordcount = (self.attr_end - 4)/4
+        self.PACK = '=%dL' % wordcount
+        self.LEN = calcsize(self.PACK)
+
+        try:
+            self.value = unpack(self.PACK, self.data[4:])
+        except struct.error:
+            self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:])))
+            raise
+
+    def dump_lines(self, dump_buffer, line_number, color):
+        line_number = self.dump_first_line(dump_buffer, line_number, color)
+        idx = 1
+        for val in self.value:
+            dump_buffer.append(data_to_color_text(line_number, color, self.data[4*idx:4*(idx+1)], val))
+            line_number += 1
+            idx += 1
+        return line_number
+
+
+class AttributeFourByteValue(Attribute):
+
+    def __init__(self, atype, string, family, logger):
+        Attribute.__init__(self, atype, string, logger)
+        self.PACK = '=L'
+        self.LEN = calcsize(self.PACK)
+
+    def decode(self, parent_msg, data):
+        self.decode_length_type(data)
+        assert self.attr_end == 8, "Attribute length for %s must be 8, it is %d" % (self, self.attr_end)
+
+        try:
+            self.value = int(unpack(self.PACK, self.data[4:])[0])
+        except struct.error:
+            self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:])))
+            raise
+
+    def dump_lines(self, dump_buffer, line_number, color):
+        line_number = self.dump_first_line(dump_buffer, line_number, color)
+        dump_buffer.append(data_to_color_text(line_number, color, self.data[4:8], self.value))
+        return line_number + 1
+
+
+class AttributeTwoByteValue(Attribute):
+
+    def __init__(self, atype, string, family, logger):
+        Attribute.__init__(self, atype, string, logger)
+        self.PACK = '=Hxx'
+        self.LEN = calcsize(self.PACK)
+
+    def decode(self, parent_msg, data):
+        self.decode_length_type(data)
+        assert self.attr_end == 8, "Attribute length for %s must be 8, it is %d" % (self, self.attr_end)
+
+        try:
+            self.value = int(unpack(self.PACK, self.data[4:8])[0])
+        except struct.error:
+            self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:6])))
+            raise
+
+    def encode(self):
+        length = self.HEADER_LEN + self.LEN
+        raw = pack(self.HEADER_PACK, length-2, self.atype) + pack(self.PACK, self.value)
+        raw = self.pad(length, raw)
+        return raw
+
+    def dump_lines(self, dump_buffer, line_number, color):
+        line_number = self.dump_first_line(dump_buffer, line_number, color)
+        dump_buffer.append(data_to_color_text(line_number, color, self.data[4:8], self.value))
+        return line_number + 1
+
+
+class AttributeString(Attribute):
+
+    def __init__(self, atype, string, family, logger):
+        Attribute.__init__(self, atype, string, logger)
+        self.PACK = None
+        self.LEN = None
+
+    def encode(self):
+        # some interface names come from JSON as unicode strings
+        # and cannot be packed as is so we must convert them to strings
+        if isinstance(self.value, unicode):
+            self.value = str(self.value)
+        self.PACK = '%ds' % len(self.value)
+        self.LEN = calcsize(self.PACK)
+
+        length = self.HEADER_LEN + self.LEN
+        raw = pack(self.HEADER_PACK, length, self.atype) + pack(self.PACK, self.value)
+        raw = self.pad(length, raw)
+        return raw
+
+    def decode(self, parent_msg, data):
+        self.decode_length_type(data)
+        self.PACK = '%ds' % (self.length - 4)
+        self.LEN = calcsize(self.PACK)
+
+        try:
+            self.value = remove_trailing_null(unpack(self.PACK, self.data[4:self.length])[0])
+        except struct.error:
+            self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:self.length])))
+            raise
+
+
+class AttributeStringInterfaceName(AttributeString):
+
+    def __init__(self, atype, string, family, logger):
+        AttributeString.__init__(self, atype, string, family, logger)
+
+    def set_value(self, value):
+        if value and len(value) > IF_NAME_SIZE:
+            raise Exception('interface name exceeds max length of %d' % IF_NAME_SIZE)
+        self.value = value
+
+
+class AttributeIPAddress(Attribute):
+
+    def __init__(self, atype, string, family, logger):
+        Attribute.__init__(self, atype, string, logger)
+        self.value_int = None
+        self.value_int_str = None
+        self.family = family
+
+        if self.family == AF_INET:
+            self.PACK = '>L'
+
+        elif self.family == AF_INET6:
+            self.PACK = '>QQ'
+
+        elif self.family == AF_BRIDGE:
+            self.PACK = '>L'
+
+        else:
+            raise Exception("%s is not a supported address family" % self.family)
+
+        self.LEN = calcsize(self.PACK)
+
+    def set_value(self, value):
+        if value is None:
+            self.value = None
+        else:
+            self.value = IPAddress(value)
+
+    def decode(self, parent_msg, data):
+        self.decode_length_type(data)
+
+        try:
+            if self.family == AF_INET:
+                self.value = IPv4Address(unpack(self.PACK, self.data[4:])[0])
+
+            elif self.family == AF_INET6:
+                (data1, data2) = unpack(self.PACK, self.data[4:])
+                self.value = IPv6Address(data1 << 64 | data2)
+
+            elif self.family == AF_BRIDGE:
+                self.value = IPv4Address(unpack(self.PACK, self.data[4:])[0])
+
+            self.value_int = int(self.value)
+            self.value_int_str = str(self.value_int)
+
+        except struct.error:
+            self.value = None
+            self.value_int = None
+            self.value_int_str = None
+            self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:])))
+            raise
+
+    def encode(self):
+        length = self.HEADER_LEN + self.LEN
+
+        if self.family not in [AF_INET, AF_INET6, AF_BRIDGE]:
+            raise Exception("%s is not a supported address family" % self.family)
+
+        raw = pack(self.HEADER_PACK, length, self.atype) + self.value.packed
+        raw = self.pad(length, raw)
+        return raw
+
+    def dump_lines(self, dump_buffer, line_number, color):
+        line_number = self.dump_first_line(dump_buffer, line_number, color)
+
+        if self.family == AF_INET:
+            dump_buffer.append(data_to_color_text(line_number, color, self.data[4:8], self.value))
+            line_number += 1
+
+        elif self.family == AF_INET6:
+
+            for x in xrange(1, self.attr_end/4):
+                start = x * 4
+                end = start + 4
+                dump_buffer.append(data_to_color_text(line_number, color, self.data[start:end], self.value))
+                line_number += 1
+
+        elif self.family == AF_BRIDGE:
+            dump_buffer.append(data_to_color_text(line_number, color, self.data[4:8], self.value))
+            line_number += 1
+
+        return line_number
+
+
+class AttributeMACAddress(Attribute):
+
+    def __init__(self, atype, string, family, logger):
+        Attribute.__init__(self, atype, string, logger)
+        self.PACK = '>LHxx'
+        self.LEN = calcsize(self.PACK)
+
+    def decode(self, parent_msg, data):
+        self.decode_length_type(data)
+
+        try:
+            if self.length == 10:
+                (data1, data2) = unpack(self.PACK, self.data[4:])
+                self.value = mac_int_to_str(data1 << 16 | data2)
+            elif self.length == 8:
+                self.value = IPv4Address(unpack('>L', self.data[4:])[0])
+                self.value_int = int(self.value)
+                self.value_int_str = str(self.value_int)
+            else:
+                raise Exception("Length of MACAddress attribute not supported: %d" % self.length)
+
+        except struct.error:
+            self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:])))
+            raise
+
+    def encode(self):
+        length = self.HEADER_LEN + self.LEN
+        mac_raw = int(self.value.replace('.', '').replace(':', ''), 16)
+        raw = pack(self.HEADER_PACK, length-2, self.atype) + pack(self.PACK, mac_raw >> 16, mac_raw & 0x0000FFFF)
+        raw = self.pad(length, raw)
+        return raw
+
+    def dump_lines(self, dump_buffer, line_number, color):
+        line_number = self.dump_first_line(dump_buffer, line_number, color)
+        dump_buffer.append(data_to_color_text(line_number, color, self.data[4:8], self.value))
+        line_number += 1
+        if len(self.data) >= 12:
+            dump_buffer.append(data_to_color_text(line_number, color, self.data[8:12]))
+            line_number += 1
+        return line_number
+
+
+class AttributeMplsLabel(Attribute):
+
+    def __init__(self, atype, string, family, logger):
+        Attribute.__init__(self, atype, string, logger)
+        self.value_int = None
+        self.value_int_str = None
+        self.family = family
+        self.PACK = '>HBB'
+
+    def decode(self, parent_msg, data):
+        self.decode_length_type(data)
+
+        try:
+            (label_high, label_low_tc_s, self.ttl) = unpack(self.PACK, self.data[4:])
+            self.s_bit = label_low_tc_s & 0x1
+            self.traffic_class = ((label_low_tc_s & 0xf) >> 1)
+            self.label = (label_high << 4) | (label_low_tc_s >> 4)
+            self.value = self.label
+            self.value_int = self.value
+            self.value_int_str = str(self.value_int)
+
+        except struct.error:
+            self.value = None
+            self.value_int = None
+            self.value_int_str = None
+            self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:])))
+            raise
+
+    def dump_lines(self, dump_buffer, line_number, color):
+        line_number = self.dump_first_line(dump_buffer, line_number, color)
+        dump_buffer.append(data_to_color_text(line_number, color, self.data[4:8],
+                                              'label %s, TC %s, bottom-of-stack %s, TTL %d' %
+                                              (self.label, self.traffic_class, self.s_bit, self.ttl)))
+        line_number += 1
+
+        return line_number
+
+
+class AttributeGeneric(Attribute):
+
+    def __init__(self, atype, string, family, logger):
+        Attribute.__init__(self, atype, string, logger)
+        self.PACK = None
+        self.LEN = None
+
+    def decode(self, parent_msg, data):
+        self.decode_length_type(data)
+        wordcount = (self.attr_end - 4)/4
+        self.PACK = '=%dL' % wordcount
+        self.LEN = calcsize(self.PACK)
+
+        try:
+            self.value = ''.join(map(str, unpack(self.PACK, self.data[4:])))
+        except struct.error:
+            self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:])))
+            raise
+
+
+class AttributeOneByteValue(AttributeGeneric):
+
+    def __init__(self, atype, string, family, logger):
+        AttributeGeneric.__init__(self, atype, string, family, logger)
+        self.PACK = '=Bxxx'
+        self.LEN = calcsize(self.PACK)
+
+    def decode(self, parent_msg, data):
+        self.decode_length_type(data)
+        assert self.attr_end == 8, "Attribute length for %s must be 8, it is %d" % (self, self.attr_end)
+
+        try:
+            self.value = int(unpack(self.PACK, self.data[4:8])[0])
+        except struct.error:
+            self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:5])))
+            raise
+
+    def encode(self):
+        length = self.HEADER_LEN + self.LEN
+        raw = pack(self.HEADER_PACK, length-3, self.atype) + pack(self.PACK, self.value)
+        raw = self.pad(length, raw)
+        return raw
+
+    def dump_lines(self, dump_buffer, line_number, color):
+        line_number = self.dump_first_line(dump_buffer, line_number, color)
+        dump_buffer.append(data_to_color_text(line_number, color, self.data[4:8], self.value))
+        return line_number + 1
+
+
+class AttributeIFLA_AF_SPEC(Attribute):
+    """
+    value will be a dictionary such as:
+    {
+        Link.IFLA_BRIDGE_FLAGS: flags,
+        Link.IFLA_BRIDGE_VLAN_INFO: (vflags, vlanid)
+    }
+    """
+    def __init__(self, atype, string, family, logger):
+        Attribute.__init__(self, atype, string, logger)
+        self.family = family
+
+    def encode(self):
+        pack_layout = [self.HEADER_PACK]
+        payload = [0, self.atype | NLA_F_NESTED]
+        attr_length_index = 0
+
+        # For now this assumes that all data will be packed in the native endian
+        # order (=). If a field is added that needs to be packed via network
+        # order (>) then some smarts will need to be added to split the pack_layout
+        # string at the >, split the payload and make the needed pack() calls.
+        #
+        # Until we cross that bridge though we will keep things nice and simple and
+        # pack everything via a single pack() call.
+        sub_attr_to_add = []
+
+        for (sub_attr_type, sub_attr_value) in self.value.iteritems():
+
+            if sub_attr_type == Link.IFLA_BRIDGE_FLAGS:
+                sub_attr_to_add.append((sub_attr_type, sub_attr_value))
+
+            elif sub_attr_type == Link.IFLA_BRIDGE_VLAN_INFO:
+                for (vlan_flag, vlan_id) in sub_attr_value:
+                    sub_attr_to_add.append((sub_attr_type, (vlan_flag, vlan_id)))
+
+            else:
+                self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for encoding IFLA_AF_SPEC sub-attribute type %d' % sub_attr_type)
+                continue
+
+        for (sub_attr_type, sub_attr_value) in sub_attr_to_add:
+            sub_attr_pack_layout = ['=', 'HH']
+            sub_attr_payload = [0, sub_attr_type]
+            sub_attr_length_index = 0
+
+            if sub_attr_type == Link.IFLA_BRIDGE_FLAGS:
+                sub_attr_pack_layout.append('H')
+                sub_attr_payload.append(sub_attr_value)
+
+            elif sub_attr_type == Link.IFLA_BRIDGE_VLAN_INFO:
+                sub_attr_pack_layout.append('HH')
+                sub_attr_payload.append(sub_attr_value[0])
+                sub_attr_payload.append(sub_attr_value[1])
+
+            sub_attr_length = calcsize(''.join(sub_attr_pack_layout))
+            sub_attr_payload[sub_attr_length_index] = sub_attr_length
+
+            # add padding
+            for x in xrange(self.pad_bytes_needed(sub_attr_length)):
+                sub_attr_pack_layout.append('x')
+
+            # The [1:] is to remove the leading = so that when we do the ''.join() later
+            # we do not end up with an = in the middle of the pack layout string. There
+            # will be an = at the beginning via self.HEADER_PACK
+            sub_attr_pack_layout = sub_attr_pack_layout[1:]
+
+            # Now extend the ovarall attribute pack_layout/payload to include this sub-attribute
+            pack_layout.extend(sub_attr_pack_layout)
+            payload.extend(sub_attr_payload)
+
+        pack_layout = ''.join(pack_layout)
+
+        # Fill in the length field
+        length = calcsize(pack_layout)
+        payload[attr_length_index] = length
+
+        raw = pack(pack_layout, *payload)
+        raw = self.pad(length, raw)
+        return raw
+
+    def decode(self, parent_msg, data):
+        """
+        value is a dictionary such as:
+        {
+            Link.IFLA_BRIDGE_FLAGS: flags,
+            Link.IFLA_BRIDGE_VLAN_INFO: (vflags, vlanid)
+        }
+
+        FROM: David Ahern
+        The encoding of the IFLA_AF_SPEC attribute varies depending on the family
+        used for the request (RTM_GETLINK) message. For AF_UNSPEC the encoding
+        has another level of nesting for each address family with the type encoded
+        first. i.e.,
+            af_spec = nla_nest_start(skb, IFLA_AF_SPEC)
+            for each family:
+                af = nla_nest_start(skb, af_ops->family)
+                af_ops->fill_link_af(skb, dev, ext_filter_mask)
+                nest_end
+            nest_end
+
+        This allows the parser to find the address family by looking at the first
+        type.
+
+        Whereas AF_BRIDGE encoding is just:
+            af_spec = nla_nest_start(skb, IFLA_AF_SPEC)
+            br_fill_ifvlaninfo{_compressed}(skb, vg)
+            nest_end
+
+        which means the parser can not use the attribute itself to know the family
+        to which the attribute belongs.
+
+        /include/uapi/linux/if_link.h
+        /*
+         * IFLA_AF_SPEC
+         *   Contains nested attributes for address family specific attributes.
+         *   Each address family may create a attribute with the address family
+         *   number as type and create its own attribute structure in it.
+         *
+         *   Example:
+         *   [IFLA_AF_SPEC] = {
+         *       [AF_INET] = {
+         *           [IFLA_INET_CONF] = ...,
+         *       },
+         *       [AF_INET6] = {
+         *           [IFLA_INET6_FLAGS] = ...,
+         *           [IFLA_INET6_CONF] = ...,
+         *       }
+         *   }
+         */
+
+        """
+        self.decode_length_type(data)
+        self.value = {}
+
+        # opti for now, since we only support AF_BRIDGE
+        if self.family != AF_BRIDGE:
+            return
+
+        data = self.data[4:]
+
+        while data:
+            (sub_attr_length, sub_attr_type) = unpack('=HH', data[:4])
+            sub_attr_end = padded_length(sub_attr_length)
+
+            if not sub_attr_length:
+                self.log.error('parsed a zero length sub-attr')
+                return
+
+            sub_attr_data = data[4:sub_attr_end]
+
+            if self.family == AF_BRIDGE:
+                if sub_attr_type == Link.IFLA_BRIDGE_FLAGS:
+                    self.value[Link.IFLA_BRIDGE_FLAGS] = unpack("=H", sub_attr_data[0:2])[0]
+
+                elif sub_attr_type == Link.IFLA_BRIDGE_VLAN_INFO:
+                    if Link.IFLA_BRIDGE_VLAN_INFO not in self.value:
+                        self.value[Link.IFLA_BRIDGE_VLAN_INFO] = []
+                    self.value[Link.IFLA_BRIDGE_VLAN_INFO].append(tuple(unpack("=HH", sub_attr_data[0:4])))
+
+                else:
+                    self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for decoding IFLA_AF_SPEC sub-attribute '
+                                                     'type %s (%d), length %d, padded to %d' %
+                                 (parent_msg.get_ifla_bridge_af_spec_to_string(sub_attr_type),
+                                  sub_attr_type, sub_attr_length, sub_attr_end))
+
+            elif self.family == AF_UNSPEC:
+                self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for decoding IFLA_AF_SPEC sub-attribute '
+                                                 'type AF_UNSPEC (0), length %d, padded to %d'
+                             % (sub_attr_length, sub_attr_end))
+
+            else:
+                self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for decoding IFLA_AF_SPEC sub-attribute '
+                                                 'family %d, length %d, padded to %d'
+                             % (self.family, sub_attr_length, sub_attr_end))
+
+            data = data[sub_attr_end:]
+
+    def dump_lines(self, dump_buffer, line_number, color):
+        line_number = self.dump_first_line(dump_buffer, line_number, color)
+        extra = ''
+
+        next_sub_attr_line = 0
+        sub_attr_line = True
+
+        for x in xrange(1, self.attr_end/4):
+            start = x * 4
+            end = start + 4
+
+            if line_number == next_sub_attr_line:
+                sub_attr_line = True
+
+            if sub_attr_line:
+                sub_attr_line = False
+
+                (sub_attr_length, sub_attr_type) = unpack('=HH', self.data[start:start+4])
+                sub_attr_end = padded_length(sub_attr_length)
+
+                next_sub_attr_line = line_number + (sub_attr_end/4)
+
+                if sub_attr_end == sub_attr_length:
+                    padded_to = ', '
+                else:
+                    padded_to = ' padded to %d, ' % sub_attr_end
+
+                if self.family == AF_BRIDGE:
+                    extra = 'Nested Attribute - Length %s (%d)%s Type %s (%d) %s' % \
+                            (zfilled_hex(sub_attr_length, 4), sub_attr_length,
+                             padded_to,
+                             zfilled_hex(sub_attr_type, 4), sub_attr_type,
+                             Link.ifla_bridge_af_spec_to_string.get(sub_attr_type))
+            else:
+                extra = ''
+
+            dump_buffer.append(data_to_color_text(line_number, color, self.data[start:end], extra))
+            line_number += 1
+
+        return line_number
+
+    def get_pretty_value(self, obj=None):
+
+        if obj and callable(obj):
+            return obj(self.value)
+
+        # We do this so we can print a more human readable dictionary
+        # with the names of the nested keys instead of their numbers
+        value_pretty = {}
+
+        for (sub_key, sub_value) in self.value.iteritems():
+            sub_key_pretty = "(%2d) %s" % (sub_key, Link.ifla_bridge_af_spec_to_string.get(sub_key))
+            value_pretty[sub_key_pretty] = sub_value
+
+        return value_pretty
+
+
+
+class AttributeRTA_MULTIPATH(Attribute):
+    """
+/* RTA_MULTIPATH --- array of struct rtnexthop.
+ *
+ * "struct rtnexthop" describes all necessary nexthop information,
+ * i.e. parameters of path to a destination via this nexthop.
+ *
+ * At the moment it is impossible to set different prefsrc, mtu, window
+ * and rtt for different paths from multipath.
+ */
+
+struct rtnexthop {
+    unsigned short rtnh_len;
+    unsigned char  rtnh_flags;
+    unsigned char  rtnh_hops;
+    int            rtnh_ifindex;
+};
+    """
+
+    def __init__(self, atype, string, family, logger):
+        Attribute.__init__(self, atype, string, logger)
+        self.family = family
+        self.PACK = None
+        self.LEN = None
+        self.RTNH_PACK = '=HBBL'  # rtnh_len, flags, hops, ifindex
+        self.RTNH_LEN = calcsize(self.RTNH_PACK)
+        self.IPV4_LEN = 4
+        self.IPV6_LEN = 16
+
+    def encode(self):
+
+        # Calculate the length
+        if self.family == AF_INET:
+            ip_len = self.IPV4_LEN
+        elif self.family == AF_INET6:
+            ip_len = self.IPV6_LEN
+
+        # Attribute header
+        length = self.HEADER_LEN + ((self.RTNH_LEN + self.HEADER_LEN + ip_len) * len(self.value))
+        raw = pack(self.HEADER_PACK, length, self.atype)
+
+        rtnh_flags = 0
+        rtnh_hops = 0
+        rtnh_len = self.RTNH_LEN + self.HEADER_LEN + ip_len
+
+        for (nexthop, rtnh_ifindex) in self.value:
+
+            # rtnh structure
+            raw += pack(self.RTNH_PACK, rtnh_len, rtnh_flags, rtnh_hops, rtnh_ifindex)
+
+            # Gateway
+            raw += pack(self.HEADER_PACK, self.HEADER_LEN + ip_len, Route.RTA_GATEWAY)
+
+            if self.family == AF_INET:
+                raw += pack('>L', nexthop)
+            elif self.family == AF_INET6:
+                raw += pack('>QQ', nexthop >> 64, nexthop & 0x0000000000000000FFFFFFFFFFFFFFFF)
+
+        raw = self.pad(length, raw)
+        return raw
+
+    def decode(self, parent_msg, data):
+        self.decode_length_type(data)
+        self.value = []
+
+        data = self.data[4:]
+
+        while data:
+            (rtnh_len, rtnh_flags, rtnh_hops, rtnh_ifindex) = unpack(self.RTNH_PACK, data[:self.RTNH_LEN])
+            data = data[self.RTNH_LEN:]
+
+            (attr_type, attr_length) = unpack(self.HEADER_PACK, self.data[:self.HEADER_LEN])
+            data = data[self.HEADER_LEN:]
+
+            if self.family == AF_INET:
+                if len(data) < self.IPV4_LEN:
+                    break
+                nexthop = IPv4Address(unpack('>L', data[:self.IPV4_LEN])[0])
+                self.value.append((nexthop, rtnh_ifindex, rtnh_flags, rtnh_hops))
+
+            elif self.family == AF_INET6:
+                if len(data) < self.IPV6_LEN:
+                    break
+                (data1, data2) = unpack('>QQ', data[:self.IPV6_LEN])
+                nexthop = IPv6Address(data1 << 64 | data2)
+                self.value.append((nexthop, rtnh_ifindex, rtnh_flags, rtnh_hops))
+
+            data = data[(rtnh_len-self.RTNH_LEN-self.HEADER_LEN):]
+
+        self.value = tuple(self.value)
+
+
+class AttributeIFLA_LINKINFO(Attribute):
+    """
+    value is a dictionary such as:
+
+    {
+        Link.IFLA_INFO_KIND : 'vlan',
+        Link.IFLA_INFO_DATA : {
+            Link.IFLA_VLAN_ID : vlanid,
+        }
+    }
+    """
+    def __init__(self, atype, string, family, logger):
+        Attribute.__init__(self, atype, string, logger)
+
+    def encode(self):
+        pack_layout = [self.HEADER_PACK]
+        payload = [0, self.atype | NLA_F_NESTED]
+        attr_length_index = 0
+
+        kind        = self.value.get(Link.IFLA_INFO_KIND)
+        slave_kind  = self.value.get(Link.IFLA_INFO_SLAVE_KIND)
+
+        if not slave_kind and kind not in ('vlan', 'macvlan', 'vxlan', 'bond', 'bridge'):
+            raise Exception('Unsupported IFLA_INFO_KIND %s' % kind)
+        elif not kind and slave_kind != 'bridge':
+            # only support brport for now.
+            raise Exception('Unsupported IFLA_INFO_SLAVE_KIND %s' % slave_kind)
+
+        # For now this assumes that all data will be packed in the native endian
+        # order (=). If a field is added that needs to be packed via network
+        # order (>) then some smarts will need to be added to split the pack_layout
+        # string at the >, split the payload and make the needed pack() calls.
+        #
+        # Until we cross that bridge though we will keep things nice and simple and
+        # pack everything via a single pack() call.
+        for (sub_attr_type, sub_attr_value) in self.value.iteritems():
+            sub_attr_pack_layout = ['=', 'HH']
+            sub_attr_payload = [0, sub_attr_type]
+            sub_attr_length_index = 0
+
+            if sub_attr_type == Link.IFLA_INFO_KIND:
+                sub_attr_pack_layout.append('%ds' % len(sub_attr_value))
+                sub_attr_payload.append(sub_attr_value)
+
+            elif sub_attr_type == Link.IFLA_INFO_DATA:
+
+                sub_attr_payload = [0, sub_attr_type | NLA_F_NESTED]
+
+                for (info_data_type, info_data_value) in sub_attr_value.iteritems():
+
+                    if kind == 'vlan':
+                        if info_data_type == Link.IFLA_VLAN_ID:
+                            sub_attr_pack_layout.append('HH')
+                            sub_attr_payload.append(6)  # length
+                            sub_attr_payload.append(info_data_type)
+
+                            # The vlan-id
+                            sub_attr_pack_layout.append('H')
+                            sub_attr_payload.append(info_data_value)
+
+                            # pad 2 bytes
+                            sub_attr_pack_layout.extend('xx')
+
+                        elif info_data_type == Link.IFLA_VLAN_PROTOCOL:
+                            sub_attr_pack_layout.append('HH')
+                            sub_attr_payload.append(6)  # length
+                            sub_attr_payload.append(info_data_type)
+
+                            # vlan protocol
+                            vlan_protocol = Link.ifla_vlan_protocol_dict.get(info_data_value)
+                            if not vlan_protocol:
+                                raise NotImplementedError('vlan protocol %s not implemented' % info_data_value)
+
+                            sub_attr_pack_layout.append('H')
+                            sub_attr_payload.append(htons(vlan_protocol))
+
+                            # pad 2 bytes
+                            sub_attr_pack_layout.extend('xx')
+                        else:
+                            self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for encoding IFLA_INFO_DATA vlan sub-attribute type %d' % info_data_type)
+
+                    elif kind == 'macvlan':
+                        if info_data_type == Link.IFLA_MACVLAN_MODE:
+                            sub_attr_pack_layout.append('HH')
+                            sub_attr_payload.append(8)  # length
+                            sub_attr_payload.append(info_data_type)
+
+                            # macvlan mode
+                            sub_attr_pack_layout.append('L')
+                            sub_attr_payload.append(info_data_value)
+
+                        else:
+                            self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for encoding IFLA_INFO_DATA macvlan sub-attribute type %d' % info_data_type)
+
+                    elif kind == 'vxlan':
+                        if info_data_type in (Link.IFLA_VXLAN_ID,
+                                              Link.IFLA_VXLAN_LINK,
+                                              Link.IFLA_VXLAN_AGEING,
+                                              Link.IFLA_VXLAN_LIMIT,
+                                              Link.IFLA_VXLAN_PORT_RANGE):
+                            sub_attr_pack_layout.append('HH')
+                            sub_attr_payload.append(8)  # length
+                            sub_attr_payload.append(info_data_type)
+
+                            sub_attr_pack_layout.append('L')
+                            sub_attr_payload.append(info_data_value)
+
+                        elif info_data_type in (Link.IFLA_VXLAN_GROUP,
+                                                Link.IFLA_VXLAN_LOCAL):
+                            sub_attr_pack_layout.append('HH')
+                            sub_attr_payload.append(8)  # length
+                            sub_attr_payload.append(info_data_type)
+
+                            sub_attr_pack_layout.append('L')
+
+                            reorder = unpack('<L', IPv4Address(info_data_value).packed)[0]
+                            sub_attr_payload.append(IPv4Address(reorder))
+
+                        elif info_data_type in (Link.IFLA_VXLAN_PORT,):
+                            sub_attr_pack_layout.append('HH')
+                            sub_attr_payload.append(6)
+                            sub_attr_payload.append(info_data_type)
+
+                            sub_attr_pack_layout.append('H')
+
+                            # byte swap
+                            swaped = pack(">H", info_data_value)
+                            sub_attr_payload.append(unpack("<H", swaped)[0])
+
+                            sub_attr_pack_layout.extend('xx')
+
+                        elif info_data_type in (Link.IFLA_VXLAN_TTL,
+                                                Link.IFLA_VXLAN_TOS,
+                                                Link.IFLA_VXLAN_LEARNING,
+                                                Link.IFLA_VXLAN_PROXY,
+                                                Link.IFLA_VXLAN_RSC,
+                                                Link.IFLA_VXLAN_L2MISS,
+                                                Link.IFLA_VXLAN_L3MISS,
+                                                Link.IFLA_VXLAN_UDP_CSUM,
+                                                Link.IFLA_VXLAN_UDP_ZERO_CSUM6_TX,
+                                                Link.IFLA_VXLAN_UDP_ZERO_CSUM6_RX,
+                                                Link.IFLA_VXLAN_REMCSUM_TX,
+                                                Link.IFLA_VXLAN_REMCSUM_RX,
+                                                Link.IFLA_VXLAN_REPLICATION_TYPE):
+                            sub_attr_pack_layout.append('HH')
+                            sub_attr_payload.append(6)
+                            sub_attr_payload.append(info_data_type)
+
+                            sub_attr_pack_layout.append('B')
+                            sub_attr_payload.append(info_data_value)
+                            sub_attr_pack_layout.extend('xxx')
+
+                        else:
+                            self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for encoding IFLA_INFO_DATA vxlan sub-attribute type %d' % info_data_type)
+
+                    elif kind == 'bond':
+                        if info_data_type in (Link.IFLA_BOND_AD_ACTOR_SYSTEM, ):
+                            sub_attr_pack_layout.append('HH')
+                            sub_attr_payload.append(10)  # length
+                            sub_attr_payload.append(info_data_type)
+
+                            sub_attr_pack_layout.append('6B')
+                            for mbyte in info_data_value.replace('.', ' ').replace(':', ' ').split():
+                                sub_attr_payload.append(int(mbyte, 16))
+                            sub_attr_pack_layout.extend('xx')
+
+                        elif info_data_type == Link.IFLA_BOND_AD_ACTOR_SYS_PRIO:
+                            sub_attr_pack_layout.append('HH')
+                            sub_attr_payload.append(6)  # length
+                            sub_attr_payload.append(info_data_type)
+
+                            # 2 bytes
+                            sub_attr_pack_layout.append('H')
+                            sub_attr_payload.append(int(info_data_value))
+
+                            # pad 2 bytes
+                            sub_attr_pack_layout.extend('xx')
+
+                        elif info_data_type == Link.IFLA_BOND_NUM_PEER_NOTIF:
+                            sub_attr_pack_layout.append('HH')
+                            sub_attr_payload.append(5)  # length
+                            sub_attr_payload.append(info_data_type)
+
+                            # 1 byte
+                            sub_attr_pack_layout.append('B')
+                            sub_attr_payload.append(int(info_data_value))
+
+                            # pad 3 bytes
+                            sub_attr_pack_layout.extend('xxx')
+
+
+                        elif info_data_type in (Link.IFLA_BOND_AD_LACP_RATE,
+                                                Link.IFLA_BOND_AD_LACP_BYPASS,
+                                                Link.IFLA_BOND_USE_CARRIER):
+                            # converts yes/no/on/off/0/1 strings to boolean value
+                            bool_value = self.get_bool_value(info_data_value)
+
+                            sub_attr_pack_layout.append('HH')
+                            sub_attr_payload.append(5)  # length
+                            sub_attr_payload.append(info_data_type)
+
+                            # 1 byte
+                            sub_attr_pack_layout.append('B')
+                            sub_attr_payload.append(bool_value)
+
+                            # pad 3 bytes
+                            sub_attr_pack_layout.extend('xxx')
+
+                        elif info_data_type == Link.IFLA_BOND_XMIT_HASH_POLICY:
+                            index = self.get_index(Link.ifla_bond_xmit_hash_policy_tbl,
+                                                   'bond xmit hash policy',
+                                                   info_data_value)
+
+                            sub_attr_pack_layout.append('HH')
+                            sub_attr_payload.append(5)  # length
+                            sub_attr_payload.append(info_data_type)
+
+                            # 1 byte
+                            sub_attr_pack_layout.append('B')
+                            sub_attr_payload.append(index)
+
+                            # pad 3 bytes
+                            sub_attr_pack_layout.extend('xxx')
+
+                        elif info_data_type == Link.IFLA_BOND_MODE:
+                            index = self.get_index(Link.ifla_bond_mode_tbl,
+                                                   'bond mode',
+                                                   info_data_value)
+
+                            sub_attr_pack_layout.append('HH')
+                            sub_attr_payload.append(5)  # length
+                            sub_attr_payload.append(info_data_type)
+
+                            # 1 byte
+                            sub_attr_pack_layout.append('B')
+                            sub_attr_payload.append(index)
+
+                            # pad 3 bytes
+                            sub_attr_pack_layout.extend('xxx')
+
+                        elif info_data_type in (Link.IFLA_BOND_MIIMON,
+                                                Link.IFLA_BOND_UPDELAY,
+                                                Link.IFLA_BOND_DOWNDELAY,
+                                                Link.IFLA_BOND_MIN_LINKS):
+                            sub_attr_pack_layout.append('HH')
+                            sub_attr_payload.append(8)  # length
+                            sub_attr_payload.append(info_data_type)
+
+                            sub_attr_pack_layout.append('L')
+                            sub_attr_payload.append(int(info_data_value))
+
+                        else:
+                            self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for encoding IFLA_INFO_DATA bond sub-attribute type %d' % info_data_type)
+
+                    elif kind == 'bridge':
+                        if info_data_type == Link.IFLA_BR_VLAN_PROTOCOL:
+                            sub_attr_pack_layout.append('HH')
+                            sub_attr_payload.append(6)  # length
+                            sub_attr_payload.append(info_data_type)
+
+                            # vlan protocol
+                            vlan_protocol = Link.ifla_vlan_protocol_dict.get(info_data_value)
+                            if not vlan_protocol:
+                                raise NotImplementedError('vlan protocol %s not implemented' % info_data_value)
+
+                            sub_attr_pack_layout.append('H')
+                            sub_attr_payload.append(htons(vlan_protocol))
+
+                            # pad 2 bytes
+                            sub_attr_pack_layout.extend('xx')
+
+                        # 1 byte
+                        elif info_data_type in (Link.IFLA_BR_VLAN_FILTERING,
+                                              Link.IFLA_BR_TOPOLOGY_CHANGE,
+                                              Link.IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
+                                              Link.IFLA_BR_MCAST_ROUTER,
+                                              Link.IFLA_BR_MCAST_SNOOPING,
+                                              Link.IFLA_BR_MCAST_QUERY_USE_IFADDR,
+                                              Link.IFLA_BR_MCAST_QUERIER,
+                                              Link.IFLA_BR_NF_CALL_IPTABLES,
+                                              Link.IFLA_BR_NF_CALL_IP6TABLES,
+                                              Link.IFLA_BR_NF_CALL_ARPTABLES,
+                                              Link.IFLA_BR_VLAN_STATS_ENABLED,
+                                              Link.IFLA_BR_MCAST_STATS_ENABLED,
+                                              Link.IFLA_BR_MCAST_IGMP_VERSION,
+                                              Link.IFLA_BR_MCAST_MLD_VERSION):
+                            sub_attr_pack_layout.append('HH')
+                            sub_attr_payload.append(5)  # length
+                            sub_attr_payload.append(info_data_type)
+
+                            # 1 byte
+                            sub_attr_pack_layout.append('B')
+                            sub_attr_payload.append(int(info_data_value))
+
+                            # pad 3 bytes
+                            sub_attr_pack_layout.extend('xxx')
+
+                        # 2 bytes
+                        elif info_data_type in (Link.IFLA_BR_PRIORITY,
+                                              Link.IFLA_BR_GROUP_FWD_MASK,
+                                              Link.IFLA_BR_ROOT_PORT,
+                                              Link.IFLA_BR_VLAN_DEFAULT_PVID):
+                            sub_attr_pack_layout.append('HH')
+                            sub_attr_payload.append(6)  # length
+                            sub_attr_payload.append(info_data_type)
+
+                            # 2 bytes
+                            sub_attr_pack_layout.append('H')
+                            sub_attr_payload.append(int(info_data_value))
+
+                            # pad 2 bytes
+                            sub_attr_pack_layout.extend('xx')
+
+                        # 4 bytes
+                        elif info_data_type in (Link.IFLA_BR_FORWARD_DELAY,
+                                                Link.IFLA_BR_HELLO_TIME,
+                                                Link.IFLA_BR_MAX_AGE,
+                                                Link.IFLA_BR_AGEING_TIME,
+                                                Link.IFLA_BR_STP_STATE,
+                                                Link.IFLA_BR_ROOT_PATH_COST,
+                                                Link.IFLA_BR_MCAST_QUERIER,
+                                                Link.IFLA_BR_MCAST_HASH_ELASTICITY,
+                                                Link.IFLA_BR_MCAST_HASH_MAX,
+                                                Link.IFLA_BR_MCAST_LAST_MEMBER_CNT,
+                                                Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT):
+                            sub_attr_pack_layout.append('HH')
+                            sub_attr_payload.append(8)  # length
+                            sub_attr_payload.append(info_data_type)
+
+                            sub_attr_pack_layout.append('L')
+                            sub_attr_payload.append(int(info_data_value))
+
+                        # 8 bytes
+                        elif info_data_type in (Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL,
+                                                Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL,
+                                                Link.IFLA_BR_MCAST_QUERIER_INTVL,
+                                                Link.IFLA_BR_MCAST_QUERY_INTVL,
+                                                Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL,
+                                                Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL):
+                            sub_attr_pack_layout.append('HH')
+                            sub_attr_payload.append(12)  # length
+                            sub_attr_payload.append(info_data_type)
+
+                            sub_attr_pack_layout.append('Q')
+                            sub_attr_payload.append(int(info_data_value))
+
+                        else:
+                            self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for encoding IFLA_INFO_DATA bridge sub-attribute type %d' % info_data_type)
+
+            elif sub_attr_type == Link.IFLA_INFO_SLAVE_DATA:
+
+                sub_attr_payload = [0, sub_attr_type | NLA_F_NESTED]
+
+                for (info_slave_data_type, info_slave_data_value) in sub_attr_value.iteritems():
+
+                    if slave_kind == 'bridge':
+
+                        # 1 byte
+                        if info_slave_data_type in (Link.IFLA_BRPORT_STATE,
+                                                    Link.IFLA_BRPORT_MODE,
+                                                    Link.IFLA_BRPORT_GUARD,
+                                                    Link.IFLA_BRPORT_PROTECT,
+                                                    Link.IFLA_BRPORT_FAST_LEAVE,
+                                                    Link.IFLA_BRPORT_LEARNING,
+                                                    Link.IFLA_BRPORT_UNICAST_FLOOD,
+                                                    Link.IFLA_BRPORT_PROXYARP,
+                                                    Link.IFLA_BRPORT_LEARNING_SYNC,
+                                                    Link.IFLA_BRPORT_PROXYARP_WIFI,
+                                                    Link.IFLA_BRPORT_TOPOLOGY_CHANGE_ACK,
+                                                    Link.IFLA_BRPORT_CONFIG_PENDING,
+                                                    Link.IFLA_BRPORT_MULTICAST_ROUTER,
+                                                    Link.IFLA_BRPORT_MCAST_FLOOD,
+                                                    Link.IFLA_BRPORT_MCAST_TO_UCAST,
+                                                    Link.IFLA_BRPORT_VLAN_TUNNEL,
+                                                    Link.IFLA_BRPORT_BCAST_FLOOD,
+                                                    Link.IFLA_BRPORT_PEER_LINK,
+                                                    Link.IFLA_BRPORT_DUAL_LINK,
+                                                    Link.IFLA_BRPORT_ARP_SUPPRESS,
+                                                    Link.IFLA_BRPORT_DOWN_PEERLINK_REDIRECT):
+                            sub_attr_pack_layout.append('HH')
+                            sub_attr_payload.append(5)  # length
+                            sub_attr_payload.append(info_slave_data_type)
+
+                            # 1 byte
+                            sub_attr_pack_layout.append('B')
+                            sub_attr_payload.append(int(info_slave_data_value))
+
+                            # pad 3 bytes
+                            sub_attr_pack_layout.extend('xxx')
+
+                        # 2 bytes
+                        elif info_slave_data_type in (Link.IFLA_BRPORT_PRIORITY,
+                                                      Link.IFLA_BRPORT_DESIGNATED_PORT,
+                                                      Link.IFLA_BRPORT_DESIGNATED_COST,
+                                                      Link.IFLA_BRPORT_ID,
+                                                      Link.IFLA_BRPORT_NO,
+                                                      Link.IFLA_BRPORT_GROUP_FWD_MASK,
+                                                      Link.IFLA_BRPORT_GROUP_FWD_MASKHI):
+                            sub_attr_pack_layout.append('HH')
+                            sub_attr_payload.append(6)  # length
+                            sub_attr_payload.append(info_slave_data_type)
+
+                            # 2 bytes
+                            sub_attr_pack_layout.append('H')
+                            sub_attr_payload.append(int(info_slave_data_value))
+
+                            # pad 2 bytes
+                            sub_attr_pack_layout.extend('xx')
+
+                        # 4 bytes
+                        elif info_slave_data_type == Link.IFLA_BRPORT_COST:
+                            sub_attr_pack_layout.append('HH')
+                            sub_attr_payload.append(8)  # length
+                            sub_attr_payload.append(info_slave_data_type)
+
+                            sub_attr_pack_layout.append('L')
+                            sub_attr_payload.append(int(info_slave_data_value))
+
+                        else:
+                            self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for encoding IFLA_INFO_SLAVE_DATA bond sub-attribute type %d' % info_slave_data_type)
+
+                    else:
+                        self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for encoding IFLA_LINKINFO kind %s' % slave_kind)
+
+            else:
+                self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for encoding IFLA_LINKINFO sub-attribute type %d' % sub_attr_type)
+                continue
+
+            sub_attr_length = calcsize(''.join(sub_attr_pack_layout))
+            sub_attr_payload[sub_attr_length_index] = sub_attr_length
+
+            # add padding
+            for x in xrange(self.pad_bytes_needed(sub_attr_length)):
+                sub_attr_pack_layout.append('x')
+
+            # The [1:] is to remove the leading = so that when we do the ''.join() later
+            # we do not end up with an = in the middle of the pack layout string. There
+            # will be an = at the beginning via self.HEADER_PACK
+            sub_attr_pack_layout = sub_attr_pack_layout[1:]
+
+            # Now extend the ovarall attribute pack_layout/payload to include this sub-attribute
+            pack_layout.extend(sub_attr_pack_layout)
+            payload.extend(sub_attr_payload)
+
+        pack_layout = ''.join(pack_layout)
+
+        # Fill in the length field
+        length = calcsize(pack_layout)
+        payload[attr_length_index] = length
+
+        raw = pack(pack_layout, *payload)
+        raw = self.pad(length, raw)
+        return raw
+
+    def get_bool_value(self, value, default=None):
+        try:
+            return value_to_bool_dict[value]
+        except KeyError:
+            self.log.debug('%s: unsupported boolean value' % value)
+            return default
+
+    def get_index(self, tbl, attr, value, default=None):
+        try:
+            return tbl[value]
+        except KeyError:
+            self.log.debug('unsupported %s value %s (%s)' % (attr, value, tbl.keys()))
+            return default
+
+    def decode(self, parent_msg, data):
+        """
+        value is a dictionary such as:
+
+        {
+            Link.IFLA_INFO_KIND : 'vlan',
+            Link.IFLA_INFO_DATA : {
+                Link.IFLA_VLAN_ID : vlanid,
+            }
+        }
+        """
+        self.decode_length_type(data)
+        self.value = {}
+
+        data = self.data[4:]
+
+        # IFLA_MACVLAN_MODE and IFLA_VLAN_ID both have a value of 1 and both are
+        # valid IFLA_INFO_DATA entries :( The sender must TX IFLA_INFO_KIND
+        # first in order for us to know if "1" is IFLA_MACVLAN_MODE vs IFLA_VLAN_ID.
+
+        while data:
+            (sub_attr_length, sub_attr_type) = unpack('=HH', data[:4])
+            sub_attr_end = padded_length(sub_attr_length)
+
+            if not sub_attr_length:
+                self.log.error('parsed a zero length sub-attr')
+                return
+
+            if sub_attr_type in (Link.IFLA_INFO_KIND, Link.IFLA_INFO_SLAVE_KIND):
+                self.value[sub_attr_type] = remove_trailing_null(unpack('%ds' % (sub_attr_length - 4), data[4:sub_attr_length])[0])
+
+            elif sub_attr_type == Link.IFLA_INFO_SLAVE_DATA:
+                sub_attr_data = data[4:sub_attr_end]
+
+                ifla_info_slave_data = dict()
+                ifla_info_slave_kind = self.value.get(Link.IFLA_INFO_SLAVE_KIND)
+
+                if not ifla_info_slave_kind:
+                    self.log.warning('IFLA_INFO_SLAVE_KIND is not known...we cannot parse IFLA_INFO_SLAVE_DATA')
+                else:
+                    while sub_attr_data:
+                        (info_data_length, info_data_type) = unpack('=HH', sub_attr_data[:4])
+                        info_data_end = padded_length(info_data_length)
+                        try:
+                            if ifla_info_slave_kind == 'bridge':
+                                # 1 byte
+                                if info_data_type in (Link.IFLA_BRPORT_STATE,
+                                                      Link.IFLA_BRPORT_MODE,
+                                                      Link.IFLA_BRPORT_GUARD,
+                                                      Link.IFLA_BRPORT_PROTECT,
+                                                      Link.IFLA_BRPORT_FAST_LEAVE,
+                                                      Link.IFLA_BRPORT_LEARNING,
+                                                      Link.IFLA_BRPORT_UNICAST_FLOOD,
+                                                      Link.IFLA_BRPORT_PROXYARP,
+                                                      Link.IFLA_BRPORT_LEARNING_SYNC,
+                                                      Link.IFLA_BRPORT_PROXYARP_WIFI,
+                                                      Link.IFLA_BRPORT_TOPOLOGY_CHANGE_ACK,
+                                                      Link.IFLA_BRPORT_CONFIG_PENDING,
+                                                      Link.IFLA_BRPORT_MULTICAST_ROUTER,
+                                                      Link.IFLA_BRPORT_MCAST_FLOOD,
+                                                      Link.IFLA_BRPORT_MCAST_TO_UCAST,
+                                                      Link.IFLA_BRPORT_VLAN_TUNNEL,
+                                                      Link.IFLA_BRPORT_PEER_LINK,
+                                                      Link.IFLA_BRPORT_DUAL_LINK,
+                                                      Link.IFLA_BRPORT_ARP_SUPPRESS,
+                                                      Link.IFLA_BRPORT_DOWN_PEERLINK_REDIRECT):
+                                    ifla_info_slave_data[info_data_type] = unpack('=B', sub_attr_data[4])[0]
+
+                                # 2 bytes
+                                elif info_data_type in (Link.IFLA_BRPORT_PRIORITY,
+                                                        Link.IFLA_BRPORT_DESIGNATED_PORT,
+                                                        Link.IFLA_BRPORT_DESIGNATED_COST,
+                                                        Link.IFLA_BRPORT_ID,
+                                                        Link.IFLA_BRPORT_NO,
+                                                        Link.IFLA_BRPORT_GROUP_FWD_MASK,
+                                                        Link.IFLA_BRPORT_GROUP_FWD_MASKHI):
+                                    ifla_info_slave_data[info_data_type] = unpack('=H', sub_attr_data[4:6])[0]
+
+                                # 4 bytes
+                                elif info_data_type == Link.IFLA_BRPORT_COST:
+                                    ifla_info_slave_data[info_data_type] = unpack('=L', sub_attr_data[4:8])[0]
+
+                            elif ifla_info_slave_kind == 'bond':
+
+                                # 1 byte
+                                if info_data_type in (
+                                        Link.IFLA_BOND_SLAVE_STATE,
+                                        Link.IFLA_BOND_SLAVE_MII_STATUS,
+                                        Link.IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE,
+                                        Link.IFLA_BOND_SLAVE_AD_RX_BYPASS,
+                                ):
+                                    ifla_info_slave_data[info_data_type] = unpack('=B', sub_attr_data[4])[0]
+
+                                # 2 bytes
+                                elif info_data_type in (
+                                        Link.IFLA_BOND_SLAVE_QUEUE_ID,
+                                        Link.IFLA_BOND_SLAVE_AD_AGGREGATOR_ID
+                                ):
+                                    ifla_info_slave_data[info_data_type] = unpack('=H', sub_attr_data[4:6])[0]
+
+                                # 4 bytes
+                                elif info_data_type == (
+                                        Link.IFLA_BOND_SLAVE_PERM_HWADDR,
+                                        Link.IFLA_BOND_SLAVE_LINK_FAILURE_COUNT
+                                ):
+                                    ifla_info_slave_data[info_data_type] = unpack('=L', sub_attr_data[4:8])[0]
+
+                        except Exception as e:
+                            self.log.debug('%s: attribute %s: %s'
+                                            % (self.value[Link.IFLA_INFO_SLAVE_KIND],
+                                               info_data_type,
+                                               str(e)))
+                        sub_attr_data = sub_attr_data[info_data_end:]
+
+                self.value[Link.IFLA_INFO_SLAVE_DATA] = ifla_info_slave_data
+
+            elif sub_attr_type == Link.IFLA_INFO_DATA:
+                sub_attr_data = data[4:sub_attr_end]
+                self.value[Link.IFLA_INFO_DATA] = {}
+
+                ifla_info_kind = self.value.get(Link.IFLA_INFO_KIND)
+                if not ifla_info_kind:
+                    self.log.warning('IFLA_INFO_KIND is not known...we cannot parse IFLA_INFO_DATA')
+                else:
+                    while sub_attr_data:
+                        (info_data_length, info_data_type) = unpack('=HH', sub_attr_data[:4])
+                        info_data_end = padded_length(info_data_length)
+                        try:
+                            if ifla_info_kind == 'vlan':
+                                if info_data_type == Link.IFLA_VLAN_ID:
+                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=H', sub_attr_data[4:6])[0]
+
+                                elif info_data_type == Link.IFLA_VLAN_PROTOCOL:
+                                    hex_value = '0x%s' % sub_attr_data[4:6].encode('hex')
+                                    vlan_protocol = Link.ifla_vlan_protocol_dict.get(int(hex_value, base=16))
+
+                                    if vlan_protocol:
+                                        self.value[Link.IFLA_INFO_DATA][info_data_type] = vlan_protocol
+                                    else:
+                                        self.log.warning('IFLA_VLAN_PROTOCOL: cannot decode vlan protocol %s' % hex_value)
+
+                                else:
+                                    self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for decoding IFLA_INFO_KIND vlan type %s (%d), length %d, padded to %d' %
+                                                (parent_msg.get_ifla_vlan_string(info_data_type), info_data_type, info_data_length, info_data_end))
+
+                            elif ifla_info_kind == 'macvlan':
+                                if info_data_type == Link.IFLA_MACVLAN_MODE:
+                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[0]
+                                else:
+                                    self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for decoding IFLA_INFO_KIND macvlan type %s (%d), length %d, padded to %d' %
+                                                (parent_msg.get_ifla_macvlan_string(info_data_type), info_data_type, info_data_length, info_data_end))
+
+                            elif ifla_info_kind == 'vxlan':
+
+                                # IPv4Address
+                                if info_data_type in (Link.IFLA_VXLAN_GROUP,
+                                                      Link.IFLA_VXLAN_LOCAL):
+                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = IPv4Address(unpack('>L', sub_attr_data[4:8])[0])
+
+                                # 4-byte int
+                                elif info_data_type in (Link.IFLA_VXLAN_ID,
+                                                        Link.IFLA_VXLAN_LINK,
+                                                        Link.IFLA_VXLAN_AGEING,
+                                                        Link.IFLA_VXLAN_LIMIT,
+                                                        Link.IFLA_VXLAN_PORT_RANGE):
+                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[0]
+
+                                # 2-byte int
+                                elif info_data_type in (Link.IFLA_VXLAN_PORT, ):
+                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('!H', sub_attr_data[4:6])[0]
+                                    # The form '!' is available for those poor souls who claim they can't
+                                    # remember whether network byte order is big-endian or little-endian.
+
+                                # 1-byte int
+                                elif info_data_type in (Link.IFLA_VXLAN_TTL,
+                                                        Link.IFLA_VXLAN_TOS,
+                                                        Link.IFLA_VXLAN_LEARNING,
+                                                        Link.IFLA_VXLAN_PROXY,
+                                                        Link.IFLA_VXLAN_RSC,
+                                                        Link.IFLA_VXLAN_L2MISS,
+                                                        Link.IFLA_VXLAN_L3MISS,
+                                                        Link.IFLA_VXLAN_UDP_CSUM,
+                                                        Link.IFLA_VXLAN_UDP_ZERO_CSUM6_TX,
+                                                        Link.IFLA_VXLAN_UDP_ZERO_CSUM6_RX,
+                                                        Link.IFLA_VXLAN_REMCSUM_TX,
+                                                        Link.IFLA_VXLAN_REMCSUM_RX,
+                                                        Link.IFLA_VXLAN_REPLICATION_TYPE):
+                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=B', sub_attr_data[4])[0]
+
+                                else:
+                                    # sub_attr_end = padded_length(sub_attr_length)
+                                    self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for decoding IFLA_INFO_KIND vxlan type %s (%d), length %d, padded to %d' %
+                                                (parent_msg.get_ifla_vxlan_string(info_data_type), info_data_type, info_data_length, info_data_end))
+
+                            elif ifla_info_kind == 'bond':
+
+                                if info_data_type in (Link.IFLA_BOND_AD_INFO, ):
+                                    ad_attr_data = sub_attr_data[4:info_data_end]
+                                    self.value[Link.IFLA_INFO_DATA][Link.IFLA_BOND_AD_INFO] = {}
+
+                                    while ad_attr_data:
+                                        (ad_data_length, ad_data_type) = unpack('=HH', ad_attr_data[:4])
+                                        ad_data_end = padded_length(ad_data_length)
+
+                                        if ad_data_type in (Link.IFLA_BOND_AD_INFO_PARTNER_MAC,):
+                                            (data1, data2) = unpack('>LHxx', ad_attr_data[4:12])
+                                            self.value[Link.IFLA_INFO_DATA][Link.IFLA_BOND_AD_INFO][ad_data_type] = mac_int_to_str(data1 << 16 | data2)
+
+                                        ad_attr_data = ad_attr_data[ad_data_end:]
+
+                                # 1-byte int
+                                elif info_data_type in (Link.IFLA_BOND_MODE,
+                                                        Link.IFLA_BOND_USE_CARRIER,
+                                                        Link.IFLA_BOND_AD_LACP_RATE,
+                                                        Link.IFLA_BOND_AD_LACP_BYPASS,
+                                                        Link.IFLA_BOND_XMIT_HASH_POLICY,
+                                                        Link.IFLA_BOND_NUM_PEER_NOTIF):
+                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=B', sub_attr_data[4])[0]
+
+                                # 2-bytes int
+                                elif info_data_type == Link.IFLA_BOND_AD_ACTOR_SYS_PRIO:
+                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=H', sub_attr_data[4:6])[0]
+
+                                # 4-bytes int
+                                elif info_data_type in (Link.IFLA_BOND_MIIMON,
+                                                        Link.IFLA_BOND_UPDELAY,
+                                                        Link.IFLA_BOND_DOWNDELAY,
+                                                        Link.IFLA_BOND_MIN_LINKS):
+                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[0]
+
+                                # mac address
+                                elif info_data_type in (Link.IFLA_BOND_AD_ACTOR_SYSTEM, ):
+                                    (data1, data2) = unpack('>LHxx', sub_attr_data[4:12])
+                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = mac_int_to_str(data1 << 16 | data2)
+
+                                else:
+                                    self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for decoding IFLA_INFO_KIND bond type %s (%d), length %d, padded to %d' %
+                                                (parent_msg.get_ifla_bond_string(info_data_type), info_data_type, info_data_length, info_data_end))
+
+                            elif ifla_info_kind == 'bridge':
+                                # 4 bytes
+                                if info_data_type in (Link.IFLA_BR_AGEING_TIME,
+                                                      Link.IFLA_BR_FORWARD_DELAY,
+                                                      Link.IFLA_BR_HELLO_TIME,
+                                                      Link.IFLA_BR_MAX_AGE,
+                                                      Link.IFLA_BR_STP_STATE,
+                                                      Link.IFLA_BR_ROOT_PATH_COST,
+                                                      Link.IFLA_BR_MCAST_HASH_ELASTICITY,
+                                                      Link.IFLA_BR_MCAST_HASH_MAX,
+                                                      Link.IFLA_BR_MCAST_LAST_MEMBER_CNT,
+                                                      Link.IFLA_BR_MCAST_STARTUP_QUERY_CNT):
+                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[0]
+
+                                # 2 bytes
+                                elif info_data_type in (Link.IFLA_BR_PRIORITY,
+                                                        Link.IFLA_BR_GROUP_FWD_MASK,
+                                                        Link.IFLA_BR_ROOT_PORT,
+                                                        Link.IFLA_BR_VLAN_DEFAULT_PVID):
+                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=H', sub_attr_data[4:6])[0]
+
+                                elif info_data_type == Link.IFLA_BR_VLAN_PROTOCOL:
+                                    hex_value = '0x%s' % sub_attr_data[4:6].encode('hex')
+                                    vlan_protocol = Link.ifla_vlan_protocol_dict.get(int(hex_value, base=16))
+
+                                    if vlan_protocol:
+                                        self.value[Link.IFLA_INFO_DATA][info_data_type] = vlan_protocol
+                                    else:
+                                        self.log.warning('IFLA_VLAN_PROTOCOL: cannot decode vlan protocol %s' % hex_value)
+
+                                # 8 bytes
+                                elif info_data_type in (Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL,
+                                                        Link.IFLA_BR_MCAST_QUERIER_INTVL,
+                                                        Link.IFLA_BR_MCAST_LAST_MEMBER_INTVL,
+                                                        Link.IFLA_BR_MCAST_MEMBERSHIP_INTVL,
+                                                        Link.IFLA_BR_MCAST_QUERIER_INTVL,
+                                                        Link.IFLA_BR_MCAST_QUERY_INTVL,
+                                                        Link.IFLA_BR_MCAST_QUERY_RESPONSE_INTVL,
+                                                        Link.IFLA_BR_MCAST_STARTUP_QUERY_INTVL):
+                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=Q', sub_attr_data[4:12])[0]
+
+                                # 1 bytes
+                                elif info_data_type in (Link.IFLA_BR_VLAN_FILTERING,
+                                                        Link.IFLA_BR_TOPOLOGY_CHANGE,
+                                                        Link.IFLA_BR_TOPOLOGY_CHANGE_DETECTED,
+                                                        Link.IFLA_BR_MCAST_ROUTER,
+                                                        Link.IFLA_BR_MCAST_SNOOPING,
+                                                        Link.IFLA_BR_MCAST_QUERY_USE_IFADDR,
+                                                        Link.IFLA_BR_MCAST_QUERIER,
+                                                        Link.IFLA_BR_NF_CALL_IPTABLES,
+                                                        Link.IFLA_BR_NF_CALL_IP6TABLES,
+                                                        Link.IFLA_BR_NF_CALL_ARPTABLES,
+                                                        Link.IFLA_BR_VLAN_STATS_ENABLED,
+                                                        Link.IFLA_BR_MCAST_STATS_ENABLED,
+                                                        Link.IFLA_BR_MCAST_IGMP_VERSION,
+                                                        Link.IFLA_BR_MCAST_MLD_VERSION):
+                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=B', sub_attr_data[4])[0]
+                                else:
+                                    self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for decoding IFLA_INFO_KIND bridge type %s (%d), length %d, padded to %d' %
+                                                (parent_msg.get_ifla_br_string(info_data_type), info_data_type, info_data_length, info_data_end))
+
+                            elif ifla_info_kind == 'vrf':
+
+                                if info_data_type in (Link.IFLA_VRF_TABLE,):
+                                    self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[0]
+
+
+                            else:
+                                self.log.log(SYSLOG_EXTRA_DEBUG, "Add support for decoding IFLA_INFO_KIND %s (%d), length %d, padded to %d" %
+                                            (ifla_info_kind, info_data_type, info_data_length, info_data_end))
+
+                        except Exception as e:
+                            self.log.debug('%s: attribute %s: %s'
+                                           % (self.value[Link.IFLA_INFO_KIND],
+                                              info_data_type,
+                                              str(e)))
+                        sub_attr_data = sub_attr_data[info_data_end:]
+
+            else:
+                self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for decoding IFLA_LINKINFO sub-attribute type %s (%d), length %d, padded to %d' %
+                            (parent_msg.get_ifla_info_string(sub_attr_type), sub_attr_type, sub_attr_length, sub_attr_end))
+
+            data = data[sub_attr_end:]
+
+        # self.log.info('IFLA_LINKINFO values %s' % pformat(self.value))
+
+    def dump_lines(self, dump_buffer, line_number, color):
+        line_number = self.dump_first_line(dump_buffer, line_number, color)
+        extra = ''
+
+        next_sub_attr_line = 0
+        sub_attr_line = True
+
+        for x in xrange(1, self.attr_end/4):
+            start = x * 4
+            end = start + 4
+
+            if line_number == next_sub_attr_line:
+                sub_attr_line = True
+
+            if sub_attr_line:
+                sub_attr_line = False
+
+                (sub_attr_length, sub_attr_type) = unpack('=HH', self.data[start:start+4])
+                sub_attr_end = padded_length(sub_attr_length)
+
+                next_sub_attr_line = line_number + (sub_attr_end/4)
+
+                if sub_attr_end == sub_attr_length:
+                    padded_to = ', '
+                else:
+                    padded_to = ' padded to %d, ' % sub_attr_end
+
+                extra = 'Nested Attribute - Length %s (%d)%s Type %s (%d) %s' % \
+                        (zfilled_hex(sub_attr_length, 4), sub_attr_length,
+                         padded_to,
+                         zfilled_hex(sub_attr_type, 4), sub_attr_type,
+                         Link.ifla_info_to_string.get(sub_attr_type))
+            else:
+                extra = ''
+
+            dump_buffer.append(data_to_color_text(line_number, color, self.data[start:end], extra))
+            line_number += 1
+
+        return line_number
+
+    def get_pretty_value(self, obj=None):
+
+        if obj and callable(obj):
+            return obj(self.value)
+
+        value_pretty            = self.value
+        ifla_info_kind          = self.value.get(Link.IFLA_INFO_KIND)
+        ifla_info_slave_kind    = self.value.get(Link.IFLA_INFO_SLAVE_KIND)
+
+        kind_dict = dict()
+
+        # We do this so we can print a more human readable dictionary
+        # with the names of the nested keys instead of their numbers
+
+        # Most of these are placeholders...we need to add support
+        # for more human readable dictionaries for bond, bridge, etc
+        kind_dict[Link.IFLA_INFO_DATA] = {
+            'bond':     Link.ifla_bond_to_string,
+            'vlan':     Link.ifla_vlan_to_string,
+            'vxlan':    Link.ifla_vxlan_to_string,
+            'bridge':   Link.ifla_br_to_string,
+            'macvlan':  Link.ifla_macvlan_to_string
+        }.get(ifla_info_kind, {})
+
+        kind_dict[Link.IFLA_INFO_SLAVE_DATA] = {
+            'bridge': Link.ifla_brport_to_string,
+            'bond': Link.ifla_bond_slave_to_string
+        }.get(ifla_info_slave_kind, {})
+
+        if ifla_info_kind or ifla_info_slave_kind:
+            value_pretty = {}
+
+            for (sub_key, sub_value) in self.value.iteritems():
+                sub_key_pretty = "(%2d) %s" % (sub_key, Link.ifla_info_to_string.get(sub_key, 'UNKNOWN'))
+                sub_value_pretty = sub_value
+
+                if sub_key in (Link.IFLA_INFO_DATA, Link.IFLA_INFO_SLAVE_DATA):
+                    kind_to_string_dict = kind_dict.get(sub_key, {})
+                    sub_value_pretty = {}
+
+                    for (sub_sub_key, sub_sub_value) in sub_value.iteritems():
+                        sub_sub_key_pretty = "(%2d) %s" % (sub_sub_key, kind_to_string_dict.get(sub_sub_key, 'UNKNOWN'))
+                        sub_value_pretty[sub_sub_key_pretty] = sub_sub_value
+
+                value_pretty[sub_key_pretty] = sub_value_pretty
+
+        return value_pretty
+
+
+class AttributeIFLA_PROTINFO(Attribute):
+    """
+    IFLA_PROTINFO nested attributes.
+    """
+    def __init__(self, atype, string, family, logger):
+        Attribute.__init__(self, atype, string, logger)
+        self.family = family
+
+    def encode(self):
+        pack_layout = [self.HEADER_PACK]
+        payload = [0, self.atype | NLA_F_NESTED]
+        attr_length_index = 0
+
+        if self.family not in (AF_BRIDGE,):
+            raise Exception('Unsupported IFLA_PROTINFO family %d' % self.family)
+
+        # For now this assumes that all data will be packed in the native endian
+        # order (=). If a field is added that needs to be packed via network
+        # order (>) then some smarts will need to be added to split the pack_layout
+        # string at the >, split the payload and make the needed pack() calls.
+        #
+        # Until we cross that bridge though we will keep things nice and simple and
+        # pack everything via a single pack() call.
+        for (sub_attr_type, sub_attr_value) in self.value.iteritems():
+            sub_attr_pack_layout = ['=', 'HH']
+            sub_attr_payload = [0, sub_attr_type]
+            sub_attr_length_index = 0
+
+            if self.family == AF_BRIDGE:
+                # 1 Byte attributes
+                if sub_attr_type in (Link.IFLA_BRPORT_STATE,
+                                     Link.IFLA_BRPORT_MODE,
+                                     Link.IFLA_BRPORT_GUARD,
+                                     Link.IFLA_BRPORT_PROTECT,
+                                     Link.IFLA_BRPORT_FAST_LEAVE,
+                                     Link.IFLA_BRPORT_LEARNING,
+                                     Link.IFLA_BRPORT_UNICAST_FLOOD,
+                                     Link.IFLA_BRPORT_PROXYARP,
+                                     Link.IFLA_BRPORT_LEARNING_SYNC,
+                                     Link.IFLA_BRPORT_PROXYARP_WIFI,
+                                     Link.IFLA_BRPORT_TOPOLOGY_CHANGE_ACK,
+                                     Link.IFLA_BRPORT_CONFIG_PENDING,
+                                     Link.IFLA_BRPORT_FLUSH,
+                                     Link.IFLA_BRPORT_MULTICAST_ROUTER,
+                                     Link.IFLA_BRPORT_PEER_LINK,
+                                     Link.IFLA_BRPORT_DUAL_LINK,
+                                     Link.IFLA_BRPORT_ARP_SUPPRESS,
+                                     Link.IFLA_BRPORT_DOWN_PEERLINK_REDIRECT):
+                    sub_attr_pack_layout.append('B')
+                    sub_attr_payload.append(sub_attr_value)
+                    sub_attr_pack_layout.extend('xxx')
+
+                # 2 Byte attributes
+                elif sub_attr_type in (Link.IFLA_BRPORT_PRIORITY,
+                                       Link.IFLA_BRPORT_DESIGNATED_PORT,
+                                       Link.IFLA_BRPORT_DESIGNATED_COST,
+                                       Link.IFLA_BRPORT_ID,
+                                       Link.IFLA_BRPORT_NO):
+                    sub_attr_pack_layout.append('H')
+                    sub_attr_payload.append(sub_attr_value)
+                    sub_attr_pack_layout.extend('xx')
+
+                # 4 Byte attributes
+                elif sub_attr_type in (Link.IFLA_BRPORT_COST,):
+                    sub_attr_pack_layout.append('L')
+                    sub_attr_payload.append(sub_attr_value)
+
+                # 8 Byte attributes
+                elif sub_attr_type in (Link.IFLA_BRPORT_MESSAGE_AGE_TIMER,
+                                       Link.IFLA_BRPORT_FORWARD_DELAY_TIMER,
+                                       Link.IFLA_BRPORT_HOLD_TIMER):
+                    sub_attr_pack_layout.append('Q')
+                    sub_attr_payload.append(sub_attr_value)
+
+                else:
+                    self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for encoding IFLA_PROTINFO sub-attribute type %d' % sub_attr_type)
+
+            sub_attr_length = calcsize(''.join(sub_attr_pack_layout))
+            sub_attr_payload[sub_attr_length_index] = sub_attr_length
+
+            # add padding
+            for x in xrange(self.pad_bytes_needed(sub_attr_length)):
+                sub_attr_pack_layout.append('x')
+
+            # The [1:] is to remove the leading = so that when we do the ''.join() later
+            # we do not end up with an = in the middle of the pack layout string. There
+            # will be an = at the beginning via self.HEADER_PACK
+            sub_attr_pack_layout = sub_attr_pack_layout[1:]
+
+            # Now extend the ovarall attribute pack_layout/payload to include this sub-attribute
+            pack_layout.extend(sub_attr_pack_layout)
+            payload.extend(sub_attr_payload)
+
+        pack_layout = ''.join(pack_layout)
+
+        # Fill in the length field
+        length = calcsize(pack_layout)
+        payload[attr_length_index] = length
+
+        raw = pack(pack_layout, *payload)
+        raw = self.pad(length, raw)
+        return raw
+
+    def decode(self, parent_msg, data):
+        """
+        value is a dictionary such as:
+        {
+            Link.IFLA_BRPORT_STATE : 3,
+            Link.IFLA_BRPORT_PRIORITY : 8
+            Link.IFLA_BRPORT_COST : 2
+            ...
+        }
+        """
+        self.decode_length_type(data)
+        self.value = {}
+
+        data = self.data[4:]
+
+        while data:
+            (sub_attr_length, sub_attr_type) = unpack('=HH', data[:4])
+            sub_attr_end = padded_length(sub_attr_length)
+
+            if not sub_attr_length:
+                self.log.error('parsed a zero length sub-attr')
+                return
+
+            if self.family == AF_BRIDGE:
+
+                # 1 Byte attributes
+                if sub_attr_type in (Link.IFLA_BRPORT_STATE,
+                                     Link.IFLA_BRPORT_MODE,
+                                     Link.IFLA_BRPORT_GUARD,
+                                     Link.IFLA_BRPORT_PROTECT,
+                                     Link.IFLA_BRPORT_FAST_LEAVE,
+                                     Link.IFLA_BRPORT_LEARNING,
+                                     Link.IFLA_BRPORT_UNICAST_FLOOD,
+                                     Link.IFLA_BRPORT_PROXYARP,
+                                     Link.IFLA_BRPORT_LEARNING_SYNC,
+                                     Link.IFLA_BRPORT_PROXYARP_WIFI,
+                                     Link.IFLA_BRPORT_TOPOLOGY_CHANGE_ACK,
+                                     Link.IFLA_BRPORT_CONFIG_PENDING,
+                                     Link.IFLA_BRPORT_FLUSH,
+                                     Link.IFLA_BRPORT_MULTICAST_ROUTER,
+                                     Link.IFLA_BRPORT_PEER_LINK,
+                                     Link.IFLA_BRPORT_DUAL_LINK,
+                                     Link.IFLA_BRPORT_ARP_SUPPRESS,
+                                     Link.IFLA_BRPORT_DOWN_PEERLINK_REDIRECT):
+                    self.value[sub_attr_type] = unpack('=B', data[4])[0]
+
+                # 2 Byte attributes
+                elif sub_attr_type in (Link.IFLA_BRPORT_PRIORITY,
+                                       Link.IFLA_BRPORT_DESIGNATED_PORT,
+                                       Link.IFLA_BRPORT_DESIGNATED_COST,
+                                       Link.IFLA_BRPORT_ID,
+                                       Link.IFLA_BRPORT_NO):
+                    self.value[sub_attr_type] = unpack('=H', data[4:6])[0]
+
+                # 4 Byte attributes
+                elif sub_attr_type in (Link.IFLA_BRPORT_COST,):
+                    self.value[sub_attr_type] = unpack('=L', data[4:8])[0]
+
+                # 8 Byte attributes
+                elif sub_attr_type in (Link.IFLA_BRPORT_MESSAGE_AGE_TIMER,
+                                       Link.IFLA_BRPORT_FORWARD_DELAY_TIMER,
+                                       Link.IFLA_BRPORT_HOLD_TIMER):
+                    self.value[sub_attr_type] = unpack('=Q', data[4:12])[0]
+
+                else:
+                    self.log.log(SYSLOG_EXTRA_DEBUG, 'Add support for decoding IFLA_PROTINFO sub-attribute type %s (%d), length %d, padded to %d' %
+                                (parent_msg.get_ifla_brport_string(sub_attr_type), sub_attr_type, sub_attr_length, sub_attr_end))
+
+            data = data[sub_attr_end:]
+
+    def dump_lines(self, dump_buffer, line_number, color):
+        line_number = self.dump_first_line(dump_buffer, line_number, color)
+        extra = ''
+
+        next_sub_attr_line = 0
+        sub_attr_line = True
+
+        for x in xrange(1, self.attr_end/4):
+            start = x * 4
+            end = start + 4
+
+            if line_number == next_sub_attr_line:
+                sub_attr_line = True
+
+            if sub_attr_line:
+                sub_attr_line = False
+
+                (sub_attr_length, sub_attr_type) = unpack('=HH', self.data[start:start+4])
+                sub_attr_end = padded_length(sub_attr_length)
+
+                next_sub_attr_line = line_number + (sub_attr_end/4)
+
+                if sub_attr_end == sub_attr_length:
+                    padded_to = ', '
+                else:
+                    padded_to = ' padded to %d, ' % sub_attr_end
+
+                extra = 'Nested Attribute - Length %s (%d)%s Type %s (%d) %s' % \
+                        (zfilled_hex(sub_attr_length, 4), sub_attr_length,
+                         padded_to,
+                         zfilled_hex(sub_attr_type, 4), sub_attr_type,
+                         Link.ifla_brport_to_string.get(sub_attr_type))
+            else:
+                extra = ''
+
+            dump_buffer.append(data_to_color_text(line_number, color, self.data[start:end], extra))
+            line_number += 1
+
+        return line_number
+
+    def get_pretty_value(self, obj=None):
+
+        if obj and callable(obj):
+            return obj(self.value)
+
+        value_pretty = {}
+
+        for (sub_key, sub_value) in self.value.iteritems():
+            sub_key_pretty = "(%2d) %s" % (sub_key, Link.ifla_brport_to_string.get(sub_key, 'UNKNOWN'))
+            sub_value_pretty = sub_value
+            value_pretty[sub_key_pretty] = sub_value_pretty
+
+        return value_pretty
+
+
+
+class NetlinkPacket(object):
+    """
+    Netlink Header
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                          Length                             |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |            Type              |           Flags              |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                      Sequence Number                        |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                      Process ID (PID)                       |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    """
+
+    header_PACK = 'IHHII'
+    header_LEN  = calcsize(header_PACK)
+
+    # Netlink packet types
+    # /usr/include/linux/rtnetlink.h
+    type_to_string = {
+        NLMSG_NOOP    : 'NLMSG_NOOP',
+        NLMSG_ERROR   : 'NLMSG_ERROR',
+        NLMSG_DONE    : 'NLMSG_DONE',
+        NLMSG_OVERRUN : 'NLMSG_OVERRUN',
+        RTM_NEWLINK   : 'RTM_NEWLINK',
+        RTM_DELLINK   : 'RTM_DELLINK',
+        RTM_GETLINK   : 'RTM_GETLINK',
+        RTM_SETLINK   : 'RTM_SETLINK',
+        RTM_NEWADDR   : 'RTM_NEWADDR',
+        RTM_DELADDR   : 'RTM_DELADDR',
+        RTM_GETADDR   : 'RTM_GETADDR',
+        RTM_NEWNEIGH  : 'RTM_NEWNEIGH',
+        RTM_DELNEIGH  : 'RTM_DELNEIGH',
+        RTM_GETNEIGH  : 'RTM_GETNEIGH',
+        RTM_NEWROUTE  : 'RTM_NEWROUTE',
+        RTM_DELROUTE  : 'RTM_DELROUTE',
+        RTM_GETROUTE  : 'RTM_GETROUTE',
+        RTM_NEWQDISC  : 'RTM_NEWQDISC',
+        RTM_DELQDISC  : 'RTM_DELQDISC',
+        RTM_GETQDISC  : 'RTM_GETQDISC'
+    }
+
+    af_family_to_string = {
+        AF_INET     : 'inet',
+        AF_INET6    : 'inet6'
+    }
+
+    def __init__(self, msgtype, debug, owner_logger=None, use_color=True):
+        self.msgtype     = msgtype
+        self.attributes  = {}
+        self.dump_buffer = ['']
+        self.line_number = 1
+        self.debug       = debug
+        self.message     = None
+        self.use_color   = use_color
+        self.family      = None
+
+        if owner_logger:
+            self.log = owner_logger
+        else:
+            self.log = log
+
+    def __str__(self):
+        return self.get_type_string()
+
+    def get_string(self, to_string, index):
+        """
+        Used to do lookups in all of the various FOO_to_string dictionaries
+        but returns 'UNKNOWN' if the key is bogus
+        """
+        if index in to_string:
+            return to_string[index]
+        return 'UNKNOWN'
+
+    def get_type_string(self, msgtype=None):
+        if not msgtype:
+            msgtype = self.msgtype
+        return self.get_string(self.type_to_string, msgtype)
+
+    def get_flags_string(self):
+        foo = []
+
+        for (flag, flag_string) in self.flag_to_string.iteritems():
+            if self.flags & flag:
+                foo.append(flag_string)
+
+        return ', '.join(foo)
+
+    def decode_packet(self, length, flags, seq, pid, data):
+        self.length      = length
+        self.flags       = flags
+        self.seq         = seq
+        self.pid         = pid
+        self.header_data = data[0:self.header_LEN]
+        self.msg_data    = data[self.header_LEN:length]
+
+        self.decode_netlink_header()
+        self.decode_service_header()
+
+        # NLMSG_ERROR is special case, it does not have attributes to decode
+        if self.msgtype != NLMSG_ERROR:
+            self.decode_attributes()
+
+    def get_netlink_header_flags_string(self, msg_type, flags):
+        foo = []
+
+        if flags & NLM_F_REQUEST:
+            foo.append('NLM_F_REQUEST')
+
+        if flags & NLM_F_MULTI:
+            foo.append('NLM_F_MULTI')
+
+        if flags & NLM_F_ACK:
+            foo.append('NLM_F_ACK')
+
+        if flags & NLM_F_ECHO:
+            foo.append('NLM_F_ECHO')
+
+        # Modifiers to GET query
+        if msg_type in (RTM_GETLINK, RTM_GETADDR, RTM_GETNEIGH, RTM_GETROUTE, RTM_GETQDISC):
+            if flags & NLM_F_DUMP:
+                foo.append('NLM_F_DUMP')
+            else:
+                if flags & NLM_F_MATCH:
+                    foo.append('NLM_F_MATCH')
+
+                if flags & NLM_F_ROOT:
+                    foo.append('NLM_F_ROOT')
+
+            if flags & NLM_F_ATOMIC:
+                foo.append('NLM_F_ATOMIC')
+
+        # Modifiers to NEW query
+        elif msg_type in (RTM_NEWLINK, RTM_NEWADDR, RTM_NEWNEIGH, RTM_NEWROUTE, RTM_NEWQDISC):
+            if flags & NLM_F_REPLACE:
+                foo.append('NLM_F_REPLACE')
+
+            if flags & NLM_F_EXCL:
+                foo.append('NLM_F_EXCL')
+
+            if flags & NLM_F_CREATE:
+                foo.append('NLM_F_CREATE')
+
+            if flags & NLM_F_APPEND:
+                foo.append('NLM_F_APPEND')
+
+        return ', '.join(foo)
+
+    # When we first RXed the netlink message we had to decode the header to
+    # determine what type of netlink message we were dealing with.  So the
+    # header has actually already been decoded...what we do here is
+    # populate the dump_buffer lines with the header content.
+    def decode_netlink_header(self):
+
+        if not self.debug:
+            return
+
+        header_data = self.header_data
+
+        # Print the netlink header in red
+        netlink_header_length = 16
+        color = red if self.use_color else None
+        color_start = "\033[%dm" % color if color else ""
+        color_end = "\033[0m" if color else ""
+        self.dump_buffer.append("  %sNetlink Header%s" % (color_start, color_end))
+
+        for x in range(0, netlink_header_length/4):
+            start = x * 4
+            end = start + 4
+
+            if self.line_number == 1:
+                data = unpack('=L', header_data[start:end])[0]
+                extra = "Length %s (%d)" % (zfilled_hex(data, 8), data)
+
+            elif self.line_number == 2:
+                (data1, data2) = unpack('HH', header_data[start:end])
+                extra = "Type %s (%d - %s), Flags %s (%s)" % \
+                    (zfilled_hex(data1, 4), data1, self.get_type_string(data1),
+                     zfilled_hex(data2, 4), self.get_netlink_header_flags_string(data1, data2))
+
+            elif self.line_number == 3:
+                data = unpack('=L', header_data[start:end])[0]
+                extra = "Sequence Number %s (%d)" % (zfilled_hex(data, 8), data)
+
+            elif self.line_number == 4:
+                data = unpack('=L', header_data[start:end])[0]
+                extra = "Process ID %s (%d)" % (zfilled_hex(data, 8), data)
+            else:
+                extra = "Unexpected line number %d" % self.line_number
+
+            self.dump_buffer.append(data_to_color_text(self.line_number, color, header_data[start:end], extra))
+            self.line_number += 1
+
+    def decode_attributes(self):
+        """
+        Decode the attributes and populate the dump_buffer
+        """
+
+        if self.debug:
+            self.dump_buffer.append("  Attributes")
+            color = green if self.use_color else None
+
+        data = self.msg_data[self.LEN:]
+
+        while data:
+            (length, attr_type) = unpack('=HH', data[:4])
+
+            # If this is zero we will stay in this loop for forever
+            if not length:
+                self.log.error('Length is zero')
+                return
+
+            if len(data) < length:
+                self.log.error("Buffer underrun %d < %d" % (len(data), length))
+                return
+
+            attr = self.add_attribute(attr_type, None)
+
+            # Find the end of 'data' for this attribute and decode our section
+            # of 'data'. attributes are padded for alignment thus the attr_end.
+            #
+            # How the attribute is decoded/unpacked is specific per AttributeXXXX class.
+            attr_end = padded_length(length)
+            attr.decode(self, data[0:attr_end])
+
+            if self.debug:
+                self.line_number = attr.dump_lines(self.dump_buffer, self.line_number, color)
+
+                # Alternate back and forth between green and blue
+                if self.use_color:
+                    if color == green:
+                        color = blue
+                    else:
+                        color = green
+
+            data = data[attr_end:]
+
+    def add_attribute(self, attr_type, value):
+        nested = True if attr_type & NLA_F_NESTED else False
+        net_byteorder = True if attr_type & NLA_F_NET_BYTEORDER else False
+        attr_type = attr_type & NLA_TYPE_MASK
+
+        # Given an attr_type (say RTA_DST) find the type of AttributeXXXX class
+        # that we will use to store this attribute...AttributeIPAddress in the
+        # case of RTA_DST.
+        if attr_type in self.attribute_to_class:
+            (attr_string, attr_class) = self.attribute_to_class[attr_type]
+
+            '''
+            attribute_to_class is a dictionary where the key is the attr_type, it doesn't
+            take the family into account. For now we'll handle this as a special case for
+            MPLS but long term we may need to make key a tuple of the attr_type and family.
+            '''
+            if attr_type == Route.RTA_DST and self.family == AF_MPLS:
+                attr_string = 'RTA_DST'
+                attr_class = AttributeMplsLabel
+
+        else:
+            attr_string = "UNKNOWN_ATTRIBUTE_%d" % attr_type
+            attr_class = AttributeGeneric
+            self.log.debug("Attribute %d is not defined in %s.attribute_to_class, assuming AttributeGeneric" %
+                           (attr_type, self.__class__.__name__))
+
+        attr = attr_class(attr_type, attr_string, self.family, self.log)
+
+        attr.set_value(value)
+        attr.set_nested(nested)
+        attr.set_net_byteorder(net_byteorder)
+
+        # self.attributes is a dictionary keyed by the attribute type where
+        # the value is an instance of the corresponding AttributeXXXX class.
+        self.attributes[attr_type] = attr
+
+        return attr
+
+    def get_attribute_value(self, attr_type, default=None):
+        if attr_type not in self.attributes:
+            return default
+
+        return self.attributes[attr_type].value
+
+    def get_attr_string(self, attr_type):
+        """
+        Example: If attr_type is Address.IFA_CACHEINFO return the string 'IFA_CACHEINFO'
+        """
+        if attr_type in self.attribute_to_class:
+            (attr_string, attr_class) = self.attribute_to_class[attr_type]
+            return attr_string
+        return str(attr_type)
+
+    def build_message(self, seq, pid):
+        self.seq = seq
+        self.pid = pid
+        attrs = ''
+
+        for attr in self.attributes.itervalues():
+            attrs += attr.encode()
+
+        self.length = self.header_LEN + len(self.body) + len(attrs)
+        self.header_data = pack(self.header_PACK, self.length, self.msgtype, self.flags, self.seq, self.pid)
+        self.msg_data = self.body + attrs
+        self.message = self.header_data + self.msg_data
+
+        if self.debug:
+            self.decode_netlink_header()
+            self.decode_service_header()
+            self.decode_attributes()
+            self.dump("TXed %s, length %d, seq %d, pid %d, flags 0x%x (%s)" %
+                      (self, self.length, self.seq, self.pid, self.flags,
+                       self.get_netlink_header_flags_string(self.msgtype, self.flags)))
+
+    def pretty_display_dict(self, dic, level):
+        for k,v in dic.iteritems():
+            if isinstance(v, dict):
+                self.log.debug(' '*level + str(k) + ':')
+                self.pretty_display_dict(v, level+5)
+            else:
+                self.log.debug(' '*level + str(k) + ': ' + str(v))
+
+    # Print the netlink message in hex. This is only used for debugging.
+    def dump(self, desc=None):
+        attr_string = {}
+
+        if desc is None:
+            desc = "RXed %s, length %d, seq %d, pid %d, flags 0x%x" % (self, self.length, self.seq, self.pid, self.flags)
+
+        for (attr_type, attr_obj) in self.attributes.iteritems():
+            key_string = "(%2d) %s" % (attr_type, self.get_attr_string(attr_type))
+            attr_string[key_string] = attr_obj.get_pretty_value()
+
+        if self.use_color:
+            self.log.debug("%s\n%s\n\nAttributes Summary\n%s\n" %
+                           (desc, '\n'.join(self.dump_buffer), pformat(attr_string)))
+        else:
+            # Assume if we are not allowing color output we also don't want embedded
+            # newline characters in the output. Output each line individually.
+            self.log.debug(desc)
+            for line in self.dump_buffer:
+                self.log.debug(line)
+            self.log.debug("")
+            self.log.debug("Attributes Summary")
+            self.pretty_display_dict(attr_string, 1)
+
+
+class Address(NetlinkPacket):
+    """
+    Service Header
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |   Family    |     Length    |     Flags     |    Scope      |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                     Interface Index                         |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    """
+
+    # Address attributes
+    # /usr/include/linux/if_addr.h
+    IFA_UNSPEC    = 0x00
+    IFA_ADDRESS   = 0x01
+    IFA_LOCAL     = 0x02
+    IFA_LABEL     = 0x03
+    IFA_BROADCAST = 0x04
+    IFA_ANYCAST   = 0x05
+    IFA_CACHEINFO = 0x06
+    IFA_MULTICAST = 0x07
+    IFA_FLAGS     = 0x08
+
+    attribute_to_class = {
+        IFA_UNSPEC    : ('IFA_UNSPEC', AttributeGeneric),
+        IFA_ADDRESS   : ('IFA_ADDRESS', AttributeIPAddress),
+        IFA_LOCAL     : ('IFA_LOCAL', AttributeIPAddress),
+        IFA_LABEL     : ('IFA_LABEL', AttributeString),
+        IFA_BROADCAST : ('IFA_BROADCAST', AttributeIPAddress),
+        IFA_ANYCAST   : ('IFA_ANYCAST', AttributeIPAddress),
+        IFA_CACHEINFO : ('IFA_CACHEINFO', AttributeGeneric),
+        IFA_MULTICAST : ('IFA_MULTICAST', AttributeIPAddress),
+        IFA_FLAGS     : ('IFA_FLAGS', AttributeGeneric)
+    }
+
+    # Address flags
+    # /usr/include/linux/if_addr.h
+    IFA_F_SECONDARY   = 0x01
+    IFA_F_NODAD       = 0x02
+    IFA_F_OPTIMISTIC  = 0x04
+    IFA_F_DADFAILED   = 0x08
+    IFA_F_HOMEADDRESS = 0x10
+    IFA_F_DEPRECATED  = 0x20
+    IFA_F_TENTATIVE   = 0x40
+    IFA_F_PERMANENT   = 0x80
+
+    flag_to_string = {
+        IFA_F_SECONDARY   : 'IFA_F_SECONDARY',
+        IFA_F_NODAD       : 'IFA_F_NODAD',
+        IFA_F_OPTIMISTIC  : 'IFA_F_OPTIMISTIC',
+        IFA_F_DADFAILED   : 'IFA_F_DADFAILED',
+        IFA_F_HOMEADDRESS : 'IFA_F_HOMEADDRESS',
+        IFA_F_DEPRECATED  : 'IFA_F_DEPRECATED',
+        IFA_F_TENTATIVE   : 'IFA_F_TENTATIVE',
+        IFA_F_PERMANENT   : 'IFA_F_PERMANENT'
+    }
+
+    def __init__(self, msgtype, debug=False, logger=None, use_color=True):
+        NetlinkPacket.__init__(self, msgtype, debug, logger, use_color)
+        self.PACK = '4Bi'
+        self.LEN = calcsize(self.PACK)
+
+    def decode_service_header(self):
+
+        # Nothing to do if the message did not contain a service header
+        if self.length == self.header_LEN:
+            return
+
+        (self.family, self.prefixlen, self.flags, self.scope,
+         self.ifindex) = \
+            unpack(self.PACK, self.msg_data[:self.LEN])
+
+        if self.debug:
+            color = yellow if self.use_color else None
+            color_start = "\033[%dm" % color if color else ""
+            color_end = "\033[0m" if color else ""
+            self.dump_buffer.append("  %sService Header%s" % (color_start, color_end))
+
+            for x in range(0, self.LEN/4):
+                if self.line_number == 5:
+                    extra = "Family %s (%d), Length %s (%d), Flags %s, Scope %s (%d)" % \
+                            (zfilled_hex(self.family, 2), self.family,
+                             zfilled_hex(self.prefixlen, 2), self.prefixlen,
+                             zfilled_hex(self.flags, 2),
+                             zfilled_hex(self.scope, 2), self.scope)
+                elif self.line_number == 6:
+                    extra = "Interface Index %s (%d)" % (zfilled_hex(self.ifindex, 8), self.ifindex)
+                else:
+                    extra = "Unexpected line number %d" % self.line_number
+
+                start = x * 4
+                end = start + 4
+                self.dump_buffer.append(data_to_color_text(self.line_number, color, self.msg_data[start:end], extra))
+                self.line_number += 1
+
+
+class Error(NetlinkPacket):
+
+    # Error codes
+    # /include/netlink/errno.h
+    NLE_SUCCESS           = 0x00
+    NLE_FAILURE           = 0x01
+    NLE_INTR              = 0x02
+    NLE_BAD_SOCK          = 0x03
+    NLE_AGAIN             = 0x04
+    NLE_NOMEM             = 0x05
+    NLE_EXIST             = 0x06
+    NLE_INVAL             = 0x07
+    NLE_RANGE             = 0x08
+    NLE_MSGSIZE           = 0x09
+    NLE_OPNOTSUPP         = 0x0A
+    NLE_AF_NOSUPPORT      = 0x0B
+    NLE_OBJ_NOTFOUND      = 0x0C
+    NLE_NOATTR            = 0x0D
+    NLE_MISSING_ATTR      = 0x0E
+    NLE_AF_MISMATCH       = 0x0F
+    NLE_SEQ_MISMATCH      = 0x10
+    NLE_MSG_OVERFLOW      = 0x11
+    NLE_MSG_TRUNC         = 0x12
+    NLE_NOADDR            = 0x13
+    NLE_SRCRT_NOSUPPORT   = 0x14
+    NLE_MSG_TOOSHORT      = 0x15
+    NLE_MSGTYPE_NOSUPPORT = 0x16
+    NLE_OBJ_MISMATCH      = 0x17
+    NLE_NOCACHE           = 0x18
+    NLE_BUSY              = 0x19
+    NLE_PROTO_MISMATCH    = 0x1A
+    NLE_NOACCESS          = 0x1B
+    NLE_PERM              = 0x1C
+    NLE_PKTLOC_FILE       = 0x1D
+    NLE_PARSE_ERR         = 0x1E
+    NLE_NODEV             = 0x1F
+    NLE_IMMUTABLE         = 0x20
+    NLE_DUMP_INTR         = 0x21
+
+    error_to_string = {
+        NLE_SUCCESS           : 'NLE_SUCCESS',
+        NLE_FAILURE           : 'NLE_FAILURE',
+        NLE_INTR              : 'NLE_INTR',
+        NLE_BAD_SOCK          : 'NLE_BAD_SOCK',
+        NLE_AGAIN             : 'NLE_AGAIN',
+        NLE_NOMEM             : 'NLE_NOMEM',
+        NLE_EXIST             : 'NLE_EXIST',
+        NLE_INVAL             : 'NLE_INVAL',
+        NLE_RANGE             : 'NLE_RANGE',
+        NLE_MSGSIZE           : 'NLE_MSGSIZE',
+        NLE_OPNOTSUPP         : 'NLE_OPNOTSUPP',
+        NLE_AF_NOSUPPORT      : 'NLE_AF_NOSUPPORT',
+        NLE_OBJ_NOTFOUND      : 'NLE_OBJ_NOTFOUND',
+        NLE_NOATTR            : 'NLE_NOATTR',
+        NLE_MISSING_ATTR      : 'NLE_MISSING_ATTR',
+        NLE_AF_MISMATCH       : 'NLE_AF_MISMATCH',
+        NLE_SEQ_MISMATCH      : 'NLE_SEQ_MISMATCH',
+        NLE_MSG_OVERFLOW      : 'NLE_MSG_OVERFLOW',
+        NLE_MSG_TRUNC         : 'NLE_MSG_TRUNC',
+        NLE_NOADDR            : 'NLE_NOADDR',
+        NLE_SRCRT_NOSUPPORT   : 'NLE_SRCRT_NOSUPPORT',
+        NLE_MSG_TOOSHORT      : 'NLE_MSG_TOOSHORT',
+        NLE_MSGTYPE_NOSUPPORT : 'NLE_MSGTYPE_NOSUPPORT',
+        NLE_OBJ_MISMATCH      : 'NLE_OBJ_MISMATCH',
+        NLE_NOCACHE           : 'NLE_NOCACHE',
+        NLE_BUSY              : 'NLE_BUSY',
+        NLE_PROTO_MISMATCH    : 'NLE_PROTO_MISMATCH',
+        NLE_NOACCESS          : 'NLE_NOACCESS',
+        NLE_PERM              : 'NLE_PERM',
+        NLE_PKTLOC_FILE       : 'NLE_PKTLOC_FILE',
+        NLE_PARSE_ERR         : 'NLE_PARSE_ERR',
+        NLE_NODEV             : 'NLE_NODEV',
+        NLE_IMMUTABLE         : 'NLE_IMMUTABLE',
+        NLE_DUMP_INTR         : 'NLE_DUMP_INTR'
+    }
+
+    def __init__(self, msgtype, debug=False, logger=None, use_color=True):
+        NetlinkPacket.__init__(self, msgtype, debug, logger, use_color)
+        self.PACK = '=iLHHLL'
+        self.LEN = calcsize(self.PACK)
+
+    def decode_service_header(self):
+
+        # Nothing to do if the message did not contain a service header
+        if self.length == self.header_LEN:
+            return
+
+        (self.negative_errno, self.bad_msg_len, self.bad_msg_type,
+         self.bad_msg_flag, self.bad_msg_seq, self.bad_msg_pid) =\
+            unpack(self.PACK, self.msg_data[:self.LEN])
+
+        if self.debug:
+            color = yellow if self.use_color else None
+            color_start = "\033[%dm" % color if color else ""
+            color_end = "\033[0m" if color else ""
+            self.dump_buffer.append("  %sService Header%s" % (color_start, color_end))
+
+            for x in range(0, self.LEN/4):
+
+                if self.line_number == 5:
+                    extra = "Error Number %s is %s" % (self.negative_errno, self.error_to_string.get(abs(self.negative_errno)))
+                    # zfilled_hex(self.negative_errno, 2)
+
+                elif self.line_number == 6:
+                    extra = "Length %s (%d)" % (zfilled_hex(self.bad_msg_len, 8), self.bad_msg_len)
+
+                elif self.line_number == 7:
+                    extra = "Type %s (%d - %s), Flags %s (%s)" % \
+                        (zfilled_hex(self.bad_msg_type, 4), self.bad_msg_type, self.get_type_string(self.bad_msg_type),
+                         zfilled_hex(self.bad_msg_flag, 4), self.get_netlink_header_flags_string(self.bad_msg_type, self.bad_msg_flag))
+
+                elif self.line_number == 8:
+                    extra = "Sequence Number %s (%d)" % (zfilled_hex(self.bad_msg_seq, 8), self.bad_msg_seq)
+
+                elif self.line_number == 9:
+                    extra = "Process ID %s (%d)" % (zfilled_hex(self.bad_msg_pid, 8), self.bad_msg_pid)
+
+                else:
+                    extra = "Unexpected line number %d" % self.line_number
+
+                start = x * 4
+                end = start + 4
+                self.dump_buffer.append(data_to_color_text(self.line_number, color, self.msg_data[start:end], extra))
+                self.line_number += 1
+
+
+class Link(NetlinkPacket):
+    """
+    Service Header
+
+     0                   1                   2                   3
+     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |   Family    |   Reserved  |          Device Type              |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                     Interface Index                           |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                      Device Flags                             |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                      Change Mask                              |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    """
+
+    # Link attributes
+    # /usr/include/linux/if_link.h
+    IFLA_UNSPEC          = 0
+    IFLA_ADDRESS         = 1
+    IFLA_BROADCAST       = 2
+    IFLA_IFNAME          = 3
+    IFLA_MTU             = 4
+    IFLA_LINK            = 5
+    IFLA_QDISC           = 6
+    IFLA_STATS           = 7
+    IFLA_COST            = 8
+    IFLA_PRIORITY        = 9
+    IFLA_MASTER          = 10
+    IFLA_WIRELESS        = 11
+    IFLA_PROTINFO        = 12
+    IFLA_TXQLEN          = 13
+    IFLA_MAP             = 14
+    IFLA_WEIGHT          = 15
+    IFLA_OPERSTATE       = 16
+    IFLA_LINKMODE        = 17
+    IFLA_LINKINFO        = 18
+    IFLA_NET_NS_PID      = 19
+    IFLA_IFALIAS         = 20
+    IFLA_NUM_VF          = 21
+    IFLA_VFINFO_LIST     = 22
+    IFLA_STATS64         = 23
+    IFLA_VF_PORTS        = 24
+    IFLA_PORT_SELF       = 25
+    IFLA_AF_SPEC         = 26
+    IFLA_GROUP           = 27
+    IFLA_NET_NS_FD       = 28
+    IFLA_EXT_MASK        = 29
+    IFLA_PROMISCUITY     = 30
+    IFLA_NUM_TX_QUEUES   = 31
+    IFLA_NUM_RX_QUEUES   = 32
+    IFLA_CARRIER         = 33
+    IFLA_PHYS_PORT_ID    = 34
+    IFLA_CARRIER_CHANGES = 35
+    IFLA_PHYS_SWITCH_ID  = 36
+    IFLA_LINK_NETNSID    = 37
+    IFLA_PHYS_PORT_NAME  = 38
+    IFLA_PROTO_DOWN      = 39
+    IFLA_GSO_MAX_SEGS    = 40
+    IFLA_GSO_MAX_SIZE    = 41
+
+    attribute_to_class = {
+        IFLA_UNSPEC          : ('IFLA_UNSPEC', AttributeGeneric),
+        IFLA_ADDRESS         : ('IFLA_ADDRESS', AttributeMACAddress),
+        IFLA_BROADCAST       : ('IFLA_BROADCAST', AttributeMACAddress),
+        IFLA_IFNAME          : ('IFLA_IFNAME', AttributeStringInterfaceName),
+        IFLA_MTU             : ('IFLA_MTU', AttributeFourByteValue),
+        IFLA_LINK            : ('IFLA_LINK', AttributeFourByteValue),
+        IFLA_QDISC           : ('IFLA_QDISC', AttributeString),
+        IFLA_STATS           : ('IFLA_STATS', AttributeGeneric),
+        IFLA_COST            : ('IFLA_COST', AttributeGeneric),
+        IFLA_PRIORITY        : ('IFLA_PRIORITY', AttributeGeneric),
+        IFLA_MASTER          : ('IFLA_MASTER', AttributeFourByteValue),
+        IFLA_WIRELESS        : ('IFLA_WIRELESS', AttributeGeneric),
+        IFLA_PROTINFO        : ('IFLA_PROTINFO', AttributeIFLA_PROTINFO),
+        IFLA_TXQLEN          : ('IFLA_TXQLEN', AttributeFourByteValue),
+        IFLA_MAP             : ('IFLA_MAP', AttributeGeneric),
+        IFLA_WEIGHT          : ('IFLA_WEIGHT', AttributeGeneric),
+        IFLA_OPERSTATE       : ('IFLA_OPERSTATE', AttributeOneByteValue),
+        IFLA_LINKMODE        : ('IFLA_LINKMODE', AttributeOneByteValue),
+        IFLA_LINKINFO        : ('IFLA_LINKINFO', AttributeIFLA_LINKINFO),
+        IFLA_NET_NS_PID      : ('IFLA_NET_NS_PID', AttributeGeneric),
+        IFLA_IFALIAS         : ('IFLA_IFALIAS', AttributeGeneric),
+        IFLA_NUM_VF          : ('IFLA_NUM_VF', AttributeGeneric),
+        IFLA_VFINFO_LIST     : ('IFLA_VFINFO_LIST', AttributeGeneric),
+        IFLA_STATS64         : ('IFLA_STATS64', AttributeGeneric),
+        IFLA_VF_PORTS        : ('IFLA_VF_PORTS', AttributeGeneric),
+        IFLA_PORT_SELF       : ('IFLA_PORT_SELF', AttributeGeneric),
+        IFLA_AF_SPEC         : ('IFLA_AF_SPEC', AttributeIFLA_AF_SPEC),
+        IFLA_GROUP           : ('IFLA_GROUP', AttributeFourByteValue),
+        IFLA_NET_NS_FD       : ('IFLA_NET_NS_FD', AttributeGeneric),
+        IFLA_EXT_MASK        : ('IFLA_EXT_MASK', AttributeFourByteValue),
+        IFLA_PROMISCUITY     : ('IFLA_PROMISCUITY', AttributeGeneric),
+        IFLA_NUM_TX_QUEUES   : ('IFLA_NUM_TX_QUEUES', AttributeGeneric),
+        IFLA_NUM_RX_QUEUES   : ('IFLA_NUM_RX_QUEUES', AttributeGeneric),
+        IFLA_CARRIER         : ('IFLA_CARRIER', AttributeGeneric),
+        IFLA_PHYS_PORT_ID    : ('IFLA_PHYS_PORT_ID', AttributeGeneric),
+        IFLA_CARRIER_CHANGES : ('IFLA_CARRIER_CHANGES', AttributeGeneric),
+        IFLA_PHYS_SWITCH_ID  : ('IFLA_PHYS_SWITCH_ID', AttributeGeneric),
+        IFLA_LINK_NETNSID    : ('IFLA_LINK_NETNSID', AttributeGeneric),
+        IFLA_PHYS_PORT_NAME  : ('IFLA_PHYS_PORT_NAME', AttributeGeneric),
+        IFLA_PROTO_DOWN      : ('IFLA_PROTO_DOWN', AttributeOneByteValue),
+        IFLA_GSO_MAX_SEGS    : ('IFLA_GSO_MAX_SEGS', AttributeFourByteValue),
+        IFLA_GSO_MAX_SIZE    : ('IFLA_GSO_MAX_SIZE', AttributeFourByteValue)
+    }
+
+    # Link flags
+    # /usr/include/linux/if.h
+    IFF_UP          = 0x0001     # Interface is administratively up.
+    IFF_BROADCAST   = 0x0002     # Valid broadcast address set.
+    IFF_DEBUG       = 0x0004     # Internal debugging flag.
+    IFF_LOOPBACK    = 0x0008     # Interface is a loopback interface.
+    IFF_POINTOPOINT = 0x0010     # Interface is a point-to-point link.
+    IFF_NOTRAILERS  = 0x0020     # Avoid use of trailers.
+    IFF_RUNNING     = 0x0040     # Interface is operationally up.
+    IFF_NOARP       = 0x0080     # No ARP protocol needed for this interface.
+    IFF_PROMISC     = 0x0100     # Interface is in promiscuous mode.
+    IFF_ALLMULTI    = 0x0200     # Receive all multicast packets.
+    IFF_MASTER      = 0x0400     # Master of a load balancing bundle.
+    IFF_SLAVE       = 0x0800     # Slave of a load balancing bundle.
+    IFF_MULTICAST   = 0x1000     # Supports multicast.
+    IFF_PORTSEL     = 0x2000     # Is able to select media type via ifmap.
+    IFF_AUTOMEDIA   = 0x4000     # Auto media selection active.
+    IFF_DYNAMIC     = 0x8000     # Interface was dynamically created.
+    IFF_LOWER_UP    = 0x10000    # driver signals L1 up
+    IFF_DORMANT     = 0x20000    # driver signals dormant
+    IFF_ECHO        = 0x40000    # echo sent packet
+    IFF_PROTO_DOWN  = 0x1000000  # protocol is down on the interface
+
+    flag_to_string = {
+        IFF_UP          : 'IFF_UP',
+        IFF_BROADCAST   : 'IFF_BROADCAST',
+        IFF_DEBUG       : 'IFF_DEBUG',
+        IFF_LOOPBACK    : 'IFF_LOOPBACK',
+        IFF_POINTOPOINT : 'IFF_POINTOPOINT',
+        IFF_NOTRAILERS  : 'IFF_NOTRAILERS',
+        IFF_RUNNING     : 'IFF_RUNNING',
+        IFF_NOARP       : 'IFF_NOARP',
+        IFF_PROMISC     : 'IFF_PROMISC',
+        IFF_ALLMULTI    : 'IFF_ALLMULTI',
+        IFF_MASTER      : 'IFF_MASTER',
+        IFF_SLAVE       : 'IFF_SLAVE',
+        IFF_MULTICAST   : 'IFF_MULTICAST',
+        IFF_PORTSEL     : 'IFF_PORTSEL',
+        IFF_AUTOMEDIA   : 'IFF_AUTOMEDIA',
+        IFF_DYNAMIC     : 'IFF_DYNAMIC',
+        IFF_LOWER_UP    : 'IFF_LOWER_UP',
+        IFF_DORMANT     : 'IFF_DORMANT',
+        IFF_ECHO        : 'IFF_ECHO',
+        IFF_PROTO_DOWN  : 'IFF_PROTO_DOWN'
+    }
+
+    # RFC 2863 operational status
+    IF_OPER_UNKNOWN        = 0
+    IF_OPER_NOTPRESENT     = 1
+    IF_OPER_DOWN           = 2
+    IF_OPER_LOWERLAYERDOWN = 3
+    IF_OPER_TESTING        = 4
+    IF_OPER_DORMANT        = 5
+    IF_OPER_UP             = 6
+
+    oper_to_string = {
+        IF_OPER_UNKNOWN        : 'IF_OPER_UNKNOWN',
+        IF_OPER_NOTPRESENT     : 'IF_OPER_NOTPRESENT',
+        IF_OPER_DOWN           : 'IF_OPER_DOWN',
+        IF_OPER_LOWERLAYERDOWN : 'IF_OPER_LOWERLAYERDOWN',
+        IF_OPER_TESTING        : 'IF_OPER_TESTING',
+        IF_OPER_DORMANT        : 'IF_OPER_DORMANT',
+        IF_OPER_UP             : 'IF_OPER_UP'
+    }
+
+    # Link types
+    # /usr/include/linux/if_arp.h
+    # ARP protocol HARDWARE identifiers
+    ARPHRD_NETROM             = 0      # from KA9Q: NET/ROM pseudo
+    ARPHRD_ETHER              = 1      # Ethernet 10Mbps
+    ARPHRD_EETHER             = 2      # Experimental Ethernet
+    ARPHRD_AX25               = 3      # AX.25 Level 2
+    ARPHRD_PRONET             = 4      # PROnet token ring
+    ARPHRD_CHAOS              = 5      # Chaosnet
+    ARPHRD_IEEE802            = 6      # IEEE 802.2 Ethernet/TR/TB
+    ARPHRD_ARCNET             = 7      # ARCnet
+    ARPHRD_APPLETLK           = 8      # APPLEtalk
+    ARPHRD_DLCI               = 15     # Frame Relay DLCI
+    ARPHRD_ATM                = 19     # ATM
+    ARPHRD_METRICOM           = 23     # Metricom STRIP (new IANA id)
+    ARPHRD_IEEE1394           = 24     # IEEE 1394 IPv4 - RFC 2734
+    ARPHRD_EUI64              = 27     # EUI-64
+    ARPHRD_INFINIBAND         = 32     # InfiniBand
+    # Dummy types for non ARP hardware
+    ARPHRD_SLIP               = 256
+    ARPHRD_CSLIP              = 257
+    ARPHRD_SLIP6              = 258
+    ARPHRD_CSLIP6             = 259
+    ARPHRD_RSRVD              = 260    # Notional KISS type
+    ARPHRD_ADAPT              = 264
+    ARPHRD_ROSE               = 270
+    ARPHRD_X25                = 271    # CCITT X.25
+    ARPHRD_HWX25              = 272    # Boards with X.25 in firmware
+    ARPHRD_CAN                = 280    # Controller Area Network
+    ARPHRD_PPP                = 512
+    ARPHRD_CISCO              = 513    # Cisco HDLC
+    ARPHRD_HDLC               = ARPHRD_CISCO
+    ARPHRD_LAPB               = 516    # LAPB
+    ARPHRD_DDCMP              = 517    # Digital's DDCMP protocol
+    ARPHRD_RAWHDLC            = 518    # Raw HDLC
+    ARPHRD_TUNNEL             = 768    # IPIP tunnel
+    ARPHRD_TUNNEL6            = 769    # IP6IP6 tunnel
+    ARPHRD_FRAD               = 770    # Frame Relay Access Device
+    ARPHRD_SKIP               = 771    # SKIP vif
+    ARPHRD_LOOPBACK           = 772    # Loopback device
+    ARPHRD_LOCALTLK           = 773    # Localtalk device
+    ARPHRD_FDDI               = 774    # Fiber Distributed Data Interface
+    ARPHRD_BIF                = 775    # AP1000 BIF
+    ARPHRD_SIT                = 776    # sit0 device - IPv6-in-IPv4
+    ARPHRD_IPDDP              = 777    # IP over DDP tunneller
+    ARPHRD_IPGRE              = 778    # GRE over IP
+    ARPHRD_PIMREG             = 779    # PIMSM register interface
+    ARPHRD_HIPPI              = 780    # High Performance Parallel Interface
+    ARPHRD_ASH                = 781    # Nexus 64Mbps Ash
+    ARPHRD_ECONET             = 782    # Acorn Econet
+    ARPHRD_IRDA               = 783    # Linux-IrDA
+    ARPHRD_FCPP               = 784    # Point to point fibrechannel
+    ARPHRD_FCAL               = 785    # Fibrechannel arbitrated loop
+    ARPHRD_FCPL               = 786    # Fibrechannel public loop
+    ARPHRD_FCFABRIC           = 787    # Fibrechannel fabric
+    # 787->799 reserved for fibrechannel media types
+    ARPHRD_IEEE802_TR         = 800    # Magic type ident for TR
+    ARPHRD_IEEE80211          = 801    # IEEE 802.11
+    ARPHRD_IEEE80211_PRISM    = 802    # IEEE 802.11 + Prism2 header
+    ARPHRD_IEEE80211_RADIOTAP = 803    # IEEE 802.11 + radiotap header
+    ARPHRD_IEEE802154         = 804
+    ARPHRD_PHONET             = 820    # PhoNet media type
+    ARPHRD_PHONET_PIPE        = 821    # PhoNet pipe header
+    ARPHRD_CAIF               = 822    # CAIF media type
+    ARPHRD_VOID               = 0xFFFF  # Void type, nothing is known
+    ARPHRD_NONE               = 0xFFFE  # zero header length
+
+    link_type_to_string = {
+        ARPHRD_NETROM             : 'ARPHRD_NETROM',
+        ARPHRD_ETHER              : 'ARPHRD_ETHER',
+        ARPHRD_EETHER             : 'ARPHRD_EETHER',
+        ARPHRD_AX25               : 'ARPHRD_AX25',
+        ARPHRD_PRONET             : 'ARPHRD_PRONET',
+        ARPHRD_CHAOS              : 'ARPHRD_CHAOS',
+        ARPHRD_IEEE802            : 'ARPHRD_IEEE802',
+        ARPHRD_ARCNET             : 'ARPHRD_ARCNET',
+        ARPHRD_APPLETLK           : 'ARPHRD_APPLETLK',
+        ARPHRD_DLCI               : 'ARPHRD_DLCI',
+        ARPHRD_ATM                : 'ARPHRD_ATM',
+        ARPHRD_METRICOM           : 'ARPHRD_METRICOM',
+        ARPHRD_IEEE1394           : 'ARPHRD_IEEE1394',
+        ARPHRD_EUI64              : 'ARPHRD_EUI64',
+        ARPHRD_INFINIBAND         : 'ARPHRD_INFINIBAND',
+        ARPHRD_SLIP               : 'ARPHRD_SLIP',
+        ARPHRD_CSLIP              : 'ARPHRD_CSLIP',
+        ARPHRD_SLIP6              : 'ARPHRD_SLIP6',
+        ARPHRD_CSLIP6             : 'ARPHRD_CSLIP6',
+        ARPHRD_RSRVD              : 'ARPHRD_RSRVD',
+        ARPHRD_ADAPT              : 'ARPHRD_ADAPT',
+        ARPHRD_ROSE               : 'ARPHRD_ROSE',
+        ARPHRD_X25                : 'ARPHRD_X25',
+        ARPHRD_HWX25              : 'ARPHRD_HWX25',
+        ARPHRD_CAN                : 'ARPHRD_CAN',
+        ARPHRD_PPP                : 'ARPHRD_PPP',
+        ARPHRD_CISCO              : 'ARPHRD_CISCO',
+        ARPHRD_HDLC               : 'ARPHRD_HDLC',
+        ARPHRD_LAPB               : 'ARPHRD_LAPB',
+        ARPHRD_DDCMP              : 'ARPHRD_DDCMP',
+        ARPHRD_RAWHDLC            : 'ARPHRD_RAWHDLC',
+        ARPHRD_TUNNEL             : 'ARPHRD_TUNNEL',
+        ARPHRD_TUNNEL6            : 'ARPHRD_TUNNEL6',
+        ARPHRD_FRAD               : 'ARPHRD_FRAD',
+        ARPHRD_SKIP               : 'ARPHRD_SKIP',
+        ARPHRD_LOOPBACK           : 'ARPHRD_LOOPBACK',
+        ARPHRD_LOCALTLK           : 'ARPHRD_LOCALTLK',
+        ARPHRD_FDDI               : 'ARPHRD_FDDI',
+        ARPHRD_BIF                : 'ARPHRD_BIF',
+        ARPHRD_SIT                : 'ARPHRD_SIT',
+        ARPHRD_IPDDP              : 'ARPHRD_IPDDP',
+        ARPHRD_IPGRE              : 'ARPHRD_IPGRE',
+        ARPHRD_PIMREG             : 'ARPHRD_PIMREG',
+        ARPHRD_HIPPI              : 'ARPHRD_HIPPI',
+        ARPHRD_ASH                : 'ARPHRD_ASH',
+        ARPHRD_ECONET             : 'ARPHRD_ECONET',
+        ARPHRD_IRDA               : 'ARPHRD_IRDA',
+        ARPHRD_FCPP               : 'ARPHRD_FCPP',
+        ARPHRD_FCAL               : 'ARPHRD_FCAL',
+        ARPHRD_FCPL               : 'ARPHRD_FCPL',
+        ARPHRD_FCFABRIC           : 'ARPHRD_FCFABRIC',
+        ARPHRD_IEEE802_TR         : 'ARPHRD_IEEE802_TR',
+        ARPHRD_IEEE80211          : 'ARPHRD_IEEE80211',
+        ARPHRD_IEEE80211_PRISM    : 'ARPHRD_IEEE80211_PRISM',
+        ARPHRD_IEEE80211_RADIOTAP : 'ARPHRD_IEEE80211_RADIOTAP',
+        ARPHRD_IEEE802154         : 'ARPHRD_IEEE802154',
+        ARPHRD_PHONET             : 'ARPHRD_PHONET',
+        ARPHRD_PHONET_PIPE        : 'ARPHRD_PHONET_PIPE',
+        ARPHRD_CAIF               : 'ARPHRD_CAIF',
+        ARPHRD_VOID               : 'ARPHRD_VOID',
+        ARPHRD_NONE               : 'ARPHRD_NONE'
+    }
+
+    # =========================================
+    # IFLA_LINKINFO attributes
+    # =========================================
+    IFLA_INFO_UNSPEC     = 0
+    IFLA_INFO_KIND       = 1
+    IFLA_INFO_DATA       = 2
+    IFLA_INFO_XSTATS     = 3
+    IFLA_INFO_SLAVE_KIND = 4
+    IFLA_INFO_SLAVE_DATA = 5
+    IFLA_INFO_MAX        = 6
+
+    ifla_info_to_string = {
+        IFLA_INFO_UNSPEC     : 'IFLA_INFO_UNSPEC',
+        IFLA_INFO_KIND       : 'IFLA_INFO_KIND',
+        IFLA_INFO_DATA       : 'IFLA_INFO_DATA',
+        IFLA_INFO_XSTATS     : 'IFLA_INFO_XSTATS',
+        IFLA_INFO_SLAVE_KIND : 'IFLA_INFO_SLAVE_KIND',
+        IFLA_INFO_SLAVE_DATA : 'IFLA_INFO_SLAVE_DATA',
+        IFLA_INFO_MAX        : 'IFLA_INFO_MAX'
+    }
+
+    # =========================================
+    # IFLA_INFO_DATA attributes for vlan
+    # =========================================
+    IFLA_VLAN_UNSPEC      = 0
+    IFLA_VLAN_ID          = 1
+    IFLA_VLAN_FLAGS       = 2
+    IFLA_VLAN_EGRESS_QOS  = 3
+    IFLA_VLAN_INGRESS_QOS = 4
+    IFLA_VLAN_PROTOCOL    = 5
+
+    ifla_vlan_to_string = {
+        IFLA_VLAN_UNSPEC      : 'IFLA_VLAN_UNSPEC',
+        IFLA_VLAN_ID          : 'IFLA_VLAN_ID',
+        IFLA_VLAN_FLAGS       : 'IFLA_VLAN_FLAGS',
+        IFLA_VLAN_EGRESS_QOS  : 'IFLA_VLAN_EGRESS_QOS',
+        IFLA_VLAN_INGRESS_QOS : 'IFLA_VLAN_INGRESS_QOS',
+        IFLA_VLAN_PROTOCOL    : 'IFLA_VLAN_PROTOCOL'
+    }
+
+    ifla_vlan_protocol_dict = {
+        '802.1Q':   0x8100,
+        '802.1q':   0x8100,
+
+        '802.1ad':  0x88A8,
+        '802.1AD':  0x88A8,
+        '802.1Ad':  0x88A8,
+        '802.1aD':  0x88A8,
+
+        0x8100:     '802.1Q',
+        0x88A8:     '802.1ad'
+    }
+
+    # =========================================
+    # IFLA_INFO_DATA attributes for macvlan
+    # =========================================
+    IFLA_MACVLAN_UNSPEC = 0
+    IFLA_MACVLAN_MODE   = 1
+
+    ifla_macvlan_to_string = {
+        IFLA_MACVLAN_UNSPEC : 'IFLA_MACVLAN_UNSPEC',
+        IFLA_MACVLAN_MODE   : 'IFLA_MACVLAN_MODE'
+    }
+
+    # macvlan modes
+    MACVLAN_MODE_PRIVATE  = 1
+    MACVLAN_MODE_VEPA     = 2
+    MACVLAN_MODE_BRIDGE   = 3
+    MACVLAN_MODE_PASSTHRU = 4
+
+    macvlan_mode_to_string = {
+        MACVLAN_MODE_PRIVATE  : 'MACVLAN_MODE_PRIVATE',
+        MACVLAN_MODE_VEPA     : 'MACVLAN_MODE_VEPA',
+        MACVLAN_MODE_BRIDGE   : 'MACVLAN_MODE_BRIDGE',
+        MACVLAN_MODE_PASSTHRU : 'MACVLAN_MODE_PASSTHRU'
+    }
+
+    # =========================================
+    # IFLA_INFO_DATA attributes for vxlan
+    # =========================================
+    IFLA_VXLAN_UNSPEC            = 0
+    IFLA_VXLAN_ID                = 1
+    IFLA_VXLAN_GROUP             = 2
+    IFLA_VXLAN_LINK              = 3
+    IFLA_VXLAN_LOCAL             = 4
+    IFLA_VXLAN_TTL               = 5
+    IFLA_VXLAN_TOS               = 6
+    IFLA_VXLAN_LEARNING          = 7
+    IFLA_VXLAN_AGEING            = 8
+    IFLA_VXLAN_LIMIT             = 9
+    IFLA_VXLAN_PORT_RANGE        = 10
+    IFLA_VXLAN_PROXY             = 11
+    IFLA_VXLAN_RSC               = 12
+    IFLA_VXLAN_L2MISS            = 13
+    IFLA_VXLAN_L3MISS            = 14
+    IFLA_VXLAN_PORT              = 15
+    IFLA_VXLAN_GROUP6            = 16
+    IFLA_VXLAN_LOCAL6            = 17
+    IFLA_VXLAN_UDP_CSUM          = 18
+    IFLA_VXLAN_UDP_ZERO_CSUM6_TX = 19
+    IFLA_VXLAN_UDP_ZERO_CSUM6_RX = 20
+    IFLA_VXLAN_REMCSUM_TX        = 21
+    IFLA_VXLAN_REMCSUM_RX        = 22
+    IFLA_VXLAN_GBP               = 23
+    IFLA_VXLAN_REMCSUM_NOPARTIAL = 24
+    IFLA_VXLAN_COLLECT_METADATA  = 25
+    IFLA_VXLAN_REPLICATION_NODE  = 253
+    IFLA_VXLAN_REPLICATION_TYPE  = 254
+
+    ifla_vxlan_to_string = {
+        IFLA_VXLAN_UNSPEC            : 'IFLA_VXLAN_UNSPEC',
+        IFLA_VXLAN_ID                : 'IFLA_VXLAN_ID',
+        IFLA_VXLAN_GROUP             : 'IFLA_VXLAN_GROUP',
+        IFLA_VXLAN_LINK              : 'IFLA_VXLAN_LINK',
+        IFLA_VXLAN_LOCAL             : 'IFLA_VXLAN_LOCAL',
+        IFLA_VXLAN_TTL               : 'IFLA_VXLAN_TTL',
+        IFLA_VXLAN_TOS               : 'IFLA_VXLAN_TOS',
+        IFLA_VXLAN_LEARNING          : 'IFLA_VXLAN_LEARNING',
+        IFLA_VXLAN_AGEING            : 'IFLA_VXLAN_AGEING',
+        IFLA_VXLAN_LIMIT             : 'IFLA_VXLAN_LIMIT',
+        IFLA_VXLAN_PORT_RANGE        : 'IFLA_VXLAN_PORT_RANGE',
+        IFLA_VXLAN_PROXY             : 'IFLA_VXLAN_PROXY',
+        IFLA_VXLAN_RSC               : 'IFLA_VXLAN_RSC',
+        IFLA_VXLAN_L2MISS            : 'IFLA_VXLAN_L2MISS',
+        IFLA_VXLAN_L3MISS            : 'IFLA_VXLAN_L3MISS',
+        IFLA_VXLAN_PORT              : 'IFLA_VXLAN_PORT',
+        IFLA_VXLAN_GROUP6            : 'IFLA_VXLAN_GROUP6',
+        IFLA_VXLAN_LOCAL6            : 'IFLA_VXLAN_LOCAL6',
+        IFLA_VXLAN_UDP_CSUM          : 'IFLA_VXLAN_UDP_CSUM',
+        IFLA_VXLAN_UDP_ZERO_CSUM6_TX : 'IFLA_VXLAN_UDP_ZERO_CSUM6_TX',
+        IFLA_VXLAN_UDP_ZERO_CSUM6_RX : 'IFLA_VXLAN_UDP_ZERO_CSUM6_RX',
+        IFLA_VXLAN_REMCSUM_TX        : 'IFLA_VXLAN_REMCSUM_TX',
+        IFLA_VXLAN_REMCSUM_RX        : 'IFLA_VXLAN_REMCSUM_RX',
+        IFLA_VXLAN_GBP               : 'IFLA_VXLAN_GBP',
+        IFLA_VXLAN_REMCSUM_NOPARTIAL : 'IFLA_VXLAN_REMCSUM_NOPARTIAL',
+        IFLA_VXLAN_COLLECT_METADATA  : 'IFLA_VXLAN_COLLECT_METADATA',
+        IFLA_VXLAN_REPLICATION_NODE  : 'IFLA_VXLAN_REPLICATION_NODE',
+        IFLA_VXLAN_REPLICATION_TYPE  : 'IFLA_VXLAN_REPLICATION_TYPE'
+    }
+
+    # =========================================
+    # IFLA_INFO_DATA attributes for bonds
+    # =========================================
+    IFLA_BOND_UNSPEC                    = 0
+    IFLA_BOND_MODE                      = 1
+    IFLA_BOND_ACTIVE_SLAVE              = 2
+    IFLA_BOND_MIIMON                    = 3
+    IFLA_BOND_UPDELAY                   = 4
+    IFLA_BOND_DOWNDELAY                 = 5
+    IFLA_BOND_USE_CARRIER               = 6
+    IFLA_BOND_ARP_INTERVAL              = 7
+    IFLA_BOND_ARP_IP_TARGET             = 8
+    IFLA_BOND_ARP_VALIDATE              = 9
+    IFLA_BOND_ARP_ALL_TARGETS           = 10
+    IFLA_BOND_PRIMARY                   = 11
+    IFLA_BOND_PRIMARY_RESELECT          = 12
+    IFLA_BOND_FAIL_OVER_MAC             = 13
+    IFLA_BOND_XMIT_HASH_POLICY          = 14
+    IFLA_BOND_RESEND_IGMP               = 15
+    IFLA_BOND_NUM_PEER_NOTIF            = 16
+    IFLA_BOND_ALL_SLAVES_ACTIVE         = 17
+    IFLA_BOND_MIN_LINKS                 = 18
+    IFLA_BOND_LP_INTERVAL               = 19
+    IFLA_BOND_PACKETS_PER_SLAVE         = 20
+    IFLA_BOND_AD_LACP_RATE              = 21
+    IFLA_BOND_AD_SELECT                 = 22
+    IFLA_BOND_AD_INFO                   = 23
+    IFLA_BOND_AD_ACTOR_SYS_PRIO         = 24
+    IFLA_BOND_AD_USER_PORT_KEY          = 25
+    IFLA_BOND_AD_ACTOR_SYSTEM           = 26
+    IFLA_BOND_AD_LACP_BYPASS            = 100
+
+    ifla_bond_to_string = {
+        IFLA_BOND_UNSPEC                    : 'IFLA_BOND_UNSPEC',
+        IFLA_BOND_MODE                      : 'IFLA_BOND_MODE',
+        IFLA_BOND_ACTIVE_SLAVE              : 'IFLA_BOND_ACTIVE_SLAVE',
+        IFLA_BOND_MIIMON                    : 'IFLA_BOND_MIIMON',
+        IFLA_BOND_UPDELAY                   : 'IFLA_BOND_UPDELAY',
+        IFLA_BOND_DOWNDELAY                 : 'IFLA_BOND_DOWNDELAY',
+        IFLA_BOND_USE_CARRIER               : 'IFLA_BOND_USE_CARRIER',
+        IFLA_BOND_ARP_INTERVAL              : 'IFLA_BOND_ARP_INTERVAL',
+        IFLA_BOND_ARP_IP_TARGET             : 'IFLA_BOND_ARP_IP_TARGET',
+        IFLA_BOND_ARP_VALIDATE              : 'IFLA_BOND_ARP_VALIDATE',
+        IFLA_BOND_ARP_ALL_TARGETS           : 'IFLA_BOND_ARP_ALL_TARGETS',
+        IFLA_BOND_PRIMARY                   : 'IFLA_BOND_PRIMARY',
+        IFLA_BOND_PRIMARY_RESELECT          : 'IFLA_BOND_PRIMARY_RESELECT',
+        IFLA_BOND_FAIL_OVER_MAC             : 'IFLA_BOND_FAIL_OVER_MAC',
+        IFLA_BOND_XMIT_HASH_POLICY          : 'IFLA_BOND_XMIT_HASH_POLICY',
+        IFLA_BOND_RESEND_IGMP               : 'IFLA_BOND_RESEND_IGMP',
+        IFLA_BOND_NUM_PEER_NOTIF            : 'IFLA_BOND_NUM_PEER_NOTIF',
+        IFLA_BOND_ALL_SLAVES_ACTIVE         : 'IFLA_BOND_ALL_SLAVES_ACTIVE',
+        IFLA_BOND_MIN_LINKS                 : 'IFLA_BOND_MIN_LINKS',
+        IFLA_BOND_LP_INTERVAL               : 'IFLA_BOND_LP_INTERVAL',
+        IFLA_BOND_PACKETS_PER_SLAVE         : 'IFLA_BOND_PACKETS_PER_SLAVE',
+        IFLA_BOND_AD_LACP_RATE              : 'IFLA_BOND_AD_LACP_RATE',
+        IFLA_BOND_AD_SELECT                 : 'IFLA_BOND_AD_SELECT',
+        IFLA_BOND_AD_INFO                   : 'IFLA_BOND_AD_INFO',
+        IFLA_BOND_AD_ACTOR_SYS_PRIO         : 'IFLA_BOND_AD_ACTOR_SYS_PRIO',
+        IFLA_BOND_AD_USER_PORT_KEY          : 'IFLA_BOND_AD_USER_PORT_KEY',
+        IFLA_BOND_AD_ACTOR_SYSTEM           : 'IFLA_BOND_AD_ACTOR_SYSTEM',
+        IFLA_BOND_AD_LACP_BYPASS            : 'IFLA_BOND_AD_LACP_BYPASS'
+    }
+
+    IFLA_BOND_AD_INFO_UNSPEC            = 0
+    IFLA_BOND_AD_INFO_AGGREGATOR        = 1
+    IFLA_BOND_AD_INFO_NUM_PORTS         = 2
+    IFLA_BOND_AD_INFO_ACTOR_KEY         = 3
+    IFLA_BOND_AD_INFO_PARTNER_KEY       = 4
+    IFLA_BOND_AD_INFO_PARTNER_MAC       = 5
+
+    ifla_bond_ad_to_string = {
+        IFLA_BOND_AD_INFO_UNSPEC            : 'IFLA_BOND_AD_INFO_UNSPEC',
+        IFLA_BOND_AD_INFO_AGGREGATOR        : 'IFLA_BOND_AD_INFO_AGGREGATOR',
+        IFLA_BOND_AD_INFO_NUM_PORTS         : 'IFLA_BOND_AD_INFO_NUM_PORTS',
+        IFLA_BOND_AD_INFO_ACTOR_KEY         : 'IFLA_BOND_AD_INFO_ACTOR_KEY',
+        IFLA_BOND_AD_INFO_PARTNER_KEY       : 'IFLA_BOND_AD_INFO_PARTNER_KEY',
+        IFLA_BOND_AD_INFO_PARTNER_MAC       : 'IFLA_BOND_AD_INFO_PARTNER_MAC'
+    }
+
+    ifla_bond_mode_tbl = {
+        'balance-rr': 0,
+        'active-backup': 1,
+        'balance-xor': 2,
+        'broadcast': 3,
+        '802.3ad': 4,
+        'balance-tlb': 5,
+        'balance-alb': 6,
+        '0': 0,
+        '1': 1,
+        '2': 2,
+        '3': 3,
+        '4': 4,
+        '5': 5,
+        '6': 6,
+        0: 0,
+        1: 1,
+        2: 2,
+        3: 3,
+        4: 4,
+        5: 5,
+        6: 6
+    }
+
+    ifla_bond_mode_pretty_tbl = {
+        0: 'balance-rr',
+        1: 'active-backup',
+        2: 'balance-xor',
+        3: 'broadcast',
+        4: '802.3ad',
+        5: 'balance-tlb',
+        6: 'balance-alb'
+    }
+
+    ifla_bond_xmit_hash_policy_tbl = {
+        'layer2': 0,
+        'layer3+4': 1,
+        'layer2+3': 2,
+        'encap2+3': 3,
+        'encap3+4': 4,
+        '0': 0,
+        '1': 1,
+        '2': 2,
+        '3': 3,
+        '4': 4,
+        0: 0,
+        1: 1,
+        2: 2,
+        3: 3,
+        4: 4
+    }
+
+    ifla_bond_xmit_hash_policy_pretty_tbl = {
+        0: 'layer2',
+        1: 'layer3+4',
+        2: 'layer2+3',
+        3: 'encap2+3',
+        4: 'encap3+4',
+    }
+
+    # =========================================
+    # IFLA_INFO_SLAVE_DATA attributes for bonds
+    # =========================================
+    IFLA_BOND_SLAVE_UNSPEC                      = 0
+    IFLA_BOND_SLAVE_STATE                       = 1
+    IFLA_BOND_SLAVE_MII_STATUS                  = 2
+    IFLA_BOND_SLAVE_LINK_FAILURE_COUNT          = 3
+    IFLA_BOND_SLAVE_PERM_HWADDR                 = 4
+    IFLA_BOND_SLAVE_QUEUE_ID                    = 5
+    IFLA_BOND_SLAVE_AD_AGGREGATOR_ID            = 6
+    IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE    = 7
+    IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE  = 8
+    IFLA_BOND_SLAVE_CL_START                    = 50
+    IFLA_BOND_SLAVE_AD_RX_BYPASS                = IFLA_BOND_SLAVE_CL_START
+
+    ifla_bond_slave_to_string = {
+        IFLA_BOND_SLAVE_UNSPEC                      : 'IFLA_BOND_SLAVE_UNSPEC',
+        IFLA_BOND_SLAVE_STATE                       : 'IFLA_BOND_SLAVE_STATE',
+        IFLA_BOND_SLAVE_MII_STATUS                  : 'IFLA_BOND_SLAVE_MII_STATUS',
+        IFLA_BOND_SLAVE_LINK_FAILURE_COUNT          : 'IFLA_BOND_SLAVE_LINK_FAILURE_COUNT',
+        IFLA_BOND_SLAVE_PERM_HWADDR                 : 'IFLA_BOND_SLAVE_PERM_HWADDR',
+        IFLA_BOND_SLAVE_QUEUE_ID                    : 'IFLA_BOND_SLAVE_QUEUE_ID',
+        IFLA_BOND_SLAVE_AD_AGGREGATOR_ID            : 'IFLA_BOND_SLAVE_AD_AGGREGATOR_ID',
+        IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE    : 'IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE',
+        IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE  : 'IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE',
+        IFLA_BOND_SLAVE_CL_START                    : 'IFLA_BOND_SLAVE_CL_START',
+        IFLA_BOND_SLAVE_AD_RX_BYPASS                : 'IFLA_BOND_SLAVE_AD_RX_BYPASS'
+    }
+
+    # =========================================
+    # IFLA_PROTINFO attributes for bridge ports
+    # =========================================
+    IFLA_BRPORT_UNSPEC              = 0
+    IFLA_BRPORT_STATE               = 1
+    IFLA_BRPORT_PRIORITY            = 2
+    IFLA_BRPORT_COST                = 3
+    IFLA_BRPORT_MODE                = 4
+    IFLA_BRPORT_GUARD               = 5
+    IFLA_BRPORT_PROTECT             = 6
+    IFLA_BRPORT_FAST_LEAVE          = 7
+    IFLA_BRPORT_LEARNING            = 8
+    IFLA_BRPORT_UNICAST_FLOOD       = 9
+    IFLA_BRPORT_PROXYARP            = 10
+    IFLA_BRPORT_LEARNING_SYNC       = 11
+    IFLA_BRPORT_PROXYARP_WIFI       = 12
+    IFLA_BRPORT_ROOT_ID             = 13
+    IFLA_BRPORT_BRIDGE_ID           = 14
+    IFLA_BRPORT_DESIGNATED_PORT     = 15
+    IFLA_BRPORT_DESIGNATED_COST     = 16
+    IFLA_BRPORT_ID                  = 17
+    IFLA_BRPORT_NO                  = 18
+    IFLA_BRPORT_TOPOLOGY_CHANGE_ACK = 19
+    IFLA_BRPORT_CONFIG_PENDING      = 20
+    IFLA_BRPORT_MESSAGE_AGE_TIMER   = 21
+    IFLA_BRPORT_FORWARD_DELAY_TIMER = 22
+    IFLA_BRPORT_HOLD_TIMER          = 23
+    IFLA_BRPORT_FLUSH               = 24
+    IFLA_BRPORT_MULTICAST_ROUTER    = 25
+    IFLA_BRPORT_PAD                 = 26
+    IFLA_BRPORT_MCAST_FLOOD         = 27
+    IFLA_BRPORT_MCAST_TO_UCAST      = 28
+    IFLA_BRPORT_VLAN_TUNNEL         = 29
+    IFLA_BRPORT_BCAST_FLOOD         = 30
+    IFLA_BRPORT_GROUP_FWD_MASK      = 31
+    IFLA_BRPORT_PEER_LINK           = 150
+    IFLA_BRPORT_DUAL_LINK           = 151
+    IFLA_BRPORT_ARP_SUPPRESS        = 152
+    IFLA_BRPORT_GROUP_FWD_MASKHI    = 153
+    IFLA_BRPORT_DOWN_PEERLINK_REDIRECT = 154
+
+    ifla_brport_to_string = {
+        IFLA_BRPORT_UNSPEC              : 'IFLA_BRPORT_UNSPEC',
+        IFLA_BRPORT_STATE               : 'IFLA_BRPORT_STATE',
+        IFLA_BRPORT_PRIORITY            : 'IFLA_BRPORT_PRIORITY',
+        IFLA_BRPORT_COST                : 'IFLA_BRPORT_COST',
+        IFLA_BRPORT_MODE                : 'IFLA_BRPORT_MODE',
+        IFLA_BRPORT_GUARD               : 'IFLA_BRPORT_GUARD',
+        IFLA_BRPORT_PROTECT             : 'IFLA_BRPORT_PROTECT',
+        IFLA_BRPORT_FAST_LEAVE          : 'IFLA_BRPORT_FAST_LEAVE',
+        IFLA_BRPORT_LEARNING            : 'IFLA_BRPORT_LEARNING',
+        IFLA_BRPORT_UNICAST_FLOOD       : 'IFLA_BRPORT_UNICAST_FLOOD',
+        IFLA_BRPORT_PROXYARP            : 'IFLA_BRPORT_PROXYARP',
+        IFLA_BRPORT_LEARNING_SYNC       : 'IFLA_BRPORT_LEARNING_SYNC',
+        IFLA_BRPORT_PROXYARP_WIFI       : 'IFLA_BRPORT_PROXYARP_WIFI',
+        IFLA_BRPORT_ROOT_ID             : 'IFLA_BRPORT_ROOT_ID',
+        IFLA_BRPORT_BRIDGE_ID           : 'IFLA_BRPORT_BRIDGE_ID',
+        IFLA_BRPORT_DESIGNATED_PORT     : 'IFLA_BRPORT_DESIGNATED_PORT',
+        IFLA_BRPORT_DESIGNATED_COST     : 'IFLA_BRPORT_DESIGNATED_COST',
+        IFLA_BRPORT_ID                  : 'IFLA_BRPORT_ID',
+        IFLA_BRPORT_NO                  : 'IFLA_BRPORT_NO',
+        IFLA_BRPORT_TOPOLOGY_CHANGE_ACK : 'IFLA_BRPORT_TOPOLOGY_CHANGE_ACK',
+        IFLA_BRPORT_CONFIG_PENDING      : 'IFLA_BRPORT_CONFIG_PENDING',
+        IFLA_BRPORT_MESSAGE_AGE_TIMER   : 'IFLA_BRPORT_MESSAGE_AGE_TIMER',
+        IFLA_BRPORT_FORWARD_DELAY_TIMER : 'IFLA_BRPORT_FORWARD_DELAY_TIMER',
+        IFLA_BRPORT_HOLD_TIMER          : 'IFLA_BRPORT_HOLD_TIMER',
+        IFLA_BRPORT_FLUSH               : 'IFLA_BRPORT_FLUSH',
+        IFLA_BRPORT_MULTICAST_ROUTER    : 'IFLA_BRPORT_MULTICAST_ROUTER',
+        IFLA_BRPORT_PAD                 : 'IFLA_BRPORT_PAD',
+        IFLA_BRPORT_MCAST_FLOOD         : 'IFLA_BRPORT_MCAST_FLOOD',
+        IFLA_BRPORT_MCAST_TO_UCAST      : 'IFLA_BRPORT_MCAST_TO_UCAST',
+        IFLA_BRPORT_VLAN_TUNNEL         : 'IFLA_BRPORT_VLAN_TUNNEL',
+        IFLA_BRPORT_BCAST_FLOOD         : 'IFLA_BRPORT_BCAST_FLOOD',
+        IFLA_BRPORT_GROUP_FWD_MASK      : 'IFLA_BRPORT_GROUP_FWD_MASK',
+        IFLA_BRPORT_PEER_LINK           : 'IFLA_BRPORT_PEER_LINK',
+        IFLA_BRPORT_DUAL_LINK           : 'IFLA_BRPORT_DUAL_LINK',
+        IFLA_BRPORT_ARP_SUPPRESS        : 'IFLA_BRPORT_ARP_SUPPRESS',
+        IFLA_BRPORT_GROUP_FWD_MASKHI    : 'IFLA_BRPORT_GROUP_FWD_MASKHI',
+        IFLA_BRPORT_DOWN_PEERLINK_REDIRECT : 'IFLA_BRPORT_DOWN_PEERLINK_REDIRECT'
+    }
+
+    # BRIDGE IFLA_AF_SPEC attributes
+    IFLA_BRIDGE_FLAGS     = 0
+    IFLA_BRIDGE_MODE      = 1
+    IFLA_BRIDGE_VLAN_INFO = 2
+
+    ifla_bridge_af_spec_to_string = {
+        IFLA_BRIDGE_FLAGS     : 'IFLA_BRIDGE_FLAGS',
+        IFLA_BRIDGE_MODE      : 'IFLA_BRIDGE_MODE',
+        IFLA_BRIDGE_VLAN_INFO : 'IFLA_BRIDGE_VLAN_INFO'
+    }
+
+    # BRIDGE_VLAN_INFO flags
+    BRIDGE_VLAN_INFO_MASTER      = 1 << 0  # Operate on Bridge device as well
+    BRIDGE_VLAN_INFO_PVID        = 1 << 1  # VLAN is PVID, ingress untagged
+    BRIDGE_VLAN_INFO_UNTAGGED    = 1 << 2  # VLAN egresses untagged
+    BRIDGE_VLAN_INFO_RANGE_BEGIN = 1 << 3  # VLAN is start of vlan range
+    BRIDGE_VLAN_INFO_RANGE_END   = 1 << 4  # VLAN is end of vlan range
+    BRIDGE_VLAN_INFO_BRENTRY     = 1 << 5  # Global bridge VLAN entry
+
+    bridge_vlan_to_string = {
+        BRIDGE_VLAN_INFO_MASTER      : 'BRIDGE_VLAN_INFO_MASTER',
+        BRIDGE_VLAN_INFO_PVID        : 'BRIDGE_VLAN_INFO_PVID',
+        BRIDGE_VLAN_INFO_UNTAGGED    : 'BRIDGE_VLAN_INFO_UNTAGGED',
+        BRIDGE_VLAN_INFO_RANGE_BEGIN : 'BRIDGE_VLAN_INFO_RANGE_BEGIN',
+        BRIDGE_VLAN_INFO_RANGE_END   : 'BRIDGE_VLAN_INFO_RANGE_END',
+        BRIDGE_VLAN_INFO_BRENTRY     : 'BRIDGE_VLAN_INFO_BRENTRY'
+    }
+
+    # Bridge flags
+    BRIDGE_FLAGS_MASTER = 1
+    BRIDGE_FLAGS_SELF   = 2
+
+    bridge_flags_to_string = {
+        BRIDGE_FLAGS_MASTER : 'BRIDGE_FLAGS_MASTER',
+        BRIDGE_FLAGS_SELF   : 'BRIDGE_FLAGS_SELF'
+    }
+
+    # filters for IFLA_EXT_MASK
+    RTEXT_FILTER_VF                = 1 << 0
+    RTEXT_FILTER_BRVLAN            = 1 << 1
+    RTEXT_FILTER_BRVLAN_COMPRESSED = 1 << 2
+    RTEXT_FILTER_SKIP_STATS        = 1 << 3
+
+    rtext_to_string = {
+        RTEXT_FILTER_VF                : 'RTEXT_FILTER_VF',
+        RTEXT_FILTER_BRVLAN            : 'RTEXT_FILTER_BRVLAN',
+        RTEXT_FILTER_BRVLAN_COMPRESSED : 'RTEXT_FILTER_BRVLAN_COMPRESSED',
+        RTEXT_FILTER_SKIP_STATS        : 'RTEXT_FILTER_SKIP_STATS'
+    }
+
+    IFLA_BR_UNSPEC                     =  0
+    IFLA_BR_FORWARD_DELAY              =  1
+    IFLA_BR_HELLO_TIME                 =  2
+    IFLA_BR_MAX_AGE                    =  3
+    IFLA_BR_AGEING_TIME                =  4
+    IFLA_BR_STP_STATE                  =  5
+    IFLA_BR_PRIORITY                   =  6
+    IFLA_BR_VLAN_FILTERING             =  7
+    IFLA_BR_VLAN_PROTOCOL              =  8
+    IFLA_BR_GROUP_FWD_MASK             =  9
+    IFLA_BR_ROOT_ID                    = 10
+    IFLA_BR_BRIDGE_ID                  = 11
+    IFLA_BR_ROOT_PORT                  = 12
+    IFLA_BR_ROOT_PATH_COST             = 13
+    IFLA_BR_TOPOLOGY_CHANGE            = 14
+    IFLA_BR_TOPOLOGY_CHANGE_DETECTED   = 15
+    IFLA_BR_HELLO_TIMER                = 16
+    IFLA_BR_TCN_TIMER                  = 17
+    IFLA_BR_TOPOLOGY_CHANGE_TIMER      = 18
+    IFLA_BR_GC_TIMER                   = 19
+    IFLA_BR_GROUP_ADDR                 = 20
+    IFLA_BR_FDB_FLUSH                  = 21
+    IFLA_BR_MCAST_ROUTER               = 22
+    IFLA_BR_MCAST_SNOOPING             = 23
+    IFLA_BR_MCAST_QUERY_USE_IFADDR     = 24
+    IFLA_BR_MCAST_QUERIER              = 25
+    IFLA_BR_MCAST_HASH_ELASTICITY      = 26
+    IFLA_BR_MCAST_HASH_MAX             = 27
+    IFLA_BR_MCAST_LAST_MEMBER_CNT      = 28
+    IFLA_BR_MCAST_STARTUP_QUERY_CNT    = 29
+    IFLA_BR_MCAST_LAST_MEMBER_INTVL    = 30
+    IFLA_BR_MCAST_MEMBERSHIP_INTVL     = 31
+    IFLA_BR_MCAST_QUERIER_INTVL        = 32
+    IFLA_BR_MCAST_QUERY_INTVL          = 33
+    IFLA_BR_MCAST_QUERY_RESPONSE_INTVL = 34
+    IFLA_BR_MCAST_STARTUP_QUERY_INTVL  = 35
+    IFLA_BR_NF_CALL_IPTABLES           = 36
+    IFLA_BR_NF_CALL_IP6TABLES          = 37
+    IFLA_BR_NF_CALL_ARPTABLES          = 38
+    IFLA_BR_VLAN_DEFAULT_PVID          = 39
+    IFLA_BR_PAD                        = 40
+    IFLA_BR_VLAN_STATS_ENABLED         = 41
+    IFLA_BR_MCAST_STATS_ENABLED        = 42
+    IFLA_BR_MCAST_IGMP_VERSION         = 43
+    IFLA_BR_MCAST_MLD_VERSION          = 44
+
+    ifla_br_to_string = {
+        IFLA_BR_UNSPEC                     : 'IFLA_BR_UNSPEC',
+        IFLA_BR_FORWARD_DELAY              : 'IFLA_BR_FORWARD_DELAY',
+        IFLA_BR_HELLO_TIME                 : 'IFLA_BR_HELLO_TIME',
+        IFLA_BR_MAX_AGE                    : 'IFLA_BR_MAX_AGE',
+        IFLA_BR_AGEING_TIME                : 'IFLA_BR_AGEING_TIME',
+        IFLA_BR_STP_STATE                  : 'IFLA_BR_STP_STATE',
+        IFLA_BR_PRIORITY                   : 'IFLA_BR_PRIORITY',
+        IFLA_BR_VLAN_FILTERING             : 'IFLA_BR_VLAN_FILTERING',
+        IFLA_BR_VLAN_PROTOCOL              : 'IFLA_BR_VLAN_PROTOCOL',
+        IFLA_BR_GROUP_FWD_MASK             : 'IFLA_BR_GROUP_FWD_MASK',
+        IFLA_BR_ROOT_ID                    : 'IFLA_BR_ROOT_ID',
+        IFLA_BR_BRIDGE_ID                  : 'IFLA_BR_BRIDGE_ID',
+        IFLA_BR_ROOT_PORT                  : 'IFLA_BR_ROOT_PORT',
+        IFLA_BR_ROOT_PATH_COST             : 'IFLA_BR_ROOT_PATH_COST',
+        IFLA_BR_TOPOLOGY_CHANGE            : 'IFLA_BR_TOPOLOGY_CHANGE',
+        IFLA_BR_TOPOLOGY_CHANGE_DETECTED   : 'IFLA_BR_TOPOLOGY_CHANGE_DETECTED',
+        IFLA_BR_HELLO_TIMER                : 'IFLA_BR_HELLO_TIMER',
+        IFLA_BR_TCN_TIMER                  : 'IFLA_BR_TCN_TIMER',
+        IFLA_BR_TOPOLOGY_CHANGE_TIMER      : 'IFLA_BR_TOPOLOGY_CHANGE_TIMER',
+        IFLA_BR_GC_TIMER                   : 'IFLA_BR_GC_TIMER',
+        IFLA_BR_GROUP_ADDR                 : 'IFLA_BR_GROUP_ADDR',
+        IFLA_BR_FDB_FLUSH                  : 'IFLA_BR_FDB_FLUSH',
+        IFLA_BR_MCAST_ROUTER               : 'IFLA_BR_MCAST_ROUTER',
+        IFLA_BR_MCAST_SNOOPING             : 'IFLA_BR_MCAST_SNOOPING',
+        IFLA_BR_MCAST_QUERY_USE_IFADDR     : 'IFLA_BR_MCAST_QUERY_USE_IFADDR',
+        IFLA_BR_MCAST_QUERIER              : 'IFLA_BR_MCAST_QUERIER',
+        IFLA_BR_MCAST_HASH_ELASTICITY      : 'IFLA_BR_MCAST_HASH_ELASTICITY',
+        IFLA_BR_MCAST_HASH_MAX             : 'IFLA_BR_MCAST_HASH_MAX',
+        IFLA_BR_MCAST_LAST_MEMBER_CNT      : 'IFLA_BR_MCAST_LAST_MEMBER_CNT',
+        IFLA_BR_MCAST_STARTUP_QUERY_CNT    : 'IFLA_BR_MCAST_STARTUP_QUERY_CNT',
+        IFLA_BR_MCAST_LAST_MEMBER_INTVL    : 'IFLA_BR_MCAST_LAST_MEMBER_INTVL',
+        IFLA_BR_MCAST_MEMBERSHIP_INTVL     : 'IFLA_BR_MCAST_MEMBERSHIP_INTVL',
+        IFLA_BR_MCAST_QUERIER_INTVL        : 'IFLA_BR_MCAST_QUERIER_INTVL',
+        IFLA_BR_MCAST_QUERY_INTVL          : 'IFLA_BR_MCAST_QUERY_INTVL',
+        IFLA_BR_MCAST_QUERY_RESPONSE_INTVL : 'IFLA_BR_MCAST_QUERY_RESPONSE_INTVL',
+        IFLA_BR_MCAST_STARTUP_QUERY_INTVL  : 'IFLA_BR_MCAST_STARTUP_QUERY_INTVL',
+        IFLA_BR_NF_CALL_IPTABLES           : 'IFLA_BR_NF_CALL_IPTABLES',
+        IFLA_BR_NF_CALL_IP6TABLES          : 'IFLA_BR_NF_CALL_IP6TABLES',
+        IFLA_BR_NF_CALL_ARPTABLES          : 'IFLA_BR_NF_CALL_ARPTABLES',
+        IFLA_BR_VLAN_DEFAULT_PVID          : 'IFLA_BR_VLAN_DEFAULT_PVID',
+        IFLA_BR_PAD                        : 'IFLA_BR_PAD',
+        IFLA_BR_VLAN_STATS_ENABLED         : 'IFLA_BR_VLAN_STATS_ENABLED',
+        IFLA_BR_MCAST_STATS_ENABLED        : 'IFLA_BR_MCAST_STATS_ENABLED',
+        IFLA_BR_MCAST_IGMP_VERSION         : 'IFLA_BR_MCAST_IGMP_VERSION',
+        IFLA_BR_MCAST_MLD_VERSION          : 'IFLA_BR_MCAST_MLD_VERSION'
+    }
+
+    # =========================================
+    # IFLA_INFO_DATA attributes for vrfs
+    # =========================================
+    IFLA_VRF_UNSPEC                         = 0
+    IFLA_VRF_TABLE                          = 1
+
+    ifla_vrf_to_string = {
+        IFLA_VRF_UNSPEC                     : 'IFLA_VRF_UNSPEC',
+        IFLA_VRF_TABLE                      : 'IFLA_VRF_TABLE'
+    }
+
+    def __init__(self, msgtype, debug=False, logger=None, use_color=True):
+        NetlinkPacket.__init__(self, msgtype, debug, logger, use_color)
+        self.PACK = 'BxHiII'
+        self.LEN  = calcsize(self.PACK)
+
+    def get_link_type_string(self, index):
+        return self.get_string(self.link_type_to_string, index)
+
+    def get_ifla_bridge_af_spec_to_string(self, index):
+        return self.get_string(self.ifla_bridge_af_spec_to_string, index)
+
+    def get_ifla_info_string(self, index):
+        return self.get_string(self.ifla_info_to_string, index)
+
+    def get_ifla_vlan_string(self, index):
+        return self.get_string(self.ifla_vlan_to_string, index)
+
+    def get_ifla_vxlan_string(self, index):
+        return self.get_string(self.ifla_vxlan_to_string, index)
+
+    def get_ifla_macvlan_string(self, index):
+        return self.get_string(self.ifla_macvlan_to_string, index)
+
+    def get_macvlan_mode_string(self, index):
+        return self.get_string(self.macvlan_mode_to_string, index)
+
+    def get_ifla_bond_string(self, index):
+        return self.get_string(self.ifla_bond_to_string, index)
+
+    def get_ifla_bond_ad_string(self, index):
+        return self.get_string(self.ifla_bond_ad_to_string, index)
+
+    def get_ifla_brport_string(self, index):
+        return self.get_string(self.ifla_brport_to_string, index)
+
+    def get_ifla_br_string(self, index):
+        return self.get_string(self.ifla_br_to_string, index)
+
+    def get_bridge_vlan_string(self, index):
+        return self.get_string(self.bridge_vlan_to_string, index)
+
+    def get_bridge_flags_string(self, index):
+        return self.get_string(self.bridge_flags_to_string, index)
+
+    def decode_service_header(self):
+
+        # Nothing to do if the message did not contain a service header
+        if self.length == self.header_LEN:
+            return
+
+        (self.family, self.device_type,
+         self.ifindex,
+         self.flags,
+         self.change_mask) = \
+            unpack(self.PACK, self.msg_data[:self.LEN])
+
+        if self.debug:
+            color = yellow if self.use_color else None
+            color_start = "\033[%dm" % color if color else ""
+            color_end = "\033[0m" if color else ""
+            self.dump_buffer.append("  %sService Header%s" % (color_start, color_end))
+
+            for x in range(0, self.LEN/4):
+                if self.line_number == 5:
+                    extra = "Family %s (%d), Device Type %s (%d - %s)" % \
+                            (zfilled_hex(self.family, 2), self.family,
+                             zfilled_hex(self.device_type, 4), self.device_type, self.get_link_type_string(self.device_type))
+                elif self.line_number == 6:
+                    extra = "Interface Index %s (%d)" % (zfilled_hex(self.ifindex, 8), self.ifindex)
+                elif self.line_number == 7:
+                    extra = "Device Flags %s (%s)" % (zfilled_hex(self.flags, 8), self.get_flags_string())
+                elif self.line_number == 8:
+                    extra = "Change Mask %s" % zfilled_hex(self.change_mask, 8)
+                else:
+                    extra = "Unexpected line number %d" % self.line_number
+
+                start = x * 4
+                end = start + 4
+                self.dump_buffer.append(data_to_color_text(self.line_number, color, self.msg_data[start:end], extra))
+                self.line_number += 1
+
+    def is_up(self):
+        if self.flags & Link.IFF_UP:
+            return True
+        return False
+
+
+class Neighbor(NetlinkPacket):
+    """
+    Service Header
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |   Family    |    Reserved1  |           Reserved2           |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                     Interface Index                         |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |           State             |     Flags     |     Type      |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    """
+
+    # Neighbor attributes
+    # /usr/include/linux/neighbour.h
+    NDA_UNSPEC       = 0x00  # Unknown type
+    NDA_DST          = 0x01  # A neighbour cache network. layer destination address
+    NDA_LLADDR       = 0x02  # A neighbor cache link layer address.
+    NDA_CACHEINFO    = 0x03  # Cache statistics
+    NDA_PROBES       = 0x04
+    NDA_VLAN         = 0x05
+    NDA_PORT         = 0x06
+    NDA_VNI          = 0x07
+    NDA_IFINDEX      = 0x08
+    NDA_MASTER       = 0x09
+    NDA_LINK_NETNSID = 0x0A
+
+    attribute_to_class = {
+        NDA_UNSPEC       : ('NDA_UNSPEC', AttributeGeneric),
+        NDA_DST          : ('NDA_DST', AttributeIPAddress),
+        NDA_LLADDR       : ('NDA_LLADDR', AttributeMACAddress),
+        NDA_CACHEINFO    : ('NDA_CACHEINFO', AttributeFourByteList),
+        NDA_PROBES       : ('NDA_PROBES', AttributeFourByteValue),
+        NDA_VLAN         : ('NDA_VLAN', AttributeTwoByteValue),
+        NDA_PORT         : ('NDA_PORT', AttributeGeneric),
+        NDA_VNI          : ('NDA_VNI', AttributeFourByteValue),
+        NDA_IFINDEX      : ('NDA_IFINDEX', AttributeFourByteValue),
+        NDA_MASTER       : ('NDA_MASTER', AttributeFourByteValue),
+        NDA_LINK_NETNSID : ('NDA_LINK_NETNSID', AttributeGeneric)
+    }
+
+    # Neighbor flags
+    # /usr/include/linux/neighbour.h
+    NTF_USE         = 0x01
+    NTF_SELF        = 0x02
+    NTF_MASTER      = 0x04
+    NTF_PROXY       = 0x08  # A proxy ARP entry
+    NTF_EXT_LEARNED = 0x10  # neigh entry installed by an external APP
+    NTF_ROUTER      = 0x80  # An IPv6 router
+
+    flag_to_string = {
+        NTF_USE          : 'NTF_USE',
+        NTF_SELF         : 'NTF_SELF',
+        NTF_MASTER       : 'NTF_MASTER',
+        NTF_PROXY        : 'NTF_PROXY',
+        NTF_EXT_LEARNED  : 'NTF_EXT_LEARNED',
+        NTF_ROUTER       : 'NTF_ROUTER'
+    }
+
+    # Neighbor states
+    # /usr/include/linux/neighbour.h
+    NUD_NONE       = 0x00
+    NUD_INCOMPLETE = 0x01  # Still attempting to resolve
+    NUD_REACHABLE  = 0x02  # A confirmed working cache entry
+    NUD_STALE      = 0x04  # an expired cache entry
+    NUD_DELAY      = 0x08  # Neighbor no longer reachable.  Traffic sent, waiting for confirmatio.
+    NUD_PROBE      = 0x10  # A cache entry that is currently being re-solicited
+    NUD_FAILED     = 0x20  # An invalid cache entry
+    NUD_NOARP      = 0x40  # A device which does not do neighbor discovery(ARP)
+    NUD_PERMANENT  = 0x80  # A static entry
+
+    state_to_string = {
+        NUD_NONE       : 'NUD_NONE',
+        NUD_INCOMPLETE : 'NUD_INCOMPLETE',
+        NUD_REACHABLE  : 'NUD_REACHABLE',
+        NUD_STALE      : 'NUD_STALE',
+        NUD_DELAY      : 'NUD_DELAY',
+        NUD_PROBE      : 'NUD_PROBE',
+        NUD_FAILED     : 'NUD_FAILED',
+        NUD_NOARP      : 'NUD_NOARP',
+        NUD_PERMANENT  : 'NUD_PERMANENT'
+    }
+
+    def __init__(self, msgtype, debug=False, logger=None, use_color=True):
+        NetlinkPacket.__init__(self, msgtype, debug, logger, use_color)
+        self.PACK = 'BxxxiHBB'
+        self.LEN = calcsize(self.PACK)
+
+    def get_state_string(self, index):
+        return self.get_string(self.state_to_string, index)
+
+    def get_states_string(self, states):
+        for_string = []
+
+        if states & Neighbor.NUD_INCOMPLETE:
+            for_string.append('NUD_INCOMPLETE')
+
+        if states & Neighbor.NUD_REACHABLE:
+            for_string.append('NUD_REACHABLE')
+
+        if states & Neighbor.NUD_STALE:
+            for_string.append('NUD_STALE')
+
+        if states & Neighbor.NUD_DELAY:
+            for_string.append('NUD_DELAY')
+
+        if states & Neighbor.NUD_PROBE:
+            for_string.append('NUD_PROBE')
+
+        if states & Neighbor.NUD_FAILED:
+            for_string.append('NUD_FAILED')
+
+        if states & Neighbor.NUD_NOARP:
+            for_string.append('NUD_NOARP')
+
+        if states & Neighbor.NUD_PERMANENT:
+            for_string.append('NUD_PERMANENT')
+
+        return ', '.join(for_string)
+
+    def get_flags_string(self, flags):
+        for_string = []
+
+        if flags & Neighbor.NTF_USE:
+            for_string.append('NTF_USE')
+
+        if flags & Neighbor.NTF_SELF:
+            for_string.append('NTF_SELF')
+
+        if flags & Neighbor.NTF_MASTER:
+            for_string.append('NTF_MASTER')
+
+        if flags & Neighbor.NTF_PROXY:
+            for_string.append('NTF_PROXY')
+
+        if flags & Neighbor.NTF_ROUTER:
+            for_string.append('NTF_ROUTER')
+
+        return ', '.join(for_string)
+
+    def decode_service_header(self):
+
+        # Nothing to do if the message did not contain a service header
+        if self.length == self.header_LEN:
+            return
+
+        (self.family,
+         self.ifindex,
+         self.state, self.flags, self.neighbor_type) = \
+            unpack(self.PACK, self.msg_data[:self.LEN])
+
+        if self.debug:
+            color = yellow if self.use_color else None
+            color_start = "\033[%dm" % color if color else ""
+            color_end = "\033[0m" if color else ""
+            self.dump_buffer.append("  %sService Header%s" % (color_start, color_end))
+
+            for x in range(0, self.LEN/4):
+                if self.line_number == 5:
+                    extra = "Family %s (%d)" % (zfilled_hex(self.family, 2), self.family)
+                elif self.line_number == 6:
+                    extra = "Interface Index %s (%d)" % (zfilled_hex(self.ifindex, 8), self.ifindex)
+                elif self.line_number == 7:
+                    extra = "State %s (%d) %s, Flags %s (%s) %s, Type %s (%d)" % \
+                        (zfilled_hex(self.state, 4), self.state, self.get_states_string(self.state),
+                         zfilled_hex(self.flags, 2), self.flags, self.get_flags_string(self.flags),
+                         zfilled_hex(self.neighbor_type, 4), self.neighbor_type)
+                else:
+                    extra = "Unexpected line number %d" % self.line_number
+
+                start = x * 4
+                end = start + 4
+                self.dump_buffer.append(data_to_color_text(self.line_number, color, self.msg_data[start:end], extra))
+                self.line_number += 1
+
+
+class Route(NetlinkPacket):
+    """
+    Service Header
+
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |   Family    |  Dest length  |   Src length  |     TOS       |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |  Table ID   |   Protocol    |     Scope     |     Type      |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                          Flags                              |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    """
+
+    # Route attributes
+    # /usr/include/linux/rtnetlink.h
+    RTA_UNSPEC    = 0x00  # Ignored.
+    RTA_DST       = 0x01  # Protocol address for route destination address.
+    RTA_SRC       = 0x02  # Protocol address for route source address.
+    RTA_IIF       = 0x03  # Input interface index.
+    RTA_OIF       = 0x04  # Output interface index.
+    RTA_GATEWAY   = 0x05  # Protocol address for the gateway of the route
+    RTA_PRIORITY  = 0x06  # Priority of broker.
+    RTA_PREFSRC   = 0x07  # Preferred source address in cases where more than one source address could be used.
+    RTA_METRICS   = 0x08  # Route metrics attributed to route and associated protocols(e.g., RTT, initial TCP window, etc.).
+    RTA_MULTIPATH = 0x09  # Multipath route next hop's attributes.
+    RTA_PROTOINFO = 0x0A  # Firewall based policy routing attribute.
+    RTA_FLOW      = 0x0B  # Route realm.
+    RTA_CACHEINFO = 0x0C  # Cached route information.
+    RTA_SESSION   = 0x0D
+    RTA_MP_ALGO   = 0x0E
+    RTA_TABLE     = 0x0F
+    RTA_MARK      = 0x10
+    RTA_MFC_STATS = 0x11
+    RTA_VIA       = 0x12
+    RTA_NEWDST    = 0x13
+    RTA_PREF      = 0x14
+    RTA_ENCAP_TYPE= 0x15
+    RTA_ENCAP     = 0x16
+
+    attribute_to_class = {
+        RTA_UNSPEC    : ('RTA_UNSPEC', AttributeGeneric),
+        RTA_DST       : ('RTA_DST', AttributeIPAddress),
+        RTA_SRC       : ('RTA_SRC', AttributeIPAddress),
+        RTA_IIF       : ('RTA_IIF', AttributeFourByteValue),
+        RTA_OIF       : ('RTA_OIF', AttributeFourByteValue),
+        RTA_GATEWAY   : ('RTA_GATEWAY', AttributeIPAddress),
+        RTA_PRIORITY  : ('RTA_PRIORITY', AttributeFourByteValue),
+        RTA_PREFSRC   : ('RTA_PREFSRC', AttributeIPAddress),
+        RTA_METRICS   : ('RTA_METRICS', AttributeGeneric),
+        RTA_MULTIPATH : ('RTA_MULTIPATH', AttributeRTA_MULTIPATH),
+        RTA_PROTOINFO : ('RTA_PROTOINFO', AttributeGeneric),
+        RTA_FLOW      : ('RTA_FLOW', AttributeGeneric),
+        RTA_CACHEINFO : ('RTA_CACHEINFO', AttributeGeneric),
+        RTA_SESSION   : ('RTA_SESSION', AttributeGeneric),
+        RTA_MP_ALGO   : ('RTA_MP_ALGO', AttributeGeneric),
+        RTA_TABLE     : ('RTA_TABLE', AttributeFourByteValue),
+        RTA_MARK      : ('RTA_MARK', AttributeGeneric),
+        RTA_MFC_STATS : ('RTA_MFC_STATS', AttributeGeneric),
+        RTA_VIA       : ('RTA_VIA', AttributeGeneric),
+        RTA_NEWDST    : ('RTA_NEWDST', AttributeGeneric),
+        RTA_PREF      : ('RTA_PREF', AttributeGeneric),
+        RTA_ENCAP_TYPE: ('RTA_ENCAP_TYPE', AttributeGeneric),
+        RTA_ENCAP     : ('RTA_ENCAP', AttributeGeneric)
+    }
+
+    # Route tables
+    # /usr/include/linux/rtnetlink.h
+    RT_TABLE_UNSPEC  = 0x00  # An unspecified routing table
+    RT_TABLE_COMPAT  = 0xFC
+    RT_TABLE_DEFAULT = 0xFD  # The default table
+    RT_TABLE_MAIN    = 0xFE  # The main table
+    RT_TABLE_LOCAL   = 0xFF  # The local table
+
+    table_to_string = {
+        RT_TABLE_UNSPEC  : 'RT_TABLE_UNSPEC',
+        RT_TABLE_COMPAT  : 'RT_TABLE_COMPAT',
+        RT_TABLE_DEFAULT : 'RT_TABLE_DEFAULT',
+        RT_TABLE_MAIN    : 'RT_TABLE_MAIN',
+        RT_TABLE_LOCAL   : 'RT_TABLE_LOCAL'
+    }
+
+    # Route scope
+    # /usr/include/linux/rtnetlink.h
+    RT_SCOPE_UNIVERSE = 0x00  # Global route
+    RT_SCOPE_SITE     = 0xC8  # Interior route in the local autonomous system
+    RT_SCOPE_LINK     = 0xFD  # Route on this link
+    RT_SCOPE_HOST     = 0xFE  # Route on the local host
+    RT_SCOPE_NOWHERE  = 0xFF  # Destination does not exist
+
+    scope_to_string = {
+        RT_SCOPE_UNIVERSE : 'RT_SCOPE_UNIVERSE',
+        RT_SCOPE_SITE     : 'RT_SCOPE_SITE',
+        RT_SCOPE_LINK     : 'RT_SCOPE_LINK',
+        RT_SCOPE_HOST     : 'RT_SCOPE_HOST',
+        RT_SCOPE_NOWHERE  : 'RT_SCOPE_NOWHERE'
+    }
+
+    # Route scope to string
+    # iproute2/lib/rt_names.c
+    rtnl_rtscope_tab = {
+        RT_SCOPE_UNIVERSE: 'global',
+        RT_SCOPE_NOWHERE: 'nowhere',
+        RT_SCOPE_HOST: 'host',
+        RT_SCOPE_LINK: 'link',
+        RT_SCOPE_SITE: 'site'
+    }
+
+    # Routing stack
+    # /usr/include/linux/rtnetlink.h
+    RT_PROT_UNSPEC   = 0x00  # Identifies what/who added the route
+    RT_PROT_REDIRECT = 0x01  # By an ICMP redirect
+    RT_PROT_KERNEL   = 0x02  # By the kernel
+    RT_PROT_BOOT     = 0x03  # During bootup
+    RT_PROT_STATIC   = 0x04  # By the administrator
+    RT_PROT_GATED    = 0x08  # GateD
+    RT_PROT_RA       = 0x09  # RDISC/ND router advertissements
+    RT_PROT_MRT      = 0x0A  # Merit MRT
+    RT_PROT_ZEBRA    = 0x0B  # ZEBRA
+    RT_PROT_BIRD     = 0x0C  # BIRD
+    RT_PROT_DNROUTED = 0x0D  # DECnet routing daemon
+    RT_PROT_XORP     = 0x0E  # XORP
+    RT_PROT_NTK      = 0x0F  # Netsukuku
+    RT_PROT_DHCP     = 0x10  # DHCP client
+    RT_PROT_EXABGP   = 0x11  # Exa Networks ExaBGP
+
+    prot_to_string = {
+        RT_PROT_UNSPEC   : 'RT_PROT_UNSPEC',
+        RT_PROT_REDIRECT : 'RT_PROT_REDIRECT',
+        RT_PROT_KERNEL   : 'RT_PROT_KERNEL',
+        RT_PROT_BOOT     : 'RT_PROT_BOOT',
+        RT_PROT_STATIC   : 'RT_PROT_STATIC',
+        RT_PROT_GATED    : 'RT_PROT_GATED',
+        RT_PROT_RA       : 'RT_PROT_RA',
+        RT_PROT_MRT      : 'RT_PROT_MRT',
+        RT_PROT_ZEBRA    : 'RT_PROT_ZEBRA',
+        RT_PROT_BIRD     : 'RT_PROT_BIRD',
+        RT_PROT_DNROUTED : 'RT_PROT_DNROUTED',
+        RT_PROT_XORP     : 'RT_PROT_XORP',
+        RT_PROT_NTK      : 'RT_PROT_NTK',
+        RT_PROT_DHCP     : 'RT_PROT_DHCP',
+        RT_PROT_EXABGP   : 'RT_PROT_EXABGP'
+    }
+
+    # Route types
+    # /usr/include/linux/rtnetlink.h
+    RTN_UNSPEC      = 0x00  # Unknown broker.
+    RTN_UNICAST     = 0x01  # A gateway or direct broker.
+    RTN_LOCAL       = 0x02  # A local interface broker.
+    RTN_BROADCAST   = 0x03  # A local broadcast route(sent as a broadcast).
+    RTN_ANYCAST     = 0x04  # An anycast broker.
+    RTN_MULTICAST   = 0x05  # A multicast broker.
+    RTN_BLACKHOLE   = 0x06  # A silent packet dropping broker.
+    RTN_UNREACHABLE = 0x07  # An unreachable destination.  Packets dropped and
+                            # host unreachable ICMPs are sent to the originator.
+    RTN_PROHIBIT    = 0x08  # A packet rejection broker.  Packets are dropped and
+                            # communication prohibited ICMPs are sent to the originator.
+    RTN_THROW       = 0x09  # When used with policy routing, continue routing lookup
+                            # in another table.  Under normal routing, packets are
+                            # dropped and net unreachable ICMPs are sent to the originator.
+    RTN_NAT         = 0x0A  # A network address translation rule.
+    RTN_XRESOLVE    = 0x0B  # Refer to an external resolver(not implemented).
+
+    rt_type_to_string = {
+        RTN_UNSPEC      : 'RTN_UNSPEC',
+        RTN_UNICAST     : 'RTN_UNICAST',
+        RTN_LOCAL       : 'RTN_LOCAL',
+        RTN_BROADCAST   : 'RTN_BROADCAST',
+        RTN_ANYCAST     : 'RTN_ANYCAST',
+        RTN_MULTICAST   : 'RTN_MULTICAST',
+        RTN_BLACKHOLE   : 'RTN_BLACKHOLE',
+        RTN_UNREACHABLE : 'RTN_UNREACHABLE',
+        RTN_PROHIBIT    : 'RTN_PROHIBIT',
+        RTN_THROW       : 'RTN_THROW',
+        RTN_NAT         : 'RTN_NAT',
+        RTN_XRESOLVE    : 'RTN_XRESOLVE'
+    }
+
+    # Route flags
+    # /usr/include/linux/rtnetlink.h
+    RTM_F_NOTIFY   = 0x100  # If the route changes, notify the user
+    RTM_F_CLONED   = 0x200  # Route is cloned from another route
+    RTM_F_EQUALIZE = 0x400  # Allow randomization of next hop path in multi-path routing(currently not implemented)
+    RTM_F_PREFIX   = 0x800  # Prefix Address
+
+    flag_to_string = {
+        RTM_F_NOTIFY   : 'RTM_F_NOTIFY',
+        RTM_F_CLONED   : 'RTM_F_CLONED',
+        RTM_F_EQUALIZE : 'RTM_F_EQUALIZE',
+        RTM_F_PREFIX   : 'RTM_F_PREFIX'
+    }
+
+    def __init__(self, msgtype, debug=False, logger=None, use_color=True):
+        NetlinkPacket.__init__(self, msgtype, debug, logger, use_color)
+        self.PACK = '=8BI'  # or is it 8Bi ?
+        self.LEN = calcsize(self.PACK)
+
+    def get_prefix_string(self):
+        dst = self.get_attribute_value(self.RTA_DST)
+
+        if dst:
+            return "%s/%d" % (dst, self.src_len)
+        else:
+            if self.family == AF_INET:
+                return "0.0.0.0/0"
+            elif self.family == AF_INET6:
+                return "::/0"
+
+    def get_protocol_string(self, index=None):
+        if index is None:
+            index = self.protocol
+        return self.get_string(self.prot_to_string, index)
+
+    def get_rt_type_string(self, index=None):
+        if index is None:
+            index = self.route_type
+        return self.get_string(self.rt_type_to_string, index)
+
+    def get_scope_string(self, index=None):
+        if index is None:
+            index = self.scope
+        return self.get_string(self.scope_to_string, index)
+
+    def get_table_id_string(self, index=None):
+        if index is None:
+            index = self.table_id
+        return self.get_string(self.table_to_string, index)
+
+    def _get_ifname_from_index(self, ifindex, ifname_by_index):
+        if ifindex:
+            ifname = ifname_by_index.get(ifindex)
+
+            if ifname is None:
+                ifname = str(ifindex)
+        else:
+            ifname = None
+
+        return ifname
+
+    def get_nexthops(self, ifname_by_index={}):
+        nexthop = self.get_attribute_value(self.RTA_GATEWAY)
+        multipath = self.get_attribute_value(self.RTA_MULTIPATH)
+        nexthops = []
+
+        if nexthop:
+            rta_oif = self.get_attribute_value(self.RTA_OIF)
+            ifname = self._get_ifname_from_index(rta_oif, ifname_by_index)
+            nexthops.append((nexthop, ifname))
+
+        elif multipath:
+            for (nexthop, rtnh_ifindex, rtnh_flags, rtnh_hops) in multipath:
+                ifname = self._get_ifname_from_index(rtnh_ifindex, ifname_by_index)
+                nexthops.append((nexthop, ifname))
+
+        return nexthops
+
+    def get_nexthops_string(self, ifname_by_index={}):
+        output = []
+
+        for (nexthop, ifname) in self.get_nexthops(ifname_by_index):
+            output.append(" via %s on %s" % (nexthop, ifname))
+
+        return ",".join(output)
+
+    def decode_service_header(self):
+
+        # Nothing to do if the message did not contain a service header
+        if self.length == self.header_LEN:
+            return
+
+        (self.family, self.src_len, self.dst_len, self.tos,
+         self.table_id, self.protocol, self.scope, self.route_type,
+         self.flags) = \
+            unpack(self.PACK, self.msg_data[:self.LEN])
+
+        if self.debug:
+            color = yellow if self.use_color else None
+            color_start = "\033[%dm" % color if color else ""
+            color_end = "\033[0m" if color else ""
+            self.dump_buffer.append("  %sService Header%s" % (color_start, color_end))
+
+            for x in range(0, self.LEN/4):
+                if self.line_number == 5:
+                    extra = "Family %s (%d), Source Length %s (%d), Destination Length %s (%d), TOS %s (%d)" % \
+                            (zfilled_hex(self.family, 2), self.family,
+                             zfilled_hex(self.src_len, 2), self.src_len,
+                             zfilled_hex(self.dst_len, 2), self.dst_len,
+                             zfilled_hex(self.tos, 2), self.tos)
+                elif self.line_number == 6:
+                    extra = "Table ID %s (%d - %s), Protocol %s (%d - %s), Scope %s (%d - %s), Type %s (%d - %s)" % \
+                            (zfilled_hex(self.table_id, 2), self.table_id, self.get_table_id_string(),
+                             zfilled_hex(self.protocol, 2), self.protocol, self.get_protocol_string(),
+                             zfilled_hex(self.scope, 2), self.scope, self.get_scope_string(),
+                             zfilled_hex(self.route_type, 2), self.route_type, self.get_rt_type_string())
+                elif self.line_number == 7:
+                    extra = "Flags %s" % zfilled_hex(self.flags, 8)
+                else:
+                    extra = "Unexpected line number %d" % self.line_number
+
+                start = x * 4
+                end = start + 4
+                self.dump_buffer.append(data_to_color_text(self.line_number, color, self.msg_data[start:end], extra))
+                self.line_number += 1
+
+class Done(NetlinkPacket):
+    """
+    NLMSG_DONE
+
+    Service Header
+    0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    |                             TBD                             |
+    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    """
+
+    def __init__(self, msgtype, debug=False, logger=None, use_color=True):
+        NetlinkPacket.__init__(self, msgtype, debug, logger, use_color)
+        self.PACK = 'i'
+        self.LEN = calcsize(self.PACK)
+
+    def decode_service_header(self):
+        foo = unpack(self.PACK, self.msg_data[:self.LEN])
+
+        if self.debug:
+            color = yellow if self.use_color else None
+            color_start = "\033[%dm" % color if color else ""
+            color_end = "\033[0m" if color else ""
+            self.dump_buffer.append("  %sService Header%s" % (color_start, color_end))
+
+            for x in range(0, self.LEN/4):
+                extra = ''
+                start = x * 4
+                end = start + 4
+                self.dump_buffer.append(data_to_color_text(self.line_number, color, self.msg_data[start:end], extra))
+                self.line_number += 1
diff --git a/ifupdown2/sbin/ifaddon b/ifupdown2/sbin/ifaddon
new file mode 100755 (executable)
index 0000000..4a2f748
--- /dev/null
@@ -0,0 +1,355 @@
+#!/usr/bin/python
+#
+# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+import sys
+import os
+import re
+import argparse
+from collections import OrderedDict
+
+lockfile="/run/network/.lock"
+modules_configfile='/var/lib/ifupdownaddons/addons.conf'
+modules_dir='/usr/share/ifupdownaddons'
+
+addon_config = OrderedDict([('pre-up', []),
+                            ('up', []),
+                            ('post-up', []),
+                            ('pre-down', []),
+                            ('down', []),
+                            ('post-down', [])])
+
+def read_modules_config():
+    with open(modules_configfile, 'r') as f:
+        lines = f.readlines()
+        for l in lines:
+            litems = l.rstrip(' \n').split(',')
+            operation = litems[0]
+            mname = litems[1]
+            addon_config[operation].append(mname)
+
+def man_rst_header():
+    print '=========================='
+    print 'ifupdown-addons-interfaces'
+    print '=========================='
+
+    print '---------------------------------------------------------'
+    print 'ifupdown2 addon modules interface configuration'
+    print '---------------------------------------------------------'
+
+    print ':Author: roopa@cumulusnetworks.com'
+    print ':Date:   2013-09-25'
+    print ':Copyright: Copyright 2013 Cumulus Networks, Inc.  All rights reserved.'
+    print ':Version: 0.1'
+    print ':Manual section: 5'
+    print '\n'
+
+def man_rst_body():
+
+    print 'DESCRIPTION'
+    print '==========='
+
+    print ('''    ifupdown2 addon modules add incremental functionality to
+    core ifupdown2 tool.
+           
+    All installed addon modules are executed on every interface
+    listed in the interfaces file. Addon modules are installed under
+    /usr/share/ifupdownaddons. To see the list of active addon
+    modules, see ifaddon(8).
+
+    Addon modules add new attributes to the interfaces(5) file.
+    Below is a list of attribute options provided by each module.
+    These can be listed under each iface section in the interfaces(5)
+    file.  ''')
+
+    print '\n'
+
+def get_addon_modinfo(modules_dir):
+    """ load python modules from modules_dir
+
+    Default modules_dir is /usr/share/ifupdownmodules
+
+    """
+    if not modules_dir in sys.path:
+        sys.path.append(modules_dir)
+    read_modules_config()
+    modinfo = {}
+    try:
+        for op, mlist in addon_config.items():
+            for mname in mlist:
+                if mname in modinfo.keys(): continue
+                mpath = modules_dir + '/' + mname + '.py'
+                if os.path.exists(mpath):
+                    try:
+                        m = __import__(mname)
+                        mclass = getattr(m, mname)
+                    except:
+                        pass
+                        continue
+                    minstance = mclass()
+                    if hasattr(minstance, 'get_modinfo'):
+                       modinfo[mname] = minstance.get_modinfo()
+    except: 
+        raise
+
+    return modinfo
+
+def print_long_string(indent, strarg):
+    slen = 70 - len(indent)
+    tmphelpstr = strarg
+    l = len(strarg)
+    while l > 0:
+        rem = slen if l >= slen else l
+        print('%s%s' %(indent, tmphelpstr[:rem]))
+        tmphelpstr = tmphelpstr[rem:].strip()
+        l -= rem
+
+def man_rst_examples():
+    print 'EXAMPLES'
+    print '========'
+    print '''    Listed below are addon modules and their supported attributes.
+    The attributes if applicable go under the iface section in the
+    interfaces(5) file.\n'''
+
+    indent = '    '
+    modinfo = get_addon_modinfo(modules_dir)
+    for m, mdict in modinfo.items():
+        aindent = indent + '  '
+        aindentplus = aindent + '  '
+        if not mdict:
+            continue
+        print_long_string(indent, '**%s**: %s' %(m, mdict.get('mhelp', '')))
+        attrdict = mdict.get('attrs')
+        if not attrdict:
+            continue
+        print '\n'
+        try:
+            for attrname, attrvaldict in attrdict.items():
+                if attrvaldict.get('compat', False):
+                    continue
+                print('%s**%s**\n' %(aindent, attrname))
+                print_long_string(aindentplus, '**help**: %s'
+                        %(attrvaldict.get('help', '')))
+                print '\n'
+                print('%s**required**: %s\n' %(aindentplus,
+                            attrvaldict.get('required', False)))
+                default = attrvaldict.get('default')
+                if default:
+                    print('%s**default**: %s\n' %(aindentplus, default))
+                validrange = attrvaldict.get('validrange')
+                if validrange:
+                    print('%svalidrange: %s\n'
+                          %(aindentplus, '-'.join(validrange)))
+                validvals = attrvaldict.get('validvals')
+                if validvals:
+                    print('%s**validvals**: %s\n'
+                              %(aindentplus, ','.join(validvals)))
+                examples = attrvaldict.get('example')
+                if not examples:
+                    continue
+                print '%s**example**:' %(aindentplus)
+                for e in examples:
+                    print '%s%s\n' %(aindentplus + indent, e)
+                print ''
+        except Exception, e:
+            print "Roopa: m = %s, str(e) = %s\n"  %(m, str(e))
+            pass
+        print ''
+
+def man_rst_see_also():
+    print 'SEE ALSO'
+    print '========'
+    print '''    interfaces(5),
+    ifup(8),
+    ip(8),
+    mstpctl(8),
+    brctl(8),
+    ethtool(8),
+    clagctl(8)'''
+
+def show_man_rst():
+    man_rst_header()
+    man_rst_body()
+    man_rst_examples()
+    man_rst_see_also()
+
+def show():
+    for operation, mlist in addon_config.items():
+        postion = 1
+        for m in mlist:
+            print '%d. %s' %(postion, m)
+            postion += 1
+
+def write_modules_config():
+    with open(modules_configfile, 'w') as f:
+        for op, mlist in addon_config.items():
+            [f.write('%s,%s\n' %(op, m)) for m in mlist]
+
+def process_add_cmd(args):
+    op = args.operation
+    module = args.module
+    position = args.position
+    if not op:
+        for k, vlist in addon_config.items():
+            if module not in vlist:
+                addon_config[k].append(module)
+            else:
+                print '%s: module %s already present' %(k, module)
+        return
+    if module in addon_config.get(op):
+        print 'module already present'
+        return
+    if position:
+       try:
+            addon_config[op].insert(position, module)
+       except Exception, e:
+           print ('error inserting module %s at postion %s (%s)'
+                    %(module, position, str(e)))
+           raise
+    else:
+       addon_config[op].append(module)
+
+
+def process_del_cmd(args):
+    op = args.operation
+    module = args.module
+
+    if op:
+        del addon_config[op]
+    else:
+       try:
+            [addon_config[op].remove(module) for op in addon_config.keys()]
+       except ValueError:
+           pass
+
+def process_move_cmd(args):
+    op = args.operation
+    module = args.module
+    pos = 0
+
+    try:
+        pos = int(args.position)
+        if pos < 0 or pos > len(addon_config.get(op)):
+            raise Exception('invalid value for position')
+    except:
+        raise
+
+    if addon_config[op].index(module) == pos:
+        print '%s module %s already at location %d' %(op, module, pos)
+        return
+
+    addon_config[op].remove(module)
+    addon_config[op].insert(pos, module)
+
+def print_mlist(mlist, indent):
+    for idx, val in enumerate(mlist):
+        print '%s%d. %s' %(indent, idx, val)
+
+def process_show_cmd(args):
+    indent = '   '
+    op = args.operation
+
+    if args.man:
+        show_man_rst()
+        return
+
+    if op:
+        mlist = addon_config[op]
+        print '%s:' %op
+        print_mlist(mlist, indent)
+    else:
+        for op, mlist in addon_config.items():
+            print '%s:' %op
+            print_mlist(mlist, indent)
+            print ''
+
+cmdhandlers = {'add' : process_add_cmd,
+               'del' : process_del_cmd,
+               'move' : process_move_cmd,
+               'show' : process_show_cmd}
+
+def update_subparser_add(subparser):
+    subparser.add_argument('module', metavar='MODULE', help='module name')
+    subparser.add_argument('operation', metavar='OPERATION',
+                           choices=['pre-up', 'up', 'post-up',
+                                    'pre-down', 'down', 'post-down'],
+                                    help='operations', nargs='?')
+    subparser.add_argument('position', metavar='POSITION', nargs='?',
+                           help='position')
+    subparser.set_defaults(func=process_add_cmd)
+
+def update_subparser_del(subparser):
+    subparser.add_argument('module', metavar='MODULE', help='module name')
+    subparser.add_argument('operation', metavar='OPERATION',
+                           choices=['pre-up', 'up', 'post-up',
+                                    'pre-down', 'down', 'post-down'],
+                                    help='operations', nargs='?')
+    subparser.add_argument('position', metavar='POSITION', nargs='?',
+                           help='position')
+    subparser.set_defaults(func=process_del_cmd)
+
+def update_subparser_move(subparser):
+    subparser.add_argument('module', metavar='MODULE', help='module name')
+    subparser.add_argument('operation', metavar='OPERATION',
+                           choices=['pre-up', 'up', 'post-up',
+                                    'pre-down', 'down', 'post-down'],
+                                    help='operations')
+    subparser.add_argument('position', metavar='POSITION',
+                           help='position')
+    subparser.set_defaults(func=process_move_cmd)
+
+
+def update_subparser_show(subparser):
+    subparser.add_argument('--man', action='store_true',
+                           help=argparse.SUPPRESS)
+    subparser.add_argument('operation', metavar='OPERATION',
+                           choices=addon_config.keys(),
+                           help='operations %s' %str(addon_config.keys()),
+                           nargs='?')
+    subparser.set_defaults(func=process_show_cmd)
+
+def update_argparser(argparser):
+    subparsers = argparser.add_subparsers(help='sub-command help')
+
+    parser_add = subparsers.add_parser('add')
+    update_subparser_add(parser_add)
+
+    parser_del = subparsers.add_parser('del', help='del help')
+    update_subparser_del(parser_del)
+
+    parser_move = subparsers.add_parser('move', help='move help')
+    update_subparser_move(parser_move)
+
+    parser_show = subparsers.add_parser('show', help='show help')
+    update_subparser_show(parser_show)
+
+def parse_args(argsv):
+    descr = 'ifupdown addon modules management command.\n \
+            This command helps add/del/display/reorder modules \n \
+            in all ifupdown module categories'
+
+    argparser = argparse.ArgumentParser(description=descr)
+    update_argparser(argparser)
+
+    args = argparser.parse_args(argsv)
+    return args
+
+def main(argv):
+    """ main function """
+    try:
+        # Command line arg parser
+        args = parse_args(argv[1:])
+        read_modules_config()
+        args.func(args)
+        write_modules_config()
+    except Exception, e:
+        print 'error processing command (%s)' %str(e)
+
+if __name__ == "__main__":
+    if not os.geteuid() == 0:
+        print 'Error: Must be root to run this command'
+        exit(1)
+
+    main(sys.argv)
diff --git a/ifupdown2/sbin/ifupdown2d b/ifupdown2/sbin/ifupdown2d
new file mode 100755 (executable)
index 0000000..d223b80
--- /dev/null
@@ -0,0 +1,288 @@
+#!/usr/bin/python
+#
+# Copyright 2017 Cumulus Networks, Inc. All rights reserved.
+# Authors:
+#           Roopa Prabhu, roopa@cumulusnetworks.com
+#           Julien Fortin, julien@cumulusnetworks.com
+#
+# ifupdown2 --
+#    tool to configure network interfaces
+#
+
+import os
+import re
+import json
+import fcntl
+import struct
+import signal
+import socket
+import daemon
+import select
+import datetime
+import threading
+
+try:
+    import ifupdown2.ifupdown.argv
+
+    from ifupdown2.ifupdown.log import log
+    from ifupdown2.ifupdown.main import Ifupdown2
+except ImportError:
+    import ifupdown.argv
+
+    from ifupdown.log import log
+    from ifupdown.main import Ifupdown2
+
+
+class Daemon:
+    shutdown_event = threading.Event()
+
+    def __init__(self):
+        self.uds = None
+        self.context = None
+        self.working_directory = '/var/run/ifupdown2d/'
+        self.server_address = '/var/run/ifupdown2d/uds'
+
+        if not os.path.exists(self.working_directory):
+            log.info('creating %s' % self.working_directory)
+            os.makedirs(self.working_directory, mode=0755)
+
+        if os.path.exists(self.server_address):
+            log.info('removing uds %s' % self.server_address)
+            os.remove(self.server_address)
+
+        self.context = daemon.DaemonContext(
+            working_directory=self.working_directory,
+            signal_map={
+                signal.SIGINT: self.signal_handler,
+                signal.SIGTERM: self.signal_handler,
+                signal.SIGQUIT: self.signal_handler,
+            },
+            umask=0o22
+        )
+
+        try:
+            self.SO_PEERCRED = socket.SO_PEERCRED
+        except AttributeError:
+            # powerpc is the only non-generic we care about. alpha, mips,
+            # sparc, and parisc also have non-generic values.
+            machine = os.uname()[4]
+            if re.search(r'^(ppc|powerpc)', machine):
+                self.SO_PASSCRED = 20
+                self.SO_PEERCRED = 21
+            else:
+                self.SO_PASSCRED = 16
+                self.SO_PEERCRED = 17
+
+        log.info('daemonizing ifupdown2d...')
+        self.context.open()
+
+        log.info('preloading all necessary modules')
+        self.preload_imports()
+
+        try:
+            log.info('opening UNIX socket')
+            self.uds = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+            fcntl.fcntl(self.uds.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
+        except Exception as e:
+            raise Exception('socket: %s' % str(e))
+        try:
+            self.uds.bind(self.server_address)
+        except Exception as e:
+            raise Exception('bind: %s' % str(e))
+        try:
+            self.uds.setsockopt(socket.SOL_SOCKET, self.SO_PASSCRED, 1)
+        except Exception as e:
+            raise Exception('setsockopt: %s' % str(e))
+        try:
+            self.uds.listen(1)
+        except Exception as e:
+            raise Exception('listen: %s' % str(e))
+        os.chmod(self.server_address, 0777)
+
+    def __del__(self):
+        if self.context:
+            self.context.close()
+        if self.uds:
+            self.uds.close()
+
+    @staticmethod
+    def preload_imports():
+        """
+            preloading all the necessary modules
+            at first will increase performances
+        """
+        try:
+            import io
+            import pdb
+            import imp
+            import sets
+            import json
+            import glob
+            import time
+            import copy
+            import errno
+            import pprint
+            import atexit
+            import ipaddr
+            import cPickle
+            import logging
+            import argparse
+            import StringIO
+            import datetime
+            import traceback
+            import itertools
+            import subprocess
+            import argcomplete
+            import collections
+            import ConfigParser
+            import pkg_resources
+
+            import ifupdown2.ifupdown.exceptions
+            import ifupdown2.ifupdown.graph
+            import ifupdown2.ifupdown.iface
+            import ifupdown2.ifupdown.iff
+            import ifupdown2.ifupdown.ifupdownbase
+            import ifupdown2.ifupdown.ifupdownbase
+            import ifupdown2.ifupdown.ifupdownconfig
+            import ifupdown2.ifupdown.ifupdownflags
+            import ifupdown2.ifupdown.ifupdownmain
+            import ifupdown2.ifupdown.netlink
+            import ifupdown2.ifupdown.networkinterfaces
+            import ifupdown2.ifupdown.policymanager
+            import ifupdown2.ifupdown.scheduler
+            import ifupdown2.ifupdown.statemanager
+            import ifupdown2.ifupdown.template
+            import ifupdown2.ifupdown.utils
+
+            import ifupdown2.ifupdownaddons.cache
+            import ifupdown2.ifupdownaddons.dhclient
+            import ifupdown2.ifupdownaddons.mstpctlutil
+            import ifupdown2.ifupdownaddons.LinkUtils
+            import ifupdown2.ifupdownaddons.modulebase
+            import ifupdown2.ifupdownaddons.systemutils
+            import ifupdown2.ifupdownaddons.utilsbase
+        except ImportError, e:
+            raise ImportError('%s - required module not found' % str(e))
+
+    @staticmethod
+    def signal_handler(sig, frame):
+        log.info('received %s' % 'SIGINT' if sig == signal.SIGINT else 'SIGTERM')
+        Daemon.shutdown_event.set()
+
+    @staticmethod
+    def user_waiting_for_reply():
+        return not log.is_syslog()
+
+    def run(self):
+        try:
+            while True:
+                if Daemon.shutdown_event.is_set():
+                    log.info("shutdown signal RXed, breaking out loop")
+                    break
+
+                try:
+                    (client_socket, client_address) = self.uds.accept()
+                except socket.error as e:
+                    log.error(str(e))
+                    break
+
+                pid = os.fork()
+                if pid == 0:
+                    exit(self.ifupdown2(client_socket))
+                else:
+                    log.tx_data(json.dumps({'pid': pid}), socket=client_socket)
+
+                    start = datetime.datetime.now()
+                    status = os.WEXITSTATUS(os.waitpid(pid, 0)[1])
+                    end = datetime.datetime.now()
+
+                    log.tx_data(json.dumps({'status': status}), socket=client_socket)
+                    client_socket.close()
+
+                    log.info('exit status %d - in %ssecs'
+                             % (status, (end - start).total_seconds()))
+
+        except Exception as e:
+            log.error(e)
+        self.uds.close()
+
+    def get_client_uid(self, client_socket):
+        creds = client_socket.getsockopt(socket.SOL_SOCKET, self.SO_PEERCRED, struct.calcsize('3i'))
+        (pid, uid, gid) = struct.unpack('3i', creds)
+        log.debug('client uid %d' % uid)
+        return uid
+
+    @staticmethod
+    def get_client_request(client_socket):
+        """
+            This function handles requests of any length.
+
+                if the received json is longer than 65k it will be truncated
+                several calls to recv will be needed, we store the data until
+                we can decode them with the json library.
+        """
+        data = []
+        while True:
+            log.debug('waiting for request on client socket')
+            ready = select.select([client_socket], [], [])
+
+            if ready and ready[0] and ready[0][0] == client_socket:
+                # data available start reading
+                raw_data = client_socket.recv(65536)
+
+                try:
+                    return json.loads(raw_data)
+                except ValueError:
+                    # the json is incomplete
+                    data.append(raw_data)
+
+                    if len(data) > 1:
+                        try:
+                            return json.loads(''.join(data))
+                        except ValueError:
+                            pass
+
+    def ifupdown2(self, client_socket):
+        try:
+            fcntl.fcntl(client_socket.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
+
+            ifupdown2 = Ifupdown2(daemon=True, uid=self.get_client_uid(client_socket))
+            ifupdown2.set_signal_handlers()
+
+            request = self.get_client_request(client_socket)
+            log.info('request: %s' % request['argv'])
+
+            ifupdown2.parse_argv(request['argv'])
+            # adjust the logger with argv
+            ifupdown2.update_logger(socket=client_socket)
+
+            try:
+                status = ifupdown2.main(request['stdin'])
+            except Exception as e:
+                log.error(str(e))
+                status = 1
+
+        except ifupdown2.ifupdown.argv.ArgvParseError as e:
+            log.update_current_logger(syslog=False, verbose=True, debug=False)
+            log.set_socket(client_socket)
+            e.log_error()
+            status = 1
+        except Exception as e:
+            log.error(e)
+            status = 1
+
+        log.flush()
+        log.set_socket(None)
+        client_socket.close()
+        return status
+
+
+if __name__ == '__main__':
+    try:
+        Daemon().run()
+    except Exception as e:
+        print e
+        log.error(str(e))
+        import traceback
+        log.error(traceback.format_exc())
+        exit(1)
diff --git a/ifupdown2/sbin/start-networking b/ifupdown2/sbin/start-networking
new file mode 100755 (executable)
index 0000000..da6f9d0
--- /dev/null
@@ -0,0 +1,168 @@
+#!/bin/bash
+
+# This replaces the old init.d script, and is run from the networking.service
+# Only has start, stop, reload, because that's all systemd has.
+# restart is implemented in systemd by stop then start.
+
+RUN_DIR="/run/network"
+IFSTATE_LOCKFILE="${RUN_DIR}/ifstatelock"
+
+STATE_DIR="/var/tmp/network"
+IFSTATE_FILE="${STATE_DIR}/ifstatenew"
+
+NAME=networking
+
+[ -x /sbin/ifup ] || exit 0
+[ -x /sbin/ifdown ] || exit 0
+
+CONFIGURE_INTERFACES=yes
+
+EXTRA_ARGS=
+
+[ -f /etc/default/networking ] && . /etc/default/networking
+
+[ "$VERBOSE" = yes ] && EXTRA_ARGS=-v
+[ "$DEBUG" = yes ] && EXTRA_ARGS="$EXTRA_ARGS -d"
+[ "$SYSLOG" = yes ] && EXTRA_ARGS="$EXTRA_ARGS --syslog"
+
+perf_options() {
+    # At bootup lets set perfmode
+    [ -f ${IFSTATE_LOCKFILE} ] && echo -n "" && return
+
+    echo -n "--perfmode"
+}
+
+process_exclusions() {
+    set -- $EXCLUDE_INTERFACES
+    exclusions=""
+    for d
+    do
+       exclusions="-X $d $exclusions"
+    done
+    echo $exclusions
+}
+
+check_network_file_systems() {
+    [ -e /proc/mounts ] || return 0
+
+    if [ -e /etc/iscsi/iscsi.initramfs ]; then
+       echo ${NAME}':' "not deconfiguring network interfaces: iSCSI root is mounted."
+       exit 0
+    fi
+
+    while read DEV MTPT FSTYPE REST; do
+       case $DEV in
+       /dev/nbd*|/dev/nd[a-z]*|/dev/etherd/e*)
+           echo ${NAME}':' "not deconfiguring network interfaces: network devices still mounted."
+           exit 0
+           ;;
+       esac
+       case $FSTYPE in
+       nfs|nfs4|smbfs|ncp|ncpfs|cifs|coda|ocfs2|gfs|pvfs|pvfs2|fuse.httpfs|fuse.curlftpfs)
+           echo ${NAME}':' "not deconfiguring network interfaces: network file systems still mounted."
+           exit 0
+           ;;
+       esac
+    done < /proc/mounts
+}
+
+check_network_swap() {
+    [ -e /proc/swaps ] || return 0
+
+    while read DEV MTPT FSTYPE REST; do
+       case $DEV in
+       /dev/nbd*|/dev/nd[a-z]*|/dev/etherd/e*)
+           echo ${NAME}':' "not deconfiguring network interfaces: network swap still mounted."
+           exit 0
+           ;;
+       esac
+    done < /proc/swaps
+}
+
+ifup_hotplug () {
+    if [ -d /sys/class/net ]
+    then
+           ifaces=$(for iface in $(ifquery --list --allow=hotplug 2>/dev/null)
+                           do
+                                   link=${iface##:*}
+                                   link=${link##.*}
+                                   if [ -e "/sys/class/net/$link" ]
+                                   then
+                                       echo "$iface"
+                                   fi
+                           done)
+           if [ -n "$ifaces" ]
+           then
+               ifup $ifaces "$@" || true
+           fi
+    fi
+}
+
+ifup_mgmt () {
+       ifaces=$(ifquery --list --allow=mgmt 2>/dev/null)
+       if [ -n "$ifaces" ]; then
+               echo "bringing up mgmt class interfaces"
+               ifup --allow=mgmt
+       fi
+}
+
+ifupdown_init() {
+       # remove state file at boot
+       [ ! -e ${IFSTATE_LOCKFILE} ] && rm -f ${IFSTATE_FILE}
+
+       [ ! -e /run/network ] && mkdir -p /run/network &>/dev/null
+       [ ! -e /etc/network/run ] && \
+               ln -sf /run/network /etc/network/run &>/dev/null
+}
+
+case "$1" in
+start)
+       ifupdown_init
+       if [ "$CONFIGURE_INTERFACES" = no ]
+       then
+           echo ${NAME}':' "Not configuring network interfaces, see /etc/default/networking"
+           exit 0
+       fi
+       set -f
+       exclusions=$(process_exclusions)
+       perfoptions=$(perf_options)
+       echo ${NAME}':' "Configuring network interfaces"
+       ifup_mgmt
+       ifup -a $EXTRA_ARGS $exclusions $perfoptions
+       ifup_hotplug $HOTPLUG_ARGS $EXTRA_ARGS $exclusions
+       ;;
+stop)
+       if [ "$SKIP_DOWN_AT_SYSRESET" = "yes" ]; then
+        SYSRESET=0
+        systemctl list-jobs | egrep -q '(shutdown|reboot|halt|poweroff)\.target'
+        [ $? -eq 0 ] && SYSRESET=1
+        if [ $SYSRESET -eq 1 ]; then
+            echo ${NAME}':' "Skipping deconfiguring network interfaces"
+            exit 0
+        fi
+       fi
+       ifupdown_init
+       check_network_file_systems
+       check_network_swap
+       exclusions=$(process_exclusions)
+
+       echo ${NAME}':' "Deconfiguring network interfaces"
+       ifdown -a $EXTRA_ARGS $exclusions
+       ;;
+
+reload)
+
+       ifupdown_init
+       exclusions=$(process_exclusions)
+
+       echo ${NAME}':' "Reloading network interfaces configuration"
+       ifreload -a $EXTRA_ARGS $exclusions
+       ;;
+
+*)
+       echo ${NAME}':' "Usage: $0 {start|stop|reload}"
+       exit 1
+       ;;
+esac
+
+exit 0
diff --git a/ifupdownaddons/__init__.py b/ifupdownaddons/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/ifupdownaddons/bondutil.py b/ifupdownaddons/bondutil.py
deleted file mode 100644 (file)
index 859ff7b..0000000
+++ /dev/null
@@ -1,426 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-
-import os
-import re
-import ifupdown.ifupdownflags as ifupdownflags
-from ifupdown.utils import utils
-from ifupdown.iface import *
-from utilsbase import *
-from iproute2 import *
-from cache import *
-
-class bondutil(utilsBase):
-    """ This class contains methods to interact with linux kernel bond
-    related interfaces """
-
-    _cache_fill_done = False
-
-    def __init__(self, *args, **kargs):
-        utilsBase.__init__(self, *args, **kargs)
-        if ifupdownflags.flags.CACHE and not self._cache_fill_done:
-            self._bond_linkinfo_fill_all()
-            self._cache_fill_done = True
-
-    def _bond_linkinfo_fill_attrs(self, bondname):
-        try:
-            linkCache.links[bondname]['linkinfo'] = {}
-        except:
-            linkCache.links[bondname] = {'linkinfo': {}}
-
-        try:
-            linkCache.set_attr([bondname, 'linkinfo', 'min_links'],
-                               self.read_file_oneline(
-                                   '/sys/class/net/%s/bonding/min_links'
-                                   % bondname))
-        except Exception as e:
-            self.logger.debug(str(e))
-        try:
-            linkCache.set_attr([bondname, 'linkinfo', 'slaves'],
-                self.read_file_oneline('/sys/class/net/%s/bonding/slaves'
-                %bondname).split())
-        except Exception as e:
-            self.logger.debug(str(e))
-        try:
-            linkCache.set_attr([bondname, 'linkinfo', 'mode'],
-                self.read_file_oneline('/sys/class/net/%s/bonding/mode'
-                %bondname).split()[0])
-        except Exception as e:
-            self.logger.debug(str(e))
-        try:
-            linkCache.set_attr([bondname, 'linkinfo', 'xmit_hash_policy'],
-                self.read_file_oneline(
-                    '/sys/class/net/%s/bonding/xmit_hash_policy'
-                    %bondname).split()[0])
-        except Exception as e:
-            self.logger.debug(str(e))
-        try:
-            linkCache.set_attr([bondname, 'linkinfo', 'lacp_rate'],
-                self.read_file_oneline('/sys/class/net/%s/bonding/lacp_rate'
-                                       %bondname).split()[1])
-        except Exception as e:
-            self.logger.debug(str(e))
-        try:
-            linkCache.set_attr([bondname, 'linkinfo', 'ad_actor_sys_prio'],
-                self.read_file_oneline('/sys/class/net/%s/bonding/ad_actor_sys_prio'
-                                       %bondname))
-        except Exception as e:
-            self.logger.debug(str(e))
-        try:
-            linkCache.set_attr([bondname, 'linkinfo', 'ad_actor_system'],
-                self.read_file_oneline('/sys/class/net/%s/bonding/ad_actor_system'
-                                       %bondname))
-        except Exception as e:
-            self.logger.debug(str(e))
-        try:
-            linkCache.set_attr([bondname, 'linkinfo', 'lacp_bypass'],
-                self.read_file_oneline('/sys/class/net/%s/bonding/lacp_bypass'
-                                       %bondname).split()[1])
-        except Exception as e:
-            self.logger.debug(str(e))
-        try:
-            linkCache.set_attr([bondname, 'linkinfo', 'updelay'],
-                self.read_file_oneline('/sys/class/net/%s/bonding/updelay'
-                                       %bondname))
-        except Exception as e:
-            self.logger.debug(str(e))
-        try:
-            linkCache.set_attr([bondname, 'linkinfo', 'downdelay'],
-                self.read_file_oneline('/sys/class/net/%s/bonding/downdelay'
-                                       %bondname))
-        except Exception as e:
-            self.logger.debug(str(e))
-        try:
-            map(lambda x: linkCache.set_attr([bondname, 'linkinfo', x],
-                   self.read_file_oneline('/sys/class/net/%s/bonding/%s'
-                        %(bondname, x))),
-                       ['use_carrier', 'miimon', 'min_links', 'num_unsol_na',
-                        'num_grat_arp'])
-        except Exception as e:
-            self.logger.debug(str(e))
-
-    def _bond_linkinfo_fill_all(self):
-        bondstr = self.read_file_oneline('/sys/class/net/bonding_masters')
-        if not bondstr:
-            return
-        [self._bond_linkinfo_fill_attrs(b) for b in bondstr.split()]
-
-    def _bond_linkinfo_fill(self, bondname, refresh=False):
-        if not refresh:
-            try:
-                linkCache.get_attr([bondname, 'linkinfo', 'slaves'])
-                return
-            except:
-                pass
-        bondstr = self.read_file_oneline('/sys/class/net/bonding_masters')
-        if (not bondstr or bondname not in bondstr.split()):
-            raise Exception('bond %s not found' %bondname)
-        self._bond_linkinfo_fill_attrs(bondname)
-
-    def _cache_get(self, attrlist, refresh=False):
-        try:
-            if ifupdownflags.flags.DRYRUN:
-                return None
-            if ifupdownflags.flags.CACHE:
-                if not bondutil._cache_fill_done:
-                    self._bond_linkinfo_fill_all()
-                    bondutil._cache_fill_done = True
-                    return linkCache.get_attr(attrlist)
-                if not refresh:
-                    return linkCache.get_attr(attrlist)
-            self._bond_linkinfo_fill(attrlist[0], refresh)
-            return linkCache.get_attr(attrlist)
-        except Exception, e:
-            self.logger.debug('_cache_get(%s) : [%s]'
-                    %(str(attrlist), str(e)))
-            pass
-        return None
-
-    def _cache_check(self, attrlist, value, refresh=False):
-        try:
-            attrvalue = self._cache_get(attrlist, refresh)
-            if attrvalue and attrvalue == value:
-                return True
-        except Exception, e:
-            self.logger.debug('_cache_check(%s) : [%s]'
-                    %(str(attrlist), str(e)))
-            pass
-        return False
-
-    def _cache_update(self, attrlist, value):
-        if ifupdownflags.flags.DRYRUN: return
-        try:
-            if attrlist[-1] == 'slaves':
-                linkCache.add_to_attrlist(attrlist, value)
-                return
-            linkCache.add_attr(attrlist, value)
-        except:
-            pass
-
-    def _cache_delete(self, attrlist, value=None):
-        if ifupdownflags.flags.DRYRUN: return
-        try:
-            if attrlist[-1] == 'slaves':
-                linkCache.remove_from_attrlist(attrlist, value)
-                return
-            linkCache.del_attr(attrlist)
-        except:
-            pass
-
-    def _cache_invalidate(self):
-        if ifupdownflags.flags.DRYRUN: return
-        linkCache.invalidate()
-
-    def set_attrs(self, bondname, attrdict, prehook):
-        for attrname, attrval in attrdict.items():
-            if (self._cache_check([bondname, 'linkinfo',
-                attrname], attrval)):
-                continue
-            if (attrname == 'mode' or attrname == 'xmit_hash_policy' or
-                    attrname == 'lacp_rate' or attrname == 'min_links'):
-                if prehook:
-                    prehook(bondname)
-            try:
-                if ((attrname not in ['lacp_rate',
-                                      'lacp_bypass']) or
-                    self._cache_check([bondname, 'linkinfo', 'mode'], '802.3ad',
-                                      True)):
-                    self.write_file('/sys/class/net/%s/bonding/%s'
-                                    %(bondname, attrname), attrval)
-            except Exception, e:
-                if ifupdownflags.flags.FORCE:
-                    self.logger.warn(str(e))
-                    pass
-                else:
-                    raise
-
-    def set_use_carrier(self, bondname, use_carrier):
-        if not use_carrier or (use_carrier != '0' and use_carrier != '1'):
-            return
-        if (self._cache_check([bondname, 'linkinfo', 'use_carrier'],
-                use_carrier)):
-                return
-        self.write_file('/sys/class/net/%s' %bondname +
-                         '/bonding/use_carrier', use_carrier)
-        self._cache_update([bondname, 'linkinfo',
-                            'use_carrier'], use_carrier)
-
-    def get_use_carrier(self, bondname):
-        return self._cache_get([bondname, 'linkinfo', 'use_carrier'])
-
-    def set_xmit_hash_policy(self, bondname, hash_policy, prehook=None):
-        valid_values = ['layer2', 'layer3+4', 'layer2+3']
-        if not hash_policy:
-            return
-        if hash_policy not in valid_values:
-            raise Exception('invalid hash policy value %s' %hash_policy)
-        if (self._cache_check([bondname, 'linkinfo', 'xmit_hash_policy'],
-                hash_policy)):
-            return
-        if prehook:
-            prehook(bondname)
-        self.write_file('/sys/class/net/%s' %bondname +
-                         '/bonding/xmit_hash_policy', hash_policy)
-        self._cache_update([bondname, 'linkinfo', 'xmit_hash_policy'],
-                hash_policy)
-
-    def get_xmit_hash_policy(self, bondname):
-        return self._cache_get([bondname, 'linkinfo', 'xmit_hash_policy'])
-
-    def set_miimon(self, bondname, miimon):
-        if (self._cache_check([bondname, 'linkinfo', 'miimon'],
-                miimon)):
-            return
-        self.write_file('/sys/class/net/%s' %bondname +
-                '/bonding/miimon', miimon)
-        self._cache_update([bondname, 'linkinfo', 'miimon'], miimon)
-
-    def get_miimon(self, bondname):
-        return self._cache_get([bondname, 'linkinfo', 'miimon'])
-
-    def set_mode(self, bondname, mode, prehook=None):
-        valid_modes = ['balance-rr', 'active-backup', 'balance-xor',
-                       'broadcast', '802.3ad', 'balance-tlb', 'balance-alb']
-        if not mode:
-            return
-        if mode not in valid_modes:
-            raise Exception('invalid mode %s' %mode)
-        if (self._cache_check([bondname, 'linkinfo', 'mode'],
-                mode)):
-            return
-        if prehook:
-            prehook(bondname)
-        self.write_file('/sys/class/net/%s' %bondname + '/bonding/mode', mode)
-        self._cache_update([bondname, 'linkinfo', 'mode'], mode)
-
-    def get_mode(self, bondname):
-        return self._cache_get([bondname, 'linkinfo', 'mode'])
-
-    def set_lacp_rate(self, bondname, lacp_rate, prehook=None, posthook=None):
-        if not lacp_rate or (lacp_rate != '0' and lacp_rate != '1'):
-            return
-        if (self._cache_check([bondname, 'linkinfo', 'lacp_rate'],
-                lacp_rate)):
-            return
-        if prehook:
-            prehook(bondname)
-        try:
-            self.write_file('/sys/class/net/%s' %bondname +
-                            '/bonding/lacp_rate', lacp_rate)
-        except:
-            raise
-        finally:
-            if posthook:
-                prehook(bondname)
-            self._cache_update([bondname, 'linkinfo',
-                                'lacp_rate'], lacp_rate)
-
-    def get_lacp_rate(self, bondname):
-        return self._cache_get([bondname, 'linkinfo', 'lacp_rate'])
-
-    def set_lacp_bypass_allow(self, bondname, allow, prehook=None, posthook=None):
-        if (self._cache_check([bondname, 'linkinfo', 'lacp_bypass'], allow)):
-            return
-        if prehook:
-            prehook(bondname)
-        try:
-            self.write_file('/sys/class/net/%s' %bondname +
-                            '/bonding/lacp_bypass', allow)
-        except:
-            raise
-        finally:
-            if posthook:
-                posthook(bondname)
-            self._cache_update([bondname, 'linkinfo',
-                               'lacp_bypass'], allow)
-
-    def get_lacp_bypass_allow(self, bondname):
-        return self._cache_get([bondname, 'linkinfo', 'lacp_bypass'])
-
-    def set_min_links(self, bondname, min_links, prehook=None):
-        if (self._cache_check([bondname, 'linkinfo', 'min_links'],
-                min_links)):
-            return
-        if prehook:
-            prehook(bondname)
-        self.write_file('/sys/class/net/%s/bonding/min_links' %bondname,
-                         min_links)
-        self._cache_update([bondname, 'linkinfo', 'min_links'], min_links)
-
-    def get_min_links(self, bondname):
-        return self._cache_get([bondname, 'linkinfo', 'min_links'])
-
-    def get_ad_actor_system(self, bondname):
-        return self._cache_get([bondname, 'linkinfo', 'ad_actor_system'])
-
-    def get_ad_actor_sys_prio(self, bondname):
-        return self._cache_get([bondname, 'linkinfo', 'ad_actor_sys_prio'])
-
-    def get_num_unsol_na(self, bondname):
-        return self._cache_get([bondname, 'linkinfo', 'num_unsol_na'])
-
-    def get_num_grat_arp(self, bondname):
-        return self._cache_get([bondname, 'linkinfo', 'num_grat_arp'])
-
-    def get_updelay(self, bondname):
-        return self._cache_get([bondname, 'linkinfo', 'updelay'])
-
-    def get_downdelay(self, bondname):
-        return self._cache_get([bondname, 'linkinfo', 'downdelay'])
-
-    def enslave_slave(self, bondname, slave, prehook=None, posthook=None):
-        slaves = self._cache_get([bondname, 'linkinfo', 'slaves'])
-        if slaves and slave in slaves: return
-        if prehook:
-            prehook(slave)
-        self.write_file('/sys/class/net/%s' %bondname +
-                         '/bonding/slaves', '+' + slave)
-        if posthook:
-            posthook(slave)
-        self._cache_update([bondname, 'linkinfo', 'slaves'], slave)
-
-    def remove_slave(self, bondname, slave):
-        slaves = self._cache_get([bondname, 'linkinfo', 'slaves'])
-        if slave not in slaves:
-            return
-        sysfs_bond_path = ('/sys/class/net/%s' %bondname +
-                           '/bonding/slaves')
-        if not os.path.exists(sysfs_bond_path):
-           return
-        self.write_file(sysfs_bond_path, '-' + slave)
-        self._cache_delete([bondname, 'linkinfo', 'slaves'], slave)
-
-    def remove_slaves_all(self, bondname):
-        if not self._cache_get([bondname, 'linkinfo', 'slaves']):
-            return
-        slaves = None
-        sysfs_bond_path = ('/sys/class/net/%s' %bondname +
-                           '/bonding/slaves')
-        ipcmd = iproute2()
-        try:
-            with open(sysfs_bond_path, 'r') as f:
-                slaves = f.readline().strip().split()
-        except IOError, e:
-            raise Exception('error reading slaves of bond %s' %bondname
-                + '(' + str(e) + ')')
-        for slave in slaves:
-            ipcmd.ip_link_down(slave)
-            try:
-                self.remove_slave(bondname, slave)
-            except Exception, e:
-                if not ifupdownflags.flags.FORCE:
-                    raise Exception('error removing slave %s'
-                        %slave + ' from bond %s' %bondname +
-                        '(%s)' %str(e))
-                else:
-                    pass
-        self._cache_del([bondname, 'linkinfo', 'slaves'])
-
-    def load_bonding_module(self):
-        return utils.exec_command('modprobe -q bonding')
-
-    def create_bond(self, bondname):
-        if self.bond_exists(bondname):
-            return
-        sysfs_net = '/sys/class/net/'
-        sysfs_bonding_masters = sysfs_net + 'bonding_masters'
-        if not os.path.exists(sysfs_bonding_masters):
-            self.logger.debug('loading bonding driver')
-            self.load_bonding_module()
-        self.write_file(sysfs_bonding_masters, '+' + bondname)
-        self._cache_update([bondname], {})
-
-    def delete_bond(self, bondname):
-        if not os.path.exists('/sys/class/net/%s' %bondname):
-            return
-        self.write_file('/sys/class/net/bonding_masters', '-' + bondname)
-        self._cache_delete([bondname])
-
-    def unset_master(self, bondname):
-        print 'Do nothing yet'
-        return 0
-
-    def get_slaves(self, bondname):
-        slaves = self._cache_get([bondname, 'linkinfo', 'slaves'])
-        if slaves:
-            return list(slaves)
-        slavefile = '/sys/class/net/%s/bonding/slaves' %bondname
-        if os.path.exists(slavefile):
-            buf = self.read_file_oneline(slavefile)
-            if buf:
-                slaves = buf.split()
-        if not slaves:
-            return slaves
-        self._cache_update([bondname, 'linkinfo', 'slaves'], slaves)
-        return list(slaves)
-
-    def bond_slave_exists(self, bond, slave):
-        slaves = self.get_slaves(bond)
-        if not slaves: return False
-        return slave in slaves
-
-    def bond_exists(self, bondname):
-        return os.path.exists('/sys/class/net/%s/bonding' %bondname)
diff --git a/ifupdownaddons/bridgeutils.py b/ifupdownaddons/bridgeutils.py
deleted file mode 100644 (file)
index 159a87f..0000000
+++ /dev/null
@@ -1,544 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-
-from ifupdown.iface import *
-from utilsbase import *
-import os
-import re
-import logging
-from ifupdown.utils import utils
-import ifupdown.ifupdownflags as ifupdownflags
-from cache import *
-
-class brctl(utilsBase):
-    """ This class contains helper functions to interact with the bridgeutils
-    commands """
-
-    _cache_fill_done = False
-
-    def __init__(self, *args, **kargs):
-        utilsBase.__init__(self, *args, **kargs)
-        if ifupdownflags.flags.CACHE and not brctl._cache_fill_done:
-            if os.path.exists('/sbin/brctl'):
-                self._bridge_fill()
-            brctl._cache_fill_done = True
-        self.supported_command = {'showmcqv4src': True}
-
-
-    def _bridge_get_mcattrs_from_sysfs(self, bridgename):
-        mcattrs = {}
-        mcattrmap = {'mclmc': 'multicast_last_member_count',
-                     'mcrouter': 'multicast_router',
-                     'mcsnoop' : 'multicast_snooping',
-                     'mcsqc' : 'multicast_startup_query_count',
-                     'mcqifaddr' : 'multicast_query_use_ifaddr',
-                     'mcquerier' : 'multicast_querier',
-                     'hashel' : 'hash_elasticity',
-                     'hashmax' : 'hash_max',
-                     'mclmi' : 'multicast_last_member_interval',
-                     'mcmi' : 'multicast_membership_interval',
-                     'mcqpi' : 'multicast_querier_interval',
-                     'mcqi' : 'multicast_query_interval',
-                     'mcqri' : 'multicast_query_response_interval',
-                     'mcsqi' : 'multicast_startup_query_interval'}
-
-        mcattrsdivby100 = ['mclmi', 'mcmi', 'mcqpi', 'mcqi', 'mcqri', 'mcsqi']
-
-        for m, s in mcattrmap.items():
-            n = self.read_file_oneline('/sys/class/net/%s/bridge/%s'
-                                    %(bridgename, s))
-            if m in mcattrsdivby100:
-                try:
-                    v = int(n) / 100
-                    mcattrs[m] = str(v)
-                except Exception, e:
-                    self.logger.warn('error getting mc attr %s (%s)'
-                                     %(m, str(e)))
-                    pass
-            else:
-                mcattrs[m] = n
-        return mcattrs
-
-    def _bridge_attrs_fill(self, bridgename):
-        battrs = {}
-        bports = {}
-
-        brout = utils.exec_command('/sbin/brctl showstp %s' % bridgename)
-        chunks = re.split(r'\n\n', brout, maxsplit=0, flags=re.MULTILINE)
-
-        try:
-            # Get all bridge attributes
-            broutlines = chunks[0].splitlines()
-            #battrs['pathcost'] = broutlines[3].split('path cost')[1].strip()
-
-            try:
-                battrs['maxage'] = broutlines[4].split('bridge max age')[
-                    1].strip().replace('.00', '')
-            except:
-                pass
-
-            try:
-                battrs['hello'] = broutlines[5].split('bridge hello time')[
-                    1].strip().replace('.00', '')
-            except:
-                pass
-
-            try:
-                battrs['fd'] = broutlines[6].split('bridge forward delay')[
-                    1].strip().replace('.00', '')
-            except:
-                pass
-
-            try:
-                battrs['ageing'] = broutlines[7].split('ageing time')[
-                    1].strip().replace('.00', '')
-            except:
-                pass
-
-            try:
-                battrs['mcrouter'] = broutlines[12].split('mc router')[
-                    1].strip().split('\t\t\t')[0]
-            except:
-                pass
-
-            try:
-                battrs['bridgeprio'] = self.read_file_oneline(
-                    '/sys/class/net/%s/bridge/priority' % bridgename)
-            except:
-                pass
-
-            try:
-                battrs.update(self._bridge_get_mcattrs_from_sysfs(bridgename))
-            except:
-                pass
-
-            # XXX: comment this out until mc attributes become available
-            # with brctl again
-            #battrs['hashel'] = broutlines[10].split('hash elasticity')[1].split()[0].strip()
-            #battrs['hashmax'] = broutlines[10].split('hash max')[1].strip()
-            #battrs['mclmc'] = broutlines[11].split('mc last member count')[1].split()[0].strip()
-            #battrs['mciqc'] = broutlines[11].split('mc init query count')[1].strip()
-            #battrs['mcrouter'] = broutlines[12].split('mc router')[1].split()[0].strip()
-            ##battrs['mcsnoop'] = broutlines[12].split('mc snooping')[1].strip()
-            #battrs['mclmt'] = broutlines[13].split('mc last member timer')[1].split()[0].strip()
-        except Exception, e:
-            self.logger.warn('%s: error while processing bridge attributes: %s' % (bridgename, str(e)))
-            pass
-
-        linkCache.update_attrdict([bridgename, 'linkinfo'], battrs)
-        for cidx in range(1, len(chunks)):
-            bpout = chunks[cidx].lstrip('\n')
-            if not bpout or bpout[0] == ' ':
-                continue
-            bplines = bpout.splitlines()
-            pname = bplines[0].split()[0]
-            bportattrs = {}
-            try:
-                bportattrs['pathcost'] = bplines[2].split(
-                                            'path cost')[1].strip()
-                bportattrs['fdelay'] = bplines[4].split(
-                                            'forward delay timer')[1].strip()
-                bportattrs['portmcrouter'] = self.read_file_oneline(
-                         '/sys/class/net/%s/brport/multicast_router' %pname)
-                bportattrs['portmcfl'] = self.read_file_oneline(
-                         '/sys/class/net/%s/brport/multicast_fast_leave' %pname)
-                bportattrs['portprio'] = self.read_file_oneline(
-                         '/sys/class/net/%s/brport/priority' %pname)
-                #bportattrs['mcrouters'] = bplines[6].split('mc router')[1].split()[0].strip()
-                #bportattrs['mc fast leave'] = bplines[6].split('mc fast leave')[1].strip()
-            except Exception, e:
-                self.logger.warn('%s: error while processing bridge attributes: %s' % (bridgename, str(e)))
-            bports[pname] = bportattrs
-            linkCache.update_attrdict([bridgename, 'linkinfo', 'ports'], bports)
-
-    def _bridge_fill(self, bridgename=None, refresh=False):
-        try:
-            # if cache is already filled, return
-            linkCache.get_attr([bridgename, 'linkinfo', 'fd'])
-            return
-        except:
-            pass
-        if not bridgename:
-            brctlout = utils.exec_command('/sbin/brctl show')
-        else:
-            brctlout = utils.exec_command('/sbin/brctl show %s' % bridgename)
-        if not brctlout:
-            return
-
-        for bline in brctlout.splitlines()[1:]:
-            bitems = bline.split()
-            if len(bitems) < 2:
-                continue
-            try:
-                linkCache.update_attrdict([bitems[0], 'linkinfo'],
-                                      {'stp' : bitems[2]})
-            except KeyError:
-                linkCache.update_attrdict([bitems[0]], 
-                                {'linkinfo' : {'stp' : bitems[2]}})
-            self._bridge_attrs_fill(bitems[0])
-
-    def _cache_get(self, attrlist, refresh=False):
-        try:
-            if ifupdownflags.flags.DRYRUN:
-                return None
-            if ifupdownflags.flags.CACHE:
-                if not self._cache_fill_done: 
-                    self._bridge_fill()
-                    self._cache_fill_done = True
-                    return linkCache.get_attr(attrlist)
-                if not refresh:
-                    return linkCache.get_attr(attrlist)
-            self._bridge_fill(attrlist[0], refresh)
-            return linkCache.get_attr(attrlist)
-        except Exception, e:
-            self.logger.debug('_cache_get(%s) : [%s]'
-                    %(str(attrlist), str(e)))
-            pass
-        return None
-
-    def _cache_check(self, attrlist, value, refresh=False):
-        try:
-            attrvalue = self._cache_get(attrlist, refresh)
-            if attrvalue and attrvalue == value:
-                return True
-        except Exception, e:
-            self.logger.debug('_cache_check(%s) : [%s]'
-                    %(str(attrlist), str(e)))
-            pass
-        return False
-
-    def _cache_update(self, attrlist, value):
-        if ifupdownflags.flags.DRYRUN: return
-        try:
-            linkCache.add_attr(attrlist, value)
-        except:
-            pass
-
-    def _cache_delete(self, attrlist):
-        if ifupdownflags.flags.DRYRUN: return
-        try:
-            linkCache.del_attr(attrlist)
-        except:
-            pass
-
-    def _cache_invalidate(self):
-        if ifupdownflags.flags.DRYRUN: return
-        linkCache.invalidate()
-
-    def create_bridge(self, bridgename):
-        if self.bridge_exists(bridgename):
-            return
-        utils.exec_command('/sbin/brctl addbr %s' % bridgename)
-        self._cache_update([bridgename], {})
-
-    def delete_bridge(self, bridgename):
-        if not self.bridge_exists(bridgename):
-            return
-        utils.exec_command('/sbin/brctl delbr %s' % bridgename)
-        self._cache_invalidate()
-
-    def add_bridge_port(self, bridgename, bridgeportname):
-        """ Add port to bridge """
-        ports = self._cache_get([bridgename, 'linkinfo', 'ports'])
-        if ports and ports.get(bridgeportname):
-            return
-        utils.exec_command('/sbin/brctl addif %s %s' %
-                           (bridgename, bridgeportname))
-        self._cache_update([bridgename, 'linkinfo', 'ports',
-                            bridgeportname], {})
-
-    def delete_bridge_port(self, bridgename, bridgeportname):
-        """ Delete port from bridge """
-        ports = self._cache_get([bridgename, 'linkinfo', 'ports'])
-        if not ports or not ports.get(bridgeportname):
-            return
-        utils.exec_command('/sbin/brctl delif %s %s' %
-                           (bridgename, bridgeportname))
-        self._cache_delete([bridgename, 'linkinfo', 'ports',
-                           'bridgeportname'])
-
-    def set_bridgeport_attrs(self, bridgename, bridgeportname, attrdict):
-        portattrs = self._cache_get([bridgename, 'linkinfo',
-                                       'ports', bridgeportname])
-        if portattrs == None: portattrs = {}
-        for k, v in attrdict.iteritems():
-            if ifupdownflags.flags.CACHE:
-                curval = portattrs.get(k)
-                if curval and curval == v:
-                    continue
-            utils.exec_command('/sbin/brctl set%s %s %s %s' %
-                               (k, bridgename, bridgeportname, v))
-
-    def set_bridgeport_attr(self, bridgename, bridgeportname,
-                            attrname, attrval):
-        if self._cache_check([bridgename, 'linkinfo', 'ports',
-                        bridgeportname, attrname], attrval):
-            return
-        utils.exec_command('/sbin/brctl set%s %s %s %s' %
-                           (attrname,
-                            bridgename,
-                            bridgeportname,
-                            attrval))
-
-    def set_bridge_attrs(self, bridgename, attrdict):
-        for k, v in attrdict.iteritems():
-            if not v:
-                continue
-            if self._cache_check([bridgename, 'linkinfo', k], v):
-                continue
-            try:
-                cmd = '/sbin/brctl set%s %s %s' % (k, bridgename, v)
-                utils.exec_command(cmd)
-            except Exception, e:
-                self.logger.warn('%s: %s' %(bridgename, str(e)))
-                pass
-
-    def set_bridge_attr(self, bridgename, attrname, attrval):
-        if self._cache_check([bridgename, 'linkinfo', attrname], attrval):
-            return
-        utils.exec_command('/sbin/brctl set%s %s %s' %
-                           (attrname, bridgename, attrval))
-
-    def get_bridge_attrs(self, bridgename):
-        return self._cache_get([bridgename, 'linkinfo'])
-
-    def get_bridgeport_attrs(self, bridgename, bridgeportname):
-        return self._cache_get([bridgename, 'linkinfo', 'ports',
-                                      bridgeportname])
-
-    def get_bridgeport_attr(self, bridgename, bridgeportname, attrname):
-        return self._cache_get([bridgename, 'linkinfo', 'ports',
-                                      bridgeportname, attrname])
-
-    def set_stp(self, bridge, stp_state):
-        utils.exec_command('/sbin/brctl stp %s %s' % (bridge, stp_state))
-
-    def get_stp(self, bridge):
-        sysfs_stpstate = '/sys/class/net/%s/bridge/stp_state' %bridge
-        if not os.path.exists(sysfs_stpstate):
-            return 'error'
-        stpstate = self.read_file_oneline(sysfs_stpstate)
-        if not stpstate:
-            return 'error'
-        try:
-            if int(stpstate) > 0:
-                return 'yes'
-            elif int(stpstate) == 0:
-                return 'no'
-        except:
-            return 'unknown'
-
-    def conv_value_to_user(self, str):
-        try:
-            ret = int(str) / 100
-        except:
-            return None
-        finally:
-            return '%d' %ret
-
-    def read_value_from_sysfs(self, filename, preprocess_func):
-        value = self.read_file_oneline(filename)
-        if not value:
-            return None
-        return preprocess_func(value)
-
-    def set_ageing(self, bridge, ageing):
-        utils.exec_command('/sbin/brctl setageing %s %s' % (bridge, ageing))
-
-    def get_ageing(self, bridge):
-        return self.read_value_from_sysfs('/sys/class/net/%s/bridge/ageing_time'
-                                     %bridge, self.conv_value_to_user)
-
-    def set_bridgeprio(self, bridge, prio):
-        utils.exec_command('/sbin/brctl setbridgeprio %s %s' % (bridge, prio))
-
-    def get_bridgeprio(self, bridge):
-        return self.read_file_oneline(
-                       '/sys/class/net/%s/bridge/priority' %bridge)
-
-    def set_fd(self, bridge, fd):
-        utils.exec_command('/sbin/brctl setfd %s %s' % (bridge, fd))
-
-    def get_fd(self, bridge):
-        return self.read_value_from_sysfs(
-                            '/sys/class/net/%s/bridge/forward_delay'
-                            %bridge, self.conv_value_to_user)
-
-    def set_gcint(self, bridge, gcint):
-        #cmd = '/sbin/brctl setgcint ' + bridge + ' ' + gcint
-        raise Exception('set_gcint not implemented')
-
-    def set_hello(self, bridge, hello):
-        utils.exec_command('/sbin/brctl sethello %s %s' % (bridge, hello))
-
-    def get_hello(self, bridge):
-        return self.read_value_from_sysfs('/sys/class/net/%s/bridge/hello_time'
-                                          %bridge, self.conv_value_to_user)
-
-    def set_maxage(self, bridge, maxage):
-        utils.exec_command('/sbin/brctl setmaxage %s %s' % (bridge, maxage))
-
-    def get_maxage(self, bridge):
-        return self.read_value_from_sysfs('/sys/class/net/%s/bridge/max_age'
-                                          %bridge, self.conv_value_to_user)
-
-    def set_pathcost(self, bridge, port, pathcost):
-        utils.exec_command('/sbin/brctl setpathcost %s %s %s' %
-                           (bridge, port, pathcost))
-
-    def get_pathcost(self, bridge, port):
-        return self.read_file_oneline('/sys/class/net/%s/brport/path_cost'
-                                        %port)
-
-    def set_portprio(self, bridge, port, prio):
-        utils.exec_command('/sbin/brctl setportprio %s %s %s' %
-                           (bridge, port, prio))
-
-    def get_portprio(self, bridge, port):
-        return self.read_file_oneline('/sys/class/net/%s/brport/priority'
-                                        %port)
-
-    def set_hashmax(self, bridge, hashmax):
-        utils.exec_command('/sbin/brctl sethashmax %s %s' % (bridge, hashmax))
-
-    def get_hashmax(self, bridge):
-        return self.read_file_oneline('/sys/class/net/%s/bridge/hash_max'
-                                        %bridge)
-
-    def set_hashel(self, bridge, hashel):
-        utils.exec_command('/sbin/brctl sethashel %s %s' % (bridge, hashel))
-
-    def get_hashel(self, bridge):
-        return self.read_file_oneline('/sys/class/net/%s/bridge/hash_elasticity'
-                                        %bridge)
-
-    def set_mclmc(self, bridge, mclmc):
-        utils.exec_command('/sbin/brctl setmclmc %s %s' % (bridge, mclmc))
-
-    def get_mclmc(self, bridge):
-        return self.read_file_oneline(
-                    '/sys/class/net/%s/bridge/multicast_last_member_count'
-                    %bridge)
-
-    def set_mcrouter(self, bridge, mcrouter):
-        utils.exec_command('/sbin/brctl setmcrouter %s %s' % (bridge, mcrouter))
-
-    def get_mcrouter(self, bridge):
-        return self.read_file_oneline(
-                    '/sys/class/net/%s/bridge/multicast_router' %bridge)
-
-    def set_mcsnoop(self, bridge, mcsnoop):
-        utils.exec_command('/sbin/brctl setmcsnoop %s %s' % (bridge, mcsnoop))
-
-    def get_mcsnoop(self, bridge):
-        return self.read_file_oneline(
-                    '/sys/class/net/%s/bridge/multicast_snooping' %bridge)
-
-    def set_mcsqc(self, bridge, mcsqc):
-        utils.exec_command('/sbin/brctl setmcsqc %s %s' % (bridge, mcsqc))
-
-    def get_mcsqc(self, bridge):
-        return self.read_file_oneline(
-                    '/sys/class/net/%s/bridge/multicast_startup_query_count'
-                    %bridge)
-
-    def set_mcqifaddr(self, bridge, mcqifaddr):
-        utils.exec_command('/sbin/brctl setmcqifaddr %s %s' %
-                           (bridge, mcqifaddr))
-
-    def get_mcqifaddr(self, bridge):
-        return self.read_file_oneline(
-                 '/sys/class/net/%s/bridge/multicast_startup_query_use_ifaddr'
-                 %bridge)
-
-    def set_mcquerier(self, bridge, mcquerier):
-        utils.exec_command('/sbin/brctl setmcquerier %s %s' %
-                           (bridge, mcquerier))
-
-    def get_mcquerier(self, bridge):
-        return self.read_file_oneline(
-                 '/sys/class/net/%s/bridge/multicast_querier' %bridge)
-
-    def set_mcqv4src(self, bridge, vlan, mcquerier):
-        if vlan == 0 or vlan > 4095:
-            self.logger.warn('mcqv4src vlan \'%d\' invalid range' %vlan)
-            return
-
-        ip = mcquerier.split('.')
-        if len(ip) != 4:
-            self.logger.warn('mcqv4src \'%s\' invalid IPv4 address' %mcquerier)
-            return
-        for k in ip:
-            if not k.isdigit() or int(k, 10) < 0 or int(k, 10) > 255:
-                self.logger.warn('mcqv4src \'%s\' invalid IPv4 address' %mcquerier)
-                return
-
-        utils.exec_command('/sbin/brctl setmcqv4src %s %d %s' %
-                           (bridge, vlan, mcquerier))
-
-    def del_mcqv4src(self, bridge, vlan):
-        utils.exec_command('/sbin/brctl delmcqv4src %s %d' % (bridge, vlan))
-
-    def get_mcqv4src(self, bridge, vlan=None):
-        if not self.supported_command['showmcqv4src']:
-            return {}
-        mcqv4src = {}
-        try:
-            mcqout = utils.exec_command('/sbin/brctl showmcqv4src %s' % bridge)
-        except Exception as e:
-            s = str(e).lower()
-            if 'never heard' in s:
-                self.logger.info('/sbin/brctl showmcqv4src: '
-                                 'skipping unsupported command')
-                self.supported_command['showmcqv4src'] = False
-                return {}
-            raise
-        if not mcqout: return {}
-        mcqlines = mcqout.splitlines()
-        for l in mcqlines[1:]:
-            l=l.strip()
-            k, d, v = l.split('\t')
-            if not k or not v:
-                continue
-            mcqv4src[k] = v
-        if vlan:
-            return mcqv4src.get(vlan)
-        return mcqv4src
-
-    def set_mclmi(self, bridge, mclmi):
-        utils.exec_command('/sbin/brctl setmclmi %s %s' % (bridge, mclmi))
-
-    def get_mclmi(self, bridge):
-        return self.read_file_oneline(
-                 '/sys/class/net/%s/bridge/multicast_last_member_interval'
-                 %bridge)
-
-    def set_mcmi(self, bridge, mcmi):
-        utils.exec_command('/sbin/brctl setmcmi %s %s' % (bridge, mcmi))
-
-    def get_mcmi(self, bridge):
-        return self.read_file_oneline(
-                 '/sys/class/net/%s/bridge/multicast_membership_interval'
-                 %bridge)
-
-    def bridge_exists(self, bridge):
-        return os.path.exists('/sys/class/net/%s/bridge' %bridge)
-
-    def is_bridge_port(self, ifacename):
-        return os.path.exists('/sys/class/net/%s/brport' %ifacename)
-
-    def bridge_port_exists(self, bridge, bridgeportname):
-        try:
-            return os.path.exists('/sys/class/net/%s/brif/%s'
-                                  %(bridge, bridgeportname))
-        except Exception:
-            return False
-   
-    def get_bridge_ports(self, bridgename):
-        try:
-            return os.listdir('/sys/class/net/%s/brif/' %bridgename)
-        except:
-            return []
diff --git a/ifupdownaddons/cache.py b/ifupdownaddons/cache.py
deleted file mode 100644 (file)
index 35da9f6..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-
-import pprint
-
-
-class MSTPAttrsCache():
-    bridges = {}
-
-    @classmethod
-    def get(cls, bridgename, default=None):
-        if bridgename in MSTPAttrsCache.bridges:
-            return MSTPAttrsCache.bridges[bridgename]
-        else:
-            return default
-
-    @classmethod
-    def set(cls, bridgename, attrs):
-        MSTPAttrsCache.bridges[bridgename] = attrs
-
-    @classmethod
-    def invalidate(cls):
-        MSTPAttrsCache.bridges = {}
-
-
-class linkCache():
-    """ This class contains methods and instance variables to cache
-    link info """
-
-    _shared_state = {}
-
-    """ { <ifacename> : { 'ifindex': <index>,
-                          'mtu': <mtu>,
-                          'state' : <state>',
-                          'flags' : <flags>,
-                          'kind' : <kind: bridge, bond, vlan>,
-                          'linkinfo' : {<attr1> : <attrval1>,
-                                        <attr2> : <attrval2>,
-                                        <ports> : {
-                                                  } """
-    links = {}
-    vrfs = {}
-
-    @classmethod
-    def get_attr(cls, mapList):
-        return reduce(lambda d, k: d[k], mapList, linkCache.links)
-
-    @classmethod
-    def set_attr(cls, mapList, value):
-        cls.get_attr(mapList[:-1])[mapList[-1]] = value
-
-    @classmethod
-    def del_attr(cls, mapList):
-        try:
-            del cls.get_attr(mapList[:-1])[mapList[-1]]
-        except:
-            pass
-
-    @classmethod
-    def update_attrdict(cls, mapList, valuedict):
-        try:
-            cls.get_attr(mapList[:-1])[mapList[-1]].update(valuedict)
-        except:
-            cls.get_attr(mapList[:-1])[mapList[-1]] = valuedict
-            pass
-
-    @classmethod
-    def append_to_attrlist(cls, mapList, value):
-        cls.get_attr(mapList[:-1])[mapList[-1]].append(value)
-
-    @classmethod
-    def remove_from_attrlist(cls, mapList, value):
-        try:
-            cls.get_attr(mapList[:-1])[mapList[-1]].remove(value)
-        except:
-            pass
-
-    @classmethod
-    def check_attr(cls, attrlist, value=None):
-        try:
-            cachedvalue = cls.get_attr(attrlist)
-            if value:
-                if cachedvalue == value:
-                    return True
-                else:
-                    return False
-            elif cachedvalue:
-                return True
-            else:
-                return False
-        except:
-            return False
-
-    @classmethod
-    def invalidate(cls):
-        cls.links = {}
-
-    @classmethod
-    def dump(cls):
-        print 'Dumping link cache'
-        pp = pprint.PrettyPrinter(indent=4)
-        pp.pprint(cls.links)
-
-    @classmethod
-    def dump_link(cls, linkname):
-        print 'Dumping link %s' % linkname
-        pp = pprint.PrettyPrinter(indent=4)
-        pp.pprint(cls.links.get(linkname))
diff --git a/ifupdownaddons/dhclient.py b/ifupdownaddons/dhclient.py
deleted file mode 100644 (file)
index b982d85..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-
-from ifupdown.utils import utils
-from utilsbase import *
-import os
-
-
-class dhclient(utilsBase):
-    """ This class contains helper methods to interact with the dhclient
-    utility """
-
-    def _pid_exists(self, pidfilename):
-        if os.path.exists(pidfilename):
-            pid = self.read_file_oneline(pidfilename)
-            if not os.path.exists('/proc/%s' %pid):
-                return False
-        else:
-            return False
-        return True
-
-    def is_running(self, ifacename):
-        return self._pid_exists('/run/dhclient.%s.pid' %ifacename)
-
-    def is_running6(self, ifacename):
-        return self._pid_exists('/run/dhclient6.%s.pid' %ifacename)
-
-    def _run_dhclient_cmd(self, cmd, cmd_prefix=None):
-        if not cmd_prefix:
-            cmd_aslist = []
-        else:
-            cmd_aslist = cmd_prefix.split()
-        if cmd_aslist:
-            cmd_aslist.extend(cmd)
-        else:
-            cmd_aslist = cmd
-        utils.exec_commandl(cmd_aslist, stdout=None, stderr=None)
-
-    def stop(self, ifacename, cmd_prefix=None):
-        if os.path.exists('/sbin/dhclient3'):
-            cmd = ['/sbin/dhclient3', '-x', '-pf',
-                   '/run/dhclient.%s.pid' %ifacename, '-lf',
-                   '/var/lib/dhcp3/dhclient.%s.leases' %ifacename,
-                   '%s' %ifacename]
-        else:
-            cmd = ['/sbin/dhclient', '-x', '-pf',
-                   '/run/dhclient.%s.pid' %ifacename,
-                   '-lf', '/var/lib/dhcp/dhclient.%s.leases' %ifacename,
-                   '%s' %ifacename]
-        self._run_dhclient_cmd(cmd, cmd_prefix)
-
-    def start(self, ifacename, wait=True, cmd_prefix=None):
-        if os.path.exists('/sbin/dhclient3'):
-            cmd = ['/sbin/dhclient3', '-pf',
-                   '/run/dhclient.%s.pid' %ifacename,
-                   '-lf', '/var/lib/dhcp3/dhclient.%s.leases' %ifacename,
-                   '%s' %ifacename]
-        else:
-            cmd = ['/sbin/dhclient', '-pf',
-                   '/run/dhclient.%s.pid' %ifacename, '-lf',
-                   '/var/lib/dhcp/dhclient.%s.leases' %ifacename,
-                   '%s' %ifacename]
-        if not wait:
-            cmd.append('-nw')
-        self._run_dhclient_cmd(cmd, cmd_prefix)
-
-    def release(self, ifacename, cmd_prefix=None):
-        if os.path.exists('/sbin/dhclient3'):
-            cmd = ['/sbin/dhclient3', '-r', '-pf',
-                   '/run/dhclient.%s.pid' %ifacename, '-lf',
-                   '/var/lib/dhcp3/dhclient.%s.leases' %ifacename,
-                   '%s' %ifacename]
-        else:
-            cmd = ['/sbin/dhclient', '-r', '-pf',
-                   '/run/dhclient.%s.pid' %ifacename,
-                   '-lf', '/var/lib/dhcp/dhclient.%s.leases' %ifacename,
-                   '%s' %ifacename]
-        self._run_dhclient_cmd(cmd, cmd_prefix)
-
-    def start6(self, ifacename, wait=True, cmd_prefix=None):
-        cmd = ['/sbin/dhclient', '-6', '-pf',
-                '/run/dhclient6.%s.pid' %ifacename, '-lf',
-                '/var/lib/dhcp/dhclient6.%s.leases' % ifacename,
-                '%s' %ifacename]
-        if not wait:
-            cmd.append('-nw')
-        self._run_dhclient_cmd(cmd, cmd_prefix)
-
-    def stop6(self, ifacename, cmd_prefix=None):
-        cmd = ['/sbin/dhclient', '-6', '-x', '-pf',
-               '/run/dhclient6.%s.pid' % ifacename, '-lf',
-               '/var/lib/dhcp/dhclient6.%s.leases' % ifacename,
-               '%s' %ifacename]
-        self._run_dhclient_cmd(cmd, cmd_prefix)
-
-    def release6(self, ifacename, cmd_prefix=None):
-        cmd = ['/sbin/dhclient', '-6', '-r', '-pf',
-               '/run/dhclient6.%s.pid' %ifacename,
-              '-lf', '/var/lib/dhcp/dhclient6.%s.leases' % ifacename,
-               '%s' %ifacename]
-        self._run_dhclient_cmd(cmd, cmd_prefix)
diff --git a/ifupdownaddons/ifenslaveutil.py b/ifupdownaddons/ifenslaveutil.py
deleted file mode 100644 (file)
index c7e0d42..0000000
+++ /dev/null
@@ -1,421 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-
-import os
-import re
-from ifupdown.iface import *
-from utilsbase import *
-from iproute2 import *
-from cache import *
-
-class ifenslaveutil(utilsBase):
-    """ This class contains methods to interact with linux kernel bond
-    related interfaces """
-
-    _cache_fill_done = False
-
-    def __init__(self, *args, **kargs):
-        utilsBase.__init__(self, *args, **kargs)
-        if self.CACHE and not self._cache_fill_done:
-            self._bond_linkinfo_fill_all()
-            self._cache_fill_done = True
-
-    def _bond_linkinfo_fill_attrs(self, bondname):
-        try:
-            linkCache.links[bondname]['linkinfo'] = {}
-        except:
-            linkCache.links[bondname] = {'linkinfo': {}}
-
-        try:
-            linkCache.set_attr([bondname, 'linkinfo', 'slaves'],
-                self.read_file_oneline('/sys/class/net/%s/bonding/slaves'
-                %bondname).split())
-            linkCache.set_attr([bondname, 'linkinfo', 'mode'],
-                self.read_file_oneline('/sys/class/net/%s/bonding/mode'
-                %bondname).split()[0])
-            linkCache.set_attr([bondname, 'linkinfo', 'xmit_hash_policy'],
-                self.read_file_oneline(
-                    '/sys/class/net/%s/bonding/xmit_hash_policy'
-                    %bondname).split()[0])
-            linkCache.set_attr([bondname, 'linkinfo', 'lacp_rate'],
-                self.read_file_oneline('/sys/class/net/%s/bonding/lacp_rate'
-                                       %bondname).split()[1])
-            linkCache.set_attr([bondname, 'linkinfo', 'ad_sys_priority'],
-                self.read_file_oneline('/sys/class/net/%s/bonding/ad_sys_priority'
-                                       %bondname))
-            linkCache.set_attr([bondname, 'linkinfo', 'ad_sys_mac_addr'],
-                self.read_file_oneline('/sys/class/net/%s/bonding/ad_sys_mac_addr'
-                                       %bondname))
-            map(lambda x: linkCache.set_attr([bondname, 'linkinfo', x],
-                   self.read_file_oneline('/sys/class/net/%s/bonding/%s'
-                        %(bondname, x))),
-                       ['use_carrier', 'miimon', 'min_links', 'num_unsol_na',
-                        'num_grat_arp', 'lacp_bypass_allow', 'lacp_bypass_period', 
-                        'clag_enable'])
-        except Exception, e:
-            pass
-
-    def _bond_linkinfo_fill_all(self):
-        bondstr = self.read_file_oneline('/sys/class/net/bonding_masters')
-        if not bondstr:
-            return
-        [self._bond_linkinfo_fill_attrs(b) for b in bondstr.split()]
-
-    def _bond_linkinfo_fill(self, bondname, refresh=False):
-        try:
-            linkCache.get_attr([bondname, 'linkinfo', 'slaves'])
-            return
-        except:
-            pass
-        bondstr = self.read_file_oneline('/sys/class/net/bonding_masters')
-        if (not bondstr or bondname not in bondstr.split()):
-            raise Exception('bond %s not found' %bondname)
-        self._bond_linkinfo_fill_attrs(bondname)
-
-    def _cache_get(self, attrlist, refresh=False):
-        try:
-            if self.DRYRUN:
-                return None
-            if self.CACHE:
-                if not ifenslaveutil._cache_fill_done: 
-                    self._bond_linkinfo_fill_all()
-                    ifenslaveutil._cache_fill_done = True
-                    return linkCache.get_attr(attrlist)
-                if not refresh:
-                    return linkCache.get_attr(attrlist)
-            self._bond_linkinfo_fill(attrlist[0], refresh)
-            return linkCache.get_attr(attrlist)
-        except Exception, e:
-            self.logger.debug('_cache_get(%s) : [%s]'
-                    %(str(attrlist), str(e)))
-            pass
-        return None
-
-    def _cache_check(self, attrlist, value, refresh=False):
-        try:
-            attrvalue = self._cache_get(attrlist, refresh)
-            if attrvalue and attrvalue == value:
-                return True
-        except Exception, e:
-            self.logger.debug('_cache_check(%s) : [%s]'
-                    %(str(attrlist), str(e)))
-            pass
-        return False
-
-    def _cache_update(self, attrlist, value):
-        if self.DRYRUN: return
-        try:
-            if attrlist[-1] == 'slaves':
-                linkCache.add_to_attrlist(attrlist, value)
-                return
-            linkCache.add_attr(attrlist, value)
-        except:
-            pass
-
-    def _cache_delete(self, attrlist, value=None):
-        if self.DRYRUN: return
-        try:
-            if attrlist[-1] == 'slaves':
-                linkCache.remove_from_attrlist(attrlist, value)
-                return
-            linkCache.del_attr(attrlist)
-        except:
-            pass
-
-    def _cache_invalidate(self):
-        if self.DRYRUN: return
-        linkCache.invalidate()
-
-    def set_attrs(self, bondname, attrdict, prehook):
-        for attrname, attrval in attrdict.items():
-            if (self._cache_check([bondname, 'linkinfo',
-                attrname], attrval)):
-                continue
-            if (attrname == 'mode' or attrname == 'xmit_hash_policy' or
-                    attrname == 'lacp_rate' or attrname == 'min_links'):
-                if prehook:
-                    prehook(bondname)
-            try:
-                self.write_file('/sys/class/net/%s/bonding/%s'
-                                %(bondname, attrname), attrval)
-            except Exception, e:
-                if self.FORCE:
-                    self.logger.warn(str(e))
-                    pass
-                else:
-                    raise
-
-    def set_use_carrier(self, bondname, use_carrier):
-        if not use_carrier or (use_carrier != '0' and use_carrier != '1'):
-            return
-        if (self._cache_check([bondname, 'linkinfo', 'use_carrier'],
-                use_carrier)):
-                return
-        self.write_file('/sys/class/net/%s' %bondname +
-                         '/bonding/use_carrier', use_carrier)
-        self._cache_update([bondname, 'linkinfo',
-                            'use_carrier'], use_carrier)
-
-    def get_use_carrier(self, bondname):
-        return self._cache_get([bondname, 'linkinfo', 'use_carrier'])
-
-    def set_xmit_hash_policy(self, bondname, hash_policy, prehook=None):
-        valid_values = ['layer2', 'layer3+4', 'layer2+3']
-        if not hash_policy:
-            return
-        if hash_policy not in valid_values:
-            raise Exception('invalid hash policy value %s' %hash_policy)
-        if (self._cache_check([bondname, 'linkinfo', 'xmit_hash_policy'],
-                hash_policy)):
-            return
-        if prehook:
-            prehook(bondname)
-        self.write_file('/sys/class/net/%s' %bondname +
-                         '/bonding/xmit_hash_policy', hash_policy)
-        self._cache_update([bondname, 'linkinfo', 'xmit_hash_policy'],
-                hash_policy)
-
-    def get_xmit_hash_policy(self, bondname):
-        return self._cache_get([bondname, 'linkinfo', 'xmit_hash_policy'])
-
-    def set_miimon(self, bondname, miimon):
-        if (self._cache_check([bondname, 'linkinfo', 'miimon'],
-                miimon)):
-            return
-        self.write_file('/sys/class/net/%s' %bondname +
-                '/bonding/miimon', miimon)
-        self._cache_update([bondname, 'linkinfo', 'miimon'], miimon)
-
-    def get_miimon(self, bondname):
-        return self._cache_get([bondname, 'linkinfo', 'miimon'])
-
-    def set_clag_enable(self, bondname, clag_id):
-        clag_enable = '0' if clag_id == '0' else '1'
-        if self._cache_check([bondname, 'linkinfo', 'clag_enable'], 
-                        clag_enable) == False:
-            self.write_file('/sys/class/net/%s' %bondname +
-                        '/bonding/clag_enable', clag_enable)
-            self._cache_update([bondname, 'linkinfo', 'clag_enable'], 
-                        clag_enable)
-
-    def get_clag_enable(self, bondname):
-        return self._cache_get([bondname, 'linkinfo', 'clag_enable'])
-
-    def set_mode(self, bondname, mode, prehook=None):
-        valid_modes = ['balance-rr', 'active-backup', 'balance-xor',
-                       'broadcast', '802.3ad', 'balance-tlb', 'balance-alb']
-        if not mode:
-            return
-        if mode not in valid_modes:
-            raise Exception('invalid mode %s' %mode)
-        if (self._cache_check([bondname, 'linkinfo', 'mode'],
-                mode)):
-            return
-        if prehook:
-            prehook(bondname)
-        self.write_file('/sys/class/net/%s' %bondname + '/bonding/mode', mode)
-        self._cache_update([bondname, 'linkinfo', 'mode'], mode)
-
-    def get_mode(self, bondname):
-        return self._cache_get([bondname, 'linkinfo', 'mode'])
-
-    def set_lacp_rate(self, bondname, lacp_rate, prehook=None, posthook=None):
-        if not lacp_rate or (lacp_rate != '0' and lacp_rate != '1'):
-            return
-        if (self._cache_check([bondname, 'linkinfo', 'lacp_rate'],
-                lacp_rate)):
-            return
-        if prehook:
-            prehook(bondname)
-        try:
-            self.write_file('/sys/class/net/%s' %bondname +
-                            '/bonding/lacp_rate', lacp_rate)
-        except:
-            raise
-        finally:
-            if posthook:
-                prehook(bondname)
-            self._cache_update([bondname, 'linkinfo',
-                                'lacp_rate'], lacp_rate)
-
-    def get_lacp_rate(self, bondname):
-        return self._cache_get([bondname, 'linkinfo', 'lacp_rate'])
-
-    def set_lacp_fallback_allow(self, bondname, allow, prehook=None, posthook=None):
-        if (self._cache_check([bondname, 'linkinfo', 'lacp_bypass_allow'],
-                lacp_bypass_allow)):
-            return
-        if prehook:
-            prehook(bondname)
-        try:
-            self.write_file('/sys/class/net/%s' %bondname +
-                            '/bonding/lacp_bypass_allow', allow)
-        except:
-            raise
-        finally:
-            if posthook:
-                posthook(bondname)
-            self._cache_update([bondname, 'linkinfo',
-                               'lacp_bypass_allow'], allow)
-
-    def get_lacp_fallback_allow(self, bondname):
-        return self._cache_get([bondname, 'linkinfo', 'lacp_bypass_allow'])
-
-    def set_lacp_fallback_period(self, bondname, period, prehook=None, posthook=None):
-        if (self._cache_check([bondname, 'linkinfo', 'lacp_bypass_period'],
-                lacp_bypass_period)):
-            return
-        if prehook:
-            prehook(bondname)
-        try:
-            self.write_file('/sys/class/net/%s' %bondname + 
-                            '/bonding/lacp_bypass_period', period)
-        except:
-            raise
-        finally:
-            if posthook:
-                posthook(bondname)
-            self._cache_update([bondname, 'linkinfo',
-                               'lacp_bypass_period'], period)
-
-    def get_lacp_fallback_period(self, bondname):
-        return self._cache_get([bondname, 'linkinfo', 'lacp_bypass_period']) 
-
-    def set_min_links(self, bondname, min_links, prehook=None):
-        if (self._cache_check([bondname, 'linkinfo', 'min_links'],
-                min_links)):
-            return
-        if prehook:
-            prehook(bondname)
-        self.write_file('/sys/class/net/%s/bonding/min_links' %bondname,
-                         min_links)
-        self._cache_update([bondname, 'linkinfo', 'min_links'], min_links)
-
-    def get_min_links(self, bondname):
-        return self._cache_get([bondname, 'linkinfo', 'min_links'])
-
-    def set_lacp_fallback_priority(self, bondname, port, val):
-        slavefile = '/sys/class/net/%s/bonding_slave/lacp_bypass_priority' %port
-        if os.path.exists(slavefile):
-            self.write_file(slavefile, val)
-
-    def get_lacp_fallback_priority(self, bondname):
-        slaves = self.get_slaves(bondname)
-        if not slaves:
-            return slaves
-        prios = []
-        for slave in slaves:
-            priofile = '/sys/class/net/%s/bonding_slave/lacp_bypass_priority' %slave
-            if os.path.exists(priofile):
-                val = self.read_file_oneline(priofile)
-                if val and val != '0':
-                    prio = slave + '=' + val
-                    prios.append(prio)
-        prios.sort()
-        prio_str = ' '.join(prios)
-        return prio_str
-
-    def get_ad_sys_mac_addr(self, bondname):
-        return self._cache_get([bondname, 'linkinfo', 'ad_sys_mac_addr'])
-
-    def get_ad_sys_priority(self, bondname):
-        return self._cache_get([bondname, 'linkinfo', 'ad_sys_priority'])
-
-    def enslave_slave(self, bondname, slave, prehook=None, posthook=None):
-        slaves = self._cache_get([bondname, 'linkinfo', 'slaves'])
-        if slaves and slave in slaves: return
-        if prehook:
-            prehook(slave)
-        self.write_file('/sys/class/net/%s' %bondname +
-                         '/bonding/slaves', '+' + slave)
-        if posthook:
-            posthook(slave)
-        self._cache_update([bondname, 'linkinfo', 'slaves'], slave) 
-
-    def remove_slave(self, bondname, slave):
-        slaves = self._cache_get([bondname, 'linkinfo', 'slaves'])
-        if slave not in slaves:
-            return
-        sysfs_bond_path = ('/sys/class/net/%s' %bondname +
-                           '/bonding/slaves')
-        if not os.path.exists(sysfs_bond_path):
-           return
-        self.write_file(sysfs_bond_path, '-' + slave)
-        self._cache_delete([bondname, 'linkinfo', 'slaves'], slave) 
-
-    def remove_slaves_all(self, bondname):
-        if not _self._cache_get([bondname, 'linkinfo', 'slaves']):
-            return
-        slaves = None
-        sysfs_bond_path = ('/sys/class/net/%s' %bondname +
-                           '/bonding/slaves')
-        ipcmd = iproute2()
-        try:
-            f = open(sysfs_bond_path, 'r')
-            slaves = f.readline().strip().split()
-            f.close()
-        except IOError, e:
-            raise Exception('error reading slaves of bond %s' %bondname
-                + '(' + str(e) + ')')
-        for slave in slaves:
-            ipcmd.ip_link_down(slave)
-            try:
-                self.remove_slave(bondname, slave)
-            except Exception, e:
-                if not self.FORCE:
-                    raise Exception('error removing slave %s'
-                        %slave + ' from bond %s' %bondname +
-                        '(%s)' %str(e))
-                else:
-                    pass
-        self._cache_del([bondname, 'linkinfo', 'slaves'])
-
-    def load_bonding_module(self):
-        return self.exec_command('modprobe -q bonding')
-
-    def create_bond(self, bondname):
-        if self.bond_exists(bondname):
-            return
-        sysfs_net = '/sys/class/net/'
-        sysfs_bonding_masters = sysfs_net + 'bonding_masters'
-        if not os.path.exists(sysfs_bonding_masters):
-            self.logger.debug('loading bonding driver')
-            self.load_bonding_module()
-            return True
-        self.write_file(sysfs_bonding_masters, '+' + bondname)
-        self._cache_update([bondname], {})
-
-    def delete_bond(self, bondname):
-        if not os.path.exists('/sys/class/net/%s' %bondname):
-            return
-        self.write_file('/sys/class/net/bonding_masters', '-' + bondname)
-        self._cache_delete([bondname])
-
-    def unset_master(self, bondname):
-        print 'Do nothing yet'
-        return 0
-
-    def get_slaves(self, bondname):
-        slaves = self._cache_get([bondname, 'linkinfo', 'slaves'])
-        if slaves:
-            return list(slaves)
-        slavefile = '/sys/class/net/%s/bonding/slaves' %bondname
-        if os.path.exists(slavefile):
-            buf = self.read_file_oneline(slavefile)
-            if buf:
-                slaves = buf.split()
-        if not slaves:
-            return slaves
-        self._cache_update([bondname, 'linkinfo', 'slaves'], slaves)
-        return list(slaves)
-
-    def bond_slave_exists(self, bond, slave):
-        slaves = self.get_slaves(bond)
-        if not slaves: return False
-        return slave in slaves
-
-    def bond_exists(self, bondname):
-        return os.path.exists('/sys/class/net/%s/bonding' %bondname)
diff --git a/ifupdownaddons/iproute2.py b/ifupdownaddons/iproute2.py
deleted file mode 100644 (file)
index e9476a9..0000000
+++ /dev/null
@@ -1,1028 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-
-import os
-import glob
-import shlex
-import signal
-import subprocess
-
-from ifupdown.utils import utils
-from collections import OrderedDict
-from utilsbase import *
-from systemutils import *
-from cache import *
-import ifupdown.ifupdownflags as ifupdownflags
-
-VXLAN_UDP_PORT = 4789
-
-class iproute2(utilsBase):
-    """ This class contains helper methods to cache and interact with the
-    commands in the iproute2 package """
-
-    _cache_fill_done = False
-    ipbatchbuf = ''
-    ipbatch = False
-    ipbatch_pause = False
-
-    def __init__(self, *args, **kargs):
-        utilsBase.__init__(self, *args, **kargs)
-        if ifupdownflags.flags.CACHE:
-            self._fill_cache()
-        self.supported_command = {
-            '/sbin/bridge -c -json vlan show': True
-        }
-
-    def _fill_cache(self):
-        if not iproute2._cache_fill_done:
-            self._link_fill()
-            self._addr_fill()
-            iproute2._cache_fill_done = True
-            return True
-        return False
-
-    def _get_vland_id(self, citems, i, warn):
-        try:
-            sub = citems[i:]
-            index = sub.index('id')
-            int(sub[index + 1])
-            return sub[index + 1]
-        except:
-            if warn:
-                raise Exception('invalid use of \'vlan\' keyword')
-            return None
-
-    def _link_fill(self, ifacename=None, refresh=False):
-        """ fills cache with link information
-       
-        if ifacename argument given, fill cache for ifacename, else
-        fill cache for all interfaces in the system
-        """
-
-        warn = True
-        linkout = {}
-        vxrd_running = False
-        if iproute2._cache_fill_done and not refresh: return
-        try:
-            # if ifacename already present, return
-            if (ifacename and not refresh and
-                    linkCache.get_attr([ifacename, 'ifflag'])):
-                return
-        except:
-            pass
-        cmdout = self.link_show(ifacename=ifacename)
-        if not cmdout:
-            return
-        # read vxrd.pid and cache the running state before going through
-        # every interface in the system
-        if systemUtils.is_service_running(None, '/var/run/vxrd.pid'):
-            vxrd_running = True
-        for c in cmdout.splitlines():
-            citems = c.split()
-            ifnamenlink = citems[1].split('@')
-            if len(ifnamenlink) > 1:
-                ifname = ifnamenlink[0]
-                iflink = ifnamenlink[1].strip(':')
-            else:
-                ifname = ifnamenlink[0].strip(':')
-                iflink = None
-            linkattrs = {}
-            linkattrs['link'] = iflink
-            linkattrs['ifindex'] = citems[0].strip(':')
-            flags = citems[2].strip('<>').split(',')
-            linkattrs['flags'] = flags
-            linkattrs['ifflag'] = 'UP' if 'UP' in flags else 'DOWN'
-            for i in range(0, len(citems)):
-                try:
-                    if citems[i] == 'mtu':
-                        linkattrs['mtu'] = citems[i + 1]
-                    elif citems[i] == 'state':
-                        linkattrs['state'] = citems[i + 1]
-                    elif citems[i] == 'link/ether':
-                        linkattrs['hwaddress'] = citems[i + 1]
-                    elif citems[i] in [ 'link/gre', 'link/ipip', 'link/sit', 'link/gre6', 'link/tunnel6', 'gretap' ]:
-                        linkattrs['kind'] = 'tunnel'
-                        tunattrs = {'mode' : citems[i].split ('/')[-1],
-                                    'endpoint' : None,
-                                    'local' : None,
-                                    'ttl' : None,
-                                    'physdev' : None}
-                        for j in range(i, len(citems)):
-                            if citems[j] == 'local':
-                                tunattrs['local'] = citems[j + 1]
-                            elif citems[j] == 'remote':
-                                tunattrs['endpoint'] = citems[j + 1]
-                            elif citems[j] == 'ttl':
-                                tunattrs['ttl'] = citems[j + 1]
-                            elif citems[j] == 'dev':
-                                tunattrs['physdev'] = citems[j + 1]
-                            elif citems[j] in ['vti', 'vti6', 'ip6gre', 'ipip6', 'ip6ip6']:
-                                tunattrs['mode'] = citems[j]
-                        linkattrs['linkinfo'] = tunattrs
-                        break
-                    elif citems[i] == 'link/ppp':
-                        linkattrs['kind'] = 'ppp'
-                    elif citems[i] == 'vlan':
-                        vlanid = self._get_vland_id(citems, i, warn)
-                        if vlanid:
-                            linkattrs['linkinfo'] = {'vlanid': vlanid}
-                            linkattrs['kind'] = 'vlan'
-                    elif citems[i] == 'dummy':
-                        linkattrs['kind'] = 'dummy'
-                    elif citems[i] == 'vxlan' and citems[i + 1] == 'id':
-                        linkattrs['kind'] = 'vxlan'
-                        vattrs = {'vxlanid': citems[i + 2],
-                                  'svcnode': None,
-                                  'physdev': None,
-                                  'remote': [],
-                                  'ageing': citems[i + 2],
-                                  'learning': 'on'}
-                        for j in range(i + 2, len(citems)):
-                            if citems[j] == 'local':
-                                vattrs['local'] = citems[j + 1]
-                            elif citems[j] == 'group':
-                                vattrs['svcnode'] = citems[j + 1]
-                            elif citems[j] == 'ageing':
-                                vattrs['ageing'] = citems[j + 1]
-                            elif citems[j] == 'nolearning':
-                                vattrs['learning'] = 'off'
-                            elif citems[j] == 'dev':
-                                vattrs['physdev'] = citems[j + 1]
-                        # get vxlan peer nodes if provisioned by user and not by vxrd
-                        if not vxrd_running:
-                            peers = self.get_vxlan_peers(ifname, vattrs['svcnode'])
-                            if peers:
-                                vattrs['remote'] = peers
-                        linkattrs['linkinfo'] = vattrs
-                        break
-                    elif citems[i] == 'vrf' and citems[i + 1] == 'table':
-                        vattrs = {'table': citems[i + 2]}
-                        linkattrs['linkinfo'] = vattrs
-                        linkattrs['kind'] = 'vrf'
-                        linkCache.vrfs[ifname] = vattrs
-                        break
-                    elif citems[i] == 'veth':
-                        linkattrs['kind'] = 'veth'
-                    elif citems[i] == 'vrf_slave':
-                        linkattrs['slave_kind'] = 'vrf_slave'
-                        break
-                    elif citems[i] == 'macvlan' and citems[i + 1] == 'mode':
-                        linkattrs['kind'] = 'macvlan'
-                except Exception as e:
-                    if warn:
-                        self.logger.debug('%s: parsing error: id, mtu, state, link/ether, vlan, dummy, vxlan, local, remote, ageing, nolearning, vrf, table, vrf_slave are reserved keywords: %s' % (ifname, str(e)))
-                        warn = False
-            #linkattrs['alias'] = self.read_file_oneline(
-            #            '/sys/class/net/%s/ifalias' %ifname)
-            linkout[ifname] = linkattrs
-        [linkCache.update_attrdict([ifname], linkattrs)
-                    for ifname, linkattrs in linkout.items()]
-
-    def _addr_filter(self, ifname, addr, scope=None):
-        default_addrs = ['127.0.0.1/8', '::1/128' , '0.0.0.0']
-        if ifname == 'lo' and addr in default_addrs:
-            return True
-        if scope and scope == 'link':
-            return True
-        return False
-
-    def _addr_fill(self, ifacename=None, refresh=False):
-        """ fills cache with address information
-       
-        if ifacename argument given, fill cache for ifacename, else
-        fill cache for all interfaces in the system
-        """
-        linkout = {}
-        if iproute2._cache_fill_done and not refresh: return
-
-        try:
-            # Check if ifacename is already full, in which case, return
-            if ifacename and not refresh:
-                linkCache.get_attr([ifacename, 'addrs'])
-                return
-        except:
-            pass
-        cmdout = self.addr_show(ifacename=ifacename)
-        if not cmdout:
-            return
-        for c in cmdout.splitlines():
-            citems = c.split()
-            ifnamenlink = citems[1].split('@')
-            if len(ifnamenlink) > 1:
-                ifname = ifnamenlink[0]
-            else:
-                ifname = ifnamenlink[0].strip(':')
-            if not linkout.get(ifname):
-                linkattrs = {}
-                linkattrs['addrs'] = OrderedDict({})
-                try:
-                    linkout[ifname].update(linkattrs)
-                except KeyError:
-                    linkout[ifname] = linkattrs
-            if citems[2] == 'inet':
-                if self._addr_filter(ifname, citems[3], scope=citems[5]):
-                    continue
-                addrattrs = {}
-                addrattrs['scope'] = citems[5]
-                addrattrs['type'] = 'inet'
-                linkout[ifname]['addrs'][citems[3]] = addrattrs
-            elif citems[2] == 'inet6':
-                if self._addr_filter(ifname, citems[3], scope=citems[5]):
-                    continue
-                if citems[5] == 'link': continue #skip 'link' addresses
-                addrattrs = {}
-                addrattrs['scope'] = citems[5]
-                addrattrs['type'] = 'inet6'
-                linkout[ifname]['addrs'][citems[3]] = addrattrs
-        [linkCache.update_attrdict([ifname], linkattrs)
-                    for ifname, linkattrs in linkout.items()]
-
-    def _cache_get(self, type, attrlist, refresh=False):
-        try:
-            if ifupdownflags.flags.DRYRUN:
-                return False
-            if ifupdownflags.flags.CACHE:
-                if self._fill_cache():
-                    # if we filled the cache, return new data
-                    return linkCache.get_attr(attrlist)
-                if not refresh:
-                    return linkCache.get_attr(attrlist)
-            if type == 'link':
-                self._link_fill(attrlist[0], refresh)
-            elif type == 'addr':
-                self._addr_fill(attrlist[0], refresh)
-            else:
-                self._link_fill(attrlist[0], refresh)
-                self._addr_fill(attrlist[0], refresh)
-            return linkCache.get_attr(attrlist)
-        except Exception, e:
-            self.logger.debug('_cache_get(%s) : [%s]'
-                    %(str(attrlist), str(e)))
-            pass
-        return None
-
-    def _cache_check(self, type, attrlist, value, refresh=False):
-        try:
-            attrvalue = self._cache_get(type, attrlist, refresh)
-            if attrvalue and attrvalue == value:
-                return True
-        except Exception, e:
-            self.logger.debug('_cache_check(%s) : [%s]'
-                    %(str(attrlist), str(e)))
-            pass
-        return False
-
-    def _cache_update(self, attrlist, value):
-        if ifupdownflags.flags.DRYRUN: return
-        try:
-            linkCache.set_attr(attrlist, value)
-        except:
-            pass
-
-    def _cache_delete(self, attrlist):
-        if ifupdownflags.flags.DRYRUN: return
-        try:
-            linkCache.del_attr(attrlist)
-        except:
-            pass
-
-    def _cache_invalidate(self):
-        linkCache.invalidate()
-        iproute2._cache_fill_done = False
-
-    def batch_start(self):
-        self.ipbatcbuf = ''
-        self.ipbatch = True
-        self.ipbatch_pause = False
-
-    def add_to_batch(self, cmd):
-        self.ipbatchbuf += cmd + '\n'
-
-    def batch_pause(self):
-        self.ipbatch_pause = True
-
-    def batch_resume(self):
-        self.ipbatch_pause = False
-
-    def batch_commit(self):
-        if not self.ipbatchbuf:
-            self.ipbatchbuf = ''
-            self.ipbatch = False
-            self.ipbatch_pause = False
-            return
-        try:
-            utils.exec_command('ip -force -batch -', stdin=self.ipbatchbuf)
-        except:
-            raise
-        finally:
-            self.ipbatchbuf = ''
-            self.ipbatch = False
-            self.ipbatch_pause = False
-
-    def bridge_batch_commit(self):
-        if not self.ipbatchbuf:
-            self.ipbatchbuf = ''
-            self.ipbatch = False
-            self.ipbatch_pause = False
-            return
-        try:
-            utils.exec_command('bridge -force -batch -', stdin=self.ipbatchbuf)
-        except:
-            raise
-        finally:
-            self.ipbatchbuf = ''
-            self.ipbatch = False
-            self.ipbatch_pause = False
-
-    def addr_show(self, ifacename=None):
-        if ifacename:
-            if not self.link_exists(ifacename):
-                return
-            return utils.exec_commandl(['ip', '-o', 'addr', 'show', 'dev',
-                                        ifacename])
-        else:
-            return utils.exec_commandl(['ip', '-o', 'addr', 'show'])
-
-    def link_show(self, ifacename=None):
-        if ifacename:
-            return utils.exec_commandl(['ip', '-o', '-d', 'link', 'show', 'dev',
-                                        ifacename])
-        else:
-            return utils.exec_commandl(['ip', '-o', '-d', 'link', 'show'])
-
-    def addr_add(self, ifacename, address, broadcast=None,
-                    peer=None, scope=None, preferred_lifetime=None):
-        if not address:
-            return
-        cmd = 'addr add %s' %address
-        if broadcast:
-            cmd += ' broadcast %s' %broadcast
-        if peer:
-            cmd += ' peer %s' %peer
-        if scope:
-            cmd += ' scope %s' %scope
-        if preferred_lifetime:
-            cmd += ' preferred_lft %s' %preferred_lifetime
-        cmd += ' dev %s' %ifacename
-        if self.ipbatch and not self.ipbatch_pause:
-            self.add_to_batch(cmd)
-        else:
-            utils.exec_command('ip %s' % cmd)
-        self._cache_update([ifacename, 'addrs', address], {})
-
-    def addr_del(self, ifacename, address, broadcast=None,
-                    peer=None, scope=None):
-        """ Delete ipv4 address """
-        if not address:
-            return
-        if not self._cache_get('addr', [ifacename, 'addrs', address]):
-            return
-        cmd = 'addr del %s' %address
-        if broadcast:
-            cmd += 'broadcast %s' %broadcast
-        if peer:
-            cmd += 'peer %s' %peer
-        if scope:
-            cmd += 'scope %s' %scope
-        cmd += ' dev %s' %ifacename
-        utils.exec_command('ip %s' % cmd)
-        self._cache_delete([ifacename, 'addrs', address])
-
-    def addr_flush(self, ifacename):
-        cmd = 'addr flush dev %s' %ifacename
-        if self.ipbatch and not self.ipbatch_pause:
-            self.add_to_batch(cmd)
-        else:
-            utils.exec_command('ip %s' % cmd)
-        self._cache_delete([ifacename, 'addrs'])
-
-    def del_addr_all(self, ifacename, skip_addrs=[]):
-        if not skip_addrs: skip_addrs = []
-        runningaddrsdict = self.addr_get(ifacename)
-        try:
-            # XXX: ignore errors. Fix this to delete secondary addresses
-            # first
-            [self.addr_del(ifacename, a) for a in
-                set(runningaddrsdict.keys()).difference(skip_addrs)]
-        except:
-            # ignore errors
-            pass
-
-    def addr_get(self, ifacename, details=True, refresh=False):
-        addrs = self._cache_get('addr', [ifacename, 'addrs'],
-                                refresh=refresh)
-        if not addrs:
-            return None
-        if details:
-            return addrs
-        return addrs.keys()
-
-    def addr_add_multiple(self, ifacename, addrs, purge_existing=False):
-        # purges address
-        if purge_existing:
-            # if perfmode is not set and also if iface has no sibling
-            # objects, purge addresses that are not present in the new
-            # config
-            runningaddrs = self.addr_get(ifacename, details=False)
-            if addrs == runningaddrs:
-                return
-            try:
-                # if primary address is not same, there is no need to keep any.
-                # reset all addresses
-                if (addrs and runningaddrs and
-                        (addrs[0] != runningaddrs[0])):
-                    self.del_addr_all(ifacename)
-                else:
-                    self.del_addr_all(ifacename, addrs)
-            except Exception, e:
-                self.log_warn(str(e))
-        for a in addrs:
-            try:
-                self.addr_add(ifacename, a)
-            except Exception, e:
-                self.logger.error(str(e))
-
-    def _link_set_ifflag(self, ifacename, value):
-        # Dont look at the cache, the cache may have stale value
-        # because link status can be changed by external
-        # entity (One such entity is ifupdown main program)
-        cmd = 'link set dev %s %s' %(ifacename, value.lower())
-        if self.ipbatch:
-            self.add_to_batch(cmd)
-        else:
-            utils.exec_command('ip %s' % cmd)
-
-    def link_up(self, ifacename):
-        self._link_set_ifflag(ifacename, 'UP')
-
-    def link_down(self, ifacename):
-        self._link_set_ifflag(ifacename, 'DOWN')
-
-    def link_set(self, ifacename, key, value=None,
-                 force=False, type=None, state=None):
-        if not force:
-            if (key not in ['master', 'nomaster'] and
-                self._cache_check('link', [ifacename, key], value)):
-                return
-        cmd = 'link set dev %s' %ifacename
-        if type:
-            cmd += ' type %s' %type
-        cmd += ' %s' %key
-        if value:
-            cmd += ' %s' %value
-        if state:
-            cmd += ' %s' %state
-        if self.ipbatch:
-            self.add_to_batch(cmd)
-        else:
-            utils.exec_command('ip %s' % cmd)
-        if key not in ['master', 'nomaster']:
-            self._cache_update([ifacename, key], value)
-
-    def link_set_hwaddress(self, ifacename, hwaddress, force=False):
-        if not force:
-            if self._cache_check('link', [ifacename, 'hwaddress'], hwaddress):
-               return
-        self.link_down(ifacename)
-        cmd = 'link set dev %s address %s' %(ifacename, hwaddress)
-        if self.ipbatch:
-            self.add_to_batch(cmd)
-        else:
-            utils.exec_command('ip %s' % cmd)
-        self.link_up(ifacename)
-        self._cache_update([ifacename, 'hwaddress'], hwaddress)
-
-    def link_set_mtu(self, ifacename, mtu):
-        if ifupdownflags.flags.DRYRUN:
-            return True
-        if not mtu or not ifacename: return
-        with open('/sys/class/net/%s/mtu' % ifacename, 'w') as f:
-            f.write(mtu)
-        self._cache_update([ifacename, 'mtu'], mtu)
-
-    def link_set_alias(self, ifacename, alias):
-        if not alias:
-            utils.exec_user_command('echo "" > /sys/class/net/%s/ifalias'
-                                    % ifacename)
-        else:
-            self.write_file('/sys/class/net/%s/ifalias' % ifacename, alias)
-
-    def link_get_alias(self, ifacename):
-        return self.read_file_oneline('/sys/class/net/%s/ifalias'
-                    %ifacename)
-
-    def link_isloopback(self, ifacename):
-        flags = self._cache_get('link', [ifacename, 'flags'])
-        if not flags:
-            return
-        if 'LOOPBACK' in flags:
-            return True
-        return False
-
-    def link_get_status(self, ifacename):
-        return self._cache_get('link', [ifacename, 'ifflag'], refresh=True)
-
-    def route_add_gateway(self, ifacename, gateway, vrf=None, metric=None):
-        if not gateway:
-           return
-        if not vrf:
-            cmd = 'ip route add default via %s' %gateway
-        else:
-            cmd = 'ip route add table %s default via %s' %(vrf, gateway)
-        # Add metric
-        if metric:
-            cmd += 'metric %s' %metric
-        cmd += ' dev %s' %ifacename
-        utils.exec_command(cmd)
-
-    def route_del_gateway(self, ifacename, gateway, vrf=None, metric=None):
-        # delete default gw
-        if not gateway:
-            return
-        if not vrf:
-            cmd = 'ip route del default via %s' %gateway
-        else:
-            cmd = 'ip route del table %s default via %s' %(vrf, gateway)
-        if metric:
-            cmd += ' metric %s' %metric
-        cmd += ' dev %s' %ifacename
-        utils.exec_command(cmd)
-
-    def route6_add_gateway(self, ifacename, gateway):
-        if not gateway:
-            return
-        return utils.exec_command('ip -6 route add default via %s dev %s' %
-                                  (gateway, ifacename))
-
-    def route6_del_gateway(self, ifacename, gateway):
-        if not gateway:
-            return
-        return utils.exec_command('ip -6 route del default via %s dev %s' %
-                                  (gateway, ifacename))
-
-    def link_create_vlan(self, vlan_device_name, vlan_raw_device, vlanid):
-        if self.link_exists(vlan_device_name):
-            return
-        utils.exec_command('ip link add link %s name %s type vlan id %d' %
-                           (vlan_raw_device, vlan_device_name, vlanid))
-        self._cache_update([vlan_device_name], {})
-
-    def link_create_vlan_from_name(self, vlan_device_name):
-        v = vlan_device_name.split('.')
-        if len(v) != 2:
-            self.logger.warn('invalid vlan device name %s' %vlan_device_name)
-            return
-        self.link_create_vlan(vlan_device_name, v[0], v[1])
-
-    def link_create_macvlan(self, name, linkdev, mode='private'):
-        if self.link_exists(name):
-            return
-        cmd = ('link add link %s' %linkdev +
-                          ' name %s' %name +
-                          ' type macvlan mode %s' %mode)
-        if self.ipbatch and not self.ipbatch_pause:
-            self.add_to_batch(cmd)
-        else:
-            utils.exec_command('ip %s' % cmd)
-        self._cache_update([name], {})
-
-    def get_vxlan_peers(self, dev, svcnodeip):
-        cmd = 'bridge fdb show brport %s' % dev
-        cur_peers = []
-        try:
-            ps = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, close_fds=False)
-            utils.enable_subprocess_signal_forwarding(ps, signal.SIGINT)
-            output = subprocess.check_output(('grep', '00:00:00:00:00:00'), stdin=ps.stdout)
-            ps.wait()
-            utils.disable_subprocess_signal_forwarding(signal.SIGINT)
-            try:
-                ppat = re.compile('\s+dst\s+(\d+.\d+.\d+.\d+)\s+')
-                for l in output.split('\n'):
-                    m = ppat.search(l)
-                    if m and m.group(1) != svcnodeip:
-                        cur_peers.append(m.group(1))
-            except:
-                self.logger.warn('error parsing ip link output')
-                pass
-        except subprocess.CalledProcessError as e:
-            if e.returncode != 1:
-                self.logger.error(str(e))
-        finally:
-            utils.disable_subprocess_signal_forwarding(signal.SIGINT)
-
-        return cur_peers
-
-    def link_create_vxlan(self, name, vxlanid,
-                          localtunnelip=None,
-                          svcnodeip=None,
-                          remoteips=None,
-                          learning='on',
-                          ageing=None,
-                          anycastip=None):
-        if svcnodeip and remoteips:
-            raise Exception("svcnodeip and remoteip is mutually exclusive")
-        args = ''
-        if svcnodeip:
-            args += ' remote %s' %svcnodeip
-        if ageing:
-            args += ' ageing %s' %ageing
-        if learning == 'off':
-            args += ' nolearning'
-
-        if self.link_exists(name):
-            cmd = 'link set dev %s type vxlan dstport %d' %(name, VXLAN_UDP_PORT)
-            vxlanattrs = self.get_vxlandev_attrs(name)
-            # on ifreload do not overwrite anycast_ip to individual ip if clagd
-            # has modified
-            if vxlanattrs:
-                running_localtunnelip = vxlanattrs.get('local')
-                if anycastip and running_localtunnelip and anycastip == running_localtunnelip:
-                    localtunnelip = running_localtunnelip
-                running_svcnode = vxlanattrs.get('svcnode')
-                if running_svcnode and not svcnodeip:
-                    args += ' noremote'
-        else:
-            cmd = 'link add dev %s type vxlan id %s dstport %d' %(name, vxlanid, VXLAN_UDP_PORT)
-
-        if localtunnelip:
-            args += ' local %s' %localtunnelip
-        cmd += args
-
-        if self.ipbatch and not self.ipbatch_pause:
-            self.add_to_batch(cmd)
-        else:
-            utils.exec_command('ip %s' % cmd)
-
-        # XXX: update linkinfo correctly
-        self._cache_update([name], {})
-
-    def link_exists(self, ifacename):
-        if ifupdownflags.flags.DRYRUN:
-            return True
-        return os.path.exists('/sys/class/net/%s' %ifacename)
-
-    def link_get_ifindex(self, ifacename):
-        if ifupdownflags.flags.DRYRUN:
-            return True
-        return self.read_file_oneline('/sys/class/net/%s/ifindex' %ifacename)
-
-    def is_vlan_device_by_name(self, ifacename):
-        if re.search(r'\.', ifacename):
-            return True
-        return False
-
-    def route_add(self, route):
-        utils.exec_command('ip route add %s' % route)
-
-    def route6_add(self, route):
-        utils.exec_command('ip -6 route add %s' % route)
-
-    def get_vlandev_attrs(self, ifacename):
-        return (self._cache_get('link', [ifacename, 'link']),
-                self._cache_get('link', [ifacename, 'linkinfo', 'vlanid']))
-
-    def get_vxlandev_attrs(self, ifacename):
-        return self._cache_get('link', [ifacename, 'linkinfo'])
-
-    def get_vxlandev_learning(self, ifacename):
-        return self._cache_get('link', [ifacename, 'linkinfo', 'learning'])
-
-    def set_vxlandev_learning(self, ifacename, learn):
-        if learn == 'on':
-            utils.exec_command('ip link set dev %s type vxlan learning' %ifacename)
-            self._cache_update([ifacename, 'linkinfo', 'learning'], 'on')
-        else:
-            utils.exec_command('ip link set dev %s type vxlan nolearning' %ifacename)
-            self._cache_update([ifacename, 'linkinfo', 'learning'], 'off')
-
-    def link_get_linkinfo_attrs(self, ifacename):
-        return self._cache_get('link', [ifacename, 'linkinfo'])
-
-    def link_get_mtu(self, ifacename, refresh=False):
-        return self._cache_get('link', [ifacename, 'mtu'], refresh=refresh)
-
-    def link_get_mtu_sysfs(self, ifacename):
-        return self.read_file_oneline('/sys/class/net/%s/mtu'
-                                      %ifacename)
-
-    def link_get_kind(self, ifacename):
-        return self._cache_get('link', [ifacename, 'kind'])
-
-    def link_get_slave_kind(self, ifacename):
-        return self._cache_get('link', [ifacename, 'slave_kind'])
-
-    def link_get_hwaddress(self, ifacename):
-        address = self._cache_get('link', [ifacename, 'hwaddress'])
-        # newly created logical interface addresses dont end up in the cache
-        # read hwaddress from sysfs file for these interfaces
-        if not address:
-            address = self.read_file_oneline('/sys/class/net/%s/address'
-                                             %ifacename)
-        return address
-
-    def link_create(self, ifacename, type, attrs={}):
-        """ generic link_create function """
-        if self.link_exists(ifacename):
-            return
-        cmd = 'link add'
-        cmd += ' name %s type %s' %(ifacename, type)
-        if attrs:
-            for k, v in attrs.iteritems():
-                cmd += ' %s' %k
-                if v:
-                    cmd += ' %s' %v
-        if self.ipbatch and not self.ipbatch_pause:
-            self.add_to_batch(cmd)
-        else:
-            utils.exec_command('ip %s' % cmd)
-        self._cache_update([ifacename], {})
-
-    def link_delete(self, ifacename):
-        if not self.link_exists(ifacename):
-            return
-        cmd = 'link del %s' %ifacename
-        if self.ipbatch and not self.ipbatch_pause:
-            self.add_to_batch(cmd)
-        else:
-            utils.exec_command('ip %s' % cmd)
-        self._cache_invalidate()
-
-    def link_get_master(self, ifacename):
-        sysfs_master_path = '/sys/class/net/%s/master' %ifacename
-        if os.path.exists(sysfs_master_path):
-            link_path = os.readlink(sysfs_master_path)
-            if link_path:
-                return os.path.basename(link_path)
-            else:
-                return None
-        else:
-            return self._cache_get('link', [ifacename, 'master'])
-
-    def tunnel_create(self, tunnelname, mode, attrs={}):
-        """ generic link_create function """
-        if self.link_exists(tunnelname):
-            return
-
-        cmd = ''
-        if '6' in mode:
-            cmd = ' -6 '
-
-        if mode in ['gretap']:
-            cmd += 'link add %s type %s' % (tunnelname, mode)
-        else:
-            cmd += 'tunnel add %s mode %s' % (tunnelname, mode)
-
-        if attrs:
-            for k, v in attrs.iteritems():
-                cmd += ' %s' % k
-                if v:
-                    cmd += ' %s' % v
-        if self.ipbatch and not self.ipbatch_pause:
-            self.add_to_batch(cmd)
-        else:
-            utils.exec_command('ip %s' % cmd)
-        self._cache_update([tunnelname], {})
-
-    def bridge_port_vids_add(self, bridgeportname, vids):
-        [utils.exec_command('bridge vlan add vid %s dev %s' %
-                            (v, bridgeportname)) for v in vids]
-
-    def bridge_port_vids_del(self, bridgeportname, vids):
-        if not vids:
-            return
-        [utils.exec_command('bridge vlan del vid %s dev %s' %
-                            (v, bridgeportname)) for v in vids]
-
-    def bridge_port_vids_flush(self, bridgeportname, vid):
-        utils.exec_command('bridge vlan del vid %s dev %s' %
-                           (vid, bridgeportname))
-
-    def bridge_port_vids_get(self, bridgeportname):
-        utils.exec_command('/sbin/bridge vlan show %s' % bridgeportname)
-        bridgeout = utils.exec_command('/sbin/bridge vlan show dev %s' %
-                                       bridgeportname)
-        if not bridgeout: return []
-        brvlanlines = bridgeout.readlines()[2:]
-        vids = [l.strip() for l in brvlanlines]
-        return [v for v in vids if v]
-
-    def bridge_port_vids_get_all(self):
-        brvlaninfo = {}
-        bridgeout = utils.exec_command('/sbin/bridge -c vlan show')
-        if not bridgeout: return brvlaninfo
-        brvlanlines = bridgeout.splitlines()
-        brportname=None
-        for l in brvlanlines[1:]:
-            if l and not l.startswith(' ') and not l.startswith('\t'):
-                attrs = l.split()
-                brportname = attrs[0].strip()
-                brvlaninfo[brportname] = {'pvid' : None, 'vlan' : []}
-                l = ' '.join(attrs[1:])
-            if not brportname or not l:
-                continue
-            l = l.strip()
-            if 'PVID' in l:
-                brvlaninfo[brportname]['pvid'] = l.split()[0]
-            elif 'Egress Untagged' not in l:
-                brvlaninfo[brportname]['vlan'].append(l)
-        return brvlaninfo
-
-    def bridge_port_vids_get_all_json(self):
-        if not self.supported_command['/sbin/bridge -c -json vlan show']:
-            return {}
-        brvlaninfo = {}
-        try:
-            bridgeout = utils.exec_command('/sbin/bridge -c -json vlan show')
-        except:
-            self.supported_command['/sbin/bridge -c -json vlan show'] = False
-            self.logger.info('/sbin/bridge -c -json vlan show: skipping unsupported command')
-            return {}
-        if not bridgeout: return brvlaninfo
-        try:
-            vlan_json_dict = json.loads(bridgeout, encoding="utf-8")
-        except Exception, e:
-            self.logger.info('json loads failed with (%s)' %str(e))
-            return {}
-        return vlan_json_dict
-
-    def bridge_port_pvid_add(self, bridgeportname, pvid):
-        if self.ipbatch and not self.ipbatch_pause:
-            self.add_to_batch('vlan add vid %s untagged pvid dev %s' %
-                              (pvid, bridgeportname))
-        else:
-            utils.exec_command('bridge vlan add vid %s untagged pvid dev %s' %
-                               (pvid, bridgeportname))
-
-    def bridge_port_pvid_del(self, bridgeportname, pvid):
-        if self.ipbatch and not self.ipbatch_pause:
-            self.add_to_batch('vlan del vid %s untagged pvid dev %s' %
-                              (pvid, bridgeportname))
-        else:
-            utils.exec_command('bridge vlan del vid %s untagged pvid dev %s' %
-                               (pvid, bridgeportname))
-
-    def bridge_port_pvids_get(self, bridgeportname):
-        return self.read_file_oneline('/sys/class/net/%s/brport/pvid'
-                                      %bridgeportname)
-
-    def bridge_vids_add(self, bridgeportname, vids, bridge=True):
-        target = 'self' if bridge else ''
-        if self.ipbatch and not self.ipbatch_pause:
-            [self.add_to_batch('vlan add vid %s dev %s %s' %
-                               (v, bridgeportname, target)) for v in vids]
-        else:
-            [utils.exec_command('bridge vlan add vid %s dev %s %s' %
-                                (v, bridgeportname, target)) for v in vids]
-
-    def bridge_vids_del(self, bridgeportname, vids, bridge=True):
-        target = 'self' if bridge else ''
-        if self.ipbatch and not self.ipbatch_pause:
-            [self.add_to_batch('vlan del vid %s dev %s %s' %
-                               (v, bridgeportname, target)) for v in vids]
-        else:
-            [utils.exec_command('bridge vlan del vid %s dev %s %s' %
-                                (v, bridgeportname, target)) for v in vids]
-
-    def bridge_fdb_add(self, dev, address, vlan=None, bridge=True, remote=None):
-        target = 'self' if bridge else ''
-        vlan_str = ''
-        if vlan:
-            vlan_str = 'vlan %s ' % vlan
-
-        dst_str = ''
-        if remote:
-            dst_str = 'dst %s ' % remote
-
-        utils.exec_command('bridge fdb replace %s dev %s %s %s %s' %
-                           (address, dev, vlan_str, target, dst_str))
-
-    def bridge_fdb_append(self, dev, address, vlan=None, bridge=True, remote=None):
-        target = 'self' if bridge else ''
-        vlan_str = ''
-        if vlan:
-            vlan_str = 'vlan %s ' % vlan
-
-        dst_str = ''
-        if remote:
-            dst_str = 'dst %s ' % remote
-
-        utils.exec_command('bridge fdb append %s dev %s %s %s %s' %
-                           (address, dev, vlan_str, target, dst_str))
-
-    def bridge_fdb_del(self, dev, address, vlan=None, bridge=True, remote=None):
-        target = 'self' if bridge else ''
-        vlan_str = ''
-        if vlan:
-            vlan_str = 'vlan %s ' % vlan
-
-        dst_str = ''
-        if remote:
-            dst_str = 'dst %s ' % remote
-        utils.exec_command('bridge fdb del %s dev %s %s %s %s' %
-                           (address, dev, vlan_str, target, dst_str))
-
-    def bridge_is_vlan_aware(self, bridgename):
-        filename = '/sys/class/net/%s/bridge/vlan_filtering' %bridgename
-        if os.path.exists(filename) and self.read_file_oneline(filename) == '1':
-            return True
-        return False
-
-    def bridge_port_get_bridge_name(self, bridgeport):
-        filename = '/sys/class/net/%s/brport/bridge' %bridgeport
-        try:
-            return os.path.basename(os.readlink(filename))
-        except:
-            return None
-
-    def bridge_port_exists(self, bridge, bridgeportname):
-        try:
-            return os.path.exists('/sys/class/net/%s/brif/%s'
-                                  %(bridge, bridgeportname))
-        except Exception:
-            return False
-
-    def bridge_fdb_show_dev(self, dev):
-        try:
-            fdbs = {}
-            output = utils.exec_command('bridge fdb show dev %s' % dev)
-            if output:
-                for fdb_entry in output.splitlines():
-                    try:
-                        entries = fdb_entry.split()
-                        fdbs.setdefault(entries[2], []).append(entries[0])
-                    except:
-                        self.logger.debug('%s: invalid fdb line \'%s\''
-                                %(dev, fdb_entry))
-                        pass
-            return fdbs
-        except Exception:
-            return None
-
-    def is_bridge(self, bridge):
-        return os.path.exists('/sys/class/net/%s/bridge' %bridge)
-
-    def is_link_up(self, ifacename):
-        ret = False
-        try:
-            flags = self.read_file_oneline('/sys/class/net/%s/flags' %ifacename)
-            iflags = int(flags, 16)
-            if (iflags & 0x0001):
-                ret = True
-        except:
-            ret = False
-            pass
-        return ret
-
-    def ip_route_get_dev(self, prefix):
-        try:
-            output = utils.exec_command('ip route get %s' % prefix)
-            if output:
-               rline = output.splitlines()[0]
-               if rline:
-                    rattrs = rline.split()
-                    return rattrs[rattrs.index('dev') + 1]
-        except Exception, e:
-            self.logger.debug('ip_route_get_dev: failed .. %s' %str(e))
-            pass
-        return None
-
-    def link_get_lowers(self, ifacename):
-        try:
-            lowers = glob.glob("/sys/class/net/%s/lower_*" %ifacename)
-            if not lowers:
-                return []
-            return [os.path.basename(l)[6:] for l in lowers]
-        except:
-            return []
-
-    def link_get_uppers(self, ifacename):
-        try:
-            uppers = glob.glob("/sys/class/net/%s/upper_*" %ifacename)
-            if not uppers:
-                return None
-            return [ os.path.basename(u)[6:] for u in uppers ]
-        except:
-            return None
-
-    def link_get_vrfs(self):
-        self._fill_cache()
-        return linkCache.vrfs
-
-    def get_brport_learning(self, ifacename):
-        learn = self.read_file_oneline('/sys/class/net/%s/brport/learning'
-                                       %ifacename)
-        if learn and learn == '1':
-            return 'on'
-        else:
-            return 'off'
-
-    def set_brport_learning(self, ifacename, learn):
-        if learn == 'off':
-            return self.write_file('/sys/class/net/%s/brport/learning'
-                                    %ifacename, '0')
-        else:
-            return self.write_file('/sys/class/net/%s/brport/learning'
-                                    %ifacename, '1')
diff --git a/ifupdownaddons/modulebase.py b/ifupdownaddons/modulebase.py
deleted file mode 100644 (file)
index bcbb865..0000000
+++ /dev/null
@@ -1,379 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-
-import os
-import re
-import io
-import logging
-import traceback
-
-from ifupdown.utils import utils
-from ifupdown.iface import *
-import ifupdown.policymanager as policymanager
-import ifupdown.ifupdownflags as ifupdownflags
-
-class NotSupported(Exception):
-    pass
-
-class moduleBase(object):
-    """ Base class for ifupdown addon modules
-
-    Provides common infrastructure methods for all addon modules """
-
-    def __init__(self, *args, **kargs):
-        modulename = self.__class__.__name__
-        self.logger = logging.getLogger('ifupdown.' + modulename)
-
-        # vrfs are a global concept and a vrf context can be applicable
-        # to all global vrf commands. Get the default vrf-exec-cmd-prefix
-        # here so that all modules can use it
-        self.vrf_exec_cmd_prefix = policymanager.policymanager_api.get_module_globals('vrf', attr='vrf-exec-cmd-prefix')
-
-        # explanations are shown in parse_glob
-        self.glob_regexs = [re.compile(r"([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\]([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\](.*)"),
-                            re.compile(r"([A-Za-z0-9\-]+[A-Za-z])(\d+)\-(\d+)(.*)"),
-                            re.compile(r"([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\](.*)")]
-
-        self._bridge_stp_user_space = None
-
-
-    def log_warn(self, str, ifaceobj=None):
-        """ log a warning if err str is not one of which we should ignore """
-        if not self.ignore_error(str) and not ifupdownflags.flags.IGNORE_ERRORS:
-            if self.logger.getEffectiveLevel() == logging.DEBUG:
-                traceback.print_stack()
-            self.logger.warn(str)
-            if ifaceobj:
-                ifaceobj.set_status(ifaceStatus.WARNING)
-        pass
-
-    def log_error(self, str, ifaceobj=None, raise_error=True):
-        """ log an err if err str is not one of which we should ignore and raise an exception """
-        if not self.ignore_error(str) and not ifupdownflags.flags.IGNORE_ERRORS:
-            if self.logger.getEffectiveLevel() == logging.DEBUG:
-                traceback.print_stack()
-            if raise_error:
-                if ifaceobj:
-                    ifaceobj.set_status(ifaceStatus.ERROR)
-                raise Exception(str)
-            else:
-                self.logger.error(str)
-        else:
-            pass
-
-    def is_process_running(self, procName):
-        try:
-            utils.exec_command('/bin/pidof -x %s' % procName)
-        except:
-            return False
-        else:
-            return True
-
-    def get_ifaces_from_proc(self):
-        ifacenames = []
-        with open('/proc/net/dev') as f:
-            try:
-                lines = f.readlines()
-                for line in lines[2:]:
-                    ifacenames.append(line.split()[0].strip(': '))
-            except:
-                raise
-        return ifacenames
-
-    def parse_regex(self, ifacename, expr, ifacenames=None):
-        try:
-            proc_ifacenames = self.get_ifaces_from_proc()
-        except:
-            self.logger.warn('%s: error reading ifaces from proc' %ifacename)
-
-        for proc_ifacename in proc_ifacenames:
-            try:
-                if re.search(expr + '$', proc_ifacename):
-                    yield proc_ifacename
-            except Exception, e:
-                raise Exception('%s: error searching regex \'%s\' in %s (%s)'
-                                %(ifacename, expr, proc_ifacename, str(e)))
-        if not ifacenames:
-            return
-        for ifacename in ifacenames:
-            try:
-                if re.search(expr + '$', ifacename):
-                    yield ifacename
-            except Exception, e:
-                raise Exception('%s: error searching regex \'%s\' in %s (%s)'
-                                %(ifacename, expr, ifacename, str(e)))
-
-    def ifname_is_glob(self, ifname):
-        """
-        Used by iface where ifname could be swp7 or swp[1-10].300
-        """
-        if (self.glob_regexs[0].match(ifname) or
-            self.glob_regexs[1].match(ifname) or
-            self.glob_regexs[2].match(ifname)):
-            return True
-        return False
-
-    def parse_glob(self, ifacename, expr):
-        errmsg = ('error parsing glob expression \'%s\'' %expr +
-                    ' (supported glob syntax: swp1-10.300 or swp[1-10].300' +
-                    '  or swp[1-10]sub[0-4].300')
-        regexs = self.glob_regexs
-
-        if regexs[0].match(expr):
-            # the first regex checks for exactly two levels of ranges defined only with square brackets
-            # (e.g. swpxyz[10-23]subqwe[0-4].100) to handle naming with two levels of port names.
-            m = regexs[0].match(expr)
-            mlist = m.groups()
-            if len(mlist) < 7:
-                # we have problems and should not continue
-                raise Exception('%s: error: unhandled glob expression %s\n%s' % (ifacename, expr,errmsg))
-
-            prefix = mlist[0]
-            suffix = mlist[6]
-            start_index = int(mlist[1])
-            end_index = int(mlist[2])
-            sub_string = mlist[3]
-            start_sub = int(mlist[4])
-            end_sub = int(mlist[5])
-            for i in range(start_index, end_index + 1):
-                for j in range(start_sub, end_sub + 1):
-                    yield prefix + '%d%s%d' % (i,sub_string,j) + suffix
-
-        elif regexs[1].match(expr) or regexs[2].match(expr):
-            # the second regex for 1 level with a range (e.g. swp10-14.100
-            # the third regex checks for 1 level with [] (e.g. swp[10-14].100)
-            start_index = 0
-            end_index = 0
-            if regexs[1].match(expr):
-                m = regexs[1].match(expr)
-            else:
-                m = regexs[2].match(expr)
-            mlist = m.groups()
-            if len(mlist) != 4:
-                raise Exception('%s: ' %ifacename + errmsg + '(unexpected len)')
-            prefix = mlist[0]
-            suffix = mlist[3]
-            start_index = int(mlist[1])
-            end_index = int(mlist[2])
-            for i in range(start_index, end_index + 1):
-                yield prefix + '%d' %i + suffix
-
-        else:
-            # Could not match anything.
-            self.logger.warn('%s: %s' %(ifacename, errmsg))
-            yield expr
-
-    def parse_port_list(self, ifacename, port_expr, ifacenames=None):
-        """ parse port list containing glob and regex
-
-        Args:
-            port_expr (str): expression
-            ifacenames (list): list of interface names. This needs to be specified if the expression has a regular expression
-        """
-        regex = 0
-        glob = 0
-        portlist = []
-
-        if not port_expr:
-            return None
-        exprs = re.split(r'[\s\t]\s*', port_expr)
-        self.logger.debug('%s: evaluating port expr \'%s\''
-                         %(ifacename, str(exprs)))
-        for expr in exprs:
-            if expr == 'noregex':
-                regex = 0
-            elif expr == 'noglob':
-                glob = 0
-            elif expr == 'regex':
-                regex = 1
-            elif expr == 'glob':
-                glob = 1
-            elif regex:
-                for port in self.parse_regex(ifacename, expr, ifacenames):
-                    if port not in portlist:
-                        portlist.append(port)
-                regex = 0
-            elif glob:
-                for port in self.parse_glob(ifacename, expr):
-                    portlist.append(port)
-                glob = 0
-            else:
-                portlist.append(expr)
-        if not portlist:
-            return None
-        return portlist
-
-    def ignore_error(self, errmsg):
-        if (ifupdownflags.flags.FORCE or re.search(r'exists', errmsg,
-            re.IGNORECASE | re.MULTILINE)):
-            return True
-        return False
-
-    def write_file(self, filename, strexpr):
-        """ writes string to a file """
-        try:
-            self.logger.info('writing \'%s\'' %strexpr +
-                ' to file %s' %filename)
-            if ifupdownflags.flags.DRYRUN:
-                return 0
-            with open(filename, 'w') as f:
-                f.write(strexpr)
-        except IOError, e:
-            self.logger.warn('error writing to file %s'
-                %filename + '(' + str(e) + ')')
-            return -1
-        return 0
-
-    def read_file(self, filename):
-        """ read file and return lines from the file """
-        try:
-            self.logger.info('reading \'%s\'' %filename)
-            with open(filename, 'r') as f:
-                return f.readlines()
-        except:
-            return None
-        return None
-
-    def read_file_oneline(self, filename):
-        """ reads and returns first line from the file """
-        try:
-            self.logger.info('reading \'%s\'' %filename)
-            with open(filename, 'r') as f:
-                return f.readline().strip('\n')
-        except:
-            return None
-        return None
-
-    def sysctl_set(self, variable, value):
-        """ set sysctl variable to value passed as argument """
-        utils.exec_command('sysctl %s=%s' % (variable, value))
-
-    def sysctl_get(self, variable):
-        """ get value of sysctl variable """
-        output = utils.exec_command('sysctl %s' % variable)
-        split = output.split('=')
-        if len(split) > 1:
-            return split[1].strip()
-        return None
-
-    def systcl_get_net_bridge_stp_user_space(self):
-        if self._bridge_stp_user_space:
-            return self._bridge_stp_user_space
-        self._bridge_stp_user_space = self.sysctl_get('net.bridge.bridge-stp-user-space')
-        return self._bridge_stp_user_space
-
-    def set_iface_attr(self, ifaceobj, attr_name, attr_valsetfunc,
-                       prehook=None, prehookargs=None):
-        ifacename = ifaceobj.name
-        attrvalue = ifaceobj.get_attr_value_first(attr_name)
-        if attrvalue:
-            if prehook:
-                if prehookargs:
-                    prehook(prehookargs)
-                else:
-                    prehook(ifacename)
-            attr_valsetfunc(ifacename, attrvalue)
-
-    def query_n_update_ifaceobjcurr_attr(self, ifaceobj, ifaceobjcurr,
-                                       attr_name, attr_valgetfunc,
-                                       attr_valgetextraarg=None):
-        attrvalue = ifaceobj.get_attr_value_first(attr_name)
-        if not attrvalue:
-            return
-        if attr_valgetextraarg:
-            runningattrvalue = attr_valgetfunc(ifaceobj.name,
-                                             attr_valgetextraarg)
-        else:
-            runningattrvalue = attr_valgetfunc(ifaceobj.name)
-        if (not runningattrvalue or
-            (runningattrvalue != attrvalue)):
-            ifaceobjcurr.update_config_with_status(attr_name,
-                runningattrvalue, 1)
-        else:
-            ifaceobjcurr.update_config_with_status(attr_name,
-                runningattrvalue, 0)
-
-    def dict_key_subset(self, a, b): 
-        """ returns a list of differing keys """
-        return [x for x in a if x in b]
-
-    def get_mod_attrs(self):
-        """ returns list of all module attrs defined in the module _modinfo
-            dict
-        """
-        try:
-            retattrs = []
-            attrsdict = self._modinfo.get('attrs')
-            for attrname, attrvals in attrsdict.iteritems():
-                if not attrvals or attrvals.get('deprecated'):
-                    continue
-                retattrs.append(attrname)
-                if 'aliases' in attrvals:
-                    retattrs.extend(attrvals['aliases'])
-            return retattrs
-        except:
-            return None
-
-    def get_mod_attr(self, attrname):
-        """ returns module attr info """
-        try:
-            return self._modinfo.get('attrs', {}).get(attrname)
-        except:
-            return None
-
-    def get_mod_subattr(self, attrname, subattrname):
-        """ returns module attrs defined in the module _modinfo dict"""
-        try:
-            return reduce(lambda d, k: d[k], ['attrs', attrname, subattrname],
-                         self._modinfo)
-        except:
-            return None
-
-    def get_modinfo(self):
-        """ return module info """
-        try:
-            return self._modinfo
-        except:
-            return None
-
-    def get_overrides_ifupdown_scripts(self):
-        """ return the ifupdown scripts replaced by the current module """
-        try:
-            return self.overrides_ifupdown_scripts
-        except:
-            return []
-
-    def _get_reserved_vlan_range(self):
-        start = end = 0
-        get_resvvlan = '/var/lib/ifupdown2/hooks/get_reserved_vlan_range.sh'
-        if not os.path.exists(get_resvvlan):
-            return (start, end)
-        try:
-            (s, e) = utils.exec_command(get_resvvlan).strip('\n').split('-')
-            start = int(s)
-            end = int(e)
-        except Exception, e:
-            self.logger.debug('%s failed (%s)' %(get_resvvlan, str(e)))
-            # ignore errors
-            pass
-        return (start, end)
-
-    def _handle_reserved_vlan(self, vlanid, logprefix=''):
-        """ Helper function to check and warn if the vlanid falls in the
-        reserved vlan range """
-        if vlanid in range(self._resv_vlan_range[0],
-                           self._resv_vlan_range[1]):
-           self.logger.error('%s: reserved vlan %d being used'
-                   %(logprefix, vlanid) + ' (reserved vlan range %d-%d)'
-                   %(self._resv_vlan_range[0], self._resv_vlan_range[1]))
-           return True
-        return False
-
-    def _valid_ethaddr(self, ethaddr):
-        """ Check if address is 00:00:00:00:00:00 """
-        if not ethaddr or re.match('00:00:00:00:00:00', ethaddr):
-            return False
-        return True
diff --git a/ifupdownaddons/mstpctlutil.py b/ifupdownaddons/mstpctlutil.py
deleted file mode 100644 (file)
index 1452bd4..0000000
+++ /dev/null
@@ -1,232 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-
-from cache import MSTPAttrsCache
-from utilsbase import *
-from ifupdown.iface import *
-from ifupdown.utils import utils
-from cache import *
-import json
-
-class mstpctlutil(utilsBase):
-    """ This class contains helper methods to interact with mstpd using
-    mstputils commands """
-
-    _DEFAULT_PORT_PRIO = '128'
-
-    _cache_fill_done = False
-
-    _bridgeattrmap = {'bridgeid' : 'bridge-id',
-                     'maxage' : 'max-age',
-                     'fdelay' : 'forward-delay',
-                     'txholdcount' : 'tx-hold-count',
-                     'maxhops' : 'max-hops',
-                     'ageing' : 'ageing-time',
-                     'hello' : 'hello-time',
-                     'forcevers' : 'force-protocol-version'}
-
-    _bridge_jsonAttr_map = {
-                            'treeprio': 'bridgeId',
-                            'maxage': 'maxAge',
-                            'fdelay': 'fwdDelay',
-                            'txholdcount': 'txHoldCounter',
-                            'maxhops': 'maxHops',
-                            'ageing': 'ageingTime',
-                            'hello': 'helloTime',
-                            'forcevers': 'forceProtocolVersion',
-                            }
-
-    _bridgeportattrmap = {'portadminedge' : 'admin-edge-port',
-                     'portp2p' : 'admin-point-to-point',
-                     'portrestrrole' : 'restricted-role',
-                     'portrestrtcn' : 'restricted-TCN',
-                     'bpduguard' : 'bpdu-guard-port',
-                     'portautoedge' : 'auto-edge-port',
-                     'portnetwork' : 'network-port',
-                     'portbpdufilter' : 'bpdufilter-port',
-                     'portpathcost' : 'external-port-cost',
-                     'treeportcost' : 'internal-port-cost'}
-
-    def __init__(self, *args, **kargs):
-        utilsBase.__init__(self, *args, **kargs)
-
-    def is_mstpd_running(self):
-        try:
-            utils.exec_command('/bin/pidof mstpd')
-        except:
-            return False
-        else:
-            return True
-
-    def _extract_bridge_port_prio(self, portid):
-        try:
-            return str(int(portid[0], 16) * 16)
-        except:
-            return mstpctlutil._DEFAULT_PORT_PRIO
-
-    def _get_bridge_and_port_attrs_from_cache(self, bridgename):
-        attrs = MSTPAttrsCache.get(bridgename)
-        if attrs:
-            return attrs
-        mstpctl_bridgeport_attrs_dict = {}
-        try:
-            cmd = ['/sbin/mstpctl', 'showportdetail', bridgename, 'json']
-            output = utils.exec_commandl(cmd)
-            if not output:
-                return mstpctl_bridgeport_attrs_dict
-        except Exception as e:
-            self.logger.info(str(e))
-            return mstpctl_bridgeport_attrs_dict
-        try:
-            mstpctl_bridge_cache = json.loads(output.strip('\n'))
-            for portname in mstpctl_bridge_cache.keys():
-                for portid in mstpctl_bridge_cache[portname].keys():
-                    mstpctl_bridgeport_attrs_dict[portname] = {}
-                    mstpctl_bridgeport_attrs_dict[portname]['treeportprio'] = self._extract_bridge_port_prio(portid)
-                    for jsonAttr in mstpctl_bridge_cache[portname][portid].keys():
-                        jsonVal = mstpctl_bridge_cache[portname][portid][jsonAttr]
-                        mstpctl_bridgeport_attrs_dict[portname][jsonAttr] = str(jsonVal)
-            MSTPAttrsCache.set(bridgename, mstpctl_bridgeport_attrs_dict)
-        except Exception as e:
-            self.logger.info('%s: cannot fetch mstpctl bridge port attributes: %s' % str(e))
-
-        mstpctl_bridge_attrs_dict = {}
-        try:
-            cmd = ['/sbin/mstpctl', 'showbridge', 'json', bridgename]
-            output = utils.exec_commandl(cmd)
-            if not output:
-                return mstpctl_bridge_attrs_dict
-        except Exception as e:
-            self.logger.info(str(e))
-            return mstpctl_bridge_attrs_dict
-        try:
-            mstpctl_bridge_cache = json.loads(output.strip('\n'))
-            for jsonAttr in mstpctl_bridge_cache[bridgename].keys():
-                mstpctl_bridge_attrs_dict[jsonAttr] = (
-                    str(mstpctl_bridge_cache[bridgename][jsonAttr]))
-            mstpctl_bridge_attrs_dict['treeprio'] = '%d' %(
-                                   int(mstpctl_bridge_attrs_dict.get('bridgeId',
-                                   '').split('.')[0], base=16) * 4096)
-            del mstpctl_bridge_attrs_dict['bridgeId']
-            MSTPAttrsCache.bridges[bridgename].update(mstpctl_bridge_attrs_dict)
-        except Exception as e:
-            self.logger.info('%s: cannot fetch mstpctl bridge attributes: %s' % str(e))
-        return MSTPAttrsCache.get(bridgename)
-
-    def get_bridge_ports_attrs(self, bridgename):
-        return self._get_bridge_and_port_attrs_from_cache(bridgename)
-
-    def get_bridge_port_attr(self, bridgename, portname, attrname):
-        attrs = self._get_bridge_and_port_attrs_from_cache(bridgename)
-        value = attrs.get(portname, {}).get(attrname, 'no')
-        if value == 'True' or value == 'true':
-            return 'yes'
-        return str(value)
-
-    def update_bridge_port_cache(self, bridgename, portname, attrname, value):
-        attrs = self.get_bridge_ports_attrs(bridgename)
-        if not attrs:
-            attrs = {}
-        if not portname in attrs:
-            attrs[portname] = {}
-        attrs[portname][attrname] = value
-        MSTPAttrsCache.set(bridgename, attrs)
-
-    def update_bridge_cache(self, bridgename, attrname, value):
-        attrs = self.get_bridge_ports_attrs(bridgename)
-        if not attrs:
-            attrs = {}
-        attrs[attrname] = value
-        MSTPAttrsCache.set(bridgename, attrs)
-
-    def set_bridge_port_attr(self, bridgename, portname, attrname, value, json_attr=None):
-        cache_value = self.get_bridge_port_attr(bridgename, portname, json_attr)
-        if cache_value and cache_value == value:
-            return
-        if attrname == 'treeportcost' or attrname == 'treeportprio':
-            utils.exec_commandl(['/sbin/mstpctl', 'set%s' % attrname,
-                                 bridgename, portname, '0', value])
-        else:
-            utils.exec_commandl(['/sbin/mstpctl', 'set%s' % attrname,
-                                 bridgename, portname, value])
-        if json_attr:
-            self.update_bridge_port_cache(bridgename, portname, json_attr, value)
-
-    def get_bridge_attrs(self, bridgename):
-        bridgeattrs = {}
-        try:
-            bridgeattrs = dict((k, self.get_bridge_attr(bridgename, v))
-                                 for k,v in self._bridge_jsonAttr_map.items())
-        except Exception, e:
-            self.logger.debug(bridgeattrs)
-            self.logger.debug(str(e))
-            pass
-        return bridgeattrs
-
-    def get_bridge_attr(self, bridgename, attrname):
-        if attrname == 'bridgeId':
-            attrname = 'treeprio'
-        return self._get_bridge_and_port_attrs_from_cache(bridgename).get(attrname)
-
-    def set_bridge_attr(self, bridgename, attrname, attrvalue, check=True):
-
-        if check:
-            if attrname == 'treeprio':
-                attrvalue_curr = self.get_bridge_attr(bridgename, attrname)
-            else:
-                attrvalue_curr = self.get_bridge_attr(bridgename,
-                                        self._bridge_jsonAttr_map[attrname])
-            if attrvalue_curr and attrvalue_curr == attrvalue:
-                return
-        if attrname == 'treeprio':
-            utils.exec_commandl(['/sbin/mstpctl', 'set%s' % attrname,
-                                 '%s' % bridgename, '0', '%s' % attrvalue],
-                                stdout=False, stderr=None)
-            self.update_bridge_cache(bridgename, attrname, str(attrvalue))
-        else:
-            utils.exec_commandl(['/sbin/mstpctl', 'set%s' % attrname,
-                                 '%s' % bridgename, '%s' % attrvalue],
-                                stdout=False, stderr=None)
-            self.update_bridge_cache(bridgename,
-                                     self._bridge_jsonAttr_map[attrname],
-                                     str(attrvalue))
-
-    def set_bridge_attrs(self, bridgename, attrdict, check=True):
-        for k, v in attrdict.iteritems():
-            if not v:
-                continue
-            try:
-                self.set_bridge_attr(bridgename, k, v, check)
-            except Exception, e:
-                self.logger.warn('%s: %s' %(bridgename, str(e)))
-
-    def get_bridge_treeprio(self, bridgename):
-        return self.get_bridge_attr(bridgename, 'treeprio')
-
-    def set_bridge_treeprio(self, bridgename, attrvalue, check=True):
-        if check:
-            attrvalue_curr = self.get_bridge_treeprio(bridgename)
-            if attrvalue_curr and attrvalue_curr == attrvalue:
-                return
-        utils.exec_commandl(['/sbin/mstpctl', 'settreeprio', bridgename, '0',
-                             str(attrvalue)])
-        self.update_bridge_cache(bridgename, 'treeprio', str(attrvalue))
-
-    def showbridge(self, bridgename=None):
-        if bridgename:
-            return utils.exec_command('/sbin/mstpctl showbridge %s' % bridgename)
-        else:
-            return utils.exec_command('/sbin/mstpctl showbridge')
-
-    def showportdetail(self, bridgename):
-        return utils.exec_command('/sbin/mstpctl showportdetail %s' % bridgename)
-
-    def mstpbridge_exists(self, bridgename):
-        try:
-            utils.exec_command('mstpctl showbridge %s' % bridgename, stdout=False)
-            return True
-        except:
-            return False
diff --git a/ifupdownaddons/systemutils.py b/ifupdownaddons/systemutils.py
deleted file mode 100644 (file)
index a6aad67..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2015 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-
-import os
-from utilsbase import *
-from ifupdown.utils import utils
-
-class systemUtils():
-    @classmethod
-    def is_service_running(cls, procname=None, pidfile=None):
-        utilsobj = utilsBase()
-        if pidfile:
-            if os.path.exists(pidfile):
-                pid = utilsobj.read_file_oneline(pidfile)
-                if not os.path.exists('/proc/%s' %pid):
-                    return False
-            else:
-                return False
-            return True
-
-        if procname:
-            try:
-                utils.exec_command('/bin/pidof %s' % procname, stdout=False)
-            except:
-                return False
-            else:
-                return True
-
-        return False
-
-    @classmethod
-    def check_service_status(cls, servicename=None):
-        if not servicename:
-            return False
-        try:
-            utils.exec_commandl(['/usr/sbin/service', servicename, 'status'],
-                                stdout=False)
-        except Exception:
-            # XXX: check for subprocess errors vs os error
-            return False
-        return True
-
-    @classmethod
-    def is_process_running(self, processname):
-        if not processname:
-            return False
-        try:
-            utils.exec_command('/bin/pidof %s' % processname, stdout=False)
-        except:
-            return False
-        else:
-            return True
diff --git a/ifupdownaddons/utilsbase.py b/ifupdownaddons/utilsbase.py
deleted file mode 100644 (file)
index 477fcc8..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-
-import logging
-import re
-import io
-
-from ifupdown.utils import utils
-import ifupdown.ifupdownflags as ifupdownflags
-from ifupdown.iface import *
-from cache import *
-import time
-import logging
-
-def profile(func):
-    def wrap(*args, **kwargs):
-        started_at = time.time()
-        result = func(*args, **kwargs)
-        print str(func)
-        print (time.time() - started_at)
-        return result
-    return wrap
-
-class utilsBase(object):
-    """ Base class for ifupdown addon utilities """
-
-    def __init__(self, *args, **kargs):
-        modulename = self.__class__.__name__
-        self.logger = logging.getLogger('ifupdown.' + modulename)
-
-    def write_file(self, filename, strexpr):
-        try:
-            self.logger.info('writing \'%s\'' %strexpr +
-                ' to file %s' %filename)
-            if ifupdownflags.flags.DRYRUN:
-                return 0
-            with open(filename, 'w') as f:
-                f.write(strexpr)
-        except IOError, e:
-            self.logger.warn('error writing to file %s'
-                %filename + '(' + str(e) + ')')
-            return -1
-        return 0
-
-    def read_file(self, filename):
-        try:
-            self.logger.debug('reading \'%s\'' %filename)
-            with open(filename, 'r') as f:
-                return f.readlines()
-        except:
-            return None
-        return None
-
-    def read_file_oneline(self, filename):
-        try:
-            self.logger.debug('reading \'%s\'' %filename)
-            with open(filename, 'r') as f:
-                return f.readline().strip('\n')
-        except:
-            return None
-        return None
-
-    def sysctl_set(self, variable, value):
-        utils.exec_command('sysctl %s=%s' % (variable, value))
-
-    def sysctl_get(self, variable):
-        return utils.exec_command('sysctl %s' % variable).split('=')[1].strip()
diff --git a/init.d/networking b/init.d/networking
deleted file mode 100644 (file)
index 09ead81..0000000
+++ /dev/null
@@ -1,223 +0,0 @@
-#!/bin/bash
-### BEGIN INIT INFO
-# Provides:          networking ifupdown
-# Required-Start:    mountkernfs $local_fs urandom
-# Required-Stop:     $local_fs
-# Default-Start:     S
-# Default-Stop:      0 6
-# Short-Description: Raise network interfaces.
-# Description:       Prepare /run/network directory, ifstate file and raise network interfaces, or take them down.
-### END INIT INFO
-
-PATH="/sbin:/bin"
-RUN_DIR="/run/network"
-IFSTATE_LOCKFILE="${RUN_DIR}/ifstatelock"
-
-STATE_DIR="/var/tmp/network"
-IFSTATE_FILE="${STATE_DIR}/ifstatenew"
-
-NAME=networking
-SCRIPTNAME=/etc/init.d/$NAME
-
-[ -x /sbin/ifup ] || exit 0
-[ -x /sbin/ifdown ] || exit 0
-
-. /lib/lsb/init-functions
-
-CONFIGURE_INTERFACES=yes
-
-EXTRA_ARGS=
-
-[ -f /etc/default/networking ] && . /etc/default/networking
-
-[ "$VERBOSE" = yes ] && EXTRA_ARGS=-v
-[ "$DEBUG" = yes ] && EXTRA_ARGS="$EXTRA_ARGS -d"
-[ "$SYSLOG" = yes ] && EXTRA_ARGS="$EXTRA_ARGS --syslog"
-
-gen_examples() {
-    # Generate sample interfaces file. The interfaces files are
-    # generated under /usr/share/doc/python-ifupdown2/examples/
-    #
-
-    # generate files only at boot
-    [ -f ${IFSTATE_LOCKFILE} ] && return
-
-    python_ifupdown2_docdir="/usr/share/doc/python-ifupdown2"
-    swpfile=${python_ifupdown2_docdir}"/examples/swp_defaults"
-    bridgedefaultfile=${python_ifupdown2_docdir}"/examples/bridge_untagged_default"
-    interfaces_gen_script=${python_ifupdown2_docdir}"/examples/generate_interfaces.py"
-
-    [ ! -e $interfaces_gen_script ] && return
-    ret=$($interfaces_gen_script -s 2>&1 >$swpfile)
-    ret=$($interfaces_gen_script -b 2>&1 >$bridgedefaultfile)
-    return
-}
-
-perf_options() {
-    # At bootup lets set perfmode
-    [ -f ${IFSTATE_LOCKFILE} ] && echo -n "" && return
-
-    echo -n "--perfmode"
-}
-
-process_exclusions() {
-    set -- $EXCLUDE_INTERFACES
-    exclusions=""
-    for d
-    do
-       exclusions="-X $d $exclusions"
-    done
-    echo $exclusions
-}
-
-check_network_file_systems() {
-    [ -e /proc/mounts ] || return 0
-
-    if [ -e /etc/iscsi/iscsi.initramfs ]; then
-       log_warning_msg "not deconfiguring network interfaces: iSCSI root is mounted."
-       exit 0
-    fi
-
-    while read DEV MTPT FSTYPE REST; do
-       case $DEV in
-       /dev/nbd*|/dev/nd[a-z]*|/dev/etherd/e*)
-           log_warning_msg "not deconfiguring network interfaces: network devices still mounted."
-           exit 0
-           ;;
-       esac
-       case $FSTYPE in
-       nfs|nfs4|smbfs|ncp|ncpfs|cifs|coda|ocfs2|gfs|pvfs|pvfs2|fuse.httpfs|fuse.curlftpfs)
-           log_warning_msg "not deconfiguring network interfaces: network file systems still mounted."
-           exit 0
-           ;;
-       esac
-    done < /proc/mounts
-}
-
-check_network_swap() {
-    [ -e /proc/swaps ] || return 0
-
-    while read DEV MTPT FSTYPE REST; do
-       case $DEV in
-       /dev/nbd*|/dev/nd[a-z]*|/dev/etherd/e*)
-           log_warning_msg "not deconfiguring network interfaces: network swap still mounted."
-           exit 0
-           ;;
-       esac
-    done < /proc/swaps
-}
-
-ifup_hotplug () {
-    if [ -d /sys/class/net ]
-    then
-           ifaces=$(for iface in $(ifquery --list --allow=hotplug 2>/dev/null)
-                           do
-                                   link=${iface##:*}
-                                   link=${link##.*}
-                                   if [ -e "/sys/class/net/$link" ] && [ "$(cat /sys/class/net/$link/operstate)" = up ]
-                                   then
-                                           echo "$iface"
-                                   fi
-                           done)
-           if [ -n "$ifaces" ]
-           then
-               ifup $ifaces "$@" || true
-           fi
-    fi
-}
-
-ifupdown_init() {
-       # remove state file at boot
-       [ ! -e ${IFSTATE_LOCKFILE} ] && rm -f ${IFSTATE_FILE}
-       [ ! -e /run/network ] && mkdir -p /run/network &>/dev/null
-       [ ! -e /etc/network/run ] && \
-               ln -sf /run/network /etc/network/run &>/dev/null
-}
-
-case "$1" in
-start)
-       gen_examples
-       ifupdown_init
-       if [ "$CONFIGURE_INTERFACES" = no ]
-       then
-           log_action_msg "Not configuring network interfaces, see /etc/default/networking"
-           exit 0
-       fi
-       set -f
-       exclusions=$(process_exclusions)
-       perfoptions=$(perf_options)
-       log_action_begin_msg "Configuring network interfaces"
-       ifup -a $EXTRA_ARGS $exclusions $perfoptions 
-       log_action_end_msg $?
-       ;;
-
-stop)
-       if [ "$SKIP_DOWN_AT_SYSRESET" = "yes" ]; then
-               shutdown_or_reboot=$(runlevel 2>/dev/null | \
-                                    /usr/bin/tr -s " " | \
-                                    /usr/bin/cut -d " " \
-                                    -f1- --output-delimiter=$'\n' | \
-                                    /bin/grep -e "0" -e "6")
-               if [ -n "$shutdown_or_reboot" ]; then
-                       log_action_begin_msg "Deconfiguring network interfaces..skip"
-                       log_action_end_msg 0
-                       exit 0
-               fi
-       fi
-       ifupdown_init
-       check_network_file_systems
-       check_network_swap
-       exclusions=$(process_exclusions)
-
-       log_action_begin_msg "Deconfiguring network interfaces"
-       ifdown -a $EXTRA_ARGS $exclusions
-       log_action_end_msg $?
-       ;;
-
-reload)
-
-       ifupdown_init
-       log_action_begin_msg "Reloading network interfaces configuration"
-
-       ifreload -a $EXTRA_ARGS
-       log_action_end_msg $?
-       ;;
-
-reload-currently-up)
-
-       ifupdown_init
-       log_action_begin_msg "Reloading currently up network interfaces configuration"
-
-       ifreload --currently-up $EXTRA_ARGS
-       log_action_end_msg $?
-       ;;
-
-force-reload)
-
-       ifupdown_init
-
-       log_action_begin_msg "Reloading network interfaces configuration"
-       ifreload -f -a $EXTRA_ARGS
-       log_action_end_msg $?
-       ;;
-
-restart)
-       ifupdown_init
-
-       set -f
-       exclusions=$(process_exclusions)
-       log_action_begin_msg "Reconfiguring network interfaces"
-       ifdown -a $EXTRA_ARGS $exclusions || true
-       ifup -a $EXTRA_ARGS $exclusions
-       log_action_end_msg $?
-       ;;
-
-*)
-       echo "Usage: /etc/init.d/networking {start|stop|reload|restart|force-reload}"
-       exit 1
-       ;;
-esac
-
-exit 0
-
-# vim: noet ts=8
diff --git a/man.rst/ifquery.8.rst b/man.rst/ifquery.8.rst
deleted file mode 100644 (file)
index ed808ca..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-=======
-ifquery
-=======
-
--------------------------------------
-query network interface configuration
--------------------------------------
-
-:Author: Roopa Prabhu <roopa@cumulusnetworks.com>
-:Date:   2014-02-05
-:Copyright: Copyright 2014 Cumulus Networks, Inc.  All rights reserved.
-:Version: 0.1
-:Manual section: 8
-
-SYNOPSIS
-========
-
-    **ifquery [-v] [--allow CLASS] [--with-depends] -a|IFACE...**
-
-    **ifquery [-v] [-r|--running] [--allow CLASS] [--with-depends] -a|IFACE...**
-
-    **ifquery [-v] [-c|--check] [--allow CLASS] [--with-depends] -a|IFACE...**
-
-    **ifquery [-v] [-p|--print-dependency {list,dot}] [--allow CLASS] [--with-depends] -a|IFACE...**
-
-    **ifquery [-v] -s|--syntax-help**
-
-DESCRIPTION
-===========
-    **ifquery** can be used to parse interface configuration file, query
-    running state or check running state of the interface with configuration
-    in **/etc/network/interfaces** file.
-
-    **ifquery** always works on the current **interfaces(5)** file
-    **/etc/network/interfaces** unless an alternate interfaces file is
-    defined in ifupdown2.conf or provided with the **-i** option.
-    Note: the -i option is disabled by default in ifupdown2.conf.
-
-OPTIONS
-=======
-    positional arguments:
-
-    **IFACE**   interface list separated by spaces. **IFACE** list and **'-a'** argument are mutually exclusive.
-
-    optional arguments:
-
-    -h, --help            show this help message and exit
-
-    -a, --all             process all interfaces marked "auto"
-
-    -v, --verbose         verbose
-
-    -d, --debug           output debug info
-    --allow CLASS         ignore non-"allow-CLASS" interfaces
-
-    -w, --with-depends    run with all dependent interfaces. This option
-                          is redundant when -a is specified. When '-a' is
-                          specified, interfaces are always executed in
-                          dependency order.
-
-    -X EXCLUDEPATS, --exclude EXCLUDEPATS
-                          Exclude interfaces from the list of interfaces to
-                          operate on. Can be specified multiple times
-                          If the excluded interface has dependent interfaces,
-                          (e.g. a bridge or a bond with multiple enslaved interfaces)
-                          then each dependent interface must be specified in order
-                          to be excluded.
-
-    -i INTERFACESFILE, --interfaces INTERFACESFILE
-                          Use interfaces file instead of default
-                          defined in ifupdown2.conf (default /etc/network/interfaces)
-
-    -t {native,json}, --interfaces-format {native,json}
-                          interfaces file format
-
-    -r, --running         print raw interfaces file entries
-
-    -c, --check           check interface file contents against running state
-                          of an interface. Returns exit code 0 on success and
-                          1 on error
-
-    -x, --raw             print raw config file entries
-
-    -o {native,json}, --format {native,json}
-                          interface display format
-
-    -p, --print-dependency {list,dot}
-                          print iface dependency in list or dot format
-
-    -s, --syntax-help     print supported interface config syntax. Scans all
-                          addon modules and dumps supported syntax from them
-                          if provided by the module.
-
-EXAMPLES
-========
-    # dump all or some interfaces config file entries
-    # (pretty prints user provided entries)
-
-        **ifquery -a**
-
-        **ifquery br0**
-
-    # Same as above but dump with dependencies
-
-        **ifquery br0 --with-depends**
-
-    # Check running state with the config in /etc/network/interfaces
-
-        **ifquery --check br0**
-
-        **ifquery --check --with-depends br0**
-
-        **ifquery --check -a** 
-
-    # dump running state of all interfaces in /etc/network/interfaces format
-
-        **ifquery --running br0**
-
-        **ifquery --running --with-depends br0**
-
-        **ifquery --running -a**
-
-    # print dependency info in list format
-
-        **ifquery --print-dependency=list -a**
-
-        **ifquery --print-dependency=list  br2000**
-
-    # print dependency info in dot format
-
-        **ifquery --print-dependency=dot -a**
-
-        **ifquery --print-dependency=dot br2000**
-
-    # Create an image (png) from the dot format
-
-        **ifquery --print-dependency=dot -a > interfaces.dot**
-
-        **dot -Tpng interfaces.dot > interfaces.png**
-
-        (The above command only works on a system with dot installed)
-
-KNOWN_ISSUES
-============
-    **ifquery --check** is currently experimental
-
-    **ifquery --check** cannot validate usercommands given under pre-up, post-up etc
-    There is currently no support to check/validate ethtool iface attributes
-
-SEE ALSO
-========
-    ifup(8),
-    ifdown(8),
-    ifreload(8),
-    interfaces(5),
-    ifupdown-addons-interfaces(5)
diff --git a/man.rst/ifreload.8.rst b/man.rst/ifreload.8.rst
deleted file mode 100644 (file)
index e5c2f9d..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-========
-ifreload
-========
-
---------------------------------------
-reload network interface configuration
---------------------------------------
-
-:Author: Roopa Prabhu <roopa@cumulusnetworks.com>
-:Date:   2014-02-05
-:Copyright: Copyright 2014 Cumulus Networks, Inc.  All rights reserved.
-:Version: 0.1
-:Manual section: 8
-
-SYNOPSIS
-========
-    ifreload [-h] (-a|-c) [-v] [-d] [-f] [-n] [-s]
-
-DESCRIPTION
-===========
-    reloads network **interfaces(5)** file **/etc/network/interfaces**
-    or config file defined in ifupdown2.conf file.
-
-    Runs **ifdown** on interfaces that were removed from the file and
-    subsequently runs **ifup** on all interfaces.
-
-    ifreload is non-disruptive. It will fix running config to match what
-    is configured in the interfaces file without bringing the interface
-    down. There are some cases where on linux an interface config cannot
-    be applied unless the interface is brought down...eg: change of mac
-    address and a few bond attributes. For such attribute changes, it may
-    flap the interface only if the linux kernel requires it to.
-
-    When removing an interface (iface section) from the interfaces file
-    please make sure all its references are removed as well. Similarly
-    when renaming an interface, please make sure all references to the
-    interface are changed to the new name. Renaming an interface
-    in the interfaces file results in ifdown of the old and ifup
-    of the interface with the new name.
-
-    If you do not wish to execute **down** on any interfaces, but only **up** on
-    interfaces that were already **up**, please see the **--currently-up**
-    option below.
-
-OPTIONS
-=======
-    -h, --help            show this help message and exit
-
-    -a, --all             process all interfaces marked "auto"
-
-    -v, --verbose         verbose
-
-    -d, --debug           output debug info
-
-    -f, --force           force run all operations
-
-    -c, --currently-up    only reload auto and other interfaces that are
-                          currently up. This can be used as a non-disruptive
-                          alternative to -a because it will not down any
-                          interfaces
-
-    -X EXCLUDEPATS, --exclude EXCLUDEPATS
-                          Exclude interfaces from the list of interfaces to
-                          operate on. Can be specified multiple times
-                          If the excluded interface has dependent interfaces,
-                          (e.g. a bridge or a bond with multiple enslaved interfaces)
-                          then each dependent interface must be specified in order
-                          to be excluded.
-
-    -s, --syntax-check    Only run the interfaces file parser
-
-
-EXAMPLES
-========
-    # reload all auto interfaces in **interfaces(5)** file
-
-    **ifreload -a**
-
-    # reload all interfaces using service command
-
-    **service networking reload**
-
-    # reload all currently up interfaces without bringing any interfaces down
-
-    **service networking reload-currently-up**
-
-SEE ALSO
-========
-    ifup(8),
-    ifdown(8),
-    ifquery(8),
-    interfaces(5),
-    ifupdown-addons-interfaces(5)
diff --git a/man.rst/ifup.8.rst b/man.rst/ifup.8.rst
deleted file mode 100644 (file)
index 6fc202c..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-====
-ifup
-====
-
--------------------------------------
-network interface management commands 
--------------------------------------
-
-:Author: Roopa Prabhu <roopa@cumulusnetworks.com>
-:Date:   2014-02-05
-:Copyright: Copyright 2014 Cumulus Networks, Inc.  All rights reserved.
-:Version: 0.1
-:Manual section: 8
-
-NAME
-====
-    **ifup** - bring a network interface up
-
-    **ifdown** - take a network interface down
-
-SYNOPSIS
-========
-
-    ifup [-h] [-a] [-v] [-d] [--allow CLASS] [--with-depends]
-       **[-X EXCLUDEPATS] [-f] [-n] [-s] [--print-dependency {list,dot}]**
-       **[IFACE [IFACE ...]]**
-
-    ifdown [-h] [-a] [-v] [-d] [--allow CLASS] [--with-depends]
-           **[-X EXCLUDEPATS] [-f] [-n] [--print-dependency {list,dot}]**
-           **[IFACE [IFACE ...]]**
-
-DESCRIPTION
-===========
-    **ifup** and **ifdown** commands can be used to configure (or, respectively,
-    deconfigure) network interfaces based on interface definitions in the
-    config file ifupdown2.conf (defaults to **/etc/network/interfaces/** file).
-
-    **ifquery(8)** maybe used in conjunction with **ifup** and **ifdown**
-    commands to query and validate applied/running configuration.
-
-    **ifup** always works on the current **interfaces(5)** file defined in ifupdown2.conf
-    (default **/etc/network/interfaces**). **ifdown** works on the last applied interface
-    configuration.
-
-    **ifup** on an already ifup'ed interface will re-apply the configuration,
-    skipping already applied configuration wherever possible. In many cases
-    where config commands are idempotent, you will see that ifup/ifdown will
-    reapply the config even if the interface already has that config.
-
-    **ifup** and **ifdown** understands interface dependency order.
-
-    For logical interfaces like vlans, bridges, bonds, **ifup** creates the
-    interface and **ifdown** deletes the interface. Use **--admin-state**
-    option if you only want to administratively bring the interface up/down.
-
-    When **ifup** and **ifdown** are used with interfaces on command line,
-    they must be have a **iface** section in the **interfaces(5)** file.
-
-OPTIONS
-=======
-    positional arguments:
-
-    **IFACE**  interface list separated by spaces. **IFACE** list and **'-a'**
-    argument are mutually exclusive.
-
-    optional arguments:
-
-    -h, --help            show this help message and exit
-
-    -a, --all             process all interfaces marked "auto"
-
-    -v, --verbose         verbose
-
-    -d, --debug           output debug info
-    --allow CLASS         ignore non-"allow-CLASS" interfaces
-
-    -w, --with-depends        run with all dependent interfaces. This option
-                          is redundant when -a is specified. When '-a' is
-                          specified, interfaces are always executed in
-                          dependency order.
-                        
-    -X EXCLUDEPATS, --exclude EXCLUDEPATS
-                          Exclude interfaces from the list of interfaces to
-                          operate on. Can be specified multiple times
-                          If the excluded interface has dependent interfaces,
-                          (e.g. a bridge or a bond with multiple enslaved interfaces)
-                          then each dependent interface must be specified in order
-                          to be excluded.
-
-    -i INTERFACESFILE, --interfaces INTERFACESFILE
-                          Uses interfaces file instead of default defined in 
-                          ifupdown2.conf (default /etc/network/interfaces).
-                         Also in ifupdown2.conf, users are not allowed to specify their own
-                         interfaces file unless disable_cli_interfacesfile is set to 0
-                         (default is 1).
-
-    -t {native,json}, --interfaces-format {native,json}
-                          interfaces file format
-
-    -f, --force           force run all operations
-
-    -n, --no-act          print out what would happen, but don't do it
-
-    -p, --print-dependency {list,dot}
-                          print iface dependency in list or dot format
-
-    -m, --admin-state, --no-scripts
-                          don't run any addon modules/scripts. Only bring
-                          the interface administratively up/down
-
-    -u, --use-current-config
-                          By default ifdown looks at the saved state for
-                          interfaces to bring down. This option allows ifdown
-                          to look at the current interfaces file. Useful when
-                          your state file is corrupted or you want down to use
-                          the latest from the interfaces file
-
-    -s, --syntax-check    Only run the interfaces file parser
-
-EXAMPLES
-========
-    # bringing up all interfaces
-
-        **ifup -a**
-
-    # bringing up interface list
-
-        **ifup swp1 swp2**
-
-    # bringing up interface with its dependents
-
-        **ifup br0 --with-depends**
-
-    # bringing down all interfaces
-
-        **ifdown -a**
-
-    # bringing down a single interface
-
-        **ifdown swp1**
-
-    # excluding interfaces using -X option
-
-        **ifdown -X eth0 -a**
-
-        **ifup -X eth0 -a**
-
-        **ifdown -X eth0 -X lo -a**
-
-    # using verbose -v option to see what is going on
-
-        **ifup -v -a**
-
-    # using debug -d option to see more of what is going on
-
-        **ifup -d -a**
-
-    # ignore errors
-
-        **ifup -a -f**
-
-        **ifdown -a -f**
-
-    # run ifdown and ifup on all interfaces using service command/init script
-
-        **service networking restart**
-
-    # run ifup on all interfaces using service command/init script
-
-        **service networking start**
-
-    # ifdown on all interfaces using service command/init script
-
-        **service networking stop**
-
-    # To run ifup/ifdown on only interfaces that changed see **ifreload(8)**
-
-SEE ALSO
-========
-    ifquery(8),
-    ifreload(8),
-    interfaces(5),
-    ifupdown-addons-interfaces(5)
diff --git a/man.rst/ifupdown-addons-interfaces.5.rst b/man.rst/ifupdown-addons-interfaces.5.rst
deleted file mode 100644 (file)
index 9f61a8a..0000000
+++ /dev/null
@@ -1,1390 +0,0 @@
-==========================
-ifupdown-addons-interfaces
-==========================
----------------------------------------------------------
-ifupdown2 addon modules interface configuration
----------------------------------------------------------
-:Author: roopa@cumulusnetworks.com
-:Date:   2013-09-25
-:Copyright: Copyright 2013 Cumulus Networks, Inc.  All rights reserved.
-:Version: 0.1
-:Manual section: 5
-
-
-DESCRIPTION
-===========
-    ifupdown2 addon modules add incremental functionality to
-    core ifupdown2 tool.
-           
-    All installed addon modules are executed on every interface
-    listed in the interfaces file. Addon modules are installed under
-    /usr/share/ifupdownaddons. To see the list of active addon
-    modules, see ifaddon(8).
-
-    Addon modules add new attributes to the interfaces(5) file.
-    Below is a list of attribute options provided by each module.
-    These can be listed under each iface section in the interfaces(5)
-    file.  
-
-
-EXAMPLES
-========
-    Listed below are addon modules and their supported attributes.
-    The attributes if applicable go under the iface section in the
-    interfaces(5) file.
-
-    **ethtool**: ethtool configuration module for interfaces
-
-
-      **link-duplex**
-
-        **help**: set link duplex
-
-
-        **required**: False
-
-        **default**: half
-
-        **validvals**: half,full
-
-        **example**:
-            link-duplex full
-
-
-      **link-autoneg**
-
-        **help**: set autonegotiation
-
-
-        **required**: False
-
-        **default**: off
-
-        **validvals**: on,off
-
-        **example**:
-            link-autoneg on
-
-
-      **link-speed**
-
-        **help**: set link speed
-
-
-        **required**: False
-
-        **example**:
-            link-speed 1000
-
-
-
-    **bridge**: Bridge configuration module. Supports both vlan aware 
-    and non vlan aware bridges. For the vlan aware bridge, the port sp
-    ecific attributes must be specified under the port. And for vlan u
-    naware bridge port specific attributes must be specified under the
-    bridge.
-
-
-      **bridge-vlan-aware**
-
-        **help**: vlan aware bridge. Setting this attribute to yes ena
-        bles vlan filtering on the bridge
-
-
-        **required**: False
-
-        **example**:
-            bridge-vlan-aware yes/no
-
-
-      **bridge-pathcosts**
-
-        **help**: bridge set port path costs
-
-
-        **required**: False
-
-        **default**: 100
-
-        **example**:
-            bridge-pathcosts swp1=100 swp2=100
-
-
-      **bridge-portprios**
-
-        **help**: bridge port prios
-
-
-        **required**: False
-
-        **default**: 32
-
-        **example**:
-            bridge-portprios swp1=32 swp2=32
-
-
-      **bridge-fd**
-
-        **help**: bridge forward delay
-
-
-        **required**: False
-
-        **default**: 15
-
-        **example**:
-            bridge-fd 15
-
-
-      **bridge-ageing**
-
-        **help**: bridge ageing
-
-
-        **required**: False
-
-        **default**: 300
-
-        **example**:
-            bridge-ageing 300
-
-
-      **bridge-hello**
-
-        **help**: bridge set hello time
-
-
-        **required**: False
-
-        **default**: 2
-
-        **example**:
-            bridge-hello 2
-
-
-      **bridge-gcint**
-
-        **help**: bridge garbage collection interval in secs
-
-
-        **required**: False
-
-        **default**: 4
-
-        **example**:
-            bridge-gcint 4
-
-
-      **bridge-mcquerier**
-
-        **help**: set multicast querier
-
-
-        **required**: False
-
-        **default**: 0
-
-        **example**:
-            bridge-mcquerier 0
-
-
-      **bridge-mclmc**
-
-        **help**: set multicast last member count
-
-
-        **required**: False
-
-        **default**: 2
-
-        **example**:
-            bridge-mclmc 2
-
-
-      **bridge-mcsqc**
-
-        **help**: set multicast startup query count
-
-
-        **required**: False
-
-        **default**: 2
-
-        **example**:
-            bridge-mcsqc 2
-
-
-      **bridge-mcrouter**
-
-        **help**: set multicast router
-
-
-        **required**: False
-
-        **default**: 1
-
-        **example**:
-            bridge-mcrouter 1
-
-
-      **bridge-stp**
-
-        **help**: bridge-stp yes/no
-
-
-        **required**: False
-
-        **default**: no
-
-        **validvals**: yes,on,off,no
-
-        **example**:
-            bridge-stp no
-
-
-      **bridge-pvid**
-
-        **help**: bridge port pvid. Must be specified under the bridge
-        port
-
-
-        **required**: False
-
-        **example**:
-            bridge-pvid 1
-
-
-      **bridge-mcsqi**
-
-        **help**: set multicast startup query interval (in secs)
-
-
-        **required**: False
-
-        **default**: 31
-
-        **example**:
-            bridge-mcsqi 31
-
-
-      **bridge-mcmi**
-
-        **help**: set multicast membership interval (in secs)
-
-
-        **required**: False
-
-        **default**: 260
-
-        **example**:
-            bridge-mcmi 260
-
-
-      **bridge-mclmi**
-
-        **help**: set multicast last member interval (in secs)
-
-
-        **required**: False
-
-        **default**: 1
-
-        **example**:
-            bridge-mclmi 1
-
-
-      **bridge-vids**
-
-        **help**: bridge port vids. Can be specified under the bridge 
-        or under the port. If specified under the bridge the ports inh
-        erit it unless overridden by a bridge-vids attribuet under the
-        port
-
-
-        **required**: False
-
-        **example**:
-            bridge-vids 4000
-
-            bridge-vids 2000 2200-3000
-
-
-      **bridge-ports**
-
-        **help**: bridge ports
-
-
-        **required**: True
-
-        **example**:
-            bridge-ports swp1.100 swp2.100 swp3.100
-
-            bridge-ports glob swp1-3.100
-
-            bridge-ports glob swp[1-3]s[0-4].100
-
-            bridge-ports regex (swp[1|2|3].100)
-
-
-      **bridge-mcqifaddr**
-
-        **help**: set multicast query to use ifaddr
-
-
-        **required**: False
-
-        **default**: 0
-
-        **example**:
-            bridge-mcqifaddr 0
-
-
-      **bridge-waitport**
-
-        **help**: wait for a max of time secs for the specified ports 
-        to become available,if no ports are specified then those speci
-        fied on bridge-ports will be used here. Specifying no ports he
-        re should not be used if we are using regex or "all" on bridge
-        _ports,as it wouldnt work.
-
-
-        **required**: False
-
-        **default**: 0
-
-        **example**:
-            bridge-waitport 4 swp1 swp2
-
-
-      **bridge-mcqri**
-
-        **help**: set multicast query response interval (in secs)
-
-
-        **required**: False
-
-        **default**: 10
-
-        **example**:
-            bridge-mcqri 10
-
-
-      **bridge-hashel**
-
-        **help**: set hash elasticity
-
-
-        **required**: False
-
-        **default**: 4096
-
-        **example**:
-            bridge-hashel 4096
-
-
-      **bridge-mcqpi**
-
-        **help**: set multicast querier interval (in secs)
-
-
-        **required**: False
-
-        **default**: 255
-
-        **example**:
-            bridge-mcqpi 255
-
-
-      **bridge-hashmax**
-
-        **help**: set hash max
-
-
-        **required**: False
-
-        **default**: 4096
-
-        **example**:
-            bridge-hashmax 4096
-
-
-      **bridge-bridgeprio**
-
-        **help**: bridge priority
-
-
-        **required**: False
-
-        **default**: 32768
-
-        **example**:
-            bridge-bridgeprio 32768
-
-
-      **bridge-maxage**
-
-        **help**: bridge set maxage
-
-
-        **required**: False
-
-        **default**: 20
-
-        **example**:
-            bridge-maxage 20
-
-
-      **bridge-mcsnoop**
-
-        **help**: set multicast snooping
-
-
-        **required**: False
-
-        **default**: 1
-
-        **example**:
-            bridge-mcsnoop 1
-
-
-      **bridge-access**
-
-        **help**: bridge port access vlan. Must be specified under the
-        bridge port
-
-
-        **required**: False
-
-        **example**:
-            bridge-access 300
-
-
-      **bridge-maxwait**
-
-        **help**: forces to time seconds the maximum time that the Deb
-        ian bridge setup  scripts will wait for the bridge ports to ge
-        t to the forwarding status, doesn't allow factional part. If i
-        t is equal to 0 then no waiting is done
-
-
-        **required**: False
-
-        **default**: 0
-
-        **example**:
-            bridge-maxwait 3
-
-
-      **bridge-portmcrouter**
-
-        **help**: set port multicast routers
-
-
-        **required**: False
-
-        **default**: 1
-
-        **example**:
-            under the bridge: bridge-portmcrouter swp1=1 swp2=1
-
-            under the port: bridge-portmcrouter 1
-
-
-      **bridge-portmcfl**
-
-        **help**: port multicast fast leave.
-
-
-        **required**: False
-
-        **default**: 0
-
-        **example**:
-            under the bridge: bridge-portmcfl swp1=0 swp2=0
-
-            under the port: bridge-portmcfl 0
-
-
-      **bridge-mcqi**
-
-        **help**: set multicast query interval (in secs)
-
-
-        **required**: False
-
-        **default**: 125
-
-        **example**:
-            bridge-mcqi 125
-
-
-
-    **usercmds**: user commands for interfaces
-
-
-      **down**
-
-        **help**: run command at interface down
-
-
-        **required**: False
-
-      **post-up**
-
-        **help**: run command after interface bring up
-
-
-        **required**: False
-
-      **up**
-
-        **help**: run command at interface bring up
-
-
-        **required**: False
-
-      **pre-down**
-
-        **help**: run command before bringing the interface down
-
-
-        **required**: False
-
-      **pre-up**
-
-        **help**: run command before bringing the interface up
-
-
-        **required**: False
-
-      **post-down**
-
-        **help**: run command after bringing interface down
-
-
-        **required**: False
-
-
-    **mstpctl**: mstp configuration module for bridges
-
-
-      **mstpctl-portadminedge**
-
-        **help**: enable/disable initial edge state of the port
-
-
-        **required**: False
-
-        **default**: no
-
-        **validvals**: yes,no
-
-        **example**:
-            mstpctl-portadminedge swp1=no swp2=no
-
-
-      **mstpctl-portbpdufilter**
-
-        **help**: enable/disable bpdu filter on a port. syntax varies 
-        when defined under a bridge vs under a port
-
-
-        **required**: False
-
-        **default**: no
-
-        **validvals**: yes,no
-
-        **example**:
-            under a bridge: mstpctl-portbpdufilter swp1=no swp2=no
-
-            under a port: mstpctl-portbpdufilter yes
-
-
-      **mstpctl-fdelay**
-
-        **help**: set forwarding delay
-
-
-        **required**: False
-
-        **default**: 15
-
-        **example**:
-            mstpctl-fdelay 15
-
-
-      **mstpctl-portnetwork**
-
-        **help**: enable/disable bridge assurance capability for a por
-        t
-
-
-        **required**: False
-
-        **default**: no
-
-        **validvals**: yes,no
-
-        **example**:
-            mstpctl-portnetwork swp1=no swp2=no
-
-
-      **mstpctl-txholdcount**
-
-        **help**: bridge transmit holdcount
-
-
-        **required**: False
-
-        **default**: 6
-
-        **example**:
-            mstpctl-txholdcount 6
-
-
-      **mstpctl-forcevers**
-
-        **help**: bridge force stp version
-
-
-        **required**: False
-
-        **default**: rstp
-
-        **example**:
-            mstpctl-forcevers rstp
-
-
-      **mstpctl-portautoedge**
-
-        **help**: enable/disable auto transition to/from edge state of
-        the port
-
-
-        **required**: False
-
-        **default**: yes
-
-        **validvals**: yes,no
-
-        **example**:
-            mstpctl-portautoedge swp1=yes swp2=yes
-
-
-      **mstpctl-maxhops**
-
-        **help**: bridge max hops
-
-
-        **required**: False
-
-        **default**: 15
-
-        **example**:
-            mstpctl-maxhops 15
-
-
-      **mstpctl-treeprio**
-
-        **help**: tree priority
-
-
-        **required**: False
-
-        **default**: 32768
-
-        validrange: 0-65535
-
-        **example**:
-            mstpctl-treeprio 32768
-
-
-      **mstpctl-treeportprio**
-
-        **help**: port priority for MSTI instance
-
-
-        **required**: False
-
-        **default**: 128
-
-        validrange: 0-240
-
-        **example**:
-            mstpctl-treeportprio swp1=128 swp2=128
-
-
-      **mstpctl-portpathcost**
-
-        **help**: bridge port path cost
-
-
-        **required**: False
-
-        **default**: 0
-
-        **example**:
-            mstpctl-portpathcost swp1=0 swp2=1
-
-
-      **mstpctl-portrestrtcn**
-
-        **help**: enable/disable port ability to propagate received to
-        pology change notification of the port
-
-
-        **required**: False
-
-        **default**: no
-
-        **validvals**: yes,no
-
-        **example**:
-            mstpctl-portrestrtcn swp1=no swp2=no
-
-
-      **mstpctl-maxage**
-
-        **help**: max message age
-
-
-        **required**: False
-
-        **default**: 20
-
-        **example**:
-            mstpctl-maxage 20
-
-
-      **mstpctl-hello**
-
-        **help**: set hello time
-
-
-        **required**: False
-
-        **default**: 2
-
-        **example**:
-            mstpctl-hello 2
-
-
-      **mstpctl-portrestrrole**
-
-        **help**: enable/disable port ability to take root role of the
-        port
-
-
-        **required**: False
-
-        **default**: no
-
-        **validvals**: yes,no
-
-        **example**:
-            mstpctl-portrestrrole swp1=no swp2=no
-
-
-      **mstpctl-bpduguard**
-
-        **help**: enable/disable bpduguard
-
-
-        **required**: False
-
-        **default**: no
-
-        **validvals**: yes,no
-
-        **example**:
-            mstpctl-bpduguard swp1=no swp2=no
-
-
-      **mstpctl-ageing**
-
-        **help**: ageing time
-
-
-        **required**: False
-
-        **default**: 300
-
-        **example**:
-            mstpctl-ageing 300
-
-
-      **mstpctl-treeportcost**
-
-        **help**: port tree cost
-
-
-        **required**: False
-
-      **mstpctl-portp2p**
-
-        **help**: bridge port p2p detection mode
-
-
-        **required**: False
-
-        **default**: auto
-
-        **validvals**: yes,no,auto
-
-        **example**:
-            mstpctl-portp2p swp1=no swp2=no
-
-
-
-    **clagd**: This module generates the clagd defaults file.
-
-
-      **clagd-priority**
-
-        **help**: The priority of this clagd switch
-
-
-        **required**: False
-
-        **example**:
-            clagd-priority 30000
-
-
-      **clagd-backup-ip**
-
-        **help**: Backup IP address of the clagd peer
-
-
-        **required**: False
-
-        **example**:
-            clagd-backup-ip 192.1.1.1
-
-
-      **clagd-enable**
-
-        **help**: enable clagd
-
-
-        **required**: False
-
-        **validvals**: yes,no
-
-        **example**:
-            clagd-enable yes
-
-
-      **clag-id**
-
-        **help**: multi-chassis lag id
-
-
-        **required**: False
-
-        **default**: 0
-
-        validrange: 0-65535
-
-        **example**:
-            clag-id 1
-
-
-      **clagd-peer-ip**
-
-        **help**: The IP address of the clagd peer
-
-
-        **required**: True
-
-        **example**:
-            clagd-peer 10.10.10.2
-
-
-      **clagd-sys-mac**
-
-        **help**: The system ID of the CLAG pair
-
-
-        **required**: True
-
-        **example**:
-            clagd-sys-mac 44:38:39:ff:00:00
-
-
-      **clagd-args**
-
-        **help**: Additional command line arguments for clagd
-
-
-        **required**: False
-
-        **example**:
-            clagd-args --log /var/log/clagd.log
-
-            clagd-args --verbose --lacpPoll 10
-
-            clagd-args --debug 0x4
-
-
-
-    **vlan**: vlan module configures vlan interfaces.This module under
-    stands vlan interfaces with dot notations. eg swp1.100. Vlan inter
-    faces with any other names need to have raw device and vlan id att
-    ributes
-
-
-      **vlan-id**
-
-        **help**: vlan id
-
-
-        **required**: False
-
-      **vlan-raw-device**
-
-        **help**: vlan raw device
-
-
-        **required**: False
-
-
-    **bridgevlan**: bridgevlan module configures vlan attributes on a 
-    vlan aware bridge. This module only understands vlan interface name
-    with dot notations. eg br0.100. where br0 is the vlan aware bridge
-    this config is for.
-
-
-      **bridge-igmp-querier-src**
-
-        **help**: bridge igmp querier src. Must be specified under the
-        vlan interface
-
-
-        **required**: False
-
-        **example**:
-            bridge-igmp-querier-src 172.16.101.1
-
-
-
-    **bond**: bond configuration module
-
-
-      **bond-use-carrier**
-
-        **help**: bond use carrier
-
-
-        **required**: False
-
-        **default**: 1
-
-        **validvals**: 0,1
-
-        **example**:
-            bond-use-carrier 1
-
-
-      **bond-lacp-bypass-period**
-
-        **help**: grace period (seconds) for lacp bypass
-
-
-        **required**: False
-
-        **default**: 0
-
-        validrange: 0-900
-
-        **example**:
-            bond-lacp-bypass-period 100
-
-
-      **bond-miimon**
-
-        **help**: bond miimon
-
-
-        **required**: False
-
-        **default**: 0
-
-        validrange: 0-255
-
-        **example**:
-            bond-miimon 0
-
-
-      **bond-lacp-rate**
-
-        **help**: bond lacp rate
-
-
-        **required**: False
-
-        **default**: 0
-
-        **validvals**: 0,1
-
-        **example**:
-            bond-lacp-rate 0
-
-
-      **bond-lacp-bypass-priority**
-
-        **help**: slave priority for lacp bypass
-
-
-        **required**: False
-
-        **example**:
-            bond-lacp-bypass-priority swp1=1 swp2=1 swp3=2
-
-
-      **bond-min-links**
-
-        **help**: bond min links
-
-
-        **required**: False
-
-        **default**: 0
-
-        **example**:
-            bond-min-links 0
-
-
-      **bond-slaves**
-
-        **help**: bond slaves
-
-
-        **required**: True
-
-        **example**:
-            bond-slaves swp1 swp2
-
-            bond-slaves glob swp1-2
-
-            bond-slaves regex (swp[1|2)
-
-
-      **bond-lacp-bypass-allow**
-
-        **help**: allow lacp bypass
-
-
-        **required**: False
-
-        **default**: 0
-
-        **validvals**: 0,1
-
-        **example**:
-            bond-lacp-bypass-allow 0
-
-
-      **bond-lacp-bypass-allow-all-active**
-
-        **help**: allow all slaves to be active in lacp bypass irrespective of priority
-
-
-        **required**: False
-
-        **default**: 0
-
-        **validvals**: 0,1
-
-        **example**:
-            bond-lacp-bypass-all-active 1
-
-
-      **bond-mode**
-
-        **help**: bond mode
-
-
-        **required**: False
-
-        **default**: balance-rr
-
-        **validvals**: balance-rr,active-backup,balance-xor,broadcast,802.3ad,balance-tlb,balance-alb
-
-        **example**:
-            bond-mode 802.3ad
-
-
-      **bond-num-unsol-na**
-
-        **help**: bond slave devices
-
-
-        **required**: False
-
-        **default**: 1
-
-        validrange: 0-255
-
-        **example**:
-            bond-num-unsol-na 1
-
-
-      **bond-ad-sys-priority**
-
-        **help**: 802.3ad system priority
-
-
-        **required**: False
-
-        **default**: 65535
-
-        **example**:
-            bond-ad-sys-priority 65535
-
-
-      **bond-xmit-hash-policy**
-
-        **help**: bond slave devices
-
-
-        **required**: False
-
-        **default**: layer2
-
-        **validvals**: layer2,layer3+4,layer2+3
-
-        **example**:
-            bond-xmit-hash-policy layer2
-
-
-      **bond-num-grat-arp**
-
-        **help**: bond use carrier
-
-
-        **required**: False
-
-        **default**: 1
-
-        validrange: 0-255
-
-        **example**:
-            bond-num-grat-arp 1
-
-
-      **bond-ad-sys-mac-addr**
-
-        **help**: 802.3ad system mac address
-
-
-        **required**: False
-
-        **default**: 00:00:00:00:00:00
-
-        **example**:
-            bond-ad-sys-mac-addr 00:00:00:00:00:00
-
-
-
-    **address**: address configuration module for interfaces
-
-
-      **broadcast**
-
-        **help**: broadcast address
-
-
-        **required**: False
-
-        **example**:
-            broadcast 10.0.1.255
-
-
-      **hwaddress**
-
-        **help**: hw address
-
-
-        **required**: False
-
-        **example**:
-            hwaddress 44:38:39:00:27:b8
-
-
-      **alias**
-
-        **help**: description/alias
-
-
-        **required**: False
-
-        **example**:
-            alias testnetwork
-
-
-      **address**
-
-        **help**: ipv4 or ipv6 addresses
-
-
-        **required**: False
-
-        **example**:
-            address 10.0.12.3/24
-
-            address 2000:1000:1000:1000:3::5/128
-
-
-      **scope**
-
-        **help**: scope
-
-
-        **required**: False
-
-        **example**:
-            scope host
-
-
-      **address-purge**
-
-        **help**: purge existing addresses. By default any existing ip
-        addresses on an interface are purged to match persistent addre
-        sses in the interfaces file. Set this attribute to 'no'if you 
-        want to preserve existing addresses
-
-
-        **required**: False
-
-        **default**: yes
-
-        **example**:
-            address-purge yes/no
-
-
-      **preferred-lifetime**
-
-        **help**: preferred lifetime
-
-
-        **required**: False
-
-        **example**:
-            preferred-lifetime forever
-
-            preferred-lifetime 10
-
-
-      **gateway**
-
-        **help**: default gateway
-
-
-        **required**: False
-
-        **example**:
-            gateway 255.255.255.0
-
-
-      **mtu**
-
-        **help**: interface mtu
-
-
-        **required**: False
-
-        **default**: 1500
-
-        **example**:
-            mtu 1600
-
-
-
-    **addressvirtual**: address module configures virtual addresses fo
-    r interfaces. It creates a macvlan interface for every mac ip addr
-    ess-virtual line
-
-
-      **address-virtual**
-
-        **help**: bridge router virtual mac and ip
-
-
-        **required**: False
-
-        **example**:
-            address-virtual 00:11:22:33:44:01 11.0.1.254/24 11.0.1.254/24
-
-
-
-    **vxlan**: vxlan module configures vxlan interfaces.
-
-
-      **vxlan-learning**
-
-        **help**: vxlan learning on/off
-
-
-        **required**: False
-
-        **default**: on
-
-        **example**:
-            vxlan-learning off
-
-
-      **vxlan-id**
-
-        **help**: vxlan id
-
-
-        **required**: True
-
-        **example**:
-            vxlan-id 100
-
-
-      **vxlan-remoteip**
-
-        **help**: vxlan remote ip
-
-
-        **required**: False
-
-        **example**:
-            vxlan-remoteip 172.16.22.127
-
-
-      **vxlan-svcnodeip**
-
-        **help**: vxlan id
-
-
-        **required**: False
-
-        **example**:
-            vxlan-svcnodeip 172.16.22.125
-
-
-      **vxlan-local-tunnelip**
-
-        **help**: vxlan local tunnel ip
-
-
-        **required**: False
-
-        **example**:
-            vxlan-local-tunnelip 172.16.20.103
-
-
-
-SEE ALSO
-========
-    interfaces(5),
-    ifup(8),
-    ip(8),
-    mstpctl(8),
-    brctl(8),
-    ethtool(8),
-    clagctl(8)
diff --git a/man.rst/interfaces.5.rst b/man.rst/interfaces.5.rst
deleted file mode 100644 (file)
index 9247c98..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-==========
-interfaces
-==========
-
---------------------------------------------
-network interface configuration for ifupdown
---------------------------------------------
-
-:Author: Roopa Prabhu <roopa@cumulusnetworks.com>
-:Date:   2014-02-05
-:Copyright: Copyright 2014 Cumulus Networks, Inc.  All rights reserved.
-:Version: 0.1
-:Manual section: 5 
-
-DESCRIPTION
-===========
-    By default, ifupdown2.conf sets **/etc/network/interfaces** as the
-    network interface configuration file.  This file contains information
-    for the **ifup(8)**, **ifdown(8)** and **ifquery(8)** commands.
-
-    This is where you configure how your system is connected to the network.
-
-    Lines starting with # are ignored. Note that end-of-line comments are
-    NOT supported, comments must be on a line of their own.
-
-    A line may be extended across multiple lines by making the last character
-    a backslash.
-
-    The file consists of zero or more "iface", "auto",  "allow-"
-    and "source" stanzas. Here is an example::
-
-        auto lo eth0
-        allow-hotplug eth1
-
-        iface lo inet loopback
-
-        source /etc/network/interfaces.d/bridges
-
-        iface eth0 inet static
-            address 192.168.1.1/24
-            up flush-mail
-
-        iface eth1 inet dhcp
-
-    Lines beginning with the word "auto" are used to identify the physical
-    interfaces to be brought up when ifup is run with the -a option.
-    (This option is used by the system boot scripts.) Physical interface names
-    should follow the word "auto" on the same line.  There can be  multiple
-    "auto"  stanzas.
-
-    Lines beginning with "allow-" are  used  to  identify  interfaces  that
-    should  be  brought  up automatically by various subsystems. This may be
-    done using a command such as "ifup --allow=hotplug  eth0  eth1",  which
-    will  only  bring up eth0 or eth1 if it is listed in an "allow-hotplug"
-    line. Note that "allow-auto" and "auto" are synonyms.
-
-    Lines beginning with "source" are used to include  stanzas  from  other
-    files, so configuration can be split into many files. The word "source"
-    is followed by the path of file to be sourced. Shell wildcards  can  be
-    used. Currently only supports absolute
-    path names.
-
-    iface is normally given a interface name as its first non-option
-    argument. 
-
-    The interface name is followed by the name of the address family that the
-    interface uses. This will be "inet" for TCP/IP networking and inet6 for
-    ipv6. Following that is the name of the method used to configure the
-    interface.
-
-    ifupdown supports iface stanzas without a family or a method. This enables
-    using the same stanza for inet and inet6 family addresses. And the method
-    defaults to "static"
-
-    Additional interface options/attributes can be given on subsequent lines
-    in the iface stanza. These options come from addon modules. see
-    **ifupdown-addons-interfaces(5)** for these options.
-
-    example bridge interface with additional attributes listed in the
-    **ifupdown-addons-interfaces(5)** man page::
-
-        auto br0
-        iface br0
-            address 12.0.0.4/24
-            address 2000:1000:1000:1000:3::5/128
-            bridge-ports swp1 swp2 swp3
-            bridge-stp on
-
-    ifupdown supports python-mako style templates in the interfaces file.
-    See examples section for details.
-
-    See **/usr/share/doc/python-ifupdown2/examples/** for **interfaces(5)**
-    file examples and interfaces file generation scripts.
-
-METHODS
-=======
-    Both **inet** and **inet6** address family interfaces can use the following
-    methods (However they are not required):
-
-    The loopback Method
-           This method may be used to define the loopback interface.
-
-    The static Method
-           This method may be used to define ethernet interfaces with
-           statically allocated addresses.
-
-    The dhcp Method
-           This method may be used to obtain an address via DHCP.
-
-BUILTIN INTERFACES
-==================
-    **iface** sections for some interfaces like physical interfaces or vlan
-    interfaces in dot notation (like eth1.100) are understood by ifupdown.
-    These interfaces do not need an entry in the interfaces file if
-    they are dependents of other interfaces and don't need any specific
-    configurations like addresses etc.
-
-EXAMPLES
-========
-    Sample /etc/network/interfaces file::
-
-        auto lo
-        iface lo
-            address 192.168.2.0/24
-            address 2001:dee:eeee:1::4/128
-
-        auto eth0
-        iface eth0 inet dhcp
-
-        auto eth1
-        iface eth1 inet manual
-            address 192.168.2.0/24
-            address 2001:dee:eeee:1::4/128
-
-        # source files from a directory /etc/network/interfaces.d
-        source /etc/network/interfaces.d/*
-
-        # Using mako style templates
-        % for v in [11,12]:
-            auto vlan${v}
-            iface vlan${v} inet static
-                address 10.20.${v}.3/24
-        % endfor
-
-    For additional syntax and examples see **ifupdown-addons-interfaces(5)**
-
-FILES
-=====
-    configuration file defined in ifupdown2.conf (default /etc/network/interfaces)
-
-SEE ALSO
-========
-    ifupdown-addons-interfaces(5),
-    ifup(8),
-    ifquery(8),
-    ifreload(8)
diff --git a/man/interfaces.5 b/man/interfaces.5
deleted file mode 100644 (file)
index e5d15a4..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-.\" Man page generated from reStructeredText.
-.
-.TH INTERFACES 5 "2014-02-05" "0.1" ""
-.SH NAME
-interfaces \- network interface configuration for ifupdown
-.
-.nr rst2man-indent-level 0
-.
-.de1 rstReportMargin
-\\$1 \\n[an-margin]
-level \\n[rst2man-indent-level]
-level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
--
-\\n[rst2man-indent0]
-\\n[rst2man-indent1]
-\\n[rst2man-indent2]
-..
-.de1 INDENT
-.\" .rstReportMargin pre:
-. RS \\$1
-. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
-. nr rst2man-indent-level +1
-.\" .rstReportMargin post:
-..
-.de UNINDENT
-. RE
-.\" indent \\n[an-margin]
-.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.nr rst2man-indent-level -1
-.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
-.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
-..
-.SH DESCRIPTION
-.INDENT 0.0
-.INDENT 3.5
-\fB/etc/network/interfaces\fP contains network interface configuration
-information for the \fBifup(8)\fP, \fBifdown(8)\fP and \fBifquery(8)\fP commands.
-.sp
-This is where you configure how your system is connected to the network.
-.sp
-Lines starting with # are ignored. Note that end\-of\-line comments are
-NOT supported, comments must be on a line of their own.
-.sp
-A line may be extended across multiple lines by making the last character
-a backslash.
-.sp
-The file consists of zero or more "iface", "auto",  "allow\-"
-and "source" stanzas. Here is an example:
-.sp
-.nf
-.ft C
-auto lo eth0
-allow\-hotplug eth1
-
-iface lo inet loopback
-
-source /etc/network/interfaces.d/bridges
-
-iface eth0 inet static
-    address 192.168.1.1/24
-    up flush\-mail
-
-iface eth1 inet dhcp
-.ft P
-.fi
-.sp
-Lines beginning with the word "auto" are used to identify the physical
-interfaces to be brought up when ifup is run with the \-a option.
-(This option is used by the system boot scripts.) Physical interface names
-should follow the word "auto" on the same line.  There can be  multiple
-"auto"  stanzas.
-.sp
-Lines beginning with "allow\-" are  used  to  identify  interfaces  that
-should  be  brought  up automatically by various subsystems. This may be
-done using a command such as "ifup \-\-allow=hotplug  eth0  eth1",  which
-will  only  bring up eth0 or eth1 if it is listed in an "allow\-hotplug"
-line. Note that "allow\-auto" and "auto" are synonyms.
-.sp
-Lines beginning with "source" are used to include  stanzas  from  other
-files, so configuration can be split into many files. The word "source"
-is followed by the path of file to be sourced. Shell wildcards  can  be
-used. Currently only supports absolute
-path names.
-.sp
-iface is normally given a interface name as its first non\-option
-argument.
-.sp
-The interface name is followed by the name of the address family that the
-interface uses. This will be "inet" for TCP/IP networking and inet6 for
-ipv6. Following that is the name of the method used to configure the
-interface.
-.sp
-ifupdown supports iface stanzas without a family or a method. This enables
-using the same stanza for inet and inet6 family addresses. And the method
-defaults to "static"
-.sp
-Additional interface options/attributes can be given on subsequent lines
-in the iface stanza. These options come from addon modules. see
-\fBifupdown\-addons\-interfaces(5)\fP for these options.
-.sp
-example bridge interface with additional attributes listed in the
-\fBifupdown\-addons\-interfaces(5)\fP man page:
-.sp
-.nf
-.ft C
-auto br0
-iface br0
-    address 12.0.0.4/24
-    address 2000:1000:1000:1000:3::5/128
-    bridge\-ports swp1 swp2 swp3
-    bridge\-stp on
-.ft P
-.fi
-.sp
-ifupdown supports python\-mako style templates in the interfaces file.
-See examples section for details.
-.sp
-See \fB/usr/share/doc/python\-ifupdown2/examples/\fP for \fBinterfaces(5)\fP
-file examples and interfaces file generation scripts.
-.UNINDENT
-.UNINDENT
-.SH METHODS
-.INDENT 0.0
-.INDENT 3.5
-Both \fBinet\fP and \fBinet6\fP address family interfaces can use the following
-methods (However they are not required):
-.INDENT 0.0
-.TP
-.B The loopback Method
-This method may be used to define the loopback interface.
-.TP
-.B The static Method
-This method may be used to define ethernet interfaces with
-statically allocated addresses.
-.TP
-.B The dhcp Method
-This method may be used to obtain an address via DHCP.
-.UNINDENT
-.UNINDENT
-.UNINDENT
-.SH BUILTIN INTERFACES
-.INDENT 0.0
-.INDENT 3.5
-\fBiface\fP sections for some interfaces like physical interfaces or vlan
-interfaces in dot notation (like eth1.100) are understood by ifupdown.
-These interfaces do not need an entry in the interfaces file if
-they are dependents of other interfaces and dont need any specific
-configurations like addresses etc.
-.UNINDENT
-.UNINDENT
-.SH EXAMPLES
-.INDENT 0.0
-.INDENT 3.5
-Sample /etc/network/interfaces file:
-.sp
-.nf
-.ft C
-auto lo
-iface lo
-    address 192.168.2.0/24
-    address 2001:dee:eeee:1::4/128
-
-auto eth0
-iface eth0 inet dhcp
-
-auto eth1
-iface eth1 inet manual
-    address 192.168.2.0/24
-    address 2001:dee:eeee:1::4/128
-
-# source files from a directory /etc/network/interfaces.d
-source /etc/network/interfaces.d/*
-
-# Using mako style templates
-% for v in [11,12]:
-    auto vlan${v}
-    iface vlan${v} inet static
-        address 10.20.${v}.3/24
-% endfor
-.ft P
-.fi
-.sp
-For additional syntax and examples see \fBifupdown\-addons\-interfaces(5)\fP
-.UNINDENT
-.UNINDENT
-.SH FILES
-.INDENT 0.0
-.INDENT 3.5
-/etc/network/interfaces
-.UNINDENT
-.UNINDENT
-.SH SEE ALSO
-.INDENT 0.0
-.INDENT 3.5
-ifupdown\-addons\-interfaces(5),
-ifup(8),
-ifquery(8),
-ifreload(8)
-.UNINDENT
-.UNINDENT
-.SH AUTHOR
-Roopa Prabhu <roopa@cumulusnetworks.com>
-.SH COPYRIGHT
-Copyright 2014 Cumulus Networks, Inc.  All rights reserved.
-.\" Generated by docutils manpage writer.
-.\" 
-.
diff --git a/nlmanager/README b/nlmanager/README
deleted file mode 100644 (file)
index 8adac90..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-DO NOT EDIT NLMANAGER SOURCES.
-This is a mirror copy of python-nlmanager sources.
-It was extracted and directly included here to support some usecases where
-user don't have python-nlmanager already installed on their system. So we
-decided to have local copy and build with it. It is the mainter responsability
-to keep an updated version of nlmanager.
\ No newline at end of file
diff --git a/nlmanager/__init__.py b/nlmanager/__init__.py
deleted file mode 100644 (file)
index 8b13789..0000000
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/nlmanager/nllistener.py b/nlmanager/nllistener.py
deleted file mode 100644 (file)
index 46ffe7c..0000000
+++ /dev/null
@@ -1,551 +0,0 @@
-#!/usr/bin/env python
-
-from nlpacket import *
-from nlmanager import NetlinkManager
-from select import select
-from struct import pack, unpack, calcsize
-from threading import Thread, Event, Lock
-from Queue import Queue
-import logging
-import socket
-
-log = logging.getLogger(__name__)
-
-
-class NetlinkListener(Thread):
-
-    def __init__(self, manager, groups, pid_offset=1):
-        """
-        groups controls what types of messages we are interested in hearing
-        To get everything pass:
-            RTMGRP_LINK | \
-            RTMGRP_IPV4_IFADDR | \
-            RTMGRP_IPV4_ROUTE | \
-            RTMGRP_IPV6_IFADDR | \
-            RTMGRP_IPV6_ROUTE
-        """
-        Thread.__init__(self)
-        self.manager = manager
-        self.shutdown_event = Event()
-        self.groups = groups
-        self.pid_offset = pid_offset
-
-    def __str__(self):
-        return 'NetlinkListener'
-
-    def run(self):
-        manager = self.manager
-        header_PACK = 'IHHII'
-        header_LEN = calcsize(header_PACK)
-
-        # The RX socket is used to listen to all netlink messages that fly by
-        # as things change in the kernel. We need a very large SO_RCVBUF here
-        # else we tend to miss messages.
-        # PID_MAX_LIMIT is 2^22 allowing 1024 sockets per-pid. We default to 
-        # use 2 in the upper space (top 10 bits) instead of 0 to avoid conflicts
-        # with the netlink manager which always attempts to bind with the pid.
-        self.rx_socket = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, 0)
-        self.rx_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 10000000)
-        self.rx_socket.bind((manager.pid | (self.pid_offset << 22), self.groups))
-        self.rx_socket_prev_seq = {}
-
-        if not manager.tx_socket:
-            manager.tx_socket_allocate()
-
-        my_sockets = (manager.tx_socket, self.rx_socket)
-
-        socket_string = {
-            manager.tx_socket: "TX",
-            self.rx_socket: "RX"
-        }
-
-        supported_messages = (RTM_NEWLINK, RTM_DELLINK, RTM_NEWADDR,
-                              RTM_DELADDR, RTM_NEWNEIGH, RTM_DELNEIGH,
-                              RTM_NEWROUTE, RTM_DELROUTE)
-
-        ignore_messages = (RTM_GETLINK, RTM_GETADDR, RTM_GETNEIGH,
-                           RTM_GETROUTE, RTM_GETQDISC, NLMSG_ERROR, NLMSG_DONE)
-
-        while True:
-
-            if self.shutdown_event.is_set():
-                log.info("%s: shutting down" % self)
-                return
-
-            # Only block for 1 second so we can wake up to see if shutdown_event is set
-            try:
-                (readable, writeable, exceptional) = select(my_sockets, [], my_sockets, 1)
-            except Exception as e:
-                log.error('select() error: ' + str(e))
-                continue
-
-            if not readable:
-                continue
-
-            set_alarm = False
-            set_tx_socket_rxed_ack_alarm = False
-
-            for s in readable:
-                data = []
-
-                try:
-                    data = s.recv(4096)
-                except Exception as e:
-                    log.error('recv() error: ' + str(e))
-                    continue
-
-                total_length = len(data)
-                while data:
-
-                    # Extract the length, etc from the header
-                    (length, msgtype, flags, seq, pid) = unpack(header_PACK, data[:header_LEN])
-
-                    log.debug('%s %s: RXed %s seq %d, pid %d, %d bytes (%d total)' %
-                              (self, socket_string[s], NetlinkPacket.type_to_string[msgtype],
-                               seq, pid, length, total_length))
-                    possible_ack = False
-
-                    if msgtype == NLMSG_DONE:
-                        possible_ack = True
-
-                    elif msgtype == NLMSG_ERROR:
-                        possible_ack = True
-
-                        # The error code is a signed negative number.
-                        error_code = abs(unpack('=i', data[header_LEN:header_LEN+4])[0])
-                        msg = Error(msgtype, True)
-                        msg.decode_packet(length, flags, seq, pid, data)
-
-                        if error_code:
-                            log.debug("%s %s: RXed NLMSG_ERROR code %s (%d)" % (self, socket_string[s], msg.error_to_string.get(error_code), error_code))
-
-                    if possible_ack and seq == manager.target_seq and pid == manager.target_pid:
-                        log.debug("%s %s: Setting RXed ACK alarm for seq %d, pid %d" %
-                                  (self, socket_string[s], seq, pid))
-                        set_tx_socket_rxed_ack_alarm = True
-
-                    # Put the message on the manager's netlinkq
-                    if msgtype in supported_messages:
-                        set_alarm = True
-                        manager.netlinkq.append((msgtype, length, flags, seq, pid, data[0:length]))
-
-                    # There are certain message types we do not care about
-                    # (RTM_GETs for example)
-                    elif msgtype in ignore_messages:
-                        pass
-
-                    # And there are certain message types we have not added
-                    # support for yet (QDISC). Log an error for these just
-                    # as a reminder to add support for them.
-                    else:
-                        if msgtype in NetlinkPacket.type_to_string:
-                            log.warning('%s %s: RXed unsupported message %s (type %d)' %
-                                        (self, socket_string[s], NetlinkPacket.type_to_string[msgtype], msgtype))
-                        else:
-                            log.warning('%s %s: RXed unknown message type %d' %
-                                        (self, socket_string[s], msgtype))
-
-                    # Track the previous PID sequence number for RX and TX sockets
-                    if s == self.rx_socket:
-                        prev_seq = self.rx_socket_prev_seq
-                    elif s == manager.tx_socket:
-                        prev_seq = manager.tx_socket_prev_seq
-
-                    if pid in prev_seq and prev_seq[pid] and prev_seq[pid] != seq and (prev_seq[pid]+1 != seq):
-                        log.debug('%s %s: went from seq %d to %d' % (self, socket_string[s], prev_seq[pid], seq))
-                    prev_seq[pid] = seq
-
-                    data = data[length:]
-
-            if set_tx_socket_rxed_ack_alarm:
-                manager.target_lock.acquire()
-                manager.target_seq = None
-                manager.target_pid = None
-                manager.target_lock.release()
-                manager.tx_socket_rxed_ack.set()
-
-            if set_alarm:
-                manager.workq.put(('SERVICE_NETLINK_QUEUE', None))
-                manager.alarm.set()
-
-        self.rx_socket.close()
-
-
-class NetlinkManagerWithListener(NetlinkManager):
-
-    def __init__(self, groups, start_listener=True, use_color=True):
-        NetlinkManager.__init__(self, use_color=use_color)
-        self.groups = groups
-        self.workq = Queue()
-        self.netlinkq = []
-        self.alarm = Event()
-        self.shutdown_event = Event()
-        self.tx_socket_rxed_ack = Event()
-        self.tx_socket_rxed_ack.clear()
-        self.target_seq = None
-        self.target_pid = None
-        self.target_seq_pid_debug = False
-        self.target_lock = Lock()
-        self.tx_socket_prev_seq = {}
-        self.debug_listener = False
-        self.debug_seq_pid = {}
-        self.ifname_by_index = {}
-        self.blacklist_filter = {}
-        self.whitelist_filter = {}
-
-        # Listen to netlink messages
-        if start_listener:
-            self.listener = NetlinkListener(self, self.groups)
-            self.listener.start()
-        else:
-            self.listener = None
-
-    def __str__(self):
-        return 'NetlinkManagerWithListener'
-
-    def signal_term_handler(self, signal, frame):
-        log.info("NetlinkManagerWithListener: Caught SIGTERM")
-
-        if self.listener:
-            self.listener.shutdown_event.set()
-
-        self.shutdown_flag = True  # For NetlinkManager shutdown
-        self.shutdown_event.set()
-        self.alarm.set()
-
-    def signal_int_handler(self, signal, frame):
-        log.info("NetlinkManagerWithListener: Caught SIGINT")
-
-        if self.listener:
-            self.listener.shutdown_event.set()
-
-        self.shutdown_flag = True  # For NetlinkManager shutdown
-        self.shutdown_event.set()
-        self.alarm.set()
-
-    def tx_nlpacket_get_response(self, nlpacket):
-        """
-        TX the message and wait for an ack
-        """
-
-        # NetlinkListener looks at the manager's target_seq and target_pid
-        # to know when we've RXed the ack that we want
-        self.target_lock.acquire()
-        self.target_seq = nlpacket.seq
-        self.target_pid = nlpacket.pid
-        self.target_lock.release()
-
-        if not self.tx_socket:
-            self.tx_socket_allocate()
-
-        log.debug('%s TX: TXed %s seq %d, pid %d, %d bytes' %
-                   (self,  NetlinkPacket.type_to_string[nlpacket.msgtype],
-                    nlpacket.seq, nlpacket.pid, nlpacket.length))
-
-        self.tx_socket.sendall(nlpacket.message)
-
-        # Wait for NetlinkListener to RX an ACK or DONE for this (seq, pid)
-        self.tx_socket_rxed_ack.wait()
-        self.tx_socket_rxed_ack.clear()
-
-    # These are here to show some basic examples of how one might react to RXing
-    # various netlink message types. Odds are our child class will redefine these
-    # to do more than log a message.
-    def rx_rtm_newlink(self, msg):
-        log.debug("RXed RTM_NEWLINK seq %d, pid %d, %d bytes, for %s, state %s" %
-                  (msg.seq, msg.pid, msg.length, msg.get_attribute_value(msg.IFLA_IFNAME), "up" if msg.is_up() else "down"))
-
-    def rx_rtm_dellink(self, msg):
-        log.debug("RXed RTM_DELLINK seq %d, pid %d, %d bytes, for %s, state %s" %
-                  (msg.seq, msg.pid, msg.length, msg.get_attribute_value(msg.IFLA_IFNAME), "up" if msg.is_up() else "down"))
-
-    def rx_rtm_newaddr(self, msg):
-        log.debug("RXed RTM_NEWADDR seq %d, pid %d, %d bytes, for %s/%d on %s" %
-                  (msg.seq, msg.pid, msg.length, msg.get_attribute_value(msg.IFA_ADDRESS), msg.prefixlen, self.ifname_by_index.get(msg.ifindex)))
-
-    def rx_rtm_deladdr(self, msg):
-        log.debug("RXed RTM_DELADDR seq %d, pid %d, %d bytes, for %s/%d on %s" %
-                  (msg.seq, msg.pid, msg.length, msg.get_attribute_value(msg.IFA_ADDRESS), msg.prefixlen, self.ifname_by_index.get(msg.ifindex)))
-
-    def rx_rtm_newneigh(self, msg):
-        log.debug("RXed RTM_NEWNEIGH seq %d, pid %d, %d bytes, for %s on %s" %
-                  (msg.seq, msg.pid, msg.length, msg.get_attribute_value(msg.NDA_DST), self.ifname_by_index.get(msg.ifindex)))
-
-    def rx_rtm_delneigh(self, msg):
-        log.debug("RXed RTM_DELNEIGH seq %d, pid %d, %d bytes, for %s on %s" %
-                  (msg.seq, msg.pid, msg.length, msg.get_attribute_value(msg.NDA_DST), self.ifname_by_index.get(msg.ifindex)))
-
-    def rx_rtm_newroute(self, msg):
-        log.debug("RXed RTM_NEWROUTE seq %d, pid %d, %d bytes, for %s%s" %
-                  (msg.seq, msg.pid, msg.length, msg.get_prefix_string(), msg.get_nexthops_string(self.ifname_by_index)))
-
-    def rx_rtm_delroute(self, msg):
-        log.debug("RXed RTM_DELROUTE seq %d, pid %d, %d bytes, for %s%s" %
-                  (msg.seq, msg.pid, msg.length, msg.get_prefix_string(), msg.get_nexthops_string(self.ifname_by_index)))
-
-    # Note that tx_nlpacket_get_response will block until NetlinkListener has RXed
-    # an Ack/DONE for the message we TXed
-    def get_all_addresses(self):
-        family = socket.AF_UNSPEC
-        debug = RTM_GETADDR in self.debug
-
-        addr = Address(RTM_GETADDR, debug, use_color=self.use_color)
-        addr.flags = NLM_F_REQUEST | NLM_F_DUMP
-        addr.body = pack('Bxxxi', family, 0)
-        addr.build_message(self.sequence.next(), self.pid)
-
-        if debug:
-            self.debug_seq_pid[(addr.seq, addr.pid)] = True
-
-        self.tx_nlpacket_get_response(addr)
-
-    def get_all_links(self):
-        family = socket.AF_UNSPEC
-        debug = RTM_GETLINK in self.debug
-
-        link = Link(RTM_GETLINK, debug, use_color=self.use_color)
-        link.flags = NLM_F_REQUEST | NLM_F_DUMP
-        link.body = pack('Bxxxiii', family, 0, 0, 0)
-        link.build_message(self.sequence.next(), self.pid)
-
-        if debug:
-            self.debug_seq_pid[(link.seq, link.pid)] = True
-
-        self.tx_nlpacket_get_response(link)
-
-    def get_all_neighbors(self):
-        family = socket.AF_UNSPEC
-        debug = RTM_GETNEIGH in self.debug
-
-        neighbor = Neighbor(RTM_GETNEIGH, debug, use_color=self.use_color)
-        neighbor.flags = NLM_F_REQUEST | NLM_F_DUMP
-        neighbor.body = pack('Bxxxii', family, 0, 0)
-        neighbor.build_message(self.sequence.next(), self.pid)
-
-        if debug:
-            self.debug_seq_pid[(neighbor.seq, neighbor.pid)] = True
-
-        self.tx_nlpacket_get_response(neighbor)
-
-    def get_all_routes(self):
-        family = socket.AF_UNSPEC
-        debug = RTM_GETROUTE in self.debug
-
-        route = Route(RTM_GETROUTE, debug, use_color=self.use_color)
-        route.flags = NLM_F_REQUEST | NLM_F_DUMP
-        route.body = pack('Bxxxii', family, 0, 0)
-        route.build_message(self.sequence.next(), self.pid)
-
-        if debug:
-            self.debug_seq_pid[(route.seq, route.pid)] = True
-
-        self.tx_nlpacket_get_response(route)
-
-    def nested_attributes_match(self, msg, attr_filter):
-        """
-        attr_filter will be a dictionary such as:
-        attr_filter = {
-            Link.IFLA_LINKINFO: {
-                Link.IFLA_INFO_KIND: 'vlan'
-            }
-        }
-        """
-        for (key, value) in attr_filter.items():
-            if type(value) is dict:
-                if not self.nested_attributes_match(msg, value):
-                    return False
-            else:
-                attr_value = msg.get_attribute_value(key)
-                if attr_value != value:
-                    return False
-        return True
-
-    def filter_rule_matches(self, msg, rule):
-        field = rule[0]
-        options = rule[1:]
-
-        if field == 'IFINDEX':
-            ifindex = options[0]
-
-            if msg.ifindex == ifindex:
-                return True
-
-        elif field == 'ATTRIBUTE':
-            (attr_type, target_value) = options[0:2]
-            attr_value = msg.get_attribute_value(attr_type)
-
-            if attr_value == target_value:
-                return True
-
-        elif field == 'NESTED_ATTRIBUTE':
-            if self.nested_attributes_match(msg, options[0]):
-                return True
-
-        elif field == 'FAMILY':
-            family = options[0]
-
-            if msg.family == family:
-                return True
-        else:
-            raise Exception("Add support to filter based on %s" % field)
-
-        return False
-
-    def filter_permit(self, msg):
-        """
-        Return True if our whitelist/blacklist filters permit this netlink msg
-        """
-        if msg.msgtype in self.whitelist_filter:
-            found_it = False
-
-            for rule in self.whitelist_filter[msg.msgtype]:
-                if self.filter_rule_matches(msg, rule):
-                    found_it = True
-                    break
-
-            return found_it
-
-        elif msg.msgtype in self.blacklist_filter:
-            for rule in self.blacklist_filter[msg.msgtype]:
-                if self.filter_rule_matches(msg, rule):
-                    return False
-            return True
-
-        else:
-            return True
-
-    def _filter_update(self, add, filter_type, msgtype, filter_guts):
-        assert filter_type in ('whitelist', 'blacklist'), "whitelist and blacklist are the only supported filter options"
-
-        if add:
-            if filter_type == 'whitelist':
-
-                # Keep things simple, do not allow both whitelist and blacklist
-                if self.blacklist_filter and self.blacklist_filter.get(msgtype):
-                    raise Exception("whitelist and blacklist filters cannot be used at the same time")
-
-                if msgtype not in self.whitelist_filter:
-                    self.whitelist_filter[msgtype] = []
-                self.whitelist_filter[msgtype].append(filter_guts)
-
-            elif filter_type == 'blacklist':
-
-                # Keep things simple, do not allow both whitelist and blacklist
-                if self.whitelist_filter and self.whitelist_filter.get(msgtype):
-                    raise Exception("whitelist and blacklist filters cannot be used at the same time")
-
-                if msgtype not in self.blacklist_filter:
-                    self.blacklist_filter[msgtype] = []
-                self.blacklist_filter[msgtype].append(filter_guts)
-
-        else:
-            if filter_type == 'whitelist':
-                if msgtype in self.whitelist_filter:
-                    self.whitelist_filter[msgtype].remove(filter_guts)
-
-                    if not self.whitelist_filter[msgtype]:
-                        del self.whitelist_filter[msgtype]
-
-            elif filter_type == 'blacklist':
-                if msgtype in self.blacklist_filter:
-                    self.blacklist_filter[msgtype].remove(filter_guts)
-
-                    if not self.blacklist_filter[msgtype]:
-                        del self.blacklist_filter[msgtype]
-
-    def filter_by_address_family(self, add, filter_type, msgtype, family):
-        self._filter_update(add, filter_type, msgtype, ('FAMILY', family))
-
-    def filter_by_ifindex(self, add, filter_type, msgtype, ifindex):
-        self._filter_update(add, filter_type, msgtype, ('IFINDEX', ifindex))
-
-    def filter_by_attribute(self, add, filter_type, msgtype, attribute, attribute_value):
-        self._filter_update(add, filter_type, msgtype, ('ATTRIBUTE', attribute, attribute_value))
-
-    def filter_by_nested_attribute(self, add, filter_type, msgtype, attr_filter):
-        self._filter_update(add, filter_type, msgtype, ('NESTED_ATTRIBUTE', attr_filter))
-
-    def service_netlinkq(self):
-        msg_count = {}
-        processed = 0
-
-        for (msgtype, length, flags, seq, pid, data) in self.netlinkq:
-            processed += 1
-
-            # If this is a reply to a TX message that debugs were enabled for then debug the reply
-            if (seq, pid) in self.debug_seq_pid:
-                debug = True
-            else:
-                debug = self.debug_this_packet(msgtype)
-
-            if msgtype == RTM_NEWLINK or msgtype == RTM_DELLINK:
-                msg = Link(msgtype, debug, use_color=self.use_color)
-
-            elif msgtype == RTM_NEWADDR or msgtype == RTM_DELADDR:
-                msg = Address(msgtype, debug, use_color=self.use_color)
-
-            elif msgtype == RTM_NEWNEIGH or msgtype == RTM_DELNEIGH:
-                msg = Neighbor(msgtype, debug, use_color=self.use_color)
-
-            elif msgtype == RTM_NEWROUTE or msgtype == RTM_DELROUTE:
-                msg = Route(msgtype, debug, use_color=self.use_color)
-
-            else:
-                log.warning('RXed unknown netlink message type %s' % msgtype)
-                continue
-
-            msg.decode_packet(length, flags, seq, pid, data)
-
-            if not self.filter_permit(msg):
-                continue
-
-            if debug:
-                msg.dump()
-
-            # Only used for printing debugs about how many we RXed of each type
-            if msg.msgtype not in msg_count:
-                msg_count[msg.msgtype] = 0
-            msg_count[msg.msgtype] += 1
-
-            # Call the appropriate handler method based on the msgtype.  The handler
-            # functions are defined in our child class.
-            if msg.msgtype == RTM_NEWLINK:
-
-                # We will use ifname_by_index to display the interface name in debug output
-                self.ifname_by_index[msg.ifindex] = msg.get_attribute_value(msg.IFLA_IFNAME)
-                self.rx_rtm_newlink(msg)
-
-            elif msg.msgtype == RTM_DELLINK:
-
-                # We will use ifname_by_index to display the interface name in debug output
-                if msg.ifindex in self.ifname_by_index:
-                    del self.ifname_by_index[msg.ifindex]
-                self.rx_rtm_dellink(msg)
-
-            elif msg.msgtype == RTM_NEWADDR:
-                self.rx_rtm_newaddr(msg)
-
-            elif msg.msgtype == RTM_DELADDR:
-                self.rx_rtm_deladdr(msg)
-
-            elif msg.msgtype == RTM_NEWNEIGH:
-                self.rx_rtm_newneigh(msg)
-
-            elif msg.msgtype == RTM_DELNEIGH:
-                self.rx_rtm_delneigh(msg)
-
-            elif msg.msgtype == RTM_NEWROUTE:
-                self.rx_rtm_newroute(msg)
-
-            elif msg.msgtype == RTM_DELROUTE:
-                self.rx_rtm_delroute(msg)
-
-            else:
-                log.warning('RXed unknown netlink message type %s' % msgtype)
-
-        if processed:
-            self.netlinkq = self.netlinkq[processed:]
-
-        # too chatty
-        # for msgtype in msg_count:
-        #     log.debug('RXed %d %s messages' % (msg_count[msgtype], NetlinkPacket.type_to_string[msgtype]))
diff --git a/nlmanager/nlmanager.py b/nlmanager/nlmanager.py
deleted file mode 100644 (file)
index 8299a46..0000000
+++ /dev/null
@@ -1,852 +0,0 @@
-#!/usr/bin/env python
-
-from collections import OrderedDict
-from ipaddr import IPv4Address, IPv6Address
-from nlpacket import *
-from select import select
-from struct import pack, unpack
-import logging
-import os
-import socket
-
-log = logging.getLogger(__name__)
-
-
-class NetlinkError(Exception):
-    pass
-
-
-class NetlinkNoAddressError(NetlinkError):
-    pass
-
-
-class NetlinkInterruptedSystemCall(NetlinkError):
-    pass
-
-
-class InvalidInterfaceNameVlanCombo(Exception):
-    pass
-
-
-class Sequence(object):
-
-    def __init__(self):
-        self._next = 0
-
-    def next(self):
-        self._next += 1
-        return self._next
-
-
-class NetlinkManager(object):
-
-    def __init__(self, pid_offset=0, use_color=True, extra_debug=False):
-        # PID_MAX_LIMIT is 2^22 allowing 1024 sockets per-pid. We default to 0
-        # in the upper space (top 10 bits), which will simply be the PID. Other
-        # NetlinkManager instantiations in the same process can choose other
-        # offsets to avoid conflicts with each other.
-        self.pid = os.getpid() | (pid_offset << 22)
-        self.sequence = Sequence()
-        self.shutdown_flag = False
-        self.ifindexmap = {}
-        self.tx_socket = None
-        self.use_color = use_color
-
-        # debugs
-        self.debug = {}
-        self.debug_link(False)
-        self.debug_address(False)
-        self.debug_neighbor(False)
-        self.debug_route(False)
-        set_extra_debug(extra_debug)
-
-    def __str__(self):
-        return 'NetlinkManager'
-
-    def signal_term_handler(self, signal, frame):
-        log.info("NetlinkManager: Caught SIGTERM")
-        self.shutdown_flag = True
-
-    def signal_int_handler(self, signal, frame):
-        log.info("NetlinkManager: Caught SIGINT")
-        self.shutdown_flag = True
-
-    def shutdown(self):
-        if self.tx_socket:
-            self.tx_socket.close()
-            self.tx_socket = None
-        log.info("NetlinkManager: shutdown complete")
-
-    def _debug_set_clear(self, msg_types, enabled):
-        """
-        Enable or disable debugs for all msgs_types messages
-        """
-
-        for x in msg_types:
-            if enabled:
-                self.debug[x] = True
-            else:
-                if x in self.debug:
-                    del self.debug[x]
-
-    def debug_link(self, enabled):
-        self._debug_set_clear((RTM_NEWLINK, RTM_DELLINK, RTM_GETLINK, RTM_SETLINK), enabled)
-
-    def debug_address(self, enabled):
-        self._debug_set_clear((RTM_NEWADDR, RTM_DELADDR, RTM_GETADDR), enabled)
-
-    def debug_neighbor(self, enabled):
-        self._debug_set_clear((RTM_NEWNEIGH, RTM_DELNEIGH, RTM_GETNEIGH), enabled)
-
-    def debug_route(self, enabled):
-        self._debug_set_clear((RTM_NEWROUTE, RTM_DELROUTE, RTM_GETROUTE), enabled)
-
-    def debug_this_packet(self, mtype):
-        if mtype in self.debug:
-            return True
-        return False
-
-    def tx_socket_allocate(self):
-        """
-        The TX socket is used for install requests, sending RTM_GETXXXX
-        requests, etc
-        """
-        self.tx_socket = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, 0)
-        self.tx_socket.bind((self.pid, 0))
-
-    def tx_nlpacket_raw(self, message):
-        """
-        TX a bunch of concatenated nlpacket.messages....do NOT wait for an ACK
-        """
-        if not self.tx_socket:
-            self.tx_socket_allocate()
-        self.tx_socket.sendall(message)
-
-    def tx_nlpacket(self, nlpacket):
-        """
-        TX a netlink packet but do NOT wait for an ACK
-        """
-        if not nlpacket.message:
-            log.error('You must first call build_message() to create the packet')
-            return
-
-        if not self.tx_socket:
-            self.tx_socket_allocate()
-        self.tx_socket.sendall(nlpacket.message)
-
-    def tx_nlpacket_get_response(self, nlpacket):
-
-        if not nlpacket.message:
-            log.error('You must first call build_message() to create the packet')
-            return
-
-        if not self.tx_socket:
-            self.tx_socket_allocate()
-        self.tx_socket.sendall(nlpacket.message)
-
-        # If nlpacket.debug is True we already printed the following in the
-        # build_message() call...so avoid printing two messages for one packet.
-        if not nlpacket.debug:
-            log.debug("TXed %12s, pid %d, seq %d, %d bytes" %
-                     (nlpacket.get_type_string(), nlpacket.pid, nlpacket.seq, nlpacket.length))
-
-        header_PACK = NetlinkPacket.header_PACK
-        header_LEN = NetlinkPacket.header_LEN
-        null_read = 0
-        nle_intr_count = 0
-        MAX_NULL_READS = 3
-        MAX_ERROR_NLE_INTR = 3
-        msgs = []
-
-        # Now listen to our socket and wait for the reply
-        while True:
-
-            if self.shutdown_flag:
-                log.info('shutdown flag is True, exiting')
-                return msgs
-
-            # Only block for 1 second so we can wake up to see if self.shutdown_flag is True
-            try:
-                (readable, writeable, exceptional) = select([self.tx_socket, ], [], [self.tx_socket, ], 1)
-            except Exception as e:
-                # 4 is Interrupted system call
-                if isinstance(e.args, tuple) and e[0] == 4:
-                    nle_intr_count += 1
-                    log.info("select() Interrupted system call %d/%d" % (nle_intr_count, MAX_ERROR_NLE_INTR))
-
-                    if nle_intr_count >= MAX_ERROR_NLE_INTR:
-                        raise NetlinkInterruptedSystemCall(error_str)
-                    else:
-                        continue
-                else:
-                    raise
-
-            if readable:
-                null_read = 0
-            else:
-                null_read += 1
-
-                # Safety net to make sure we do not spend too much time in
-                # this while True loop
-                if null_read >= MAX_NULL_READS:
-                    log.info('Socket was not readable for %d attempts' % null_read)
-                    return msgs
-                else:
-                    continue
-
-            for s in readable:
-                data = []
-
-                try:
-                    data = s.recv(4096)
-                except Exception as e:
-                    # 4 is Interrupted system call
-                    if isinstance(e.args, tuple) and e[0] == 4:
-                        nle_intr_count += 1
-                        log.info("%s: recv() Interrupted system call %d/%d" % (s, nle_intr_count, MAX_ERROR_NLE_INTR))
-
-                        if nle_intr_count >= MAX_ERROR_NLE_INTR:
-                            raise NetlinkInterruptedSystemCall(error_str)
-                        else:
-                            continue
-                    else:
-                        raise
-
-                if not data:
-                    log.info('RXed zero length data, the socket is closed')
-                    return msgs
-
-                while data:
-
-                    # Extract the length, etc from the header
-                    (length, msgtype, flags, seq, pid) = unpack(header_PACK, data[:header_LEN])
-
-                    debug_str = "RXed %12s, pid %d, seq %d, %d bytes" % (NetlinkPacket.type_to_string[msgtype], pid, seq, length)
-
-                    # This shouldn't happen but it would be nice to be aware of it if it does
-                    if pid != nlpacket.pid:
-                        log.debug(debug_str + '...we are not interested in this pid %s since ours is %s' %
-                                    (pid, nlpacket.pid))
-                        data = data[length:]
-                        continue
-
-                    if seq != nlpacket.seq:
-                        log.debug(debug_str + '...we are not interested in this seq %s since ours is %s' %
-                                    (seq, nlpacket.seq))
-                        data = data[length:]
-                        continue
-
-                    # See if we RXed an ACK for our RTM_GETXXXX
-                    if msgtype == NLMSG_DONE:
-                        log.debug(debug_str + '...this is an ACK')
-                        return msgs
-
-                    elif msgtype == NLMSG_ERROR:
-
-                        # The error code is a signed negative number.
-                        error_code = abs(unpack('=i', data[header_LEN:header_LEN+4])[0])
-                        msg = Error(msgtype, nlpacket.debug)
-                        msg.decode_packet(length, flags, seq, pid, data)
-
-                        # 0 is NLE_SUCCESS...everything else is a true error
-                        if error_code:
-                            error_code_str = msg.error_to_string.get(error_code)
-                            if error_code_str:
-                                error_str = 'Operation failed with \'%s\'' % error_code_str
-                            else:
-                                error_str = 'Operation failed with code %s' % error_code
-
-                            log.debug(debug_str)
-
-                            if error_code == Error.NLE_NOADDR:
-                                raise NetlinkNoAddressError(error_str)
-                            elif error_code == Error.NLE_INTR:
-                                nle_intr_count += 1
-                                log.debug("%s: RXed NLE_INTR Interrupted system call %d/%d" % (s, nle_intr_count, MAX_ERROR_NLE_INTR))
-
-                                if nle_intr_count >= MAX_ERROR_NLE_INTR:
-                                    raise NetlinkInterruptedSystemCall(error_str)
-
-                            else:
-                                msg.dump()
-                                if not error_code_str:
-                                    try:
-                                        # os.strerror might raise ValueError
-                                        strerror = os.strerror(error_code)
-                                        if strerror:
-                                            raise NetlinkError('Operation failed with \'%s\'' % strerror)
-                                        else:
-                                            raise NetlinkError(error_str)
-                                    except ValueError:
-                                        pass
-                                raise NetlinkError(error_str)
-                        else:
-                            log.debug('%s code NLE_SUCCESS...this is an ACK' % debug_str)
-                            return msgs
-
-                    # No ACK...create a nlpacket object and append it to msgs
-                    else:
-                        nle_intr_count = 0
-
-                        if msgtype == RTM_NEWLINK or msgtype == RTM_DELLINK:
-                            msg = Link(msgtype, nlpacket.debug, use_color=self.use_color)
-
-                        elif msgtype == RTM_NEWADDR or msgtype == RTM_DELADDR:
-                            msg = Address(msgtype, nlpacket.debug, use_color=self.use_color)
-
-                        elif msgtype == RTM_NEWNEIGH or msgtype == RTM_DELNEIGH:
-                            msg = Neighbor(msgtype, nlpacket.debug, use_color=self.use_color)
-
-                        elif msgtype == RTM_NEWROUTE or msgtype == RTM_DELROUTE:
-                            msg = Route(msgtype, nlpacket.debug, use_color=self.use_color)
-
-                        else:
-                            raise Exception("RXed unknown netlink message type %s" % msgtype)
-
-                        msg.decode_packet(length, flags, seq, pid, data)
-                        msgs.append(msg)
-
-                        if nlpacket.debug:
-                            msg.dump()
-
-                    data = data[length:]
-
-    def ip_to_afi(self, ip):
-        type_ip = type(ip)
-
-        if type_ip == IPv4Address:
-            return socket.AF_INET
-        elif type_ip == IPv6Address:
-            return socket.AF_INET6
-        else:
-            raise Exception("%s is an invalid IP type" % type_ip)
-
-    def request_dump(self, rtm_type, family, debug):
-        """
-        Issue a RTM_GETROUTE, etc with the NLM_F_DUMP flag
-        set and return the results
-        """
-
-        if rtm_type == RTM_GETADDR:
-            msg = Address(rtm_type, debug, use_color=self.use_color)
-            msg.body = pack('Bxxxi', family, 0)
-
-        elif rtm_type == RTM_GETLINK:
-            msg = Link(rtm_type, debug, use_color=self.use_color)
-            msg.body = pack('Bxxxiii', family, 0, 0, 0)
-
-        elif rtm_type == RTM_GETNEIGH:
-            msg = Neighbor(rtm_type, debug, use_color=self.use_color)
-            msg.body = pack('Bxxxii', family, 0, 0)
-
-        elif rtm_type == RTM_GETROUTE:
-            msg = Route(rtm_type, debug, use_color=self.use_color)
-            msg.body = pack('Bxxxii', family, 0, 0)
-
-        else:
-            log.error("request_dump RTM_GET %s is not supported" % rtm_type)
-            return None
-
-        msg.flags = NLM_F_REQUEST | NLM_F_DUMP
-        msg.attributes = {}
-        msg.build_message(self.sequence.next(), self.pid)
-        return self.tx_nlpacket_get_response(msg)
-
-    # ======
-    # Routes
-    # ======
-    def _routes_add_or_delete(self, add_route, routes, ecmp_routes, table, protocol, route_scope, route_type):
-
-        def tx_or_concat_message(total_message, route):
-            """
-            Adding an ipv4 route only takes 60 bytes, if we are adding thousands
-            of them this can add up to a lot of send calls.  Concat several of
-            them together before TXing.
-            """
-
-            if not total_message:
-                total_message = route.message
-            else:
-                total_message += route.message
-
-            if len(total_message) >= PACKET_CONCAT_SIZE:
-                self.tx_nlpacket_raw(total_message)
-                total_message = None
-
-            return total_message
-
-        if add_route:
-            rtm_command = RTM_NEWROUTE
-        else:
-            rtm_command = RTM_DELROUTE
-
-        total_message = None
-        PACKET_CONCAT_SIZE = 16384
-        debug = rtm_command in self.debug
-
-        if routes:
-            for (afi, ip, mask, nexthop, interface_index) in routes:
-                route = Route(rtm_command, debug, use_color=self.use_color)
-                route.flags = NLM_F_REQUEST | NLM_F_CREATE
-                route.body = pack('BBBBBBBBi', afi, mask, 0, 0, table, protocol,
-                                  route_scope, route_type, 0)
-                route.family = afi
-                route.add_attribute(Route.RTA_DST, ip)
-                if nexthop:
-                    route.add_attribute(Route.RTA_GATEWAY, nexthop)
-                route.add_attribute(Route.RTA_OIF, interface_index)
-                route.build_message(self.sequence.next(), self.pid)
-                total_message = tx_or_concat_message(total_message, route)
-
-            if total_message:
-                self.tx_nlpacket_raw(total_message)
-
-        if ecmp_routes:
-
-            for (route_key, value) in ecmp_routes.iteritems():
-                (afi, ip, mask) = route_key
-
-                route = Route(rtm_command, debug, use_color=self.use_color)
-                route.flags = NLM_F_REQUEST | NLM_F_CREATE
-                route.body = pack('BBBBBBBBi', afi, mask, 0, 0, table, protocol,
-                                  route_scope, route_type, 0)
-                route.family = afi
-                route.add_attribute(Route.RTA_DST, ip)
-                route.add_attribute(Route.RTA_MULTIPATH, value)
-                route.build_message(self.sequence.next(), self.pid)
-                total_message = tx_or_concat_message(total_message, route)
-
-            if total_message:
-                self.tx_nlpacket_raw(total_message)
-
-    def routes_add(self, routes, ecmp_routes,
-                   table=Route.RT_TABLE_MAIN,
-                   protocol=Route.RT_PROT_XORP,
-                   route_scope=Route.RT_SCOPE_UNIVERSE,
-                   route_type=Route.RTN_UNICAST):
-        self._routes_add_or_delete(True, routes, ecmp_routes, table, protocol, route_scope, route_type)
-
-    def routes_del(self, routes, ecmp_routes,
-                   table=Route.RT_TABLE_MAIN,
-                   protocol=Route.RT_PROT_XORP,
-                   route_scope=Route.RT_SCOPE_UNIVERSE,
-                   route_type=Route.RTN_UNICAST):
-        self._routes_add_or_delete(False, routes, ecmp_routes, table, protocol, route_scope, route_type)
-
-    def route_get(self, ip, debug=False):
-        """
-        ip must be one of the following:
-        - IPv4Address
-        - IPv6Address
-        """
-        # Transmit a RTM_GETROUTE to query for the route we want
-        route = Route(RTM_GETROUTE, debug, use_color=self.use_color)
-        route.flags = NLM_F_REQUEST | NLM_F_ACK
-
-        # Set everything in the service header as 0 other than the afi
-        afi = self.ip_to_afi(ip)
-        route.body = pack('Bxxxxxxxi', afi, 0)
-        route.family = afi
-        route.add_attribute(Route.RTA_DST, ip)
-        route.build_message(self.sequence.next(), self.pid)
-        return self.tx_nlpacket_get_response(route)
-
-    def routes_dump(self, family=socket.AF_UNSPEC, debug=True):
-        return self.request_dump(RTM_GETROUTE, family, debug)
-
-    def routes_print(self, routes):
-        """
-        Print a table of 'routes'
-        """
-        print "Prefix            Nexthop           ifindex"
-
-        for x in routes:
-            if Route.RTA_DST not in x.attributes:
-                log.warning("Route is missing RTA_DST")
-                continue
-
-            ip = "%s/%d" % (x.attributes[Route.RTA_DST].value, x.src_len)
-            print "%-15s   %-15s   %s" %\
-                (ip,
-                 str(x.attributes[Route.RTA_GATEWAY].value) if Route.RTA_GATEWAY in x.attributes else None,
-                 x.attributes[Route.RTA_OIF].value)
-
-    # =====
-    # Links
-    # =====
-    def _get_iface_by_name(self, ifname):
-        """
-        Return a Link object for ifname
-        """
-        debug = RTM_GETLINK in self.debug
-
-        link = Link(RTM_GETLINK, debug, use_color=self.use_color)
-        link.flags = NLM_F_REQUEST | NLM_F_ACK
-        link.body = pack('=Bxxxiii', socket.AF_UNSPEC, 0, 0, 0)
-        link.add_attribute(Link.IFLA_IFNAME, ifname)
-        link.build_message(self.sequence.next(), self.pid)
-
-        try:
-            return self.tx_nlpacket_get_response(link)[0]
-
-        except NetlinkNoAddressError:
-            log.info("Netlink did not find interface %s" % ifname)
-            return None
-
-    def get_iface_index(self, ifname):
-        """
-        Return the interface index for ifname
-        """
-        iface = self._get_iface_by_name(ifname)
-
-        if iface:
-            return iface.ifindex
-        return None
-
-    def _link_add(self, ifindex, ifname, kind, ifla_info_data):
-        """
-        Build and TX a RTM_NEWLINK message to add the desired interface
-        """
-        debug = RTM_NEWLINK in self.debug
-
-        link = Link(RTM_NEWLINK, debug, use_color=self.use_color)
-        link.flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK
-        link.body = pack('Bxxxiii', socket.AF_UNSPEC, 0, 0, 0)
-        link.add_attribute(Link.IFLA_IFNAME, ifname)
-        link.add_attribute(Link.IFLA_LINK, ifindex)
-        link.add_attribute(Link.IFLA_LINKINFO, {
-            Link.IFLA_INFO_KIND: kind,
-            Link.IFLA_INFO_DATA: ifla_info_data
-        })
-        link.build_message(self.sequence.next(), self.pid)
-        return self.tx_nlpacket_get_response(link)
-
-    def link_add_vlan(self, ifindex, ifname, vlanid):
-        """
-        ifindex is the index of the parent interface that this sub-interface
-        is being added to
-        """
-
-        '''
-        If you name an interface swp2.17 but assign it to vlan 12, the kernel
-        will return a very misleading NLE_MSG_OVERFLOW error.  It only does
-        this check if the ifname uses dot notation.
-
-        Do this check here so we can provide a more intuitive error
-        '''
-        if '.' in ifname:
-            ifname_vlanid = int(ifname.split('.')[-1])
-
-            if ifname_vlanid != vlanid:
-                raise InvalidInterfaceNameVlanCombo("Interface %s must belong "
-                                                    "to VLAN %d (VLAN %d was requested)" %
-                                                    (ifname, ifname_vlanid, vlanid))
-
-        return self._link_add(ifindex, ifname, 'vlan', {Link.IFLA_VLAN_ID: vlanid})
-
-    def link_add_macvlan(self, ifindex, ifname):
-        """
-        ifindex is the index of the parent interface that this sub-interface
-        is being added to
-        """
-        return self._link_add(ifindex, ifname, 'macvlan', {Link.IFLA_MACVLAN_MODE: Link.MACVLAN_MODE_PRIVATE})
-
-    def vlan_get(self, filter_ifindex=(), filter_vlanid=(), compress_vlans=True):
-        """
-        filter_ifindex should be a tuple if interface indexes, this is a whitelist filter
-        filter_vlandid should be a tuple if VLAN IDs, this is a whitelist filter
-        """
-        debug = RTM_GETLINK in self.debug
-
-        link = Link(RTM_GETLINK, debug, use_color=self.use_color)
-        link.family = AF_BRIDGE
-        link.flags = NLM_F_DUMP | NLM_F_REQUEST
-        link.body = pack('Bxxxiii', socket.AF_BRIDGE, 0, 0, 0)
-
-        if compress_vlans:
-            link.add_attribute(Link.IFLA_EXT_MASK, Link.RTEXT_FILTER_BRVLAN_COMPRESSED)
-        else:
-            link.add_attribute(Link.IFLA_EXT_MASK, Link.RTEXT_FILTER_BRVLAN)
-
-        link.build_message(self.sequence.next(), self.pid)
-        reply = self.tx_nlpacket_get_response(link)
-
-        iface_vlans = {}
-
-        for msg in reply:
-            if msg.family != socket.AF_BRIDGE:
-                continue
-
-            if filter_ifindex and msg.ifindex not in filter_ifindex:
-                continue
-
-            ifla_af_spec = msg.get_attribute_value(Link.IFLA_AF_SPEC)
-
-            if not ifla_af_spec:
-                continue
-
-            ifname = msg.get_attribute_value(Link.IFLA_IFNAME)
-
-            '''
-            Example IFLA_AF_SPEC
-
-              20: 0x1c001a00  ....  Length 0x001c (28), Type 0x001a (26) IFLA_AF_SPEC
-              21: 0x08000200  ....  Nested Attribute - Length 0x0008 (8),  Type 0x0002 (2) IFLA_BRIDGE_VLAN_INFO
-              22: 0x00000a00  ....
-              23: 0x08000200  ....  Nested Attribute - Length 0x0008 (8),  Type 0x0002 (2) IFLA_BRIDGE_VLAN_INFO
-              24: 0x00001000  ....
-              25: 0x08000200  ....  Nested Attribute - Length 0x0008 (8),  Type 0x0002 (2) IFLA_BRIDGE_VLAN_INFO
-              26: 0x00001400  ....
-            '''
-            for (x_type, x_value) in ifla_af_spec.iteritems():
-                if x_type == Link.IFLA_BRIDGE_VLAN_INFO:
-                    for (vlan_flag, vlan_id) in x_value:
-                        if filter_vlanid is None or vlan_id in filter_vlanid:
-
-                            if ifname not in iface_vlans:
-                                iface_vlans[ifname] = []
-
-                            # We store these in the tuple as (vlan, flag) instead (flag, vlan)
-                            # so that we can sort the list of tuples
-                            iface_vlans[ifname].append((vlan_id, vlan_flag))
-
-        return iface_vlans
-
-    def vlan_show(self, filter_ifindex=None, filter_vlanid=None, compress_vlans=True):
-
-        def vlan_flag_to_string(vlan_flag):
-            flag_str = []
-            if vlan_flag & Link.BRIDGE_VLAN_INFO_PVID:
-                flag_str.append('PVID')
-
-            if vlan_flag & Link.BRIDGE_VLAN_INFO_UNTAGGED:
-                flag_str.append('Egress Untagged')
-
-            return ', '.join(flag_str)
-
-        iface_vlans = self.vlan_get(filter_ifindex, filter_vlanid, compress_vlans)
-        log.debug("iface_vlans:\n%s\n" % pformat(iface_vlans))
-        range_begin_vlan_id = None
-        range_flag = None
-
-        print "   Interface  VLAN  Flags"
-        print "  ==========  ====  ====="
-
-        for (ifname, vlan_tuples) in sorted(iface_vlans.iteritems()):
-            for (vlan_id, vlan_flag) in sorted(vlan_tuples):
-
-                if vlan_flag & Link.BRIDGE_VLAN_INFO_RANGE_BEGIN:
-                    range_begin_vlan_id = vlan_id
-                    range_flag = vlan_flag
-
-                elif vlan_flag & Link.BRIDGE_VLAN_INFO_RANGE_END:
-                    range_flag |= vlan_flag
-
-                    if not range_begin_vlan_id:
-                        log.warning("BRIDGE_VLAN_INFO_RANGE_END is %d but we never saw a BRIDGE_VLAN_INFO_RANGE_BEGIN" % vlan_id)
-                        range_begin_vlan_id = vlan_id
-
-                    for x in xrange(range_begin_vlan_id, vlan_id + 1):
-                        print "  %10s  %4d  %s" % (ifname, x, vlan_flag_to_string(vlan_flag))
-                        ifname = ''
-
-                    range_begin_vlan_id = None
-                    range_flag = None
-
-                else:
-                    print "  %10s  %4d  %s" % (ifname, vlan_id, vlan_flag_to_string(vlan_flag))
-                    ifname = ''
-
-
-    def vlan_modify(self, msgtype, ifindex, vlanid_start, vlanid_end=None, bridge_self=False, bridge_master=False, pvid=False, untagged=False):
-        """
-        iproute2 bridge/vlan.c vlan_modify()
-        """
-        assert msgtype in (RTM_SETLINK, RTM_DELLINK), "Invalid msgtype %s, must be RTM_SETLINK or RTM_DELLINK" % msgtype
-        assert vlanid_start >= 1 and vlanid_start <= 4096, "Invalid VLAN start %s" % vlanid_start
-
-        if vlanid_end is None:
-            vlanid_end = vlanid_start
-
-        assert vlanid_end >= 1 and vlanid_end <= 4096, "Invalid VLAN end %s" % vlanid_end
-        assert vlanid_start <= vlanid_end, "Invalid VLAN range %s-%s, start must be <= end" % (vlanid_start, vlanid_end)
-
-        debug = msgtype in self.debug
-        bridge_flags = 0
-        vlan_info_flags = 0
-
-        link = Link(msgtype, debug, use_color=self.use_color)
-        link.flags = NLM_F_REQUEST | NLM_F_ACK
-        link.body = pack('Bxxxiii', socket.AF_BRIDGE, ifindex, 0, 0)
-
-        if bridge_self:
-            bridge_flags |= Link.BRIDGE_FLAGS_SELF
-
-        if bridge_master:
-            bridge_flags |= Link.BRIDGE_FLAGS_MASTER
-
-        if pvid:
-            vlan_info_flags |= Link.BRIDGE_VLAN_INFO_PVID
-
-        if untagged:
-            vlan_info_flags |= Link.BRIDGE_VLAN_INFO_UNTAGGED
-
-        ifla_af_spec = OrderedDict()
-
-        if bridge_flags:
-            ifla_af_spec[Link.IFLA_BRIDGE_FLAGS] = bridge_flags
-
-        # just one VLAN
-        if vlanid_start == vlanid_end:
-            ifla_af_spec[Link.IFLA_BRIDGE_VLAN_INFO] = [(vlan_info_flags, vlanid_start), ]
-
-        # a range of VLANs
-        else:
-            ifla_af_spec[Link.IFLA_BRIDGE_VLAN_INFO] = [
-                (vlan_info_flags | Link.BRIDGE_VLAN_INFO_RANGE_BEGIN, vlanid_start),
-                (vlan_info_flags | Link.BRIDGE_VLAN_INFO_RANGE_END, vlanid_end)
-            ]
-
-        link.add_attribute(Link.IFLA_AF_SPEC, ifla_af_spec)
-        link.build_message(self.sequence.next(), self.pid)
-        return self.tx_nlpacket_get_response(link)
-
-    def link_add_bridge_vlan(self, ifindex, vlanid_start, vlanid_end=None, pvid=False, untagged=False, master=False):
-        """
-        Add VLAN(s) to a bridge interface
-        """
-        bridge_self = False if master else True
-        self.vlan_modify(RTM_SETLINK, ifindex, vlanid_start, vlanid_end, bridge_self, master, pvid, untagged)
-
-    def link_del_bridge_vlan(self, ifindex, vlanid_start, vlanid_end=None, pvid=False, untagged=False, master=False):
-        """
-        Delete VLAN(s) from a bridge interface
-        """
-        bridge_self = False if master else True
-        self.vlan_modify(RTM_DELLINK, ifindex, vlanid_start, vlanid_end, bridge_self, master, pvid, untagged)
-
-    def link_set_updown(self, ifname, state):
-        """
-        Either bring ifname up or take it down
-        """
-
-        if state == 'up':
-            if_flags = Link.IFF_UP
-        elif state == 'down':
-            if_flags = 0
-        else:
-            raise Exception('Unsupported state %s, valid options are "up" and "down"' % state)
-
-        debug = RTM_NEWLINK in self.debug
-        if_change = Link.IFF_UP
-
-        link = Link(RTM_NEWLINK, debug, use_color=self.use_color)
-        link.flags = NLM_F_REQUEST | NLM_F_ACK
-        link.body = pack('=BxxxiLL', socket.AF_UNSPEC, 0, if_flags, if_change)
-        link.add_attribute(Link.IFLA_IFNAME, ifname)
-        link.build_message(self.sequence.next(), self.pid)
-        return self.tx_nlpacket_get_response(link)
-
-    def link_set_protodown(self, ifname, state):
-        """
-        Either bring ifname up or take it down by setting IFLA_PROTO_DOWN on or off
-        """
-        flags = 0
-        protodown = 1 if state == "on" else 0
-
-        debug = RTM_NEWLINK in self.debug
-
-        link = Link(RTM_NEWLINK, debug, use_color=self.use_color)
-        link.flags = NLM_F_REQUEST | NLM_F_ACK
-        link.body = pack('=BxxxiLL', socket.AF_UNSPEC, 0, 0, 0)
-        link.add_attribute(Link.IFLA_IFNAME, ifname)
-        link.add_attribute(Link.IFLA_PROTO_DOWN, protodown)
-        link.build_message(self.sequence.next(), self.pid)
-        return self.tx_nlpacket_get_response(link)
-
-    def link_set_master(self, ifname, master_ifindex=0, state=None):
-        """
-            ip link set %ifname master %master_ifindex %state
-            use master_ifindex=0 for nomaster
-        """
-        if state == 'up':
-            if_change = Link.IFF_UP
-            if_flags = Link.IFF_UP
-        elif state == 'down':
-            if_change = Link.IFF_UP
-            if_flags = 0
-        else:
-            if_change = 0
-            if_flags = 0
-
-        debug = RTM_NEWLINK in self.debug
-
-        link = Link(RTM_NEWLINK, debug, use_color=self.use_color)
-        link.flags = NLM_F_REQUEST | NLM_F_ACK
-        link.body = pack('=BxxxiLL', socket.AF_UNSPEC, 0, if_flags, if_change)
-        link.add_attribute(Link.IFLA_IFNAME, ifname)
-        link.add_attribute(Link.IFLA_MASTER, master_ifindex)
-        link.build_message(self.sequence.next(), self.pid)
-        return self.tx_nlpacket_get_response(link)
-
-    # =========
-    # Neighbors
-    # =========
-    def neighbor_add(self, afi, ifindex, ip, mac):
-        debug = RTM_NEWNEIGH in self.debug
-        service_hdr_flags = 0
-
-        nbr = Neighbor(RTM_NEWNEIGH, debug, use_color=self.use_color)
-        nbr.flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK
-        nbr.family = afi
-        nbr.body = pack('=BxxxiHBB', afi, ifindex, Neighbor.NUD_REACHABLE, service_hdr_flags, Route.RTN_UNICAST)
-        nbr.add_attribute(Neighbor.NDA_DST, ip)
-        nbr.add_attribute(Neighbor.NDA_LLADDR, mac)
-        nbr.build_message(self.sequence.next(), self.pid)
-        return self.tx_nlpacket_get_response(nbr)
-
-    def neighbor_del(self, afi, ifindex, ip, mac):
-        debug = RTM_DELNEIGH in self.debug
-        service_hdr_flags = 0
-
-        nbr = Neighbor(RTM_DELNEIGH, debug, use_color=self.use_color)
-        nbr.flags = NLM_F_REQUEST | NLM_F_ACK
-        nbr.family = afi
-        nbr.body = pack('=BxxxiHBB', afi, ifindex, Neighbor.NUD_REACHABLE, service_hdr_flags, Route.RTN_UNICAST)
-        nbr.add_attribute(Neighbor.NDA_DST, ip)
-        nbr.add_attribute(Neighbor.NDA_LLADDR, mac)
-        nbr.build_message(self.sequence.next(), self.pid)
-        return self.tx_nlpacket_get_response(nbr)
-
-    def link_add_vxlan(self, ifname, vxlanid, dstport=None, local=None,
-                       group=None, learning='on', ageing=None, physdev=None):
-
-        debug = RTM_NEWLINK in self.debug
-
-        info_data = {Link.IFLA_VXLAN_ID: int(vxlanid)}
-        if dstport:
-            info_data[Link.IFLA_VXLAN_PORT] = int(dstport)
-        if local:
-            info_data[Link.IFLA_VXLAN_LOCAL] = local
-        if group:
-            info_data[Link.IFLA_VXLAN_GROUP] = group
-        if physdev:
-            info_data[Link.IFLA_VXLAN_LINK] = int (physdev)
-
-        learning = 0 if learning == 'off' else 1
-        info_data[Link.IFLA_VXLAN_LEARNING] = learning
-
-        if ageing:
-            info_data[Link.IFLA_VXLAN_AGEING] = int(ageing)
-
-        link = Link(RTM_NEWLINK, debug, use_color=self.use_color)
-        link.flags = NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK
-        link.body = pack('Bxxxiii', socket.AF_UNSPEC, 0, 0, 0)
-        link.add_attribute(Link.IFLA_IFNAME, ifname)
-        link.add_attribute(Link.IFLA_LINKINFO, {
-            Link.IFLA_INFO_KIND: "vxlan",
-            Link.IFLA_INFO_DATA: info_data
-        })
-
-        link.build_message(self.sequence.next(), self.pid)
-        return self.tx_nlpacket_get_response(link)
diff --git a/nlmanager/nlpacket.py b/nlmanager/nlpacket.py
deleted file mode 100644 (file)
index f2d7b45..0000000
+++ /dev/null
@@ -1,2971 +0,0 @@
-# Copyright (c) 2009-2013, Exa Networks Limited
-# Copyright (c) 2009-2013, Thomas Mangin
-# Copyright (c) 2015 Cumulus Networks, Inc.
-#
-# All rights reserved.
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# Redistributions of source code must retain the above copyright notice, this
-# list of conditions and the following disclaimer.
-#
-# Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-#
-# The names of the Exa Networks Limited, Cumulus Networks, Inc. nor the names
-# of its contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
-# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-import logging
-import struct
-from ipaddr import IPv4Address, IPv6Address, IPAddress
-from binascii import hexlify
-from pprint import pformat
-from socket import AF_INET, AF_INET6, AF_BRIDGE
-from string import printable
-from struct import pack, unpack, calcsize
-
-log = logging.getLogger(__name__)
-
-# Interface name buffer size #define IFNAMSIZ 16 (kernel source)
-IF_NAME_SIZE = 15 # 15 because python doesn't have \0
-
-# Netlink message types
-NLMSG_NOOP    = 0x01
-NLMSG_ERROR   = 0x02
-NLMSG_DONE    = 0x03
-NLMSG_OVERRUN = 0x04
-
-RTM_NEWLINK   = 0x10  # Create a new network interface
-RTM_DELLINK   = 0x11  # Destroy a network interface
-RTM_GETLINK   = 0x12  # Retrieve information about a network interface(ifinfomsg)
-RTM_SETLINK   = 0x13  #
-
-RTM_NEWADDR   = 0x14
-RTM_DELADDR   = 0x15
-RTM_GETADDR   = 0x16
-
-RTM_NEWNEIGH  = 0x1C
-RTM_DELNEIGH  = 0x1D
-RTM_GETNEIGH  = 0x1E
-
-RTM_NEWROUTE  = 0x18
-RTM_DELROUTE  = 0x19
-RTM_GETROUTE  = 0x1A
-
-RTM_NEWQDISC  = 0x24
-RTM_DELQDISC  = 0x25
-RTM_GETQDISC  = 0x26
-
-# Netlink message flags
-NLM_F_REQUEST = 0x01  # It is query message.
-NLM_F_MULTI   = 0x02  # Multipart message, terminated by NLMSG_DONE
-NLM_F_ACK     = 0x04  # Reply with ack, with zero or error code
-NLM_F_ECHO    = 0x08  # Echo this query
-
-# Modifiers to GET query
-NLM_F_ROOT   = 0x100  # specify tree root
-NLM_F_MATCH  = 0x200  # return all matching
-NLM_F_DUMP   = NLM_F_ROOT | NLM_F_MATCH
-NLM_F_ATOMIC = 0x400  # atomic GET
-
-# Modifiers to NEW query
-NLM_F_REPLACE = 0x100  # Override existing
-NLM_F_EXCL    = 0x200  # Do not touch, if it exists
-NLM_F_CREATE  = 0x400  # Create, if it does not exist
-NLM_F_APPEND  = 0x800  # Add to end of list
-
-NLA_F_NESTED        = 0x8000
-NLA_F_NET_BYTEORDER = 0x4000
-NLA_TYPE_MASK       = ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER)
-
-# Groups
-RTMGRP_LINK          = 0x1
-RTMGRP_NOTIFY        = 0x2
-RTMGRP_NEIGH         = 0x4
-RTMGRP_TC            = 0x8
-RTMGRP_IPV4_IFADDR   = 0x10
-RTMGRP_IPV4_MROUTE   = 0x20
-RTMGRP_IPV4_ROUTE    = 0x40
-RTMGRP_IPV4_RULE     = 0x80
-RTMGRP_IPV6_IFADDR   = 0x100
-RTMGRP_IPV6_MROUTE   = 0x200
-RTMGRP_IPV6_ROUTE    = 0x400
-RTMGRP_IPV6_IFINFO   = 0x800
-RTMGRP_DECnet_IFADDR = 0x1000
-RTMGRP_DECnet_ROUTE  = 0x4000
-RTMGRP_IPV6_PREFIX   = 0x20000
-
-RTMGRP_ALL = (RTMGRP_LINK | RTMGRP_NOTIFY | RTMGRP_NEIGH | RTMGRP_TC |
-              RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_MROUTE | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_RULE |
-              RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_MROUTE | RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFINFO |
-              RTMGRP_DECnet_IFADDR | RTMGRP_DECnet_ROUTE |
-              RTMGRP_IPV6_PREFIX)
-
-# Colors for logging
-red    = 91
-green  = 92
-yellow = 93
-blue   = 94
-
-EXTRA_DEBUG = False
-
-def set_extra_debug(debug):
-    global EXTRA_DEBUG
-    EXTRA_DEBUG = debug
-
-
-def zfilled_hex(value, digits):
-    return '0x' + hex(value)[2:].zfill(digits)
-
-
-def remove_trailing_null(line):
-    """
-    Remove the last character if it is a NULL...having that NULL
-    causes python to print a garbage character
-    """
-
-    if ord(line[-1]) == 0:
-        line = line[:-1]
-
-    return line
-
-
-def mac_int_to_str(mac_int):
-    """
-    Return an integer in MAC string format
-    """
-
-    # [2:] to remove the leading 0x, then fill out to 12 zeroes, then uppercase
-    all_caps = hex(int(mac_int))[2:].zfill(12).upper()
-
-    if all_caps[-1] == 'L':
-        all_caps = all_caps[:-1]
-        all_caps = all_caps.zfill(12).upper()
-
-    return "%s.%s.%s" % (all_caps[0:4], all_caps[4:8], all_caps[8:12])
-
-
-def data_to_color_text(line_number, color, data, extra=''):
-    (c1, c2, c3, c4) = unpack('BBBB', data[0:4])
-    in_ascii = []
-
-    for c in (c1, c2, c3, c4):
-        char_c = chr(c)
-
-        if char_c in printable[:-5]:
-            in_ascii.append(char_c)
-        else:
-            in_ascii.append('.')
-
-    if color:
-        return '  %2d: \033[%dm0x%02x%02x%02x%02x\033[0m  %s  %s' % (line_number, color, c1, c2, c3, c4, ''.join(in_ascii), extra)
-
-    return '  %2d: 0x%02x%02x%02x%02x  %s  %s' % (line_number, c1, c2, c3, c4, ''.join(in_ascii), extra)
-
-
-def padded_length(length):
-    return int((length + 3) / 4) * 4
-
-
-class Attribute(object):
-
-    def __init__(self, atype, string, logger):
-        self.atype = atype
-        self.string = string
-        self.HEADER_PACK = '=HH'
-        self.HEADER_LEN = calcsize(self.HEADER_PACK)
-        self.PACK = None
-        self.LEN = None
-        self.value = None
-        self.nested = False
-        self.net_byteorder = False
-        self.log = logger
-
-    def __str__(self):
-        return self.string
-
-    def set_value(self, value):
-        self.value = value
-
-    def set_nested(self, nested):
-        self.nested = nested
-
-    def set_net_byteorder(self, net_byteorder):
-        self.net_byteorder = net_byteorder
-
-    def pad_bytes_needed(self, length):
-        """
-        Return the number of bytes that should be added to align on a 4-byte boundry
-        """
-        remainder = length % 4
-
-        if remainder:
-            return 4 - remainder
-
-        return 0
-
-    def pad(self, length, raw):
-        pad = self.pad_bytes_needed(length)
-
-        if pad:
-            raw += '\0' * pad
-
-        return raw
-
-    def encode(self):
-
-        if not self.LEN:
-            raise Exception('Please define an encode() method in your child attribute class, or do not use AttributeGeneric')
-
-        length = self.HEADER_LEN + self.LEN
-        attr_type_with_flags = self.atype
-
-        if self.nested:
-            attr_type_with_flags = attr_type_with_flags | NLA_F_NESTED
-
-        if self.net_byteorder:
-            attr_type_with_flags = attr_type_with_flags | NLA_F_NET_BYTEORDER
-
-        raw = pack(self.HEADER_PACK, length, attr_type_with_flags) + pack(self.PACK, self.value)
-        raw = self.pad(length, raw)
-        return raw
-
-    def decode_length_type(self, data):
-        """
-        The first two bytes of an attribute are the length, the next two bytes are the type
-        """
-        self.data = data
-        prev_atype = self.atype
-        (data1, data2) = unpack(self.HEADER_PACK, data[:self.HEADER_LEN])
-        self.length = int(data1)
-        self.atype = int(data2)
-        self.attr_end = padded_length(self.length)
-
-        self.nested = True if self.atype & NLA_F_NESTED else False
-        self.net_byteorder = True if self.atype & NLA_F_NET_BYTEORDER else False
-        self.atype = self.atype & NLA_TYPE_MASK
-
-        # Should never happen
-        assert self.atype == prev_atype, "This object changes attribute type from %d to %d, this is bad" % (prev_atype, self.atype)
-
-    def dump_first_line(self, dump_buffer, line_number, color):
-        """
-        Add the "Length....Type..." line to the dump buffer
-        """
-        if self.attr_end == self.length:
-            padded_to = ', '
-        else:
-            padded_to = ' padded to %d, ' % self.attr_end
-
-        extra = 'Length %s (%d)%sType %s%s%s (%d) %s' % \
-                 (zfilled_hex(self.length, 4), self.length,
-                  padded_to,
-                  zfilled_hex(self.atype, 4),
-                  " (NLA_F_NESTED set)" if self.nested else "",
-                  " (NLA_F_NET_BYTEORDER set)" if self.net_byteorder else "",
-                  self.atype,
-                  self)
-
-        dump_buffer.append(data_to_color_text(line_number, color, self.data[0:4], extra))
-        return line_number + 1
-
-    def dump_lines(self, dump_buffer, line_number, color):
-        line_number = self.dump_first_line(dump_buffer, line_number, color)
-
-        for x in xrange(1, self.attr_end/4):
-            start = x * 4
-            end = start + 4
-            dump_buffer.append(data_to_color_text(line_number, color, self.data[start:end], ''))
-            line_number += 1
-
-        return line_number
-
-    def get_pretty_value(self):
-        return self.value
-
-
-class AttributeFourByteList(Attribute):
-
-    def __init__(self, atype, string, family, logger):
-        Attribute.__init__(self, atype, string, logger)
-
-    def decode(self, parent_msg, data):
-        self.decode_length_type(data)
-        wordcount = (self.attr_end - 4)/4
-        self.PACK = '=%dL' % wordcount
-        self.LEN = calcsize(self.PACK)
-
-        try:
-            self.value = unpack(self.PACK, self.data[4:])
-        except struct.error:
-            self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:])))
-            raise
-
-    def dump_lines(self, dump_buffer, line_number, color):
-        line_number = self.dump_first_line(dump_buffer, line_number, color)
-        idx = 1
-        for val in self.value:
-            dump_buffer.append(data_to_color_text(line_number, color, self.data[4*idx:4*(idx+1)], val))
-            line_number += 1
-            idx += 1
-        return line_number
-
-
-class AttributeFourByteValue(Attribute):
-
-    def __init__(self, atype, string, family, logger):
-        Attribute.__init__(self, atype, string, logger)
-        self.PACK = '=L'
-        self.LEN = calcsize(self.PACK)
-
-    def decode(self, parent_msg, data):
-        self.decode_length_type(data)
-        assert self.attr_end == 8, "Attribute length for %s must be 8, it is %d" % (self, self.attr_end)
-
-        try:
-            self.value = int(unpack(self.PACK, self.data[4:])[0])
-        except struct.error:
-            self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:])))
-            raise
-
-    def dump_lines(self, dump_buffer, line_number, color):
-        line_number = self.dump_first_line(dump_buffer, line_number, color)
-        dump_buffer.append(data_to_color_text(line_number, color, self.data[4:8], self.value))
-        return line_number + 1
-
-
-class AttributeTwoByteValue(Attribute):
-
-    def __init__(self, atype, string, family, logger):
-        Attribute.__init__(self, atype, string, logger)
-        self.PACK = '=Hxx'
-        self.LEN = calcsize(self.PACK)
-
-    def decode(self, parent_msg, data):
-        self.decode_length_type(data)
-        assert self.attr_end == 8, "Attribute length for %s must be 8, it is %d" % (self, self.attr_end)
-
-        try:
-            self.value = int(unpack(self.PACK, self.data[4:8])[0])
-        except struct.error:
-            self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:6])))
-            raise
-
-    def encode(self):
-        length = self.HEADER_LEN + self.LEN
-        raw = pack(self.HEADER_PACK, length-2, self.atype) + pack(self.PACK, self.value)
-        raw = self.pad(length, raw)
-        return raw
-
-    def dump_lines(self, dump_buffer, line_number, color):
-        line_number = self.dump_first_line(dump_buffer, line_number, color)
-        dump_buffer.append(data_to_color_text(line_number, color, self.data[4:8], self.value))
-        return line_number + 1
-
-
-class AttributeString(Attribute):
-
-    def __init__(self, atype, string, family, logger):
-        Attribute.__init__(self, atype, string, logger)
-        self.PACK = None
-        self.LEN = None
-
-    def encode(self):
-        # some interface names come from JSON as unicode strings
-        # and cannot be packed as is so we must convert them to strings
-        if isinstance(self.value, unicode):
-            self.value = str(self.value)
-        self.PACK = '%ds' % len(self.value)
-        self.LEN = calcsize(self.PACK)
-
-        length = self.HEADER_LEN + self.LEN
-        raw = pack(self.HEADER_PACK, length, self.atype) + pack(self.PACK, self.value)
-        raw = self.pad(length, raw)
-        return raw
-
-    def decode(self, parent_msg, data):
-        self.decode_length_type(data)
-        self.PACK = '%ds' % (self.length - 4)
-        self.LEN = calcsize(self.PACK)
-
-        try:
-            self.value = remove_trailing_null(unpack(self.PACK, self.data[4:self.length])[0])
-        except struct.error:
-            self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:self.length])))
-            raise
-
-
-class AttributeStringInterfaceName(AttributeString):
-
-    def __init__(self, atype, string, family, logger):
-        AttributeString.__init__(self, atype, string, family, logger)
-
-    def set_value(self, value):
-        if value and len(value) > IF_NAME_SIZE:
-            raise Exception('interface name exceeds max length of %d' % IF_NAME_SIZE)
-        self.value = value
-
-
-class AttributeIPAddress(Attribute):
-
-    def __init__(self, atype, string, family, logger):
-        Attribute.__init__(self, atype, string, logger)
-        self.value_int = None
-        self.value_int_str = None
-        self.family = family
-
-        if self.family == AF_INET:
-            self.PACK = '>L'
-
-        elif self.family == AF_INET6:
-            self.PACK = '>QQ'
-
-        elif self.family == AF_BRIDGE:
-            self.PACK = '>L'
-
-        else:
-            raise Exception("%s is not a supported address family" % self.family)
-
-        self.LEN = calcsize(self.PACK)
-
-    def set_value(self, value):
-        if value is None:
-            self.value = None
-        else:
-            self.value = IPAddress(value)
-
-    def decode(self, parent_msg, data):
-        self.decode_length_type(data)
-
-        try:
-            if self.family == AF_INET:
-                self.value = IPv4Address(unpack(self.PACK, self.data[4:])[0])
-
-            elif self.family == AF_INET6:
-                (data1, data2) = unpack(self.PACK, self.data[4:])
-                self.value = IPv6Address(data1 << 64 | data2)
-
-            elif self.family == AF_BRIDGE:
-                self.value = IPv4Address(unpack(self.PACK, self.data[4:])[0])
-
-            self.value_int = int(self.value)
-            self.value_int_str = str(self.value_int)
-
-        except struct.error:
-            self.value = None
-            self.value_int = None
-            self.value_int_str = None
-            self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:])))
-            raise
-
-    def encode(self):
-        length = self.HEADER_LEN + self.LEN
-
-        if self.family not in [AF_INET, AF_INET6, AF_BRIDGE]:
-            raise Exception("%s is not a supported address family" % self.family)
-
-        raw = pack(self.HEADER_PACK, length, self.atype) + self.value.packed
-        raw = self.pad(length, raw)
-        return raw
-
-    def dump_lines(self, dump_buffer, line_number, color):
-        line_number = self.dump_first_line(dump_buffer, line_number, color)
-
-        if self.family == AF_INET:
-            dump_buffer.append(data_to_color_text(line_number, color, self.data[4:8], self.value))
-            line_number += 1
-
-        elif self.family == AF_INET6:
-
-            for x in xrange(1, self.attr_end/4):
-                start = x * 4
-                end = start + 4
-                dump_buffer.append(data_to_color_text(line_number, color, self.data[start:end], self.value))
-                line_number += 1
-
-        elif self.family == AF_BRIDGE:
-            dump_buffer.append(data_to_color_text(line_number, color, self.data[4:8], self.value))
-            line_number += 1
-
-        return line_number
-
-
-class AttributeMACAddress(Attribute):
-
-    def __init__(self, atype, string, family, logger):
-        Attribute.__init__(self, atype, string, logger)
-        self.PACK = '>LHxx'
-        self.LEN = calcsize(self.PACK)
-
-    def decode(self, parent_msg, data):
-        self.decode_length_type(data)
-
-        try:
-            if self.length == 10:
-                (data1, data2) = unpack(self.PACK, self.data[4:])
-                self.value = mac_int_to_str(data1 << 16 | data2)
-            elif self.length == 8:
-                self.value = IPv4Address(unpack('>L', self.data[4:])[0])
-                self.value_int = int(self.value)
-                self.value_int_str = str(self.value_int)
-            else:
-                raise Exception("Length of MACAddress attribute not supported: %d" % self.length)
-
-        except struct.error:
-            self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:])))
-            raise
-
-    def encode(self):
-        length = self.HEADER_LEN + self.LEN
-        mac_raw = int(self.value.replace('.', '').replace(':', ''), 16)
-        raw = pack(self.HEADER_PACK, length-2, self.atype) + pack(self.PACK, mac_raw >> 16, mac_raw & 0x0000FFFF)
-        raw = self.pad(length, raw)
-        return raw
-
-    def dump_lines(self, dump_buffer, line_number, color):
-        line_number = self.dump_first_line(dump_buffer, line_number, color)
-        dump_buffer.append(data_to_color_text(line_number, color, self.data[4:8], self.value))
-        line_number += 1
-        if len(self.data) >= 12:
-            dump_buffer.append(data_to_color_text(line_number, color, self.data[8:12]))
-            line_number += 1
-        return line_number
-
-
-class AttributeGeneric(Attribute):
-
-    def __init__(self, atype, string, family, logger):
-        Attribute.__init__(self, atype, string, logger)
-        self.PACK = None
-        self.LEN = None
-
-    def decode(self, parent_msg, data):
-        self.decode_length_type(data)
-        wordcount = (self.attr_end - 4)/4
-        self.PACK = '=%dL' % wordcount
-        self.LEN = calcsize(self.PACK)
-
-        try:
-            self.value = ''.join(map(str, unpack(self.PACK, self.data[4:])))
-        except struct.error:
-            self.log.error("%s unpack of %s failed, data 0x%s" % (self, self.PACK, hexlify(self.data[4:])))
-            raise
-
-
-class AttributeOneByteValue(AttributeGeneric):
-
-    def __init__(self, atype, string, family, logger):
-        AttributeGeneric.__init__(self, atype, string, family, logger)
-        self.PACK = '=B'
-        self.LEN = calcsize(self.PACK)
-
-
-class AttributeIFLA_AF_SPEC(Attribute):
-    """
-    value will be a dictionary such as:
-    {
-        Link.IFLA_BRIDGE_FLAGS: flags,
-        Link.IFLA_BRIDGE_VLAN_INFO: (vflags, vlanid)
-    }
-    """
-    def __init__(self, atype, string, family, logger):
-        Attribute.__init__(self, atype, string, logger)
-
-    def encode(self):
-        pack_layout = [self.HEADER_PACK]
-        payload = [0, self.atype]
-        attr_length_index = 0
-
-        # For now this assumes that all data will be packed in the native endian
-        # order (=). If a field is added that needs to be packed via network
-        # order (>) then some smarts will need to be added to split the pack_layout
-        # string at the >, split the payload and make the needed pack() calls.
-        #
-        # Until we cross that bridge though we will keep things nice and simple and
-        # pack everything via a single pack() call.
-        sub_attr_to_add = []
-
-        for (sub_attr_type, sub_attr_value) in self.value.iteritems():
-
-            if sub_attr_type == Link.IFLA_BRIDGE_FLAGS:
-                sub_attr_to_add.append((sub_attr_type, sub_attr_value))
-
-            elif sub_attr_type == Link.IFLA_BRIDGE_VLAN_INFO:
-                for (vlan_flag, vlan_id) in sub_attr_value:
-                    sub_attr_to_add.append((sub_attr_type, (vlan_flag, vlan_id)))
-
-            else:
-                self.log.debug('Add support for encoding IFLA_AF_SPEC sub-attribute type %d' % sub_attr_type)
-                continue
-
-        for (sub_attr_type, sub_attr_value) in sub_attr_to_add:
-            sub_attr_pack_layout = ['=', 'HH']
-            sub_attr_payload = [0, sub_attr_type]
-            sub_attr_length_index = 0
-
-            if sub_attr_type == Link.IFLA_BRIDGE_FLAGS:
-                sub_attr_pack_layout.append('H')
-                sub_attr_payload.append(sub_attr_value)
-
-            elif sub_attr_type == Link.IFLA_BRIDGE_VLAN_INFO:
-                sub_attr_pack_layout.append('HH')
-                sub_attr_payload.append(sub_attr_value[0])
-                sub_attr_payload.append(sub_attr_value[1])
-
-            sub_attr_length = calcsize(''.join(sub_attr_pack_layout))
-            sub_attr_payload[sub_attr_length_index] = sub_attr_length
-
-            # add padding
-            for x in xrange(self.pad_bytes_needed(sub_attr_length)):
-                sub_attr_pack_layout.append('x')
-
-            # The [1:] is to remove the leading = so that when we do the ''.join() later
-            # we do not end up with an = in the middle of the pack layout string. There
-            # will be an = at the beginning via self.HEADER_PACK
-            sub_attr_pack_layout = sub_attr_pack_layout[1:]
-
-            # Now extend the ovarall attribute pack_layout/payload to include this sub-attribute
-            pack_layout.extend(sub_attr_pack_layout)
-            payload.extend(sub_attr_payload)
-
-        pack_layout = ''.join(pack_layout)
-
-        # Fill in the length field
-        length = calcsize(pack_layout)
-        payload[attr_length_index] = length
-
-        raw = pack(pack_layout, *payload)
-        raw = self.pad(length, raw)
-        return raw
-
-    def decode(self, parent_msg, data):
-        """
-        value is a dictionary such as:
-        {
-            Link.IFLA_BRIDGE_FLAGS: flags,
-            Link.IFLA_BRIDGE_VLAN_INFO: (vflags, vlanid)
-        }
-        """
-        self.decode_length_type(data)
-        self.value = {}
-
-        data = self.data[4:]
-
-        while data:
-            (sub_attr_length, sub_attr_type) = unpack('=HH', data[:4])
-            sub_attr_end = padded_length(sub_attr_length)
-
-            if not sub_attr_length:
-                self.log.error('parsed a zero length sub-attr')
-                return
-
-            sub_attr_data = data[4:sub_attr_end]
-
-            if sub_attr_type == Link.IFLA_BRIDGE_FLAGS:
-                self.value[Link.IFLA_BRIDGE_FLAGS] = unpack("=H", sub_attr_data[0:2])[0]
-
-            elif sub_attr_type == Link.IFLA_BRIDGE_VLAN_INFO:
-                if Link.IFLA_BRIDGE_VLAN_INFO not in self.value:
-                    self.value[Link.IFLA_BRIDGE_VLAN_INFO] = []
-                self.value[Link.IFLA_BRIDGE_VLAN_INFO].append(tuple(unpack("=HH", sub_attr_data[0:4])))
-
-            elif EXTRA_DEBUG:
-                self.log.debug('Add support for decoding IFLA_AF_SPEC sub-attribute type %s (%d), length %d, padded to %d' %
-                               (parent_msg.get_ifla_bridge_af_spec_to_string(sub_attr_type), sub_attr_type, sub_attr_length, sub_attr_end))
-
-            data = data[sub_attr_end:]
-
-    def dump_lines(self, dump_buffer, line_number, color):
-        line_number = self.dump_first_line(dump_buffer, line_number, color)
-        extra = ''
-
-        next_sub_attr_line = 0
-        sub_attr_line = True
-
-        for x in xrange(1, self.attr_end/4):
-            start = x * 4
-            end = start + 4
-
-            if line_number == next_sub_attr_line:
-                sub_attr_line = True
-
-            if sub_attr_line:
-                sub_attr_line = False
-
-                (sub_attr_length, sub_attr_type) = unpack('=HH', self.data[start:start+4])
-                sub_attr_end = padded_length(sub_attr_length)
-
-                next_sub_attr_line = line_number + (sub_attr_end/4)
-
-                if sub_attr_end == sub_attr_length:
-                    padded_to = ', '
-                else:
-                    padded_to = ' padded to %d, ' % sub_attr_end
-
-                extra = 'Nested Attribute - Length %s (%d)%s Type %s (%d) %s' % \
-                        (zfilled_hex(sub_attr_length, 4), sub_attr_length,
-                         padded_to,
-                         zfilled_hex(sub_attr_type, 4), sub_attr_type,
-                         Link.ifla_bridge_af_spec_to_string.get(sub_attr_type))
-            else:
-                extra = ''
-
-            dump_buffer.append(data_to_color_text(line_number, color, self.data[start:end], extra))
-            line_number += 1
-
-        return line_number
-
-    def get_pretty_value(self):
-        # We do this so we can print a more human readable dictionary
-        # with the names of the nested keys instead of their numbers
-        value_pretty = {}
-
-        for (sub_key, sub_value) in self.value.iteritems():
-            sub_key_pretty = "(%2d) % s" % (sub_key, Link.ifla_bridge_af_spec_to_string.get(sub_key))
-            value_pretty[sub_key_pretty] = sub_value
-
-        return value_pretty
-
-
-
-class AttributeRTA_MULTIPATH(Attribute):
-    """
-/* RTA_MULTIPATH --- array of struct rtnexthop.
- *
- * "struct rtnexthop" describes all necessary nexthop information,
- * i.e. parameters of path to a destination via this nexthop.
- *
- * At the moment it is impossible to set different prefsrc, mtu, window
- * and rtt for different paths from multipath.
- */
-
-struct rtnexthop {
-    unsigned short rtnh_len;
-    unsigned char  rtnh_flags;
-    unsigned char  rtnh_hops;
-    int            rtnh_ifindex;
-};
-    """
-
-    def __init__(self, atype, string, family, logger):
-        Attribute.__init__(self, atype, string, logger)
-        self.family = family
-        self.PACK = None
-        self.LEN = None
-        self.RTNH_PACK = '=HBBL'  # rtnh_len, flags, hops, ifindex
-        self.RTNH_LEN = calcsize(self.RTNH_PACK)
-        self.IPV4_LEN = 4
-        self.IPV6_LEN = 16
-
-    def encode(self):
-
-        # Calculate the length
-        if self.family == AF_INET:
-            ip_len = self.IPV4_LEN
-        elif self.family == AF_INET6:
-            ip_len = self.IPV6_LEN
-
-        # Attribute header
-        length = self.HEADER_LEN + ((self.RTNH_LEN + self.HEADER_LEN + ip_len) * len(self.value))
-        raw = pack(self.HEADER_PACK, length, self.atype)
-
-        rtnh_flags = 0
-        rtnh_hops = 0
-        rtnh_len = self.RTNH_LEN + self.HEADER_LEN + ip_len
-
-        for (nexthop, rtnh_ifindex) in self.value:
-
-            # rtnh structure
-            raw += pack(self.RTNH_PACK, rtnh_len, rtnh_flags, rtnh_hops, rtnh_ifindex)
-
-            # Gateway
-            raw += pack(self.HEADER_PACK, self.HEADER_LEN + ip_len, Route.RTA_GATEWAY)
-
-            if self.family == AF_INET:
-                raw += pack('>L', nexthop)
-            elif self.family == AF_INET6:
-                raw += pack('>QQ', nexthop >> 64, nexthop & 0x0000000000000000FFFFFFFFFFFFFFFF)
-
-        raw = self.pad(length, raw)
-        return raw
-
-    def decode(self, parent_msg, data):
-        self.decode_length_type(data)
-        self.value = []
-
-        data = self.data[4:]
-
-        while data:
-            (rtnh_len, rtnh_flags, rtnh_hops, rtnh_ifindex) = unpack(self.RTNH_PACK, data[:self.RTNH_LEN])
-            data = data[self.RTNH_LEN:]
-
-            (attr_type, attr_length) = unpack(self.HEADER_PACK, self.data[:self.HEADER_LEN])
-            data = data[self.HEADER_LEN:]
-
-            if self.family == AF_INET:
-                nexthop = IPv4Address(unpack('>L', data[:self.IPV4_LEN])[0])
-                self.value.append((nexthop, rtnh_ifindex, rtnh_flags, rtnh_hops))
-                data = data[self.IPV4_LEN:]
-
-            elif self.family == AF_INET6:
-                (data1, data2) = unpack('>QQ', data[:self.IPV6_LEN])
-                nexthop = IPv6Address(data1 << 64 | data2)
-                self.value.append((nexthop, rtnh_ifindex, rtnh_flags, rtnh_hops))
-                data = data[self.IPV6_LEN:]
-
-        self.value = tuple(self.value)
-
-
-class AttributeIFLA_LINKINFO(Attribute):
-    """
-    value is a dictionary such as:
-
-    {
-        Link.IFLA_INFO_KIND : 'vlan',
-        Link.IFLA_INFO_DATA : {
-            Link.IFLA_VLAN_ID : vlanid,
-        }
-    }
-    """
-    def __init__(self, atype, string, family, logger):
-        Attribute.__init__(self, atype, string, logger)
-
-    def encode(self):
-        pack_layout = [self.HEADER_PACK]
-        payload = [0, self.atype]
-        attr_length_index = 0
-
-        kind = self.value[Link.IFLA_INFO_KIND]
-
-        if kind not in ('vlan', 'macvlan', 'vxlan'):
-            raise Exception('Unsupported IFLA_INFO_KIND %s' % kind)
-
-        # For now this assumes that all data will be packed in the native endian
-        # order (=). If a field is added that needs to be packed via network
-        # order (>) then some smarts will need to be added to split the pack_layout
-        # string at the >, split the payload and make the needed pack() calls.
-        #
-        # Until we cross that bridge though we will keep things nice and simple and
-        # pack everything via a single pack() call.
-        for (sub_attr_type, sub_attr_value) in self.value.iteritems():
-            sub_attr_pack_layout = ['=', 'HH']
-            sub_attr_payload = [0, sub_attr_type]
-            sub_attr_length_index = 0
-
-            if sub_attr_type == Link.IFLA_INFO_KIND:
-                sub_attr_pack_layout.append('%ds' % len(sub_attr_value))
-                sub_attr_payload.append(sub_attr_value)
-
-            elif sub_attr_type == Link.IFLA_INFO_DATA:
-
-                for (info_data_type, info_data_value) in sub_attr_value.iteritems():
-
-                    if kind == 'vlan':
-                        if info_data_type == Link.IFLA_VLAN_ID:
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(6)  # length
-                            sub_attr_payload.append(info_data_type)
-
-                            # The vlan-id
-                            sub_attr_pack_layout.append('H')
-                            sub_attr_payload.append(info_data_value)
-
-                            # pad 2 bytes
-                            sub_attr_pack_layout.extend('xx')
-
-                        else:
-                            self.log.debug('Add support for encoding IFLA_INFO_DATA vlan sub-attribute type %d' % info_data_type)
-
-                    elif kind == 'macvlan':
-                        if info_data_type == Link.IFLA_MACVLAN_MODE:
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(8)  # length
-                            sub_attr_payload.append(info_data_type)
-
-                            # macvlan mode
-                            sub_attr_pack_layout.append('L')
-                            sub_attr_payload.append(info_data_value)
-
-                        else:
-                            self.log.debug('Add support for encoding IFLA_INFO_DATA macvlan sub-attribute type %d' % info_data_type)
-
-                    elif kind == 'vxlan':
-                        if info_data_type in (Link.IFLA_VXLAN_ID,
-                                              Link.IFLA_VXLAN_LINK,
-                                              Link.IFLA_VXLAN_AGEING,
-                                              Link.IFLA_VXLAN_LIMIT,
-                                              Link.IFLA_VXLAN_PORT_RANGE):
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(8)  # length
-                            sub_attr_payload.append(info_data_type)
-
-                            sub_attr_pack_layout.append('L')
-                            sub_attr_payload.append(info_data_value)
-
-                        elif info_data_type in (Link.IFLA_VXLAN_GROUP,
-                                                Link.IFLA_VXLAN_LOCAL):
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(8)  # length
-                            sub_attr_payload.append(info_data_type)
-
-                            sub_attr_pack_layout.append('L')
-
-                            reorder = unpack('<L', IPv4Address(info_data_value).packed)[0]
-                            sub_attr_payload.append(IPv4Address(reorder))
-
-                        elif info_data_type in (Link.IFLA_VXLAN_PORT,):
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(6)
-                            sub_attr_payload.append(info_data_type)
-
-                            sub_attr_pack_layout.append('H')
-
-                            # byte swap
-                            swaped = pack(">H", info_data_value)
-                            sub_attr_payload.append(unpack("<H", swaped)[0])
-
-                            sub_attr_pack_layout.extend('xx')
-
-                        elif info_data_type in (Link.IFLA_VXLAN_TTL,
-                                                Link.IFLA_VXLAN_TOS,
-                                                Link.IFLA_VXLAN_LEARNING,
-                                                Link.IFLA_VXLAN_PROXY,
-                                                Link.IFLA_VXLAN_RSC,
-                                                Link.IFLA_VXLAN_L2MISS,
-                                                Link.IFLA_VXLAN_L3MISS,
-                                                Link.IFLA_VXLAN_UDP_CSUM,
-                                                Link.IFLA_VXLAN_UDP_ZERO_CSUM6_TX,
-                                                Link.IFLA_VXLAN_UDP_ZERO_CSUM6_RX,
-                                                Link.IFLA_VXLAN_REMCSUM_TX,
-                                                Link.IFLA_VXLAN_REMCSUM_RX,
-                                                Link.IFLA_VXLAN_REPLICATION_TYPE):
-                            sub_attr_pack_layout.append('HH')
-                            sub_attr_payload.append(6)
-                            sub_attr_payload.append(info_data_type)
-
-                            sub_attr_pack_layout.append('B')
-                            sub_attr_payload.append(info_data_value)
-                            sub_attr_pack_layout.extend('xxx')
-
-                        else:
-                            self.log.debug('Add support for encoding IFLA_INFO_DATA vxlan sub-attribute type %d' % info_data_type)
-
-            else:
-                self.log.debug('Add support for encoding IFLA_LINKINFO sub-attribute type %d' % sub_attr_type)
-                continue
-
-            sub_attr_length = calcsize(''.join(sub_attr_pack_layout))
-            sub_attr_payload[sub_attr_length_index] = sub_attr_length
-
-            # add padding
-            for x in xrange(self.pad_bytes_needed(sub_attr_length)):
-                sub_attr_pack_layout.append('x')
-
-            # The [1:] is to remove the leading = so that when we do the ''.join() later
-            # we do not end up with an = in the middle of the pack layout string. There
-            # will be an = at the beginning via self.HEADER_PACK
-            sub_attr_pack_layout = sub_attr_pack_layout[1:]
-
-            # Now extend the ovarall attribute pack_layout/payload to include this sub-attribute
-            pack_layout.extend(sub_attr_pack_layout)
-            payload.extend(sub_attr_payload)
-
-        pack_layout = ''.join(pack_layout)
-
-        # Fill in the length field
-        length = calcsize(pack_layout)
-        payload[attr_length_index] = length
-
-        raw = pack(pack_layout, *payload)
-        raw = self.pad(length, raw)
-        return raw
-
-    def decode(self, parent_msg, data):
-        """
-        value is a dictionary such as:
-
-        {
-            Link.IFLA_INFO_KIND : 'vlan',
-            Link.IFLA_INFO_DATA : {
-                Link.IFLA_VLAN_ID : vlanid,
-            }
-        }
-        """
-        self.decode_length_type(data)
-        self.value = {}
-
-        data = self.data[4:]
-
-        # IFLA_MACVLAN_MODE and IFLA_VLAN_ID both have a value of 1 and both are
-        # valid IFLA_INFO_DATA entries :( The sender must TX IFLA_INFO_KIND
-        # first in order for us to know if "1" is IFLA_MACVLAN_MODE vs IFLA_VLAN_ID.
-        while data:
-            (sub_attr_length, sub_attr_type) = unpack('=HH', data[:4])
-            sub_attr_end = padded_length(sub_attr_length)
-
-            if not sub_attr_length:
-                self.log.error('parsed a zero length sub-attr')
-                return
-
-            if sub_attr_type == Link.IFLA_INFO_KIND:
-                self.value[Link.IFLA_INFO_KIND] = remove_trailing_null(unpack('%ds' % (sub_attr_length - 4), data[4:sub_attr_length])[0])
-
-            elif sub_attr_type == Link.IFLA_INFO_DATA:
-
-                sub_attr_data = data[4:sub_attr_end]
-                self.value[Link.IFLA_INFO_DATA] = {}
-
-                while sub_attr_data:
-                    (info_data_length, info_data_type) = unpack('=HH', sub_attr_data[:4])
-                    info_data_end = padded_length(info_data_length)
-                    # self.log.info('sub attr length %d, end %d, type %d' % (info_data_length, info_data_end, info_data_type))
-
-                    if not sub_attr_data:
-                        self.log.error('RXed zero length sub-attribute')
-                        break
-
-                    if Link.IFLA_INFO_KIND not in self.value:
-                        self.log.warning('IFLA_INFO_KIND is not known...we cannot parse IFLA_INFO_DATA')
-
-                    elif self.value[Link.IFLA_INFO_KIND] == 'vlan':
-                        if info_data_type == Link.IFLA_VLAN_ID:
-                            self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=H', sub_attr_data[4:6])[0]
-                        elif EXTRA_DEBUG:
-                            self.log.debug('Add support for decoding IFLA_INFO_KIND vlan type %s (%d), length %d, padded to %d' %
-                                           (parent_msg.get_ifla_vlan_string(info_data_type), info_data_type, info_data_length, info_data_end))
-
-                    elif self.value[Link.IFLA_INFO_KIND] == 'macvlan':
-                        if info_data_type == Link.IFLA_MACVLAN_MODE:
-                            self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[0]
-                        elif EXTRA_DEBUG:
-                            self.log.debug('Add support for decoding IFLA_INFO_KIND macvlan type %s (%d), length %d, padded to %d' %
-                                           (parent_msg.get_ifla_macvlan_string(info_data_type), info_data_type, info_data_length, info_data_end))
-
-                    elif self.value[Link.IFLA_INFO_KIND] == 'vxlan':
-
-                        # IPv4Address
-                        if info_data_type in (Link.IFLA_VXLAN_GROUP,
-                                              Link.IFLA_VXLAN_LOCAL):
-                            self.value[Link.IFLA_INFO_DATA][info_data_type] = IPv4Address(unpack('>L', sub_attr_data[4:8])[0])
-
-                        # 4-byte int
-                        elif info_data_type in (Link.IFLA_VXLAN_ID,
-                                                Link.IFLA_VXLAN_LINK,
-                                                Link.IFLA_VXLAN_AGEING,
-                                                Link.IFLA_VXLAN_LIMIT,
-                                                Link.IFLA_VXLAN_PORT_RANGE):
-                            self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[0]
-
-                        # 2-byte int
-                        elif info_data_type in (Link.IFLA_VXLAN_PORT, ):
-                            self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=H', sub_attr_data[4:6])[0]
-
-                        # 1-byte int
-                        elif info_data_type in (Link.IFLA_VXLAN_TTL,
-                                                Link.IFLA_VXLAN_TOS,
-                                                Link.IFLA_VXLAN_LEARNING,
-                                                Link.IFLA_VXLAN_PROXY,
-                                                Link.IFLA_VXLAN_RSC,
-                                                Link.IFLA_VXLAN_L2MISS,
-                                                Link.IFLA_VXLAN_L3MISS,
-                                                Link.IFLA_VXLAN_UDP_CSUM,
-                                                Link.IFLA_VXLAN_UDP_ZERO_CSUM6_TX,
-                                                Link.IFLA_VXLAN_UDP_ZERO_CSUM6_RX,
-                                                Link.IFLA_VXLAN_REMCSUM_TX,
-                                                Link.IFLA_VXLAN_REMCSUM_RX,
-                                                Link.IFLA_VXLAN_REPLICATION_TYPE):
-                            self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=B', sub_attr_data[4])[0]
-
-                        elif EXTRA_DEBUG:
-                            # sub_attr_end = padded_length(sub_attr_length)
-                            self.log.debug('Add support for decoding IFLA_INFO_KIND vxlan type %s (%d), length %d, padded to %d' %
-                                           (parent_msg.get_ifla_vxlan_string(info_data_type), info_data_type, info_data_length, info_data_end))
-
-                    elif self.value[Link.IFLA_INFO_KIND] == 'bond':
-
-                        if info_data_type in (Link.IFLA_BOND_AD_INFO, ):
-                            bond_value = {}
-
-                            self.value[Link.IFLA_INFO_DATA][info_data_type] = bond_value
-                        elif EXTRA_DEBUG:
-                            self.log.debug('Add support for decoding IFLA_INFO_KIND bond type %s (%d), length %d, padded to %d' %
-                                           (parent_msg.get_ifla_bond_string(info_data_type), info_data_type, info_data_length, info_data_end))
-
-                    elif self.value[Link.IFLA_INFO_KIND] == 'bridge':
-
-                        if info_data_type in (Link.IFLA_BRPORT_STATE,
-                                              Link.IFLA_BRPORT_PRIORITY,
-                                              Link.IFLA_BRPORT_COST):
-                            self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=L', sub_attr_data[4:8])[0]
-
-                        elif info_data_type in (Link.IFLA_BRPORT_FAST_LEAVE, ):
-                            self.value[Link.IFLA_INFO_DATA][info_data_type] = unpack('=B', sub_attr_data[4])[0]
-
-                        elif EXTRA_DEBUG:
-                            self.log.debug('Add support for decoding IFLA_INFO_KIND bridge type %s (%d), length %d, padded to %d' %
-                                           (parent_msg.get_ifla_bridge_string(info_data_type), info_data_type, info_data_length, info_data_end))
-
-                    elif EXTRA_DEBUG:
-                        self.log.debug("Add support for decoding IFLA_INFO_KIND %s (%d), length %d, padded to %d" %
-                                        (self.value[Link.IFLA_INFO_KIND], info_data_type, info_data_length, info_data_end))
-
-                    sub_attr_data = sub_attr_data[info_data_end:]
-
-            elif sub_attr_type == Link.IFLA_INFO_SLAVE_KIND:
-                self.value[Link.IFLA_INFO_SLAVE_KIND] = remove_trailing_null(unpack('%ds' % (sub_attr_length - 4), data[4:sub_attr_length])[0])
-
-            elif EXTRA_DEBUG:
-                self.log.debug('Add support for decoding IFLA_LINKINFO sub-attribute type %s (%d), length %d, padded to %d' %
-                               (parent_msg.get_ifla_info_string(sub_attr_type), sub_attr_type, sub_attr_length, sub_attr_end))
-
-            data = data[sub_attr_end:]
-
-        # self.log.info('IFLA_LINKINFO values %s' % pformat(self.value))
-
-    def dump_lines(self, dump_buffer, line_number, color):
-        line_number = self.dump_first_line(dump_buffer, line_number, color)
-        extra = ''
-
-        next_sub_attr_line = 0
-        sub_attr_line = True
-
-        for x in xrange(1, self.attr_end/4):
-            start = x * 4
-            end = start + 4
-
-            if line_number == next_sub_attr_line:
-                sub_attr_line = True
-
-            if sub_attr_line:
-                sub_attr_line = False
-
-                (sub_attr_length, sub_attr_type) = unpack('=HH', self.data[start:start+4])
-                sub_attr_end = padded_length(sub_attr_length)
-
-                next_sub_attr_line = line_number + (sub_attr_end/4)
-
-                if sub_attr_end == sub_attr_length:
-                    padded_to = ', '
-                else:
-                    padded_to = ' padded to %d, ' % sub_attr_end
-
-                extra = 'Nested Attribute - Length %s (%d)%s Type %s (%d) %s' % \
-                        (zfilled_hex(sub_attr_length, 4), sub_attr_length,
-                         padded_to,
-                         zfilled_hex(sub_attr_type, 4), sub_attr_type,
-                         Link.ifla_info_to_string.get(sub_attr_type))
-            else:
-                extra = ''
-
-            dump_buffer.append(data_to_color_text(line_number, color, self.data[start:end], extra))
-            line_number += 1
-
-        return line_number
-
-    def get_pretty_value(self):
-        value_pretty = self.value
-        ifla_info_kind = self.value.get(Link.IFLA_INFO_KIND)
-
-        # We do this so we can print a more human readable dictionary
-        # with the names of the nested keys instead of their numbers
-
-        # Most of these are placeholders...we need to add support
-        # for more human readable dictionaries for bond, bridge, etc
-        if ifla_info_kind == 'bond':
-            pass
-
-        elif ifla_info_kind == 'bridge':
-            pass
-
-        elif ifla_info_kind == 'macvlan':
-            pass
-
-        elif ifla_info_kind == 'vlan':
-            pass
-
-        elif ifla_info_kind == 'vxlan':
-            value_pretty = {}
-
-            for (sub_key, sub_value) in self.value.iteritems():
-                sub_key_pretty = "(%2d) % s" % (sub_key, Link.ifla_info_to_string[sub_key])
-                sub_value_pretty = sub_value
-
-                if sub_key == Link.IFLA_INFO_DATA:
-                    sub_value_pretty = {}
-
-                    for (sub_sub_key, sub_sub_value) in sub_value.iteritems():
-                        sub_sub_key_pretty = "(%2d) %s" % (sub_sub_key, Link.ifla_vxlan_to_string[sub_sub_key])
-                        sub_value_pretty[sub_sub_key_pretty] = sub_sub_value
-
-                value_pretty[sub_key_pretty] = sub_value_pretty
-
-        return value_pretty
-
-
-class NetlinkPacket(object):
-    """
-    Netlink Header
-
-    0                   1                   2                   3
-    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |                          Length                             |
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |            Type              |           Flags              |
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |                      Sequence Number                        |
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |                      Process ID (PID)                       |
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    """
-
-    header_PACK = 'IHHII'
-    header_LEN  = calcsize(header_PACK)
-
-    # Netlink packet types
-    # /usr/include/linux/rtnetlink.h
-    type_to_string = {
-        NLMSG_NOOP    : 'NLMSG_NOOP',
-        NLMSG_ERROR   : 'NLMSG_ERROR',
-        NLMSG_DONE    : 'NLMSG_DONE',
-        NLMSG_OVERRUN : 'NLMSG_OVERRUN',
-        RTM_NEWLINK   : 'RTM_NEWLINK',
-        RTM_DELLINK   : 'RTM_DELLINK',
-        RTM_GETLINK   : 'RTM_GETLINK',
-        RTM_SETLINK   : 'RTM_SETLINK',
-        RTM_NEWADDR   : 'RTM_NEWADDR',
-        RTM_DELADDR   : 'RTM_DELADDR',
-        RTM_GETADDR   : 'RTM_GETADDR',
-        RTM_NEWNEIGH  : 'RTM_NEWNEIGH',
-        RTM_DELNEIGH  : 'RTM_DELNEIGH',
-        RTM_GETNEIGH  : 'RTM_GETNEIGH',
-        RTM_NEWROUTE  : 'RTM_NEWROUTE',
-        RTM_DELROUTE  : 'RTM_DELROUTE',
-        RTM_GETROUTE  : 'RTM_GETROUTE',
-        RTM_NEWQDISC  : 'RTM_NEWQDISC',
-        RTM_DELQDISC  : 'RTM_DELQDISC',
-        RTM_GETQDISC  : 'RTM_GETQDISC'
-    }
-
-    def __init__(self, msgtype, debug, owner_logger=None, use_color=True):
-        self.msgtype     = msgtype
-        self.attributes  = {}
-        self.dump_buffer = ['']
-        self.line_number = 1
-        self.debug       = debug
-        self.message     = None
-        self.use_color   = use_color
-        self.family      = None
-
-        if owner_logger:
-            self.log = owner_logger
-        else:
-            self.log = log
-
-    def __str__(self):
-        return self.get_type_string()
-
-    def get_string(self, to_string, index):
-        """
-        Used to do lookups in all of the various FOO_to_string dictionaries
-        but returns 'UNKNOWN' if the key is bogus
-        """
-        if index in to_string:
-            return to_string[index]
-        return 'UNKNOWN'
-
-    def get_type_string(self, msgtype=None):
-        if not msgtype:
-            msgtype = self.msgtype
-        return self.get_string(self.type_to_string, msgtype)
-
-    def get_flags_string(self):
-        foo = []
-
-        for (flag, flag_string) in self.flag_to_string.iteritems():
-            if self.flags & flag:
-                foo.append(flag_string)
-
-        return ', '.join(foo)
-
-    def decode_packet(self, length, flags, seq, pid, data):
-        self.length      = length
-        self.flags       = flags
-        self.seq         = seq
-        self.pid         = pid
-        self.header_data = data[0:self.header_LEN]
-        self.msg_data    = data[self.header_LEN:length]
-
-        self.decode_netlink_header()
-        self.decode_service_header()
-
-        # NLMSG_ERROR is special case, it does not have attributes to decode
-        if self.msgtype != NLMSG_ERROR:
-            self.decode_attributes()
-
-    def get_netlink_header_flags_string(self, msg_type, flags):
-        foo = []
-
-        if flags & NLM_F_REQUEST:
-            foo.append('NLM_F_REQUEST')
-
-        if flags & NLM_F_MULTI:
-            foo.append('NLM_F_MULTI')
-
-        if flags & NLM_F_ACK:
-            foo.append('NLM_F_ACK')
-
-        if flags & NLM_F_ECHO:
-            foo.append('NLM_F_ECHO')
-
-        # Modifiers to GET query
-        if msg_type in (RTM_GETLINK, RTM_GETADDR, RTM_GETNEIGH, RTM_GETROUTE, RTM_GETQDISC):
-            if flags & NLM_F_DUMP:
-                foo.append('NLM_F_DUMP')
-            else:
-                if flags & NLM_F_MATCH:
-                    foo.append('NLM_F_MATCH')
-
-                if flags & NLM_F_ROOT:
-                    foo.append('NLM_F_ROOT')
-
-            if flags & NLM_F_ATOMIC:
-                foo.append('NLM_F_ATOMIC')
-
-        # Modifiers to NEW query
-        elif msg_type in (RTM_NEWLINK, RTM_NEWADDR, RTM_NEWNEIGH, RTM_NEWROUTE, RTM_NEWQDISC):
-            if flags & NLM_F_REPLACE:
-                foo.append('NLM_F_REPLACE')
-
-            if flags & NLM_F_EXCL:
-                foo.append('NLM_F_EXCL')
-
-            if flags & NLM_F_CREATE:
-                foo.append('NLM_F_CREATE')
-
-            if flags & NLM_F_APPEND:
-                foo.append('NLM_F_APPEND')
-
-        return ', '.join(foo)
-
-    # When we first RXed the netlink message we had to decode the header to
-    # determine what type of netlink message we were dealing with.  So the
-    # header has actually already been decoded...what we do here is
-    # populate the dump_buffer lines with the header content.
-    def decode_netlink_header(self):
-
-        if not self.debug:
-            return
-
-        header_data = self.header_data
-
-        # Print the netlink header in red
-        netlink_header_length = 16
-        color = red if self.use_color else None
-        color_start = "\033[%dm" % color if color else ""
-        color_end = "\033[0m" if color else ""
-        self.dump_buffer.append("  %sNetlink Header%s" % (color_start, color_end))
-
-        for x in range(0, netlink_header_length/4):
-            start = x * 4
-            end = start + 4
-
-            if self.line_number == 1:
-                data = unpack('=L', header_data[start:end])[0]
-                extra = "Length %s (%d)" % (zfilled_hex(data, 8), data)
-
-            elif self.line_number == 2:
-                (data1, data2) = unpack('HH', header_data[start:end])
-                extra = "Type %s (%d - %s), Flags %s (%s)" % \
-                    (zfilled_hex(data1, 4), data1, self.get_type_string(data1),
-                     zfilled_hex(data2, 4), self.get_netlink_header_flags_string(data1, data2))
-
-            elif self.line_number == 3:
-                data = unpack('=L', header_data[start:end])[0]
-                extra = "Sequence Number %s (%d)" % (zfilled_hex(data, 8), data)
-
-            elif self.line_number == 4:
-                data = unpack('=L', header_data[start:end])[0]
-                extra = "Process ID %s (%d)" % (zfilled_hex(data, 8), data)
-            else:
-                extra = "Unexpected line number %d" % self.line_number
-
-            self.dump_buffer.append(data_to_color_text(self.line_number, color, header_data[start:end], extra))
-            self.line_number += 1
-
-    def decode_attributes(self):
-        """
-        Decode the attributes and populate the dump_buffer
-        """
-
-        if self.debug:
-            self.dump_buffer.append("  Attributes")
-            color = green if self.use_color else None
-
-        data = self.msg_data[self.LEN:]
-
-        while data:
-            (length, attr_type) = unpack('=HH', data[:4])
-
-            # If this is zero we will stay in this loop for forever
-            if not length:
-                self.log.error('Length is zero')
-                return
-
-            if len(data) < length:
-                self.log.error("Buffer underrun %d < %d" % (len(data), length))
-                return
-
-            attr = self.add_attribute(attr_type, None)
-
-            # Find the end of 'data' for this attribute and decode our section
-            # of 'data'. attributes are padded for alignment thus the attr_end.
-            #
-            # How the attribute is decoded/unpacked is specific per AttributeXXXX class.
-            attr_end = padded_length(length)
-            attr.decode(self, data[0:attr_end])
-
-            if self.debug:
-                self.line_number = attr.dump_lines(self.dump_buffer, self.line_number, color)
-
-                # Alternate back and forth between green and blue
-                if self.use_color:
-                    if color == green:
-                        color = blue
-                    else:
-                        color = green
-
-            data = data[attr_end:]
-
-    def add_attribute(self, attr_type, value):
-        nested = True if attr_type & NLA_F_NESTED else False
-        net_byteorder = True if attr_type & NLA_F_NET_BYTEORDER else False
-        attr_type = attr_type & NLA_TYPE_MASK
-
-        # Given an attr_type (say RTA_DST) find the type of AttributeXXXX class
-        # that we will use to store this attribute...AttributeIPAddress in the
-        # case of RTA_DST.
-        if attr_type in self.attribute_to_class:
-            (attr_string, attr_class) = self.attribute_to_class[attr_type]
-        else:
-            attr_string = "UNKNOWN_ATTRIBUTE_%d" % attr_type
-            attr_class = AttributeGeneric
-            self.log.debug("Attribute %d is not defined in %s.attribute_to_class, assuming AttributeGeneric" %
-                           (attr_type, self.__class__.__name__))
-
-        attr = attr_class(attr_type, attr_string, self.family, self.log)
-
-        attr.set_value(value)
-        attr.set_nested(nested)
-        attr.set_net_byteorder(net_byteorder)
-
-        # self.attributes is a dictionary keyed by the attribute type where
-        # the value is an instance of the corresponding AttributeXXXX class.
-        self.attributes[attr_type] = attr
-
-        return attr
-
-    def get_attribute_value(self, attr_type):
-        if attr_type not in self.attributes:
-            return None
-
-        return self.attributes[attr_type].value
-
-    def get_attr_string(self, attr_type):
-        """
-        Example: If attr_type is Address.IFA_CACHEINFO return the string 'IFA_CACHEINFO'
-        """
-        if attr_type in self.attribute_to_class:
-            (attr_string, attr_class) = self.attribute_to_class[attr_type]
-            return attr_string
-        return str(attr_type)
-
-    def build_message(self, seq, pid):
-        self.seq = seq
-        self.pid = pid
-        attrs = ''
-
-        for attr in self.attributes.itervalues():
-            attrs += attr.encode()
-
-        self.length = self.header_LEN + len(self.body) + len(attrs)
-        self.header_data = pack(self.header_PACK, self.length, self.msgtype, self.flags, self.seq, self.pid)
-        self.msg_data = self.body + attrs
-        self.message = self.header_data + self.msg_data
-
-        if self.debug:
-            self.decode_netlink_header()
-            self.decode_service_header()
-            self.decode_attributes()
-            self.dump("TXed %s, length %d, seq %d, pid %d, flags 0x%x (%s)" %
-                      (self, self.length, self.seq, self.pid, self.flags,
-                       self.get_netlink_header_flags_string(self.msgtype, self.flags)))
-
-    def pretty_display_dict(self, dic, level):
-        for k,v in dic.iteritems():
-            if isinstance(v, dict):
-                self.log.debug(' '*level + str(k) + ':')
-                self.pretty_display_dict(v, level+1)
-            else:
-                self.log.debug(' '*level + str(k) + ': ' + str(v))
-
-    # Print the netlink message in hex. This is only used for debugging.
-    def dump(self, desc=None):
-        attr_string = {}
-
-        if desc is None:
-            desc = "RXed %s, length %d, seq %d, pid %d, flags 0x%x" % (self, self.length, self.seq, self.pid, self.flags)
-
-        for (attr_type, attr_obj) in self.attributes.iteritems():
-            key_string = "(%2d) %s" % (attr_type, self.get_attr_string(attr_type))
-            attr_string[key_string] = attr_obj.get_pretty_value()
-
-        if self.use_color:
-            self.log.debug("%s\n%s\n\nAttributes Summary\n%s\n" %
-                           (desc, '\n'.join(self.dump_buffer), pformat(attr_string)))
-        else:
-            # Assume if we are not allowing color output we also don't want embedded
-            # newline characters in the output. Output each line individually.
-            self.log.debug(desc)
-            for line in self.dump_buffer:
-                self.log.debug(line)
-            self.log.debug("")
-            self.log.debug("Attributes Summary")
-            self.pretty_display_dict(attr_string, 1)
-
-
-class Address(NetlinkPacket):
-    """
-    Service Header
-    0                   1                   2                   3
-    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |   Family    |     Length    |     Flags     |    Scope      |
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |                     Interface Index                         |
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    """
-
-    # Address attributes
-    # /usr/include/linux/if_addr.h
-    IFA_UNSPEC    = 0x00
-    IFA_ADDRESS   = 0x01
-    IFA_LOCAL     = 0x02
-    IFA_LABEL     = 0x03
-    IFA_BROADCAST = 0x04
-    IFA_ANYCAST   = 0x05
-    IFA_CACHEINFO = 0x06
-    IFA_MULTICAST = 0x07
-    IFA_FLAGS     = 0x08
-
-    attribute_to_class = {
-        IFA_UNSPEC    : ('IFA_UNSPEC', AttributeGeneric),
-        IFA_ADDRESS   : ('IFA_ADDRESS', AttributeIPAddress),
-        IFA_LOCAL     : ('IFA_LOCAL', AttributeIPAddress),
-        IFA_LABEL     : ('IFA_LABEL', AttributeString),
-        IFA_BROADCAST : ('IFA_BROADCAST', AttributeIPAddress),
-        IFA_ANYCAST   : ('IFA_ANYCAST', AttributeIPAddress),
-        IFA_CACHEINFO : ('IFA_CACHEINFO', AttributeGeneric),
-        IFA_MULTICAST : ('IFA_MULTICAST', AttributeIPAddress),
-        IFA_FLAGS     : ('IFA_FLAGS', AttributeGeneric)
-    }
-
-    # Address flags
-    # /usr/include/linux/if_addr.h
-    IFA_F_SECONDARY   = 0x01
-    IFA_F_NODAD       = 0x02
-    IFA_F_OPTIMISTIC  = 0x04
-    IFA_F_DADFAILED   = 0x08
-    IFA_F_HOMEADDRESS = 0x10
-    IFA_F_DEPRECATED  = 0x20
-    IFA_F_TENTATIVE   = 0x40
-    IFA_F_PERMANENT   = 0x80
-
-    flag_to_string = {
-        IFA_F_SECONDARY   : 'IFA_F_SECONDARY',
-        IFA_F_NODAD       : 'IFA_F_NODAD',
-        IFA_F_OPTIMISTIC  : 'IFA_F_OPTIMISTIC',
-        IFA_F_DADFAILED   : 'IFA_F_DADFAILED',
-        IFA_F_HOMEADDRESS : 'IFA_F_HOMEADDRESS',
-        IFA_F_DEPRECATED  : 'IFA_F_DEPRECATED',
-        IFA_F_TENTATIVE   : 'IFA_F_TENTATIVE',
-        IFA_F_PERMANENT   : 'IFA_F_PERMANENT'
-    }
-
-    def __init__(self, msgtype, debug=False, logger=None, use_color=True):
-        NetlinkPacket.__init__(self, msgtype, debug, logger, use_color)
-        self.PACK = '4Bi'
-        self.LEN = calcsize(self.PACK)
-
-    def decode_service_header(self):
-
-        # Nothing to do if the message did not contain a service header
-        if self.length == self.header_LEN:
-            return
-
-        (self.family, self.prefixlen, self.flags, self.scope,
-         self.ifindex) = \
-            unpack(self.PACK, self.msg_data[:self.LEN])
-
-        if self.debug:
-            color = yellow if self.use_color else None
-            color_start = "\033[%dm" % color if color else ""
-            color_end = "\033[0m" if color else ""
-            self.dump_buffer.append("  %sService Header%s" % (color_start, color_end))
-
-            for x in range(0, self.LEN/4):
-                if self.line_number == 5:
-                    extra = "Family %s (%d), Length %s (%d), Flags %s, Scope %s (%d)" % \
-                            (zfilled_hex(self.family, 2), self.family,
-                             zfilled_hex(self.prefixlen, 2), self.prefixlen,
-                             zfilled_hex(self.flags, 2),
-                             zfilled_hex(self.scope, 2), self.scope)
-                elif self.line_number == 6:
-                    extra = "Interface Index %s (%d)" % (zfilled_hex(self.ifindex, 8), self.ifindex)
-                else:
-                    extra = "Unexpected line number %d" % self.line_number
-
-                start = x * 4
-                end = start + 4
-                self.dump_buffer.append(data_to_color_text(self.line_number, color, self.msg_data[start:end], extra))
-                self.line_number += 1
-
-
-class Error(NetlinkPacket):
-
-    # Error codes
-    # /include/netlink/errno.h
-    NLE_SUCCESS           = 0x00
-    NLE_FAILURE           = 0x01
-    NLE_INTR              = 0x02
-    NLE_BAD_SOCK          = 0x03
-    NLE_AGAIN             = 0x04
-    NLE_NOMEM             = 0x05
-    NLE_EXIST             = 0x06
-    NLE_INVAL             = 0x07
-    NLE_RANGE             = 0x08
-    NLE_MSGSIZE           = 0x09
-    NLE_OPNOTSUPP         = 0x0A
-    NLE_AF_NOSUPPORT      = 0x0B
-    NLE_OBJ_NOTFOUND      = 0x0C
-    NLE_NOATTR            = 0x0D
-    NLE_MISSING_ATTR      = 0x0E
-    NLE_AF_MISMATCH       = 0x0F
-    NLE_SEQ_MISMATCH      = 0x10
-    NLE_MSG_OVERFLOW      = 0x11
-    NLE_MSG_TRUNC         = 0x12
-    NLE_NOADDR            = 0x13
-    NLE_SRCRT_NOSUPPORT   = 0x14
-    NLE_MSG_TOOSHORT      = 0x15
-    NLE_MSGTYPE_NOSUPPORT = 0x16
-    NLE_OBJ_MISMATCH      = 0x17
-    NLE_NOCACHE           = 0x18
-    NLE_BUSY              = 0x19
-    NLE_PROTO_MISMATCH    = 0x1A
-    NLE_NOACCESS          = 0x1B
-    NLE_PERM              = 0x1C
-    NLE_PKTLOC_FILE       = 0x1D
-    NLE_PARSE_ERR         = 0x1E
-    NLE_NODEV             = 0x1F
-    NLE_IMMUTABLE         = 0x20
-    NLE_DUMP_INTR         = 0x21
-
-    error_to_string = {
-        NLE_SUCCESS           : 'NLE_SUCCESS',
-        NLE_FAILURE           : 'NLE_FAILURE',
-        NLE_INTR              : 'NLE_INTR',
-        NLE_BAD_SOCK          : 'NLE_BAD_SOCK',
-        NLE_AGAIN             : 'NLE_AGAIN',
-        NLE_NOMEM             : 'NLE_NOMEM',
-        NLE_EXIST             : 'NLE_EXIST',
-        NLE_INVAL             : 'NLE_INVAL',
-        NLE_RANGE             : 'NLE_RANGE',
-        NLE_MSGSIZE           : 'NLE_MSGSIZE',
-        NLE_OPNOTSUPP         : 'NLE_OPNOTSUPP',
-        NLE_AF_NOSUPPORT      : 'NLE_AF_NOSUPPORT',
-        NLE_OBJ_NOTFOUND      : 'NLE_OBJ_NOTFOUND',
-        NLE_NOATTR            : 'NLE_NOATTR',
-        NLE_MISSING_ATTR      : 'NLE_MISSING_ATTR',
-        NLE_AF_MISMATCH       : 'NLE_AF_MISMATCH',
-        NLE_SEQ_MISMATCH      : 'NLE_SEQ_MISMATCH',
-        NLE_MSG_OVERFLOW      : 'NLE_MSG_OVERFLOW',
-        NLE_MSG_TRUNC         : 'NLE_MSG_TRUNC',
-        NLE_NOADDR            : 'NLE_NOADDR',
-        NLE_SRCRT_NOSUPPORT   : 'NLE_SRCRT_NOSUPPORT',
-        NLE_MSG_TOOSHORT      : 'NLE_MSG_TOOSHORT',
-        NLE_MSGTYPE_NOSUPPORT : 'NLE_MSGTYPE_NOSUPPORT',
-        NLE_OBJ_MISMATCH      : 'NLE_OBJ_MISMATCH',
-        NLE_NOCACHE           : 'NLE_NOCACHE',
-        NLE_BUSY              : 'NLE_BUSY',
-        NLE_PROTO_MISMATCH    : 'NLE_PROTO_MISMATCH',
-        NLE_NOACCESS          : 'NLE_NOACCESS',
-        NLE_PERM              : 'NLE_PERM',
-        NLE_PKTLOC_FILE       : 'NLE_PKTLOC_FILE',
-        NLE_PARSE_ERR         : 'NLE_PARSE_ERR',
-        NLE_NODEV             : 'NLE_NODEV',
-        NLE_IMMUTABLE         : 'NLE_IMMUTABLE',
-        NLE_DUMP_INTR         : 'NLE_DUMP_INTR'
-    }
-
-    def __init__(self, msgtype, debug=False, logger=None, use_color=True):
-        NetlinkPacket.__init__(self, msgtype, debug, logger, use_color)
-        self.PACK = '=iLHHLL'
-        self.LEN = calcsize(self.PACK)
-
-    def decode_service_header(self):
-
-        # Nothing to do if the message did not contain a service header
-        if self.length == self.header_LEN:
-            return
-
-        (self.negative_errno, self.bad_msg_len, self.bad_msg_type,
-         self.bad_msg_flag, self.bad_msg_seq, self.bad_msg_pid) =\
-            unpack(self.PACK, self.msg_data[:self.LEN])
-
-        if self.debug:
-            color = yellow if self.use_color else None
-            color_start = "\033[%dm" % color if color else ""
-            color_end = "\033[0m" if color else ""
-            self.dump_buffer.append("  %sService Header%s" % (color_start, color_end))
-
-            for x in range(0, self.LEN/4):
-
-                if self.line_number == 5:
-                    extra = "Error Number %s is %s" % (self.negative_errno, self.error_to_string.get(abs(self.negative_errno)))
-                    # zfilled_hex(self.negative_errno, 2)
-
-                elif self.line_number == 6:
-                    extra = "Length %s (%d)" % (zfilled_hex(self.bad_msg_len, 8), self.bad_msg_len)
-
-                elif self.line_number == 7:
-                    extra = "Type %s (%d - %s), Flags %s (%s)" % \
-                        (zfilled_hex(self.bad_msg_type, 4), self.bad_msg_type, self.get_type_string(self.bad_msg_type),
-                         zfilled_hex(self.bad_msg_flag, 4), self.get_netlink_header_flags_string(self.bad_msg_type, self.bad_msg_flag))
-
-                elif self.line_number == 8:
-                    extra = "Sequence Number %s (%d)" % (zfilled_hex(self.bad_msg_seq, 8), self.bad_msg_seq)
-
-                elif self.line_number == 9:
-                    extra = "Process ID %s (%d)" % (zfilled_hex(self.bad_msg_pid, 8), self.bad_msg_pid)
-
-                else:
-                    extra = "Unexpected line number %d" % self.line_number
-
-                start = x * 4
-                end = start + 4
-                self.dump_buffer.append(data_to_color_text(self.line_number, color, self.msg_data[start:end], extra))
-                self.line_number += 1
-
-
-class Link(NetlinkPacket):
-    """
-    Service Header
-
-     0                   1                   2                   3
-     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |   Family    |   Reserved  |          Device Type              |
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |                     Interface Index                           |
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |                      Device Flags                             |
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |                      Change Mask                              |
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    """
-
-    # Link attributes
-    # /usr/include/linux/if_link.h
-    IFLA_UNSPEC          = 0
-    IFLA_ADDRESS         = 1
-    IFLA_BROADCAST       = 2
-    IFLA_IFNAME          = 3
-    IFLA_MTU             = 4
-    IFLA_LINK            = 5
-    IFLA_QDISC           = 6
-    IFLA_STATS           = 7
-    IFLA_COST            = 8
-    IFLA_PRIORITY        = 9
-    IFLA_MASTER          = 10
-    IFLA_WIRELESS        = 11
-    IFLA_PROTINFO        = 12
-    IFLA_TXQLEN          = 13
-    IFLA_MAP             = 14
-    IFLA_WEIGHT          = 15
-    IFLA_OPERSTATE       = 16
-    IFLA_LINKMODE        = 17
-    IFLA_LINKINFO        = 18
-    IFLA_NET_NS_PID      = 19
-    IFLA_IFALIAS         = 20
-    IFLA_NUM_VF          = 21
-    IFLA_VFINFO_LIST     = 22
-    IFLA_STATS64         = 23
-    IFLA_VF_PORTS        = 24
-    IFLA_PORT_SELF       = 25
-    IFLA_AF_SPEC         = 26
-    IFLA_GROUP           = 27
-    IFLA_NET_NS_FD       = 28
-    IFLA_EXT_MASK        = 29
-    IFLA_PROMISCUITY     = 30
-    IFLA_NUM_TX_QUEUES   = 31
-    IFLA_NUM_RX_QUEUES   = 32
-    IFLA_CARRIER         = 33
-    IFLA_PHYS_PORT_ID    = 34
-    IFLA_CARRIER_CHANGES = 35
-    IFLA_PHYS_SWITCH_ID  = 36
-    IFLA_LINK_NETNSID    = 37
-    IFLA_PHYS_PORT_NAME  = 38
-    IFLA_PROTO_DOWN      = 39
-    IFLA_LINKPROTODOWN   = 200
-
-    attribute_to_class = {
-        IFLA_UNSPEC          : ('IFLA_UNSPEC', AttributeGeneric),
-        IFLA_ADDRESS         : ('IFLA_ADDRESS', AttributeMACAddress),
-        IFLA_BROADCAST       : ('IFLA_BROADCAST', AttributeMACAddress),
-        IFLA_IFNAME          : ('IFLA_IFNAME', AttributeStringInterfaceName),
-        IFLA_MTU             : ('IFLA_MTU', AttributeFourByteValue),
-        IFLA_LINK            : ('IFLA_LINK', AttributeFourByteValue),
-        IFLA_QDISC           : ('IFLA_QDISC', AttributeString),
-        IFLA_STATS           : ('IFLA_STATS', AttributeGeneric),
-        IFLA_COST            : ('IFLA_COST', AttributeGeneric),
-        IFLA_PRIORITY        : ('IFLA_PRIORITY', AttributeGeneric),
-        IFLA_MASTER          : ('IFLA_MASTER', AttributeFourByteValue),
-        IFLA_WIRELESS        : ('IFLA_WIRELESS', AttributeGeneric),
-        IFLA_PROTINFO        : ('IFLA_PROTINFO', AttributeGeneric), # Create an AttributeProtinfo class for this
-        IFLA_TXQLEN          : ('IFLA_TXQLEN', AttributeFourByteValue),
-        IFLA_MAP             : ('IFLA_MAP', AttributeGeneric),
-        IFLA_WEIGHT          : ('IFLA_WEIGHT', AttributeGeneric),
-        IFLA_OPERSTATE       : ('IFLA_OPERSTATE', AttributeFourByteValue),
-        IFLA_LINKMODE        : ('IFLA_LINKMODE', AttributeFourByteValue),
-        IFLA_LINKINFO        : ('IFLA_LINKINFO', AttributeIFLA_LINKINFO),
-        IFLA_NET_NS_PID      : ('IFLA_NET_NS_PID', AttributeGeneric),
-        IFLA_IFALIAS         : ('IFLA_IFALIAS', AttributeGeneric),
-        IFLA_NUM_VF          : ('IFLA_NUM_VF', AttributeGeneric),
-        IFLA_VFINFO_LIST     : ('IFLA_VFINFO_LIST', AttributeGeneric),
-        IFLA_STATS64         : ('IFLA_STATS64', AttributeGeneric),
-        IFLA_VF_PORTS        : ('IFLA_VF_PORTS', AttributeGeneric),
-        IFLA_PORT_SELF       : ('IFLA_PORT_SELF', AttributeGeneric),
-        IFLA_AF_SPEC         : ('IFLA_AF_SPEC', AttributeIFLA_AF_SPEC),
-        IFLA_GROUP           : ('IFLA_GROUP', AttributeFourByteValue),
-        IFLA_NET_NS_FD       : ('IFLA_NET_NS_FD', AttributeGeneric),
-        IFLA_EXT_MASK        : ('IFLA_EXT_MASK', AttributeFourByteValue),
-        IFLA_PROMISCUITY     : ('IFLA_PROMISCUITY', AttributeGeneric),
-        IFLA_NUM_TX_QUEUES   : ('IFLA_NUM_TX_QUEUES', AttributeGeneric),
-        IFLA_NUM_RX_QUEUES   : ('IFLA_NUM_RX_QUEUES', AttributeGeneric),
-        IFLA_CARRIER         : ('IFLA_CARRIER', AttributeGeneric),
-        IFLA_PHYS_PORT_ID    : ('IFLA_PHYS_PORT_ID', AttributeGeneric),
-        IFLA_CARRIER_CHANGES : ('IFLA_CARRIER_CHANGES', AttributeGeneric),
-        IFLA_PHYS_SWITCH_ID  : ('IFLA_PHYS_SWITCH_ID', AttributeGeneric),
-        IFLA_LINK_NETNSID    : ('IFLA_LINK_NETNSID', AttributeGeneric),
-        IFLA_PHYS_PORT_NAME  : ('IFLA_PHYS_PORT_NAME', AttributeGeneric),
-        IFLA_PROTO_DOWN      : ('IFLA_PROTO_DOWN', AttributeOneByteValue),
-        IFLA_LINKPROTODOWN   : ('IFLA_LINKPROTODOWN', AttributeGeneric)
-    }
-
-    # Link flags
-    # /usr/include/linux/if.h
-    IFF_UP          = 0x0001     # Interface is administratively up.
-    IFF_BROADCAST   = 0x0002     # Valid broadcast address set.
-    IFF_DEBUG       = 0x0004     # Internal debugging flag.
-    IFF_LOOPBACK    = 0x0008     # Interface is a loopback interface.
-    IFF_POINTOPOINT = 0x0010     # Interface is a point-to-point link.
-    IFF_NOTRAILERS  = 0x0020     # Avoid use of trailers.
-    IFF_RUNNING     = 0x0040     # Interface is operationally up.
-    IFF_NOARP       = 0x0080     # No ARP protocol needed for this interface.
-    IFF_PROMISC     = 0x0100     # Interface is in promiscuous mode.
-    IFF_ALLMULTI    = 0x0200     # Receive all multicast packets.
-    IFF_MASTER      = 0x0400     # Master of a load balancing bundle.
-    IFF_SLAVE       = 0x0800     # Slave of a load balancing bundle.
-    IFF_MULTICAST   = 0x1000     # Supports multicast.
-    IFF_PORTSEL     = 0x2000     # Is able to select media type via ifmap.
-    IFF_AUTOMEDIA   = 0x4000     # Auto media selection active.
-    IFF_DYNAMIC     = 0x8000     # Interface was dynamically created.
-    IFF_LOWER_UP    = 0x10000    # driver signals L1 up
-    IFF_DORMANT     = 0x20000    # driver signals dormant
-    IFF_ECHO        = 0x40000    # echo sent packet
-    IFF_PROTO_DOWN  = 0x1000000  # protocol is down on the interface
-
-    flag_to_string = {
-        IFF_UP          : 'IFF_UP',
-        IFF_BROADCAST   : 'IFF_BROADCAST',
-        IFF_DEBUG       : 'IFF_DEBUG',
-        IFF_LOOPBACK    : 'IFF_LOOPBACK',
-        IFF_POINTOPOINT : 'IFF_POINTOPOINT',
-        IFF_NOTRAILERS  : 'IFF_NOTRAILERS',
-        IFF_RUNNING     : 'IFF_RUNNING',
-        IFF_NOARP       : 'IFF_NOARP',
-        IFF_PROMISC     : 'IFF_PROMISC',
-        IFF_ALLMULTI    : 'IFF_ALLMULTI',
-        IFF_MASTER      : 'IFF_MASTER',
-        IFF_SLAVE       : 'IFF_SLAVE',
-        IFF_MULTICAST   : 'IFF_MULTICAST',
-        IFF_PORTSEL     : 'IFF_PORTSEL',
-        IFF_AUTOMEDIA   : 'IFF_AUTOMEDIA',
-        IFF_DYNAMIC     : 'IFF_DYNAMIC',
-        IFF_LOWER_UP    : 'IFF_LOWER_UP',
-        IFF_DORMANT     : 'IFF_DORMANT',
-        IFF_ECHO        : 'IFF_ECHO',
-        IFF_PROTO_DOWN  : 'IFF_PROTO_DOWN'
-    }
-
-    # RFC 2863 operational status
-    IF_OPER_UNKNOWN        = 0
-    IF_OPER_NOTPRESENT     = 1
-    IF_OPER_DOWN           = 2
-    IF_OPER_LOWERLAYERDOWN = 3
-    IF_OPER_TESTING        = 4
-    IF_OPER_DORMANT        = 5
-    IF_OPER_UP             = 6
-
-    oper_to_string = {
-        IF_OPER_UNKNOWN        : 'IF_OPER_UNKNOWN',
-        IF_OPER_NOTPRESENT     : 'IF_OPER_NOTPRESENT',
-        IF_OPER_DOWN           : 'IF_OPER_DOWN',
-        IF_OPER_LOWERLAYERDOWN : 'IF_OPER_LOWERLAYERDOWN',
-        IF_OPER_TESTING        : 'IF_OPER_TESTING',
-        IF_OPER_DORMANT        : 'IF_OPER_DORMANT',
-        IF_OPER_UP             : 'IF_OPER_UP'
-    }
-
-    # Link types
-    # /usr/include/linux/if_arp.h
-    # ARP protocol HARDWARE identifiers
-    ARPHRD_NETROM             = 0      # from KA9Q: NET/ROM pseudo
-    ARPHRD_ETHER              = 1      # Ethernet 10Mbps
-    ARPHRD_EETHER             = 2      # Experimental Ethernet
-    ARPHRD_AX25               = 3      # AX.25 Level 2
-    ARPHRD_PRONET             = 4      # PROnet token ring
-    ARPHRD_CHAOS              = 5      # Chaosnet
-    ARPHRD_IEEE802            = 6      # IEEE 802.2 Ethernet/TR/TB
-    ARPHRD_ARCNET             = 7      # ARCnet
-    ARPHRD_APPLETLK           = 8      # APPLEtalk
-    ARPHRD_DLCI               = 15     # Frame Relay DLCI
-    ARPHRD_ATM                = 19     # ATM
-    ARPHRD_METRICOM           = 23     # Metricom STRIP (new IANA id)
-    ARPHRD_IEEE1394           = 24     # IEEE 1394 IPv4 - RFC 2734
-    ARPHRD_EUI64              = 27     # EUI-64
-    ARPHRD_INFINIBAND         = 32     # InfiniBand
-    # Dummy types for non ARP hardware
-    ARPHRD_SLIP               = 256
-    ARPHRD_CSLIP              = 257
-    ARPHRD_SLIP6              = 258
-    ARPHRD_CSLIP6             = 259
-    ARPHRD_RSRVD              = 260    # Notional KISS type
-    ARPHRD_ADAPT              = 264
-    ARPHRD_ROSE               = 270
-    ARPHRD_X25                = 271    # CCITT X.25
-    ARPHRD_HWX25              = 272    # Boards with X.25 in firmware
-    ARPHRD_CAN                = 280    # Controller Area Network
-    ARPHRD_PPP                = 512
-    ARPHRD_CISCO              = 513    # Cisco HDLC
-    ARPHRD_HDLC               = ARPHRD_CISCO
-    ARPHRD_LAPB               = 516    # LAPB
-    ARPHRD_DDCMP              = 517    # Digital's DDCMP protocol
-    ARPHRD_RAWHDLC            = 518    # Raw HDLC
-    ARPHRD_TUNNEL             = 768    # IPIP tunnel
-    ARPHRD_TUNNEL6            = 769    # IP6IP6 tunnel
-    ARPHRD_FRAD               = 770    # Frame Relay Access Device
-    ARPHRD_SKIP               = 771    # SKIP vif
-    ARPHRD_LOOPBACK           = 772    # Loopback device
-    ARPHRD_LOCALTLK           = 773    # Localtalk device
-    ARPHRD_FDDI               = 774    # Fiber Distributed Data Interface
-    ARPHRD_BIF                = 775    # AP1000 BIF
-    ARPHRD_SIT                = 776    # sit0 device - IPv6-in-IPv4
-    ARPHRD_IPDDP              = 777    # IP over DDP tunneller
-    ARPHRD_IPGRE              = 778    # GRE over IP
-    ARPHRD_PIMREG             = 779    # PIMSM register interface
-    ARPHRD_HIPPI              = 780    # High Performance Parallel Interface
-    ARPHRD_ASH                = 781    # Nexus 64Mbps Ash
-    ARPHRD_ECONET             = 782    # Acorn Econet
-    ARPHRD_IRDA               = 783    # Linux-IrDA
-    ARPHRD_FCPP               = 784    # Point to point fibrechannel
-    ARPHRD_FCAL               = 785    # Fibrechannel arbitrated loop
-    ARPHRD_FCPL               = 786    # Fibrechannel public loop
-    ARPHRD_FCFABRIC           = 787    # Fibrechannel fabric
-    # 787->799 reserved for fibrechannel media types
-    ARPHRD_IEEE802_TR         = 800    # Magic type ident for TR
-    ARPHRD_IEEE80211          = 801    # IEEE 802.11
-    ARPHRD_IEEE80211_PRISM    = 802    # IEEE 802.11 + Prism2 header
-    ARPHRD_IEEE80211_RADIOTAP = 803    # IEEE 802.11 + radiotap header
-    ARPHRD_IEEE802154         = 804
-    ARPHRD_PHONET             = 820    # PhoNet media type
-    ARPHRD_PHONET_PIPE        = 821    # PhoNet pipe header
-    ARPHRD_CAIF               = 822    # CAIF media type
-    ARPHRD_VOID               = 0xFFFF  # Void type, nothing is known
-    ARPHRD_NONE               = 0xFFFE  # zero header length
-
-    link_type_to_string = {
-        ARPHRD_NETROM             : 'ARPHRD_NETROM',
-        ARPHRD_ETHER              : 'ARPHRD_ETHER',
-        ARPHRD_EETHER             : 'ARPHRD_EETHER',
-        ARPHRD_AX25               : 'ARPHRD_AX25',
-        ARPHRD_PRONET             : 'ARPHRD_PRONET',
-        ARPHRD_CHAOS              : 'ARPHRD_CHAOS',
-        ARPHRD_IEEE802            : 'ARPHRD_IEEE802',
-        ARPHRD_ARCNET             : 'ARPHRD_ARCNET',
-        ARPHRD_APPLETLK           : 'ARPHRD_APPLETLK',
-        ARPHRD_DLCI               : 'ARPHRD_DLCI',
-        ARPHRD_ATM                : 'ARPHRD_ATM',
-        ARPHRD_METRICOM           : 'ARPHRD_METRICOM',
-        ARPHRD_IEEE1394           : 'ARPHRD_IEEE1394',
-        ARPHRD_EUI64              : 'ARPHRD_EUI64',
-        ARPHRD_INFINIBAND         : 'ARPHRD_INFINIBAND',
-        ARPHRD_SLIP               : 'ARPHRD_SLIP',
-        ARPHRD_CSLIP              : 'ARPHRD_CSLIP',
-        ARPHRD_SLIP6              : 'ARPHRD_SLIP6',
-        ARPHRD_CSLIP6             : 'ARPHRD_CSLIP6',
-        ARPHRD_RSRVD              : 'ARPHRD_RSRVD',
-        ARPHRD_ADAPT              : 'ARPHRD_ADAPT',
-        ARPHRD_ROSE               : 'ARPHRD_ROSE',
-        ARPHRD_X25                : 'ARPHRD_X25',
-        ARPHRD_HWX25              : 'ARPHRD_HWX25',
-        ARPHRD_CAN                : 'ARPHRD_CAN',
-        ARPHRD_PPP                : 'ARPHRD_PPP',
-        ARPHRD_CISCO              : 'ARPHRD_CISCO',
-        ARPHRD_HDLC               : 'ARPHRD_HDLC',
-        ARPHRD_LAPB               : 'ARPHRD_LAPB',
-        ARPHRD_DDCMP              : 'ARPHRD_DDCMP',
-        ARPHRD_RAWHDLC            : 'ARPHRD_RAWHDLC',
-        ARPHRD_TUNNEL             : 'ARPHRD_TUNNEL',
-        ARPHRD_TUNNEL6            : 'ARPHRD_TUNNEL6',
-        ARPHRD_FRAD               : 'ARPHRD_FRAD',
-        ARPHRD_SKIP               : 'ARPHRD_SKIP',
-        ARPHRD_LOOPBACK           : 'ARPHRD_LOOPBACK',
-        ARPHRD_LOCALTLK           : 'ARPHRD_LOCALTLK',
-        ARPHRD_FDDI               : 'ARPHRD_FDDI',
-        ARPHRD_BIF                : 'ARPHRD_BIF',
-        ARPHRD_SIT                : 'ARPHRD_SIT',
-        ARPHRD_IPDDP              : 'ARPHRD_IPDDP',
-        ARPHRD_IPGRE              : 'ARPHRD_IPGRE',
-        ARPHRD_PIMREG             : 'ARPHRD_PIMREG',
-        ARPHRD_HIPPI              : 'ARPHRD_HIPPI',
-        ARPHRD_ASH                : 'ARPHRD_ASH',
-        ARPHRD_ECONET             : 'ARPHRD_ECONET',
-        ARPHRD_IRDA               : 'ARPHRD_IRDA',
-        ARPHRD_FCPP               : 'ARPHRD_FCPP',
-        ARPHRD_FCAL               : 'ARPHRD_FCAL',
-        ARPHRD_FCPL               : 'ARPHRD_FCPL',
-        ARPHRD_FCFABRIC           : 'ARPHRD_FCFABRIC',
-        ARPHRD_IEEE802_TR         : 'ARPHRD_IEEE802_TR',
-        ARPHRD_IEEE80211          : 'ARPHRD_IEEE80211',
-        ARPHRD_IEEE80211_PRISM    : 'ARPHRD_IEEE80211_PRISM',
-        ARPHRD_IEEE80211_RADIOTAP : 'ARPHRD_IEEE80211_RADIOTAP',
-        ARPHRD_IEEE802154         : 'ARPHRD_IEEE802154',
-        ARPHRD_PHONET             : 'ARPHRD_PHONET',
-        ARPHRD_PHONET_PIPE        : 'ARPHRD_PHONET_PIPE',
-        ARPHRD_CAIF               : 'ARPHRD_CAIF',
-        ARPHRD_VOID               : 'ARPHRD_VOID',
-        ARPHRD_NONE               : 'ARPHRD_NONE'
-    }
-
-    # =========================================
-    # IFLA_LINKINFO attributes
-    # =========================================
-    IFLA_INFO_UNSPEC     = 0
-    IFLA_INFO_KIND       = 1
-    IFLA_INFO_DATA       = 2
-    IFLA_INFO_XSTATS     = 3
-    IFLA_INFO_SLAVE_KIND = 4
-    IFLA_INFO_SLAVE_DATA = 5
-    IFLA_INFO_MAX        = 6
-
-    ifla_info_to_string = {
-        IFLA_INFO_UNSPEC     : 'IFLA_INFO_UNSPEC',
-        IFLA_INFO_KIND       : 'IFLA_INFO_KIND',
-        IFLA_INFO_DATA       : 'IFLA_INFO_DATA',
-        IFLA_INFO_XSTATS     : 'IFLA_INFO_XSTATS',
-        IFLA_INFO_SLAVE_KIND : 'IFLA_INFO_SLAVE_KIND',
-        IFLA_INFO_SLAVE_DATA : 'IFLA_INFO_SLAVE_DATA',
-        IFLA_INFO_MAX        : 'IFLA_INFO_MAX'
-    }
-
-    # =========================================
-    # IFLA_INFO_DATA attributes for vlan
-    # =========================================
-    IFLA_VLAN_UNSPEC      = 0
-    IFLA_VLAN_ID          = 1
-    IFLA_VLAN_FLAGS       = 2
-    IFLA_VLAN_EGRESS_QOS  = 3
-    IFLA_VLAN_INGRESS_QOS = 4
-    IFLA_VLAN_PROTOCOL    = 5
-
-    ifla_vlan_to_string = {
-        IFLA_VLAN_UNSPEC      : 'IFLA_VLAN_UNSPEC',
-        IFLA_VLAN_ID          : 'IFLA_VLAN_ID',
-        IFLA_VLAN_FLAGS       : 'IFLA_VLAN_FLAGS',
-        IFLA_VLAN_EGRESS_QOS  : 'IFLA_VLAN_EGRESS_QOS',
-        IFLA_VLAN_INGRESS_QOS : 'IFLA_VLAN_INGRESS_QOS',
-        IFLA_VLAN_PROTOCOL    : 'IFLA_VLAN_PROTOCOL'
-    }
-
-    # =========================================
-    # IFLA_INFO_DATA attributes for macvlan
-    # =========================================
-    IFLA_MACVLAN_UNSPEC = 0
-    IFLA_MACVLAN_MODE   = 1
-
-    ifla_macvlan_to_string = {
-        IFLA_MACVLAN_UNSPEC : 'IFLA_MACVLAN_UNSPEC',
-        IFLA_MACVLAN_MODE   : 'IFLA_MACVLAN_MODE'
-    }
-
-    # macvlan modes
-    MACVLAN_MODE_PRIVATE  = 1
-    MACVLAN_MODE_VEPA     = 2
-    MACVLAN_MODE_BRIDGE   = 3
-    MACVLAN_MODE_PASSTHRU = 4
-
-    macvlan_mode_to_string = {
-        MACVLAN_MODE_PRIVATE  : 'MACVLAN_MODE_PRIVATE',
-        MACVLAN_MODE_VEPA     : 'MACVLAN_MODE_VEPA',
-        MACVLAN_MODE_BRIDGE   : 'MACVLAN_MODE_BRIDGE',
-        MACVLAN_MODE_PASSTHRU : 'MACVLAN_MODE_PASSTHRU'
-    }
-
-    # =========================================
-    # IFLA_INFO_DATA attributes for vxlan
-    # =========================================
-    IFLA_VXLAN_UNSPEC            = 0
-    IFLA_VXLAN_ID                = 1
-    IFLA_VXLAN_GROUP             = 2
-    IFLA_VXLAN_LINK              = 3
-    IFLA_VXLAN_LOCAL             = 4
-    IFLA_VXLAN_TTL               = 5
-    IFLA_VXLAN_TOS               = 6
-    IFLA_VXLAN_LEARNING          = 7
-    IFLA_VXLAN_AGEING            = 8
-    IFLA_VXLAN_LIMIT             = 9
-    IFLA_VXLAN_PORT_RANGE        = 10
-    IFLA_VXLAN_PROXY             = 11
-    IFLA_VXLAN_RSC               = 12
-    IFLA_VXLAN_L2MISS            = 13
-    IFLA_VXLAN_L3MISS            = 14
-    IFLA_VXLAN_PORT              = 15
-    IFLA_VXLAN_GROUP6            = 16
-    IFLA_VXLAN_LOCAL6            = 17
-    IFLA_VXLAN_UDP_CSUM          = 18
-    IFLA_VXLAN_UDP_ZERO_CSUM6_TX = 19
-    IFLA_VXLAN_UDP_ZERO_CSUM6_RX = 20
-    IFLA_VXLAN_REMCSUM_TX        = 21
-    IFLA_VXLAN_REMCSUM_RX        = 22
-    IFLA_VXLAN_GBP               = 23
-    IFLA_VXLAN_REMCSUM_NOPARTIAL = 24
-    IFLA_VXLAN_COLLECT_METADATA  = 25
-    IFLA_VXLAN_REPLICATION_NODE  = 253
-    IFLA_VXLAN_REPLICATION_TYPE  = 254
-
-    ifla_vxlan_to_string = {
-        IFLA_VXLAN_UNSPEC            : 'IFLA_VXLAN_UNSPEC',
-        IFLA_VXLAN_ID                : 'IFLA_VXLAN_ID',
-        IFLA_VXLAN_GROUP             : 'IFLA_VXLAN_GROUP',
-        IFLA_VXLAN_LINK              : 'IFLA_VXLAN_LINK',
-        IFLA_VXLAN_LOCAL             : 'IFLA_VXLAN_LOCAL',
-        IFLA_VXLAN_TTL               : 'IFLA_VXLAN_TTL',
-        IFLA_VXLAN_TOS               : 'IFLA_VXLAN_TOS',
-        IFLA_VXLAN_LEARNING          : 'IFLA_VXLAN_LEARNING',
-        IFLA_VXLAN_AGEING            : 'IFLA_VXLAN_AGEING',
-        IFLA_VXLAN_LIMIT             : 'IFLA_VXLAN_LIMIT',
-        IFLA_VXLAN_PORT_RANGE        : 'IFLA_VXLAN_PORT_RANGE',
-        IFLA_VXLAN_PROXY             : 'IFLA_VXLAN_PROXY',
-        IFLA_VXLAN_RSC               : 'IFLA_VXLAN_RSC',
-        IFLA_VXLAN_L2MISS            : 'IFLA_VXLAN_L2MISS',
-        IFLA_VXLAN_L3MISS            : 'IFLA_VXLAN_L3MISS',
-        IFLA_VXLAN_PORT              : 'IFLA_VXLAN_PORT',
-        IFLA_VXLAN_GROUP6            : 'IFLA_VXLAN_GROUP6',
-        IFLA_VXLAN_LOCAL6            : 'IFLA_VXLAN_LOCAL6',
-        IFLA_VXLAN_UDP_CSUM          : 'IFLA_VXLAN_UDP_CSUM',
-        IFLA_VXLAN_UDP_ZERO_CSUM6_TX : 'IFLA_VXLAN_UDP_ZERO_CSUM6_TX',
-        IFLA_VXLAN_UDP_ZERO_CSUM6_RX : 'IFLA_VXLAN_UDP_ZERO_CSUM6_RX',
-        IFLA_VXLAN_REMCSUM_TX        : 'IFLA_VXLAN_REMCSUM_TX',
-        IFLA_VXLAN_REMCSUM_RX        : 'IFLA_VXLAN_REMCSUM_RX',
-        IFLA_VXLAN_GBP               : 'IFLA_VXLAN_GBP',
-        IFLA_VXLAN_REMCSUM_NOPARTIAL : 'IFLA_VXLAN_REMCSUM_NOPARTIAL',
-        IFLA_VXLAN_COLLECT_METADATA  : 'IFLA_VXLAN_COLLECT_METADATA',
-        IFLA_VXLAN_REPLICATION_NODE  : 'IFLA_VXLAN_REPLICATION_NODE',
-        IFLA_VXLAN_REPLICATION_TYPE  : 'IFLA_VXLAN_REPLICATION_TYPE'
-    }
-
-    # =========================================
-    # IFLA_INFO_DATA attributes for bonds
-    # =========================================
-    IFLA_BOND_UNSPEC                    = 0
-    IFLA_BOND_MODE                      = 1
-    IFLA_BOND_ACTIVE_SLAVE              = 2
-    IFLA_BOND_MIIMON                    = 3
-    IFLA_BOND_UPDELAY                   = 4
-    IFLA_BOND_DOWNDELAY                 = 5
-    IFLA_BOND_USE_CARRIER               = 6
-    IFLA_BOND_ARP_INTERVAL              = 7
-    IFLA_BOND_ARP_IP_TARGET             = 8
-    IFLA_BOND_ARP_VALIDATE              = 9
-    IFLA_BOND_ARP_ALL_TARGETS           = 10
-    IFLA_BOND_PRIMARY                   = 11
-    IFLA_BOND_PRIMARY_RESELECT          = 12
-    IFLA_BOND_FAIL_OVER_MAC             = 13
-    IFLA_BOND_XMIT_HASH_POLICY          = 14
-    IFLA_BOND_RESEND_IGMP               = 15
-    IFLA_BOND_NUM_PEER_NOTIF            = 16
-    IFLA_BOND_ALL_SLAVES_ACTIVE         = 17
-    IFLA_BOND_MIN_LINKS                 = 18
-    IFLA_BOND_LP_INTERVAL               = 19
-    IFLA_BOND_PACKETS_PER_SLAVE         = 20
-    IFLA_BOND_AD_LACP_RATE              = 21
-    IFLA_BOND_AD_SELECT                 = 22
-    IFLA_BOND_AD_INFO                   = 23
-    IFLA_BOND_AD_ACTOR_SYS_PRIO         = 24
-    IFLA_BOND_AD_USER_PORT_KEY          = 25
-    IFLA_BOND_AD_ACTOR_SYSTEM           = 26
-    IFLA_BOND_CL_LACP_BYPASS_ALLOW      = 100
-    IFLA_BOND_CL_LACP_BYPASS_ACTIVE     = 101
-    IFLA_BOND_CL_LACP_BYPASS_PERIOD     = 102
-    IFLA_BOND_CL_CLAG_ENABLE            = 103
-    IFLA_BOND_CL_LACP_BYPASS_ALL_ACTIVE = 104
-
-    ifla_bond_to_string = {
-        IFLA_BOND_UNSPEC                    : 'IFLA_BOND_UNSPEC',
-        IFLA_BOND_MODE                      : 'IFLA_BOND_MODE',
-        IFLA_BOND_ACTIVE_SLAVE              : 'IFLA_BOND_ACTIVE_SLAVE',
-        IFLA_BOND_MIIMON                    : 'IFLA_BOND_MIIMON',
-        IFLA_BOND_UPDELAY                   : 'IFLA_BOND_UPDELAY',
-        IFLA_BOND_DOWNDELAY                 : 'IFLA_BOND_DOWNDELAY',
-        IFLA_BOND_USE_CARRIER               : 'IFLA_BOND_USE_CARRIER',
-        IFLA_BOND_ARP_INTERVAL              : 'IFLA_BOND_ARP_INTERVAL',
-        IFLA_BOND_ARP_IP_TARGET             : 'IFLA_BOND_ARP_IP_TARGET',
-        IFLA_BOND_ARP_VALIDATE              : 'IFLA_BOND_ARP_VALIDATE',
-        IFLA_BOND_ARP_ALL_TARGETS           : 'IFLA_BOND_ARP_ALL_TARGETS',
-        IFLA_BOND_PRIMARY                   : 'IFLA_BOND_PRIMARY',
-        IFLA_BOND_PRIMARY_RESELECT          : 'IFLA_BOND_PRIMARY_RESELECT',
-        IFLA_BOND_FAIL_OVER_MAC             : 'IFLA_BOND_FAIL_OVER_MAC',
-        IFLA_BOND_XMIT_HASH_POLICY          : 'IFLA_BOND_XMIT_HASH_POLICY',
-        IFLA_BOND_RESEND_IGMP               : 'IFLA_BOND_RESEND_IGMP',
-        IFLA_BOND_NUM_PEER_NOTIF            : 'IFLA_BOND_NUM_PEER_NOTIF',
-        IFLA_BOND_ALL_SLAVES_ACTIVE         : 'IFLA_BOND_ALL_SLAVES_ACTIVE',
-        IFLA_BOND_MIN_LINKS                 : 'IFLA_BOND_MIN_LINKS',
-        IFLA_BOND_LP_INTERVAL               : 'IFLA_BOND_LP_INTERVAL',
-        IFLA_BOND_PACKETS_PER_SLAVE         : 'IFLA_BOND_PACKETS_PER_SLAVE',
-        IFLA_BOND_AD_LACP_RATE              : 'IFLA_BOND_AD_LACP_RATE',
-        IFLA_BOND_AD_SELECT                 : 'IFLA_BOND_AD_SELECT',
-        IFLA_BOND_AD_INFO                   : 'IFLA_BOND_AD_INFO',
-        IFLA_BOND_AD_ACTOR_SYS_PRIO         : 'IFLA_BOND_AD_ACTOR_SYS_PRIO',
-        IFLA_BOND_AD_USER_PORT_KEY          : 'IFLA_BOND_AD_USER_PORT_KEY',
-        IFLA_BOND_AD_ACTOR_SYSTEM           : 'IFLA_BOND_AD_ACTOR_SYSTEM',
-        IFLA_BOND_CL_LACP_BYPASS_ALLOW      : 'IFLA_BOND_CL_LACP_BYPASS_ALLOW',
-        IFLA_BOND_CL_LACP_BYPASS_ACTIVE     : 'IFLA_BOND_CL_LACP_BYPASS_ACTIVE',
-        IFLA_BOND_CL_LACP_BYPASS_PERIOD     : 'IFLA_BOND_CL_LACP_BYPASS_PERIOD',
-        IFLA_BOND_CL_CLAG_ENABLE            : 'IFLA_BOND_CL_CLAG_ENABLE',
-        IFLA_BOND_CL_LACP_BYPASS_ALL_ACTIVE : 'IFLA_BOND_CL_LACP_BYPASS_ALL_ACTIVE'
-    }
-
-    # =========================================
-    # IFLA_INFO_DATA attributes for bridges
-    # =========================================
-    IFLA_BRPORT_UNSPEC              = 0
-    IFLA_BRPORT_STATE               = 1
-    IFLA_BRPORT_PRIORITY            = 2
-    IFLA_BRPORT_COST                = 3
-    IFLA_BRPORT_MODE                = 4
-    IFLA_BRPORT_GUARD               = 5
-    IFLA_BRPORT_PROTECT             = 6
-    IFLA_BRPORT_FAST_LEAVE          = 7
-    IFLA_BRPORT_LEARNING            = 8
-    IFLA_BRPORT_UNICAST_FLOOD       = 9
-    IFLA_BRPORT_PROXYARP            = 10
-    IFLA_BRPORT_LEARNING_SYNC       = 11
-    IFLA_BRPORT_PROXYARP_WIFI       = 12
-    IFLA_BRPORT_ROOT_ID             = 13
-    IFLA_BRPORT_BRIDGE_ID           = 14
-    IFLA_BRPORT_DESIGNATED_PORT     = 15
-    IFLA_BRPORT_DESIGNATED_COST     = 16
-    IFLA_BRPORT_ID                  = 17
-    IFLA_BRPORT_NO                  = 18
-    IFLA_BRPORT_TOPOLOGY_CHANGE_ACK = 19
-    IFLA_BRPORT_CONFIG_PENDING      = 20
-    IFLA_BRPORT_MESSAGE_AGE_TIMER   = 21
-    IFLA_BRPORT_FORWARD_DELAY_TIMER = 22
-    IFLA_BRPORT_HOLD_TIMER          = 23
-    IFLA_BRPORT_FLUSH               = 24
-    IFLA_BRPORT_MULTICAST_ROUTER    = 25
-    IFLA_BRPORT_PEER_LINK           = 150
-    IFLA_BRPORT_DUAL_LINK           = 151
-
-    ifla_bridge_to_string = {
-        IFLA_BRPORT_UNSPEC              : 'IFLA_BRPORT_UNSPEC',
-        IFLA_BRPORT_STATE               : 'IFLA_BRPORT_STATE',
-        IFLA_BRPORT_PRIORITY            : 'IFLA_BRPORT_PRIORITY',
-        IFLA_BRPORT_COST                : 'IFLA_BRPORT_COST',
-        IFLA_BRPORT_MODE                : 'IFLA_BRPORT_MODE',
-        IFLA_BRPORT_GUARD               : 'IFLA_BRPORT_GUARD',
-        IFLA_BRPORT_PROTECT             : 'IFLA_BRPORT_PROTECT',
-        IFLA_BRPORT_FAST_LEAVE          : 'IFLA_BRPORT_FAST_LEAVE',
-        IFLA_BRPORT_LEARNING            : 'IFLA_BRPORT_LEARNING',
-        IFLA_BRPORT_UNICAST_FLOOD       : 'IFLA_BRPORT_UNICAST_FLOOD',
-        IFLA_BRPORT_PROXYARP            : 'IFLA_BRPORT_PROXYARP',
-        IFLA_BRPORT_LEARNING_SYNC       : 'IFLA_BRPORT_LEARNING_SYNC',
-        IFLA_BRPORT_PROXYARP_WIFI       : 'IFLA_BRPORT_PROXYARP_WIFI',
-        IFLA_BRPORT_ROOT_ID             : 'IFLA_BRPORT_ROOT_ID',
-        IFLA_BRPORT_BRIDGE_ID           : 'IFLA_BRPORT_BRIDGE_ID',
-        IFLA_BRPORT_DESIGNATED_PORT     : 'IFLA_BRPORT_DESIGNATED_PORT',
-        IFLA_BRPORT_DESIGNATED_COST     : 'IFLA_BRPORT_DESIGNATED_COST',
-        IFLA_BRPORT_ID                  : 'IFLA_BRPORT_ID',
-        IFLA_BRPORT_NO                  : 'IFLA_BRPORT_NO',
-        IFLA_BRPORT_TOPOLOGY_CHANGE_ACK : 'IFLA_BRPORT_TOPOLOGY_CHANGE_ACK',
-        IFLA_BRPORT_CONFIG_PENDING      : 'IFLA_BRPORT_CONFIG_PENDING',
-        IFLA_BRPORT_MESSAGE_AGE_TIMER   : 'IFLA_BRPORT_MESSAGE_AGE_TIMER',
-        IFLA_BRPORT_FORWARD_DELAY_TIMER : 'IFLA_BRPORT_FORWARD_DELAY_TIMER',
-        IFLA_BRPORT_HOLD_TIMER          : 'IFLA_BRPORT_HOLD_TIMER',
-        IFLA_BRPORT_FLUSH               : 'IFLA_BRPORT_FLUSH',
-        IFLA_BRPORT_MULTICAST_ROUTER    : 'IFLA_BRPORT_MULTICAST_ROUTER',
-        IFLA_BRPORT_PEER_LINK           : 'IFLA_BRPORT_PEER_LINK',
-        IFLA_BRPORT_DUAL_LINK           : 'IFLA_BRPORT_DUAL_LINK'
-    }
-
-    # BRIDGE IFLA_AF_SPEC attributes
-    IFLA_BRIDGE_FLAGS     = 0
-    IFLA_BRIDGE_MODE      = 1
-    IFLA_BRIDGE_VLAN_INFO = 2
-
-    ifla_bridge_af_spec_to_string = {
-        IFLA_BRIDGE_FLAGS     : 'IFLA_BRIDGE_FLAGS',
-        IFLA_BRIDGE_MODE      : 'IFLA_BRIDGE_MODE',
-        IFLA_BRIDGE_VLAN_INFO : 'IFLA_BRIDGE_VLAN_INFO'
-    }
-
-    # BRIDGE_VLAN_INFO flags
-    BRIDGE_VLAN_INFO_MASTER      = 1 << 0
-    BRIDGE_VLAN_INFO_PVID        = 1 << 1
-    BRIDGE_VLAN_INFO_UNTAGGED    = 1 << 2
-    BRIDGE_VLAN_INFO_RANGE_BEGIN = 1 << 3
-    BRIDGE_VLAN_INFO_RANGE_END   = 1 << 4
-
-    bridge_vlan_to_string = {
-        BRIDGE_VLAN_INFO_MASTER      : 'BRIDGE_VLAN_INFO_MASTER',
-        BRIDGE_VLAN_INFO_PVID        : 'BRIDGE_VLAN_INFO_PVID',
-        BRIDGE_VLAN_INFO_UNTAGGED    : 'BRIDGE_VLAN_INFO_UNTAGGED',
-        BRIDGE_VLAN_INFO_RANGE_BEGIN : 'BRIDGE_VLAN_INFO_RANGE_BEGIN',
-        BRIDGE_VLAN_INFO_RANGE_END   : 'BRIDGE_VLAN_INFO_RANGE_END'
-    }
-
-    # Bridge flags
-    BRIDGE_FLAGS_MASTER = 1
-    BRIDGE_FLAGS_SELF   = 2
-
-    bridge_flags_to_string = {
-        BRIDGE_FLAGS_MASTER : 'BRIDGE_FLAGS_MASTER',
-        BRIDGE_FLAGS_SELF   : 'BRIDGE_FLAGS_SELF'
-    }
-
-    # filters for IFLA_EXT_MASK
-    RTEXT_FILTER_VF                = 1 << 0
-    RTEXT_FILTER_BRVLAN            = 1 << 1
-    RTEXT_FILTER_BRVLAN_COMPRESSED = 1 << 2
-    RTEXT_FILTER_SKIP_STATS        = 1 << 3
-
-    rtext_to_string = {
-        RTEXT_FILTER_VF                : 'RTEXT_FILTER_VF',
-        RTEXT_FILTER_BRVLAN            : 'RTEXT_FILTER_BRVLAN',
-        RTEXT_FILTER_BRVLAN_COMPRESSED : 'RTEXT_FILTER_BRVLAN_COMPRESSED',
-        RTEXT_FILTER_SKIP_STATS        : 'RTEXT_FILTER_SKIP_STATS'
-    }
-
-    IFLA_BR_UNSPEC                     =  0
-    IFLA_BR_FORWARD_DELAY              =  1
-    IFLA_BR_HELLO_TIME                 =  2
-    IFLA_BR_MAX_AGE                    =  3
-    IFLA_BR_AGEING_TIME                =  4
-    IFLA_BR_STP_STATE                  =  5
-    IFLA_BR_PRIORITY                   =  6
-    IFLA_BR_VLAN_FILTERING             =  7
-    IFLA_BR_VLAN_PROTOCOL              =  8
-    IFLA_BR_GROUP_FWD_MASK             =  9
-    IFLA_BR_ROOT_ID                    = 10
-    IFLA_BR_BRIDGE_ID                  = 11
-    IFLA_BR_ROOT_PORT                  = 12
-    IFLA_BR_ROOT_PATH_COST             = 13
-    IFLA_BR_TOPOLOGY_CHANGE            = 14
-    IFLA_BR_TOPOLOGY_CHANGE_DETECTED   = 15
-    IFLA_BR_HELLO_TIMER                = 16
-    IFLA_BR_TCN_TIMER                  = 17
-    IFLA_BR_TOPOLOGY_CHANGE_TIMER      = 18
-    IFLA_BR_GC_TIMER                   = 19
-    IFLA_BR_GROUP_ADDR                 = 20
-    IFLA_BR_FDB_FLUSH                  = 21
-    IFLA_BR_MCAST_ROUTER               = 22
-    IFLA_BR_MCAST_SNOOPING             = 23
-    IFLA_BR_MCAST_QUERY_USE_IFADDR     = 24
-    IFLA_BR_MCAST_QUERIER              = 25
-    IFLA_BR_MCAST_HASH_ELASTICITY      = 26
-    IFLA_BR_MCAST_HASH_MAX             = 27
-    IFLA_BR_MCAST_LAST_MEMBER_CNT      = 28
-    IFLA_BR_MCAST_STARTUP_QUERY_CNT    = 29
-    IFLA_BR_MCAST_LAST_MEMBER_INTVL    = 30
-    IFLA_BR_MCAST_MEMBERSHIP_INTVL     = 31
-    IFLA_BR_MCAST_QUERIER_INTVL        = 32
-    IFLA_BR_MCAST_QUERY_INTVL          = 33
-    IFLA_BR_MCAST_QUERY_RESPONSE_INTVL = 34
-    IFLA_BR_MCAST_STARTUP_QUERY_INTVL  = 35
-    IFLA_BR_NF_CALL_IPTABLES           = 36
-    IFLA_BR_NF_CALL_IP6TABLES          = 37
-    IFLA_BR_NF_CALL_ARPTABLES          = 38
-    IFLA_BR_VLAN_DEFAULT_PVID          = 39
-
-    ifla_br_to_string = {
-        IFLA_BR_UNSPEC                     : 'IFLA_BR_UNSPEC',
-        IFLA_BR_FORWARD_DELAY              : 'IFLA_BR_FORWARD_DELAY',
-        IFLA_BR_HELLO_TIME                 : 'IFLA_BR_HELLO_TIME',
-        IFLA_BR_MAX_AGE                    : 'IFLA_BR_MAX_AGE',
-        IFLA_BR_AGEING_TIME                : 'IFLA_BR_AGEING_TIME',
-        IFLA_BR_STP_STATE                  : 'IFLA_BR_STP_STATE',
-        IFLA_BR_PRIORITY                   : 'IFLA_BR_PRIORITY',
-        IFLA_BR_VLAN_FILTERING             : 'IFLA_BR_VLAN_FILTERING',
-        IFLA_BR_VLAN_PROTOCOL              : 'IFLA_BR_VLAN_PROTOCOL',
-        IFLA_BR_GROUP_FWD_MASK             : 'IFLA_BR_GROUP_FWD_MASK',
-        IFLA_BR_ROOT_ID                    : 'IFLA_BR_ROOT_ID',
-        IFLA_BR_BRIDGE_ID                  : 'IFLA_BR_BRIDGE_ID',
-        IFLA_BR_ROOT_PORT                  : 'IFLA_BR_ROOT_PORT',
-        IFLA_BR_ROOT_PATH_COST             : 'IFLA_BR_ROOT_PATH_COST',
-        IFLA_BR_TOPOLOGY_CHANGE            : 'IFLA_BR_TOPOLOGY_CHANGE',
-        IFLA_BR_TOPOLOGY_CHANGE_DETECTED   : 'IFLA_BR_TOPOLOGY_CHANGE_DETECTED',
-        IFLA_BR_HELLO_TIMER                : 'IFLA_BR_HELLO_TIMER',
-        IFLA_BR_TCN_TIMER                  : 'IFLA_BR_TCN_TIMER',
-        IFLA_BR_TOPOLOGY_CHANGE_TIMER      : 'IFLA_BR_TOPOLOGY_CHANGE_TIMER',
-        IFLA_BR_GC_TIMER                   : 'IFLA_BR_GC_TIMER',
-        IFLA_BR_GROUP_ADDR                 : 'IFLA_BR_GROUP_ADDR',
-        IFLA_BR_FDB_FLUSH                  : 'IFLA_BR_FDB_FLUSH',
-        IFLA_BR_MCAST_ROUTER               : 'IFLA_BR_MCAST_ROUTER',
-        IFLA_BR_MCAST_SNOOPING             : 'IFLA_BR_MCAST_SNOOPING',
-        IFLA_BR_MCAST_QUERY_USE_IFADDR     : 'IFLA_BR_MCAST_QUERY_USE_IFADDR',
-        IFLA_BR_MCAST_QUERIER              : 'IFLA_BR_MCAST_QUERIER',
-        IFLA_BR_MCAST_HASH_ELASTICITY      : 'IFLA_BR_MCAST_HASH_ELASTICITY',
-        IFLA_BR_MCAST_HASH_MAX             : 'IFLA_BR_MCAST_HASH_MAX',
-        IFLA_BR_MCAST_LAST_MEMBER_CNT      : 'IFLA_BR_MCAST_LAST_MEMBER_CNT',
-        IFLA_BR_MCAST_STARTUP_QUERY_CNT    : 'IFLA_BR_MCAST_STARTUP_QUERY_CNT',
-        IFLA_BR_MCAST_LAST_MEMBER_INTVL    : 'IFLA_BR_MCAST_LAST_MEMBER_INTVL',
-        IFLA_BR_MCAST_MEMBERSHIP_INTVL     : 'IFLA_BR_MCAST_MEMBERSHIP_INTVL',
-        IFLA_BR_MCAST_QUERIER_INTVL        : 'IFLA_BR_MCAST_QUERIER_INTVL',
-        IFLA_BR_MCAST_QUERY_INTVL          : 'IFLA_BR_MCAST_QUERY_INTVL',
-        IFLA_BR_MCAST_QUERY_RESPONSE_INTVL : 'IFLA_BR_MCAST_QUERY_RESPONSE_INTVL',
-        IFLA_BR_MCAST_STARTUP_QUERY_INTVL  : 'IFLA_BR_MCAST_STARTUP_QUERY_INTVL',
-        IFLA_BR_NF_CALL_IPTABLES           : 'IFLA_BR_NF_CALL_IPTABLES',
-        IFLA_BR_NF_CALL_IP6TABLES          : 'IFLA_BR_NF_CALL_IP6TABLES',
-        IFLA_BR_NF_CALL_ARPTABLES          : 'IFLA_BR_NF_CALL_ARPTABLES',
-        IFLA_BR_VLAN_DEFAULT_PVID          : 'IFLA_BR_VLAN_DEFAULT_PVID',
-    }
-
-    def __init__(self, msgtype, debug=False, logger=None, use_color=True):
-        NetlinkPacket.__init__(self, msgtype, debug, logger, use_color)
-        self.PACK = 'BxHiII'
-        self.LEN  = calcsize(self.PACK)
-
-    def get_link_type_string(self, index):
-        return self.get_string(self.link_type_to_string, index)
-
-    def get_ifla_bridge_af_spec_to_string(self, index):
-        return self.get_string(self.ifla_bridge_af_spec_to_string, index)
-
-    def get_ifla_info_string(self, index):
-        return self.get_string(self.ifla_info_to_string, index)
-
-    def get_ifla_vlan_string(self, index):
-        return self.get_string(self.ifla_vlan_to_string, index)
-
-    def get_ifla_vxlan_string(self, index):
-        return self.get_string(self.ifla_vxlan_to_string, index)
-
-    def get_ifla_macvlan_string(self, index):
-        return self.get_string(self.ifla_macvlan_to_string, index)
-
-    def get_macvlan_mode_string(self, index):
-        return self.get_string(self.macvlan_mode_to_string, index)
-
-    def get_ifla_bond_string(self, index):
-        return self.get_string(self.ifla_bond_to_string, index)
-
-    def get_ifla_bridge_string(self, index):
-        return self.get_string(self.ifla_bridge_to_string, index)
-
-    def get_ifla_br_string(self, index):
-        return self.get_string(self.ifla_br_to_string, index)
-
-    def get_bridge_vlan_string(self, index):
-        return self.get_string(self.bridge_vlan_to_string, index)
-
-    def get_bridge_flags_string(self, index):
-        return self.get_string(self.bridge_flags_to_string, index)
-
-    def decode_service_header(self):
-
-        # Nothing to do if the message did not contain a service header
-        if self.length == self.header_LEN:
-            return
-
-        (self.family, self.device_type,
-         self.ifindex,
-         self.flags,
-         self.change_mask) = \
-            unpack(self.PACK, self.msg_data[:self.LEN])
-
-        if self.debug:
-            color = yellow if self.use_color else None
-            color_start = "\033[%dm" % color if color else ""
-            color_end = "\033[0m" if color else ""
-            self.dump_buffer.append("  %sService Header%s" % (color_start, color_end))
-
-            for x in range(0, self.LEN/4):
-                if self.line_number == 5:
-                    extra = "Family %s (%d), Device Type %s (%d - %s)" % \
-                            (zfilled_hex(self.family, 2), self.family,
-                             zfilled_hex(self.device_type, 4), self.device_type, self.get_link_type_string(self.device_type))
-                elif self.line_number == 6:
-                    extra = "Interface Index %s (%d)" % (zfilled_hex(self.ifindex, 8), self.ifindex)
-                elif self.line_number == 7:
-                    extra = "Device Flags %s (%s)" % (zfilled_hex(self.flags, 8), self.get_flags_string())
-                elif self.line_number == 8:
-                    extra = "Change Mask %s" % zfilled_hex(self.change_mask, 8)
-                else:
-                    extra = "Unexpected line number %d" % self.line_number
-
-                start = x * 4
-                end = start + 4
-                self.dump_buffer.append(data_to_color_text(self.line_number, color, self.msg_data[start:end], extra))
-                self.line_number += 1
-
-    def is_up(self):
-        if self.flags & Link.IFF_UP:
-            return True
-        return False
-
-
-class Neighbor(NetlinkPacket):
-    """
-    Service Header
-
-    0                   1                   2                   3
-    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |   Family    |    Reserved1  |           Reserved2           |
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |                     Interface Index                         |
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |           State             |     Flags     |     Type      |
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    """
-
-    # Neighbor attributes
-    # /usr/include/linux/neighbour.h
-    NDA_UNSPEC       = 0x00  # Unknown type
-    NDA_DST          = 0x01  # A neighbour cache network. layer destination address
-    NDA_LLADDR       = 0x02  # A neighbor cache link layer address.
-    NDA_CACHEINFO    = 0x03  # Cache statistics
-    NDA_PROBES       = 0x04
-    NDA_VLAN         = 0x05
-    NDA_PORT         = 0x06
-    NDA_VNI          = 0x07
-    NDA_IFINDEX      = 0x08
-    NDA_MASTER       = 0x09
-    NDA_LINK_NETNSID = 0x0A
-
-    attribute_to_class = {
-        NDA_UNSPEC       : ('NDA_UNSPEC', AttributeGeneric),
-        NDA_DST          : ('NDA_DST', AttributeIPAddress),
-        NDA_LLADDR       : ('NDA_LLADDR', AttributeMACAddress),
-        NDA_CACHEINFO    : ('NDA_CACHEINFO', AttributeFourByteList),
-        NDA_PROBES       : ('NDA_PROBES', AttributeFourByteValue),
-        NDA_VLAN         : ('NDA_VLAN', AttributeTwoByteValue),
-        NDA_PORT         : ('NDA_PORT', AttributeGeneric),
-        NDA_VNI          : ('NDA_VNI', AttributeFourByteValue),
-        NDA_IFINDEX      : ('NDA_IFINDEX', AttributeFourByteValue),
-        NDA_MASTER       : ('NDA_MASTER', AttributeFourByteValue),
-        NDA_LINK_NETNSID : ('NDA_LINK_NETNSID', AttributeGeneric)
-    }
-
-    # Neighbor flags
-    # /usr/include/linux/neighbour.h
-    NTF_USE    = 0x01
-    NTF_SELF   = 0x02
-    NTF_MASTER = 0x04
-    NTF_PROXY  = 0x08  # A proxy ARP entry
-    NTF_ROUTER = 0x80  # An IPv6 router
-
-    flag_to_string = {
-        NTF_USE    : 'NTF_USE',
-        NTF_SELF   : 'NTF_SELF',
-        NTF_MASTER : 'NTF_MASTER',
-        NTF_PROXY  : 'NTF_PROXY',
-        NTF_ROUTER : 'NTF_ROUTER'
-    }
-
-    # Neighbor states
-    # /usr/include/linux/neighbour.h
-    NUD_NONE       = 0x00
-    NUD_INCOMPLETE = 0x01  # Still attempting to resolve
-    NUD_REACHABLE  = 0x02  # A confirmed working cache entry
-    NUD_STALE      = 0x04  # an expired cache entry
-    NUD_DELAY      = 0x08  # Neighbor no longer reachable.  Traffic sent, waiting for confirmatio.
-    NUD_PROBE      = 0x10  # A cache entry that is currently being re-solicited
-    NUD_FAILED     = 0x20  # An invalid cache entry
-    NUD_NOARP      = 0x40  # A device which does not do neighbor discovery(ARP)
-    NUD_PERMANENT  = 0x80  # A static entry
-
-    state_to_string = {
-        NUD_NONE       : 'NUD_NONE',
-        NUD_INCOMPLETE : 'NUD_INCOMPLETE',
-        NUD_REACHABLE  : 'NUD_REACHABLE',
-        NUD_STALE      : 'NUD_STALE',
-        NUD_DELAY      : 'NUD_DELAY',
-        NUD_PROBE      : 'NUD_PROBE',
-        NUD_FAILED     : 'NUD_FAILED',
-        NUD_NOARP      : 'NUD_NOARP',
-        NUD_PERMANENT  : 'NUD_PERMANENT'
-    }
-
-    def __init__(self, msgtype, debug=False, logger=None, use_color=True):
-        NetlinkPacket.__init__(self, msgtype, debug, logger, use_color)
-        self.PACK = 'BxxxiHBB'
-        self.LEN = calcsize(self.PACK)
-
-    def get_state_string(self, index):
-        return self.get_string(self.state_to_string, index)
-
-    def decode_service_header(self):
-
-        # Nothing to do if the message did not contain a service header
-        if self.length == self.header_LEN:
-            return
-
-        (self.family,
-         self.ifindex,
-         self.state, self.flags, self.neighbor_type) = \
-            unpack(self.PACK, self.msg_data[:self.LEN])
-
-        if self.debug:
-            color = yellow if self.use_color else None
-            color_start = "\033[%dm" % color if color else ""
-            color_end = "\033[0m" if color else ""
-            self.dump_buffer.append("  %sService Header%s" % (color_start, color_end))
-
-            for x in range(0, self.LEN/4):
-                if self.line_number == 5:
-                    extra = "Family %s (%d)" % (zfilled_hex(self.family, 2), self.family)
-                elif self.line_number == 6:
-                    extra = "Interface Index %s (%d)" % (zfilled_hex(self.ifindex, 8), self.ifindex)
-                elif self.line_number == 7:
-                    extra = "State %s (%d), Flags %s, Type %s (%d)" % \
-                        (zfilled_hex(self.state, 4), self.state,
-                         zfilled_hex(self.flags, 2),
-                         zfilled_hex(self.neighbor_type, 4), self.neighbor_type)
-                else:
-                    extra = "Unexpected line number %d" % self.line_number
-
-                start = x * 4
-                end = start + 4
-                self.dump_buffer.append(data_to_color_text(self.line_number, color, self.msg_data[start:end], extra))
-                self.line_number += 1
-
-
-class Route(NetlinkPacket):
-    """
-    Service Header
-
-    0                   1                   2                   3
-    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |   Family    |  Dest length  |   Src length  |     TOS       |
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |  Table ID   |   Protocol    |     Scope     |     Type      |
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |                          Flags                              |
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    """
-
-    # Route attributes
-    # /usr/include/linux/rtnetlink.h
-    RTA_UNSPEC    = 0x00  # Ignored.
-    RTA_DST       = 0x01  # Protocol address for route destination address.
-    RTA_SRC       = 0x02  # Protocol address for route source address.
-    RTA_IIF       = 0x03  # Input interface index.
-    RTA_OIF       = 0x04  # Output interface index.
-    RTA_GATEWAY   = 0x05  # Protocol address for the gateway of the route
-    RTA_PRIORITY  = 0x06  # Priority of broker.
-    RTA_PREFSRC   = 0x07  # Preferred source address in cases where more than one source address could be used.
-    RTA_METRICS   = 0x08  # Route metrics attributed to route and associated protocols(e.g., RTT, initial TCP window, etc.).
-    RTA_MULTIPATH = 0x09  # Multipath route next hop's attributes.
-    RTA_PROTOINFO = 0x0A  # Firewall based policy routing attribute.
-    RTA_FLOW      = 0x0B  # Route realm.
-    RTA_CACHEINFO = 0x0C  # Cached route information.
-    RTA_SESSION   = 0x0D
-    RTA_MP_ALGO   = 0x0E
-    RTA_TABLE     = 0x0F
-    RTA_MARK      = 0x10
-    RTA_MFC_STATS = 0x11
-    RTA_VIA       = 0x12
-    RTA_NEWDST    = 0x13
-    RTA_PREF      = 0x14
-    RTA_ENCAP_TYPE= 0x15
-    RTA_ENCAP     = 0x16
-
-    attribute_to_class = {
-        RTA_UNSPEC    : ('RTA_UNSPEC', AttributeGeneric),
-        RTA_DST       : ('RTA_DST', AttributeIPAddress),
-        RTA_SRC       : ('RTA_SRC', AttributeIPAddress),
-        RTA_IIF       : ('RTA_IIF', AttributeFourByteValue),
-        RTA_OIF       : ('RTA_OIF', AttributeFourByteValue),
-        RTA_GATEWAY   : ('RTA_GATEWAY', AttributeIPAddress),
-        RTA_PRIORITY  : ('RTA_PRIORITY', AttributeFourByteValue),
-        RTA_PREFSRC   : ('RTA_PREFSRC', AttributeIPAddress),
-        RTA_METRICS   : ('RTA_METRICS', AttributeGeneric),
-        RTA_MULTIPATH : ('RTA_MULTIPATH', AttributeRTA_MULTIPATH),
-        RTA_PROTOINFO : ('RTA_PROTOINFO', AttributeGeneric),
-        RTA_FLOW      : ('RTA_FLOW', AttributeGeneric),
-        RTA_CACHEINFO : ('RTA_CACHEINFO', AttributeGeneric),
-        RTA_SESSION   : ('RTA_SESSION', AttributeGeneric),
-        RTA_MP_ALGO   : ('RTA_MP_ALGO', AttributeGeneric),
-        RTA_TABLE     : ('RTA_TABLE', AttributeFourByteValue),
-        RTA_MARK      : ('RTA_MARK', AttributeGeneric),
-        RTA_MFC_STATS : ('RTA_MFC_STATS', AttributeGeneric),
-        RTA_VIA       : ('RTA_VIA', AttributeGeneric),
-        RTA_NEWDST    : ('RTA_NEWDST', AttributeGeneric),
-        RTA_PREF      : ('RTA_PREF', AttributeGeneric),
-        RTA_ENCAP_TYPE: ('RTA_ENCAP_TYPE', AttributeGeneric),
-        RTA_ENCAP     : ('RTA_ENCAP', AttributeGeneric)
-    }
-
-    # Route tables
-    # /usr/include/linux/rtnetlink.h
-    RT_TABLE_UNSPEC  = 0x00  # An unspecified routing table
-    RT_TABLE_COMPAT  = 0xFC
-    RT_TABLE_DEFAULT = 0xFD  # The default table
-    RT_TABLE_MAIN    = 0xFE  # The main table
-    RT_TABLE_LOCAL   = 0xFF  # The local table
-
-    table_to_string = {
-        RT_TABLE_UNSPEC  : 'RT_TABLE_UNSPEC',
-        RT_TABLE_COMPAT  : 'RT_TABLE_COMPAT',
-        RT_TABLE_DEFAULT : 'RT_TABLE_DEFAULT',
-        RT_TABLE_MAIN    : 'RT_TABLE_MAIN',
-        RT_TABLE_LOCAL   : 'RT_TABLE_LOCAL'
-    }
-
-    # Route scope
-    # /usr/include/linux/rtnetlink.h
-    RT_SCOPE_UNIVERSE = 0x00  # Global route
-    RT_SCOPE_SITE     = 0xC8  # Interior route in the local autonomous system
-    RT_SCOPE_LINK     = 0xFD  # Route on this link
-    RT_SCOPE_HOST     = 0xFE  # Route on the local host
-    RT_SCOPE_NOWHERE  = 0xFF  # Destination does not exist
-
-    scope_to_string = {
-        RT_SCOPE_UNIVERSE : 'RT_SCOPE_UNIVERSE',
-        RT_SCOPE_SITE     : 'RT_SCOPE_SITE',
-        RT_SCOPE_LINK     : 'RT_SCOPE_LINK',
-        RT_SCOPE_HOST     : 'RT_SCOPE_HOST',
-        RT_SCOPE_NOWHERE  : 'RT_SCOPE_NOWHERE'
-    }
-
-    # Routing stack
-    # /usr/include/linux/rtnetlink.h
-    RT_PROT_UNSPEC   = 0x00  # Identifies what/who added the route
-    RT_PROT_REDIRECT = 0x01  # By an ICMP redirect
-    RT_PROT_KERNEL   = 0x02  # By the kernel
-    RT_PROT_BOOT     = 0x03  # During bootup
-    RT_PROT_STATIC   = 0x04  # By the administrator
-    RT_PROT_GATED    = 0x08  # GateD
-    RT_PROT_RA       = 0x09  # RDISC/ND router advertissements
-    RT_PROT_MRT      = 0x0A  # Merit MRT
-    RT_PROT_ZEBRA    = 0x0B  # ZEBRA
-    RT_PROT_BIRD     = 0x0C  # BIRD
-    RT_PROT_DNROUTED = 0x0D  # DECnet routing daemon
-    RT_PROT_XORP     = 0x0E  # XORP
-    RT_PROT_NTK      = 0x0F  # Netsukuku
-    RT_PROT_DHCP     = 0x10  # DHCP client
-    RT_PROT_EXABGP   = 0x11  # Exa Networks ExaBGP
-
-    prot_to_string = {
-        RT_PROT_UNSPEC   : 'RT_PROT_UNSPEC',
-        RT_PROT_REDIRECT : 'RT_PROT_REDIRECT',
-        RT_PROT_KERNEL   : 'RT_PROT_KERNEL',
-        RT_PROT_BOOT     : 'RT_PROT_BOOT',
-        RT_PROT_STATIC   : 'RT_PROT_STATIC',
-        RT_PROT_GATED    : 'RT_PROT_GATED',
-        RT_PROT_RA       : 'RT_PROT_RA',
-        RT_PROT_MRT      : 'RT_PROT_MRT',
-        RT_PROT_ZEBRA    : 'RT_PROT_ZEBRA',
-        RT_PROT_BIRD     : 'RT_PROT_BIRD',
-        RT_PROT_DNROUTED : 'RT_PROT_DNROUTED',
-        RT_PROT_XORP     : 'RT_PROT_XORP',
-        RT_PROT_NTK      : 'RT_PROT_NTK',
-        RT_PROT_DHCP     : 'RT_PROT_DHCP',
-        RT_PROT_EXABGP   : 'RT_PROT_EXABGP'
-    }
-
-    # Route types
-    # /usr/include/linux/rtnetlink.h
-    RTN_UNSPEC      = 0x00  # Unknown broker.
-    RTN_UNICAST     = 0x01  # A gateway or direct broker.
-    RTN_LOCAL       = 0x02  # A local interface broker.
-    RTN_BROADCAST   = 0x03  # A local broadcast route(sent as a broadcast).
-    RTN_ANYCAST     = 0x04  # An anycast broker.
-    RTN_MULTICAST   = 0x05  # A multicast broker.
-    RTN_BLACKHOLE   = 0x06  # A silent packet dropping broker.
-    RTN_UNREACHABLE = 0x07  # An unreachable destination.  Packets dropped and
-                            # host unreachable ICMPs are sent to the originator.
-    RTN_PROHIBIT    = 0x08  # A packet rejection broker.  Packets are dropped and
-                            # communication prohibited ICMPs are sent to the originator.
-    RTN_THROW       = 0x09  # When used with policy routing, continue routing lookup
-                            # in another table.  Under normal routing, packets are
-                            # dropped and net unreachable ICMPs are sent to the originator.
-    RTN_NAT         = 0x0A  # A network address translation rule.
-    RTN_XRESOLVE    = 0x0B  # Refer to an external resolver(not implemented).
-
-    rt_type_to_string = {
-        RTN_UNSPEC      : 'RTN_UNSPEC',
-        RTN_UNICAST     : 'RTN_UNICAST',
-        RTN_LOCAL       : 'RTN_LOCAL',
-        RTN_BROADCAST   : 'RTN_BROADCAST',
-        RTN_ANYCAST     : 'RTN_ANYCAST',
-        RTN_MULTICAST   : 'RTN_MULTICAST',
-        RTN_BLACKHOLE   : 'RTN_BLACKHOLE',
-        RTN_UNREACHABLE : 'RTN_UNREACHABLE',
-        RTN_PROHIBIT    : 'RTN_PROHIBIT',
-        RTN_THROW       : 'RTN_THROW',
-        RTN_NAT         : 'RTN_NAT',
-        RTN_XRESOLVE    : 'RTN_XRESOLVE'
-    }
-
-    # Route flags
-    # /usr/include/linux/rtnetlink.h
-    RTM_F_NOTIFY   = 0x100  # If the route changes, notify the user
-    RTM_F_CLONED   = 0x200  # Route is cloned from another route
-    RTM_F_EQUALIZE = 0x400  # Allow randomization of next hop path in multi-path routing(currently not implemented)
-    RTM_F_PREFIX   = 0x800  # Prefix Address
-
-    flag_to_string = {
-        RTM_F_NOTIFY   : 'RTM_F_NOTIFY',
-        RTM_F_CLONED   : 'RTM_F_CLONED',
-        RTM_F_EQUALIZE : 'RTM_F_EQUALIZE',
-        RTM_F_PREFIX   : 'RTM_F_PREFIX'
-    }
-
-    def __init__(self, msgtype, debug=False, logger=None, use_color=True):
-        NetlinkPacket.__init__(self, msgtype, debug, logger, use_color)
-        self.PACK = '=8BI'  # or is it 8Bi ?
-        self.LEN = calcsize(self.PACK)
-
-    def get_prefix_string(self):
-        dst = self.get_attribute_value(self.RTA_DST)
-
-        if dst:
-            return "%s/%d" % (dst, self.src_len)
-        else:
-            if self.family == AF_INET:
-                return "0.0.0.0/0"
-            elif self.family == AF_INET6:
-                return "::/0"
-
-    def get_protocol_string(self, index=None):
-        if index is None:
-            index = self.protocol
-        return self.get_string(self.prot_to_string, index)
-
-    def get_rt_type_string(self, index=None):
-        if index is None:
-            index = self.route_type
-        return self.get_string(self.rt_type_to_string, index)
-
-    def get_scope_string(self, index=None):
-        if index is None:
-            index = self.scope
-        return self.get_string(self.scope_to_string, index)
-
-    def get_table_id_string(self, index=None):
-        if index is None:
-            index = self.table_id
-        return self.get_string(self.table_to_string, index)
-
-    def _get_ifname_from_index(self, ifindex, ifname_by_index):
-        if ifindex:
-            ifname = ifname_by_index.get(ifindex)
-
-            if ifname is None:
-                ifname = str(ifindex)
-        else:
-            ifname = None
-
-        return ifname
-
-    def get_nexthops(self, ifname_by_index={}):
-        nexthop = self.get_attribute_value(self.RTA_GATEWAY)
-        multipath = self.get_attribute_value(self.RTA_MULTIPATH)
-        nexthops = []
-
-        if nexthop:
-            rta_oif = self.get_attribute_value(self.RTA_OIF)
-            ifname = self._get_ifname_from_index(rta_oif, ifname_by_index)
-            nexthops.append((nexthop, ifname))
-
-        elif multipath:
-            for (nexthop, rtnh_ifindex, rtnh_flags, rtnh_hops) in multipath:
-                ifname = self._get_ifname_from_index(rtnh_ifindex, ifname_by_index)
-                nexthops.append((nexthop, ifname))
-
-        return nexthops
-
-    def get_nexthops_string(self, ifname_by_index={}):
-        output = []
-
-        for (nexthop, ifname) in self.get_nexthops(ifname_by_index):
-            output.append(" via %s on %s" % (nexthop, ifname))
-
-        return ",".join(output)
-
-    def decode_service_header(self):
-
-        # Nothing to do if the message did not contain a service header
-        if self.length == self.header_LEN:
-            return
-
-        (self.family, self.src_len, self.dst_len, self.tos,
-         self.table_id, self.protocol, self.scope, self.route_type,
-         self.flags) = \
-            unpack(self.PACK, self.msg_data[:self.LEN])
-
-        if self.debug:
-            color = yellow if self.use_color else None
-            color_start = "\033[%dm" % color if color else ""
-            color_end = "\033[0m" if color else ""
-            self.dump_buffer.append("  %sService Header%s" % (color_start, color_end))
-
-            for x in range(0, self.LEN/4):
-                if self.line_number == 5:
-                    extra = "Family %s (%d), Source Length %s (%d), Destination Length %s (%d), TOS %s (%d)" % \
-                            (zfilled_hex(self.family, 2), self.family,
-                             zfilled_hex(self.src_len, 2), self.src_len,
-                             zfilled_hex(self.dst_len, 2), self.dst_len,
-                             zfilled_hex(self.tos, 2), self.tos)
-                elif self.line_number == 6:
-                    extra = "Table ID %s (%d - %s), Protocol %s (%d - %s), Scope %s (%d - %s), Type %s (%d - %s)" % \
-                            (zfilled_hex(self.table_id, 2), self.table_id, self.get_table_id_string(),
-                             zfilled_hex(self.protocol, 2), self.protocol, self.get_protocol_string(),
-                             zfilled_hex(self.scope, 2), self.scope, self.get_scope_string(),
-                             zfilled_hex(self.route_type, 2), self.route_type, self.get_rt_type_string())
-                elif self.line_number == 7:
-                    extra = "Flags %s" % zfilled_hex(self.flags, 8)
-                else:
-                    extra = "Unexpected line number %d" % self.line_number
-
-                start = x * 4
-                end = start + 4
-                self.dump_buffer.append(data_to_color_text(self.line_number, color, self.msg_data[start:end], extra))
-                self.line_number += 1
diff --git a/packages/ifupdown2/ifupdown/ifupdownconfig.py b/packages/ifupdown2/ifupdown/ifupdownconfig.py
deleted file mode 100644 (file)
index b156b6a..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright 2015 Cumulus Networks, Inc. All rights reserved.
-#
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-#
-
-class ifupdownConfig():
-
-       def __init__(self):
-               self.conf = {}
-
-config = ifupdownConfig()
diff --git a/sbin/ifaddon b/sbin/ifaddon
deleted file mode 100755 (executable)
index 193331e..0000000
+++ /dev/null
@@ -1,351 +0,0 @@
-#!/usr/bin/python
-
-import sys
-import os
-import re
-import argparse
-from collections import OrderedDict
-
-lockfile="/run/network/.lock"
-modules_configfile='/var/lib/ifupdownaddons/addons.conf'
-modules_dir='/usr/share/ifupdownaddons'
-
-addon_config = OrderedDict([('pre-up', []),
-                            ('up', []),
-                            ('post-up', []),
-                            ('pre-down', []),
-                            ('down', []),
-                            ('post-down', [])])
-
-def read_modules_config():
-    with open(modules_configfile, 'r') as f:
-        lines = f.readlines()
-        for l in lines:
-            litems = l.rstrip(' \n').split(',')
-            operation = litems[0]
-            mname = litems[1]
-            addon_config[operation].append(mname)
-
-def man_rst_header():
-    print '=========================='
-    print 'ifupdown-addons-interfaces'
-    print '=========================='
-
-    print '---------------------------------------------------------'
-    print 'ifupdown2 addon modules interface configuration'
-    print '---------------------------------------------------------'
-
-    print ':Author: roopa@cumulusnetworks.com'
-    print ':Date:   2013-09-25'
-    print ':Copyright: Copyright 2013 Cumulus Networks, Inc.  All rights reserved.'
-    print ':Version: 0.1'
-    print ':Manual section: 5'
-    print '\n'
-
-def man_rst_body():
-
-    print 'DESCRIPTION'
-    print '==========='
-
-    print ('''    ifupdown2 addon modules add incremental functionality to
-    core ifupdown2 tool.
-           
-    All installed addon modules are executed on every interface
-    listed in the interfaces file. Addon modules are installed under
-    /usr/share/ifupdownaddons. To see the list of active addon
-    modules, see ifaddon(8).
-
-    Addon modules add new attributes to the interfaces(5) file.
-    Below is a list of attribute options provided by each module.
-    These can be listed under each iface section in the interfaces(5)
-    file.  ''')
-
-    print '\n'
-
-def get_addon_modinfo(modules_dir):
-    """ load python modules from modules_dir
-
-    Default modules_dir is /usr/share/ifupdownmodules
-
-    """
-    if not modules_dir in sys.path:
-        sys.path.append(modules_dir)
-    read_modules_config()
-    modinfo = {}
-    try:
-        for op, mlist in addon_config.items():
-            for mname in mlist:
-                if mname in modinfo.keys(): continue
-                mpath = modules_dir + '/' + mname + '.py'
-                if os.path.exists(mpath):
-                    try:
-                        m = __import__(mname)
-                        mclass = getattr(m, mname)
-                    except:
-                        pass
-                        continue
-                    minstance = mclass()
-                    if hasattr(minstance, 'get_modinfo'):
-                       modinfo[mname] = minstance.get_modinfo()
-    except: 
-        raise
-
-    return modinfo
-
-def print_long_string(indent, strarg):
-    slen = 70 - len(indent)
-    tmphelpstr = strarg
-    l = len(strarg)
-    while l > 0:
-        rem = slen if l >= slen else l
-        print('%s%s' %(indent, tmphelpstr[:rem]))
-        tmphelpstr = tmphelpstr[rem:].strip()
-        l -= rem
-
-def man_rst_examples():
-    print 'EXAMPLES'
-    print '========'
-    print '''    Listed below are addon modules and their supported attributes.
-    The attributes if applicable go under the iface section in the
-    interfaces(5) file.\n'''
-
-    indent = '    '
-    modinfo = get_addon_modinfo(modules_dir)
-    for m, mdict in modinfo.items():
-        aindent = indent + '  '
-        aindentplus = aindent + '  '
-        if not mdict:
-            continue
-        print_long_string(indent, '**%s**: %s' %(m, mdict.get('mhelp', '')))
-        attrdict = mdict.get('attrs')
-        if not attrdict:
-            continue
-        print '\n'
-        try:
-            for attrname, attrvaldict in attrdict.items():
-                if attrvaldict.get('compat', False):
-                    continue
-                print('%s**%s**\n' %(aindent, attrname))
-                print_long_string(aindentplus, '**help**: %s'
-                        %(attrvaldict.get('help', '')))
-                print '\n'
-                print('%s**required**: %s\n' %(aindentplus,
-                            attrvaldict.get('required', False)))
-                default = attrvaldict.get('default')
-                if default:
-                    print('%s**default**: %s\n' %(aindentplus, default))
-                validrange = attrvaldict.get('validrange')
-                if validrange:
-                    print('%svalidrange: %s\n'
-                          %(aindentplus, '-'.join(validrange)))
-                validvals = attrvaldict.get('validvals')
-                if validvals:
-                    print('%s**validvals**: %s\n'
-                              %(aindentplus, ','.join(validvals)))
-                examples = attrvaldict.get('example')
-                if not examples:
-                    continue
-                print '%s**example**:' %(aindentplus)
-                for e in examples:
-                    print '%s%s\n' %(aindentplus + indent, e)
-                print ''
-        except Exception, e:
-            print "Roopa: m = %s, str(e) = %s\n"  %(m, str(e))
-            pass
-        print ''
-
-def man_rst_see_also():
-    print 'SEE ALSO'
-    print '========'
-    print '''    interfaces(5),
-    ifup(8),
-    ip(8),
-    mstpctl(8),
-    brctl(8),
-    ethtool(8),
-    clagctl(8)'''
-
-def show_man_rst():
-    man_rst_header()
-    man_rst_body()
-    man_rst_examples()
-    man_rst_see_also()
-
-def show():
-    for operation, mlist in addon_config.items():
-        postion = 1
-        for m in mlist:
-            print '%d. %s' %(postion, m)
-            postion += 1
-
-def write_modules_config():
-    with open(modules_configfile, 'w') as f:
-        for op, mlist in addon_config.items():
-            [f.write('%s,%s\n' %(op, m)) for m in mlist]
-
-def process_add_cmd(args):
-    op = args.operation
-    module = args.module
-    position = args.position
-    if not op:
-        for k, vlist in addon_config.items():
-            if module not in vlist:
-                addon_config[k].append(module)
-            else:
-                print '%s: module %s already present' %(k, module)
-        return
-    if module in addon_config.get(op):
-        print 'module already present'
-        return
-    if position:
-       try:
-            addon_config[op].insert(position, module)
-       except Exception, e:
-           print ('error inserting module %s at postion %s (%s)'
-                    %(module, position, str(e)))
-           raise
-    else:
-       addon_config[op].append(module)
-
-
-def process_del_cmd(args):
-    op = args.operation
-    module = args.module
-
-    if op:
-        del addon_config[op]
-    else:
-       try:
-            [addon_config[op].remove(module) for op in addon_config.keys()]
-       except ValueError:
-           pass
-
-def process_move_cmd(args):
-    op = args.operation
-    module = args.module
-    pos = 0
-
-    try:
-        pos = int(args.position)
-        if pos < 0 or pos > len(addon_config.get(op)):
-            raise Exception('invalid value for position')
-    except:
-        raise
-
-    if addon_config[op].index(module) == pos:
-        print '%s module %s already at location %d' %(op, module, pos)
-        return
-
-    addon_config[op].remove(module)
-    addon_config[op].insert(pos, module)
-
-def print_mlist(mlist, indent):
-    for idx, val in enumerate(mlist):
-        print '%s%d. %s' %(indent, idx, val)
-
-def process_show_cmd(args):
-    indent = '   '
-    op = args.operation
-
-    if args.man:
-        show_man_rst()
-        return
-
-    if op:
-        mlist = addon_config[op]
-        print '%s:' %op
-        print_mlist(mlist, indent)
-    else:
-        for op, mlist in addon_config.items():
-            print '%s:' %op
-            print_mlist(mlist, indent)
-            print ''
-
-cmdhandlers = {'add' : process_add_cmd,
-               'del' : process_del_cmd,
-               'move' : process_move_cmd,
-               'show' : process_show_cmd}
-
-def update_subparser_add(subparser):
-    subparser.add_argument('module', metavar='MODULE', help='module name')
-    subparser.add_argument('operation', metavar='OPERATION',
-                           choices=['pre-up', 'up', 'post-up',
-                                    'pre-down', 'down', 'post-down'],
-                                    help='operations', nargs='?')
-    subparser.add_argument('position', metavar='POSITION', nargs='?',
-                           help='position')
-    subparser.set_defaults(func=process_add_cmd)
-
-def update_subparser_del(subparser):
-    subparser.add_argument('module', metavar='MODULE', help='module name')
-    subparser.add_argument('operation', metavar='OPERATION',
-                           choices=['pre-up', 'up', 'post-up',
-                                    'pre-down', 'down', 'post-down'],
-                                    help='operations', nargs='?')
-    subparser.add_argument('position', metavar='POSITION', nargs='?',
-                           help='position')
-    subparser.set_defaults(func=process_del_cmd)
-
-def update_subparser_move(subparser):
-    subparser.add_argument('module', metavar='MODULE', help='module name')
-    subparser.add_argument('operation', metavar='OPERATION',
-                           choices=['pre-up', 'up', 'post-up',
-                                    'pre-down', 'down', 'post-down'],
-                                    help='operations')
-    subparser.add_argument('position', metavar='POSITION',
-                           help='position')
-    subparser.set_defaults(func=process_move_cmd)
-
-
-def update_subparser_show(subparser):
-    subparser.add_argument('--man', action='store_true',
-                           help=argparse.SUPPRESS)
-    subparser.add_argument('operation', metavar='OPERATION',
-                           choices=addon_config.keys(),
-                           help='operations %s' %str(addon_config.keys()),
-                           nargs='?')
-    subparser.set_defaults(func=process_show_cmd)
-
-def update_argparser(argparser):
-    subparsers = argparser.add_subparsers(help='sub-command help')
-
-    parser_add = subparsers.add_parser('add')
-    update_subparser_add(parser_add)
-
-    parser_del = subparsers.add_parser('del', help='del help')
-    update_subparser_del(parser_del)
-
-    parser_move = subparsers.add_parser('move', help='move help')
-    update_subparser_move(parser_move)
-
-    parser_show = subparsers.add_parser('show', help='show help')
-    update_subparser_show(parser_show)
-
-def parse_args(argsv):
-    descr = 'ifupdown addon modules management command.\n \
-            This command helps add/del/display/reorder modules \n \
-            in all ifupdown module categories'
-
-    argparser = argparse.ArgumentParser(description=descr)
-    update_argparser(argparser)
-
-    args = argparser.parse_args(argsv)
-    return args
-
-def main(argv):
-    """ main function """
-    try:
-        # Command line arg parser
-        args = parse_args(argv[1:])
-        read_modules_config()
-        args.func(args)
-        write_modules_config()
-    except Exception, e:
-        print 'error processing command (%s)' %str(e)
-
-if __name__ == "__main__":
-    if not os.geteuid() == 0:
-        print 'Error: Must be root to run this command'
-        exit(1)
-
-    main(sys.argv)
diff --git a/sbin/ifupdown b/sbin/ifupdown
deleted file mode 100755 (executable)
index d6bdeb2..0000000
+++ /dev/null
@@ -1,496 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-# ifupdown --
-#    tool to configure network interfaces
-#
-import sys
-import os
-import argcomplete
-import argparse
-import ConfigParser
-import StringIO
-import logging
-import logging.handlers
-import resource
-from ifupdown.ifupdownmain import *
-from ifupdown.utils import *
-
-lockfile="/run/network/.lock"
-configfile="/etc/network/ifupdown2/ifupdown2.conf"
-configmap_g=None
-logger = None
-interfacesfileiobuf=None
-ENVPATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
-
-def run_up(args):
-    logger.debug('args = %s' %str(args))
-
-    try:
-        iflist = args.iflist
-        if len(args.iflist) == 0:
-            iflist = None
-        logger.debug('creating ifupdown object ..')
-        cachearg=(False if (iflist or args.nocache or
-                            args.perfmode or args.noact)
-                            else True)
-        ifupdown_handle = ifupdownMain(config=configmap_g,
-                                       force=args.force,
-                                       withdepends=args.withdepends,
-                                       perfmode=args.perfmode,
-                                       dryrun=args.noact,
-                                       cache=cachearg,
-                                       addons_enable=not args.noaddons,
-                                       statemanager_enable=not args.noaddons,
-                                       interfacesfile=args.interfacesfile,
-                                       interfacesfileiobuf=interfacesfileiobuf,
-                                       interfacesfileformat=args.interfacesfileformat)
-        if args.noaddons:
-            ifupdown_handle.up(['up'], args.all, args.CLASS, iflist,
-                               excludepats=args.excludepats,
-                               printdependency=args.printdependency,
-                               syntaxcheck=args.syntaxcheck, type=args.type,
-                               skipupperifaces=args.skipupperifaces)
-        else:
-            ifupdown_handle.up(['pre-up', 'up', 'post-up'],
-                               args.all, args.CLASS, iflist,
-                               excludepats=args.excludepats,
-                               printdependency=args.printdependency,
-                               syntaxcheck=args.syntaxcheck, type=args.type,
-                               skipupperifaces=args.skipupperifaces)
-    except:
-        raise
-
-def run_down(args):
-    logger.debug('args = %s' %str(args))
-
-    try:
-        iflist = args.iflist
-        logger.debug('creating ifupdown object ..')
-        ifupdown_handle = ifupdownMain(config=configmap_g, force=args.force,
-                                       withdepends=args.withdepends,
-                                       perfmode=args.perfmode,
-                                       dryrun=args.noact,
-                                       addons_enable=not args.noaddons,
-                                       statemanager_enable=not args.noaddons,
-                                       interfacesfile=args.interfacesfile,
-                                       interfacesfileiobuf=interfacesfileiobuf,
-                                       interfacesfileformat=args.interfacesfileformat)
-
-        ifupdown_handle.down(['pre-down', 'down', 'post-down'],
-                             args.all, args.CLASS, iflist,
-                             excludepats=args.excludepats,
-                             printdependency=args.printdependency,
-                             usecurrentconfig=args.usecurrentconfig,
-                             type=args.type)
-    except:
-        raise
-
-def run_query(args):
-    logger.debug('args = %s' %str(args))
-
-    try:
-        iflist = args.iflist
-        if args.checkcurr:
-            qop='query-checkcurr'
-        elif args.running:
-            qop='query-running'
-        elif args.raw:
-            qop='query-raw'
-        elif args.syntaxhelp:
-            qop = 'query-syntax'
-        elif args.printdependency:
-            qop = 'query-dependency'
-        elif args.printsavedstate:
-            qop = 'query-savedstate'
-        else:
-            qop='query'
-        cachearg=(False if (iflist or args.nocache or
-                            args.perfmode or args.syntaxhelp or
-                            (qop != 'query-checkcurr' and
-                            qop != 'query-running')) else True)
-        if not iflist and qop == 'query-running':
-            iflist = [i for i in os.listdir('/sys/class/net/')
-                        if os.path.isdir('/sys/class/net/%s' %i)]
-        logger.debug('creating ifupdown object ..')
-        ifupdown_handle = ifupdownMain(config=configmap_g,
-                                       withdepends=args.withdepends,
-                                       perfmode=args.perfmode,
-                                       cache=cachearg,
-                                       interfacesfile=args.interfacesfile,
-                                       interfacesfileiobuf=interfacesfileiobuf,
-                                       interfacesfileformat=args.interfacesfileformat)
-
-        ifupdown_handle.query([qop], args.all, args.CLASS, iflist,
-                              excludepats=args.excludepats,
-                              printdependency=args.printdependency,
-                              format=args.format, type=args.type)
-    except:
-        raise
-
-def run_reload(args):
-    logger.debug('args = %s' %str(args))
-
-    try:
-        logger.debug('creating ifupdown object ..')
-        ifupdown_handle = ifupdownMain(config=configmap_g,
-                                       withdepends=args.withdepends,
-                                       perfmode=args.perfmode)
-        ifupdown_handle.reload(['pre-up', 'up', 'post-up'],
-                               ['pre-down', 'down', 'post-down'],
-                               auto=args.all, allow=None, ifacenames=None,
-                               excludepats=args.excludepats,
-                               usecurrentconfig=args.usecurrentconfig,
-                               currentlyup=args.currentlyup)
-    except:
-        raise
-
-def init(args):
-    global logger
-    global interfacesfileiobuf
-
-    log_level = logging.WARNING
-    if args.verbose:
-        log_level = logging.INFO
-    if args.debug:
-        log_level = logging.DEBUG
-
-    try:
-        if hasattr(args, 'syslog') and args.syslog:
-            root_logger = logging.getLogger()
-            syslog_handler = logging.handlers.SysLogHandler(address='/dev/log',
-                             facility=logging.handlers.SysLogHandler.LOG_DAEMON)
-            logging.addLevelName(logging.ERROR, 'error')
-            logging.addLevelName(logging.WARNING, 'warning')
-            logging.addLevelName(logging.DEBUG, 'debug')
-            logging.addLevelName(logging.INFO, 'info')
-            root_logger.setLevel(log_level)
-            syslog_handler.setFormatter(logging.Formatter(
-                '%(name)s: %(levelname)s: %(message)s'))
-            root_logger.addHandler(syslog_handler)
-            logger = logging.getLogger('ifupdown')
-        else:
-            logging.basicConfig(level=log_level,
-                format='%(levelname)s: %(message)s')
-            logging.addLevelName(logging.ERROR, 'error')
-            logging.addLevelName(logging.WARNING, 'warning')
-            logging.addLevelName(logging.DEBUG, 'debug')
-            logging.addLevelName(logging.INFO, 'info')
-            logger = logging.getLogger('ifupdown')
-    except:
-        raise
-
-    # If interfaces file is stdin, read
-    if hasattr(args, 'interfacesfile') and args.interfacesfile == '-':
-        interfacesfileiobuf = sys.stdin.read()
-
-def deinit():
-    {}
-
-def update_argparser(argparser):
-    """ base parser, common to all commands """
-
-    argparser.add_argument('-a', '--all', action='store_true', required=False,
-                help='process all interfaces marked \"auto\"')
-    argparser.add_argument('iflist', metavar='IFACE',
-                nargs='*', help='interface list separated by spaces. ' +
-                'IFACE list is mutually exclusive with -a option.')
-    argparser.add_argument('-v', '--verbose', dest='verbose',
-                action='store_true', help='verbose')
-    argparser.add_argument('-d', '--debug', dest='debug',
-                action='store_true',
-                help='output debug info')
-    argparser.add_argument('-q', '--quiet', dest='quiet',
-                action='store_true',
-                help=argparse.SUPPRESS)
-    argparser.add_argument('--allow', dest='CLASS', 
-                help='ignore non-\"allow-CLASS\" interfaces')
-    argparser.add_argument('-w', '--with-depends', dest='withdepends',
-                action='store_true', help='run with all dependent interfaces.'+
-                ' This option is redundant when \'-a\' is specified. With ' +
-                '\'-a\' interfaces are always executed in dependency order')
-    argparser.add_argument('--perfmode', dest='perfmode',
-                action='store_true', help=argparse.SUPPRESS)
-    #argparser.add_argument('-j', '--jobs', dest='jobs', type=int,
-    #            default=-1, choices=range(1,12), help=argparse.SUPPRESS)
-    argparser.add_argument('--nocache', dest='nocache', action='store_true',
-                help=argparse.SUPPRESS)
-    argparser.add_argument('-X', '--exclude', dest='excludepats',
-                action='append',
-                help='Exclude interfaces from the list of interfaces' +
-                ' to operate on. Can be specified multiple times.')
-    argparser.add_argument('-i', '--interfaces', dest='interfacesfile',
-                default='/etc/network/interfaces',
-                help='use interfaces file instead of default ' +
-                '/etc/network/interfaces')
-    argparser.add_argument('-t', '--interfaces-format',
-                dest='interfacesfileformat',
-                default='native',
-                choices=['native', 'json'],
-                help='interfaces file format')
-    argparser.add_argument('-T', '--type',
-                dest='type',
-                default=None,
-                choices=['iface', 'vlan'],
-                help='type of interface entry (iface or vlan).' +
-                     'This option can be used in case of ambiguity between ' +
-                     'a vlan interface and an iface interface of the same name')
-
-def update_ifupdown_argparser(argparser):
-    """ common arg parser for ifup and ifdown """
-    argparser.add_argument('-f', '--force', dest='force',
-                action='store_true',
-                help='force run all operations')
-    argparser.add_argument('-l', '--syslog', dest='syslog',
-                action='store_true',
-                help=argparse.SUPPRESS)
-    group = argparser.add_mutually_exclusive_group(required=False)
-    group.add_argument('-n', '--no-act', dest='noact',
-                action='store_true', help='print out what would happen,' +
-                'but don\'t do it')
-    group.add_argument('-p', '--print-dependency',
-                dest='printdependency', choices=['list', 'dot'],
-                help='print iface dependency')
-    group.add_argument('--no-scripts', '--admin-state',
-                dest='noaddons',  action='store_true',
-                help='dont run any addon modules/scripts. Only bring the ' +
-                    'interface administratively up/down')
-
-def update_ifup_argparser(argparser):
-    argparser.add_argument('-s', '--syntax-check', dest='syntaxcheck',
-                action='store_true',
-                help='Only run the interfaces file parser')
-    argparser.add_argument('-k', '--skip-upperifaces', dest='skipupperifaces',
-                action='store_true',
-                help='ifup by default tries to add newly created interfaces' +
-                ' into its upper/parent interfaces. Eg. if a bridge port is' +
-                ' created as a result of ifup on the port, ifup automatically' +
-                ' adds the port to the bridge. This option can be used to ' +
-                'disable this default behaviour')
-    update_ifupdown_argparser(argparser)
-
-def update_ifdown_argparser(argparser):
-    update_ifupdown_argparser(argparser)
-    argparser.add_argument('-u', '--use-current-config',
-                dest='usecurrentconfig',  action='store_true',
-                help='By default ifdown looks at the saved state for ' +
-                'interfaces to bring down. This option allows ifdown to ' +
-                'look at the current interfaces file. Useful when your ' +
-                'state file is corrupted or you want down to use the latest '
-                'from the interfaces file')
-
-def update_ifquery_argparser(argparser):
-    """ arg parser for ifquery options """
-
-    # -l is same as '-a', only here for backward compatibility
-    argparser.add_argument('-l', '--list', action='store_true', dest='all',
-                help=argparse.SUPPRESS)
-    group = argparser.add_mutually_exclusive_group(required=False)
-    group.add_argument('-r', '--running', dest='running',
-                       action='store_true',
-                       help='query running state of an interface')
-    group.add_argument('-c', '--check', dest='checkcurr',
-                       action='store_true',
-                       help='check interface file contents against ' +
-                       'running state of an interface')
-    group.add_argument('-x', '--raw', action='store_true', dest='raw',
-                       help='print raw config file entries')
-    group.add_argument('--print-savedstate', action='store_true',
-                       dest='printsavedstate',
-                       help=argparse.SUPPRESS)
-    argparser.add_argument('-o', '--format', dest='format', default='native',
-                           choices=['native', 'json'],
-                           help='interface display format')
-    argparser.add_argument('-p', '--print-dependency',
-                           dest='printdependency', choices=['list', 'dot'],
-                           help='print interface dependency')
-    argparser.add_argument('-s', '--syntax-help', action='store_true',
-                           dest='syntaxhelp',
-                           help='print supported interface config syntax')
-
-def update_ifreload_argparser(argparser):
-    """ parser for ifreload """
-    group = argparser.add_mutually_exclusive_group(required=True)
-    group.add_argument('-a', '--all', action='store_true',
-                help='process all interfaces marked \"auto\"')
-    group.add_argument('-c', '--currently-up', dest='currentlyup',
-                action='store_true',
-                help='only reload auto and other interfaces that are ' +
-                'currently up')
-    argparser.add_argument('iflist', metavar='IFACE',
-                nargs='*', help=argparse.SUPPRESS)
-    argparser.add_argument('-n', '--no-act', dest='noact',
-                action='store_true', help=argparse.SUPPRESS)
-    argparser.add_argument('-v', '--verbose', dest='verbose',
-                action='store_true', help='verbose')
-    argparser.add_argument('-d', '--debug', dest='debug',
-                action='store_true',
-                help='output debug info')
-    argparser.add_argument('-w', '--with-depends', dest='withdepends',
-                action='store_true', help=argparse.SUPPRESS)
-    argparser.add_argument('--perfmode', dest='perfmode',
-                action='store_true', help=argparse.SUPPRESS)
-    argparser.add_argument('--nocache', dest='nocache', action='store_true',
-                help=argparse.SUPPRESS)
-    argparser.add_argument('-X', '--exclude', dest='excludepats',
-                action='append',
-                help=argparse.SUPPRESS)
-    #argparser.add_argument('-j', '--jobs', dest='jobs', type=int,
-    #            default=-1, choices=range(1,12), help=argparse.SUPPRESS)
-    #argparser.add_argument('-i', '--interfaces', dest='interfacesfile',
-    #            default='/etc/network/interfaces',
-    #            help='use interfaces file instead of default ' +
-    #            '/etc/network/interfaces')
-    argparser.add_argument('-u', '--use-current-config',
-                dest='usecurrentconfig',  action='store_true',
-                help='By default ifreload looks at saved state for ' +
-                'interfaces to bring down. With this option ifreload will'
-                ' only look at the current interfaces file. Useful when your ' +
-                'state file is corrupted or you want down to use the latest '
-                'from the interfaces file')
-    argparser.add_argument('-l', '--syslog', dest='syslog',
-                action='store_true',
-                help=argparse.SUPPRESS)
-    argparser.add_argument('-f', '--force', dest='force',
-                action='store_true',
-                help='force run all operations')
-
-def parse_args(argsv, op):
-    if op == 'query':
-        descr = 'query interfaces (all or interface list)'
-    elif op == 'reload':
-        descr = 'reload interface configuration.'
-    else:
-        descr = 'interface management'
-    argparser = argparse.ArgumentParser(description=descr)
-    if op == 'reload':
-        update_ifreload_argparser(argparser)
-    else:
-        update_argparser(argparser)
-        if op == 'up':
-            update_ifup_argparser(argparser)
-        elif op == 'down':
-            update_ifdown_argparser(argparser)
-        elif op == 'query':
-            update_ifquery_argparser(argparser)
-        elif op == 'reload':
-            update_ifreload_argparser(argparser)
-    argcomplete.autocomplete(argparser)
-    return argparser.parse_args(argsv)
-
-handlers = {'up' : run_up,
-            'down' : run_down,
-            'query' : run_query,
-            'reload' : run_reload }
-
-def validate_args(op, args):
-    #if op == 'up' and args.syntaxcheck:
-    #    if args.iflist or args.all:
-    #        print 'ignoring interface options ..'
-    #    return True
-    if op == 'query' and args.syntaxhelp:
-        return True
-    if op == 'reload':
-        if not args.all and not args.currentlyup:
-            print '\'-a\' or \'-c\' option is required'
-            return False
-    elif (not args.iflist and
-            not args.all and not args.CLASS):
-        print '\'-a\' option or interface list are required'
-        return False
-    if args.iflist and args.all:
-        print '\'-a\' option and interface list are mutually exclusive'
-        return False
-    if op != 'reload' and args.CLASS and (args.all or args.iflist):
-        print ('\'--allow\' option is mutually exclusive ' +
-               'with interface list and \'-a\'')
-        return False
-    return True
-
-def read_config(args):
-    global configmap_g
-
-    config = open(configfile, 'r').read()
-    configStr = '[ifupdown2]\n' + config
-    configFP =  StringIO.StringIO(configStr)
-    parser = ConfigParser.RawConfigParser()
-    parser.readfp(configFP)
-    configmap_g = dict(parser.items('ifupdown2'))
-
-    # Preprocess config map
-    configval = configmap_g.get('multiple_vlan_aware_bridge_support', '0')
-    if configval == '0':
-        # if multiple bridges not allowed, set the bridge-vlan-aware
-        # attribute in the 'no_repeats' config, so that the ifupdownmain
-        # module can catch it appropriately
-        configmap_g['no_repeats'] = {'bridge-vlan-aware' : 'yes'}
-
-
-    configval = configmap_g.get('link_master_slave', '0')
-    if configval == '1':
-        # link_master_slave is only valid when all is set
-        if hasattr(args, 'all') and not args.all:
-            configmap_g['link_master_slave'] = '0'
-
-    configval = configmap_g.get('delay_admin_state_change', '0')
-    if configval == '1':
-        # reset link_master_slave if delay_admin_state_change is on
-        configmap_g['link_master_slave'] = '0'
-
-def main(argv):
-    """ main function """
-    args = None
-    try:
-        op = None
-        if argv[0].endswith('ifup'):
-            op = 'up'
-        elif argv[0].endswith('ifdown'):
-            op = 'down'
-        elif argv[0].endswith('ifquery'):
-            op = 'query'
-        elif argv[0].endswith('ifreload'):
-            op = 'reload'
-        else:
-            print ('Unexpected executable.' +
-                   ' Should be \'ifup\' or \'ifdown\' or \'ifquery\'')
-            exit(1)
-        # Command line arg parser
-        args = parse_args(argv[1:], op)
-        if not validate_args(op, args):
-            exit(1)
-
-        if not sys.argv[0].endswith('ifquery') and not os.geteuid() == 0:
-            print 'error: must be root to run this command'
-            exit(1)
-
-        if not sys.argv[0].endswith('ifquery') and not utils.lockFile(lockfile):
-            print 'Another instance of this program is already running.'
-            exit(0)
-
-        read_config(args)
-        init(args)
-        handlers.get(op)(args)
-    except Exception, e:
-        if not str(e):
-            exit(1)
-        if args and args.debug:
-            raise
-        else:
-            if logger:
-                logger.error(str(e))
-            else:
-                print str(e)
-            #if args and not args.debug:
-            #    print '\nrerun the command with \'-d\' for a detailed errormsg'
-        exit(1)
-    finally:
-        deinit()
-
-if __name__ == "__main__":
-
-    # required during boot
-    os.putenv('PATH', ENVPATH)
-    resource.setrlimit(resource.RLIMIT_CORE, (0,0))
-    main(sys.argv)
diff --git a/sbin/ifupdown2 b/sbin/ifupdown2
deleted file mode 100755 (executable)
index a7f7e2a..0000000
+++ /dev/null
@@ -1,549 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
-# Author: Roopa Prabhu, roopa@cumulusnetworks.com
-#
-# ifupdown --
-#    tool to configure network interfaces
-#
-import sys
-import os
-import argcomplete
-import argparse
-import ConfigParser
-import StringIO
-import logging
-import logging.handlers
-import resource
-from ifupdown.ifupdownmain import *
-from ifupdown.utils import *
-
-IFUPDOWN2_VERSION = '20170223-1'
-
-lockfile="/run/network/.lock"
-configfile="/etc/network/ifupdown2/ifupdown2.conf"
-configmap_g=None
-logger = None
-interfacesfileiobuf=None
-interfacesfilename=None
-ENVPATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
-
-def run_up(args):
-    logger.debug('args = %s' %str(args))
-
-    try:
-        iflist = args.iflist
-        if len(args.iflist) == 0:
-            iflist = None
-        logger.debug('creating ifupdown object ..')
-        cachearg=(False if (iflist or args.nocache or
-                            args.perfmode or args.noact)
-                            else True)
-        ifupdown_handle = ifupdownMain(config=configmap_g,
-                                       force=args.force,
-                                       withdepends=args.withdepends,
-                                       perfmode=args.perfmode,
-                                       dryrun=args.noact,
-                                       cache=cachearg,
-                                       addons_enable=not args.noaddons,
-                                       statemanager_enable=not args.noaddons,
-                                       interfacesfile=interfacesfilename,
-                                       interfacesfileiobuf=interfacesfileiobuf,
-                                       interfacesfileformat=args.interfacesfileformat)
-        if args.noaddons:
-            ifupdown_handle.up(['up'], args.all, args.CLASS, iflist,
-                               excludepats=args.excludepats,
-                               printdependency=args.printdependency,
-                               syntaxcheck=args.syntaxcheck, type=args.type,
-                               skipupperifaces=args.skipupperifaces)
-        else:
-            ifupdown_handle.up(['pre-up', 'up', 'post-up'],
-                               args.all, args.CLASS, iflist,
-                               excludepats=args.excludepats,
-                               printdependency=args.printdependency,
-                               syntaxcheck=args.syntaxcheck, type=args.type,
-                               skipupperifaces=args.skipupperifaces)
-    except:
-        raise
-
-def run_down(args):
-    logger.debug('args = %s' %str(args))
-
-    try:
-        iflist = args.iflist
-        logger.debug('creating ifupdown object ..')
-        ifupdown_handle = ifupdownMain(config=configmap_g, force=args.force,
-                                       withdepends=args.withdepends,
-                                       perfmode=args.perfmode,
-                                       dryrun=args.noact,
-                                       addons_enable=not args.noaddons,
-                                       statemanager_enable=not args.noaddons,
-                                       interfacesfile=interfacesfilename,
-                                       interfacesfileiobuf=interfacesfileiobuf,
-                                       interfacesfileformat=args.interfacesfileformat)
-
-        ifupdown_handle.down(['pre-down', 'down', 'post-down'],
-                             args.all, args.CLASS, iflist,
-                             excludepats=args.excludepats,
-                             printdependency=args.printdependency,
-                             usecurrentconfig=args.usecurrentconfig,
-                             type=args.type)
-    except:
-        raise
-
-def run_query(args):
-    logger.debug('args = %s' %str(args))
-
-    try:
-        iflist = args.iflist
-        if args.checkcurr:
-            qop='query-checkcurr'
-        elif args.running:
-            qop='query-running'
-        elif args.raw:
-            qop='query-raw'
-        elif args.syntaxhelp:
-            qop = 'query-syntax'
-        elif args.printdependency:
-            qop = 'query-dependency'
-        elif args.printsavedstate:
-            qop = 'query-savedstate'
-        else:
-            qop='query'
-        cachearg=(False if (iflist or args.nocache or
-                            args.perfmode or args.syntaxhelp or
-                            (qop != 'query-checkcurr' and
-                            qop != 'query-running')) else True)
-        if not iflist and qop == 'query-running':
-            iflist = [i for i in os.listdir('/sys/class/net/')
-                        if os.path.isdir('/sys/class/net/%s' %i)]
-        logger.debug('creating ifupdown object ..')
-        ifupdown_handle = ifupdownMain(config=configmap_g,
-                                       withdepends=args.withdepends,
-                                       perfmode=args.perfmode,
-                                       cache=cachearg,
-                                       interfacesfile=interfacesfilename,
-                                       interfacesfileiobuf=interfacesfileiobuf,
-                                       interfacesfileformat=args.interfacesfileformat,
-                                       withdefaults=args.withdefaults)
-        # list implies all auto interfaces (this is how ifupdown behaves)
-        if args.list:
-            args.all = True
-        ifupdown_handle.query([qop], args.all, args.list, args.CLASS, iflist,
-                              excludepats=args.excludepats,
-                              printdependency=args.printdependency,
-                              format=args.format, type=args.type)
-    except:
-        raise
-
-def run_reload(args):
-    logger.debug('args = %s' %str(args))
-
-    try:
-        logger.debug('creating ifupdown object ..')
-        ifupdown_handle = ifupdownMain(config=configmap_g,
-                                       interfacesfile=interfacesfilename,
-                                       withdepends=args.withdepends,
-                                       perfmode=args.perfmode,
-                                       dryrun=args.noact)
-        ifupdown_handle.reload(['pre-up', 'up', 'post-up'],
-                               ['pre-down', 'down', 'post-down'],
-                               auto=args.all, allow=args.CLASS, ifacenames=None,
-                               excludepats=args.excludepats,
-                               usecurrentconfig=args.usecurrentconfig,
-                               syntaxcheck=args.syntaxcheck,
-                               currentlyup=args.currentlyup)
-    except:
-        raise
-
-def init(args):
-    global logger
-    global interfacesfileiobuf
-    global interfacesfilename
-
-    log_level = logging.WARNING
-    if args.verbose:
-        log_level = logging.INFO
-    if args.debug:
-        log_level = logging.DEBUG
-
-    try:
-        if hasattr(args, 'syslog') and args.syslog:
-            root_logger = logging.getLogger()
-            syslog_handler = logging.handlers.SysLogHandler(address='/dev/log',
-                             facility=logging.handlers.SysLogHandler.LOG_DAEMON)
-            logging.addLevelName(logging.ERROR, 'error')
-            logging.addLevelName(logging.WARNING, 'warning')
-            logging.addLevelName(logging.DEBUG, 'debug')
-            logging.addLevelName(logging.INFO, 'info')
-            root_logger.setLevel(log_level)
-            syslog_handler.setFormatter(logging.Formatter(
-                '%(name)s: %(levelname)s: %(message)s'))
-            root_logger.addHandler(syslog_handler)
-            logger = logging.getLogger('ifupdown')
-        else:
-            logging.basicConfig(level=log_level,
-                format='%(levelname)s: %(message)s')
-            logging.addLevelName(logging.ERROR, 'error')
-            logging.addLevelName(logging.WARNING, 'warning')
-            logging.addLevelName(logging.DEBUG, 'debug')
-            logging.addLevelName(logging.INFO, 'info')
-            logger = logging.getLogger('ifupdown')
-    except:
-        raise
-
-    if hasattr(args, 'interfacesfile') and args.interfacesfile != None:
-        # Check to see if -i option is allowed by config file
-        # But for ifquery, we will not check this
-        if (not sys.argv[0].endswith('ifquery') and
-                configmap_g.get('disable_cli_interfacesfile','0') == '1'):
-            logger.error('disable_cli_interfacesfile is set so users '
-                         'not allowed to specify interfaces file on cli.')
-            exit(1)
-        if args.interfacesfile == '-':
-            # If interfaces file is stdin, read
-            interfacesfileiobuf = sys.stdin.read()
-        else:
-            interfacesfilename = args.interfacesfile
-    else:
-        # if the ifupdown2 config file does not have it, default to standard
-        interfacesfilename = configmap_g.get('default_interfaces_configfile',
-                                             '/etc/network/interfaces')
-
-
-
-
-def deinit():
-    {}
-
-def update_argparser(argparser):
-    """ base parser, common to all commands """
-
-    argparser.add_argument('-a', '--all', action='store_true', required=False,
-                help='process all interfaces marked \"auto\"')
-    argparser.add_argument('iflist', metavar='IFACE',
-                nargs='*', help='interface list separated by spaces. ' +
-                'IFACE list is mutually exclusive with -a option.')
-    argparser.add_argument('-v', '--verbose', dest='verbose',
-                action='store_true', help='verbose')
-    argparser.add_argument('-d', '--debug', dest='debug',
-                action='store_true',
-                help='output debug info')
-    argparser.add_argument('-q', '--quiet', dest='quiet',
-                action='store_true',
-                help=argparse.SUPPRESS)
-    argparser.add_argument('--allow', dest='CLASS', 
-                help='ignore non-\"allow-CLASS\" interfaces')
-    argparser.add_argument('-w', '--with-depends', dest='withdepends',
-                action='store_true', help='run with all dependent interfaces.'+
-                ' This option is redundant when \'-a\' is specified. With ' +
-                '\'-a\' interfaces are always executed in dependency order')
-    argparser.add_argument('--perfmode', dest='perfmode',
-                action='store_true', help=argparse.SUPPRESS)
-    #argparser.add_argument('-j', '--jobs', dest='jobs', type=int,
-    #            default=-1, choices=range(1,12), help=argparse.SUPPRESS)
-    argparser.add_argument('--nocache', dest='nocache', action='store_true',
-                help=argparse.SUPPRESS)
-    argparser.add_argument('-X', '--exclude', dest='excludepats',
-                action='append',
-                help='Exclude interfaces from the list of interfaces' +
-                ' to operate on. Can be specified multiple times.')
-    argparser.add_argument('-i', '--interfaces', dest='interfacesfile',
-                default=None,
-                help='Specify interfaces file instead of file defined ' +
-                'in ifupdown2.conf file')
-    argparser.add_argument('-t', '--interfaces-format',
-                dest='interfacesfileformat',
-                default='native',
-                choices=['native', 'json'],
-                help='interfaces file format')
-    argparser.add_argument('-T', '--type',
-                dest='type',
-                default=None,
-                choices=['iface', 'vlan'],
-                help='type of interface entry (iface or vlan).' +
-                     'This option can be used in case of ambiguity between ' +
-                     'a vlan interface and an iface interface of the same name')
-
-def update_ifupdown_argparser(argparser):
-    """ common arg parser for ifup and ifdown """
-    argparser.add_argument('-f', '--force', dest='force',
-                action='store_true',
-                help='force run all operations')
-    argparser.add_argument('-l', '--syslog', dest='syslog',
-                action='store_true',
-                help=argparse.SUPPRESS)
-    group = argparser.add_mutually_exclusive_group(required=False)
-    group.add_argument('-n', '--no-act', dest='noact',
-                action='store_true', help='print out what would happen,' +
-                'but don\'t do it')
-    group.add_argument('-p', '--print-dependency',
-                dest='printdependency', choices=['list', 'dot'],
-                help='print iface dependency')
-    group.add_argument('--no-scripts', '--admin-state',
-                dest='noaddons',  action='store_true',
-                help='dont run any addon modules/scripts. Only bring the ' +
-                    'interface administratively up/down')
-
-def update_ifup_argparser(argparser):
-    argparser.add_argument('-s', '--syntax-check', dest='syntaxcheck',
-                action='store_true',
-                help='Only run the interfaces file parser')
-    argparser.add_argument('-k', '--skip-upperifaces', dest='skipupperifaces',
-                action='store_true',
-                help='ifup by default tries to add newly created interfaces' +
-                ' into its upper/parent interfaces. Eg. if a bridge port is' +
-                ' created as a result of ifup on the port, ifup automatically' +
-                ' adds the port to the bridge. This option can be used to ' +
-                'disable this default behaviour')
-    update_ifupdown_argparser(argparser)
-
-def update_ifdown_argparser(argparser):
-    update_ifupdown_argparser(argparser)
-    argparser.add_argument('-u', '--use-current-config',
-                dest='usecurrentconfig',  action='store_true',
-                help='By default ifdown looks at the saved state for ' +
-                'interfaces to bring down. This option allows ifdown to ' +
-                'look at the current interfaces file. Useful when your ' +
-                'state file is corrupted or you want down to use the latest '
-                'from the interfaces file')
-
-def update_ifquery_argparser(argparser):
-    """ arg parser for ifquery options """
-
-    # -l is same as '-a', only here for backward compatibility
-    argparser.add_argument('-l', '--list', action='store_true', dest='list',
-                           help='list all matching known interfaces')
-    group = argparser.add_mutually_exclusive_group(required=False)
-    group.add_argument('-r', '--running', dest='running',
-                       action='store_true',
-                       help='query running state of an interface')
-    group.add_argument('-c', '--check', dest='checkcurr',
-                       action='store_true',
-                       help='check interface file contents against ' +
-                       'running state of an interface')
-    group.add_argument('-x', '--raw', action='store_true', dest='raw',
-                       help='print raw config file entries')
-    group.add_argument('--print-savedstate', action='store_true',
-                       dest='printsavedstate',
-                       help=argparse.SUPPRESS)
-    argparser.add_argument('-o', '--format', dest='format', default='native',
-                           choices=['native', 'json'],
-                           help='interface display format')
-    argparser.add_argument('-p', '--print-dependency',
-                           dest='printdependency', choices=['list', 'dot'],
-                           help='print interface dependency')
-    argparser.add_argument('-s', '--syntax-help', action='store_true',
-                           dest='syntaxhelp',
-                           help='print supported interface config syntax')
-    argparser.add_argument('--with-defaults', action='store_true',
-                           dest='withdefaults',
-                           help='check policy default file contents, ' +
-                                'for unconfigured attributes, against ' +
-                                'running state of an interface')
-
-def update_ifreload_argparser(argparser):
-    """ parser for ifreload """
-    group = argparser.add_mutually_exclusive_group(required=True)
-    group.add_argument('-a', '--all', action='store_true',
-                help='process all interfaces marked \"auto\"')
-    group.add_argument('-c', '--currently-up', dest='currentlyup',
-                action='store_true',
-                help='only reload auto and other interfaces that are ' +
-                'currently up')
-    group.add_argument('--allow', dest='CLASS',
-                help='ignore non-\"allow-CLASS\" interfaces')
-    argparser.add_argument('iflist', metavar='IFACE',
-                nargs='*', help=argparse.SUPPRESS)
-    argparser.add_argument('-n', '--no-act', dest='noact',
-                action='store_true', help='print out what would happen,' +
-                'but don\'t do it')
-    argparser.add_argument('-v', '--verbose', dest='verbose',
-                action='store_true', help='verbose')
-    argparser.add_argument('-d', '--debug', dest='debug',
-                action='store_true',
-                help='output debug info')
-    argparser.add_argument('-w', '--with-depends', dest='withdepends',
-                action='store_true', help=argparse.SUPPRESS)
-    argparser.add_argument('--perfmode', dest='perfmode',
-                action='store_true', help=argparse.SUPPRESS)
-    argparser.add_argument('--nocache', dest='nocache', action='store_true',
-                help=argparse.SUPPRESS)
-    argparser.add_argument('-X', '--exclude', dest='excludepats',
-                action='append',
-                help=argparse.SUPPRESS)
-    #argparser.add_argument('-j', '--jobs', dest='jobs', type=int,
-    #            default=-1, choices=range(1,12), help=argparse.SUPPRESS)
-    #argparser.add_argument('-i', '--interfaces', dest='interfacesfile',
-    #            default='/etc/network/interfaces',
-    #            help='use interfaces file instead of default ' +
-    #            '/etc/network/interfaces')
-    argparser.add_argument('-u', '--use-current-config',
-                dest='usecurrentconfig',  action='store_true',
-                help='By default ifreload looks at saved state for ' +
-                'interfaces to bring down. With this option ifreload will'
-                ' only look at the current interfaces file. Useful when your ' +
-                'state file is corrupted or you want down to use the latest '
-                'from the interfaces file')
-    argparser.add_argument('-l', '--syslog', dest='syslog',
-                action='store_true',
-                help=argparse.SUPPRESS)
-    argparser.add_argument('-f', '--force', dest='force',
-                action='store_true',
-                help='force run all operations')
-    argparser.add_argument('-s', '--syntax-check', dest='syntaxcheck',
-                action='store_true',
-                help='Only run the interfaces file parser')
-
-def update_common_argparser(argparser):
-    ''' general parsing rules '''
-
-    argparser.add_argument('-V', '--version',
-                action='version',
-                version='ifupdown2:%(prog)s ' + IFUPDOWN2_VERSION,
-                help='display current ifupdown2 version')
-
-def parse_args(argsv, op):
-    if op == 'query':
-        descr = 'query interfaces (all or interface list)'
-    elif op == 'reload':
-        descr = 'reload interface configuration.\n\n \
-                ifreload downs interfaces that were removed\n \
-                from the interfaces file and subsequently runs ifup\n \
-                on all interfaces. ifreload is non-disruptive. It will fix\n \
-                running config to match what is configured in the interfaces\n \
-                file without bringing the interface down. It may flap an interface\n\
-                for some attribute changes. Please see man ifreload\n \
-                for details.'
-    else:
-        descr = 'interface management'
-    argparser = argparse.ArgumentParser(description=descr)
-    if op == 'reload':
-        update_ifreload_argparser(argparser)
-    else:
-        update_argparser(argparser)
-        if op == 'up':
-            update_ifup_argparser(argparser)
-        elif op == 'down':
-            update_ifdown_argparser(argparser)
-        elif op == 'query':
-            update_ifquery_argparser(argparser)
-    update_common_argparser(argparser)
-    argcomplete.autocomplete(argparser)
-    return argparser.parse_args(argsv)
-
-handlers = {'up' : run_up,
-            'down' : run_down,
-            'query' : run_query,
-            'reload' : run_reload }
-
-def validate_args(op, args):
-    #if op == 'up' and args.syntaxcheck:
-    #    if args.iflist or args.all:
-    #        print 'ignoring interface options ..'
-    #    return True
-    if op == 'query' and (args.syntaxhelp or args.list):
-        return True
-    if op == 'reload':
-        if not args.all and not args.currentlyup and not args.CLASS:
-            print '\'-a\' or \'-c\' or \'-allow\' option is required'
-            return False
-    elif (not args.iflist and
-            not args.all and not args.CLASS):
-        print '\'-a\' option or interface list are required'
-        return False
-    if args.iflist and args.all:
-        print '\'-a\' option and interface list are mutually exclusive'
-        return False
-    if op != 'reload' and args.CLASS and (args.all or args.iflist):
-        print ('\'--allow\' option is mutually exclusive ' +
-               'with interface list and \'-a\'')
-        return False
-    return True
-
-def read_config(args):
-    global configmap_g
-
-    with open(configfile, 'r') as f:
-        config = f.read()
-    configStr = '[ifupdown2]\n' + config
-    configFP =  StringIO.StringIO(configStr)
-    parser = ConfigParser.RawConfigParser()
-    parser.readfp(configFP)
-    configmap_g = dict(parser.items('ifupdown2'))
-
-    # Preprocess config map
-    configval = configmap_g.get('multiple_vlan_aware_bridge_support', '0')
-    if configval == '0':
-        # if multiple bridges not allowed, set the bridge-vlan-aware
-        # attribute in the 'no_repeats' config, so that the ifupdownmain
-        # module can catch it appropriately
-        configmap_g['no_repeats'] = {'bridge-vlan-aware' : 'yes'}
-
-
-    configval = configmap_g.get('link_master_slave', '0')
-    if configval == '1':
-        # link_master_slave is only valid when all is set
-        if hasattr(args, 'all') and not args.all:
-            configmap_g['link_master_slave'] = '0'
-
-    configval = configmap_g.get('delay_admin_state_change', '0')
-    if configval == '1':
-        # reset link_master_slave if delay_admin_state_change is on
-        configmap_g['link_master_slave'] = '0'
-
-def main(argv):
-    """ main function """
-    args = None
-    try:
-        op = None
-        if argv[0].endswith('ifup'):
-            op = 'up'
-        elif argv[0].endswith('ifdown'):
-            op = 'down'
-        elif argv[0].endswith('ifquery'):
-            op = 'query'
-        elif argv[0].endswith('ifreload'):
-            op = 'reload'
-        else:
-            print ('Unexpected executable.' +
-                   ' Should be \'ifup\' or \'ifdown\' or \'ifquery\'')
-            exit(1)
-        # Command line arg parser
-        args = parse_args(argv[1:], op)
-        if not validate_args(op, args):
-            exit(1)
-
-        if not sys.argv[0].endswith('ifquery') and not os.geteuid() == 0:
-            print 'error: must be root to run this command'
-            exit(1)
-
-        if not sys.argv[0].endswith('ifquery') and not utils.lockFile(lockfile):
-            print 'Another instance of this program is already running.'
-            exit(0)
-
-        read_config(args)
-        init(args)
-        handlers.get(op)(args)
-    except Exception, e:
-        if not str(e):
-            exit(1)
-        if args and args.debug:
-            raise
-        else:
-            if logger:
-                logger.error(str(e))
-            else:
-                print str(e)
-            #if args and not args.debug:
-            #    print '\nrerun the command with \'-d\' for a detailed errormsg'
-        exit(1)
-    finally:
-        deinit()
-
-if __name__ == "__main__":
-
-    # required during boot
-    os.putenv('PATH', ENVPATH)
-    resource.setrlimit(resource.RLIMIT_CORE, (0,0))
-    main(sys.argv)
diff --git a/sbin/start-networking b/sbin/start-networking
deleted file mode 100755 (executable)
index 5232e88..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-#!/bin/bash
-
-# This replaces the old init.d script, and is run from the networking.service
-# Only has start, stop, reload, because that's all systemd has.
-# restart is implemented in systemd by stop then start.
-
-PATH="/sbin:/bin"
-RUN_DIR="/run/network"
-IFSTATE_LOCKFILE="${RUN_DIR}/ifstatelock"
-
-STATE_DIR="/var/tmp/network"
-IFSTATE_FILE="${STATE_DIR}/ifstatenew"
-
-NAME=networking
-
-[ -x /sbin/ifup ] || exit 0
-[ -x /sbin/ifdown ] || exit 0
-
-CONFIGURE_INTERFACES=yes
-
-EXTRA_ARGS=
-
-[ -f /etc/default/networking ] && . /etc/default/networking
-
-[ "$VERBOSE" = yes ] && EXTRA_ARGS=-v
-[ "$DEBUG" = yes ] && EXTRA_ARGS="$EXTRA_ARGS -d"
-[ "$SYSLOG" = yes ] && EXTRA_ARGS="$EXTRA_ARGS --syslog"
-
-perf_options() {
-    # At bootup lets set perfmode
-    [ -f ${IFSTATE_LOCKFILE} ] && echo -n "" && return
-
-    echo -n "--perfmode"
-}
-
-process_exclusions() {
-    set -- $EXCLUDE_INTERFACES
-    exclusions=""
-    for d
-    do
-       exclusions="-X $d $exclusions"
-    done
-    echo $exclusions
-}
-
-check_network_file_systems() {
-    [ -e /proc/mounts ] || return 0
-
-    if [ -e /etc/iscsi/iscsi.initramfs ]; then
-       echo ${NAME}':' "not deconfiguring network interfaces: iSCSI root is mounted."
-       exit 0
-    fi
-
-    while read DEV MTPT FSTYPE REST; do
-       case $DEV in
-       /dev/nbd*|/dev/nd[a-z]*|/dev/etherd/e*)
-           echo ${NAME}':' "not deconfiguring network interfaces: network devices still mounted."
-           exit 0
-           ;;
-       esac
-       case $FSTYPE in
-       nfs|nfs4|smbfs|ncp|ncpfs|cifs|coda|ocfs2|gfs|pvfs|pvfs2|fuse.httpfs|fuse.curlftpfs)
-           echo ${NAME}':' "not deconfiguring network interfaces: network file systems still mounted."
-           exit 0
-           ;;
-       esac
-    done < /proc/mounts
-}
-
-check_network_swap() {
-    [ -e /proc/swaps ] || return 0
-
-    while read DEV MTPT FSTYPE REST; do
-       case $DEV in
-       /dev/nbd*|/dev/nd[a-z]*|/dev/etherd/e*)
-           echo ${NAME}':' "not deconfiguring network interfaces: network swap still mounted."
-           exit 0
-           ;;
-       esac
-    done < /proc/swaps
-}
-
-ifup_hotplug () {
-    if [ -d /sys/class/net ]
-    then
-           ifaces=$(for iface in $(ifquery --list --allow=hotplug 2>/dev/null)
-                           do
-                                   link=${iface##:*}
-                                   link=${link##.*}
-
-                                   ip link set "$iface" up || true
-                                   if [ "$(cat /sys/class/net/$link/operstate)" = up ]
-                                   then
-                                       echo "$iface"
-                                   fi
-                           done)
-           if [ -n "$ifaces" ]
-           then
-               ifup $ifaces "$@" || true
-           fi
-    fi
-}
-
-ifup_mgmt () {
-       ifaces=$(ifquery --list --allow=mgmt 2>/dev/null)
-       if [ -n "$ifaces" ]; then
-               echo "bringing up mgmt class interfaces"
-               ifup --allow=mgmt
-       fi
-}
-
-ifupdown_init() {
-       # remove state file at boot
-       [ ! -e ${IFSTATE_LOCKFILE} ] && rm -f ${IFSTATE_FILE}
-
-       [ ! -e /run/network ] && mkdir -p /run/network &>/dev/null
-       [ ! -e /etc/network/run ] && \
-               ln -sf /run/network /etc/network/run &>/dev/null
-}
-
-case "$1" in
-start)
-       ifupdown_init
-       if [ "$CONFIGURE_INTERFACES" = no ]
-       then
-           echo ${NAME}':' "Not configuring network interfaces, see /etc/default/networking"
-           exit 0
-       fi
-       set -f
-       exclusions=$(process_exclusions)
-       perfoptions=$(perf_options)
-       echo ${NAME}':' "Configuring network interfaces"
-       ifup_mgmt
-       ifup -a $EXTRA_ARGS $exclusions $perfoptions
-       ifup_hotplug $HOTPLUG_ARGS $EXTRA_ARGS $exclusions
-       ;;
-stop)
-       if [ "$SKIP_DOWN_AT_SYSRESET" = "yes" ]; then
-        SYSRESET=0
-        systemctl list-jobs | egrep -q '(shutdown|reboot|halt|poweroff)\.target'
-        [ $? -eq 0 ] && SYSRESET=1
-        if [ $SYSRESET -eq 1 ]; then
-            echo ${NAME}':' "Skipping deconfiguring network interfaces"
-            exit 0
-        fi
-       fi
-       ifupdown_init
-       check_network_file_systems
-       check_network_swap
-       exclusions=$(process_exclusions)
-
-       echo ${NAME}':' "Deconfiguring network interfaces"
-       ifdown -a $EXTRA_ARGS $exclusions
-       ;;
-
-reload)
-
-       ifupdown_init
-       exclusions=$(process_exclusions)
-
-       echo ${NAME}':' "Reloading network interfaces configuration"
-       ifreload -a $EXTRA_ARGS $exclusions
-       ;;
-
-*)
-       echo ${NAME}':' "Usage: $0 {start|stop|reload}"
-       exit 1
-       ;;
-esac
-
-exit 0
diff --git a/scripts/genmanpages.sh b/scripts/genmanpages.sh
deleted file mode 100755 (executable)
index 2553dc7..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/sh
-#
-# Copyright 2013 Cumulus Networks, Inc.
-# All rights reserved.
-#
-
-# Install the man pages into the sysroot
-SRC_MAN_DIR=$1
-DST_MAN_DIR=$2
-
-echo -n "Generating man pages .."
-# Loop over all the man directories
-mkdir -p $DST_MAN_DIR
-for p in $(ls $SRC_MAN_DIR/*.rst) ; do
-    # strip src man path
-    src_file=$p
-    dst_file=${p##.*\/}
-    dst_file="${DST_MAN_DIR}/${dst_file%.rst}"
-    # treat warnings as errors
-    rst2man --halt=2 "$p" > $dst_file || {
-        echo
-        echo "Error: problems generating man page: $p"
-        rm -f $dst_file &>/dev/null
-        exit 1
-    }
-    echo -n "."
-done
-echo " done."
index 5d4979dea40ef8ffc43e3c72c2e953f826657a1f..a799c3d62639e356ade19b24768ddec5f1a51f32 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -1,33 +1,73 @@
-from distutils.core import setup
-
-setup(name='ifupdown2',
-      version='1.1',
-      description = "ifupdown 2",
-      author='Roopa Prabhu',
-      author_email='roopa@cumulusnetworks.com',
-      url='cumulusnetworks.com',
-      packages=['ifupdown', 'ifupdownaddons'],
-      data_files=[ ('/etc/network/ifupdown2/',
-                      ['config/ifupdown2.conf']),
-                  ('/usr/share/bash-completion/completions/', ['completion/ifup']),
-                  ('/usr/share/ifupdown2/addons/', ['addons/bridge.py',
-                      'addons/bond.py', 'addons/vlan.py',
-                      'addons/mstpctl.py', 'addons/address.py',
-                      'addons/dhcp.py', 'addons/usercmds.py',
-                      'addons/ethtool.py',
-                      'addons/addressvirtual.py', 'addons/vxlan.py',
-                      'addons/link.py', 'addons/tunnel.py',
-                      'addons/ppp.py', 'addons/vrf.py',
-                      'addons/bridgevlan.py', 'addons/batman_adv.py']),
-                   ('/usr/share/ifupdown2/nlmanager/',
-                    ['nlmanager/nllistener.py',
-                     'nlmanager/nlmanager.py',
-                     'nlmanager/nlpacket.py',
-                     'nlmanager/__init__.py',
-                     'nlmanager/README']),
-                   ('/etc/network/ifupdown2/', ['config/addons.conf']),
-                  ('/etc/network/ifupdown2/', ['config/addons.conf']),
-                  ('/var/lib/ifupdown2/policy.d/', []),
-                  ('/etc/network/ifupdown2/policy.d/', [])
-                  ]
-      )
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import sys
+
+from setuptools import setup
+from setuptools import find_packages
+
+INSTALL_REQUIRES = [
+    'argcomplete',
+    'ipaddr',
+]
+
+DATA_FILES = [
+    ('/etc/default/', ['etc/default/networking']),
+    ('/etc/network/ifupdown2/', ['etc/network/ifupdown2/addons.conf']),
+    ('/etc/network/ifupdown2/', ['etc/network/ifupdown2/ifupdown2.conf']),
+]
+
+SCRIPTS = []
+
+ENTRY_POINTS = {}
+
+
+def build_deb_package():
+    try:
+        return sys.argv[sys.argv.index('--root') + 1].endswith('/debian/ifupdown2')
+    except:
+        return False
+
+
+if build_deb_package():
+    DATA_FILES.append(('/usr/share/ifupdown2/sbin/', ['ifupdown2/sbin/start-networking']))
+else:
+    ENTRY_POINTS = {
+        'console_scripts': [
+            'ifup = ifupdown2.__main__:main',
+            'ifdown = ifupdown2.__main__:main',
+            'ifquery = ifupdown2.__main__:main',
+            'ifreload = ifupdown2.__main__:main',
+        ],
+    }
+
+setup(
+    author='Roopa Prabhu',
+    author_email='roopa@cumulusnetworks.com',
+    maintainer='Julien Fortin',
+    maintainer_email='julien@cumulusnetworks.com',
+    classifiers=[
+        'Development Status :: 5 - Production/Stable',
+        'Environment :: Console',
+        'Intended Audience :: System Administrators',
+        'License :: OSI Approved :: GNU General Public License v2 (GPLv2)',
+        'Natural Language :: English',
+        'Operating System :: POSIX :: Linux',
+        'Programming Language :: Python :: 2',
+        'Programming Language :: Python :: 2.7',
+        'Topic :: System :: Networking',
+        'Topic :: System :: Systems Administration'
+    ],
+    description='interface network manager',
+    install_requires=INSTALL_REQUIRES,
+    license='GNU General Public License v2',
+    keywords='ifupdown2',
+    name='ifupdown2',
+    packages=find_packages(),
+    url='https://github.com/CumulusNetworks/ifupdown2',
+    version='2.0.0',
+    data_files=DATA_FILES,
+    setup_requires=['setuptools'],
+    scripts=SCRIPTS,
+    entry_points=ENTRY_POINTS
+)
diff --git a/stdeb.cfg b/stdeb.cfg
deleted file mode 100644 (file)
index db6414a..0000000
--- a/stdeb.cfg
+++ /dev/null
@@ -1,2 +0,0 @@
-[DEFAULT]
-Conflicts: ifupdown
diff --git a/tests/ifstatetest b/tests/ifstatetest
deleted file mode 100755 (executable)
index a761e75..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/usr/bin/python
-
-""" test for testing and profiling state manager """
-
-import cProfile
-
-from ifupdown.networkinterfaces import *
-from ifupdown.iface import *
-from ifupdown.statemanager import pickling
-import os
-
-ifaceobjdict = {}
-state_file = '/tmp/ifstatetest'
-
-def save_iface(ifaceobj):
-    ifaceobjdict[ifaceobj.get_name()] = ifaceobj
-    
-def read_default_iface_config():
-    """ Reads default network interface config /etc/network/interfaces. """
-    nifaces = networkInterfaces()
-    nifaces.subscribe('iface_found', save_iface)
-    nifaces.load()
-
-def save_state():
-    try:
-        with open(state_file, 'w') as f:
-            for ifaceobj in ifaceobjdict.values():
-                pickling.save_obj(f, ifaceobj)
-    except:
-        raise
-
-def load_state():
-    global ifaceobjdict
-
-    if not os.path.exists(state_file):
-        return
-
-    del ifaceobjdict
-    ifaceobjdict = {}
-
-    # Read all ifaces from file
-    for ifaceobj in pickling.load(state_file):
-        save_iface(ifaceobj)
-
-
-print 'Reading iface config files ..'
-cProfile.run('read_default_iface_config()')
-print 'number of objects: %d' %len(ifaceobjdict)
-
-print 'saving iface state ..'
-cProfile.run('save_state()')
-
-print 'loading iface state ..'
-cProfile.run('load_state()')
-print 'number of objects: %d' %len(ifaceobjdict)
-
diff --git a/tests/test_ifupdown2.py b/tests/test_ifupdown2.py
new file mode 100644 (file)
index 0000000..643dc54
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""Tests for `ifupdown2` package."""
+
+import pytest
+
+
+@pytest.fixture
+def response():
+    """Sample pytest fixture.
+
+    See more at: http://doc.pytest.org/en/latest/fixture.html
+    """
+    # import requests
+    # return requests.get('https://github.com/audreyr/cookiecutter-pypackage')
+
+
+def test_content(response):
+    """Sample pytest test function with the pytest fixture as an argument."""
+    # from bs4 import BeautifulSoup
+    # assert 'GitHub' in BeautifulSoup(response.content).title.string