Statistics
| Branch: | Tag: | Revision:

root / tools / cfgupgrade @ 72bb6b4e

History | View | Annotate | Download (9.3 kB)

1 0006af7d Michael Hanselmann
#!/usr/bin/python
2 0006af7d Michael Hanselmann
#
3 0006af7d Michael Hanselmann
4 aeefe835 Iustin Pop
# Copyright (C) 2007, 2008, 2009, 2010 Google Inc.
5 0006af7d Michael Hanselmann
#
6 0006af7d Michael Hanselmann
# This program is free software; you can redistribute it and/or modify
7 0006af7d Michael Hanselmann
# it under the terms of the GNU General Public License as published by
8 0006af7d Michael Hanselmann
# the Free Software Foundation; either version 2 of the License, or
9 0006af7d Michael Hanselmann
# (at your option) any later version.
10 0006af7d Michael Hanselmann
#
11 0006af7d Michael Hanselmann
# This program is distributed in the hope that it will be useful, but
12 0006af7d Michael Hanselmann
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 0006af7d Michael Hanselmann
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 0006af7d Michael Hanselmann
# General Public License for more details.
15 0006af7d Michael Hanselmann
#
16 0006af7d Michael Hanselmann
# You should have received a copy of the GNU General Public License
17 0006af7d Michael Hanselmann
# along with this program; if not, write to the Free Software
18 0006af7d Michael Hanselmann
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 0006af7d Michael Hanselmann
# 02110-1301, USA.
20 0006af7d Michael Hanselmann
21 0006af7d Michael Hanselmann
22 0006af7d Michael Hanselmann
"""Tool to upgrade the configuration file.
23 0006af7d Michael Hanselmann
24 a421fdeb Iustin Pop
This code handles only the types supported by simplejson. As an
25 a421fdeb Iustin Pop
example, 'set' is a 'list'.
26 0006af7d Michael Hanselmann
27 0006af7d Michael Hanselmann
"""
28 0006af7d Michael Hanselmann
29 0006af7d Michael Hanselmann
30 0006af7d Michael Hanselmann
import os
31 0006af7d Michael Hanselmann
import os.path
32 0006af7d Michael Hanselmann
import sys
33 0006af7d Michael Hanselmann
import optparse
34 eda37a5a Michael Hanselmann
import logging
35 0006af7d Michael Hanselmann
36 95e4a814 Michael Hanselmann
from ganeti import constants
37 95e4a814 Michael Hanselmann
from ganeti import serializer
38 319856a9 Michael Hanselmann
from ganeti import utils
39 f97c7901 Michael Hanselmann
from ganeti import cli
40 a421fdeb Iustin Pop
from ganeti import bootstrap
41 ac4d25b6 Iustin Pop
from ganeti import config
42 011974df Michael Hanselmann
from ganeti import netutils
43 0006af7d Michael Hanselmann
44 0006af7d Michael Hanselmann
45 319856a9 Michael Hanselmann
options = None
46 319856a9 Michael Hanselmann
args = None
47 0006af7d Michael Hanselmann
48 6f285030 Iustin Pop
49 319856a9 Michael Hanselmann
class Error(Exception):
50 319856a9 Michael Hanselmann
  """Generic exception"""
51 319856a9 Michael Hanselmann
  pass
52 0006af7d Michael Hanselmann
53 0006af7d Michael Hanselmann
54 eda37a5a Michael Hanselmann
def SetupLogging():
55 eda37a5a Michael Hanselmann
  """Configures the logging module.
56 eda37a5a Michael Hanselmann
57 eda37a5a Michael Hanselmann
  """
58 eda37a5a Michael Hanselmann
  formatter = logging.Formatter("%(asctime)s: %(message)s")
59 eda37a5a Michael Hanselmann
60 eda37a5a Michael Hanselmann
  stderr_handler = logging.StreamHandler()
61 eda37a5a Michael Hanselmann
  stderr_handler.setFormatter(formatter)
62 eda37a5a Michael Hanselmann
  if options.debug:
63 eda37a5a Michael Hanselmann
    stderr_handler.setLevel(logging.NOTSET)
64 eda37a5a Michael Hanselmann
  elif options.verbose:
