Disable twisted signal handlers for remote API daemon
[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
39 from optparse import OptionParser
40
41 from ganeti import constants
42 from ganeti import errors
43 from ganeti import ssconf
44 from ganeti import utils
45
46 EXIT_OK = 0
47 EXIT_SOME_ERROR = 1
48 EXIT_NOTMASTER = constants.EXIT_NOTMASTER
49 EXIT_NODESETUP_ERROR = constants.EXIT_NODESETUP_ERROR
50 EXIT_DUPLICATE_IP = 13
51 EXIT_ARGS_ERROR = 14
52
53
54 def ParseOptions():
55   """Parse the command line options.
56
57   Returns:
58     (options, args) as from OptionParser.parse_args()
59
60   """
61   parser = OptionParser(description="Ganeti master",
62                         usage="%prog [-d]",
63                         version="%%prog (ganeti) %s" %
64                         constants.RELEASE_VERSION)
65
66   parser.add_option("-d", "--debug", dest="debug",
67                     help="Enable some debug messages",
68                     default=False, action="store_true")
69   options, args = parser.parse_args()
70
71   if len(args) != 1 or args[0] not in ("start", "stop"):
72     sys.stderr.write("Usage: %s [-d] start|stop\n" % sys.argv[0])
73     sys.exit(EXIT_ARGS_ERROR)
74
75   return options, args
76
77
78 def CheckNodeSetup(debug):
79   """Checks the node setup.
80
81   If the node setup if ok, this function will return the tuple
82   (master_hostname, master_netdev, master_ip). Otherwise the return
83   value will be None.
84
85   """
86   for fname in (constants.SSL_CERT_FILE,):
87     if not os.path.isfile(fname):
88       if debug:
89         sys.stderr.write("Missing config file %s.\n" % fname)
90       return None
91   try:
92     ss = ssconf.SimpleStore()
93     port = ss.GetNodeDaemonPort()
94     pwdata = ss.GetNodeDaemonPassword()
95     master_name = ss.GetMasterNode()
96     master_netdev = ss.GetMasterNetdev()
97     master_ip = ss.GetMasterIP()
98   except errors.ConfigurationError, err:
99     if debug:
100       sys.stderr.write("Cluster configuration incomplete: '%s'\n" % str(err))
101     return None
102   return (master_name, master_netdev, master_ip)
103
104
105 def StartMaster(master_netdev, master_ip, debug):
106   """Starts the master.
107
108   """
109   if utils.TcpPing(master_ip, constants.DEFAULT_NODED_PORT):
110     if utils.TcpPing(master_ip, constants.DEFAULT_NODED_PORT,
111                      source=constants.LOCALHOST_IP_ADDRESS):
112       # we already have the ip:
113       if debug:
114         sys.stderr.write("Notice: already started.\n")
115       return EXIT_OK
116     else:
117       return EXIT_DUPLICATE_IP
118
119   result = utils.RunCmd(["ip", "address", "add", "%s/32" % master_ip,
120                          "dev", master_netdev, "label",
121                          "%s:0" % master_netdev])
122   if result.failed:
123     if debug:
124       sys.stderr.write("Can't activate master IP: %s\n" % result.output)
125     return EXIT_SOME_ERROR
126
127   result = utils.RunCmd(["arping", "-q", "-U", "-c 3", "-I", master_netdev,
128                          "-s", master_ip, master_ip])
129   # we'll ignore the exit code of arping
130
131   if constants.RAPI_ENABLE:
132     # Start remote API
133     result = utils.RunCmd(["ganeti-rapi", "--port=%s" % constants.RAPI_PORT])
134     if debug and result.failed:
135       sys.stderr.write("Failed to start ganeti-rapi, error: %s\n" %
136                        result.output)
137
138   return EXIT_OK
139
140
141 def StopMaster(master_netdev, master_ip, debug):
142   """Stops the master.
143
144   """
145   if constants.RAPI_ENABLE:
146     # Stop remote API
147     result = utils.RunCmd(["fuser", "-k", "-n", "tcp",
148                           str(constants.RAPI_PORT)])
149     if debug and result.failed:
150       sys.stderr.write("Failed to stop ganeti-rapi, error: %s\n" %
151                        result.output)
152
153   result = utils.RunCmd(["ip", "address", "del", "%s/32" % master_ip,
154                          "dev", master_netdev])
155   if result.failed:
156     if debug:
157       sys.stderr.write("Can't remove the master IP, error: %s" % result.output)
158     # but otherwise ignore the failure
159
160   return EXIT_OK
161
162
163 def main():
164   """Main function.
165
166   """
167   options, args = ParseOptions()
168   debug = options.debug
169   try:
170     myself = utils.HostInfo()
171   except errors.ResolverError, err:
172     sys.stderr.write("Cannot resolve my own name (%s)\n" % err.args[0])
173     return EXIT_NODESETUP_ERROR
174
175   result = CheckNodeSetup(debug)
176   if not result:
177     if debug:
178       sys.stderr.write("Node configuration incomplete.\n")
179     return EXIT_NODESETUP_ERROR
180
181   master_node, master_netdev, master_ip = result
182   if myself.name != master_node and args[0] == "start":
183     if debug:
184       sys.stderr.write("Not master, ignoring request.\n")
185     return EXIT_NOTMASTER
186
187   if args[0] == "start":
188     fn = StartMaster
189   else:
190     fn = StopMaster
191
192   result = fn(master_netdev, master_ip, debug)
193   sys.exit(result)
194
195
196 if __name__ == '__main__':
197   main()