Statistics
| Branch: | Tag: | Revision:

root / tools / cfgupgrade @ 11c31f5c

History | View | Annotate | Download (5.9 kB)

1
#!/usr/bin/python
2
#
3

    
4
# Copyright (C) 2007, 2008, 2009 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
This code handles only the types supported by simplejson. As an
25
example, 'set' is a 'list'.
26

    
27
"""
28

    
29

    
30
import os
31
import os.path
32
import sys
33
import optparse
34
import logging
35
import errno
36

    
37
from ganeti import constants
38
from ganeti import serializer
39
from ganeti import utils
40
from ganeti import cli
41
from ganeti import bootstrap
42
from ganeti import config
43

    
44

    
45
options = None
46
args = None
47

    
48
# Dictionary with instance old keys, and new hypervisor keys
49
INST_HV_CHG = {
50
  'hvm_pae': constants.HV_PAE,
51
  'vnc_bind_address': constants.HV_VNC_BIND_ADDRESS,
52
  'initrd_path': constants.HV_INITRD_PATH,
53
  'hvm_nic_type': constants.HV_NIC_TYPE,
54
  'kernel_path': constants.HV_KERNEL_PATH,
55
  'hvm_acpi': constants.HV_ACPI,
56
  'hvm_cdrom_image_path': constants.HV_CDROM_IMAGE_PATH,
57
  'hvm_boot_order': constants.HV_BOOT_ORDER,
58
  'hvm_disk_type': constants.HV_DISK_TYPE,
59
  }
60

    
61
# Instance beparams changes
62
INST_BE_CHG = {
63
  'vcpus': constants.BE_VCPUS,
64
  'memory': constants.BE_MEMORY,
65
  'auto_balance': constants.BE_AUTO_BALANCE,
66
  }
67

    
68
# Field names
69
F_SERIAL = 'serial_no'
70

    
71

    
72
class Error(Exception):
73
  """Generic exception"""
74
  pass
75

    
76

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

    
80
  """
81
  formatter = logging.Formatter("%(asctime)s: %(message)s")
82

    
83
  stderr_handler = logging.StreamHandler()
84
  stderr_handler.setFormatter(formatter)
85
  if options.debug:
86
    stderr_handler.setLevel(logging.NOTSET)
87
  elif options.verbose:
88
    stderr_handler.setLevel(logging.INFO)
89
  else:
90
    stderr_handler.setLevel(logging.CRITICAL)
91

    
92
  root_logger = logging.getLogger("")
93
  root_logger.setLevel(logging.NOTSET)
94
  root_logger.addHandler(stderr_handler)
95

    
96

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

    
100
  """
101
  global options, args
102

    
103
  program = os.path.basename(sys.argv[0])
104

    
105
  # Option parsing
106
  parser = optparse.OptionParser(usage="%prog [--debug|--verbose] [--force]")
107
  parser.add_option('--dry-run', dest='dry_run',
108
                    action="store_true",
109
                    help="Try to do the conversion, but don't write"
110
                         " output file")
111
  parser.add_option(cli.FORCE_OPT)
112
  parser.add_option(cli.DEBUG_OPT)
113
  parser.add_option(cli.VERBOSE_OPT)
114
  parser.add_option('--path', help="Convert configuration in this"
115
                    " directory instead of '%s'" % constants.DATA_DIR,
116
                    default=constants.DATA_DIR, dest="data_dir")
117
  (options, args) = parser.parse_args()
118

    
119
  # We need to keep filenames locally because they might be renamed between
120
  # versions.
121
  options.CONFIG_DATA_PATH = options.data_dir + "/config.data"
122
  options.SERVER_PEM_PATH = options.data_dir + "/server.pem"
123
  options.KNOWN_HOSTS_PATH = options.data_dir + "/known_hosts"
124
  options.RAPI_CERT_FILE = options.data_dir + "/rapi.pem"
125

    
126
  SetupLogging()
127

    
128
  # Option checking
129
  if args:
130
    raise Error("No arguments expected")
131

    
132
  if not options.force:
133
    usertext = ("%s MUST be run on the master node. Is this the master"
134
                " node and are ALL instances down?" % program)
135
    if not cli.AskUser(usertext):
136
      sys.exit(1)
137

    
138
  # Check whether it's a Ganeti configuration directory
139
  if not (os.path.isfile(options.CONFIG_DATA_PATH) and
140
          os.path.isfile(options.SERVER_PEM_PATH) or
141
          os.path.isfile(options.KNOWN_HOSTS_PATH)):
142
    raise Error(("%s does not seem to be a known Ganeti configuration"
143
                 " directory") % options.data_dir)
144

    
145
  config_data = serializer.LoadJson(utils.ReadFile(options.CONFIG_DATA_PATH))
146

    
147
  try:
148
    config_version = config_data["version"]
149
  except KeyError:
150
    raise Error("Unable to determine configuration version")
151

    
152
  (config_major, config_minor, config_revision) = \
153
    constants.SplitVersion(config_version)
154

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

    
158
  if "config_version" in config_data["cluster"]:
159
    raise Error("Inconsistent configuration: found config_version in"
160
                " configuration file")
161

    
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)
169

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

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

    
180
  # test loading the config file
181
  if not options.dry_run:
182
    logging.info("Testing the new config file...")
183
    cfg = config.ConfigWriter(cfg_file=options.CONFIG_DATA_PATH,
184
                              offline=True)
185
    # if we reached this, it's all fine
186
    vrfy = cfg.VerifyConfig()
187
    if vrfy:
188
      logging.error("Errors after conversion:")
189
      for item in vrfy:
190
        logging.error(" - %s" % item)
191
    del cfg
192
    logging.info("File loaded successfully")
193

    
194

    
195
if __name__ == "__main__":
196
  main()
197

    
198
# vim: set foldmethod=marker :