Statistics
| Branch: | Tag: | Revision:

root / tools / cfgupgrade @ fc0726b9

History | View | Annotate | Download (6.4 kB)

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

    
4
# Copyright (C) 2007, 2008, 2009, 2010 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.data_dir = os.path.abspath(options.data_dir)
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
  options.CONFD_HMAC_KEY = options.data_dir + "/hmac.key"
126
  options.CDS_FILE = options.data_dir + "/cluster-domain-secret"
127

    
128
  SetupLogging()
129

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

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

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

    
147
  config_data = serializer.LoadJson(utils.ReadFile(options.CONFIG_DATA_PATH))
148

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

    
154
  (config_major, config_minor, config_revision) = \
155
    constants.SplitVersion(config_version)
156

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

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

    
164
  if config_major == 2 and config_minor == 0:
165
    if config_revision != 0:
166
      logging.warning("Config revision is not 0")
167

    
168
    config_data["version"] = constants.BuildVersion(2, 1, 0)
169

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

    
178
    if not options.dry_run:
179
      bootstrap.GenerateClusterCrypto(False, False, False, False,
180
                                      nodecert_file=options.SERVER_PEM_PATH,
181
                                      rapicert_file=options.RAPI_CERT_FILE,
182
                                      hmackey_file=options.CONFD_HMAC_KEY,
183
                                      cds_file=options.CDS_FILE)
184

    
185
  except:
186
    logging.critical("Writing configuration failed. It is probably in an"
187
                     " inconsistent state and needs manual intervention.")
188
    raise
189

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

    
204

    
205
if __name__ == "__main__":
206
  main()
207

    
208
# vim: set foldmethod=marker :