Statistics
| Branch: | Tag: | Revision:

root / tools / cfgupgrade @ 0006af7d

History | View | Annotate | Download (4.9 kB)

1 0006af7d Michael Hanselmann
#!/usr/bin/python
2 0006af7d Michael Hanselmann
#
3 0006af7d Michael Hanselmann
4 0006af7d Michael Hanselmann
# Copyright (C) 2007 Google Inc.
5 0006af7d Michael Hanselmann
#
6 0006af7d Michael Hanselmann
# This program is free software; you can redistribute it and/or modify
7 0006af7d Michael Hanselmann
# it under the terms of the GNU General Public License as published by
8 0006af7d Michael Hanselmann
# the Free Software Foundation; either version 2 of the License, or
9 0006af7d Michael Hanselmann
# (at your option) any later version.
10 0006af7d Michael Hanselmann
#
11 0006af7d Michael Hanselmann
# This program is distributed in the hope that it will be useful, but
12 0006af7d Michael Hanselmann
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 0006af7d Michael Hanselmann
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 0006af7d Michael Hanselmann
# General Public License for more details.
15 0006af7d Michael Hanselmann
#
16 0006af7d Michael Hanselmann
# You should have received a copy of the GNU General Public License
17 0006af7d Michael Hanselmann
# along with this program; if not, write to the Free Software
18 0006af7d Michael Hanselmann
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 0006af7d Michael Hanselmann
# 02110-1301, USA.
20 0006af7d Michael Hanselmann
21 0006af7d Michael Hanselmann
22 0006af7d Michael Hanselmann
"""Tool to upgrade the configuration file.
23 0006af7d Michael Hanselmann
24 0006af7d Michael Hanselmann
The upgrade is done by unpickling the configuration file into custom classes
25 0006af7d Michael Hanselmann
derivating from dict. We then update the configuration by modifying these
26 0006af7d Michael Hanselmann
dicts. To save the configuration, it's pickled into a buffer and unpickled
27 0006af7d Michael Hanselmann
again using the Ganeti objects before being finally pickled into a file.
28 0006af7d Michael Hanselmann
29 0006af7d Michael Hanselmann
Not using the custom classes wouldn't allow us to rename or remove attributes
30 0006af7d Michael Hanselmann
between versions without loosing their values.
31 0006af7d Michael Hanselmann
32 0006af7d Michael Hanselmann
"""
33 0006af7d Michael Hanselmann
34 0006af7d Michael Hanselmann
35 0006af7d Michael Hanselmann
import os
36 0006af7d Michael Hanselmann
import os.path
37 0006af7d Michael Hanselmann
import sys
38 0006af7d Michael Hanselmann
import optparse
39 0006af7d Michael Hanselmann
import cPickle
40 0006af7d Michael Hanselmann
import tempfile
41 0006af7d Michael Hanselmann
from cStringIO import StringIO
42 0006af7d Michael Hanselmann
43 0006af7d Michael Hanselmann
from ganeti import objects
44 0006af7d Michael Hanselmann
45 0006af7d Michael Hanselmann
class Error(Exception):
46 0006af7d Michael Hanselmann
  """Generic exception"""
47 0006af7d Michael Hanselmann
  pass
48 0006af7d Michael Hanselmann
49 0006af7d Michael Hanselmann
50 0006af7d Michael Hanselmann
def _BaseFindGlobal(module, name):
51 0006af7d Michael Hanselmann
  """Helper function for the other FindGlobal functions.
52 0006af7d Michael Hanselmann
53 0006af7d Michael Hanselmann
  """
54 0006af7d Michael Hanselmann
  return getattr(sys.modules[module], name)
55 0006af7d Michael Hanselmann
56 0006af7d Michael Hanselmann
57 0006af7d Michael Hanselmann
# Internal config representation
58 0006af7d Michael Hanselmann
class UpgradeDict(dict):
59 0006af7d Michael Hanselmann
  """Base class for internal config classes.
60 0006af7d Michael Hanselmann
61 0006af7d Michael Hanselmann
  """
62 0006af7d Michael Hanselmann
  def __setstate__(self, state):
63 0006af7d Michael Hanselmann
    self.update(state)
64 0006af7d Michael Hanselmann
65 0006af7d Michael Hanselmann
  def __getstate__(self):
66 0006af7d Michael Hanselmann
    return self.copy()
