Revision f154a7a3

b/.gitignore
31 31
/ganeti-[0-9]*.[0-9]*.[0-9]*
32 32

  
33 33
# daemons
34
/daemons/daemon-util
34 35
/daemons/ganeti-cleaner
35 36

  
36 37
# devel
b/Makefile.am
53 53

  
54 54
CLEANFILES = \
55 55
	autotools/replace_vars.sed \
56
	daemons/daemon-util \
56 57
	daemons/ganeti-cleaner \
57 58
	devel/upload \
58 59
	doc/examples/bash_completion \
......
220 221
	tools/cfgupgrade \
221 222
	tools/lvmstrap
222 223

  
224
pkglib_SCRIPTS = \
225
	daemons/daemon-util
226

  
223 227
EXTRA_DIST = \
224 228
	NEWS \
225 229
	pylintrc \
......
227 231
	autotools/check-python-code \
228 232
	autotools/docbook-wrapper \
229 233
	$(RUN_IN_TEMPDIR) \
234
	daemons/daemon-util.in \
230 235
	daemons/ganeti-cleaner.in \
231 236
	devel/upload.in \
232 237
	$(docdot) \
......
342 347
	sed -f $(REPLACE_VARS_SED) < $< > $@
343 348
	chmod u+x $@
344 349

  
345
daemons/ganeti-cleaner: daemons/ganeti-cleaner.in \
350
daemons/%: daemons/%.in \
346 351
		$(REPLACE_VARS_SED)
347 352
	sed -f $(REPLACE_VARS_SED) < $< > $@
348 353
	chmod +x $@
......
418 423
	  echo "LVM_STRIPECOUNT = $(LVM_STRIPECOUNT)"; \
419 424
	  echo "TOOLSDIR = '$(toolsdir)'"; \
420 425
	  echo "GNT_SCRIPTS = [$(foreach i,$(notdir $(gnt_scripts)),'$(i)',)]"; \
426
	  echo "PKGLIBDIR = '$(pkglibdir)'"; \
421 427
	} > $@
422 428

  
423 429
$(REPLACE_VARS_SED): Makefile
b/NEWS
72 72
  (``rapi_users``)
73 73
- Added option to specify maximum timeout on instance shutdown
74 74
- Added ``--no-ssh-init`` option to ``gnt-cluster init``
75
- Added new helper script to start and stop Ganeti daemons
76
  (``daemon-util``), with the intent to reduce the work necessary to
77
  adjust Ganeti for non-Debian distributions and to start/stop daemons
78
  from one place
75 79
- Added more unittests
76 80
- Fixed critical bug in ganeti-masterd startup
77 81
- Pass ``INSTANCE_REINSTALL`` variable to OS installation script when
b/daemons/daemon-util.in
1
#!/bin/bash
2
#
3

  
4
# Copyright (C) 2009 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

  
21
set -e
22

  
23
defaults_file=@SYSCONFDIR@/default/ganeti
24

  
25
NODED_ARGS=
26
MASTERD_ARGS=
27
CONFD_ARGS=
28
RAPI_ARGS=
29

  
30
# Read defaults file if it exists
31
if [[ -s $defaults_file ]]; then
32
  . $defaults_file
