From bfc6a83048b47ebfda72100179ac90e1b8355875 Mon Sep 17 00:00:00 2001 From: Chaoyi Huang Date: Fri, 11 Dec 2015 15:29:10 +0800 Subject: [PATCH] Change the gate to OpenStack infrastrucure Change the gate to OpenStack infrastrcuture and update the README.md to reflect the work on the stateless design proposal and branch. BP: https://blueprints.launchpad.net/tricircle/+spec/new-design Change-Id: I51accdf33d8d9e50774c16d7649975ca91c2f5ec Signed-off-by: Chaoyi Huang --- .gitignore | 88 ++++++++++--------- .testr.conf | 7 ++ README.md | 4 +- doc/source/conf.py | 75 ++++++++++++++++ doc/source/index.rst | 21 +++++ requirements.txt | 44 ++++++---- setup.cfg | 48 ++++++++++ setup.py | 29 ++++++ test-requirements.txt | 23 +++++ tox.ini | 43 +++++++++ .../tests/unit/api/controllers/test_root.py | 42 +++++++++ .../tests/unit/networking/test_plugin.py | 9 +- tricircle/tests/unit/networking/test_rpc.py | 9 +- 13 files changed, 379 insertions(+), 63 deletions(-) create mode 100644 .testr.conf create mode 100755 doc/source/conf.py create mode 100644 doc/source/index.rst create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 test-requirements.txt create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore index 2101ce8a..26c75d8c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,45 +1,53 @@ -*.DS_Store -*.egg* -*.log -*.mo -*.pyc -*.swo -*.swp -*.sqlite -*.iml -*~ -.autogenerated +*.py[cod] + +# C extensions +*.so + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +var +sdist +develop-eggs +.installed.cfg +lib +lib64 + +# Installer logs +pip-log.txt + +# Unit test / coverage reports .coverage -.nova-venv +.tox +nosetests.xml +.testrepository +.venv + +# Translations +*.mo + +# Mr Developer +.mr.developer.cfg .project .pydevproject -.ropeproject -.testrepository/ -.tox -.idea -.venv + +# Complexity +output/*.html +output/*/index.html + +# Sphinx +doc/build + +# pbr generates these AUTHORS -Authors -build-stamp -build/* -CA/ ChangeLog -coverage.xml -cover/* -covhtml -dist/* -doc/source/api/* -doc/build/* -etc/nova/nova.conf.sample -instances -keeper -keys -local_settings.py -MANIFEST -nosetests.xml -nova/tests/cover/* -nova/vcsversion.py -tools/conf/nova.conf* -tools/lintstack.head.py -tools/pylint_exceptions -etc/nova/nova.conf.sample + +# Editors +*~ +.*.swp +.*sw? + diff --git a/.testr.conf b/.testr.conf new file mode 100644 index 00000000..6d83b3c4 --- /dev/null +++ b/.testr.conf @@ -0,0 +1,7 @@ +[DEFAULT] +test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \ + OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \ + OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \ + ${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION +test_id_option=--load-list $IDFILE +test_list_option=--list diff --git a/README.md b/README.md index ce871e44..a5524d86 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # Tricircle -(For PoC source code, please switch to ["poc"](https://github.com/openstack/tricircle/tree/poc) tag, or ["stable/fortest"](https://github.com/openstack/tricircle/tree/stable/fortest) branch) +(Attention Please, Stateless Design Proposal is being worked on the ["experiment"](https://github.com/openstack/tricircle/tree/experiment) branch). + +(The origningal PoC source code, please switch to ["poc"](https://github.com/openstack/tricircle/tree/poc) tag, or ["stable/fortest"](https://github.com/openstack/tricircle/tree/stable/fortest) branch) Tricircle is a openstack project that aims to deal with OpenStack deployment across multiple sites. It provides users a single management view by having only one OpenStack instance on behalf of all the involved ones. It essentially serves as a communication bus between the central OpenStack instance and the other OpenStack instances that are called upon. diff --git a/doc/source/conf.py b/doc/source/conf.py new file mode 100755 index 00000000..e1d893da --- /dev/null +++ b/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'tricircle' +copyright = u'2015, 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/doc/source/index.rst b/doc/source/index.rst new file mode 100644 index 00000000..97797499 --- /dev/null +++ b/doc/source/index.rst @@ -0,0 +1,21 @@ +.. tricircle documentation master file, created by + sphinx-quickstart on Wed Dec 2 17:00:36 2015. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to tricircle's documentation! +======================================================== + +Contents: + +.. toctree:: + :maxdepth: 2 + + api_v1 + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/requirements.txt b/requirements.txt index be9d6890..f0aa5075 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,8 @@ # 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<2.0,>=1.3 +pbr>=1.6 +Babel>=1.3 Paste PasteDeploy>=1.5.0 @@ -9,30 +10,37 @@ Routes!=2.0,!=2.1,>=1.12.3;python_version=='2.7' Routes!=2.0,>=1.12.3;python_version!='2.7' debtcollector>=0.3.0 # Apache-2.0 eventlet>=0.17.4 -pecan>=0.9.0 +pecan>=1.0.0 greenlet>=0.3.2 httplib2>=0.7.5 -requests>=2.5.2 -Jinja2>=2.6 # BSD License (3 clause) -keystonemiddleware>=2.0.0 -netaddr>=0.7.12 +requests>=2.8.1 +Jinja2>=2.8 # BSD License (3 clause) +keystonemiddleware>=4.0.0 +netaddr!=0.7.16,>=0.7.12 +netifaces>=0.10.4 retrying!=1.3.0,>=1.2.3 # Apache-2.0 -SQLAlchemy<1.1.0,>=0.9.7 +SQLAlchemy<1.1.0,>=0.9.9 WebOb>=1.2.3 -python-keystoneclient>=1.6.0 -alembic>=0.7.2 +python-cinderclient>=1.3.1 +python-glanceclient>=0.18.0 +python-keystoneclient!=1.8.0,>=1.6.0 +python-neutronclient>=2.6.0 +python-novaclient>=2.29.0,!=2.33.0 +alembic>=0.8.0 six>=1.9.0 stevedore>=1.5.0 # Apache-2.0 -oslo.concurrency>=2.1.0 # Apache-2.0 -oslo.config>=1.11.0 # Apache-2.0 +oslo.concurrency>=2.3.0 # Apache-2.0 +oslo.config>=2.7.0 # Apache-2.0 oslo.context>=0.2.0 # Apache-2.0 -oslo.db>=1.12.0 # Apache-2.0 +oslo.db>=4.1.0 # Apache-2.0 oslo.i18n>=1.5.0 # Apache-2.0 -oslo.log>=1.6.0 # Apache-2.0 -oslo.messaging>=1.16.0 # Apache-2.0 -oslo.middleware>=2.4.0 # Apache-2.0 +oslo.log>=1.12.0 # Apache-2.0 +oslo.messaging!=2.8.0,!=3.1.0,>2.6.1 # Apache-2.0 +oslo.middleware>=3.0.0 # Apache-2.0 oslo.policy>=0.5.0 # Apache-2.0 oslo.rootwrap>=2.0.0 # Apache-2.0 -oslo.serialization>=1.4.0 # Apache-2.0 -oslo.service>=0.1.0 # Apache-2.0 -oslo.utils>=1.9.0 # Apache-2.0 +oslo.serialization>=1.10.0 # Apache-2.0 +oslo.service>=1.0.0 # Apache-2.0 +oslo.utils!=3.1.0,>=2.8.0 # Apache-2.0 +oslo.versionedobjects>=0.13.0 +sqlalchemy-migrate>=0.9.6 diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..7627afd3 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,48 @@ +[metadata] +name = tricircle +summary = Tricircle is an OpenStack project that aims to deal with OpenStack deployment across multiple sites. +description-file = + README.md +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 + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.3 + Programming Language :: Python :: 3.4 + +[files] +packages = + tricircle + +[build_sphinx] +source-dir = doc/source +build-dir = doc/build +all_files = 1 + +[upload_sphinx] +upload-dir = doc/build/html + +[compile_catalog] +directory = tricircle/locale +domain = tricircle + +[update_catalog] +domain = tricircle +output_dir = tricircle/locale +input_file = tricircle/locale/tricircle.pot + +[extract_messages] +keywords = _ gettext ngettext l_ lazy_gettext +mapping_file = babel.cfg +output_file = tricircle/locale/tricircle.pot + +[entry_points] diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..056c16c2 --- /dev/null +++ b/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'], + pbr=True) diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 00000000..30aa4011 --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1,23 @@ +# 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.2 + +cliff>=1.15.0 # Apache-2.0 +coverage>=3.6 +fixtures>=1.3.1 +mock>=1.2 +python-subunit>=0.0.18 +requests-mock>=0.7.0 # Apache-2.0 +sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 +oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0 +testrepository>=0.0.18 +testtools>=1.4.0 +testresources>=0.2.4 +testscenarios>=0.4 +WebTest>=2.0 +oslotest>=1.10.0 # Apache-2.0 +os-testr>=0.4.1 +tempest-lib>=0.11.0 +ddt>=1.0.1 +pylint==1.4.5 # GNU GPL v2 diff --git a/tox.ini b/tox.ini new file mode 100644 index 00000000..a93057b5 --- /dev/null +++ b/tox.ini @@ -0,0 +1,43 @@ +[tox] +minversion = 1.6 +envlist = py34,py27,pypy,pep8 +skipsdist = True + +[testenv] +sitepackages = True +usedevelop = True +install_command = + pip install -U --force-reinstall {opts} {packages} +setenv = + VIRTUAL_ENV={envdir} +deps = + -egit+https://git.openstack.org/openstack/neutron@master#egg=neutron + -r{toxinidir}/test-requirements.txt +commands = python setup.py testr --slowest --testr-args='{posargs}' +whitelist_externals = rm + +[testenv:common-constraints] +install_command = {toxinidir}/tools/tox_install.sh constrained -c{env:UPPER_CONTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages} + +[testenv:pep8] +commands = flake8 + +[testenv:venv] +commands = {posargs} + +[testenv:cover] +commands = python setup.py testr --coverage --testr-args='{posargs}' + +[testenv:docs] +commands = python setup.py build_sphinx + +[testenv:debug] +commands = oslo_debug_helper {posargs} + +[flake8] +# E123, E125 skipped as they are invalid PEP-8. + +show-source = True +ignore = E123,E125 +builtins = _ +exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build diff --git a/tricircle/tests/unit/api/controllers/test_root.py b/tricircle/tests/unit/api/controllers/test_root.py index 0185dd78..4a86953c 100644 --- a/tricircle/tests/unit/api/controllers/test_root.py +++ b/tricircle/tests/unit/api/controllers/test_root.py @@ -20,12 +20,24 @@ import unittest import pecan import tricircle.api.controllers.root as root_controller + +from tricircle.common import cascading_site_api from tricircle.common import context +from tricircle.common import rpc + from tricircle.db import client from tricircle.db import core from tricircle.db import models +def fake_create_client(target): + return None + + +def fake_cast_message(self, context, method, payload): + return None + + class ControllerTest(unittest.TestCase): def setUp(self): core.initialize() @@ -49,6 +61,9 @@ class SitesControllerTest(ControllerTest): super(SitesControllerTest, self).setUp() self.controller = root_controller.SitesController() + @patch.object(rpc, 'create_client', new=fake_create_client) + @patch.object(cascading_site_api.CascadingSiteNotifyAPI, + '_cast_message', new=fake_cast_message) def test_post_top_site(self): kw = {'name': 'TopSite', 'top': True} site_id = self.controller.post(**kw)['site']['site_id'] @@ -56,6 +71,9 @@ class SitesControllerTest(ControllerTest): self.assertEqual(site['site_name'], 'TopSite') self.assertEqual(site['az_id'], '') + @patch.object(rpc, 'create_client', new=fake_create_client) + @patch.object(cascading_site_api.CascadingSiteNotifyAPI, + '_cast_message', new=fake_cast_message) @patch.object(client.Client, 'create_resources') def test_post_bottom_site(self, mock_method): kw = {'name': 'BottomSite'} @@ -66,11 +84,17 @@ class SitesControllerTest(ControllerTest): mock_method.assert_called_once_with('aggregate', self.context, 'ag_BottomSite', 'az_BottomSite') + @patch.object(rpc, 'create_client', new=fake_create_client) + @patch.object(cascading_site_api.CascadingSiteNotifyAPI, + '_cast_message', new=fake_cast_message) def test_post_site_name_missing(self): kw = {'top': True} self.controller.post(**kw) pecan.abort.assert_called_once_with(400, 'Name of site required') + @patch.object(rpc, 'create_client', new=fake_create_client) + @patch.object(cascading_site_api.CascadingSiteNotifyAPI, + '_cast_message', new=fake_cast_message) def test_post_conflict(self): kw = {'name': 'TopSite', 'top': True} self.controller.post(**kw) @@ -78,6 +102,9 @@ class SitesControllerTest(ControllerTest): pecan.abort.assert_called_once_with(409, 'Site with name TopSite exists') + @patch.object(rpc, 'create_client', new=fake_create_client) + @patch.object(cascading_site_api.CascadingSiteNotifyAPI, + '_cast_message', new=fake_cast_message) def test_post_not_admin(self): self.context.is_admin = False kw = {'name': 'TopSite', 'top': True} @@ -85,6 +112,9 @@ class SitesControllerTest(ControllerTest): pecan.abort.assert_called_once_with( 400, 'Admin role required to create sites') + @patch.object(rpc, 'create_client', new=fake_create_client) + @patch.object(cascading_site_api.CascadingSiteNotifyAPI, + '_cast_message', new=fake_cast_message) @patch.object(client.Client, 'create_resources') def test_post_decide_top(self, mock_method): # 'top' default to False @@ -100,6 +130,9 @@ class SitesControllerTest(ControllerTest): 'az_Site%d' % i) for i in xrange(2, 4)] mock_method.assert_has_calls(calls) + @patch.object(rpc, 'create_client', new=fake_create_client) + @patch.object(cascading_site_api.CascadingSiteNotifyAPI, + '_cast_message', new=fake_cast_message) @patch.object(models, 'create_site') def test_post_create_site_exception(self, mock_method): mock_method.side_effect = Exception @@ -121,6 +154,9 @@ class SitesControllerTest(ControllerTest): sites = models.list_sites(self.context, site_filter) self.assertEqual(len(sites), 0) + @patch.object(rpc, 'create_client', new=fake_create_client) + @patch.object(cascading_site_api.CascadingSiteNotifyAPI, + '_cast_message', new=fake_cast_message) def test_get_one(self): kw = {'name': 'TopSite', 'top': True} site_id = self.controller.post(**kw)['site']['site_id'] @@ -129,11 +165,17 @@ class SitesControllerTest(ControllerTest): 'site_name': 'TopSite', 'az_id': ''}) + @patch.object(rpc, 'create_client', new=fake_create_client) + @patch.object(cascading_site_api.CascadingSiteNotifyAPI, + '_cast_message', new=fake_cast_message) def test_get_one_not_found(self): self.controller.get_one('fake_id') pecan.abort.assert_called_once_with(404, 'Site with id fake_id not found') + @patch.object(rpc, 'create_client', new=fake_create_client) + @patch.object(cascading_site_api.CascadingSiteNotifyAPI, + '_cast_message', new=fake_cast_message) @patch.object(client.Client, 'create_resources', new=mock.Mock) def test_get_all(self): kw1 = {'name': 'TopSite', 'top': True} diff --git a/tricircle/tests/unit/networking/test_plugin.py b/tricircle/tests/unit/networking/test_plugin.py index 01eff127..f9f9e040 100644 --- a/tricircle/tests/unit/networking/test_plugin.py +++ b/tricircle/tests/unit/networking/test_plugin.py @@ -45,6 +45,11 @@ def fake_update_port(instance, context, port_id, port): return FAKE_PORT +class FakePlugin(TricirclePlugin): + def __init__(self): + pass + + class TricirclePluginTest(unittest.TestCase): def setUp(self): FAKE_PORT['status'] = neutron_const.PORT_STATUS_DOWN @@ -55,7 +60,7 @@ class TricirclePluginTest(unittest.TestCase): @patch.object(db_base_plugin_v2.NeutronDbPluginV2, 'get_port', new=fake_get_port) def test_update_port_status(self): - plugin = TricirclePlugin() + plugin = FakePlugin() # this method requires a neutron context, but for test we just pass # a tricircle context port = plugin.update_port_status(context.Context(), FAKE_PORT_ID, @@ -69,7 +74,7 @@ class TricirclePluginTest(unittest.TestCase): @patch.object(db_base_plugin_v2.NeutronDbPluginV2, 'get_port', new=fake_get_port) def test_update_port_status_port_not_found(self): - plugin = TricirclePlugin() + plugin = FakePlugin() port = plugin.update_port_status(context.Context(), 'no_such_port', neutron_const.PORT_STATUS_ACTIVE) self.assertEqual(FAKE_PORT['status'], neutron_const.PORT_STATUS_DOWN) diff --git a/tricircle/tests/unit/networking/test_rpc.py b/tricircle/tests/unit/networking/test_rpc.py index 70fc8cc4..e072595e 100644 --- a/tricircle/tests/unit/networking/test_rpc.py +++ b/tricircle/tests/unit/networking/test_rpc.py @@ -29,6 +29,11 @@ FAKE_PORT_ID = 'fake_port_uuid' FAKE_CONTEXT = object() +class FakePlugin(plugin.TricirclePlugin): + def __init__(self): + pass + + class RpcCallbacksTest(unittest.TestCase): def setUp(self): self.callbacks = rpc.RpcCallbacks() @@ -39,7 +44,7 @@ class RpcCallbacksTest(unittest.TestCase): 'get_plugin') as get_plugin_method: with patch.object(plugin.TricirclePlugin, 'update_port_status') as update_method: - get_plugin_method.return_value = plugin.TricirclePlugin() + get_plugin_method.return_value = FakePlugin() self.callbacks.update_port_up(FAKE_CONTEXT, port_id=FAKE_PORT_ID) update_method.assert_called_once_with( @@ -52,7 +57,7 @@ class RpcCallbacksTest(unittest.TestCase): 'get_plugin') as get_plugin_method: with patch.object(plugin.TricirclePlugin, 'update_port_status') as update_method: - get_plugin_method.return_value = plugin.TricirclePlugin() + get_plugin_method.return_value = FakePlugin() self.callbacks.update_port_down(FAKE_CONTEXT, port_id=FAKE_PORT_ID) update_method.assert_called_once_with(