67 0006af7d Michael Hanselmann
68 0006af7d Michael Hanselmann
69 0006af7d Michael Hanselmann
class UpgradeConfigData(UpgradeDict): pass
70 0006af7d Michael Hanselmann
class UpgradeCluster(UpgradeDict): pass
71 0006af7d Michael Hanselmann
class UpgradeNode(UpgradeDict): pass
72 0006af7d Michael Hanselmann
class UpgradeInstance(UpgradeDict): pass
73 0006af7d Michael Hanselmann
class UpgradeDisk(UpgradeDict): pass
74 0006af7d Michael Hanselmann
class UpgradeNIC(UpgradeDict): pass
75 0006af7d Michael Hanselmann
class UpgradeOS(UpgradeDict): pass
76 0006af7d Michael Hanselmann
77 0006af7d Michael Hanselmann
78 0006af7d Michael Hanselmann
_ClassMap = {
79 0006af7d Michael Hanselmann
  objects.ConfigData: UpgradeConfigData,
80 0006af7d Michael Hanselmann
  objects.Cluster: UpgradeCluster,
81 0006af7d Michael Hanselmann
  objects.Node: UpgradeNode,
82 0006af7d Michael Hanselmann
  objects.Instance: UpgradeInstance,
83 0006af7d Michael Hanselmann
  objects.Disk: UpgradeDisk,
84 0006af7d Michael Hanselmann
  objects.NIC: UpgradeNIC,
85 0006af7d Michael Hanselmann
  objects.OS: UpgradeOS,
86 0006af7d Michael Hanselmann
}
87 0006af7d Michael Hanselmann
88 0006af7d Michael Hanselmann
# Build mapping dicts
89 0006af7d Michael Hanselmann
WriteMapping = dict()
90 0006af7d Michael Hanselmann
ReadMapping = dict()
91 0006af7d Michael Hanselmann
for key, value in _ClassMap.iteritems():
92 0006af7d Michael Hanselmann
  WriteMapping[value.__name__] = key
93 0006af7d Michael Hanselmann
  ReadMapping[key.__name__] = value
94 0006af7d Michael Hanselmann
95 0006af7d Michael Hanselmann
96 0006af7d Michael Hanselmann
# Read config
97 0006af7d Michael Hanselmann
def _ReadFindGlobal(module, name):
98 0006af7d Michael Hanselmann
  """Wraps Ganeti config classes to internal ones.
99 0006af7d Michael Hanselmann
100 0006af7d Michael Hanselmann
  """
101 0006af7d Michael Hanselmann
  if module == "ganeti.objects" and name in ReadMapping:
102 0006af7d Michael Hanselmann
    return ReadMapping[name]
103 0006af7d Michael Hanselmann
104 0006af7d Michael Hanselmann
  return _BaseFindGlobal(module, name)
105 0006af7d Michael Hanselmann
106 0006af7d Michael Hanselmann
107 0006af7d Michael Hanselmann
def ReadConfig(path):
108 0006af7d Michael Hanselmann
  """Reads configuration file.
109 0006af7d Michael Hanselmann
110 0006af7d Michael Hanselmann
  """
111 0006af7d Michael Hanselmann
  f = open(path, 'r')
112 0006af7d Michael Hanselmann
  try:
113 0006af7d Michael Hanselmann
    loader = cPickle.Unpickler(f)
114 0006af7d Michael Hanselmann
    loader.find_global = _ReadFindGlobal
115 0006af7d Michael Hanselmann
    data = loader.load()
116 0006af7d Michael Hanselmann
  finally:
117 0006af7d Michael Hanselmann
    f.close()
118 0006af7d Michael Hanselmann
119 0006af7d Michael Hanselmann
  return data
120 0006af7d Michael Hanselmann
121 0006af7d Michael Hanselmann
122 0006af7d Michael Hanselmann
# Write config
123 0006af7d Michael Hanselmann
def _WriteFindGlobal(module, name):
124 0006af7d Michael Hanselmann
  """Maps our internal config classes to Ganeti's.
125 0006af7d Michael Hanselmann
126 0006af7d Michael Hanselmann
  """
127 0006af7d Michael Hanselmann
  if module == "__main__" and name in WriteMapping:
128 0006af7d Michael Hanselmann
    return WriteMapping[name]
129 0006af7d Michael Hanselmann
130 0006af7d Michael Hanselmann
  return _BaseFindGlobal(module, name)
131 0006af7d Michael Hanselmann
132 0006af7d Michael Hanselmann
133 0006af7d Michael Hanselmann
def WriteConfig(path, data):
134 0006af7d Michael Hanselmann
  """Writes the configuration file.
135 0006af7d Michael Hanselmann
136 0006af7d Michael Hanselmann
  """
137 0006af7d Michael Hanselmann
  buf = StringIO()
138 0006af7d Michael Hanselmann
139 0006af7d Michael Hanselmann
  # Write intermediate representation
140 0006af7d Michael Hanselmann
  dumper = cPickle.Pickler(buf, cPickle.HIGHEST_PROTOCOL)
141 0006af7d Michael Hanselmann
  dumper.dump(data)
142 0006af7d Michael Hanselmann
  del dumper