65 eda37a5a Michael Hanselmann
    stderr_handler.setLevel(logging.INFO)
66 eda37a5a Michael Hanselmann
  else:
67 011974df Michael Hanselmann
    stderr_handler.setLevel(logging.WARNING)
68 eda37a5a Michael Hanselmann
69 eda37a5a Michael Hanselmann
  root_logger = logging.getLogger("")
70 eda37a5a Michael Hanselmann
  root_logger.setLevel(logging.NOTSET)
71 eda37a5a Michael Hanselmann
  root_logger.addHandler(stderr_handler)
72 eda37a5a Michael Hanselmann
73 eda37a5a Michael Hanselmann
74 011974df Michael Hanselmann
def CheckHostname(path):
75 011974df Michael Hanselmann
  """Ensures hostname matches ssconf value.
76 011974df Michael Hanselmann
77 011974df Michael Hanselmann
  @param path: Path to ssconf file
78 011974df Michael Hanselmann
79 011974df Michael Hanselmann
  """
80 011974df Michael Hanselmann
  ssconf_master_node = utils.ReadOneLineFile(path)
81 011974df Michael Hanselmann
  hostname = netutils.GetHostname().name
82 011974df Michael Hanselmann
83 011974df Michael Hanselmann
  if ssconf_master_node == hostname:
84 011974df Michael Hanselmann
    return True
85 011974df Michael Hanselmann
86 011974df Michael Hanselmann
  logging.warning("Warning: ssconf says master node is '%s', but this"
87 011974df Michael Hanselmann
                  " machine's name is '%s'; this tool must be run on"
88 011974df Michael Hanselmann
                  " the master node", ssconf_master_node, hostname)
89 011974df Michael Hanselmann
  return False
90 011974df Michael Hanselmann
91 011974df Michael Hanselmann
92 6d691282 Michael Hanselmann
def main():
93 6d691282 Michael Hanselmann
  """Main program.
94 6d691282 Michael Hanselmann
95 6d691282 Michael Hanselmann
  """
96 b459a848 Andrea Spadaccini
  global options, args # pylint: disable=W0603
97 6d691282 Michael Hanselmann
98 0006af7d Michael Hanselmann
  # Option parsing
99 95e4a814 Michael Hanselmann
  parser = optparse.OptionParser(usage="%prog [--debug|--verbose] [--force]")
100 60edf71e Michael Hanselmann
  parser.add_option('--dry-run', dest='dry_run',
101 60edf71e Michael Hanselmann
                    action="store_true",
102 f4bc1f2c Michael Hanselmann
                    help="Try to do the conversion, but don't write"
103 f4bc1f2c Michael Hanselmann
                         " output file")
104 f97c7901 Michael Hanselmann
  parser.add_option(cli.FORCE_OPT)
105 eda37a5a Michael Hanselmann
  parser.add_option(cli.DEBUG_OPT)
106 9cdb9578 Iustin Pop
  parser.add_option(cli.VERBOSE_OPT)
107 011974df Michael Hanselmann
  parser.add_option("--ignore-hostname", dest="ignore_hostname",
108 011974df Michael Hanselmann
                    action="store_true", default=False,
109 011974df Michael Hanselmann
                    help="Don't abort if hostname doesn't match")
110 ac4d25b6 Iustin Pop
  parser.add_option('--path', help="Convert configuration in this"
111 ac4d25b6 Iustin Pop
                    " directory instead of '%s'" % constants.DATA_DIR,
112 ac4d25b6 Iustin Pop
                    default=constants.DATA_DIR, dest="data_dir")
113 02e1292d Michael Hanselmann
  parser.add_option("--no-verify",
114 02e1292d Michael Hanselmann
                    help="Do not verify configuration after upgrade",
115 02e1292d Michael Hanselmann
                    action="store_true", dest="no_verify", default=False)
116 0006af7d Michael Hanselmann
  (options, args) = parser.parse_args()
117 0006af7d Michael Hanselmann
118 ac4d25b6 Iustin Pop
  # We need to keep filenames locally because they might be renamed between
119 ac4d25b6 Iustin Pop
  # versions.
120 0cddd44d Iustin Pop
  options.data_dir = os.path.abspath(options.data_dir)
