root / daemons / ganeti-master @ a2d32034
History | View | Annotate | Download (5.1 kB)
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 |
result = utils.RunCmd(["ip", "address", "add", "%s/32" % master_ip, |
119 |
"dev", master_netdev, "label", |
120 |
"%s:0" % master_netdev]) |
121 |
if result.failed: |
122 |
if debug: |
123 |
sys.stderr.write("Can't activate master IP: %s\n" % result.output) |
124 |
return EXIT_SOME_ERROR |
125 |
|
126 |
result = utils.RunCmd(["arping", "-q", "-U", "-c 3", "-I", master_netdev, |
127 |
"-s", master_ip, master_ip]) |
128 |
# we'll ignore the exit code of arping |
129 |
return EXIT_OK |
130 |
|
131 |
|
132 |
def StopMaster(master_netdev, master_ip, debug): |
133 |
"""Stops the master. |
134 |
|
135 |
""" |
136 |
result = utils.RunCmd(["ip", "address", "del", "%s/32" % master_ip, |
137 |
"dev", master_netdev]) |
138 |
if result.failed: |
139 |
if debug: |
140 |
sys.stderr.write("Can't remove the master IP, error: %s" % result.output) |
141 |
# but otherwise ignore the failure |
142 |
return EXIT_OK |
143 |
|
144 |
|
145 |
def main(): |
146 |
"""Main function. |
147 |
|
148 |
""" |
149 |
options, args = ParseOptions() |
150 |
debug = options.debug |
151 |
try: |
152 |
myself = utils.HostInfo() |
153 |
except errors.ResolverError, err: |
154 |
sys.stderr.write("Cannot resolve my own name (%s)\n" % err.args[0]) |
155 |
return EXIT_NODESETUP_ERROR |
156 |
|
157 |
result = CheckNodeSetup(debug) |
158 |
if not result: |
159 |
if debug: |
160 |
sys.stderr.write("Node configuration incomplete.\n") |
161 |
return EXIT_NODESETUP_ERROR |
162 |
|
163 |
master_node, master_netdev, master_ip = result |
164 |
if myself.name != master_node and args[0] == "start": |
165 |
if debug: |
166 |
sys.stderr.write("Not master, ignoring request.\n") |
167 |
return EXIT_NOTMASTER |
168 |
|
169 |
if args[0] == "start": |
170 |
fn = StartMaster |
171 |
else: |
172 |
fn = StopMaster |
173 |
|
174 |
result = fn(master_netdev, master_ip, debug) |
175 |
sys.exit(result) |
176 |
|
177 |
|
178 |
if __name__ == '__main__': |
179 |
main() |