Statistics
| Branch: | Tag: | Revision:

root / tools / cfgupgrade @ 728489a3

History | View | Annotate | Download (6 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

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

    
43

    
44
options = None
45
args = None
46

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

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

    
67
# Field names
68
F_SERIAL = 'serial_no'
69

    
70

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

    
75

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

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

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

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

    
95

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

    
99
  """
100
  global options, args # pylint: disable-msg=W0603
101

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

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

    
118
  # We need to keep filenames locally because they might be renamed between
119
  # versions.
120
  options.CONFIG_DATA_PATH = options.data_dir + "/config.data"
121
  options.SERVER_PEM_PATH = options.data_dir + "/server.pem"
122
  options.KNOWN_HOSTS_PATH = options.data_dir + "/known_hosts"
123
  options.RAPI_CERT_FILE = options.data_dir + "/rapi.pem"
124
  options.CONFD_HMAC_KEY = options.data_dir + "/hmac.key"
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
  if config_major == 2 and config_minor == 0:
163
    if config_revision != 0:
164
      logging.warning("Config revision is not 0")
165

    
166
    config_data["version"] = constants.BuildVersion(2, 1, 0)
167

    
168
  try:
169
    logging.info("Writing configuration file to %s", options.CONFIG_DATA_PATH)
170
    utils.WriteFile(file_name=options.CONFIG_DATA_PATH,
171
                    data=serializer.DumpJson(config_data),
172
                    mode=0600,
173
                    dry_run=options.dry_run,
174
                    backup=True)
175

    
176
    if not options.dry_run:
177
      bootstrap.GenerateClusterCrypto(False, False, False)
178

    
179
  except:
180
    logging.critical("Writing configuration failed. It is probably in an"
181
                     " inconsistent state and needs manual intervention.")
182
    raise
183

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

    
198

    
199
if __name__ == "__main__":
200
  main()
201

    
202
# vim: set foldmethod=marker :