root / daemons / ganeti-master @ 2ec08468
History | View | Annotate | Download (4.9 kB)
1 | 880478f8 | Iustin Pop | #!/usr/bin/python |
---|---|---|---|
2 | 880478f8 | Iustin Pop | # |
3 | 880478f8 | Iustin Pop | |
4 | 880478f8 | Iustin Pop | # Copyright (C) 2006, 2007 Google Inc. |
5 | 880478f8 | Iustin Pop | # |
6 | 880478f8 | Iustin Pop | # This program is free software; you can redistribute it and/or modify |
7 | 880478f8 | Iustin Pop | # it under the terms of the GNU General Public License as published by |
8 | 880478f8 | Iustin Pop | # the Free Software Foundation; either version 2 of the License, or |
9 | 880478f8 | Iustin Pop | # (at your option) any later version. |
10 | 880478f8 | Iustin Pop | # |
11 | 880478f8 | Iustin Pop | # This program is distributed in the hope that it will be useful, but |
12 | 880478f8 | Iustin Pop | # WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | 880478f8 | Iustin Pop | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | 880478f8 | Iustin Pop | # General Public License for more details. |
15 | 880478f8 | Iustin Pop | # |
16 | 880478f8 | Iustin Pop | # You should have received a copy of the GNU General Public License |
17 | 880478f8 | Iustin Pop | # along with this program; if not, write to the Free Software |
18 | 880478f8 | Iustin Pop | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
19 | 880478f8 | Iustin Pop | # 02110-1301, USA. |
20 | 880478f8 | Iustin Pop | |
21 | 880478f8 | Iustin Pop | |
22 | 880478f8 | Iustin Pop | """Ganeti master script |
23 | 880478f8 | Iustin Pop | |
24 | 880478f8 | Iustin Pop | Exit codes, for both start and stop: |
25 | 880478f8 | Iustin Pop | - 0: master setup successful |
26 | 880478f8 | Iustin Pop | - 1: some generic error (this exit code can also be thrown by exceptions) |
27 | 880478f8 | Iustin Pop | - 11: node is not master, nothing to do |
28 | 880478f8 | Iustin Pop | - 12: node setup incomplete, cannot start |
29 | 880478f8 | Iustin Pop | - 13: node should be master, but someone has the ip address already |
30 | 880478f8 | Iustin Pop | |
31 | 880478f8 | Iustin Pop | Only exit codes 0 and 11 represent an ok state. Code 1 was left for |
32 | 880478f8 | Iustin Pop | generic errors as other python code can cause exit with code 1. |
33 | 880478f8 | Iustin Pop | |
34 | 880478f8 | Iustin Pop | """ |
35 | 880478f8 | Iustin Pop | |
36 | 880478f8 | Iustin Pop | import os |
37 | 880478f8 | Iustin Pop | import sys |
38 | 880478f8 | Iustin Pop | import socket |
39 | 880478f8 | Iustin Pop | |
40 | 880478f8 | Iustin Pop | from optparse import OptionParser |
41 | 880478f8 | Iustin Pop | |
42 | 880478f8 | Iustin Pop | from ganeti import constants |
43 | 880478f8 | Iustin Pop | from ganeti import errors |
44 | 880478f8 | Iustin Pop | from ganeti import ssconf |
45 | 880478f8 | Iustin Pop | from ganeti import utils |
46 | 880478f8 | Iustin Pop | |
47 | 880478f8 | Iustin Pop | EXIT_OK = 0 |
48 | 880478f8 | Iustin Pop | EXIT_SOME_ERROR = 1 |
49 | 38242904 | Iustin Pop | EXIT_NOTMASTER = constants.EXIT_NOTMASTER |
50 | 880478f8 | Iustin Pop | EXIT_NODESETUP_ERROR = 12 |
51 | 880478f8 | Iustin Pop | EXIT_DUPLICATE_IP = 13 |
52 | 880478f8 | Iustin Pop | EXIT_ARGS_ERROR = 14 |
53 | 880478f8 | Iustin Pop | |
54 | 880478f8 | Iustin Pop | |
55 | 880478f8 | Iustin Pop | def ParseOptions(): |
56 | 880478f8 | Iustin Pop | """Parse the command line options. |
57 | 880478f8 | Iustin Pop | |
58 | 880478f8 | Iustin Pop | Returns: |
59 | 880478f8 | Iustin Pop | (options, args) as from OptionParser.parse_args() |
60 | 880478f8 | Iustin Pop | |
61 | 880478f8 | Iustin Pop | """ |
62 | 880478f8 | Iustin Pop | parser = OptionParser(description="Ganeti master", |
63 | 880478f8 | Iustin Pop | usage="%prog [-d]", |
64 | 880478f8 | Iustin Pop | version="%%prog (ganeti) %s" % |
65 | 880478f8 | Iustin Pop | constants.RELEASE_VERSION) |
66 | 880478f8 | Iustin Pop | |
67 | 880478f8 | Iustin Pop | parser.add_option("-d", "--debug", dest="debug", |
68 | 880478f8 | Iustin Pop | help="Enable some debug messages", |
69 | 880478f8 | Iustin Pop | default=False, action="store_true") |
70 | 880478f8 | Iustin Pop | options, args = parser.parse_args() |
71 | 880478f8 | Iustin Pop | |
72 | 880478f8 | Iustin Pop | if len(args) != 1 or args[0] not in ("start", "stop"): |
73 | 880478f8 | Iustin Pop | sys.stderr.write("Usage: %s [-d] start|stop\n" % sys.argv[0]) |
74 | 880478f8 | Iustin Pop | sys.exit(EXIT_ARGS_ERROR) |
75 | 880478f8 | Iustin Pop | |
76 | 880478f8 | Iustin Pop | return options, args |
77 | 880478f8 | Iustin Pop | |
78 | 880478f8 | Iustin Pop | |
79 | 880478f8 | Iustin Pop | def CheckNodeSetup(debug): |
80 | 880478f8 | Iustin Pop | """Checks the node setup. |
81 | 880478f8 | Iustin Pop | |
82 | 880478f8 | Iustin Pop | If the node setup if ok, this function will return the tuple |
83 | 880478f8 | Iustin Pop | (master_hostname, master_netdev, master_ip). Otherwise the return |
84 | 880478f8 | Iustin Pop | value will be None. |
85 | 880478f8 | Iustin Pop | |
86 | 880478f8 | Iustin Pop | """ |
87 | 880478f8 | Iustin Pop | for fname in (constants.SSL_CERT_FILE,): |
88 | 880478f8 | Iustin Pop | if not os.path.isfile(fname): |
89 | 880478f8 | Iustin Pop | if debug: |
90 | 880478f8 | Iustin Pop | sys.stderr.write("Missing config file %s.\n" % fname) |
91 | 880478f8 | Iustin Pop | return None |
92 | 880478f8 | Iustin Pop | try: |
93 | 880478f8 | Iustin Pop | ss = ssconf.SimpleStore() |
94 | 880478f8 | Iustin Pop | port = ss.GetNodeDaemonPort() |
95 | 880478f8 | Iustin Pop | pwdata = ss.GetNodeDaemonPassword() |
96 | 880478f8 | Iustin Pop | master_name = ss.GetMasterNode() |
97 | 880478f8 | Iustin Pop | master_netdev = ss.GetMasterNetdev() |
98 | 880478f8 | Iustin Pop | master_ip = ss.GetMasterIP() |
99 | 880478f8 | Iustin Pop | except errors.ConfigurationError, err: |
100 | 880478f8 | Iustin Pop | if debug: |
101 | 880478f8 | Iustin Pop | sys.stderr.write("Cluster configuration incomplete: '%s'\n" % str(err)) |
102 | 880478f8 | Iustin Pop | return None |
103 | 880478f8 | Iustin Pop | return (master_name, master_netdev, master_ip) |
104 | 880478f8 | Iustin Pop | |
105 | 880478f8 | Iustin Pop | |
106 | 880478f8 | Iustin Pop | def StartMaster(master_netdev, master_ip, debug): |
107 | 880478f8 | Iustin Pop | """Starts the master. |
108 | 880478f8 | Iustin Pop | |
109 | 880478f8 | Iustin Pop | """ |
110 | 880478f8 | Iustin Pop | result = utils.RunCmd(["fping", "-q", master_ip]) |
111 | 880478f8 | Iustin Pop | if not result.failed: |
112 | 880478f8 | Iustin Pop | r2 = utils.RunCmd(["fping", "-q", "-S127.0.0.1", master_ip]) |
113 | 3ecf6786 | Iustin Pop | if not r2.failed: |
114 | 880478f8 | Iustin Pop | # we already have the ip: |
115 | 880478f8 | Iustin Pop | if debug: |
116 | 880478f8 | Iustin Pop | sys.stderr.write("Notice: already started.\n") |
117 | 880478f8 | Iustin Pop | return EXIT_OK |
118 | 880478f8 | Iustin Pop | else: |
119 | 880478f8 | Iustin Pop | return EXIT_DUPLICATE_IP |
120 | 880478f8 | Iustin Pop | result = utils.RunCmd(["ip", "address", "add", "%s/32" % master_ip, |
121 | 880478f8 | Iustin Pop | "dev", master_netdev, "label", |
122 | 880478f8 | Iustin Pop | "%s:0" % master_netdev]) |
123 | 880478f8 | Iustin Pop | if result.failed: |
124 | 880478f8 | Iustin Pop | if debug: |
125 | 880478f8 | Iustin Pop | sys.stderr.write("Can't activate master IP: %s\n" % result.output) |
126 | 880478f8 | Iustin Pop | return EXIT_SOME_ERROR |
127 | 880478f8 | Iustin Pop | |
128 | 880478f8 | Iustin Pop | result = utils.RunCmd(["arping", "-q", "-U", "-c 3", "-I", master_netdev, |
129 | 880478f8 | Iustin Pop | "-s", master_ip, master_ip]) |
130 | 880478f8 | Iustin Pop | # we'll ignore the exit code of arping |
131 | 880478f8 | Iustin Pop | return EXIT_OK |
132 | 880478f8 | Iustin Pop | |
133 | 880478f8 | Iustin Pop | |
134 | 880478f8 | Iustin Pop | def StopMaster(master_netdev, master_ip, debug): |
135 | 880478f8 | Iustin Pop | """Stops the master. |
136 | 880478f8 | Iustin Pop | |
137 | 880478f8 | Iustin Pop | """ |
138 | 880478f8 | Iustin Pop | result = utils.RunCmd(["ip", "address", "del", "%s/32" % master_ip, |
139 | 880478f8 | Iustin Pop | "dev", master_netdev]) |
140 | 880478f8 | Iustin Pop | if result.failed: |
141 | 880478f8 | Iustin Pop | if debug: |
142 | 880478f8 | Iustin Pop | sys.stderr.write("Can't remove the master IP, error: %s" % result.output) |
143 | 880478f8 | Iustin Pop | # but otherwise ignore the failure |
144 | 880478f8 | Iustin Pop | return EXIT_OK |
145 | 880478f8 | Iustin Pop | |
146 | 880478f8 | Iustin Pop | |
147 | 880478f8 | Iustin Pop | def main(): |
148 | 880478f8 | Iustin Pop | """Main function. |
149 | 880478f8 | Iustin Pop | |
150 | 880478f8 | Iustin Pop | """ |
151 | 880478f8 | Iustin Pop | options, args = ParseOptions() |
152 | 880478f8 | Iustin Pop | debug = options.debug |
153 | 880478f8 | Iustin Pop | result = CheckNodeSetup(debug) |
154 | 880478f8 | Iustin Pop | if not result: |
155 | 880478f8 | Iustin Pop | if debug: |
156 | 880478f8 | Iustin Pop | sys.stderr.write("Node configuration incomplete.\n") |
157 | 880478f8 | Iustin Pop | return EXIT_NODESETUP_ERROR |
158 | 880478f8 | Iustin Pop | |
159 | 880478f8 | Iustin Pop | master_node, master_netdev, master_ip = result |
160 | 880478f8 | Iustin Pop | if socket.gethostname() != master_node and args[0] == "start": |
161 | 880478f8 | Iustin Pop | if debug: |
162 | 880478f8 | Iustin Pop | sys.stderr.write("Not master, ignoring request.\n") |
163 | 880478f8 | Iustin Pop | return EXIT_NOTMASTER |
164 | 880478f8 | Iustin Pop | |
165 | 880478f8 | Iustin Pop | if args[0] == "start": |
166 | 880478f8 | Iustin Pop | fn = StartMaster |
167 | 880478f8 | Iustin Pop | else: |
168 | 880478f8 | Iustin Pop | fn = StopMaster |
169 | 880478f8 | Iustin Pop | |
170 | 3ecf6786 | Iustin Pop | result = fn(master_netdev, master_ip, debug) |
171 | 3ecf6786 | Iustin Pop | sys.exit(result) |
172 | 880478f8 | Iustin Pop | |
173 | 880478f8 | Iustin Pop | |
174 | 3ecf6786 | Iustin Pop | if __name__ == '__main__': |
175 | 3ecf6786 | Iustin Pop | main() |