Statistics
| Branch: | Tag: | Revision:

root / tools / cfgupgrade @ 07b8a2b5

History | View | Annotate | Download (6.3 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
  options.HMAC_CLUSTER_KEY = options.data_dir + "/hmac.key"
126

    
127
  SetupLogging()
128

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

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

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

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

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

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

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

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

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

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

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

    
177
    if not options.dry_run:
178
      if not os.path.exists(options.RAPI_CERT_FILE):
179
        logging.debug("Writing RAPI certificate to %s", options.RAPI_CERT_FILE)
180
        bootstrap.GenerateSelfSignedSslCert(options.RAPI_CERT_FILE)
181

    
182
      if not os.path.exists(options.HMAC_CLUSTER_KEY):
183
        logging.debug("Writing HMAC key to %s", options.HMAC_CLUSTER_KEY)
184
        bootstrap.GenerateHmacKey(options.HMAC_CLUSTER_KEY)
185

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

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

    
205

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

    
209
# vim: set foldmethod=marker :