Statistics
| Branch: | Tag: | Revision:

root / tools / cfgupgrade @ f4bc1f2c

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
    print
203

    
204
  UpdateFromVersion2To3(config)
205

    
206
  if options.verbose:
207
    print "After upgrade:"
208
    pprint.pprint(config)
209
    print
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 :