root / daemons / ganeti-master @ e9f745aa
History | View | Annotate | Download (4.9 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 |
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 = 12 |
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() |