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