Make the ganeti-watcher exit gracefully if it's not run on the master.
[ganeti-local] / daemons / ganeti-master
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2006, 2007 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
22 """Ganeti master script
23
24 Exit codes, for both start and stop:
25   - 0: master setup successful
26   - 1: some generic error (this exit code can also be thrown by exceptions)
27   - 11: node is not master, nothing to do
28   - 12: node setup incomplete, cannot start
29   - 13: node should be master, but someone has the ip address already
30
31 Only exit codes 0 and 11 represent an ok state. Code 1 was left for
32 generic errors as other python code can cause exit with code 1.
33
34 """
35
36 import os
37 import sys
38 import socket
39
40 from optparse import OptionParser
41
42 from ganeti import logger
43 from ganeti import constants
44 from ganeti import errors
45 from ganeti import ssconf
46 from ganeti import utils
47
48 EXIT_OK = 0
49 EXIT_SOME_ERROR = 1
50 EXIT_NOTMASTER = constants.EXIT_NOTMASTER
51 EXIT_NODESETUP_ERROR = 12
52 EXIT_DUPLICATE_IP = 13
53 EXIT_ARGS_ERROR = 14
54
55
56 def ParseOptions():
57   """Parse the command line options.
58
59   Returns:
60     (options, args) as from OptionParser.parse_args()
61
62   """
63   parser = OptionParser(description="Ganeti master",
64                         usage="%prog [-d]",
65                         version="%%prog (ganeti) %s" %
66                         constants.RELEASE_VERSION)
67
68   parser.add_option("-d", "--debug", dest="debug",
69                     help="Enable some debug messages",
70                     default=False, action="store_true")
71   options, args = parser.parse_args()
72
73   if len(args) != 1 or args[0] not in ("start", "stop"):
74     sys.stderr.write("Usage: %s [-d] start|stop\n" % sys.argv[0])
75     sys.exit(EXIT_ARGS_ERROR)
76
77   return options, args
78
79
80 def CheckNodeSetup(debug):
81   """Checks the node setup.
82
83   If the node setup if ok, this function will return the tuple
84   (master_hostname, master_netdev, master_ip). Otherwise the return
85   value will be None.
86
87   """
88   for fname in (constants.SSL_CERT_FILE,):
89     if not os.path.isfile(fname):
90       if debug:
91         sys.stderr.write("Missing config file %s.\n" % fname)
92       return None
93   try:
94     ss = ssconf.SimpleStore()
95     port = ss.GetNodeDaemonPort()
96     pwdata = ss.GetNodeDaemonPassword()
97     master_name = ss.GetMasterNode()
98     master_netdev = ss.GetMasterNetdev()
99     master_ip = ss.GetMasterIP()
100   except errors.ConfigurationError, err:
101     if debug:
102       sys.stderr.write("Cluster configuration incomplete: '%s'\n" % str(err))
103     return None
104   return (master_name, master_netdev, master_ip)
105
106
107 def StartMaster(master_netdev, master_ip, debug):
108   """Starts the master.
109
110   """
111   result = utils.RunCmd(["fping", "-q", master_ip])
112   if not result.failed:
113     r2 = utils.RunCmd(["fping", "-q", "-S127.0.0.1", master_ip])
114     if not result.failed:
115       # we already have the ip:
116       if debug:
117         sys.stderr.write("Notice: already started.\n")
118       return EXIT_OK
119     else:
120       return EXIT_DUPLICATE_IP
121   result = utils.RunCmd(["ip", "address", "add", "%s/32" % master_ip,
122                          "dev", master_netdev, "label",
123                          "%s:0" % master_netdev])
124   if result.failed:
125     if debug:
126       sys.stderr.write("Can't activate master IP: %s\n" % result.output)
127     return EXIT_SOME_ERROR
128
129   result = utils.RunCmd(["arping", "-q", "-U", "-c 3", "-I", master_netdev,
130                          "-s", master_ip, master_ip])
131   # we'll ignore the exit code of arping
132   return EXIT_OK
133
134
135 def StopMaster(master_netdev, master_ip, debug):
136   """Stops the master.
137
138   """
139   result = utils.RunCmd(["ip", "address", "del", "%s/32" % master_ip,
140                          "dev", master_netdev])
141   if result.failed:
142     if debug:
143       sys.stderr.write("Can't remove the master IP, error: %s" % result.output)
144     # but otherwise ignore the failure
145   return EXIT_OK
146
147
148 def main():
149   """Main function.
150
151   """
152   options, args = ParseOptions()
153   debug = options.debug
154   result = CheckNodeSetup(debug)
155   if not result:
156     if debug:
157       sys.stderr.write("Node configuration incomplete.\n")
158     return EXIT_NODESETUP_ERROR
159
160   master_node, master_netdev, master_ip = result
161   if socket.gethostname() != master_node and args[0] == "start":
162     if debug:
163       sys.stderr.write("Not master, ignoring request.\n")
164     return EXIT_NOTMASTER
165
166   if args[0] == "start":
167     fn = StartMaster
168   else:
169     fn = StopMaster
170
171   return fn(master_netdev, master_ip, debug)
172
173
174 if __name__=='__main__':
175   exit_code = main()
176   sys.exit(exit_code)