Statistics
| Branch: | Tag: | Revision:

root / tools / cfgupgrade @ 93fd9bb1

History | View | Annotate | Download (9.5 kB)

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