33
fi
34

  
35
_daemon_pidfile() {
36
  echo "@LOCALSTATEDIR@/run/ganeti/$1.pid"
37
}
38

  
39
# Checks whether daemon is running
40
check() {
41
  if [[ "$#" -lt 1 ]]; then
42
    echo 'Missing daemon name.' >&2
43
    exit 1
44
  fi
45

  
46
  local name="$1"; shift
47

  
48
  start-stop-daemon --stop --signal 0 --quiet \
49
    --pidfile $(_daemon_pidfile $name)
50
}
51

  
52
# Starts a daemon
53
start() {
54
  if [[ "$#" -lt 1 ]]; then
55
    echo 'Missing daemon name.' >&2
56
    exit 1
57
  fi
58

  
59
  local name="$1"; shift
60

  
61
  # Convert daemon name to uppercase after removing "ganeti-" prefix
62
  local ucname=$(tr a-z A-Z <<< ${name#ganeti-})
63

  
64
  # Read $<daemon>_ARGS and $EXTRA_<daemon>_ARGS
65
  eval local args="\$${ucname}_ARGS \$EXTRA_${ucname}_ARGS"
66

  
67
  start-stop-daemon --start --quiet --oknodo \
68
    --pidfile $(_daemon_pidfile $name) \
69
    --startas "@PREFIX@/sbin/$name" \
70
    -- $args "$@"
71
}
72

  
73
# Stops a daemon
74
stop() {
75
  if [[ "$#" -lt 1 ]]; then
76
    echo 'Missing daemon name.' >&2
77
    exit 1
78
  fi
79

  
80
  local name="$1"; shift
81

  
82
  start-stop-daemon --stop --quiet --oknodo --retry 30 \
83
    --pidfile $(_daemon_pidfile $name)
84
}
85

  
86
# Starts a daemon if it's not yet running
87
check_and_start() {
88
  local name="$1"
89

  
90
  if ! check $name; then
91
    start $name
92
  fi
93
}
94

  
95
# Starts the master role
96
start_master() {
97
  start ganeti-masterd
98
  start ganeti-rapi
99
}
100

  
101
# Stops the master role
102
stop_master() {
103
  stop ganeti-rapi
104
  stop ganeti-masterd
105
}
106

  
107
if [[ "$#" -lt 1 ]]; then
108
  echo "Usage: $0 <action>" >&2
109
  exit 1
110
fi
111

  
112
orig_action=$1; shift
113

  
114
# Replace all dashes (-) with underlines (_)
115
action=${orig_action//-/_}
116

  
117
# Is it a known function?
118
if ! declare -F "$action" >/dev/null 2>&1; then
119
  echo "Unknown command: $orig_action" >&2
120
  exit 1
121
fi
122

  
123
# Call handler function
124
$action "$@"
b/daemons/ganeti-watcher
78 78
  return bool(utils.ReadWatcherPauseFile(constants.WATCHER_PAUSEFILE))
79 79

  
80 80

  
81
def StartMaster():
82
  """Try to start the master daemon.
81
def EnsureDaemon(name):
82
  """Check for and start daemon if not alive.
83 83

  
84 84
  """
85
  result = utils.RunCmd(['ganeti-masterd'])
85
  result = utils.RunCmd([constants.DAEMON_UTIL, "check-and-start", name])
86 86
  if result.failed:
87
    logging.error("Can't start the master daemon: output '%s'", result.output)
88
  return not result.failed
89

  
87
    logging.error("Can't start daemon '%s', failure %s, output: %s",
88
                  name, result.fail_reason, result.output)
89
    return False
90 90

  
91
def EnsureDaemon(daemon):
92
  """Check for and start daemon if not alive.
93

  
94
  """
95
  pidfile = utils.DaemonPidFileName(daemon)
96
  pid = utils.ReadPidFile(pidfile)
97
  if pid == 0 or not utils.IsProcessAlive(pid): # no file or dead pid
98
    logging.debug("Daemon '%s' not alive, trying to restart", daemon)
99
    result = utils.RunCmd([daemon])
100
    if not result:
101
      logging.error("Can't start daemon '%s', failure %s, output: %s",
102
                    daemon, result.fail_reason, result.output)
91
  return True
103 92

  
104 93

  
105 94
class WatcherState(object):
......
503 492
      except luxi.NoMasterError, err:
504 493
        logging.warning("Master seems to be down (%s), trying to restart",
505 494
                        str(err))
506
        if not StartMaster():
495
        if not EnsureDaemon(constants.MASTERD):
507 496
          logging.critical("Can't start the master, exiting")
508 497
          sys.exit(constants.EXIT_FAILURE)
509 498
        # else retry the connection
b/doc/examples/ganeti.initd.in
14 14
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
15 15
DESC="Ganeti cluster"
16 16

  
17
GANETIRUNDIR="@LOCALSTATEDIR@/run/ganeti"
18

  
19
GANETI_DEFAULTS_FILE="@SYSCONFDIR@/default/ganeti"
20

  
21 17
NODED="ganeti-noded"
22
NODED_ARGS=""
23

  
24 18
MASTERD="ganeti-masterd"
25
MASTERD_ARGS=""
26

  
27 19
CONFD="ganeti-confd"
28
CONFD_ARGS=""
29

  
30 20
RAPI="ganeti-rapi"
31
RAPI_ARGS=""
21

  
22
DAEMON_UTIL=@PKGLIBDIR@/daemon-util
32 23

  
33 24
SCRIPTNAME="@SYSCONFDIR@/init.d/ganeti"
34 25

  
......
36 27

  
37 28
. /lib/lsb/init-functions
38 29

  
39
if [ -s $GANETI_DEFAULTS_FILE ]; then
40
    . $GANETI_DEFAULTS_FILE
41
fi
42

  
43 30
check_config() {
44 31
    for fname in \
45 32
        "@LOCALSTATEDIR@/lib/ganeti/server.pem"
......
69 56

  
70 57
start_action() {
71 58
    # called as start_action daemon-name
72
    local daemon="$1"; shift
59
    local daemon="$1"
73 60
    log_action_begin_msg "$daemon"
74
    start-stop-daemon --start --quiet \
75
        --pidfile "${GANETIRUNDIR}/${daemon}.pid" \
76
        --startas "@PREFIX@/sbin/$daemon" \
77
        --oknodo \
78
        -- "$@"
61
    $DAEMON_UTIL start "$@"
79 62
    check_exitcode $?
80 63
}
81 64

  
......
83 66
    # called as stop_action daemon-name
84 67
    local daemon="$1"
85 68
    log_action_begin_msg "$daemon"
86
    start-stop-daemon --stop --quiet --oknodo \
87
        --retry 30 --pidfile "${GANETIRUNDIR}/${daemon}.pid"
69
    $DAEMON_UTIL stop "$@"
88 70
    check_exitcode $?
89 71
}
90 72

  
......
97 79
    fi
98 80
}
99 81

  
82
start_all() {
83
    check_config
84
    for i in $NODED $MASTERD $CONFD $RAPI; do \
85
        maybe_do "$1" stop_action $i
86
    done
87
}
88

  
89
stop_all() {
90
    for i in $RAPI $CONFD $MASTERD $NODED; do \
91
        maybe_do "$1" stop_action $i
92
    done
93
}
94

  
100 95
if [ -n "$2" -a \
101 96
    "$2" != "$NODED" -a \
102 97
    "$2" != "$CONFD" -a \
......
109 104
case "$1" in
110 105
    start)
111 106
        log_daemon_msg "Starting $DESC" "$2"
112
        check_config
113
        maybe_do "$2" start_action $NODED $NODED_ARGS
114
        maybe_do "$2" start_action $MASTERD $MASTERD_ARGS
115
        maybe_do "$2" start_action $CONFD $CONFD_ARGS
116
        maybe_do "$2" start_action $RAPI $RAPI_ARGS
107
        start_all "$2"
117 108
        ;;
118 109
    stop)
119 110
        log_daemon_msg "Stopping $DESC" "$2"
120
        maybe_do "$2" stop_action $RAPI
121
        maybe_do "$2" stop_action $CONFD
122
        maybe_do "$2" stop_action $MASTERD
123
        maybe_do "$2" stop_action $NODED
111
        stop_all "$2"
124 112
        ;;
125 113
    restart|force-reload)
126
        maybe_do "$2" stop_action $RAPI
127
        maybe_do "$2" stop_action $CONFD
128
        maybe_do "$2" stop_action $MASTERD
129
        maybe_do "$2" stop_action $NODED
130
        check_config
131
        maybe_do "$2" start_action $NODED $NODED_ARGS
132
        maybe_do "$2" start_action $MASTERD $MASTERD_ARGS
133
        maybe_do "$2" start_action $CONFD $CONFD_ARGS
134
        maybe_do "$2" start_action $RAPI $RAPI_ARGS
114
        stop_all "$2"
115
        start_all "$2"
135 116
        ;;
136 117
    *)
137 118
        log_success_msg "Usage: $SCRIPTNAME {start|stop|force-reload|restart}"
b/lib/backend.py
255 255

  
256 256
  # and now start the master and rapi daemons
257 257
  if start_daemons:
258
    daemons_params = {
259
        'ganeti-masterd': [],
260
        'ganeti-rapi': [],
261
        }
262 258
    if no_voting:
263
      daemons_params['ganeti-masterd'].append('--no-voting')
264
      daemons_params['ganeti-masterd'].append('--yes-do-it')
265
    for daemon in daemons_params:
266
      cmd = [daemon]
267
      cmd.extend(daemons_params[daemon])
268
      result = utils.RunCmd(cmd)
269
      if result.failed:
270
        msg = "Can't start daemon %s: %s" % (daemon, result.output)
271
        logging.error(msg)
272
        err_msgs.append(msg)
259
      masterd_args = "--no-voting --yes-do-it"
260
    else:
261
      masterd_args = ""
262

  
263
    env = {
264
      "EXTRA_MASTERD_ARGS": masterd_args,
265
      }
266

  
267
    result = utils.RunCmd([constants.DAEMON_UTIL, "start-master"], env=env)
268
    if result.failed:
269
      msg = "Can't start Ganeti master: %s" % result.output
270
      logging.error(msg)
271
      err_msgs.append(msg)
273 272

  
274 273
  if err_msgs:
275 274
    _Fail("; ".join(err_msgs))
......
301 300
    # but otherwise ignore the failure
302 301

  
303 302
  if stop_daemons:
304
    # stop/kill the rapi and the master daemon
305
    for daemon in constants.RAPI, constants.MASTERD:
306
      utils.KillProcess(utils.ReadPidFile(utils.DaemonPidFileName(daemon)))
303
    result = utils.RunCmd([constants.DAEMON_UTIL, "stop-master"])
304
    if result.failed:
305
      logging.error("Could not stop Ganeti master, command %s had exitcode %s"
306
                    " and error %s",
307
                    result.cmd, result.exit_code, result.output)
307 308

  
308 309

  
309 310
def AddNode(dsa, dsapub, rsa, rsapub, sshkey, sshpub):
......
385 386
  except:
386 387
    logging.exception("Error while removing cluster secrets")
387 388

  
388
  confd_pid = utils.ReadPidFile(utils.DaemonPidFileName(constants.CONFD))
389

  
390
  if confd_pid:
391
    utils.KillProcess(confd_pid, timeout=2)
389
  result = utils.RunCmd([constants.DAEMON_UTIL, "stop", constants.CONFD])
390
  if result.failed:
391
    logging.error("Command %s failed with exitcode %s and error %s",
392
                  result.cmd, result.exit_code, result.output)
392 393

  
393 394
  # Raise a custom exception (handled in ganeti-noded)
394 395
  raise errors.QuitGanetiException(True, 'Shutdown scheduled')
......
2435 2436
  master, myself = ssconf.GetMasterAndMyself()
2436 2437
  if master == myself:
2437 2438
    _Fail("ssconf status shows I'm the master node, will not demote")
2438
  pid_file = utils.DaemonPidFileName(constants.MASTERD)
2439
  if utils.IsProcessAlive(utils.ReadPidFile(pid_file)):
2439

  
2440
  result = utils.RunCmd([constants.DAEMON_UTIL, "check", constants.MASTERD])
2441
  if not result.failed:
2440 2442
    _Fail("The master daemon is running, will not demote")
2443

  
2441 2444
  try:
2442 2445
    if os.path.isfile(constants.CLUSTER_CONF_FILE):
2443 2446
      utils.CreateBackup(constants.CLUSTER_CONF_FILE)
2444 2447
  except EnvironmentError, err:
2445 2448
    if err.errno != errno.ENOENT:
2446 2449
      _Fail("Error while backing up cluster file: %s", err, exc=True)
2450

  
2447 2451
  utils.RemoveFile(constants.CLUSTER_CONF_FILE)
2448 2452

  
2449 2453

  
b/lib/bootstrap.py
126 126
  if not os.path.exists(constants.HMAC_CLUSTER_KEY):
127 127
    GenerateHmacKey(constants.HMAC_CLUSTER_KEY)
128 128

  
129
  result = utils.RunCmd([constants.NODE_INITD_SCRIPT, "restart"])
130

  
129
  result = utils.RunCmd([constants.DAEMON_UTIL, "start", constants.NODED])
131 130
  if result.failed:
132 131
    raise errors.OpExecError("Could not start the node daemon, command %s"
133 132
                             " had exitcode %s and error %s" %
......
241 240
                               (master_netdev,
242 241
                                result.output.strip()), errors.ECODE_INVAL)
243 242

  
244
  if not (os.path.isfile(constants.NODE_INITD_SCRIPT) and
245
          os.access(constants.NODE_INITD_SCRIPT, os.X_OK)):
246
    raise errors.OpPrereqError("Init.d script '%s' missing or not"
247
                               " executable." % constants.NODE_INITD_SCRIPT,
248
                               errors.ECODE_ENVIRON)
249

  
250 243
  dirs = [(constants.RUN_GANETI_DIR, constants.RUN_DIRS_MODE)]
251 244
  utils.EnsureDirs(dirs)
252 245

  
......
416 409
               "cat > '%s' << '!EOF.' && \n"
417 410
               "%s!EOF.\n"
418 411
               "chmod 0400 %s %s %s && "
419
               "%s restart" %
412
               "%s start %s" %
420 413
               (constants.SSL_CERT_FILE, noded_cert,
421 414
                constants.RAPI_CERT_FILE, rapi_cert,
422 415
                constants.HMAC_CLUSTER_KEY, hmac_key,
423 416
                constants.SSL_CERT_FILE, constants.RAPI_CERT_FILE,
424 417
                constants.HMAC_CLUSTER_KEY,
425
                constants.NODE_INITD_SCRIPT))
418
                constants.DAEMON_UTIL, constants.NODED))
426 419

  
427 420
  result = sshrunner.Run(node, 'root', mycommand, batch=False,
428 421
                         ask_key=ssh_key_check,
b/lib/constants.py
104 104
SSH_KNOWN_HOSTS_FILE = DATA_DIR + "/known_hosts"
105 105
RAPI_USERS_FILE = DATA_DIR + "/rapi_users"
106 106
QUEUE_DIR = DATA_DIR + "/queue"
107
DAEMON_UTIL = _autoconf.PKGLIBDIR + "/daemon-util"
107 108
ETC_HOSTS = "/etc/hosts"
108 109
DEFAULT_FILE_STORAGE_DIR = _autoconf.FILE_STORAGE_DIR
109 110
SYSCONFDIR = _autoconf.SYSCONFDIR
......
112 113

  
113 114
MASTER_SOCKET = SOCKET_DIR + "/ganeti-master"
114 115

  
115
NODE_INITD_SCRIPT = SYSCONFDIR + "/init.d/ganeti"
116

  
117 116
NODED = "ganeti-noded"
118 117
CONFD = "ganeti-confd"
119 118
RAPI = "ganeti-rapi"

Also available in: Unified diff