121 ac4d25b6 Iustin Pop
  options.CONFIG_DATA_PATH = options.data_dir + "/config.data"
122 ac4d25b6 Iustin Pop
  options.SERVER_PEM_PATH = options.data_dir + "/server.pem"
123 ac4d25b6 Iustin Pop
  options.KNOWN_HOSTS_PATH = options.data_dir + "/known_hosts"
124 ac4d25b6 Iustin Pop
  options.RAPI_CERT_FILE = options.data_dir + "/rapi.pem"
125 b6267745 Andrea Spadaccini
  options.SPICE_CERT_FILE = options.data_dir + "/spice.pem"
126 b6267745 Andrea Spadaccini
  options.SPICE_CACERT_FILE = options.data_dir + "/spice-ca.pem"
127 fdd9ac5b Michael Hanselmann
  options.RAPI_USERS_FILE = options.data_dir + "/rapi/users"
128 fdd9ac5b Michael Hanselmann
  options.RAPI_USERS_FILE_PRE24 = options.data_dir + "/rapi_users"
129 6b7d5878 Michael Hanselmann
  options.CONFD_HMAC_KEY = options.data_dir + "/hmac.key"
130 fc0726b9 Michael Hanselmann
  options.CDS_FILE = options.data_dir + "/cluster-domain-secret"
131 011974df Michael Hanselmann
  options.SSCONF_MASTER_NODE = options.data_dir + "/ssconf_master_node"
132 a292020f Michael Hanselmann
  options.WATCHER_STATEFILE = options.data_dir + "/watcher.data"
133 ac4d25b6 Iustin Pop
134 eda37a5a Michael Hanselmann
  SetupLogging()
135 eda37a5a Michael Hanselmann
136 0006af7d Michael Hanselmann
  # Option checking
137 0006af7d Michael Hanselmann
  if args:
138 95e4a814 Michael Hanselmann
    raise Error("No arguments expected")
139 0006af7d Michael Hanselmann
140 011974df Michael Hanselmann
  # Check master name
141 011974df Michael Hanselmann
  if not (CheckHostname(options.SSCONF_MASTER_NODE) or options.ignore_hostname):
142 011974df Michael Hanselmann
    logging.error("Aborting due to hostname mismatch")
143 011974df Michael Hanselmann
    sys.exit(constants.EXIT_FAILURE)
144 011974df Michael Hanselmann
145 319856a9 Michael Hanselmann
  if not options.force:
146 011974df Michael Hanselmann
    usertext = ("Please make sure you have read the upgrade notes for"
147 011974df Michael Hanselmann
                " Ganeti %s (available in the UPGRADE file and included"
148 011974df Michael Hanselmann
                " in other documentation formats). Continue with upgrading"
149 011974df Michael Hanselmann
                " configuration?" % constants.RELEASE_VERSION)
150 f97c7901 Michael Hanselmann
    if not cli.AskUser(usertext):
151 a9221f09 Michael Hanselmann
      sys.exit(constants.EXIT_FAILURE)
152 319856a9 Michael Hanselmann
153 95e4a814 Michael Hanselmann
  # Check whether it's a Ganeti configuration directory
154 ac4d25b6 Iustin Pop
  if not (os.path.isfile(options.CONFIG_DATA_PATH) and
155 30acff6c Michael Hanselmann
          os.path.isfile(options.SERVER_PEM_PATH) and
156 ac4d25b6 Iustin Pop
          os.path.isfile(options.KNOWN_HOSTS_PATH)):
157 a9221f09 Michael Hanselmann
    raise Error(("%s does not seem to be a Ganeti configuration"
158 ac4d25b6 Iustin Pop
                 " directory") % options.data_dir)
159 95e4a814 Michael Hanselmann
160 11c31f5c Michael Hanselmann
  config_data = serializer.LoadJson(utils.ReadFile(options.CONFIG_DATA_PATH))
161 a421fdeb Iustin Pop
162 11c31f5c Michael Hanselmann
  try:
163 11c31f5c Michael Hanselmann
    config_version = config_data["version"]
164 11c31f5c Michael Hanselmann
  except KeyError:
165 11c31f5c Michael Hanselmann
    raise Error("Unable to determine configuration version")
