diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f85b714 --- /dev/null +++ b/.gitignore @@ -0,0 +1,39 @@ +*.py[co] +*.egg +*.egg-info +dist +build +eggs +parts +var +sdist +develop-eggs +.installed.cfg +pip-log.txt +.tox +*.mo +.mr.developer.cfg +.DS_Store +Thumbs.db +.venv +.idea +out +target +*.iml +*.ipr +*.iws +doc/html +doc/source/apidoc +doc/source/api +doc/build +*.db +.coverage +nosetests.xml +pylint-report.txt +ChangeLog +cscope.out +horizon +trove_dashboard/test/configs/config.conf +trove_dashboard/test/.secret_key_store +AUTHORS +*.lock diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 0000000..b1c7f96 --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,20 @@ +If you would like to contribute to the development of OpenStack, +you must follow the steps in this page: + + http://docs.openstack.org/infra/manual/developers.html + +You can find more trove specific info: + + http://docs.openstack.org/developer/trove/ + +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/trove diff --git a/HACKING.rst b/HACKING.rst new file mode 100644 index 0000000..18ed211 --- /dev/null +++ b/HACKING.rst @@ -0,0 +1,12 @@ +Trove Style Commandments +========================== + +- Step 1: Read the OpenStack Style Commandments + http://docs.openstack.org/developer/hacking/ +- Step 2: Read on + +Trove Specific Commandments +----------------------------- + +None so far + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..68c771a --- /dev/null +++ b/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/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..bebfca8 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,4 @@ +recursive-include trove_dashboard *.html *.scss *.js + +include AUTHORS +include ChangeLog diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..f6f3bec --- /dev/null +++ b/README.rst @@ -0,0 +1,38 @@ +OpenStack Dashboard plugin for Trove project +============================================ + +How to use with Horizon on server: +---------------------------------- + +Use pip to install the package on the server running Horizon. Then either copy +or link the files in trove_dashboard/enabled to +openstack_dashboard/local/enabled. This step will cause the Horizon service to +pick up the trove plugin when it starts. + +How to use with devstack: +------------------------- + +Add the following to your devstack ``local.conf`` file:: + +enable_plugin trove-dashboard git://git.openstack.org/openstack/trove-dashboard + + +To run unit tests: +------------------ + +./run_tests.sh + +NOTE: +===== + +As of the Mitaka release, the dashboard for trove is now maintained +outside of the Horizon codebase, in this repository. + +Links: +------ + +Trove project: https://git.openstack.org/openstack/trove + +Trove at wiki.openstack.org: https://wiki.openstack.org/wiki/Trove + +Launchpad project: https://launchpad.net/trove diff --git a/devstack/plugin.sh b/devstack/plugin.sh new file mode 100644 index 0000000..ee8f2f9 --- /dev/null +++ b/devstack/plugin.sh @@ -0,0 +1,47 @@ +# plugin.sh - DevStack plugin.sh dispatch script trove-dashboard + +TROVE_DASHBOARD_DIR=$(cd $(dirname $BASH_SOURCE)/.. && pwd) + +function install_trove_dashboard { + sudo pip install --upgrade ${TROVE_DASHBOARD_DIR} + + cp -a ${TROVE_DASHBOARD_DIR}/trove_dashboard/enabled/* ${DEST}/horizon/openstack_dashboard/enabled/ + python ${DEST}/horizon/manage.py collectstatic --noinput + python ${DEST}/horizon/manage.py compress --force +} + +# check for service enabled +if is_service_enabled trove-dashboard; then + + if [[ "$1" == "stack" && "$2" == "pre-install" ]]; then + # Set up system services + # no-op + : + + elif [[ "$1" == "stack" && "$2" == "install" ]]; then + # Perform installation of service source + # no-op + : + + elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then + # Configure after the other layer 1 and 2 services have been configured + echo_summary "Installing Trove UI" + install_trove_dashboard + + elif [[ "$1" == "stack" && "$2" == "extra" ]]; then + # no-op + : + fi + + if [[ "$1" == "unstack" ]]; then + # no-op + : + fi + + if [[ "$1" == "clean" ]]; then + # Remove state and transient data + # Remember clean.sh first calls unstack.sh + # no-op + : + fi +fi diff --git a/devstack/settings b/devstack/settings new file mode 100644 index 0000000..cca7136 --- /dev/null +++ b/devstack/settings @@ -0,0 +1,2 @@ +# settings file for trove-dashboard plugin +enable_service trove-dashboard diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..7acb0a2 --- /dev/null +++ b/manage.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +# 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 + +from django.core.management import execute_from_command_line + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", + "trove_dashboard.test.settings") + execute_from_command_line(sys.argv) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..235d709 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,14 @@ +# 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 +# Horizon Core Requirements +Babel>=1.3 +Django<1.9,>=1.8 +django-compressor>=1.4 +django-openstack-auth>=2.0.0 +iso8601>=0.1.9 +python-keystoneclient!=1.8.0,>=1.6.0 +python-swiftclient>=2.2.0 +python-troveclient>=1.2.0 diff --git a/run_tests.sh b/run_tests.sh new file mode 100755 index 0000000..285a48e --- /dev/null +++ b/run_tests.sh @@ -0,0 +1,554 @@ +#!/bin/bash + +set -o errexit + +function usage { + echo "Usage: $0 [OPTION]..." + echo "Run Horizon's test suite(s)" + echo "" + echo " -V, --virtual-env Always use virtualenv. Install automatically" + echo " if not present" + echo " -N, --no-virtual-env Don't use virtualenv. Run tests in local" + echo " environment" + echo " -c, --coverage Generate reports using Coverage" + echo " -f, --force Force a clean re-build of the virtual" + echo " environment. Useful when dependencies have" + echo " been added." + echo " -m, --manage Run a Django management command." + echo " --makemessages Create/Update English translation files." + echo " --compilemessages Compile all translation files." + echo " --check-only Do not update translation files (--makemessages only)." + echo " --pseudo Pseudo translate a language." + echo " -p, --pep8 Just run pep8" + echo " -8, --pep8-changed []" + echo " Just run PEP8 and HACKING compliance check" + echo " on files changed since HEAD~1 (or )" + echo " -P, --no-pep8 Don't run pep8 by default" + echo " -t, --tabs Check for tab characters in files." + echo " -y, --pylint Just run pylint" + echo " -q, --quiet Run non-interactively. (Relatively) quiet." + echo " Implies -V if -N is not set." + echo " --only-selenium Run only the Selenium unit tests" + echo " --with-selenium Run unit tests including Selenium tests" + echo " --selenium-headless Run Selenium tests headless" + echo " --integration Run the integration tests (requires a running " + echo " OpenStack environment)" + echo " --runserver Run the Django development server for" + echo " openstack_dashboard in the virtual" + echo " environment." + echo " --docs Just build the documentation" + echo " --backup-environment Make a backup of the environment on exit" + echo " --restore-environment Restore the environment before running" + echo " --destroy-environment Destroy the environment and exit" + echo " -h, --help Print this usage message" + echo "" + echo "Note: with no options specified, the script will try to run the tests in" + echo " a virtual environment, If no virtualenv is found, the script will ask" + echo " if you would like to create one. If you prefer to run tests NOT in a" + echo " virtual environment, simply pass the -N option." + exit +} + +# DEFAULTS FOR RUN_TESTS.SH +# +root=`pwd -P` +venv=$root/.venv +venv_env_version=$venv/environments +with_venv=tools/with_venv.sh +included_dirs="trove_dashboard" + +always_venv=0 +backup_env=0 +command_wrapper="" +destroy=0 +force=0 +just_pep8=0 +just_pep8_changed=0 +no_pep8=0 +just_pylint=0 +just_docs=0 +just_tabs=0 +never_venv=0 +quiet=0 +restore_env=0 +runserver=0 +only_selenium=0 +with_selenium=0 +selenium_headless=0 +integration=0 +testopts="" +testargs="" +with_coverage=0 +makemessages=0 +compilemessages=0 +check_only=0 +pseudo=0 +manage=0 + +# Jenkins sets a "JOB_NAME" variable, if it's not set, we'll make it "default" +[ "$JOB_NAME" ] || JOB_NAME="default" + +function process_option { + # If running manage command, treat the rest of options as arguments. + if [ $manage -eq 1 ]; then + testargs="$testargs $1" + return 0 + fi + + case "$1" in + -h|--help) usage;; + -V|--virtual-env) always_venv=1; never_venv=0;; + -N|--no-virtual-env) always_venv=0; never_venv=1;; + -p|--pep8) just_pep8=1;; + -8|--pep8-changed) just_pep8_changed=1;; + -P|--no-pep8) no_pep8=1;; + -y|--pylint) just_pylint=1;; + -f|--force) force=1;; + -t|--tabs) just_tabs=1;; + -q|--quiet) quiet=1;; + -c|--coverage) with_coverage=1;; + -m|--manage) manage=1;; + --makemessages) makemessages=1;; + --compilemessages) compilemessages=1;; + --check-only) check_only=1;; + --pseudo) pseudo=1;; + --only-selenium) only_selenium=1;; + --with-selenium) with_selenium=1;; + --selenium-headless) selenium_headless=1;; + --integration) integration=1;; + --docs) just_docs=1;; + --runserver) runserver=1;; + --backup-environment) backup_env=1;; + --restore-environment) restore_env=1;; + --destroy-environment) destroy=1;; + -*) testopts="$testopts $1";; + *) testargs="$testargs $1" + esac +} + +function run_management_command { + ${command_wrapper} python $root/manage.py $testopts $testargs +} + +function run_server { + echo "Starting Django development server..." + ${command_wrapper} python $root/manage.py runserver $testopts $testargs + echo "Server stopped." +} + +function run_pylint { + echo "Running pylint ..." + PYTHONPATH=$root ${command_wrapper} pylint --rcfile=.pylintrc -f parseable $included_dirs > pylint.txt || true + CODE=$? + grep Global -A2 pylint.txt + if [ $CODE -lt 32 ]; then + echo "Completed successfully." + exit 0 + else + echo "Completed with problems." + exit $CODE + fi +} + +function warn_on_flake8_without_venv { + set +o errexit + ${command_wrapper} python -c "import hacking" 2>/dev/null + no_hacking=$? + set -o errexit + if [ $never_venv -eq 1 -a $no_hacking -eq 1 ]; then + echo "**WARNING**:" >&2 + echo "OpenStack hacking is not installed on your host. Its detection will be missed." >&2 + echo "Please install or use virtual env if you need OpenStack hacking detection." >&2 + fi +} + +function run_pep8 { + echo "Running flake8 ..." + warn_on_flake8_without_venv + DJANGO_SETTINGS_MODULE=trove_dashboard.test.settings ${command_wrapper} flake8 +} + +function run_pep8_changed { + # NOTE(gilliard) We want use flake8 to check the entirety of every file that has + # a change in it. Unfortunately the --filenames argument to flake8 only accepts + # file *names* and there are no files named (eg) "nova/compute/manager.py". The + # --diff argument behaves surprisingly as well, because although you feed it a + # diff, it actually checks the file on disk anyway. + local base_commit=${testargs:-HEAD~1} + files=$(git diff --name-only $base_commit | tr '\n' ' ') + echo "Running flake8 on ${files}" + warn_on_flake8_without_venv + diff -u --from-file /dev/null ${files} | DJANGO_SETTINGS_MODULE=trove_dashboard.test.settings ${command_wrapper} flake8 --diff + exit +} + +function run_sphinx { + echo "Building sphinx..." + DJANGO_SETTINGS_MODULE=trove_dashboard.test.settings ${command_wrapper} python setup.py build_sphinx + echo "Build complete." +} + +function tab_check { + TAB_VIOLATIONS=`find $included_dirs -type f -regex ".*\.\(css\|js\|py\|html\)" -print0 | xargs -0 awk '/\t/' | wc -l` + if [ $TAB_VIOLATIONS -gt 0 ]; then + echo "TABS! $TAB_VIOLATIONS of them! Oh no!" + HORIZON_FILES=`find $included_dirs -type f -regex ".*\.\(css\|js\|py|\html\)"` + for TABBED_FILE in $HORIZON_FILES + do + TAB_COUNT=`awk '/\t/' $TABBED_FILE | wc -l` + if [ $TAB_COUNT -gt 0 ]; then + echo "$TABBED_FILE: $TAB_COUNT" + fi + done + fi + return $TAB_VIOLATIONS; +} + +function destroy_venv { + echo "Cleaning environment..." + echo "Removing virtualenv..." + rm -rf $venv + echo "Virtualenv removed." +} + +function environment_check { + echo "Checking environment." + if [ -f $venv_env_version ]; then + set +o errexit + cat requirements.txt test-requirements.txt | cmp $venv_env_version - > /dev/null + local env_check_result=$? + set -o errexit + if [ $env_check_result -eq 0 ]; then + # If the environment exists and is up-to-date then set our variables + command_wrapper="${root}/${with_venv}" + echo "Environment is up to date." + return 0 + fi + fi + + if [ $always_venv -eq 1 ]; then + install_venv + else + if [ ! -e ${venv} ]; then + echo -e "Environment not found. Install? (Y/n) \c" + else + echo -e "Your environment appears to be out of date. Update? (Y/n) \c" + fi + read update_env + if [ "x$update_env" = "xY" -o "x$update_env" = "x" -o "x$update_env" = "xy" ]; then + install_venv + else + # Set our command wrapper anyway. + command_wrapper="${root}/${with_venv}" + fi + fi +} + +function sanity_check { + # Anything that should be determined prior to running the tests, server, etc. + # Don't sanity-check anything environment-related in -N flag is set + if [ $never_venv -eq 0 ]; then + if [ ! -e ${venv} ]; then + echo "Virtualenv not found at $venv. Did install_venv.py succeed?" + exit 1 + fi + fi + # Remove .pyc files. This is sanity checking because they can linger + # after old files are deleted. + find . -name "*.pyc" -exec rm -rf {} \; +} + +function backup_environment { + if [ $backup_env -eq 1 ]; then + echo "Backing up environment \"$JOB_NAME\"..." + if [ ! -e ${venv} ]; then + echo "Environment not installed. Cannot back up." + return 0 + fi + if [ -d /tmp/.horizon_environment/$JOB_NAME ]; then + mv /tmp/.horizon_environment/$JOB_NAME /tmp/.horizon_environment/$JOB_NAME.old + rm -rf /tmp/.horizon_environment/$JOB_NAME + fi + mkdir -p /tmp/.horizon_environment/$JOB_NAME + cp -r $venv /tmp/.horizon_environment/$JOB_NAME/ + # Remove the backup now that we've completed successfully + rm -rf /tmp/.horizon_environment/$JOB_NAME.old + echo "Backup completed" + fi +} + +function restore_environment { + if [ $restore_env -eq 1 ]; then + echo "Restoring environment from backup..." + if [ ! -d /tmp/.horizon_environment/$JOB_NAME ]; then + echo "No backup to restore from." + return 0 + fi + + cp -r /tmp/.horizon_environment/$JOB_NAME/.venv ./ || true + + echo "Environment restored successfully." + fi +} + +function install_venv { + # Install with install_venv.py + export PIP_DOWNLOAD_CACHE=${PIP_DOWNLOAD_CACHE-/tmp/.pip_download_cache} + export PIP_USE_MIRRORS=true + if [ $quiet -eq 1 ]; then + export PIP_NO_INPUT=true + fi + echo "Fetching new src packages..." + rm -rf $venv/src + python tools/install_venv.py + command_wrapper="$root/${with_venv}" + # Make sure it worked and record the environment version + sanity_check + chmod -R 754 $venv + cat requirements.txt test-requirements.txt > $venv_env_version +} + +function run_tests { + sanity_check + + if [ $with_selenium -eq 1 ]; then + export WITH_SELENIUM=1 + elif [ $only_selenium -eq 1 ]; then + export WITH_SELENIUM=1 + export SKIP_UNITTESTS=1 + fi + + if [ $selenium_headless -eq 1 ]; then + export SELENIUM_HEADLESS=1 + fi + + # TODO(david-lyle) remove when configuration files for Trove are not loaded + # by default in Horizon + ${command_wrapper} python tools/clean_enabled_files.py + + if [ -z "$testargs" ]; then + run_tests_all + else + run_tests_subset + fi +} + +function run_tests_subset { + project=`echo $testargs | awk -F. '{print $1}'` + ${command_wrapper} python $root/manage.py test --settings=$project.test.settings $testopts $testargs +} + +function run_tests_all { + echo "Running trove_dashboard application tests" + export NOSE_XUNIT_FILE=trove_dashboard/nosetests.xml + if [ "$NOSE_WITH_HTML_OUTPUT" = '1' ]; then + export NOSE_HTML_OUT_FILE='trove_dashboard_nose_results.html' + fi + if [ $with_coverage -eq 1 ]; then + ${command_wrapper} python -m coverage.__main__ erase + coverage_run="python -m coverage.__main__ run -p" + fi + ${command_wrapper} ${coverage_run} $root/manage.py test trove_dashboard --settings=trove_dashboard.test.settings $testopts + # get results of the Horizon tests + trove_dashboard_RESULT=$? + + if [ $with_coverage -eq 1 ]; then + echo "Generating coverage reports" + ${command_wrapper} python -m coverage.__main__ combine + ${command_wrapper} python -m coverage.__main__ xml -i --omit='/usr*,setup.py,*egg*,.venv/*' + ${command_wrapper} python -m coverage.__main__ html -i --omit='/usr*,setup.py,*egg*,.venv/*' -d reports + fi + # Remove the leftover coverage files from the -p flag earlier. + rm -f .coverage.* + + PEP8_RESULT=0 + if [ $no_pep8 -eq 0 ] && [ $only_selenium -eq 0 ]; then + run_pep8 + PEP8_RESULT=$? + fi + + TEST_RESULT=$(($trove_dashboard_RESULT || $PEP8_RESULT)) + if [ $TEST_RESULT -eq 0 ]; then + echo "Tests completed successfully." + else + echo "Tests failed." + fi + exit $TEST_RESULT +} + +function run_integration_tests { + export INTEGRATION_TESTS=1 + + if [ $selenium_headless -eq 1 ]; then + export SELENIUM_HEADLESS=1 + fi + + export HORIZON_INTEGRATION_TESTS_CONFIG_FILE="trove_dashboard/test/integration_tests/horizon.conf" + + echo "Running Horizon integration tests..." + if [ -z "$testargs" ]; then + ${command_wrapper} nosetests trove_dashboard/test/integration_tests/tests + else + ${command_wrapper} nosetests $testargs + fi + exit 0 +} + +function run_makemessages { + OPTS="-l en --no-obsolete --settings=openstack_dashboard.test.settings" + DASHBOARD_OPTS="--extension=html,txt,csv --ignore=openstack" + echo -n "horizon: " + cd horizon + ${command_wrapper} $root/manage.py makemessages $OPTS + HORIZON_PY_RESULT=$? + echo -n "horizon javascript: " + ${command_wrapper} $root/manage.py makemessages -d djangojs $OPTS + HORIZON_JS_RESULT=$? + echo -n "openstack_dashboard: " + cd ../openstack_dashboard + ${command_wrapper} $root/manage.py makemessages $DASHBOARD_OPTS $OPTS + DASHBOARD_RESULT=$? + cd .. + if [ $check_only -eq 1 ]; then + git checkout -- horizon/locale/en/LC_MESSAGES/django*.po + git checkout -- openstack_dashboard/locale/en/LC_MESSAGES/django.po + fi + exit $(($HORIZON_PY_RESULT || $HORIZON_JS_RESULT || $DASHBOARD_RESULT)) +} + +function run_compilemessages { + OPTS="--settings=openstack_dashboard.test.settings" + cd horizon + ${command_wrapper} $root/manage.py compilemessages $OPTS + HORIZON_PY_RESULT=$? + cd ../openstack_dashboard + ${command_wrapper} $root/manage.py compilemessages $OPTS + DASHBOARD_RESULT=$? + cd .. + # English is the source language, so compiled catalogs are unnecessary. + rm -vf horizon/locale/en/LC_MESSAGES/django*.mo + rm -vf openstack_dashboard/locale/en/LC_MESSAGES/django.mo + exit $(($HORIZON_PY_RESULT || $DASHBOARD_RESULT)) +} + +function run_pseudo { + for lang in $testargs + # Use English po file as the source file/pot file just like real Horizon translations + do + ${command_wrapper} $root/tools/pseudo.py openstack_dashboard/locale/en/LC_MESSAGES/django.po openstack_dashboard/locale/$lang/LC_MESSAGES/django.po $lang + ${command_wrapper} $root/tools/pseudo.py horizon/locale/en/LC_MESSAGES/django.po horizon/locale/$lang/LC_MESSAGES/django.po $lang + ${command_wrapper} $root/tools/pseudo.py horizon/locale/en/LC_MESSAGES/djangojs.po horizon/locale/$lang/LC_MESSAGES/djangojs.po $lang + done + exit $? +} + + +# ---------PREPARE THE ENVIRONMENT------------ # + +# PROCESS ARGUMENTS, OVERRIDE DEFAULTS +for arg in "$@"; do + process_option $arg +done + +if [ $quiet -eq 1 ] && [ $never_venv -eq 0 ] && [ $always_venv -eq 0 ] +then + always_venv=1 +fi + +# If destroy is set, just blow it away and exit. +if [ $destroy -eq 1 ]; then + destroy_venv + exit 0 +fi + +# Ignore all of this if the -N flag was set +if [ $never_venv -eq 0 ]; then + + # Restore previous environment if desired + if [ $restore_env -eq 1 ]; then + restore_environment + fi + + # Remove the virtual environment if --force used + if [ $force -eq 1 ]; then + destroy_venv + fi + + # Then check if it's up-to-date + environment_check + + # Create a backup of the up-to-date environment if desired + if [ $backup_env -eq 1 ]; then + backup_environment + fi +fi + +# ---------EXERCISE THE CODE------------ # + +# Run management commands +if [ $manage -eq 1 ]; then + run_management_command + exit $? +fi + +# Build the docs +if [ $just_docs -eq 1 ]; then + run_sphinx + exit $? +fi + +# Update translation files +if [ $makemessages -eq 1 ]; then + run_makemessages + exit $? +fi + +# Compile translation files +if [ $compilemessages -eq 1 ]; then + run_compilemessages + exit $? +fi + +# Generate Pseudo translation +if [ $pseudo -eq 1 ]; then + run_pseudo + exit $? +fi + +# PEP8 +if [ $just_pep8 -eq 1 ]; then + run_pep8 + exit $? +fi + +if [ $just_pep8_changed -eq 1 ]; then + run_pep8_changed + exit $? +fi + +# Pylint +if [ $just_pylint -eq 1 ]; then + run_pylint + exit $? +fi + +# Tab checker +if [ $just_tabs -eq 1 ]; then + tab_check + exit $? +fi + +# Integration tests +if [ $integration -eq 1 ]; then + run_integration_tests + exit $? +fi + +# Django development server +if [ $runserver -eq 1 ]; then + run_server + exit $? +fi + +# Full test suite +run_tests || exit diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..c048245 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,46 @@ +[metadata] +name = trove-dashboard +summary = Trove Management Dashboard +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 + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.3 + Programming Language :: Python :: 3.4 + +[files] +packages = + trove_dashboard + +[build_sphinx] +source-dir = doc/source +build-dir = doc/build +all_files = 1 + +[upload_sphinx] +upload-dir = doc/build/html + +[compile_catalog] +directory = trove_dashboard/locale +domain = trove-dashboard + +[update_catalog] +domain = trove-dashboard +output_dir = trove_dashboard/locale +input_file = trove_dashboard/locale/trove_dashboard.pot + +[extract_messages] +keywords = _ gettext ngettext l_ lazy_gettext +mapping_file = babel.cfg +output_file = trove_dashboard/locale/trove_dashboard.pot diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..782bb21 --- /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>=1.8'], + pbr=True) diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 0000000..e1b87d8 --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1,22 @@ +# 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. + +-e git://github.com/openstack/horizon.git#egg=horizon + +hacking<0.11,>=0.10.0 +coverage>=3.6 +ddt>=0.7.0 +django-nose>=1.2 +discover +mock>=1.2 +mox3>=0.7.0 +python-subunit>=0.0.18 +selenium +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 +testscenarios>=0.4 +testtools>=1.4.0 +# This also needs xvfb library installed on your OS +xvfbwrapper>=0.1.3 #license: MIT diff --git a/tools/clean_enabled_files.py b/tools/clean_enabled_files.py new file mode 100644 index 0000000..5a30702 --- /dev/null +++ b/tools/clean_enabled_files.py @@ -0,0 +1,45 @@ +# 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 temporarily needed to allow the conversion from integrated +# Sahara content in Horizon to plugin based content. Horizon currently defines +# the same module name data_processing and imports it by default. This utility +# removes the configuration files that are responsible for importing the old +# version of the module. Only Sahara content configuration files are effected +# in Horizon. + +import os + +from openstack_dashboard import enabled as local_enabled + +from trove_dashboard import enabled + +ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) +WITH_VENV = os.path.join(ROOT, 'tools', 'with_venv.sh') + +def main(): + src_path = os.path.dirname(enabled.__file__) + dest_path = os.path.dirname(local_enabled.__file__) + + src_files = os.listdir(src_path) + for file in src_files: + # skip the __init__.py or bad things happen + if file == "__init__.py": + continue + + file_path = os.path.join(dest_path, file) + if os.path.isfile(file_path): + print ("removing ", file_path) + os.remove(file_path) + +if __name__ == '__main__': + main() diff --git a/tools/install_venv.py b/tools/install_venv.py new file mode 100644 index 0000000..8550e2c --- /dev/null +++ b/tools/install_venv.py @@ -0,0 +1,154 @@ +# Copyright 2012 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# Copyright 2012 OpenStack, LLC +# +# Copyright 2012 Nebula, Inc. +# +# 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 the OpenStack Dashboard development virtualenv. +""" + +import os +import subprocess +import sys + + +ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) +VENV = os.path.join(ROOT, '.venv') +WITH_VENV = os.path.join(ROOT, 'tools', 'with_venv.sh') +PIP_REQUIRES = os.path.join(ROOT, 'requirements.txt') +TEST_REQUIRES = os.path.join(ROOT, 'test-requirements.txt') + + +def die(message, *args): + print >> sys.stderr, message % args + sys.exit(1) + + +def run_command(cmd, redirect_output=True, check_exit_code=True, cwd=ROOT, + die_message=None): + """ + Runs a command in an out-of-process shell, returning the + output of that command. Working directory is ROOT. + """ + if redirect_output: + stdout = subprocess.PIPE + else: + stdout = None + + proc = subprocess.Popen(cmd, cwd=cwd, stdout=stdout) + output = proc.communicate()[0] + if check_exit_code and proc.returncode != 0: + if die_message is None: + die('Command "%s" failed.\n%s', ' '.join(cmd), output) + else: + die(die_message) + return output + + +HAS_EASY_INSTALL = bool(run_command(['which', 'easy_install'], + check_exit_code=False).strip()) +HAS_VIRTUALENV = bool(run_command(['which', 'virtualenv'], + check_exit_code=False).strip()) + + +def check_dependencies(): + """Make sure virtualenv is in the path.""" + + print 'Checking dependencies...' + if not HAS_VIRTUALENV: + print 'Virtual environment not found.' + # Try installing it via easy_install... + if HAS_EASY_INSTALL: + print 'Installing virtualenv via easy_install...', + run_command(['easy_install', 'virtualenv'], + die_message='easy_install failed to install virtualenv' + '\ndevelopment requires virtualenv, please' + ' install it using your favorite tool') + if not run_command(['which', 'virtualenv']): + die('ERROR: virtualenv not found in path.\n\ndevelopment ' + ' requires virtualenv, please install it using your' + ' favorite package management tool and ensure' + ' virtualenv is in your path') + print 'virtualenv installation done.' + else: + die('easy_install not found.\n\nInstall easy_install' + ' (python-setuptools in ubuntu) or virtualenv by hand,' + ' then rerun.') + print 'dependency check done.' + + +def create_virtualenv(venv=VENV): + """Creates the virtual environment and installs PIP only into the + virtual environment + """ + print 'Creating venv...', + run_command(['virtualenv', '-q', '--no-site-packages', VENV]) + print 'done.' + print 'Installing pip in virtualenv...', + if not run_command([WITH_VENV, 'easy_install', 'pip']).strip(): + die("Failed to install pip.") + print 'done.' + print 'Installing distribute in virtualenv...' + pip_install('distribute>=0.6.24') + print 'done.' + + +def pip_install(*args): + args = [WITH_VENV, 'pip', 'install', '--upgrade'] + list(args) + run_command(args, redirect_output=False) + + +def install_dependencies(venv=VENV): + print "Installing dependencies..." + print "(This may take several minutes, don't panic)" + pip_install('-r', TEST_REQUIRES) + pip_install('-r', PIP_REQUIRES) + + # Tell the virtual env how to "import dashboard" + py = 'python%d.%d' % (sys.version_info[0], sys.version_info[1]) + pthfile = os.path.join(venv, "lib", py, "site-packages", "dashboard.pth") + f = open(pthfile, 'w') + f.write("%s\n" % ROOT) + + +def install_horizon(): + print 'Installing horizon module in development mode...' + run_command([WITH_VENV, 'python', 'setup.py', 'develop'], cwd=ROOT) + + +def print_summary(): + summary = """ +Horizon development environment setup is complete. + +To activate the virtualenv for the extent of your current shell session you +can run: + +$ source .venv/bin/activate +""" + print summary + + +def main(): + check_dependencies() + create_virtualenv() + install_dependencies() + install_horizon() + print_summary() + +if __name__ == '__main__': + main() diff --git a/tools/with_venv.sh b/tools/with_venv.sh new file mode 100755 index 0000000..7303990 --- /dev/null +++ b/tools/with_venv.sh @@ -0,0 +1,7 @@ +#!/bin/bash +TOOLS_PATH=${TOOLS_PATH:-$(dirname $0)} +VENV_PATH=${VENV_PATH:-${TOOLS_PATH}} +VENV_DIR=${VENV_NAME:-/../.venv} +TOOLS=${TOOLS_PATH} +VENV=${VENV:-${VENV_PATH}/${VENV_DIR}} +source ${VENV}/bin/activate && "$@" diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..e6950e8 --- /dev/null +++ b/tox.ini @@ -0,0 +1,54 @@ +[tox] +minversion = 1.6 +envlist = py27,pep8,py27dj17 +skipsdist = True + +[testenv] +usedevelop = True +install_command = pip install -U {opts} {packages} +setenv = + VIRTUAL_ENV={envdir} +deps = -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt +commands = /bin/bash run_tests.sh -N --no-pep8 {posargs} + +[testenv:py27] +setenv = DJANGO_SETTINGS_MODULE=trove_dashboard.test.settings + +[testenv:pep8] +commands = flake8 + +[testenv:venv] +commands = {posargs} + +[testenv:py27dj17] +basepython = python2.7 +commands = pip install django>=1.7,<1.8 + /bin/bash run_tests.sh -N --no-pep8 {posargs} + +# Django-1.8 is LTS +[testenv:py27dj18] +basepython = python2.7 +commands = pip install django>=1.8,<1.9 + /bin/bash run_tests.sh -N --no-pep8 {posargs} + +[testenv:py27integration] +basepython = python2.7 +commands = /bin/bash run_tests.sh -N --integration --selenium-headless {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] +show-source = True +# E123, E125 skipped as they are invalid PEP-8. +# H405 multi line docstring summary not separated with an empty line +ignore = E123,E125,H405 +builtins = _ +exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build,.ropeproject,tools diff --git a/trove_dashboard/__init__.py b/trove_dashboard/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/trove_dashboard/content/__init__.py b/trove_dashboard/content/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/trove_dashboard/content/database_backups/panel.py b/trove_dashboard/content/database_backups/panel.py index 0e3d6f9..74fdfd3 100644 --- a/trove_dashboard/content/database_backups/panel.py +++ b/trove_dashboard/content/database_backups/panel.py @@ -15,6 +15,7 @@ from django.utils.translation import ugettext_lazy as _ import horizon +from openstack_dashboard.dashboards.project import dashboard class Backups(horizon.Panel): @@ -22,3 +23,6 @@ class Backups(horizon.Panel): slug = 'database_backups' permissions = ('openstack.services.database', 'openstack.services.object-store',) + + +dashboard.Project.register(Backups) diff --git a/trove_dashboard/content/database_backups/tables.py b/trove_dashboard/content/database_backups/tables.py index 0da12ba..594931f 100644 --- a/trove_dashboard/content/database_backups/tables.py +++ b/trove_dashboard/content/database_backups/tables.py @@ -21,7 +21,7 @@ from django.utils.translation import ungettext_lazy from horizon import tables from horizon.utils import filters -from openstack_dashboard.contrib.trove import api +from trove_dashboard import api STATUS_CHOICES = ( diff --git a/trove_dashboard/content/database_backups/tests.py b/trove_dashboard/content/database_backups/tests.py index c1aac5a..604e26a 100644 --- a/trove_dashboard/content/database_backups/tests.py +++ b/trove_dashboard/content/database_backups/tests.py @@ -17,8 +17,8 @@ from django import http from mox3.mox import IsA # noqa import six -from openstack_dashboard.contrib.trove import api -from openstack_dashboard.test import helpers as test +from trove_dashboard import api +from trove_dashboard.test import helpers as test INDEX_URL = reverse('horizon:project:database_backups:index') BACKUP_URL = reverse('horizon:project:database_backups:create') diff --git a/trove_dashboard/content/database_backups/urls.py b/trove_dashboard/content/database_backups/urls.py index b9ad96c..7cfe69e 100644 --- a/trove_dashboard/content/database_backups/urls.py +++ b/trove_dashboard/content/database_backups/urls.py @@ -15,7 +15,7 @@ from django.conf.urls import patterns from django.conf.urls import url -from openstack_dashboard.contrib.trove.content.database_backups import views +from trove_dashboard.content.database_backups import views urlpatterns = patterns( '', diff --git a/trove_dashboard/content/database_backups/views.py b/trove_dashboard/content/database_backups/views.py index a9a33bd..d2fe462 100644 --- a/trove_dashboard/content/database_backups/views.py +++ b/trove_dashboard/content/database_backups/views.py @@ -24,9 +24,9 @@ from horizon.utils import filters from horizon import views as horizon_views from horizon import workflows as horizon_workflows -from openstack_dashboard.contrib.trove import api -from openstack_dashboard.contrib.trove.content.database_backups import tables -from openstack_dashboard.contrib.trove.content.database_backups \ +from trove_dashboard import api +from trove_dashboard.content.database_backups import tables +from trove_dashboard.content.database_backups \ import workflows diff --git a/trove_dashboard/content/database_backups/workflows/create_backup.py b/trove_dashboard/content/database_backups/workflows/create_backup.py index 6ca78af..e1b8ca8 100644 --- a/trove_dashboard/content/database_backups/workflows/create_backup.py +++ b/trove_dashboard/content/database_backups/workflows/create_backup.py @@ -20,8 +20,8 @@ from horizon import exceptions from horizon import forms from horizon import workflows -from openstack_dashboard.contrib.trove import api -from openstack_dashboard.contrib.trove.content.databases \ +from trove_dashboard import api +from trove_dashboard.content.databases \ import tables as project_tables diff --git a/trove_dashboard/content/database_clusters/forms.py b/trove_dashboard/content/database_clusters/forms.py index 4fc8e00..cb78209 100644 --- a/trove_dashboard/content/database_clusters/forms.py +++ b/trove_dashboard/content/database_clusters/forms.py @@ -23,10 +23,10 @@ from horizon import exceptions from horizon import forms from horizon import messages from horizon.utils import memoized - from openstack_dashboard import api -from openstack_dashboard.contrib.trove import api as trove_api -from openstack_dashboard.contrib.trove.content.databases import db_capability + +from trove_dashboard import api as trove_api +from trove_dashboard.content.databases import db_capability LOG = logging.getLogger(__name__) diff --git a/trove_dashboard/content/database_clusters/panel.py b/trove_dashboard/content/database_clusters/panel.py index 1575ff3..7905d41 100644 --- a/trove_dashboard/content/database_clusters/panel.py +++ b/trove_dashboard/content/database_clusters/panel.py @@ -17,6 +17,7 @@ from django.utils.translation import ugettext_lazy as _ import horizon +from openstack_dashboard.dashboards.project import dashboard class Clusters(horizon.Panel): @@ -24,3 +25,6 @@ class Clusters(horizon.Panel): slug = 'database_clusters' permissions = ('openstack.services.database', 'openstack.services.object-store',) + + +dashboard.Project.register(Clusters) diff --git a/trove_dashboard/content/database_clusters/tables.py b/trove_dashboard/content/database_clusters/tables.py index e52008b..b2b41e2 100644 --- a/trove_dashboard/content/database_clusters/tables.py +++ b/trove_dashboard/content/database_clusters/tables.py @@ -23,8 +23,9 @@ from horizon import tables from horizon.templatetags import sizeformat from horizon.utils import filters from horizon.utils import memoized -from openstack_dashboard.contrib.trove import api -from openstack_dashboard.contrib.trove.content.databases import db_capability + +from trove_dashboard import api +from trove_dashboard.content.databases import db_capability ACTIVE_STATES = ("ACTIVE",) diff --git a/trove_dashboard/content/database_clusters/tabs.py b/trove_dashboard/content/database_clusters/tabs.py index e107bd5..d83cb59 100644 --- a/trove_dashboard/content/database_clusters/tabs.py +++ b/trove_dashboard/content/database_clusters/tabs.py @@ -19,8 +19,9 @@ from django.utils.translation import ugettext_lazy as _ from horizon import exceptions from horizon import tabs -from openstack_dashboard.contrib.trove import api -from openstack_dashboard.contrib.trove.content.database_clusters import tables + +from trove_dashboard import api +from trove_dashboard.content.database_clusters import tables class OverviewTab(tabs.Tab): diff --git a/trove_dashboard/content/database_clusters/tests.py b/trove_dashboard/content/database_clusters/tests.py index fd5d705..9790360 100644 --- a/trove_dashboard/content/database_clusters/tests.py +++ b/trove_dashboard/content/database_clusters/tests.py @@ -20,11 +20,10 @@ from django import http from mox3.mox import IsA # noqa from openstack_dashboard import api -from openstack_dashboard.contrib.trove import api as trove_api -from openstack_dashboard.test import helpers as test - from troveclient import common +from trove_dashboard import api as trove_api +from trove_dashboard.test import helpers as test INDEX_URL = reverse('horizon:project:database_clusters:index') LAUNCH_URL = reverse('horizon:project:database_clusters:launch') diff --git a/trove_dashboard/content/database_clusters/urls.py b/trove_dashboard/content/database_clusters/urls.py index 1962457..f2c5c62 100644 --- a/trove_dashboard/content/database_clusters/urls.py +++ b/trove_dashboard/content/database_clusters/urls.py @@ -17,7 +17,7 @@ from django.conf.urls import patterns # noqa from django.conf.urls import url # noqa -from openstack_dashboard.contrib.trove.content.database_clusters import views +from trove_dashboard.content.database_clusters import views CLUSTERS = r'^(?P[^/]+)/%s$' diff --git a/trove_dashboard/content/database_clusters/views.py b/trove_dashboard/content/database_clusters/views.py index e0de3ee..d335e3c 100644 --- a/trove_dashboard/content/database_clusters/views.py +++ b/trove_dashboard/content/database_clusters/views.py @@ -32,10 +32,10 @@ from horizon import tables as horizon_tables from horizon import tabs as horizon_tabs from horizon.utils import memoized -from openstack_dashboard.contrib.trove import api -from openstack_dashboard.contrib.trove.content.database_clusters import forms -from openstack_dashboard.contrib.trove.content.database_clusters import tables -from openstack_dashboard.contrib.trove.content.database_clusters import tabs +from trove_dashboard import api +from trove_dashboard.content.database_clusters import forms +from trove_dashboard.content.database_clusters import tables +from trove_dashboard.content.database_clusters import tabs LOG = logging.getLogger(__name__) diff --git a/trove_dashboard/content/databases/forms.py b/trove_dashboard/content/databases/forms.py index 3e0c0a9..73b4864 100644 --- a/trove_dashboard/content/databases/forms.py +++ b/trove_dashboard/content/databases/forms.py @@ -19,7 +19,8 @@ from django.utils.translation import ugettext_lazy as _ from horizon import exceptions from horizon import forms from horizon import messages -from openstack_dashboard.contrib.trove import api + +from trove_dashboard import api class ResizeVolumeForm(forms.SelfHandlingForm): diff --git a/trove_dashboard/content/databases/panel.py b/trove_dashboard/content/databases/panel.py index 9180477..16cf6c0 100644 --- a/trove_dashboard/content/databases/panel.py +++ b/trove_dashboard/content/databases/panel.py @@ -15,9 +15,13 @@ from django.utils.translation import ugettext_lazy as _ import horizon +from openstack_dashboard.dashboards.project import dashboard class Databases(horizon.Panel): name = _("Instances") slug = 'databases' permissions = ('openstack.services.database',) + + +dashboard.Project.register(Databases) diff --git a/trove_dashboard/content/databases/tables.py b/trove_dashboard/content/databases/tables.py index cb1d8d2..1189531 100644 --- a/trove_dashboard/content/databases/tables.py +++ b/trove_dashboard/content/databases/tables.py @@ -23,8 +23,8 @@ from horizon import tables from horizon.templatetags import sizeformat from horizon.utils import filters -from openstack_dashboard.contrib.trove import api -from openstack_dashboard.contrib.trove.content.database_backups \ +from trove_dashboard import api +from trove_dashboard.content.database_backups \ import tables as backup_tables diff --git a/trove_dashboard/content/databases/tabs.py b/trove_dashboard/content/databases/tabs.py index f93c1df..e24a3fa 100644 --- a/trove_dashboard/content/databases/tabs.py +++ b/trove_dashboard/content/databases/tabs.py @@ -18,8 +18,8 @@ from django.utils.translation import ugettext_lazy as _ from horizon import exceptions from horizon import tabs -from openstack_dashboard.contrib.trove import api -from openstack_dashboard.contrib.trove.content.databases import tables +from trove_dashboard import api +from trove_dashboard.content.databases import tables class OverviewTab(tabs.Tab): diff --git a/trove_dashboard/content/databases/tests.py b/trove_dashboard/content/databases/tests.py index 6a1643d..55c2e7d 100644 --- a/trove_dashboard/content/databases/tests.py +++ b/trove_dashboard/content/databases/tests.py @@ -25,11 +25,10 @@ import six from horizon import exceptions from openstack_dashboard import api as dash_api -from openstack_dashboard.contrib.trove import api -from openstack_dashboard.test import helpers as test - from troveclient import common +from trove_dashboard import api +from trove_dashboard.test import helpers as test INDEX_URL = reverse('horizon:project:databases:index') LAUNCH_URL = reverse('horizon:project:databases:launch') diff --git a/trove_dashboard/content/databases/urls.py b/trove_dashboard/content/databases/urls.py index ffbb938..b37a348 100644 --- a/trove_dashboard/content/databases/urls.py +++ b/trove_dashboard/content/databases/urls.py @@ -15,7 +15,7 @@ from django.conf.urls import patterns from django.conf.urls import url -from openstack_dashboard.contrib.trove.content.databases import views +from trove_dashboard.content.databases import views INSTANCES = r'^(?P[^/]+)/%s$' diff --git a/trove_dashboard/content/databases/views.py b/trove_dashboard/content/databases/views.py index 02fdbbf..aa037fa 100644 --- a/trove_dashboard/content/databases/views.py +++ b/trove_dashboard/content/databases/views.py @@ -30,16 +30,14 @@ from horizon import tables as horizon_tables from horizon import tabs as horizon_tabs from horizon.utils import memoized from horizon import workflows as horizon_workflows - -from openstack_dashboard.contrib.trove import api -from openstack_dashboard.contrib.trove.content.databases import forms -from openstack_dashboard.contrib.trove.content.databases import tables -from openstack_dashboard.contrib.trove.content.databases import tabs -from openstack_dashboard.contrib.trove.content.databases import workflows - from openstack_dashboard.dashboards.project.instances \ import utils as instance_utils +from trove_dashboard import api +from trove_dashboard.content.databases import forms +from trove_dashboard.content.databases import tables +from trove_dashboard.content.databases import tabs +from trove_dashboard.content.databases import workflows LOG = logging.getLogger(__name__) diff --git a/trove_dashboard/content/databases/workflows/create_instance.py b/trove_dashboard/content/databases/workflows/create_instance.py index 7912b5a..532bf3d 100644 --- a/trove_dashboard/content/databases/workflows/create_instance.py +++ b/trove_dashboard/content/databases/workflows/create_instance.py @@ -23,11 +23,10 @@ from horizon import forms from horizon.utils import memoized from horizon import workflows from openstack_dashboard import api as dash_api -from openstack_dashboard.contrib.trove import api - from openstack_dashboard.dashboards.project.instances \ import utils as instance_utils +from trove_dashboard import api LOG = logging.getLogger(__name__) diff --git a/trove_dashboard/enabled/_1710_database_panel_group.py b/trove_dashboard/enabled/_1710_database_panel_group.py index 86574b2..3409b8f 100644 --- a/trove_dashboard/enabled/_1710_database_panel_group.py +++ b/trove_dashboard/enabled/_1710_database_panel_group.py @@ -1,3 +1,15 @@ +# 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 django.utils.translation import ugettext_lazy as _ # The slug of the panel group to be added to HORIZON_CONFIG. Required. diff --git a/trove_dashboard/enabled/_1720_project_databases_panel.py b/trove_dashboard/enabled/_1720_project_databases_panel.py index 6c1eb66..0c61385 100644 --- a/trove_dashboard/enabled/_1720_project_databases_panel.py +++ b/trove_dashboard/enabled/_1720_project_databases_panel.py @@ -1,3 +1,17 @@ +# 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 trove_dashboard import exceptions + # The slug of the panel to be added to HORIZON_CONFIG. Required. PANEL = 'databases' # The slug of the dashboard the PANEL associated with. Required. @@ -6,5 +20,12 @@ PANEL_DASHBOARD = 'project' PANEL_GROUP = 'database' # Python panel class of the PANEL to be added. -ADD_PANEL = ('openstack_dashboard.contrib.trove.' - 'content.databases.panel.Databases') +ADD_PANEL = ('trove_dashboard.content.databases.panel.Databases') + +ADD_INSTALLED_APPS = ["trove_dashboard", ] + +ADD_EXCEPTIONS = { + 'not_found': exceptions.NOT_FOUND, + 'recoverable': exceptions.RECOVERABLE, + 'unauthorized': exceptions.UNAUTHORIZED +} diff --git a/trove_dashboard/enabled/_1730_project_database_backups_panel.py b/trove_dashboard/enabled/_1730_project_database_backups_panel.py index a64e331..2f5de85 100644 --- a/trove_dashboard/enabled/_1730_project_database_backups_panel.py +++ b/trove_dashboard/enabled/_1730_project_database_backups_panel.py @@ -1,3 +1,17 @@ +# 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 trove_dashboard import exceptions + # The slug of the panel to be added to HORIZON_CONFIG. Required. PANEL = 'database_backups' # The slug of the dashboard the PANEL associated with. Required. @@ -6,5 +20,10 @@ PANEL_DASHBOARD = 'project' PANEL_GROUP = 'database' # Python panel class of the PANEL to be added. -ADD_PANEL = ('openstack_dashboard.contrib.trove.' - 'content.database_backups.panel.Backups') +ADD_PANEL = ('trove_dashboard.content.database_backups.panel.Backups') + +ADD_EXCEPTIONS = { + 'not_found': exceptions.NOT_FOUND, + 'recoverable': exceptions.RECOVERABLE, + 'unauthorized': exceptions.UNAUTHORIZED, +} diff --git a/trove_dashboard/enabled/_1740_project_database_clusters_panel.py b/trove_dashboard/enabled/_1740_project_database_clusters_panel.py index 0aedc98..e18d994 100644 --- a/trove_dashboard/enabled/_1740_project_database_clusters_panel.py +++ b/trove_dashboard/enabled/_1740_project_database_clusters_panel.py @@ -13,6 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. +from trove_dashboard import exceptions + # The slug of the panel to be added to HORIZON_CONFIG. Required. PANEL = 'database_clusters' # The slug of the dashboard the PANEL associated with. Required. @@ -21,5 +23,10 @@ PANEL_DASHBOARD = 'project' PANEL_GROUP = 'database' # Python panel class of the PANEL to be added. -ADD_PANEL = ('openstack_dashboard.contrib.trove.' - 'content.database_clusters.panel.Clusters') +ADD_PANEL = ('trove_dashboard.content.database_clusters.panel.Clusters') + +ADD_EXCEPTIONS = { + 'not_found': exceptions.NOT_FOUND, + 'recoverable': exceptions.RECOVERABLE, + 'unauthorized': exceptions.UNAUTHORIZED +} diff --git a/trove_dashboard/enabled/__init__.py b/trove_dashboard/enabled/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/trove_dashboard/exceptions.py b/trove_dashboard/exceptions.py new file mode 100644 index 0000000..e5ea809 --- /dev/null +++ b/trove_dashboard/exceptions.py @@ -0,0 +1,29 @@ +# +# 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 troveclient import exceptions as troveclient + + +UNAUTHORIZED = ( + troveclient.Unauthorized, +) + + +NOT_FOUND = ( + troveclient.NotFound, +) + + +RECOVERABLE = ( + troveclient.ClientException, +) diff --git a/trove_dashboard/test/helpers.py b/trove_dashboard/test/helpers.py index f635dc3..59b4aa6 100644 --- a/trove_dashboard/test/helpers.py +++ b/trove_dashboard/test/helpers.py @@ -15,7 +15,26 @@ from troveclient import client as trove_client from openstack_dashboard.test import helpers -from openstack_dashboard.contrib.trove import api +from trove_dashboard import api +from trove_dashboard.test.test_data import utils + + +def create_stubs(stubs_to_create={}): + return helpers.create_stubs(stubs_to_create) + + +class TroveTestsMixin(object): + def _setup_test_data(self): + super(TroveTestsMixin, self)._setup_test_data() + utils.load_test_data(self) + + +class TestCase(TroveTestsMixin, helpers.TestCase): + pass + + +class BaseAdminViewTests(TroveTestsMixin, helpers.TestCase): + pass class TroveAPITestCase(helpers.APITestCase): diff --git a/trove_dashboard/test/settings.py b/trove_dashboard/test/settings.py new file mode 100644 index 0000000..5181013 --- /dev/null +++ b/trove_dashboard/test/settings.py @@ -0,0 +1,187 @@ +# +# 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 importlib +import os +import six + +from horizon.test.settings import * # noqa +from horizon.utils import secret_key +from openstack_dashboard import exceptions + + +DEBUG = True +TEMPLATE_DEBUG = DEBUG + +TEST_DIR = os.path.dirname(os.path.abspath(__file__)) +ROOT_PATH = os.path.abspath(os.path.join(TEST_DIR, "..")) + +MEDIA_ROOT = os.path.abspath(os.path.join(ROOT_PATH, '..', 'media')) +MEDIA_URL = '/media/' +STATIC_ROOT = os.path.abspath(os.path.join(ROOT_PATH, '..', 'static')) +STATIC_URL = '/static/' + +SECRET_KEY = secret_key.generate_or_read_from_file( + os.path.join(TEST_DIR, '.secret_key_store')) +ROOT_URLCONF = 'trove_dashboard.test.urls' +TEMPLATE_DIRS = ( + os.path.join(TEST_DIR, 'templates'), +) + +TEMPLATE_CONTEXT_PROCESSORS += ( + 'openstack_dashboard.context_processors.openstack', +) + +INSTALLED_APPS = ( + 'django.contrib.contenttypes', + 'django.contrib.auth', + 'django.contrib.sessions', + 'django.contrib.staticfiles', + 'django.contrib.messages', + 'django.contrib.humanize', + 'django_nose', + 'openstack_auth', + 'compressor', + 'horizon', + 'openstack_dashboard', + 'openstack_dashboard.dashboards', +) + +AUTHENTICATION_BACKENDS = ('openstack_auth.backend.KeystoneBackend',) + +SITE_BRANDING = 'OpenStack' + +HORIZON_CONFIG = { + "password_validator": { + "regex": '^.{8,18}$', + "help_text": "Password must be between 8 and 18 characters." + }, + 'user_home': None, + 'help_url': "http://docs.openstack.org", + 'exceptions': {'recoverable': exceptions.RECOVERABLE, + 'not_found': exceptions.NOT_FOUND, + 'unauthorized': exceptions.UNAUTHORIZED}, + 'angular_modules': [], + 'js_files': [], +} + +# Load the pluggable dashboard settings +from openstack_dashboard.utils import settings +dashboard_module_names = [ + 'openstack_dashboard.enabled', + 'openstack_dashboard.local.enabled', + 'trove_dashboard.enabled', +] +dashboard_modules = [] +# All dashboards must be enabled for the namespace to get registered, which is +# needed by the unit tests. +for module_name in dashboard_module_names: + module = importlib.import_module(module_name) + dashboard_modules.append(module) + for submodule in six.itervalues(settings.import_submodules(module)): + if getattr(submodule, 'DISABLED', None): + delattr(submodule, 'DISABLED') +INSTALLED_APPS = list(INSTALLED_APPS) # Make sure it's mutable +settings.update_dashboards(dashboard_modules, HORIZON_CONFIG, INSTALLED_APPS) + +# Set to True to allow users to upload images to glance via Horizon server. +# When enabled, a file form field will appear on the create image form. +# See documentation for deployment considerations. +HORIZON_IMAGES_ALLOW_UPLOAD = True + +AVAILABLE_REGIONS = [ + ('http://localhost:5000/v2.0', 'local'), + ('http://remote:5000/v2.0', 'remote'), +] + +OPENSTACK_API_VERSIONS = { + "identity": 3 +} + +OPENSTACK_KEYSTONE_URL = "http://localhost:5000/v2.0" +OPENSTACK_KEYSTONE_DEFAULT_ROLE = "_member_" + +OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT = True +OPENSTACK_KEYSTONE_DEFAULT_DOMAIN = 'test_domain' + +OPENSTACK_KEYSTONE_BACKEND = { + 'name': 'native', + 'can_edit_user': True, + 'can_edit_group': True, + 'can_edit_project': True, + 'can_edit_domain': True, + 'can_edit_role': True +} + +OPENSTACK_CINDER_FEATURES = { + 'enable_backup': True, +} + +OPENSTACK_NEUTRON_NETWORK = { + 'enable_lb': False, + 'enable_firewall': False, + 'enable_vpn': False, +} + +OPENSTACK_HYPERVISOR_FEATURES = { + 'can_set_mount_point': True, + + # NOTE: as of Grizzly this is not yet supported in Nova so enabling this + # setting will not do anything useful + 'can_encrypt_volumes': False +} + +LOGGING['loggers']['openstack_dashboard'] = { + 'handlers': ['test'], + 'propagate': False, +} + +LOGGING['loggers']['selenium'] = { + 'handlers': ['test'], + 'propagate': False, +} + +LOGGING['loggers']['trove_dashboard'] = { + 'handlers': ['test'], + 'propagate': False, +} + +SECURITY_GROUP_RULES = { + 'all_tcp': { + 'name': 'ALL TCP', + 'ip_protocol': 'tcp', + 'from_port': '1', + 'to_port': '65535', + }, + 'http': { + 'name': 'HTTP', + 'ip_protocol': 'tcp', + 'from_port': '80', + 'to_port': '80', + }, +} + +NOSE_ARGS = ['--nocapture', + '--nologcapture', + '--cover-package=openstack_dashboard', + '--cover-inclusive', + '--all-modules'] + +POLICY_FILES_PATH = os.path.join(ROOT_PATH, "conf") +POLICY_FILES = { + 'identity': 'keystone_policy.json', + 'compute': 'nova_policy.json' +} + +# The openstack_auth.user.Token object isn't JSON-serializable ATM +SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer' diff --git a/trove_dashboard/test/test_data/__init__.py b/trove_dashboard/test/test_data/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/trove_dashboard/test/test_data/keystone_data.py b/trove_dashboard/test/test_data/keystone_data.py new file mode 100644 index 0000000..491732a --- /dev/null +++ b/trove_dashboard/test/test_data/keystone_data.py @@ -0,0 +1,26 @@ +# 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. + + +def data(TEST): + + # Add trove to the keystone data + TEST.service_catalog.append( + {"type": "database", + "name": "Trove", + "endpoints_links": [], + "endpoints": [ + {"region": "RegionOne", + "adminURL": "http://admin.trove.example.com:8779/v1.0", + "publicURL": "http://public.trove.example.com:8779/v1.0", + "internalURL": "http://int.trove.example.com:8779/v1.0"}]} + ) diff --git a/trove_dashboard/test/test_data/utils.py b/trove_dashboard/test/test_data/utils.py new file mode 100644 index 0000000..ad6dcc1 --- /dev/null +++ b/trove_dashboard/test/test_data/utils.py @@ -0,0 +1,51 @@ +# +# 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 openstack_dashboard.test.test_data import utils + + +def load_test_data(load_onto=None): + from openstack_dashboard.test.test_data import ceilometer_data + from openstack_dashboard.test.test_data import cinder_data + from openstack_dashboard.test.test_data import exceptions + from openstack_dashboard.test.test_data import glance_data + from openstack_dashboard.test.test_data import heat_data + from openstack_dashboard.test.test_data import keystone_data + from openstack_dashboard.test.test_data import neutron_data + from openstack_dashboard.test.test_data import nova_data + from openstack_dashboard.test.test_data import swift_data + + from trove_dashboard.test.test_data import keystone_data \ + as trove_keystone_data + from trove_dashboard.test.test_data import trove_data + + # The order of these loaders matters, some depend on others. + loaders = ( + exceptions.data, + keystone_data.data, + glance_data.data, + nova_data.data, + cinder_data.data, + neutron_data.data, + swift_data.data, + heat_data.data, + ceilometer_data.data, + trove_data.data, + trove_keystone_data.data, + ) + if load_onto: + for data_func in loaders: + data_func(load_onto) + return load_onto + else: + return utils.TestData(*loaders) diff --git a/trove_dashboard/test/urls.py b/trove_dashboard/test/urls.py new file mode 100644 index 0000000..9bef20f --- /dev/null +++ b/trove_dashboard/test/urls.py @@ -0,0 +1,20 @@ +# +# 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 django.conf import urls +import openstack_dashboard.urls + +urlpatterns = urls.patterns( + '', + urls.url(r'', urls.include(openstack_dashboard.urls)) +)