diff --git a/.gitignore b/.gitignore index 6133130..93a5058 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,6 @@ dist/ #Translation build *.mo -*.pot #SQLite Database files -*.sqlite \ No newline at end of file +*.sqlite diff --git a/README.rst b/README.rst index 3bd1a3b..baa99fc 100644 --- a/README.rst +++ b/README.rst @@ -3,6 +3,10 @@ Murano Conductor README Conductor is an Murano orchestration engine that transforms object model sent by REST API service into a series of Heat and Murano-Agent commands +INSTALL FROM SOURCE +===================== +Please, use setup.sh script with root user privileges for install/uninstall of the software. + SEE ALSO -------- * `Murano `__ diff --git a/common.inc b/common.inc new file mode 100644 index 0000000..ed7b662 --- /dev/null +++ b/common.inc @@ -0,0 +1,379 @@ +#!/bin/bash +# +# Common functions file +# +DEBUGLVL=2 +RUN_DIR=${RUN_DIR:-$(cd $(dirname "$0") && pwd)} +LOGFILE="$RUN_DIR/install.log" +PIPAPPS="pip python-pip pip-python" +PIPCMD="" +PIPARGS="" +TRBL_FILE="" + +if [ "$DEBUGLVL" -eq 4 ]; then + set -x +fi +function log { + if [ "$DEBUGLVL" -gt 0 ]; then + chars=$(echo "@$" | wc -c) + case $DEBUGLVL in + 1) + echo -e "LOG:>$@" + ;; + 2) + echo -e "$(date +"%m-%d-%Y %H:%M") LOG:>$@" | tee --append $LOGFILE + ;; + 3) + echo -e "$(date +"%m-%d-%Y %H:%M") LOG:>$@" >> $LOGFILE + ;; + 4) + echo -e "$(date +"%m-%d-%Y %H:%M") LOG:>$@" | tee --append $LOGFILE + ;; + esac + fi +} +function lowercase(){ + echo "$1" | sed "y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/" +} +function get_os(){ + KERNEL=$(uname -r) + MACH=$(uname -m) + OS=$(uname) + if [ "${OS}" = "Linux" ] ; then + if [ -f /etc/redhat-release ] ; then + DISTRO_BASED_ON='RedHat' + PACKAGER='yum' + PKG_MGR='rpm' + DIST=$(cat /etc/redhat-release |sed s/\ release.*//) + PSUEDONAME=$(cat /etc/redhat-release | sed s/.*\(// | sed s/\)//) + REV=$(cat /etc/redhat-release | sed s/.*release\ // | sed s/\ .*//) + elif [ -f /etc/SuSE-release ] ; then + DISTRO_BASED_ON='SuSe' + PACKAGER='zypper' + PKG_MGR='rpm' + PSUEDONAME=$(cat /etc/SuSE-release | tr "\n" ' '| sed s/VERSION.*//) + REV=$(cat /etc/SuSE-release | tr "\n" ' ' | sed s/.*=\ //) + elif [ -f /etc/debian_version ] ; then + DISTRO_BASED_ON='Debian' + PACKAGER='apt-get' + PKG_MGR='dpkg' + DIST=$(cat /etc/lsb-release | grep '^DISTRIB_ID' | awk -F= '{ print $2 }') + PSUEDONAME=$(cat /etc/lsb-release | grep '^DISTRIB_CODENAME' | awk -F= '{ print $2 }') + REV=$(cat /etc/lsb-release | grep '^DISTRIB_RELEASE' | awk -F= '{ print $2 }') + fi + if [ -f /etc/UnitedLinux-release ] ; then + DIST="${DIST}[$(cat /etc/UnitedLinux-release | tr "\n" ' ' | sed s/VERSION.*//)]" + fi + OS=$(lowercase $OS) + DISTRO_BASED_ON=$(lowercase $DISTRO_BASED_ON) + readonly OS + readonly DIST + readonly DISTRO_BASED_ON + readonly PSUEDONAME + readonly REV + readonly KERNEL + readonly MACH + #readonly PACKAGER + else + OS=unknown + readonly OS + log "Unsupported OS:\"$OS\", sorry, exiting!" + exit 1 + fi +} +function find_or_install() +{ + _searching_for=$1 + _pkg_mrg_cmd='' + _pkgr_cmd='' + retval=0 + case $(lowercase $DISTRO_BASED_ON) in + "debian") + _pkg_mrg_cmd="$PKG_MGR -s $_searching_for" + _pkgr_cmd="$PACKAGER install $_searching_for --yes" + ;; + *) + _pkg_mrg_cmd="$PKG_MGR -q $_searching_for" + _pkgr_cmd="$PACKAGER install $_searching_for -y" + ;; + esac + $_pkg_mrg_cmd > /dev/null 2>&1 + if [ $? -eq 0 ]; then + log "Package \"$_searching_for\" already installed" + retval=2 + else + log "Installing \"$_searching_for\"..." + $_pkgr_cmd > /dev/null 2>&1 + if [ $? -ne 0 ];then + log "...installation fails, exiting!" + retval=1 + else + log "...success" + retval=0 + fi + fi + return $retval +} +function is_py_package_installed() +{ + retval=0 + py_pkg=$1 + found_pkg=$($PIPCMD freeze | grep -E "^$py_pkg") + if [ $? -ne 0 ]; then + retval=1 + fi + echo $found_pkg + return $retval +} +function genpass() +{ + echo $(date | md5sum |head -c${5:-13}) +} +function shslash() +{ + echo $1 | sed 's/\//\\\//g' +} +function split() +{ + # Prefix local names with the function name to try to avoid conflicts + # local split_wordlist + split_wordlist="$1" + shift + read "$@" <= v2, false if v1 < v2 +function version_ge() +{ + # Prefix local names with the function name to try to avoid conflicts + # local version_ge_1 version_ge_2 version_ge_a version_ge_b + # local version_ge_save_ifs + version_ge_v1="$1" + version_ge_v2="$2" + version_ge_save_ifs="$IFS" + while test -n "${version_ge_v1}${version_ge_v2}"; do + IFS="." + split "$version_ge_v1" version_ge_a version_ge_v1 + split "$version_ge_v2" version_ge_b version_ge_v2 + IFS="$version_ge_save_ifs" + #echo " compare $version_ge_a $version_ge_b" + test "0$version_ge_a" -gt "0$version_ge_b" && return 0 # v1>v2: true + test "0$version_ge_a" -lt "0$version_ge_b" && return 1 # v1/dev/null) + if [ $? -eq 0 ];then + break + fi + done + if [ -z "$_cmd" ];then + echo "Can't find \"pip\" in system, please install it first, exiting!" + exit 1 + else + _pip_ver=$($_cmd --version | grep -oE "[0-9]\.[0-9]" | head -n1) + if [ -n "$_pip_ver" ]; then + version_ge $_pip_ver $pip_min_ver + if [ $? -ne 0 ]; then + log "Upgrading pip ..." + $_cmd install --upgrade pip==$pip_min_ver + if [ $? -ne 0 ]; then + log "...pip upgrade fails, exiting!" + exit 1 + else + log "...success" + sleep 2 + for cmd in $PIPAPPS + do + _cmd=$(which $cmd 2>/dev/null) + if [ $? -eq 0 ];then + break + fi + done + fi + fi + _pip_ver=$($_cmd --version | grep -oE "[0-9]\.[0-9]" | head -n1) + version_ge $_pip_ver "1.5" + if [ $? -eq 0 ]; then + log "For future use, sorry, use pip v$pip_min_ver, exiting!" + exit 1 + ##_pipargs="--allow-unverified --allow-external" + #_pipargs="--allow-all-external" + #mk_dir "/root/.pip" + #_pipcfg="/root/.pip/pip.conf" + #if [ ! -f "$_pipcfg" ]; then + # touch $_pipcfg + #fi + #iniset 'install' 'allow-all-external' 'true' "$_pipcfg" + #iniset 'install' 'allow-all-unverified' 'true' "$_pipcfg" + #log "Setuptools upgrade required..." + #$cmd install setuptools --no-use-wheel --upgrade >> $LOGFILE 2>&1 + #if [ $? -ne 0 ]; then + # log "...upgrade fails, exiting" + # exit 1 + #else + # log "...success" + #fi + fi + log "Found pip version - $_pip_ver" + fi + PIPARGS=$_pipargs + PIPCMD=$_cmd + fi +} +function add_daemon_credentials() +{ + retval=0 + daemonuser=${1:-murano} + daemongroup=${2:-murano} + daemonhomedir=${3:-/home/$daemonuser} + getent group $daemongroup > /dev/null + if [ $? -ne 0 ]; then + log "Creating group \"$daemongroup\"..." + groupadd -r $daemongroup + if [ $? -eq 0 ]; then + log "...success" + else + log "Can't create \"$daemongroup\", exiting!" + retval=1 + exit 1 + fi + else + log "Group \"$daemongroup\" exists" + fi + getent passwd $daemonuser > /dev/null + if [ $? -ne 0 ]; then + log "Creating user \"$daemonuser\"..." + useradd -r -g $daemongroup -G $daemongroup -d $daemonhomedir -s $(which nologin) -c "Murano Daemons" $daemonuser + if [ $? -eq 0 ]; then + log "...success" + else + log "Can't create \"$daemonuser\", exiting!" + retval=1 + exit 1 + fi + else + log "User \"$daemonuser\" exists" + fi + return $retval +} +function remove_daemon_credentials() +{ + retval=0 + daemonuser=${1:-murano} + daemongroup=${2:-murano} + daemonhomedir=${3:-/home/$daemonuser} + getent passwd $daemonuser > /dev/null + if [ $? -eq 0 ]; then + log "Deleting user \"$daemonuser\"..." + userdel -f $daemonuser + if [ $? -eq 0 ]; then + if [ -d "$daemonhomedir" ]; then + rm -rf $daemonhomedir + fi + log "...success" + else + log "Can't delete \"$daemonuser\", exiting!" + retval=1 + exit 1 + fi + fi + getent group $daemongroup > /dev/null + if [ $? -eq 0 ]; then + log "Deleting group \"$daemongroup\"..." + groupdel $daemongroup + if [ $? -eq 0 ]; then + log "...success" + else + log "Can't delete \"$daemongroup\", exiting!" + retval=1 + exit 1 + fi + fi + return $retval +} +function iniset() +{ + local section=$1 + local option=$2 + local value=$3 + local file=$4 + local line + + if [ -z "$section" ] ; then + # No section name specified + sed -i -e "s/^\($option[ \t]*=[ \t]*\).*$/\1$value/" "$file" + else + # Check if section already exists + if ! grep -q "^\[$section\]" "$file" ; then + # Add section at the end + echo -e "\n[$section]" >>"$file" + fi + + # Check if parameter in the section exists + line=$(sed -ne "/^\[$section\]/,/^\[.*\]/ { /^$option[ \t]*=/ p; }" "$file") + if [ -z "$line" ] ; then + # Add parameter if it is not exists + sed -i -e "/^\[$section\]/ a\\ +$option = $value +" "$file" + else + # Replace existing parameter + sed -i -e "/^\[$section\]/,/^\[.*\]/ s|^\($option[ \t]*=[ \t]*\).*$|\1$value|" "$file" + fi + fi +} +function mk_dir() +{ + retval=0 + path_to_check=$1 + if [ -d "$path_to_check" ]; then + log "Path \"$path_to_check\" already exists." + elif [ -f "$path_to_check" ]; then + log "Path \"path_to_check\" is an existing file, exiting!" + exit 1 + else + mkdir -p "$path_to_check" + if [ $? -ne 0 ]; then + log "Can't create \"$path_to_check\", exiting!" + retval=1 + exit 1 + fi + fi + if [ $# -eq 3 ]; then + owner_user=$2 + owner_group=$3 + chown -R $owner_user:$owner_group $path_to_check + if [ $? -ne 0 ]; then + log "Can't set ownership to \"$owner_user:$owner_group\" for \"$path_to_check\", exiting!" + retval=1 + exit 1 + fi + fi + return $retval +} +function get_service_exec_path() +{ + retval=0 + if [ -z "$SERVICE_EXEC_PATH" ]; then + SERVICE_EXEC_PATH=$(which $DAEMON_NAME) + if [ $? -ne 0 ]; then + log "Can't find \"$DAEMON_NAME\", please install the \"$SERVICE_SRV_NAME\" by running \"$(basename "$0") install\" or set variable SERVICE_EXEC_PATH=/path/to/daemon before running setup script, exiting!" + retval=1 + fi + else + if [ ! -x "$SERVICE_EXEC_PATH" ]; then + log "\"$SERVICE_EXEC_PATH\" in not executable, please install the \"$DAEMON_NAME\" or set variable SERVICE_EXEC_PATH=/path/to/daemon before running setup script, exiting!" + retval=1 + fi + fi + return $retval +} diff --git a/etc/init.d/README.rst b/etc/init.d/README.rst new file mode 100644 index 0000000..83ca470 --- /dev/null +++ b/etc/init.d/README.rst @@ -0,0 +1,4 @@ +SysV init scripts +===================== +murano-conductor-redhat - for RedHat based Linux distibution +murano-conductor-debian - for Debian based Linux distibution diff --git a/etc/init.d/murano-conductor-debian b/etc/init.d/murano-conductor-debian new file mode 100644 index 0000000..0a5f243 --- /dev/null +++ b/etc/init.d/murano-conductor-debian @@ -0,0 +1,104 @@ +#!/bin/sh +# Copyright (c) 2014 Mirantis, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# Author: Igor Yozhikov +# +### BEGIN INIT INFO +# Provides: murano-conductor +# Required-Start: $network $local_fs $remote_fs $syslog +# Required-Stop: $remote_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: OpenStack Murano Conductor Service +# Description: This startup script launches murano-conductor service daemon. +### END INIT INFO + + +PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin +DESC="murano-conductor" +NAME=murano-conductor +DAEMON=$(which muranoconductor) +PIDFILE=/var/run/murano/$NAME.pid +SCRIPTNAME=/etc/init.d/openstack-$NAME +SYSTEM_USER=murano +CONFIG_FILE=/etc/murano/murano-conductor.conf +# Exit if the package is not installed +[ -x $DAEMON ] || exit 5 + +# source function library +. /lib/lsb/init-functions + + + +do_start() +{ + if [ ! -d "/var/run/murano" ]; then + mkdir -p /var/run/murano + chown -R $SYSTEM_USER /var/run/murano + fi + start-stop-daemon --start --background --quiet --chuid $SYSTEM_USER:$SYSTEM_USER --make-pidfile --pidfile $PIDFILE --startas $DAEMON --test -- --config-file=$CONFIG_FILE > /dev/null || return 1 + start-stop-daemon --start --background --quiet --chuid $SYSTEM_USER:$SYSTEM_USER --make-pidfile --pidfile $PIDFILE --startas $DAEMON -- --config-file=$CONFIG_FILE || return 2 +} + +do_stop() +{ + start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE + RETVAL="$?" + rm -f $PIDFILE + return "$RETVAL" +} + +case "$1" in + start) + log_daemon_msg "Starting $DESC" "$NAME" + do_start + case "$?" in + 0|1) log_end_msg 0 ;; + 2) log_end_msg 1 ;; + esac + ;; + stop) + log_daemon_msg "Stopping $DESC" "$NAME" + do_stop + case "$?" in + 0|1) log_end_msg 0 ;; + 2) log_end_msg 1 ;; + esac + ;; + status) + status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? + ;; + restart|force-reload) + log_daemon_msg "Restarting $DESC" "$NAME" + do_stop + case "$?" in + 0|1) + do_start + case "$?" in + 0) log_end_msg 0 ;; + 1) log_end_msg 1 ;; # Old process is still running + *) log_end_msg 1 ;; # Failed to start + esac + ;; + *) + # Failed to stop + log_end_msg 1 + ;; + esac + ;; + *) + echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 + exit 3 + ;; +esac diff --git a/etc/init.d/murano-conductor-redhat b/etc/init.d/murano-conductor-redhat new file mode 100644 index 0000000..807ca38 --- /dev/null +++ b/etc/init.d/murano-conductor-redhat @@ -0,0 +1,102 @@ +#!/bin/sh +# Copyright (c) 2014 Mirantis, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# Author: Igor Yozhikov +# +### BEGIN INIT INFO +# Provides: murano-conductor +# Required-Start: $network $local_fs $remote_fs $syslog +# Required-Stop: $remote_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: OpenStack Murano Conductor Service +# Description: This startup script launches murano-conductor service daemon. +### END INIT INFO +# chkconfig: 3 90 10 +# description: This startup script launches murano-conductor service daemon. +# config: /etc/murano/murano-conductor.conf, /etc/murano/murano-conductor-paste.ini +# +PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin +DESC="murano-conductor" +NAME=murano-conductor +DAEMON=$(which muranoconductor) +PIDFILE=/var/run/murano/$NAME.pid +SCRIPTNAME=/etc/init.d/openstack-$NAME +SYSTEM_USER=murano +CONFIG_FILE=/etc/murano/murano-conductor.conf +LOCKFILE=/var/lock/subsys/$NAME +# Exit if the package is not installed +[ -x $DAEMON ] || exit 5 + +# source function library +. /etc/init.d/functions + +RETVAL=0 + + +start() { + if [ ! -d "/var/run/murano" ]; then + mkdir -p /var/run/murano + chown -R $SYSTEM_USER /var/run/murano + fi + echo -n "Starting $NAME: " + daemon --user $SYSTEM_USER "$DAEMON --config-file=$CONFIG_FILE &>/dev/null & echo \$! > $PIDFILE" + RETVAL=$? + echo + [ $RETVAL -eq 0 ] && touch $LOCKFILE + return $RETVAL +} + +stop() { + echo -n "Stopping $NAME: " + #killproc $DAEMON -TERM + killproc -p $PIDFILE $DAEMON + RETVAL=$? + echo + [ $RETVAL -eq 0 ] && rm -f $LOCKFILE + return $RETVAL +} + +restart() { + stop + start +} + +rh_status() { + # run checks to determine if the service is running or use generic status + status $DAEMON +} + + +case "$1" in + start) + start + ;; + + stop) + stop + ;; + + restart) + restart + ;; + + status) + rh_status + ;; + *) + echo $"Usage: $0 {start|stop|status|restart}" + exit 2 +esac +exit $? \ No newline at end of file diff --git a/etc/conductor.conf b/etc/murano/conductor.conf.sample similarity index 89% rename from etc/conductor.conf rename to etc/murano/conductor.conf.sample index 703ed91..3059b45 100644 --- a/etc/conductor.conf +++ b/etc/murano/conductor.conf.sample @@ -1,96 +1,98 @@ -[DEFAULT] - -# Path where log will be written -log_file = /tmp/conductor.log - -# Log verbosity -debug=True -verbose=True - -# Provide directory with initialization scripts -init_scripts_dir = etc/init-scripts - -# Provide directory with agent configs -agent_config_dir = etc/agent-config - -# Directory for data cache, OS temp directory is used by default -#data_dir = /tmp/muranoconductor-cache - -# Provide url to Murano Metadata repository -# Comment this line if you registered murano-metadata in keystone catalog -murano_metadata_url = http://localhost:8084/v1 - -# Maximum number of environments that can be processed simultaneously -max_environments = 20 - -# Maximum number of VMs per environment -max_hosts = 250 - -# Template IP address for generating environment subnet cidrs -env_ip_template = 10.0.0.0 - -# Enforces default network topology. -# Allowed values: nova, flat, routed -# default is routed -network_topology = routed - -[keystone] -# URL of OpenStack KeyStone service REST API. -# Typically only hostname (or IP) needs to be changed -auth_url = http://localhost:5000/v2.0 - -# Keystone SSL parameters -# Optional CA cert file to use in SSL connections -#ca_file = -# Optional PEM-formatted certificate chain file -#cert_file = -# Optional PEM-formatted file that contains the private key -#key_file = -# If set then the server's certificate will not be verified -insecure = False - -[heat] -# Heat SSL parameters -# Optional CA cert file to use in SSL connections -#ca_file = -# Optional PEM-formatted certificate chain file -#cert_file = -# Optional PEM-formatted file that contains the private key -#key_file = -# If set then the server's certificate will not be verified -insecure = False -# Valid endpoint types: publicURL (default), internalURL, adminURL -endpoint_type = publicURL - -[neutron] -# Optional CA cert file to use in SSL connections -#ca_cert = -# Allow self signed server certificate -insecure = False -# Valid endpoint types: publicURL (default), internalURL, adminURL -endpoint_type = publicURL - -[rabbitmq] -# Connection parameters to RabbitMQ service - -# Hostname or IP address where RabbitMQ is located. -# !!! Change localhost to your real IP or hostname as this address must be reachable from VMs !!! -host = localhost - -# RabbitMQ port (5672 is a default) -port = 5672 - -# Use SSL for RabbitMQ connections (True or False) -ssl = False - -# Path to SSL CA certificate or empty to allow self signed server certificate -#ca_certs = - -# RabbitMQ credentials. Fresh RabbitMQ installation has "guest" account with "guest" password. -# It is recommended to create dedicated user account for Murano using RabbitMQ web console or command line utility -login = guest -password = guest - -# RabbitMQ virtual host (vhost). Fresh RabbitMQ installation has "/" vhost preconfigured. -# It is recommended to create dedicated vhost for Murano using RabbitMQ web console or command line utility -virtual_host = / +[DEFAULT] + +# Set up logging. To use syslog just set use_syslog parameter value to 'True' +log_file = /tmp/murano-conductor.log +use_syslog = False +syslog_log_facility = LOG_LOCAL0 + +# Log verbosity +debug = True +verbose = True + +# Provide directory with initialization scripts +init_scripts_dir = etc/murano/init-scripts + +# Provide directory with agent configs +agent_config_dir = etc/murano/agent-config + +# Directory for data cache, OS temp directory is used by default +data_dir = /tmp/muranoconductor-cache + +# Provide url to Murano Metadata repository +# Comment this line if you registered murano-metadata in keystone catalog +murano_metadata_url = http://localhost:8084/v1 + +# Maximum number of environments that can be processed simultaneously +max_environments = 20 + +# Maximum number of VMs per environment +max_hosts = 250 + +# Template IP address for generating environment subnet cidrs +env_ip_template = 10.0.0.0 + +# Enforces default network topology. +# Allowed values: nova, flat, routed +# default is routed +network_topology = routed + +[keystone] +# URL of OpenStack KeyStone service REST API. +# Typically only hostname (or IP) needs to be changed +auth_url = http://localhost:5000/v2.0 + +# Keystone SSL parameters +# Optional CA cert file to use in SSL connections +#ca_file = +# Optional PEM-formatted certificate chain file +#cert_file = +# Optional PEM-formatted file that contains the private key +#key_file = +# If set then the server's certificate will not be verified +insecure = False + +[heat] +# Heat SSL parameters +# Optional CA cert file to use in SSL connections +#ca_file = +# Optional PEM-formatted certificate chain file +#cert_file = +# Optional PEM-formatted file that contains the private key +#key_file = +# If set then the server's certificate will not be verified +insecure = False +# Valid endpoint types: publicURL (default), internalURL, adminURL +endpoint_type = publicURL + +[neutron] +# Optional CA cert file to use in SSL connections +#ca_cert = +# Allow self signed server certificate +insecure = False +# Valid endpoint types: publicURL (default), internalURL, adminURL +endpoint_type = publicURL + +[rabbitmq] +# Connection parameters to RabbitMQ service + +# Hostname or IP address where RabbitMQ is located. +# !!! Change localhost to your real IP or hostname as this address must be reachable from VMs !!! +host = localhost + +# RabbitMQ port (5672 is a default) +port = 5672 + +# Use SSL for RabbitMQ connections (True or False) +ssl = False + +# Path to SSL CA certificate or empty to allow self signed server certificate +#ca_certs = + +# RabbitMQ credentials. Fresh RabbitMQ installation has "guest" account with "guest" password. +# It is recommended to create dedicated user account for Murano using RabbitMQ web console or command line utility +login = guest +password = guest + +# RabbitMQ virtual host (vhost). Fresh RabbitMQ installation has "/" vhost preconfigured. +# It is recommended to create dedicated vhost for Murano using RabbitMQ web console or command line utility +virtual_host = / diff --git a/muranoconductor/commands/network.py b/muranoconductor/commands/network.py index 476fa3b..e15cfb3 100644 --- a/muranoconductor/commands/network.py +++ b/muranoconductor/commands/network.py @@ -107,17 +107,21 @@ class NeutronExecutor(CommandBase): routers = self.neutron.list_routers(tenant_id=self.tenant_id). \ get("routers") if not len(routers): - routerId = "NOT_FOUND" + routerId = externalNetId = "NOT_FOUND" else: routerId = routers[0]["id"] + externalNetId = routers[0]['external_gateway_info']['network_id'] if len(routers) > 1: for router in routers: if "murano" in router["name"].lower(): routerId = router["id"] + externalNetId = \ + router['external_gateway_info']['network_id'] break + for callback in self.router_requests: - callback(routerId) + callback(routerId, externalNetId) self.router_requests = [] return True diff --git a/muranoconductor/commands/vm_agent.py b/muranoconductor/commands/vm_agent.py index 69f7a3d..11e4f81 100644 --- a/muranoconductor/commands/vm_agent.py +++ b/muranoconductor/commands/vm_agent.py @@ -62,13 +62,49 @@ class VmAgentExecutor(CommandBase): else: return self._build_v2_execution_plan(template, path) + def _split_path(self, _path, parts=None): + if parts is None: + parts = [] + head, tail = os.path.split(_path) + if tail: + parts.append(tail) + elif os.path.isabs(head): # head is '/' and tail is '' - stop + parts.append(head) + head = None + if head: + return self._split_path(head, parts) + else: + parts.reverse() + return parts + + @staticmethod + def _join(*args): + return os.path.join(*args) if args else '' + + def _split_agent_path(self, path, agent_root_dir_depth=3): + agent_subdir = os.path.dirname(os.path.normpath(path)) + dir_parts = self._split_path(agent_subdir) + return (self._join(*dir_parts[:agent_root_dir_depth]), + self._join(*dir_parts[agent_root_dir_depth:])) + + def _ensure_relpath(self, path): + parts = self._split_path(os.path.normpath(path)) + if parts and os.path.isabs(parts[0]): + return self._join(*parts[1:]), True + else: + return path, False + def _build_v1_execution_plan(self, template, path): - scripts_folder = os.path.join( - os.path.dirname(path), 'scripts') + agent_dir_root, rest_dirs = self._split_agent_path(path) + scripts_folder = os.path.join(agent_dir_root, 'scripts') script_files = template.get('Scripts', []) scripts = [] for script in script_files: - script_path = os.path.join(scripts_folder, script) + script, was_abspath = self._ensure_relpath(script) + if was_abspath: + script_path = os.path.join(scripts_folder, script) + else: + script_path = os.path.join(scripts_folder, rest_dirs, script) log.debug('Loading script "{0}"'.format(script_path)) with open(script_path) as script_file: script_data = script_file.read() diff --git a/muranoconductor/locale/conductor.pot b/muranoconductor/locale/conductor.pot new file mode 100644 index 0000000..75d8f38 --- /dev/null +++ b/muranoconductor/locale/conductor.pot @@ -0,0 +1,249 @@ +# Translations template for murano-conductor. +# Copyright (C) 2014 ORGANIZATION +# This file is distributed under the same license as the murano-conductor +# project. +# FIRST AUTHOR , 2014. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: murano-conductor 0.4\n" +"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" +"POT-Creation-Date: 2014-01-20 14:55+0400\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 1.3\n" + +#: muranoconductor/config.py:151 +msgid "Invalid syslog facility" +msgstr "" + +#: muranoconductor/config.py:225 +#, python-format +msgid "Loading %(app_name)s from %(conf_file)s" +msgstr "" + +#: muranoconductor/config.py:236 +#, python-format +msgid "" +"Unable to load %(app_name)s from configuration file %(conf_file)s.\n" +"Got: %(e)r" +msgstr "" + +#: muranoconductor/openstack/common/eventlet_backdoor.py:141 +#, python-format +msgid "Eventlet backdoor listening on %(port)s for process %(pid)d" +msgstr "" + +#: muranoconductor/openstack/common/exception.py:103 +msgid "Uncaught exception" +msgstr "" + +#: muranoconductor/openstack/common/excutils.py:62 +#, python-format +msgid "Original exception being dropped: %s" +msgstr "" + +#: muranoconductor/openstack/common/excutils.py:90 +#, python-format +msgid "Unexpected exception occurred %d time(s)... retrying." +msgstr "" + +#: muranoconductor/openstack/common/fileutils.py:64 +#, python-format +msgid "Reloading cached file %s" +msgstr "" + +#: muranoconductor/openstack/common/lockutils.py:100 +#, python-format +msgid "Could not release the acquired lock `%s`" +msgstr "" + +#: muranoconductor/openstack/common/lockutils.py:166 +#, python-format +msgid "Got semaphore \"%(lock)s\"" +msgstr "" + +#: muranoconductor/openstack/common/lockutils.py:175 +#, python-format +msgid "Attempting to grab file lock \"%(lock)s\"" +msgstr "" + +#: muranoconductor/openstack/common/lockutils.py:185 +#, python-format +msgid "Created lock path: %s" +msgstr "" + +#: muranoconductor/openstack/common/lockutils.py:203 +#, python-format +msgid "Got file lock \"%(lock)s\" at %(path)s" +msgstr "" + +#: muranoconductor/openstack/common/lockutils.py:207 +#, python-format +msgid "Released file lock \"%(lock)s\" at %(path)s" +msgstr "" + +#: muranoconductor/openstack/common/lockutils.py:244 +#, python-format +msgid "Got semaphore / lock \"%(function)s\"" +msgstr "" + +#: muranoconductor/openstack/common/lockutils.py:248 +#, python-format +msgid "Semaphore / lock released \"%(function)s\"" +msgstr "" + +#: muranoconductor/openstack/common/log.py:244 +#, python-format +msgid "Deprecated: %s" +msgstr "" + +#: muranoconductor/openstack/common/log.py:336 +#, python-format +msgid "Error loading logging config %(log_config)s: %(err_msg)s" +msgstr "" + +#: muranoconductor/openstack/common/log.py:386 +#, python-format +msgid "syslog facility must be one of: %s" +msgstr "" + +#: muranoconductor/openstack/common/log.py:556 +#, python-format +msgid "Fatal call to deprecated config: %(msg)s" +msgstr "" + +#: muranoconductor/openstack/common/loopingcall.py:84 +#, python-format +msgid "task run outlasted interval by %s sec" +msgstr "" + +#: muranoconductor/openstack/common/loopingcall.py:91 +msgid "in fixed duration looping call" +msgstr "" + +#: muranoconductor/openstack/common/loopingcall.py:131 +#, python-format +msgid "Dynamic looping call sleeping for %.02f seconds" +msgstr "" + +#: muranoconductor/openstack/common/loopingcall.py:138 +msgid "in dynamic looping call" +msgstr "" + +#: muranoconductor/openstack/common/service.py:103 +#: muranoconductor/openstack/common/service.py:271 +msgid "Full set of CONF:" +msgstr "" + +#: muranoconductor/openstack/common/service.py:112 +#: muranoconductor/openstack/common/service.py:214 +#, python-format +msgid "Caught %s, exiting" +msgstr "" + +#: muranoconductor/openstack/common/service.py:123 +msgid "Exception during rpc cleanup." +msgstr "" + +#: muranoconductor/openstack/common/service.py:159 +msgid "Parent process has died unexpectedly, exiting" +msgstr "" + +#: muranoconductor/openstack/common/service.py:196 +msgid "Forking too fast, sleeping" +msgstr "" + +#: muranoconductor/openstack/common/service.py:219 +msgid "Unhandled exception" +msgstr "" + +#: muranoconductor/openstack/common/service.py:226 +#, python-format +msgid "Started child %d" +msgstr "" + +#: muranoconductor/openstack/common/service.py:236 +#, python-format +msgid "Starting %d workers" +msgstr "" + +#: muranoconductor/openstack/common/service.py:253 +#, python-format +msgid "Child %(pid)d killed by signal %(sig)d" +msgstr "" + +#: muranoconductor/openstack/common/service.py:257 +#, python-format +msgid "Child %(pid)s exited with status %(code)d" +msgstr "" + +#: muranoconductor/openstack/common/service.py:261 +#, python-format +msgid "pid %d not in child list" +msgstr "" + +#: muranoconductor/openstack/common/service.py:289 +#, python-format +msgid "Caught %s, stopping children" +msgstr "" + +#: muranoconductor/openstack/common/service.py:300 +#, python-format +msgid "Waiting on %d children to exit" +msgstr "" + +#: muranoconductor/openstack/common/sslutils.py:52 +#, python-format +msgid "Unable to find cert_file : %s" +msgstr "" + +#: muranoconductor/openstack/common/sslutils.py:55 +#, python-format +msgid "Unable to find ca_file : %s" +msgstr "" + +#: muranoconductor/openstack/common/sslutils.py:58 +#, python-format +msgid "Unable to find key_file : %s" +msgstr "" + +#: muranoconductor/openstack/common/sslutils.py:61 +msgid "" +"When running server in SSL mode, you must specify both a cert_file and " +"key_file option value in your configuration file" +msgstr "" + +#: muranoconductor/openstack/common/sslutils.py:100 +#, python-format +msgid "Invalid SSL version : %s" +msgstr "" + +#: muranoconductor/openstack/common/notifier/api.py:129 +#, python-format +msgid "%s not in valid priorities" +msgstr "" + +#: muranoconductor/openstack/common/notifier/api.py:145 +#, python-format +msgid "" +"Problem '%(e)s' attempting to send to notification system. " +"Payload=%(payload)s" +msgstr "" + +#: muranoconductor/openstack/common/notifier/api.py:164 +#, python-format +msgid "Failed to load notifier %s. These notifications will not be sent." +msgstr "" + +#: muranoconductor/openstack/common/notifier/rpc_notifier.py:45 +#: muranoconductor/openstack/common/notifier/rpc_notifier2.py:51 +#, python-format +msgid "Could not send notification to %(topic)s. Payload=%(message)s" +msgstr "" + diff --git a/muranoconductor/metadata.py b/muranoconductor/metadata.py index a86aa79..8529db1 100644 --- a/muranoconductor/metadata.py +++ b/muranoconductor/metadata.py @@ -135,14 +135,27 @@ def release(folder): def prepare(data_dir): if not os.path.exists(data_dir): os.makedirs(data_dir) + log.info("Creating directory '{0}' to store " + "conductor data".format(data_dir)) init_scripts_dst = os.path.join(data_dir, os.path.basename(CONF.init_scripts_dir)) - if not os.path.exists(init_scripts_dst): - shutil.copytree(CONF.init_scripts_dir, init_scripts_dst) + if os.path.exists(init_scripts_dst): + log.info("Found existing init scripts directory at" + " '{0}'. Deleting it.'".format(init_scripts_dst)) + shutil.rmtree(init_scripts_dst) + log.info("Copying init scripts directory from '{0}' " + "to '{1}'".format(CONF.init_scripts_dir, init_scripts_dst)) + shutil.copytree(CONF.init_scripts_dir, init_scripts_dst) + agent_config_dst = os.path.join(data_dir, os.path.basename(CONF.agent_config_dir)) - if not os.path.exists(agent_config_dst): - shutil.copytree(CONF.agent_config_dir, agent_config_dst) + if os.path.exists(agent_config_dst): + log.info("Found existing agent config directory at" + " '{0}'. Deleting it.'".format(agent_config_dst)) + shutil.rmtree(agent_config_dst) + log.info("Copying agent config directory from '{0}' " + "to '{1}'".format(CONF.agent_config_dir, agent_config_dst)) + shutil.copytree(CONF.agent_config_dir, agent_config_dst) os.chdir(data_dir) diff --git a/muranoconductor/network.py b/muranoconductor/network.py index 6f7bf0f..f551365 100644 --- a/muranoconductor/network.py +++ b/muranoconductor/network.py @@ -48,9 +48,10 @@ def get_subnet(engine, context, body, routerId=None, existingNetwork=None, def get_default_router(engine, context, body, result=None): command_dispatcher = context['/commandDispatcher'] - def callback(result_value): + def callback(routerId, externalNetId): if result is not None: - context[result] = {"routerId": result_value} + context[result] = {"routerId": routerId, + "floatingId": externalNetId} success_handler = body.find('success') if success_handler is not None: diff --git a/requirements.txt b/requirements.txt index 35cd3f1..12c9680 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,8 +11,7 @@ netaddr>=0.7.6 oslo.config>=1.2.0 deep -# Please, revert to murano-common>=x.x.x before release -http://tarballs.openstack.org/murano-common/murano-common-master.tar.gz#egg=muranocommon-0.4 +murano-common==0.4.1 PyYAML>=3.1.0 -http://tarballs.openstack.org/murano-metadataclient/murano-metadataclient-master.tar.gz#egg=metadataclient-0.4 -python-neutronclient>=2.3.1 +murano-metadataclient==0.4.1 +python-neutronclient>=2.3.0,<3 diff --git a/setup-centos.sh b/setup-centos.sh deleted file mode 100644 index 6f45198..0000000 --- a/setup-centos.sh +++ /dev/null @@ -1,280 +0,0 @@ -#!/bin/sh -# Copyright (c) 2013 Mirantis, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# CentOS script - -LOGLVL=1 -SERVICE_CONTENT_DIRECTORY=`cd $(dirname "$0") && pwd` -PREREQ_PKGS="upstart wget git make python-pip python-devel mysql-connector-python libffi-devel" -PIPAPPS="pip python-pip pip-python" -PIPCMD="" -SERVICE_SRV_NAME="murano-conductor" -GIT_CLONE_DIR=`echo $SERVICE_CONTENT_DIRECTORY | sed -e "s/$SERVICE_SRV_NAME//"` -#ETC_CFG_DIR="/etc/$SERVICE_SRV_NAME" -ETC_CFG_DIR="/etc/murano" -LOG_DIR="/var/log/murano/" -SERVICE_CONFIG_FILE_PATH="$ETC_CFG_DIR/conductor.conf" - -# Functions -# Loger function -log() -{ - MSG=$1 - if [ $LOGLVL -gt 0 ]; then - echo "LOG:> $MSG" - fi -} - -# Check or install package -in_sys_pkg() -{ - PKG=$1 - rpm -q $PKG > /dev/null 2>&1 - if [ $? -eq 0 ]; then - log "Package \"$PKG\" already installed" - else - log "Installing \"$PKG\"..." - yum install $PKG --assumeyes > /dev/null 2>&1 - if [ $? -ne 0 ];then - log "installation fails, exiting!!!" - exit - fi - fi -} - -# find pip -find_pip() -{ - for cmd in $PIPAPPS - do - _cmd=$(which $cmd 2>/dev/null) - if [ $? -eq 0 ];then - break - fi - done - if [ -z $_cmd ];then - echo "Can't find \"pip\" in system, please install it first, exiting!" - exit 1 - else - PIPCMD=$_cmd - fi -} - -# git clone -gitclone() -{ - FROM=$1 - CLONEROOT=$2 - log "Cloning from \"$FROM\" repo to \"$CLONEROOT\"" - cd $CLONEROOT && git clone $FROM > /dev/null 2>&1 - if [ $? -ne 0 ];then - log "cloning from \"$FROM\" fails, exiting!!!" - exit - fi -} - -# install -inst() -{ -CLONE_FROM_GIT=$1 -# Checking packages - for PKG in $PREREQ_PKGS - do - in_sys_pkg $PKG - done -# Find python pip - find_pip -# If clone from git set - if [ ! -z $CLONE_FROM_GIT ]; then -# Preparing clone root directory - if [ ! -d $GIT_CLONE_DIR ];then - log "Creating $GIT_CLONE_DIR direcory..." - mkdir -p $GIT_CLONE_DIR - if [ $? -ne 0 ];then - log "Can't create $GIT_CLONE_DIR, exiting!!!" - exit - fi - fi -# Cloning from GIT - GIT_WEBPATH_PRFX="https://github.com/stackforge/" - gitclone "$GIT_WEBPATH_PRFX$SERVICE_SRV_NAME.git" $GIT_CLONE_DIR -# End clone from git section - fi - -# Setupping... - log "Running setup.py" - MRN_CND_SPY=$GIT_CLONE_DIR/$SERVICE_SRV_NAME/setup.py - if [ -e $MRN_CND_SPY ];then - chmod +x $MRN_CND_SPY - log "$MRN_CND_SPY output:_____________________________________________________________" -## Setup through pip - # Creating tarball - rm -rf $SERVICE_CONTENT_DIRECTORY/*.egg-info - cd $SERVICE_CONTENT_DIRECTORY && python $MRN_CND_SPY egg_info - if [ $? -ne 0 ];then - log "\"$MRN_CND_SPY\" egg info creation FAILS, exiting!!!" - exit 1 - fi - rm -rf $SERVICE_CONTENT_DIRECTORY/dist/* - cd $SERVICE_CONTENT_DIRECTORY && $MRN_CND_SPY sdist - if [ $? -ne 0 ];then - log "\"$MRN_CND_SPY\" tarball creation FAILS, exiting!!!" - exit 1 - fi - # Running tarball install - TRBL_FILE=$(basename `ls $SERVICE_CONTENT_DIRECTORY/dist/*.tar.gz`) - $PIPCMD install $SERVICE_CONTENT_DIRECTORY/dist/$TRBL_FILE - if [ $? -ne 0 ];then - log "$PIPCMD install \"$TRBL_FILE\" FAILS, exiting!!!" - exit 1 - fi - else - log "$MRN_CND_SPY not found!" - fi -# Creating etc directory for config files - if [ ! -d $ETC_CFG_DIR ];then - log "Creating $ETC_CFG_DIR direcory..." - mkdir -p $ETC_CFG_DIR - if [ $? -ne 0 ];then - log "Can't create $ETC_CFG_DIR, exiting!!!" - exit - fi - fi -# Creating log directory for the murano - if [ ! -d $LOG_DIR ];then - log "Creating $LOG_DIR direcory..." - mkdir -p $LOG_DIR - if [ $? -ne 0 ];then - log "Can't create $LOG_DIR, exiting!!!" - exit 1 - fi - chmod -R a+rw $LOG_DIR - fi -# making sample configs - log "Making sample configuration files at \"$ETC_CFG_DIR\"" - for file in $(ls $SERVICE_CONTENT_DIRECTORY/etc) - do - if [ -d "$SERVICE_CONTENT_DIRECTORY/etc/$file" ];then - cp -f -R "$SERVICE_CONTENT_DIRECTORY/etc/$file" "$ETC_CFG_DIR/" - else - cp -f "$SERVICE_CONTENT_DIRECTORY/etc/$file" "$ETC_CFG_DIR/$file.sample" - fi - done -# making templates data - #log "Making templates directory" - #cp -f -R "$SERVICE_CONTENT_DIRECTORY/data" "$ETC_CFG_DIR/" -} - -# searching for service executable in path -get_service_exec_path() -{ - if [ -z "$SERVICE_EXEC_PATH" ]; then - SERVICE_EXEC_PATH=$(which muranoconductor) - if [ $? -ne 0 ]; then - log "Can't find \"conductor ($SERVICE_SRV_NAME)\", please install the \"$SERVICE_SRV_NAME\" by running \"$(basename "$0") install\" or set variable SERVICE_EXEC_PATH=/path/to/daemon before running setup script, exiting!" - exit 1 - fi - else - if [ ! -x "$SERVICE_EXEC_PATH" ]; then - log "\"$SERVICE_EXEC_PATH\" in not executable, please install the \"conductor ($SERVICE_SRV_NAME)\" or set variable SERVICE_EXEC_PATH=/path/to/daemon before running setup script, exiting!" - exit 1 - fi - fi -} - -# inject init -injectinit() -{ -echo "description \"$SERVICE_SRV_NAME service\" -author \"Igor Yozhikov \" -start on runlevel [2345] -stop on runlevel [!2345] -respawn -exec $SERVICE_EXEC_PATH --config-file=$SERVICE_CONFIG_FILE_PATH" > "/etc/init/$SERVICE_SRV_NAME.conf" - log "Reloading initctl" - initctl reload-configuration -} - -# purge init -purgeinit() -{ - rm -f /etc/init/$SERVICE_SRV_NAME.conf - log "Reloading initctl" - initctl reload-configuration -} - -# uninstall -uninst() -{ -# Uninstall trough pip - find_pip -# looking up for python package installed - PYPKG=$SERVICE_SRV_NAME - _pkg=$($PIPCMD freeze | grep $PYPKG) - if [ $? -eq 0 ]; then - log "Removing package \"$PYPKG\" with pip" - $PIPCMD uninstall $_pkg --yes - else - log "Python package \"$PYPKG\" not found" - fi -} - -# postinstall -postinst() -{ - log "Please, make proper configuration,located at \"$ETC_CFG_DIR\", before starting the \"$SERVICE_SRV_NAME\" daemon!" -} -# Command line args' -COMMAND="$1" -case $COMMAND in - inject-init ) - get_service_exec_path - log "Injecting \"$SERVICE_SRV_NAME\" to init..." - injectinit - postinst - ;; - - install ) - inst - get_service_exec_path - injectinit - postinst - ;; - - installfromgit ) - inst "yes" - get_service_exec_path - injectinit - postinst - ;; - - purge-init ) - log "Purging \"$SERVICE_SRV_NAME\" from init..." - stop $SERVICE_SRV_NAME - purgeinit - ;; - - uninstall ) - log "Uninstalling \"$SERVICE_SRV_NAME\" from system..." - stop $SERVICE_SRV_NAME - purgeinit - uninst - ;; - - * ) - echo -e "Usage: $(basename "$0") command \nCommands:\n\tinstall - Install $SERVICE_SRV_NAME software\n\tuninstall - Uninstall $SERVICE_SRV_NAME software\n\tinject-init - Add $SERVICE_SRV_NAME to the system start-up\n\tpurge-init - Remove $SERVICE_SRV_NAME from the system start-up" - exit 1 - ;; -esac diff --git a/setup.cfg b/setup.cfg index 1e1b0df..1041f8c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,13 +1,13 @@ [metadata] name = murano-conductor summary = The Conductor is orchestration engine server -version = 0.3 +version = 0.4.1 description-file = README.rst license = Apache License, Version 2.0 author = Mirantis, Inc. author-email = murano-all@lists.openstack.org -home-page = htts://launchpad.net/murano +home-page = https://launchpad.net/murano classifier = Development Status :: 5 - Production/Stable Environment :: OpenStack diff --git a/setup.sh b/setup.sh old mode 100644 new mode 100755 index e9844c6..2f25adf --- a/setup.sh +++ b/setup.sh @@ -1,5 +1,5 @@ -#!/bin/sh -# Copyright (c) 2013 Mirantis, Inc. +#!/bin/bash +# Copyright (c) 2014 Mirantis, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain @@ -13,252 +13,250 @@ # License for the specific language governing permissions and limitations # under the License. # -# Ubuntu script. - -LOGLVL=1 -SERVICE_CONTENT_DIRECTORY=`cd $(dirname "$0") && pwd` -PREREQ_PKGS="upstart wget git make python-pip python-dev python-mysqldb libxml2-dev libxslt-dev libffi-dev" -SERVICE_SRV_NAME="murano-conductor" -GIT_CLONE_DIR=`echo $SERVICE_CONTENT_DIRECTORY | sed -e "s/$SERVICE_SRV_NAME//"` -#ETC_CFG_DIR="/etc/$SERVICE_SRV_NAME" -ETC_CFG_DIR="/etc/murano" -LOG_DIR="/var/log/murano/" -SERVICE_CONFIG_FILE_PATH="$ETC_CFG_DIR/conductor.conf" - -# Functions -# Loger function -log() -{ - MSG=$1 - if [ $LOGLVL -gt 0 ]; then - echo "LOG:> $MSG" - fi -} - -# Check or install package -in_sys_pkg() -{ - PKG=$1 - dpkg -s $PKG > /dev/null 2>&1 - if [ $? -eq 0 ]; then - log "Package \"$PKG\" already installed" - else - log "Installing \"$PKG\"..." - apt-get install $PKG --yes > /dev/null 2>&1 - if [ $? -ne 0 ];then - log "installation fails, exiting!!!" - exit - fi - fi -} - -# git clone -gitclone() -{ - FROM=$1 - CLONEROOT=$2 - log "Cloning from \"$FROM\" repo to \"$CLONEROOT\"" - cd $CLONEROOT && git clone $FROM > /dev/null 2>&1 - if [ $? -ne 0 ];then - log "cloning from \"$FROM\" fails, exiting!!!" - exit - fi -} - -# install -inst() -{ -CLONE_FROM_GIT=$1 -# Checking packages - for PKG in $PREREQ_PKGS - do - in_sys_pkg $PKG - done -# If clone from git set - if [ ! -z $CLONE_FROM_GIT ]; then -# Preparing clone root directory - if [ ! -d $GIT_CLONE_DIR ];then - log "Creating $GIT_CLONE_DIR direcory..." - mkdir -p $GIT_CLONE_DIR - if [ $? -ne 0 ];then - log "Can't create $GIT_CLONE_DIR, exiting!!!" - exit - fi - fi -# Cloning from GIT - GIT_WEBPATH_PRFX="https://github.com/stackforge/" - gitclone "$GIT_WEBPATH_PRFX$SERVICE_SRV_NAME.git" $GIT_CLONE_DIR -# End clone from git section - fi - -# Setupping... - log "Running setup.py" - MRN_CND_SPY=$SERVICE_CONTENT_DIRECTORY/setup.py - if [ -e $MRN_CND_SPY ];then - chmod +x $MRN_CND_SPY - log "$MRN_CND_SPY output:_____________________________________________________________" -## Setup through pip - # Creating tarball - rm -rf $SERVICE_CONTENT_DIRECTORY/*.egg-info - cd $SERVICE_CONTENT_DIRECTORY && python $MRN_CND_SPY egg_info - if [ $? -ne 0 ];then - log "\"$MRN_CND_SPY\" egg info creation FAILS, exiting!!!" - exit 1 - fi - rm -rf $SERVICE_CONTENT_DIRECTORY/dist/* - cd $SERVICE_CONTENT_DIRECTORY && $MRN_CND_SPY sdist - if [ $? -ne 0 ];then - log "\"$MRN_CND_SPY\" tarball creation FAILS, exiting!!!" - exit 1 - fi - # Running tarball install - TRBL_FILE=$(basename `ls $SERVICE_CONTENT_DIRECTORY/dist/*.tar.gz`) - pip install $SERVICE_CONTENT_DIRECTORY/dist/$TRBL_FILE - if [ $? -ne 0 ];then - log "pip install \"$TRBL_FILE\" FAILS, exiting!!!" - exit 1 - fi - else - log "$MRN_CND_SPY not found!" - fi -# Creating etc directory for config files - if [ ! -d $ETC_CFG_DIR ];then - log "Creating $ETC_CFG_DIR direcory..." - mkdir -p $ETC_CFG_DIR - if [ $? -ne 0 ];then - log "Can't create $ETC_CFG_DIR, exiting!!!" - exit 1 - fi - fi -# Creating log directory for the murano - if [ ! -d $LOG_DIR ];then - log "Creating $LOG_DIR direcory..." - mkdir -p $LOG_DIR - if [ $? -ne 0 ];then - log "Can't create $LOG_DIR, exiting!!!" - exit 1 - fi - chmod -R a+rw $LOG_DIR - fi -# making sample configs - log "Making sample configuration files at \"$ETC_CFG_DIR\"" - for file in $(ls $SERVICE_CONTENT_DIRECTORY/etc) - do - if [ -d "$SERVICE_CONTENT_DIRECTORY/etc/$file" ];then - cp -f -R "$SERVICE_CONTENT_DIRECTORY/etc/$file" "$ETC_CFG_DIR/" - else - cp -f "$SERVICE_CONTENT_DIRECTORY/etc/$file" "$ETC_CFG_DIR/$file.sample" - fi - done -# making templates data - #log "Making templates directory" - #cp -f -R "$SERVICE_CONTENT_DIRECTORY/data" "$ETC_CFG_DIR/" -} - -# searching for service executable in path -get_service_exec_path() -{ - if [ -z "$SERVICE_EXEC_PATH" ]; then - SERVICE_EXEC_PATH=$(which muranoconductor) - if [ $? -ne 0 ]; then - log "Can't find \"conductor ($SERVICE_SRV_NAME)\", please install the \"$SERVICE_SRV_NAME\" by running \"$(basename "$0") install\" or set variable SERVICE_EXEC_PATH=/path/to/daemon before running setup script, exiting!" - exit 1 - fi - else - if [ ! -x "$SERVICE_EXEC_PATH" ]; then - log "\"$SERVICE_EXEC_PATH\" in not executable, please install the \"conductor ($SERVICE_SRV_NAME)\" or set variable SERVICE_EXEC_PATH=/path/to/daemon before running setup script, exiting!" - exit 1 - fi - fi -} - -# inject init -injectinit() -{ -ln -s /lib/init/upstart-job /etc/init.d/$SERVICE_SRV_NAME -if [ $? -ne 0 ]; then - log "Can't create symlink, please run \"$(basename "$0") purge-init\" before \"$(basename "$0") inject-init\", exiting" - exit 1 +RUN_DIR=$(cd $(dirname "$0") && pwd) +INC_FILE="$RUN_DIR/common.inc" +if [ -f "$INC_FILE" ]; then + source "$INC_FILE" +else + echo "Can't load \"$INC_FILE\" or file not found, exiting!" + exit 1 fi -echo "description \"$SERVICE_SRV_NAME service\" -author \"Igor Yozhikov \" -start on runlevel [2345] -stop on runlevel [!2345] -respawn -exec start-stop-daemon --start --chuid root --user root --name $SERVICE_SRV_NAME --exec $SERVICE_EXEC_PATH -- --config-file=$SERVICE_CONFIG_FILE_PATH" > "/etc/init/$SERVICE_SRV_NAME.conf" - log "Reloading initctl" - initctl reload-configuration +# +DAEMON_NAME="murano-conductor" +DAEMON_USER="murano" +DAEMON_GROUP="murano" +DAEMON_CFG_DIR="/etc/murano" +DAEMON_CACHE_DIR="/var/cache/murano" +DAEMON_LOG_DIR="/var/log/murano" +LOGFILE="/tmp/${DAEMON_NAME}_install.log" +DAEMON_DB_CONSTR="sqlite:///$DAEMON_CFG_DIR/$DAEMON_NAME.sqlite" +common_pkgs="wget git make gcc python-pip python-setuptools dos2unix" +# Distro-specific package namings +debian_pkgs="python-dev python-mysqldb libxml2-dev libxslt1-dev libffi-dev libssl-dev" +redhat_pkgs="python-devel MySQL-python libxml2-devel libxslt-devel libffi-devel openssl-devel" +# +get_os +eval req_pkgs="\$$(lowercase $DISTRO_BASED_ON)_pkgs" +REQ_PKGS="$common_pkgs $req_pkgs" + +function install_prerequisites() +{ + retval=0 + _dist=$(lowercase $DISTRO_BASED_ON) + if [ $_dist = "redhat" ]; then + yum repolist | grep -qoE "epel" + if [ $? -ne 0 ]; then + log "Enabling EPEL6..." + rpm -ivh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm >> $LOGFILE 2>&1 + if [ $? -ne 0 ]; then + log "... can't enable EPEL6, exiting!" + retval=1 + return $retval + fi + fi + yum --quiet makecache + fi + for pack in $REQ_PKGS + do + find_or_install "$pack" + if [ $? -eq 1 ]; then + retval=1 + break + else + retval=0 + fi + done + return $retval +} +function make_tarball() +{ + retval=0 + log "Preparing tarball package..." + setuppy="$RUN_DIR/setup.py" + if [ -e "$setuppy" ]; then + chmod +x $setuppy + rm -rf $RUN_DIR/*.egg-info + cd $RUN_DIR && python $setuppy egg_info > /dev/null 2>&1 + if [ $? -ne 0 ];then + log "...\"$setuppy\" egg info creation fails, exiting!!!" + retval=1 + exit 1 + fi + rm -rf $RUN_DIR/dist/* + log "...\"setup.py sdist\" output will be recorded in \"$LOGFILE\"" + cd $RUN_DIR && $setuppy sdist >> $LOGFILE 2>&1 + if [ $? -ne 0 ];then + log "...\"$setuppy\" tarball creation fails, exiting!!!" + retval=1 + exit 1 + fi + #TRBL_FILE=$(basename $(ls $RUN_DIR/dist/*.tar.gz | head -n 1)) + TRBL_FILE=$(ls $RUN_DIR/dist/*.tar.gz | head -n 1) + if [ ! -e "$TRBL_FILE" ]; then + log "...tarball not found, exiting!" + retval=1 + else + log "...success, tarball created as \"$TRBL_FILE\"" + retval=0 + fi + else + log "...\"$setuppy\" not found, exiting!" + retval=1 + fi + return $retval +} +function run_pip_install() +{ + find_pip + retval=0 + tarball_file=${1:-$TRBL_FILE} + log "Running \"$PIPCMD install $PIPARGS $tarball_file\" output will be recorded in \"$LOGFILE\"" + $PIPCMD install $PIPARGS $tarball_file >> $LOGFILE 2>&1 + if [ $? -ne 0 ]; then + log "...pip install fails, exiting!" + retval=1 + exit 1 + fi + return $retval } -# purge init -purgeinit() +function inject_init() { - rm -f /etc/init.d/$SERVICE_SRV_NAME - rm -f /etc/init/$SERVICE_SRV_NAME.conf - log "Reloading initctl" - initctl reload-configuration + retval=0 + _dist=$(lowercase $DISTRO_BASED_ON) + eval src_init_sctipt="$DAEMON_NAME-$_dist" + _initscript="openstack-$DAEMON_NAME" + cp -f "$RUN_DIR/etc/init.d/$src_init_sctipt" "/etc/init.d/$_initscript" || retval=$? + chmod +x "/etc/init.d/$_initscript" || retval=$? + iniset '' 'SYSTEM_USER' "$DAEMON_USER" "/etc/init.d/$_initscript" + iniset '' 'DAEMON' "$(shslash $SERVICE_EXEC_PATH)" "/etc/init.d/$_initscript" + case $_dist in + "debian") + update-rc.d $_initscript defaults || retval=$? + update-rc.d $_initscript enable || retval=$? + ;; + *) + chkconfig --add $_initscript || retval=$? + chkconfig $_initscript on || retval=$? + ;; + esac + return $retval } - -# uninstall -uninst() +function purge_init() { -# Uninstall trough pip -# looking up for python package installed - PYPKG=$SERVICE_SRV_NAME - pip freeze | grep $PYPKG - if [ $? -eq 0 ]; then - log "Removing package \"$PYPKG\" with pip" - pip uninstall $PYPKG --yes - else - log "Python package \"$PYPKG\" not found" - fi + retval=0 + _dist=$(lowercase $DISTRO_BASED_ON) + _initscript="openstack-$DAEMON_NAME" + service $_initscript stop + if [ $? -ne 0 ]; then + retval=1 + fi + case $_dist in + "debian") + update-rc.d $_initscript disable + update-rc.d -f $_initscript remove || retval=$? + ;; + *) + chkconfig $_initscript off || retval=$? + chkconfig --del $_initscript || retval=$? + ;; + esac + rm -f "/etc/init.d/$_initscript" || retval=$? + return $retval } - -# postinstall -postinst() +function run_pip_uninstall() { - log "Please, make proper configuration,located at \"$ETC_CFG_DIR\", before starting the \"$SERVICE_SRV_NAME\" daemon!" + find_pip + retval=0 + pack_to_del=$(is_py_package_installed "$DAEMON_NAME") + if [ $? -eq 0 ]; then + log "Running \"$PIPCMD uninstall $PIPARGS $DAEMON_NAME\" output will be recorded in \"$LOGFILE\"" + $PIPCMD uninstall $pack_to_del --yes >> $LOGFILE 2>&1 + if [ $? -ne 0 ]; then + log "...can't uninstall $DAEMON_NAME with $PIPCMD" + retval=1 + else + log "...success" + fi + else + log "Python package for \"$DAEMON_NAME\" not found" + fi + return $retval +} +function install_daemon() +{ + install_prerequisites || exit 1 + make_tarball || exit $? + run_pip_install || exit $? + add_daemon_credentials "$DAEMON_USER" "$DAEMON_GROUP" || exit $? + log "Creating required directories..." + mk_dir "$DAEMON_CFG_DIR" "$DAEMON_USER" "$DAEMON_GROUP" || exit 1 + mk_dir "$DAEMON_CACHE_DIR" "$DAEMON_USER" "$DAEMON_GROUP" || exit 1 + mk_dir "$DAEMON_LOG_DIR" "$DAEMON_USER" "$DAEMON_GROUP" || exit 1 + log "Making sample configuration files at \"$DAEMON_CFG_DIR\"" + _src_conf_dir="$RUN_DIR/etc/murano" + _prefix="murano-" + for file in $(ls $_src_conf_dir) + do + if [ -d "$_src_conf_dir/$file" ]; then + #Dir copy + cp -f -r "$_src_conf_dir/$file" "$DAEMON_CFG_DIR/$file" + else + #cp -f "$_src_conf_dir/$file" "$DAEMON_CFG_DIR/${_prefix}${file}.sample" + cp -f "$_src_conf_dir/$file" "$DAEMON_CFG_DIR/$file" + config_file=$_prefix$(echo $file | sed -e 's/.sample$//') + #removing Cr Lf + dos2unix "$DAEMON_CFG_DIR/$file" + if [ ! -e "$DAEMON_CFG_DIR/$config_file" ]; then + cp -f "$_src_conf_dir/$file" "$DAEMON_CFG_DIR/$config_file" + dos2unix "$DAEMON_CFG_DIR/$config_file" + else + log "\"$DAEMON_CFG_DIR/$config_file\" exists, skipping copy." + fi + fi + done + log "Setting log file and sqlite db placement..." + iniset 'DEFAULT' 'log_file' "$(shslash $DAEMON_LOG_DIR/$DAEMON_NAME.log)" "$DAEMON_CFG_DIR/$DAEMON_NAME.conf" + iniset 'DEFAULT' 'verbose' 'True' "$DAEMON_CFG_DIR/$DAEMON_NAME.conf" + iniset 'DEFAULT' 'debug' 'True' "$DAEMON_CFG_DIR/$DAEMON_NAME.conf" + iniset 'DEFAULT' 'init_scripts_dir' "$(shslash $DAEMON_CFG_DIR/init-scripts)" "$DAEMON_CFG_DIR/$DAEMON_NAME.conf" + iniset 'DEFAULT' 'agent_config_dir' "$(shslash $DAEMON_CFG_DIR/agent-config)" "$DAEMON_CFG_DIR/$DAEMON_NAME.conf" + iniset 'DEFAULT' 'data_dir' "$(shslash $DAEMON_CACHE_DIR/muranoconductor-data)" "$DAEMON_CFG_DIR/$DAEMON_NAME.conf" + log "Searching daemon in \$PATH..." + OLD_DAEMON_NAME=$DAEMON_NAME + #murano-conductor->muranoconductor + DAEMON_NAME=$(echo $DAEMON_NAME | tr -d '-') + get_service_exec_path || exit $? + DAEMON_NAME=$OLD_DAEMON_NAME + log "...found at \"$SERVICE_EXEC_PATH\"" + log "Installing SysV init script." + inject_init || exit $? + log "Everything done, please, verify \"$DAEMON_CFG_DIR/$DAEMON_NAME.conf\", service created as \"openstack-${DAEMON_NAME}\"." +} +function uninstall_daemon() +{ + log "Removing SysV init script..." + purge_init || exit $? + remove_daemon_credentials "$DAEMON_USER" "$DAEMON_GROUP" || exit $? + run_pip_uninstall || exit $? + log "Software uninstalled, daemon configuration files and logs located at \"$DAEMON_CFG_DIR\" and \"$DAEMON_LOG_DIR\"." } # Command line args' COMMAND="$1" case $COMMAND in - inject-init ) - get_service_exec_path - log "Injecting \"$SERVICE_SRV_NAME\" to init..." - injectinit - postinst - ;; + install) + rm -rf $LOGFILE + log "Installing \"$DAEMON_NAME\" to system..." + install_daemon + ;; - install ) - inst - get_service_exec_path - injectinit - postinst - ;; + uninstall ) + log "Uninstalling \"$DAEMON_NAME\" from system..." + uninstall_daemon + ;; - installfromgit ) - inst "yes" - get_service_exec_path - injectinit - postinst - ;; - - purge-init ) - log "Purging \"$SERVICE_SRV_NAME\" from init..." - stop $SERVICE_SRV_NAME - purgeinit - ;; - - uninstall ) - log "Uninstalling \"$SERVICE_SRV_NAME\" from system..." - stop $SERVICE_SRV_NAME - purgeinit - uninst - ;; - - * ) - echo "Usage: $(basename "$0") command \nCommands:\n\tinstall - Install $SERVICE_SRV_NAME software\n\tuninstall - Uninstall $SERVICE_SRV_NAME software\n\tinject-init - Add $SERVICE_SRV_NAME to the system start-up\n\tpurge-init - Remove $SERVICE_SRV_NAME from the system start-up" - exit 1 - ;; + * ) + echo -e "Usage: $(basename "$0") [command] \nCommands:\n\tinstall - Install \"$DAEMON_NAME\" software\n\tuninstall - Uninstall \"$DAEMON_NAME\" software" + exit 1 + ;; esac -