cfgupgrade 4.62 KB
Newer Older
1
2
3
#!/usr/bin/python
#

4
# Copyright (C) 2007, 2008 Google Inc.
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.


"""Tool to upgrade the configuration file.

24
25
26
This code handles only the types supported by simplejson. As an example, "set"
is a "list". Old Pickle based configurations files are converted to JSON during
the process.
27
28
29
30
31
32
33

"""


import os
import os.path
import sys
34
import re
35
36
import optparse
import tempfile
37
import simplejson
38

39
from ganeti import utils
40
from ganeti import cli
41
42


43
44
options = None
args = None
45
46


47
48
49
class Error(Exception):
  """Generic exception"""
  pass
50
51


52
# {{{ Support for old Pickle files
53
54
55
56
57
58
59
60
61
62
63
class UpgradeDict(dict):
  """Base class for internal config classes.

  """
  def __setstate__(self, state):
    self.update(state)

  def __getstate__(self):
    return self.copy()


64
65
def FindGlobal(module, name):
  """Wraps Ganeti config classes to internal ones.
66

67
  This function may only return types supported by simplejson.
68

69
70
71
72
73
  """
  if module == "ganeti.objects":
    return UpgradeDict
  elif module == "__builtin__" and name == "set":
    return list
74

75
  return getattr(sys.modules[module], name)
76
77


78
79
def ReadPickleFile(f):
  """Reads an old Pickle configuration.
80
81

  """
82
  import cPickle
83

84
85
86
  loader = cPickle.Unpickler(f)
  loader.find_global = FindGlobal
  return loader.load()
87
88


89
90
def IsPickleFile(f):
  """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
98
    f.seek(-len(magic), 1)
# }}}
99
100


101
102
def ReadJsonFile(f):
  """Reads a JSON file.
103
104

  """
105
  return simplejson.load(f)
106
107


108
109
def ReadConfig(path):
  """Reads configuration file.
110
111

  """
112
113
114
115
116
117
118
119
  f = open(path, 'r')
  try:
    if IsPickleFile(f):
      return ReadPickleFile(f)
    else:
      return ReadJsonFile(f)
  finally:
    f.close()
120
121


122
123
124
125
126
127
def WriteConfig(path, data):
  """Writes the configuration file.

  """
  if not options.dry_run:
    utils.CreateBackup(path)
128
129
130
131
132

  (fd, name) = tempfile.mkstemp(dir=os.path.dirname(path))
  f = os.fdopen(fd, 'w')
  try:
    try:
133
      simplejson.dump(data, f)
134
      f.flush()
135
      if options.dry_run:
136
137
138
        os.unlink(name)
      else:
        os.rename(name, path)
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
    except:
      os.unlink(name)
      raise
  finally:
    f.close()


def UpdateFromVersion2To3(cfg):
  """Updates the configuration from version 2 to 3.

  """
  if cfg['cluster']['config_version'] != 2:
    return

  # Add port pool
  if 'tcpudp_port_pool' not in cfg['cluster']:
155
    cfg['cluster']['tcpudp_port_pool'] = []
156
157
158
159
160
161
162
163
164
165
166
167
168
169

  # Add bridge settings
  if 'default_bridge' not in cfg['cluster']:
    cfg['cluster']['default_bridge'] = 'xen-br0'
  for inst in cfg['instances'].values():
    for nic in inst['nics']:
      if 'bridge' not in nic:
        nic['bridge'] = None

  cfg['cluster']['config_version'] = 3


# Main program
if __name__ == "__main__":
170
171
  program = os.path.basename(sys.argv[0])

172
173
  # Option parsing
  parser = optparse.OptionParser()
174
175
  parser.add_option('--dry-run', dest='dry_run',
                    action="store_true",
176
177
                    help="Try to do the conversion, but don't write"
                         " output file")
178
  parser.add_option(cli.FORCE_OPT)
179
180
181
182
183
184
185
186
187
  parser.add_option('--verbose', dest='verbose',
                    action="store_true",
                    help="Verbose output")
  (options, args) = parser.parse_args()

  # Option checking
  if args:
    cfg_file = args[0]
  else:
188
    raise Error("Configuration file not specified")
189

190
  if not options.force:
191
192
    usertext = ("%s MUST run on the master node. Is this the master"
                " node?" % program)
193
    if not cli.AskUser(usertext):
194
195
      sys.exit(1)

196
197
  config = ReadConfig(cfg_file)

198
199
200
201
202
203
  if options.verbose:
    import pprint
    print "Before upgrade:"
    pprint.pprint(config)
    print

204
205
206
  UpdateFromVersion2To3(config)

  if options.verbose:
207
    print "After upgrade:"
208
    pprint.pprint(config)
209
210
211
212
213
214
215
    print

  WriteConfig(cfg_file, config)

  print "The configuration file has been updated successfully. Please run"
  print "  gnt-cluster copyfile %s" % cfg_file
  print "now."
216

217
# vim: set foldmethod=marker :