166 0006af7d Michael Hanselmann
167 11c31f5c Michael Hanselmann
  (config_major, config_minor, config_revision) = \
168 11c31f5c Michael Hanselmann
    constants.SplitVersion(config_version)
169 319856a9 Michael Hanselmann
170 11c31f5c Michael Hanselmann
  logging.info("Found configuration version %s (%d.%d.%d)",
171 11c31f5c Michael Hanselmann
               config_version, config_major, config_minor, config_revision)
172 319856a9 Michael Hanselmann
173 11c31f5c Michael Hanselmann
  if "config_version" in config_data["cluster"]:
174 11c31f5c Michael Hanselmann
    raise Error("Inconsistent configuration: found config_version in"
175 11c31f5c Michael Hanselmann
                " configuration file")
176 95e4a814 Michael Hanselmann
177 a91f69c4 Michael Hanselmann
  # Upgrade from 2.0/2.1/2.2/2.3 to 2.4
178 9f604ab8 Michael Hanselmann
  if config_major == 2 and config_minor in (0, 1, 2, 3, 4):
179 aeb0c953 Michael Hanselmann
    if config_revision != 0:
180 a9221f09 Michael Hanselmann
      logging.warning("Config revision is %s, not 0", config_revision)
181 aeb0c953 Michael Hanselmann
182 9f604ab8 Michael Hanselmann
    config_data["version"] = constants.BuildVersion(2, 5, 0)
183 a9221f09 Michael Hanselmann
184 9f604ab8 Michael Hanselmann
  elif config_major == 2 and config_minor == 5:
185 a9221f09 Michael Hanselmann
    logging.info("No changes necessary")
186 a9221f09 Michael Hanselmann
187 a9221f09 Michael Hanselmann
  else:
188 a9221f09 Michael Hanselmann
    raise Error("Configuration version %d.%d.%d not supported by this tool" %
189 a9221f09 Michael Hanselmann
                (config_major, config_minor, config_revision))
190 aeb0c953 Michael Hanselmann
191 87c80992 Michael Hanselmann
  if (os.path.isfile(options.RAPI_USERS_FILE_PRE24) and
192 87c80992 Michael Hanselmann
      not os.path.islink(options.RAPI_USERS_FILE_PRE24)):
193 87c80992 Michael Hanselmann
    if os.path.exists(options.RAPI_USERS_FILE):
194 87c80992 Michael Hanselmann
      raise Error("Found pre-2.4 RAPI users file at %s, but another file"
195 87c80992 Michael Hanselmann
                  " already exists at %s" %
196 87c80992 Michael Hanselmann
                  (options.RAPI_USERS_FILE_PRE24, options.RAPI_USERS_FILE))
197 fdd9ac5b Michael Hanselmann
    logging.info("Found pre-2.4 RAPI users file at %s, renaming to %s",
198 fdd9ac5b Michael Hanselmann
                 options.RAPI_USERS_FILE_PRE24, options.RAPI_USERS_FILE)
199 87c80992 Michael Hanselmann
    if not options.dry_run:
200 87c80992 Michael Hanselmann
      utils.RenameFile(options.RAPI_USERS_FILE_PRE24, options.RAPI_USERS_FILE,
201 87c80992 Michael Hanselmann
                       mkdir=True, mkdir_mode=0750)
202 fdd9ac5b Michael Hanselmann
203 fdd9ac5b Michael Hanselmann
  # Create a symlink for RAPI users file
204 87c80992 Michael Hanselmann
  if (not (os.path.islink(options.RAPI_USERS_FILE_PRE24) or
205 87c80992 Michael Hanselmann
           os.path.isfile(options.RAPI_USERS_FILE_PRE24)) and
206 87c80992 Michael Hanselmann
      os.path.isfile(options.RAPI_USERS_FILE)):
207 fdd9ac5b Michael Hanselmann
    logging.info("Creating symlink from %s to %s",
208 fdd9ac5b Michael Hanselmann
                 options.RAPI_USERS_FILE_PRE24, options.RAPI_USERS_FILE)
209 87c80992 Michael Hanselmann
    if not options.dry_run:
210 87c80992 Michael Hanselmann
      os.symlink(options.RAPI_USERS_FILE, options.RAPI_USERS_FILE_PRE24)
