Statistics
| Branch: | Tag: | Revision:

root / tools / cfgupgrade @ fdd9ac5b

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

    
48
class Error(Exception):
49
  """Generic exception"""
50
  pass
51

    
52

    
53
def SetupLogging():
54
  """Configures the logging module.
55

    
56
  """
57
  formatter = logging.Formatter("%(asctime)s: %(message)s")
58

    
59
  stderr_handler = logging.StreamHandler()
60
  stderr_handler.setFormatter(formatter)
61
  if options.debug:
62
    stderr_handler.setLevel(logging.NOTSET)
63
  elif options.verbose:
64
    stderr_handler.setLevel(logging.INFO)
65
  else:
66
    stderr_handler.setLevel(logging.CRITICAL)
67

    
68
  root_logger = logging.getLogger("")
69
  root_logger.setLevel(logging.NOTSET)
70
  root_logger.addHandler(stderr_handler)
71

    
72

    
73
def main():
74
  """Main program.
75

    
76
  """
77
  global options, args # pylint: disable-msg=W0603
78

    
79
  program = os.path.basename(sys.argv[0])
80

    
81
  # Option parsing
82
  parser = optparse.OptionParser(usage="%prog [--debug|--verbose] [--force]")
83
  parser.add_option('--dry-run', dest='dry_run',
84
                    action="store_true",
85
                    help="Try to do the conversion, but don't write"
86
                         " output file")
87
  parser.add_option(cli.FORCE_OPT)
88
  parser.add_option(cli.DEBUG_OPT)
89
  parser.add_option(cli.VERBOSE_OPT)
90
  parser.add_option('--path', help="Convert configuration in this"
91
                    " directory instead of '%s'" % constants.DATA_DIR,
92
                    default=constants.DATA_DIR, dest="data_dir")
93
  parser.add_option("--no-verify",
94
                    help="Do not verify configuration after upgrade",
95
                    action="store_true", dest="no_verify", default=False)
96
  (options, args) = parser.parse_args()
97

    
98
  # We need to keep filenames locally because they might be renamed between
99
  # versions.
100
  options.data_dir = os.path.abspath(options.data_dir)
101
  options.CONFIG_DATA_PATH = options.data_dir + "/config.data"
102
  options.SERVER_PEM_PATH = options.data_dir + "/server.pem"
103
  options.KNOWN_HOSTS_PATH = options.data_dir + "/known_hosts"
104
  options.RAPI_CERT_FILE = options.data_dir + "/rapi.pem"
105
  options.RAPI_USERS_FILE = options.data_dir + "/rapi/users"
106
  options.RAPI_USERS_FILE_PRE24 = options.data_dir + "/rapi_users"
107
  options.CONFD_HMAC_KEY = options.data_dir + "/hmac.key"
108
  options.CDS_FILE = options.data_dir + "/cluster-domain-secret"
109

    
110
  SetupLogging()
111

    
112
  # Option checking
113
  if args:
114
    raise Error("No arguments expected")
115

    
116
  if not options.force:
117
    usertext = ("%s MUST be run on the master node. Is this the master"
118
                " node and are ALL instances down?" % program)
119
    if not cli.AskUser(usertext):
120
      sys.exit(constants.EXIT_FAILURE)
121

    
122
  # Check whether it's a Ganeti configuration directory
123
  if not (os.path.isfile(options.CONFIG_DATA_PATH) and
124
          os.path.isfile(options.SERVER_PEM_PATH) and
125
          os.path.isfile(options.KNOWN_HOSTS_PATH)):
126
    raise Error(("%s does not seem to be a Ganeti configuration"
127
                 " directory") % options.data_dir)
128

    
129
  config_data = serializer.LoadJson(utils.ReadFile(options.CONFIG_DATA_PATH))
130

    
131
  try:
132
    config_version = config_data["version"]
133
  except KeyError:
134
    raise Error("Unable to determine configuration version")
135

    
136
  (config_major, config_minor, config_revision) = \
137
    constants.SplitVersion(config_version)
138

    
139
  logging.info("Found configuration version %s (%d.%d.%d)",
140
               config_version, config_major, config_minor, config_revision)
141

    
142
  if "config_version" in config_data["cluster"]:
143
    raise Error("Inconsistent configuration: found config_version in"
144
                " configuration file")
145

    
146
  # Upgrade from 2.0/2.1/2.2 to 2.3
147
  if config_major == 2 and config_minor in (0, 1, 2):
148
    if config_revision != 0:
149
      logging.warning("Config revision is %s, not 0", config_revision)
150

    
151
    config_data["version"] = constants.BuildVersion(2, 3, 0)
152

    
153
  elif config_major == 2 and config_minor == 3:
154
    logging.info("No changes necessary")
155

    
156
  else:
157
    raise Error("Configuration version %d.%d.%d not supported by this tool" %
158
                (config_major, config_minor, config_revision))
159

    
160
  if os.path.isfile(options.RAPI_USERS_FILE_PRE24):
161
    logging.info("Found pre-2.4 RAPI users file at %s, renaming to %s",
162
                 options.RAPI_USERS_FILE_PRE24, options.RAPI_USERS_FILE)
163
    utils.RenameFile(options.RAPI_USERS_FILE_PRE24, options.RAPI_USERS_FILE,
164
                     mkdir=True, mkdir_mode=0750)
165

    
166
  # Create a symlink for RAPI users file
167
  if not os.path.islink(options.RAPI_USERS_FILE_PRE24):
168
    logging.info("Creating symlink from %s to %s",
169
                 options.RAPI_USERS_FILE_PRE24, options.RAPI_USERS_FILE)
170
    os.symlink(options.RAPI_USERS_FILE, options.RAPI_USERS_FILE_PRE24)
171

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

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

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

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

    
206

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

    
210
# vim: set foldmethod=marker :