root / tools / cfgupgrade @ c05bcecf
History | View | Annotate | Download (4.6 kB)
1 |
#!/usr/bin/python |
---|---|
2 |
# |
3 |
|
4 |
# Copyright (C) 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 |
"""Tool to upgrade the configuration file. |
23 |
|
24 |
This code handles only the types supported by simplejson. As an example, "set" |
25 |
is a "list". Old Pickle based configurations files are converted to JSON during |
26 |
the process. |
27 |
|
28 |
""" |
29 |
|
30 |
|
31 |
import os |
32 |
import os.path |
33 |
import sys |
34 |
import re |
35 |
import optparse |
36 |
import tempfile |
37 |
import simplejson |
38 |
|
39 |
from ganeti import utils |
40 |
from ganeti.cli import AskUser, FORCE_OPT |
41 |
|
42 |
|
43 |
options = None |
44 |
args = None |
45 |
|
46 |
|
47 |
class Error(Exception): |
48 |
"""Generic exception""" |
49 |
pass |
50 |
|
51 |
|
52 |
# {{{ Support for old Pickle files |
53 |
class UpgradeDict(dict): |
54 |
"""Base class for internal config classes. |
55 |
|
56 |
""" |
57 |
def __setstate__(self, state): |
58 |
self.update(state) |
59 |
|
60 |
def __getstate__(self): |
61 |
return self.copy() |
62 |
|
63 |
|
64 |
def FindGlobal(module, name): |
65 |
"""Wraps Ganeti config classes to internal ones. |
66 |
|
67 |
This function may only return types supported by simplejson. |
68 |
|
69 |
""" |
70 |
if module == "ganeti.objects": |
71 |
return UpgradeDict |
72 |
elif module == "__builtin__" and name == "set": |
73 |
return list |
74 |
|
75 |
return getattr(sys.modules[module], name) |
76 |
|
77 |
|
78 |
def ReadPickleFile(f): |
79 |
"""Reads an old Pickle configuration. |
80 |
|
81 |
""" |
82 |
import cPickle |
83 |
|
84 |
loader = cPickle.Unpickler(f) |
85 |
loader.find_global = FindGlobal |
86 |
return loader.load() |
87 |
|
88 |
|
89 |
def IsPickleFile(f): |
90 |
"""Checks whether a file is using the Pickle format. |
91 |
|
92 |
""" |
93 |
magic = f.read(128) |
94 |
try: |
95 |
return not re.match('^\s*\{', magic) |
96 |
finally: |
97 |
f.seek(-len(magic), 1) |
98 |
# }}} |
99 |
|
100 |
|
101 |
def ReadJsonFile(f): |
102 |
"""Reads a JSON file. |
103 |
|
104 |
""" |
105 |
return simplejson.load(f) |
106 |
|
107 |
|
108 |
def ReadConfig(path): |
109 |
"""Reads configuration file. |
110 |
|
111 |
""" |
112 |
f = open(path, 'r') |
113 |
try: |
114 |
if IsPickleFile(f): |
115 |
return ReadPickleFile(f) |
116 |
else: |
117 |
return ReadJsonFile(f) |
118 |
finally: |
119 |
f.close() |
120 |
|
121 |
|
122 |
def WriteConfig(path, data): |
123 |
"""Writes the configuration file. |
124 |
|
125 |
""" |
126 |
if not options.dry_run: |
127 |
utils.CreateBackup(path) |
128 |
|
129 |
(fd, name) = tempfile.mkstemp(dir=os.path.dirname(path)) |
130 |
f = os.fdopen(fd, 'w') |
131 |
try: |
132 |
try: |
133 |
simplejson.dump(data, f) |
134 |
f.flush() |
135 |
if options.dry_run: |
136 |
os.unlink(name) |
137 |
else: |
138 |
os.rename(name, path) |
139 |
except: |
140 |
os.unlink(name) |
141 |
raise |
142 |
finally: |
143 |
f.close() |
144 |
|
145 |
|
146 |
def UpdateFromVersion2To3(cfg): |
147 |
"""Updates the configuration from version 2 to 3. |
148 |
|
149 |
""" |
150 |
if cfg['cluster']['config_version'] != 2: |
151 |
return |
152 |
|
153 |
# Add port pool |
154 |
if 'tcpudp_port_pool' not in cfg['cluster']: |
155 |
cfg['cluster']['tcpudp_port_pool'] = [] |
156 |
|
157 |
# Add bridge settings |
158 |
if 'default_bridge' not in cfg['cluster']: |
159 |
cfg['cluster']['default_bridge'] = 'xen-br0' |
160 |
for inst in cfg['instances'].values(): |
161 |
for nic in inst['nics']: |
162 |
if 'bridge' not in nic: |
163 |
nic['bridge'] = None |
164 |
|
165 |
cfg['cluster']['config_version'] = 3 |
166 |
|
167 |
|
168 |
# Main program |
169 |
if __name__ == "__main__": |
170 |
program = os.path.basename(sys.argv[0]) |
171 |
|
172 |
# Option parsing |
173 |
parser = optparse.OptionParser() |
174 |
parser.add_option('--dry-run', dest='dry_run', |
175 |
action="store_true", |
176 |
help="Try to do the conversion, but don't write" |
177 |
" output file") |
178 |
parser.add_option(FORCE_OPT) |
179 |
parser.add_option('--verbose', dest='verbose', |
180 |
action="store_true", |
181 |
help="Verbose output") |
182 |
(options, args) = parser.parse_args() |
183 |
|
184 |
# Option checking |
185 |
if args: |
186 |
cfg_file = args[0] |
187 |
else: |
188 |
raise Error("Configuration file not specified") |
189 |
|
190 |
if not options.force: |
191 |
usertext = ("%s MUST run on the master node. Is this the master" |
192 |
" node?" % program) |
193 |
if not AskUser(usertext): |
194 |
sys.exit(1) |
195 |
|
196 |
config = ReadConfig(cfg_file) |
197 |
|
198 |
if options.verbose: |
199 |
import pprint |
200 |
print "Before upgrade:" |
201 |
pprint.pprint(config) |
202 |
|
203 |
|
204 |
UpdateFromVersion2To3(config) |
205 |
|
206 |
if options.verbose: |
207 |
print "After upgrade:" |
208 |
pprint.pprint(config) |
209 |
|
210 |
|
211 |
WriteConfig(cfg_file, config) |
212 |
|
213 |
print "The configuration file has been updated successfully. Please run" |
214 |
print " gnt-cluster copyfile %s" % cfg_file |
215 |
print "now." |
216 |
|
217 |
# vim: set foldmethod=marker : |