211 fdd9ac5b Michael Hanselmann
212 a292020f Michael Hanselmann
  # Remove old watcher state file if it exists
213 a292020f Michael Hanselmann
  if os.path.exists(options.WATCHER_STATEFILE):
214 a292020f Michael Hanselmann
    logging.info("Removing watcher state file %s", options.WATCHER_STATEFILE)
215 a292020f Michael Hanselmann
    if not options.dry_run:
216 a292020f Michael Hanselmann
      utils.RemoveFile(options.WATCHER_STATEFILE)
217 a292020f Michael Hanselmann
218 11c31f5c Michael Hanselmann
  try:
219 11c31f5c Michael Hanselmann
    logging.info("Writing configuration file to %s", options.CONFIG_DATA_PATH)
220 11c31f5c Michael Hanselmann
    utils.WriteFile(file_name=options.CONFIG_DATA_PATH,
221 11c31f5c Michael Hanselmann
                    data=serializer.DumpJson(config_data),
222 11c31f5c Michael Hanselmann
                    mode=0600,
223 11c31f5c Michael Hanselmann
                    dry_run=options.dry_run,
224 11c31f5c Michael Hanselmann
                    backup=True)
225 a421fdeb Iustin Pop
226 a421fdeb Iustin Pop
    if not options.dry_run:
227 b6267745 Andrea Spadaccini
      bootstrap.GenerateClusterCrypto(False, False, False, False, False,
228 b6267745 Andrea Spadaccini
                                     nodecert_file=options.SERVER_PEM_PATH,
229 b6267745 Andrea Spadaccini
                                     rapicert_file=options.RAPI_CERT_FILE,
230 b6267745 Andrea Spadaccini
                                     spicecert_file=options.SPICE_CERT_FILE,
231 b6267745 Andrea Spadaccini
                                     spicecacert_file=options.SPICE_CACERT_FILE,
232 b6267745 Andrea Spadaccini
                                     hmackey_file=options.CONFD_HMAC_KEY,
233 b6267745 Andrea Spadaccini
                                     cds_file=options.CDS_FILE)
234 aeb0c953 Michael Hanselmann
235 a9221f09 Michael Hanselmann
  except Exception:
236 11c31f5c Michael Hanselmann
    logging.critical("Writing configuration failed. It is probably in an"
237 95e4a814 Michael Hanselmann
                     " inconsistent state and needs manual intervention.")
238 95e4a814 Michael Hanselmann
    raise
239 0006af7d Michael Hanselmann
240 ac4d25b6 Iustin Pop
  # test loading the config file
241 02e1292d Michael Hanselmann
  if not (options.dry_run or options.no_verify):
242 ac4d25b6 Iustin Pop
    logging.info("Testing the new config file...")
243 ac4d25b6 Iustin Pop
    cfg = config.ConfigWriter(cfg_file=options.CONFIG_DATA_PATH,
244 959b6fe5 Apollon Oikonomopoulos
                              accept_foreign=options.ignore_hostname,
245 ac4d25b6 Iustin Pop
                              offline=True)
246 ac4d25b6 Iustin Pop
    # if we reached this, it's all fine
247 ac4d25b6 Iustin Pop
    vrfy = cfg.VerifyConfig()
248 ac4d25b6 Iustin Pop
    if vrfy:
249 ac4d25b6 Iustin Pop
      logging.error("Errors after conversion:")
250 ac4d25b6 Iustin Pop
      for item in vrfy:
251 07b8a2b5 Iustin Pop
        logging.error(" - %s", item)
252 ac4d25b6 Iustin Pop
    del cfg
253 ac4d25b6 Iustin Pop
    logging.info("File loaded successfully")
254 ac4d25b6 Iustin Pop
255 66a66fa7 Michael Hanselmann
  cli.ToStderr("Configuration successfully upgraded for version %s.",
256 66a66fa7 Michael Hanselmann
               constants.RELEASE_VERSION)
257 66a66fa7 Michael Hanselmann
258 6d691282 Michael Hanselmann
259 6d691282 Michael Hanselmann
if __name__ == "__main__":
260 6d691282 Michael Hanselmann
  main()