mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-23 22:50:09 +03:00
Merge branch 'master' of git.opennebula.org:one
This commit is contained in:
commit
d636981a6b
161
install.sh
161
install.sh
@ -43,6 +43,7 @@ usage() {
|
||||
echo "-s: install OpenNebula Sunstone"
|
||||
echo "-G: install OpenNebula Gate"
|
||||
echo "-o: install OpenNebula Zones (OZones)"
|
||||
echo "-f: install OpenNebula Flow"
|
||||
echo "-r: remove Opennebula, only useful if -d was not specified, otherwise"
|
||||
echo " rm -rf \$ONE_LOCATION would do the job"
|
||||
echo "-l: creates symlinks instead of copying files, useful for development"
|
||||
@ -72,6 +73,7 @@ CLIENT="no"
|
||||
ONEGATE="no"
|
||||
SUNSTONE="no"
|
||||
OZONES="no"
|
||||
ONEFLOW="no"
|
||||
ONEADMIN_USER=`id -u`
|
||||
ONEADMIN_GROUP=`id -g`
|
||||
SRC_DIR=$PWD
|
||||
@ -86,6 +88,7 @@ while true ; do
|
||||
-G) ONEGATE="yes"; shift ;;
|
||||
-s) SUNSTONE="yes"; shift ;;
|
||||
-o) OZONES="yes"; shift ;;
|
||||
-f) ONEFLOW="yes"; shift ;;
|
||||
-u) ONEADMIN_USER="$2" ; shift 2;;
|
||||
-g) ONEADMIN_GROUP="$2"; shift 2;;
|
||||
-d) ROOT="$2" ; shift 2 ;;
|
||||
@ -109,6 +112,7 @@ if [ -z "$ROOT" ] ; then
|
||||
ONEGATE_LOCATION="$LIB_LOCATION/onegate"
|
||||
SUNSTONE_LOCATION="$LIB_LOCATION/sunstone"
|
||||
OZONES_LOCATION="$LIB_LOCATION/ozones"
|
||||
ONEFLOW_LOCATION="$LIB_LOCATION/oneflow"
|
||||
SYSTEM_DS_LOCATION="$VAR_LOCATION/datastores/0"
|
||||
DEFAULT_DS_LOCATION="$VAR_LOCATION/datastores/1"
|
||||
RUN_LOCATION="/var/run/one"
|
||||
@ -144,13 +148,20 @@ if [ -z "$ROOT" ] ; then
|
||||
|
||||
DELETE_DIRS="$MAKE_DIRS"
|
||||
|
||||
CHOWN_DIRS=""
|
||||
elif [ "$ONEFLOW" = "yes" ]; then
|
||||
MAKE_DIRS="$BIN_LOCATION $LIB_LOCATION $VAR_LOCATION $ONEFLOW_LOCATION \
|
||||
$ETC_LOCATION"
|
||||
|
||||
DELETE_DIRS="$MAKE_DIRS"
|
||||
|
||||
CHOWN_DIRS=""
|
||||
else
|
||||
MAKE_DIRS="$BIN_LOCATION $LIB_LOCATION $ETC_LOCATION $VAR_LOCATION \
|
||||
$INCLUDE_LOCATION $SHARE_LOCATION \
|
||||
$LOG_LOCATION $RUN_LOCATION $LOCK_LOCATION \
|
||||
$SYSTEM_DS_LOCATION $DEFAULT_DS_LOCATION $MAN_LOCATION \
|
||||
$VM_LOCATION $ONEGATE_LOCATION"
|
||||
$VM_LOCATION $ONEGATE_LOCATION $ONEFLOW_LOCATION"
|
||||
|
||||
DELETE_DIRS="$LIB_LOCATION $ETC_LOCATION $LOG_LOCATION $VAR_LOCATION \
|
||||
$RUN_LOCATION $SHARE_DIRS"
|
||||
@ -166,6 +177,7 @@ else
|
||||
ONEGATE_LOCATION="$LIB_LOCATION/onegate"
|
||||
SUNSTONE_LOCATION="$LIB_LOCATION/sunstone"
|
||||
OZONES_LOCATION="$LIB_LOCATION/ozones"
|
||||
ONEFLOW_LOCATION="$LIB_LOCATION/oneflow"
|
||||
SYSTEM_DS_LOCATION="$VAR_LOCATION/datastores/0"
|
||||
DEFAULT_DS_LOCATION="$VAR_LOCATION/datastores/1"
|
||||
INCLUDE_LOCATION="$ROOT/include"
|
||||
@ -191,12 +203,17 @@ else
|
||||
MAKE_DIRS="$BIN_LOCATION $LIB_LOCATION $VAR_LOCATION $OZONES_LOCATION \
|
||||
$ETC_LOCATION"
|
||||
|
||||
DELETE_DIRS="$MAKE_DIRS"
|
||||
elif [ "$ONEFLOW" = "yes" ]; then
|
||||
MAKE_DIRS="$BIN_LOCATION $LIB_LOCATION $VAR_LOCATION $ONEFLOW_LOCATION \
|
||||
$ETC_LOCATION"
|
||||
|
||||
DELETE_DIRS="$MAKE_DIRS"
|
||||
else
|
||||
MAKE_DIRS="$BIN_LOCATION $LIB_LOCATION $ETC_LOCATION $VAR_LOCATION \
|
||||
$INCLUDE_LOCATION $SHARE_LOCATION $SYSTEM_DS_LOCATION \
|
||||
$DEFAULT_DS_LOCATION $MAN_LOCATION $OZONES_LOCATION \
|
||||
$VM_LOCATION $ONEGATE_LOCATION"
|
||||
$VM_LOCATION $ONEGATE_LOCATION $ONEFLOW_LOCATION"
|
||||
|
||||
DELETE_DIRS="$MAKE_DIRS"
|
||||
|
||||
@ -366,6 +383,10 @@ OZONES_CLIENT_DIRS="$LIB_LOCATION/ruby \
|
||||
$LIB_LOCATION/ruby/cli/ozones_helper \
|
||||
$LIB_LOCATION/ruby/zona"
|
||||
|
||||
ONEFLOW_DIRS="$ONEFLOW_LOCATION/lib \
|
||||
$ONEFLOW_LOCATION/lib/strategy \
|
||||
$ONEFLOW_LOCATION/lib/models"
|
||||
|
||||
LIB_ECO_CLIENT_DIRS="$LIB_LOCATION/ruby \
|
||||
$LIB_LOCATION/ruby/opennebula \
|
||||
$LIB_LOCATION/ruby/cloud/ \
|
||||
@ -397,9 +418,11 @@ elif [ "$SUNSTONE" = "yes" ]; then
|
||||
MAKE_DIRS="$MAKE_DIRS $SUNSTONE_DIRS $LIB_OCA_CLIENT_DIRS"
|
||||
elif [ "$OZONES" = "yes" ]; then
|
||||
MAKE_DIRS="$MAKE_DIRS $OZONES_DIRS $OZONES_CLIENT_DIRS $LIB_OCA_CLIENT_DIRS"
|
||||
elif [ "$ONEFLOW" = "yes" ]; then
|
||||
MAKE_DIRS="$MAKE_DIRS $ONEFLOW_DIRS $LIB_OCA_CLIENT_DIRS"
|
||||
else
|
||||
MAKE_DIRS="$MAKE_DIRS $SHARE_DIRS $ETC_DIRS $LIB_DIRS $VAR_DIRS \
|
||||
$OZONES_DIRS $OZONES_CLIENT_DIRS $SUNSTONE_DIRS"
|
||||
$OZONES_DIRS $OZONES_CLIENT_DIRS $SUNSTONE_DIRS $ONEFLOW_DIRS"
|
||||
fi
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
@ -522,6 +545,7 @@ INSTALL_SUNSTONE_FILES=(
|
||||
SUNSTONE_VIEWS_FILES:$SUNSTONE_LOCATION/views
|
||||
SUNSTONE_PUBLIC_JS_FILES:$SUNSTONE_LOCATION/public/js
|
||||
SUNSTONE_PUBLIC_JS_PLUGINS_FILES:$SUNSTONE_LOCATION/public/js/plugins
|
||||
SUNSTONE_ROUTES_FILES:$SUNSTONE_LOCATION/routes
|
||||
SUNSTONE_PUBLIC_CSS_FILES:$SUNSTONE_LOCATION/public/css
|
||||
SUNSTONE_PUBLIC_VENDOR_CRYPTOJS:$SUNSTONE_LOCATION/public/vendor/crypto-js
|
||||
SUNSTONE_PUBLIC_VENDOR_EXPLORERCANVAS:$SUNSTONE_LOCATION/public/vendor/explorercanvas
|
||||
@ -614,6 +638,17 @@ INSTALL_OZONES_ETC_FILES=(
|
||||
OZONES_ETC_FILES:$ETC_LOCATION
|
||||
)
|
||||
|
||||
INSTALL_ONEFLOW_FILES=(
|
||||
ONEFLOW_FILES:$ONEFLOW_LOCATION
|
||||
ONEFLOW_BIN_FILES:$BIN_LOCATION
|
||||
ONEFLOW_LIB_FILES:$ONEFLOW_LOCATION/lib
|
||||
ONEFLOW_LIB_STRATEGY_FILES:$ONEFLOW_LOCATION/lib/strategy
|
||||
ONEFLOW_LIB_MODELS_FILES:$ONEFLOW_LOCATION/lib/models
|
||||
)
|
||||
|
||||
INSTALL_ONEFLOW_ETC_FILES=(
|
||||
ONEFLOW_ETC_FILES:$ETC_LOCATION
|
||||
)
|
||||
|
||||
INSTALL_ETC_FILES=(
|
||||
ETC_FILES:$ETC_LOCATION
|
||||
@ -647,6 +682,8 @@ BIN_FILES="src/nebula/oned \
|
||||
src/cli/oneacl \
|
||||
src/cli/onedatastore \
|
||||
src/cli/onecluster \
|
||||
src/cli/oneflow \
|
||||
src/cli/oneflow-template \
|
||||
src/onedb/onedb \
|
||||
src/onedb/onezonedb/onezonedb \
|
||||
src/mad/utils/tty_expect \
|
||||
@ -1182,38 +1219,39 @@ INSTALL_GEMS_SHARE_FILE="share/install_gems/install_gems"
|
||||
#-------------------------------------------------------------------------------
|
||||
OCA_LIB_FILES="src/oca/ruby/opennebula.rb"
|
||||
|
||||
RUBY_OPENNEBULA_LIB_FILES="src/oca/ruby/opennebula/host.rb \
|
||||
src/oca/ruby/opennebula/host_pool.rb \
|
||||
src/oca/ruby/opennebula/pool.rb \
|
||||
src/oca/ruby/opennebula/user.rb \
|
||||
src/oca/ruby/opennebula/user_pool.rb \
|
||||
src/oca/ruby/opennebula/virtual_machine.rb \
|
||||
src/oca/ruby/opennebula/virtual_machine_pool.rb \
|
||||
src/oca/ruby/opennebula/virtual_network.rb \
|
||||
src/oca/ruby/opennebula/virtual_network_pool.rb \
|
||||
src/oca/ruby/opennebula/image.rb \
|
||||
src/oca/ruby/opennebula/image_pool.rb \
|
||||
src/oca/ruby/opennebula/template.rb \
|
||||
src/oca/ruby/opennebula/template_pool.rb \
|
||||
src/oca/ruby/opennebula/document.rb \
|
||||
src/oca/ruby/opennebula/document_pool.rb \
|
||||
src/oca/ruby/opennebula/document_json.rb \
|
||||
src/oca/ruby/opennebula/document_pool_json.rb \
|
||||
src/oca/ruby/opennebula/group.rb \
|
||||
src/oca/ruby/opennebula/group_pool.rb \
|
||||
src/oca/ruby/opennebula/acl.rb \
|
||||
src/oca/ruby/opennebula/acl_pool.rb \
|
||||
src/oca/ruby/opennebula/datastore.rb \
|
||||
src/oca/ruby/opennebula/datastore_pool.rb \
|
||||
src/oca/ruby/opennebula/cluster.rb \
|
||||
src/oca/ruby/opennebula/cluster_pool.rb \
|
||||
src/oca/ruby/opennebula/xml_utils.rb \
|
||||
src/oca/ruby/opennebula/client.rb \
|
||||
src/oca/ruby/opennebula/error.rb \
|
||||
src/oca/ruby/opennebula/pool_element.rb \
|
||||
src/oca/ruby/opennebula/xml_element.rb \
|
||||
src/oca/ruby/opennebula/xml_pool.rb \
|
||||
src/oca/ruby/opennebula/system.rb"
|
||||
RUBY_OPENNEBULA_LIB_FILES="src/oca/ruby/opennebula/acl_pool.rb \
|
||||
src/oca/ruby/opennebula/acl.rb \
|
||||
src/oca/ruby/opennebula/client.rb \
|
||||
src/oca/ruby/opennebula/cluster_pool.rb \
|
||||
src/oca/ruby/opennebula/cluster.rb \
|
||||
src/oca/ruby/opennebula/datastore_pool.rb \
|
||||
src/oca/ruby/opennebula/datastore.rb \
|
||||
src/oca/ruby/opennebula/document_json.rb \
|
||||
src/oca/ruby/opennebula/document_pool_json.rb \
|
||||
src/oca/ruby/opennebula/document_pool.rb \
|
||||
src/oca/ruby/opennebula/document.rb \
|
||||
src/oca/ruby/opennebula/error.rb \
|
||||
src/oca/ruby/opennebula/group_pool.rb \
|
||||
src/oca/ruby/opennebula/group.rb \
|
||||
src/oca/ruby/opennebula/host_pool.rb \
|
||||
src/oca/ruby/opennebula/host.rb \
|
||||
src/oca/ruby/opennebula/image_pool.rb \
|
||||
src/oca/ruby/opennebula/image.rb \
|
||||
src/oca/ruby/opennebula/pool_element.rb \
|
||||
src/oca/ruby/opennebula/pool.rb \
|
||||
src/oca/ruby/opennebula/system.rb \
|
||||
src/oca/ruby/opennebula/template_pool.rb \
|
||||
src/oca/ruby/opennebula/template.rb \
|
||||
src/oca/ruby/opennebula/user_pool.rb \
|
||||
src/oca/ruby/opennebula/user.rb \
|
||||
src/oca/ruby/opennebula/virtual_machine_pool.rb \
|
||||
src/oca/ruby/opennebula/virtual_machine.rb \
|
||||
src/oca/ruby/opennebula/virtual_network_pool.rb \
|
||||
src/oca/ruby/opennebula/virtual_network.rb \
|
||||
src/oca/ruby/opennebula/xml_element.rb \
|
||||
src/oca/ruby/opennebula/xml_pool.rb \
|
||||
src/oca/ruby/opennebula/xml_utils.rb \
|
||||
src/oca/ruby/opennebula/oneflow_client.rb"
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Common Cloud Files
|
||||
@ -1405,6 +1443,8 @@ CLI_BIN_FILES="src/cli/onevm \
|
||||
src/cli/oneacl \
|
||||
src/cli/onedatastore \
|
||||
src/cli/onecluster \
|
||||
src/cli/oneflow \
|
||||
src/cli/oneflow-template \
|
||||
src/cli/oneacct"
|
||||
|
||||
CLI_CONF_FILES="src/cli/etc/onegroup.yaml \
|
||||
@ -1432,7 +1472,8 @@ SUNSTONE_BIN_FILES="src/sunstone/bin/sunstone-server \
|
||||
src/sunstone/bin/novnc-server"
|
||||
|
||||
SUNSTONE_ETC_FILES="src/sunstone/etc/sunstone-server.conf \
|
||||
src/sunstone/etc/sunstone-views.yaml"
|
||||
src/sunstone/etc/sunstone-views.yaml \
|
||||
src/sunstone/etc/sunstone-oneflow.conf"
|
||||
|
||||
SUNSTONE_ETC_VIEW_FILES="src/sunstone/etc/sunstone-views/admin.yaml \
|
||||
src/sunstone/etc/sunstone-views/user.yaml"
|
||||
@ -1485,7 +1526,12 @@ SUNSTONE_PUBLIC_JS_PLUGINS_FILES="\
|
||||
src/sunstone/public/js/plugins/acls-tab.js \
|
||||
src/sunstone/public/js/plugins/vnets-tab.js \
|
||||
src/sunstone/public/js/plugins/marketplace-tab.js \
|
||||
src/sunstone/public/js/plugins/config-tab.js"
|
||||
src/sunstone/public/js/plugins/config-tab.js \
|
||||
src/sunstone/public/js/plugins/oneflow-dashboard.js \
|
||||
src/sunstone/public/js/plugins/oneflow-services.js \
|
||||
src/sunstone/public/js/plugins/oneflow-templates.js"
|
||||
|
||||
SUNSTONE_ROUTES_FILES="src/sunstone/routes/oneflow.rb"
|
||||
|
||||
SUNSTONE_PUBLIC_CSS_FILES="src/sunstone/public/css/app.css \
|
||||
src/sunstone/public/css/login.css"
|
||||
@ -1892,6 +1938,35 @@ OZONES_BIN_CLIENT_FILES="src/ozones/Client/bin/onevdc \
|
||||
|
||||
OZONES_RUBY_LIB_FILES="src/oca/ruby/OpenNebula.rb"
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# OneFlow files
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
ONEFLOW_FILES="src/flow/oneflow-server.rb \
|
||||
src/flow/config.ru"
|
||||
|
||||
ONEFLOW_BIN_FILES="src/flow/bin/oneflow-server"
|
||||
|
||||
ONEFLOW_ETC_FILES="src/flow/etc/oneflow-server.conf"
|
||||
|
||||
ONEFLOW_LIB_FILES="src/flow/lib/grammar.rb \
|
||||
src/flow/lib/grammar.treetop \
|
||||
src/flow/lib/LifeCycleManager.rb \
|
||||
src/flow/lib/log.rb \
|
||||
src/flow/lib/models.rb \
|
||||
src/flow/lib/strategy.rb \
|
||||
src/flow/lib/validator.rb"
|
||||
|
||||
ONEFLOW_LIB_STRATEGY_FILES="src/flow/lib/strategy/straight.rb"
|
||||
|
||||
ONEFLOW_LIB_MODELS_FILES="src/flow/lib/models/role.rb \
|
||||
src/flow/lib/models/service_pool.rb \
|
||||
src/flow/lib/models/service.rb \
|
||||
src/flow/lib/models/service_template_pool.rb \
|
||||
src/flow/lib/models/service_template.rb"
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# MAN files
|
||||
#-----------------------------------------------------------------------------
|
||||
@ -1909,6 +1984,8 @@ MAN_FILES="share/man/oneauth.1.gz \
|
||||
share/man/onedb.1.gz \
|
||||
share/man/onedatastore.1.gz \
|
||||
share/man/onecluster.1.gz \
|
||||
share/man/oneflow.1.gz \
|
||||
share/man/oneflow-template.1.gz \
|
||||
share/man/econe-allocate-address.1.gz \
|
||||
share/man/econe-associate-address.1.gz \
|
||||
share/man/econe-attach-volume.1.gz \
|
||||
@ -2022,9 +2099,12 @@ elif [ "$SUNSTONE" = "yes" ]; then
|
||||
INSTALL_SET="${INSTALL_SUNSTONE_RUBY_FILES[@]} ${INSTALL_SUNSTONE_FILES[@]}"
|
||||
elif [ "$OZONES" = "yes" ]; then
|
||||
INSTALL_SET="${INSTALL_OZONES_RUBY_FILES[@]} ${INSTALL_OZONES_FILES[@]}"
|
||||
elif [ "$ONEFLOW" = "yes" ]; then
|
||||
INSTALL_SET="${INSTALL_ONEFLOW_FILES[@]}"
|
||||
else
|
||||
INSTALL_SET="${INSTALL_FILES[@]} ${INSTALL_OZONES_FILES[@]} \
|
||||
${INSTALL_SUNSTONE_FILES[@]} ${INSTALL_ONEGATE_FILES[@]}"
|
||||
${INSTALL_SUNSTONE_FILES[@]} ${INSTALL_ONEGATE_FILES[@]} \
|
||||
${INSTALL_ONEFLOW_FILES[@]}"
|
||||
fi
|
||||
|
||||
for i in ${INSTALL_SET[@]}; do
|
||||
@ -2045,11 +2125,14 @@ if [ "$INSTALL_ETC" = "yes" ] ; then
|
||||
INSTALL_ETC_SET="${INSTALL_ONEGATE_ETC_FILES[@]}"
|
||||
elif [ "$OZONES" = "yes" ]; then
|
||||
INSTALL_ETC_SET="${INSTALL_OZONES_ETC_FILES[@]}"
|
||||
elif [ "$ONEFLOW" = "yes" ]; then
|
||||
INSTALL_ETC_SET="${INSTALL_ONEFLOW_ETC_FILES[@]}"
|
||||
else
|
||||
INSTALL_ETC_SET="${INSTALL_ETC_FILES[@]} \
|
||||
${INSTALL_SUNSTONE_ETC_FILES[@]} \
|
||||
${INSTALL_OZONES_ETC_FILES[@]} \
|
||||
${INSTALL_ONEGATE_ETC_FILES[@]}"
|
||||
${INSTALL_ONEGATE_ETC_FILES[@]} \
|
||||
${INSTALL_ONEFLOW_ETC_FILES[@]}"
|
||||
fi
|
||||
|
||||
for i in ${INSTALL_ETC_SET[@]}; do
|
||||
|
63
share/doc/states/service.dot
Normal file
63
share/doc/states/service.dot
Normal file
@ -0,0 +1,63 @@
|
||||
|
||||
|
||||
digraph AppFlow {
|
||||
graph [
|
||||
nodesep= 0.5,
|
||||
ratio=1,
|
||||
pad=0.5,
|
||||
];
|
||||
|
||||
node [fontname="Helvetica"];
|
||||
edge [fontname="Menlo"];
|
||||
|
||||
user [
|
||||
fillcolor="black",
|
||||
style="filled", shape="box",
|
||||
fontcolor="white", fontsize= "24.0"];
|
||||
|
||||
ANY [ shape="box" ];
|
||||
|
||||
end [ width=0.15, height=0.15, shape=point ];
|
||||
|
||||
running [ label="running /\nwarning"];
|
||||
|
||||
subgraph { rank = min; user; pending}
|
||||
subgraph { rank = same; deploying; failed_deploying; color="white"}
|
||||
|
||||
subgraph { rank = same; running; scaling; failed_scaling; color="white"}
|
||||
|
||||
subgraph { rank = same; undeploying; failed_undeploying; color="white"}
|
||||
// subgraph { rank = same; done; end; color="white"}
|
||||
|
||||
|
||||
user -> pending [label="instantiate"];
|
||||
|
||||
pending -> deploying [style="dashed"];
|
||||
deploying -> running [style="dashed"];
|
||||
running -> undeploying [label="shutdown"];
|
||||
undeploying -> done [style="dashed"];
|
||||
done -> end [label="delete"];
|
||||
|
||||
|
||||
// running -> warning [style="dotted"];
|
||||
// warning -> running [style="dotted"];
|
||||
|
||||
|
||||
running -> scaling [style="dashed"];
|
||||
scaling -> cooldown [style="dashed"];
|
||||
cooldown -> running [style="dashed"];
|
||||
|
||||
cooldown -> failed_undeploying [style="invis"];
|
||||
|
||||
failed_deploying -> deploying [label="recover"];
|
||||
deploying -> failed_deploying [style="dotted"];
|
||||
|
||||
failed_undeploying -> undeploying [label="recover"];
|
||||
undeploying -> failed_undeploying [style="dotted"];
|
||||
|
||||
failed_scaling -> scaling [label="recover"];
|
||||
scaling -> failed_scaling [style="dotted"];
|
||||
|
||||
|
||||
ANY -> end [label="delete"];
|
||||
}
|
34
share/examples/oneflow/none.json
Normal file
34
share/examples/oneflow/none.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "my_first_service",
|
||||
"roles": [
|
||||
{
|
||||
"name": "frontend",
|
||||
"cardinality": 1,
|
||||
"parents": [
|
||||
"mysql_server",
|
||||
"kvm_host",
|
||||
"nfs_server"
|
||||
],
|
||||
"vm_template": 3
|
||||
},
|
||||
{
|
||||
"name": "nfs_server",
|
||||
"cardinality": 1,
|
||||
"vm_template": 2
|
||||
},
|
||||
{
|
||||
"name": "mysql_server",
|
||||
"cardinality": 1,
|
||||
"vm_template": 5
|
||||
},
|
||||
{
|
||||
"name": "kvm_host",
|
||||
"parents": [
|
||||
"nfs_server"
|
||||
],
|
||||
"cardinality": 3,
|
||||
"vm_template": 7
|
||||
}
|
||||
],
|
||||
"deployment": "none"
|
||||
}
|
57
share/examples/oneflow/scal.json
Normal file
57
share/examples/oneflow/scal.json
Normal file
@ -0,0 +1,57 @@
|
||||
{
|
||||
"name": "scalability",
|
||||
"deployment": "straight",
|
||||
"roles": [
|
||||
{
|
||||
"name": "frontend",
|
||||
"cardinality": 1,
|
||||
"vm_template": 0,
|
||||
|
||||
"min_vms" : 1,
|
||||
"max_vms" : 5,
|
||||
|
||||
"elasticity_policies" : [
|
||||
{
|
||||
// +2 VMs when the exp. is true for 3 times in a row,
|
||||
// separated by 10 seconds
|
||||
"type" : "CHANGE",
|
||||
"expression" : "ATT > 50",
|
||||
"adjust" : 2,
|
||||
|
||||
"period_number" : 3,
|
||||
"period" : 10
|
||||
},
|
||||
{
|
||||
// -10 percent VMs when the exp. is true.
|
||||
// If 10 percent is less than 2, -2 VMs.
|
||||
"type" : "PERCENTAGE_CHANGE",
|
||||
"expression" : "ATT < 10",
|
||||
"adjust" : -10,
|
||||
"min_adjust_step" : 2
|
||||
},
|
||||
{
|
||||
// Set cardinality to 4 when the exp. is true
|
||||
"type" : "CARDINALITY",
|
||||
"expression" : "SOME_ATT <= 50 && OTHER_ATT > 90",
|
||||
"adjust" : 4
|
||||
}
|
||||
],
|
||||
|
||||
"scheduled_policies" : [
|
||||
{
|
||||
// Set cardinality to 2 each 10 minutes
|
||||
"type" : "CARDINALITY",
|
||||
"recurrence" : "*/10 * * * *",
|
||||
"adjust" : 2
|
||||
},
|
||||
{
|
||||
// +10 percent at the given date and time
|
||||
"type" : "PERCENTAGE_CHANGE",
|
||||
"start_time" : "2nd oct 2013 15:45",
|
||||
"adjust" : 10
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
35
share/examples/oneflow/straight.json
Normal file
35
share/examples/oneflow/straight.json
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "my_first_service",
|
||||
"deployment": "straight",
|
||||
"roles": [
|
||||
{
|
||||
"name": "frontend",
|
||||
"cardinality": 1,
|
||||
"parents": [
|
||||
"mysql_server",
|
||||
"kvm_host",
|
||||
"nfs_server"
|
||||
],
|
||||
"vm_template": 3
|
||||
},
|
||||
{
|
||||
"name": "nfs_server",
|
||||
"cardinality": 1,
|
||||
"vm_template": 2
|
||||
},
|
||||
{
|
||||
"name": "mysql_server",
|
||||
"cardinality": 1,
|
||||
"vm_template": 5
|
||||
},
|
||||
{
|
||||
"name": "kvm_host",
|
||||
"parents": [
|
||||
"nfs_server"
|
||||
],
|
||||
"cardinality": 3,
|
||||
"vm_template": 7
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ require 'pp'
|
||||
|
||||
$nokogiri='nokogiri'
|
||||
|
||||
DEFAULT=%w{sunstone quota cloud ozones_server auth_ldap vmware}
|
||||
DEFAULT=%w{sunstone quota cloud ozones_server auth_ldap vmware oneflow}
|
||||
|
||||
if defined?(RUBY_VERSION) && RUBY_VERSION>="1.8.7"
|
||||
SQLITE='sqlite3'
|
||||
@ -23,7 +23,8 @@ GROUPS={
|
||||
:ozones_server_sqlite => %w{json sequel}<<SQLITE,
|
||||
:ozones_server_mysql => %w{json sequel mysql},
|
||||
:auth_ldap => 'net-ldap',
|
||||
:vmware => 'builder'
|
||||
:vmware => 'builder',
|
||||
:oneflow => %w{sinatra json treetop parse-cron}
|
||||
}
|
||||
|
||||
PACKAGES=GROUPS.keys
|
||||
|
673
src/cli/oneflow
Executable file
673
src/cli/oneflow
Executable file
@ -0,0 +1,673 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2010-2013, C12G Labs S.L. #
|
||||
# #
|
||||
# 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. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
ONE_LOCATION=ENV["ONE_LOCATION"]
|
||||
|
||||
if !ONE_LOCATION
|
||||
RUBY_LIB_LOCATION="/usr/lib/one/ruby"
|
||||
else
|
||||
RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby"
|
||||
end
|
||||
|
||||
$: << RUBY_LIB_LOCATION
|
||||
$: << RUBY_LIB_LOCATION+'/cli'
|
||||
|
||||
require 'command_parser'
|
||||
require 'opennebula/oneflow_client'
|
||||
|
||||
require 'cli_helper'
|
||||
require 'one_helper/onevm_helper'
|
||||
|
||||
require 'json'
|
||||
|
||||
USER_AGENT = "CLI"
|
||||
|
||||
# Base Path representing the resource to be used in the requests
|
||||
RESOURCE_PATH = "/service"
|
||||
|
||||
#
|
||||
# Table
|
||||
#
|
||||
|
||||
SERVICE_TABLE = CLIHelper::ShowTable.new(nil, self) do
|
||||
column :ID, "ID", :size=>10 do |d|
|
||||
d["ID"]
|
||||
end
|
||||
|
||||
column :USER, "Username", :left, :size=>15 do |d|
|
||||
d["UNAME"]
|
||||
end
|
||||
|
||||
column :GROUP, "Group", :left, :size=>15 do |d|
|
||||
d["GNAME"]
|
||||
end
|
||||
|
||||
column :NAME, "Name", :size=>25, :left=>true do |d|
|
||||
d["NAME"]
|
||||
end
|
||||
|
||||
column :STATE, "State", :size=>11, :left=>true do |d|
|
||||
Service.state_str(d["TEMPLATE"]["BODY"]['state'])
|
||||
end
|
||||
|
||||
default :ID, :USER, :GROUP, :NAME, :STATE
|
||||
end
|
||||
|
||||
NODE_TABLE = CLIHelper::ShowTable.new(nil, self) do
|
||||
column :VM_ID, "ONE identifier for Virtual Machine", :size=>6 do |d|
|
||||
st = ""
|
||||
if d['scale_up']
|
||||
st << "\u2191 "
|
||||
elsif d['disposed']
|
||||
st << "\u2193 "
|
||||
end
|
||||
|
||||
if d['vm_info'].nil?
|
||||
st << d['deploy_id'].to_s
|
||||
else
|
||||
st << d['vm_info']['VM']["ID"]
|
||||
end
|
||||
|
||||
st
|
||||
end
|
||||
|
||||
column :NAME, "Name of the Virtual Machine", :left,
|
||||
:size=>23 do |d|
|
||||
if !d['vm_info'].nil?
|
||||
if d['vm_info']['VM']["RESCHED"] == "1"
|
||||
"*#{d["NAME"]}"
|
||||
else
|
||||
d['vm_info']['VM']["NAME"]
|
||||
end
|
||||
else
|
||||
""
|
||||
end
|
||||
end
|
||||
|
||||
column :USER, "Username of the Virtual Machine owner", :left,
|
||||
:size=>8 do |d|
|
||||
if !d['vm_info'].nil?
|
||||
d['vm_info']['VM']["UNAME"]
|
||||
else
|
||||
""
|
||||
end
|
||||
end
|
||||
|
||||
column :GROUP, "Group of the Virtual Machine", :left,
|
||||
:size=>8 do |d|
|
||||
if !d['vm_info'].nil?
|
||||
d['vm_info']['VM']["GNAME"]
|
||||
else
|
||||
""
|
||||
end
|
||||
end
|
||||
|
||||
column :STAT, "Actual status", :size=>4 do |d,e|
|
||||
if !d['vm_info'].nil?
|
||||
OneVMHelper.state_to_str(d['vm_info']['VM']["STATE"], d['vm_info']['VM']["LCM_STATE"])
|
||||
else
|
||||
""
|
||||
end
|
||||
end
|
||||
|
||||
column :UCPU, "CPU percentage used by the VM", :size=>4 do |d|
|
||||
if !d['vm_info'].nil?
|
||||
d['vm_info']['VM']["CPU"]
|
||||
else
|
||||
""
|
||||
end
|
||||
end
|
||||
|
||||
column :UMEM, "Memory used by the VM", :size=>7 do |d|
|
||||
if !d['vm_info'].nil?
|
||||
OpenNebulaHelper.unit_to_str(d['vm_info']['VM']["MEMORY"].to_i, {})
|
||||
else
|
||||
""
|
||||
end
|
||||
end
|
||||
|
||||
column :HOST, "Host where the VM is running", :left, :size=>20 do |d|
|
||||
if !d['vm_info'].nil?
|
||||
if d['vm_info']['VM']['HISTORY_RECORDS'] && d['vm_info']['VM']['HISTORY_RECORDS']['HISTORY']
|
||||
state_str = VirtualMachine::VM_STATE[d['vm_info']['VM']['STATE'].to_i]
|
||||
if %w{ACTIVE SUSPENDED}.include? state_str
|
||||
history = if d['vm_info']['VM']['HISTORY_RECORDS']['HISTORY'].instance_of?(Array)
|
||||
d['vm_info']['VM']['HISTORY_RECORDS']['HISTORY'].last
|
||||
else
|
||||
d['vm_info']['VM']['HISTORY_RECORDS']['HISTORY']
|
||||
end
|
||||
|
||||
history['HOSTNAME']
|
||||
end
|
||||
end
|
||||
else
|
||||
""
|
||||
end
|
||||
end
|
||||
|
||||
column :TIME, "Time since the VM was submitted", :size=>10 do |d|
|
||||
if !d['vm_info'].nil?
|
||||
stime = d['vm_info']['VM']["STIME"].to_i
|
||||
etime = d['vm_info']['VM']["ETIME"]=="0" ? Time.now.to_i : d['vm_info']['VM']["ETIME"].to_i
|
||||
dtime = etime-stime
|
||||
OpenNebulaHelper.period_to_str(dtime, false)
|
||||
else
|
||||
""
|
||||
end
|
||||
end
|
||||
|
||||
default :VM_ID, :NAME, :STAT, :UCPU, :UMEM, :HOST, :TIME
|
||||
end
|
||||
|
||||
# List the services. This method is used in top and list commands
|
||||
# @param [Service::Client] client
|
||||
# @param [Array] args
|
||||
# @param [Hash] options
|
||||
# @return [[Integer, String], Integer] Returns the exit_code and optionally
|
||||
# a String to be printed
|
||||
def list_services(client, args, options)
|
||||
response = client.get(RESOURCE_PATH)
|
||||
|
||||
if CloudClient::is_error?(response)
|
||||
[response.code.to_i, response.to_s]
|
||||
else
|
||||
#[0,response.body]
|
||||
if options[:json]
|
||||
[0,response.body]
|
||||
else
|
||||
array_list = JSON.parse(response.body)
|
||||
SERVICE_TABLE.show(array_list['DOCUMENT_POOL']['DOCUMENT'])
|
||||
0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Show the service information. This method is used in top and show commands
|
||||
# @param [Service::Client] client
|
||||
# @param [Array] args
|
||||
# @param [Hash] options
|
||||
# @return [[Integer, String], Integer] Returns the exit_code and optionally
|
||||
# a String to be printed
|
||||
def show_service(client, args, options)
|
||||
response = client.get("#{RESOURCE_PATH}/#{args[0]}")
|
||||
|
||||
if CloudClient::is_error?(response)
|
||||
[response.code.to_i, response.to_s]
|
||||
else
|
||||
#[0,response.body]
|
||||
if options[:json]
|
||||
[0,response.body]
|
||||
else
|
||||
str="%-20s: %-20s"
|
||||
str_h1="%-80s"
|
||||
|
||||
document_hash = JSON.parse(response.body)
|
||||
template = document_hash['DOCUMENT']['TEMPLATE']['BODY']
|
||||
|
||||
CLIHelper.print_header(str_h1 % "SERVICE #{document_hash['DOCUMENT']['ID']} INFORMATION")
|
||||
|
||||
puts str % ["ID", document_hash['DOCUMENT']['ID']]
|
||||
puts str % ["NAME", document_hash['DOCUMENT']['NAME']]
|
||||
puts str % ["USER", document_hash['DOCUMENT']['UNAME']]
|
||||
puts str % ["GROUP",document_hash['DOCUMENT']['GNAME']]
|
||||
|
||||
puts str % ["STRATEGY", template['deployment']]
|
||||
puts str % ["SERVICE STATE", Service.state_str(template['state'])]
|
||||
puts str % ["SHUTDOWN", template['shutdown_action']] if template['shutdown_action']
|
||||
|
||||
puts
|
||||
|
||||
CLIHelper.print_header(str_h1 % "PERMISSIONS",false)
|
||||
|
||||
["OWNER", "GROUP", "OTHER"].each { |e|
|
||||
mask = "---"
|
||||
mask[0] = "u" if document_hash['DOCUMENT']['PERMISSIONS']["#{e}_U"] == "1"
|
||||
mask[1] = "m" if document_hash['DOCUMENT']['PERMISSIONS']["#{e}_M"] == "1"
|
||||
mask[2] = "a" if document_hash['DOCUMENT']['PERMISSIONS']["#{e}_A"] == "1"
|
||||
|
||||
puts str % [e, mask]
|
||||
}
|
||||
|
||||
puts
|
||||
|
||||
template['roles'].each {|role|
|
||||
CLIHelper.print_header("ROLE #{role['name']}", false)
|
||||
|
||||
puts str % ["ROLE STATE", Role.state_str(role['state'])]
|
||||
puts str % ["PARENTS", role['parents'].join(', ')] if role['parents']
|
||||
puts str % ["VM TEMPLATE", role['vm_template']]
|
||||
puts str % ["CARNIDALITY", role['cardinality']]
|
||||
puts str % ["MIN VMS", role['min_vms']] if role['min_vms']
|
||||
puts str % ["MAX VMS", role['max_vms']] if role['max_vms']
|
||||
puts str % ["SHUTDOWN", role['shutdown_action']] if role['shutdown_action']
|
||||
|
||||
puts "NODES INFORMATION"
|
||||
NODE_TABLE.show(role['nodes'])
|
||||
|
||||
if !role['elasticity_policies'].nil? || !role['scheduled_policies'].nil?
|
||||
puts
|
||||
puts "ELASTICITY RULES"
|
||||
puts str % ["COOLDOWN", "#{role['cooldown']}s"] if role['cooldown']
|
||||
|
||||
if role['elasticity_policies']
|
||||
puts
|
||||
# puts "ELASTICITY POLICIES"
|
||||
CLIHelper::ShowTable.new(nil, self) do
|
||||
column :ADJUST, "", :left, :size=>12 do |d|
|
||||
adjust_str(d)
|
||||
end
|
||||
|
||||
column :EXPRESSION, "", :left, :size=>48 do |d|
|
||||
if !d['expression_evaluated'].nil?
|
||||
d['expression_evaluated']
|
||||
else
|
||||
d['expression']
|
||||
end
|
||||
end
|
||||
|
||||
column :'EVALS', "", :left, :size=>5 do |d|
|
||||
"#{d['true_evals'].to_i} / #{d['period_number']}"
|
||||
end
|
||||
|
||||
column :PERIOD, "", :size=>6 do |d|
|
||||
"#{d['period']}s"
|
||||
end
|
||||
|
||||
column :COOL, "", :size=>5 do |d|
|
||||
d['cooldown'] ? "#{d['cooldown']}s" : '-'
|
||||
end
|
||||
|
||||
default :ADJUST, :EXPRESSION, :EVALS, :PERIOD, :COOL
|
||||
end.show([role['elasticity_policies']].flatten, {})
|
||||
end
|
||||
|
||||
if role['scheduled_policies']
|
||||
puts
|
||||
# puts "SCHEDULED POLICIES"
|
||||
CLIHelper::ShowTable.new(nil, self) do
|
||||
column :ADJUST, "", :left, :size=>12 do |d|
|
||||
adjust_str(d)
|
||||
end
|
||||
|
||||
column :TIME, "", :left, :size=>67 do |d|
|
||||
if d['start_time']
|
||||
Time.parse(d['start_time']).to_s
|
||||
else
|
||||
d['recurrence']
|
||||
end
|
||||
end
|
||||
|
||||
default :ADJUST, :TIME
|
||||
end.show([role['scheduled_policies']].flatten, {})
|
||||
end
|
||||
end
|
||||
|
||||
puts
|
||||
}
|
||||
|
||||
puts
|
||||
|
||||
CLIHelper.print_header(str_h1 % "LOG MESSAGES",false)
|
||||
|
||||
if template['log']
|
||||
template['log'].each { |log|
|
||||
t = Time.at(log['timestamp']).strftime("%m/%d/%y %H:%M")
|
||||
puts "#{t} [#{log['severity']}] #{log['message']}"
|
||||
}
|
||||
end
|
||||
|
||||
0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def adjust_str(policy)
|
||||
sign = policy['adjust'].to_i >= 0 ? "+" : "-"
|
||||
adjust = policy['adjust'].to_i.abs
|
||||
|
||||
case policy['type']
|
||||
when 'CARDINALITY'
|
||||
"= #{adjust}"
|
||||
when 'PERCENTAGE_CHANGE'
|
||||
st = "#{sign} #{adjust} %"
|
||||
if policy['min_adjust_step']
|
||||
st << " (#{policy['min_adjust_step']})"
|
||||
end
|
||||
|
||||
st
|
||||
else
|
||||
"#{sign} #{adjust}"
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Commands
|
||||
#
|
||||
|
||||
cmd=CommandParser::CmdParser.new(ARGV) do
|
||||
usage "`oneflow` <command> [<args>] [<options>]"
|
||||
version OpenNebulaHelper::ONE_VERSION
|
||||
|
||||
set :option, Service::DEFAULT_OPTIONS
|
||||
set :option, CommandParser::VERSION
|
||||
set :option, CommandParser::HELP
|
||||
|
||||
#
|
||||
# Formatters for arguments
|
||||
#
|
||||
set :format, :groupid, OpenNebulaHelper.rname_to_id_desc("GROUP") do |arg|
|
||||
OpenNebulaHelper.rname_to_id(arg, "GROUP")
|
||||
end
|
||||
|
||||
set :format, :userid, OpenNebulaHelper.rname_to_id_desc("USER") do |arg|
|
||||
OpenNebulaHelper.rname_to_id(arg, "USER")
|
||||
end
|
||||
|
||||
set :format, :service_id, Service.rname_to_id_desc("SERVICE") do |arg|
|
||||
Service.rname_to_id(arg, "SERVICE")
|
||||
end
|
||||
|
||||
set :format, :service_id_list, Service.list_to_id_desc("SERVICE") do |arg|
|
||||
Service.list_to_id(arg, "SERVICE")
|
||||
end
|
||||
|
||||
set :format, :vm_action, "Actions supported: #{Role::SCHEDULE_ACTIONS.join(', ')}" do |arg|
|
||||
if Role::SCHEDULE_ACTIONS.include?(arg)
|
||||
[0, arg]
|
||||
else
|
||||
[-1, "Action #{arg} is not supported. Actions supported: #{Role::SCHEDULE_ACTIONS.join(', ')}"]
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# List
|
||||
#
|
||||
|
||||
list_desc = <<-EOT.unindent
|
||||
List the available services
|
||||
EOT
|
||||
|
||||
command :list, list_desc, :options => Service::JSON_FORMAT do
|
||||
client = Service::Client.new(
|
||||
:username => options[:username],
|
||||
:password => options[:password],
|
||||
:url => options[:server],
|
||||
:user_agent => USER_AGENT)
|
||||
|
||||
list_services(client, args, options)
|
||||
end
|
||||
|
||||
#
|
||||
# Show
|
||||
#
|
||||
|
||||
show_desc = <<-EOT.unindent
|
||||
Show detailed information of a given service
|
||||
EOT
|
||||
|
||||
command :show, show_desc, :service_id, :options => Service::JSON_FORMAT do
|
||||
client = Service::Client.new(
|
||||
:username => options[:username],
|
||||
:password => options[:password],
|
||||
:url => options[:server],
|
||||
:user_agent => USER_AGENT)
|
||||
|
||||
show_service(client, args, options)
|
||||
end
|
||||
|
||||
#
|
||||
# Top
|
||||
#
|
||||
|
||||
top_desc = <<-EOT.unindent
|
||||
Top the services or the extended information of the target service if a
|
||||
id is specified
|
||||
EOT
|
||||
|
||||
command :top, top_desc, [:service_id, nil],
|
||||
:options => [Service::JSON_FORMAT, Service::TOP, CLIHelper::DELAY] do
|
||||
client = Service::Client.new(
|
||||
:username => options[:username],
|
||||
:password => options[:password],
|
||||
:url => options[:server],
|
||||
:user_agent => USER_AGENT)
|
||||
|
||||
delay=options[:delay] ? options[:delay] : 3
|
||||
|
||||
begin
|
||||
while true
|
||||
CLIHelper.scr_cls
|
||||
CLIHelper.scr_move(0,0)
|
||||
|
||||
if args[0]
|
||||
rc, message = show_service(client, args, options)
|
||||
|
||||
if rc != 0
|
||||
raise message
|
||||
end
|
||||
else
|
||||
rc, message = list_services(client, args, options)
|
||||
|
||||
if rc != 0
|
||||
raise message
|
||||
end
|
||||
end
|
||||
|
||||
sleep delay
|
||||
end
|
||||
rescue Exception => e
|
||||
puts e.message
|
||||
-1
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Delete
|
||||
#
|
||||
|
||||
delete_desc = <<-EOT.unindent
|
||||
Delete a given service
|
||||
EOT
|
||||
|
||||
command :delete, delete_desc, [:range, :service_id_list] do
|
||||
client = Service::Client.new(
|
||||
:username => options[:username],
|
||||
:password => options[:password],
|
||||
:url => options[:server],
|
||||
:user_agent => USER_AGENT)
|
||||
|
||||
Service.perform_actions(args[0]) { |service_id|
|
||||
client.delete("#{RESOURCE_PATH}/#{service_id}")
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Shutdown
|
||||
#
|
||||
|
||||
shutdown_desc = <<-EOT.unindent
|
||||
Shutdown a service.
|
||||
From RUNNING or WARNING shuts down the Service
|
||||
EOT
|
||||
|
||||
command :shutdown, shutdown_desc, [:range, :service_id_list] do
|
||||
client = Service::Client.new(
|
||||
:username => options[:username],
|
||||
:password => options[:password],
|
||||
:url => options[:server],
|
||||
:user_agent => USER_AGENT)
|
||||
|
||||
Service.perform_actions(args[0]) { |service_id|
|
||||
json_action = Service.build_json_action('shutdown')
|
||||
|
||||
client.post("#{RESOURCE_PATH}/#{service_id}/action", json_action)
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Recover
|
||||
#
|
||||
|
||||
recover_desc = <<-EOT.unindent
|
||||
Recover a failed service, cleaning the failed VMs.
|
||||
From FAILED_DEPLOYING continues deploying the Service
|
||||
From FAILED_SCALING continues scaling the Service
|
||||
From FAILED_UNDEPLOYING continues shutting down the Service
|
||||
From COOLDOWN the Service is set to running ignoring the cooldown duration
|
||||
From WARNING failed VMs are deleted, and new VMs are instantiated
|
||||
EOT
|
||||
|
||||
command :recover, recover_desc, [:range, :service_id_list] do
|
||||
client = Service::Client.new(
|
||||
:username => options[:username],
|
||||
:password => options[:password],
|
||||
:url => options[:server],
|
||||
:user_agent => USER_AGENT)
|
||||
|
||||
Service.perform_actions(args[0]) { |service_id|
|
||||
json_action = Service.build_json_action('recover')
|
||||
|
||||
client.post("#{RESOURCE_PATH}/#{service_id}/action", json_action)
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Scale
|
||||
#
|
||||
|
||||
scale_desc = <<-EOT.unindent
|
||||
Scale a role to the given cardinality
|
||||
EOT
|
||||
|
||||
command :'scale', scale_desc, :service_id, :role_name,
|
||||
:cardinality, :options => [Service::FORCE] do
|
||||
client = Service::Client.new(
|
||||
:username => options[:username],
|
||||
:password => options[:password],
|
||||
:url => options[:server],
|
||||
:user_agent => USER_AGENT)
|
||||
|
||||
if !(args[2] =~ /^\d+$/)
|
||||
puts "Cardinality must be an integer number"
|
||||
exit -1
|
||||
end
|
||||
|
||||
exit_code = 0
|
||||
|
||||
json = "{ \"cardinality\" : #{args[2]},\n"<<
|
||||
" \"force\" : #{options[:force] == true} }"
|
||||
|
||||
response = client.put("#{RESOURCE_PATH}/#{args[0]}/role/#{args[1]}", json)
|
||||
|
||||
if CloudClient::is_error?(response)
|
||||
puts response.to_s
|
||||
exit_code = response.code.to_i
|
||||
end
|
||||
|
||||
exit_code
|
||||
end
|
||||
|
||||
chgrp_desc = <<-EOT.unindent
|
||||
Changes the service group
|
||||
EOT
|
||||
|
||||
command :chgrp, chgrp_desc, [:range, :service_id_list], :groupid do
|
||||
client = Service::Client.new(
|
||||
:username => options[:username],
|
||||
:password => options[:password],
|
||||
:url => options[:server],
|
||||
:user_agent => USER_AGENT)
|
||||
|
||||
Service.perform_actions(args[0]) { |service_id|
|
||||
params = Hash.new
|
||||
params['group_id'] = args[1].to_i
|
||||
|
||||
json_action = Service.build_json_action('chgrp', params)
|
||||
|
||||
client.post("#{RESOURCE_PATH}/#{service_id}/action", json_action)
|
||||
}
|
||||
end
|
||||
|
||||
chown_desc = <<-EOT.unindent
|
||||
Changes the service owner and group
|
||||
EOT
|
||||
|
||||
command :chown, chown_desc, [:range, :service_id_list], :userid, [:groupid, nil] do
|
||||
client = Service::Client.new(
|
||||
:username => options[:username],
|
||||
:password => options[:password],
|
||||
:url => options[:server],
|
||||
:user_agent => USER_AGENT)
|
||||
|
||||
Service.perform_actions(args[0]) { |service_id|
|
||||
params = Hash.new
|
||||
params['owner_id'] = args[1]
|
||||
params['group_id'] = args[2] if args[2]
|
||||
|
||||
json_action = Service.build_json_action('chown', params)
|
||||
|
||||
client.post("#{RESOURCE_PATH}/#{service_id}/action", json_action)
|
||||
}
|
||||
end
|
||||
|
||||
chmod_desc = <<-EOT.unindent
|
||||
Changes the service permissions
|
||||
EOT
|
||||
|
||||
command :chmod, chmod_desc, [:range, :service_id_list], :octet do
|
||||
client = Service::Client.new(
|
||||
:username => options[:username],
|
||||
:password => options[:password],
|
||||
:url => options[:server],
|
||||
:user_agent => USER_AGENT)
|
||||
|
||||
Service.perform_actions(args[0]) { |service_id|
|
||||
params = Hash.new
|
||||
params['octet'] = args[1]
|
||||
|
||||
json_action = Service.build_json_action('chmod', params)
|
||||
|
||||
client.post("#{RESOURCE_PATH}/#{service_id}/action", json_action)
|
||||
}
|
||||
end
|
||||
|
||||
action_desc = <<-EOT.unindent
|
||||
Perform an action on all the Virtual Machines of a given role.
|
||||
Actions supported: #{Role::SCHEDULE_ACTIONS.join(",")}
|
||||
EOT
|
||||
|
||||
command :"action", action_desc, :service_id, :role_name, :vm_action,
|
||||
:options => [Service::PERIOD, Service::NUMBER] do
|
||||
|
||||
client = Service::Client.new(
|
||||
:username => options[:username],
|
||||
:password => options[:password],
|
||||
:url => options[:server],
|
||||
:user_agent => USER_AGENT)
|
||||
|
||||
Service.perform_actions([args[0]]) { |service_id|
|
||||
params = Hash.new
|
||||
params[:period] = options[:period].to_i if options[:period]
|
||||
params[:number] = options[:number].to_i if options[:number]
|
||||
|
||||
json_action = Service.build_json_action(args[2], params)
|
||||
|
||||
client.post("#{RESOURCE_PATH}/#{service_id}/role/#{args[1]}/action", json_action)
|
||||
}
|
||||
end
|
||||
end
|
451
src/cli/oneflow-template
Executable file
451
src/cli/oneflow-template
Executable file
@ -0,0 +1,451 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2010-2013, C12G Labs S.L. #
|
||||
# #
|
||||
# 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. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
ONE_LOCATION=ENV["ONE_LOCATION"]
|
||||
|
||||
if !ONE_LOCATION
|
||||
RUBY_LIB_LOCATION="/usr/lib/one/ruby"
|
||||
else
|
||||
RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby"
|
||||
end
|
||||
|
||||
$: << RUBY_LIB_LOCATION
|
||||
$: << RUBY_LIB_LOCATION+'/cli'
|
||||
|
||||
require 'command_parser'
|
||||
require 'opennebula/oneflow_client'
|
||||
|
||||
require 'cli_helper'
|
||||
require 'one_helper'
|
||||
|
||||
require 'json'
|
||||
|
||||
USER_AGENT = "CLI"
|
||||
|
||||
# Base Path representing the resource to be used in the requests
|
||||
RESOURCE_PATH = '/service_template'
|
||||
|
||||
#
|
||||
# Table
|
||||
#
|
||||
|
||||
TABLE = CLIHelper::ShowTable.new(nil, self) do
|
||||
column :ID, "ID", :size=>10 do |d|
|
||||
d["ID"]
|
||||
end
|
||||
|
||||
column :USER, "Username", :left, :size=>15 do |d|
|
||||
d["UNAME"]
|
||||
end
|
||||
|
||||
column :GROUP, "Group", :left, :size=>15 do |d|
|
||||
d["GNAME"]
|
||||
end
|
||||
|
||||
column :NAME, "Name", :left, :size=>37 do |d|
|
||||
d["NAME"]
|
||||
end
|
||||
|
||||
default :ID, :USER, :GROUP, :NAME
|
||||
end
|
||||
|
||||
|
||||
# Show the service template information. This method is used in top and
|
||||
# show commands
|
||||
# @param [Service::Client] client
|
||||
# @param [Array] args
|
||||
# @param [Hash] options
|
||||
# @return [[Integer, String], Integer] Returns the exit_code and optionally
|
||||
# a String to be printed
|
||||
def show_service_template(client, args, options)
|
||||
response = client.get("#{RESOURCE_PATH}/#{args[0]}")
|
||||
|
||||
if CloudClient::is_error?(response)
|
||||
[response.code.to_i, response.to_s]
|
||||
else
|
||||
#[0,response.body]
|
||||
if options[:json]
|
||||
[0,response.body]
|
||||
else
|
||||
str="%-20s: %-20s"
|
||||
str_h1="%-80s"
|
||||
|
||||
document_hash = JSON.parse(response.body)
|
||||
template = document_hash['DOCUMENT']['TEMPLATE']['BODY']
|
||||
|
||||
CLIHelper.print_header(str_h1 %
|
||||
"SERVICE TEMPLATE #{document_hash['DOCUMENT']['ID']} INFORMATION")
|
||||
|
||||
puts str % ["ID", document_hash['DOCUMENT']['ID']]
|
||||
puts str % ["NAME", document_hash['DOCUMENT']['NAME']]
|
||||
puts str % ["USER", document_hash['DOCUMENT']['UNAME']]
|
||||
puts str % ["GROUP",document_hash['DOCUMENT']['GNAME']]
|
||||
|
||||
puts
|
||||
|
||||
CLIHelper.print_header(str_h1 % "PERMISSIONS",false)
|
||||
|
||||
["OWNER", "GROUP", "OTHER"].each { |e|
|
||||
mask = "---"
|
||||
mask[0] = "u" if document_hash['DOCUMENT']['PERMISSIONS']["#{e}_U"] == "1"
|
||||
mask[1] = "m" if document_hash['DOCUMENT']['PERMISSIONS']["#{e}_M"] == "1"
|
||||
mask[2] = "a" if document_hash['DOCUMENT']['PERMISSIONS']["#{e}_A"] == "1"
|
||||
|
||||
puts str % [e, mask]
|
||||
}
|
||||
|
||||
puts
|
||||
|
||||
CLIHelper.print_header(str_h1 % "TEMPLATE CONTENTS",false)
|
||||
puts JSON.pretty_generate(template)
|
||||
|
||||
0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# List the services. This method is used in top and list commands
|
||||
# @param [Service::Client] client
|
||||
# @param [Array] args
|
||||
# @param [Hash] options
|
||||
# @return [[Integer, String], Integer] Returns the exit_code and optionally
|
||||
# a String to be printed
|
||||
def list_service_templates(client, args, options)
|
||||
response = client.get(RESOURCE_PATH)
|
||||
|
||||
if CloudClient::is_error?(response)
|
||||
[response.code.to_i, response.to_s]
|
||||
else
|
||||
#[0,response.body]
|
||||
if options[:json]
|
||||
[0,response.body]
|
||||
else
|
||||
array_list = JSON.parse(response.body)
|
||||
TABLE.show(array_list['DOCUMENT_POOL']['DOCUMENT'])
|
||||
0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Commands
|
||||
#
|
||||
|
||||
cmd=CommandParser::CmdParser.new(ARGV) do
|
||||
usage "`oneflow-template` <command> [<args>] [<options>]"
|
||||
version OpenNebulaHelper::ONE_VERSION
|
||||
|
||||
set :option, Service::DEFAULT_OPTIONS
|
||||
set :option, CommandParser::VERSION
|
||||
set :option, CommandParser::HELP
|
||||
|
||||
#
|
||||
# Formatters for arguments
|
||||
#
|
||||
set :format, :groupid, OpenNebulaHelper.rname_to_id_desc("GROUP") do |arg|
|
||||
OpenNebulaHelper.rname_to_id(arg, "GROUP")
|
||||
end
|
||||
|
||||
set :format, :userid, OpenNebulaHelper.rname_to_id_desc("USER") do |arg|
|
||||
OpenNebulaHelper.rname_to_id(arg, "USER")
|
||||
end
|
||||
|
||||
set :format, :template_id, Service.rname_to_id_desc("SERVICE TEMPLATE") do |arg|
|
||||
Service.rname_to_id(arg, "SERVICE TEMPLATE")
|
||||
end
|
||||
|
||||
set :format, :templateid_list, Service.list_to_id_desc("SERVICE TEMPLATE") do |arg|
|
||||
Service.list_to_id(arg, "SERVICE TEMPLATE")
|
||||
end
|
||||
|
||||
#
|
||||
# List
|
||||
#
|
||||
|
||||
list_desc = <<-EOT.unindent
|
||||
List the available Service Templates
|
||||
EOT
|
||||
|
||||
command :list, list_desc, :options => Service::JSON_FORMAT do
|
||||
client = Service::Client.new(
|
||||
:username => options[:username],
|
||||
:password => options[:password],
|
||||
:url => options[:server],
|
||||
:user_agent => USER_AGENT)
|
||||
|
||||
list_service_templates(client, args, options)
|
||||
end
|
||||
|
||||
#
|
||||
# Top
|
||||
#
|
||||
|
||||
top_desc = <<-EOT.unindent
|
||||
List the available Service Templates continuously
|
||||
EOT
|
||||
|
||||
command :top, top_desc,
|
||||
:options => [Service::JSON_FORMAT, Service::TOP, CLIHelper::DELAY] do
|
||||
client = Service::Client.new(
|
||||
:username => options[:username],
|
||||
:password => options[:password],
|
||||
:url => options[:server],
|
||||
:user_agent => USER_AGENT)
|
||||
|
||||
delay=options[:delay] ? options[:delay] : 3
|
||||
|
||||
begin
|
||||
while true
|
||||
CLIHelper.scr_cls
|
||||
CLIHelper.scr_move(0,0)
|
||||
|
||||
rc, message = list_service_templates(client, args, options)
|
||||
|
||||
if rc != 0
|
||||
raise message
|
||||
end
|
||||
|
||||
sleep delay
|
||||
end
|
||||
rescue Exception => e
|
||||
puts e.message
|
||||
-1
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Create
|
||||
#
|
||||
|
||||
create_desc = <<-EOT.unindent
|
||||
Create a new Service Template
|
||||
EOT
|
||||
|
||||
command :create, create_desc, :file, :options => Service::JSON_FORMAT do
|
||||
client = Service::Client.new(
|
||||
:username => options[:username],
|
||||
:password => options[:password],
|
||||
:url => options[:server],
|
||||
:user_agent => USER_AGENT)
|
||||
|
||||
response = client.post(RESOURCE_PATH, File.read(args[0]))
|
||||
|
||||
if CloudClient::is_error?(response)
|
||||
[response.code.to_i, response.to_s]
|
||||
else
|
||||
if options[:json]
|
||||
[0,response.body]
|
||||
else
|
||||
template = JSON.parse(response.body)
|
||||
puts "ID: #{template['DOCUMENT']['ID']}"
|
||||
0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Show
|
||||
#
|
||||
|
||||
show_desc = <<-EOT.unindent
|
||||
Show detailed information of a given Service Template
|
||||
EOT
|
||||
|
||||
command :show, show_desc, :template_id, :options => Service::JSON_FORMAT do
|
||||
client = Service::Client.new(
|
||||
:username => options[:username],
|
||||
:password => options[:password],
|
||||
:url => options[:server],
|
||||
:user_agent => USER_AGENT)
|
||||
|
||||
show_service_template(client, args, options)
|
||||
end
|
||||
|
||||
#
|
||||
# Delete
|
||||
#
|
||||
|
||||
delete_desc = <<-EOT.unindent
|
||||
Delete a given Service Template
|
||||
EOT
|
||||
|
||||
command :delete, delete_desc, [:range, :templateid_list] do
|
||||
client = Service::Client.new(
|
||||
:username => options[:username],
|
||||
:password => options[:password],
|
||||
:url => options[:server],
|
||||
:user_agent => USER_AGENT)
|
||||
|
||||
Service.perform_actions(args[0]) { |template_id|
|
||||
client.delete("#{RESOURCE_PATH}/#{template_id}")
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Instantiate
|
||||
#
|
||||
|
||||
instantiate_desc = <<-EOT.unindent
|
||||
Instantiate a Service Template
|
||||
EOT
|
||||
|
||||
command :instantiate, instantiate_desc, :template_id,
|
||||
:options => [Service::JSON_FORMAT, Service::TOP] do
|
||||
client = Service::Client.new(
|
||||
:username => options[:username],
|
||||
:password => options[:password],
|
||||
:url => options[:server],
|
||||
:user_agent => USER_AGENT)
|
||||
|
||||
json_str = Service.build_json_action('instantiate')
|
||||
|
||||
response = client.post("#{RESOURCE_PATH}/#{args[0]}/action", json_str)
|
||||
|
||||
if CloudClient::is_error?(response)
|
||||
[response.code.to_i, response.to_s]
|
||||
else
|
||||
if options[:json]
|
||||
[0,response.body]
|
||||
else
|
||||
template = JSON.parse(response.body)
|
||||
puts "ID: #{template['DOCUMENT']['ID']}"
|
||||
0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
chgrp_desc = <<-EOT.unindent
|
||||
Changes the service template group
|
||||
EOT
|
||||
|
||||
command :chgrp, chgrp_desc, [:range, :templateid_list], :groupid do
|
||||
client = Service::Client.new(
|
||||
:username => options[:username],
|
||||
:password => options[:password],
|
||||
:url => options[:server],
|
||||
:user_agent => USER_AGENT)
|
||||
|
||||
Service.perform_actions(args[0]) { |service_id|
|
||||
params = Hash.new
|
||||
params['group_id'] = args[1].to_i
|
||||
|
||||
json_action = Service.build_json_action('chgrp', params)
|
||||
|
||||
client.post("#{RESOURCE_PATH}/#{service_id}/action", json_action)
|
||||
}
|
||||
end
|
||||
|
||||
chown_desc = <<-EOT.unindent
|
||||
Changes the service template owner and group
|
||||
EOT
|
||||
|
||||
command :chown, chown_desc, [:range, :templateid_list], :userid, [:groupid, nil] do
|
||||
client = Service::Client.new(
|
||||
:username => options[:username],
|
||||
:password => options[:password],
|
||||
:url => options[:server],
|
||||
:user_agent => USER_AGENT)
|
||||
|
||||
Service.perform_actions(args[0]) { |service_id|
|
||||
params = Hash.new
|
||||
params['owner_id'] = args[1]
|
||||
params['group_id'] = args[2] if args[2]
|
||||
|
||||
json_action = Service.build_json_action('chown', params)
|
||||
|
||||
client.post("#{RESOURCE_PATH}/#{service_id}/action", json_action)
|
||||
}
|
||||
end
|
||||
|
||||
chmod_desc = <<-EOT.unindent
|
||||
Changes the service template permissions
|
||||
EOT
|
||||
|
||||
command :chmod, chmod_desc, [:range, :templateid_list], :octet do
|
||||
client = Service::Client.new(
|
||||
:username => options[:username],
|
||||
:password => options[:password],
|
||||
:url => options[:server],
|
||||
:user_agent => USER_AGENT)
|
||||
|
||||
Service.perform_actions(args[0]) { |service_id|
|
||||
params = Hash.new
|
||||
params['octet'] = args[1]
|
||||
|
||||
json_action = Service.build_json_action('chmod', params)
|
||||
|
||||
client.post("#{RESOURCE_PATH}/#{service_id}/action", json_action)
|
||||
}
|
||||
end
|
||||
|
||||
update_desc = <<-EOT.unindent
|
||||
Update the template contents. If a path is not provided the editor will
|
||||
be launched to modify the current content.
|
||||
EOT
|
||||
|
||||
command :update, update_desc, :templateid, [:file, nil] do
|
||||
template_id = args[0]
|
||||
client = Service::Client.new(
|
||||
:username => options[:username],
|
||||
:password => options[:password],
|
||||
:url => options[:server],
|
||||
:user_agent => USER_AGENT)
|
||||
|
||||
if args[1]
|
||||
path = args[1]
|
||||
else
|
||||
require 'tempfile'
|
||||
|
||||
tmp = Tempfile.new(template_id.to_s)
|
||||
path = tmp.path
|
||||
|
||||
response = client.get("#{RESOURCE_PATH}/#{template_id}")
|
||||
|
||||
if CloudClient::is_error?(response)
|
||||
exit_with_code response.code.to_i, response.to_s
|
||||
else
|
||||
document_hash = JSON.parse(response.body)
|
||||
template = document_hash['DOCUMENT']['TEMPLATE']['BODY']
|
||||
|
||||
tmp << JSON.pretty_generate(template)
|
||||
tmp.flush
|
||||
|
||||
editor_path = ENV["EDITOR"] ? ENV["EDITOR"] : OpenNebulaHelper::EDITOR_PATH
|
||||
system("#{editor_path} #{path}")
|
||||
|
||||
unless $?.exitstatus == 0
|
||||
puts "Editor not defined"
|
||||
exit -1
|
||||
end
|
||||
|
||||
tmp.close
|
||||
end
|
||||
end
|
||||
|
||||
exit_code = 0
|
||||
|
||||
t_str = File.read(path)
|
||||
response = client.put("#{RESOURCE_PATH}/#{template_id}", t_str)
|
||||
if CloudClient::is_error?(response)
|
||||
puts response.to_s
|
||||
exit_code = response.code.to_i
|
||||
end
|
||||
|
||||
exit_code
|
||||
end
|
||||
end
|
7
src/flow/Gemfile
Normal file
7
src/flow/Gemfile
Normal file
@ -0,0 +1,7 @@
|
||||
source "http://rubygems.org"
|
||||
|
||||
gem 'sinatra'
|
||||
gem 'json'
|
||||
gem 'xml-simple'
|
||||
gem 'treetop'
|
||||
gem 'parse-cron'
|
28
src/flow/Gemfile.lock
Normal file
28
src/flow/Gemfile.lock
Normal file
@ -0,0 +1,28 @@
|
||||
GEM
|
||||
remote: http://rubygems.org/
|
||||
specs:
|
||||
json (1.7.3)
|
||||
parse-cron (0.1.2)
|
||||
polyglot (0.3.3)
|
||||
rack (1.4.1)
|
||||
rack-protection (1.2.0)
|
||||
rack
|
||||
sinatra (1.3.2)
|
||||
rack (~> 1.3, >= 1.3.6)
|
||||
rack-protection (~> 1.2)
|
||||
tilt (~> 1.3, >= 1.3.3)
|
||||
tilt (1.3.3)
|
||||
treetop (1.4.12)
|
||||
polyglot
|
||||
polyglot (>= 0.3.1)
|
||||
xml-simple (1.1.1)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
json
|
||||
parse-cron
|
||||
sinatra
|
||||
treetop
|
||||
xml-simple
|
123
src/flow/bin/oneflow-server
Executable file
123
src/flow/bin/oneflow-server
Executable file
@ -0,0 +1,123 @@
|
||||
#! /bin/sh
|
||||
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2010-2013, C12G Labs S.L. #
|
||||
# #
|
||||
# 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. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
if [ -z "$ONE_LOCATION" ]; then
|
||||
FLOW_PID=/var/run/one/oneflow.pid
|
||||
FLOW_SERVER=/usr/lib/one/oneflow/oneflow-server.rb
|
||||
FLOW_LOCK_FILE=/var/lock/one/.oneflow.lock
|
||||
FLOW_LOG=/var/log/one/oneflow.log
|
||||
FLOW_LOG_ERROR=/var/log/one/oneflow.error
|
||||
FLOW_CONF=/etc/one/oneflow-server.conf
|
||||
else
|
||||
FLOW_PID=$ONE_LOCATION/var/oneflow.pid
|
||||
FLOW_SERVER=$ONE_LOCATION/lib/oneflow/oneflow-server.rb
|
||||
FLOW_LOCK_FILE=$ONE_LOCATION/var/.oneflow.lock
|
||||
FLOW_LOG=$ONE_LOCATION/var/oneflow.log
|
||||
FLOW_LOG_ERROR=$ONE_LOCATION/var/oneflow.error
|
||||
FLOW_CONF=$ONE_LOCATION/etc/oneflow-server.conf
|
||||
fi
|
||||
|
||||
setup()
|
||||
{
|
||||
if [ -f $FLOW_LOCK_FILE ]; then
|
||||
if [ -f $FLOW_PID ]; then
|
||||
FLOWPID=`cat $FLOW_PID`
|
||||
ps $FLOWPID > /dev/null 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "OneFlow Server is still running (PID:$FLOWPID)."
|
||||
echo "Please try 'oneflow-server stop' first."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
echo "Stale .lock detected. Erasing it."
|
||||
rm $FLOW_LOCK_FILE
|
||||
fi
|
||||
}
|
||||
|
||||
start()
|
||||
{
|
||||
if [ ! -f "$FLOW_SERVER" ]; then
|
||||
echo "Cannot find $FLOW_SERVER."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
touch $FLOW_LOCK_FILE
|
||||
|
||||
# Start the oneflow-server daemon
|
||||
ruby $FLOW_SERVER >$FLOW_LOG 2>$FLOW_LOG_ERROR &
|
||||
|
||||
LASTRC=$?
|
||||
LASTPID=$!
|
||||
|
||||
if [ $LASTRC -ne 0 ]; then
|
||||
echo "Error executing oneflow-server."
|
||||
echo "Check $FLOW_LOG_ERROR and $FLOW_LOG for more information"
|
||||
exit 1
|
||||
else
|
||||
echo $LASTPID > $FLOW_PID
|
||||
fi
|
||||
|
||||
sleep 2
|
||||
ps $LASTPID > /dev/null 2>&1
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error executing oneflow-server."
|
||||
echo "Check $FLOW_LOG_ERROR and $FLOW_LOG for more information"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "oneflow-server started"
|
||||
}
|
||||
|
||||
#
|
||||
# Function that stops the daemon/service
|
||||
#
|
||||
stop()
|
||||
{
|
||||
if [ ! -f $FLOW_PID ]; then
|
||||
echo "Couldn't find oneflow-server process pid."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Kill the oneflow-server daemon
|
||||
|
||||
kill -INT `cat $FLOW_PID` > /dev/null 2>&1
|
||||
|
||||
# Remove pid files
|
||||
rm -f $FLOW_PID > /dev/null 2>&1
|
||||
rm -f $FLOW_LOCK_FILE &> /dev/null
|
||||
|
||||
echo "oneflow-server stopped"
|
||||
}
|
||||
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
setup
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
*)
|
||||
echo "Usage: oneflow-server {start|stop}" >&2
|
||||
exit 3
|
||||
;;
|
||||
esac
|
||||
|
||||
|
20
src/flow/config.ru
Normal file
20
src/flow/config.ru
Normal file
@ -0,0 +1,20 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2010-2013, C12G Labs S.L. #
|
||||
# #
|
||||
# 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. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
$: << '.'
|
||||
require 'oneflow-server'
|
||||
|
||||
run Sinatra::Application
|
63
src/flow/etc/oneflow-server.conf
Normal file
63
src/flow/etc/oneflow-server.conf
Normal file
@ -0,0 +1,63 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2010-2013, C12G Labs S.L. #
|
||||
# #
|
||||
# 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. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
################################################################################
|
||||
# Server Configuration
|
||||
################################################################################
|
||||
|
||||
# OpenNebula daemon contact information
|
||||
#
|
||||
:one_xmlrpc: http://localhost:2633/RPC2
|
||||
|
||||
# Time in seconds between Life Cycle Manager steps
|
||||
#
|
||||
:lcm_interval: 30
|
||||
|
||||
# Host and port where OneFlow server will run
|
||||
:host: 127.0.0.1
|
||||
:port: 2474
|
||||
|
||||
################################################################################
|
||||
# Defaults
|
||||
################################################################################
|
||||
|
||||
# Default cooldown period after a scale operation, in seconds
|
||||
:default_cooldown: 300
|
||||
|
||||
# Default shutdown action. Values: 'shutdown', 'shutdown-hard'
|
||||
:shutdown_action: 'shutdown'
|
||||
|
||||
# Default oneflow action options when only one is supplied
|
||||
:action_number: 1
|
||||
:action_period: 60
|
||||
|
||||
#############################################################
|
||||
# Auth
|
||||
#############################################################
|
||||
|
||||
# Authentication driver to communicate with OpenNebula core
|
||||
# - cipher, for symmetric cipher encryption of tokens
|
||||
# - x509, for x509 certificate encryption of tokens
|
||||
:core_auth: cipher
|
||||
|
||||
################################################################################
|
||||
# Log
|
||||
################################################################################
|
||||
|
||||
# Log debug level
|
||||
# 0 = ERROR, 1 = WARNING, 2 = INFO, 3 = DEBUG
|
||||
#
|
||||
:debug_level: 2
|
174
src/flow/lib/LifeCycleManager.rb
Normal file
174
src/flow/lib/LifeCycleManager.rb
Normal file
@ -0,0 +1,174 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2010-2013, C12G Labs S.L. #
|
||||
# #
|
||||
# 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. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
require 'strategy'
|
||||
|
||||
class ServiceLCM
|
||||
|
||||
LOG_COMP = "LCM"
|
||||
|
||||
def initialize(sleep_time, cloud_auth)
|
||||
@sleep_time = sleep_time
|
||||
@cloud_auth = cloud_auth
|
||||
end
|
||||
|
||||
def loop()
|
||||
Log.info LOG_COMP, "Starting Life Cycle Manager"
|
||||
|
||||
while true
|
||||
srv_pool = ServicePool.new(@cloud_auth.client)
|
||||
|
||||
rc = srv_pool.info_all()
|
||||
|
||||
if OpenNebula.is_error?(rc)
|
||||
Log.error LOG_COMP, "Error retrieving the Service Pool: #{rc.message}"
|
||||
else
|
||||
srv_pool.each_xpath('DOCUMENT/ID') { |id|
|
||||
|
||||
service = srv_pool.get(id.to_i) { |service|
|
||||
|
||||
owner_client = @cloud_auth.client(service.owner_name)
|
||||
service.replace_client(owner_client)
|
||||
|
||||
Log.debug LOG_COMP, "Loop for service #{service.id()} #{service.name()}" \
|
||||
" #{service.state_str()} #{service.strategy()}"
|
||||
|
||||
strategy = get_deploy_strategy(service)
|
||||
|
||||
case service.state()
|
||||
when Service::STATE['PENDING']
|
||||
service.set_state(Service::STATE['DEPLOYING'])
|
||||
|
||||
rc = strategy.boot_step(service)
|
||||
if !rc[0]
|
||||
service.set_state(Service::STATE['FAILED_DEPLOYING'])
|
||||
end
|
||||
when Service::STATE['DEPLOYING']
|
||||
strategy.monitor_step(service)
|
||||
|
||||
if service.all_roles_running?
|
||||
service.set_state(Service::STATE['RUNNING'])
|
||||
elsif service.any_role_failed?
|
||||
service.set_state(Service::STATE['FAILED_DEPLOYING'])
|
||||
else
|
||||
rc = strategy.boot_step(service)
|
||||
if !rc[0]
|
||||
service.set_state(Service::STATE['FAILED_DEPLOYING'])
|
||||
end
|
||||
end
|
||||
when Service::STATE['RUNNING'], Service::STATE['WARNING']
|
||||
strategy.monitor_step(service)
|
||||
|
||||
if service.all_roles_running?
|
||||
if service.state() == Service::STATE['WARNING']
|
||||
service.set_state(Service::STATE['RUNNING'])
|
||||
end
|
||||
else
|
||||
if service.state() == Service::STATE['RUNNING']
|
||||
service.set_state(Service::STATE['WARNING'])
|
||||
end
|
||||
end
|
||||
|
||||
if strategy.apply_scaling_policies(service)
|
||||
service.set_state(Service::STATE['SCALING'])
|
||||
|
||||
rc = strategy.scale_step(service)
|
||||
if !rc[0]
|
||||
service.set_state(Service::STATE['FAILED_SCALING'])
|
||||
end
|
||||
end
|
||||
when Service::STATE['SCALING']
|
||||
strategy.monitor_step(service)
|
||||
|
||||
if service.any_role_failed_scaling?
|
||||
service.set_state(Service::STATE['FAILED_SCALING'])
|
||||
elsif service.any_role_cooldown?
|
||||
service.set_state(Service::STATE['COOLDOWN'])
|
||||
elsif !service.any_role_scaling?
|
||||
service.set_state(Service::STATE['RUNNING'])
|
||||
else
|
||||
rc = strategy.scale_step(service)
|
||||
if !rc[0]
|
||||
service.set_state(Service::STATE['FAILED_SCALING'])
|
||||
end
|
||||
end
|
||||
when Service::STATE['COOLDOWN']
|
||||
strategy.monitor_step(service)
|
||||
|
||||
if !service.any_role_cooldown?
|
||||
service.set_state(Service::STATE['RUNNING'])
|
||||
end
|
||||
when Service::STATE['FAILED_SCALING']
|
||||
strategy.monitor_step(service)
|
||||
|
||||
if !service.any_role_failed_scaling?
|
||||
service.set_state(Service::STATE['SCALING'])
|
||||
end
|
||||
when Service::STATE['UNDEPLOYING']
|
||||
strategy.monitor_step(service)
|
||||
|
||||
if service.all_roles_done?
|
||||
service.set_state(Service::STATE['DONE'])
|
||||
elsif service.any_role_failed?
|
||||
service.set_state(Service::STATE['FAILED_UNDEPLOYING'])
|
||||
else
|
||||
rc = strategy.shutdown_step(service)
|
||||
if !rc[0]
|
||||
service.set_state(Service::STATE['FAILED_UNDEPLOYING'])
|
||||
end
|
||||
end
|
||||
when Service::STATE['FAILED_DEPLOYING']
|
||||
strategy.monitor_step(service)
|
||||
|
||||
if !service.any_role_failed?
|
||||
service.set_state(Service::STATE['DEPLOYING'])
|
||||
end
|
||||
when Service::STATE['FAILED_UNDEPLOYING']
|
||||
strategy.monitor_step(service)
|
||||
|
||||
if !service.any_role_failed?
|
||||
service.set_state(Service::STATE['UNDEPLOYING'])
|
||||
end
|
||||
end
|
||||
|
||||
rc = service.update()
|
||||
if OpenNebula.is_error?(rc)
|
||||
Log.error LOG_COMP, "Error trying to update " <<
|
||||
"Service #{service.id()} : #{rc.message}"
|
||||
end
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
sleep @sleep_time
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
# Returns the deployment strategy for the given Service
|
||||
# @param [Service] service the service
|
||||
# @return [Strategy] the deployment Strategy
|
||||
def get_deploy_strategy(service)
|
||||
strategy = Strategy.new
|
||||
|
||||
case service.strategy
|
||||
when 'straight'
|
||||
strategy.extend(Straight)
|
||||
end
|
||||
|
||||
return strategy
|
||||
end
|
||||
end
|
1153
src/flow/lib/grammar.rb
Normal file
1153
src/flow/lib/grammar.rb
Normal file
File diff suppressed because it is too large
Load Diff
234
src/flow/lib/grammar.treetop
Normal file
234
src/flow/lib/grammar.treetop
Normal file
@ -0,0 +1,234 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2010-2013, C12G Labs S.L. #
|
||||
# #
|
||||
# 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. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
grammar ElasticityGrammar
|
||||
|
||||
rule expression
|
||||
space exp:(boolean_exp / logic_cond) space {
|
||||
def result(role)
|
||||
return exp.result(role)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
rule boolean_exp
|
||||
left:logic_cond space op:('&&' / '&') space right:expression {
|
||||
def result(role)
|
||||
l_val, l_st = left.result(role)
|
||||
r_val, r_st = right.result(role)
|
||||
|
||||
st = "#{l_st} #{op.text_value} #{r_st}"
|
||||
val = l_val && r_val
|
||||
|
||||
return [val, st]
|
||||
end
|
||||
}
|
||||
/
|
||||
left:logic_cond space op:('||' / '|') space right:expression {
|
||||
def result(role)
|
||||
l_val, l_st = left.result(role)
|
||||
r_val, r_st = right.result(role)
|
||||
|
||||
st = "#{l_st} #{op.text_value} #{r_st}"
|
||||
val = l_val || r_val
|
||||
|
||||
return [val, st]
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
rule logic_cond
|
||||
# 'true' {
|
||||
# def result(role)
|
||||
# return true
|
||||
# end
|
||||
# }
|
||||
# /
|
||||
# 'false' {
|
||||
# def result(role)
|
||||
# return false
|
||||
# end
|
||||
# }
|
||||
# /
|
||||
left:operand space comp_op space right:operand {
|
||||
def result(role)
|
||||
l_val, l_st = left.result(role)
|
||||
r_val, r_st = right.result(role)
|
||||
|
||||
st = "#{l_st} #{comp_op.text_value} #{r_st}"
|
||||
|
||||
if l_val.nil? || r_val.nil?
|
||||
# An attribute was not found, we return false instead
|
||||
# of assuming a value of 0
|
||||
|
||||
val = false
|
||||
else
|
||||
val = comp_op.apply(l_val, r_val)
|
||||
end
|
||||
|
||||
return [val, st]
|
||||
end
|
||||
}
|
||||
/
|
||||
'!' space expression {
|
||||
def result(role)
|
||||
e_val, e_st = expression.result(role)
|
||||
|
||||
val = !e_val
|
||||
st = "!#{e_st}"
|
||||
|
||||
return [val, st]
|
||||
end
|
||||
}
|
||||
/
|
||||
'(' space expression space ')' {
|
||||
def result(role)
|
||||
e_val, e_st = expression.result(role)
|
||||
|
||||
st = "(#{e_st})"
|
||||
|
||||
return [e_val, st]
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
rule comp_op
|
||||
('==' / '=') {
|
||||
def apply(a,b)
|
||||
a == b
|
||||
end
|
||||
}
|
||||
/
|
||||
('!=' / '<>') {
|
||||
def apply(a,b)
|
||||
a != b
|
||||
end
|
||||
}
|
||||
/
|
||||
'>=' {
|
||||
def apply(a,b)
|
||||
a >= b
|
||||
end
|
||||
}
|
||||
/
|
||||
'>' {
|
||||
def apply(a,b)
|
||||
a > b
|
||||
end
|
||||
}
|
||||
/
|
||||
'<=' {
|
||||
def apply(a,b)
|
||||
a <= b
|
||||
end
|
||||
}
|
||||
/
|
||||
'<' {
|
||||
def apply(a,b)
|
||||
a < b
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
rule operand
|
||||
( number ) {
|
||||
def result
|
||||
number.result(role)
|
||||
end
|
||||
}
|
||||
/
|
||||
( variable ) {
|
||||
def result
|
||||
variable.result(role)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
rule number
|
||||
'-'? [0-9]+ '.' [0-9]+ {
|
||||
def result(role)
|
||||
val = text_value.to_f
|
||||
st = val.to_s
|
||||
|
||||
return [val, st]
|
||||
end
|
||||
}
|
||||
/
|
||||
'-'? [0-9]+ {
|
||||
def result(role)
|
||||
val = text_value.to_i
|
||||
st = val.to_s
|
||||
|
||||
return [val, st]
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
rule variable
|
||||
( '"' [a-zA-Z] [0-9a-zA-Z_]* '"'
|
||||
/ '\'' [a-zA-Z] [0-9a-zA-Z_]* '\''
|
||||
/ [a-zA-Z] [0-9a-zA-Z_]*
|
||||
) {
|
||||
|
||||
def result(role)
|
||||
nodes = role.get_nodes
|
||||
total = 0
|
||||
n_nodes = 0
|
||||
att = text_value.upcase
|
||||
|
||||
nodes.each { |node|
|
||||
if node && node['vm_info']
|
||||
|
||||
vm_state = node['vm_info']['VM']['STATE']
|
||||
lcm_state = node['vm_info']['VM']['LCM_STATE']
|
||||
|
||||
# Use values from VMs in RUNNING only
|
||||
|
||||
if vm_state != '3' || lcm_state != '3'
|
||||
next
|
||||
end
|
||||
|
||||
if node['vm_info']['VM']['USER_TEMPLATE'][att]
|
||||
total += (node['vm_info']['VM']['USER_TEMPLATE'][att]).to_f
|
||||
n_nodes += 1
|
||||
elsif node['vm_info']['VM'][att]
|
||||
total += (node['vm_info']['VM'][att]).to_f
|
||||
n_nodes += 1
|
||||
elsif node['vm_info']['VM']['TEMPLATE'][att]
|
||||
total += (node['vm_info']['VM']['TEMPLATE'][att]).to_f
|
||||
n_nodes += 1
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
# The attribute wasn't found for any of the nodes
|
||||
if n_nodes == 0
|
||||
val = nil
|
||||
st = "#{att}[--]"
|
||||
else
|
||||
val = total / n_nodes
|
||||
st = "#{att}[#{val.to_s}]"
|
||||
end
|
||||
|
||||
return [val, st]
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
rule space
|
||||
' '*
|
||||
end
|
||||
end
|
157
src/flow/lib/log.rb
Normal file
157
src/flow/lib/log.rb
Normal file
@ -0,0 +1,157 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2010-2013, C12G Labs S.L. #
|
||||
# #
|
||||
# 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. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
class Log
|
||||
|
||||
class << self
|
||||
# This class handles logging for each service and for the service server
|
||||
# Log.info("SERVER", "It works"), it will be written in the main log
|
||||
# Log.info("LCM", "Service 3 started", 3), it will be written in a
|
||||
# specific log for service number 3
|
||||
|
||||
LOG_COMP = "LOG"
|
||||
|
||||
DEBUG_LEVEL = [
|
||||
Logger::ERROR, # 0
|
||||
Logger::WARN, # 1
|
||||
Logger::INFO, # 2
|
||||
Logger::DEBUG # 3
|
||||
]
|
||||
|
||||
#LOG_LOCATION = '/var/log/one'
|
||||
|
||||
@log_level = Logger::DEBUG
|
||||
|
||||
# Mon Feb 27 06:02:30 2012 [Clo] [E]: Error message example
|
||||
MSG_FORMAT = %{%s [%s]: [%s] %s\n}
|
||||
|
||||
# Mon Feb 27 06:02:30 2012
|
||||
DATE_FORMAT = "%a %b %d %H:%M:%S %Y"
|
||||
|
||||
# Message to be used in CloudLogger
|
||||
CLOUD_LOGGER_MSG = %{[%s] %s}
|
||||
|
||||
# Sets the server logger
|
||||
# @param [CloudLogger::CloudLogger, Logger] logger
|
||||
def logger=(logger)
|
||||
@@logger = logger
|
||||
end
|
||||
|
||||
# Sets the log level
|
||||
# @param [Integer] log_level (0:ERROR, 1:WARN, 2:INFO, 3:DEBUG)
|
||||
def level=(log_level)
|
||||
@@log_level = DEBUG_LEVEL[log_level]
|
||||
end
|
||||
|
||||
# Writes a info message to the log. If a service_id is specified the
|
||||
# message will be written to the service log, otherwise the server log
|
||||
# will be used.
|
||||
# @param [String] component
|
||||
# @param [String] message
|
||||
# @param [String] service_id
|
||||
#
|
||||
# @example
|
||||
# Mon Feb 27 06:02:30 2012 [<component>] [I]: <message>
|
||||
def info(component, message, service_id=nil)
|
||||
if service_id
|
||||
add(Logger::INFO, component, message, service_id)
|
||||
else
|
||||
@@logger.info(CLOUD_LOGGER_MSG % [component, message])
|
||||
end
|
||||
end
|
||||
|
||||
# Writes a debug message to the log. If a service_id is specified the
|
||||
# message will be written to the service log, otherwise the server log
|
||||
# will be used.
|
||||
# @param [String] component
|
||||
# @param [String] message
|
||||
# @param [String] service_id
|
||||
#
|
||||
# @example
|
||||
# Mon Feb 27 06:02:30 2012 [<component>] [D]: <message>
|
||||
def debug(component, message, service_id=nil)
|
||||
if service_id
|
||||
add(Logger::DEBUG, component, message, service_id)
|
||||
else
|
||||
@@logger.debug(CLOUD_LOGGER_MSG % [component, message])
|
||||
end
|
||||
end
|
||||
|
||||
# Writes a error message to the log. If a service_id is specified the
|
||||
# message will be written to the service log, otherwise the server log
|
||||
# will be used.
|
||||
# @param [String] component
|
||||
# @param [String] message
|
||||
# @param [String] service_id
|
||||
#
|
||||
# @example
|
||||
# Mon Feb 27 06:02:30 2012 [<component>] [E]: <message>
|
||||
def error(component, message, service_id=nil)
|
||||
if service_id
|
||||
add(Logger::ERROR, component, message, service_id)
|
||||
else
|
||||
@@logger.error(CLOUD_LOGGER_MSG % [component, message])
|
||||
end
|
||||
end
|
||||
|
||||
# Writes a warn message to the log. If a service_id is specified the
|
||||
# message will be written to the service log, otherwise the server log
|
||||
# will be used.
|
||||
# @param [String] component
|
||||
# @param [String] message
|
||||
# @param [String] service_id
|
||||
#
|
||||
# @example
|
||||
# Mon Feb 27 06:02:30 2012 [<component>] [W]: <message>
|
||||
def warn(component, message, service_id=nil)
|
||||
if service_id
|
||||
add(Logger::WARN, component, message, service_id)
|
||||
else
|
||||
@@logger.warn(CLOUD_LOGGER_MSG % [component, message])
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add(severity, component, message, service_id)
|
||||
if severity < @@log_level
|
||||
return true
|
||||
end
|
||||
|
||||
begin
|
||||
msg = MSG_FORMAT % [
|
||||
Time.now.strftime(DATE_FORMAT),
|
||||
Logger::SEV_LABEL[severity][0..0],
|
||||
component,
|
||||
message ]
|
||||
|
||||
open("#{LOG_LOCATION}/oneflow/#{service_id}.log", 'a') do |f|
|
||||
f << msg
|
||||
end
|
||||
rescue Errno::ENOENT => e
|
||||
FileUtils.mkdir("#{LOG_LOCATION}/oneflow/")
|
||||
|
||||
open("#{LOG_LOCATION}/oneflow/#{service_id}.log", 'a') do |f|
|
||||
f << msg
|
||||
end
|
||||
rescue => e
|
||||
message = "Could not log into #{LOG_LOCATION}/oneflow/#{service_id}.log: #{e.message}"
|
||||
error LOG_COMP, message
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
30
src/flow/lib/models.rb
Normal file
30
src/flow/lib/models.rb
Normal file
@ -0,0 +1,30 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2010-2013, C12G Labs S.L. #
|
||||
# #
|
||||
# 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. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
require 'opennebula'
|
||||
|
||||
include OpenNebula
|
||||
|
||||
require 'opennebula/document_json'
|
||||
require 'opennebula/document_pool_json'
|
||||
|
||||
require 'validator'
|
||||
|
||||
require 'models/role.rb'
|
||||
require 'models/service_pool.rb'
|
||||
require 'models/service.rb'
|
||||
require 'models/service_template_pool.rb'
|
||||
require 'models/service_template.rb'
|
899
src/flow/lib/models/role.rb
Normal file
899
src/flow/lib/models/role.rb
Normal file
@ -0,0 +1,899 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2010-2013, C12G Labs S.L. #
|
||||
# #
|
||||
# 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. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
require 'treetop'
|
||||
require 'grammar'
|
||||
require 'parse-cron'
|
||||
|
||||
module OpenNebula
|
||||
class Role
|
||||
|
||||
# Actions that can be performed on the VMs of a given Role
|
||||
SCHEDULE_ACTIONS = [
|
||||
'shutdown',
|
||||
'delete',
|
||||
'hold',
|
||||
'release',
|
||||
'stop',
|
||||
'shutdown-hard',
|
||||
'suspend',
|
||||
'resume',
|
||||
'boot',
|
||||
'delete-recreate',
|
||||
'reboot',
|
||||
'reboot-hard',
|
||||
'poweroff',
|
||||
'snapshot-create'
|
||||
]
|
||||
|
||||
STATE = {
|
||||
'PENDING' => 0,
|
||||
'DEPLOYING' => 1,
|
||||
'RUNNING' => 2,
|
||||
'UNDEPLOYING' => 3,
|
||||
'WARNING' => 4,
|
||||
'DONE' => 5,
|
||||
'FAILED_UNDEPLOYING' => 6,
|
||||
'FAILED_DEPLOYING' => 7,
|
||||
'SCALING' => 8,
|
||||
'FAILED_SCALING' => 9,
|
||||
'COOLDOWN' => 10
|
||||
}
|
||||
|
||||
STATE_STR = [
|
||||
'PENDING',
|
||||
'DEPLOYING',
|
||||
'RUNNING',
|
||||
'UNDEPLOYING',
|
||||
'WARNING',
|
||||
'DONE',
|
||||
'FAILED_UNDEPLOYING',
|
||||
'FAILED_DEPLOYING',
|
||||
'SCALING',
|
||||
'FAILED_SCALING',
|
||||
'COOLDOWN'
|
||||
]
|
||||
|
||||
LOG_COMP = "ROL"
|
||||
|
||||
def initialize(body, service)
|
||||
@body = body
|
||||
@service = service
|
||||
|
||||
@body['nodes'] ||= []
|
||||
@body['disposed_nodes'] ||= []
|
||||
end
|
||||
|
||||
def name
|
||||
return @body['name']
|
||||
end
|
||||
|
||||
# Returns the role state
|
||||
# @return [Integer] the role state
|
||||
def state
|
||||
return @body['state'].to_i
|
||||
end
|
||||
|
||||
# Returns the role parents
|
||||
# @return [Array] the role parents
|
||||
def parents
|
||||
return @body['parents'] || []
|
||||
end
|
||||
|
||||
# Returns the role cardinality
|
||||
# @return [Integer] the role cardinality
|
||||
def cardinality
|
||||
return @body['cardinality'].to_i
|
||||
end
|
||||
|
||||
# Sets a new cardinality for this role
|
||||
# @param [Integer] the new cardinality
|
||||
def set_cardinality(target_cardinality)
|
||||
dir = target_cardinality > cardinality ? "up" : "down"
|
||||
msg = "Role #{name} scaling #{dir} from #{cardinality} to #{target_cardinality} nodes"
|
||||
Log.info LOG_COMP, msg, @service.id()
|
||||
@service.log_info(msg)
|
||||
|
||||
@body['cardinality'] = target_cardinality.to_i
|
||||
end
|
||||
|
||||
# Updates the cardinality with the current number of nodes
|
||||
def update_cardinality()
|
||||
@body['cardinality'] = @body['nodes'].size()
|
||||
end
|
||||
|
||||
# Returns the role max cardinality
|
||||
# @return [Integer,nil] the role cardinality, or nil if it was not defined
|
||||
def max_cardinality
|
||||
max = @body['max_vms']
|
||||
|
||||
return nil if max.nil?
|
||||
|
||||
return max.to_i
|
||||
end
|
||||
|
||||
# Returns the role min cardinality
|
||||
# @return [Integer,nil] the role cardinality, or nil if it was not defined
|
||||
def min_cardinality
|
||||
min = @body['min_vms']
|
||||
|
||||
return nil if min.nil?
|
||||
|
||||
return min.to_i
|
||||
end
|
||||
|
||||
# Returns the string representation of the service state
|
||||
# @return [String] the state string
|
||||
def state_str
|
||||
return STATE_STR[state]
|
||||
end
|
||||
|
||||
# Returns the nodes of the role
|
||||
# @return [Array] the nodes
|
||||
def get_nodes
|
||||
@body['nodes']
|
||||
end
|
||||
|
||||
# Sets a new state
|
||||
# @param [Integer] the new state
|
||||
# @return [true, false] true if the value was changed
|
||||
def set_state(state)
|
||||
if state < 0 || state > STATE_STR.size
|
||||
return false
|
||||
end
|
||||
|
||||
@body['state'] = state.to_s
|
||||
|
||||
if state == STATE['SCALING']
|
||||
|
||||
elasticity_pol = @body['elasticity_policies']
|
||||
|
||||
if !elasticity_pol.nil?
|
||||
elasticity_pol.each do |policy|
|
||||
policy.delete('true_evals')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Log.info LOG_COMP, "Role #{name} new state: #{STATE_STR[state]}", @service.id()
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
# Retrieves the VM information for each Node in this Role. If a Node
|
||||
# is to be disposed and it is found in DONE, it will be cleaned
|
||||
#
|
||||
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
||||
# otherwise
|
||||
def info
|
||||
success = true
|
||||
|
||||
nodes = @body['nodes']
|
||||
new_nodes = []
|
||||
disposed_nodes = @body['disposed_nodes']
|
||||
|
||||
nodes.each do |node|
|
||||
vm_id = node['deploy_id']
|
||||
vm = OpenNebula::VirtualMachine.new_with_id(vm_id, @service.client)
|
||||
rc = vm.info
|
||||
|
||||
if OpenNebula.is_error?(rc)
|
||||
msg = "Role #{name} : VM #{vm_id} monitorization failed; #{rc.message}"
|
||||
Log.error LOG_COMP, msg, @service.id()
|
||||
@service.log_error(msg)
|
||||
|
||||
success = false
|
||||
node['vm_info'] = nil
|
||||
|
||||
new_nodes << node
|
||||
else
|
||||
node['vm_info'] = vm.to_hash
|
||||
|
||||
vm_state = node['vm_info']['VM']['STATE']
|
||||
lcm_state = node['vm_info']['VM']['LCM_STATE']
|
||||
|
||||
if (vm_state == '6')
|
||||
# Store the VM id in the array of disposed nodes
|
||||
disposed_nodes << vm_id
|
||||
else
|
||||
if (node['scale_up'] == "1" && vm_state == '3' && lcm_state == '3')
|
||||
# If the VM was a scale-up and it reaches RUNNING,
|
||||
# clear the flag
|
||||
node.delete('scale_up')
|
||||
end
|
||||
|
||||
new_nodes << node
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@body['nodes'] = new_nodes
|
||||
|
||||
if !success
|
||||
return OpenNebula::Error.new()
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
# Deploys all the nodes in this role
|
||||
# @return [Array<true, nil>, Array<false, String>] true if all the VMs
|
||||
# were created, false and the error reason if there was a problem
|
||||
# creating the VMs
|
||||
def deploy(scale_up=false)
|
||||
n_nodes = cardinality() - get_nodes.size
|
||||
|
||||
n_nodes.times { |i|
|
||||
vm_name = "#{@body['name']}_#{i}_(service_#{@service.id()})"
|
||||
|
||||
template_id = @body['vm_template']
|
||||
|
||||
Log.debug LOG_COMP, "Role #{name} : Trying to instantiate template "\
|
||||
"#{template_id}, with name #{vm_name}", @service.id()
|
||||
|
||||
template = OpenNebula::Template.new_with_id(template_id, @service.client)
|
||||
|
||||
extra_template = "SERVICE_ID = #{@service.id()}\n"\
|
||||
"ROLE_NAME = #{@body['name']}"
|
||||
|
||||
vm_id = template.instantiate(vm_name, false, extra_template)
|
||||
|
||||
if OpenNebula.is_error?(vm_id)
|
||||
msg = "Role #{name} : Instantiate failed for template #{template_id}; #{vm_id.message}"
|
||||
Log.error LOG_COMP, msg, @service.id()
|
||||
@service.log_error(msg)
|
||||
|
||||
return [false, "Error trying to instantiate the VM Template" \
|
||||
" #{template_id} in Role #{self.name}: #{vm_id.message}"]
|
||||
end
|
||||
|
||||
Log.debug LOG_COMP, "Role #{name} : Instantiate success, VM ID #{vm_id}", @service.id()
|
||||
node = {
|
||||
'deploy_id' => vm_id,
|
||||
}
|
||||
|
||||
vm = OpenNebula::VirtualMachine.new_with_id(vm_id, @service.client)
|
||||
rc = vm.info
|
||||
if OpenNebula.is_error?(rc)
|
||||
node['vm_info'] = nil
|
||||
else
|
||||
node['vm_info'] = vm.to_hash
|
||||
end
|
||||
|
||||
if scale_up
|
||||
node['scale_up'] = '1'
|
||||
end
|
||||
|
||||
@body['nodes'] << node
|
||||
}
|
||||
|
||||
return [true, nil]
|
||||
end
|
||||
|
||||
# Shutdown all the nodes in this role
|
||||
#
|
||||
# @param scale_down [true, false] true to shutdown and dispose the
|
||||
# number of VMs needed to get down to cardinality nodes
|
||||
# @return [Array<true, nil>, Array<false, String>] true if all the VMs
|
||||
# were shutdown, false and the error reason if there was a problem
|
||||
# shutting down the VMs
|
||||
def shutdown(scale_down=false)
|
||||
success = true
|
||||
|
||||
nodes = get_nodes
|
||||
|
||||
if scale_down
|
||||
n_nodes = nodes.size - cardinality()
|
||||
else
|
||||
n_nodes = nodes.size
|
||||
end
|
||||
|
||||
shutdown_nodes(nodes[0..n_nodes-1], scale_down)
|
||||
|
||||
return [success, nil]
|
||||
end
|
||||
|
||||
# Delete all the nodes in this role
|
||||
# @return [Array<true, nil>] All the VMs are deleted, and the return
|
||||
# ignored
|
||||
def delete
|
||||
get_nodes.each { |node|
|
||||
vm_id = node['deploy_id']
|
||||
|
||||
Log.debug LOG_COMP, "Role #{name} : Deleting VM #{vm_id}", @service.id()
|
||||
|
||||
vm = OpenNebula::VirtualMachine.new_with_id(vm_id, @service.client)
|
||||
rc = vm.finalize
|
||||
|
||||
if OpenNebula.is_error?(rc)
|
||||
msg = "Role #{name} : Delete failed for VM #{vm_id}; #{rc.message}"
|
||||
Log.error LOG_COMP, msg, @service.id()
|
||||
@service.log_error(msg)
|
||||
else
|
||||
Log.debug LOG_COMP, "Role #{name} : Delete success for VM #{vm_id}", @service.id()
|
||||
end
|
||||
}
|
||||
|
||||
return [true, nil]
|
||||
end
|
||||
|
||||
# Changes the owner/group of all the nodes in this role
|
||||
#
|
||||
# @param [Integer] uid the new owner id. Set to -1 to leave the current one
|
||||
# @param [Integer] gid the new group id. Set to -1 to leave the current one
|
||||
#
|
||||
# @return [Array<true, nil>, Array<false, String>] true if all the VMs
|
||||
# were updated, false and the error reason if there was a problem
|
||||
# updating the VMs
|
||||
def chown(uid, gid)
|
||||
get_nodes.each { |node|
|
||||
vm_id = node['deploy_id']
|
||||
|
||||
Log.debug LOG_COMP, "Role #{name} : Chown for VM #{vm_id}", @service.id()
|
||||
|
||||
vm = OpenNebula::VirtualMachine.new_with_id(vm_id, @service.client)
|
||||
rc = vm.chown(uid, gid)
|
||||
|
||||
if OpenNebula.is_error?(rc)
|
||||
msg = "Role #{name} : Chown failed for VM #{vm_id}; #{rc.message}"
|
||||
Log.error LOG_COMP, msg, @service.id()
|
||||
@service.log_error(msg)
|
||||
|
||||
return [false, rc.message]
|
||||
else
|
||||
Log.debug LOG_COMP, "Role #{name} : Chown success for VM #{vm_id}", @service.id()
|
||||
end
|
||||
}
|
||||
|
||||
return [true, nil]
|
||||
end
|
||||
|
||||
# Schedule the given action on all the VMs that belong to the Role
|
||||
# @param [String] action one of the available actions defined in SCHEDULE_ACTIONS
|
||||
# @param [Integer] period
|
||||
# @param [Integer] vm_per_period
|
||||
def batch_action(action, period, vms_per_period)
|
||||
vms_id = []
|
||||
|
||||
# TODO: check action is a valid string, period vm_per_period integer
|
||||
|
||||
error_msgs = []
|
||||
nodes = @body['nodes']
|
||||
now = Time.now.to_i
|
||||
|
||||
do_offset = ( !period.nil? && period.to_i > 0 &&
|
||||
!vms_per_period.nil? && vms_per_period.to_i > 0 )
|
||||
|
||||
time_offset = 0
|
||||
|
||||
nodes.each_with_index do |node, index|
|
||||
vm_id = node['deploy_id']
|
||||
vm = OpenNebula::VirtualMachine.new_with_id(vm_id, @service.client)
|
||||
rc = vm.info
|
||||
|
||||
if OpenNebula.is_error?(rc)
|
||||
msg = "Role #{name} : VM #{vm_id} monitorization failed; #{rc.message}"
|
||||
error_msgs << msg
|
||||
Log.error LOG_COMP, msg, @service.id()
|
||||
@service.log_error(msg)
|
||||
else
|
||||
ids = vm.retrieve_elements('USER_TEMPLATE/SCHED_ACTION/ID')
|
||||
|
||||
id = 0
|
||||
if (!ids.nil? && !ids.empty?)
|
||||
ids.map! {|e| e.to_i }
|
||||
id = ids.max + 1
|
||||
end
|
||||
|
||||
tmp_str = vm.user_template_str
|
||||
|
||||
if do_offset
|
||||
time_offset = (index / vms_per_period.to_i).floor * period.to_i
|
||||
end
|
||||
|
||||
tmp_str << "\nSCHED_ACTION = "<<
|
||||
"[ID = #{id}, ACTION = #{action}, TIME = #{now + time_offset}]"
|
||||
|
||||
rc = vm.update(tmp_str)
|
||||
if OpenNebula.is_error?(rc)
|
||||
msg = "Role #{name} : VM #{vm_id} error scheduling action; #{rc.message}"
|
||||
error_msgs << msg
|
||||
Log.error LOG_COMP, msg, @service.id()
|
||||
@service.log_error(msg)
|
||||
else
|
||||
vms_id << vm.id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
log_msg = "Action:#{action} scheduled on Role:#{self.name} VMs:#{vms_id.join(',')}"
|
||||
Log.info LOG_COMP, log_msg, @service.id()
|
||||
|
||||
if error_msgs.empty?
|
||||
return [true, log_msg]
|
||||
else
|
||||
error_msgs << log_msg
|
||||
return [false, error_msgs.join('\n')]
|
||||
end
|
||||
end
|
||||
|
||||
########################################################################
|
||||
# Scalability
|
||||
########################################################################
|
||||
|
||||
# Returns a positive, 0, or negative number of nodes to adjust,
|
||||
# according to the elasticity and scheduled policies
|
||||
# @return [Array<Integer>] positive, 0, or negative number of nodes to
|
||||
# adjust, plus the cooldown period duration
|
||||
def scale?()
|
||||
elasticity_pol = @body['elasticity_policies']
|
||||
scheduled_pol = @body['scheduled_policies']
|
||||
|
||||
elasticity_pol ||= []
|
||||
scheduled_pol ||= []
|
||||
|
||||
scheduled_pol.each do |policy|
|
||||
diff = scale_time?(policy)
|
||||
return [diff, 0] if diff != 0
|
||||
end
|
||||
|
||||
elasticity_pol.each do |policy|
|
||||
diff, cooldown_duration = scale_attributes?(policy)
|
||||
if diff != 0
|
||||
cooldown_duration = @body['cooldown'] if cooldown_duration.nil?
|
||||
cooldown_duration = @@default_cooldown if cooldown_duration.nil?
|
||||
|
||||
return [diff, cooldown_duration]
|
||||
end
|
||||
end
|
||||
|
||||
# Implicit rule that scales up to maintain the min_cardinality, with
|
||||
# no cooldown period
|
||||
if cardinality < min_cardinality.to_i
|
||||
return [min_cardinality.to_i - cardinality, 0]
|
||||
end
|
||||
|
||||
return [0, 0]
|
||||
end
|
||||
|
||||
# Scales up or down the number of nodes needed to match the current
|
||||
# cardinality
|
||||
#
|
||||
# @return [Array<true, nil>, Array<false, String>] true if all the VMs
|
||||
# were created/shut down, false and the error reason if there
|
||||
# was a problem
|
||||
def scale()
|
||||
n_nodes = 0
|
||||
|
||||
get_nodes.each do |node|
|
||||
n_nodes += 1 if node['disposed'] != "1"
|
||||
end
|
||||
|
||||
diff = cardinality - n_nodes
|
||||
|
||||
if diff > 0
|
||||
return deploy(true)
|
||||
elsif diff < 0
|
||||
return shutdown(true)
|
||||
end
|
||||
|
||||
return [true, nil]
|
||||
end
|
||||
|
||||
# Updates the duration for the next cooldown
|
||||
# @param cooldown_duration [Integer] duration for the next cooldown
|
||||
def set_cooldown_duration(cooldown_duration)
|
||||
@body['cooldown_duration'] = cooldown_duration.to_i
|
||||
end
|
||||
|
||||
# Updates the duration for the next cooldown with the default value
|
||||
def set_default_cooldown_duration()
|
||||
cooldown_duration = @body['cooldown']
|
||||
cooldown_duration = @@default_cooldown if cooldown_duration.nil?
|
||||
|
||||
set_cooldown_duration(cooldown_duration)
|
||||
end
|
||||
|
||||
# Sets the cooldown end time from now + the duration set in set_cooldown_duration
|
||||
# @return [true, false] true if the cooldown duration is bigger than 0
|
||||
def apply_cooldown_duration()
|
||||
cooldown_duration = @body['cooldown_duration'].to_i
|
||||
|
||||
if cooldown_duration != 0
|
||||
@body['cooldown_end'] = Time.now.to_i + cooldown_duration
|
||||
@body.delete('cooldown_duration')
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
# Returns true if the cooldown period ended
|
||||
# @return [true, false] true if the cooldown period ended
|
||||
def cooldown_over?()
|
||||
return Time.now.to_i >= @body['cooldown_end'].to_i
|
||||
end
|
||||
|
||||
def self.init_default_cooldown(default_cooldown)
|
||||
@@default_cooldown = default_cooldown
|
||||
end
|
||||
|
||||
def self.init_default_shutdown(shutdown_action)
|
||||
@@default_shutdown = shutdown_action
|
||||
end
|
||||
|
||||
# Updates the role
|
||||
# @param [Hash] template
|
||||
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
||||
# otherwise
|
||||
def update(template)
|
||||
|
||||
force = template['force'] == true
|
||||
new_cardinality = template["cardinality"]
|
||||
|
||||
if new_cardinality.nil?
|
||||
return nil
|
||||
end
|
||||
|
||||
new_cardinality = new_cardinality.to_i
|
||||
|
||||
if !force
|
||||
if new_cardinality < min_cardinality().to_i
|
||||
return OpenNebula::Error.new(
|
||||
"Minimum cardinality is #{min_cardinality()}")
|
||||
|
||||
elsif !max_cardinality().nil? && new_cardinality > max_cardinality().to_i
|
||||
return OpenNebula::Error.new(
|
||||
"Maximum cardinality is #{max_cardinality()}")
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
set_cardinality(new_cardinality)
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
########################################################################
|
||||
# Recover
|
||||
########################################################################
|
||||
|
||||
def recover_deployment()
|
||||
recover()
|
||||
end
|
||||
|
||||
def recover_warning()
|
||||
recover()
|
||||
deploy()
|
||||
end
|
||||
|
||||
def recover_scale()
|
||||
recover()
|
||||
retry_scale()
|
||||
end
|
||||
|
||||
|
||||
########################################################################
|
||||
########################################################################
|
||||
|
||||
|
||||
private
|
||||
|
||||
# Returns a positive, 0, or negative number of nodes to adjust,
|
||||
# according to a SCHEDULED type policy
|
||||
# @param [Hash] A SCHEDULED type policy
|
||||
# @return [Integer] positive, 0, or negative number of nodes to adjust
|
||||
def scale_time?(elasticity_pol)
|
||||
now = Time.now.to_i
|
||||
|
||||
last_eval = elasticity_pol['last_eval'].to_i
|
||||
|
||||
elasticity_pol['last_eval'] = now
|
||||
|
||||
# If this is the first time this is evaluated, ignore it.
|
||||
# We don't want to execute actions planned in the past when the
|
||||
# server starts.
|
||||
|
||||
if last_eval == 0
|
||||
return 0
|
||||
end
|
||||
|
||||
start_time = elasticity_pol['start_time']
|
||||
target_vms = elasticity_pol['adjust']
|
||||
|
||||
if target_vms.nil?
|
||||
# TODO error msg
|
||||
return 0
|
||||
end
|
||||
|
||||
if !(start_time.nil? || start_time.empty?)
|
||||
begin
|
||||
start_time = Time.parse(start_time).to_i
|
||||
rescue ArgumentError
|
||||
# TODO error msg
|
||||
return 0
|
||||
end
|
||||
else
|
||||
recurrence = elasticity_pol['recurrence']
|
||||
|
||||
if recurrence.nil? || recurrence.empty?
|
||||
# TODO error msg
|
||||
return 0
|
||||
end
|
||||
|
||||
begin
|
||||
cron_parser = CronParser.new(recurrence)
|
||||
|
||||
# This returns the next planned time, starting from the last
|
||||
# step
|
||||
start_time = cron_parser.next(Time.at(last_eval)).to_i
|
||||
rescue
|
||||
# TODO error msg bad format
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
# Only actions planned between last step and this one are triggered
|
||||
if start_time > last_eval && start_time <= now
|
||||
Log.debug LOG_COMP, "Role #{name} : scheduled scalability for "\
|
||||
"#{Time.at(start_time)} triggered", @service.id()
|
||||
|
||||
new_cardinality = calculate_new_cardinality(elasticity_pol)
|
||||
|
||||
return new_cardinality - cardinality()
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
# Returns a positive, 0, or negative number of nodes to adjust,
|
||||
# according to a policy based on attributes
|
||||
# @param [Hash] A policy based on attributes
|
||||
# @return [Array<Integer>] positive, 0, or negative number of nodes to
|
||||
# adjust, plus the cooldown period duration
|
||||
def scale_attributes?(elasticity_pol)
|
||||
|
||||
now = Time.now.to_i
|
||||
|
||||
# TODO: enforce true_up_evals type in ServiceTemplate::ROLE_SCHEMA ?
|
||||
|
||||
period_duration = elasticity_pol['period'].to_i
|
||||
period_number = elasticity_pol['period_number'].to_i
|
||||
last_eval = elasticity_pol['last_eval'].to_i
|
||||
true_evals = elasticity_pol['true_evals'].to_i
|
||||
expression = elasticity_pol['expression']
|
||||
|
||||
if !last_eval.nil?
|
||||
if now < (last_eval + period_duration)
|
||||
return [0, 0]
|
||||
end
|
||||
end
|
||||
|
||||
elasticity_pol['last_eval'] = now
|
||||
|
||||
new_cardinality = cardinality()
|
||||
new_evals = 0
|
||||
|
||||
exp_value, exp_st = scale_rule(expression)
|
||||
|
||||
if exp_value
|
||||
new_evals = true_evals + 1
|
||||
new_evals = period_number if new_evals > period_number
|
||||
|
||||
if new_evals >= period_number
|
||||
Log.debug LOG_COMP, "Role #{name} : elasticy policy #{exp_st} "\
|
||||
"triggered", @service.id()
|
||||
new_cardinality = calculate_new_cardinality(elasticity_pol)
|
||||
end
|
||||
end
|
||||
|
||||
elasticity_pol['true_evals'] = new_evals
|
||||
elasticity_pol['expression_evaluated'] = exp_st
|
||||
|
||||
return [new_cardinality - cardinality(), elasticity_pol['cooldown']]
|
||||
end
|
||||
|
||||
# Returns true if the scalability rule is triggered
|
||||
# @return true if the scalability rule is triggered
|
||||
def scale_rule(elas_expr)
|
||||
parser = ElasticityGrammarParser.new
|
||||
|
||||
if elas_expr.nil? || elas_expr.empty?
|
||||
return false
|
||||
end
|
||||
|
||||
treetop = parser.parse(elas_expr)
|
||||
if treetop.nil?
|
||||
return [false, "Parse error. '#{elas_expr}': #{parser.failure_reason}"]
|
||||
end
|
||||
|
||||
val, st = treetop.result(self)
|
||||
|
||||
return [val, st]
|
||||
end
|
||||
|
||||
def calculate_new_cardinality(elasticity_pol)
|
||||
type = elasticity_pol['type']
|
||||
adjust = elasticity_pol['adjust'].to_i
|
||||
|
||||
# Min is a hard limit, if the current cardinality + adjustment does
|
||||
# not reach it, the difference is added
|
||||
|
||||
max = [cardinality(), max_cardinality.to_i].max()
|
||||
# min = [cardinality(), min_cardinality.to_i].min()
|
||||
min = min_cardinality.to_i
|
||||
|
||||
case type.upcase
|
||||
when 'CHANGE'
|
||||
new_cardinality = cardinality() + adjust
|
||||
when 'PERCENTAGE_CHANGE'
|
||||
min_adjust_step = elasticity_pol['min_adjust_step'].to_i
|
||||
|
||||
change = cardinality() * adjust / 100.0
|
||||
|
||||
sign = change > 0 ? 1 : -1
|
||||
change = change.abs
|
||||
|
||||
if change < 1
|
||||
change = 1
|
||||
else
|
||||
change = change.to_i
|
||||
end
|
||||
|
||||
change = sign * [change, min_adjust_step].max
|
||||
|
||||
new_cardinality = cardinality() + change
|
||||
|
||||
when 'CARDINALITY'
|
||||
new_cardinality = adjust
|
||||
else
|
||||
# TODO: error message
|
||||
return cardinality()
|
||||
end
|
||||
|
||||
# The cardinality can be forced to be outside the min,max
|
||||
# range. If that is the case, the scale up/down will not
|
||||
# move further outside the range. It will move towards the
|
||||
# range with the adjustement set, instead of jumping the
|
||||
# difference
|
||||
if (adjust > 0)
|
||||
new_cardinality = max if new_cardinality > max
|
||||
elsif (adjust < 0)
|
||||
new_cardinality = min if new_cardinality < min
|
||||
end
|
||||
|
||||
return new_cardinality
|
||||
end
|
||||
|
||||
# For a failed scale up, the cardinality is updated to the actual value
|
||||
# For a failed scale down, the shutdown actions are retried
|
||||
def retry_scale()
|
||||
nodes_dispose = get_nodes.select { |node|
|
||||
node['disposed'] == "1"
|
||||
}
|
||||
|
||||
shutdown_nodes(nodes_dispose, true)
|
||||
|
||||
set_cardinality( get_nodes.size() - n_dispose.size() )
|
||||
end
|
||||
|
||||
# Deletes VMs in DONE or FAILED, and sends a boot action to VMs in UNKNOWN
|
||||
def recover()
|
||||
|
||||
nodes = @body['nodes']
|
||||
new_nodes = []
|
||||
disposed_nodes = @body['disposed_nodes']
|
||||
|
||||
nodes.each do |node|
|
||||
vm_state = nil
|
||||
vm_id = node['deploy_id']
|
||||
|
||||
if node['vm_info'] && node['vm_info']['VM'] && node['vm_info']['VM']['STATE']
|
||||
vm_state = node['vm_info']['VM']['STATE']
|
||||
lcm_state = node['vm_info']['VM']['LCM_STATE']
|
||||
end
|
||||
|
||||
if vm_state == '6' # DONE
|
||||
# Store the VM id in the array of disposed nodes
|
||||
disposed_nodes << vm_id
|
||||
|
||||
elsif vm_state == '7' # FAILED
|
||||
vm = OpenNebula::VirtualMachine.new_with_id(vm_id, @service.client)
|
||||
rc = vm.finalize
|
||||
|
||||
if !OpenNebula.is_error?(rc)
|
||||
# Store the VM id in the array of disposed nodes
|
||||
disposed_nodes << vm_id
|
||||
|
||||
Log.debug LOG_COMP, "Role #{name} : Delete success for VM #{vm_id}", @service.id()
|
||||
else
|
||||
msg = "Role #{name} : Delete failed for VM #{vm_id}; #{rc.message}"
|
||||
Log.error LOG_COMP, msg, @service.id()
|
||||
@service.log_error(msg)
|
||||
|
||||
success = false
|
||||
|
||||
new_nodes << node
|
||||
end
|
||||
elsif vm_state == '3' && lcm_state == '16' # UNKNOWN
|
||||
vm = OpenNebula::VirtualMachine.new_with_id(vm_id, @service.client)
|
||||
vm.boot
|
||||
else
|
||||
new_nodes << node
|
||||
end
|
||||
end
|
||||
|
||||
@body['nodes'] = new_nodes
|
||||
end
|
||||
|
||||
|
||||
# Shuts down all the given roles
|
||||
# @param scale_down [true,false] True to set the 'disposed' node flag
|
||||
def shutdown_nodes(nodes, scale_down)
|
||||
|
||||
action = @body['shutdown_action']
|
||||
|
||||
if action.nil?
|
||||
action = @service.get_shutdown_action()
|
||||
end
|
||||
|
||||
if action.nil?
|
||||
action = @@default_shutdown
|
||||
end
|
||||
|
||||
nodes.each { |node|
|
||||
vm_id = node['deploy_id']
|
||||
|
||||
Log.debug LOG_COMP, "Role #{name} : Shutting down VM #{vm_id}", @service.id()
|
||||
|
||||
vm = OpenNebula::VirtualMachine.new_with_id(vm_id, @service.client)
|
||||
|
||||
if action == 'shutdown-hard'
|
||||
rc = vm.shutdown(true)
|
||||
else
|
||||
rc = vm.shutdown
|
||||
end
|
||||
|
||||
if scale_down
|
||||
node['disposed'] = '1'
|
||||
end
|
||||
|
||||
if OpenNebula.is_error?(rc)
|
||||
msg = "Role #{name} : Shutdown failed for VM #{vm_id}, will perform a Delete; #{rc.message}"
|
||||
Log.error LOG_COMP, msg, @service.id()
|
||||
@service.log_error(msg)
|
||||
|
||||
rc = vm.finalize
|
||||
|
||||
if OpenNebula.is_error?(rc)
|
||||
msg = "Role #{name} : Delete failed for VM #{vm_id}; #{rc.message}"
|
||||
Log.error LOG_COMP, msg, @service.id()
|
||||
@service.log_error(msg)
|
||||
|
||||
success = false
|
||||
#return [false, rc.message]
|
||||
else
|
||||
Log.debug LOG_COMP, "Role #{name} : Delete success for VM #{vm_id}", @service.id()
|
||||
end
|
||||
else
|
||||
Log.debug LOG_COMP, "Role #{name} : Shutdown success for VM #{vm_id}", @service.id()
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
end
|
409
src/flow/lib/models/service.rb
Normal file
409
src/flow/lib/models/service.rb
Normal file
@ -0,0 +1,409 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2010-2013, C12G Labs S.L. #
|
||||
# #
|
||||
# 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. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
module OpenNebula
|
||||
class Service < DocumentJSON
|
||||
|
||||
DOCUMENT_TYPE = 100
|
||||
|
||||
STATE = {
|
||||
'PENDING' => 0,
|
||||
'DEPLOYING' => 1,
|
||||
'RUNNING' => 2,
|
||||
'UNDEPLOYING' => 3,
|
||||
'WARNING' => 4,
|
||||
'DONE' => 5,
|
||||
'FAILED_UNDEPLOYING' => 6,
|
||||
'FAILED_DEPLOYING' => 7,
|
||||
'SCALING' => 8,
|
||||
'FAILED_SCALING' => 9,
|
||||
'COOLDOWN' => 10
|
||||
}
|
||||
|
||||
STATE_STR = [
|
||||
'PENDING',
|
||||
'DEPLOYING',
|
||||
'RUNNING',
|
||||
'UNDEPLOYING',
|
||||
'WARNING',
|
||||
'DONE',
|
||||
'FAILED_UNDEPLOYING',
|
||||
'FAILED_DEPLOYING',
|
||||
'SCALING',
|
||||
'FAILED_SCALING',
|
||||
'COOLDOWN'
|
||||
]
|
||||
|
||||
LOG_COMP = "SER"
|
||||
|
||||
# Returns the service state
|
||||
# @return [Integer] the service state
|
||||
def state
|
||||
return @body['state'].to_i
|
||||
end
|
||||
|
||||
# Returns the service strategy
|
||||
# @return [String] the service strategy
|
||||
def strategy
|
||||
return @body['deployment']
|
||||
end
|
||||
|
||||
# Returns the string representation of the service state
|
||||
# @return the state string
|
||||
def state_str
|
||||
return STATE_STR[state]
|
||||
end
|
||||
|
||||
# Sets a new state
|
||||
# @param [Integer] the new state
|
||||
# @return [true, false] true if the value was changed
|
||||
def set_state(state)
|
||||
if state < 0 || state > STATE_STR.size
|
||||
return false
|
||||
end
|
||||
|
||||
@body['state'] = state
|
||||
|
||||
msg = "New state: #{STATE_STR[state]}"
|
||||
Log.info LOG_COMP, msg, self.id()
|
||||
self.log_info(msg)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
# Returns the owner username
|
||||
# @return [String] the service's owner username
|
||||
def owner_name()
|
||||
return self['UNAME']
|
||||
end
|
||||
|
||||
# Replaces this object's client with a new one
|
||||
# @param [OpenNebula::Client] owner_client the new client
|
||||
def replace_client(owner_client)
|
||||
@client = owner_client
|
||||
end
|
||||
|
||||
# Returns all the node Roles
|
||||
# @return [Hash<String,Role>] all the node Roles
|
||||
def get_roles
|
||||
return @roles
|
||||
end
|
||||
|
||||
# Returns true if all the nodes are correctly deployed
|
||||
# @return [true, false] true if all the nodes are correctly deployed
|
||||
def all_roles_running?()
|
||||
@roles.each { |name, role|
|
||||
if role.state != Role::STATE['RUNNING']
|
||||
return false
|
||||
end
|
||||
}
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
# Returns true if all the nodes are in done state
|
||||
# @return [true, false] true if all the nodes are correctly deployed
|
||||
def all_roles_done?()
|
||||
@roles.each { |name, role|
|
||||
if role.state != Role::STATE['DONE']
|
||||
return false
|
||||
end
|
||||
}
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
# Returns true if any of the roles is in failed state
|
||||
# @return [true, false] true if any of the roles is in failed state
|
||||
def any_role_failed?()
|
||||
failed_states = [
|
||||
Role::STATE['FAILED_DEPLOYING'],
|
||||
Role::STATE['FAILED_UNDEPLOYING']]
|
||||
|
||||
@roles.each { |name, role|
|
||||
if failed_states.include?(role.state)
|
||||
return true
|
||||
end
|
||||
}
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
def any_role_scaling?()
|
||||
@roles.each do |name, role|
|
||||
if role.state == Role::STATE['SCALING']
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
def any_role_failed_scaling?()
|
||||
@roles.each do |name, role|
|
||||
if role.state == Role::STATE['FAILED_SCALING']
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
def any_role_cooldown?()
|
||||
@roles.each do |name, role|
|
||||
if role.state == Role::STATE['COOLDOWN']
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
# Create a new service based on the template provided
|
||||
# @param [String] template_json
|
||||
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
||||
# otherwise
|
||||
def allocate(template_json)
|
||||
template = JSON.parse(template_json)
|
||||
template['state'] = STATE['PENDING']
|
||||
|
||||
super(template.to_json, template['name'])
|
||||
end
|
||||
|
||||
# Shutdown the service. This action is called when user wants to shutdwon
|
||||
# the Service
|
||||
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
||||
# otherwise
|
||||
def shutdown
|
||||
if ![Service::STATE['FAILED_SCALING'],
|
||||
Service::STATE['DONE']].include?(self.state)
|
||||
self.set_state(Service::STATE['UNDEPLOYING'])
|
||||
return self.update
|
||||
else
|
||||
return OpenNebula::Error.new("Action shutdown: Wrong state" \
|
||||
" #{self.state_str()}")
|
||||
end
|
||||
end
|
||||
|
||||
# Recover a failed service.
|
||||
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
||||
# otherwise
|
||||
def recover
|
||||
if [Service::STATE['FAILED_DEPLOYING']].include?(self.state)
|
||||
@roles.each do |name, role|
|
||||
if role.state == Role::STATE['FAILED_DEPLOYING']
|
||||
role.set_state(Role::STATE['PENDING'])
|
||||
role.recover_deployment()
|
||||
end
|
||||
end
|
||||
|
||||
self.set_state(Service::STATE['DEPLOYING'])
|
||||
|
||||
elsif self.state == Service::STATE['FAILED_SCALING']
|
||||
@roles.each do |name, role|
|
||||
if role.state == Role::STATE['FAILED_SCALING']
|
||||
role.recover_scale()
|
||||
role.set_state(Role::STATE['SCALING'])
|
||||
end
|
||||
end
|
||||
|
||||
self.set_state(Service::STATE['SCALING'])
|
||||
|
||||
elsif self.state == Service::STATE['FAILED_UNDEPLOYING']
|
||||
@roles.each do |name, role|
|
||||
if role.state == Role::STATE['FAILED_UNDEPLOYING']
|
||||
role.set_state(Role::STATE['RUNNING'])
|
||||
end
|
||||
end
|
||||
|
||||
self.set_state(Service::STATE['UNDEPLOYING'])
|
||||
|
||||
elsif self.state == Service::STATE['COOLDOWN']
|
||||
@roles.each do |name, role|
|
||||
if role.state == Role::STATE['COOLDOWN']
|
||||
role.set_state(Role::STATE['RUNNING'])
|
||||
end
|
||||
end
|
||||
|
||||
self.set_state(Service::STATE['RUNNING'])
|
||||
|
||||
elsif self.state == Service::STATE['WARNING']
|
||||
@roles.each do |name, role|
|
||||
if role.state == Role::STATE['WARNING']
|
||||
role.recover_warning()
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
return OpenNebula::Error.new("Action recover: Wrong state" \
|
||||
" #{self.state_str()}")
|
||||
end
|
||||
|
||||
return self.update
|
||||
end
|
||||
|
||||
# Delete the service. All the VMs are also deleted from OpenNebula.
|
||||
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
||||
# otherwise
|
||||
def delete
|
||||
@roles.each { |name, role|
|
||||
role.delete()
|
||||
}
|
||||
|
||||
return super()
|
||||
end
|
||||
|
||||
# Retrieves the information of the Service and all its Nodes.
|
||||
#
|
||||
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
||||
# otherwise
|
||||
def info
|
||||
rc = super
|
||||
if OpenNebula.is_error?(rc)
|
||||
return rc
|
||||
end
|
||||
|
||||
@roles = {}
|
||||
|
||||
if @body['roles']
|
||||
@body['roles'].each { |elem|
|
||||
elem['state'] ||= Role::STATE['PENDING']
|
||||
role = Role.new(elem, self)
|
||||
@roles[role.name] = role
|
||||
}
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
# Add an info message in the service information that will be stored
|
||||
# in OpenNebula
|
||||
# @param [String] message
|
||||
def log_info(message)
|
||||
add_log(Logger::INFO, message)
|
||||
end
|
||||
|
||||
# Add an error message in the service information that will be stored
|
||||
# in OpenNebula
|
||||
# @param [String] message
|
||||
def log_error(message)
|
||||
add_log(Logger::ERROR, message)
|
||||
end
|
||||
|
||||
# Retrieve the service client
|
||||
def client
|
||||
@client
|
||||
end
|
||||
|
||||
# Changes the owner/group
|
||||
#
|
||||
# @param [Integer] uid the new owner id. Set to -1 to leave the current one
|
||||
# @param [Integer] gid the new group id. Set to -1 to leave the current one
|
||||
#
|
||||
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
||||
# otherwise
|
||||
def chown(uid, gid)
|
||||
old_uid = self['UID'].to_i
|
||||
old_gid = self['GID'].to_i
|
||||
|
||||
rc = super(uid, gid)
|
||||
|
||||
if OpenNebula.is_error?(rc)
|
||||
return rc
|
||||
end
|
||||
|
||||
@roles.each { |name, role|
|
||||
rc = role.chown(uid, gid)
|
||||
|
||||
break if rc[0] == false
|
||||
}
|
||||
|
||||
if rc[0] == false
|
||||
self.log_error("Chown operation failed, will try to rollback all VMs to the old user and group")
|
||||
update()
|
||||
|
||||
super(old_uid, old_gid)
|
||||
|
||||
@roles.each { |name, role|
|
||||
role.chown(old_uid, old_gid)
|
||||
}
|
||||
|
||||
return OpenNebula::Error.new(rc[1])
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
# Updates a role
|
||||
# @param [String] role_name
|
||||
# @param [String] template_json
|
||||
# @return [nil, OpenNebula::Error] nil in case of success, Error
|
||||
# otherwise
|
||||
def update_role(role_name, template_json)
|
||||
|
||||
if ![Service::STATE['RUNNING'], Service::STATE['WARNING']].include?(self.state)
|
||||
return OpenNebula::Error.new("Update role: Wrong state" \
|
||||
" #{self.state_str()}")
|
||||
end
|
||||
|
||||
template = JSON.parse(template_json)
|
||||
|
||||
# TODO: Validate template?
|
||||
|
||||
role = @roles[role_name]
|
||||
|
||||
if role.nil?
|
||||
return OpenNebula::Error.new("ROLE \"#{role_name}\" does not exist")
|
||||
end
|
||||
|
||||
rc = role.update(template)
|
||||
|
||||
if OpenNebula.is_error?(rc)
|
||||
return rc
|
||||
end
|
||||
|
||||
# TODO: The update may not change the cardinality, only
|
||||
# the max and min vms...
|
||||
|
||||
role.set_state(Role::STATE['SCALING'])
|
||||
|
||||
role.set_default_cooldown_duration()
|
||||
|
||||
self.set_state(Service::STATE['SCALING'])
|
||||
|
||||
return self.update
|
||||
end
|
||||
|
||||
def get_shutdown_action()
|
||||
return @body['shutdown_action']
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# @param [Logger::Severity] severity
|
||||
# @param [String] message
|
||||
def add_log(severity, message)
|
||||
severity_str = Logger::SEV_LABEL[severity][0..0]
|
||||
|
||||
@body['log'] ||= Array.new
|
||||
@body['log'] << {
|
||||
:timestamp => Time.now.to_i,
|
||||
:severity => severity_str,
|
||||
:message => message
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
97
src/flow/lib/models/service_pool.rb
Normal file
97
src/flow/lib/models/service_pool.rb
Normal file
@ -0,0 +1,97 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2010-2013, C12G Labs S.L. #
|
||||
# #
|
||||
# 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. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
module OpenNebula
|
||||
class ServicePool < DocumentPoolJSON
|
||||
|
||||
DOCUMENT_TYPE = 100
|
||||
|
||||
@@mutex = Mutex.new
|
||||
@@mutex_hash = Hash.new
|
||||
|
||||
# Class constructor
|
||||
#
|
||||
# @param [OpenNebula::Client] client the xml-rpc client
|
||||
# @param [Integer] user_id the filter flag, see
|
||||
# http://opennebula.org/documentation:rel3.6:api
|
||||
#
|
||||
# @return [DocumentPool] the new object
|
||||
def initialize(client, user_id=-1)
|
||||
super(client, user_id)
|
||||
end
|
||||
|
||||
def factory(element_xml)
|
||||
service = OpenNebula::Service.new(element_xml, @client)
|
||||
service.load_body
|
||||
service
|
||||
end
|
||||
|
||||
# Retrieves a Service element from OpenNebula. The Service::info()
|
||||
# method is called
|
||||
#
|
||||
# @param [Integer] service_id Numerical Id of the service to retrieve
|
||||
# @yieldparam [Service] this block will have the service's mutex locked.
|
||||
# The mutex will be unlocked after the block execution.
|
||||
#
|
||||
# @return [Service, OpenNebula::Error] The Service in case of success
|
||||
def get(service_id, &block)
|
||||
service_id = service_id.to_i if service_id
|
||||
service = Service.new_with_id(service_id, @client)
|
||||
|
||||
rc = service.info
|
||||
|
||||
if OpenNebula.is_error?(rc)
|
||||
return rc
|
||||
else
|
||||
if block_given?
|
||||
obj_mutex = nil
|
||||
entry = nil
|
||||
|
||||
@@mutex.synchronize {
|
||||
# entry is an array of [Mutex, waiting]
|
||||
# waiting is the number of threads waiting on this mutex
|
||||
entry = @@mutex_hash[service_id]
|
||||
|
||||
if entry.nil?
|
||||
entry = [Mutex.new, 0]
|
||||
@@mutex_hash[service_id] = entry
|
||||
end
|
||||
|
||||
obj_mutex = entry[0]
|
||||
entry[1] = entry[1] + 1
|
||||
|
||||
if @@mutex_hash.size > 10000
|
||||
@@mutex_hash.delete_if { |s_id, entry|
|
||||
entry[1] == 0
|
||||
}
|
||||
end
|
||||
}
|
||||
|
||||
obj_mutex.synchronize {
|
||||
block.call(service)
|
||||
}
|
||||
|
||||
@@mutex.synchronize {
|
||||
entry[1] = entry[1] - 1
|
||||
}
|
||||
end
|
||||
|
||||
return service
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
311
src/flow/lib/models/service_template.rb
Normal file
311
src/flow/lib/models/service_template.rb
Normal file
@ -0,0 +1,311 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2010-2013, C12G Labs S.L. #
|
||||
# #
|
||||
# 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. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
require 'parse-cron'
|
||||
|
||||
module OpenNebula
|
||||
|
||||
class ServiceTemplate < DocumentJSON
|
||||
ROLE_SCHEMA = {
|
||||
:type => :object,
|
||||
:properties => {
|
||||
'name' => {
|
||||
:type => :string,
|
||||
:required => true
|
||||
},
|
||||
'cardinality' => {
|
||||
:type => :integer,
|
||||
:default => 1,
|
||||
:minimum => 0
|
||||
},
|
||||
'vm_template' => {
|
||||
:type => :integer,
|
||||
:required => true
|
||||
},
|
||||
'parents' => {
|
||||
:type => :array,
|
||||
:items => {
|
||||
:type => :string
|
||||
}
|
||||
},
|
||||
'shutdown_action' => {
|
||||
:type => :string,
|
||||
:enum => %w{shutdown shutdown-hard},
|
||||
:required => false
|
||||
},
|
||||
'min_vms' => {
|
||||
:type => :integer,
|
||||
:required => false,
|
||||
:minimum => 0
|
||||
},
|
||||
'max_vms' => {
|
||||
:type => :integer,
|
||||
:required => false,
|
||||
:minimum => 0
|
||||
},
|
||||
'cooldown' => {
|
||||
:type => :integer,
|
||||
:required => false,
|
||||
:minimum => 0
|
||||
},
|
||||
'elasticity_policies' => {
|
||||
:type => :array,
|
||||
:items => {
|
||||
:type => :object,
|
||||
:properties => {
|
||||
'type' => {
|
||||
:type => :string,
|
||||
:enum => %w{CHANGE CARDINALITY PERCENTAGE_CHANGE},
|
||||
:required => true
|
||||
},
|
||||
'adjust' => {
|
||||
:type => :integer,
|
||||
:required => true
|
||||
},
|
||||
'min_adjust_step' => {
|
||||
:type => :integer,
|
||||
:required => false,
|
||||
:minimum => 1
|
||||
},
|
||||
'period_number' => {
|
||||
:type => :integer,
|
||||
:required => false,
|
||||
:minimum => 0
|
||||
},
|
||||
'period' => {
|
||||
:type => :integer,
|
||||
:required => false,
|
||||
:minimum => 0
|
||||
},
|
||||
'expression' => {
|
||||
:type => :string,
|
||||
:required => true
|
||||
},
|
||||
'cooldown' => {
|
||||
:type => :integer,
|
||||
:required => false,
|
||||
:minimum => 0
|
||||
}
|
||||
#'statistic' => {
|
||||
# # SampleCount | Average | Sum | Minimum | Maximum
|
||||
# :type => :string
|
||||
#}
|
||||
}
|
||||
}
|
||||
},
|
||||
'scheduled_policies' => {
|
||||
:type => :array,
|
||||
:items => {
|
||||
:type => :object,
|
||||
:properties => {
|
||||
'type' => {
|
||||
:type => :string,
|
||||
:enum => %w{CHANGE CARDINALITY PERCENTAGE_CHANGE},
|
||||
:required => true
|
||||
},
|
||||
'adjust' => {
|
||||
:type => :integer,
|
||||
:required => true
|
||||
},
|
||||
'min_adjust_step' => {
|
||||
:type => :integer,
|
||||
:required => false,
|
||||
:minimum => 1
|
||||
},
|
||||
'start_time' => {
|
||||
:type => :string,
|
||||
:required => false
|
||||
},
|
||||
'recurrence' => {
|
||||
:type => :string,
|
||||
:required => false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCHEMA = {
|
||||
:type => :object,
|
||||
:properties => {
|
||||
'name' => {
|
||||
:type => :string,
|
||||
:required => true
|
||||
},
|
||||
'deployment' => {
|
||||
:type => :string,
|
||||
:enum => %w{none straight},
|
||||
:default => 'none'
|
||||
},
|
||||
'shutdown_action' => {
|
||||
:type => :string,
|
||||
:enum => %w{shutdown shutdown-hard},
|
||||
:required => false
|
||||
},
|
||||
'roles' => {
|
||||
:type => :array,
|
||||
:items => ROLE_SCHEMA,
|
||||
:required => true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
DOCUMENT_TYPE = 101
|
||||
|
||||
def allocate(template_json)
|
||||
template = JSON.parse(template_json)
|
||||
|
||||
validator = Validator::Validator.new(
|
||||
:default_values => true,
|
||||
:delete_extra_properties => false
|
||||
)
|
||||
|
||||
validator.validate!(template, SCHEMA)
|
||||
|
||||
validate_values(template)
|
||||
|
||||
super(template.to_json, template['name'])
|
||||
end
|
||||
|
||||
# Retrieves the template
|
||||
#
|
||||
# @return [String] json template
|
||||
def template
|
||||
@body.to_json
|
||||
end
|
||||
|
||||
def update(template_json)
|
||||
template = JSON.parse(template_json)
|
||||
|
||||
validator = Validator::Validator.new(
|
||||
:default_values => true,
|
||||
:delete_extra_properties => false
|
||||
)
|
||||
|
||||
validator.validate!(template, SCHEMA)
|
||||
|
||||
validate_values(template)
|
||||
|
||||
super(template.to_json)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def validate_values(template)
|
||||
parser = ElasticityGrammarParser.new
|
||||
|
||||
roles = template['roles']
|
||||
|
||||
roles.each_with_index do |role, role_index|
|
||||
|
||||
roles[role_index+1..-1].each do |other_role|
|
||||
if role['name'] == other_role['name']
|
||||
raise Validator::ParseException,
|
||||
"Role name '#{role['name']}' is repeated"
|
||||
end
|
||||
end
|
||||
|
||||
if (!role['min_vms'].nil? && role['min_vms'].to_i > role['cardinality'].to_i)
|
||||
|
||||
raise Validator::ParseException,
|
||||
"Role '#{role['name']}' 'cardinality' must be greater than "\
|
||||
"or equal to 'min_vms'"
|
||||
end
|
||||
|
||||
if !role['max_vms'].nil? &&
|
||||
role['max_vms'].to_i < role['cardinality'].to_i
|
||||
|
||||
raise Validator::ParseException,
|
||||
"Role '#{role['name']}' 'cardinality' must be lower than "\
|
||||
"or equal to 'max_vms'"
|
||||
end
|
||||
|
||||
if ((role['elasticity_policies'] && role['elasticity_policies'].size > 0) ||
|
||||
(role['scheduled_policies'] && role['scheduled_policies'].size > 0))
|
||||
|
||||
if role['min_vms'].nil? || role['max_vms'].nil?
|
||||
raise Validator::ParseException,
|
||||
"Role '#{role['name']}' with 'elasticity_policies' or "<<
|
||||
"'scheduled_policies' must define both 'min_vms'"<<
|
||||
"and 'max_vms'"
|
||||
end
|
||||
end
|
||||
|
||||
if role['elasticity_policies']
|
||||
role['elasticity_policies'].each_with_index do |policy, index|
|
||||
exp = policy['expression']
|
||||
|
||||
if exp.empty?
|
||||
raise Validator::ParseException,
|
||||
"Role '#{role['name']}', elasticity policy "\
|
||||
"##{index} 'expression' cannot be empty"
|
||||
end
|
||||
|
||||
treetop = parser.parse(exp)
|
||||
if treetop.nil?
|
||||
raise Validator::ParseException,
|
||||
"Role '#{role['name']}', elasticity policy "\
|
||||
"##{index} 'expression' parse error: #{parser.failure_reason}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if role['scheduled_policies']
|
||||
role['scheduled_policies'].each_with_index do |policy, index|
|
||||
|
||||
start_time = policy['start_time']
|
||||
recurrence = policy['recurrence']
|
||||
|
||||
if !start_time.nil?
|
||||
if !policy['recurrence'].nil?
|
||||
raise Validator::ParseException,
|
||||
"Role '#{role['name']}', scheduled policy "\
|
||||
"##{index} must define "\
|
||||
"'start_time' or 'recurrence', but not both"
|
||||
end
|
||||
|
||||
begin
|
||||
Time.parse(start_time)
|
||||
rescue ArgumentError
|
||||
raise Validator::ParseException,
|
||||
"Role '#{role['name']}', scheduled policy "\
|
||||
"##{index} 'start_time' is not a valid Time. "\
|
||||
"Try with YYYY-MM-DD hh:mm:ss or YYYY-MM-DDThh:mm:ssZ"
|
||||
end
|
||||
elsif !recurrence.nil?
|
||||
begin
|
||||
cron_parser = CronParser.new(recurrence)
|
||||
start_time = cron_parser.next()
|
||||
rescue Exception => e
|
||||
raise Validator::ParseException,
|
||||
"Role '#{role['name']}', scheduled policy "\
|
||||
"##{index} 'recurrence' is not a valid "\
|
||||
"cron expression"
|
||||
end
|
||||
else
|
||||
raise Validator::ParseException,
|
||||
"Role '#{role['name']}', scheduled policy ##{index} needs to define either "<<
|
||||
"'start_time' or 'recurrence'"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
28
src/flow/lib/models/service_template_pool.rb
Normal file
28
src/flow/lib/models/service_template_pool.rb
Normal file
@ -0,0 +1,28 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2010-2013, C12G Labs S.L. #
|
||||
# #
|
||||
# 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. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
module OpenNebula
|
||||
class ServiceTemplatePool < DocumentPoolJSON
|
||||
|
||||
DOCUMENT_TYPE = 101
|
||||
|
||||
def factory(element_xml)
|
||||
s_template = OpenNebula::ServiceTemplate.new(element_xml, @client)
|
||||
s_template.load_body
|
||||
s_template
|
||||
end
|
||||
end
|
||||
end
|
382
src/flow/lib/strategy.rb
Normal file
382
src/flow/lib/strategy.rb
Normal file
@ -0,0 +1,382 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2010-2013, C12G Labs S.L. #
|
||||
# #
|
||||
# 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. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
require 'strategy/straight'
|
||||
|
||||
class Strategy
|
||||
|
||||
LOG_COMP = "STR"
|
||||
|
||||
# Performs a boot step, deploying all nodes that meet the requirements
|
||||
# @param [Service] service service to boot
|
||||
# @return [Array<true, nil>, Array<false, String>] true if all the nodes
|
||||
# were created, false and the error reason if there was a problem
|
||||
# creating the VMs
|
||||
def boot_step(service)
|
||||
Log.debug LOG_COMP, "Boot step", service.id()
|
||||
|
||||
roles_deploy = get_roles_deploy(service)
|
||||
|
||||
roles_deploy.each { |name, role|
|
||||
Log.debug LOG_COMP, "Deploying role #{name}", service.id()
|
||||
|
||||
rc = role.deploy
|
||||
|
||||
if !rc[0]
|
||||
role.set_state(Role::STATE['FAILED_DEPLOYING'])
|
||||
|
||||
return rc
|
||||
else
|
||||
role.set_state(Role::STATE['DEPLOYING'])
|
||||
end
|
||||
}
|
||||
|
||||
return [true, nil]
|
||||
end
|
||||
|
||||
# Performs a shutdown step, shutting down all nodes that meet the requirements
|
||||
# @param [Service] service service to boot
|
||||
# @return [Array<true, nil>, Array<false, String>] true if all the nodes
|
||||
# were created, false and the error reason if there was a problem
|
||||
# creating the VMs
|
||||
def shutdown_step(service)
|
||||
Log.debug LOG_COMP, "Shutdown step", service.id()
|
||||
|
||||
roles_shutdown = get_roles_shutdown(service)
|
||||
|
||||
roles_shutdown.each { |name, role|
|
||||
Log.debug LOG_COMP, "Shutting down role #{name}", service.id()
|
||||
|
||||
rc = role.shutdown
|
||||
|
||||
if !rc[0]
|
||||
role.set_state(Role::STATE['FAILED_UNDEPLOYING'])
|
||||
|
||||
return rc
|
||||
else
|
||||
role.set_state(Role::STATE['UNDEPLOYING'])
|
||||
end
|
||||
}
|
||||
|
||||
return [true, nil]
|
||||
end
|
||||
|
||||
# If a role needs to scale, its cardinality is updated, and its state is set
|
||||
# to SCALING. Only one role is set to scale.
|
||||
# @param [Service] service
|
||||
# @return [true|false] true if any role needs to scale
|
||||
def apply_scaling_policies(service)
|
||||
Log.debug LOG_COMP, "Apply scaling policies", service.id()
|
||||
|
||||
service.get_roles.each do |name, role|
|
||||
diff, cooldown_duration = role.scale?
|
||||
|
||||
if diff != 0
|
||||
Log.debug LOG_COMP, "Role #{name} needs to scale #{diff} nodes", service.id()
|
||||
|
||||
role.set_cardinality(role.cardinality() + diff)
|
||||
|
||||
role.set_state(Role::STATE['SCALING'])
|
||||
role.set_cooldown_duration(cooldown_duration)
|
||||
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
# If a role is scaling, the nodes are created/destroyed to match the current
|
||||
# cardinality
|
||||
# @return [Array<true, nil>, Array<false, String>] true if the action was
|
||||
# performed, false and the error reason if there was a problem
|
||||
def scale_step(service)
|
||||
Log.debug LOG_COMP, "Scale step", service.id()
|
||||
|
||||
service.get_roles.each do |name, role|
|
||||
|
||||
if role.state == Role::STATE['SCALING']
|
||||
rc = role.scale()
|
||||
|
||||
if !rc[0]
|
||||
role.set_state(Role::STATE['FAILED_SCALING'])
|
||||
|
||||
return rc
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return [true, nil]
|
||||
end
|
||||
|
||||
# Performs a monitor step, check if the roles already deployed are running
|
||||
# @param [Service] service service to monitor
|
||||
# @return [nil]
|
||||
def monitor_step(service)
|
||||
Log.debug LOG_COMP, "Monitor step", service.id()
|
||||
|
||||
roles_monitor = get_roles_monitor(service)
|
||||
|
||||
roles_monitor.each { |name, role|
|
||||
Log.debug LOG_COMP, "Monitoring role #{name}", service.id()
|
||||
|
||||
rc = role.info
|
||||
|
||||
case role.state()
|
||||
when Role::STATE['RUNNING']
|
||||
if OpenNebula.is_error?(rc) || role_nodes_warning?(role)
|
||||
role.set_state(Role::STATE['WARNING'])
|
||||
end
|
||||
|
||||
role.update_cardinality()
|
||||
when Role::STATE['WARNING']
|
||||
if !OpenNebula.is_error?(rc) && !role_nodes_warning?(role)
|
||||
role.set_state(Role::STATE['RUNNING'])
|
||||
end
|
||||
|
||||
role.update_cardinality()
|
||||
when Role::STATE['DEPLOYING']
|
||||
if OpenNebula.is_error?(rc)
|
||||
role.set_state(Role::STATE['FAILED_DEPLOYING'])
|
||||
elsif role_nodes_running?(role)
|
||||
role.set_state(Role::STATE['RUNNING'])
|
||||
elsif any_node_failed?(role)
|
||||
role.set_state(Role::STATE['FAILED_DEPLOYING'])
|
||||
end
|
||||
when Role::STATE['SCALING']
|
||||
if OpenNebula.is_error?(rc)
|
||||
role.set_state(Role::STATE['FAILED_SCALING'])
|
||||
elsif role_finished_scaling?(role)
|
||||
if role.apply_cooldown_duration()
|
||||
role.set_state(Role::STATE['COOLDOWN'])
|
||||
else
|
||||
role.set_state(Role::STATE['RUNNING'])
|
||||
end
|
||||
elsif any_node_failed_scaling?(role)
|
||||
role.set_state(Role::STATE['FAILED_SCALING'])
|
||||
end
|
||||
when Role::STATE['COOLDOWN']
|
||||
if role.cooldown_over?
|
||||
role.set_state(Role::STATE['RUNNING'])
|
||||
end
|
||||
|
||||
role.update_cardinality()
|
||||
when Role::STATE['UNDEPLOYING']
|
||||
if OpenNebula.is_error?(rc)
|
||||
role.set_state(Role::STATE['FAILED_UNDEPLOYING'])
|
||||
elsif role_nodes_done?(role)
|
||||
role.set_state(Role::STATE['DONE'])
|
||||
elsif any_node_failed?(role)
|
||||
role.set_state(Role::STATE['FAILED_UNDEPLOYING'])
|
||||
end
|
||||
when Role::STATE['FAILED_DEPLOYING']
|
||||
if !OpenNebula.is_error?(rc) && role_nodes_running?(role)
|
||||
role.set_state(Role::STATE['RUNNING'])
|
||||
end
|
||||
when Role::STATE['FAILED_UNDEPLOYING']
|
||||
if !OpenNebula.is_error?(rc) && role_nodes_done?(role)
|
||||
role.set_state(Role::STATE['DONE'])
|
||||
end
|
||||
when Role::STATE['FAILED_SCALING']
|
||||
if !OpenNebula.is_error?(rc) && role_finished_scaling?(role)
|
||||
role.set_state(Role::STATE['SCALING'])
|
||||
end
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
protected
|
||||
# All subclasses must define these methods
|
||||
|
||||
# Returns all node Roles ready to be deployed
|
||||
# @param [Service] service
|
||||
# @return [Hash<String, Role>] Roles
|
||||
def get_roles_deploy(service)
|
||||
result = service.get_roles.select {|name, role|
|
||||
role.state == Role::STATE['PENDING'] ||
|
||||
role.state == Role::STATE['DEPLOYING']
|
||||
}
|
||||
|
||||
# Ruby 1.8 compatibility
|
||||
if result.instance_of?(Array)
|
||||
result = Hash[result]
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
# Returns all node Roles be monitored
|
||||
# @param [Service] service
|
||||
# @return [Hash<String, Role>] Roles
|
||||
def get_roles_monitor(service)
|
||||
result = service.get_roles.select {|name, role|
|
||||
![Role::STATE['PENDING'], Role::STATE['DONE']].include?(role.state)
|
||||
}
|
||||
|
||||
# Ruby 1.8 compatibility
|
||||
if result.instance_of?(Array)
|
||||
result = Hash[result]
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
# Returns all node Roles ready to be shutdown
|
||||
# @param [Service] service
|
||||
# @return [Hash<String, Role>] Roles
|
||||
def get_roles_shutdown(service)
|
||||
result = service.get_roles.select {|name, role|
|
||||
![Role::STATE['UNDEPLOYING'],
|
||||
Role::STATE['DONE'],
|
||||
Role::STATE['FAILED_UNDEPLOYING']].include?(role.state)
|
||||
}
|
||||
|
||||
# Ruby 1.8 compatibility
|
||||
if result.instance_of?(Array)
|
||||
result = Hash[result]
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
# Determine if the role nodes are running
|
||||
# @param [Role] role
|
||||
# @return [true|false]
|
||||
def role_nodes_running?(role)
|
||||
if role.get_nodes.size() != role.cardinality()
|
||||
return false
|
||||
end
|
||||
|
||||
role.get_nodes.each { |node|
|
||||
if node && node['vm_info']
|
||||
vm_state = node['vm_info']['VM']['STATE']
|
||||
lcm_state = node['vm_info']['VM']['LCM_STATE']
|
||||
|
||||
# !(ACTIVE && RUNNING)
|
||||
if (vm_state != '3') || (lcm_state != '3')
|
||||
return false
|
||||
end
|
||||
else
|
||||
return false
|
||||
end
|
||||
}
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
# Returns true if any VM is in UNKNOWN or FAILED
|
||||
# @param [Role] role
|
||||
# @return [true|false]
|
||||
def role_nodes_warning?(role)
|
||||
role.get_nodes.each do |node|
|
||||
if node && node['vm_info']
|
||||
vm_state = node['vm_info']['VM']['STATE']
|
||||
lcm_state = node['vm_info']['VM']['LCM_STATE']
|
||||
|
||||
# UNKNOWN or FAILED
|
||||
if (vm_state == '3' && lcm_state == '16') || vm_state == '7'
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
# Determine if any of the role nodes failed
|
||||
# @param [Role] role
|
||||
# @return [true|false]
|
||||
def any_node_failed?(role)
|
||||
role.get_nodes.each { |node|
|
||||
if node && node['vm_info']
|
||||
vm_state = node['vm_info']['VM']['STATE']
|
||||
|
||||
if vm_state == '7' # FAILED
|
||||
return true
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
# Determine if the role nodes are in done state
|
||||
# @param [Role] role
|
||||
# @return [true|false]
|
||||
def role_nodes_done?(role)
|
||||
role.get_nodes.each { |node|
|
||||
if node && node['vm_info']
|
||||
vm_state = node['vm_info']['VM']['STATE']
|
||||
|
||||
if vm_state != '6' # DONE
|
||||
return false
|
||||
end
|
||||
else
|
||||
return false
|
||||
end
|
||||
}
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
# Determine if any of the role nodes failed to scale
|
||||
# @param [Role] role
|
||||
# @return [true|false]
|
||||
def any_node_failed_scaling?(role)
|
||||
role.get_nodes.each { |node|
|
||||
if node && node['vm_info'] &&
|
||||
(node['disposed'] == '1' || node['scale_up'] == '1') &&
|
||||
node['vm_info']['VM']['STATE'] == '7' # FAILED
|
||||
|
||||
return true
|
||||
end
|
||||
}
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
def role_finished_scaling?(role)
|
||||
role.get_nodes.each { |node|
|
||||
if node && node['vm_info']
|
||||
# For scale up, check new nodes are running, or past running
|
||||
if node['scale_up'] == '1'
|
||||
vm_state = node['vm_info']['VM']['STATE'].to_i
|
||||
lcm_state = node['vm_info']['VM']['LCM_STATE'].to_i
|
||||
|
||||
# If any node didn't make it through the initial deployment,
|
||||
# return false
|
||||
|
||||
# INIT, PENDING, HOLD ||
|
||||
# ACTIVE && (LCM_INIT, PROLOG, BOOT)
|
||||
|
||||
if vm_state == 7 ||
|
||||
((vm_state < 3) || (vm_state == 3 && lcm_state < 3 ))
|
||||
|
||||
return false
|
||||
end
|
||||
end
|
||||
else
|
||||
return false
|
||||
end
|
||||
}
|
||||
|
||||
# TODO: If a shutdown ends in running again (VM doesn't have acpi),
|
||||
# the role/service will stay in SCALING
|
||||
|
||||
# For scale down, it will finish when scaling nodes are deleted
|
||||
return role.get_nodes.size() == role.cardinality()
|
||||
end
|
||||
end
|
151
src/flow/lib/strategy/straight.rb
Normal file
151
src/flow/lib/strategy/straight.rb
Normal file
@ -0,0 +1,151 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2010-2013, C12G Labs S.L. #
|
||||
# #
|
||||
# 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. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
module Straight
|
||||
# Using this strategy the service is deployed based on a directed
|
||||
# acyclic graph where each node defines its parents.
|
||||
#
|
||||
# For example:
|
||||
#
|
||||
# mysql nfs
|
||||
# | | \
|
||||
# | | kvm
|
||||
# \ | /
|
||||
# front-end
|
||||
#
|
||||
# The above graph represents the following service:
|
||||
#
|
||||
# "name": "my_first_service",
|
||||
# "roles": [
|
||||
# {
|
||||
# "name": "front-end",
|
||||
# "parents": [
|
||||
# "mysql",
|
||||
# "kvm",
|
||||
# "nfs"
|
||||
# ],
|
||||
# ...
|
||||
# },
|
||||
# {
|
||||
# "name": "nfs",
|
||||
# ...
|
||||
# },
|
||||
# {
|
||||
# "name": "mysql",
|
||||
# ...
|
||||
# },
|
||||
# {
|
||||
# "name": "kvm",
|
||||
# "parents": "nfs",
|
||||
# ...
|
||||
# }
|
||||
# ],
|
||||
# "deployment": "straight"
|
||||
# }
|
||||
#
|
||||
# The roles will be deployed in the following order:
|
||||
# 1. myslq & nfs
|
||||
# 2. kvm
|
||||
# 3. front-end
|
||||
#
|
||||
# And the service will be shutdown in the reverse order
|
||||
# 1. front-end
|
||||
# 2. kvm & myslq
|
||||
# 3. nfs
|
||||
|
||||
|
||||
|
||||
# Returns all node Roles ready to be deployed
|
||||
# @param [Service] service
|
||||
# @return [Hash<String, Role>] Roles
|
||||
def get_roles_deploy(service)
|
||||
roles = service.get_roles
|
||||
|
||||
running_roles = roles.select {|name, role|
|
||||
role.state == Role::STATE['RUNNING']
|
||||
}
|
||||
|
||||
# Ruby 1.8 compatibility
|
||||
if running_roles.instance_of?(Array)
|
||||
running_roles = Hash[running_roles]
|
||||
end
|
||||
|
||||
result = roles.select {|name, role|
|
||||
check = true
|
||||
|
||||
if role.state == Role::STATE['PENDING']
|
||||
role.parents.each { |parent|
|
||||
if !running_roles.include?(parent)
|
||||
check = false
|
||||
break
|
||||
end
|
||||
}
|
||||
elsif role.state == Role::STATE['DEPLOYING']
|
||||
check = true
|
||||
else
|
||||
check = false
|
||||
end
|
||||
|
||||
check
|
||||
}
|
||||
|
||||
# Ruby 1.8 compatibility
|
||||
if result.instance_of?(Array)
|
||||
result = Hash[result]
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
# Returns all node Roles ready to be shutdown
|
||||
# @param [Service] service
|
||||
# @return [Hash<String, Role>] Roles
|
||||
def get_roles_shutdown(service)
|
||||
roles = service.get_roles
|
||||
|
||||
# Get all the parents from running roles
|
||||
parents = []
|
||||
running_roles = {}
|
||||
|
||||
roles.each { |name, role|
|
||||
# All roles can be shutdown, except the ones in these states
|
||||
if (![Role::STATE['UNDEPLOYING'],
|
||||
Role::STATE['DONE'],
|
||||
Role::STATE['FAILED_UNDEPLOYING']].include?(role.state) )
|
||||
|
||||
running_roles[name]= role
|
||||
end
|
||||
|
||||
# Only the parents of DONE roles can be shutdown
|
||||
if (role.state != Role::STATE['DONE'] )
|
||||
parents += role.parents
|
||||
end
|
||||
}
|
||||
|
||||
# Select the nodes that are not parent from any node
|
||||
result = running_roles.select {|name, role|
|
||||
!parents.include?(name)
|
||||
}
|
||||
|
||||
# Ruby 1.8 compatibility
|
||||
if result.instance_of?(Array)
|
||||
result = Hash[result]
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
end
|
435
src/flow/lib/validator.rb
Normal file
435
src/flow/lib/validator.rb
Normal file
@ -0,0 +1,435 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2010-2013, C12G Labs S.L. #
|
||||
# #
|
||||
# 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. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
require 'uri'
|
||||
|
||||
class Hash
|
||||
# Returns a new hash containing the contents of other_hash and the
|
||||
# contents of self. If the value for entries with duplicate keys
|
||||
# is a Hash, it will be merged recursively, otherwise it will be that
|
||||
# of other_hash.
|
||||
#
|
||||
# @param [Hash] other_hash
|
||||
#
|
||||
# @return [Hash] Containing the merged values
|
||||
#
|
||||
# @example Merging two hashes
|
||||
# h1 = {:a => 3, {:b => 3, :c => 7}}
|
||||
# h2 = {:a => 22, c => 4, {:b => 5}}
|
||||
#
|
||||
# h1.deep_merge(h2) #=> {:a => 22, c => 4, {:b => 5, :c => 7}}
|
||||
def deep_merge(other_hash)
|
||||
target = dup
|
||||
|
||||
other_hash.each do |hash_key, hash_value|
|
||||
if hash_value.is_a?(Hash) and self[hash_key].is_a?(Hash)
|
||||
target[hash_key] = self[hash_key].deep_merge(hash_value)
|
||||
elsif hash_value.is_a?(Array) and self[hash_key].is_a?(Array)
|
||||
hash_value.each_with_index { |elem, i|
|
||||
if self[hash_key][i].is_a?(Hash) and elem.is_a?(Hash)
|
||||
target[hash_key][i] = self[hash_key][i].deep_merge(elem)
|
||||
else
|
||||
target[hash_key] = hash_value
|
||||
end
|
||||
}
|
||||
else
|
||||
target[hash_key] = hash_value
|
||||
end
|
||||
end
|
||||
|
||||
target
|
||||
end
|
||||
end
|
||||
|
||||
module Validator
|
||||
|
||||
class ParseException < StandardError; end
|
||||
class SchemaException < StandardError; end
|
||||
|
||||
class Validator
|
||||
|
||||
# @param [Hash] opts the options to validate a body
|
||||
# @option opts [Boolean] :default_values Set default values if the schema
|
||||
# specifies it (if true)
|
||||
# @option opts [Booblean] :delete_extra_properties If the body contains properties
|
||||
# not specified in the schema delete them from the body (if true)
|
||||
# or raise an exception (if false)
|
||||
def initialize(opts={})
|
||||
@opts = {
|
||||
:default_values => true,
|
||||
:delete_extra_properties => false
|
||||
}.merge(opts)
|
||||
end
|
||||
|
||||
# Recursively validate and modify a JSON body based on a schema.
|
||||
#
|
||||
# @see http://tools.ietf.org/html/draft-zyp-json-schema-03
|
||||
#
|
||||
# @param [Hash, Array, String, nil] body JSON represented as Ruby objects
|
||||
# @param [Hash] schema that will be used to validate
|
||||
# @param [String] key of the body that will be validated in this step
|
||||
#
|
||||
# @return [Hash, Array, String, nil] The modified body
|
||||
#
|
||||
# @raise [SchemaException] If the schema is not correctly defined
|
||||
# @raise [ParseException] if the body does not meet the schema definition
|
||||
#
|
||||
# @example Validate a User
|
||||
# schema = {
|
||||
# :type => :object,
|
||||
# :properties => {
|
||||
# 'username' => {
|
||||
# :type => :string
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# hash = {
|
||||
# 'username' => 'pepe'
|
||||
# }
|
||||
#
|
||||
# Validator.validate!(hash, schema)
|
||||
# #=> {'username' => 'pepe'}
|
||||
#
|
||||
# @note The parameter body will be modified
|
||||
# @note Schema options supported
|
||||
# :extends
|
||||
# :type => [:object, :array, :string, :null]
|
||||
#
|
||||
def validate!(body, schema, key="")
|
||||
if schema[:extends]
|
||||
base_schema = schema.delete(:extends)
|
||||
schema = base_schema.deep_merge(schema)
|
||||
end
|
||||
|
||||
case schema[:type]
|
||||
when :object then validate_object(body, schema, key)
|
||||
when :array then validate_array(body, schema, key)
|
||||
when :string then validate_string(body, schema, key)
|
||||
when :integer then validate_integer(body, schema, key)
|
||||
when :null then validate_null(body, schema, key)
|
||||
else raise SchemaException, "type #{schema[:type]} is not a valid type"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Validate an object type
|
||||
#
|
||||
# @param [Hash] body to be validated
|
||||
# @param [Hash] schema_object of the objectto validate the body
|
||||
# @param [String] key of the body that will be validated in this step
|
||||
#
|
||||
# @return [Hash] The modified body
|
||||
#
|
||||
# @raise [ParseException] if the body does not meet the schema definition
|
||||
#
|
||||
# @example Validate with default values
|
||||
# schema_body = {
|
||||
# :type => :object,
|
||||
# :properties => {
|
||||
# 'username' => {
|
||||
# :type => :string,
|
||||
# :default => 'def'
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# body = {}
|
||||
#
|
||||
# Validator.validate_object(body, schema_body)
|
||||
# #=> {'username' => 'def'}
|
||||
#
|
||||
# @note The parameter body will be modified
|
||||
# @note Schema options supported
|
||||
# :properties
|
||||
# :required
|
||||
# :default
|
||||
#
|
||||
def validate_object(body, schema_object, key)
|
||||
unless body.is_a?(Hash)
|
||||
raise ParseException, "KEY: #{key} must be a Hash; SCHEMA:"\
|
||||
"#{schema_object}"
|
||||
end
|
||||
|
||||
new_body = body.dup
|
||||
|
||||
schema_object[:properties].each{ |schema_key, schema_value|
|
||||
body_value = new_body.delete(schema_key)
|
||||
|
||||
if body_value
|
||||
body[schema_key] = validate!(body_value, schema_value,
|
||||
schema_key)
|
||||
else
|
||||
if schema_value[:required]
|
||||
raise ParseException, "KEY: '#{schema_key}' is required;"\
|
||||
" SCHEMA: #{schema_value}"
|
||||
end
|
||||
|
||||
if @opts[:default_values] && schema_value[:default]
|
||||
body[schema_key] = schema_value[:default]
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
# raise error if body.keys is not empty
|
||||
unless new_body.keys.empty?
|
||||
if @opts[:delete_extra_properties]
|
||||
new_body.keys.each{ |key|
|
||||
body.delete(key)
|
||||
}
|
||||
else
|
||||
raise ParseException, "KEY: #{new_body.keys.join(', ')} not"\
|
||||
" allowed; SCHEMA: #{schema_object}"
|
||||
end
|
||||
end
|
||||
|
||||
body
|
||||
end
|
||||
|
||||
# Validate an array type
|
||||
#
|
||||
# @param [Array] body to be validated
|
||||
# @param [Hash] schema_array of the object to validate the body
|
||||
# @param [String] schema_key of the body that will be validated in this step
|
||||
#
|
||||
# @return [Hash] The modified body
|
||||
#
|
||||
# @raise [ParseException] if the body does not meet the schema definition
|
||||
#
|
||||
# @example Validate array
|
||||
# schema = {
|
||||
# :type => :array,
|
||||
# :items => {
|
||||
# :type => :string
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# body = ['pepe', 'luis', 'juan']
|
||||
#
|
||||
# Validator.validate_array(body, schema)
|
||||
# #=> 'username' => ['pepe', 'luis', 'juan']
|
||||
#
|
||||
# @note The parameter body will be modified
|
||||
# @note Schema options supported
|
||||
# :items
|
||||
#
|
||||
def validate_array(body, schema_array, schema_key)
|
||||
if body.instance_of?(Array)
|
||||
body.collect { |body_item|
|
||||
validate!(body_item, schema_array[:items], schema_key)
|
||||
}
|
||||
else
|
||||
raise ParseException, "KEY: '#{schema_key}' must be an Array;"\
|
||||
" SCHEMA: #{schema_array}"
|
||||
end
|
||||
end
|
||||
|
||||
# Validate an integer type
|
||||
#
|
||||
# @param [Array] body to be validated
|
||||
# @param [Hash] schema_array of the object to validate the body
|
||||
# @param [String] schema_key of the body that will be validated in this step
|
||||
#
|
||||
# @return [Hash] The modified body
|
||||
#
|
||||
# @raise [ParseException] if the body does not meet the schema definition
|
||||
#
|
||||
# @example Validate array
|
||||
# schema = {
|
||||
# :type => :integer
|
||||
# }
|
||||
#
|
||||
# body = 5
|
||||
#
|
||||
# Validator.validate_integer(body, schema)
|
||||
# #=> 5
|
||||
#
|
||||
#
|
||||
def validate_integer(body, schema_array, schema_key)
|
||||
value = Integer(body)
|
||||
|
||||
if schema_array[:maximum]
|
||||
excl = schema_array[:exclusiveMaximum]
|
||||
max = schema_array[:maximum]
|
||||
if !(excl ? value < max : value <= max)
|
||||
raise ParseException, "KEY: '#{schema_key}' must be "\
|
||||
"lower than #{excl ? '' : 'or equal to'} #{max};"\
|
||||
" SCHEMA: #{schema_array}"
|
||||
end
|
||||
end
|
||||
|
||||
if schema_array[:minimum]
|
||||
excl = schema_array[:exclusiveMinimum]
|
||||
min = schema_array[:minimum]
|
||||
if !(excl ? value > min : value >= min)
|
||||
raise ParseException, "KEY: '#{schema_key}' must be "\
|
||||
"greater than #{excl ? '' : 'or equal to'} #{min};"\
|
||||
" SCHEMA: #{schema_array}"
|
||||
end
|
||||
end
|
||||
|
||||
value
|
||||
rescue ArgumentError
|
||||
raise ParseException, "KEY: '#{schema_key}' must be an Integer;"\
|
||||
" SCHEMA: #{schema_array}"
|
||||
end
|
||||
|
||||
# Validate an null type
|
||||
#
|
||||
# @param [nil] body to be validated
|
||||
# @param [Hash] schema_null of the object to validate the body
|
||||
# @param [String] schema_key of the body that will be validated in this step
|
||||
#
|
||||
# @return [nil]
|
||||
#
|
||||
# @raise [ParseException] if the body is not nil
|
||||
#
|
||||
# @example Validate array
|
||||
# schema = {
|
||||
# :type => :null
|
||||
# }
|
||||
#
|
||||
# body = nil
|
||||
#
|
||||
# Validator.validate_null(body, schema)
|
||||
# #=> nil
|
||||
#
|
||||
#
|
||||
def validate_null(body, schema_null, schema_key)
|
||||
if body != nil
|
||||
raise ParseException, "KEY: '#{schema_key}' is not allowed;"\
|
||||
" SCHEMA: #{schema_null}"
|
||||
end
|
||||
end
|
||||
|
||||
# Validate an string type
|
||||
#
|
||||
# @param [String] body to be validated
|
||||
# @param [Hash] schema_string of the object to validate the body
|
||||
# @param [String] schema_key of the body that will be validated in this step
|
||||
#
|
||||
# @return [String] The modified body
|
||||
#
|
||||
# @raise [ParseException] if the body does not meet the schema definition
|
||||
#
|
||||
# @example Validate array
|
||||
# schema = {
|
||||
# :type => :string
|
||||
# }
|
||||
#
|
||||
# body = "pepe"
|
||||
#
|
||||
# Validator.validate_string(body, schema)
|
||||
# #=> "pepe"
|
||||
#
|
||||
# @note The parameter body will be modified
|
||||
# @note Schema options supported
|
||||
# :format
|
||||
# :enum
|
||||
#
|
||||
def validate_string(body, schema_string, schema_key)
|
||||
if body.instance_of?(String)
|
||||
if schema_string[:format]
|
||||
check_format(body, schema_string, schema_key)
|
||||
elsif schema_string[:enum]
|
||||
check_enum(body, schema_string, schema_key)
|
||||
else
|
||||
body
|
||||
end
|
||||
else
|
||||
raise ParseException, "KEY: '#{schema_key}' must be a String;"\
|
||||
" SCHEMA: #{schema_string}"
|
||||
end
|
||||
end
|
||||
|
||||
# Validate an string format
|
||||
#
|
||||
# @param [String] body_value to be validated
|
||||
# @param [Hash] schema_string of the object to validate the body
|
||||
# @param [String] schema_key of the body that will be validated in this step
|
||||
#
|
||||
# @return [String] The modified body
|
||||
#
|
||||
# @raise [ParseException] if the body does not meet the schema definition
|
||||
#
|
||||
# @example Validate array
|
||||
# schema = {
|
||||
# :type => :string,
|
||||
# :format => :url
|
||||
# }
|
||||
#
|
||||
# body = "http://localhost:4567"
|
||||
#
|
||||
# Validator.check_format(body, schema)
|
||||
# #=> "http://localhost:4567"
|
||||
#
|
||||
# @note The parameter body will be modified
|
||||
# @note Schema options supported
|
||||
# :url
|
||||
#
|
||||
def check_format(body_value, schema_string, schema_key)
|
||||
case schema_string[:format]
|
||||
when :uri
|
||||
begin
|
||||
require 'uri'
|
||||
uri = URI.parse(body_value)
|
||||
rescue
|
||||
raise ParseException, "KEY: '#{schema_key}' must be a valid URL;"\
|
||||
" SCHEMA: #{schema_string} #{$!.message}"
|
||||
end
|
||||
|
||||
body_value
|
||||
end
|
||||
|
||||
body_value
|
||||
end
|
||||
|
||||
# Validate an string enum
|
||||
#
|
||||
# @param [String] body_value to be validated
|
||||
# @param [Hash] schema_string of the object to validate the body
|
||||
# @param [String] schema_key of the body that will be validated in this step
|
||||
#
|
||||
# @return [String] The modified body
|
||||
#
|
||||
# @raise [ParseException] if the body does not meet the schema definition
|
||||
#
|
||||
# @example Validate array
|
||||
# schema = {
|
||||
# :type => :string,
|
||||
# :enum => ['juan', 'luis']
|
||||
# }
|
||||
#
|
||||
# body = "juan"
|
||||
#
|
||||
# Validator.check_enum(body, schema)
|
||||
# #=> "juan"
|
||||
#
|
||||
# @note The parameter body will be modified
|
||||
# @note Schema options supported
|
||||
# :enum
|
||||
#
|
||||
def check_enum(body_value, schema_string, schema_key)
|
||||
if schema_string[:enum].include?(body_value)
|
||||
body_value
|
||||
else
|
||||
raise ParseException, "KEY: '#{schema_key}' must be one of"\
|
||||
" #{schema_string[:enum].join(', ')}; SCHEMA: #{schema_string}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
468
src/flow/oneflow-server.rb
Normal file
468
src/flow/oneflow-server.rb
Normal file
@ -0,0 +1,468 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2010-2013, C12G Labs S.L. #
|
||||
# #
|
||||
# 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. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
require 'rubygems'
|
||||
require 'sinatra'
|
||||
require 'yaml'
|
||||
|
||||
ONE_LOCATION = ENV["ONE_LOCATION"]
|
||||
|
||||
if !ONE_LOCATION
|
||||
LOG_LOCATION = "/var/log/one"
|
||||
VAR_LOCATION = "/var/lib/one"
|
||||
ETC_LOCATION = "/etc/one"
|
||||
LIB_LOCATION = "/usr/lib/one"
|
||||
RUBY_LIB_LOCATION = "/usr/lib/one/ruby"
|
||||
else
|
||||
VAR_LOCATION = ONE_LOCATION + "/var"
|
||||
LOG_LOCATION = ONE_LOCATION + "/var"
|
||||
ETC_LOCATION = ONE_LOCATION + "/etc"
|
||||
LIB_LOCATION = ONE_LOCATION+"/lib"
|
||||
RUBY_LIB_LOCATION = ONE_LOCATION+"/lib/ruby"
|
||||
end
|
||||
|
||||
ONEFLOW_AUTH = VAR_LOCATION + "/.one/oneflow_auth"
|
||||
|
||||
ONEFLOW_LOG = LOG_LOCATION + "/oneflow.log"
|
||||
CONFIGURATION_FILE = ETC_LOCATION + "/oneflow-server.conf"
|
||||
|
||||
$: << RUBY_LIB_LOCATION
|
||||
$: << RUBY_LIB_LOCATION+'/cloud'
|
||||
$: << LIB_LOCATION+'/oneflow/lib'
|
||||
|
||||
require 'CloudAuth'
|
||||
require 'CloudServer'
|
||||
|
||||
require 'models'
|
||||
require 'log'
|
||||
|
||||
##############################################################################
|
||||
# Configuration
|
||||
##############################################################################
|
||||
|
||||
begin
|
||||
conf = YAML.load_file(CONFIGURATION_FILE)
|
||||
rescue Exception => e
|
||||
STDERR.puts "Error parsing config file #{CONFIGURATION_FILE}: #{e.message}"
|
||||
exit 1
|
||||
end
|
||||
|
||||
conf[:debug_level] ||= 2
|
||||
conf[:lcm_interval] ||= 30
|
||||
conf[:default_cooldown] ||= 300
|
||||
conf[:shutdown_action] ||= 'shutdown'
|
||||
conf[:action_number] ||= 1
|
||||
conf[:action_period] ||= 60
|
||||
|
||||
conf[:auth] = 'opennebula'
|
||||
|
||||
set :bind, conf[:host]
|
||||
set :port, conf[:port]
|
||||
|
||||
set :config, conf
|
||||
|
||||
include CloudLogger
|
||||
logger = enable_logging ONEFLOW_LOG, conf[:debug_level].to_i
|
||||
|
||||
use Rack::Session::Pool, :key => 'oneflow'
|
||||
|
||||
Log.logger = logger
|
||||
Log.level = conf[:debug_level].to_i
|
||||
|
||||
|
||||
LOG_COMP = "ONEFLOW"
|
||||
|
||||
Log.info LOG_COMP, "Starting server"
|
||||
|
||||
begin
|
||||
ENV["ONE_CIPHER_AUTH"] = ONEFLOW_AUTH
|
||||
cloud_auth = CloudAuth.new(conf)
|
||||
rescue => e
|
||||
message = "Error initializing authentication system : #{e.message}"
|
||||
Log.error LOG_COMP, message
|
||||
STDERR.puts message
|
||||
exit -1
|
||||
end
|
||||
|
||||
set :cloud_auth, cloud_auth
|
||||
|
||||
##############################################################################
|
||||
# Helpers
|
||||
##############################################################################
|
||||
|
||||
|
||||
before do
|
||||
auth = Rack::Auth::Basic::Request.new(request.env)
|
||||
|
||||
if auth.provided? && auth.basic?
|
||||
username, password = auth.credentials
|
||||
|
||||
@client = OpenNebula::Client.new("#{username}:#{password}")
|
||||
else
|
||||
error 401, "A username and password must be provided"
|
||||
end
|
||||
end
|
||||
|
||||
##############################################################################
|
||||
# Defaults
|
||||
##############################################################################
|
||||
|
||||
Role.init_default_cooldown(conf[:default_cooldown])
|
||||
Role.init_default_shutdown(conf[:shutdown_action])
|
||||
|
||||
##############################################################################
|
||||
# LCM thread
|
||||
##############################################################################
|
||||
|
||||
t = Thread.new {
|
||||
require 'LifeCycleManager'
|
||||
|
||||
ServiceLCM.new(conf[:lcm_interval], settings.cloud_auth).loop
|
||||
}
|
||||
t.abort_on_exception = true
|
||||
|
||||
|
||||
##############################################################################
|
||||
# Service
|
||||
##############################################################################
|
||||
|
||||
get '/service' do
|
||||
service_pool = OpenNebula::ServicePool.new(@client, OpenNebula::Pool::INFO_ALL)
|
||||
|
||||
rc = service_pool.info
|
||||
if OpenNebula.is_error?(rc)
|
||||
error CloudServer::HTTP_ERROR_CODE[rc.errno], rc.message
|
||||
end
|
||||
|
||||
status 200
|
||||
|
||||
body service_pool.to_json
|
||||
end
|
||||
|
||||
get '/service/:id' do
|
||||
service_pool = OpenNebula::ServicePool.new(@client)
|
||||
|
||||
service = service_pool.get(params[:id])
|
||||
|
||||
if OpenNebula.is_error?(service)
|
||||
error CloudServer::HTTP_ERROR_CODE[service.errno], service.message
|
||||
end
|
||||
|
||||
status 200
|
||||
|
||||
body service.to_json
|
||||
end
|
||||
|
||||
delete '/service/:id' do
|
||||
service_pool = OpenNebula::ServicePool.new(@client)
|
||||
|
||||
rc = nil
|
||||
service = service_pool.get(params[:id]) { |service|
|
||||
rc = service.delete
|
||||
}
|
||||
|
||||
if OpenNebula.is_error?(service)
|
||||
error CloudServer::HTTP_ERROR_CODE[service.errno], service.message
|
||||
end
|
||||
|
||||
if OpenNebula.is_error?(rc)
|
||||
error CloudServer::HTTP_ERROR_CODE[rc.errno], rc.message
|
||||
end
|
||||
|
||||
status 201
|
||||
end
|
||||
|
||||
post '/service/:id/action' do
|
||||
service_pool = OpenNebula::ServicePool.new(@client)
|
||||
action = JSON.parse(request.body.read)['action']
|
||||
opts = action['params']
|
||||
|
||||
rc = nil
|
||||
service = service_pool.get(params[:id]) { |service|
|
||||
rc = case action['perform']
|
||||
when 'shutdown'
|
||||
service.shutdown
|
||||
when 'recover', 'deploy'
|
||||
service.recover
|
||||
when 'chown'
|
||||
if opts && opts['owner_id']
|
||||
args = Array.new
|
||||
args << opts['owner_id'].to_i
|
||||
args << (opts['group_id'] || -1).to_i
|
||||
|
||||
ret = service.chown(*args)
|
||||
|
||||
if !OpenNebula.is_error?(ret)
|
||||
Log.info(LOG_COMP, "Service owner changed to #{args[0]}:#{args[1]}", params[:id])
|
||||
end
|
||||
|
||||
ret
|
||||
else
|
||||
OpenNebula::Error.new("Action #{action['perform']}: " <<
|
||||
"You have to specify a UID")
|
||||
end
|
||||
when 'chgrp'
|
||||
if opts && opts['group_id']
|
||||
ret = service.chown(-1, opts['group_id'].to_i)
|
||||
|
||||
if !OpenNebula.is_error?(ret)
|
||||
Log.info(LOG_COMP, "Service group changed to #{opts['group_id']}", params[:id])
|
||||
end
|
||||
|
||||
ret
|
||||
else
|
||||
OpenNebula::Error.new("Action #{action['perform']}: " <<
|
||||
"You have to specify a GID")
|
||||
end
|
||||
when 'chmod'
|
||||
if opts && opts['octet']
|
||||
ret = service.chmod_octet(opts['octet'])
|
||||
|
||||
if !OpenNebula.is_error?(ret)
|
||||
Log.info(LOG_COMP, "Service permissions changed to #{opts['octet']}", params[:id])
|
||||
end
|
||||
|
||||
ret
|
||||
else
|
||||
OpenNebula::Error.new("Action #{action['perform']}: " <<
|
||||
"You have to specify an OCTET")
|
||||
end
|
||||
else
|
||||
OpenNebula::Error.new("Action #{action['perform']} not supported")
|
||||
end
|
||||
}
|
||||
|
||||
if OpenNebula.is_error?(service)
|
||||
error CloudServer::HTTP_ERROR_CODE[service.errno], service.message
|
||||
end
|
||||
|
||||
if OpenNebula.is_error?(rc)
|
||||
error CloudServer::HTTP_ERROR_CODE[rc.errno], rc.message
|
||||
end
|
||||
|
||||
status 201
|
||||
end
|
||||
|
||||
put '/service/:id/role/:name' do
|
||||
|
||||
service_pool = OpenNebula::ServicePool.new(@client)
|
||||
|
||||
rc = nil
|
||||
service = service_pool.get(params[:id]) do |service|
|
||||
begin
|
||||
rc = service.update_role(params[:name], request.body.read)
|
||||
rescue Validator::ParseException, JSON::ParserError
|
||||
return error 400, $!.message
|
||||
end
|
||||
end
|
||||
|
||||
if OpenNebula.is_error?(service)
|
||||
error CloudServer::HTTP_ERROR_CODE[service.errno], service.message
|
||||
end
|
||||
|
||||
if OpenNebula.is_error?(rc)
|
||||
error CloudServer::HTTP_ERROR_CODE[rc.errno], rc.message
|
||||
end
|
||||
|
||||
status 200
|
||||
end
|
||||
|
||||
post '/service/:id/role/:role_name/action' do
|
||||
service_pool = OpenNebula::ServicePool.new(@client)
|
||||
action = JSON.parse(request.body.read)['action']
|
||||
opts = action['params']
|
||||
|
||||
rc = nil
|
||||
service = service_pool.get(params[:id]) { |service|
|
||||
roles = service.get_roles
|
||||
|
||||
role = roles[params[:role_name]]
|
||||
if role.nil?
|
||||
rc = OpenNebula::Error.new("Role '#{params[:role_name]}' not found")
|
||||
else
|
||||
# Use defaults only if one of the options is supplied
|
||||
if opts['period'].nil? ^ opts['number'].nil?
|
||||
opts['period'] = conf[:action_period] if opts['period'].nil?
|
||||
opts['number'] = conf[:action_number] if opts['number'].nil?
|
||||
end
|
||||
|
||||
rc = role.batch_action(action['perform'], opts['period'], opts['number'])
|
||||
end
|
||||
}
|
||||
|
||||
if OpenNebula.is_error?(service)
|
||||
error CloudServer::HTTP_ERROR_CODE[service.errno], service.message
|
||||
end
|
||||
|
||||
if OpenNebula.is_error?(rc)
|
||||
error CloudServer::HTTP_ERROR_CODE[rc.errno], rc.message
|
||||
end
|
||||
|
||||
status 201
|
||||
body rc.to_json
|
||||
end
|
||||
|
||||
##############################################################################
|
||||
# Service Template
|
||||
##############################################################################
|
||||
|
||||
get '/service_template' do
|
||||
s_template_pool = OpenNebula::ServiceTemplatePool.new(@client, OpenNebula::Pool::INFO_ALL)
|
||||
|
||||
rc = s_template_pool.info
|
||||
if OpenNebula.is_error?(rc)
|
||||
error CloudServer::HTTP_ERROR_CODE[rc.errno], rc.message
|
||||
end
|
||||
|
||||
status 200
|
||||
|
||||
body s_template_pool.to_json
|
||||
end
|
||||
|
||||
get '/service_template/:id' do
|
||||
service_template = OpenNebula::ServiceTemplate.new_with_id(params[:id], @client)
|
||||
|
||||
rc = service_template.info
|
||||
if OpenNebula.is_error?(rc)
|
||||
error CloudServer::HTTP_ERROR_CODE[rc.errno], rc.message
|
||||
end
|
||||
|
||||
status 200
|
||||
|
||||
body service_template.to_json
|
||||
end
|
||||
|
||||
delete '/service_template/:id' do
|
||||
service_template = OpenNebula::ServiceTemplate.new_with_id(params[:id], @client)
|
||||
|
||||
rc = service_template.delete
|
||||
if OpenNebula.is_error?(rc)
|
||||
error CloudServer::HTTP_ERROR_CODE[rc.errno], rc.message
|
||||
end
|
||||
|
||||
status 201
|
||||
end
|
||||
|
||||
put '/service_template/:id' do
|
||||
service_template = OpenNebula::ServiceTemplate.new_with_id(params[:id], @client)
|
||||
|
||||
begin
|
||||
rc = service_template.update(request.body.read)
|
||||
rescue Validator::ParseException, JSON::ParserError
|
||||
error 400, $!.message
|
||||
end
|
||||
|
||||
if OpenNebula.is_error?(rc)
|
||||
error CloudServer::HTTP_ERROR_CODE[rc.errno], rc.message
|
||||
end
|
||||
|
||||
service_template.info
|
||||
|
||||
status 200
|
||||
body service_template.to_json
|
||||
end
|
||||
|
||||
post '/service_template' do
|
||||
s_template = OpenNebula::ServiceTemplate.new(
|
||||
OpenNebula::ServiceTemplate.build_xml,
|
||||
@client)
|
||||
|
||||
begin
|
||||
rc = s_template.allocate(request.body.read)
|
||||
rescue Validator::ParseException, JSON::ParserError
|
||||
error 400, $!.message
|
||||
end
|
||||
|
||||
if OpenNebula.is_error?(rc)
|
||||
error CloudServer::HTTP_ERROR_CODE[rc.errno], rc.message
|
||||
end
|
||||
|
||||
s_template.info
|
||||
|
||||
status 201
|
||||
#body Parser.render(rc)
|
||||
body s_template.to_json
|
||||
end
|
||||
|
||||
post '/service_template/:id/action' do
|
||||
service_template = OpenNebula::ServiceTemplate.new_with_id(params[:id], @client)
|
||||
|
||||
action = JSON.parse(request.body.read)['action']
|
||||
|
||||
opts = action['params']
|
||||
|
||||
rc = case action['perform']
|
||||
when 'instantiate'
|
||||
rc = service_template.info
|
||||
if OpenNebula.is_error?(rc)
|
||||
error CloudServer::HTTP_ERROR_CODE[rc.errno], rc.message
|
||||
end
|
||||
|
||||
service = OpenNebula::Service.new(OpenNebula::Service.build_xml, @client)
|
||||
rc = service.allocate(service_template.template)
|
||||
if OpenNebula.is_error?(rc)
|
||||
error CloudServer::HTTP_ERROR_CODE[rc.errno], rc.message
|
||||
end
|
||||
|
||||
service.info
|
||||
|
||||
body service.to_json
|
||||
when 'chown'
|
||||
if opts && opts['owner_id']
|
||||
args = Array.new
|
||||
args << opts['owner_id'].to_i
|
||||
args << (opts['group_id'].to_i || -1)
|
||||
|
||||
service_template.chown(*args)
|
||||
else
|
||||
OpenNebula::Error.new("Action #{action['perform']}: " <<
|
||||
"You have to specify a UID")
|
||||
end
|
||||
when 'chgrp'
|
||||
if opts && opts['group_id']
|
||||
service_template.chown(-1, opts['group_id'].to_i)
|
||||
else
|
||||
OpenNebula::Error.new("Action #{action['perform']}: " <<
|
||||
"You have to specify a GID")
|
||||
end
|
||||
when 'chmod'
|
||||
if opts && opts['octet']
|
||||
service_template.chmod_octet(opts['octet'])
|
||||
else
|
||||
OpenNebula::Error.new("Action #{action['perform']}: " <<
|
||||
"You have to specify an OCTET")
|
||||
end
|
||||
when 'update'
|
||||
if opts && opts['template_json']
|
||||
begin
|
||||
rc = service_template.update(opts['template_json'])
|
||||
rescue Validator::ParseException, JSON::ParserError
|
||||
OpenNebula::Error.new($!.message)
|
||||
end
|
||||
else
|
||||
OpenNebula::Error.new("Action #{action['perform']}: " <<
|
||||
"You have to provide a template")
|
||||
end
|
||||
else
|
||||
OpenNebula::Error.new("Action #{action['perform']} not supported")
|
||||
end
|
||||
|
||||
if OpenNebula.is_error?(rc)
|
||||
error CloudServer::HTTP_ERROR_CODE[rc.errno], rc.message
|
||||
else
|
||||
status 201
|
||||
end
|
||||
end
|
387
src/oca/ruby/opennebula/oneflow_client.rb
Normal file
387
src/oca/ruby/opennebula/oneflow_client.rb
Normal file
@ -0,0 +1,387 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2010-2013, C12G Labs S.L. #
|
||||
# #
|
||||
# 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. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
require 'uri'
|
||||
require 'cloud/CloudClient'
|
||||
|
||||
include CloudCLI
|
||||
|
||||
module Role
|
||||
# Actions that can be performed on the VMs of a given Role
|
||||
SCHEDULE_ACTIONS = [
|
||||
'shutdown',
|
||||
'shutdown-hard',
|
||||
'undeploy',
|
||||
'undeploy-hard',
|
||||
'hold',
|
||||
'release',
|
||||
'stop',
|
||||
'suspend',
|
||||
'resume',
|
||||
'boot',
|
||||
'delete',
|
||||
'delete-recreate',
|
||||
'reboot',
|
||||
'reboot-hard',
|
||||
'poweroff',
|
||||
'poweroff-hard',
|
||||
'snapshot-create'
|
||||
]
|
||||
|
||||
STATE = {
|
||||
'PENDING' => 0,
|
||||
'DEPLOYING' => 1,
|
||||
'RUNNING' => 2,
|
||||
'UNDEPLOYING' => 3,
|
||||
'WARNING' => 4,
|
||||
'DONE' => 5,
|
||||
'FAILED_UNDEPLOYING' => 6,
|
||||
'FAILED_DEPLOYING' => 7,
|
||||
'SCALING' => 8,
|
||||
'FAILED_SCALING' => 9,
|
||||
'COOLDOWN' => 10
|
||||
}
|
||||
|
||||
STATE_STR = [
|
||||
'PENDING',
|
||||
'DEPLOYING',
|
||||
'RUNNING',
|
||||
'UNDEPLOYING',
|
||||
'WARNING',
|
||||
'DONE',
|
||||
'FAILED_UNDEPLOYING',
|
||||
'FAILED_DEPLOYING',
|
||||
'SCALING',
|
||||
'FAILED_SCALING',
|
||||
'COOLDOWN'
|
||||
]
|
||||
|
||||
# Returns the string representation of the role state
|
||||
# @param [String] state String number representing the state
|
||||
# @return the state string
|
||||
def self.state_str(state_number)
|
||||
return STATE_STR[state_number.to_i]
|
||||
end
|
||||
end
|
||||
|
||||
module Service
|
||||
|
||||
STATE = {
|
||||
'PENDING' => 0,
|
||||
'DEPLOYING' => 1,
|
||||
'RUNNING' => 2,
|
||||
'UNDEPLOYING' => 3,
|
||||
'WARNING' => 4,
|
||||
'DONE' => 5,
|
||||
'FAILED_UNDEPLOYING' => 6,
|
||||
'FAILED_DEPLOYING' => 7,
|
||||
'SCALING' => 8,
|
||||
'FAILED_SCALING' => 9,
|
||||
'COOLDOWN' => 10
|
||||
}
|
||||
|
||||
STATE_STR = [
|
||||
'PENDING',
|
||||
'DEPLOYING',
|
||||
'RUNNING',
|
||||
'UNDEPLOYING',
|
||||
'WARNING',
|
||||
'DONE',
|
||||
'FAILED_UNDEPLOYING',
|
||||
'FAILED_DEPLOYING',
|
||||
'SCALING',
|
||||
'FAILED_SCALING',
|
||||
'COOLDOWN'
|
||||
]
|
||||
|
||||
# Returns the string representation of the service state
|
||||
# @param [String] state String number representing the state
|
||||
# @return the state string
|
||||
def self.state_str(state_number)
|
||||
return STATE_STR[state_number.to_i]
|
||||
end
|
||||
|
||||
# Build a json specifying an action
|
||||
# @param [String] perform action to be performed (i.e: shutdowm)
|
||||
# @param [Hash, nil] params contains the params for the action
|
||||
# @return [String] json representing the action
|
||||
def self.build_json_action(perform, params=nil)
|
||||
body = Hash.new
|
||||
body['perform'] = perform
|
||||
body['params'] = params if params
|
||||
|
||||
action = Hash.new
|
||||
action['action'] = body
|
||||
|
||||
JSON.pretty_generate action
|
||||
end
|
||||
|
||||
# CLI options
|
||||
|
||||
DEFAULT_OPTIONS = [
|
||||
ENDPOINT = {
|
||||
:name => "server",
|
||||
:short => "-s url",
|
||||
:large => "--server url",
|
||||
:format => String,
|
||||
:description => "Service endpoint"
|
||||
},
|
||||
USERNAME={
|
||||
:name => "username",
|
||||
:short => "-u name",
|
||||
:large => "--username name",
|
||||
:format => String,
|
||||
:description => "User name"
|
||||
},
|
||||
PASSWORD={
|
||||
:name => "password",
|
||||
:short => "-p pass",
|
||||
:large => "--password pass",
|
||||
:format => String,
|
||||
:description => "User password"
|
||||
}
|
||||
]
|
||||
|
||||
JSON_FORMAT = {
|
||||
:name => "json",
|
||||
:short => "-j",
|
||||
:large => "--json",
|
||||
:description => "Print the resource in JSON"
|
||||
}
|
||||
|
||||
TOP = {
|
||||
:name => "top",
|
||||
:short => "-t",
|
||||
:large => "--top",
|
||||
:description => "Top for the command"
|
||||
}
|
||||
|
||||
PERIOD = {
|
||||
:name => "period",
|
||||
:short => "-p x",
|
||||
:large => "--period x",
|
||||
:format => Integer,
|
||||
:description => "Seconds between each group of actions"
|
||||
}
|
||||
|
||||
NUMBER = {
|
||||
:name => "number",
|
||||
:short => "-n x",
|
||||
:large => "--number x",
|
||||
:format => Integer,
|
||||
:description => "Number of VMs to apply the action to each period"
|
||||
}
|
||||
|
||||
FORCE = {
|
||||
:name => "force",
|
||||
:short => "-f",
|
||||
:large => "--force",
|
||||
:description => "Force the new cardinality even if it is outside the limits"
|
||||
}
|
||||
|
||||
# Format helpers
|
||||
|
||||
# def self.rname_to_id(name, poolname, options)
|
||||
def self.rname_to_id(name, poolname)
|
||||
return 0, name.to_i if name.match(/^[0123456789]+$/)
|
||||
|
||||
client = Service::Client.new()
|
||||
|
||||
resource_path = case poolname
|
||||
when "SERVICE" then "/service"
|
||||
when "SERVICE TEMPLATE" then "/service_template"
|
||||
end
|
||||
|
||||
response = client.get(resource_path)
|
||||
|
||||
if CloudClient::is_error?(response)
|
||||
return -1, "OpenNebula #{poolname} name not found," <<
|
||||
" use the ID instead"
|
||||
end
|
||||
|
||||
pool = JSON.parse(response.body)
|
||||
name_to_id(name, pool, poolname)
|
||||
end
|
||||
|
||||
def self.rname_to_id_desc(poolname)
|
||||
"OpenNebula #{poolname} name or id"
|
||||
end
|
||||
|
||||
def self.name_to_id(name, pool, ename)
|
||||
if pool['DOCUMENT_POOL']['DOCUMENT'].nil?
|
||||
return -1, "#{ename} named #{name} not found."
|
||||
end
|
||||
|
||||
objects = pool['DOCUMENT_POOL']['DOCUMENT'].select {|object| object['NAME'] == name }
|
||||
|
||||
if objects.length>0
|
||||
if objects.length>1
|
||||
return -1, "There are multiple #{ename}s with name #{name}."
|
||||
else
|
||||
result = objects.first['ID']
|
||||
end
|
||||
else
|
||||
return -1, "#{ename} named #{name} not found."
|
||||
end
|
||||
|
||||
return 0, result
|
||||
end
|
||||
|
||||
def self.list_to_id(names, poolname)
|
||||
|
||||
client = Service::Client.new()
|
||||
|
||||
resource_path = case poolname
|
||||
when "SERVICE" then "/service"
|
||||
when "SERVICE TEMPLATE" then "/service_template"
|
||||
end
|
||||
|
||||
response = client.get(resource_path)
|
||||
|
||||
if CloudClient::is_error?(response)
|
||||
return -1, "OpenNebula #{poolname} name not found," <<
|
||||
" use the ID instead"
|
||||
end
|
||||
|
||||
pool = JSON.parse(response.body)
|
||||
|
||||
result = names.split(',').collect { |name|
|
||||
if name.match(/^[0123456789]+$/)
|
||||
name.to_i
|
||||
else
|
||||
rc = name_to_id(name, pool, poolname)
|
||||
|
||||
if rc.first == -1
|
||||
return rc[0], rc[1]
|
||||
end
|
||||
|
||||
rc[1]
|
||||
end
|
||||
}
|
||||
|
||||
return 0, result
|
||||
end
|
||||
|
||||
def self.list_to_id_desc(poolname)
|
||||
"Comma-separated list of OpenNebula #{poolname} names or ids"
|
||||
end
|
||||
|
||||
# Perform an action on several resources
|
||||
# @param [Array] ids resources ids
|
||||
# @param [Block] block action to be performed
|
||||
# @return [Integer] exit_code
|
||||
def self.perform_actions(ids, &block)
|
||||
exit_code = 0
|
||||
|
||||
ids.each do |id|
|
||||
response = block.call(id)
|
||||
|
||||
if CloudClient::is_error?(response)
|
||||
puts response.to_s
|
||||
exit_code = response.code.to_i
|
||||
end
|
||||
end
|
||||
|
||||
exit_code
|
||||
end
|
||||
|
||||
class Client
|
||||
def initialize(opts={})
|
||||
@username = opts[:username] || ENV['ONEFLOW_USER']
|
||||
@password = opts[:password] || ENV['ONEFLOW_PASSWORD']
|
||||
|
||||
url = opts[:url] || ENV['ONEFLOW_URL'] || 'http://localhost:2474'
|
||||
|
||||
if @username.nil? && @password.nil?
|
||||
if ENV["ONE_AUTH"] and !ENV["ONE_AUTH"].empty? and File.file?(ENV["ONE_AUTH"])
|
||||
one_auth = File.read(ENV["ONE_AUTH"])
|
||||
elsif File.file?(ENV["HOME"]+"/.one/one_auth")
|
||||
one_auth = File.read(ENV["HOME"]+"/.one/one_auth")
|
||||
end
|
||||
|
||||
one_auth.rstrip!
|
||||
|
||||
@username, @password = one_auth.split(':')
|
||||
end
|
||||
|
||||
@uri = URI.parse(url)
|
||||
|
||||
@user_agent = "OpenNebula #{CloudClient::VERSION} " <<
|
||||
"(#{opts[:user_agent]||"Ruby"})"
|
||||
|
||||
@host = nil
|
||||
@port = nil
|
||||
|
||||
if ENV['http_proxy']
|
||||
uri_proxy = URI.parse(ENV['http_proxy'])
|
||||
@host = uri_proxy.host
|
||||
@port = uri_proxy.port
|
||||
end
|
||||
end
|
||||
|
||||
def get(path)
|
||||
req = Net::HTTP::Proxy(@host, @port)::Get.new(path)
|
||||
|
||||
do_request(req)
|
||||
end
|
||||
|
||||
def delete(path)
|
||||
req =Net::HTTP::Proxy(@host, @port)::Delete.new(path)
|
||||
|
||||
do_request(req)
|
||||
end
|
||||
|
||||
def post(path, body)
|
||||
req = Net::HTTP::Proxy(@host, @port)::Post.new(path)
|
||||
req.body = body
|
||||
|
||||
do_request(req)
|
||||
end
|
||||
|
||||
def put(path, body)
|
||||
req = Net::HTTP::Proxy(@host, @port)::Put.new(path)
|
||||
req.body = body
|
||||
|
||||
do_request(req)
|
||||
end
|
||||
|
||||
def login
|
||||
req = Net::HTTP::Proxy(@host, @port)::Post.new('/login')
|
||||
|
||||
do_request(req)
|
||||
end
|
||||
|
||||
def logout
|
||||
req = Net::HTTP::Proxy(@host, @port)::Post.new('/logout')
|
||||
|
||||
do_request(req)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def do_request(req)
|
||||
req.basic_auth @username, @password
|
||||
|
||||
req['User-Agent'] = @user_agent
|
||||
|
||||
res = CloudClient::http_start(@uri, @timeout) do |http|
|
||||
http.request(req)
|
||||
end
|
||||
|
||||
res
|
||||
end
|
||||
end
|
||||
end
|
@ -30,10 +30,13 @@ module Migrator
|
||||
begin
|
||||
FileUtils.cp("#{VAR_LOCATION}/.one/sunstone_auth",
|
||||
"#{VAR_LOCATION}/.one/onegate_auth", :preserve => true)
|
||||
|
||||
FileUtils.cp("#{VAR_LOCATION}/.one/sunstone_auth",
|
||||
"#{VAR_LOCATION}/.one/oneflow_auth", :preserve => true)
|
||||
rescue
|
||||
puts "Error trying to copy #{VAR_LOCATION}/.one/sunstone_auth "<<
|
||||
"to #{VAR_LOCATION}/.one/onegate_auth."
|
||||
puts "Please copy the file manually."
|
||||
"to #{VAR_LOCATION}/.one/onegate_auth and #{VAR_LOCATION}/.one/oneflow_auth."
|
||||
puts "Please copy the files manually."
|
||||
end
|
||||
|
||||
@db.run "ALTER TABLE user_pool RENAME TO old_user_pool;"
|
||||
|
23
src/sunstone/etc/sunstone-oneflow.conf
Normal file
23
src/sunstone/etc/sunstone-oneflow.conf
Normal file
@ -0,0 +1,23 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2010-2013, C12G Labs S.L. #
|
||||
# #
|
||||
# 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. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
################################################################################
|
||||
# OneFlow
|
||||
################################################################################
|
||||
|
||||
# OneFlow endpoint
|
||||
#
|
||||
:oneflow_server: http://localhost:2474/
|
@ -124,3 +124,6 @@
|
||||
#:routes:
|
||||
# - custom
|
||||
# - other
|
||||
|
||||
:routes:
|
||||
- oneflow
|
@ -16,6 +16,9 @@ enabled_tabs:
|
||||
- datastores-tab
|
||||
- vnets-tab
|
||||
- marketplace-tab
|
||||
#- oneflow-dashboard
|
||||
#- oneflow-services
|
||||
#- oneflow-templates
|
||||
tabs:
|
||||
dashboard-tab:
|
||||
panel_tabs:
|
||||
@ -341,3 +344,86 @@ tabs:
|
||||
actions:
|
||||
Marketplace.refresh: true
|
||||
Marketplace.import: true
|
||||
oneflow-dashboard:
|
||||
panel_tabs:
|
||||
table_columns:
|
||||
actions:
|
||||
oneflow-services:
|
||||
panel_tabs:
|
||||
service_info_tab: true
|
||||
service_roles_tab: true
|
||||
service_log_tab: true
|
||||
panel_tabs_actions:
|
||||
service_roles_tab:
|
||||
Role.scale: true
|
||||
Role.hold: true
|
||||
Role.release: true
|
||||
Role.suspend: true
|
||||
Role.resume: true
|
||||
Role.stop: true
|
||||
Role.boot: true
|
||||
Role.reboot: true
|
||||
Role.reboot_hard: true
|
||||
Role.poweroff: true
|
||||
Role.poweroff_hard: true
|
||||
Role.shutdown: true
|
||||
Role.shutdown_hard: true
|
||||
Role.delete: true
|
||||
Role.delete_recreate: true
|
||||
RoleVM.chown: true
|
||||
RoleVM.chgrp: true
|
||||
RoleVM.deploy: true
|
||||
RoleVM.migrate: true
|
||||
RoleVM.migrate_live: true
|
||||
RoleVM.hold: true
|
||||
RoleVM.release: true
|
||||
RoleVM.suspend: true
|
||||
RoleVM.resume: true
|
||||
RoleVM.stop: true
|
||||
RoleVM.boot: true
|
||||
RoleVM.reboot: true
|
||||
RoleVM.reboot_hard: true
|
||||
RoleVM.poweroff: true
|
||||
RoleVM.poweroff_hard: true
|
||||
RoleVM.undeploy: true
|
||||
RoleVM.undeploy_hard: true
|
||||
RoleVM.shutdown: true
|
||||
RoleVM.shutdown_hard: true
|
||||
RoleVM.delete: true
|
||||
RoleVM.delete_recreate: true
|
||||
RoleVM.resched: true
|
||||
RoleVM.unresched: true
|
||||
RoleVM.recover: true
|
||||
table_columns:
|
||||
- 0 # Checkbox
|
||||
- 1 # ID
|
||||
- 2 # Owner
|
||||
- 3 # Group
|
||||
- 4 # Name
|
||||
- 5 # State
|
||||
actions:
|
||||
Service.refresh: true
|
||||
Service.chown: true
|
||||
Service.chgrp: true
|
||||
Service.chmod: true
|
||||
Service.shutdown: true
|
||||
Service.recover: true
|
||||
Service.delete: true
|
||||
oneflow-templates:
|
||||
panel_tabs:
|
||||
service_template_info_panel: true
|
||||
service_template_roles_tab: true
|
||||
table_columns:
|
||||
- 0 # Checkbox
|
||||
- 1 # ID
|
||||
- 2 # Owner
|
||||
- 3 # Group
|
||||
- 4 # Name
|
||||
actions:
|
||||
ServiceTemplate.refresh: true
|
||||
ServiceTemplate.create_dialog: true
|
||||
ServiceTemplate.instantiate: true
|
||||
ServiceTemplate.chown: true
|
||||
ServiceTemplate.chgrp: true
|
||||
ServiceTemplate.chmod: true
|
||||
ServiceTemplate.delete: true
|
||||
|
@ -16,6 +16,9 @@ enabled_tabs:
|
||||
- datastores-tab
|
||||
- vnets-tab
|
||||
- marketplace-tab
|
||||
#- oneflow-dashboard
|
||||
#- oneflow-services
|
||||
#- oneflow-templates
|
||||
tabs:
|
||||
dashboard-tab:
|
||||
panel_tabs:
|
||||
@ -342,3 +345,86 @@ tabs:
|
||||
actions:
|
||||
Marketplace.refresh: true
|
||||
Marketplace.import: true
|
||||
oneflow-dashboard:
|
||||
panel_tabs:
|
||||
table_columns:
|
||||
actions:
|
||||
oneflow-services:
|
||||
panel_tabs:
|
||||
service_info_tab: true
|
||||
service_roles_tab: true
|
||||
service_log_tab: true
|
||||
panel_tabs_actions:
|
||||
service_roles_tab:
|
||||
Role.scale: true
|
||||
Role.hold: true
|
||||
Role.release: true
|
||||
Role.suspend: true
|
||||
Role.resume: true
|
||||
Role.stop: true
|
||||
Role.boot: true
|
||||
Role.reboot: true
|
||||
Role.reboot_hard: true
|
||||
Role.poweroff: true
|
||||
Role.poweroff_hard: true
|
||||
Role.shutdown: true
|
||||
Role.shutdown_hard: true
|
||||
Role.delete: true
|
||||
Role.delete_recreate: true
|
||||
RoleVM.chown: false
|
||||
RoleVM.chgrp: false
|
||||
RoleVM.deploy: false
|
||||
RoleVM.migrate: false
|
||||
RoleVM.migrate_live: false
|
||||
RoleVM.hold: true
|
||||
RoleVM.release: true
|
||||
RoleVM.suspend: true
|
||||
RoleVM.resume: true
|
||||
RoleVM.stop: true
|
||||
RoleVM.boot: true
|
||||
RoleVM.reboot: true
|
||||
RoleVM.reboot_hard: true
|
||||
RoleVM.poweroff: true
|
||||
RoleVM.poweroff_hard: true
|
||||
RoleVM.undeploy: true
|
||||
RoleVM.undeploy_hard: true
|
||||
RoleVM.shutdown: true
|
||||
RoleVM.shutdown_hard: true
|
||||
RoleVM.delete: true
|
||||
RoleVM.delete_recreate: true
|
||||
RoleVM.resched: false
|
||||
RoleVM.unresched: false
|
||||
RoleVM.recover: false
|
||||
table_columns:
|
||||
- 0 # Checkbox
|
||||
- 1 # ID
|
||||
- 2 # Owner
|
||||
- 3 # Group
|
||||
- 4 # Name
|
||||
- 5 # State
|
||||
actions:
|
||||
Service.refresh: true
|
||||
Service.chown: false
|
||||
Service.chgrp: false
|
||||
Service.chmod: true
|
||||
Service.shutdown: true
|
||||
Service.recover: true
|
||||
Service.delete: true
|
||||
oneflow-templates:
|
||||
panel_tabs:
|
||||
service_template_info_panel: true
|
||||
service_template_roles_tab: true
|
||||
table_columns:
|
||||
- 0 # Checkbox
|
||||
- 1 # ID
|
||||
- 2 # Owner
|
||||
- 3 # Group
|
||||
- 4 # Name
|
||||
actions:
|
||||
ServiceTemplate.refresh: true
|
||||
ServiceTemplate.create_dialog: true
|
||||
ServiceTemplate.instantiate: true
|
||||
ServiceTemplate.chown: false
|
||||
ServiceTemplate.chgrp: false
|
||||
ServiceTemplate.chmod: true
|
||||
ServiceTemplate.delete: true
|
||||
|
22
src/sunstone/public/js/plugins/oneflow-dashboard.js
Normal file
22
src/sunstone/public/js/plugins/oneflow-dashboard.js
Normal file
@ -0,0 +1,22 @@
|
||||
// ------------------------------------------------------------------------ //
|
||||
// Copyright 2010-2013, C12G Labs S.L. //
|
||||
// //
|
||||
// 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. //
|
||||
//------------------------------------------------------------------------- //
|
||||
|
||||
var oneflow_dashboard_tab = {
|
||||
title: '<i class="icon-sitemap"></i>OneFlow'
|
||||
}
|
||||
|
||||
|
||||
Sunstone.addMainTab('oneflow_dashboard_tab',oneflow_dashboard_tab);
|
1833
src/sunstone/public/js/plugins/oneflow-services.js
Normal file
1833
src/sunstone/public/js/plugins/oneflow-services.js
Normal file
File diff suppressed because it is too large
Load Diff
1474
src/sunstone/public/js/plugins/oneflow-templates.js
Normal file
1474
src/sunstone/public/js/plugins/oneflow-templates.js
Normal file
File diff suppressed because it is too large
Load Diff
151
src/sunstone/routes/oneflow.rb
Normal file
151
src/sunstone/routes/oneflow.rb
Normal file
@ -0,0 +1,151 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2010-2013, C12G Labs S.L. #
|
||||
# #
|
||||
# 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. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
ONEFLOW_CONF_FILE = ETC_LOCATION + "/sunstone-oneflow.conf"
|
||||
|
||||
$: << RUBY_LIB_LOCATION+"/oneflow"
|
||||
|
||||
require 'opennebula/oneflow_client'
|
||||
|
||||
begin
|
||||
oneflow_conf = YAML.load_file(ONEFLOW_CONF_FILE)
|
||||
rescue Exception => e
|
||||
STDERR.puts "Error parsing config file #{ONEFLOW_CONF_FILE}: #{e.message}"
|
||||
exit 1
|
||||
end
|
||||
|
||||
set :oneflow_config, oneflow_conf
|
||||
|
||||
helpers do
|
||||
def af_build_client
|
||||
flow_client = settings.cloud_auth.client(session[:user])
|
||||
split_array = flow_client.one_auth.split(':')
|
||||
|
||||
Service::Client.new(
|
||||
:url => settings.oneflow_config[:oneflow_server],
|
||||
:user_agent => "Sunstone",
|
||||
:username => split_array.shift,
|
||||
:password => split_array.join(':'))
|
||||
end
|
||||
|
||||
def af_format_response(resp)
|
||||
if CloudClient::is_error?(resp)
|
||||
logger.error("[OneFlow] " + resp.to_s)
|
||||
|
||||
error = Error.new(resp.to_s)
|
||||
error resp.code.to_i, error.to_json
|
||||
else
|
||||
body resp.body.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
##############################################################################
|
||||
# Service
|
||||
##############################################################################
|
||||
|
||||
get '/service' do
|
||||
client = af_build_client
|
||||
|
||||
resp = client.get('/service')
|
||||
|
||||
af_format_response(resp)
|
||||
end
|
||||
|
||||
get '/service/:id' do
|
||||
client = af_build_client
|
||||
|
||||
resp = client.get('/service/' + params[:id])
|
||||
|
||||
af_format_response(resp)
|
||||
end
|
||||
|
||||
delete '/service/:id' do
|
||||
client = af_build_client
|
||||
|
||||
resp = client.delete('/service/' + params[:id])
|
||||
|
||||
af_format_response(resp)
|
||||
end
|
||||
|
||||
post '/service/:id/action' do
|
||||
client = af_build_client
|
||||
|
||||
resp = client.post('/service/' + params[:id] + '/action', request.body.read)
|
||||
|
||||
af_format_response(resp)
|
||||
end
|
||||
|
||||
post '/service/:id/role/:role_name/action' do
|
||||
client = af_build_client
|
||||
|
||||
resp = client.post('/service/' + params[:id] + '/role/' + params[:role_name] + '/action', request.body.read)
|
||||
|
||||
af_format_response(resp)
|
||||
end
|
||||
|
||||
|
||||
put '/service/:id/role/:role_name' do
|
||||
client = af_build_client
|
||||
|
||||
resp = client.put('/service/' + params[:id] + '/role/' + params[:role_name], request.body.read)
|
||||
|
||||
af_format_response(resp)
|
||||
end
|
||||
|
||||
##############################################################################
|
||||
# Service Template
|
||||
##############################################################################
|
||||
|
||||
get '/service_template' do
|
||||
client = af_build_client
|
||||
|
||||
resp = client.get('/service_template')
|
||||
|
||||
af_format_response(resp)
|
||||
end
|
||||
|
||||
get '/service_template/:id' do
|
||||
client = af_build_client
|
||||
|
||||
resp = client.get('/service_template/' + params[:id])
|
||||
|
||||
af_format_response(resp)
|
||||
end
|
||||
|
||||
delete '/service_template/:id' do
|
||||
client = af_build_client
|
||||
|
||||
resp = client.delete('/service_template/' + params[:id])
|
||||
|
||||
af_format_response(resp)
|
||||
end
|
||||
|
||||
post '/service_template/:id/action' do
|
||||
client = af_build_client
|
||||
|
||||
resp = client.post('/service_template/' + params[:id] + '/action', request.body.read)
|
||||
|
||||
af_format_response(resp)
|
||||
end
|
||||
|
||||
post '/service_template' do
|
||||
client = af_build_client
|
||||
|
||||
resp = client.post('/service_template', request.body.read)
|
||||
|
||||
af_format_response(resp)
|
||||
end
|
@ -72,7 +72,7 @@ UserPool::UserPool(SqlDB * db,
|
||||
const char * one_auth;
|
||||
ifstream file;
|
||||
|
||||
string filenames[4];
|
||||
string filenames[5];
|
||||
string error_str;
|
||||
|
||||
Nebula& nd = Nebula::instance();
|
||||
@ -147,10 +147,11 @@ UserPool::UserPool(SqlDB * db,
|
||||
filenames[1] = nd.get_var_location() + "/.one/occi_auth";
|
||||
filenames[2] = nd.get_var_location() + "/.one/ec2_auth";
|
||||
filenames[3] = nd.get_var_location() + "/.one/onegate_auth";
|
||||
filenames[4] = nd.get_var_location() + "/.one/oneflow_auth";
|
||||
|
||||
mkdir(string(nd.get_var_location() + "/.one").c_str(), S_IRWXU);
|
||||
|
||||
for (i=0 ; i < 4; i++)
|
||||
for (i=0 ; i < 5; i++)
|
||||
{
|
||||
int cfile = creat(filenames[i].c_str(), S_IRUSR | S_IWUSR);
|
||||
close(cfile);
|
||||
|
Loading…
x
Reference in New Issue
Block a user