Statistics
| Branch: | Tag: | Revision:

root / tools / cfgupgrade @ 3ecf6786

History | View | Annotate | Download (5.1 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
The upgrade is done by unpickling the configuration file into custom classes
25
derivating from dict. We then update the configuration by modifying these
26
dicts. To save the configuration, it's pickled into a buffer and unpickled
27
again using the Ganeti objects before being finally pickled into a file.
28

    
29
Not using the custom classes wouldn't allow us to rename or remove attributes
30
between versions without loosing their values.
31

    
32
"""
33

    
34

    
35
import os
36
import os.path
37
import sys
38
import optparse
39
import cPickle
40
import tempfile
41
from cStringIO import StringIO
42

    
43
from ganeti import objects
44

    
45
class Error(Exception):
46
  """Generic exception"""
47
  pass
48

    
49

    
50
def _BaseFindGlobal(module, name):
51
  """Helper function for the other FindGlobal functions.
52

    
53
  """
54
  return getattr(sys.modules[module], name)
55

    
56

    
57
# Internal config representation
58
class UpgradeDict(dict):
59
  """Base class for internal config classes.
60

    
61
  """
62
  def __setstate__(self, state):
63
    self.update(state)
64

    
65
  def __getstate__(self):
66
    return self.copy()
67

    
68

    
69
class UpgradeConfigData(UpgradeDict): pass
70
class UpgradeCluster(UpgradeDict): pass
71
class UpgradeNode(UpgradeDict): pass
72
class UpgradeInstance(UpgradeDict): pass
73
class UpgradeDisk(UpgradeDict): pass
74
class UpgradeNIC(UpgradeDict): pass
75
class UpgradeOS(UpgradeDict): pass
76

    
77

    
78
_ClassMap = {
79
  objects.ConfigData: UpgradeConfigData,
80
  objects.Cluster: UpgradeCluster,
81
  objects.Node: UpgradeNode,
82
  objects.Instance: UpgradeInstance,
83
  objects.Disk: UpgradeDisk,
84
  objects.NIC: UpgradeNIC,
85
  objects.OS: UpgradeOS,
86
}
87

    
88
# Build mapping dicts
89
WriteMapping = dict()
90
ReadMapping = dict()
91
for key, value in _ClassMap.iteritems():
92
  WriteMapping[value.__name__] = key
93
  ReadMapping[key.__name__] = value
94

    
95

    
96
# Read config
97
def _ReadFindGlobal(module, name):
98
  """Wraps Ganeti config classes to internal ones.
99

    
100
  """
101
  if module == "ganeti.objects" and name in ReadMapping:
102
    return ReadMapping[name]
103

    
104
  return _BaseFindGlobal(module, name)
105

    
106

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

    
110
  """
111
  f = open(path, 'r')
112
  try:
113
    loader = cPickle.Unpickler(f)
114
    loader.find_global = _ReadFindGlobal
115
    data = loader.load()
116
  finally:
117
    f.close()
118

    
119
  return data
120

    
121

    
122
# Write config
123
def _WriteFindGlobal(module, name):
124
  """Maps our internal config classes to Ganeti's.
125

    
126
  """
127
  if module == "__main__" and name in WriteMapping:
128
    return WriteMapping[name]
129

    
130
  return _BaseFindGlobal(module, name)
131

    
132

    
133
def WriteConfig(path, data, dry_run):
134
  """Writes the configuration file.
135

    
136
  """
137
  buf = StringIO()
138

    
139
  # Write intermediate representation
140
  dumper = cPickle.Pickler(buf, cPickle.HIGHEST_PROTOCOL)
141
  dumper.dump(data)
142
  del dumper
143

    
144
  # Convert back to Ganeti objects
145
  buf.seek(0)
146
  loader = cPickle.Unpickler(buf)
147
  loader.find_global = _WriteFindGlobal
148
  data = loader.load()
149

    
150
  # Write target file
151
  (fd, name) = tempfile.mkstemp(dir=os.path.dirname(path))
152
  f = os.fdopen(fd, 'w')
153
  try:
154
    try:
155
      dumper = cPickle.Pickler(f, cPickle.HIGHEST_PROTOCOL)
156
      dumper.dump(data)
157
      f.flush()
158
      if dry_run:
159
        os.unlink(name)
160
      else:
161
        os.rename(name, path)
162
    except:
163
      os.unlink(name)
164
      raise
165
  finally:
166
    f.close()
167

    
168

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

    
172
  """
173
  if cfg['cluster']['config_version'] != 2:
174
    return
175

    
176
  # Add port pool
177
  if 'tcpudp_port_pool' not in cfg['cluster']:
178
    cfg['cluster']['tcpudp_port_pool'] = set()
179

    
180
  # Add bridge settings
181
  if 'default_bridge' not in cfg['cluster']:
182
    cfg['cluster']['default_bridge'] = 'xen-br0'
183
  for inst in cfg['instances'].values():
184
    for nic in inst['nics']:
185
      if 'bridge' not in nic:
186
        nic['bridge'] = None
187

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

    
190

    
191
# Main program
192
if __name__ == "__main__":
193
  # Option parsing
194
  parser = optparse.OptionParser()
195
  parser.add_option('--dry-run', dest='dry_run',
196
                    action="store_true",
197
                    help="Try to do the conversion, but don't write "
198
                      "output file")
199
  parser.add_option('--verbose', dest='verbose',
200
                    action="store_true",
201
                    help="Verbose output")
202
  (options, args) = parser.parse_args()
203

    
204
  # Option checking
205
  if args:
206
    cfg_file = args[0]
207
  else:
208
    raise Error("Configuration file not specified")
209

    
210
  config = ReadConfig(cfg_file)
211

    
212
  UpdateFromVersion2To3(config)
213

    
214
  if options.verbose:
215
    import pprint
216
    pprint.pprint(config)
217

    
218
  WriteConfig(cfg_file, config, options.dry_run)