Revision 11c31f5c tools/cfgupgrade

b/tools/cfgupgrade
31 31
import os.path
32 32
import sys
33 33
import optparse
34
import tempfile
35 34
import logging
36 35
import errno
37 36

  
......
46 45
options = None
47 46
args = None
48 47

  
49
# Unique object to identify calls without default value
50
NoDefault = object()
51

  
52 48
# Dictionary with instance old keys, and new hypervisor keys
53 49
INST_HV_CHG = {
54 50
  'hvm_pae': constants.HV_PAE,
......
78 74
  pass
79 75

  
80 76

  
81
def SsconfName(key):
82
  """Returns the file name of an (old) ssconf key.
83

  
84
  """
85
  return "%s/ssconf_%s" % (options.data_dir, key)
86

  
87

  
88
def ReadFile(file_name, default=NoDefault):
89
  """Reads a file.
90

  
91
  """
92
  logging.debug("Reading %s", file_name)
93
  try:
94
    fh = open(file_name, 'r')
95
  except IOError, err:
96
    if default is not NoDefault and err.errno == errno.ENOENT:
97
      return default
98
    raise
99

  
100
  try:
101
    return fh.read()
102
  finally:
103
    fh.close()
104

  
105

  
106
def WriteFile(file_name, data):
107
  """Writes a configuration file.
108

  
109
  """
110
  logging.debug("Writing %s", file_name)
111
  utils.WriteFile(file_name=file_name, data=data, mode=0600,
112
                  dry_run=options.dry_run, backup=True)
113

  
114

  
115
def GenerateSecret(all_secrets):
116
  """Generate an unique DRBD secret.
117

  
118
  This is a copy from ConfigWriter.
119

  
120
  """
121
  retries = 64
122
  while retries > 0:
123
    secret = utils.GenerateSecret()
124
    if secret not in all_secrets:
125
      break
126
    retries -= 1
127
  else:
128
    raise Error("Can't generate unique DRBD secret")
129
  return secret
130

  
131

  
132 77
def SetupLogging():
133 78
  """Configures the logging module.
134 79

  
......
149 94
  root_logger.addHandler(stderr_handler)
150 95

  
151 96

  
152
def Cluster12To20(cluster):
153
  """Upgrades the cluster object from 1.2 to 2.0.
154

  
155
  """
156
  logging.info("Upgrading the cluster object")
157
  # Upgrade the configuration version
158
  if 'config_version' in cluster:
159
    del cluster['config_version']
160

  
161
  # Add old ssconf keys back to config
162
  logging.info(" - importing ssconf keys")
163
  for key in ('master_node', 'master_ip', 'master_netdev', 'cluster_name'):
164
    if key not in cluster:
165
      cluster[key] = ReadFile(SsconfName(key)).strip()
166

  
167
  if 'default_hypervisor' not in cluster:
168
    old_hyp = ReadFile(SsconfName('hypervisor')).strip()
169
    if old_hyp == "xen-3.0":
170
      hyp = "xen-pvm"
171
    elif old_hyp == "xen-hvm-3.1":
172
      hyp = "xen-hvm"
173
    elif old_hyp == "fake":
174
      hyp = "fake"
175
    else:
176
      raise Error("Unknown old hypervisor name '%s'" % old_hyp)
177

  
178
    logging.info("Setting the default and enabled hypervisor")
179
    cluster['default_hypervisor'] = hyp
180
    cluster['enabled_hypervisors'] = [hyp]
181

  
182
  # hv/be params
183
  if 'hvparams' not in cluster:
184
    logging.info(" - adding hvparams")
185
    cluster['hvparams'] = constants.HVC_DEFAULTS
186
  if 'beparams' not in cluster:
187
    logging.info(" - adding beparams")
188
    cluster['beparams'] = {constants.PP_DEFAULT: constants.BEC_DEFAULTS}
189

  
190
  # file storage
191
  if 'file_storage_dir' not in cluster:
192
    cluster['file_storage_dir'] = constants.DEFAULT_FILE_STORAGE_DIR
193

  
194
  # candidate pool size
195
  if 'candidate_pool_size' not in cluster:
196
    cluster['candidate_pool_size'] = constants.MASTER_POOL_SIZE_DEFAULT
197

  
198

  
199
def Node12To20(node):
200
  """Upgrades a node from 1.2 to 2.0.
201

  
202
  """
203
  logging.info("Upgrading node %s" % node['name'])
204
  if F_SERIAL not in node:
205
    node[F_SERIAL] = 1
206
  if 'master_candidate' not in node:
207
    node['master_candidate'] = True
208
  for key in 'offline', 'drained':
209
    if key not in node:
210
      node[key] = False
211

  
212

  
213
def Instance12To20(drbd_minors, secrets, hypervisor, instance):
214
  """Upgrades an instance from 1.2 to 2.0.
215

  
216
  """
217
  if F_SERIAL not in instance:
218
    instance[F_SERIAL] = 1
219

  
220
  if 'hypervisor' not in instance:
221
    instance['hypervisor'] = hypervisor
222

  
223
  # hvparams changes
224
  if 'hvparams' not in instance:
225
    instance['hvparams'] = hvp = {}
226
  for old, new in INST_HV_CHG.items():
227
    if old in instance:
228
      if (instance[old] is not None and
229
          instance[old] != constants.VALUE_DEFAULT and # no longer valid in 2.0
230
          new in constants.HVC_DEFAULTS[hypervisor]):
231
        hvp[new] = instance[old]
232
      del instance[old]
233

  
234
  # beparams changes
235
  if 'beparams' not in instance:
236
    instance['beparams'] = bep = {}
237
  for old, new in INST_BE_CHG.items():
238
    if old in instance:
239
      if instance[old] is not None:
240
        bep[new] = instance[old]
241
      del instance[old]
242

  
243
  # disk changes
244
  for disk in instance['disks']:
245
    Disk12To20(drbd_minors, secrets, disk)
246

  
247
  # other instance changes
248
  if 'status' in instance:
249
    instance['admin_up'] = instance['status'] == 'up'
250
    del instance['status']
251

  
252

  
253
def Disk12To20(drbd_minors, secrets, disk):
254
  """Upgrades a disk from 1.2 to 2.0.
255

  
256
  """
257
  if 'mode' not in disk:
258
    disk['mode'] = constants.DISK_RDWR
259
  if disk['dev_type'] == constants.LD_DRBD8:
260
    old_lid = disk['logical_id']
261
    for node in old_lid[:2]:
262
      if node not in drbd_minors:
263
        raise Error("Can't find node '%s' while upgrading disk" % node)
264
      drbd_minors[node] += 1
265
      minor = drbd_minors[node]
266
      old_lid.append(minor)
267
    old_lid.append(GenerateSecret(secrets))
268
    del disk['physical_id']
269
  if disk['children']:
270
    for child in disk['children']:
271
      Disk12To20(drbd_minors, secrets, child)
272

  
273

  
274 97
def main():
275 98
  """Main program.
276 99

  
......
319 142
    raise Error(("%s does not seem to be a known Ganeti configuration"
320 143
                 " directory") % options.data_dir)
321 144

  
322
  config_version = ReadFile(SsconfName('config_version'), "1.2").strip()
323
  logging.info("Found configuration version %s", config_version)
324

  
325
  config_data = serializer.LoadJson(ReadFile(options.CONFIG_DATA_PATH))
326

  
327
  # Ganeti 1.2?
328
  if config_version == "1.2":
329
    logging.info("Found a Ganeti 1.2 configuration")
330

  
331
    cluster = config_data["cluster"]
332

  
333
    old_config_version = cluster.get("config_version", None)
334
    logging.info("Found old configuration version %s", old_config_version)
335
    if old_config_version not in (3, ):
336
      raise Error("Unsupported configuration version: %s" %
337
                  old_config_version)
338
    if 'version' not in config_data:
339
      config_data['version'] = constants.BuildVersion(2, 0, 0)
340
    if F_SERIAL not in config_data:
341
      config_data[F_SERIAL] = 1
342

  
343
    # Make sure no instance uses remote_raid1 anymore
344
    remote_raid1_instances = []
345
    for instance in config_data["instances"].values():
346
      if instance["disk_template"] == "remote_raid1":
347
        remote_raid1_instances.append(instance["name"])
348
    if remote_raid1_instances:
349
      for name in remote_raid1_instances:
350
        logging.error("Instance %s still using remote_raid1 disk template")
351
      raise Error("Unable to convert configuration as long as there are"
352
                  " instances using remote_raid1 disk template")
353

  
354
    # Build content of new known_hosts file
355
    cluster_name = ReadFile(SsconfName('cluster_name')).rstrip()
356
    cluster_key = cluster['rsahostkeypub']
357
    known_hosts = "%s ssh-rsa %s\n" % (cluster_name, cluster_key)
358

  
359
    Cluster12To20(cluster)
360

  
361
    # Add node attributes
362
    logging.info("Upgrading nodes")
363
    # stable-sort the names to have repeatable runs
364
    for node_name in utils.NiceSort(config_data['nodes'].keys()):
365
      Node12To20(config_data['nodes'][node_name])
366

  
367
    # Instance changes
368
    logging.info("Upgrading instances")
369
    drbd_minors = dict.fromkeys(config_data['nodes'], 0)
370
    secrets = set()
371
    # stable-sort the names to have repeatable runs
372
    for instance_name in utils.NiceSort(config_data['instances'].keys()):
373
      Instance12To20(drbd_minors, secrets, cluster['default_hypervisor'],
374
                     config_data['instances'][instance_name])
145
  config_data = serializer.LoadJson(utils.ReadFile(options.CONFIG_DATA_PATH))
375 146

  
376
  else:
377
    logging.info("Found a Ganeti 2.0 configuration")
147
  try:
148
    config_version = config_data["version"]
149
  except KeyError:
150
    raise Error("Unable to determine configuration version")
378 151

  
379
    if "config_version" in config_data["cluster"]:
380
      raise Error("Inconsistent configuration: found config_data in"
381
                  " configuration file")
152
  (config_major, config_minor, config_revision) = \
153
    constants.SplitVersion(config_version)
382 154

  
383
    known_hosts = None
155
  logging.info("Found configuration version %s (%d.%d.%d)",
156
               config_version, config_major, config_minor, config_revision)
384 157

  
385
  try:
386
    logging.info("Writing configuration file")
387
    WriteFile(options.CONFIG_DATA_PATH, serializer.DumpJson(config_data))
158
  if "config_version" in config_data["cluster"]:
159
    raise Error("Inconsistent configuration: found config_version in"
160
                " configuration file")
388 161

  
389
    if known_hosts is not None:
390
      logging.info("Writing SSH known_hosts file (%s)", known_hosts.strip())
391
      WriteFile(options.KNOWN_HOSTS_PATH, known_hosts)
162
  try:
163
    logging.info("Writing configuration file to %s", options.CONFIG_DATA_PATH)
164
    utils.WriteFile(file_name=options.CONFIG_DATA_PATH,
165
                    data=serializer.DumpJson(config_data),
166
                    mode=0600,
167
                    dry_run=options.dry_run,
168
                    backup=True)
392 169

  
393 170
    if not options.dry_run:
394 171
      if not os.path.exists(options.RAPI_CERT_FILE):
172
        logging.debug("Writing RAPI certificate to %s", options.RAPI_CERT_FILE)
395 173
        bootstrap._GenerateSelfSignedSslCert(options.RAPI_CERT_FILE)
396 174

  
397 175
  except:
398
    logging.critical("Writing configuration failed. It is proably in an"
176
    logging.critical("Writing configuration failed. It is probably in an"
399 177
                     " inconsistent state and needs manual intervention.")
400 178
    raise
401 179

  

Also available in: Unified diff