Retire cue
Cue has been retired in mid 2016 as official project and did not continue developement, it's time to retire it completely. Remove everything, update README. Depends-On: https://review.openstack.org/551202 Change-Id: I1f4a71fbea8a90303036ad0adaec95fa15b6522f
This commit is contained in:
parent
4978f34055
commit
bb7e81e1ab
@ -1,7 +0,0 @@
|
||||
[run]
|
||||
branch = True
|
||||
source = cue, os_tasklib
|
||||
omit = cue/tests/*,cue/openstack/*
|
||||
|
||||
[report]
|
||||
ignore_errors = True
|
79
.gitignore
vendored
79
.gitignore
vendored
@ -1,79 +0,0 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
env/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
cover/
|
||||
htmlcov/
|
||||
.tox/
|
||||
.testrepository/
|
||||
.coverage
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
|
||||
# Sphinx documentation
|
||||
doc/build
|
||||
doc/source/api
|
||||
doc/source/autoindex.rst
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Vagrant
|
||||
.vagrant
|
||||
|
||||
# Rope
|
||||
.ropeproject
|
||||
|
||||
# Virtualenv
|
||||
.virtualenv
|
||||
.venv
|
||||
|
||||
# OSX Finder
|
||||
.DS_Store
|
||||
|
||||
# Testr coverage
|
||||
.coverage.*
|
||||
|
||||
# pyenv
|
||||
.python-version
|
@ -1,4 +0,0 @@
|
||||
[gerrit]
|
||||
host=review.openstack.org
|
||||
port=29418
|
||||
project=openstack/cue.git
|
@ -1,9 +0,0 @@
|
||||
[DEFAULT]
|
||||
test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
|
||||
OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
|
||||
OS_LOG_CAPTURE=${OS_LOG_CAPTURE:-1} \
|
||||
OS_DEBUG=${OS_DEBUG:-1} \
|
||||
OS_TEST_TIMEOUT=60 \
|
||||
${PYTHON:-python} -m subunit.run discover ./cue/tests $LISTOPT $IDOPTION
|
||||
test_id_option=--load-list $IDFILE
|
||||
test_list_option=--list
|
202
LICENSE
202
LICENSE
@ -1,202 +0,0 @@
|
||||
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.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
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.
|
||||
|
25
README.md
25
README.md
@ -1,17 +1,14 @@
|
||||
Cue
|
||||
===
|
||||
This project is no longer maintained.
|
||||
|
||||
Openstack Message Broker Provisioning Service.
|
||||
The contents of this repository are still available in the Git
|
||||
source code management system. To see the contents of this
|
||||
repository before it reached its end of life, please check out the
|
||||
previous commit with "git checkout HEAD^1".
|
||||
|
||||
This service provides Provisioning and Management of Message Brokers.
|
||||
(Optional:)
|
||||
For an alternative project, please see <alternative project name> at
|
||||
<alternative project URL>.
|
||||
|
||||
Supported MQ's
|
||||
==============
|
||||
|
||||
RabbitMQ
|
||||
|
||||
|
||||
Getting Started
|
||||
===============
|
||||
|
||||
http://cue.readthedocs.org/en/latest/getting-started.html
|
||||
For any further questions, please email
|
||||
openstack-dev@lists.openstack.org or join #openstack-dev on
|
||||
Freenode.
|
||||
|
@ -1,17 +0,0 @@
|
||||
The contrib/devstack directory contrains the files to integrate Cue with Devstack.
|
||||
|
||||
To install Cue
|
||||
|
||||
# Clone devstack and cue
|
||||
git clone https://github.com/openstack-dev/devstack.git
|
||||
git clone https://github.com/openstack/cue.git
|
||||
|
||||
# Install the cue plugins onto Devstack
|
||||
./cue/contrib/devstack/install.sh
|
||||
|
||||
# Copy the local.conf to your devstack
|
||||
cp cue/contrib/devstack/local.conf devstack/
|
||||
|
||||
This will create the neccessary symlinks to the Cue-devstack-plugin, and setup
|
||||
devstack with a local.conf that enables the Cue services and its dependencies.
|
||||
|
@ -1,27 +0,0 @@
|
||||
# dib.sh - Devstack extras script to install diskimage-builder
|
||||
|
||||
if is_service_enabled dib; then
|
||||
if [[ "$1" == "source" ]]; then
|
||||
# Initial source
|
||||
source $TOP_DIR/lib/dib
|
||||
elif [[ "$1" == "stack" && "$2" == "install" ]]; then
|
||||
echo_summary "Installing diskimage-builder"
|
||||
install_dib
|
||||
elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then
|
||||
# no-op
|
||||
:
|
||||
elif [[ "$1" == "stack" && "$2" == "extra" ]]; then
|
||||
# no-op
|
||||
:
|
||||
fi
|
||||
|
||||
if [[ "$1" == "unstack" ]]; then
|
||||
# no-op
|
||||
:
|
||||
fi
|
||||
|
||||
if [[ "$1" == "clean" ]]; then
|
||||
# no-op
|
||||
:
|
||||
fi
|
||||
fi
|
@ -1,47 +0,0 @@
|
||||
# check for service enabled
|
||||
if is_service_enabled cue; then
|
||||
|
||||
if [[ "$1" == "source" ]]; then
|
||||
# Initial source of lib script
|
||||
source $TOP_DIR/lib/cue
|
||||
fi
|
||||
|
||||
if [[ "$1" == "stack" && "$2" == "install" ]]; then
|
||||
echo_summary "Installing Cue"
|
||||
install_cue
|
||||
|
||||
echo_summary "Installing Cue Client"
|
||||
install_cueclient
|
||||
|
||||
echo_summary "Installing Cue Dashboard"
|
||||
install_cuedashboard
|
||||
|
||||
elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then
|
||||
echo_summary "Configuring Cue"
|
||||
configure_cue
|
||||
|
||||
if is_service_enabled key; then
|
||||
echo_summary "Creating Cue Keystone Accounts"
|
||||
create_cue_accounts
|
||||
fi
|
||||
|
||||
elif [[ "$1" == "stack" && "$2" == "extra" ]]; then
|
||||
echo_summary "Initializing Cue"
|
||||
init_cue
|
||||
|
||||
echo_summary "Starting Cue"
|
||||
start_cue
|
||||
|
||||
echo_summary "Creating Initial Cue Resources"
|
||||
create_cue_initial_resources
|
||||
fi
|
||||
|
||||
if [[ "$1" == "unstack" ]]; then
|
||||
stop_cue
|
||||
fi
|
||||
|
||||
if [[ "$1" == "clean" ]]; then
|
||||
echo_summary "Cleaning Cue"
|
||||
cleanup_cue
|
||||
fi
|
||||
fi
|
@ -1,31 +0,0 @@
|
||||
|
||||
if [[ "$1" == "stack" && "$2" == "post-config" ]]; then
|
||||
if [[ ! -z $RALLY_AUTH_URL ]]; then
|
||||
# rally deployment create
|
||||
tmpfile=$(mktemp)
|
||||
_create_deployment_config $tmpfile
|
||||
|
||||
iniset $RALLY_CONF_DIR/$RALLY_CONF_FILE database connection `database_connection_url rally`
|
||||
recreate_database rally utf8
|
||||
# Recreate rally database
|
||||
$RALLY_BIN_DIR/rally-manage --config-file $RALLY_CONF_DIR/$RALLY_CONF_FILE db recreate
|
||||
|
||||
rally --config-file /etc/rally/rally.conf deployment create --name cue-devstack2 --file $tmpfile
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
# _create_deployment_config filename
|
||||
function _create_deployment_config() {
|
||||
cat >$1 <<EOF
|
||||
{
|
||||
"type": "ExistingCloud",
|
||||
"auth_url": "$KEYSTONE_AUTH_PROTOCOL://$KEYSTONE_AUTH_HOST:$KEYSTONE_AUTH_PORT/$RALLY_AUTH_VERSION",
|
||||
"admin": {
|
||||
"username": "admin",
|
||||
"password": "$ADMIN_PASSWORD",
|
||||
"project_name": "admin"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
DIR=$(readlink -e $(dirname $(readlink -f $0)))
|
||||
|
||||
pushd $DIR
|
||||
|
||||
for f in lib/* extras.d/*; do
|
||||
if [ ! -e "$DIR/../../../devstack/$f" ]; then
|
||||
echo "Installing symlink for $f"
|
||||
ln -fs $DIR/$f $DIR/../../../devstack/$f
|
||||
fi
|
||||
done
|
||||
|
||||
popd
|
@ -1,351 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# lib/cue
|
||||
# Install and start **Cue** service
|
||||
|
||||
# To enable Cue services, add the following to localrc
|
||||
# enable_service cue,cue-api,cue-worker
|
||||
|
||||
# stack.sh
|
||||
# ---------
|
||||
# install_cue
|
||||
# configure_cue
|
||||
# init_cue
|
||||
# start_cue
|
||||
# stop_cue
|
||||
# cleanup_cue
|
||||
|
||||
# Save trace setting
|
||||
XTRACE=$(set +o | grep xtrace)
|
||||
set +o xtrace
|
||||
|
||||
|
||||
# Defaults
|
||||
# --------
|
||||
CUE_PLUGINS=$TOP_DIR/lib/cue_plugins
|
||||
|
||||
# Set up default repos
|
||||
CUE_REPO=${CUE_REPO:-${GIT_BASE}/openstack/cue.git}
|
||||
CUE_BRANCH=${CUE_BRANCH:-master}
|
||||
CUECLIENT_REPO=${CUECLIENT_REPO:-${GIT_BASE}/openstack/python-cueclient.git}
|
||||
CUECLIENT_BRANCH=${CUECLIENT_BRANCH:-master}
|
||||
CUEDASHBOARD_REPO=${CUEDASHBOARD_REPO:-${GIT_BASE}/openstack/cue-dashboard.git}
|
||||
CUEDASHBOARD_BRANCH=${CUEDASHBOARD_BRANCH:-master}
|
||||
CUE_MANAGEMENT_NETWORK_SUBNET=${CUE_MANAGEMENT_NETWORK_SUBNET-"172.16.0.0/24"}
|
||||
|
||||
# Set up default paths
|
||||
CUE_BIN_DIR=$(get_python_exec_prefix)
|
||||
CUE_DIR=$DEST/cue
|
||||
CUECLIENT_DIR=$DEST/python-cueclient
|
||||
CUEDASHBOARD_DIR=$DEST/cue-dashboard
|
||||
CUE_CONF_DIR=/etc/cue
|
||||
CUE_STATE_PATH=${CUE_STATE_PATH:=$DATA_DIR/cue}
|
||||
CUE_CONF=$CUE_CONF_DIR/cue.conf
|
||||
CUE_LOG_DIR=/var/log/cue
|
||||
CUE_AUTH_CACHE_DIR=${CUE_AUTH_CACHE_DIR:-/var/cache/cue}
|
||||
|
||||
CUE_TF_DB=${CUE_TF_DB:-cue_taskflow}
|
||||
CUE_TF_PERSISTENCE=${CUE_TF_PERSISTENCE:-}
|
||||
CUE_TF_CREATE_CLUSTER_NODE_VM_ACTIVE_RETRY_COUNT=${CUE_TF_CREATE_CLUSTER_NODE_VM_ACTIVE_RETRY_COUNT:-12}
|
||||
|
||||
# Public IP/Port Settings
|
||||
CUE_SERVICE_PROTOCOL=${CUE_SERVICE_PROTOCOL:-$SERVICE_PROTOCOL}
|
||||
CUE_SERVICE_HOST=${CUE_SERVICE_HOST:-$SERVICE_HOST}
|
||||
CUE_SERVICE_PORT=${CUE_SERVICE_PORT:-8795}
|
||||
|
||||
CUE_DEFAULT_BROKER_NAME=${CUE_DEFAULT_BROKER_NAME:-rabbitmq}
|
||||
|
||||
CUE_MANAGEMENT_KEY='cue-mgmt-key'
|
||||
CUE_RABBIT_SECURITY_GROUP='cue-rabbitmq'
|
||||
|
||||
CUE_RABBIT_IMAGE_MINDISK=4
|
||||
|
||||
CUE_FLAVOR=cue.small
|
||||
CUE_FLAVOR_PARAMS="--id 8795 --ram 512 --disk $CUE_RABBIT_IMAGE_MINDISK --vcpus 1"
|
||||
CUE_RABBIT_SECURITY_GROUP='cue-rabbitmq'
|
||||
CUE_MANAGEMENT_NETWORK_NAME='cue_management_net'
|
||||
CUE_MANAGEMENT_SUBNET_NAME='cue_management_subnet'
|
||||
|
||||
CUE_RABBIT_IMAGE_ELEMENTS=${CUE_RABBIT_IMAGE_ELEMENTS:-\
|
||||
vm ubuntu os-refresh-config os-apply-config ntp hosts \
|
||||
ifmetric cue-rabbitmq-base}
|
||||
|
||||
# cleanup_cue - Remove residual data files, anything left over from previous
|
||||
# runs that a clean run would need to clean up
|
||||
function cleanup_cue {
|
||||
sudo rm -rf $CUE_STATE_PATH $CUE_AUTH_CACHE_DIR
|
||||
}
|
||||
|
||||
# configure_cue - Set config files, create data dirs, etc
|
||||
function configure_cue {
|
||||
[ ! -d $CUE_CONF_DIR ] && sudo mkdir -m 755 -p $CUE_CONF_DIR
|
||||
sudo chown $STACK_USER $CUE_CONF_DIR
|
||||
|
||||
[ ! -d $CUE_LOG_DIR ] && sudo mkdir -m 755 -p $CUE_LOG_DIR
|
||||
sudo chown $STACK_USER $CUE_LOG_DIR
|
||||
|
||||
# (Re)create ``cue.conf``
|
||||
rm -f $CUE_CONF
|
||||
|
||||
iniset_rpc_backend cue $CUE_CONF DEFAULT
|
||||
iniset $CUE_CONF DEFAULT debug $ENABLE_DEBUG_LOG_LEVEL
|
||||
iniset $CUE_CONF DEFAULT verbose True
|
||||
iniset $CUE_CONF DEFAULT state_path $CUE_STATE_PATH
|
||||
iniset $CUE_CONF database connection `database_connection_url cue`
|
||||
|
||||
# Support db as a persistence backend
|
||||
if [ "$CUE_TF_PERSISTENCE" == "db" ]; then
|
||||
iniset $CUE_CONF taskflow persistence_connection `database_connection_url $CUE_TF_DB`
|
||||
fi
|
||||
|
||||
# Set cluster node check timeouts
|
||||
iniset $CUE_CONF taskflow cluster_node_check_timeout 30
|
||||
iniset $CUE_CONF taskflow cluster_node_check_max_count 120
|
||||
|
||||
# Set flow create cluster node vm active retry count
|
||||
iniset $CUE_CONF flow_options create_cluster_node_vm_active_retry_count $CUE_TF_CREATE_CLUSTER_NODE_VM_ACTIVE_RETRY_COUNT
|
||||
|
||||
iniset $CUE_CONF openstack os_auth_url $KEYSTONE_AUTH_PROTOCOL://$KEYSTONE_AUTH_HOST:$KEYSTONE_AUTH_PORT/v3
|
||||
iniset $CUE_CONF openstack os_project_name admin
|
||||
iniset $CUE_CONF openstack os_username admin
|
||||
iniset $CUE_CONF openstack os_password $ADMIN_PASSWORD
|
||||
iniset $CUE_CONF openstack os_project_domain_name default
|
||||
iniset $CUE_CONF openstack os_user_domain_name default
|
||||
iniset $CUE_CONF openstack os_auth_version 3
|
||||
|
||||
if [ "$SYSLOG" != "False" ]; then
|
||||
iniset $CUE_CONF DEFAULT use_syslog True
|
||||
fi
|
||||
|
||||
# Format logging
|
||||
if [ "$LOG_COLOR" == "True" ] && [ "$SYSLOG" == "False" ]; then
|
||||
setup_colorized_logging $CUE_CONF DEFAULT "tenant" "user"
|
||||
fi
|
||||
|
||||
# Set some libraries' log level to INFO so that the log isn't overrun with useless DEBUG messages
|
||||
iniset $CUE_CONF DEFAULT default_log_levels "kazoo.client=INFO,stevedore=INFO"
|
||||
|
||||
if is_service_enabled key; then
|
||||
# Setup the Keystone Integration
|
||||
iniset $CUE_CONF service:api auth_strategy keystone
|
||||
configure_auth_token_middleware $CUE_CONF cue $CUE_AUTH_CACHE_DIR
|
||||
fi
|
||||
|
||||
iniset $CUE_CONF service:api api_host $CUE_SERVICE_HOST
|
||||
iniset $CUE_CONF service:api api_base_uri $CUE_SERVICE_PROTOCOL://$CUE_SERVICE_HOST:$CUE_SERVICE_PORT/
|
||||
if is_service_enabled tls-proxy; then
|
||||
# Set the service port for a proxy to take the original
|
||||
iniset $CUE_CONF service:api api_port $CUE_SERVICE_PORT_INT
|
||||
else
|
||||
iniset $CUE_CONF service:api api_port $CUE_SERVICE_PORT
|
||||
fi
|
||||
|
||||
# Install the policy file for the API server
|
||||
cp $CUE_DIR/etc/cue/policy.json $CUE_CONF_DIR/policy.json
|
||||
iniset $CUE_CONF DEFAULT policy_file $CUE_CONF_DIR/policy.json
|
||||
}
|
||||
|
||||
# create_cue_accounts - Set up common required cue accounts
|
||||
|
||||
# Tenant User Roles
|
||||
# ------------------------------------------------------------------
|
||||
# service cue admin # if enabled
|
||||
function create_cue_accounts {
|
||||
local admin_role=$(openstack role list | awk "/ admin / { print \$2 }")
|
||||
|
||||
if [[ "$ENABLED_SERVICES" =~ "cue-api" ]]; then
|
||||
local cue_user=$(get_or_create_user "cue" \
|
||||
"$SERVICE_PASSWORD" "default")
|
||||
get_or_add_user_project_role $admin_role $cue_user $SERVICE_TENANT_NAME
|
||||
|
||||
if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
|
||||
local cue_service=$(get_or_create_service "cue" \
|
||||
"message-broker" "Message Broker Provisioning Service")
|
||||
get_or_create_endpoint $cue_service \
|
||||
"$REGION_NAME" \
|
||||
"$CUE_SERVICE_PROTOCOL://$CUE_SERVICE_HOST:$CUE_SERVICE_PORT/" \
|
||||
"$CUE_SERVICE_PROTOCOL://$CUE_SERVICE_HOST:$CUE_SERVICE_PORT/" \
|
||||
"$CUE_SERVICE_PROTOCOL://$CUE_SERVICE_HOST:$CUE_SERVICE_PORT/"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function create_cue_initial_resources {
|
||||
#ADMIN_TENANT_ID=$(keystone tenant-list | grep " admin " | get_field 1)
|
||||
echo "Creating initial resources."
|
||||
}
|
||||
|
||||
# init_cue - Initialize etc.
|
||||
function init_cue {
|
||||
# Create cache dir
|
||||
sudo mkdir -p $CUE_AUTH_CACHE_DIR
|
||||
sudo chown $STACK_USER $CUE_AUTH_CACHE_DIR
|
||||
rm -f $CUE_AUTH_CACHE_DIR/*
|
||||
|
||||
# (Re)create cue database
|
||||
recreate_database cue utf8
|
||||
|
||||
# Init and migrate cue database
|
||||
cue-manage --config-file $CUE_CONF database upgrade
|
||||
|
||||
# Init and migrate cue pool-manager-cache
|
||||
if [ "$CUE_TF_PERSISTENCE" == "db" ]; then
|
||||
recreate_database $CUE_TF_DB utf8
|
||||
cue-manage --config-file $CUE_CONF taskflow upgrade
|
||||
fi
|
||||
|
||||
|
||||
NEUTRON_OS_URL="${Q_PROTOCOL}://$Q_HOST:$Q_PORT"
|
||||
OPENSTACK_CMD="openstack"
|
||||
NEUTRON_CMD="neutron"
|
||||
|
||||
# Create cue specific flavor if one does not exist
|
||||
if [[ -z $($OPENSTACK_CMD flavor list | grep $CUE_FLAVOR) ]]; then
|
||||
$OPENSTACK_CMD flavor create $CUE_FLAVOR_PARAMS $CUE_FLAVOR
|
||||
fi
|
||||
|
||||
# Set os_security_group
|
||||
if [[ -z $($OPENSTACK_CMD security group list | grep $CUE_RABBIT_SECURITY_GROUP) ]]; then
|
||||
$OPENSTACK_CMD security group create --description "Cue RabbitMQ broker security group" $CUE_RABBIT_SECURITY_GROUP
|
||||
$OPENSTACK_CMD security group rule create --src-ip 0.0.0.0/0 --proto tcp --dst-port 5672:5672 $CUE_RABBIT_SECURITY_GROUP
|
||||
$OPENSTACK_CMD security group rule create --src-ip 0.0.0.0/0 --proto tcp --dst-port 4369:4369 $CUE_RABBIT_SECURITY_GROUP
|
||||
$OPENSTACK_CMD security group rule create --src-ip 0.0.0.0/0 --proto tcp --dst-port 61000:61000 $CUE_RABBIT_SECURITY_GROUP
|
||||
$OPENSTACK_CMD security group rule create --src-ip 0.0.0.0/0 --proto tcp --dst-port 15672:15672 $CUE_RABBIT_SECURITY_GROUP
|
||||
fi
|
||||
|
||||
CUE_RABBIT_SECURITY_GROUP_ID=$($OPENSTACK_CMD security group list | grep $CUE_RABBIT_SECURITY_GROUP | tr -d ' ' | cut -f 2 -d '|')
|
||||
if [ $CUE_RABBIT_SECURITY_GROUP_ID ]; then
|
||||
iniset $CUE_CONF DEFAULT os_security_group $CUE_RABBIT_SECURITY_GROUP_ID
|
||||
fi
|
||||
|
||||
# Set VM management key
|
||||
if [ $CUE_MANAGEMENT_KEY ]; then
|
||||
iniset $CUE_CONF openstack os_key_name $CUE_MANAGEMENT_KEY
|
||||
fi
|
||||
|
||||
# Create cue management-network
|
||||
if [[ -z $($NEUTRON_CMD net-list | grep $CUE_MANAGEMENT_NETWORK_NAME) ]]; then
|
||||
$NEUTRON_CMD net-create $CUE_MANAGEMENT_NETWORK_NAME --provider:network-type local
|
||||
CUE_MANAGEMENT_SUBNET_ROUTER_IP="$(echo $CUE_MANAGEMENT_NETWORK_SUBNET | cut -f 1-3 -d '.').1"
|
||||
$NEUTRON_CMD subnet-create $CUE_MANAGEMENT_NETWORK_NAME $CUE_MANAGEMENT_NETWORK_SUBNET --name $CUE_MANAGEMENT_SUBNET_NAME --host-route destination=$FLOATING_RANGE,nexthop=$CUE_MANAGEMENT_SUBNET_ROUTER_IP
|
||||
$NEUTRON_CMD router-interface-add $Q_ROUTER_NAME $CUE_MANAGEMENT_SUBNET_NAME
|
||||
fi
|
||||
|
||||
# Configure host route to management-network
|
||||
CUE_MANAGEMENT_SUBNET_IP=$(echo $CUE_MANAGEMENT_NETWORK_SUBNET | cut -f 1 -d '/')
|
||||
if [[ -z $(netstat -rn | grep $CUE_MANAGEMENT_SUBNET_IP ) ]]; then
|
||||
if [[ ! -z $($NEUTRON_CMD router-show $Q_ROUTER_NAME 2>/dev/null) ]]; then
|
||||
ROUTER_IP=$($NEUTRON_CMD router-show $Q_ROUTER_NAME | grep ip_address | cut -f 16 -d '"')
|
||||
sudo route add -net $CUE_MANAGEMENT_NETWORK_SUBNET gw $ROUTER_IP
|
||||
fi
|
||||
fi
|
||||
|
||||
# Set management-network id
|
||||
CUE_MANAGEMENT_NETWORK_ID=$($NEUTRON_CMD net-list | grep $CUE_MANAGEMENT_NETWORK_NAME | tr -d ' ' | cut -f 2 -d '|')
|
||||
if [ $CUE_MANAGEMENT_NETWORK_ID ]; then
|
||||
iniset $CUE_CONF DEFAULT management_network_id $CUE_MANAGEMENT_NETWORK_ID
|
||||
fi
|
||||
|
||||
set_broker
|
||||
|
||||
configure_scenario_rally_tests
|
||||
|
||||
build_cue_rabbit_test_image
|
||||
}
|
||||
|
||||
# install_cue - Collect source and prepare
|
||||
function install_cue {
|
||||
git_clone $CUE_REPO $CUE_DIR $CUE_BRANCH
|
||||
setup_develop $CUE_DIR
|
||||
}
|
||||
|
||||
# install_cueclient - Collect source and prepare
|
||||
function install_cueclient {
|
||||
git_clone $CUECLIENT_REPO $CUECLIENT_DIR $CUECLIENT_BRANCH
|
||||
setup_develop $CUECLIENT_DIR
|
||||
}
|
||||
|
||||
# install_cuedashboard - Collect source and prepare
|
||||
function install_cuedashboard {
|
||||
|
||||
if is_service_enabled horizon; then
|
||||
git_clone $CUEDASHBOARD_REPO $CUEDASHBOARD_DIR $CUEDASHBOARD_BRANCH
|
||||
setup_develop $CUEDASHBOARD_DIR
|
||||
|
||||
if ! [ -h $DEST/horizon/openstack_dashboard/local/enabled/_70_cue_panel_group.py ]; then
|
||||
ln -s $DEST/cue-dashboard/_70_cue_panel_group.py $DEST/horizon/openstack_dashboard/local/enabled/_70_cue_panel_group.py
|
||||
fi
|
||||
if ! [ -h $DEST/horizon/openstack_dashboard/local/enabled/_71_cue_panel.py ]; then
|
||||
ln -s $DEST/cue-dashboard/_71_cue_panel.py $DEST/horizon/openstack_dashboard/local/enabled/_71_cue_panel.py
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# configure Cue Scenario Rally tests
|
||||
function configure_scenario_rally_tests {
|
||||
|
||||
if ! [ -d $HOME/.rally/plugins ]; then
|
||||
mkdir -p $HOME/.rally/plugins/cue_scenarios
|
||||
|
||||
SCENARIOS=$(find $DEST/cue/rally-jobs/plugins -type f -name "*.py")
|
||||
for SCENARIO in $SCENARIOS
|
||||
do
|
||||
FILE_NAME=$(echo $SCENARIO | rev | cut -d/ -f1 | rev)
|
||||
ln -s $SCENARIO $HOME/.rally/plugins/cue_scenarios/$FILE_NAME
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
# start_cue - Start running processes, including screen
|
||||
function start_cue {
|
||||
run_process cue-api "$CUE_BIN_DIR/cue-api --config-file $CUE_CONF"
|
||||
run_process cue-worker "$CUE_BIN_DIR/cue-worker --config-file $CUE_CONF"
|
||||
run_process cue-monitor "$CUE_BIN_DIR/cue-monitor --config-file $CUE_CONF"
|
||||
|
||||
# Start proxies if enabled
|
||||
if is_service_enabled cue-api && is_service_enabled tls-proxy; then
|
||||
start_tls_proxy '*' $CUE_SERVICE_PORT $CUE_SERVICE_HOST $CUE_SERVICE_PORT_INT &
|
||||
fi
|
||||
|
||||
if ! timeout $SERVICE_TIMEOUT sh -c "while ! wget --no-proxy -q -O- $CUE_SERVICE_PROTOCOL://$CUE_SERVICE_HOST:$CUE_SERVICE_PORT; do sleep 1; done"; then
|
||||
die $LINENO "Cue did not start"
|
||||
fi
|
||||
}
|
||||
|
||||
# stop_cue - Stop running processes
|
||||
function stop_cue {
|
||||
# Kill the cue screen windows
|
||||
stop_process cue-api
|
||||
}
|
||||
|
||||
# build_cue_rabbit_test_image() - Build and upload functional test image
|
||||
function build_cue_rabbit_test_image {
|
||||
if is_service_enabled dib; then
|
||||
local image_name=cue-rabbitmq-test-image
|
||||
|
||||
# Elements path for tripleo-image-elements and cue-image-elements
|
||||
local elements_path=$TIE_DIR/elements:$CUE_DIR/contrib/image-elements
|
||||
|
||||
disk_image_create_upload "$image_name" "$CUE_RABBIT_IMAGE_ELEMENTS" "$elements_path"
|
||||
|
||||
# Set image_id
|
||||
RABBIT_IMAGE_ID=$($OPENSTACK_CMD image list | grep $image_name | tr -d ' ' | cut -f 2 -d '|')
|
||||
if [ "$RABBIT_IMAGE_ID" ]; then
|
||||
cue-manage --config-file $CUE_CONF broker add_metadata $BROKER_ID --image $RABBIT_IMAGE_ID
|
||||
fi
|
||||
|
||||
else
|
||||
echo "Error, Builing RabbitMQ Image requires dib" >&2
|
||||
echo "Add \"enable_service dib\" to your localrc" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# set_broker - Set default broker
|
||||
function set_broker {
|
||||
cue-manage --config-file $CUE_CONF broker add $CUE_DEFAULT_BROKER_NAME true
|
||||
BROKER_ID=$(cue-manage --config-file $CUE_CONF broker list | grep $CUE_DEFAULT_BROKER_NAME | tr -d ' ' | cut -f 2 -d '|')
|
||||
}
|
||||
|
||||
# Restore xtrace
|
||||
$XTRACE
|
@ -1,123 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# lib/dib
|
||||
# Install and build images with **diskimage-builder**
|
||||
|
||||
# Dependencies:
|
||||
#
|
||||
# - functions
|
||||
# - DEST, DATA_DIR must be defined
|
||||
|
||||
# stack.sh
|
||||
# ---------
|
||||
# - install_dib
|
||||
|
||||
# Save trace setting
|
||||
XTRACE=$(set +o | grep xtrace)
|
||||
set +o xtrace
|
||||
|
||||
# Defaults
|
||||
# --------
|
||||
|
||||
# set up default directories
|
||||
DIB_DIR=$DEST/diskimage-builder
|
||||
TIE_DIR=$DEST/tripleo-image-elements
|
||||
|
||||
# NOTE: Setting DIB_APT_SOURCES assumes you will be building
|
||||
# Debian/Ubuntu based images. Leave unset for other flavors.
|
||||
DIB_APT_SOURCES=${DIB_APT_SOURCES:-""}
|
||||
DIB_BUILD_OFFLINE=$(trueorfalse False DIB_BUILD_OFFLINE)
|
||||
DIB_IMAGE_CACHE=$DATA_DIR/diskimage-builder/image-create
|
||||
DIB_PIP_REPO=$DATA_DIR/diskimage-builder/pip-repo
|
||||
DIB_PIP_REPO_PORT=${DIB_PIP_REPO_PORT:-8899}
|
||||
|
||||
OCC_DIR=$DEST/os-collect-config
|
||||
ORC_DIR=$DEST/os-refresh-config
|
||||
OAC_DIR=$DEST/os-apply-config
|
||||
|
||||
# Tripleo elements for diskimage-builder images
|
||||
TIE_REPO=${TIE_REPO:-${GIT_BASE}/openstack/tripleo-image-elements.git}
|
||||
TIE_BRANCH=${TIE_BRANCH:-master}
|
||||
|
||||
# QEMU Image Options
|
||||
DIB_QEMU_IMG_OPTIONS='compat=0.10'
|
||||
|
||||
# Functions
|
||||
# ---------
|
||||
|
||||
# install_dib() - Collect source and prepare
|
||||
function install_dib {
|
||||
pip_install diskimage-builder
|
||||
|
||||
git_clone $TIE_REPO $TIE_DIR $TIE_BRANCH
|
||||
git_clone $OCC_REPO $OCC_DIR $OCC_BRANCH
|
||||
git_clone $ORC_REPO $ORC_DIR $ORC_BRANCH
|
||||
git_clone $OAC_REPO $OAC_DIR $OAC_BRANCH
|
||||
mkdir -p $DIB_IMAGE_CACHE
|
||||
}
|
||||
|
||||
# disk_image_create_upload() - Creates and uploads a diskimage-builder built image
|
||||
function disk_image_create_upload {
|
||||
|
||||
local image_name=$1
|
||||
local image_elements=$2
|
||||
local elements_path=$3
|
||||
|
||||
local image_path=$TOP_DIR/files/$image_name.qcow2
|
||||
|
||||
# Include the apt-sources element in builds if we have an
|
||||
# alternative sources.list specified.
|
||||
if [ -n "$DIB_APT_SOURCES" ]; then
|
||||
if [ ! -e "$DIB_APT_SOURCES" ]; then
|
||||
die $LINENO "DIB_APT_SOURCES set but not found at $DIB_APT_SOURCES"
|
||||
fi
|
||||
local extra_elements="apt-sources"
|
||||
fi
|
||||
|
||||
# Set the local pip repo as the primary index mirror so the
|
||||
# image is built with local packages
|
||||
local pypi_mirror_url=http://$SERVICE_HOST:$DIB_PIP_REPO_PORT/
|
||||
local pypi_mirror_url_1
|
||||
|
||||
if [ -a $HOME/.pip/pip.conf ]; then
|
||||
# Add the current pip.conf index-url as an extra-index-url
|
||||
# in the image build
|
||||
pypi_mirror_url_1=$(iniget $HOME/.pip/pip.conf global index-url)
|
||||
else
|
||||
# If no pip.conf, set upstream pypi as an extra mirror
|
||||
# (this also sets the .pydistutils.cfg index-url)
|
||||
pypi_mirror_url_1=http://pypi.python.org/simple
|
||||
fi
|
||||
|
||||
QEMU_IMG_OPTION=""
|
||||
if [ ! -z "${DIB_QEMU_IMG_OPTIONS}" ]; then
|
||||
QEMU_IMG_OPTION="--qemu-img-options ${DIB_QEMU_IMG_OPTIONS}"
|
||||
fi
|
||||
|
||||
# The disk-image-create command to run
|
||||
ELEMENTS_PATH=$elements_path \
|
||||
DIB_APT_SOURCES=$DIB_APT_SOURCES \
|
||||
DIB_OFFLINE=$DIB_BUILD_OFFLINE \
|
||||
PYPI_MIRROR_URL=$pypi_mirror_url \
|
||||
PYPI_MIRROR_URL_1=$pypi_mirror_url_1 \
|
||||
disk-image-create -a amd64 $image_elements ${extra_elements:-} \
|
||||
--image-cache $DIB_IMAGE_CACHE \
|
||||
${QEMU_IMG_OPTION} \
|
||||
-o $image_path
|
||||
|
||||
local token=$(openstack token issue | grep ' id ' | get_field 2)
|
||||
die_if_not_set $LINENO token "Keystone fail to get token"
|
||||
|
||||
glance --os-auth-token $token --os-image-url http://$GLANCE_HOSTPORT \
|
||||
image-create --name $image_name --visibility public \
|
||||
--container-format=bare --disk-format qcow2 \
|
||||
< $image_path
|
||||
}
|
||||
|
||||
# Restore xtrace
|
||||
$XTRACE
|
||||
|
||||
# Tell emacs to use shell-script-mode
|
||||
## Local variables:
|
||||
## mode: shell-script
|
||||
## End:
|
@ -1,72 +0,0 @@
|
||||
#
|
||||
# Default ${DEVSTACK_DIR}/local.conf file for Cue
|
||||
#
|
||||
|
||||
[[local|localrc]]
|
||||
|
||||
# Default passwords
|
||||
ADMIN_PASSWORD=password
|
||||
MYSQL_PASSWORD=password
|
||||
RABBIT_PASSWORD=password
|
||||
SERVICE_PASSWORD=password
|
||||
SERVICE_TOKEN=password
|
||||
|
||||
# Enable Logging
|
||||
LOGFILE=/opt/stack/logs/stack.sh.log
|
||||
VERBOSE=True
|
||||
LOG_COLOR=True
|
||||
SCREEN_LOGDIR=/opt/stack/logs
|
||||
|
||||
# Disable global requirements checks
|
||||
REQUIREMENTS_MODE=soft
|
||||
|
||||
# Set loopback volume size
|
||||
VOLUME_BACKING_FILE_SIZE=15G
|
||||
|
||||
# Enable novnc
|
||||
enable_service n-novnc
|
||||
|
||||
#
|
||||
# Enable Neutron
|
||||
# https://wiki.openstack.org/wiki/NeutronDevstack
|
||||
#
|
||||
disable_service n-net
|
||||
enable_service q-svc
|
||||
enable_service q-agt
|
||||
enable_service q-dhcp
|
||||
enable_service q-l3
|
||||
enable_service q-meta
|
||||
enable_service neutron
|
||||
|
||||
# Neutron Configuration
|
||||
FLOATING_RANGE=192.168.15.0/27
|
||||
FLAT_INTERFACE=eth0
|
||||
Q_FLOATING_ALLOCATION_POOL=start=192.168.15.10,end=192.168.15.30
|
||||
PUBLIC_NETWORK_GATEWAY=192.168.15.1
|
||||
|
||||
# Enable Swift
|
||||
enable_service s-proxy
|
||||
enable_service s-object
|
||||
enable_service s-container
|
||||
enable_service s-account
|
||||
|
||||
# Swift Configuration
|
||||
SWIFT_HASH=12go358snjw24501
|
||||
|
||||
|
||||
# Enable Diskimage-builder
|
||||
enable_service dib
|
||||
|
||||
# Enable Zookeeper
|
||||
enable_service zookeeper
|
||||
|
||||
# Enable Cue
|
||||
enable_service cue
|
||||
enable_service cue-api
|
||||
enable_service cue-worker
|
||||
enable_service cue-monitor
|
||||
CUE_MANAGEMENT_KEY=cue-mgmt-key
|
||||
|
||||
# Rally auth version
|
||||
RALLY_AUTH_VERSION=v3
|
||||
|
@ -1,72 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -o xtrace
|
||||
|
||||
TOP_DIR=$(cd $(dirname "$0") && pwd)
|
||||
source $TOP_DIR/functions
|
||||
source $TOP_DIR/stackrc
|
||||
source $TOP_DIR/lib/cue
|
||||
DEST=${DEST:-/opt/stack}
|
||||
|
||||
IDENTITY_API_VERSION=3 source $TOP_DIR/openrc admin admin
|
||||
|
||||
IPTABLES_RULE='iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE'
|
||||
|
||||
# Create NAT rule to allow VMs to NAT to host IP
|
||||
if [[ -z $(sudo iptables -t nat -L | grep MASQUERADE | tr -d ' ' | grep anywhereanywhere) ]]; then
|
||||
sudo $IPTABLES_RULE
|
||||
fi
|
||||
|
||||
# Make VM NAT rule persistent
|
||||
# TODO(sputnik13): this should ideally be somewhere other than /etc/rc.local
|
||||
if [[ -z $(grep "$IPTABLES_RULE" /etc/rc.local) ]]; then
|
||||
sudo sed -i -e "s/^exit 0/$IPTABLES_RULE\nexit 0/" /etc/rc.local
|
||||
fi
|
||||
|
||||
if [[ ! -x /etc/rc.local ]]; then
|
||||
sudo chmod +x /etc/rc.local
|
||||
fi
|
||||
|
||||
# Generate an ssh keypair to add to devstack
|
||||
if [[ ! -f ~/.ssh/id_rsa ]]; then
|
||||
ssh-keygen -q -t rsa -N "" -f ~/.ssh/id_rsa
|
||||
# copying key to /tmp so that tests can access it
|
||||
cp ~/.ssh/id_rsa /tmp/cue-mgmt-key
|
||||
chmod 644 /tmp/cue-mgmt-key
|
||||
fi
|
||||
|
||||
if [[ -z $CUE_MANAGEMENT_KEY ]]; then
|
||||
CUE_MANAGEMENT_KEY='vagrant'
|
||||
fi
|
||||
|
||||
# Add ssh keypair to admin account
|
||||
if [[ -z $(openstack keypair list | grep $CUE_MANAGEMENT_KEY) ]]; then
|
||||
openstack keypair create --public-key ~/.ssh/id_rsa.pub $CUE_MANAGEMENT_KEY
|
||||
fi
|
||||
|
||||
# Add ping and ssh rules to rabbitmq security group
|
||||
neutron security-group-rule-create --direction ingress --protocol icmp --remote-ip-prefix 0.0.0.0/0 $CUE_RABBIT_SECURITY_GROUP
|
||||
neutron security-group-rule-create --direction ingress --protocol tcp --port-range-min 22 --port-range-max 22 --remote-ip-prefix 0.0.0.0/0 $CUE_RABBIT_SECURITY_GROUP
|
||||
|
||||
# Add static nameserver to private-subnet
|
||||
neutron subnet-update --dns-nameserver 8.8.8.8 private-subnet
|
||||
|
||||
unset OS_PROJECT_DOMAIN_ID
|
||||
unset OS_REGION_NAME
|
||||
unset OS_USER_DOMAIN_ID
|
||||
unset OS_IDENTITY_API_VERSION
|
||||
unset OS_PASSWORD
|
||||
unset OS_AUTH_URL
|
||||
unset OS_USERNAME
|
||||
unset OS_PROJECT_NAME
|
||||
unset OS_TENANT_NAME
|
||||
unset OS_VOLUME_API_VERSION
|
||||
unset COMPUTE_API_VERSION
|
||||
unset OS_NO_CACHE
|
||||
|
||||
# Add ssh keypair to demo account
|
||||
IDENTITY_API_VERSION=3 source $TOP_DIR/openrc demo demo
|
||||
if [[ -z $(openstack keypair list | grep $CUE_MANAGEMENT_KEY) ]]; then
|
||||
openstack keypair create --public-key ~/.ssh/id_rsa.pub $CUE_MANAGEMENT_KEY
|
||||
fi
|
||||
|
@ -1,113 +0,0 @@
|
||||
RabbitMQ disk images for the Cue service
|
||||
========================================
|
||||
|
||||
These elements are used to build disk images for the Cue service.
|
||||
|
||||
#Notes on building disk images
|
||||
|
||||
Building images involves using the Tripleo `diskimage-builder` tools that are found in
|
||||
the GitHub repository given below.
|
||||
|
||||
Note that recent changes to this package mean that before the `diskimage-builder` tools
|
||||
can be used it is necessary to install `dib-utils` as shown below in order to satisfy
|
||||
all dependencies. The modified `PATH` definition should be included in `.profile`, or
|
||||
somewhere similarly appropriate.
|
||||
```
|
||||
$ git clone https://github.com/openstack/diskimage-builder
|
||||
$ export PATH=$HOME/diskimage-builder/bin:$PATH
|
||||
$ pip install dib-utils
|
||||
```
|
||||
|
||||
In addition (and in accordance with the instructions provided for the `diskimage-builder`
|
||||
package) it is also necessary to install the `qemu-utils` and `kpartx` packages:
|
||||
```
|
||||
$ sudo apt-get install qemu-utils
|
||||
$ sudo apt-get install kpartx
|
||||
```
|
||||
|
||||
It should now be possible to execute commands such as the following to create disk images.
|
||||
```
|
||||
$ disk-image-create -a amd64 -o ubuntu-amd64 vm ubuntu
|
||||
```
|
||||
|
||||
The next step is to fold in our Cue-specific image elements (the elements found here). This
|
||||
is straightforward, and basically just involves defining `ELEMENT_PATH` to include the
|
||||
locations of all applicable elements as a colon-separated list. But first, we need to be
|
||||
aware that Cue images is going to require some elements from Tripleo (namely `iptables` and
|
||||
`sysctl`), so before getting too carried away, we need to clone the repository containing
|
||||
these elements:
|
||||
```
|
||||
$ git clone https://github.com/openstack/tripleo-image-elements
|
||||
```
|
||||
|
||||
Now, assuming that we have our Cue-specific elements in `./cue-image-elements/elements`, we
|
||||
can define `ELEMENT_PATH` as follows, and then try building an image:
|
||||
```
|
||||
$ export ELEMENTS_PATH=$HOME/cue/cue-image-elements/elements:$HOME/tripleo-image-elements/elements
|
||||
$ disk-image-create -a amd64 -o ubuntu-amd64-brc-rabbit vm ubuntu cue-rabbitmq-plugins
|
||||
```
|
||||
|
||||
Change the base image (in this case Ubuntu) and other parameters as appropriate. Assuming that all is well, the above command sequence
|
||||
will result in the creation of an image named `ubuntu-amd64-brc-rabbit.qcow2`, which can then be loaded into glance and tested.
|
||||
|
||||
#What is currently in the Cue service RabbitMQ image
|
||||
The intention is to keep the RabbitMQ disk image for Cue relatively simple. The image will provide little more than a basic installation of
|
||||
RabbitMQ with the Keystone and managemnent plugins enabled; however the initial `rabbitmq.config` will not specify the use of the Keystone
|
||||
plugin for authentication. After the disk image is booted and RabbitMQ started, the Cue service will be expected to perform the necessary
|
||||
sequence of operations to construct a cluster (if more than one node) and activate Keystone-based authentication.
|
||||
|
||||
##Some point(s) to note
|
||||
- The image includes a fairly basic `rabbitmq.config` that should be retained until after the cluster has been created. Once the cluster has
|
||||
been created and verified, this initial `rabbitmq.config` should be replaced by the Cue service using the template configuration file
|
||||
`rabbitmq.config.cue-template` (both files are to be found in `/etc/rabbitmq`), populating it with the desired Keystone endpoint. Additional
|
||||
notes on this matter can be found below.
|
||||
- For testing purposes, the `rabbitmq.config` currently included in the image enables `guest` logon (`{loopback_users,[]}`). This should be
|
||||
disabled before generating any production images!
|
||||
- Two targets are provided in the `elements` directory, namely `cue-rabbitmq-base` and `cue-rabbitmq-plugins`. The former can be used to
|
||||
create an image that includes a bare-bones vanilla RabbitMQ installation with no plugins enabled. The latter depends on (inherits) `cue-rabbitmq-base`
|
||||
and can be used to create iamges with the management and Keystone authentication plugins enabled.
|
||||
|
||||
|
||||
#Notes about what the Cue service needs to do
|
||||
Once the Cue service is satisfied that all nodes have successfully booted and RabbitMQ is available, the service should perform the following
|
||||
general sequence of events to cluster the nodes (if necessary) and activate Keystone-based authentication.
|
||||
|
||||
- Update `/etc/hosts` on all nodes to include the IP addresses of the cluster nodes
|
||||
|
||||
- Create a cookie file (`/var/lib/rabbitq/.erlang.cookie`) on each node (using the same cookie). A reasonable choice for the cookie string
|
||||
might be the UUID generated by Cue to uniquely identify the cluster. Ensure that the cookie file has the correct permissions and owner.
|
||||
|
||||
```
|
||||
$ sudo chmod 400 /var/lib/rabbitmq/.erlang.cookie
|
||||
$ sudo chown rabbitmq:rabbitmq /var/lib/rabbitmq/.erlang.cookie
|
||||
```
|
||||
|
||||
Once the above two steps have been performed, it is possible to construct the cluster.
|
||||
|
||||
- On all one of the cluster nodes issue the following commands (replacing `your-hostname` as appropriate):
|
||||
```
|
||||
$ sudo rabbitmqctl stop_app
|
||||
$ sudo rabbitmqctl reset
|
||||
$ sudo rabbitmqctl join_cluster rabbit@your-hostname
|
||||
$ sudo rabbitmqctl start_app
|
||||
$ sudo rabbitmqctl cluster_status # to check the status of the cluster
|
||||
```
|
||||
|
||||
- Once the cluster has formed, replace the management plugin with the version patched for Cue and Keystone, and replace the existing
|
||||
`rabbitmq.config` file using the template configuration file (replace `X.Y.Z` with the relevant RabbitMQ version number, and replace the
|
||||
Keystone endpoint as appropriate):
|
||||
```
|
||||
$ sudo cp /usr/lib/rabbitmq/lib/rabbitmq_server-X.Y.Z/plugins/rabbitmq_management-X.Y.Z.ez.cue /usr/lib/rabbitmq/lib/rabbitmq_server-X.Y.Z/plugins//rabbitmq_management-X.Y.Z.ez
|
||||
$ sed 's/##keystone_url##/https:\/\/region-a.geo-1.identity.hpcloudsvc.com:35357\/v3\/auth\/tokens/' /etc/rabbitmq/rabbitmq.config.cue-template > /etc/rabbitmq/rabbitmq.config
|
||||
```
|
||||
|
||||
- Systematically restart each cluster node, waiting until the node comes back up before restarting the next node.
|
||||
- Finally, create the user (using the users' Keystone username) and grant them appropriate permissions (replacing `keystone-username` with the relevant username). For good measure, also delete the `guest` user:
|
||||
```
|
||||
$ sudo rabbitmqctl add_user keystone-username nopassword
|
||||
$ sudo rabbitmqctl set_permissions -p / keystone-username ".*" ".*" ".*"
|
||||
$ sudo rabbitmqctl set_user_tags keystone-username administrator
|
||||
$ sudo rabbitmqctl delete_user guest
|
||||
```
|
||||
The user can now be informed that the cluster is ready for use.
|
||||
|
@ -1 +0,0 @@
|
||||
Install RabbitMQ VM as part of MSGaaS single-tenant cluster.
|
@ -1 +0,0 @@
|
||||
package-installs
|
@ -1,8 +0,0 @@
|
||||
ntp:
|
||||
phase: install.d
|
||||
|
||||
python-pip:
|
||||
phase: install.d
|
||||
|
||||
rabbitmq-server:
|
||||
phase: install.d
|
@ -1,43 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eux
|
||||
|
||||
FILES="$(dirname $0)/../files"
|
||||
|
||||
if [ "$DISTRO_NAME" = "ubuntu" ] || [ "$DISTRO_NAME" = "debian" ]; then
|
||||
# Prevent rabbitmq-server from starting automatically
|
||||
update-rc.d -f rabbitmq-server disable
|
||||
fi
|
||||
|
||||
if [ "$DIB_INIT_SYSTEM" = "systemd" ]; then
|
||||
# Delay the rc-local.service start-up until rabbitmq-server.service is started up
|
||||
sed -i 's/\[Unit\]/\[Unit\]\nBefore=rc-local.service/g' /lib/systemd/system/rabbitmq-server.service
|
||||
|
||||
# Respawn rabbitmq-server in case the process exits with an nonzero exit code
|
||||
sed -i 's/\[Service\]/\[Service\]\nRestart=on-failure/g' /lib/systemd/system/rabbitmq-server.service
|
||||
fi
|
||||
|
||||
# Enable ulimits in pam if needed
|
||||
PAM_FILE=/etc/pam.d/su
|
||||
sed -i '/# session.*pam_limits\.so/s/# //' ${PAM_FILE}
|
||||
|
||||
# Reserve the cluster port (61000) from the ephemeral port range.
|
||||
EXISTING_RESERVED_PORTS=$(grep -r net.ipv4.ip_local_reserved_ports /etc/sysctl.conf /etc/sysctl.d 2> /dev/null | cut -d'=' -f2)
|
||||
RESERVED_PORTS=61000
|
||||
if ! [ -z $EXISTING_RESERVED_PORTS ]; then
|
||||
# create one port reservation list
|
||||
for port in $EXISTING_RESERVED_PORTS; do
|
||||
RESERVED_PORTS=$RESERVED_PORTS,$port
|
||||
done
|
||||
|
||||
# find files with port reservation settings
|
||||
RESERVATION_FILE_LIST=$(grep -r net.ipv4.ip_local_reserved_ports /etc/sysctl.conf /etc/sysctl.d 2> /dev/null | cut -d':' -f1 | sort | uniq)
|
||||
|
||||
# comment out existing port reservation lines
|
||||
for file in $RESERVATION_FILE_LIST; do
|
||||
sed -i -e 's/\(^net.ipv4.ip_local_reserved_ports=.*\)/#\1/' $file
|
||||
done
|
||||
|
||||
# add port reservation
|
||||
echo "net.ipv4.ip_local_reserved_ports=${RESERVED_PORTS}"
|
||||
fi
|
@ -1,64 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eux
|
||||
|
||||
pip install pika
|
||||
|
||||
cat > /opt/rabbitmq_test.py << EOF
|
||||
import argparse
|
||||
import time
|
||||
|
||||
import pika
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-H", "--host", required=True,
|
||||
help="Specify the RabbitMQ host")
|
||||
parser.add_argument("-R", "--receive",
|
||||
help="Specify the RabbitMQ host to receive message")
|
||||
parser.add_argument("-P", "--port", required=True,
|
||||
help="Specify the RabbitMQ port",
|
||||
type=int)
|
||||
parser.add_argument("-u", "--user", required=True,
|
||||
help="Specify the RabbitMQ username")
|
||||
parser.add_argument("-p", "--password", required=True,
|
||||
help="Specify the RabbitMQ password")
|
||||
parser.add_argument("--ssl", dest="ssl", action="store_true",
|
||||
help="Specify whether to use AMQPS protocol")
|
||||
args = parser.parse_args()
|
||||
|
||||
host = args.host
|
||||
|
||||
credentials = pika.PlainCredentials(args.user, args.password)
|
||||
connection = pika.BlockingConnection(pika.ConnectionParameters(
|
||||
credentials=credentials, host=host, port=args.port, ssl=args.ssl))
|
||||
channel = connection.channel()
|
||||
channel.queue_declare(queue='hello')
|
||||
|
||||
if args.receive:
|
||||
connection_receive = pika.BlockingConnection(pika.ConnectionParameters(
|
||||
credentials=credentials, host=args.receive, port=args.port,
|
||||
ssl=args.ssl))
|
||||
channel_receive = connection_receive.channel()
|
||||
channel_receive.queue_declare(queue='hello')
|
||||
else:
|
||||
channel_receive = channel
|
||||
|
||||
for count in range(1, 10, 1):
|
||||
print("Sending...")
|
||||
channel.basic_publish(exchange='', routing_key='hello',
|
||||
body='Hello World!' + str(count))
|
||||
print(" [x] Sent 'Hello World!'" + str(count))
|
||||
print("Receiving...")
|
||||
method_frame, header_frame, body = channel_receive.basic_get('hello')
|
||||
if method_frame:
|
||||
print(method_frame, header_frame, body)
|
||||
channel_receive.basic_ack(method_frame.delivery_tag)
|
||||
else:
|
||||
print('No message returned')
|
||||
time.sleep(1)
|
||||
connection.close()
|
||||
EOF
|
||||
|
||||
chmod 777 /opt/rabbitmq_test.py
|
@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eux
|
||||
|
||||
echo 'deb http://www.rabbitmq.com/debian/ testing main' > /etc/apt/sources.list.d/rabbitmq.list
|
||||
wget -O- https://www.rabbitmq.com/rabbitmq-release-signing-key.asc |
|
||||
sudo apt-key add -
|
@ -1,35 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eux
|
||||
|
||||
install-packages ifmetric
|
||||
|
||||
cat <<EOF > /etc/network/interfaces
|
||||
# The loopback network interface
|
||||
auto lo
|
||||
iface lo inet loopback
|
||||
|
||||
source /etc/network/interfaces.d/*
|
||||
EOF
|
||||
|
||||
mkdir -p /etc/network/interfaces.d
|
||||
rm /etc/network/interfaces.d/*
|
||||
|
||||
cat <<EOF > /etc/network/interfaces.d/eth0
|
||||
# The primary network interface
|
||||
allow-hotplug eth0
|
||||
iface eth0 inet dhcp
|
||||
metric 0
|
||||
EOF
|
||||
|
||||
cat <<EOF > /etc/network/interfaces.d/eth1
|
||||
allow-hotplug eth1
|
||||
iface eth1 inet dhcp
|
||||
metric 1
|
||||
EOF
|
||||
|
||||
cat <<EOF > /etc/network/interfaces.d/eth2
|
||||
allow-hotplug eth2
|
||||
iface eth2 inet dhcp
|
||||
metric 2
|
||||
EOF
|
@ -1,63 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eux
|
||||
|
||||
# Path Settings
|
||||
export CUE_HOME=$(readlink -e $(dirname $(readlink -f $0))/../..)
|
||||
|
||||
# BUILD_DIR Directory where builds will be performed and images will be left
|
||||
export BUILD_DIR=${BUILD_DIR:-$CUE_HOME/build}
|
||||
|
||||
# DIB Output Image Type
|
||||
export IMAGE_TYPE=${IMAGE_TYPE:-qcow2}
|
||||
|
||||
# Image Name
|
||||
BUILD_FILE="rabbitmq-cue-image.qcow2"
|
||||
|
||||
# Common elements we'll use in all builds
|
||||
COMMON_ELEMENTS=${COMMON_ELEMENTS:-"vm ubuntu"}
|
||||
|
||||
# Common Settings for all msgaas images builds
|
||||
SIZE="2"
|
||||
ELEMENTS="cue-rabbitmq-base ifmetric"
|
||||
ELEMENTS_PATH="$CUE_HOME/contrib/image-elements"
|
||||
|
||||
# QEMU Image options
|
||||
QEMU_IMG_OPTIONS='compat=0.10'
|
||||
|
||||
# Install some required apt packages if needed
|
||||
if ! [ -e /usr/sbin/debootstrap -a -e /usr/bin/qemu-img ]; then
|
||||
sudo apt-get update
|
||||
sudo apt-get install --yes debootstrap qemu-utils git python-virtualenv uuid-runtime curl wget parted kpartx
|
||||
fi
|
||||
|
||||
if [ ! -d $BUILD_DIR/diskimage-builder ]; then
|
||||
echo "---> Cloning diskimage-builder"
|
||||
git clone https://git.openstack.org/openstack/diskimage-builder $BUILD_DIR/diskimage-builder
|
||||
fi
|
||||
|
||||
# Setup the elements path
|
||||
export ELEMENTS_PATH="$ELEMENTS_PATH:$BUILD_DIR/diskimage-builder/elements"
|
||||
|
||||
# Prepare the build directory
|
||||
if [ ! -d $BUILD_DIR/dist ]; then
|
||||
mkdir $BUILD_DIR/dist
|
||||
fi
|
||||
|
||||
# Complete QEMU_IMG_OPTIONS
|
||||
if [ ! -z "${QEMU_IMG_OPTIONS}" ]; then
|
||||
QEMU_IMG_OPTIONS="--qemu-img-options ${QEMU_IMG_OPTIONS}"
|
||||
fi
|
||||
|
||||
# Prepare venv for diskimage-builder
|
||||
virtualenv $BUILD_DIR/diskimage-builder/.venv
|
||||
|
||||
# Build the image
|
||||
( set +u; . "$BUILD_DIR/diskimage-builder/.venv/bin/activate"; set -u;
|
||||
pushd $BUILD_DIR/diskimage-builder
|
||||
pip install -r requirements.txt
|
||||
python setup.py install
|
||||
popd
|
||||
disk-image-create -a amd64 -o $BUILD_DIR/dist/$BUILD_FILE --image-size $SIZE $QEMU_IMG_OPTIONS $COMMON_ELEMENTS $ELEMENTS
|
||||
)
|
||||
|
@ -1,190 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
while test $# -gt 0; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
echo "Single VM Cue Installer"
|
||||
echo " "
|
||||
echo "--h show brief help"
|
||||
echo "Required parameters:"
|
||||
echo "--image IMAGE_ID specify Nova image id to use"
|
||||
echo "--flavor FLAVOR_ID specify a Nova flavor id to use"
|
||||
echo "--cue-management-nic CUE_MANAGEMENT_NIC specify management network interface for cue"
|
||||
echo "--cue-image CUE_IMAGE_ID specify a Nova image id for Cue cluster VMs"
|
||||
echo "Optional parameters:"
|
||||
echo "--security-groups SECURITY_GROUPS specify security group"
|
||||
echo "--key-name KEY_NAME specify key-name to forward"
|
||||
echo "--nic NIC a network to attach Cue VM on"
|
||||
echo "--mysql-root-password MYSQL_ROOT_PASSWORD specify root password for MySql Server"
|
||||
echo "--mysql-cueapi-password MYSQL_CUEAPI_PASSWORD specify cue api user password for MySql Server"
|
||||
echo "--mysql-cueworker-password MYSQL_CUEWORKER_PASSWORD specify cue worker user password for MySql Server"
|
||||
exit 0
|
||||
;;
|
||||
--image)
|
||||
shift
|
||||
if test $# -gt 0; then
|
||||
export IMAGE_ID=$1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--flavor)
|
||||
shift
|
||||
if test $# -gt 0; then
|
||||
export FLAVOR_ID=$1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--cue-management-nic)
|
||||
shift
|
||||
if test $# -gt 0; then
|
||||
export CUE_MANAGEMENT_NIC=$1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--cue-image)
|
||||
shift
|
||||
if test $# -gt 0; then
|
||||
export CUE_IMAGE_ID=$1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--security-groups)
|
||||
shift
|
||||
if test $# -gt 0; then
|
||||
export SECURITY_GROUPS=$1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--cue-security-group)
|
||||
shift
|
||||
if test $# -gt 0; then
|
||||
export CUE_SECURITY_GROUP=$1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--key-name)
|
||||
shift
|
||||
if test $# -gt 0; then
|
||||
export KEY_NAME=$1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--os-key-name)
|
||||
shift
|
||||
if test $# -gt 0; then
|
||||
export OS_KEY_NAME=$1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--nic)
|
||||
shift
|
||||
if test $# -gt 0; then
|
||||
export NIC=$1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--mysql-root-password)
|
||||
shift
|
||||
if test $# -gt 0; then
|
||||
export MYSQL_ROOT_PASSWORD=$1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--mysql-cueapi-password)
|
||||
shift
|
||||
if test $# -gt 0; then
|
||||
export MYSQL_CUEAPI_PASSWORD=$1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--mysql-cueworker-password)
|
||||
shift
|
||||
if test $# -gt 0; then
|
||||
export MYSQL_CUEWORKER_PASSWORD=$1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--floating-ip)
|
||||
shift
|
||||
if test $# -gt 0; then
|
||||
export FLOATING_IP=$1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# verify required and optional input arguments
|
||||
if [ -z ${IMAGE_ID} ] || [ -z ${FLAVOR_ID} ] || [ -z ${CUE_IMAGE_ID} ] || [ -z ${CUE_MANAGEMENT_NIC} ]; then
|
||||
echo "IMAGE_ID, FLAVOR_ID, CUE_IMAGE_ID AND CUE_MANAGEMENT_NIC must be provided"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z ${MYSQL_ROOT_PASSWORD} ]; then
|
||||
MYSQL_ROOT_PASSWORD="password"
|
||||
fi
|
||||
if [ -z ${MYSQL_CUEAPI_PASSWORD} ]; then
|
||||
MYSQL_CUEAPI_PASSWORD="cuepassword"
|
||||
fi
|
||||
if [ -z ${MYSQL_CUEWORKER_PASSWORD} ]; then
|
||||
MYSQL_CUEWORKER_PASSWORD="workerpassword"
|
||||
fi
|
||||
|
||||
# set parameters required by mo to fill-in template file
|
||||
export MYSQL_ROOT_PASSWORD
|
||||
export MYSQL_CUEAPI_PASSWORD
|
||||
export MYSQL_CUEWORKER_PASSWORD
|
||||
|
||||
# set working directory to script location
|
||||
PROJECT_ROOT=$( cd $(dirname "$0") && pwd)
|
||||
pushd ${PROJECT_ROOT}
|
||||
|
||||
# Configure user data script from template file
|
||||
USERDATA_FILE=$(mktemp -t cue_install.XXXX)
|
||||
chmod +x mo
|
||||
cat user_data_template | ./mo > ${USERDATA_FILE}
|
||||
|
||||
# unset exported parameters from above
|
||||
unset MYSQL_ROOT_PASSWORD
|
||||
unset MYSQL_CUEAPI_PASSWORD
|
||||
unset MYSQL_CUEWORKER_PASSWORD
|
||||
|
||||
# Compose Nova boot command string
|
||||
NOVA_BOOT_BASE="nova boot"
|
||||
VM_NAME="cue_host"
|
||||
|
||||
NOVA_BOOT_COMMAND="${NOVA_BOOT_BASE} --flavor ${FLAVOR_ID} --image ${IMAGE_ID}"
|
||||
if [ ! -z ${SECURITY_GROUPS} ]; then
|
||||
NOVA_BOOT_COMMAND="${NOVA_BOOT_COMMAND} --security-groups ${SECURITY_GROUPS}"
|
||||
fi
|
||||
|
||||
if [ ! -z ${KEY_NAME} ]; then
|
||||
NOVA_BOOT_COMMAND="${NOVA_BOOT_COMMAND} --key-name ${KEY_NAME}"
|
||||
fi
|
||||
|
||||
OS_KEYNAME=${OS_KEYNAME:-$KEY_NAME}
|
||||
|
||||
if [ ! -z ${NIC} ]; then
|
||||
NOVA_BOOT_COMMAND="${NOVA_BOOT_COMMAND} --nic net-id=${NIC}"
|
||||
fi
|
||||
|
||||
if [ ! -z ${CUE_MANAGEMENT_NIC} ]; then
|
||||
NOVA_BOOT_COMMAND="${NOVA_BOOT_COMMAND} --nic net-id=${CUE_MANAGEMENT_NIC}"
|
||||
fi
|
||||
|
||||
NOVA_BOOT_COMMAND="${NOVA_BOOT_COMMAND} --user-data ${USERDATA_FILE} ${VM_NAME}"
|
||||
eval ${NOVA_BOOT_COMMAND}
|
||||
|
||||
if [ ! -z ${FLOATING_IP} ]; then
|
||||
echo "Waiting for cue_host VM to go ACTIVE..."
|
||||
while [ -z "$(nova show $VM_NAME 2>/dev/null | egrep 'ACTIVE|ERROR')" ]; do
|
||||
sleep 1
|
||||
done
|
||||
nova floating-ip-associate $VM_NAME ${FLOATING_IP}
|
||||
fi
|
||||
|
||||
rm ${USERDATA_FILE}
|
||||
popd
|
@ -1,47 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -x
|
||||
unset UCF_FORCE_CONFFOLD
|
||||
export UCF_FORCE_CONFFNEW=YES
|
||||
ucf --purge /boot/grub/menu.lst
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update
|
||||
apt-get -o Dpkg::Options::="--force-confnew" --force-yes -fuy dist-upgrade
|
||||
sudo apt-get -y install git
|
||||
cd /home/ubuntu/
|
||||
sudo -u ubuntu git clone https://git.openstack.org/openstack-dev/devstack
|
||||
cat > devstack/local.conf<< EOF
|
||||
[[local|localrc]]
|
||||
HOST_IP=127.0.0.1
|
||||
REQUIREMENTS_MODE=soft
|
||||
ADMIN_PASSWORD=password
|
||||
MYSQL_PASSWORD=password
|
||||
RABBIT_PASSWORD=password
|
||||
SERVICE_PASSWORD=password
|
||||
SERVICE_TOKEN=password
|
||||
LOGFILE=/opt/stack/logs/stack.sh.log
|
||||
VERBOSE=True
|
||||
LOG_COLOR=True
|
||||
SCREEN_LOGDIR=/opt/stack/logs
|
||||
disable_service g-api
|
||||
disable_service g-reg
|
||||
disable_service n-api
|
||||
disable_service n-crt
|
||||
disable_service n-obj
|
||||
disable_service n-cpu
|
||||
disable_service n-net
|
||||
disable_service n-cond
|
||||
disable_service n-sch
|
||||
disable_service n-novnc
|
||||
disable_service n-xvnc
|
||||
disable_service n-cauth
|
||||
disable_service c-sch
|
||||
disable_service c-api
|
||||
disable_service c-vol
|
||||
disable_service h-eng
|
||||
disable_service h-api
|
||||
disable_service h-api-cfn
|
||||
disable_service h-api-cw
|
||||
disable_service horizon
|
||||
disable_service tempest
|
||||
EOF
|
||||
sudo -u ubuntu ./devstack/stack.sh
|
@ -1,700 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Mo is a mustache template rendering software written in bash. It inserts
|
||||
# environment variables into templates.
|
||||
#
|
||||
# Learn more about mustache templates at https://mustache.github.io/
|
||||
#
|
||||
# Mo is under a MIT style licence with an additional non-advertising clause.
|
||||
# See LICENSE.md for the full text.
|
||||
#
|
||||
# This is open source! Please feel free to contribute.
|
||||
#
|
||||
# https://github.com/tests-always-included/mo
|
||||
|
||||
|
||||
# Scan content until the right end tag is found. Returns an array with the
|
||||
# following members:
|
||||
# [0] = Content before end tag
|
||||
# [1] = End tag (complete tag)
|
||||
# [2] = Content after end tag
|
||||
#
|
||||
# Everything using this function uses the "standalone tags" logic.
|
||||
#
|
||||
# Parameters:
|
||||
# $1: Where to store the array
|
||||
# $2: Content
|
||||
# $3: Name of end tag
|
||||
# $4: If -z, do standalone tag processing before finishing
|
||||
mustache-find-end-tag() {
|
||||
local CONTENT SCANNED
|
||||
|
||||
# Find open tags
|
||||
SCANNED=""
|
||||
mustache-split CONTENT "$2" '{{' '}}'
|
||||
|
||||
while [[ "${#CONTENT[@]}" -gt 1 ]]; do
|
||||
mustache-trim-whitespace TAG "${CONTENT[1]}"
|
||||
|
||||
# Restore CONTENT[1] before we start using it
|
||||
CONTENT[1]='{{'"${CONTENT[1]}"'}}'
|
||||
|
||||
case $TAG in
|
||||
'#'* | '^'*)
|
||||
# Start another block
|
||||
SCANNED="${SCANNED}${CONTENT[0]}${CONTENT[1]}"
|
||||
mustache-trim-whitespace TAG "${TAG:1}"
|
||||
mustache-find-end-tag CONTENT "${CONTENT[2]}" "$TAG" "loop"
|
||||
SCANNED="${SCANNED}${CONTENT[0]}${CONTENT[1]}"
|
||||
CONTENT=${CONTENT[2]}
|
||||
;;
|
||||
|
||||
'/'*)
|
||||
# End a block - could be ours
|
||||
mustache-trim-whitespace TAG "${TAG:1}"
|
||||
SCANNED="$SCANNED${CONTENT[0]}"
|
||||
|
||||
if [[ "$TAG" == "$3" ]]; then
|
||||
# Found our end tag
|
||||
if [[ -z "$4" ]] && mustache-is-standalone STANDALONE_BYTES "$SCANNED" "${CONTENT[2]}" true; then
|
||||
# This is also a standalone tag - clean up whitespace
|
||||
# and move those whitespace bytes to the "tag" element
|
||||
STANDALONE_BYTES=( $STANDALONE_BYTES )
|
||||
CONTENT[1]="${SCANNED:${STANDALONE_BYTES[0]}}${CONTENT[1]}${CONTENT[2]:0:${STANDALONE_BYTES[1]}}"
|
||||
SCANNED="${SCANNED:0:${STANDALONE_BYTES[0]}}"
|
||||
CONTENT[2]="${CONTENT[2]:${STANDALONE_BYTES[1]}}"
|
||||
fi
|
||||
|
||||
local "$1" && mustache-indirect-array "$1" "$SCANNED" "${CONTENT[1]}" "${CONTENT[2]}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
SCANNED="$SCANNED${CONTENT[1]}"
|
||||
CONTENT=${CONTENT[2]}
|
||||
;;
|
||||
|
||||
*)
|
||||
# Ignore all other tags
|
||||
SCANNED="${SCANNED}${CONTENT[0]}${CONTENT[1]}"
|
||||
CONTENT=${CONTENT[2]}
|
||||
;;
|
||||
esac
|
||||
|
||||
mustache-split CONTENT "$CONTENT" '{{' '}}'
|
||||
done
|
||||
|
||||
# Did not find our closing tag
|
||||
SCANNED="$SCANNED${CONTENT[0]}"
|
||||
local "$1" && mustache-indirect-array "$1" "${SCANNED}" "" ""
|
||||
}
|
||||
|
||||
|
||||
# Find the first index of a substring
|
||||
#
|
||||
# Parameters:
|
||||
# $1: Destination variable
|
||||
# $2: Haystack
|
||||
# $3: Needle
|
||||
mustache-find-string() {
|
||||
local POS STRING
|
||||
|
||||
STRING=${2%%$3*}
|
||||
[[ "$STRING" == "$2" ]] && POS=-1 || POS=${#STRING}
|
||||
local "$1" && mustache-indirect "$1" $POS
|
||||
}
|
||||
|
||||
|
||||
# Return a dotted name based on current context and target name
|
||||
#
|
||||
# Parameters:
|
||||
# $1: Target variable to store results
|
||||
# $2: Context name
|
||||
# $3: Desired variable name
|
||||
mustache-full-tag-name() {
|
||||
if [[ -z "$2" ]]; then
|
||||
local "$1" && mustache-indirect "$1" "$3"
|
||||
else
|
||||
local "$1" && mustache-indirect "$1" "${2}.${3}"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Return the content to parse. Can be a list of partials for files or
|
||||
# the content from stdin.
|
||||
#
|
||||
# Parameters:
|
||||
# $1: Variable name to assign this content back as
|
||||
# $2-*: File names (optional)
|
||||
mustache-get-content() {
|
||||
local CONTENT FILENAME TARGET
|
||||
|
||||
TARGET=$1
|
||||
shift
|
||||
if [[ "${#@}" -gt 0 ]]; then
|
||||
CONTENT=""
|
||||
|
||||
for FILENAME in "$@"; do
|
||||
# This is so relative paths work from inside template files
|
||||
CONTENT="$CONTENT"'{{>'"$FILENAME"'}}'
|
||||
done
|
||||
else
|
||||
mustache-load-file CONTENT /dev/stdin
|
||||
fi
|
||||
|
||||
local "$TARGET" && mustache-indirect "$TARGET" "$CONTENT"
|
||||
}
|
||||
|
||||
|
||||
# Indent a string, placing the indent at the beginning of every
|
||||
# line that has any content.
|
||||
#
|
||||
# Parameters:
|
||||
# $1: Name of destination variable to get an array of lines
|
||||
# $2: The indent string
|
||||
# $3: The string to reindent
|
||||
mustache-indent-lines() {
|
||||
local CONTENT FRAGMENT LEN POS_N POS_R RESULT TRIMMED
|
||||
|
||||
RESULT=""
|
||||
LEN=$((${#3} - 1))
|
||||
CONTENT="${3:0:$LEN}" # Remove newline and dot from workaround - in mustache-partial
|
||||
|
||||
if [ -z "$2" ]; then
|
||||
local "$1" && mustache-indirect "$1" "$CONTENT"
|
||||
return 0
|
||||
fi
|
||||
|
||||
mustache-find-string POS_N "$CONTENT" $'\n'
|
||||
mustache-find-string POS_R "$CONTENT" $'\r'
|
||||
|
||||
while [[ "$POS_N" -gt -1 ]] || [[ "$POS_R" -gt -1 ]]; do
|
||||
if [[ "$POS_N" -gt -1 ]]; then
|
||||
FRAGMENT="${CONTENT:0:$POS_N + 1}"
|
||||
CONTENT=${CONTENT:$POS_N + 1}
|
||||
else
|
||||
FRAGMENT="${CONTENT:0:$POS_R + 1}"
|
||||
CONTENT=${CONTENT:$POS_R + 1}
|
||||
fi
|
||||
|
||||
mustache-trim-chars TRIMMED "$FRAGMENT" false true " " $'\t' $'\n' $'\r'
|
||||
|
||||
if [ ! -z "$TRIMMED" ]; then
|
||||
FRAGMENT="$2$FRAGMENT"
|
||||
fi
|
||||
|
||||
RESULT="$RESULT$FRAGMENT"
|
||||
mustache-find-string POS_N "$CONTENT" $'\n'
|
||||
mustache-find-string POS_R "$CONTENT" $'\r'
|
||||
done
|
||||
|
||||
mustache-trim-chars TRIMMED "$CONTENT" false true " " $'\t'
|
||||
|
||||
if [ ! -z "$TRIMMED" ]; then
|
||||
CONTENT="$2$CONTENT"
|
||||
fi
|
||||
|
||||
RESULT="$RESULT$CONTENT"
|
||||
|
||||
local "$1" && mustache-indirect "$1" "$RESULT"
|
||||
}
|
||||
|
||||
|
||||
# Send a variable up to caller of a function
|
||||
#
|
||||
# Parameters:
|
||||
# $1: Variable name
|
||||
# $2: Value
|
||||
mustache-indirect() {
|
||||
unset -v "$1"
|
||||
printf -v "$1" '%s' "$2"
|
||||
}
|
||||
|
||||
|
||||
# Send an array up to caller of a function
|
||||
#
|
||||
# Parameters:
|
||||
# $1: Variable name
|
||||
# $2-*: Array elements
|
||||
mustache-indirect-array() {
|
||||
unset -v "$1"
|
||||
eval $1=\(\"\${@:2}\"\)
|
||||
}
|
||||
|
||||
|
||||
# Determine if a given environment variable exists and if it is an array.
|
||||
#
|
||||
# Parameters:
|
||||
# $1: Name of environment variable
|
||||
#
|
||||
# Return code:
|
||||
# 0 if the name is not empty, 1 otherwise
|
||||
mustache-is-array() {
|
||||
local MUSTACHE_TEST
|
||||
|
||||
MUSTACHE_TEST=$(declare -p "$1" 2>/dev/null) || return 1
|
||||
[[ "${MUSTACHE_TEST:0:10}" == "declare -a" ]] && return 0
|
||||
[[ "${MUSTACHE_TEST:0:10}" == "declare -A" ]] && return 0
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
# Return 0 if the passed name is a function.
|
||||
#
|
||||
# Parameters:
|
||||
# $1: Name to check if it's a function
|
||||
#
|
||||
# Return code:
|
||||
# 0 if the name is a function, 1 otherwise
|
||||
mustache-is-function() {
|
||||
local FUNCTIONS NAME
|
||||
|
||||
FUNCTIONS=$(declare -F)
|
||||
FUNCTIONS=( ${FUNCTIONS//declare -f /} )
|
||||
|
||||
for NAME in ${FUNCTIONS[@]}; do
|
||||
if [[ "$NAME" == "$1" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
# Determine if the tag is a standalone tag based on whitespace before and
|
||||
# after the tag.
|
||||
#
|
||||
# Passes back a string containing two numbers in the format "BEFORE AFTER"
|
||||
# like "27 10". It indicates the number of bytes remaining in the "before"
|
||||
# string (27) and the number of bytes to trim in the "after" string (10).
|
||||
# Useful for string manipulation:
|
||||
#
|
||||
# mustache-is-standalone RESULT "$before" "$after" false || return 0
|
||||
# RESULT_ARRAY=( $RESULT )
|
||||
# echo "${before:0:${RESULT_ARRAY[0]}}...${after:${RESULT_ARRAY[1]}}"
|
||||
#
|
||||
# Parameters:
|
||||
# $1: Variable to pass data back
|
||||
# $2: Content before the tag
|
||||
# $3: Content after the tag
|
||||
# $4: true/false: is this the beginning of the content?
|
||||
mustache-is-standalone() {
|
||||
local AFTER_TRIMMED BEFORE_TRIMMED CHAR
|
||||
|
||||
mustache-trim-chars BEFORE_TRIMMED "$2" false true " " $'\t'
|
||||
mustache-trim-chars AFTER_TRIMMED "$3" true false " " $'\t'
|
||||
CHAR=$((${#BEFORE_TRIMMED} - 1))
|
||||
CHAR=${BEFORE_TRIMMED:$CHAR}
|
||||
|
||||
if [[ "$CHAR" != $'\n' ]] && [[ "$CHAR" != $'\r' ]]; then
|
||||
if [[ ! -z "$CHAR" ]] || ! $4; then
|
||||
return 1;
|
||||
fi
|
||||
fi
|
||||
|
||||
CHAR=${AFTER_TRIMMED:0:1}
|
||||
|
||||
if [[ "$CHAR" != $'\n' ]] && [[ "$CHAR" != $'\r' ]] && [[ ! -z "$CHAR" ]]; then
|
||||
return 2;
|
||||
fi
|
||||
|
||||
if [[ "$CHAR" == $'\r' ]] && [[ "${AFTER_TRIMMED:1:1}" == $'\n' ]]; then
|
||||
CHAR="$CHAR"$'\n'
|
||||
fi
|
||||
|
||||
local "$1" && mustache-indirect "$1" "$((${#BEFORE_TRIMMED})) $((${#3} + ${#CHAR} - ${#AFTER_TRIMMED}))"
|
||||
}
|
||||
|
||||
|
||||
# Read a file
|
||||
#
|
||||
# Parameters:
|
||||
# $1: Variable name to receive the file's content
|
||||
# $2: Filename to load
|
||||
mustache-load-file() {
|
||||
local CONTENT LEN
|
||||
|
||||
# The subshell removes any trailing newlines. We forcibly add
|
||||
# a dot to the content to preserve all newlines.
|
||||
# TODO: remove cat and replace with read loop?
|
||||
CONTENT=$(cat $2; echo '.')
|
||||
LEN=$((${#CONTENT} - 1))
|
||||
CONTENT=${CONTENT:0:$LEN} # Remove last dot
|
||||
|
||||
local "$1" && mustache-indirect "$1" "$CONTENT"
|
||||
}
|
||||
|
||||
|
||||
# Process a chunk of content some number of times.
|
||||
#
|
||||
# Parameters:
|
||||
# $1: Content to parse and reparse and reparse
|
||||
# $2: Tag prefix (context name)
|
||||
# $3-*: Names to insert into the parsed content
|
||||
mustache-loop() {
|
||||
local CONTENT CONTEXT CONTEXT_BASE IGNORE
|
||||
|
||||
CONTENT=$1
|
||||
CONTEXT_BASE=$2
|
||||
shift 2
|
||||
|
||||
while [[ "${#@}" -gt 0 ]]; do
|
||||
mustache-full-tag-name CONTEXT "$CONTEXT_BASE" "$1"
|
||||
mustache-parse "$CONTENT" "$CONTEXT" false
|
||||
shift
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
# Parse a block of text
|
||||
#
|
||||
# Parameters:
|
||||
# $1: Block of text to change
|
||||
# $2: Current name (the variable NAME for what {{.}} means)
|
||||
# $3: true when no content before this, false otherwise
|
||||
mustache-parse() {
|
||||
# Keep naming variables MUSTACHE_* here to not overwrite needed variables
|
||||
# used in the string replacements
|
||||
local MUSTACHE_BLOCK MUSTACHE_CONTENT MUSTACHE_CURRENT MUSTACHE_IS_BEGINNING MUSTACHE_TAG
|
||||
|
||||
MUSTACHE_CURRENT=$2
|
||||
MUSTACHE_IS_BEGINNING=$3
|
||||
|
||||
# Find open tags
|
||||
mustache-split MUSTACHE_CONTENT "$1" '{{' '}}'
|
||||
|
||||
while [[ "${#MUSTACHE_CONTENT[@]}" -gt 1 ]]; do
|
||||
mustache-trim-whitespace MUSTACHE_TAG "${MUSTACHE_CONTENT[1]}"
|
||||
|
||||
case $MUSTACHE_TAG in
|
||||
'#'*)
|
||||
# Loop, if/then, or pass content through function
|
||||
# Sets context
|
||||
mustache-standalone-allowed MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" $MUSTACHE_IS_BEGINNING
|
||||
mustache-trim-whitespace MUSTACHE_TAG "${MUSTACHE_TAG:1}"
|
||||
mustache-find-end-tag MUSTACHE_BLOCK "$MUSTACHE_CONTENT" "$MUSTACHE_TAG"
|
||||
mustache-full-tag-name MUSTACHE_TAG "$MUSTACHE_CURRENT" "$MUSTACHE_TAG"
|
||||
|
||||
if mustache-test "$MUSTACHE_TAG"; then
|
||||
# Show / loop / pass through function
|
||||
if mustache-is-function "$MUSTACHE_TAG"; then
|
||||
# TODO: Consider piping the output to
|
||||
# mustache-get-content so the lambda does not
|
||||
# execute in a subshell?
|
||||
MUSTACHE_CONTENT=$($MUSTACHE_TAG "${MUSTACHE_BLOCK[0]}")
|
||||
mustache-parse "$MUSTACHE_CONTENT" "$MUSTACHE_CURRENT" false
|
||||
MUSTACHE_CONTENT="${MUSTACHE_BLOCK[2]}"
|
||||
elif mustache-is-array "$MUSTACHE_TAG"; then
|
||||
eval 'mustache-loop "${MUSTACHE_BLOCK[0]}" "$MUSTACHE_TAG" "${!'"$MUSTACHE_TAG"'[@]}"'
|
||||
else
|
||||
mustache-parse "${MUSTACHE_BLOCK[0]}" "$MUSTACHE_CURRENT" false
|
||||
fi
|
||||
fi
|
||||
|
||||
MUSTACHE_CONTENT="${MUSTACHE_BLOCK[2]}"
|
||||
;;
|
||||
|
||||
'>'*)
|
||||
# Load partial - get name of file relative to cwd
|
||||
mustache-partial MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" $MUSTACHE_IS_BEGINNING "$MUSTACHE_CURRENT"
|
||||
;;
|
||||
|
||||
'/'*)
|
||||
# Closing tag - If hit in this loop, we simply ignore
|
||||
# Matching tags are found in mustache-find-end-tag
|
||||
mustache-standalone-allowed MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" $MUSTACHE_IS_BEGINNING
|
||||
;;
|
||||
|
||||
'^'*)
|
||||
# Display section if named thing does not exist
|
||||
mustache-standalone-allowed MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" $MUSTACHE_IS_BEGINNING
|
||||
mustache-trim-whitespace MUSTACHE_TAG "${MUSTACHE_TAG:1}"
|
||||
mustache-find-end-tag MUSTACHE_BLOCK "$MUSTACHE_CONTENT" "$MUSTACHE_TAG"
|
||||
mustache-full-tag-name MUSTACHE_TAG "$MUSTACHE_CURRENT" "$MUSTACHE_TAG"
|
||||
|
||||
if ! mustache-test "$MUSTACHE_TAG"; then
|
||||
mustache-parse "${MUSTACHE_BLOCK[0]}" "$MUSTACHE_CURRENT" false "$MUSTACHE_CURRENT"
|
||||
fi
|
||||
|
||||
MUSTACHE_CONTENT="${MUSTACHE_BLOCK[2]}"
|
||||
;;
|
||||
|
||||
'!'*)
|
||||
# Comment - ignore the tag content entirely
|
||||
# Trim spaces/tabs before the comment
|
||||
mustache-standalone-allowed MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" $MUSTACHE_IS_BEGINNING
|
||||
;;
|
||||
|
||||
.)
|
||||
# Current content (environment variable or function)
|
||||
mustache-standalone-denied MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}"
|
||||
mustache-show "$MUSTACHE_CURRENT" "$MUSTACHE_CURRENT"
|
||||
;;
|
||||
|
||||
'=')
|
||||
# Change delimiters
|
||||
# Any two non-whitespace sequences separated by whitespace.
|
||||
# TODO
|
||||
mustache-standalone-allowed MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}" $MUSTACHE_IS_BEGINNING
|
||||
;;
|
||||
|
||||
'{'*)
|
||||
# Unescaped - split on }}} not }}
|
||||
mustache-standalone-denied MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}"
|
||||
MUSTACHE_CONTENT="${MUSTACHE_TAG:1}"'}}'"$MUSTACHE_CONTENT"
|
||||
mustache-split MUSTACHE_CONTENT "$MUSTACHE_CONTENT" '}}}'
|
||||
mustache-trim-whitespace MUSTACHE_TAG "${MUSTACHE_CONTENT[0]}"
|
||||
mustache-full-tag-name MUSTACHE_TAG "$MUSTACHE_CURRENT" "$MUSTACHE_TAG"
|
||||
MUSTACHE_CONTENT=${MUSTACHE_CONTENT[1]}
|
||||
|
||||
# Now show the value
|
||||
mustache-show "$MUSTACHE_TAG" "$MUSTACHE_CURRENT"
|
||||
;;
|
||||
|
||||
'&'*)
|
||||
# Unescaped
|
||||
mustache-standalone-denied MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}"
|
||||
mustache-trim-whitespace MUSTACHE_TAG "${MUSTACHE_TAG:1}"
|
||||
mustache-full-tag-name MUSTACHE_TAG "$MUSTACHE_CURRENT" "$MUSTACHE_TAG"
|
||||
mustache-show "$MUSTACHE_TAG" "$MUSTACHE_CURRENT"
|
||||
;;
|
||||
|
||||
*)
|
||||
# Normal environment variable or function call
|
||||
mustache-standalone-denied MUSTACHE_CONTENT "${MUSTACHE_CONTENT[@]}"
|
||||
mustache-full-tag-name MUSTACHE_TAG "$MUSTACHE_CURRENT" "$MUSTACHE_TAG"
|
||||
mustache-show "$MUSTACHE_TAG" "$MUSTACHE_CURRENT"
|
||||
;;
|
||||
esac
|
||||
|
||||
MUSTACHE_IS_BEGINNING=false
|
||||
mustache-split MUSTACHE_CONTENT "$MUSTACHE_CONTENT" '{{' '}}'
|
||||
done
|
||||
|
||||
echo -n "${MUSTACHE_CONTENT[0]}"
|
||||
}
|
||||
|
||||
|
||||
# Process a partial
|
||||
#
|
||||
# Indentation should be applied to the entire partial
|
||||
#
|
||||
# Prefix all variables
|
||||
#
|
||||
# Parameters:
|
||||
# $1: Name of destination "content" variable.
|
||||
# $2: Content before the tag that was not yet written
|
||||
# $3: Tag content
|
||||
# $4: Content after the tag
|
||||
# $5: true/false: is this the beginning of the content?
|
||||
# $6: Current context name
|
||||
mustache-partial() {
|
||||
local MUSTACHE_CONTENT MUSTACHE_FILENAME MUSTACHE_INDENT MUSTACHE_LINE MUSTACHE_PARTIAL MUSTACHE_STANDALONE
|
||||
|
||||
if mustache-is-standalone MUSTACHE_STANDALONE "$2" "$4" $5; then
|
||||
MUSTACHE_STANDALONE=( $MUSTACHE_STANDALONE )
|
||||
echo -n "${2:0:${MUSTACHE_STANDALONE[0]}}"
|
||||
MUSTACHE_INDENT=${2:${MUSTACHE_STANDALONE[0]}}
|
||||
MUSTACHE_CONTENT=${4:${MUSTACHE_STANDALONE[1]}}
|
||||
else
|
||||
MUSTACHE_INDENT=""
|
||||
echo -n "$2"
|
||||
MUSTACHE_CONTENT=$4
|
||||
fi
|
||||
|
||||
mustache-trim-whitespace MUSTACHE_FILENAME "${3:1}"
|
||||
|
||||
# Execute in subshell to preserve current cwd and environment
|
||||
(
|
||||
# TODO: Remove dirname and use a function instead
|
||||
cd "$(dirname "$MUSTACHE_FILENAME")"
|
||||
mustache-indent-lines MUSTACHE_PARTIAL "$MUSTACHE_INDENT" "$(
|
||||
mustache-load-file MUSTACHE_PARTIAL "${MUSTACHE_FILENAME##*/}"
|
||||
|
||||
# Fix bash handling of subshells
|
||||
# The extra dot is removed in mustache-indent-lines
|
||||
echo -n "${MUSTACHE_PARTIAL}."
|
||||
)"
|
||||
mustache-parse "$MUSTACHE_PARTIAL" "$6" true
|
||||
)
|
||||
|
||||
local "$1" && mustache-indirect "$1" "$MUSTACHE_CONTENT"
|
||||
}
|
||||
|
||||
|
||||
# Show an environment variable or the output of a function.
|
||||
#
|
||||
# Limit/prefix any variables used
|
||||
#
|
||||
# Parameters:
|
||||
# $1: Name of environment variable or function
|
||||
# $2: Current context
|
||||
mustache-show() {
|
||||
local MUSTACHE_NAME_PARTS
|
||||
|
||||
if mustache-is-function "$1"; then
|
||||
CONTENT=$($1 "")
|
||||
mustache-parse "$CONTENT" "$2" false
|
||||
return 0
|
||||
fi
|
||||
|
||||
mustache-split MUSTACHE_NAME_PARTS "$1" "."
|
||||
|
||||
if [[ -z "${MUSTACHE_NAME_PARTS[1]}" ]]; then
|
||||
echo -n "${!1}"
|
||||
else
|
||||
# Further subindexes are disallowed
|
||||
eval 'echo -n "${'"${MUSTACHE_NAME_PARTS[0]}"'['"${MUSTACHE_NAME_PARTS[1]%%.*}"']}"'
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Split a larger string into an array
|
||||
#
|
||||
# Parameters:
|
||||
# $1: Destination variable
|
||||
# $2: String to split
|
||||
# $3: Starting delimiter
|
||||
# $4: Ending delimiter (optional)
|
||||
mustache-split() {
|
||||
local POS RESULT
|
||||
|
||||
RESULT=( "$2" )
|
||||
mustache-find-string POS "${RESULT[0]}" "$3"
|
||||
|
||||
if [[ "$POS" -ne -1 ]]; then
|
||||
# The first delimiter was found
|
||||
RESULT[1]=${RESULT[0]:$POS + ${#3}}
|
||||
RESULT[0]=${RESULT[0]:0:$POS}
|
||||
|
||||
if [[ ! -z "$4" ]]; then
|
||||
mustache-find-string POS "${RESULT[1]}" "$4"
|
||||
|
||||
if [[ "$POS" -ne -1 ]]; then
|
||||
# The second delimiter was found
|
||||
RESULT[2]="${RESULT[1]:$POS + ${#4}}"
|
||||
RESULT[1]="${RESULT[1]:0:$POS}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
local "$1" && mustache-indirect-array "$1" "${RESULT[@]}"
|
||||
}
|
||||
|
||||
|
||||
# Handle the content for a standalone tag. This means removing whitespace
|
||||
# (not newlines) before a tag and whitespace and a newline after a tag.
|
||||
# That is, assuming, that the line is otherwise empty.
|
||||
#
|
||||
# Parameters:
|
||||
# $1: Name of destination "content" variable.
|
||||
# $2: Content before the tag that was not yet written
|
||||
# $3: Tag content (not used)
|
||||
# $4: Content after the tag
|
||||
# $5: true/false: is this the beginning of the content?
|
||||
mustache-standalone-allowed() {
|
||||
local STANDALONE_BYTES
|
||||
|
||||
if mustache-is-standalone STANDALONE_BYTES "$2" "$4" $5; then
|
||||
STANDALONE_BYTES=( $STANDALONE_BYTES )
|
||||
echo -n "${2:0:${STANDALONE_BYTES[0]}}"
|
||||
local "$1" && mustache-indirect "$1" "${4:${STANDALONE_BYTES[1]}}"
|
||||
else
|
||||
echo -n "$2"
|
||||
local "$1" && mustache-indirect "$1" "$4"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Handle the content for a tag that is never "standalone". No adjustments
|
||||
# are made for newlines and whitespace.
|
||||
#
|
||||
# Parameters:
|
||||
# $1: Name of destination "content" variable.
|
||||
# $2: Content before the tag that was not yet written
|
||||
# $3: Tag content (not used)
|
||||
# $4: Content after the tag
|
||||
mustache-standalone-denied() {
|
||||
echo -n "$2"
|
||||
local "$1" && mustache-indirect "$1" "$4"
|
||||
}
|
||||
|
||||
|
||||
# Returns 0 (success) if the named thing is a function or if it is a non-empty
|
||||
# environment variable.
|
||||
#
|
||||
# Do not use unprefixed variables here if possible as this needs to check
|
||||
# if any name exists in the environment
|
||||
#
|
||||
# Parameters:
|
||||
# $1: Name of environment variable or function
|
||||
# $2: Current value (our context)
|
||||
#
|
||||
# Return code:
|
||||
# 0 if the name is not empty, 1 otherwise
|
||||
mustache-test() {
|
||||
# Test for functions
|
||||
mustache-is-function "$1" && return 0
|
||||
|
||||
if mustache-is-array "$1"; then
|
||||
# Arrays must have at least 1 element
|
||||
eval '[[ "${#'"$1"'}" -gt 0 ]]' && return 0
|
||||
else
|
||||
# Environment variables must not be empty
|
||||
[[ ! -z "${!1}" ]] && return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
# Trim the leading whitespace only
|
||||
#
|
||||
# Parameters:
|
||||
# $1: Name of destination variable
|
||||
# $2: The string
|
||||
# $3: true/false - trim front?
|
||||
# $4: true/false - trim end?
|
||||
# $5-*: Characters to trim
|
||||
mustache-trim-chars() {
|
||||
local BACK CURRENT FRONT LAST TARGET VAR
|
||||
|
||||
TARGET=$1
|
||||
CURRENT=$2
|
||||
FRONT=$3
|
||||
BACK=$4
|
||||
LAST=""
|
||||
shift # Remove target
|
||||
shift # Remove string
|
||||
shift # Remove trim front flag
|
||||
shift # Remove trim end flag
|
||||
|
||||
while [[ "$CURRENT" != "$LAST" ]]; do
|
||||
LAST=$CURRENT
|
||||
|
||||
for VAR in "$@"; do
|
||||
$FRONT && CURRENT="${CURRENT/#$VAR}"
|
||||
$BACK && CURRENT="${CURRENT/%$VAR}"
|
||||
done
|
||||
done
|
||||
|
||||
local "$TARGET" && mustache-indirect "$TARGET" "$CURRENT"
|
||||
}
|
||||
|
||||
|
||||
# Trim leading and trailing whitespace from a string
|
||||
#
|
||||
# Parameters:
|
||||
# $1: Name of variable to store trimmed string
|
||||
# $2: The string
|
||||
mustache-trim-whitespace() {
|
||||
local RESULT
|
||||
|
||||
mustache-trim-chars RESULT "$2" true true $'\r' $'\n' $'\t' " "
|
||||
local "$1" && mustache-indirect "$1" "$RESULT"
|
||||
}
|
||||
|
||||
|
||||
mustache-get-content MUSTACHE_CONTENT "$@"
|
||||
mustache-parse "$MUSTACHE_CONTENT" "" true
|
@ -1,247 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -x #echo on
|
||||
|
||||
cat > /etc/network/interfaces << EOF
|
||||
auto lo
|
||||
iface lo inet loopback
|
||||
source interfaces.d/*
|
||||
EOF
|
||||
|
||||
cat > /etc/network/interfaces.d/eth0 << EOF
|
||||
auto eth0
|
||||
iface eth0 inet dhcp
|
||||
metric 0
|
||||
EOF
|
||||
|
||||
cat > /etc/network/interfaces.d/eth1 << EOF
|
||||
auto eth1
|
||||
iface eth1 inet dhcp
|
||||
metric 1
|
||||
EOF
|
||||
|
||||
ifup eth1
|
||||
|
||||
# Script configuration parameters ***start
|
||||
os_region_name={{OS_REGION_NAME}}
|
||||
os_tenant_name={{OS_TENANT_NAME}}
|
||||
os_username={{OS_USERNAME}}
|
||||
os_password={{OS_PASSWORD}}
|
||||
os_auth_url={{OS_AUTH_URL}}
|
||||
os_key_name={{OS_KEY_NAME}}
|
||||
os_security_group={{CUE_SECURITY_GROUP}}
|
||||
cue_image_id={{CUE_IMAGE_ID}}
|
||||
cue_management_network_id={{CUE_MANAGEMENT_NIC}}
|
||||
mysql_root_password={{MYSQL_ROOT_PASSWORD}}
|
||||
mysql_cue_api_password={{MYSQL_CUEAPI_PASSWORD}}
|
||||
mysql_cue_worker_password={{MYSQL_CUEWORKER_PASSWORD}}
|
||||
floating_ip={{FLOATING_IP}}
|
||||
# Script configuration parameters ***end
|
||||
|
||||
# Determinate is the given option present in the INI file
|
||||
# ini_has_option config-file section option
|
||||
function ini_has_option {
|
||||
local xtrace=$(set +o | grep xtrace)
|
||||
set +o xtrace
|
||||
local file=$1
|
||||
local section=$2
|
||||
local option=$3
|
||||
local line
|
||||
|
||||
line=$(sed -ne "/^\[$section\]/,/^\[.*\]/ { /^$option[ \t]*=/ p; }" "$file")
|
||||
$xtrace
|
||||
[ -n "$line" ]
|
||||
}
|
||||
|
||||
# Set an option in an INI file
|
||||
# iniset config-file section option value
|
||||
function iniset {
|
||||
local xtrace=$(set +o | grep xtrace)
|
||||
set +o xtrace
|
||||
local file=$1
|
||||
local section=$2
|
||||
local option=$3
|
||||
local value=$4
|
||||
|
||||
[[ -z ${section} || -z ${option} ]] && return
|
||||
|
||||
if ! grep -q "^\[$section\]" "$file" 2>/dev/null; then
|
||||
# Add section at the end
|
||||
echo -e "\n[$section]" >>"$file"
|
||||
fi
|
||||
if ! ini_has_option "$file" "$section" "$option"; then
|
||||
# Add it
|
||||
sed -i -e "/^\[$section\]/ a\\
|
||||
$option = $value
|
||||
" "$file"
|
||||
else
|
||||
local sep=$(echo -ne "\x01")
|
||||
# Replace it
|
||||
sed -i -e '/^\['${section}'\]/,/^\[.*\]/ s'${sep}'^\('${option}'[ \t]*=[ \t]*\).*$'${sep}'\1'"${value}"${sep} "$file"
|
||||
fi
|
||||
${xtrace}
|
||||
}
|
||||
|
||||
# Update & upgrade VM
|
||||
unset UCF_FORCE_CONFFOLD
|
||||
export UCF_FORCE_CONFFNEW=YES
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update
|
||||
apt-get -o Dpkg::Options::="--force-confnew" --force-yes -fuy dist-upgrade
|
||||
|
||||
# Install required pacakges
|
||||
apt-get install -y python-pip python-dev git build-essential zookeeper zookeeperd python-mysqldb supervisor
|
||||
|
||||
# Install keystone
|
||||
cd /home/ubuntu/
|
||||
sudo -u ubuntu -g ubuntu git clone https://git.openstack.org/openstack-dev/devstack
|
||||
mkdir -p /opt/stack
|
||||
chown ubuntu:ubuntu /opt/stack
|
||||
sudo -u ubuntu -g ubuntu git clone https://github.com/openstack/requirements /opt/stack/requirements
|
||||
cat > devstack/local.conf<< EOF
|
||||
[[local|localrc]]
|
||||
HOST_IP=127.0.0.1
|
||||
SERVICE_HOST=$floating_ip
|
||||
REQUIREMENTS_MODE=soft
|
||||
ADMIN_PASSWORD=password
|
||||
MYSQL_PASSWORD=$mysql_root_password
|
||||
RABBIT_PASSWORD=password
|
||||
SERVICE_PASSWORD=password
|
||||
SERVICE_TOKEN=password
|
||||
LOGFILE=/opt/stack/logs/stack.sh.log
|
||||
VERBOSE=True
|
||||
LOG_COLOR=True
|
||||
SCREEN_LOGDIR=/opt/stack/logs
|
||||
disable_service g-api
|
||||
disable_service g-reg
|
||||
disable_service n-api
|
||||
disable_service n-crt
|
||||
disable_service n-obj
|
||||
disable_service n-cpu
|
||||
disable_service n-net
|
||||
disable_service n-cond
|
||||
disable_service n-sch
|
||||
disable_service n-novnc
|
||||
disable_service n-xvnc
|
||||
disable_service n-cauth
|
||||
disable_service c-sch
|
||||
disable_service c-api
|
||||
disable_service c-vol
|
||||
disable_service h-eng
|
||||
disable_service h-api
|
||||
disable_service h-api-cfn
|
||||
disable_service h-api-cw
|
||||
disable_service horizon
|
||||
disable_service tempest
|
||||
EOF
|
||||
pushd /home/ubuntu/devstack
|
||||
sudo -u ubuntu -g ubuntu ./stack.sh
|
||||
popd
|
||||
|
||||
# Setup keystone user, service, and endpoint
|
||||
CUE_URL="http://${floating_ip}:8795/"
|
||||
source ./devstack/openrc admin admin
|
||||
keystone user-create --name cue --tenant service --pass password
|
||||
keystone user-role-add --user cue --role admin --tenant service
|
||||
keystone service-create --name cue --type "message-broker" --description "Message Broker Provisioning Service"
|
||||
keystone endpoint-create --region $OS_REGION_NAME --service cue --publicurl $CUE_URL --adminurl $CUE_URL --internalurl $CUE_URL
|
||||
|
||||
# Install MySQL DB
|
||||
debconf-set-selections <<< "mysql-server mysql-server/root_password password ${mysql_root_password}"
|
||||
debconf-set-selections <<< "mysql-server mysql-server/root_password_again password ${mysql_root_password}"
|
||||
apt-get -y install mysql-server
|
||||
|
||||
# Create cue database
|
||||
echo "create database cue;" | mysql -u root -p${mysql_root_password}
|
||||
|
||||
# Create MySQL DB users for Cue API and Cue worker processes
|
||||
echo "CREATE USER 'cue_api'@'%' IDENTIFIED BY '${mysql_cue_api_password}'" | mysql -u root -p${mysql_root_password}
|
||||
echo "CREATE USER 'cue_worker'@'%' IDENTIFIED BY '${mysql_cue_worker_password}'" | mysql -u root -p${mysql_root_password}
|
||||
|
||||
# Grant cue_api and cue_worker users privilidge to cue database
|
||||
echo "GRANT ALL PRIVILEGES ON cue. * TO 'cue_api'@'%';" | mysql -u root -p${mysql_root_password}
|
||||
echo "GRANT ALL PRIVILEGES ON cue. * TO 'cue_worker'@'%';" | mysql -u root -p${mysql_root_password}
|
||||
|
||||
# Restart mysql server
|
||||
service mysql restart
|
||||
|
||||
# Install cue service
|
||||
git clone https://github.com/openstack/cue.git
|
||||
cd cue
|
||||
python setup.py install
|
||||
pip install pbr
|
||||
|
||||
# Create local directory for cue configuratin and policy files
|
||||
mkdir -p /etc/cue
|
||||
|
||||
# Copy Cue's default configuration files and policy file to /etc/cue/
|
||||
CUE_CONF="/etc/cue/cue.conf"
|
||||
cp etc/cue/cue.conf.sample ${CUE_CONF}
|
||||
cp etc/cue/policy.json /etc/cue/policy.json
|
||||
|
||||
# Set required cue configuration settings
|
||||
db_connection_api=mysql://cue_api:${mysql_cue_api_password}@127.0.0.1/cue
|
||||
db_connection_worker=mysql://cue_worker:${mysql_cue_worker_password}@127.0.0.1/cue
|
||||
iniset ${CUE_CONF} DEFAULT rabbit_port 5672
|
||||
iniset ${CUE_CONF} DEFAULT debug True
|
||||
iniset ${CUE_CONF} DEFAULT os_security_group ${os_security_group}
|
||||
iniset ${CUE_CONF} DEFAULT management_network_id ${cue_management_network_id}
|
||||
iniset ${CUE_CONF} DEFAULT auth_strategy keystone
|
||||
iniset ${CUE_CONF} api host_ip '0.0.0.0'
|
||||
iniset ${CUE_CONF} api port 8795
|
||||
iniset ${CUE_CONF} api max_limit 1000
|
||||
iniset ${CUE_CONF} database connection ${db_connection_api}
|
||||
iniset ${CUE_CONF} openstack os_key_name ${os_key_name}
|
||||
iniset ${CUE_CONF} openstack os_region_name ${os_region_name}
|
||||
iniset ${CUE_CONF} openstack os_tenant_name ${os_tenant_name}
|
||||
iniset ${CUE_CONF} openstack os_username ${os_username}
|
||||
iniset ${CUE_CONF} openstack os_password ${os_password}
|
||||
iniset ${CUE_CONF} openstack os_auth_url ${os_auth_url}
|
||||
iniset ${CUE_CONF} database connection ${db_connection_worker}
|
||||
iniset ${CUE_CONF} keystone_authtoken admin_tenant_name service
|
||||
iniset ${CUE_CONF} keystone_authtoken admin_password password
|
||||
iniset ${CUE_CONF} keystone_authtoken admin_user cue
|
||||
iniset ${CUE_CONF} keystone_authtoken identity_uri http://${floating_ip}:35357
|
||||
|
||||
# Execute Cue's database upgrade scripts
|
||||
cue-manage --config-file /etc/cue/cue.conf database upgrade
|
||||
|
||||
# Execute Cue's taskflow upgrade scripts
|
||||
cue-manage --config-file /etc/cue/cue.conf taskflow upgrade
|
||||
|
||||
# set default broker and cue image
|
||||
cue-manage --config-file ${CUE_CONF} broker add rabbitmq true
|
||||
BROKER_ID=$(cue-manage --config-file ${CUE_CONF} broker list | grep rabbitmq | tr -d ' ' | cut -f 2 -d '|')
|
||||
cue-manage --config-file ${CUE_CONF} broker add_metadata ${BROKER_ID} --image ${cue_image_id}
|
||||
|
||||
# Create supervisord execution configuration for Cue API
|
||||
cat > /etc/supervisor/conf.d/cueapi.conf<< EOF
|
||||
[program:cue-api]
|
||||
command=cue-api --debug --config-file /etc/cue/cue.conf
|
||||
process_name=%(program_name)s
|
||||
stdout_logfile=/var/log/cue-api.log
|
||||
stdout_logfile_maxbytes=1MB
|
||||
stdout_logfile_backups=10
|
||||
stdout_capture_maxbytes=1MB
|
||||
stderr_logfile=/var/log/cue-api.err
|
||||
stderr_logfile_maxbytes=1MB
|
||||
stderr_logfile_backups=10
|
||||
stderr_capture_maxbytes=1MB
|
||||
EOF
|
||||
|
||||
# Create supervisord execution configuration for Cue Worker
|
||||
cat > /etc/supervisor/conf.d/cueworker.conf<< EOF
|
||||
[program:cue-worker]
|
||||
command=cue-worker --debug --config-file /etc/cue/cue.conf
|
||||
process_name=%(program_name)s
|
||||
stdout_logfile=/var/log/cue-worker.log
|
||||
stdout_logfile_maxbytes=1MB
|
||||
stdout_logfile_backups=10
|
||||
stdout_capture_maxbytes=1MB
|
||||
stderr_logfile=/var/log/cue-worker.err
|
||||
stderr_logfile_maxbytes=1MB
|
||||
stderr_logfile_backups=10
|
||||
stderr_capture_maxbytes=1MB
|
||||
EOF
|
||||
|
||||
# Restart supervisord to start Cue API and Cue Worker processes
|
||||
service supervisor restart
|
96
contrib/vagrant/Vagrantfile
vendored
96
contrib/vagrant/Vagrantfile
vendored
@ -1,96 +0,0 @@
|
||||
# -*- mode: ruby -*-
|
||||
# # vi: set ft=ruby :
|
||||
|
||||
require 'fileutils'
|
||||
|
||||
Vagrant.require_version ">= 1.6.0"
|
||||
|
||||
CONFIG = File.join(File.dirname(__FILE__), "vagrant_config.rb")
|
||||
UBUNTU_COMMON = File.join(File.dirname(__FILE__), "lib/ubuntu.rb")
|
||||
FEDORA_COMMON = File.join(File.dirname(__FILE__), "lib/fedora.rb")
|
||||
DEVSTACK_SCRIPT = File.join(File.dirname(__FILE__), "lib/devstack_script.rb")
|
||||
RALLY_SCRIPT = File.join(File.dirname(__FILE__), "lib/rally_script.rb")
|
||||
|
||||
GITCONFIG = `cat $HOME/.gitconfig`
|
||||
|
||||
VAGRANTFILE_API_VERSION = "2"
|
||||
|
||||
# Defaults for config options
|
||||
$hostname = File.basename(File.dirname(__FILE__))
|
||||
$forwarded_port = {}
|
||||
$install_devstack = false
|
||||
$install_build_deps = true
|
||||
$install_tmate = false
|
||||
$install_rally = true
|
||||
$ubuntu_box = "sputnik13/trusty64"
|
||||
$vm_memory = 6144
|
||||
$vm_cpus = 2
|
||||
|
||||
if File.exist?(CONFIG)
|
||||
require CONFIG
|
||||
end
|
||||
|
||||
require UBUNTU_COMMON
|
||||
require FEDORA_COMMON
|
||||
require DEVSTACK_SCRIPT
|
||||
require RALLY_SCRIPT
|
||||
|
||||
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
$forwarded_port.each do |guest_port, host_port|
|
||||
config.vm.network "forwarded_port", guest: guest_port, host: host_port
|
||||
end
|
||||
|
||||
config.vm.provider "virtualbox" do |v|
|
||||
v.memory = $vm_memory
|
||||
v.cpus = $vm_cpus
|
||||
end
|
||||
|
||||
config.vm.provider "vmware_fusion" do |v, override|
|
||||
v.vmx["memsize"] = $vm_memory
|
||||
v.vmx["numvcpus"] = $vm_cpus
|
||||
v.vmx["vhv.enable"] = TRUE
|
||||
v.vmx["ethernet0.virtualdev"] = "vmxnet3"
|
||||
end
|
||||
|
||||
config.vm.synced_folder "../..", "/home/vagrant/cue"
|
||||
|
||||
if File.directory?("../../../python-cueclient")
|
||||
config.vm.synced_folder "../../../python-cueclient", "/home/vagrant/python-cueclient"
|
||||
end
|
||||
|
||||
if File.directory?("../../../cue-dashboard")
|
||||
config.vm.synced_folder "../../../cue-dashboard", "/home/vagrant/cue-dashboard"
|
||||
end
|
||||
|
||||
config.ssh.shell = "bash -c 'BASH_ENV=/etc/profile exec bash'"
|
||||
config.ssh.forward_agent = true
|
||||
|
||||
config.vm.define "ubuntu" do |ubuntu|
|
||||
ubuntu.vm.hostname = "cuedev-ubuntu"
|
||||
ubuntu_common(ubuntu)
|
||||
end
|
||||
|
||||
config.vm.define "fedora" do |fedora|
|
||||
fedora.vm.hostname = "cuedev-fedora"
|
||||
fedora_common(fedora)
|
||||
end
|
||||
|
||||
# Common provisioning steps
|
||||
config.vm.provision :shell, :privileged => true,
|
||||
:inline => "test -d /opt/stack || mkdir -p /opt/stack"
|
||||
config.vm.provision :shell, :privileged => true,
|
||||
:inline => "chown vagrant /opt/stack"
|
||||
config.vm.provision :shell, :privileged => false,
|
||||
:inline => $devstack_script
|
||||
|
||||
if $install_rally
|
||||
config.vm.provision :shell, :privileged => false,
|
||||
:inline => $rally_script
|
||||
end
|
||||
|
||||
if $install_devstack
|
||||
config.vm.provision :shell, :privileged => false,
|
||||
:inline => "pushd $HOME/devstack; ./stack.sh"
|
||||
end
|
||||
|
||||
end
|
@ -1,62 +0,0 @@
|
||||
# Devstack init script
|
||||
$devstack_script = <<SCRIPT
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
DEBIAN_FRONTEND=noninteractive sudo apt-get -qqy update || sudo yum update -qy
|
||||
DEBIAN_FRONTEND=noninteractive sudo apt-get install -qqy git || sudo yum install -qy git
|
||||
pushd ~
|
||||
|
||||
# Copy over git config
|
||||
cat << EOF > /home/vagrant/.gitconfig
|
||||
#{GITCONFIG}
|
||||
EOF
|
||||
|
||||
test -d devstack || git clone https://git.openstack.org/openstack-dev/devstack
|
||||
|
||||
test -d /home/vagrant/bin || mkdir /home/vagrant/bin
|
||||
|
||||
cat << EOF > /home/vagrant/bin/refresh_devstack.sh
|
||||
#!/bin/bash
|
||||
rsync -av --exclude='.tox' --exclude='.venv' --exclude='.vagrant' /home/vagrant/cue /opt/stack
|
||||
|
||||
if [ -d "/home/vagrant/python-cueclient" ]; then
|
||||
rsync -av --exclude='.tox' --exclude='.venv' --exclude='.vagrant' --exclude='contrib/vagrant' /home/vagrant/python-cueclient /opt/stack
|
||||
fi
|
||||
|
||||
if [ -d "/home/vagrant/cue-dashboard" ]; then
|
||||
rsync -av --exclude='.tox' --exclude='.venv' --exclude='.vagrant' --exclude='contrib/vagrant' /home/vagrant/cue-dashboard /opt/stack
|
||||
fi
|
||||
|
||||
# Install Vagrant local.conf sample
|
||||
if [ ! -f "/home/vagrant/devstack/local.conf" ]; then
|
||||
cp /opt/stack/cue/devstack/local.conf /home/vagrant/devstack/local.conf
|
||||
fi
|
||||
|
||||
# Install Vagrant local.sh sample
|
||||
if [ ! -f "/home/vagrant/devstack/local.sh" ]; then
|
||||
cp /opt/stack/cue/devstack/local.sh /home/vagrant/devstack/local.sh
|
||||
fi
|
||||
|
||||
pushd /home/vagrant/cue/devstack
|
||||
for f in lib/*; do
|
||||
if [ ! -f "/home/vagrant/devstack/\\$f" ]; then
|
||||
ln -fs /opt/stack/cue/devstack/\\$f -t /home/vagrant/devstack/\\$(dirname \\$f)
|
||||
fi
|
||||
done
|
||||
popd
|
||||
|
||||
EOF
|
||||
|
||||
chmod +x /home/vagrant/bin/refresh_devstack.sh
|
||||
|
||||
cat << EOF >> /home/vagrant/.bash_aliases
|
||||
alias refresh_devstack="/home/vagrant/bin/refresh_devstack.sh"
|
||||
alias delete_ports="neutron port-list | egrep '.+_cue\[.+\]\.node\[.+\]' | tr -d ' ' | cut -f 2 -d '|' | xargs -n1 neutron port-delete"
|
||||
alias delete_clusters="openstack cue cluster list | grep rally | tr -d ' ' | cut -f 2 -d '|' | xargs -n1 openstack cue cluster delete"
|
||||
EOF
|
||||
|
||||
/home/vagrant/bin/refresh_devstack.sh
|
||||
|
||||
SCRIPT
|
||||
|
@ -1,7 +0,0 @@
|
||||
# Common provisioning steps for Fedora VMs
|
||||
def fedora_common(machine)
|
||||
machine.vm.box = $fedora_box
|
||||
|
||||
machine.vm.provision :shell, :privileged => true, :inline => "yum update -y vim-minimal" # RH Bug 1066983
|
||||
machine.vm.provision :shell, :privileged => true, :inline => "yum install -y git-core MySQL-python"
|
||||
end
|
@ -1,20 +0,0 @@
|
||||
# Rally init script
|
||||
$rally_script = <<SCRIPT
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
DEBIAN_FRONTEND=noninteractive sudo apt-get -qqy update || sudo yum update -qy
|
||||
DEBIAN_FRONTEND=noninteractive sudo apt-get install -qqy git || sudo yum install -qy git
|
||||
pushd ~
|
||||
|
||||
test -d devstack || git clone https://git.openstack.org/openstack-dev/devstack
|
||||
test -d rally || git clone https://github.com/openstack/rally
|
||||
cd devstack
|
||||
echo "enable_plugin rally https://github.com/openstack/rally master" >> local.conf
|
||||
|
||||
cat << EOF >> /home/vagrant/.bash_aliases
|
||||
alias run_rally_cue_scenarios="rally -v --debug task start --task ~/cue/rally-jobs/rabbitmq-scenarios.yaml"
|
||||
|
||||
EOF
|
||||
|
||||
SCRIPT
|
@ -1,31 +0,0 @@
|
||||
# Common provisioning steps for Ubuntu VMs
|
||||
def ubuntu_common(machine)
|
||||
machine.vm.box = $ubuntu_box
|
||||
|
||||
machine.vm.provision :shell, :privileged => true,
|
||||
:inline => "DEBIAN_FRONTEND=noninteractive apt-get update"
|
||||
machine.vm.provision :shell, :privileged => true,
|
||||
:inline => "DEBIAN_FRONTEND=noninteractive apt-get install --yes git"
|
||||
machine.vm.provision :shell, :privileged => true,
|
||||
:inline => "DEBIAN_FRONTEND=noninteractive apt-get install --yes python-software-properties software-properties-common squid-deb-proxy-client"
|
||||
|
||||
if $package_proxy
|
||||
machine.vm.provision :shell, :privileged => true,
|
||||
:inline => "echo \"Acquire { Retries \\\"0\\\"; HTTP { Proxy \\\"#{$package_proxy}\\\"; }; };\" > /etc/apt/apt.conf.d/99proxy"
|
||||
end
|
||||
|
||||
# Install build dependencies
|
||||
if $install_build_deps
|
||||
machine.vm.provision "shell", inline: "apt-get install -y build-essential git libmysqlclient-dev python-tox python-dev libxml2-dev libxslt1-dev libffi-dev libssl-dev gettext"
|
||||
end
|
||||
|
||||
# Install tmate [optional]
|
||||
if $install_tmate
|
||||
machine.vm.provision "shell", :inline => "sudo add-apt-repository ppa:nviennot/tmate"
|
||||
machine.vm.provision "shell", :inline => "sudo apt-get update"
|
||||
machine.vm.provision "shell", :inline => "sudo apt-get install -y tmate"
|
||||
end
|
||||
|
||||
# Remove anything unnecessary
|
||||
machine.vm.provision "shell", inline: "apt-get autoremove -y"
|
||||
end
|
@ -1,39 +0,0 @@
|
||||
# -*- mode: ruby -*-
|
||||
# # vi: set ft=ruby :
|
||||
|
||||
# Uncomment $hostname and set it to specify an explicit hostname for the VM
|
||||
# $hostname = "dev"
|
||||
|
||||
# Setup a guest port => host port mapping for the mappings provided below
|
||||
#$forwarded_port = {
|
||||
# 8795 => 8795,
|
||||
# 6080 => 6080,
|
||||
# 80 => 8080
|
||||
#}
|
||||
|
||||
# Ubuntu box
|
||||
$ubuntu_box = "sputnik13/trusty64"
|
||||
|
||||
# Fedora box
|
||||
$fedora_box = "box-cutter/fedora20"
|
||||
|
||||
# Specify a proxy to be used for packages
|
||||
$package_proxy = nil
|
||||
|
||||
# Install devstack in the VM
|
||||
$install_devstack = false
|
||||
|
||||
# Install build dependencies
|
||||
$install_build_deps = true
|
||||
|
||||
# Set $install_tmate to true to
|
||||
$install_tmate = false
|
||||
|
||||
# Set the amount of RAM configured for the VM
|
||||
$vm_memory = 4096
|
||||
|
||||
# Set the number of CPU cores configured for the VM
|
||||
$vm_cpus = 2
|
||||
|
||||
# Install rally in the vm
|
||||
$install_rally = true
|
40
contrib/zookeeper/Vagrantfile
vendored
40
contrib/zookeeper/Vagrantfile
vendored
@ -1,40 +0,0 @@
|
||||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
$vm_count = 3
|
||||
$hostname = File.basename(File.dirname(__FILE__))
|
||||
$domain = "localdomain"
|
||||
$ip_prefix = "10.250.250"
|
||||
#$pkg_mirror = "http://localhost/mirror"
|
||||
|
||||
|
||||
$ip_list = ""
|
||||
(1..$vm_count).each do |i|
|
||||
$ip_list += "#{$ip_prefix}.#{100+i} "
|
||||
end
|
||||
|
||||
VAGRANTFILE_API_VERSION = "2"
|
||||
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
config.vm.box = "sputnik13/wheezy64"
|
||||
if Vagrant.has_plugin?("vagrant-hostmanager")
|
||||
config.hostmanager.enabled = true
|
||||
config.hostmanager.manage_host = false
|
||||
config.hostmanager.ignore_private_ip = false
|
||||
config.hostmanager.include_offline = true
|
||||
end
|
||||
|
||||
config.ssh.shell = "bash -c 'BASH_ENV=/etc/profile exec bash'"
|
||||
|
||||
(1..$vm_count).each do |i|
|
||||
config.vm.define "#{$hostname}#{i}" do |node|
|
||||
node.vm.hostname = "#{$hostname}#{i}"
|
||||
node.vm.network :private_network, ip: "#{$ip_prefix}.#{100+i}", :netmask => "255.255.255.0"
|
||||
node.hostmanager.aliases = ["#{$hostname}#{i}.#{$domain}", "#{$hostname}#{i}"]
|
||||
|
||||
if $pkg_mirror then
|
||||
node.vm.provision "shell", inline: "sed -e 's/http:\/\/.*.archive.ubuntu.com/http:\/\/#{$pkg_mirror}/' -i /etc/apt/sources.list ; apt-get update"
|
||||
end
|
||||
node.vm.provision "shell", inline: "/vagrant/install.sh -n #{$vm_count} #{i} #{$ip_list}"
|
||||
end
|
||||
end
|
||||
end
|
@ -1,87 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
ZOOKEEPER_COUNT=1
|
||||
|
||||
if [[ -f '/etc/issue' ]]; then
|
||||
LINUX_DISTRO=`cat /etc/issue | egrep 'Amazon|Ubuntu|CentOS|RedHat|Debian' | awk -F' ' '{print $1}'`
|
||||
elif [[ -f '/etc/debian_version' ]]; then
|
||||
LINUX_DISTRO='Debian'
|
||||
fi
|
||||
|
||||
function usage {
|
||||
echo "usage: $0 [-n count] [-h] MYID IP_1 ... IP_N"
|
||||
exit -1
|
||||
}
|
||||
|
||||
while getopts n:h opt; do
|
||||
case $opt in
|
||||
n)
|
||||
ZOOKEEPER_COUNT=$OPTARG
|
||||
;;
|
||||
h)
|
||||
usage
|
||||
esac
|
||||
done
|
||||
|
||||
# Pop processed options from the option stack
|
||||
OPTIND=$OPTIND-1
|
||||
shift $OPTIND
|
||||
|
||||
if [[ ! $# -gt $ZOOKEEPER_COUNT ]]; then
|
||||
echo "Invalid number of arguments"
|
||||
echo ""
|
||||
usage
|
||||
fi
|
||||
|
||||
ZK_MYID=$1
|
||||
shift
|
||||
|
||||
for i in `seq 1 $ZOOKEEPER_COUNT`; do
|
||||
ZK_SERVER_IP[$i]=$1
|
||||
shift
|
||||
done
|
||||
|
||||
# Check for root permissions
|
||||
if [ "$(id -u)" != "0" ]; then
|
||||
SUDO='sudo'
|
||||
echo ""
|
||||
echo "This script is not being run as root, you may be asked for a password in order to execute sudo.."
|
||||
echo ""
|
||||
else
|
||||
SUDO=
|
||||
fi
|
||||
|
||||
# Set distro specific values
|
||||
case "$LINUX_DISTRO" in
|
||||
'Ubuntu'|'Debian')
|
||||
UPDATE_PKG='apt-get update'
|
||||
INSTALL_PKG='apt-get install -y'
|
||||
PKG_NAME='zookeeper zookeeper-bin zookeeperd'
|
||||
CONFIG_FILE='/etc/zookeeper/conf/zoo.cfg'
|
||||
SERVICE_NAME='zookeeper'
|
||||
MYID_FILE='/var/lib/zookeeper/myid'
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Only Ubuntu and Debian are supported at this time"
|
||||
exit -1
|
||||
esac
|
||||
|
||||
# Update package cache if necessary
|
||||
if $UPDATE_PKG; then
|
||||
$SUDO $UPDATE_PKG
|
||||
fi
|
||||
|
||||
# Install package
|
||||
$SUDO $INSTALL_PKG $PKG_NAME
|
||||
|
||||
# Generate configuration
|
||||
$SUDO sed -i.bak -e '/^#.*$/d' -e '/^$/d' -e '/^server\..*=.*/d' $CONFIG_FILE
|
||||
for i in `seq 1 $ZOOKEEPER_COUNT`; do
|
||||
echo "server.${i}=${ZK_SERVER_IP[$i]}:2888:3888" >> $CONFIG_FILE
|
||||
done
|
||||
|
||||
echo $ZK_MYID > $MYID_FILE
|
||||
|
||||
# Restart service
|
||||
$SUDO service $SERVICE_NAME restart
|
@ -1,50 +0,0 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@hp.com>
|
||||
#
|
||||
# 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.path
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
|
||||
log.register_options(cfg.CONF)
|
||||
|
||||
DEFAULT_OPTS = [
|
||||
cfg.StrOpt('pybasedir',
|
||||
default=os.path.abspath(os.path.join(os.path.dirname(__file__),
|
||||
'../')),
|
||||
help='Directory where the cue python module is installed'),
|
||||
cfg.StrOpt('state-path', default='/var/lib/cue',
|
||||
help='Top-level directory for maintaining cue\'s state'),
|
||||
cfg.StrOpt('rabbit_port',
|
||||
default='5672',
|
||||
help='The port to access RabbitMQ AMQP interface on a clustered'
|
||||
'vm'),
|
||||
cfg.StrOpt('os_security_group',
|
||||
default=None,
|
||||
help='The default Security Group to use for VMs created as '
|
||||
'part of a cluster'),
|
||||
cfg.StrOpt('management_network_id',
|
||||
default=None,
|
||||
help='The id representing the management network '),
|
||||
cfg.StrOpt('default_broker_name',
|
||||
default='rabbitmq',
|
||||
help='The name of the default broker image')
|
||||
]
|
||||
|
||||
cfg.CONF.register_opts(DEFAULT_OPTS)
|
||||
|
||||
|
||||
def list_opts():
|
||||
return [('DEFAULT', DEFAULT_OPTS)]
|
@ -1,48 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Copyright © 2014 Hewlett-Packard
|
||||
#
|
||||
## Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
|
||||
API_SERVICE_OPTS = [
|
||||
cfg.StrOpt('host_ip',
|
||||
default='0.0.0.0',
|
||||
help='The listen IP for the Cue API server.'),
|
||||
cfg.IntOpt('port',
|
||||
default=8795,
|
||||
help='The port for the Cue API server.'),
|
||||
cfg.IntOpt('max_limit',
|
||||
default=1000,
|
||||
help='The maximum number of items returned in a single '
|
||||
'response from a collection resource.'),
|
||||
# TODO(sputnik13): this needs to be removed when image selection is done
|
||||
cfg.StrOpt('os_image_id',
|
||||
help='The Image ID to use for VMs created as part of a '
|
||||
'cluster'),
|
||||
cfg.IntOpt('max_cluster_size',
|
||||
default=10,
|
||||
help='Maximum number of nodes in a cluster.'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
opt_group = cfg.OptGroup(name='api',
|
||||
title='Options for the cue-api service')
|
||||
CONF.register_group(opt_group)
|
||||
CONF.register_opts(API_SERVICE_OPTS, opt_group)
|
||||
|
||||
|
||||
def list_opts():
|
||||
return [('api', API_SERVICE_OPTS)]
|
@ -1,36 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Copyright © 2012 New Dream Network, LLC (DreamHost)
|
||||
#
|
||||
# Author: Doug Hellmann <doug.hellmann@dreamhost.com>
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Access Control Lists (ACL's) control access the API server."""
|
||||
|
||||
from cue.api.middleware import auth_token
|
||||
|
||||
|
||||
def install(app, conf, public_routes):
|
||||
"""Install ACL check on application.
|
||||
|
||||
:param app: A WSGI applicatin.
|
||||
:param conf: Settings. Dict'ified and passed to keystonemiddleware
|
||||
:param public_routes: The list of the routes which will be allowed to
|
||||
access without authentication.
|
||||
:return: The same WSGI application with ACL installed.
|
||||
|
||||
"""
|
||||
return auth_token.AuthTokenMiddleware(app,
|
||||
conf=dict(conf),
|
||||
public_api_routes=public_routes)
|
@ -1,94 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
# Copyright © 2012 New Dream Network, LLC (DreamHost)
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_middleware import cors as cors_middleware
|
||||
import pecan
|
||||
|
||||
from cue.api import acl
|
||||
from cue.api import config
|
||||
from cue.api import hooks
|
||||
from cue.api import middleware
|
||||
from cue.common import policy
|
||||
|
||||
auth_opts = [
|
||||
cfg.StrOpt('auth_strategy',
|
||||
default='keystone',
|
||||
help='Method to use for authentication: noauth or keystone.'),
|
||||
]
|
||||
|
||||
API_OPTS = [
|
||||
cfg.BoolOpt('pecan_debug', default=False,
|
||||
help='Pecan HTML Debug Interface'),
|
||||
]
|
||||
|
||||
cfg.CONF.register_opts(auth_opts)
|
||||
cfg.CONF.register_opts(API_OPTS, group='api')
|
||||
|
||||
|
||||
def list_opts():
|
||||
return [('DEFAULT', auth_opts), ('api', API_OPTS)]
|
||||
|
||||
|
||||
def get_pecan_config():
|
||||
# Set up the pecan configuration
|
||||
filename = config.__file__.replace('.pyc', '.py')
|
||||
return pecan.configuration.conf_from_file(filename)
|
||||
|
||||
|
||||
def setup_app(pecan_config=None, extra_hooks=None):
|
||||
policy.init()
|
||||
app_hooks = [hooks.ConfigHook(),
|
||||
#hooks.DBHook(),
|
||||
hooks.ContextHook(pecan_config.app.acl_public_routes),
|
||||
#hooks.RPCHook(),
|
||||
#hooks.NoExceptionTracebackHook()
|
||||
]
|
||||
if extra_hooks:
|
||||
app_hooks.extend(extra_hooks)
|
||||
|
||||
if not pecan_config:
|
||||
pecan_config = get_pecan_config()
|
||||
|
||||
pecan.configuration.set_config(dict(pecan_config), overwrite=True)
|
||||
app = pecan.make_app(
|
||||
pecan_config.app.root,
|
||||
static_root=pecan_config.app.static_root,
|
||||
debug=cfg.CONF.api.pecan_debug,
|
||||
force_canonical=getattr(pecan_config.app, 'force_canonical', True),
|
||||
hooks=app_hooks,
|
||||
wrap_app=middleware.ParsableErrorMiddleware,
|
||||
)
|
||||
|
||||
if pecan_config.app.enable_acl:
|
||||
app = acl.install(app, cfg.CONF, pecan_config.app.acl_public_routes)
|
||||
|
||||
# Create a CORS wrapper, and attach ironic-specific defaults that must be
|
||||
# included in all CORS responses.
|
||||
app = cors_middleware.CORS(app, cfg.CONF)
|
||||
|
||||
return app
|
||||
|
||||
|
||||
class VersionSelectorApplication(object):
|
||||
def __init__(self):
|
||||
pc = get_pecan_config()
|
||||
pc.app.enable_acl = (cfg.CONF.auth_strategy == 'keystone')
|
||||
self.v1 = setup_app(pecan_config=pc)
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
return self.v1(environ, start_response)
|
@ -1,28 +0,0 @@
|
||||
# -*- mode: python -*-
|
||||
# -*- encoding: 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.
|
||||
"""
|
||||
Use this file for deploying the API service under Apache2 mod_wsgi.
|
||||
"""
|
||||
|
||||
from cue.api import app
|
||||
from cue.common import service
|
||||
|
||||
import oslo_i18n
|
||||
|
||||
oslo_i18n.install('cue')
|
||||
|
||||
service.prepare_service([])
|
||||
|
||||
application = app.VersionSelectorApplication()
|
@ -1,42 +0,0 @@
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
# Server Specific Configurations
|
||||
# See https://pecan.readthedocs.org/en/latest/configuration.html#server-configuration # noqa
|
||||
server = {
|
||||
'port': '8795',
|
||||
'host': '0.0.0.0'
|
||||
}
|
||||
|
||||
# Pecan Application Configurations
|
||||
# See https://pecan.readthedocs.org/en/latest/configuration.html#application-configuration # noqa
|
||||
app = {
|
||||
'root': 'cue.api.controllers.root.RootController',
|
||||
'modules': ['cue.api'],
|
||||
'static_root': '%(confdir)s/public',
|
||||
'debug': False,
|
||||
'enable_acl': True,
|
||||
'acl_public_routes': [
|
||||
'/',
|
||||
'/v1',
|
||||
],
|
||||
}
|
||||
|
||||
# WSME Configurations
|
||||
# See https://wsme.readthedocs.org/en/latest/integrate.html#configuration
|
||||
wsme = {
|
||||
'debug': cfg.CONF.debug,
|
||||
}
|
@ -1 +0,0 @@
|
||||
__author__ = 'vipul'
|
@ -1,57 +0,0 @@
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import datetime
|
||||
|
||||
import wsme
|
||||
from wsme import types as wtypes
|
||||
|
||||
|
||||
class APIBase(wtypes.Base):
|
||||
|
||||
created_at = wsme.wsattr(datetime.datetime, readonly=True)
|
||||
"The time in UTC at which the object is created"
|
||||
|
||||
updated_at = wsme.wsattr(datetime.datetime, readonly=True)
|
||||
"The time in UTC at which the object is updated"
|
||||
|
||||
@classmethod
|
||||
def from_db_model(cls, m):
|
||||
return cls(**(m.as_dict()))
|
||||
|
||||
def as_dict(self):
|
||||
"""Render this object as a dict of its fields."""
|
||||
return dict((k, getattr(self, k))
|
||||
for k in self.fields
|
||||
if hasattr(self, k) and
|
||||
getattr(self, k) != wsme.Unset)
|
||||
|
||||
def unset_fields_except(self, except_list=None):
|
||||
"""Unset fields so they don't appear in the message body.
|
||||
|
||||
:param except_list: A list of fields that won't be touched.
|
||||
|
||||
"""
|
||||
if except_list is None:
|
||||
except_list = []
|
||||
|
||||
for k in self.as_dict():
|
||||
if k not in except_list:
|
||||
setattr(self, k, wsme.Unset)
|
||||
|
||||
def unset_empty_fields(self):
|
||||
"""Unset empty fields so they don't appear in message body."""
|
||||
for k in self.fields:
|
||||
if hasattr(self, k) and getattr(self, k) is None:
|
||||
setattr(self, k, wsme.Unset)
|
@ -1,58 +0,0 @@
|
||||
# Copyright 2013 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import pecan
|
||||
from wsme import types as wtypes
|
||||
|
||||
from cue.api.controllers import base
|
||||
|
||||
|
||||
def build_url(resource, resource_args, bookmark=False, base_url=None):
|
||||
if base_url is None:
|
||||
base_url = pecan.request.host_url
|
||||
|
||||
template = '%(url)s/%(res)s' if bookmark else '%(url)s/v1/%(res)s'
|
||||
# FIXME(lucasagomes): I'm getting a 404 when doing a GET on
|
||||
# a nested resource that the URL ends with a '/'.
|
||||
# https://groups.google.com/forum/#!topic/pecan-dev/QfSeviLg5qs
|
||||
template += '%(args)s' if resource_args.startswith('?') else '/%(args)s'
|
||||
return template % {'url': base_url, 'res': resource, 'args': resource_args}
|
||||
|
||||
|
||||
class Link(base.APIBase):
|
||||
"""A link representation."""
|
||||
|
||||
href = wtypes.text
|
||||
"The url of a link."
|
||||
|
||||
rel = wtypes.text
|
||||
"The name of a link."
|
||||
|
||||
type = wtypes.text
|
||||
"Indicates the type of document/link."
|
||||
|
||||
@classmethod
|
||||
def make_link(cls, rel_name, url, resource, resource_args,
|
||||
bookmark=False, type=wtypes.Unset):
|
||||
href = build_url(resource, resource_args,
|
||||
bookmark=bookmark, base_url=url)
|
||||
return Link(href=href, rel=rel_name, type=type)
|
||||
|
||||
@classmethod
|
||||
def sample(cls):
|
||||
sample = cls(href="http://localhost:8795/v1/cluster"
|
||||
"eaaca217-e7d8-47b4-bb41-3f99f20eed89",
|
||||
rel="bookmark")
|
||||
return sample
|
@ -1,104 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Copyright © 2012 New Dream Network, LLC (DreamHost)
|
||||
#
|
||||
# Author: Doug Hellmann <doug.hellmann@dreamhost.com>
|
||||
#
|
||||
# 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 pecan
|
||||
from pecan import rest
|
||||
from wsme import types as wtypes
|
||||
import wsmeext.pecan as wsme_pecan
|
||||
|
||||
from cue.api.controllers import base
|
||||
from cue.api.controllers import link
|
||||
from cue.api.controllers import v1
|
||||
|
||||
|
||||
class Version(base.APIBase):
|
||||
"""An API version representation."""
|
||||
|
||||
id = wtypes.text
|
||||
"The ID of the version, also acts as the release number"
|
||||
|
||||
links = [link.Link]
|
||||
"A Link that point to a specific version of the API"
|
||||
|
||||
status = wtypes.text
|
||||
"The status of this version"
|
||||
|
||||
@classmethod
|
||||
def convert(self, id, status):
|
||||
version = Version()
|
||||
version.id = id
|
||||
version.status = status
|
||||
version.links = [link.Link.make_link('self', pecan.request.host_url,
|
||||
id, '', bookmark=True)]
|
||||
return version
|
||||
|
||||
|
||||
class Root(base.APIBase):
|
||||
|
||||
name = wtypes.text
|
||||
"The name of the API"
|
||||
|
||||
description = wtypes.text
|
||||
"Some information about this API"
|
||||
|
||||
versions = [Version]
|
||||
"Links to all the versions available in this API"
|
||||
|
||||
default_version = Version
|
||||
"A link to the default version of the API"
|
||||
|
||||
@classmethod
|
||||
def convert(self):
|
||||
"""Builds link to v1 controller."""
|
||||
root = Root()
|
||||
root.name = "OpenStack Cue API"
|
||||
root.description = ("Cue is an OpenStack project which aims to "
|
||||
"provision Messaging Brokers.")
|
||||
root.versions = [Version.convert('v1', 'STABLE')]
|
||||
root.default_version = Version.convert('v1', 'STABLE')
|
||||
return root
|
||||
|
||||
|
||||
class RootController(rest.RestController):
|
||||
|
||||
_versions = ['v1']
|
||||
"All supported API versions"
|
||||
|
||||
_default_version = 'v1'
|
||||
"The default API version"
|
||||
|
||||
v1 = v1.V1Controller()
|
||||
|
||||
@wsme_pecan.wsexpose(Root)
|
||||
def get(self):
|
||||
# NOTE: The reason why convert() it's being called for every
|
||||
# request is because we need to get the host url from
|
||||
# the request object to make the links.
|
||||
return Root.convert()
|
||||
|
||||
@pecan.expose()
|
||||
def _route(self, args):
|
||||
"""Overrides the default routing behavior.
|
||||
|
||||
It redirects the request to the default version of the cue API
|
||||
if the version number is not specified in the url.
|
||||
"""
|
||||
|
||||
if args[0] and args[0] not in self._versions:
|
||||
args = [self._default_version] + args
|
||||
return super(RootController, self)._route(args)
|
@ -1,78 +0,0 @@
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Version 1 of the Cue API
|
||||
|
||||
"""
|
||||
|
||||
import pecan
|
||||
from pecan import rest
|
||||
from wsme import types as wtypes
|
||||
import wsmeext.pecan as wsme_pecan
|
||||
|
||||
from cue.api.controllers import base
|
||||
from cue.api.controllers import link
|
||||
from cue.api.controllers.v1 import cluster
|
||||
|
||||
|
||||
class V1(base.APIBase):
|
||||
"""The representation of the version 1 of the API."""
|
||||
|
||||
id = wtypes.text
|
||||
"""The ID of the version, also acts as the release number"""
|
||||
|
||||
status = wtypes.text
|
||||
"""The status of this version"""
|
||||
|
||||
clusters = [link.Link]
|
||||
"""Links to the clusters resource"""
|
||||
|
||||
@staticmethod
|
||||
def convert():
|
||||
"""Builds link to clusters controller."""
|
||||
v1 = V1()
|
||||
v1.id = "v1"
|
||||
v1.status = "Stable"
|
||||
|
||||
v1.clusters = [link.Link.make_link('self', pecan.request.host_url,
|
||||
'clusters', ''),
|
||||
link.Link.make_link('bookmark',
|
||||
pecan.request.host_url, v1.id,
|
||||
'clusters',
|
||||
bookmark=True)
|
||||
]
|
||||
return v1
|
||||
|
||||
|
||||
class V1Controller(rest.RestController):
|
||||
"""Version 1 Cue API controller root."""
|
||||
|
||||
_versions = ['v1']
|
||||
"All supported API versions"
|
||||
|
||||
_default_version = 'v1'
|
||||
"The default API version"
|
||||
clusters = cluster.ClusterController()
|
||||
|
||||
@wsme_pecan.wsexpose(V1)
|
||||
def get(self):
|
||||
# NOTE: The reason why convert() it's being called for every
|
||||
# request is because we need to get the host url from
|
||||
# the request object to make the links.
|
||||
return V1.convert()
|
||||
|
||||
@pecan.expose()
|
||||
def _route(self, args):
|
||||
return super(V1Controller, self)._route(args)
|
@ -1,405 +0,0 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Authors: Davide Agnello <davide.agnello@hp.com>
|
||||
#
|
||||
# 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.
|
||||
# Copyright [2014] Hewlett-Packard Development Company, L.P.
|
||||
# limitations under the License.
|
||||
|
||||
"""Version 1 of the Cue API
|
||||
"""
|
||||
import sys
|
||||
|
||||
from novaclient import exceptions as nova_exc
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import uuidutils
|
||||
import pecan
|
||||
from pecan import rest
|
||||
import six
|
||||
import wsme
|
||||
from wsme import types as wtypes
|
||||
import wsmeext.pecan as wsme_pecan
|
||||
|
||||
from cue.api.controllers import base
|
||||
import cue.client as client
|
||||
from cue.common import exception
|
||||
from cue.common.i18n import _ # noqa
|
||||
from cue.common.i18n import _LI # noqa
|
||||
from cue.common import policy
|
||||
from cue.common import validate_auth_token as auth_validate
|
||||
from cue import objects
|
||||
from cue.taskflow import client as task_flow_client
|
||||
from cue.taskflow.flow import create_cluster
|
||||
from cue.taskflow.flow import delete_cluster
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class AuthenticationCredential(wtypes.Base):
|
||||
"""Representation of a Broker Authentication Method."""
|
||||
|
||||
type = wtypes.text
|
||||
"type of authentication"
|
||||
|
||||
token = wtypes.DictType(six.text_type, six.text_type)
|
||||
"authentication credentials"
|
||||
|
||||
|
||||
class EndPoint(base.APIBase):
|
||||
"""Representation of an End point."""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.fields = []
|
||||
endpoint_object_fields = list(objects.Endpoint.fields)
|
||||
for k in endpoint_object_fields:
|
||||
# only add fields we expose in the api
|
||||
if hasattr(self, k):
|
||||
self.fields.append(k)
|
||||
setattr(self, k, kwargs.get(k, wtypes.Unset))
|
||||
|
||||
type = wtypes.text
|
||||
"type of endpoint"
|
||||
|
||||
uri = wtypes.text
|
||||
"URL to endpoint"
|
||||
|
||||
|
||||
class Cluster(base.APIBase):
|
||||
"""Representation of a cluster's details."""
|
||||
# todo(dagnello): WSME attribute verification sometimes triggers 500 server
|
||||
# error when user input was actually invalid (400). Example: if 'size' was
|
||||
# provided as a string/char, e.g. 'a', api returns 500 server error.
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.fields = []
|
||||
cluster_object_fields = list(objects.Cluster.fields)
|
||||
for k in cluster_object_fields:
|
||||
# only add fields we expose in the api
|
||||
if hasattr(self, k):
|
||||
self.fields.append(k)
|
||||
setattr(self, k, kwargs.get(k, wtypes.Unset))
|
||||
|
||||
id = wsme.wsattr(wtypes.UuidType(), readonly=True)
|
||||
"UUID of cluster"
|
||||
|
||||
network_id = wtypes.wsattr([wtypes.UuidType()], mandatory=True)
|
||||
"NIC of Neutron network"
|
||||
|
||||
name = wsme.wsattr(wtypes.text, mandatory=True)
|
||||
"Name of cluster"
|
||||
|
||||
status = wsme.wsattr(wtypes.text, readonly=True)
|
||||
"Current status of cluster"
|
||||
|
||||
flavor = wsme.wsattr(wtypes.text, mandatory=True)
|
||||
"Flavor of cluster"
|
||||
|
||||
size = wsme.wsattr(wtypes.IntegerType(minimum=0, maximum=sys.maxsize),
|
||||
mandatory=True)
|
||||
"Number of nodes in cluster"
|
||||
|
||||
volume_size = wtypes.IntegerType(minimum=0, maximum=sys.maxsize)
|
||||
"Volume size for nodes in cluster"
|
||||
|
||||
endpoints = wtypes.wsattr([EndPoint], default=[])
|
||||
"List of endpoints on accessing node"
|
||||
|
||||
authentication = wtypes.wsattr(AuthenticationCredential)
|
||||
"Authentication for accessing message brokers"
|
||||
|
||||
error_detail = wsme.wsattr(wtypes.text, mandatory=False)
|
||||
"Error detail(s) associated with cluster"
|
||||
|
||||
|
||||
def get_complete_cluster(context, cluster_id):
|
||||
"""Helper to retrieve the api-compatible full structure of a cluster."""
|
||||
|
||||
cluster_obj = objects.Cluster.get_cluster_by_id(context, cluster_id)
|
||||
|
||||
target = {'tenant_id': cluster_obj.project_id}
|
||||
policy.check("cluster:get", context, target)
|
||||
|
||||
cluster_as_dict = cluster_obj.as_dict()
|
||||
|
||||
# convert 'network_id' to list for ClusterDetails compatibility
|
||||
cluster_as_dict['network_id'] = [cluster_as_dict['network_id']]
|
||||
|
||||
# construct api cluster object
|
||||
cluster = Cluster(**cluster_as_dict)
|
||||
cluster.endpoints = []
|
||||
|
||||
cluster_nodes = objects.Node.get_nodes_by_cluster_id(context, cluster_id)
|
||||
|
||||
for node in cluster_nodes:
|
||||
# extract endpoints from node
|
||||
node_endpoints = objects.Endpoint.get_endpoints_by_node_id(context,
|
||||
node.id)
|
||||
|
||||
# construct api endpoint objects
|
||||
node_endpoints_dict = [EndPoint(**obj_endpoint.as_dict()) for
|
||||
obj_endpoint in node_endpoints]
|
||||
|
||||
cluster.endpoints.extend(node_endpoints_dict)
|
||||
|
||||
return cluster
|
||||
|
||||
|
||||
def delete_complete_cluster(context, cluster_id):
|
||||
cluster_obj = objects.Cluster.get_cluster_by_id(context, cluster_id)
|
||||
target = {'tenant_id': cluster_obj.project_id}
|
||||
policy.check("cluster:delete", context, target)
|
||||
|
||||
# update cluster to deleting
|
||||
objects.Cluster.update_cluster_deleting(context, cluster_id)
|
||||
|
||||
# retrieve cluster nodes
|
||||
nodes = objects.Node.get_nodes_by_cluster_id(context, cluster_id)
|
||||
|
||||
# create list with node id's for create cluster flow
|
||||
node_ids = [node.id for node in nodes]
|
||||
|
||||
# retrieve cluster record
|
||||
cluster = objects.Cluster.get_cluster_by_id(context, cluster_id)
|
||||
|
||||
# prepare and post cluster delete job to backend
|
||||
flow_kwargs = {
|
||||
'cluster_id': cluster_id,
|
||||
'node_ids': node_ids,
|
||||
'group_id': cluster.group_id,
|
||||
}
|
||||
|
||||
job_args = {
|
||||
'context': context.to_dict(),
|
||||
}
|
||||
|
||||
job_client = task_flow_client.get_client_instance()
|
||||
# TODO(dagnello): might be better to use request_id for job_uuid
|
||||
job_uuid = uuidutils.generate_uuid()
|
||||
job_client.post(delete_cluster, job_args, flow_kwargs=flow_kwargs,
|
||||
tx_uuid=job_uuid)
|
||||
|
||||
LOG.info(_LI('Delete Cluster Request Cluster ID %(cluster_id)s Job ID '
|
||||
'%(job_id)s') % ({"cluster_id": cluster_id,
|
||||
"job_id": job_uuid}))
|
||||
|
||||
|
||||
class ClusterController(rest.RestController):
|
||||
"""Manages operations on specific Cluster of nodes."""
|
||||
|
||||
def _validate_flavor(self, image_id, cluster_flavor):
|
||||
"""Checks if flavor satisfies minimum requirement of image metadata.
|
||||
|
||||
:param image_id: image id of the broker.
|
||||
:param cluster_flavor: flavor id of the cluster.
|
||||
:raises: exception.ConfigurationError
|
||||
:raises: exception.InternalServerError
|
||||
:raises: exception.Invalid
|
||||
"""
|
||||
nova_client = client.nova_client()
|
||||
|
||||
# get image metadata
|
||||
try:
|
||||
image_metadata = nova_client.images.get(image_id)
|
||||
image_minRam = image_metadata.minRam
|
||||
image_minDisk = image_metadata.minDisk
|
||||
except nova_exc.ClientException as ex:
|
||||
if ex.http_status == 404:
|
||||
raise exception.ConfigurationError(_('Invalid image %s '
|
||||
'configured') % image_id)
|
||||
else:
|
||||
raise exception.InternalServerError
|
||||
|
||||
# get flavor metadata
|
||||
try:
|
||||
flavor_metadata = nova_client.flavors.get(cluster_flavor)
|
||||
flavor_ram = flavor_metadata.ram
|
||||
flavor_disk = flavor_metadata.disk
|
||||
except nova_exc.ClientException as ex:
|
||||
if ex.http_status == 404:
|
||||
raise exception.Invalid(_('Invalid flavor %s provided') %
|
||||
cluster_flavor)
|
||||
else:
|
||||
raise exception.InternalServerError
|
||||
|
||||
# validate flavor with broker image metadata
|
||||
if (flavor_disk < image_minDisk):
|
||||
raise exception.Invalid(_("Flavor disk is smaller than the "
|
||||
"minimum %s required for broker") %
|
||||
image_minDisk)
|
||||
elif (flavor_ram < image_minRam):
|
||||
raise exception.Invalid(_("Flavor ram is smaller than the "
|
||||
"minimum %s required for broker") %
|
||||
image_minRam)
|
||||
|
||||
@wsme_pecan.wsexpose(Cluster, wtypes.text, status_code=200)
|
||||
def get_one(self, cluster_id):
|
||||
"""Return this cluster."""
|
||||
# validate cluster_id is of type Uuid
|
||||
try:
|
||||
wtypes.UuidType().validate(cluster_id)
|
||||
except ValueError:
|
||||
raise exception.Invalid(_("Invalid cluster ID format provided"))
|
||||
|
||||
context = pecan.request.context
|
||||
|
||||
cluster = get_complete_cluster(context, cluster_id)
|
||||
|
||||
cluster.unset_empty_fields()
|
||||
return cluster
|
||||
|
||||
@wsme_pecan.wsexpose(None, wtypes.text, status_code=202)
|
||||
def delete(self, cluster_id):
|
||||
"""Delete this Cluster."""
|
||||
# validate cluster_id is of type Uuid
|
||||
try:
|
||||
wtypes.UuidType().validate(cluster_id)
|
||||
except ValueError:
|
||||
raise exception.Invalid(_("Invalid cluster ID format provided"))
|
||||
|
||||
context = pecan.request.context
|
||||
delete_complete_cluster(context, cluster_id)
|
||||
|
||||
@wsme_pecan.wsexpose([Cluster], status_code=200)
|
||||
def get_all(self):
|
||||
"""Return list of Clusters."""
|
||||
|
||||
context = pecan.request.context
|
||||
clusters = objects.Cluster.get_clusters(context)
|
||||
cluster_list = [get_complete_cluster(context, obj_cluster.id)
|
||||
for obj_cluster in clusters]
|
||||
|
||||
for obj_cluster in cluster_list:
|
||||
obj_cluster.unset_empty_fields()
|
||||
|
||||
return cluster_list
|
||||
|
||||
@wsme_pecan.wsexpose(Cluster, body=Cluster,
|
||||
status_code=202)
|
||||
def post(self, data):
|
||||
"""Create a new Cluster.
|
||||
|
||||
:param data: cluster parameters within the request body.
|
||||
"""
|
||||
context = pecan.request.context
|
||||
request_data = data.as_dict()
|
||||
cluster_flavor = request_data['flavor']
|
||||
|
||||
if data.size <= 0:
|
||||
raise exception.Invalid(_("Invalid cluster size provided"))
|
||||
elif data.size > CONF.api.max_cluster_size:
|
||||
raise exception.RequestEntityTooLarge(
|
||||
_("Invalid cluster size, max size is: %d")
|
||||
% CONF.api.max_cluster_size)
|
||||
|
||||
if len(data.network_id) > 1:
|
||||
raise exception.Invalid(_("Invalid number of network_id's"))
|
||||
|
||||
# extract username/password
|
||||
if (data.authentication and data.authentication.type and
|
||||
data.authentication.token):
|
||||
auth_validator = auth_validate.AuthTokenValidator.validate_token(
|
||||
auth_type=data.authentication.type,
|
||||
token=data.authentication.token)
|
||||
if not auth_validator or not auth_validator.validate():
|
||||
raise exception.Invalid(_("Invalid broker authentication "
|
||||
"parameter(s)"))
|
||||
else:
|
||||
raise exception.Invalid(_("Missing broker authentication "
|
||||
"parameter(s)"))
|
||||
|
||||
default_rabbit_user = data.authentication.token['username']
|
||||
default_rabbit_pass = data.authentication.token['password']
|
||||
|
||||
broker_name = CONF.default_broker_name
|
||||
|
||||
# get the image id of default broker
|
||||
image_id = objects.BrokerMetadata.get_image_id_by_broker_name(
|
||||
context, broker_name)
|
||||
|
||||
# validate cluster flavor
|
||||
self._validate_flavor(image_id, cluster_flavor)
|
||||
|
||||
# convert 'network_id' from list to string type for objects/cluster
|
||||
# compatibility
|
||||
request_data['network_id'] = request_data['network_id'][0]
|
||||
|
||||
# create new cluster object with required data from user
|
||||
new_cluster = objects.Cluster(**request_data)
|
||||
|
||||
# create new cluster with node related data from user
|
||||
new_cluster.create(context)
|
||||
|
||||
# retrieve cluster data
|
||||
cluster = get_complete_cluster(context, new_cluster.id)
|
||||
|
||||
nodes = objects.Node.get_nodes_by_cluster_id(context,
|
||||
cluster.id)
|
||||
|
||||
# create list with node id's for create cluster flow
|
||||
node_ids = [node.id for node in nodes]
|
||||
|
||||
# prepare and post cluster create job to backend
|
||||
flow_kwargs = {
|
||||
'cluster_id': cluster.id,
|
||||
'node_ids': node_ids,
|
||||
'user_network_id': cluster.network_id[0],
|
||||
'management_network_id': CONF.management_network_id,
|
||||
}
|
||||
|
||||
# generate unique erlang cookie to be used by all nodes in the new
|
||||
# cluster, erlang cookies are strings of up to 255 characters
|
||||
erlang_cookie = uuidutils.generate_uuid()
|
||||
|
||||
job_args = {
|
||||
'tenant_id': new_cluster.project_id,
|
||||
'flavor': cluster.flavor,
|
||||
'image': image_id,
|
||||
'volume_size': cluster.volume_size,
|
||||
'port': '5672',
|
||||
'context': context.to_dict(),
|
||||
# TODO(sputnik13: this needs to come from the create request
|
||||
# and default to a configuration value rather than always using
|
||||
# config value
|
||||
'security_groups': [CONF.os_security_group],
|
||||
'port': CONF.rabbit_port,
|
||||
'key_name': CONF.openstack.os_key_name,
|
||||
'erlang_cookie': erlang_cookie,
|
||||
'default_rabbit_user': default_rabbit_user,
|
||||
'default_rabbit_pass': default_rabbit_pass,
|
||||
}
|
||||
job_client = task_flow_client.get_client_instance()
|
||||
# TODO(dagnello): might be better to use request_id for job_uuid
|
||||
job_uuid = uuidutils.generate_uuid()
|
||||
job_client.post(create_cluster, job_args,
|
||||
flow_kwargs=flow_kwargs,
|
||||
tx_uuid=job_uuid)
|
||||
|
||||
LOG.info(_LI('Create Cluster Request Cluster ID %(cluster_id)s '
|
||||
'Cluster size %(size)s network ID %(network_id)s '
|
||||
'Job ID %(job_id)s Broker name %(broker_name)s') %
|
||||
({"cluster_id": cluster.id,
|
||||
"size": cluster.size,
|
||||
"network_id": cluster.network_id,
|
||||
"job_id": job_uuid,
|
||||
"broker_name": broker_name}))
|
||||
|
||||
cluster.additional_information = []
|
||||
cluster.additional_information.append(
|
||||
dict(def_rabbit_user=default_rabbit_user))
|
||||
cluster.additional_information.append(
|
||||
dict(def_rabbit_pass=default_rabbit_pass))
|
||||
|
||||
cluster.unset_empty_fields()
|
||||
return cluster
|
125
cue/api/hooks.py
125
cue/api/hooks.py
@ -1,125 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Copyright © 2012 New Dream Network, LLC (DreamHost)
|
||||
#
|
||||
# Author: Doug Hellmann <doug.hellmann@dreamhost.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_config import cfg
|
||||
from pecan import hooks
|
||||
|
||||
from cue.common import context
|
||||
from cue.db import api as dbapi
|
||||
|
||||
|
||||
class ConfigHook(hooks.PecanHook):
|
||||
"""Attach the config object to the request so controllers can get to it."""
|
||||
|
||||
def before(self, state):
|
||||
state.request.cfg = cfg.CONF
|
||||
|
||||
|
||||
class DBHook(hooks.PecanHook):
|
||||
"""Attach the dbapi object to the request so controllers can get to it."""
|
||||
|
||||
def before(self, state):
|
||||
state.request.dbapi = dbapi.get_instance()
|
||||
|
||||
|
||||
class ContextHook(hooks.PecanHook):
|
||||
"""Configures a request context and attaches it to the request.
|
||||
|
||||
The following HTTP request headers are used:
|
||||
|
||||
X-User-Id or X-User:
|
||||
Used for context.user_id.
|
||||
|
||||
X-Tenant-Id or X-Tenant:
|
||||
Used for context.tenant.
|
||||
|
||||
X-Auth-Token:
|
||||
Used for context.auth_token.
|
||||
|
||||
X-Roles:
|
||||
Used for setting context.is_admin flag to either True or False.
|
||||
The flag is set to True, if X-Roles contains either an administrator
|
||||
or admin substring. Otherwise it is set to False.
|
||||
|
||||
"""
|
||||
def __init__(self, public_api_routes):
|
||||
self.public_api_routes = public_api_routes
|
||||
super(ContextHook, self).__init__()
|
||||
|
||||
def before(self, state):
|
||||
user_id = state.request.headers.get('X-User-Id')
|
||||
tenant_id = state.request.headers.get('X-Project-Id')
|
||||
domain_id = state.request.headers.get('X-Domain-Id')
|
||||
domain_name = state.request.headers.get('X-Domain-Name')
|
||||
auth_token = state.request.headers.get('X-Auth-Token')
|
||||
|
||||
is_public_api = state.request.environ.get('is_public_api', False)
|
||||
|
||||
state.request.context = context.RequestContext(
|
||||
auth_token=auth_token,
|
||||
user=user_id,
|
||||
tenant=tenant_id,
|
||||
domain_id=domain_id,
|
||||
domain_name=domain_name,
|
||||
is_public_api=is_public_api)
|
||||
|
||||
|
||||
# class RPCHook(hooks.PecanHook):
|
||||
# """Attach the rpcapi object to the request so controllers can get to it."""
|
||||
#
|
||||
# def before(self, state):
|
||||
# state.request.rpcapi = rpcapi.ConductorAPI()
|
||||
|
||||
|
||||
class NoExceptionTracebackHook(hooks.PecanHook):
|
||||
"""Workaround rpc.common: deserialize_remote_exception.
|
||||
|
||||
deserialize_remote_exception builds rpc exception traceback into error
|
||||
message which is then sent to the client. Such behavior is a security
|
||||
concern so this hook is aimed to cut-off traceback from the error message.
|
||||
|
||||
"""
|
||||
# NOTE(max_lobur): 'after' hook used instead of 'on_error' because
|
||||
# 'on_error' never fired for wsme+pecan pair. wsme @wsexpose decorator
|
||||
# catches and handles all the errors, so 'on_error' dedicated for unhandled
|
||||
# exceptions never fired.
|
||||
def after(self, state):
|
||||
# Omit empty body. Some errors may not have body at this level yet.
|
||||
if not state.response.body:
|
||||
return
|
||||
|
||||
# Do nothing if there is no error.
|
||||
if 200 <= state.response.status_int < 400:
|
||||
return
|
||||
|
||||
json_body = state.response.json
|
||||
# Do not remove traceback when server in debug mode (except 'Server'
|
||||
# errors when 'debuginfo' will be used for traces).
|
||||
if cfg.CONF.debug and json_body.get('faultcode') != 'Server':
|
||||
return
|
||||
|
||||
faultsting = json_body.get('faultstring')
|
||||
traceback_marker = 'Traceback (most recent call last):'
|
||||
if faultsting and (traceback_marker in faultsting):
|
||||
# Cut-off traceback.
|
||||
faultsting = faultsting.split(traceback_marker, 1)[0]
|
||||
# Remove trailing newlines and spaces if any.
|
||||
json_body['faultstring'] = faultsting.rstrip()
|
||||
# Replace the whole json. Cannot change original one beacause it's
|
||||
# generated on the fly.
|
||||
state.response.json = json_body
|
@ -1,23 +0,0 @@
|
||||
# -*- encoding: 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.
|
||||
|
||||
from cue.api.middleware import auth_token
|
||||
from cue.api.middleware import parsable_error
|
||||
|
||||
|
||||
ParsableErrorMiddleware = parsable_error.ParsableErrorMiddleware
|
||||
AuthTokenMiddleware = auth_token.AuthTokenMiddleware
|
||||
|
||||
__all__ = (ParsableErrorMiddleware,
|
||||
AuthTokenMiddleware)
|
@ -1,62 +0,0 @@
|
||||
# -*- encoding: 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 re
|
||||
|
||||
from keystonemiddleware import auth_token
|
||||
from oslo_log import log
|
||||
import six
|
||||
|
||||
from cue.common import exception
|
||||
from cue.common.i18n import _ # noqa
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class AuthTokenMiddleware(auth_token.AuthProtocol):
|
||||
"""A wrapper on Keystone auth_token middleware.
|
||||
|
||||
Does not perform verification of authentication tokens
|
||||
for public routes in the API.
|
||||
|
||||
"""
|
||||
def __init__(self, app, conf, public_api_routes=[]):
|
||||
route_pattern_tpl = '%s(\.json|\.xml)?$'
|
||||
|
||||
try:
|
||||
self.public_api_routes = [re.compile(route_pattern_tpl % route_tpl)
|
||||
for route_tpl in public_api_routes]
|
||||
except re.error as e:
|
||||
msg = _('Cannot compile public API routes: %s') % e
|
||||
|
||||
LOG.error(msg)
|
||||
raise exception.ConfigInvalid(error_msg=msg)
|
||||
|
||||
super(AuthTokenMiddleware, self).__init__(app, conf)
|
||||
|
||||
def __call__(self, env, start_response):
|
||||
path = env.get('PATH_INFO')
|
||||
if isinstance(path, six.string_types):
|
||||
path = path.rstrip('/') or path
|
||||
|
||||
# The information whether the API call is being performed against the
|
||||
# public API is required for some other components. Saving it to the
|
||||
# WSGI environment is reasonable thereby.
|
||||
env['is_public_api'] = any(map(lambda pattern: re.match(pattern, path),
|
||||
self.public_api_routes))
|
||||
|
||||
if env['is_public_api']:
|
||||
return self._app(env, start_response)
|
||||
|
||||
return super(AuthTokenMiddleware, self).__call__(env, start_response)
|
@ -1,96 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Copyright © 2012 New Dream Network, LLC (DreamHost)
|
||||
#
|
||||
# Author: Doug Hellmann <doug.hellmann@dreamhost.com>
|
||||
#
|
||||
# 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.
|
||||
"""
|
||||
Middleware to replace the plain text message body of an error
|
||||
response with one formatted so the client can parse it.
|
||||
|
||||
Based on pecan.middleware.errordocument
|
||||
"""
|
||||
|
||||
from xml import etree as et
|
||||
|
||||
from oslo_log import log
|
||||
from oslo_serialization import jsonutils
|
||||
import six
|
||||
import webob
|
||||
|
||||
from cue.common.i18n import _ # noqa
|
||||
from cue.common.i18n import _LE # noqa
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class ParsableErrorMiddleware(object):
|
||||
"""Replace error body with something the client can parse."""
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
# Request for this state, modified by replace_start_response()
|
||||
# and used when an error is being reported.
|
||||
state = {}
|
||||
|
||||
def replacement_start_response(status, headers, exc_info=None):
|
||||
"""Overrides the default response to make errors parsable."""
|
||||
try:
|
||||
status_code = int(status.split(' ')[0])
|
||||
state['status_code'] = status_code
|
||||
except (ValueError, TypeError): # pragma: nocover
|
||||
raise Exception(_(
|
||||
'ErrorDocumentMiddleware received an invalid '
|
||||
'status %s') % status)
|
||||
else:
|
||||
if (state['status_code'] // 100) not in (2, 3):
|
||||
# Remove some headers so we can replace them later
|
||||
# when we have the full error message and can
|
||||
# compute the length.
|
||||
headers = [(h, v)
|
||||
for (h, v) in headers
|
||||
if h not in ('Content-Length', 'Content-Type')
|
||||
]
|
||||
# Save the headers in case we need to modify them.
|
||||
state['headers'] = headers
|
||||
return start_response(status, headers, exc_info)
|
||||
|
||||
app_iter = self.app(environ, replacement_start_response)
|
||||
if (state['status_code'] // 100) not in (2, 3):
|
||||
req = webob.Request(environ)
|
||||
if (req.accept.best_match(['application/json', 'application/xml'])
|
||||
== 'application/xml'):
|
||||
try:
|
||||
# simple check xml is valid
|
||||
body = [et.ElementTree.tostring(
|
||||
et.ElementTree.fromstring('<error_message>'
|
||||
+ '\n'.join(app_iter)
|
||||
+ '</error_message>'))]
|
||||
except et.ElementTree.ParseError as err:
|
||||
LOG.error(_LE('Error parsing HTTP response: %s'), err)
|
||||
body = ['<error_message>%s' % state['status_code']
|
||||
+ '</error_message>']
|
||||
state['headers'].append(('Content-Type', 'application/xml'))
|
||||
else:
|
||||
err_msg = b'\n'.join(app_iter)
|
||||
if six.PY3: # pragma: no cover
|
||||
err_msg = err_msg.decode('utf-8')
|
||||
body = jsonutils.dump_as_bytes({'error_message': err_msg})
|
||||
body = [body]
|
||||
state['headers'].append(('Content-Type', 'application/json'))
|
||||
state['headers'].append(('Content-Length', str(len(body[0]))))
|
||||
else:
|
||||
body = app_iter
|
||||
return body
|
146
cue/client.py
146
cue/client.py
@ -1,146 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2015 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.
|
||||
|
||||
from keystoneauth1.identity import v2 as keystone_v2_auth
|
||||
from keystoneauth1.identity import v3 as keystone_v3_auth
|
||||
from keystoneauth1 import session as keystone_session
|
||||
import neutronclient.neutron.client as NeutronClient
|
||||
import novaclient.client as NovaClient
|
||||
from oslo_config import cfg
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
OS_OPTS = [
|
||||
cfg.StrOpt('os_region_name',
|
||||
help='Region name',
|
||||
default=None),
|
||||
cfg.StrOpt('os_username',
|
||||
help='Openstack Username',
|
||||
default=None),
|
||||
cfg.StrOpt('os_password',
|
||||
help='Openstack Password',
|
||||
default=None),
|
||||
cfg.StrOpt('os_auth_version',
|
||||
help='Openstack authentication version',
|
||||
choices=('2.0', '3'),
|
||||
default='3'),
|
||||
cfg.StrOpt('os_auth_url',
|
||||
help='Openstack Authentication (Identity) URL',
|
||||
default=None),
|
||||
cfg.StrOpt('os_key_name',
|
||||
help='SSH key to be provisioned to cue VMs',
|
||||
default=None),
|
||||
cfg.StrOpt('os_availability_zone',
|
||||
help='Default availability zone to provision cue VMs',
|
||||
default=None),
|
||||
cfg.BoolOpt('os_insecure',
|
||||
help='Openstack insecure',
|
||||
default=False),
|
||||
cfg.StrOpt('os_cacert',
|
||||
help='Openstack cacert',
|
||||
default=None),
|
||||
cfg.StrOpt('os_project_name',
|
||||
help='Openstack project name',
|
||||
default=None),
|
||||
cfg.StrOpt('os_project_domain_name',
|
||||
help='Openstack project domain name',
|
||||
default=None),
|
||||
cfg.StrOpt('os_user_domain_name',
|
||||
help='Openstack user domain name',
|
||||
default=None),
|
||||
cfg.StrOpt('os_endpoint_type',
|
||||
help='Openstack endpoint type [public|internal|admin]',
|
||||
default='public',
|
||||
choices=['public', 'internal', 'admin']),
|
||||
]
|
||||
|
||||
opt_group = cfg.OptGroup(
|
||||
name='openstack',
|
||||
title='Options for Openstack.'
|
||||
)
|
||||
|
||||
CONF.register_group(opt_group)
|
||||
CONF.register_opts(OS_OPTS, group=opt_group)
|
||||
|
||||
|
||||
def nova_client():
|
||||
keystone_session = get_keystone_session()
|
||||
endpoint_type = CONF.openstack.os_endpoint_type + 'URL'
|
||||
return NovaClient.Client(2,
|
||||
session=keystone_session,
|
||||
auth_url=CONF.openstack.os_auth_url,
|
||||
region_name=CONF.openstack.os_region_name,
|
||||
insecure=CONF.openstack.os_insecure,
|
||||
cacert=CONF.openstack.os_cacert,
|
||||
endpoint_type=endpoint_type,
|
||||
)
|
||||
|
||||
|
||||
def neutron_client():
|
||||
keystone_session = get_keystone_session()
|
||||
endpoint_type = CONF.openstack.os_endpoint_type + 'URL'
|
||||
return NeutronClient.Client('2.0',
|
||||
session=keystone_session,
|
||||
auth_url=CONF.openstack.os_auth_url,
|
||||
region_name=CONF.openstack.os_region_name,
|
||||
insecure=CONF.openstack.os_insecure,
|
||||
ca_cert=CONF.openstack.os_cacert,
|
||||
endpoint_type=endpoint_type,
|
||||
)
|
||||
|
||||
|
||||
def get_auth_v2():
|
||||
auth_url = CONF.openstack.os_auth_url
|
||||
username = CONF.openstack.os_username
|
||||
password = CONF.openstack.os_password
|
||||
tenant_name = CONF.openstack.os_project_name
|
||||
return keystone_v2_auth.Password(auth_url=auth_url,
|
||||
username=username,
|
||||
password=password,
|
||||
tenant_name=tenant_name,
|
||||
)
|
||||
|
||||
|
||||
def get_auth_v3():
|
||||
auth_url = CONF.openstack.os_auth_url
|
||||
username = CONF.openstack.os_username
|
||||
password = CONF.openstack.os_password
|
||||
project_name = CONF.openstack.os_project_name
|
||||
project_domain_name = CONF.openstack.os_project_domain_name
|
||||
user_domain_name = CONF.openstack.os_user_domain_name
|
||||
return keystone_v3_auth.Password(auth_url=auth_url,
|
||||
username=username,
|
||||
password=password,
|
||||
project_name=project_name,
|
||||
project_domain_name=project_domain_name,
|
||||
user_domain_name=user_domain_name,
|
||||
)
|
||||
|
||||
|
||||
def get_keystone_session():
|
||||
insecure = CONF.openstack.os_insecure
|
||||
if insecure:
|
||||
verify = False
|
||||
else:
|
||||
verify = CONF.openstack.os_cacert
|
||||
|
||||
if CONF.openstack.os_auth_version == '2.0':
|
||||
return keystone_session.Session(auth=get_auth_v2(),
|
||||
verify=verify)
|
||||
else:
|
||||
return keystone_session.Session(auth=get_auth_v3(),
|
||||
verify=verify)
|
@ -1,4 +0,0 @@
|
||||
|
||||
import oslo_i18n
|
||||
|
||||
oslo_i18n.install('cue')
|
@ -1,66 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2014-2015 Hewlett-Packard Development Company, L.P.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""The Cue Service API."""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
from wsgiref import simple_server
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
from six.moves import socketserver
|
||||
|
||||
from cue.api import app
|
||||
from cue.common.i18n import _LI # noqa
|
||||
from cue.common import service as cue_service
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class ThreadedSimpleServer(socketserver.ThreadingMixIn,
|
||||
simple_server.WSGIServer):
|
||||
"""A Mixin class to make the API service greenthread-able."""
|
||||
pass
|
||||
|
||||
|
||||
def main():
|
||||
# Pase config file and command line options, then start logging
|
||||
cue_service.prepare_service(sys.argv)
|
||||
|
||||
# Build and start the WSGI app
|
||||
host = CONF.api.host_ip
|
||||
port = CONF.api.port
|
||||
wsgi = simple_server.make_server(
|
||||
host, port,
|
||||
app.VersionSelectorApplication(),
|
||||
server_class=ThreadedSimpleServer)
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
LOG.info(_LI("Serving on http://%(host)s:%(port)s"),
|
||||
{'host': host, 'port': port})
|
||||
LOG.info(_LI("Configuration:"))
|
||||
CONF.log_opt_values(LOG, logging.INFO)
|
||||
|
||||
try:
|
||||
wsgi.serve_forever()
|
||||
except KeyboardInterrupt: # pragma: no cover
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
main()
|
@ -1,125 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright 2015 Hewlett-Packard Development Company, L.P.
|
||||
# Copyright 2012 Bouvet ASA
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@bouvet.no>
|
||||
#
|
||||
# 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 sys
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
from stevedore import extension
|
||||
|
||||
from cue.common import config
|
||||
from cue import version
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
def methods_of(obj):
|
||||
"""Utility function to get all methods of a object
|
||||
|
||||
Get all callable methods of an object that don't start with underscore
|
||||
returns a list of tuples of the form (method_name, method).
|
||||
"""
|
||||
result = []
|
||||
for i in dir(obj):
|
||||
if callable(getattr(obj, i)) and not i.startswith('_'):
|
||||
result.append((i, getattr(obj, i)))
|
||||
return result
|
||||
|
||||
|
||||
def get_available_commands():
|
||||
em = extension.ExtensionManager('cue.manage')
|
||||
return dict([(e.name, e.plugin) for e in em.extensions])
|
||||
|
||||
|
||||
def add_command_parsers(subparsers):
|
||||
for category, cls in get_available_commands().items():
|
||||
command_object = cls()
|
||||
|
||||
parser = subparsers.add_parser(category)
|
||||
parser.set_defaults(command_object=command_object)
|
||||
|
||||
category_subparsers = parser.add_subparsers(dest='action')
|
||||
|
||||
for (action, action_fn) in methods_of(command_object):
|
||||
action = getattr(action_fn, '_cmd_name', action)
|
||||
parser = category_subparsers.add_parser(action)
|
||||
|
||||
action_kwargs = []
|
||||
for args, kwargs in getattr(action_fn, 'args', []):
|
||||
parser.add_argument(*args, **kwargs)
|
||||
|
||||
parser.set_defaults(action_fn=action_fn)
|
||||
parser.set_defaults(action_kwargs=action_kwargs)
|
||||
|
||||
|
||||
category_opt = cfg.SubCommandOpt('category', title="Commands",
|
||||
help="Available Commands",
|
||||
handler=add_command_parsers)
|
||||
|
||||
|
||||
def get_arg_string(args):
|
||||
arg = None
|
||||
if args[0] == '-':
|
||||
# (Note)zhiteng: args starts with FLAGS.oparser.prefix_chars
|
||||
# is optional args. Notice that cfg module takes care of
|
||||
# actual ArgParser so prefix_chars is always '-'.
|
||||
if args[1] == '-':
|
||||
# This is long optional arg
|
||||
arg = args[2:]
|
||||
else:
|
||||
arg = args[1:]
|
||||
else:
|
||||
arg = args
|
||||
|
||||
return arg
|
||||
|
||||
|
||||
def fetch_func_args(func):
|
||||
fn_args = []
|
||||
for args, kwargs in getattr(func, 'args', []):
|
||||
arg = kwargs.get('dest', get_arg_string(args[0]))
|
||||
fn_args.append(getattr(CONF.category, arg))
|
||||
|
||||
return fn_args
|
||||
|
||||
|
||||
def main(argv=None, conf_fixture=None):
|
||||
if argv is None: # pragma: no cover
|
||||
argv = sys.argv
|
||||
|
||||
# Registering cli options directly to the global cfg.CONF causes issues
|
||||
# for unit/functional tests that test anything but the cmd.manage module
|
||||
# because cmd.manage adds required cli parameters. A conf_fixture object
|
||||
# is expected to be passed in only during tests.
|
||||
if conf_fixture is None: # pragma: no cover
|
||||
CONF.register_cli_opt(category_opt)
|
||||
else:
|
||||
conf_fixture.register_cli_opt(category_opt)
|
||||
|
||||
log.register_options(CONF)
|
||||
config.set_defaults()
|
||||
|
||||
CONF(argv[1:], project='cue',
|
||||
version=version.version_info.version_string())
|
||||
|
||||
log.setup(CONF, "cue")
|
||||
|
||||
fn = CONF.category.action_fn
|
||||
|
||||
fn_args = fetch_func_args(fn)
|
||||
fn(*fn_args)
|
@ -1,50 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2015 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.
|
||||
|
||||
"""cue-monitor
|
||||
|
||||
cue-monitor is responsible for actively monitoring cluster statuses
|
||||
"""
|
||||
import logging
|
||||
|
||||
from oslo_config import cfg
|
||||
import oslo_log.log as log
|
||||
from oslo_service import service as openstack_service
|
||||
|
||||
from cue.common.i18n import _LI # noqa
|
||||
import cue.common.service as cue_service
|
||||
import cue.monitor.monitor_service as cue_monitor_service
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
CONF = cfg.CONF
|
||||
cue_service.prepare_service(sys.argv)
|
||||
|
||||
# Log configuration and other startup information
|
||||
LOG = log.getLogger(__name__)
|
||||
LOG.info(_LI("Starting cue-monitor"))
|
||||
LOG.info(_LI("Configuration:"))
|
||||
CONF.log_opt_values(LOG, logging.INFO)
|
||||
|
||||
monitor = cue_monitor_service.MonitorService()
|
||||
launcher = openstack_service.launch(CONF, monitor)
|
||||
launcher.wait()
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
sys.exit(main())
|
@ -1,73 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2015 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.
|
||||
|
||||
"""cue-worker
|
||||
|
||||
cue-worker is responsible for executing jobs that are posted by the API in
|
||||
response to user requests.
|
||||
|
||||
TODO: multi-process capability needs to be reimplemented. The first
|
||||
implementation using oslo.service has issues in the interaction between
|
||||
eventlet, kazoo, and taskflow.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
import oslo_config.cfg as cfg
|
||||
import oslo_log.log as log
|
||||
|
||||
from cue.common.i18n import _LI # noqa
|
||||
import cue.common.service as cue_service
|
||||
import cue.taskflow.service as tf_service
|
||||
|
||||
|
||||
WORKER_OPTS = [
|
||||
cfg.IntOpt('count',
|
||||
help="Number of worker processes to spawn",
|
||||
default=10)
|
||||
]
|
||||
|
||||
opt_group = cfg.OptGroup(
|
||||
name='worker',
|
||||
title='Options for cue worker'
|
||||
)
|
||||
|
||||
cfg.CONF.register_group(opt_group)
|
||||
cfg.CONF.register_opts(WORKER_OPTS, group=opt_group)
|
||||
|
||||
|
||||
def list_opts():
|
||||
return [('worker', WORKER_OPTS)]
|
||||
|
||||
|
||||
def main():
|
||||
# Initialize environment
|
||||
CONF = cfg.CONF
|
||||
cue_service.prepare_service(sys.argv)
|
||||
|
||||
# Log configuration and other startup information
|
||||
LOG = log.getLogger(__name__)
|
||||
LOG.info(_LI("Starting cue workers"))
|
||||
LOG.info(_LI("Configuration:"))
|
||||
CONF.log_opt_values(LOG, logging.INFO)
|
||||
|
||||
cue_worker = tf_service.ConductorService.create("cue-worker")
|
||||
cue_worker.handle_signals()
|
||||
cue_worker.start()
|
||||
|
||||
|
||||
if __name__ == "__main__": # pragma: no cover
|
||||
sys.exit(main())
|
@ -1 +0,0 @@
|
||||
__author__ = 'vipul'
|
@ -1,34 +0,0 @@
|
||||
# Copyright 2016 Hewlett Packard Enterprise Development Corporation, 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.
|
||||
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_middleware import cors
|
||||
|
||||
|
||||
def set_defaults():
|
||||
"""Set all oslo.config default overrides for cue."""
|
||||
# CORS Defaults
|
||||
# TODO(krotscheck): Update with https://review.openstack.org/#/c/285368/
|
||||
cfg.set_defaults(cors.CORS_OPTS,
|
||||
allow_headers=['X-Auth-Token',
|
||||
'X-Server-Management-Url'],
|
||||
expose_headers=['X-Auth-Token',
|
||||
'X-Server-Management-Url'],
|
||||
allow_methods=['GET',
|
||||
'PUT',
|
||||
'POST',
|
||||
'DELETE',
|
||||
'PATCH']
|
||||
)
|
@ -1,86 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Copyright 2014-2015 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.
|
||||
|
||||
from oslo_context import context
|
||||
|
||||
|
||||
class RequestContext(context.RequestContext):
|
||||
"""Extends security contexts from the OpenStack common library."""
|
||||
|
||||
def __init__(self, auth_token=None, user=None, tenant=None, domain=None,
|
||||
user_domain=None, project_domain=None, is_admin=False,
|
||||
read_only=False, show_deleted=False, request_id=None,
|
||||
resource_uuid=None, overwrite=True, roles=None,
|
||||
is_public_api=False, domain_id=None, domain_name=None):
|
||||
"""Stores several additional request parameters:
|
||||
|
||||
:param roles:
|
||||
:param domain_id: The ID of the domain.
|
||||
:param domain_name: The name of the domain.
|
||||
:param is_public_api: Specifies whether the request should be processed
|
||||
without authentication.
|
||||
|
||||
"""
|
||||
super(RequestContext, self).__init__(auth_token=auth_token, user=user,
|
||||
tenant=tenant, domain=domain,
|
||||
user_domain=user_domain,
|
||||
project_domain=project_domain,
|
||||
is_admin=is_admin,
|
||||
read_only=read_only,
|
||||
show_deleted=show_deleted,
|
||||
request_id=request_id,
|
||||
resource_uuid=resource_uuid,
|
||||
overwrite=overwrite)
|
||||
|
||||
self.roles = roles or []
|
||||
self.is_public_api = is_public_api
|
||||
self.domain_id = domain_id
|
||||
self.domain_name = domain_name
|
||||
|
||||
@property
|
||||
def project_id(self):
|
||||
return self.tenant
|
||||
|
||||
@property
|
||||
def tenant_id(self):
|
||||
return self.tenant
|
||||
|
||||
@tenant_id.setter
|
||||
def tenant_id(self, tenant_id):
|
||||
self.tenant = tenant_id
|
||||
|
||||
@property
|
||||
def user_id(self):
|
||||
return self.user
|
||||
|
||||
@user_id.setter
|
||||
def user_id(self, user_id):
|
||||
self.user = user_id
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, values):
|
||||
if 'user_identity' in values:
|
||||
del values['user_identity']
|
||||
return cls(**values)
|
||||
|
||||
def to_dict(self):
|
||||
values = super(RequestContext, self).to_dict()
|
||||
values.update({
|
||||
"roles": self.roles,
|
||||
"is_public_api": self.is_public_api,
|
||||
"domain_id": self.domain_id,
|
||||
"domain_name": self.domain_name
|
||||
})
|
||||
return values
|
@ -1,156 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2014-2015 Hewlett-Packard Development Company, L.P.
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Cue base exception handling.
|
||||
|
||||
Includes decorator for re-raising Cue-type exceptions.
|
||||
|
||||
SHOULD include dedicated exception logging.
|
||||
|
||||
"""
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import six
|
||||
|
||||
from cue.common.i18n import _ # noqa
|
||||
from cue.common.i18n import _LE # noqa
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
exc_log_opts = [
|
||||
cfg.BoolOpt('fatal_exception_format_errors',
|
||||
default=False,
|
||||
help='Make exception message format errors fatal.'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(exc_log_opts)
|
||||
|
||||
|
||||
def _cleanse_dict(original):
|
||||
"""Strip all admin_password, new_pass, rescue_pass keys from a dict."""
|
||||
return dict((k, v) for k, v in original.items() if "_pass" not in k)
|
||||
|
||||
|
||||
class CueException(Exception):
|
||||
"""Base Cue Exception
|
||||
|
||||
To correctly use this class, inherit from it and define
|
||||
a 'message' property. That message will get printf'd
|
||||
with the keyword arguments provided to the constructor.
|
||||
|
||||
"""
|
||||
message = _("An unknown exception occurred.")
|
||||
code = 500
|
||||
headers = {}
|
||||
safe = False
|
||||
|
||||
def __init__(self, message=None, **kwargs):
|
||||
self.kwargs = kwargs
|
||||
|
||||
if 'code' not in self.kwargs:
|
||||
try:
|
||||
self.kwargs['code'] = self.code
|
||||
except AttributeError: # pragma: no cover
|
||||
pass
|
||||
|
||||
if not message:
|
||||
try:
|
||||
message = self.message % kwargs
|
||||
|
||||
except Exception as e:
|
||||
# kwargs doesn't match a variable in the message
|
||||
# log the issue and the kwargs
|
||||
LOG.exception(_LE('Exception in string format operation'))
|
||||
for name, value in kwargs.items():
|
||||
LOG.error("%s: %s" % (name, value))
|
||||
|
||||
if CONF.fatal_exception_format_errors:
|
||||
raise e
|
||||
else:
|
||||
# at least get the core message out if something happened
|
||||
message = self.message
|
||||
|
||||
super(CueException, self).__init__(message)
|
||||
|
||||
def format_message(self):
|
||||
if self.__class__.__name__.endswith('_Remote'):
|
||||
return self.args[0]
|
||||
else:
|
||||
return six.text_type(self)
|
||||
|
||||
|
||||
class NotFound(CueException):
|
||||
message = _("Not Found")
|
||||
code = 404
|
||||
|
||||
|
||||
class NotAuthorized(CueException):
|
||||
message = _("Not authorized.")
|
||||
code = 403
|
||||
|
||||
|
||||
class OperationNotPermitted(NotAuthorized):
|
||||
message = _("Operation not permitted.")
|
||||
|
||||
|
||||
class Invalid(CueException):
|
||||
message = _("Unacceptable parameters.")
|
||||
code = 400
|
||||
|
||||
|
||||
class Conflict(CueException):
|
||||
message = _('Conflict.')
|
||||
code = 409
|
||||
|
||||
|
||||
class RequestEntityTooLarge(CueException):
|
||||
message = _('Request too large for server.')
|
||||
code = 413
|
||||
|
||||
|
||||
class TemporaryFailure(CueException):
|
||||
message = _("Resource temporarily unavailable, please retry.")
|
||||
code = 503
|
||||
|
||||
|
||||
class InvalidState(Conflict):
|
||||
message = _("Invalid resource state.")
|
||||
|
||||
|
||||
class NodeAlreadyExists(Conflict):
|
||||
message = _("A node with UUID %(uuid)s already exists.")
|
||||
|
||||
|
||||
class ConfigurationError(CueException):
|
||||
message = _("Configuration Error")
|
||||
|
||||
|
||||
class VmBuildingException(CueException):
|
||||
message = _("VM is in building state")
|
||||
|
||||
|
||||
class VmErrorException(CueException):
|
||||
message = _("VM is not in a building state")
|
||||
|
||||
|
||||
class InternalServerError(CueException):
|
||||
message = _("Internal Server Error")
|
||||
code = 500
|
@ -1,31 +0,0 @@
|
||||
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import oslo_i18n # noqa
|
||||
|
||||
_translators = oslo_i18n.TranslatorFactory(domain='cue')
|
||||
|
||||
# The primary translation function using the well-known name "_"
|
||||
_ = _translators.primary
|
||||
|
||||
# Translators for log levels.
|
||||
#
|
||||
# The abbreviated names are meant to reflect the usual use of a short
|
||||
# name like '_'. The "L" is for "log" and the other letter comes from
|
||||
# the level.
|
||||
_LI = _translators.log_info
|
||||
_LW = _translators.log_warning
|
||||
_LE = _translators.log_error
|
||||
_LC = _translators.log_critical
|
@ -1,88 +0,0 @@
|
||||
# Copyright (c) 2011 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Policy Engine For Cue."""
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_policy import opts
|
||||
from oslo_policy import policy
|
||||
|
||||
from cue.common import exception
|
||||
from cue.common.i18n import _ # noqa
|
||||
from cue.common.i18n import _LI # noqa
|
||||
from cue.common import utils
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
# Add the default policy opts
|
||||
opts.set_defaults(CONF)
|
||||
|
||||
_ENFORCER = None
|
||||
|
||||
|
||||
def reset():
|
||||
global _ENFORCER
|
||||
if _ENFORCER:
|
||||
_ENFORCER.clear()
|
||||
_ENFORCER = None
|
||||
|
||||
|
||||
def init(default_rule=None):
|
||||
policy_files = utils.find_config(CONF['oslo_policy'].policy_file)
|
||||
|
||||
if len(policy_files) == 0:
|
||||
msg = 'Unable to determine appropriate policy json file'
|
||||
raise exception.ConfigurationError(msg)
|
||||
|
||||
LOG.info(_LI('Using policy_file found at: %s') % policy_files[0])
|
||||
|
||||
with open(policy_files[0]) as fh:
|
||||
policy_string = fh.read()
|
||||
rules = policy.Rules.load_json(policy_string, default_rule=default_rule)
|
||||
|
||||
global _ENFORCER
|
||||
if not _ENFORCER:
|
||||
LOG.debug("Enforcer is not present, recreating.")
|
||||
_ENFORCER = policy.Enforcer(cfg.CONF)
|
||||
|
||||
_ENFORCER.set_rules(rules)
|
||||
|
||||
|
||||
def check(rule, ctxt, target=None, do_raise=True, exc=exception.NotAuthorized):
|
||||
creds = ctxt.to_dict()
|
||||
target = target or {}
|
||||
|
||||
try:
|
||||
result = _ENFORCER.enforce(rule, target, creds, do_raise, exc)
|
||||
except Exception:
|
||||
result = False
|
||||
raise
|
||||
else:
|
||||
return result
|
||||
finally:
|
||||
extra = {'policy': {'rule': rule, 'target': target}}
|
||||
|
||||
if result:
|
||||
LOG.info(_("Policy check succeeded for rule '%(rule)s' "
|
||||
"on target %(target)s") %
|
||||
{'rule': rule, 'target': repr(target)}, extra=extra)
|
||||
else:
|
||||
LOG.info(_("Policy check failed for rule '%(rule)s' "
|
||||
"on target %(target)s") %
|
||||
{'rule': rule, 'target': repr(target)}, extra=extra)
|
@ -1,64 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2014-2015 Hewlett-Packard Development Company, L.P.
|
||||
# Copyright © 2012 eNovance <licensing@enovance.com>
|
||||
#
|
||||
# Author: Julien Danjou <julien@danjou.info>
|
||||
#
|
||||
# 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 socket
|
||||
import sys
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
|
||||
from cue.common import config
|
||||
|
||||
|
||||
service_opts = [
|
||||
cfg.IntOpt('periodic_interval',
|
||||
default=60,
|
||||
help='Seconds between running periodic tasks.'),
|
||||
cfg.StrOpt('host',
|
||||
default=socket.getfqdn(),
|
||||
help='Name of this node. This can be an opaque identifier. '
|
||||
'It is not necessarily a hostname, FQDN, or IP address. '
|
||||
'However, the node name must be valid within '
|
||||
'an AMQP key, and if using ZeroMQ, a valid '
|
||||
'hostname, FQDN, or IP address.'),
|
||||
]
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
CONF.register_opts(service_opts)
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
def prepare_service(argv=None):
|
||||
|
||||
log_levels = (CONF.default_log_levels +
|
||||
['stevedore=INFO', 'keystoneclient=INFO'])
|
||||
log.set_defaults(default_log_levels=log_levels)
|
||||
if argv is None:
|
||||
argv = sys.argv
|
||||
CONF(argv[1:], project='cue')
|
||||
log.setup(CONF, 'cue')
|
||||
config.set_defaults()
|
||||
|
||||
|
||||
def list_opts():
|
||||
return [('DEFAULT', service_opts)]
|
@ -1,46 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2015 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.
|
||||
import os
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def find_config(config_path):
|
||||
|
||||
"""Find a configuration file using the given hint.
|
||||
|
||||
:param config_path: Full or relative path to the config.
|
||||
:returns: List of config paths
|
||||
"""
|
||||
possible_locations = [
|
||||
config_path,
|
||||
os.path.join(cfg.CONF.pybasedir, "etc", "cue", config_path),
|
||||
os.path.join(cfg.CONF.pybasedir, "etc", config_path),
|
||||
os.path.join(cfg.CONF.pybasedir, config_path),
|
||||
"/etc/cue/%s" % config_path,
|
||||
]
|
||||
|
||||
found_locations = []
|
||||
|
||||
for path in possible_locations:
|
||||
LOG.debug('Searching for configuration at path: %s' % path)
|
||||
if os.path.exists(path):
|
||||
LOG.debug('Found configuration at path: %s' % path)
|
||||
found_locations.append(os.path.abspath(path))
|
||||
|
||||
return found_locations
|
@ -1,76 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2015 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.
|
||||
|
||||
from cue.common.i18n import _LI # noqa
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = cfg.CONF
|
||||
|
||||
MIN_USERNAME_LENGTH = 1
|
||||
MAX_USERNAME_LENGTH = 255
|
||||
MIN_PASSWORD_LENGTH = 1
|
||||
MAX_PASSWORD_LENGTH = 255
|
||||
PLAIN_AUTH = "PLAIN"
|
||||
|
||||
|
||||
class AuthTokenValidator(object):
|
||||
|
||||
@staticmethod
|
||||
def validate_token(auth_type, token):
|
||||
auth_validator = None
|
||||
|
||||
if auth_type and auth_type.upper() == PLAIN_AUTH:
|
||||
auth_validator = PlainAuthTokenValidator(token=token)
|
||||
elif not auth_type:
|
||||
return AuthTokenValidator()
|
||||
else:
|
||||
LOG.info(_LI('Invalid authentication type: %s') % auth_type)
|
||||
|
||||
return auth_validator
|
||||
|
||||
def validate(self):
|
||||
return True
|
||||
|
||||
|
||||
class PlainAuthTokenValidator(AuthTokenValidator):
|
||||
def __init__(self, token):
|
||||
self.token = token
|
||||
|
||||
def validate(self):
|
||||
valid_username = False
|
||||
valid_password = False
|
||||
|
||||
if self.token:
|
||||
if 'username' in self.token:
|
||||
if (self.token['username'] and
|
||||
(len(self.token['username']) >= MIN_USERNAME_LENGTH) and
|
||||
(len(self.token['username']) <= MAX_USERNAME_LENGTH)):
|
||||
valid_username = True
|
||||
else:
|
||||
LOG.info(_LI('Invalid username: %s')
|
||||
% self.token['username'])
|
||||
|
||||
if 'password' in self.token:
|
||||
if (self.token['password'] and
|
||||
(len(self.token['password']) >= MIN_PASSWORD_LENGTH) and
|
||||
(len(self.token['password']) <= MAX_PASSWORD_LENGTH)):
|
||||
valid_password = True
|
||||
else:
|
||||
LOG.info(_LI('Invalid password'))
|
||||
|
||||
return valid_username and valid_password
|
263
cue/db/api.py
263
cue/db/api.py
@ -1,263 +0,0 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Authors: Davide Agnello <davide.agnello@hp.com>
|
||||
#
|
||||
# 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.
|
||||
# Copyright [2014] Hewlett-Packard Development Company, L.P.
|
||||
# limitations under the License.
|
||||
"""
|
||||
Base classes for storage engines
|
||||
"""
|
||||
|
||||
import abc
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_db import api as db_api
|
||||
import six
|
||||
|
||||
_BACKEND_MAPPING = {'sqlalchemy': 'cue.db.sqlalchemy.api'}
|
||||
IMPL = db_api.DBAPI.from_config(cfg.CONF, backend_mapping=_BACKEND_MAPPING,
|
||||
lazy=True)
|
||||
|
||||
|
||||
def get_instance():
|
||||
"""Return a DB API instance."""
|
||||
return IMPL
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class Connection(object):
|
||||
"""Base class for storage system connections."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def __init__(self):
|
||||
"""Constructor."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_clusters(self, context, *args, **kwargs):
|
||||
"""Returns a list of Cluster objects for specified project_id.
|
||||
|
||||
:param context: request context object
|
||||
:returns: a list of :class:'Cluster' object
|
||||
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_cluster(self, context, cluster_values):
|
||||
"""Creates a new cluster.
|
||||
|
||||
:param context: request context object
|
||||
:param cluster_values: Dictionary of several required items
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
'network_id': obj_utils.str_or_none,
|
||||
'project_id': obj_utils.str_or_none,
|
||||
'name': obj_utils.str_or_none,
|
||||
'flavor': obj_utils.str_or_none,
|
||||
'size': obj_utils.int_or_none,
|
||||
'volume_size': obj_utils.int_or_none,
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_cluster(self, context, cluster_values, cluster_id):
|
||||
"""Updates values in a cluster record indicated by cluster_id
|
||||
|
||||
:param context: request context object
|
||||
:param cluster_values: Dictionary of cluster values to update
|
||||
:param cluster_id: UUID of a cluster
|
||||
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_cluster_by_id(self, context, cluster_id):
|
||||
"""Returns a Cluster objects for specified cluster_id.
|
||||
|
||||
:param context: request context object
|
||||
:param cluster_id: UUID of a cluster
|
||||
:returns: a :class:'Cluster' object
|
||||
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_nodes_in_cluster(self, context, cluster_id):
|
||||
"""Returns a list of Node objects for specified cluster.
|
||||
|
||||
:param context: request context object
|
||||
:param cluster_id: UUID of the cluster
|
||||
:returns: a list of :class:'Node' object
|
||||
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_node_by_id(self, context, node_id):
|
||||
"""Returns a node for the specified node_id.
|
||||
|
||||
:param context: request context object
|
||||
:param node_id: UUID of the node
|
||||
:returns: a :class:'Node' object
|
||||
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_node(self, context, node_values, node_id):
|
||||
"""Updates values in a node record indicated by node_id
|
||||
|
||||
:param context: request context object
|
||||
:param node_values: Dictionary of node values to update
|
||||
:param node_id:
|
||||
:return:
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_endpoints_in_node(self, context, node_id):
|
||||
"""Returns a list of Endpoint objects for specified node.
|
||||
|
||||
:param context: request context object
|
||||
:param node_id: UUID of the node
|
||||
:returns: a list of :class:'Endpoint' object
|
||||
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_endpoint(self, context, endpoint_values):
|
||||
"""Creates a new endpoint.
|
||||
|
||||
:param context: request context object
|
||||
:param endpoint_values: Dictionary of several required items
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
'id': obj_utils.str_or_none,
|
||||
'node_id': obj_utils.str_or_none,
|
||||
'uri': obj_utils.str_or_none,
|
||||
'type': obj_utils.str_or_none,
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_endpoint_by_id(self, context, endpoint_id):
|
||||
"""Returns an endpoint for the specified endpoint_id.
|
||||
|
||||
:param context: request context object
|
||||
:param endpoint_id: UUID of the endpoint
|
||||
:returns: a :class:'Endpoint' object
|
||||
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_endpoints_by_node_id(self, context, endpoint_values, node_id):
|
||||
"""Updates values in all endpoints belonging to a specific node
|
||||
|
||||
:param context: request context object
|
||||
:param endpoint_values: Dictionary of endpoint values to update
|
||||
:param node_id: node id to query endpoints by
|
||||
:return:
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_cluster_deleting(self, context, cluster_id):
|
||||
"""Marks specified cluster to indicate deletion.
|
||||
|
||||
:param context: request context object
|
||||
:param cluster_id: UUID of a cluster
|
||||
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_broker(self, context, broker_values):
|
||||
"""Creates a new broker.
|
||||
|
||||
:param context: request context object
|
||||
:param broker_values: Dictionary of several required items
|
||||
::
|
||||
|
||||
{
|
||||
'type': obj_utils.str_or_none,
|
||||
'active_status': obj_utils.bool_or_none
|
||||
}
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_brokers(self, context):
|
||||
"""Returns a list of Broker objects.
|
||||
|
||||
:param context: request context object
|
||||
:returns: a list of :class:'Broker' object
|
||||
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def delete_broker(self, context, broker_id):
|
||||
"""Deletes a Broker object for specified broker_id.
|
||||
|
||||
:param context: request context object
|
||||
:param broker_id: UUID of a broker
|
||||
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_broker(self, context, broker_id, broker_value):
|
||||
"""Updates a Broker type/status for specified broker_id.
|
||||
|
||||
:param context: request context object
|
||||
:param broker_id: UUID of a broker
|
||||
:param broker_value: Dictionary of attribute values to be updated
|
||||
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def create_broker_metadata(self, context, metadata_values):
|
||||
"""Creates a new broker metadata.
|
||||
|
||||
:param context: request context object
|
||||
:param metadata_values: Dictionary of several required items
|
||||
::
|
||||
|
||||
{
|
||||
'broker_id': UUID of a broker,
|
||||
'key': obj_utils.str_or_none,
|
||||
'value': obj_utils.str_or_none
|
||||
}
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_broker_metadata_by_broker_id(self, context, broker_id):
|
||||
"""Returns a list of BrokerMetadata objects for specified broker_id.
|
||||
|
||||
:param context: request context object
|
||||
:param broker_id: UUID of a broker
|
||||
:returns: a list of :class:'BrokerMetadata' object
|
||||
|
||||
"""
|
||||
@abc.abstractmethod
|
||||
def delete_broker_metadata(self, context, broker_metadata_id):
|
||||
"""Deletes a BrokerMetadata object for specified broker_id.
|
||||
|
||||
:param context: request context object
|
||||
:param broker_metadata_id: UUID of a broker metadata
|
||||
|
||||
"""
|
||||
@abc.abstractmethod
|
||||
def get_image_id_by_broker_name(self, context, broker_name):
|
||||
"""Returns a image_id for the broker
|
||||
|
||||
:param context: request context object
|
||||
:param: broker name
|
||||
|
||||
"""
|
@ -1,52 +0,0 @@
|
||||
# A generic, single database configuration.
|
||||
|
||||
[alembic]
|
||||
# path to migration scripts
|
||||
script_location = %(here)s/alembic
|
||||
|
||||
# template used to generate migration files
|
||||
# file_template = %%(rev)s_%%(slug)s
|
||||
|
||||
# set to 'true' to run the environment during
|
||||
# the 'revision' command, regardless of autogenerate
|
||||
# revision_environment = false
|
||||
|
||||
# default to an empty string because the Neutron migration cli will
|
||||
# extract the correct value and set it programatically before alemic is fully
|
||||
# invoked.
|
||||
sqlalchemy.url =
|
||||
|
||||
# Logging configuration
|
||||
[loggers]
|
||||
keys = root,sqlalchemy,alembic
|
||||
|
||||
[handlers]
|
||||
keys = console
|
||||
|
||||
[formatters]
|
||||
keys = generic
|
||||
|
||||
[logger_root]
|
||||
level = WARN
|
||||
handlers = console
|
||||
qualname =
|
||||
|
||||
[logger_sqlalchemy]
|
||||
level = WARN
|
||||
handlers =
|
||||
qualname = sqlalchemy.engine
|
||||
|
||||
[logger_alembic]
|
||||
level = INFO
|
||||
handlers =
|
||||
qualname = alembic
|
||||
|
||||
[handler_console]
|
||||
class = StreamHandler
|
||||
args = (sys.stderr,)
|
||||
level = NOTSET
|
||||
formatter = generic
|
||||
|
||||
[formatter_generic]
|
||||
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||
datefmt = %H:%M:%S
|
@ -1,16 +0,0 @@
|
||||
The migrations in the alembic/versions contain the migrations.
|
||||
|
||||
Before running this migration ensure that the database cue exists.
|
||||
|
||||
Currently the database connection string is in cue/db/migration/alembic.ini
|
||||
but this should eventually be pulled out into an cue configuration file.
|
||||
Set connection string is set by the line:
|
||||
sqlalchemy.url = mysql://<user>:<password>@localhost/<database>
|
||||
|
||||
To run migrations you must first be in the cue/db/migrate directory.
|
||||
|
||||
To migrate to the most current version run:
|
||||
$ alembic upgrade head
|
||||
|
||||
To downgrade one migration run:
|
||||
$ alembic downgrade -1
|
@ -1,73 +0,0 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@hp.com>
|
||||
#
|
||||
# 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 cue.db.sqlalchemy import api
|
||||
from cue.db.sqlalchemy import base
|
||||
|
||||
from alembic import context
|
||||
|
||||
from logging import config as log_config
|
||||
|
||||
# this is the Alembic Config object, which provides
|
||||
# access to the values within the .ini file in use.
|
||||
config = context.config
|
||||
|
||||
# Interpret the config file for Python logging.
|
||||
# This line sets up loggers basically.
|
||||
log_config.fileConfig(config.config_file_name)
|
||||
|
||||
# set the target for 'autogenerate' support
|
||||
target_metadata = base.BASE.metadata
|
||||
|
||||
|
||||
def run_migrations_offline():
|
||||
"""Run migrations in 'offline' mode.
|
||||
|
||||
This configures the context with just a URL
|
||||
and not an Engine, though an Engine is acceptable
|
||||
here as well. By skipping the Engine creation
|
||||
we don't even need a DBAPI to be available.
|
||||
|
||||
Calls to context.execute() here emit the given string to the
|
||||
script output.
|
||||
|
||||
"""
|
||||
url = config.get_main_option("sqlalchemy.url")
|
||||
context.configure(url=url)
|
||||
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
def run_migrations_online():
|
||||
"""Run migrations in 'online' mode.
|
||||
|
||||
In this scenario we need to create an Engine
|
||||
and associate a connection with the context.
|
||||
|
||||
"""
|
||||
engine = api.get_session().get_bind()
|
||||
|
||||
with engine.connect() as connection:
|
||||
context.configure(connection=connection,
|
||||
target_metadata=target_metadata)
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
if context.is_offline_mode():
|
||||
run_migrations_offline()
|
||||
else:
|
||||
run_migrations_online()
|
@ -1,22 +0,0 @@
|
||||
"""${message}
|
||||
|
||||
Revision ID: ${up_revision}
|
||||
Revises: ${down_revision}
|
||||
Create Date: ${create_date}
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = ${repr(up_revision)}
|
||||
down_revision = ${repr(down_revision)}
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
${imports if imports else ""}
|
||||
|
||||
def upgrade():
|
||||
${upgrades if upgrades else "pass"}
|
||||
|
||||
|
||||
def downgrade():
|
||||
${downgrades if downgrades else "pass"}
|
@ -1,47 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2015 Hewlett-Packard Development Company, L.P.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""add error_detail and group_id column in clusters
|
||||
|
||||
Revision ID: 17c428e0479e
|
||||
Revises: 244aa473e595
|
||||
Create Date: 2015-11-11 12:01:10.769280
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '17c428e0479e'
|
||||
down_revision = '244aa473e595'
|
||||
|
||||
from cue.db.sqlalchemy import types
|
||||
|
||||
from alembic import op
|
||||
from oslo_config import cfg
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column('clusters', sa.Column('error_detail', sa.Text(),
|
||||
nullable=True))
|
||||
op.add_column('clusters', sa.Column('group_id', types.UUID(),
|
||||
nullable=True))
|
||||
|
||||
|
||||
def downgrade():
|
||||
db_connection = cfg.CONF.database.connection
|
||||
if db_connection != "sqlite://": # pragma: nocover
|
||||
op.drop_column('clusters', 'error_detail')
|
||||
op.drop_column('clusters', 'group_id')
|
@ -1,82 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""Initial schema
|
||||
|
||||
Revision ID: 236f63c96b6a
|
||||
Revises: None
|
||||
Create Date: 2014-11-16 12:24:50.885039
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
from cue.db.sqlalchemy import types
|
||||
|
||||
revision = '236f63c96b6a'
|
||||
down_revision = None
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('clusters',
|
||||
sa.Column('id', types.UUID(), nullable=False),
|
||||
sa.Column('project_id', sa.String(length=36), nullable=False),
|
||||
sa.Column('network_id', sa.String(length=36), nullable=False),
|
||||
sa.Column('name', sa.String(length=255), nullable=False),
|
||||
sa.Column('status', sa.String(length=50), nullable=False),
|
||||
sa.Column('flavor', sa.String(length=50), nullable=False),
|
||||
sa.Column('size', sa.Integer(), nullable=False),
|
||||
sa.Column('volume_size', sa.Integer(), nullable=True),
|
||||
sa.Column('deleted', sa.Boolean(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('deleted_at', sa.DateTime(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('nodes',
|
||||
sa.Column('id', types.UUID(), nullable=False),
|
||||
sa.Column('cluster_id', types.UUID(), nullable=True),
|
||||
sa.Column('flavor', sa.String(length=36), nullable=False),
|
||||
sa.Column('instance_id', sa.String(length=36), nullable=True),
|
||||
sa.Column('status', sa.String(length=50), nullable=False),
|
||||
sa.Column('deleted', sa.Boolean(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('deleted_at', sa.DateTime(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['cluster_id'], ['clusters.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('endpoints',
|
||||
sa.Column('id', types.UUID(), nullable=False),
|
||||
sa.Column('node_id', types.UUID(), nullable=False),
|
||||
sa.Column('type', sa.String(length=255), nullable=False),
|
||||
sa.Column('uri', sa.String(length=255), nullable=False),
|
||||
sa.Column('deleted', sa.Boolean(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['node_id'], ['nodes.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('endpoints')
|
||||
op.drop_table('nodes')
|
||||
op.drop_table('clusters')
|
||||
### end Alembic commands ###
|
@ -1,41 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2015 Hewlett-Packard Development Company, L.P.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""Add management_ip column in node
|
||||
|
||||
Revision ID: 244aa473e595
|
||||
Revises: 3917e931a55a
|
||||
Create Date: 2015-10-01 12:02:57.273927
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '244aa473e595'
|
||||
down_revision = '3917e931a55a'
|
||||
|
||||
from alembic import op
|
||||
from oslo_config import cfg
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.add_column('nodes', sa.Column('management_ip', sa.String(length=45)))
|
||||
|
||||
|
||||
def downgrade():
|
||||
db_connection = cfg.CONF.database.connection
|
||||
if db_connection != "sqlite://": # pragma: nocover
|
||||
op.drop_column('nodes', 'management_ip')
|
@ -1,66 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
"""Create broker and brokerMetadata
|
||||
|
||||
Revision ID: 3917e931a55a
|
||||
Revises: 236f63c96b6a
|
||||
Create Date: 2015-03-30 16:31:57.360063
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '3917e931a55a'
|
||||
down_revision = '236f63c96b6a'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
from cue.db.sqlalchemy import types
|
||||
|
||||
|
||||
def upgrade():
|
||||
### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('broker',
|
||||
sa.Column('id', types.UUID(), nullable=False),
|
||||
sa.Column('name', sa.String(length=255), nullable=False),
|
||||
sa.Column('active', sa.Boolean(), nullable=False),
|
||||
sa.Column('deleted', sa.Boolean(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('deleted_at', sa.DateTime(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('broker_metadata',
|
||||
sa.Column('id', types.UUID(), nullable=False),
|
||||
sa.Column('broker_id', types.UUID(), nullable=False),
|
||||
sa.Column('key', sa.String(length=255), nullable=False),
|
||||
sa.Column('value', sa.String(length=255), nullable=False),
|
||||
sa.Column('deleted', sa.Boolean(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('deleted_at', sa.DateTime(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['broker_id'], ['broker.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('broker_metadata')
|
||||
op.drop_table('broker')
|
||||
### end Alembic commands ###
|
@ -1,320 +0,0 @@
|
||||
# Copyright 2011 VMware, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
# Copied from Neutron
|
||||
import uuid
|
||||
|
||||
from cue.common import exception
|
||||
from cue.common.i18n import _ # noqa
|
||||
from cue.db import api
|
||||
from cue.db.sqlalchemy import models
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_db import exception as db_exception
|
||||
from oslo_db import options as db_options
|
||||
from oslo_db.sqlalchemy import session
|
||||
from oslo_utils import timeutils
|
||||
from sqlalchemy.orm import exc as sql_exception
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
CONF.register_opt(cfg.StrOpt('sqlite_db', default='cue.sqlite'))
|
||||
|
||||
db_options.set_defaults(
|
||||
cfg.CONF, connection='sqlite:///$state_path/$sqlite_db')
|
||||
|
||||
_FACADE = None
|
||||
|
||||
|
||||
def _create_facade_lazily():
|
||||
global _FACADE
|
||||
|
||||
if _FACADE is None:
|
||||
_FACADE = session.EngineFacade.from_config(cfg.CONF, sqlite_fk=True)
|
||||
|
||||
return _FACADE
|
||||
|
||||
|
||||
def get_engine():
|
||||
"""Helper method to grab engine."""
|
||||
facade = _create_facade_lazily()
|
||||
return facade.get_engine()
|
||||
|
||||
|
||||
def get_session(autocommit=True, expire_on_commit=False):
|
||||
"""Helper method to grab session."""
|
||||
facade = _create_facade_lazily()
|
||||
return facade.get_session(autocommit=autocommit,
|
||||
expire_on_commit=expire_on_commit)
|
||||
|
||||
|
||||
def get_backend():
|
||||
"""The backend is this module itself."""
|
||||
return Connection()
|
||||
|
||||
|
||||
def model_query(context, model, *args, **kwargs):
|
||||
"""Query helper for simpler session usage.
|
||||
|
||||
:param session: if present, the session to use
|
||||
"""
|
||||
session = kwargs.get('session') or get_session()
|
||||
query = session.query(model, *args)
|
||||
|
||||
read_deleted = kwargs.get('read_deleted', False)
|
||||
project_only = kwargs.get('project_only', True)
|
||||
|
||||
if not read_deleted:
|
||||
query = query.filter_by(deleted=False)
|
||||
|
||||
if project_only:
|
||||
# filter by project_id
|
||||
if hasattr(model, 'project_id'):
|
||||
query = query.filter_by(project_id=context.project_id)
|
||||
|
||||
return query
|
||||
|
||||
|
||||
def soft_delete(record_values):
|
||||
"""Mark this object as deleted."""
|
||||
record_values['deleted'] = True
|
||||
record_values['deleted_at'] = timeutils.utcnow()
|
||||
|
||||
|
||||
class Connection(api.Connection):
|
||||
"""SqlAlchemy connection implementation."""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def get_clusters(self, context, *args, **kwargs):
|
||||
query = model_query(context, models.Cluster, *args, **kwargs)
|
||||
return query.all()
|
||||
|
||||
def create_cluster(self, context, cluster_values):
|
||||
if not cluster_values.get('id'):
|
||||
cluster_values['id'] = str(uuid.uuid4())
|
||||
|
||||
cluster_values['status'] = models.Status.BUILDING
|
||||
cluster = models.Cluster()
|
||||
cluster.update(cluster_values)
|
||||
|
||||
node_values = {
|
||||
'cluster_id': cluster_values['id'],
|
||||
'flavor': cluster_values['flavor'],
|
||||
'status': models.Status.BUILDING,
|
||||
}
|
||||
|
||||
db_session = get_session()
|
||||
with db_session.begin():
|
||||
cluster.save(db_session)
|
||||
db_session.flush()
|
||||
|
||||
for i in range(cluster_values['size']):
|
||||
node = models.Node()
|
||||
node_id = str(uuid.uuid4())
|
||||
node_values['id'] = node_id
|
||||
node.update(node_values)
|
||||
node.save(db_session)
|
||||
|
||||
return cluster
|
||||
|
||||
def update_cluster(self, context, cluster_values,
|
||||
cluster_id, *args, **kwargs):
|
||||
cluster_query = (model_query(context, models.Cluster, *args, **kwargs)
|
||||
.filter_by(id=cluster_id))
|
||||
|
||||
# if status is set to deleted, soft delete this cluster record
|
||||
if ('status' in cluster_values) and (
|
||||
cluster_values['status'] == models.Status.DELETED):
|
||||
soft_delete(cluster_values)
|
||||
|
||||
cluster_query.update(cluster_values)
|
||||
|
||||
def get_cluster_by_id(self, context, cluster_id):
|
||||
query = model_query(context, models.Cluster).filter_by(id=cluster_id)
|
||||
try:
|
||||
cluster = query.one()
|
||||
except db_exception.DBError:
|
||||
# Todo(dagnello): User input will be validated from REST API and
|
||||
# not from DB transactions.
|
||||
raise exception.Invalid(_("badly formed cluster_id UUID string"))
|
||||
except sql_exception.NoResultFound:
|
||||
raise exception.NotFound(_("Cluster was not found"))
|
||||
|
||||
return cluster
|
||||
|
||||
def get_nodes_in_cluster(self, context, cluster_id):
|
||||
query = (model_query(context, models.Node)
|
||||
.filter_by(cluster_id=cluster_id))
|
||||
# No need to catch user-derived exceptions around not found or badly
|
||||
# formed UUIDs if these happen, they should be classified as internal
|
||||
# server errors since the user is not able to access nodes directly.
|
||||
return query.all()
|
||||
|
||||
def get_node_by_id(self, context, node_id):
|
||||
query = model_query(context, models.Node).filter_by(id=node_id)
|
||||
return query.one()
|
||||
|
||||
def update_node(self, context, node_values, node_id):
|
||||
node_query = (model_query(context, models.Node).filter_by(id=node_id))
|
||||
|
||||
# if status is set to deleted, soft delete this node record
|
||||
if ('status' in node_values) and (
|
||||
node_values['status'] == models.Status.DELETED):
|
||||
soft_delete(node_values)
|
||||
|
||||
node_query.update(node_values)
|
||||
|
||||
def get_endpoints_in_node(self, context, node_id):
|
||||
query = model_query(context, models.Endpoint).filter_by(
|
||||
node_id=node_id)
|
||||
# No need to catch user-derived exceptions for same reason as above
|
||||
return query.all()
|
||||
|
||||
def create_endpoint(self, context, endpoint_values):
|
||||
if not endpoint_values.get('id'):
|
||||
endpoint_values['id'] = str(uuid.uuid4())
|
||||
|
||||
endpoint = models.Endpoint()
|
||||
endpoint.update(endpoint_values)
|
||||
|
||||
db_session = get_session()
|
||||
endpoint.save(db_session)
|
||||
|
||||
return endpoint
|
||||
|
||||
def get_endpoint_by_id(self, context, endpoint_id):
|
||||
query = model_query(context, models.Endpoint).filter_by(id=endpoint_id)
|
||||
return query.one()
|
||||
|
||||
def update_endpoints_by_node_id(self, context, endpoint_values, node_id):
|
||||
endpoints_query = model_query(context, models.Endpoint).filter_by(
|
||||
node_id=node_id)
|
||||
|
||||
# if delete flag is set, we just want to delete these records instead
|
||||
if 'deleted' in endpoint_values and endpoint_values['deleted']:
|
||||
endpoints_query.delete()
|
||||
else:
|
||||
endpoints_query.update(endpoint_values)
|
||||
|
||||
def update_cluster_deleting(self, context, cluster_id):
|
||||
values = {'status': models.Status.DELETING}
|
||||
|
||||
cluster_query = (model_query(context, models.Cluster)
|
||||
.filter_by(id=cluster_id))
|
||||
|
||||
try:
|
||||
cluster_query.one()
|
||||
except db_exception.DBError:
|
||||
# Todo(dagnello): User input will be validated from REST API and
|
||||
# not from DB transactions.
|
||||
raise exception.Invalid(_("badly formed cluster_id UUID string"))
|
||||
except sql_exception.NoResultFound:
|
||||
raise exception.NotFound(_("Cluster was not found"))
|
||||
|
||||
db_session = get_session()
|
||||
|
||||
with db_session.begin():
|
||||
cluster_query.update(values)
|
||||
nodes_query = model_query(context, models.Node).filter_by(
|
||||
cluster_id=cluster_id)
|
||||
nodes_query.update(values)
|
||||
|
||||
def create_broker(self, context, broker_values):
|
||||
|
||||
broker = models.Broker()
|
||||
broker.update(broker_values)
|
||||
|
||||
db_session = get_session()
|
||||
broker.save(db_session)
|
||||
return broker
|
||||
|
||||
def get_brokers(self, context):
|
||||
query = model_query(context, models.Broker)
|
||||
return query.all()
|
||||
|
||||
def delete_broker(self, context, broker_id):
|
||||
|
||||
broker_query = (model_query(context, models.Broker)
|
||||
.filter_by(id=broker_id))
|
||||
|
||||
broker_value = {}
|
||||
soft_delete(broker_value)
|
||||
broker_query.update(broker_value)
|
||||
|
||||
def update_broker(self, context, broker_id, broker_value):
|
||||
|
||||
broker_query = (model_query(context, models.Broker)
|
||||
.filter_by(id=broker_id))
|
||||
|
||||
broker_query.update(broker_value)
|
||||
|
||||
def create_broker_metadata(self, context, metadata_values):
|
||||
|
||||
broker_query = (model_query(context, models.Broker)
|
||||
.filter_by(id=metadata_values['broker_id']))
|
||||
try:
|
||||
# check to see if the broker_id exists
|
||||
broker_query.one()
|
||||
broker_metadata = models.BrokerMetadata()
|
||||
broker_metadata.update(metadata_values)
|
||||
db_session = get_session()
|
||||
broker_metadata.save(db_session)
|
||||
except db_exception.DBError:
|
||||
raise exception.Invalid(_("Badly formed broker_id UUID string"))
|
||||
except sql_exception.NoResultFound:
|
||||
raise exception.NotFound(_("Broker was not found"))
|
||||
|
||||
return broker_metadata
|
||||
|
||||
def get_broker_metadata_by_broker_id(self, context, broker_id):
|
||||
|
||||
query = model_query(context, models.BrokerMetadata).filter_by(
|
||||
broker_id=broker_id)
|
||||
broker_metadata = query.all()
|
||||
|
||||
return broker_metadata
|
||||
|
||||
def delete_broker_metadata(self, context, broker_metadata_id):
|
||||
|
||||
query = (model_query(context, models.BrokerMetadata).filter_by(
|
||||
id=broker_metadata_id))
|
||||
|
||||
broker_value = {}
|
||||
soft_delete(broker_value)
|
||||
query.update(broker_value)
|
||||
|
||||
def get_image_id_by_broker_name(self, context, broker_name):
|
||||
|
||||
broker_query = model_query(context, models.Broker).filter_by(
|
||||
active=True).filter_by(name=broker_name)
|
||||
|
||||
try:
|
||||
selected_broker_id = broker_query.one().id
|
||||
|
||||
# select the recently created image id
|
||||
metadata_query = (model_query(context, models.BrokerMetadata)
|
||||
.filter_by(key='IMAGE')
|
||||
.filter_by(broker_id=selected_broker_id)
|
||||
.order_by((models.BrokerMetadata.created_at.desc(
|
||||
))).limit(1))
|
||||
selected_image = metadata_query.one()
|
||||
|
||||
except sql_exception.NoResultFound:
|
||||
raise exception.NotFound(_("Broker was not found"))
|
||||
|
||||
image_id = selected_image['value']
|
||||
return image_id
|
@ -1,55 +0,0 @@
|
||||
# Copyright 2014 Rackspace
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# Copied from Octavia
|
||||
from cue.db.sqlalchemy import types
|
||||
|
||||
import uuid
|
||||
|
||||
from oslo_db.sqlalchemy import models
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.ext import declarative
|
||||
|
||||
|
||||
class CueBase(models.ModelBase):
|
||||
def as_dict(self):
|
||||
d = {}
|
||||
for c in self.__table__.columns:
|
||||
d[c.name] = self[c.name]
|
||||
return d
|
||||
|
||||
|
||||
class LookupTableMixin(object):
|
||||
"""Mixin to add to classes that are lookup tables."""
|
||||
name = sa.Column(sa.String(255), primary_key=True, nullable=False)
|
||||
description = sa.Column(sa.String(255), nullable=True)
|
||||
|
||||
|
||||
class IdMixin(object):
|
||||
"""Id mixin, add to subclasses that have a tenant."""
|
||||
id = sa.Column(types.UUID(), nullable=False,
|
||||
default=lambda i: str(uuid.uuid4()),
|
||||
primary_key=True)
|
||||
|
||||
|
||||
class ProjectMixin(object):
|
||||
"""Project mixin, add to subclasses that have a project."""
|
||||
project_id = sa.Column(sa.String(36))
|
||||
|
||||
BASE = declarative.declarative_base(cls=CueBase)
|
||||
|
||||
|
||||
class SoftDeleteMixin(object):
|
||||
deleted_at = sa.Column(sa.DateTime)
|
||||
deleted = sa.Column(sa.Boolean, default=0)
|
@ -1,100 +0,0 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@hp.com>
|
||||
#
|
||||
# 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 cue.db.sqlalchemy import base
|
||||
from cue.db.sqlalchemy import types
|
||||
|
||||
from oslo_db.sqlalchemy import models
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
class Status():
|
||||
BUILDING = 'BUILDING'
|
||||
ACTIVE = 'ACTIVE'
|
||||
DELETING = 'DELETING'
|
||||
DELETED = 'DELETED'
|
||||
ERROR = 'ERROR'
|
||||
DOWN = 'DOWN'
|
||||
|
||||
|
||||
class MetadataKey():
|
||||
IMAGE = 'IMAGE'
|
||||
SEC_GROUP = 'SEC_GROUP'
|
||||
|
||||
|
||||
class Endpoint(base.BASE, base.IdMixin):
|
||||
__tablename__ = 'endpoints'
|
||||
|
||||
node_id = sa.Column(types.UUID(), sa.ForeignKey('nodes.id'),
|
||||
primary_key=True)
|
||||
uri = sa.Column(sa.String(255), nullable=False)
|
||||
type = sa.Column(sa.String(length=255), nullable=False)
|
||||
deleted = sa.Column(sa.Boolean(), default=False, nullable=False)
|
||||
sa.Index("endpoints_id_idx", "id", unique=True)
|
||||
sa.Index("endpoints_nodes_id_idx", "node_id", unique=False)
|
||||
|
||||
|
||||
class Node(base.BASE, base.IdMixin, models.TimestampMixin,
|
||||
base.SoftDeleteMixin):
|
||||
__tablename__ = 'nodes'
|
||||
|
||||
cluster_id = sa.Column(
|
||||
'cluster_id', types.UUID(),
|
||||
sa.ForeignKey('clusters.id'), nullable=False)
|
||||
flavor = sa.Column(sa.String(36), nullable=False)
|
||||
instance_id = sa.Column(sa.String(36), nullable=True)
|
||||
status = sa.Column(sa.String(50), nullable=False)
|
||||
management_ip = sa.Column(sa.String(45), nullable=True)
|
||||
sa.Index("nodes_id_idx", "id", unique=True)
|
||||
sa.Index("nodes_cluster_id_idx", "cluster_id", unique=False)
|
||||
|
||||
|
||||
class Cluster(base.BASE, base.IdMixin, models.TimestampMixin,
|
||||
base.SoftDeleteMixin):
|
||||
__tablename__ = 'clusters'
|
||||
|
||||
project_id = sa.Column(sa.String(36), nullable=False)
|
||||
network_id = sa.Column(sa.String(36), nullable=False)
|
||||
name = sa.Column(sa.String(255), nullable=False)
|
||||
status = sa.Column(sa.String(50), nullable=False)
|
||||
flavor = sa.Column(sa.String(50), nullable=False)
|
||||
size = sa.Column(sa.Integer(), default=1, nullable=False)
|
||||
volume_size = sa.Column(sa.Integer(), nullable=True)
|
||||
error_detail = sa.Column(sa.Text(), nullable=True)
|
||||
group_id = sa.Column(types.UUID(), nullable=True)
|
||||
sa.Index("clusters_cluster_id_idx", "cluster_id", unique=True)
|
||||
|
||||
|
||||
class Broker(base.BASE, base.IdMixin, models.TimestampMixin,
|
||||
base.SoftDeleteMixin):
|
||||
__tablename__ = 'broker'
|
||||
|
||||
name = sa.Column(sa.String(255), nullable=False)
|
||||
active = sa.Column(sa.Boolean(), default=False, nullable=False)
|
||||
sa.Index("broker_id_idx", "id", unique=True)
|
||||
|
||||
|
||||
class BrokerMetadata(base.BASE, base.IdMixin, models.TimestampMixin,
|
||||
base.SoftDeleteMixin):
|
||||
__tablename__ = 'broker_metadata'
|
||||
|
||||
broker_id = sa.Column(
|
||||
'broker_id', types.UUID(),
|
||||
sa.ForeignKey('broker.id'), nullable=False)
|
||||
key = sa.Column(sa.String(255), nullable=False)
|
||||
value = sa.Column(sa.String(255), nullable=False)
|
||||
sa.Index("brokerMetadata_id_idx", "id", unique=True)
|
||||
sa.Index("brokerMetadata_broker_id_idx", "broker_id", unique=False)
|
@ -1,54 +0,0 @@
|
||||
# Copyright 2012 Managed I.T.
|
||||
#
|
||||
# Author: Kiall Mac Innes <kiall@managedit.ie>
|
||||
#
|
||||
# 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 uuid
|
||||
|
||||
from sqlalchemy.types import TypeDecorator, CHAR
|
||||
from sqlalchemy.dialects.postgresql import UUID as pgUUID
|
||||
|
||||
|
||||
class UUID(TypeDecorator):
|
||||
"""Platform-independent UUID type.
|
||||
|
||||
Uses Postgresql's UUID type, otherwise uses
|
||||
CHAR(32), storing as stringified hex values.
|
||||
|
||||
Copied verbatim from SQLAlchemy documentation.
|
||||
"""
|
||||
impl = CHAR
|
||||
|
||||
def load_dialect_impl(self, dialect):
|
||||
if dialect.name == 'postgresql':
|
||||
return dialect.type_descriptor(pgUUID())
|
||||
else:
|
||||
return dialect.type_descriptor(CHAR(32))
|
||||
|
||||
def process_bind_param(self, value, dialect):
|
||||
if value is None:
|
||||
return value
|
||||
elif dialect.name == 'postgresql':
|
||||
return str(value)
|
||||
else:
|
||||
if not isinstance(value, uuid.UUID):
|
||||
return "%.32x" % uuid.UUID(value)
|
||||
else:
|
||||
# hexstring
|
||||
return "%.32x" % value
|
||||
|
||||
def process_result_value(self, value, dialect):
|
||||
if value is None:
|
||||
return value
|
||||
else:
|
||||
return str(uuid.UUID(value))
|
@ -1,40 +0,0 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@hp.com>
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# Copied: Designate
|
||||
from cue.common import context
|
||||
|
||||
|
||||
# Decorators for actions
|
||||
def args(*args, **kwargs):
|
||||
def _decorator(func):
|
||||
func.__dict__.setdefault('args', []).insert(0, (args, kwargs))
|
||||
return func
|
||||
return _decorator
|
||||
|
||||
|
||||
def name(name):
|
||||
"""Give a command a alternate name."""
|
||||
def _decorator(func):
|
||||
func.__dict__['_cmd_name'] = name
|
||||
return func
|
||||
return _decorator
|
||||
|
||||
|
||||
class Commands(object):
|
||||
def __init__(self):
|
||||
self.context = context.RequestContext(is_admin=True)
|
||||
self.context.request_id = 'cue-manage'
|
@ -1,152 +0,0 @@
|
||||
# Copyright 2015 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.
|
||||
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import strutils
|
||||
import prettytable
|
||||
|
||||
from cue.common import context as cue_context
|
||||
from cue.common import exception
|
||||
from cue.common.i18n import _ # noqa
|
||||
from cue.db.sqlalchemy import models
|
||||
from cue.manage import base
|
||||
from cue import objects
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class BrokerCommands(base.Commands):
|
||||
"""Broker commands for accessing broker and broker_metadata tables.
|
||||
|
||||
- To add a new broker use 'broker add'.
|
||||
- To add a new metadata for a broker; get broker_id using 'broker list'
|
||||
and use the id in the command 'broker add_metadata'.
|
||||
"""
|
||||
def __init__(self):
|
||||
super(BrokerCommands, self).__init__()
|
||||
self.context = cue_context.RequestContext()
|
||||
|
||||
@base.args('NAME', help="Broker name")
|
||||
@base.args('ACTIVE', help="Broker active status(boolean)")
|
||||
def add(self, broker_name, active):
|
||||
"""Add a new broker."""
|
||||
status = strutils.bool_from_string(active)
|
||||
broker_values = {
|
||||
'name': broker_name,
|
||||
'active': status,
|
||||
}
|
||||
broker = objects.Broker(**broker_values)
|
||||
new_broker = broker.create_broker(self.context)
|
||||
|
||||
new_broker_table = prettytable.PrettyTable(
|
||||
["Broker id", "Broker Name", "Active", "Created Time",
|
||||
"Updated Time", "Deleted Time", ])
|
||||
new_broker_table.add_row(
|
||||
[new_broker.id, new_broker.name, new_broker.active,
|
||||
new_broker.created_at, new_broker.updated_at,
|
||||
new_broker.deleted_at])
|
||||
print(new_broker_table)
|
||||
return new_broker
|
||||
|
||||
def list(self):
|
||||
"""List all the brokers."""
|
||||
broker_list = objects.Broker.get_brokers(self.context)
|
||||
list_table = prettytable.PrettyTable(["Broker id", "Broker Name",
|
||||
"Active", "Created Time",
|
||||
"Updated Time", "Deleted Time",
|
||||
])
|
||||
for broker in broker_list:
|
||||
list_table.add_row([broker.id, broker.name, broker.active,
|
||||
broker.created_at, broker.updated_at,
|
||||
broker.deleted_at])
|
||||
print(list_table)
|
||||
return broker_list
|
||||
|
||||
@base.args('ID', help='Broker id')
|
||||
def delete(self, broker_id):
|
||||
"""Delete a broker."""
|
||||
broker_id = {'id': broker_id}
|
||||
broker_obj = objects.Broker(**broker_id)
|
||||
broker_obj.delete_broker(self.context)
|
||||
|
||||
@base.args('ID', help='Broker id')
|
||||
@base.args('--name', nargs='?', help='Broker name')
|
||||
@base.args('--active', nargs='?', help='Broker active status(boolean)')
|
||||
def update(self, broker_id, broker_name, active):
|
||||
"""Update name/active field or both the fields for a given broker."""
|
||||
broker_value = {}
|
||||
if broker_name is not None:
|
||||
broker_value['name'] = broker_name
|
||||
if active is not None:
|
||||
active = strutils.bool_from_string(active)
|
||||
broker_value['active'] = active
|
||||
|
||||
broker_value['id'] = broker_id
|
||||
broker_obj = objects.Broker(**broker_value)
|
||||
broker_obj.update_broker(self.context)
|
||||
|
||||
@base.args('ID', help='Broker id')
|
||||
@base.args('--image', dest='image_id', nargs='?', help='Image id')
|
||||
@base.args('--sec-group', dest='sec_group', nargs='?',
|
||||
help='Security group')
|
||||
def add_metadata(self, broker_id, image_id, sec_group):
|
||||
"""Add broker metadata - image and sec group for the given broker_id.
|
||||
|
||||
"""
|
||||
if image_id is not None:
|
||||
metadata_value = {
|
||||
'key': models.MetadataKey.IMAGE,
|
||||
'value': image_id,
|
||||
'broker_id': broker_id
|
||||
}
|
||||
metadata = objects.BrokerMetadata(**metadata_value)
|
||||
metadata.create_broker_metadata(self.context)
|
||||
|
||||
if sec_group is not None:
|
||||
metadata_value = {
|
||||
'key': models.MetadataKey.SEC_GROUP,
|
||||
'value': sec_group,
|
||||
'broker_id': broker_id
|
||||
}
|
||||
metadata = objects.BrokerMetadata(**metadata_value)
|
||||
metadata.create_broker_metadata(self.context)
|
||||
|
||||
if image_id is None and sec_group is None:
|
||||
raise exception.Invalid(_("Requires atleast one argument"))
|
||||
|
||||
@base.args('ID', help='Broker id')
|
||||
def list_metadata(self, broker_id):
|
||||
"""List broker metadata for the given broker_id."""
|
||||
broker_metadata = (
|
||||
objects.BrokerMetadata.
|
||||
get_broker_metadata_by_broker_id(self.context, broker_id))
|
||||
list_table = prettytable.PrettyTable(["Broker_Metadata id",
|
||||
"Broker id", "Key", "Value",
|
||||
"Created Time", "Updated Time",
|
||||
"Deleted Time"])
|
||||
|
||||
for broker in broker_metadata:
|
||||
list_table.add_row([broker.id, broker.broker_id, broker.key,
|
||||
broker.value, broker.created_at,
|
||||
broker.updated_at, broker.deleted_at])
|
||||
print(list_table)
|
||||
return broker_metadata
|
||||
|
||||
@base.args('ID', help='Broker metadata id')
|
||||
def delete_metadata(self, broker_metadata_id):
|
||||
"""Delete broker metadata for the given broker_metadata_id."""
|
||||
broker_metadata_id = {'id': broker_metadata_id}
|
||||
broker_obj = objects.BrokerMetadata(**broker_metadata_id)
|
||||
broker_obj.delete_broker_metadata(self.context)
|
@ -1,67 +0,0 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@hp.com>
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# Copied: Designate
|
||||
import os
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_db import options
|
||||
from oslo_db.sqlalchemy.migration_cli import manager as migration_manager
|
||||
|
||||
from cue.manage import base
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
cfg.CONF.import_opt('connection', 'cue.db.api',
|
||||
group='database')
|
||||
|
||||
|
||||
def get_manager():
|
||||
alembic_path = os.path.join(os.path.dirname(__file__),
|
||||
'..', 'db', 'sqlalchemy', 'alembic.ini')
|
||||
migrate_path = os.path.join(os.path.dirname(__file__),
|
||||
'..', 'db', 'sqlalchemy', 'alembic')
|
||||
migration_config = {'alembic_ini_path': alembic_path,
|
||||
'alembic_repo_path': migrate_path,
|
||||
'db_url': CONF.database.connection}
|
||||
return migration_manager.MigrationManager(migration_config)
|
||||
|
||||
|
||||
class DatabaseCommands(base.Commands):
|
||||
def __init__(self):
|
||||
options.set_defaults(CONF)
|
||||
|
||||
def version(self):
|
||||
print("Version %s" % get_manager().version())
|
||||
|
||||
@base.args('revision', nargs='?')
|
||||
def upgrade(self, revision):
|
||||
get_manager().upgrade(revision)
|
||||
|
||||
@base.args('revision', nargs='?')
|
||||
def downgrade(self, revision):
|
||||
get_manager().downgrade(revision)
|
||||
|
||||
@base.args('revision', nargs='?')
|
||||
def stamp(self, revision):
|
||||
get_manager().stamp(revision)
|
||||
|
||||
@base.args('-m', '--message', dest='message')
|
||||
@base.args('--autogenerate', action='store_true')
|
||||
def revision(self, message, autogenerate):
|
||||
get_manager().revision(message=message, autogenerate=autogenerate)
|
@ -1,33 +0,0 @@
|
||||
# Copyright 2015 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Author: Endre Karlson <endre.karlson@hp.com>
|
||||
#
|
||||
# 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 contextlib
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from cue.manage import base
|
||||
from cue.taskflow import client
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class TaskFlowCommands(base.Commands):
|
||||
def __init__(self):
|
||||
super(TaskFlowCommands, self).__init__()
|
||||
|
||||
def upgrade(self):
|
||||
be = client.create_persistence()
|
||||
with contextlib.closing(be.get_connection()) as conn:
|
||||
conn.upgrade()
|
@ -1,37 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2015 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.
|
||||
from oslo_config import cfg
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
MONITOR_OPTS = [
|
||||
cfg.IntOpt('loop_interval_seconds',
|
||||
help='How often Cluster Status is checked.',
|
||||
default=60)
|
||||
]
|
||||
|
||||
opt_group = cfg.OptGroup(
|
||||
name='cue_monitor',
|
||||
title='Options for cue-monitor.'
|
||||
)
|
||||
|
||||
CONF.register_group(opt_group)
|
||||
CONF.register_opts(MONITOR_OPTS, group='cue_monitor')
|
||||
|
||||
|
||||
def list_opts():
|
||||
return [('cue_monitor', MONITOR_OPTS)]
|
@ -1,117 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2015 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.
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_service import loopingcall
|
||||
from oslo_service import service
|
||||
from tooz import coordination
|
||||
|
||||
from cue import objects
|
||||
import cue.taskflow.client as taskflow_client
|
||||
from cue.taskflow.flow import check_cluster_status
|
||||
|
||||
|
||||
class MonitorService(service.Service):
|
||||
|
||||
def __init__(self):
|
||||
super(MonitorService, self).__init__()
|
||||
|
||||
coord_url = ("%s://%s:%s"
|
||||
% (
|
||||
cfg.CONF.taskflow.coord_url,
|
||||
cfg.CONF.taskflow.zk_hosts,
|
||||
cfg.CONF.taskflow.zk_port
|
||||
))
|
||||
|
||||
self.coordinator = coordination.get_coordinator(
|
||||
coord_url, b'cue-monitor')
|
||||
self.coordinator.start()
|
||||
# Create a lock
|
||||
self.lock = self.coordinator.get_lock(b"status_check")
|
||||
|
||||
def start(self):
|
||||
loop_interval_seconds = int(cfg.CONF.cue_monitor.loop_interval_seconds)
|
||||
|
||||
pulse = loopingcall.FixedIntervalLoopingCall(
|
||||
self.check
|
||||
)
|
||||
pulse.start(interval=loop_interval_seconds)
|
||||
pulse.wait()
|
||||
|
||||
# On stop, try to release the znode
|
||||
def stop(self):
|
||||
self.lock.release()
|
||||
self.coordinator.stop()
|
||||
|
||||
def wait(self):
|
||||
pass
|
||||
|
||||
def reset(self):
|
||||
self.lock.release()
|
||||
self.coordinator.stop()
|
||||
|
||||
def check(self):
|
||||
if not self.lock.acquired:
|
||||
self.lock.acquire(blocking=False)
|
||||
|
||||
if self.lock.acquired:
|
||||
|
||||
clusters = get_cluster_id_node_ids()
|
||||
|
||||
taskflow_client_instance = taskflow_client.get_client_instance()
|
||||
job_list = taskflow_client_instance.joblist()
|
||||
|
||||
cluster_ids = []
|
||||
for job in job_list:
|
||||
if 'cluster_status_check' in job.details['store']:
|
||||
cluster_ids.append(job.details['store']['cluster_id'])
|
||||
|
||||
filtered_clusters = []
|
||||
for cluster in clusters:
|
||||
if cluster[0] not in cluster_ids:
|
||||
filtered_clusters.append(cluster)
|
||||
|
||||
for cluster in filtered_clusters:
|
||||
job_args = {
|
||||
'cluster_status_check': '',
|
||||
'cluster_id': cluster[0],
|
||||
'context': {},
|
||||
'default_rabbit_user': 'cue_monitor',
|
||||
'default_rabbit_pass': cluster[0],
|
||||
}
|
||||
flow_kwargs = {
|
||||
'cluster_id': cluster[0],
|
||||
'node_ids': cluster[1]
|
||||
}
|
||||
taskflow_client_instance.post(check_cluster_status, job_args,
|
||||
flow_kwargs=flow_kwargs)
|
||||
|
||||
|
||||
# Returns a list of tuples where [0] is cluster_id
|
||||
# and [1] is a list of that clusters node ids
|
||||
def get_cluster_id_node_ids():
|
||||
|
||||
clusters = objects.Cluster.get_clusters(None, project_only=False)
|
||||
|
||||
cluster_ids = []
|
||||
for cluster in clusters:
|
||||
if cluster.status not in ['ACTIVE', 'DOWN']:
|
||||
continue
|
||||
node_ids = []
|
||||
for node in objects.Node.get_nodes_by_cluster_id(None, cluster.id):
|
||||
node_ids.append(node.id)
|
||||
cluster_ids.append((cluster.id, node_ids))
|
||||
|
||||
return cluster_ids
|
@ -1,36 +0,0 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Authors: Davide Agnello <davide.agnello@hp.com>
|
||||
#
|
||||
# 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.
|
||||
# Copyright [2014] Hewlett-Packard Development Company, L.P.
|
||||
# limitations under the License.
|
||||
|
||||
from cue.objects import broker
|
||||
from cue.objects import broker_metadata
|
||||
from cue.objects import cluster
|
||||
from cue.objects import endpoint
|
||||
from cue.objects import node
|
||||
|
||||
|
||||
Cluster = cluster.Cluster
|
||||
Node = node.Node
|
||||
Endpoint = endpoint.Endpoint
|
||||
Broker = broker.Broker
|
||||
BrokerMetadata = broker_metadata.BrokerMetadata
|
||||
|
||||
__all__ = (Cluster,
|
||||
Endpoint,
|
||||
Node,
|
||||
Broker,
|
||||
BrokerMetadata)
|
@ -1,131 +0,0 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Authors: Davide Agnello <davide.agnello@hp.com>
|
||||
#
|
||||
# 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.
|
||||
# Copyright [2014] Hewlett-Packard Development Company, L.P.
|
||||
# limitations under the License.
|
||||
|
||||
from cue.common.i18n import _LE # noqa
|
||||
from cue.objects import utils as obj_utils
|
||||
|
||||
import collections
|
||||
|
||||
from oslo_log import log as logging
|
||||
import six
|
||||
|
||||
LOG = logging.getLogger('object')
|
||||
|
||||
|
||||
def get_attrname(name):
|
||||
"""Return the mangled name of the attribute's underlying storage."""
|
||||
return '_%s' % name
|
||||
|
||||
|
||||
def make_class_properties(cls):
|
||||
# NOTE(danms/comstud): Inherit fields from super classes.
|
||||
# mro() returns the current class first and returns 'object' last, so
|
||||
# those can be skipped. Also be careful to not overwrite any fields
|
||||
# that already exist. And make sure each cls has its own copy of
|
||||
# fields and that it is not sharing the dict with a super class.
|
||||
cls.fields = dict(cls.fields)
|
||||
for supercls in cls.mro()[1:-1]:
|
||||
if not hasattr(supercls, 'fields'):
|
||||
continue
|
||||
for name, field in supercls.fields.items():
|
||||
if name not in cls.fields:
|
||||
cls.fields[name] = field
|
||||
for name, typefn in cls.fields.items():
|
||||
|
||||
def getter(self, name=name):
|
||||
attrname = get_attrname(name)
|
||||
if not hasattr(self, attrname):
|
||||
self.obj_load_attr(name)
|
||||
return getattr(self, attrname)
|
||||
|
||||
def setter(self, value, name=name, typefn=typefn):
|
||||
self._changed_fields.add(name)
|
||||
try:
|
||||
return setattr(self, get_attrname(name), typefn(value))
|
||||
except Exception:
|
||||
attr = "%s.%s" % (self.obj_name(), name)
|
||||
LOG.exception(_LE('Error setting %(attr)s'),
|
||||
{'attr': attr})
|
||||
raise
|
||||
|
||||
setattr(cls, name, property(getter, setter))
|
||||
|
||||
|
||||
class CueObjectMetaclass(type):
|
||||
"""Metaclass that allows tracking of object classes."""
|
||||
|
||||
# NOTE(danms): This is what controls whether object operations are
|
||||
# remoted. If this is not None, use it to remote things over RPC.
|
||||
indirection_api = None
|
||||
|
||||
def __init__(cls, names, bases, dict_):
|
||||
if not hasattr(cls, '_obj_classes'):
|
||||
# This means this is a base class using the metaclass. I.e.,
|
||||
# the 'CueObject' class.
|
||||
cls._obj_classes = collections.defaultdict(list)
|
||||
|
||||
def _vers_tuple(obj):
|
||||
return tuple([int(x) for x in obj.VERSION.split(".")])
|
||||
|
||||
# Add the subclass to CueObject._obj_classes. If the
|
||||
# same version already exists, replace it. Otherwise,
|
||||
# keep the list with newest version first.
|
||||
make_class_properties(cls)
|
||||
|
||||
|
||||
@six.add_metaclass(CueObjectMetaclass)
|
||||
class CueObject(object):
|
||||
"""Base class for Cue Objects."""
|
||||
|
||||
fields = {
|
||||
'deleted': obj_utils.bool_or_none,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def obj_name(cls):
|
||||
"""Get canonical object name.
|
||||
|
||||
This object name will be used over the wire for remote hydration.
|
||||
|
||||
"""
|
||||
return cls.__name__
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self._changed_fields = set()
|
||||
for key, value in kwargs.items():
|
||||
self[key] = value
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
setattr(self, key, value)
|
||||
|
||||
def __getitem__(self, item):
|
||||
return getattr(self, item)
|
||||
|
||||
def as_dict(self):
|
||||
return dict((k, getattr(self, k))
|
||||
for k in self.fields
|
||||
if hasattr(self, k))
|
||||
|
||||
def obj_get_changes(self):
|
||||
"""Returns dict of changed fields and their new values."""
|
||||
changes = {}
|
||||
|
||||
for key in self._changed_fields:
|
||||
changes[key] = self[key]
|
||||
|
||||
return changes
|
@ -1,78 +0,0 @@
|
||||
# Copyright 2014 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.
|
||||
# Copyright [2014] Hewlett-Packard Development Company, L.P.
|
||||
# limitations under the License.
|
||||
|
||||
from cue.db import api as db_api
|
||||
from cue.objects import base
|
||||
from cue.objects import utils as obj_utils
|
||||
|
||||
|
||||
class Broker(base.CueObject):
|
||||
dbapi = db_api.get_instance()
|
||||
|
||||
fields = {
|
||||
'id': obj_utils.str_or_none,
|
||||
'name': obj_utils.str_or_none,
|
||||
'active': obj_utils.bool_or_none,
|
||||
'deleted': obj_utils.bool_or_none,
|
||||
'created_at': obj_utils.datetime_or_str_or_none,
|
||||
'updated_at': obj_utils.datetime_or_str_or_none,
|
||||
'deleted_at': obj_utils.datetime_or_str_or_none,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _from_db_object(broker, db_broker):
|
||||
"""Convert a database object to a universal broker object."""
|
||||
for field in broker.fields:
|
||||
broker[field] = db_broker[field]
|
||||
return broker
|
||||
|
||||
def create_broker(self, context):
|
||||
"""Creates a new broker.
|
||||
|
||||
:param context: request context object
|
||||
"""
|
||||
broker_values = self.as_dict()
|
||||
db_broker = self.dbapi.create_broker(context, broker_values)
|
||||
|
||||
return self._from_db_object(self, db_broker)
|
||||
|
||||
def delete_broker(self, context):
|
||||
"""Deletes a Broker object for specified broker_id.
|
||||
|
||||
:param context: request context object
|
||||
|
||||
"""
|
||||
self.dbapi.delete_broker(context, self.id)
|
||||
|
||||
def update_broker(self, context):
|
||||
"""Updates a Broker type/status for specified broker_id.
|
||||
|
||||
:param context: request context object
|
||||
|
||||
"""
|
||||
broker_value = self.as_dict()
|
||||
self.dbapi.update_broker(context, self.id, broker_value)
|
||||
|
||||
@classmethod
|
||||
def get_brokers(cls, context):
|
||||
"""Returns a list of Broker objects.
|
||||
|
||||
:param context: request context object
|
||||
:returns: a list of :class:'Broker' object
|
||||
|
||||
"""
|
||||
db_brokers = cls.dbapi.get_brokers(context)
|
||||
return [Broker._from_db_object(Broker(), obj) for obj in db_brokers]
|
@ -1,87 +0,0 @@
|
||||
# Copyright 2014 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.
|
||||
# Copyright [2014] Hewlett-Packard Development Company, L.P.
|
||||
# limitations under the License.
|
||||
|
||||
from cue.db import api as db_api
|
||||
from cue.objects import base
|
||||
from cue.objects import utils as obj_utils
|
||||
|
||||
|
||||
class BrokerMetadata(base.CueObject):
|
||||
dbapi = db_api.get_instance()
|
||||
|
||||
fields = {
|
||||
'id': obj_utils.str_or_none,
|
||||
'broker_id': obj_utils.str_or_none,
|
||||
'key': obj_utils.str_or_none,
|
||||
'value': obj_utils.str_or_none,
|
||||
'deleted': obj_utils.bool_or_none,
|
||||
'created_at': obj_utils.datetime_or_str_or_none,
|
||||
'updated_at': obj_utils.datetime_or_str_or_none,
|
||||
'deleted_at': obj_utils.datetime_or_str_or_none,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _from_db_object(broker_metadata, db_broker_metadata):
|
||||
"""Convert a database object to a universal brokerMetadata object."""
|
||||
for field in BrokerMetadata.fields:
|
||||
broker_metadata[field] = db_broker_metadata[field]
|
||||
return broker_metadata
|
||||
|
||||
def create_broker_metadata(self, context):
|
||||
"""Creates a new broker metadata.
|
||||
|
||||
:param context: request context object
|
||||
|
||||
"""
|
||||
metadata_values = self.as_dict()
|
||||
db_broker = self.dbapi.create_broker_metadata(context, metadata_values)
|
||||
|
||||
self._from_db_object(self, db_broker)
|
||||
|
||||
def delete_broker_metadata(self, context):
|
||||
"""Deletes a BrokerMetadata object for specified broker_id.
|
||||
|
||||
:param context: request context object
|
||||
|
||||
"""
|
||||
self.dbapi.delete_broker_metadata(context, self.id)
|
||||
|
||||
@classmethod
|
||||
def get_broker_metadata_by_broker_id(cls, context, broker_id):
|
||||
"""Returns a list of BrokerMetadata objects for specified broker_id.
|
||||
|
||||
:param context: request context object
|
||||
:param broker_id: broker id
|
||||
:returns: a list of :class:'BrokerMetadata' object
|
||||
|
||||
"""
|
||||
db_broker_metadata = cls.dbapi.get_broker_metadata_by_broker_id(
|
||||
context, broker_id)
|
||||
|
||||
return [BrokerMetadata._from_db_object(BrokerMetadata(), obj)
|
||||
for obj in db_broker_metadata]
|
||||
|
||||
@classmethod
|
||||
def get_image_id_by_broker_name(cls, context, broker_name):
|
||||
"""Returns a image_id for the broker
|
||||
|
||||
:param context: request context object
|
||||
:param: broker name
|
||||
|
||||
"""
|
||||
image_id = cls.dbapi.get_image_id_by_broker_name(context, broker_name)
|
||||
|
||||
return image_id
|
@ -1,106 +0,0 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Authors: Davide Agnello <davide.agnello@hp.com>
|
||||
#
|
||||
# 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.
|
||||
# Copyright [2014] Hewlett-Packard Development Company, L.P.
|
||||
# limitations under the License.
|
||||
|
||||
from cue.db import api as db_api
|
||||
from cue.objects import base
|
||||
from cue.objects import utils as obj_utils
|
||||
|
||||
|
||||
class Cluster(base.CueObject):
|
||||
dbapi = db_api.get_instance()
|
||||
|
||||
fields = {
|
||||
'id': obj_utils.str_or_none,
|
||||
'network_id': obj_utils.str_or_none,
|
||||
'project_id': obj_utils.str_or_none,
|
||||
'name': obj_utils.str_or_none,
|
||||
'status': obj_utils.str_or_none,
|
||||
'flavor': obj_utils.str_or_none,
|
||||
'size': obj_utils.int_or_none,
|
||||
'volume_size': obj_utils.int_or_none,
|
||||
'deleted': obj_utils.bool_or_none,
|
||||
'created_at': obj_utils.datetime_or_str_or_none,
|
||||
'updated_at': obj_utils.datetime_or_str_or_none,
|
||||
'deleted_at': obj_utils.datetime_or_str_or_none,
|
||||
'error_detail': obj_utils.str_or_none,
|
||||
'group_id': obj_utils.str_or_none,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _from_db_object(cluster, db_cluster):
|
||||
"""Convert a database object to a universal cluster object."""
|
||||
for field in cluster.fields:
|
||||
cluster[field] = db_cluster[field]
|
||||
return cluster
|
||||
|
||||
def create(self, context):
|
||||
"""Creates a new cluster.
|
||||
|
||||
:param context: The request context
|
||||
|
||||
"""
|
||||
self['project_id'] = context.project_id
|
||||
cluster_changes = self.obj_get_changes()
|
||||
|
||||
db_cluster = self.dbapi.create_cluster(context, cluster_changes)
|
||||
|
||||
self._from_db_object(self, db_cluster)
|
||||
|
||||
def update(self, context, cluster_id, *args, **kwargs):
|
||||
"""Updates a database cluster object.
|
||||
|
||||
:param context: The request context
|
||||
:param cluster_id:
|
||||
"""
|
||||
cluster_changes = self.obj_get_changes()
|
||||
|
||||
self.dbapi.update_cluster(context, cluster_changes,
|
||||
cluster_id, *args, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def get_clusters(cls, context, *args, **kwargs):
|
||||
"""Returns a list of Cluster objects for project_id.
|
||||
|
||||
:param context: The request context.
|
||||
:returns: a list of :class:'Cluster' object.
|
||||
|
||||
"""
|
||||
db_clusters = cls.dbapi.get_clusters(context, *args, **kwargs)
|
||||
return [Cluster._from_db_object(Cluster(), obj) for obj in db_clusters]
|
||||
|
||||
@classmethod
|
||||
def get_cluster_by_id(cls, context, cluster_id):
|
||||
"""Returns a Cluster objects for specified cluster_id.
|
||||
|
||||
:param context: The request context
|
||||
:param cluster_id: the cluster_id to retrieve
|
||||
:returns: a :class:'Cluster' object
|
||||
|
||||
"""
|
||||
db_cluster = cls.dbapi.get_cluster_by_id(context, cluster_id)
|
||||
return Cluster._from_db_object(Cluster(), db_cluster)
|
||||
|
||||
@classmethod
|
||||
def update_cluster_deleting(cls, context, cluster_id):
|
||||
"""Marks specified cluster to indicate deletion.
|
||||
|
||||
:param context: The request context
|
||||
:param cluster_id: UUID of a cluster
|
||||
|
||||
"""
|
||||
cls.dbapi.update_cluster_deleting(context, cluster_id)
|
@ -1,75 +0,0 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Authors: Davide Agnello <davide.agnello@hp.com>
|
||||
#
|
||||
# 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.
|
||||
# Copyright [2014] Hewlett-Packard Development Company, L.P.
|
||||
# limitations under the License.
|
||||
|
||||
from cue.db import api as db_api
|
||||
from cue.objects import base
|
||||
from cue.objects import utils as obj_utils
|
||||
|
||||
|
||||
class Endpoint(base.CueObject):
|
||||
|
||||
dbapi = db_api.get_instance()
|
||||
|
||||
fields = {
|
||||
'id': obj_utils.str_or_none,
|
||||
'node_id': obj_utils.str_or_none,
|
||||
'uri': obj_utils.str_or_none,
|
||||
'type': obj_utils.str_or_none,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _from_db_object(cluster, db_cluster):
|
||||
"""Convert a database object to a universal endpoint object."""
|
||||
for field in cluster.fields:
|
||||
cluster[field] = db_cluster[field]
|
||||
return cluster
|
||||
|
||||
def create(self, context):
|
||||
"""Creates a new endpoint.
|
||||
|
||||
:param context: The request context
|
||||
|
||||
"""
|
||||
endpoint_changes = self.obj_get_changes()
|
||||
|
||||
db_endpoint = self.dbapi.create_endpoint(context, endpoint_changes)
|
||||
|
||||
self._from_db_object(self, db_endpoint)
|
||||
|
||||
@classmethod
|
||||
def update_by_node_id(cls, context, node_id, endpoint_changes):
|
||||
"""Updates a database endpoint object.
|
||||
|
||||
:param context: The request context
|
||||
:param node_id: The node id
|
||||
:param endpoint_changes: dictionary of endpoint changes
|
||||
"""
|
||||
cls.dbapi.update_endpoints_by_node_id(context, endpoint_changes,
|
||||
node_id)
|
||||
|
||||
@classmethod
|
||||
def get_endpoints_by_node_id(cls, context, node_id):
|
||||
"""Returns a list of Endpoint objects for specified node.
|
||||
|
||||
:param node_id: UUID of the node.
|
||||
:returns: a list of :class:'Endpoint' object.
|
||||
|
||||
"""
|
||||
db_endpoints = cls.dbapi.get_endpoints_in_node(context, node_id)
|
||||
|
||||
return [Endpoint._from_db_object(Endpoint(), obj) for obj in db_endpoints]
|
@ -1,79 +0,0 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Authors: Davide Agnello <davide.agnello@hp.com>
|
||||
#
|
||||
# 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.
|
||||
# Copyright [2014] Hewlett-Packard Development Company, L.P.
|
||||
# limitations under the License.
|
||||
|
||||
from cue.db import api as db_api
|
||||
from cue.objects import base
|
||||
from cue.objects import utils as obj_utils
|
||||
|
||||
|
||||
class Node(base.CueObject):
|
||||
|
||||
dbapi = db_api.get_instance()
|
||||
|
||||
fields = {
|
||||
'id': obj_utils.str_or_none,
|
||||
'cluster_id': obj_utils.str_or_none,
|
||||
'instance_id': obj_utils.str_or_none,
|
||||
'flavor': obj_utils.str_or_none,
|
||||
'management_ip': obj_utils.str_or_none,
|
||||
'status': obj_utils.str_or_none,
|
||||
'created_at': obj_utils.datetime_or_str_or_none,
|
||||
'updated_at': obj_utils.datetime_or_str_or_none,
|
||||
'deleted_at': obj_utils.datetime_or_str_or_none,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _from_db_object(node, db_node):
|
||||
"""Convert a database object to a universal node object."""
|
||||
for field in node.fields:
|
||||
node[field] = db_node[field]
|
||||
return node
|
||||
|
||||
def update(self, context, node_id):
|
||||
"""Updates a database node object.
|
||||
|
||||
:param context: The request context
|
||||
:param node_id:
|
||||
"""
|
||||
node_changes = self.obj_get_changes()
|
||||
|
||||
self.dbapi.update_node(context, node_changes, node_id)
|
||||
|
||||
@classmethod
|
||||
def get_nodes_by_cluster_id(cls, context, cluster_id):
|
||||
"""Returns a list of Node objects for specified cluster.
|
||||
|
||||
:param context: request context object
|
||||
:param cluster_id: UUID of the cluster
|
||||
:returns: a list of :class:'Node' object
|
||||
|
||||
"""
|
||||
db_nodes = cls.dbapi.get_nodes_in_cluster(context, cluster_id)
|
||||
|
||||
return [Node._from_db_object(Node(), obj) for obj in db_nodes]
|
||||
|
||||
@classmethod
|
||||
def get_node_by_id(cls, context, node_id):
|
||||
"""Returns a Node specified by it's id.
|
||||
|
||||
:param context: request context object
|
||||
:param node_id: UUID of a node
|
||||
|
||||
"""
|
||||
db_node = cls.dbapi.get_node_by_id(context, node_id)
|
||||
return Node._from_db_object(Node(), db_node)
|
@ -1,146 +0,0 @@
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Authors: Davide Agnello <davide.agnello@hp.com>
|
||||
#
|
||||
# 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.
|
||||
# Copyright [2014] Hewlett-Packard Development Company, L.P.
|
||||
# limitations under the License.
|
||||
|
||||
"""Utility methods for objects"""
|
||||
|
||||
from cue.common.i18n import _ # noqa
|
||||
|
||||
import ast
|
||||
import datetime
|
||||
|
||||
import iso8601
|
||||
import netaddr
|
||||
from oslo_utils import timeutils
|
||||
import six
|
||||
|
||||
|
||||
def datetime_or_none(dt):
|
||||
"""Validate a datetime or None value."""
|
||||
if dt is None:
|
||||
return None
|
||||
elif isinstance(dt, datetime.datetime):
|
||||
if dt.utcoffset() is None:
|
||||
# NOTE(danms): Legacy objects from sqlalchemy are stored in UTC,
|
||||
# but are returned without a timezone attached.
|
||||
# As a transitional aid, assume a tz-naive object is in UTC.
|
||||
return dt.replace(tzinfo=iso8601.iso8601.Utc())
|
||||
else:
|
||||
return dt
|
||||
raise ValueError(_("A datetime.datetime is required here"))
|
||||
|
||||
|
||||
def datetime_or_str_or_none(val):
|
||||
if isinstance(val, six.string_types):
|
||||
return timeutils.parse_isotime(val)
|
||||
return datetime_or_none(val)
|
||||
|
||||
|
||||
def int_or_none(val):
|
||||
"""Attempt to parse an integer value, or None."""
|
||||
if val is None:
|
||||
return val
|
||||
else:
|
||||
return int(val)
|
||||
|
||||
|
||||
def bool_or_none(val):
|
||||
"""Attempt to parse a boolean value, or None."""
|
||||
if val is None:
|
||||
return val
|
||||
else:
|
||||
return bool(val)
|
||||
|
||||
|
||||
def str_or_none(val):
|
||||
"""Attempt to stringify a value to unicode, or None."""
|
||||
if val is None:
|
||||
return val
|
||||
else:
|
||||
return six.text_type(val)
|
||||
|
||||
|
||||
def dict_or_none(val):
|
||||
"""Attempt to dictify a value, or None."""
|
||||
if val is None:
|
||||
return {}
|
||||
elif isinstance(val, six.string_types):
|
||||
return dict(ast.literal_eval(val))
|
||||
else:
|
||||
try:
|
||||
return dict(val)
|
||||
except ValueError:
|
||||
return {}
|
||||
|
||||
|
||||
def list_or_none(val):
|
||||
"""Attempt to listify a value, or None."""
|
||||
if val is None:
|
||||
return []
|
||||
elif isinstance(val, six.string_types):
|
||||
return list(ast.literal_eval(val))
|
||||
else:
|
||||
try:
|
||||
return list(val)
|
||||
except ValueError:
|
||||
return []
|
||||
|
||||
|
||||
def ip_or_none(version):
|
||||
"""Return a version-specific IP address validator."""
|
||||
def validator(val, version=version):
|
||||
if val is None:
|
||||
return val
|
||||
else:
|
||||
return netaddr.IPAddress(val, version=version)
|
||||
return validator
|
||||
|
||||
|
||||
def nested_object_or_none(objclass):
|
||||
def validator(val, objclass=objclass):
|
||||
if val is None or isinstance(val, objclass):
|
||||
return val
|
||||
raise ValueError(_("An object of class %s is required here")
|
||||
% objclass)
|
||||
return validator
|
||||
|
||||
|
||||
def dt_serializer(name):
|
||||
"""Return a datetime serializer for a named attribute."""
|
||||
def serializer(self, name=name):
|
||||
if getattr(self, name) is not None:
|
||||
return timeutils.isotime(getattr(self, name))
|
||||
else:
|
||||
return None
|
||||
return serializer
|
||||
|
||||
|
||||
def dt_deserializer(instance, val):
|
||||
"""A deserializer method for datetime attributes."""
|
||||
if val is None:
|
||||
return None
|
||||
else:
|
||||
return timeutils.parse_isotime(val)
|
||||
|
||||
|
||||
def obj_serializer(name):
|
||||
def serializer(self, name=name):
|
||||
if getattr(self, name) is not None:
|
||||
return getattr(self, name).obj_to_primitive()
|
||||
else:
|
||||
return None
|
||||
return serializer
|
@ -1,82 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2015 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.
|
||||
from oslo_config import cfg
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
TF_OPTS = [
|
||||
cfg.StrOpt('persistence_connection',
|
||||
help="Persistence connection.",
|
||||
default=None),
|
||||
|
||||
cfg.StrOpt('coord_url',
|
||||
help="Coordinator connection string prefix.",
|
||||
default='zookeeper'),
|
||||
|
||||
cfg.StrOpt('zk_hosts',
|
||||
help="Zookeeper jobboard hosts.",
|
||||
default="localhost"),
|
||||
|
||||
cfg.StrOpt('zk_port',
|
||||
help="Zookeeper jobboard port.",
|
||||
default="2181"),
|
||||
|
||||
cfg.StrOpt('zk_path',
|
||||
help="Zookeeper path for jobs.",
|
||||
default='/cue/taskflow'),
|
||||
|
||||
cfg.IntOpt('zk_timeout',
|
||||
help="Zookeeper operations timeout.",
|
||||
default=10),
|
||||
|
||||
cfg.StrOpt('jobboard_name',
|
||||
help="Board name.",
|
||||
default='cue'),
|
||||
|
||||
cfg.StrOpt('engine_type',
|
||||
help="Engine type.",
|
||||
default='serial'),
|
||||
|
||||
cfg.IntOpt('cluster_node_check_timeout',
|
||||
help="Number of seconds to wait between checks for node status",
|
||||
default=10),
|
||||
|
||||
cfg.IntOpt('cluster_node_check_max_count',
|
||||
help="Number of times to check a node for status before "
|
||||
"declaring it FAULTED",
|
||||
default=30),
|
||||
|
||||
cfg.BoolOpt('cluster_node_anti_affinity',
|
||||
help="Anti-affinity policy for cue cluster nodes",
|
||||
default=False),
|
||||
|
||||
cfg.BoolOpt('cleanup_job_details',
|
||||
help="Cleanup taskflow job details",
|
||||
default=True),
|
||||
]
|
||||
|
||||
opt_group = cfg.OptGroup(
|
||||
name='taskflow',
|
||||
title='Options for taskflow.'
|
||||
)
|
||||
|
||||
CONF.register_group(opt_group)
|
||||
CONF.register_opts(TF_OPTS, group='taskflow')
|
||||
|
||||
|
||||
def list_opts():
|
||||
return [('taskflow', TF_OPTS)]
|
@ -1,296 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2015 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.
|
||||
import contextlib
|
||||
import uuid
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import uuidutils
|
||||
from six.moves import urllib_parse
|
||||
import taskflow.engines as engines
|
||||
import taskflow.jobs.backends as job_backends
|
||||
import taskflow.persistence.backends as persistence_backends
|
||||
import taskflow.persistence.models as persistence_models
|
||||
|
||||
|
||||
def _make_conf(backend_uri):
|
||||
"""A helper function for generating persistence backend configuration.
|
||||
|
||||
This function takes a backend configuration as a URI of the form
|
||||
<backend type>://<backend host>/<path>.
|
||||
|
||||
:param backend_uri: URI for backend connection
|
||||
:return: A configuration dictionary for use with
|
||||
taskflow.persistence.backends
|
||||
"""
|
||||
parsed_url = urllib_parse.urlparse(backend_uri)
|
||||
backend_type = parsed_url.scheme.lower()
|
||||
if not backend_type:
|
||||
raise ValueError("Unknown backend type for uri: %s" % (backend_type))
|
||||
if backend_type in ('file', 'dir'):
|
||||
conf = {
|
||||
'path': parsed_url.path,
|
||||
'connection': backend_uri,
|
||||
}
|
||||
elif backend_type in ('zookeeper',):
|
||||
conf = {
|
||||
'path': parsed_url.path,
|
||||
'hosts': parsed_url.netloc,
|
||||
'connection': backend_uri,
|
||||
}
|
||||
else:
|
||||
conf = {
|
||||
'connection': backend_uri,
|
||||
}
|
||||
return conf
|
||||
|
||||
_task_flow_client = None
|
||||
|
||||
|
||||
def get_client_instance(client_name=None, persistence=None, jobboard=None):
|
||||
"""Create and access a single instance of TaskFlow client
|
||||
|
||||
:param client_name: Name of the client interacting with the jobboard
|
||||
:param persistence: A persistence backend instance to be used in lieu
|
||||
of auto-creating a backend instance based on
|
||||
configuration parameters
|
||||
:param jobboard: A jobboard backend instance to be used in lieu of
|
||||
auto-creating a backend instance based on
|
||||
configuration parameters
|
||||
:return: A :class:`.Client` instance.
|
||||
"""
|
||||
global _task_flow_client
|
||||
|
||||
if _task_flow_client is None:
|
||||
if persistence is None:
|
||||
persistence = create_persistence()
|
||||
if jobboard is None:
|
||||
jobboard = create_jobboard(persistence=persistence)
|
||||
if client_name is None:
|
||||
client_name = "cue_job_client"
|
||||
|
||||
_task_flow_client = Client(client_name,
|
||||
persistence=persistence,
|
||||
jobboard=jobboard)
|
||||
|
||||
return _task_flow_client
|
||||
|
||||
|
||||
def create_persistence(conf=None, **kwargs):
|
||||
"""Factory method for creating a persistence backend instance
|
||||
|
||||
:param conf: Configuration parameters for the persistence backend. If
|
||||
no conf is provided, zookeeper configuration parameters
|
||||
for the job backend will be used to configure the
|
||||
persistence backend.
|
||||
:param kwargs: Keyword arguments to be passed forward to the
|
||||
persistence backend constructor
|
||||
:return: A persistence backend instance.
|
||||
"""
|
||||
if conf is None:
|
||||
connection = cfg.CONF.taskflow.persistence_connection
|
||||
if connection is None:
|
||||
connection = ("zookeeper://%s/%s"
|
||||
% (
|
||||
cfg.CONF.taskflow.zk_hosts,
|
||||
cfg.CONF.taskflow.zk_path,
|
||||
))
|
||||
conf = _make_conf(connection)
|
||||
be = persistence_backends.fetch(conf=conf, **kwargs)
|
||||
with contextlib.closing(be.get_connection()) as conn:
|
||||
conn.upgrade()
|
||||
return be
|
||||
|
||||
|
||||
def create_jobboard(board_name=None, conf=None, persistence=None, **kwargs):
|
||||
"""Factory method for creating a jobboard backend instance
|
||||
|
||||
:param board_name: Name of the jobboard
|
||||
:param conf: Configuration parameters for the jobboard backend.
|
||||
:param persistence: A persistence backend instance to be used with the
|
||||
jobboard.
|
||||
:param kwargs: Keyword arguments to be passed forward to the
|
||||
persistence backend constructor
|
||||
:return: A persistence backend instance.
|
||||
"""
|
||||
if board_name is None:
|
||||
board_name = cfg.CONF.taskflow.jobboard_name
|
||||
|
||||
if conf is None:
|
||||
conf = {'board': 'zookeeper'}
|
||||
|
||||
conf.update({
|
||||
"path": "%s/jobs" % (cfg.CONF.taskflow.zk_path),
|
||||
"hosts": cfg.CONF.taskflow.zk_hosts,
|
||||
"timeout": cfg.CONF.taskflow.zk_timeout
|
||||
})
|
||||
|
||||
jb = job_backends.fetch(
|
||||
name=board_name,
|
||||
conf=conf,
|
||||
persistence=persistence,
|
||||
**kwargs)
|
||||
jb.connect()
|
||||
return jb
|
||||
|
||||
|
||||
class Client(object):
|
||||
"""An abstraction for interacting with Taskflow
|
||||
|
||||
This class provides an abstraction for Taskflow to expose a simpler
|
||||
interface for posting jobs to Taskflow Jobboards than what is provided
|
||||
out of the box with Taskflow.
|
||||
|
||||
TODO(sputnik13): persistence and jobboard should ideally be closed during
|
||||
__del__ but that seems to throw exceptions even though it
|
||||
doesn't seem like it should... this should be
|
||||
investigated further
|
||||
|
||||
:ivar persistence: persistence backend instance
|
||||
:ivar jobboard: jobboard backend instance
|
||||
"""
|
||||
|
||||
def __init__(self, client_name, board_name=None, persistence=None,
|
||||
jobboard=None, **kwargs):
|
||||
"""Constructor for Client class
|
||||
|
||||
:param client_name: Name of the client interacting with the jobboard
|
||||
:param board_name: Name of the jobboard
|
||||
:param persistence: A persistence backend instance to be used in lieu
|
||||
of auto-creating a backend instance based on
|
||||
configuration parameters
|
||||
:param jobboard: A jobboard backend instance to be used in lieu of
|
||||
auto-creating a backend instance based on
|
||||
configuration parameters
|
||||
:param kwargs: Any keyword arguments to be passed forward to
|
||||
persistence and job backend constructors
|
||||
"""
|
||||
super(Client, self).__init__()
|
||||
|
||||
if jobboard is None and board_name is None:
|
||||
raise AttributeError("board_name must be supplied "
|
||||
"if a jobboard is None")
|
||||
|
||||
self._client_name = client_name
|
||||
|
||||
self.persistence = persistence or create_persistence(**kwargs)
|
||||
|
||||
self.jobboard = jobboard or create_jobboard(board_name,
|
||||
None,
|
||||
self.persistence,
|
||||
**kwargs)
|
||||
|
||||
@classmethod
|
||||
def create(cls, client_name, board_name=None, persistence=None,
|
||||
jobboard=None, **kwargs):
|
||||
"""Factory method for creating a Client instance
|
||||
|
||||
:param client_name: Name of the client interacting with the jobboard
|
||||
:param board_name: Name of the jobboard
|
||||
:param persistence: A persistence backend instance to be used in lieu
|
||||
of auto-creating a backend instance based on
|
||||
configuration parameters
|
||||
:param jobboard: A jobboard backend instance to be used in lieu of
|
||||
auto-creating a backend instance based on
|
||||
configuration parameters
|
||||
:param kwargs: Any keyword arguments to be passed forward to
|
||||
persistence and job backend constructors
|
||||
:return: A :class:`.Client` instance.
|
||||
"""
|
||||
return cls(client_name, board_name=board_name, persistence=persistence,
|
||||
jobboard=jobboard, **kwargs)
|
||||
|
||||
def post(self, flow_factory, job_args=None,
|
||||
flow_args=None, flow_kwargs=None, tx_uuid=None):
|
||||
"""Method for posting a new job to the jobboard
|
||||
|
||||
:param flow_factory: Flow factory function for creating a flow instance
|
||||
that will be executed as part of the job.
|
||||
:param job_args: 'store' arguments to be supplied to the engine
|
||||
executing the flow for the job
|
||||
:param flow_args: Positional arguments to be passed to the flow factory
|
||||
function
|
||||
:param flow_kwargs: Keyword arguments to be passed to the flow factory
|
||||
function
|
||||
:param tx_uuid: Transaction UUID which will be injected as 'tx_uuid' in
|
||||
job_args. A tx_uuid will be generated if one is not
|
||||
provided as an argument.
|
||||
:return: A taskflow.job.Job instance that represents the job that was
|
||||
posted.
|
||||
"""
|
||||
if isinstance(job_args, dict) and 'tx_uuid' in job_args:
|
||||
raise AttributeError("tx_uuid needs to be provided as an argument"
|
||||
"to Client.post, not as a member of job_args")
|
||||
|
||||
if tx_uuid is None:
|
||||
tx_uuid = uuidutils.generate_uuid()
|
||||
|
||||
job_name = "%s[%s]" % (flow_factory.__name__, tx_uuid)
|
||||
book = persistence_models.LogBook(job_name, uuid=tx_uuid)
|
||||
|
||||
if flow_factory is not None:
|
||||
flow_detail = persistence_models.FlowDetail(
|
||||
job_name,
|
||||
str(uuid.uuid4())
|
||||
)
|
||||
book.add(flow_detail)
|
||||
|
||||
job_details = {'store': job_args or {}}
|
||||
job_details['store'].update({
|
||||
'tx_uuid': tx_uuid
|
||||
})
|
||||
job_details['flow_uuid'] = flow_detail.uuid
|
||||
|
||||
self.persistence.get_connection().save_logbook(book)
|
||||
|
||||
engines.save_factory_details(
|
||||
flow_detail, flow_factory, flow_args, flow_kwargs,
|
||||
self.persistence)
|
||||
|
||||
job = self.jobboard.post(job_name, book, details=job_details)
|
||||
return job
|
||||
|
||||
def joblist(self, only_unclaimed=False, ensure_fresh=False):
|
||||
"""Method for retrieving a list of jobs in the jobboard
|
||||
|
||||
:param only_unclaimed: Return only unclaimed jobs
|
||||
:param ensure_fresh: Return only the most recent jobs available.
|
||||
Behavior of this parameter is backend specific.
|
||||
:return: A list of jobs in the jobboard
|
||||
"""
|
||||
return list(self.jobboard.iterjobs(only_unclaimed=only_unclaimed,
|
||||
ensure_fresh=ensure_fresh))
|
||||
|
||||
def delete(self, job=None, job_id=None):
|
||||
"""Method for deleting a job from the jobboard.
|
||||
|
||||
Due to constraints in the available taskflow interfaces, deleting by
|
||||
job_id entails retrieving and iterating over the list of all jobs in
|
||||
the jobboard. Thus deleting by job rather than job_id can be faster.
|
||||
|
||||
:param job: A Taskflow.job.Job representing the job to be deleted
|
||||
:param job_id: Unique job_id referencing the job to be deleted
|
||||
:return:
|
||||
"""
|
||||
if (job is None) == (job_id is None):
|
||||
raise AttributeError("exactly one of either job or job_id must "
|
||||
"be supplied")
|
||||
|
||||
if job is None:
|
||||
for j in self.joblist():
|
||||
if j.uuid == job_id:
|
||||
job = j
|
||||
|
||||
self.jobboard.claim(job, self._client_name)
|
||||
self.jobboard.consume(job, self._client_name)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user