diff options
author | Ashlee Young <ashlee@onosfw.com> | 2015-10-30 09:20:45 -0700 |
---|---|---|
committer | Ashlee Young <ashlee@onosfw.com> | 2015-10-30 09:20:45 -0700 |
commit | 89a574196783f93e32d81244f37f726be11bbf30 (patch) | |
tree | 408c721579d29d49eebf50c78b7f4a8da7a6489e /framework/src/openstack/neutron | |
parent | 62bb467ca10e4fd0ca23499953bc7f7f413dee16 (diff) |
New ML2/L3 plugin to support SFC
Change-Id: Ie778a2b2e09a29972e28d70c8eedee407b1d8eb6
Signed-off-by: Ashlee Young <ashlee@onosfw.com>
Diffstat (limited to 'framework/src/openstack/neutron')
53 files changed, 2780 insertions, 0 deletions
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 index 00000000..7b5fe1b3 --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/.mailmap @@ -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 index 00000000..d343d72a --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/.pylintrc @@ -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 index 00000000..4b24f61e --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/.testr.conf @@ -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 index 00000000..44cb395d --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/CONTRIBUTING.rst @@ -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 index 00000000..06d3f2c1 --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/HACKING.rst @@ -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 index 00000000..68c771a0 --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/LICENSE @@ -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 index 00000000..f1c38fb2 --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/MANIFEST.in @@ -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 index 00000000..2a9d3291 --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/README.rst @@ -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 index 00000000..657fb412 --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/TESTING.rst @@ -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 index 00000000..15cd6cb7 --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/babel.cfg @@ -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 index 00000000..8519f06d --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/doc/source/conf.py @@ -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 index 00000000..1728a61c --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/doc/source/contributing.rst @@ -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 index 00000000..8b8ac262 --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/doc/source/index.rst @@ -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 index 00000000..2228fde0 --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/doc/source/installation.rst @@ -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 index 00000000..a6210d3d --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/doc/source/readme.rst @@ -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 index 00000000..fdc232a2 --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/doc/source/usage.rst @@ -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 index 00000000..45ad6ac8 --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/etc/conf_onos.ini @@ -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 index 00000000..d0b36107 --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/__init__.py @@ -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 index 00000000..e69de29b --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/common/__init__.py 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 index 00000000..8dc72286 --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/common/config.py @@ -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 index 00000000..49477149 --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/common/utils.py @@ -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 index 00000000..e69de29b --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/__init__.py 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 index 00000000..04ca224b --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/l3/README @@ -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 index 00000000..e69de29b --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/l3/__init__.py 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 index 00000000..2db3ad35 --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/l3/driver.py @@ -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 index 00000000..0748724e --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/l3/floating_ip.py @@ -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 index 00000000..495dacd6 --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/l3/router.py @@ -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 index 00000000..c3c722c8 --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/ml2/README @@ -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 index 00000000..e69de29b --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/ml2/__init__.py 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 index 00000000..b78775f4 --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/plugins/ml2/driver.py @@ -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 index 00000000..e69de29b --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/__init__.py 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 index 00000000..e69de29b --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/unit/__init__.py 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 index 00000000..e69de29b --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/unit/plugins/__init__.py 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 index 00000000..e69de29b --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/unit/plugins/l3/__init__.py 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 index 00000000..c0bba85a --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/unit/plugins/l3/test_driver.py @@ -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 index 00000000..e69de29b --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/unit/plugins/ml2/__init__.py 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 index 00000000..89969239 --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/networking_onos/tests/unit/plugins/ml2/test_driver.py @@ -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 index 00000000..7caa758d --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/openstack-common.conf @@ -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 index 00000000..26bb9244 --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/requirements.txt @@ -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 index 00000000..d7fd5b1d --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/setup.cfg @@ -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 index 00000000..782bb21f --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/setup.py @@ -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 index 00000000..2bf64e0b --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/test-requirements.txt @@ -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 index 00000000..e9d178ee --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/check_bash.sh @@ -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 index 00000000..697ad180 --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/check_i18n.py @@ -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 index 00000000..3d1391d9 --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/check_i18n_test_case.txt @@ -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 index 00000000..27bc219f --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/clean.sh @@ -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 index 00000000..5ad1a514 --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/i18n_cfg.py @@ -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 index 00000000..f8fb8fa2 --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/install_venv.py @@ -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 index 00000000..e279159a --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/install_venv_common.py @@ -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 index 00000000..a40f2482 --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/pretty_tox.sh @@ -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 index 00000000..73f2f105 --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/subunit-trace.py @@ -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 index 00000000..dea5c5fc --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tools/with_venv.sh @@ -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 index 00000000..41b875ed --- /dev/null +++ b/framework/src/openstack/neutron/plugin/networking-onos/networking-onos/tox.ini @@ -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 |