Revision 0006af7d

b/lib/constants.py
22 22
"""Module holding different constants."""
23 23

  
24 24
# various versions
25
CONFIG_VERSION = 2
25
CONFIG_VERSION = 3
26 26
PROTOCOL_VERSION = 2
27 27
RELEASE_VERSION = "1.2a1"
28 28
OS_API_VERSION = 4
b/tools/Makefile.am
1
dist_pkgdata_SCRIPTS = lvmstrap burnin cfgshell
1
dist_pkgdata_SCRIPTS = lvmstrap burnin cfgshell cfgupgrade
b/tools/cfgupgrade
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):
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
      os.rename(name, path)
159
    except:
160
      os.unlink(name)
161
      raise
162
  finally:
163
    f.close()
164

  
165

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

  
169
  """
170
  if cfg['cluster']['config_version'] != 2:
171
    return
172

  
173
  # Add port pool
174
  if 'tcpudp_port_pool' not in cfg['cluster']:
175
    cfg['cluster']['tcpudp_port_pool'] = set()
176

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

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

  
187

  
188
# Main program
189
if __name__ == "__main__":
190
  # Option parsing
191
  parser = optparse.OptionParser()
192
  parser.add_option('--verbose', dest='verbose',
193
                    action="store_true",
194
                    help="Verbose output")
195
  (options, args) = parser.parse_args()
196

  
197
  # Option checking
198
  if args:
199
    cfg_file = args[0]
200
  else:
201
    raise Error, ("Configuration file not specified")
202

  
203
  config = ReadConfig(cfg_file)
204

  
205
  UpdateFromVersion2To3(config)
206

  
207
  if options.verbose:
208
    import pprint
209
    pprint.pprint(config)
210

  
211
  WriteConfig(cfg_file, config)

Also available in: Unified diff