blob: e46a2fc6e562f99d63c29f25b19f97d625c494c2 [file] [log] [blame]
Doug Hellmannf04178f2012-07-05 17:10:03 -04001# -*- mode: Shell-script -*-
Dean Troyer7f9aa712012-01-31 12:11:56 -06002# functions - Common functions used by DevStack components
Dean Troyer13dc5cc2012-03-27 14:50:45 -05003#
4# ENABLED_SERVICES is used by is_service_enabled()
5
Dean Troyer7f9aa712012-01-31 12:11:56 -06006
Dean Troyer27e32692012-03-16 16:16:56 -05007# Save trace setting
8XTRACE=$(set +o | grep xtrace)
9set +o xtrace
10
Dean Troyer7f9aa712012-01-31 12:11:56 -060011
Vishvananda Ishayac9ad14b2012-07-03 20:29:01 +000012# Exit 0 if address is in network or 1 if
13# address is not in network or netaddr library
14# is not installed.
15function address_in_net() {
16 python -c "
17import netaddr
18import sys
19sys.exit(netaddr.IPAddress('$1') not in netaddr.IPNetwork('$2'))
20"
21}
22
23
Dean Troyer7f9aa712012-01-31 12:11:56 -060024# apt-get wrapper to set arguments correctly
Dean Troyer13dc5cc2012-03-27 14:50:45 -050025# apt_get operation package [package ...]
Dean Troyer7f9aa712012-01-31 12:11:56 -060026function apt_get() {
Dean Troyerd0b21e22012-03-07 14:52:25 -060027 [[ "$OFFLINE" = "True" || -z "$@" ]] && return
Dean Troyer7f9aa712012-01-31 12:11:56 -060028 local sudo="sudo"
29 [[ "$(id -u)" = "0" ]] && sudo="env"
30 $sudo DEBIAN_FRONTEND=noninteractive \
31 http_proxy=$http_proxy https_proxy=$https_proxy \
32 apt-get --option "Dpkg::Options::=--force-confold" --assume-yes "$@"
33}
34
35
36# Gracefully cp only if source file/dir exists
37# cp_it source destination
38function cp_it {
39 if [ -e $1 ] || [ -d $1 ]; then
40 cp -pRL $1 $2
41 fi
42}
43
44
Dean Troyer27e32692012-03-16 16:16:56 -050045# Prints "message" and exits
46# die "message"
47function die() {
Dean Troyer489bd2a2012-03-02 10:44:29 -060048 local exitcode=$?
Dean Troyer27e32692012-03-16 16:16:56 -050049 set +o xtrace
50 echo $@
51 exit $exitcode
Dean Troyer489bd2a2012-03-02 10:44:29 -060052}
53
54
55# Checks an environment variable is not set or has length 0 OR if the
56# exit code is non-zero and prints "message" and exits
57# NOTE: env-var is the variable name without a '$'
58# die_if_not_set env-var "message"
59function die_if_not_set() {
Dean Troyer27e32692012-03-16 16:16:56 -050060 (
61 local exitcode=$?
62 set +o xtrace
63 local evar=$1; shift
64 if ! is_set $evar || [ $exitcode != 0 ]; then
65 set +o xtrace
66 echo $@
67 exit -1
68 fi
69 )
Dean Troyer489bd2a2012-03-02 10:44:29 -060070}
71
72
73# Grab a numbered field from python prettytable output
74# Fields are numbered starting with 1
75# Reverse syntax is supported: -1 is the last field, -2 is second to last, etc.
76# get_field field-number
77function get_field() {
78 while read data; do
79 if [ "$1" -lt 0 ]; then
80 field="(\$(NF$1))"
81 else
82 field="\$$(($1 + 1))"
83 fi
84 echo "$data" | awk -F'[ \t]*\\|[ \t]*' "{print $field}"
85 done
86}
87
88
Dean Troyer7e270512012-06-14 15:23:24 -050089# get_packages() collects a list of package names of any type from the
90# prerequisite files in ``files/{apts|pips}``. The list is intended
91# to be passed to a package installer such as apt or pip.
92#
93# Only packages required for the services in ENABLED_SERVICES will be
94# included. Two bits of metadata are recognized in the prerequisite files:
95# - ``# NOPRIME`` defers installation to be performed later in stack.sh
96# - ``# dist:DISTRO`` or ``dist:DISTRO1,DISTRO2`` limits the selection
97# of the package to the distros listed. The distro names are case insensitive.
98#
99# get_packages dir
100function get_packages() {
101 local package_dir=$1
102 local file_to_parse
103 local service
104
105 if [[ -z "$package_dir" ]]; then
106 echo "No package directory supplied"
107 return 1
108 fi
109 if [[ -z "$DISTRO" ]]; then
110 echo "No distro set in DISTRO"
111 return 1
112 fi
113 for service in general ${ENABLED_SERVICES//,/ }; do
114 # Allow individual services to specify dependencies
115 if [[ -e ${package_dir}/${service} ]]; then
116 file_to_parse="${file_to_parse} $service"
117 fi
118 # NOTE(sdague) n-api needs glance for now because that's where
119 # glance client is
120 if [[ $service == n-api ]]; then
121 if [[ ! $file_to_parse =~ nova ]]; then
122 file_to_parse="${file_to_parse} nova"
123 fi
124 if [[ ! $file_to_parse =~ glance ]]; then
125 file_to_parse="${file_to_parse} glance"
126 fi
127 elif [[ $service == c-* ]]; then
128 if [[ ! $file_to_parse =~ cinder ]]; then
129 file_to_parse="${file_to_parse} cinder"
130 fi
131 elif [[ $service == n-* ]]; then
132 if [[ ! $file_to_parse =~ nova ]]; then
133 file_to_parse="${file_to_parse} nova"
134 fi
135 elif [[ $service == g-* ]]; then
136 if [[ ! $file_to_parse =~ glance ]]; then
137 file_to_parse="${file_to_parse} glance"
138 fi
139 elif [[ $service == key* ]]; then
140 if [[ ! $file_to_parse =~ keystone ]]; then
141 file_to_parse="${file_to_parse} keystone"
142 fi
143 fi
144 done
145
146 for file in ${file_to_parse}; do
147 local fname=${package_dir}/${file}
148 local OIFS line package distros distro
149 [[ -e $fname ]] || continue
150
151 OIFS=$IFS
152 IFS=$'\n'
153 for line in $(<${fname}); do
154 if [[ $line =~ "NOPRIME" ]]; then
155 continue
156 fi
157
158 if [[ $line =~ (.*)#.*dist:([^ ]*) ]]; then
159 # We are using BASH regexp matching feature.
160 package=${BASH_REMATCH[1]}
161 distros=${BASH_REMATCH[2]}
162 # In bash ${VAR,,} will lowecase VAR
163 [[ ${distros,,} =~ ${DISTRO,,} ]] && echo $package
164 continue
165 fi
166
167 echo ${line%#*}
168 done
169 IFS=$OIFS
170 done
171}
172
173
Dean Troyer13dc5cc2012-03-27 14:50:45 -0500174# Determine OS Vendor, Release and Update
175# Tested with OS/X, Ubuntu, RedHat, CentOS, Fedora
176# Returns results in global variables:
177# os_VENDOR - vendor name
178# os_RELEASE - release
179# os_UPDATE - update
180# os_PACKAGE - package type
181# os_CODENAME - vendor's codename for release
182# GetOSVersion
183GetOSVersion() {
184 # Figure out which vendor we are
185 if [[ -n "`which sw_vers 2>/dev/null`" ]]; then
186 # OS/X
187 os_VENDOR=`sw_vers -productName`
188 os_RELEASE=`sw_vers -productVersion`
189 os_UPDATE=${os_RELEASE##*.}
190 os_RELEASE=${os_RELEASE%.*}
191 os_PACKAGE=""
192 if [[ "$os_RELEASE" =~ "10.7" ]]; then
193 os_CODENAME="lion"
194 elif [[ "$os_RELEASE" =~ "10.6" ]]; then
195 os_CODENAME="snow leopard"
196 elif [[ "$os_RELEASE" =~ "10.5" ]]; then
197 os_CODENAME="leopard"
198 elif [[ "$os_RELEASE" =~ "10.4" ]]; then
199 os_CODENAME="tiger"
200 elif [[ "$os_RELEASE" =~ "10.3" ]]; then
201 os_CODENAME="panther"
202 else
203 os_CODENAME=""
204 fi
205 elif [[ -x $(which lsb_release 2>/dev/null) ]]; then
206 os_VENDOR=$(lsb_release -i -s)
207 os_RELEASE=$(lsb_release -r -s)
208 os_UPDATE=""
209 if [[ "Debian,Ubuntu" =~ $os_VENDOR ]]; then
210 os_PACKAGE="deb"
211 else
212 os_PACKAGE="rpm"
213 fi
214 os_CODENAME=$(lsb_release -c -s)
215 elif [[ -r /etc/redhat-release ]]; then
216 # Red Hat Enterprise Linux Server release 5.5 (Tikanga)
217 # CentOS release 5.5 (Final)
218 # CentOS Linux release 6.0 (Final)
219 # Fedora release 16 (Verne)
220 os_CODENAME=""
221 for r in "Red Hat" CentOS Fedora; do
222 os_VENDOR=$r
223 if [[ -n "`grep \"$r\" /etc/redhat-release`" ]]; then
224 ver=`sed -e 's/^.* \(.*\) (\(.*\)).*$/\1\|\2/' /etc/redhat-release`
225 os_CODENAME=${ver#*|}
226 os_RELEASE=${ver%|*}
227 os_UPDATE=${os_RELEASE##*.}
228 os_RELEASE=${os_RELEASE%.*}
229 break
230 fi
231 os_VENDOR=""
232 done
233 os_PACKAGE="rpm"
234 fi
235 export os_VENDOR os_RELEASE os_UPDATE os_PACKAGE os_CODENAME
236}
237
238
Dean Troyera9e0a482012-07-09 14:07:23 -0500239# Translate the OS version values into common nomenclature
240# Sets ``DISTRO`` from the ``os_*`` values
241function GetDistro() {
242 GetOSVersion
243 if [[ "$os_VENDOR" =~ (Ubuntu) ]]; then
244 # 'Everyone' refers to Ubuntu releases by the code name adjective
245 DISTRO=$os_CODENAME
246 elif [[ "$os_VENDOR" =~ (Fedora) ]]; then
247 # For Fedora, just use 'f' and the release
248 DISTRO="f$os_RELEASE"
249 else
250 # Catch-all for now is Vendor + Release + Update
251 DISTRO="$os_VENDOR-$os_RELEASE.$os_UPDATE"
252 fi
253 export DISTRO
254}
255
256
Dean Troyer7f9aa712012-01-31 12:11:56 -0600257# git clone only if directory doesn't exist already. Since ``DEST`` might not
258# be owned by the installation user, we create the directory and change the
259# ownership to the proper user.
260# Set global RECLONE=yes to simulate a clone when dest-dir exists
James E. Blair94cb9602012-06-22 15:28:29 -0700261# Set global ERROR_ON_CLONE=True to abort execution with an error if the git repo
262# does not exist (default is False, meaning the repo will be cloned).
Dean Troyer7f9aa712012-01-31 12:11:56 -0600263# git_clone remote dest-dir branch
264function git_clone {
265 [[ "$OFFLINE" = "True" ]] && return
266
267 GIT_REMOTE=$1
268 GIT_DEST=$2
269 GIT_BRANCH=$3
270
271 if echo $GIT_BRANCH | egrep -q "^refs"; then
272 # If our branch name is a gerrit style refs/changes/...
273 if [[ ! -d $GIT_DEST ]]; then
James E. Blair94cb9602012-06-22 15:28:29 -0700274 [[ "$ERROR_ON_CLONE" = "True" ]] && exit 1
Dean Troyer7f9aa712012-01-31 12:11:56 -0600275 git clone $GIT_REMOTE $GIT_DEST
276 fi
277 cd $GIT_DEST
278 git fetch $GIT_REMOTE $GIT_BRANCH && git checkout FETCH_HEAD
279 else
280 # do a full clone only if the directory doesn't exist
281 if [[ ! -d $GIT_DEST ]]; then
James E. Blair94cb9602012-06-22 15:28:29 -0700282 [[ "$ERROR_ON_CLONE" = "True" ]] && exit 1
Dean Troyer7f9aa712012-01-31 12:11:56 -0600283 git clone $GIT_REMOTE $GIT_DEST
284 cd $GIT_DEST
285 # This checkout syntax works for both branches and tags
286 git checkout $GIT_BRANCH
287 elif [[ "$RECLONE" == "yes" ]]; then
288 # if it does exist then simulate what clone does if asked to RECLONE
289 cd $GIT_DEST
290 # set the url to pull from and fetch
291 git remote set-url origin $GIT_REMOTE
292 git fetch origin
293 # remove the existing ignored files (like pyc) as they cause breakage
294 # (due to the py files having older timestamps than our pyc, so python
295 # thinks the pyc files are correct using them)
296 find $GIT_DEST -name '*.pyc' -delete
297 git checkout -f origin/$GIT_BRANCH
298 # a local branch might not exist
299 git branch -D $GIT_BRANCH || true
300 git checkout -b $GIT_BRANCH
301 fi
302 fi
303}
304
305
Dean Troyer13dc5cc2012-03-27 14:50:45 -0500306# Comment an option in an INI file
Chmouel Boudjnahc7214e82012-06-06 13:56:39 +0200307# inicomment config-file section option
Dean Troyer13dc5cc2012-03-27 14:50:45 -0500308function inicomment() {
309 local file=$1
310 local section=$2
311 local option=$3
312 sed -i -e "/^\[$section\]/,/^\[.*\]/ s|^\($option[ \t]*=.*$\)|#\1|" $file
313}
314
Chmouel Boudjnahc7214e82012-06-06 13:56:39 +0200315# Uncomment an option in an INI file
316# iniuncomment config-file section option
317function iniuncomment() {
318 local file=$1
319 local section=$2
320 local option=$3
321 sed -i -e "/^\[$section\]/,/^\[.*\]/ s|[^ \t]*#[ \t]*\($option[ \t]*=.*$\)|\1|" $file
322}
323
Dean Troyer13dc5cc2012-03-27 14:50:45 -0500324
325# Get an option from an INI file
Dean Troyer09e636e2012-03-19 16:31:12 -0500326# iniget config-file section option
Dean Troyer13dc5cc2012-03-27 14:50:45 -0500327function iniget() {
328 local file=$1
329 local section=$2
330 local option=$3
331 local line
332 line=$(sed -ne "/^\[$section\]/,/^\[.*\]/ { /^$option[ \t]*=/ p; }" $file)
333 echo ${line#*=}
334}
335
336
337# Set an option in an INI file
Dean Troyer09e636e2012-03-19 16:31:12 -0500338# iniset config-file section option value
Dean Troyer13dc5cc2012-03-27 14:50:45 -0500339function iniset() {
340 local file=$1
341 local section=$2
342 local option=$3
343 local value=$4
Dean Troyer09e636e2012-03-19 16:31:12 -0500344 if ! grep -q "^\[$section\]" $file; then
345 # Add section at the end
346 echo -e "\n[$section]" >>$file
347 fi
348 if [[ -z "$(iniget $file $section $option)" ]]; then
349 # Add it
350 sed -i -e "/^\[$section\]/ a\\
351$option = $value
352" $file
353 else
354 # Replace it
355 sed -i -e "/^\[$section\]/,/^\[.*\]/ s|^\($option[ \t]*=[ \t]*\).*$|\1$value|" $file
356 fi
Dean Troyer13dc5cc2012-03-27 14:50:45 -0500357}
358
359
Chmouel Boudjnah408b0092012-03-15 23:21:55 +0000360# is_service_enabled() checks if the service(s) specified as arguments are
361# enabled by the user in **ENABLED_SERVICES**.
362#
363# If there are multiple services specified as arguments the test performs a
364# boolean OR or if any of the services specified on the command line
365# return true.
366#
367# There is a special cases for some 'catch-all' services::
368# **nova** returns true if any service enabled start with **n-**
369# **glance** returns true if any service enabled start with **g-**
370# **quantum** returns true if any service enabled start with **q-**
371function is_service_enabled() {
372 services=$@
373 for service in ${services}; do
374 [[ ,${ENABLED_SERVICES}, =~ ,${service}, ]] && return 0
375 [[ ${service} == "nova" && ${ENABLED_SERVICES} =~ "n-" ]] && return 0
Dean Troyer67787e62012-05-02 11:48:15 -0500376 [[ ${service} == "cinder" && ${ENABLED_SERVICES} =~ "c-" ]] && return 0
Chmouel Boudjnah408b0092012-03-15 23:21:55 +0000377 [[ ${service} == "glance" && ${ENABLED_SERVICES} =~ "g-" ]] && return 0
378 [[ ${service} == "quantum" && ${ENABLED_SERVICES} =~ "q-" ]] && return 0
379 done
380 return 1
381}
382
Doug Hellmannf04178f2012-07-05 17:10:03 -0400383# remove extra commas from the input string (ENABLED_SERVICES)
384function _cleanup_service_list () {
385 echo "$1" | sed -e '
386 s/,,/,/g;
387 s/^,//;
388 s/,$//
389 '
390}
391
392# enable_service() adds the services passed as argument to the
393# **ENABLED_SERVICES** list, if they are not already present.
394#
395# For example:
396#
397# enable_service n-vol
398#
399# This function does not know about the special cases
400# for nova, glance, and quantum built into is_service_enabled().
401function enable_service() {
402 local tmpsvcs="${ENABLED_SERVICES}"
403 for service in $@; do
404 if ! is_service_enabled $service; then
405 tmpsvcs+=",$service"
406 fi
407 done
408 ENABLED_SERVICES=$(_cleanup_service_list "$tmpsvcs")
409 disable_negated_services
410}
411
412# disable_service() removes the services passed as argument to the
413# **ENABLED_SERVICES** list, if they are present.
414#
415# For example:
416#
417# disable_service n-vol
418#
419# This function does not know about the special cases
420# for nova, glance, and quantum built into is_service_enabled().
421function disable_service() {
422 local tmpsvcs=",${ENABLED_SERVICES},"
423 local service
424 for service in $@; do
425 if is_service_enabled $service; then
426 tmpsvcs=${tmpsvcs//,$service,/,}
427 fi
428 done
429 ENABLED_SERVICES=$(_cleanup_service_list "$tmpsvcs")
430}
431
432# disable_all_services() removes all current services
433# from **ENABLED_SERVICES** to reset the configuration
434# before a minimal installation
435function disable_all_services() {
436 ENABLED_SERVICES=""
437}
438
439# We are looking for services with a - at the beginning to force
440# excluding those services. For example if you want to install all the default
441# services but not nova-volume (n-vol) you can have this set in your localrc :
442# ENABLED_SERVICES+=",-n-vol"
443function disable_negated_services() {
444 local tmpsvcs="${ENABLED_SERVICES}"
445 local service
446 for service in ${tmpsvcs//,/ }; do
447 if [[ ${service} == -* ]]; then
448 tmpsvcs=$(echo ${tmpsvcs}|sed -r "s/(,)?(-)?${service#-}(,)?/,/g")
449 fi
450 done
451 ENABLED_SERVICES=$(_cleanup_service_list "$tmpsvcs")
452}
Dean Troyer489bd2a2012-03-02 10:44:29 -0600453
Dean Troyer13dc5cc2012-03-27 14:50:45 -0500454# Distro-agnostic package installer
455# install_package package [package ...]
456function install_package() {
457 if [[ -z "$os_PACKAGE" ]]; then
458 GetOSVersion
459 fi
460 if [[ "$os_PACKAGE" = "deb" ]]; then
461 apt_get install "$@"
462 else
463 yum_install "$@"
464 fi
465}
466
467
Dean Troyer489bd2a2012-03-02 10:44:29 -0600468# Test if the named environment variable is set and not zero length
469# is_set env-var
470function is_set() {
471 local var=\$"$1"
472 if eval "[ -z $var ]"; then
473 return 1
474 fi
475 return 0
476}
477
478
Dean Troyer7f9aa712012-01-31 12:11:56 -0600479# pip install wrapper to set cache and proxy environment variables
480# pip_install package [package ...]
481function pip_install {
Dean Troyerd0b21e22012-03-07 14:52:25 -0600482 [[ "$OFFLINE" = "True" || -z "$@" ]] && return
Dean Troyer13dc5cc2012-03-27 14:50:45 -0500483 if [[ -z "$os_PACKAGE" ]]; then
484 GetOSVersion
485 fi
486 if [[ "$os_PACKAGE" = "deb" ]]; then
487 CMD_PIP=/usr/bin/pip
488 else
489 CMD_PIP=/usr/bin/pip-python
490 fi
Doug Hellmann4d5e29d2012-07-13 11:11:50 -0400491 sudo PIP_DOWNLOAD_CACHE=${PIP_DOWNLOAD_CACHE:-/var/cache/pip} \
Dean Troyer7f9aa712012-01-31 12:11:56 -0600492 HTTP_PROXY=$http_proxy \
493 HTTPS_PROXY=$https_proxy \
Dean Troyer13dc5cc2012-03-27 14:50:45 -0500494 $CMD_PIP install --use-mirrors $@
495}
496
497
498# Service wrapper to restart services
499# restart_service service-name
500function restart_service() {
Dean Troyer5218d452012-02-04 02:13:23 -0600501 if [[ -z "$os_PACKAGE" ]]; then
502 GetOSVersion
503 fi
504 if [[ "$os_PACKAGE" = "deb" ]]; then
505 sudo /usr/sbin/service $1 restart
506 else
507 sudo /sbin/service $1 restart
508 fi
Dean Troyer13dc5cc2012-03-27 14:50:45 -0500509}
510
511
Dean Troyerbbafb1b2012-06-11 16:51:39 -0500512# pip install the dependencies of the package before we do the setup.py
513# develop, so that pip and not distutils process the dependency chain
514# setup_develop directory
515function setup_develop() {
516 (cd $1; \
517 python setup.py egg_info; \
518 raw_links=$(awk '/^.+/ {print "-f " $1}' *.egg-info/dependency_links.txt); \
519 depend_links=$(echo $raw_links | xargs); \
520 pip_install -r *-info/requires.txt $depend_links; \
521 sudo \
522 HTTP_PROXY=$http_proxy \
523 HTTPS_PROXY=$https_proxy \
524 python setup.py develop \
525 )
526}
527
528
Dean Troyer13dc5cc2012-03-27 14:50:45 -0500529# Service wrapper to start services
530# start_service service-name
531function start_service() {
Dean Troyer5218d452012-02-04 02:13:23 -0600532 if [[ -z "$os_PACKAGE" ]]; then
533 GetOSVersion
534 fi
535 if [[ "$os_PACKAGE" = "deb" ]]; then
536 sudo /usr/sbin/service $1 start
537 else
538 sudo /sbin/service $1 start
539 fi
Dean Troyer13dc5cc2012-03-27 14:50:45 -0500540}
541
542
543# Service wrapper to stop services
544# stop_service service-name
545function stop_service() {
Dean Troyer5218d452012-02-04 02:13:23 -0600546 if [[ -z "$os_PACKAGE" ]]; then
547 GetOSVersion
548 fi
549 if [[ "$os_PACKAGE" = "deb" ]]; then
550 sudo /usr/sbin/service $1 stop
551 else
552 sudo /sbin/service $1 stop
553 fi
Dean Troyer7f9aa712012-01-31 12:11:56 -0600554}
555
556
557# Normalize config values to True or False
558# VAR=`trueorfalse default-value test-value`
559function trueorfalse() {
560 local default=$1
561 local testval=$2
562
563 [[ -z "$testval" ]] && { echo "$default"; return; }
564 [[ "0 no false False FALSE" =~ "$testval" ]] && { echo "False"; return; }
565 [[ "1 yes true True TRUE" =~ "$testval" ]] && { echo "True"; return; }
566 echo "$default"
567}
Dean Troyer27e32692012-03-16 16:16:56 -0500568
Dean Troyer13dc5cc2012-03-27 14:50:45 -0500569
570# yum wrapper to set arguments correctly
571# yum_install package [package ...]
572function yum_install() {
573 [[ "$OFFLINE" = "True" ]] && return
574 local sudo="sudo"
575 [[ "$(id -u)" = "0" ]] && sudo="env"
576 $sudo http_proxy=$http_proxy https_proxy=$https_proxy \
577 yum install -y "$@"
578}
579
580
Dean Troyer27e32692012-03-16 16:16:56 -0500581# Restore xtrace
Chmouel Boudjnah408b0092012-03-15 23:21:55 +0000582$XTRACE