New ML2/L3 plugin to support SFC 61/2961/1
authorAshlee Young <ashlee@onosfw.com>
Fri, 30 Oct 2015 16:20:45 +0000 (09:20 -0700)
committerAshlee Young <ashlee@onosfw.com>
Fri, 30 Oct 2015 16:20:45 +0000 (09:20 -0700)
Change-Id: Ie778a2b2e09a29972e28d70c8eedee407b1d8eb6
Signed-off-by: Ashlee Young <ashlee@onosfw.com>
53 files changed:
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/.mailmap [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/.pylintrc [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/.testr.conf [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/CONTRIBUTING.rst [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/HACKING.rst [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/LICENSE [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/MANIFEST.in [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/README.rst [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/TESTING.rst [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/babel.cfg [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/doc/source/conf.py [new file with mode: 0755]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/doc/source/contributing.rst [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/doc/source/index.rst [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/doc/source/installation.rst [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/doc/source/readme.rst [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/doc/source/usage.rst [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/etc/conf_onos.ini [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/__init__.py [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/common/__init__.py [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/common/config.py [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/common/utils.py [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/__init__.py [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/l3/README [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/l3/__init__.py [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/l3/driver.py [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/l3/floating_ip.py [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/l3/router.py [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/ml2/README [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/ml2/__init__.py [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/ml2/driver.py [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/__init__.py [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/unit/__init__.py [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/unit/plugins/__init__.py [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/unit/plugins/l3/__init__.py [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/unit/plugins/l3/test_driver.py [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/unit/plugins/ml2/__init__.py [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/unit/plugins/ml2/test_driver.py [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/openstack-common.conf [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/requirements.txt [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/setup.cfg [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/setup.py [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/test-requirements.txt [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/check_bash.sh [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/check_i18n.py [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/check_i18n_test_case.txt [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/clean.sh [new file with mode: 0755]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/i18n_cfg.py [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/install_venv.py [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/install_venv_common.py [new file with mode: 0644]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/pretty_tox.sh [new file with mode: 0755]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/subunit-trace.py [new file with mode: 0755]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/with_venv.sh [new file with mode: 0755]
framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tox.ini [new file with mode: 0644]

diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/.mailmap b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/.mailmap
new file mode 100644 (file)
index 0000000..7b5fe1b
--- /dev/null
@@ -0,0 +1,4 @@
+# Format is:
+# <preferred e-mail> <other e-mail 1>
+# <preferred e-mail> <other e-mail 2>
+Vikram Choudhary <vikram.choudhary@huawei.com> <vikschw@gmail.com>
\ No newline at end of file
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/.pylintrc b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/.pylintrc
new file mode 100644 (file)
index 0000000..d343d72
--- /dev/null
@@ -0,0 +1,130 @@
+# The format of this file isn't really documented; just use --generate-rcfile
+[MASTER]
+# Add <file or directory> to the black list. It should be a base name, not a
+# path. You may set this option multiple times.
+#
+# Note the 'openstack' below is intended to match only
+# neutron.openstack.common.  If we ever have another 'openstack'
+# dirname, then we'll need to expand the ignore features in pylint :/
+ignore=.git,tests,openstack
+
+[MESSAGES CONTROL]
+# NOTE(gus): This is a long list.  A number of these are important and
+# should be re-enabled once the offending code is fixed (or marked
+# with a local disable)
+disable=
+# "F" Fatal errors that prevent further processing
+ import-error,
+# "I" Informational noise
+ locally-disabled,
+# "E" Error for important programming issues (likely bugs)
+ access-member-before-definition,
+ bad-super-call,
+ maybe-no-member,
+ no-member,
+ no-method-argument,
+ no-self-argument,
+ not-callable,
+ no-value-for-parameter,
+ super-on-old-class,
+ too-few-format-args,
+# "W" Warnings for stylistic problems or minor programming issues
+ abstract-method,
+ anomalous-backslash-in-string,
+ anomalous-unicode-escape-in-string,
+ arguments-differ,
+ attribute-defined-outside-init,
+ bad-builtin,
+ bad-indentation,
+ broad-except,
+ dangerous-default-value,
+ deprecated-lambda,
+ duplicate-key,
+ expression-not-assigned,
+ fixme,
+ global-statement,
+ global-variable-not-assigned,
+ logging-not-lazy,
+ no-init,
+ non-parent-init-called,
+ protected-access,
+ redefined-builtin,
+ redefined-outer-name,
+ redefine-in-handler,
+ signature-differs,
+ star-args,
+ super-init-not-called,
+ unnecessary-lambda,
+ unnecessary-pass,
+ unpacking-non-sequence,
+ unreachable,
+ unused-argument,
+ unused-import,
+ unused-variable,
+# "C" Coding convention violations
+ bad-continuation,
+ invalid-name,
+ missing-docstring,
+ old-style-class,
+ superfluous-parens,
+# "R" Refactor recommendations
+ abstract-class-little-used,
+ abstract-class-not-used,
+ duplicate-code,
+ interface-not-implemented,
+ no-self-use,
+ too-few-public-methods,
+ too-many-ancestors,
+ too-many-arguments,
+ too-many-branches,
+ too-many-instance-attributes,
+ too-many-lines,
+ too-many-locals,
+ too-many-public-methods,
+ too-many-return-statements,
+ too-many-statements
+
+[BASIC]
+# Variable names can be 1 to 31 characters long, with lowercase and underscores
+variable-rgx=[a-z_][a-z0-9_]{0,30}$
+
+# Argument names can be 2 to 31 characters long, with lowercase and underscores
+argument-rgx=[a-z_][a-z0-9_]{1,30}$
+
+# Method names should be at least 3 characters long
+# and be lowecased with underscores
+method-rgx=([a-z_][a-z0-9_]{2,}|setUp|tearDown)$
+
+# Module names matching neutron-* are ok (files in bin/)
+module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+)|(neutron-[a-z0-9_-]+))$
+
+# Don't require docstrings on tests.
+no-docstring-rgx=((__.*__)|([tT]est.*)|setUp|tearDown)$
+
+[FORMAT]
+# Maximum number of characters on a single line.
+max-line-length=79
+
+[VARIABLES]
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid to define new builtins when possible.
+# _ is used by our localization
+additional-builtins=_
+
+[CLASSES]
+# List of interface methods to ignore, separated by a comma.
+ignore-iface-methods=
+
+[IMPORTS]
+# Deprecated modules which should not be used, separated by a comma
+deprecated-modules=
+# should use openstack.common.jsonutils
+ json
+
+[TYPECHECK]
+# List of module names for which member attributes should not be checked
+ignored-modules=six.moves,_MovedItems
+
+[REPORTS]
+# Tells whether to display a full report or only the messages
+reports=no
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/.testr.conf b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/.testr.conf
new file mode 100644 (file)
index 0000000..4b24f61
--- /dev/null
@@ -0,0 +1,8 @@
+[DEFAULT]
+test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
+             OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
+             OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \
+             OS_LOG_CAPTURE=1 \
+             ${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION
+test_id_option=--load-list $IDFILE
+test_list_option=--list
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/CONTRIBUTING.rst b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/CONTRIBUTING.rst
new file mode 100644 (file)
index 0000000..44cb395
--- /dev/null
@@ -0,0 +1,16 @@
+If you would like to contribute to the development of OpenStack,
+you must follow the steps documented at:
+
+   http://docs.openstack.org/infra/manual/developers.html#development-workflow
+
+Once those steps have been completed, changes to OpenStack
+should be submitted for review via the Gerrit tool, following
+the workflow documented at:
+
+   http://docs.openstack.org/infra/manual/developers.html#development-workflow
+
+Pull requests submitted through GitHub will be ignored.
+
+Bugs should be filed on Launchpad, not GitHub:
+
+   https://bugs.launchpad.net/networking-onos
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/HACKING.rst b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/HACKING.rst
new file mode 100644 (file)
index 0000000..06d3f2c
--- /dev/null
@@ -0,0 +1,33 @@
+Neutron Style Commandments
+==========================
+
+- Step 1: Read the OpenStack Style Commandments
+  http://docs.openstack.org/developer/hacking/
+- Step 2: Read on
+
+Neutron Specific Commandments
+--------------------------
+
+- [N319] Validate that debug level logs are not translated
+- [N320] Validate that LOG messages, except debug ones, have translations
+- [N321] Validate that jsonutils module is used instead of json
+- [N322] We do not use @authors tags in source files. We have git to track
+  authorship.
+- [N323] Detect common errors with assert_called_once_with
+
+Creating Unit Tests
+-------------------
+For every new feature, unit tests should be created that both test and
+(implicitly) document the usage of said feature. If submitting a patch for a
+bug that had no unit test, a new passing unit test should be added. If a
+submitted bug fix does have a unit test, be sure to add a new one that fails
+without the patch and passes with the patch.
+
+All unittest classes must ultimately inherit from testtools.TestCase. In the
+Neutron test suite, this should be done by inheriting from
+neutron.tests.base.BaseTestCase.
+
+All setUp and tearDown methods must upcall using the super() method.
+tearDown methods should be avoided and addCleanup calls should be preferred.
+Never manually create tempfiles. Always use the tempfile fixtures from
+the fixture library to ensure that they are cleaned up.
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/LICENSE b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/LICENSE
new file mode 100644 (file)
index 0000000..68c771a
--- /dev/null
@@ -0,0 +1,176 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/MANIFEST.in b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/MANIFEST.in
new file mode 100644 (file)
index 0000000..f1c38fb
--- /dev/null
@@ -0,0 +1,9 @@
+include AUTHORS
+include README.rst
+include ChangeLog
+include LICENSE
+
+exclude .gitignore
+exclude .gitreview
+
+global-exclude *.pyc
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/README.rst b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/README.rst
new file mode 100644 (file)
index 0000000..2a9d329
--- /dev/null
@@ -0,0 +1,20 @@
+Word about ONOS
+===============
+
+Open Networking Operating System (ONOS) is a new carrier-grade SDN network
+operating system designed for high availability, performance and scale-out with
+a mission "to produce the Open Source Network Operating System that will enable
+service providers to build real Software Defined Network".
+
+Word about networking-onos
+==========================
+The "networking-onos" repository contains code which makes the interaction
+between ONOS and OpenStack Neutron possible. For more information about ONOS
+you can visit  "http://onosproject.org/" and
+"https://launchpad.net/neutron" for OpenStack Neutron.
+
+Important Pointers
+==================
+* You can visit the launchpad page "https://launchpad.net/networking-onos" to get latest project status.
+* For any issues or new requirement raise a bug at “https://bugs.launchpad.net/networking-onos”. We will get back.
+* Any contribution is appreciated. Let's start contributing.
\ No newline at end of file
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/TESTING.rst b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/TESTING.rst
new file mode 100644 (file)
index 0000000..657fb41
--- /dev/null
@@ -0,0 +1,160 @@
+Testing Neutron
+===============
+
+Overview
+--------
+
+The unit tests (neutron/test/unit/) are meant to cover as much code as
+possible and should be executed without the service running. They are
+designed to test the various pieces of the neutron tree to make sure
+any new changes don't break existing functionality.
+
+The functional tests (neutron/tests/functional/) are intended to
+validate actual system interaction.  Mocks should be used sparingly,
+if at all.  Care should be taken to ensure that existing system
+resources are not modified and that resources created in tests are
+properly cleaned up.
+
+Development process
+-------------------
+
+It is expected that any new changes that are proposed for merge
+come with tests for that feature or code area. Ideally any bugs
+fixes that are submitted also have tests to prove that they stay
+fixed!  In addition, before proposing for merge, all of the
+current tests should be passing.
+
+Virtual environments
+~~~~~~~~~~~~~~~~~~~~
+
+Testing OpenStack projects, including Neutron, is made easier with `DevStack <https://git.openstack.org/cgit/openstack-dev/devstack>`_.
+
+Create a machine (such as a VM or Vagrant box) running a distribution supported
+by DevStack and install DevStack there. For example, there is a Vagrant script
+for DevStack at https://github.com/bcwaldon/vagrant_devstack.
+
+ .. note::
+
+    If you prefer not to use DevStack, you can still check out source code on your local
+    machine and develop from there.
+
+
+Running unit tests
+------------------
+
+There are two mechanisms for running tests: tox, and nose. Before submitting
+a patch for review you should always ensure all test pass; a tox run is
+triggered by the jenkins gate executed on gerrit for each patch pushed for
+review.
+
+With these mechanisms you can either run the tests in the standard
+environment or create a virtual environment to run them in.
+
+By default after running all of the tests, any pep8 errors
+found in the tree will be reported.
+
+
+With `nose`
+~~~~~~~~~~~
+
+You can use `nose`_ to run individual tests, as well as use for debugging
+portions of your code::
+
+    source .venv/bin/activate
+    pip install nose
+    nosetests
+
+There are disadvantages to running Nose - the tests are run sequentially, so
+race condition bugs will not be triggered, and the full test suite will
+take significantly longer than tox & testr. The upside is that testr has
+some rough edges when it comes to diagnosing errors and failures, and there is
+no easy way to set a breakpoint in the Neutron code, and enter an
+interactive debugging session while using testr.
+
+.. _nose: https://nose.readthedocs.org/en/latest/index.html
+
+With `tox`
+~~~~~~~~~~
+
+Neutron, like other OpenStack projects, uses `tox`_ for managing the virtual
+environments for running test cases. It uses `Testr`_ for managing the running
+of the test cases.
+
+Tox handles the creation of a series of `virtualenvs`_ that target specific
+versions of Python (2.6, 2.7, 3.3, etc).
+
+Testr handles the parallel execution of series of test cases as well as
+the tracking of long-running tests and other things.
+
+Running unit tests is as easy as executing this in the root directory of the
+Neutron source code::
+
+    tox
+
+For more information on the standard Tox-based test infrastructure used by
+OpenStack and how to do some common test/debugging procedures with Testr,
+see this wiki page:
+
+  https://wiki.openstack.org/wiki/Testr
+
+.. _Testr: https://wiki.openstack.org/wiki/Testr
+.. _tox: http://tox.readthedocs.org/en/latest/
+.. _virtualenvs: https://pypi.python.org/pypi/virtualenv
+
+
+Running individual tests
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+For running individual test modules or cases, you just need to pass
+the dot-separated path to the module you want as an argument to it.
+
+For executing a specific test case, specify the name of the test case
+class separating it from the module path with a colon.
+
+For example, the following would run only the JSONV2TestCase tests from
+neutron/tests/unit/test_api_v2.py::
+
+      $ tox -e py27 neutron.tests.unit.test_api_v2.JSONV2TestCase
+
+Adding more tests
+~~~~~~~~~~~~~~~~~
+
+Neutron has a fast growing code base and there is plenty of areas that
+need to be covered by unit and functional tests.
+
+To get a grasp of the areas where tests are needed, you can check
+current coverage by running::
+
+    $ tox -ecover
+
+Debugging
+---------
+
+It's possible to debug tests in a tox environment::
+
+    $ tox -e venv -- python -m testtools.run [test module path]
+
+Tox-created virtual environments (venv's) can also be activated
+after a tox run and reused for debugging::
+
+    $ tox -e venv
+    $ . .tox/venv/bin/activate
+    $ python -m testtools.run [test module path]
+
+Tox packages and installs the neutron source tree in a given venv
+on every invocation, but if modifications need to be made between
+invocation (e.g. adding more pdb statements), it is recommended
+that the source tree be installed in the venv in editable mode::
+
+    # run this only after activating the venv
+    $ pip install --editable .
+
+Editable mode ensures that changes made to the source tree are
+automatically reflected in the venv, and that such changes are not
+overwritten during the next tox run.
+
+References
+==========
+
+.. [#pudb] PUDB debugger:
+   https://pypi.python.org/pypi/pudb
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/babel.cfg b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/babel.cfg
new file mode 100644 (file)
index 0000000..15cd6cb
--- /dev/null
@@ -0,0 +1,2 @@
+[python: **.py]
+
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/doc/source/conf.py b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/doc/source/conf.py
new file mode 100755 (executable)
index 0000000..8519f06
--- /dev/null
@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import sys
+
+sys.path.insert(0, os.path.abspath('../..'))
+# -- General configuration ----------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = [
+    'sphinx.ext.autodoc',
+    #'sphinx.ext.intersphinx',
+    'oslosphinx'
+]
+
+# autodoc generation is a bit aggressive and a nuisance when doing heavy
+# text edit cycles.
+# execute "export SPHINX_DEBUG=1" in your terminal to disable
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'networking-onos'
+copyright = u'2013, OpenStack Foundation'
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+add_module_names = True
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# -- Options for HTML output --------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  Major themes that come with
+# Sphinx are currently 'default' and 'sphinxdoc'.
+# html_theme_path = ["."]
+# html_theme = '_theme'
+# html_static_path = ['static']
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = '%sdoc' % project
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass
+# [howto/manual]).
+latex_documents = [
+    ('index',
+     '%s.tex' % project,
+     u'%s Documentation' % project,
+     u'OpenStack Foundation', 'manual'),
+]
+
+# Example configuration for intersphinx: refer to the Python standard library.
+#intersphinx_mapping = {'http://docs.python.org/': None}
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/doc/source/contributing.rst b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/doc/source/contributing.rst
new file mode 100644 (file)
index 0000000..1728a61
--- /dev/null
@@ -0,0 +1,4 @@
+============
+Contributing
+============
+.. include:: ../../CONTRIBUTING.rst
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/doc/source/index.rst b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/doc/source/index.rst
new file mode 100644 (file)
index 0000000..8b8ac26
--- /dev/null
@@ -0,0 +1,25 @@
+.. networking-onos documentation master file, created by
+   sphinx-quickstart on Tue Jul  9 22:26:36 2013.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+Welcome to networking-onos's documentation!
+========================================================
+
+Contents:
+
+.. toctree::
+   :maxdepth: 2
+
+   readme
+   installation
+   usage
+   contributing
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/doc/source/installation.rst b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/doc/source/installation.rst
new file mode 100644 (file)
index 0000000..2228fde
--- /dev/null
@@ -0,0 +1,12 @@
+============
+Installation
+============
+
+At the command line::
+
+    $ pip install networking-onos
+
+Or, if you have virtualenvwrapper installed::
+
+    $ mkvirtualenv networking-onos
+    $ pip install networking-onos
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/doc/source/readme.rst b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/doc/source/readme.rst
new file mode 100644 (file)
index 0000000..a6210d3
--- /dev/null
@@ -0,0 +1 @@
+.. include:: ../../README.rst
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/doc/source/usage.rst b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/doc/source/usage.rst
new file mode 100644 (file)
index 0000000..fdc232a
--- /dev/null
@@ -0,0 +1,7 @@
+========
+Usage
+========
+
+To use networking-onos in a project::
+
+    import networking-onos
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/etc/conf_onos.ini b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/etc/conf_onos.ini
new file mode 100644 (file)
index 0000000..45ad6ac
--- /dev/null
@@ -0,0 +1,11 @@
+#Configuration options for ONOS driver
+
+[onos]
+# (StrOpt) ONOS ReST interface URL. This is a mandatory field.
+# url_path =
+
+# (StrOpt) Username for authentication. This is a mandatory field.
+# username =
+
+# (StrOpt) Password for authentication. This is a mandatory field.
+# password =
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/__init__.py b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/__init__.py
new file mode 100644 (file)
index 0000000..d0b3610
--- /dev/null
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import pbr.version
+
+
+__version__ = pbr.version.VersionInfo(
+    'networking-onos').version_string()
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/common/__init__.py b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/common/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/common/config.py b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/common/config.py
new file mode 100644 (file)
index 0000000..8dc7228
--- /dev/null
@@ -0,0 +1,31 @@
+# Copyright (c) 2015 Huawei Technologies India Pvt Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_config import cfg
+
+ONOS_DRIVER_OPTS = [
+    cfg.StrOpt('url_path',
+               default='',
+               help=_('ONOS ReST interface URL')),
+    cfg.StrOpt('username',
+               default='',
+               help=_('Username for authentication.')),
+    cfg.StrOpt('password',
+               default='',
+               secret=True,  # do not expose value in the logs
+               help=_('Password for authentication.'))
+]
+
+cfg.CONF.register_opts(ONOS_DRIVER_OPTS, "onos")
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/common/utils.py b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/common/utils.py
new file mode 100644 (file)
index 0000000..4947714
--- /dev/null
@@ -0,0 +1,44 @@
+# Copyright (c) 2015 Huawei Technologies India Pvt Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_log import log as logging
+from oslo_serialization import jsonutils
+import requests
+
+
+LOG = logging.getLogger(__name__)
+
+
+def send_msg(onos_path, onos_auth, msg_type, entity_path, entity=None):
+    """Send message to the ONOS controller."""
+
+    path = '/'.join([onos_path, entity_path])
+    hdr = {'Content-Type': 'application/json'}
+    body = jsonutils.dumps(entity, indent=2) if entity else None
+    LOG.debug("Sending MSG_TYPE (%(msg)s) URL (%(path)s) "
+              "OBJECT (%(entity)s) BODY (%(body)s)",
+              {'msg': msg_type, 'path': path,
+               'entity': entity, 'body': body})
+    req = requests.request(method=msg_type, url=path,
+                           headers=hdr, data=body,
+                           auth=onos_auth)
+    # Let's raise voice for an error
+    req.raise_for_status()
+
+
+def safe_delete_from_dict(dict, keys):
+    """Ignore key errors when deleting from a dictionary."""
+    for key in keys:
+        dict.pop(key, None)
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/__init__.py b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/l3/README b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/l3/README
new file mode 100644 (file)
index 0000000..04ca224
--- /dev/null
@@ -0,0 +1,29 @@
+Open Networking Operating System (ONOS) L3 Plugin
+=================================================
+ONOS is a carrier grade SDN open operating system designed for
+High Availability, scale-out and better performance.
+
+    http://www.onosproject.org/
+
+Mode of Working:
+================
+networking-onos.plugins.l3 define onos plug-in for supporting neutron's router
+functionality. This shim layer makes the communication between ONOS and
+Openstack neutron possible via ReST call.
+The driver code can be downloaded from:
+
+    https://git.openstack.org/cgit/openstack/networking-onos
+
+Using ONOS L3 Plugin
+====================
+To use ONOS L3 Plugin one should
+1. Make sure networking-onos code is downloaded and installed. If doing
+   mannually then download the code, go inside networking_onos folder
+   and finally run "sudo python setup.py install" otherwise download the
+   required package version from "https://pypi.python.org/pypi/networking-onos/"
+   and install using pip.
+
+2. Configure ONOS credentials in networking_onos/etc/conf_onos.ini.
+
+3. Start neutron server mentioning networking_onos/etc/conf_onos.ini as
+   one of the config-file with ONOS L3 Plugin support.
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/l3/__init__.py b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/l3/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/l3/driver.py b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/l3/driver.py
new file mode 100644 (file)
index 0000000..2db3ad3
--- /dev/null
@@ -0,0 +1,126 @@
+# Copyright (C) 2015 Huawei Technologies India Pvt Ltd.
+# All Rights Reserved.
+#
+#  Licensed under the Apache License, Version 2.0 (the "License"); you may
+#  not use this file except in compliance with the License. You may obtain
+#  a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#  License for the specific language governing permissions and limitations
+#  under the License.
+#
+
+from oslo_config import cfg
+
+from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
+from neutron.api.rpc.handlers import l3_rpc
+from neutron.common import constants as q_const
+from neutron.common import rpc as n_rpc
+from neutron.common import topics
+from neutron.db import db_base_plugin_v2
+from neutron.db import extraroute_db
+from neutron.db import l3_agentschedulers_db
+from neutron.db import l3_gwmode_db
+from neutron.plugins.common import constants
+
+from networking_onos.common import config   # noqa
+from networking_onos.plugins.l3 import floating_ip as onos_fip
+from networking_onos.plugins.l3 import router as onos_router
+
+
+class ONOSL3Plugin(db_base_plugin_v2.NeutronDbPluginV2,
+                   extraroute_db.ExtraRoute_db_mixin,
+                   l3_gwmode_db.L3_NAT_db_mixin,
+                   l3_agentschedulers_db.L3AgentSchedulerDbMixin,
+                   onos_router.ONOSRouter,
+                   onos_fip.ONOSFloatingIP):
+
+    """Implementation of the ONOS L3 Router Service Plugin.
+
+    This class implements a L3 service plugin that provides
+    router and floatingip resources and manages associated
+    request/response.
+    """
+    supported_extension_aliases = ["router", "ext-gw-mode", "extraroute"]
+
+    def __init__(self):
+        self.setup_rpc()
+        self.onos_path = cfg.CONF.onos.url_path
+        self.onos_auth = (cfg.CONF.onos.username, cfg.CONF.onos.password)
+
+    def setup_rpc(self):
+        self.topic = topics.L3PLUGIN
+        self.conn = n_rpc.create_connection(new=True)
+        self.agent_notifiers.update(
+            {q_const.AGENT_TYPE_L3: l3_rpc_agent_api.L3AgentNotifyAPI()})
+        self.endpoints = [l3_rpc.L3RpcCallback()]
+        self.conn.create_consumer(self.topic, self.endpoints, fanout=False)
+        self.conn.consume_in_threads()
+
+    def get_plugin_type(self):
+        return constants.L3_ROUTER_NAT
+
+    def get_plugin_description(self):
+        """returns plug-in description"""
+        return ("L3 Router Service Plug-in for basic L3 forwarding using ONOS")
+
+    def create_router(self, context, router):
+        router_dict = super(ONOSL3Plugin, self).create_router(context, router)
+        self.handle_create_router(router_dict)
+        return router_dict
+
+    def update_router(self, context, id, router):
+        router_dict = super(ONOSL3Plugin, self).update_router(context, id,
+                                                              router)
+        self.handle_update_router(router_dict, id)
+        return router_dict
+
+    def delete_router(self, context, id):
+        super(ONOSL3Plugin, self).delete_router(context, id)
+        self.handle_delete_router(id)
+
+    def create_floatingip(self, context, floatingip,
+                          initial_status=q_const.FLOATINGIP_STATUS_ACTIVE):
+        fip_dict = super(ONOSL3Plugin, self).create_floatingip(context,
+                                                               floatingip,
+                                                               initial_status)
+        self.handle_create_floatingip(fip_dict)
+        return fip_dict
+
+    def update_floatingip(self, context, id, floatingip):
+        fip_dict = super(ONOSL3Plugin, self).update_floatingip(context, id,
+                                                               floatingip)
+        self.handle_update_floatingip(id, fip_dict)
+        return fip_dict
+
+    def delete_floatingip(self, context, id):
+        super(ONOSL3Plugin, self).delete_floatingip(context, id)
+        self.handle_delete_floatingip(id)
+
+    def add_router_interface(self, context, router_id, interface_info):
+        router = super(ONOSL3Plugin, self).add_router_interface(context,
+                                                                router_id,
+                                                                interface_info)
+        intf_add_type = self._get_intf_add_type(router, interface_info)
+        self.handle_add_router_interface(router, router_id,
+                                         interface_info, intf_add_type)
+        return router
+
+    def remove_router_interface(self, context, router_id, interface_info):
+        router = super(ONOSL3Plugin, self).remove_router_interface(
+            context, router_id, interface_info)
+        intf_add_type = self._get_intf_add_type(router, interface_info)
+        self.handle_remove_router_interface(router, router_id,
+                                            interface_info, intf_add_type)
+        return router
+
+    def _get_intf_add_type(self, router_info, intf_info):
+        add_by_port, add_by_sub = self._validate_interface_info(intf_info)
+        if add_by_sub:
+            return onos_router.ADD_INTF_BY_SUBNET
+
+        return onos_router.ADD_INTF_BY_PORT
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/l3/floating_ip.py b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/l3/floating_ip.py
new file mode 100644 (file)
index 0000000..0748724
--- /dev/null
@@ -0,0 +1,40 @@
+# Copyright (C) 2015 Huawei Technologies India Pvt Ltd.
+#
+#  Licensed under the Apache License, Version 2.0 (the "License"); you may
+#  not use this file except in compliance with the License. You may obtain
+#  a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#  License for the specific language governing permissions and limitations
+#  under the License.
+#
+
+from networking_onos.common import utils as onos_utils
+
+
+class ONOSFloatingIP(object):
+
+    """Implementation of ONOS L3 Floating IP Service.
+
+    This class sends Neutron's L3 Floating IP messages to ONOS.
+    """
+    def send_floatingip_msg(self, msg_type, entity_path, entity):
+        onos_utils.send_msg(self.onos_path, self.onos_auth,
+                            msg_type, entity_path, entity)
+
+    def handle_create_floatingip(self, fip_dict):
+        self.send_floatingip_msg('post', 'floatingips',
+                                 {'floatingip': fip_dict})
+
+    def handle_update_floatingip(self, id, fip_dict):
+        url_path = 'floatingips' + '/' + id
+        self.send_floatingip_msg('put', url_path,
+                                 {'floatingip': fip_dict})
+
+    def handle_delete_floatingip(self, id):
+        url_path = 'floatingips' + '/' + id
+        self.send_floatingip_msg('delete', url_path, None)
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/l3/router.py b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/l3/router.py
new file mode 100644 (file)
index 0000000..495dacd
--- /dev/null
@@ -0,0 +1,74 @@
+# Copyright (C) 2015 Huawei Technologies India Pvt Ltd.
+#
+#  Licensed under the Apache License, Version 2.0 (the "License"); you may
+#  not use this file except in compliance with the License. You may obtain
+#  a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#  License for the specific language governing permissions and limitations
+#  under the License.
+#
+
+from networking_onos.common import utils as onos_utils
+
+ADD_INTF_BY_PORT = 1
+ADD_INTF_BY_SUBNET = 2
+
+
+class ONOSRouter(object):
+
+    """Implementation of ONOS L3 Router Service.
+
+    This class sends Neutron's L3 router messages to ONOS.
+    """
+    def send_router_msg(self, msg_type, entity_path, entity):
+        onos_utils.send_msg(self.onos_path, self.onos_auth,
+                            msg_type, entity_path, entity)
+
+    def handle_create_router(self, router_dict):
+        self.send_router_msg('post', 'routers',
+                             {'router': router_dict})
+
+    def handle_update_router(self, router_dict, id):
+        url_path = 'routers' + '/' + id
+        resource = router_dict.copy()
+        onos_utils.safe_delete_from_dict(resource,
+                                         ['id', 'tenant_id', 'status'])
+        self.send_router_msg('put', url_path, {'router': resource})
+
+    def handle_delete_router(self, id):
+        url_path = 'routers' + '/' + id
+        self.send_router_msg('delete', url_path, None)
+
+    def handle_add_router_interface(self, new_router, router_id,
+                                    interface_info, intf_add_type):
+        url_path = 'routers' + '/' + router_id + '/add_router_interface'
+        router_dict = self._prepare_router_dict(router_id, interface_info,
+                                                new_router, intf_add_type)
+        self.send_router_msg('put', url_path, router_dict)
+
+    def handle_remove_router_interface(self, new_router, router_id,
+                                       interface_info, intf_add_type):
+        url_path = 'routers' + '/' + router_id + '/remove_router_interface'
+        router_dict = self._prepare_router_dict(router_id, interface_info,
+                                                new_router, intf_add_type)
+        self.send_router_msg('put', url_path, router_dict)
+
+    def _prepare_router_dict(self, router_id, interface_info,
+                             new_router, add_type):
+        if add_type == ADD_INTF_BY_SUBNET:
+            _port_id = new_router['port_id']
+            _subnet_id = interface_info['subnet_id']
+        else:
+            _port_id = interface_info['port_id']
+            _subnet_id = new_router['subnet_id']
+
+        router_dict = {'subnet_id': _subnet_id,
+                       'port_id': _port_id,
+                       'id': router_id,
+                       'tenant_id': new_router['tenant_id']}
+        return router_dict
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/ml2/README b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/ml2/README
new file mode 100644 (file)
index 0000000..c3c722c
--- /dev/null
@@ -0,0 +1,33 @@
+Open Networking Operating System (ONOS) ML2 MechanismDriver
+==========================================================
+ONOS is a carrier grade SDN open operating system designed for
+High Availability, scale-out and better performance.
+
+    http://www.onosproject.org/
+
+Mode of Working:
+================
+The networking-onos project provides a thin layer which makes the
+communication between ONOS and OpenStack neutron possible via ReST
+call. The driver code can be downloaded from:
+
+    https://git.openstack.org/cgit/openstack/networking-onos
+
+Using ONOS ML2 MechanismDriver
+==============================
+To use ONOS ML2 MechanismDriver one should
+1. Make sure networking-onos code is downloaded and installed. If doing
+   mannually then download the code, go inside networking_onos folder
+   and finally run "sudo python setup.py install" otherwise download the
+   required package version from "https://pypi.python.org/pypi/networking-onos/"
+   and install using pip.
+
+2. Configure ONOS as the required ML2 "mechanism_drivers" in
+   neutron/plugins/ml2/ml2_conf.ini:
+
+    mechanism_drivers=onos_ml2
+
+3. Configure ONOS credentials in networking_onos/etc/conf_onos.ini.
+
+4. Start neutron server mentioning networking_onos/etc/conf_onos.ini as
+   one of the config-file.
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/ml2/__init__.py b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/ml2/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/ml2/driver.py b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/ml2/driver.py
new file mode 100644 (file)
index 0000000..b78775f
--- /dev/null
@@ -0,0 +1,140 @@
+# Copyright (c) 2015 Huawei Technologies India Pvt Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+from oslo_config import cfg
+from oslo_log import helpers as log_helpers
+from oslo_log import log as logging
+
+from neutron.common import constants as n_const
+from neutron.extensions import portbindings
+from neutron.plugins.common import constants
+from neutron.plugins.ml2 import driver_api as api
+
+from networking_onos.common import config  # noqa
+from networking_onos.common import utils as onos_utils
+
+LOG = logging.getLogger(__name__)
+
+
+class ONOSMechanismDriver(api.MechanismDriver):
+
+    """Open Networking Operating System ML2 Driver for Neutron.
+
+    Code which makes communication between ONOS and OpenStack Neutron
+    possible.
+    """
+    def __init__(self):
+        self.onos_path = cfg.CONF.onos.url_path
+        self.onos_auth = (cfg.CONF.onos.username, cfg.CONF.onos.password)
+        self.vif_type = portbindings.VIF_TYPE_OVS
+        self.vif_details = {portbindings.CAP_PORT_FILTER: True}
+
+    def initialize(self):
+        # No action required as of now. Can be extended in
+        # the future if required.
+        pass
+
+    @log_helpers.log_method_call
+    def create_network_postcommit(self, context):
+        entity_path = 'networks'
+        resource = context.current.copy()
+        onos_utils.send_msg(self.onos_path, self.onos_auth, 'post',
+                            entity_path, {'network': resource})
+
+    @log_helpers.log_method_call
+    def update_network_postcommit(self, context):
+        entity_path = 'networks/' + context.current['id']
+        resource = context.current.copy()
+        onos_utils.send_msg(self.onos_path, self.onos_auth, 'put',
+                            entity_path, {'network': resource})
+
+    @log_helpers.log_method_call
+    def delete_network_postcommit(self, context):
+        entity_path = 'networks/' + context.current['id']
+        onos_utils.send_msg(self.onos_path, self.onos_auth, 'delete',
+                            entity_path)
+
+    @log_helpers.log_method_call
+    def create_subnet_postcommit(self, context):
+        entity_path = 'subnets'
+        resource = context.current.copy()
+        onos_utils.send_msg(self.onos_path, self.onos_auth, 'post',
+                            entity_path, {'subnet': resource})
+
+    @log_helpers.log_method_call
+    def update_subnet_postcommit(self, context):
+        entity_path = 'subnets/' + context.current['id']
+        resource = context.current.copy()
+        onos_utils.send_msg(self.onos_path, self.onos_auth, 'put',
+                            entity_path, {'subnet': resource})
+
+    @log_helpers.log_method_call
+    def delete_subnet_postcommit(self, context):
+        entity_path = 'subnets/' + context.current['id']
+        onos_utils.send_msg(self.onos_path, self.onos_auth, 'delete',
+                            entity_path)
+
+    @log_helpers.log_method_call
+    def create_port_postcommit(self, context):
+        entity_path = 'ports'
+        resource = context.current.copy()
+        onos_utils.send_msg(self.onos_path, self.onos_auth, 'post',
+                            entity_path, {'port': resource})
+
+    @log_helpers.log_method_call
+    def update_port_postcommit(self, context):
+        entity_path = 'ports/' + context.current['id']
+        resource = context.current.copy()
+        onos_utils.send_msg(self.onos_path, self.onos_auth, 'put',
+                            entity_path, {'port': resource})
+
+    @log_helpers.log_method_call
+    def delete_port_postcommit(self, context):
+        entity_path = 'ports/' + context.current['id']
+        onos_utils.send_msg(self.onos_path, self.onos_auth, 'delete',
+                            entity_path)
+
+    @log_helpers.log_method_call
+    def bind_port(self, context):
+        """Set port binding data for use with nova."""
+        LOG.debug("Attempting to bind port %(port)s on network %(network)s",
+                  {'port': context.current['id'],
+                   'network': context.network.current['id']})
+        # Prepared porting binding data
+        for segment in context.segments_to_bind:
+            if self.check_segment(segment):
+                context.set_binding(segment[api.ID],
+                                    self.vif_type,
+                                    self.vif_details,
+                                    status=n_const.PORT_STATUS_ACTIVE)
+                LOG.debug("Port bound successful for segment: %s", segment)
+                return
+            else:
+                LOG.debug("Port bound un-successfult for segment ID %(id)s, "
+                          "segment %(seg)s, phys net %(physnet)s, and "
+                          "network type %(nettype)s",
+                          {'id': segment[api.ID],
+                           'seg': segment[api.SEGMENTATION_ID],
+                           'physnet': segment[api.PHYSICAL_NETWORK],
+                           'nettype': segment[api.NETWORK_TYPE]})
+
+    @log_helpers.log_method_call
+    def check_segment(self, segment):
+        """Check whether segment is valid for the ONOS MechanismDriver."""
+
+        return segment[api.NETWORK_TYPE] in [constants.TYPE_LOCAL,
+                                             constants.TYPE_GRE,
+                                             constants.TYPE_VXLAN,
+                                             constants.TYPE_VLAN]
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/__init__.py b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/unit/__init__.py b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/unit/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/unit/plugins/__init__.py b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/unit/plugins/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/unit/plugins/l3/__init__.py b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/unit/plugins/l3/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/unit/plugins/l3/test_driver.py b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/unit/plugins/l3/test_driver.py
new file mode 100644 (file)
index 0000000..c0bba85
--- /dev/null
@@ -0,0 +1,230 @@
+# Copyright (C) 2015 Huawei Technologies India Pvt Ltd.
+# All Rights Reserved.
+#
+#  Licensed under the Apache License, Version 2.0 (the "License"); you may
+#  not use this file except in compliance with the License. You may obtain
+#  a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#  License for the specific language governing permissions and limitations
+#  under the License.
+#
+
+import copy
+import mock
+
+from oslotest import base
+
+from neutron.extensions import l3
+from neutron.tests.unit.api.v2 import test_base
+from neutron.tests.unit.extensions import base as test_neutron_extensions
+from webob import exc
+
+import networking_onos.plugins.l3.driver as onos_driver
+
+fake_tenant_id = '048aa98a3ec345dc8b14427c81e276cf'
+
+fake_router_uuid = '292f7967-c5e7-47d8-8265-dc2160678b75'
+fake_router_object = {'router': {'name': 'router_abc',
+                                 'external_gateway_info': None,
+                                 'admin_state_up': True,
+                                 'tenant_id': fake_tenant_id}}
+
+fake_network_id = '7464aaf0-27ea-448a-97df-51732f9e0e27'
+fake_router_external_info = {'external_gateway_info':
+                             {'network_id': fake_network_id,
+                              'enable_snat': False}}
+
+fake_floating_ip_id = '7464aaf0-27ea-448a-97df-51732f9e0e25'
+fake_floating_ip = {'floatingip':
+                    {'fixed_ip_address': '10.1.1.1',
+                     'id': fake_floating_ip_id,
+                     'router_id': fake_router_uuid,
+                     'port_id': None,
+                     'status': None,
+                     'tenant_id': fake_tenant_id}}
+
+fake_port_id = '7db560e9-76d4-4bf9-9c28-43efa7afa45d'
+fake_subnet_id = 'dc2b8071-c24c-4a8e-b471-dbf3fbe55830'
+fake_port = {'id': fake_port_id,
+             'network_id': fake_network_id,
+             'fixed_ips': [{'ip_address': '21.41.4.5',
+                            'prefixlen': 28,
+                            'subnet_id': fake_subnet_id}],
+             'subnets': [{'id': fake_subnet_id,
+                          'cidr': '21.41.4.0/28',
+                          'gateway_ip': '21.41.4.1'}]}
+
+fake_floating_ip_update_info = {'floating_network_id': fake_network_id,
+                                'tenant_id': fake_tenant_id,
+                                'fixed_ip_address': '20.1.1.11',
+                                'subnet_id': fake_port['subnets'][0]['id'],
+                                'port_id': fake_port_id,
+                                'floating_ip_address': '198.1.2.3'}
+
+fake_interface_add = {'subnet_id': fake_subnet_id}
+
+fake_interface_remove = {'subnet_id': fake_subnet_id,
+                         'port_id': fake_port_id}
+
+
+class ONOSL3PluginTestCase(base.BaseTestCase,
+                           test_neutron_extensions.ExtensionTestCase,
+                           onos_driver.ONOSL3Plugin):
+
+    def setUp(self):
+        super(ONOSL3PluginTestCase, self).setUp()
+        self._setUpExtension(
+            'neutron.extensions.l3.RouterPluginBase', None,
+            l3.RESOURCE_ATTRIBUTE_MAP, l3.L3, None,
+            allow_pagination=True, allow_sorting=True,
+            supported_extension_aliases=['router'],
+            use_quota=True)
+        self.instance = self.plugin.return_value
+
+    def _mock_req_res(self, status_code):
+        response = mock.Mock(status_code=status_code)
+        response.raise_for_status = mock.Mock()
+        return response
+
+    def _test_send_msg(self, dict_info, oper_type, url):
+        if oper_type == 'post':
+            resp = self.api.post(url, self.serialize(dict_info))
+        elif oper_type == 'put':
+            resp = self.api.put(url, self.serialize(dict_info))
+        else:
+            resp = self.api.delete(url)
+        return resp
+
+    def test_create_router(self):
+        router_info = copy.deepcopy(fake_router_object['router'])
+        router_info.update({'status': 'ACTIVE', 'id': fake_router_uuid})
+        self.instance.create_router.return_value = router_info
+        self.instance.get_routers_count.return_value = 0
+        url = test_base._get_path('routers', fmt=self.fmt)
+        resp = self._test_send_msg(fake_router_object, 'post', url)
+        self.instance.create_router.\
+            assert_called_once_with(mock.ANY, router=fake_router_object)
+        self._verify_resp(resp, exc.HTTPCreated.code,
+                          'router', fake_router_uuid)
+
+    def test_update_router(self):
+        router_info = copy.deepcopy(fake_router_object['router'])
+        router_info.update(fake_router_external_info)
+        router_info.update({'status': 'ACTIVE', 'id': fake_router_uuid})
+        self.instance.update_router.return_value = router_info
+        router_request = {'router': fake_router_external_info}
+        url = test_base._get_path('routers', id=fake_router_uuid, fmt=self.fmt)
+        resp = self._test_send_msg(router_request, 'put', url)
+        self.instance.update_router.\
+            assert_called_once_with(mock.ANY, fake_router_uuid,
+                                    router=router_request)
+        self._verify_resp(resp, exc.HTTPOk.code, 'router', fake_router_uuid)
+
+    def test_delete_router(self):
+        url = test_base._get_path('routers', id=fake_router_uuid, fmt=self.fmt)
+        resp = self._test_send_msg(None, 'delete', url)
+        self.instance.delete_router.assert_called_once_with(mock.ANY,
+                                                            fake_router_uuid)
+        self.assertEqual(resp.status_int, exc.HTTPNoContent.code)
+
+    def test_create_floating_ip(self):
+        floatingip_info = copy.deepcopy(fake_floating_ip['floatingip'])
+        floatingip_info.update(fake_floating_ip_update_info)
+        floatingip_info.update({'status': 'ACTIVE', 'fixed_ip_address': None})
+
+        self.instance.create_floatingip.return_value = floatingip_info
+        self.instance.get_floatingips_count.return_value = 0
+        self.instance.get_port = mock.Mock(return_value=fake_port)
+
+        floating_ip_request = {'floatingip': fake_floating_ip_update_info}
+        url = test_base._get_path('floatingips', fmt=self.fmt)
+        resp = self._test_send_msg(floating_ip_request, 'post', url)
+        self.instance.create_floatingip.\
+            assert_called_once_with(mock.ANY,
+                                    floatingip=floating_ip_request)
+        self._verify_resp(resp, exc.HTTPCreated.code,
+                          'floatingip', fake_floating_ip_id)
+
+    def test_update_floating_ip(self):
+        fake_floating_ip_update_info = {'port_id': None}
+        floatingip_info = copy.deepcopy(fake_floating_ip['floatingip'])
+        floatingip_info.update(fake_floating_ip_update_info)
+        floatingip_info.update({'status': 'ACTIVE',
+                                'tenant_id': fake_tenant_id,
+                                'floating_network_id': fake_network_id,
+                                'fixed_ip_address': None,
+                                'floating_ip_address': '172.24.4.228'})
+
+        self.instance.update_floatingip.return_value = floatingip_info
+        self.instance.get_port = mock.Mock(return_value=fake_port)
+        floating_ip_request = {'floatingip': fake_floating_ip_update_info}
+        url = test_base._get_path('floatingips',
+                                  id=fake_floating_ip_id, fmt=self.fmt)
+        resp = self._test_send_msg(floating_ip_request, 'put', url)
+        self.instance.update_floatingip.\
+            assert_called_once_with(mock.ANY,
+                                    fake_floating_ip_id,
+                                    floatingip=floating_ip_request)
+        self._verify_resp(resp, exc.HTTPOk.code,
+                          'floatingip', fake_floating_ip_id)
+
+    def test_delete_floating_ip(self):
+        self.instance.get_port = mock.Mock(return_value=fake_port)
+        url = test_base._get_path('floatingips', id=fake_floating_ip_id)
+        resp = self._test_send_msg(None, 'delete', url)
+        self.instance.delete_floatingip.\
+            assert_called_once_with(mock.ANY, fake_floating_ip_id)
+        self.assertEqual(resp.status_int, exc.HTTPNoContent.code)
+
+    def test_add_router_interface(self):
+        interface_info = {'tenant_id': fake_tenant_id,
+                          'port_id': fake_port_id,
+                          'id': fake_router_uuid}
+        interface_info.update(fake_interface_add)
+        self.instance.add_router_interface.return_value = interface_info
+        url = test_base._get_path('routers', id=fake_router_uuid,
+                                  action='add_router_interface',
+                                  fmt=self.fmt)
+        resp = self._test_send_msg(fake_interface_add, 'put', url)
+        self.instance.add_router_interface.\
+            assert_called_once_with(mock.ANY, fake_router_uuid,
+                                    fake_interface_add)
+        self._verify_resp(resp, exc.HTTPOk.code, None, fake_router_uuid)
+
+    def test_remove_router_interface(self):
+        interface_info = {'tenant_id': fake_tenant_id,
+                          'id': fake_router_uuid}
+        interface_info.update(fake_interface_remove)
+        self.instance.remove_router_interface.return_value = interface_info
+        url = test_base._get_path('routers', id=fake_router_uuid,
+                                  action='remove_router_interface',
+                                  fmt=self.fmt)
+        resp = self._test_send_msg(fake_interface_remove, 'put', url)
+        self.instance.remove_router_interface.\
+            assert_called_once_with(mock.ANY, fake_router_uuid,
+                                    fake_interface_remove)
+        self._verify_resp(resp, exc.HTTPOk.code, None, fake_router_uuid)
+
+    def _verify_resp(self, resp, return_code, context, id):
+        self.assertEqual(resp.status_int, return_code)
+        resp = self.deserialize(resp)
+
+        if context is None:
+            self.assertEqual(resp['id'], id)
+            self.assertEqual(resp['subnet_id'], fake_subnet_id)
+            return
+
+        self.assertIn(context, resp)
+        resource = resp[context]
+        self.assertEqual(resource['id'], id)
+        if context == 'router':
+            self.assertEqual(resource['status'], 'ACTIVE')
+            self.assertEqual(resource['admin_state_up'], True)
+        elif context == 'floatingip':
+            self.assertEqual(resource['status'], 'ACTIVE')
+            self.assertEqual(resource['fixed_ip_address'], None)
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/unit/plugins/ml2/__init__.py b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/unit/plugins/ml2/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/unit/plugins/ml2/test_driver.py b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/unit/plugins/ml2/test_driver.py
new file mode 100644 (file)
index 0000000..8996923
--- /dev/null
@@ -0,0 +1,237 @@
+# Copyright (c) 2015 Huawei Technologies India Pvt Ltd
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+import mock
+import requests
+
+from oslo_config import cfg
+from oslo_serialization import jsonutils
+from oslotest import base
+
+from neutron.common import constants as n_const
+from neutron.plugins.common import constants
+from neutron.plugins.ml2 import driver_api as api
+from neutron.plugins.ml2 import driver_context as ctx
+
+import networking_onos.plugins.ml2.driver as onos_ml2_driver
+
+
+fake_network_uuid = 'd897e21a-dfd6-4331-a5dd-7524fa421c3e'
+fake_network_object = {'status': 'ACTIVE',
+                       'subnets': [],
+                       'name': 'net1',
+                       'provider:physical_network': None,
+                       'admin_state_up': True,
+                       'tenant_id': 'test-tenant',
+                       'provider:network_type': 'local',
+                       'router:external': False,
+                       'shared': False,
+                       'id': fake_network_uuid,
+                       'provider:segmentation_id': None}
+
+fake_subnet_uuid = 'd897e21a-dfd6-4331-a5dd-7524fa421c3e'
+fake_subnet_object = {'ipv6_ra_mode': None,
+                      'allocation_pools': [{'start': '10.0.0.2',
+                                            'end': '10.0.1.254'}],
+                      'host_routes': [],
+                      'ipv6_address_mode': None,
+                      'cidr': '10.0.0.0/23',
+                      'id': fake_subnet_uuid,
+                      'name': '',
+                      'enable_dhcp': True,
+                      'network_id': fake_network_uuid,
+                      'tenant_id': 'test-tenant',
+                      'dns_nameservers': [],
+                      'gateway_ip': '10.0.0.1',
+                      'ip_version': 4,
+                      'shared': False}
+
+fake_port_uuid = '72c56c48-e9b8-4dcf-b3a7-0813bb3bd839'
+fake_port_object = {'status': 'DOWN',
+                    'binding:host_id': '',
+                    'allowed_address_pairs': [],
+                    'device_owner': 'fake_owner',
+                    'binding:profile': {},
+                    'fixed_ips': [],
+                    'id': fake_port_uuid,
+                    'security_groups':
+                    ['2f9244b4-9bee-4e81-bc4a-3f3c2045b3d7'],
+                    'device_id': 'fake_device',
+                    'name': '',
+                    'admin_state_up': True,
+                    'network_id': fake_network_uuid,
+                    'tenant_id': 'test-tenant',
+                    'binding:vif_details': {},
+                    'binding:vnic_type': 'normal',
+                    'binding:vif_type': 'unbound',
+                    'mac_address': '12:34:56 :78:21:b6'}
+
+
+class ONOSMechanismDriverTestCase(base.BaseTestCase,
+                                  onos_ml2_driver.ONOSMechanismDriver):
+
+    def setUp(self):
+        super(ONOSMechanismDriverTestCase, self).setUp()
+        self.set_test_config()
+
+    def set_test_config(self):
+        cfg.CONF.set_override('url_path', 'http://127.0.0.1:1111', 'onos')
+        cfg.CONF.set_override('username', 'onos_user', 'onos')
+        cfg.CONF.set_override('password', 'awesome', 'onos')
+        self.onos_path = cfg.CONF.onos.url_path
+        self.onos_auth = (cfg.CONF.onos.username,
+                          cfg.CONF.onos.password)
+
+    def _mock_req_resp(self, status_code):
+        response = mock.Mock(status_code=status_code)
+        response.raise_for_status = mock.Mock()
+        return response
+
+    def _test_response(self, context, oper_type, obj_type, mock_method):
+        body = None
+        if oper_type is not 'delete':
+            entity = {obj_type: context.current.copy()}
+            body = jsonutils.dumps(entity, indent=2)
+        if oper_type == 'post':
+            url = '%s/%s' % (self.onos_path, obj_type + 's')
+        else:
+            url = '%s/%s/%s' % (self.onos_path, obj_type + 's',
+                                context.current['id'])
+        kwargs = {'url': url, 'data': body}
+        mock_method.assert_called_once_with(
+            method=oper_type,
+            headers={'Content-Type': 'application/json'},
+            auth=self.onos_auth, **kwargs)
+
+    def test_create_network_postcommit(self):
+        context = mock.Mock(current=fake_network_object)
+        resp = self._mock_req_resp(requests.codes.created)
+        with mock.patch('requests.request',
+                        return_value=resp) as mock_method:
+            self.create_network_postcommit(context)
+            self._test_response(context, 'post', 'network', mock_method)
+
+    def test_update_network_postcommit(self):
+        context = mock.Mock(current=fake_network_object)
+        resp = self._mock_req_resp(requests.codes.created)
+        with mock.patch('requests.request',
+                        return_value=resp) as mock_method:
+            self.update_network_postcommit(context)
+            self._test_response(context, 'put', 'network', mock_method)
+
+    def test_delete_network_postcommit(self):
+        context = mock.Mock(current={'id': fake_network_uuid})
+        resp = self._mock_req_resp(requests.codes.created)
+        with mock.patch('requests.request',
+                        return_value=resp) as mock_method:
+            self.delete_network_postcommit(context)
+            self._test_response(context, 'delete', 'network', mock_method)
+
+    def test_create_subnet_postcommit(self):
+        context = mock.Mock(current=fake_subnet_object)
+        resp = self._mock_req_resp(requests.codes.created)
+        with mock.patch('requests.request',
+                        return_value=resp) as mock_method:
+            self.create_subnet_postcommit(context)
+            self._test_response(context, 'post', 'subnet', mock_method)
+
+    def test_update_subnet_postcommit(self):
+        context = mock.Mock(current=fake_subnet_object)
+        resp = self._mock_req_resp(requests.codes.created)
+        with mock.patch('requests.request',
+                        return_value=resp) as mock_method:
+            self.update_subnet_postcommit(context)
+            self._test_response(context, 'put', 'subnet', mock_method)
+
+    def test_delete_subnet_postcommit(self):
+        context = mock.Mock(current={'id': fake_subnet_uuid})
+        resp = self._mock_req_resp(requests.codes.created)
+        with mock.patch('requests.request',
+                        return_value=resp) as mock_method:
+            self.delete_subnet_postcommit(context)
+            self._test_response(context, 'delete', 'subnet', mock_method)
+
+    def test_create_port_postcommit(self):
+        context = mock.Mock(current=fake_port_object)
+        resp = self._mock_req_resp(requests.codes.created)
+        with mock.patch('requests.request',
+                        return_value=resp) as mock_method:
+            self.create_port_postcommit(context)
+            self._test_response(context, 'post', 'port', mock_method)
+
+    def test_update_port_postcommit(self):
+        context = mock.Mock(current=fake_port_object)
+        resp = self._mock_req_resp(requests.codes.created)
+        with mock.patch('requests.request',
+                        return_value=resp) as mock_method:
+            self.update_port_postcommit(context)
+            self._test_response(context, 'put', 'port', mock_method)
+
+    def test_delete_port_postcommit(self):
+        context = mock.Mock(current={'id': fake_port_uuid})
+        resp = self._mock_req_resp(requests.codes.created)
+        with mock.patch('requests.request',
+                        return_value=resp) as mock_method:
+            self.delete_port_postcommit(context)
+            self._test_response(context, 'delete', 'port', mock_method)
+
+    # given valid  and invalid segments
+    valid_segment = {
+        api.ID: 'API_ID',
+        api.NETWORK_TYPE: constants.TYPE_LOCAL,
+        api.SEGMENTATION_ID: 'API_SEGMENTATION_ID',
+        api.PHYSICAL_NETWORK: 'API_PHYSICAL_NETWORK'}
+
+    invalid_segment = {
+        api.ID: 'API_ID',
+        api.NETWORK_TYPE: constants.TYPE_NONE,
+        api.SEGMENTATION_ID: 'API_SEGMENTATION_ID',
+        api.PHYSICAL_NETWORK: 'API_PHYSICAL_NETWORK'}
+
+    def test_check_segment(self):
+        """Validate the check_segment method."""
+
+        # given driver and all network types
+        all_network_types = [constants.TYPE_FLAT, constants.TYPE_GRE,
+                             constants.TYPE_LOCAL, constants.TYPE_VXLAN,
+                             constants.TYPE_VLAN, constants.TYPE_NONE]
+
+        # when checking segments network type
+        valid_types = {network_type
+                       for network_type in all_network_types
+                       if self.check_segment({api.NETWORK_TYPE: network_type})}
+
+        # then true is returned only for valid network types
+        self.assertEqual({constants.TYPE_LOCAL, constants.TYPE_GRE,
+                          constants.TYPE_VXLAN, constants.TYPE_VLAN},
+                         valid_types)
+
+    def test_bind_port(self):
+        self.vif_type = "MY_VIF_TYPE"
+        self.vif_details = "MY_VIF_DETAILS"
+        network = mock.MagicMock(spec=api.NetworkContext)
+        port_context = mock.MagicMock(
+            spec=ctx.PortContext, current={'id': 'CURRENT_CONTEXT_ID'},
+            segments_to_bind=[self.valid_segment, self.invalid_segment],
+            network=network)
+
+        # when port is bound
+        self.bind_port(port_context)
+
+        # then context binding is setup with returned vif_type and valid
+        # segment api ID
+        port_context.set_binding.assert_called_once_with(
+            self.valid_segment[api.ID], self.vif_type,
+            self.vif_details, status=n_const.PORT_STATUS_ACTIVE)
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/openstack-common.conf b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/openstack-common.conf
new file mode 100644 (file)
index 0000000..7caa758
--- /dev/null
@@ -0,0 +1,5 @@
+[DEFAULT]
+# The list of modules to copy from oslo-incubator.git
+
+# The base module to hold the copy of openstack.common
+base=networking_onos
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/requirements.txt b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/requirements.txt
new file mode 100644 (file)
index 0000000..26bb924
--- /dev/null
@@ -0,0 +1,9 @@
+# The order of packages is significant, because pip processes them in the order
+# of appearance. Changing the order has an impact on the overall integration
+# process, which may cause wedges in the gate later.
+
+pbr>=1.6
+Babel>=1.3
+-e git://git.openstack.org/openstack/neutron.git#egg=neutron
+
+
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/setup.cfg b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/setup.cfg
new file mode 100644 (file)
index 0000000..d7fd5b1
--- /dev/null
@@ -0,0 +1,56 @@
+[metadata]
+name = networking-onos
+summary = OpenStack Networking
+description-file =
+    README.rst
+author = OpenStack
+author-email = openstack-dev@lists.openstack.org
+home-page = http://www.openstack.org/
+classifier =
+    Environment :: OpenStack
+    Intended Audience :: Information Technology
+    Intended Audience :: System Administrators
+    License :: OSI Approved :: Apache Software License
+    Operating System :: POSIX :: Linux
+    Programming Language :: Python
+    Programming Language :: Python :: 2
+    Programming Language :: Python :: 2.7
+
+[files]
+packages =
+    networking_onos
+data_files =
+    etc/neutron/plugins/ml2 =
+        etc/conf_onos.ini
+
+[global]
+setup-hooks =
+    pbr.hooks.setup_hook
+
+[entry_points]
+neutron.ml2.mechanism_drivers =
+    onos_ml2 = networking_onos.plugins.ml2.driver:ONOSMechanismDriver
+neutron.service_plugins =
+    onos_router = networking_onos.plugins.l3.driver:ONOSL3Plugin
+
+[build_sphinx]
+all_files = 1
+build-dir = doc/build
+source-dir = doc/source
+
+[extract_messages]
+keywords = _ gettext ngettext l_ lazy_gettext
+mapping_file = babel.cfg
+output_file = networking_onos/locale/networking-onos.pot
+
+[compile_catalog]
+directory = networking_onos/locale
+domain = networking-onos
+
+[update_catalog]
+domain = networking-onos
+output_dir = networking_onos/locale
+input_file = networking_onos/locale/networking-onos.pot
+
+[wheel]
+universal = 1
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/setup.py b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/setup.py
new file mode 100644 (file)
index 0000000..782bb21
--- /dev/null
@@ -0,0 +1,29 @@
+# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
+import setuptools
+
+# In python < 2.7.4, a lazy loading of package `pbr` will break
+# setuptools if some other modules registered functions in `atexit`.
+# solution from: http://bugs.python.org/issue15881#msg170215
+try:
+    import multiprocessing  # noqa
+except ImportError:
+    pass
+
+setuptools.setup(
+    setup_requires=['pbr>=1.8'],
+    pbr=True)
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/test-requirements.txt b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/test-requirements.txt
new file mode 100644 (file)
index 0000000..2bf64e0
--- /dev/null
@@ -0,0 +1,15 @@
+# The order of packages is significant, because pip processes them in the order
+# of appearance. Changing the order has an impact on the overall integration
+# process, which may cause wedges in the gate later.
+
+hacking<0.11,>=0.10.0
+
+coverage>=3.6
+python-subunit>=0.0.18
+sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2
+oslosphinx>=2.5.0 # Apache-2.0
+oslotest>=1.10.0 # Apache-2.0
+testrepository>=0.0.18
+testscenarios>=0.4
+WebTest>=2.0
+testtools>=1.4.0
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/check_bash.sh b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/check_bash.sh
new file mode 100644 (file)
index 0000000..e9d178e
--- /dev/null
@@ -0,0 +1,31 @@
+#! /bin/sh
+
+# Copyright (C) 2014 VA Linux Systems Japan K.K.
+# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+# The purpose of this script is to avoid casual introduction of more
+# bash dependency.  Please consider alternatives before commiting code
+# which uses bash specific features.
+
+# Ignore comments, but include shebangs
+OBSERVED=$(grep -E '^([^#]|#!).*bash' tox.ini tools/* | wc -l)
+EXPECTED=5
+if [ ${EXPECTED} -ne ${OBSERVED} ]; then
+    echo Unexpected number of bash usages are detected.
+    echo Please read the comment in $0
+    exit 1
+fi
+exit 0
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/check_i18n.py b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/check_i18n.py
new file mode 100644 (file)
index 0000000..697ad18
--- /dev/null
@@ -0,0 +1,153 @@
+#    Copyright 2012 OpenStack Foundation
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+from __future__ import print_function
+
+import compiler
+import imp
+import os.path
+import sys
+
+
+def is_localized(node):
+    """Check message wrapped by _()"""
+    if isinstance(node.parent, compiler.ast.CallFunc):
+        if isinstance(node.parent.node, compiler.ast.Name):
+            if node.parent.node.name == '_':
+                return True
+    return False
+
+
+class ASTWalker(compiler.visitor.ASTVisitor):
+
+    def default(self, node, *args):
+        for child in node.getChildNodes():
+            child.parent = node
+        compiler.visitor.ASTVisitor.default(self, node, *args)
+
+
+class Visitor(object):
+
+    def __init__(self, filename, i18n_msg_predicates,
+                 msg_format_checkers, debug):
+        self.filename = filename
+        self.debug = debug
+        self.error = 0
+        self.i18n_msg_predicates = i18n_msg_predicates
+        self.msg_format_checkers = msg_format_checkers
+        with open(filename) as f:
+            self.lines = f.readlines()
+
+    def visitConst(self, node):
+        if not isinstance(node.value, str):
+            return
+
+        if is_localized(node):
+            for (checker, msg) in self.msg_format_checkers:
+                if checker(node):
+                    print('%s:%d %s: %s Error: %s' %
+                          (self.filename, node.lineno,
+                           self.lines[node.lineno - 1][:-1],
+                           checker.__name__, msg),
+                           file=sys.stderr)
+                    self.error = 1
+                    return
+            if debug:
+                print('%s:%d %s: %s' %
+                      (self.filename, node.lineno,
+                       self.lines[node.lineno - 1][:-1],
+                       "Pass"))
+        else:
+            for (predicate, action, msg) in self.i18n_msg_predicates:
+                if predicate(node):
+                    if action == 'skip':
+                        if debug:
+                            print('%s:%d %s: %s' %
+                                  (self.filename, node.lineno,
+                                  self.lines[node.lineno - 1][:-1],
+                                  "Pass"))
+                        return
+                    elif action == 'error':
+                        print('%s:%d %s: %s Error: %s' %
+                              (self.filename, node.lineno,
+                               self.lines[node.lineno - 1][:-1],
+                               predicate.__name__, msg),
+                               file=sys.stderr)
+                        self.error = 1
+                        return
+                    elif action == 'warn':
+                        print('%s:%d %s: %s' %
+                              (self.filename, node.lineno,
+                              self.lines[node.lineno - 1][:-1],
+                              "Warn: %s" % msg))
+                        return
+                    print('Predicate with wrong action!', file=sys.stderr)
+
+
+def is_file_in_black_list(black_list, f):
+    for f in black_list:
+        if os.path.abspath(input_file).startswith(
+            os.path.abspath(f)):
+            return True
+    return False
+
+
+def check_i18n(input_file, i18n_msg_predicates, msg_format_checkers, debug):
+    input_mod = compiler.parseFile(input_file)
+    v = compiler.visitor.walk(input_mod,
+                              Visitor(input_file,
+                                      i18n_msg_predicates,
+                                      msg_format_checkers,
+                                      debug),
+                              ASTWalker())
+    return v.error
+
+
+if __name__ == '__main__':
+    input_path = sys.argv[1]
+    cfg_path = sys.argv[2]
+    try:
+        cfg_mod = imp.load_source('', cfg_path)
+    except Exception:
+        print("Load cfg module failed", file=sys.stderr)
+        sys.exit(1)
+
+    i18n_msg_predicates = cfg_mod.i18n_msg_predicates
+    msg_format_checkers = cfg_mod.msg_format_checkers
+    black_list = cfg_mod.file_black_list
+
+    debug = False
+    if len(sys.argv) > 3:
+        if sys.argv[3] == '-d':
+            debug = True
+
+    if os.path.isfile(input_path):
+        sys.exit(check_i18n(input_path,
+                            i18n_msg_predicates,
+                            msg_format_checkers,
+                            debug))
+
+    error = 0
+    for dirpath, dirs, files in os.walk(input_path):
+        for f in files:
+            if not f.endswith('.py'):
+                continue
+            input_file = os.path.join(dirpath, f)
+            if is_file_in_black_list(black_list, input_file):
+                continue
+            if check_i18n(input_file,
+                          i18n_msg_predicates,
+                          msg_format_checkers,
+                          debug):
+                error = 1
+    sys.exit(error)
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/check_i18n_test_case.txt b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/check_i18n_test_case.txt
new file mode 100644 (file)
index 0000000..3d1391d
--- /dev/null
@@ -0,0 +1,67 @@
+# test-case for check_i18n.py
+# python check_i18n.py check_i18n.txt -d
+
+# message format checking
+#  capital checking
+msg = _("hello world, error")
+msg = _("hello world_var, error")
+msg = _('file_list xyz, pass')
+msg = _("Hello world, pass")
+
+#  format specifier checking
+msg = _("Hello %s world %d, error")
+msg = _("Hello %s world, pass")
+msg = _("Hello %(var1)s world %(var2)s, pass")
+
+# message has been localized
+#  is_localized
+msg = _("Hello world, pass")
+msg = _("Hello world, pass") % var
+LOG.debug(_('Hello world, pass'))
+LOG.info(_('Hello world, pass'))
+raise x.y.Exception(_('Hello world, pass'))
+raise Exception(_('Hello world, pass'))
+
+# message need be localized
+#  is_log_callfunc
+LOG.debug('hello world, error')
+LOG.debug('hello world, error' % xyz)
+sys.append('hello world, warn')
+
+# is_log_i18n_msg_with_mod
+LOG.debug(_('Hello world, error') % xyz)
+
+# default warn
+msg = 'hello world, warn'
+msg = 'hello world, warn' % var
+
+# message needn't be localized
+#  skip only one word
+msg = ''
+msg = "hello,pass"
+
+#  skip dict
+msg = {'hello world, pass': 1}
+
+#  skip list
+msg = ["hello world, pass"]
+
+#  skip subscript
+msg['hello world, pass']
+
+#  skip xml marker
+msg = "<test><t></t></test>, pass"
+
+#  skip sql statement
+msg = "SELECT * FROM xyz WHERE hello=1, pass"
+msg = "select * from xyz, pass"
+
+#  skip add statement
+msg = 'hello world' + e + 'world hello, pass'
+
+#  skip doc string
+"""
+Hello world, pass
+"""
+class Msg:
+    pass
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/clean.sh b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/clean.sh
new file mode 100755 (executable)
index 0000000..27bc219
--- /dev/null
@@ -0,0 +1,5 @@
+#!/bin/bash
+rm -rf ./*.deb ./*.tar.gz ./*.dsc ./*.changes
+rm -rf */*.deb
+rm -rf ./plugins/**/build/ ./plugins/**/dist
+rm -rf ./plugins/**/lib/neutron_*_plugin.egg-info ./plugins/neutron-*
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/i18n_cfg.py b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/i18n_cfg.py
new file mode 100644 (file)
index 0000000..5ad1a51
--- /dev/null
@@ -0,0 +1,97 @@
+import compiler
+import re
+
+
+def is_log_callfunc(n):
+    """LOG.xxx('hello %s' % xyz) and LOG('hello')"""
+    if isinstance(n.parent, compiler.ast.Mod):
+        n = n.parent
+    if isinstance(n.parent, compiler.ast.CallFunc):
+        if isinstance(n.parent.node, compiler.ast.Getattr):
+            if isinstance(n.parent.node.getChildNodes()[0],
+                          compiler.ast.Name):
+                if n.parent.node.getChildNodes()[0].name == 'LOG':
+                    return True
+    return False
+
+
+def is_log_i18n_msg_with_mod(n):
+    """LOG.xxx("Hello %s" % xyz) should be LOG.xxx("Hello %s", xyz)"""
+    if not isinstance(n.parent.parent, compiler.ast.Mod):
+        return False
+    n = n.parent.parent
+    if isinstance(n.parent, compiler.ast.CallFunc):
+        if isinstance(n.parent.node, compiler.ast.Getattr):
+            if isinstance(n.parent.node.getChildNodes()[0],
+                          compiler.ast.Name):
+                if n.parent.node.getChildNodes()[0].name == 'LOG':
+                    return True
+    return False
+
+
+def is_wrong_i18n_format(n):
+    """Check _('hello %s' % xyz)"""
+    if isinstance(n.parent, compiler.ast.Mod):
+        n = n.parent
+    if isinstance(n.parent, compiler.ast.CallFunc):
+        if isinstance(n.parent.node, compiler.ast.Name):
+            if n.parent.node.name == '_':
+                return True
+    return False
+
+
+"""
+Used for check message need be localized or not.
+(predicate_func, action, message)
+"""
+i18n_msg_predicates = [
+    # Skip ['hello world', 1]
+    (lambda n: isinstance(n.parent, compiler.ast.List), 'skip', ''),
+    # Skip {'hellow world', 1}
+    (lambda n: isinstance(n.parent, compiler.ast.Dict), 'skip', ''),
+    # Skip msg['hello world']
+    (lambda n: isinstance(n.parent, compiler.ast.Subscript), 'skip', ''),
+    # Skip doc string
+    (lambda n: isinstance(n.parent, compiler.ast.Discard), 'skip', ''),
+    # Skip msg = "hello", in normal, message should more than one word
+    (lambda n: len(n.value.strip().split(' ')) <= 1, 'skip', ''),
+    # Skip msg = 'hello world' + vars + 'world hello'
+    (lambda n: isinstance(n.parent, compiler.ast.Add), 'skip', ''),
+    # Skip xml markers msg = "<test></test>"
+    (lambda n: len(re.compile("</.*>").findall(n.value)) > 0, 'skip', ''),
+    # Skip sql statement
+    (lambda n: len(
+        re.compile("^SELECT.*FROM", flags=re.I).findall(n.value)) > 0,
+     'skip', ''),
+    # LOG.xxx()
+    (is_log_callfunc, 'error', 'Message must be localized'),
+    # _('hello %s' % xyz) should be _('hello %s') % xyz
+    (is_wrong_i18n_format, 'error',
+     ("Message format was wrong, _('hello %s' % xyz) "
+      "should be _('hello %s') % xyz")),
+    # default
+    (lambda n: True, 'warn', 'Message might need localized')
+]
+
+
+"""
+Used for checking message format. (checker_func, message)
+"""
+msg_format_checkers = [
+    # If message contain more than on format specifier, it should use
+    # mapping key
+    (lambda n: len(re.compile("%[bcdeEfFgGnosxX]").findall(n.value)) > 1,
+     "The message shouldn't contain more than one format specifier"),
+    # Check capital
+    (lambda n: n.value.split(' ')[0].count('_') == 0 and
+     n.value[0].isalpha() and
+     n.value[0].islower(),
+     "First letter must be capital"),
+    (is_log_i18n_msg_with_mod,
+     'LOG.xxx("Hello %s" % xyz) should be LOG.xxx("Hello %s", xyz)')
+]
+
+
+file_black_list = ["./neutron/tests/unit",
+                   "./neutron/openstack",
+                   "./neutron/plugins/bigswitch/tests"]
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/install_venv.py b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/install_venv.py
new file mode 100644 (file)
index 0000000..f8fb8fa
--- /dev/null
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+# Copyright 2010 United States Government as represented by the
+# Administrator of the National Aeronautics and Space Administration.
+# All Rights Reserved.
+#
+# Copyright 2010 OpenStack Foundation.
+# Copyright 2013 IBM Corp.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+"""
+Installation script for Neutron's development virtualenv
+"""
+from __future__ import print_function
+
+import os
+import sys
+
+import install_venv_common as install_venv
+
+
+def print_help():
+    help = """
+ Neutron development environment setup is complete.
+
+ Neutron development uses virtualenv to track and manage Python dependencies
+ while in development and testing.
+
+ To activate the Neutron virtualenv for the extent of your current shell
+ session you can run:
+
+ $ source .venv/bin/activate
+
+ Or, if you prefer, you can run commands in the virtualenv on a case by case
+ basis by running:
+
+ $ tools/with_venv.sh <your command>
+
+ Also, make test will automatically use the virtualenv.
+    """
+    print(help)
+
+
+def main(argv):
+    root = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
+    venv = os.path.join(root, '.venv')
+    pip_requires = os.path.join(root, 'requirements.txt')
+    test_requires = os.path.join(root, 'test-requirements.txt')
+    py_version = "python%s.%s" % (sys.version_info[0], sys.version_info[1])
+    project = 'Neutron'
+    install = install_venv.InstallVenv(root, venv, pip_requires, test_requires,
+                                       py_version, project)
+    options = install.parse_args(argv)
+    install.check_python_version()
+    install.check_dependencies()
+    install.create_virtualenv(no_site_packages=options.no_site_packages)
+    install.install_dependencies()
+    print_help()
+
+
+if __name__ == '__main__':
+    main(sys.argv)
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/install_venv_common.py b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/install_venv_common.py
new file mode 100644 (file)
index 0000000..e279159
--- /dev/null
@@ -0,0 +1,172 @@
+# Copyright 2013 OpenStack Foundation
+# Copyright 2013 IBM Corp.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+"""Provides methods needed by installation script for OpenStack development
+virtual environments.
+
+Since this script is used to bootstrap a virtualenv from the system's Python
+environment, it should be kept strictly compatible with Python 2.6.
+
+Synced in from openstack-common
+"""
+
+from __future__ import print_function
+
+import optparse
+import os
+import subprocess
+import sys
+
+
+class InstallVenv(object):
+
+    def __init__(self, root, venv, requirements,
+                 test_requirements, py_version,
+                 project):
+        self.root = root
+        self.venv = venv
+        self.requirements = requirements
+        self.test_requirements = test_requirements
+        self.py_version = py_version
+        self.project = project
+
+    def die(self, message, *args):
+        print(message % args, file=sys.stderr)
+        sys.exit(1)
+
+    def check_python_version(self):
+        if sys.version_info < (2, 6):
+            self.die("Need Python Version >= 2.6")
+
+    def run_command_with_code(self, cmd, redirect_output=True,
+                              check_exit_code=True):
+        """Runs a command in an out-of-process shell.
+
+        Returns the output of that command. Working directory is self.root.
+        """
+        if redirect_output:
+            stdout = subprocess.PIPE
+        else:
+            stdout = None
+
+        proc = subprocess.Popen(cmd, cwd=self.root, stdout=stdout)
+        output = proc.communicate()[0]
+        if check_exit_code and proc.returncode != 0:
+            self.die('Command "%s" failed.\n%s', ' '.join(cmd), output)
+        return (output, proc.returncode)
+
+    def run_command(self, cmd, redirect_output=True, check_exit_code=True):
+        return self.run_command_with_code(cmd, redirect_output,
+                                          check_exit_code)[0]
+
+    def get_distro(self):
+        if (os.path.exists('/etc/fedora-release') or
+                os.path.exists('/etc/redhat-release')):
+            return Fedora(
+                self.root, self.venv, self.requirements,
+                self.test_requirements, self.py_version, self.project)
+        else:
+            return Distro(
+                self.root, self.venv, self.requirements,
+                self.test_requirements, self.py_version, self.project)
+
+    def check_dependencies(self):
+        self.get_distro().install_virtualenv()
+
+    def create_virtualenv(self, no_site_packages=True):
+        """Creates the virtual environment and installs PIP.
+
+        Creates the virtual environment and installs PIP only into the
+        virtual environment.
+        """
+        if not os.path.isdir(self.venv):
+            print('Creating venv...', end=' ')
+            if no_site_packages:
+                self.run_command(['virtualenv', '-q', '--no-site-packages',
+                                 self.venv])
+            else:
+                self.run_command(['virtualenv', '-q', self.venv])
+            print('done.')
+        else:
+            print("venv already exists...")
+            pass
+
+    def pip_install(self, *args):
+        self.run_command(['tools/with_venv.sh',
+                         'pip', 'install', '--upgrade'] + list(args),
+                         redirect_output=False)
+
+    def install_dependencies(self):
+        print('Installing dependencies with pip (this can take a while)...')
+
+        # First things first, make sure our venv has the latest pip and
+        # setuptools and pbr
+        self.pip_install('pip>=1.4')
+        self.pip_install('setuptools')
+        self.pip_install('pbr')
+
+        self.pip_install('-r', self.requirements, '-r', self.test_requirements)
+
+    def parse_args(self, argv):
+        """Parses command-line arguments."""
+        parser = optparse.OptionParser()
+        parser.add_option('-n', '--no-site-packages',
+                          action='store_true',
+                          help="Do not inherit packages from global Python "
+                               "install.")
+        return parser.parse_args(argv[1:])[0]
+
+
+class Distro(InstallVenv):
+
+    def check_cmd(self, cmd):
+        return bool(self.run_command(['which', cmd],
+                    check_exit_code=False).strip())
+
+    def install_virtualenv(self):
+        if self.check_cmd('virtualenv'):
+            return
+
+        if self.check_cmd('easy_install'):
+            print('Installing virtualenv via easy_install...', end=' ')
+            if self.run_command(['easy_install', 'virtualenv']):
+                print('Succeeded')
+                return
+            else:
+                print('Failed')
+
+        self.die('ERROR: virtualenv not found.\n\n%s development'
+                 ' requires virtualenv, please install it using your'
+                 ' favorite package management tool' % self.project)
+
+
+class Fedora(Distro):
+    """This covers all Fedora-based distributions.
+
+    Includes: Fedora, RHEL, CentOS, Scientific Linux
+    """
+
+    def check_pkg(self, pkg):
+        return self.run_command_with_code(['rpm', '-q', pkg],
+                                          check_exit_code=False)[1] == 0
+
+    def install_virtualenv(self):
+        if self.check_cmd('virtualenv'):
+            return
+
+        if not self.check_pkg('python-virtualenv'):
+            self.die("Please install 'python-virtualenv'.")
+
+        super(Fedora, self).install_virtualenv()
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/pretty_tox.sh b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/pretty_tox.sh
new file mode 100755 (executable)
index 0000000..a40f248
--- /dev/null
@@ -0,0 +1,6 @@
+#! /bin/sh
+
+TESTRARGS=$1
+
+exec 3>&1
+status=$(exec 4>&1 >&3; ( python setup.py testr --slowest --testr-args="--subunit $TESTRARGS"; echo $? >&4 ) | $(dirname $0)/subunit-trace.py -f) && exit $status
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/subunit-trace.py b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/subunit-trace.py
new file mode 100755 (executable)
index 0000000..73f2f10
--- /dev/null
@@ -0,0 +1,307 @@
+#!/usr/bin/env python
+
+# Copyright 2014 Hewlett-Packard Development Company, L.P.
+# Copyright 2014 Samsung Electronics
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""Trace a subunit stream in reasonable detail and high accuracy."""
+
+import argparse
+import functools
+import os
+import re
+import sys
+
+import mimeparse
+import subunit
+import testtools
+
+DAY_SECONDS = 60 * 60 * 24
+FAILS = []
+RESULTS = {}
+
+
+class Starts(testtools.StreamResult):
+
+    def __init__(self, output):
+        super(Starts, self).__init__()
+        self._output = output
+
+    def startTestRun(self):
+        self._neednewline = False
+        self._emitted = set()
+
+    def status(self, test_id=None, test_status=None, test_tags=None,
+               runnable=True, file_name=None, file_bytes=None, eof=False,
+               mime_type=None, route_code=None, timestamp=None):
+        super(Starts, self).status(
+            test_id, test_status,
+            test_tags=test_tags, runnable=runnable, file_name=file_name,
+            file_bytes=file_bytes, eof=eof, mime_type=mime_type,
+            route_code=route_code, timestamp=timestamp)
+        if not test_id:
+            if not file_bytes:
+                return
+            if not mime_type or mime_type == 'test/plain;charset=utf8':
+                mime_type = 'text/plain; charset=utf-8'
+            primary, sub, parameters = mimeparse.parse_mime_type(mime_type)
+            content_type = testtools.content_type.ContentType(
+                primary, sub, parameters)
+            content = testtools.content.Content(
+                content_type, lambda: [file_bytes])
+            text = content.as_text()
+            if text and text[-1] not in '\r\n':
+                self._neednewline = True
+            self._output.write(text)
+        elif test_status == 'inprogress' and test_id not in self._emitted:
+            if self._neednewline:
+                self._neednewline = False
+                self._output.write('\n')
+            worker = ''
+            for tag in test_tags or ():
+                if tag.startswith('worker-'):
+                    worker = '(' + tag[7:] + ') '
+            if timestamp:
+                timestr = timestamp.isoformat()
+            else:
+                timestr = ''
+                self._output.write('%s: %s%s [start]\n' %
+                                   (timestr, worker, test_id))
+            self._emitted.add(test_id)
+
+
+def cleanup_test_name(name, strip_tags=True, strip_scenarios=False):
+    """Clean up the test name for display.
+
+    By default we strip out the tags in the test because they don't help us
+    in identifying the test that is run to it's result.
+
+    Make it possible to strip out the testscenarios information (not to
+    be confused with tempest scenarios) however that's often needed to
+    indentify generated negative tests.
+    """
+    if strip_tags:
+        tags_start = name.find('[')
+        tags_end = name.find(']')
+        if tags_start > 0 and tags_end > tags_start:
+            newname = name[:tags_start]
+            newname += name[tags_end + 1:]
+            name = newname
+
+    if strip_scenarios:
+        tags_start = name.find('(')
+        tags_end = name.find(')')
+        if tags_start > 0 and tags_end > tags_start:
+            newname = name[:tags_start]
+            newname += name[tags_end + 1:]
+            name = newname
+
+    return name
+
+
+def get_duration(timestamps):
+    start, end = timestamps
+    if not start or not end:
+        duration = ''
+    else:
+        delta = end - start
+        duration = '%d.%06ds' % (
+            delta.days * DAY_SECONDS + delta.seconds, delta.microseconds)
+    return duration
+
+
+def find_worker(test):
+    for tag in test['tags']:
+        if tag.startswith('worker-'):
+            return int(tag[7:])
+    return 'NaN'
+
+
+# Print out stdout/stderr if it exists, always
+def print_attachments(stream, test, all_channels=False):
+    """Print out subunit attachments.
+
+    Print out subunit attachments that contain content. This
+    runs in 2 modes, one for successes where we print out just stdout
+    and stderr, and an override that dumps all the attachments.
+    """
+    channels = ('stdout', 'stderr')
+    for name, detail in test['details'].items():
+        # NOTE(sdague): the subunit names are a little crazy, and actually
+        # are in the form pythonlogging:'' (with the colon and quotes)
+        name = name.split(':')[0]
+        if detail.content_type.type == 'test':
+            detail.content_type.type = 'text'
+        if (all_channels or name in channels) and detail.as_text():
+            title = "Captured %s:" % name
+            stream.write("\n%s\n%s\n" % (title, ('~' * len(title))))
+            # indent attachment lines 4 spaces to make them visually
+            # offset
+            for line in detail.as_text().split('\n'):
+                stream.write("    %s\n" % line)
+
+
+def show_outcome(stream, test, print_failures=False, failonly=False):
+    global RESULTS
+    status = test['status']
+    # TODO(sdague): ask lifeless why on this?
+    if status == 'exists':
+        return
+
+    worker = find_worker(test)
+    name = cleanup_test_name(test['id'])
+    duration = get_duration(test['timestamps'])
+
+    if worker not in RESULTS:
+        RESULTS[worker] = []
+    RESULTS[worker].append(test)
+
+    # don't count the end of the return code as a fail
+    if name == 'process-returncode':
+        return
+
+    if status == 'fail':
+        FAILS.append(test)
+        stream.write('{%s} %s [%s] ... FAILED\n' % (
+            worker, name, duration))
+        if not print_failures:
+            print_attachments(stream, test, all_channels=True)
+    elif not failonly:
+        if status == 'success':
+            stream.write('{%s} %s [%s] ... ok\n' % (
+                worker, name, duration))
+            print_attachments(stream, test)
+        elif status == 'skip':
+            stream.write('{%s} %s ... SKIPPED: %s\n' % (
+                worker, name, test['details']['reason'].as_text()))
+        else:
+            stream.write('{%s} %s [%s] ... %s\n' % (
+                worker, name, duration, test['status']))
+            if not print_failures:
+                print_attachments(stream, test, all_channels=True)
+
+    stream.flush()
+
+
+def print_fails(stream):
+    """Print summary failure report.
+
+    Currently unused, however there remains debate on inline vs. at end
+    reporting, so leave the utility function for later use.
+    """
+    if not FAILS:
+        return
+    stream.write("\n==============================\n")
+    stream.write("Failed %s tests - output below:" % len(FAILS))
+    stream.write("\n==============================\n")
+    for f in FAILS:
+        stream.write("\n%s\n" % f['id'])
+        stream.write("%s\n" % ('-' * len(f['id'])))
+        print_attachments(stream, f, all_channels=True)
+    stream.write('\n')
+
+
+def count_tests(key, value):
+    count = 0
+    for k, v in RESULTS.items():
+        for item in v:
+            if key in item:
+                if re.search(value, item[key]):
+                    count += 1
+    return count
+
+
+def run_time():
+    runtime = 0.0
+    for k, v in RESULTS.items():
+        for test in v:
+            runtime += float(get_duration(test['timestamps']).strip('s'))
+    return runtime
+
+
+def worker_stats(worker):
+    tests = RESULTS[worker]
+    num_tests = len(tests)
+    delta = tests[-1]['timestamps'][1] - tests[0]['timestamps'][0]
+    return num_tests, delta
+
+
+def print_summary(stream):
+    stream.write("\n======\nTotals\n======\n")
+    stream.write("Run: %s in %s sec.\n" % (count_tests('status', '.*'),
+                                           run_time()))
+    stream.write(" - Passed: %s\n" % count_tests('status', 'success'))
+    stream.write(" - Skipped: %s\n" % count_tests('status', 'skip'))
+    stream.write(" - Failed: %s\n" % count_tests('status', 'fail'))
+
+    # we could have no results, especially as we filter out the process-codes
+    if RESULTS:
+        stream.write("\n==============\nWorker Balance\n==============\n")
+
+        for w in range(max(RESULTS.keys()) + 1):
+            if w not in RESULTS:
+                stream.write(
+                    " - WARNING: missing Worker %s! "
+                    "Race in testr accounting.\n" % w)
+            else:
+                num, time = worker_stats(w)
+                stream.write(" - Worker %s (%s tests) => %ss\n" %
+                             (w, num, time))
+
+
+def parse_args():
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--no-failure-debug', '-n', action='store_true',
+                        dest='print_failures', help='Disable printing failure '
+                        'debug information in realtime')
+    parser.add_argument('--fails', '-f', action='store_true',
+                        dest='post_fails', help='Print failure debug '
+                        'information after the stream is proccesed')
+    parser.add_argument('--failonly', action='store_true',
+                        dest='failonly', help="Don't print success items",
+                        default=(
+                            os.environ.get('TRACE_FAILONLY', False)
+                            is not False))
+    return parser.parse_args()
+
+
+def main():
+    args = parse_args()
+    stream = subunit.ByteStreamToStreamResult(
+        sys.stdin, non_subunit_name='stdout')
+    starts = Starts(sys.stdout)
+    outcomes = testtools.StreamToDict(
+        functools.partial(show_outcome, sys.stdout,
+                          print_failures=args.print_failures,
+                          failonly=args.failonly
+                      ))
+    summary = testtools.StreamSummary()
+    result = testtools.CopyStreamResult([starts, outcomes, summary])
+    result.startTestRun()
+    try:
+        stream.run(result)
+    finally:
+        result.stopTestRun()
+    if count_tests('status', '.*') == 0:
+        print("The test run didn't actually run any tests")
+        return 1
+    if args.post_fails:
+        print_fails(sys.stdout)
+    print_summary(sys.stdout)
+    return (0 if summary.wasSuccessful() else 1)
+
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/with_venv.sh b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/with_venv.sh
new file mode 100755 (executable)
index 0000000..dea5c5f
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/bash
+# Copyright 2011 OpenStack Foundation.
+# All Rights Reserved.
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+
+TOOLS=`dirname $0`
+VENV=$TOOLS/../.venv
+source $VENV/bin/activate && "$@"
diff --git a/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tox.ini b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tox.ini
new file mode 100644 (file)
index 0000000..41b875e
--- /dev/null
@@ -0,0 +1,41 @@
+[tox]
+envlist = py27,pep8
+minversion = 1.6
+skipsdist = True
+
+[testenv]
+setenv = VIRTUAL_ENV={envdir}
+usedevelop = True
+install_command = pip install -r requirements.txt -U {opts} {packages}
+deps = -r{toxinidir}/test-requirements.txt
+whitelist_externals = bash
+commands = bash tools/pretty_tox.sh '{posargs}'
+
+[testenv:pep8]
+commands = flake8
+
+[testenv:i18n]
+commands = python ./tools/check_i18n.py ./networking_onos ./tools/i18n_cfg.py
+
+[testenv:venv]
+commands = {posargs}
+
+[testenv:cover]
+commands = python setup.py testr --coverage --testr-args='{posargs}'
+
+[testenv:docs]
+commands = python setup.py build_sphinx
+
+[hacking]
+import_exceptions = neutron.i18n
+local-check-factory = neutron.hacking.checks.factory
+show-source = True
+ignore = E123,E124,E125,H803
+
+[flake8]
+# H803 skipped on purpose per list discussion.
+# E123, E125 skipped as they are invalid PEP-8.
+show-source = True
+ignore = E123,E125,H803
+builtins = _
+exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build,tools