143 0006af7d Michael Hanselmann
144 0006af7d Michael Hanselmann
  # Convert back to Ganeti objects
145 0006af7d Michael Hanselmann
  buf.seek(0)
146 0006af7d Michael Hanselmann
  loader = cPickle.Unpickler(buf)
147 0006af7d Michael Hanselmann
  loader.find_global = _WriteFindGlobal
148 0006af7d Michael Hanselmann
  data = loader.load()
149 0006af7d Michael Hanselmann
150 0006af7d Michael Hanselmann
  # Write target file
151 0006af7d Michael Hanselmann
  (fd, name) = tempfile.mkstemp(dir=os.path.dirname(path))
152 0006af7d Michael Hanselmann
  f = os.fdopen(fd, 'w')
153 0006af7d Michael Hanselmann
  try:
154 0006af7d Michael Hanselmann
    try:
155 0006af7d Michael Hanselmann
      dumper = cPickle.Pickler(f, cPickle.HIGHEST_PROTOCOL)
156 0006af7d Michael Hanselmann
      dumper.dump(data)
157 0006af7d Michael Hanselmann
      f.flush()
158 0006af7d Michael Hanselmann
      os.rename(name, path)
159 0006af7d Michael Hanselmann
    except:
160 0006af7d Michael Hanselmann
      os.unlink(name)
161 0006af7d Michael Hanselmann
      raise
162 0006af7d Michael Hanselmann
  finally:
163 0006af7d Michael Hanselmann
    f.close()
164 0006af7d Michael Hanselmann
165 0006af7d Michael Hanselmann
166 0006af7d Michael Hanselmann
def UpdateFromVersion2To3(cfg):
167 0006af7d Michael Hanselmann
  """Updates the configuration from version 2 to 3.
168 0006af7d Michael Hanselmann
169 0006af7d Michael Hanselmann
  """
170 0006af7d Michael Hanselmann
  if cfg['cluster']['config_version'] != 2:
171 0006af7d Michael Hanselmann
    return
172 0006af7d Michael Hanselmann
173 0006af7d Michael Hanselmann
  # Add port pool
174 0006af7d Michael Hanselmann
  if 'tcpudp_port_pool' not in cfg['cluster']:
175 0006af7d Michael Hanselmann
    cfg['cluster']['tcpudp_port_pool'] = set()
176 0006af7d Michael Hanselmann
177 0006af7d Michael Hanselmann
  # Add bridge settings
178 0006af7d Michael Hanselmann
  if 'default_bridge' not in cfg['cluster']:
179 0006af7d Michael Hanselmann
    cfg['cluster']['default_bridge'] = 'xen-br0'
180 0006af7d Michael Hanselmann
  for inst in cfg['instances'].values():
181 0006af7d Michael Hanselmann
    for nic in inst['nics']:
182 0006af7d Michael Hanselmann
      if 'bridge' not in nic:
183 0006af7d Michael Hanselmann
        nic['bridge'] = None
184 0006af7d Michael Hanselmann
185 0006af7d Michael Hanselmann
  cfg['cluster']['config_version'] = 3
186 0006af7d Michael Hanselmann
187 0006af7d Michael Hanselmann
188 0006af7d Michael Hanselmann
# Main program
189 0006af7d Michael Hanselmann
if __name__ == "__main__":
190 0006af7d Michael Hanselmann
  # Option parsing
191 0006af7d Michael Hanselmann
  parser = optparse.OptionParser()
192 0006af7d Michael Hanselmann
  parser.add_option('--verbose', dest='verbose',
193 0006af7d Michael Hanselmann
                    action="store_true",
194 0006af7d Michael Hanselmann
                    help="Verbose output")
195 0006af7d Michael Hanselmann
  (options, args) = parser.parse_args()
196 0006af7d Michael Hanselmann
197 0006af7d Michael Hanselmann
  # Option checking
198 0006af7d Michael Hanselmann
  if args:
199 0006af7d Michael Hanselmann
    cfg_file = args[0]
200 0006af7d Michael Hanselmann
  else:
201 0006af7d Michael Hanselmann
    raise Error, ("Configuration file not specified")
202 0006af7d Michael Hanselmann
203 0006af7d Michael Hanselmann
  config = ReadConfig(cfg_file)
204 0006af7d Michael Hanselmann
205 0006af7d Michael Hanselmann
  UpdateFromVersion2To3(config)
206 0006af7d Michael Hanselmann
207 0006af7d Michael Hanselmann
  if options.verbose:
208 0006af7d Michael Hanselmann
    import pprint
209 0006af7d Michael Hanselmann
    pprint.pprint(config)
210 0006af7d Michael Hanselmann
211 0006af7d Michael Hanselmann
  WriteConfig(cfg_file, config)