94630b97cd
DevStack is moving to adopt VENV to manage its python versions. However, CloudKittty DevStack integration was not using the DevStack VENV variable. This, in turn, causes issues with tempest tests, as they are based on a DevStack deployment. We need to merge this patch to fix the tempest tests. Change-Id: I17de617557fb86c002814941325d71e3c08e0e72
445 lines
16 KiB
Bash
Executable File
445 lines
16 KiB
Bash
Executable File
# CloudKitty devstack plugin
|
|
# Install and start **CloudKitty** service
|
|
|
|
# To enable a minimal set of CloudKitty services:
|
|
# - enable Ceilometer ;
|
|
# - add the following to the [[local|localrc]] section in the local.conf file:
|
|
#
|
|
# enable_service ck-api ck-proc
|
|
#
|
|
# Dependencies:
|
|
# - functions
|
|
# - OS_AUTH_URL for auth in api
|
|
# - DEST, DATA_DIR set to the destination directory
|
|
# - SERVICE_PASSWORD, SERVICE_TENANT_NAME for auth in api
|
|
# - IDENTITY_API_VERSION for the version of Keystone
|
|
# - STACK_USER service user
|
|
# - HORIZON_DIR for horizon integration
|
|
|
|
# stack.sh
|
|
# ---------
|
|
# install_cloudkitty
|
|
# configure_cloudkitty
|
|
# init_cloudkitty
|
|
# start_cloudkitty
|
|
# stop_cloudkitty
|
|
# cleanup_cloudkitty
|
|
# install_python_cloudkittyclient
|
|
|
|
# Save trace setting
|
|
XTRACE=$(set +o | grep xtrace)
|
|
set +o xtrace
|
|
|
|
|
|
# Support potential entry-points console scripts
|
|
if [[ -d $CLOUDKITTY_DIR/bin ]]; then
|
|
CLOUDKITTY_BIN_DIR=$CLOUDKITTY_DIR/bin
|
|
else
|
|
CLOUDKITTY_BIN_DIR=$(get_python_exec_prefix)
|
|
fi
|
|
|
|
# Functions
|
|
# ---------
|
|
|
|
# create_cloudkitty_accounts() - Set up common required cloudkitty accounts
|
|
# Tenant User Roles
|
|
# ------------------------------------------------------------------
|
|
# service cloudkitty admin # if enabled
|
|
function create_cloudkitty_accounts {
|
|
create_service_user "cloudkitty"
|
|
|
|
local cloudkitty_service=$(get_or_create_service "cloudkitty" \
|
|
"rating" "OpenStack Rating")
|
|
get_or_create_endpoint $cloudkitty_service \
|
|
"$REGION_NAME" \
|
|
"$CLOUDKITTY_SERVICE_PROTOCOL://$CLOUDKITTY_SERVICE_HOSTPORT/" \
|
|
"$CLOUDKITTY_SERVICE_PROTOCOL://$CLOUDKITTY_SERVICE_HOSTPORT/" \
|
|
"$CLOUDKITTY_SERVICE_PROTOCOL://$CLOUDKITTY_SERVICE_HOSTPORT/"
|
|
|
|
# Create the rating role
|
|
get_or_create_role "rating"
|
|
|
|
# Make cloudkitty an admin
|
|
get_or_add_user_project_role admin cloudkitty service
|
|
|
|
# Make CloudKitty monitor demo project for rating purposes
|
|
get_or_add_user_project_role rating cloudkitty demo
|
|
}
|
|
|
|
# Test if any CloudKitty services are enabled
|
|
# is_cloudkitty_enabled
|
|
function is_cloudkitty_enabled {
|
|
[[ ,${ENABLED_SERVICES} =~ ,"ck-" ]] && return 0
|
|
return 1
|
|
}
|
|
|
|
# Remove WSGI files, disable and remove Apache vhost file
|
|
function _cloudkitty_cleanup_apache_wsgi {
|
|
if is_service_enabled ck-api && [ "$CLOUDKITTY_USE_MOD_WSGI" == "True" ]; then
|
|
sudo rm -f "$CLOUDKITTY_WSGI_DIR"/*
|
|
sudo rm -rf "$CLOUDKITTY_WSGI_DIR"
|
|
sudo rm -f $(apache_site_config_for cloudkitty)
|
|
fi
|
|
}
|
|
|
|
# cleanup_cloudkitty() - Remove residual data files, anything left over from previous
|
|
# runs that a clean run would need to clean up
|
|
function cleanup_cloudkitty {
|
|
_cloudkitty_cleanup_apache_wsgi
|
|
# Clean up dirs
|
|
rm -rf $CLOUDKITTY_AUTH_CACHE_DIR/*
|
|
rm -rf $CLOUDKITTY_CONF_DIR/*
|
|
rm -rf $CLOUDKITTY_OUTPUT_BASEPATH/*
|
|
for i in $(find $CLOUDKITTY_ENABLED_DIR -iname '_[0-9]*.py' -printf '%f\n'); do
|
|
rm -f "${CLOUDKITTY_HORIZON_ENABLED_DIR}/$i"
|
|
done
|
|
}
|
|
|
|
|
|
# Configure mod_wsgi
|
|
function _cloudkitty_config_apache_wsgi {
|
|
sudo mkdir -m 755 -p $CLOUDKITTY_WSGI_DIR
|
|
|
|
local cloudkitty_apache_conf=$(apache_site_config_for cloudkitty)
|
|
local venv_path=""
|
|
|
|
# Copy proxy vhost and wsgi file
|
|
sudo cp $CLOUDKITTY_DIR/cloudkitty/api/app.wsgi $CLOUDKITTY_WSGI_DIR/app.wsgi
|
|
|
|
if [[ ${USE_VENV} = True ]]; then
|
|
venv_path="python-path=${PROJECT_VENV["cloudkitty"]}/lib/$(python_version)/site-packages"
|
|
fi
|
|
|
|
sudo cp $CLOUDKITTY_DIR/devstack/apache-cloudkitty.template $cloudkitty_apache_conf
|
|
sudo sed -e "
|
|
s|%PORT%|$CLOUDKITTY_SERVICE_PORT|g;
|
|
s|%APACHE_NAME%|$APACHE_NAME|g;
|
|
s|%WSGIAPP%|$CLOUDKITTY_WSGI_DIR/app.wsgi|g;
|
|
s|%USER%|$STACK_USER|g;
|
|
s|%VIRTUALENV%|$DEVSTACK_VENV|g
|
|
" -i $cloudkitty_apache_conf
|
|
}
|
|
|
|
# configure_cloudkitty() - Set config files, create data dirs, etc
|
|
function configure_cloudkitty {
|
|
setup_develop $CLOUDKITTY_DIR
|
|
|
|
sudo mkdir -m 755 -p $CLOUDKITTY_CONF_DIR
|
|
sudo chown $STACK_USER $CLOUDKITTY_CONF_DIR
|
|
|
|
sudo mkdir -m 755 -p $CLOUDKITTY_API_LOG_DIR
|
|
sudo chown $STACK_USER $CLOUDKITTY_API_LOG_DIR
|
|
|
|
touch $CLOUDKITTY_CONF
|
|
|
|
# generate policy sample file
|
|
oslopolicy-sample-generator --config-file $CLOUDKITTY_DIR/etc/oslo-policy-generator/cloudkitty.conf --output-file $CLOUDKITTY_DIR/etc/cloudkitty/policy.yaml.sample
|
|
cp $CLOUDKITTY_DIR/etc/cloudkitty/policy.yaml.sample "$CLOUDKITTY_CONF_DIR/policy.yaml"
|
|
iniset $CLOUDKITTY_CONF oslo_policy policy_file 'policy.yaml'
|
|
|
|
cp $CLOUDKITTY_DIR$CLOUDKITTY_CONF_DIR/api_paste.ini $CLOUDKITTY_CONF_DIR
|
|
cp $CLOUDKITTY_DIR$CLOUDKITTY_CONF_DIR/metrics.yml $CLOUDKITTY_CONF_DIR
|
|
iniset_rpc_backend cloudkitty $CLOUDKITTY_CONF DEFAULT
|
|
|
|
iniset $CLOUDKITTY_CONF DEFAULT notification_topics 'notifications'
|
|
iniset $CLOUDKITTY_CONF DEFAULT debug "$ENABLE_DEBUG_LOG_LEVEL"
|
|
iniset $CLOUDKITTY_CONF DEFAULT auth_strategy $CLOUDKITTY_AUTH_STRATEGY
|
|
|
|
# auth
|
|
iniset $CLOUDKITTY_CONF authinfos auth_type v3password
|
|
iniset $CLOUDKITTY_CONF authinfos auth_protocol http
|
|
iniset $CLOUDKITTY_CONF authinfos auth_url "$KEYSTONE_SERVICE_URI/v3"
|
|
iniset $CLOUDKITTY_CONF authinfos identity_uri "$KEYSTONE_SERVICE_URI/v3"
|
|
iniset $CLOUDKITTY_CONF authinfos username cloudkitty
|
|
iniset $CLOUDKITTY_CONF authinfos password $SERVICE_PASSWORD
|
|
iniset $CLOUDKITTY_CONF authinfos project_name $SERVICE_TENANT_NAME
|
|
iniset $CLOUDKITTY_CONF authinfos tenant_name $SERVICE_TENANT_NAME
|
|
iniset $CLOUDKITTY_CONF authinfos region_name $REGION_NAME
|
|
iniset $CLOUDKITTY_CONF authinfos user_domain_name default
|
|
iniset $CLOUDKITTY_CONF authinfos project_domain_name default
|
|
iniset $CLOUDKITTY_CONF authinfos debug "$ENABLE_DEBUG_LOG_LEVEL"
|
|
|
|
iniset $CLOUDKITTY_CONF fetcher backend $CLOUDKITTY_FETCHER
|
|
iniset $CLOUDKITTY_CONF "fetcher_$CLOUDKITTY_FETCHER" auth_section authinfos
|
|
if [[ "$CLOUDKITTY_FETCHER" == "keystone" ]]; then
|
|
iniset $CLOUDKITTY_CONF fetcher_keystone keystone_version 3
|
|
fi
|
|
|
|
if [ "$CLOUDKITTY_STORAGE_BACKEND" == "influxdb" ]; then
|
|
iniset $CLOUDKITTY_CONF storage_${CLOUDKITTY_STORAGE_BACKEND} user ${CLOUDKITTY_INFLUXDB_USER}
|
|
iniset $CLOUDKITTY_CONF storage_${CLOUDKITTY_STORAGE_BACKEND} password ${CLOUDKITTY_INFLUXDB_PASSWORD}
|
|
iniset $CLOUDKITTY_CONF storage_${CLOUDKITTY_STORAGE_BACKEND} database ${CLOUDKITTY_INFLUXDB_DATABASE}
|
|
iniset $CLOUDKITTY_CONF storage_${CLOUDKITTY_STORAGE_BACKEND} host ${CLOUDKITTY_INFLUXDB_HOST}
|
|
iniset $CLOUDKITTY_CONF storage_${CLOUDKITTY_STORAGE_BACKEND} port ${CLOUDKITTY_INFLUXDB_PORT}
|
|
fi
|
|
|
|
if [ "$CLOUDKITTY_STORAGE_BACKEND" == "elasticsearch" ]; then
|
|
iniset $CLOUDKITTY_CONF storage_${CLOUDKITTY_STORAGE_BACKEND} host ${CLOUDKITTY_ELASTICSEARCH_HOST}
|
|
iniset $CLOUDKITTY_CONF storage_${CLOUDKITTY_STORAGE_BACKEND} index_name ${CLOUDKITTY_ELASTICSEARCH_INDEX}
|
|
fi
|
|
|
|
# collect
|
|
iniset $CLOUDKITTY_CONF collect collector $CLOUDKITTY_COLLECTOR
|
|
iniset $CLOUDKITTY_CONF "collector_${CLOUDKITTY_COLLECTOR}" auth_section authinfos
|
|
iniset $CLOUDKITTY_CONF collect metrics_conf $CLOUDKITTY_CONF_DIR/$CLOUDKITTY_METRICS_CONF
|
|
# DO NOT DO THIS IN PRODUCTION! This is done in order to get data quicker
|
|
# when starting a devstack installation, but is NOT a recommended setting
|
|
iniset $CLOUDKITTY_CONF collect wait_periods 0
|
|
|
|
# output
|
|
iniset $CLOUDKITTY_CONF output backend $CLOUDKITTY_OUTPUT_BACKEND
|
|
iniset $CLOUDKITTY_CONF output basepath $CLOUDKITTY_OUTPUT_BASEPATH
|
|
iniset $CLOUDKITTY_CONF output pipeline $CLOUDKITTY_OUTPUT_PIPELINE
|
|
|
|
# storage
|
|
iniset $CLOUDKITTY_CONF storage backend $CLOUDKITTY_STORAGE_BACKEND
|
|
iniset $CLOUDKITTY_CONF storage version $CLOUDKITTY_STORAGE_VERSION
|
|
|
|
# database
|
|
local dburl=`database_connection_url cloudkitty`
|
|
iniset $CLOUDKITTY_CONF database connection $dburl
|
|
|
|
# keystone middleware
|
|
configure_keystone_authtoken_middleware $CLOUDKITTY_CONF cloudkitty
|
|
|
|
if is_service_enabled ck-api && [ "$CLOUDKITTY_USE_MOD_WSGI" == "True" ]; then
|
|
_cloudkitty_config_apache_wsgi
|
|
fi
|
|
}
|
|
|
|
function wait_for_gnocchi() {
|
|
local gnocchi_url=$(openstack --os-cloud devstack-admin endpoint list --service metric --interface public -c URL -f value)
|
|
if ! wait_for_service $SERVICE_TIMEOUT $gnocchi_url; then
|
|
die $LINENO "Waited for gnocchi too long."
|
|
fi
|
|
}
|
|
|
|
# create_cloudkitty_cache_dir() - Part of the init_cloudkitty() process
|
|
function create_cloudkitty_cache_dir {
|
|
# Create cache dir
|
|
sudo mkdir -p $CLOUDKITTY_AUTH_CACHE_DIR/api
|
|
sudo chown $STACK_USER $CLOUDKITTY_AUTH_CACHE_DIR/api
|
|
rm -f $CLOUDKITTY_AUTH_CACHE_DIR/api/*
|
|
sudo mkdir -p $CLOUDKITTY_AUTH_CACHE_DIR/registry
|
|
sudo chown $STACK_USER $CLOUDKITTY_AUTH_CACHE_DIR/registry
|
|
rm -f $CLOUDKITTY_AUTH_CACHE_DIR/registry/*
|
|
}
|
|
|
|
# create_cloudkitty_data_dir() - Part of the init_cloudkitty() process
|
|
function create_cloudkitty_data_dir {
|
|
# Create data dir
|
|
sudo mkdir -p $CLOUDKITTY_DATA_DIR
|
|
sudo chown $STACK_USER $CLOUDKITTY_DATA_DIR
|
|
rm -rf $CLOUDKITTY_DATA_DIR/*
|
|
# Create locks dir
|
|
sudo mkdir -p $CLOUDKITTY_DATA_DIR/locks
|
|
sudo chown $STACK_USER $CLOUDKITTY_DATA_DIR/locks
|
|
}
|
|
|
|
function create_influxdb_database {
|
|
if [ "$CLOUDKITTY_STORAGE_BACKEND" == "influxdb" ]; then
|
|
influx -execute "CREATE DATABASE ${CLOUDKITTY_INFLUXDB_DATABASE}"
|
|
fi
|
|
}
|
|
|
|
function create_elasticsearch_index {
|
|
if [ "$CLOUDKITTY_STORAGE_BACKEND" == "elasticsearch" ]; then
|
|
curl -XPUT "${CLOUDKITTY_ELASTICSEARCH_HOST}/${CLOUDKITTY_ELASTICSEARCH_INDEX}"
|
|
fi
|
|
}
|
|
|
|
# init_cloudkitty() - Initialize CloudKitty database
|
|
function init_cloudkitty {
|
|
# Delete existing cache
|
|
sudo rm -rf $CLOUDKITTY_AUTH_CACHE_DIR
|
|
sudo mkdir -p $CLOUDKITTY_AUTH_CACHE_DIR
|
|
sudo chown $STACK_USER $CLOUDKITTY_AUTH_CACHE_DIR
|
|
|
|
# Delete existing cache
|
|
sudo rm -rf $CLOUDKITTY_OUTPUT_BASEPATH
|
|
sudo mkdir -p $CLOUDKITTY_OUTPUT_BASEPATH
|
|
sudo chown $STACK_USER $CLOUDKITTY_OUTPUT_BASEPATH
|
|
|
|
# (Re)create cloudkitty database
|
|
recreate_database cloudkitty utf8
|
|
|
|
create_influxdb_database
|
|
create_elasticsearch_index
|
|
|
|
# Migrate cloudkitty database
|
|
$CLOUDKITTY_BIN_DIR/cloudkitty-dbsync upgrade
|
|
|
|
# Init the storage backend
|
|
if [ $CLOUDKITTY_STORAGE_BACKEND == 'hybrid' ]; then
|
|
wait_for_gnocchi
|
|
fi
|
|
$CLOUDKITTY_BIN_DIR/cloudkitty-storage-init
|
|
|
|
create_cloudkitty_cache_dir
|
|
create_cloudkitty_data_dir
|
|
}
|
|
|
|
function install_influx_ubuntu {
|
|
local influxdb_file=$(get_extra_file https://dl.influxdata.com/influxdb/releases/influxdb_1.6.3_amd64.deb)
|
|
sudo dpkg -i --skip-same-version ${influxdb_file}
|
|
}
|
|
|
|
function install_influx_fedora {
|
|
local influxdb_file=$(get_extra_file https://dl.influxdata.com/influxdb/releases/influxdb-1.6.3.x86_64.rpm)
|
|
sudo yum localinstall -y ${influxdb_file}
|
|
}
|
|
|
|
function install_influx {
|
|
if is_ubuntu; then
|
|
install_influx_ubuntu
|
|
elif is_fedora; then
|
|
install_influx_fedora
|
|
else
|
|
die $LINENO "Distribution must be Debian or Fedora-based"
|
|
fi
|
|
sudo cp -f "${CLOUDKITTY_DIR}"/devstack/files/influxdb.conf /etc/influxdb/influxdb.conf
|
|
sudo systemctl start influxdb || sudo systemctl restart influxdb
|
|
}
|
|
|
|
function install_opensearch {
|
|
OPENSEARCH_HOME=/usr/share/opensearch
|
|
local opensearch_file=$(get_extra_file "https://artifacts.opensearch.org/releases/bundle/opensearch/1.3.6/opensearch-1.3.6-linux-x64.tar.gz")
|
|
sudo mkdir -p $OPENSEARCH_HOME
|
|
sudo tar -xzpf ${opensearch_file} -C $OPENSEARCH_HOME --strip-components=1
|
|
sudo mkdir -p $OPENSEARCH_HOME/data /var/log/opensearch
|
|
sudo chown -R $STACK_USER $OPENSEARCH_HOME /var/log/opensearch
|
|
cat - <<EOF | sudo tee $OPENSEARCH_HOME/config/opensearch.yml >/dev/null
|
|
discovery.type: single-node
|
|
path.data: /usr/share/opensearch/data
|
|
path.logs: /var/log/opensearch
|
|
plugins.security.disabled: true
|
|
EOF
|
|
_run_under_systemd opensearch "$OPENSEARCH_HOME/bin/opensearch"
|
|
}
|
|
|
|
# install_cloudkitty() - Collect source and prepare
|
|
function install_cloudkitty {
|
|
git_clone $CLOUDKITTY_REPO $CLOUDKITTY_DIR $CLOUDKITTY_BRANCH
|
|
setup_develop $CLOUDKITTY_DIR
|
|
|
|
if [ $CLOUDKITTY_STORAGE_BACKEND == 'influxdb' ]; then
|
|
install_influx
|
|
elif [ $CLOUDKITTY_STORAGE_BACKEND == 'elasticsearch' ]; then
|
|
install_opensearch
|
|
fi
|
|
}
|
|
|
|
# start_cloudkitty() - Start running processes, including screen
|
|
function start_cloudkitty {
|
|
run_process ck-proc "$CLOUDKITTY_BIN_DIR/cloudkitty-processor --config-file=$CLOUDKITTY_CONF"
|
|
if [[ "$CLOUDKITTY_USE_MOD_WSGI" == "False" ]]; then
|
|
run_process ck-api "$CLOUDKITTY_BIN_DIR/cloudkitty-api --config-file=$CLOUDKITTY_CONF"
|
|
elif is_service_enabled ck-api; then
|
|
enable_apache_site cloudkitty
|
|
echo_summary "Waiting 15s for cloudkitty-processor to authenticate against keystone before apache is restarted."
|
|
sleep 15s
|
|
restart_apache_server
|
|
fi
|
|
echo "Waiting for ck-api ($CLOUDKITTY_SERVICE_HOST:$CLOUDKITTY_SERVICE_PORT) to start..."
|
|
if ! wait_for_service $SERVICE_TIMEOUT $CLOUDKITTY_SERVICE_PROTOCOL://$CLOUDKITTY_SERVICE_HOST:$CLOUDKITTY_SERVICE_PORT; then
|
|
die $LINENO "ck-api did not start"
|
|
fi
|
|
}
|
|
|
|
# stop_cloudkitty() - Stop running processes
|
|
function stop_cloudkitty {
|
|
# Kill the cloudkitty screen windows
|
|
if is_service_enabled ck-proc ; then
|
|
stop_process ck-proc
|
|
fi
|
|
|
|
if is_service_enabled ck-api ; then
|
|
if [ "$CLOUDKITTY_USE_MOD_WSGI" == "True" ]; then
|
|
disable_apache_site cloudkitty
|
|
restart_apache_server
|
|
else
|
|
# Kill the cloudkitty screen windows
|
|
stop_process ck-api
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# install_python_cloudkittyclient() - Collect source and prepare
|
|
function install_python_cloudkittyclient {
|
|
# Install from git since we don't have a release (yet)
|
|
git_clone_by_name "python-cloudkittyclient"
|
|
setup_dev_lib "python-cloudkittyclient"
|
|
}
|
|
|
|
# install_cloudkitty_dashboard() - Collect source and prepare
|
|
function install_cloudkitty_dashboard {
|
|
# Install from git since we don't have a release (yet)
|
|
git_clone_by_name "cloudkitty-dashboard"
|
|
setup_dev_lib "cloudkitty-dashboard"
|
|
}
|
|
|
|
# update_horizon_static() - Update Horizon static files with CloudKitty's one
|
|
function update_horizon_static {
|
|
# Code taken from Horizon lib
|
|
# Setup alias for django-admin which could be different depending on distro
|
|
local django_admin
|
|
if type -p django-admin > /dev/null; then
|
|
django_admin=django-admin
|
|
else
|
|
django_admin=django-admin.py
|
|
fi
|
|
DJANGO_SETTINGS_MODULE=openstack_dashboard.settings \
|
|
$django_admin collectstatic --noinput
|
|
DJANGO_SETTINGS_MODULE=openstack_dashboard.settings \
|
|
$django_admin compress --force
|
|
restart_apache_server
|
|
}
|
|
|
|
# configure_cloudkitty_dashboard() - Set config files, create data dirs, etc
|
|
function configure_cloudkitty_dashboard {
|
|
sudo ln -s $CLOUDKITTY_ENABLED_DIR/_[0-9]*.py \
|
|
$CLOUDKITTY_HORIZON_ENABLED_DIR/
|
|
update_horizon_static
|
|
}
|
|
|
|
if is_service_enabled ck-api; then
|
|
if [[ "$1" == "stack" && "$2" == "install" ]]; then
|
|
echo_summary "Installing CloudKitty"
|
|
install_cloudkitty
|
|
install_python_cloudkittyclient
|
|
if is_service_enabled horizon; then
|
|
install_cloudkitty_dashboard
|
|
fi
|
|
cleanup_cloudkitty
|
|
elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then
|
|
echo_summary "Configuring CloudKitty"
|
|
configure_cloudkitty
|
|
if is_service_enabled horizon; then
|
|
configure_cloudkitty_dashboard
|
|
fi
|
|
if is_service_enabled key; then
|
|
create_cloudkitty_accounts
|
|
fi
|
|
|
|
elif [[ "$1" == "stack" && "$2" == "extra" ]]; then
|
|
# Initialize cloudkitty
|
|
echo_summary "Initializing CloudKitty"
|
|
init_cloudkitty
|
|
|
|
# Start the CloudKitty API and CloudKitty processor components
|
|
echo_summary "Starting CloudKitty"
|
|
start_cloudkitty
|
|
fi
|
|
|
|
if [[ "$1" == "unstack" ]]; then
|
|
stop_cloudkitty
|
|
fi
|
|
fi
|
|
|
|
# Restore xtrace
|
|
$XTRACE
|
|
|
|
# Local variables:
|
|
# mode: shell-script
|
|
# End:
|