Statistics
| Branch: | Tag: | Revision:

root / tools / cfgupgrade @ ec5c88dc

History | View | Annotate | Download (6.3 kB)

1 0006af7d Michael Hanselmann
#!/usr/bin/python
2 0006af7d Michael Hanselmann
#
3 0006af7d Michael Hanselmann
4 f97c7901 Michael Hanselmann
# Copyright (C) 2007, 2008 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 319856a9 Michael Hanselmann
This code handles only the types supported by simplejson. As an example, "set"
25 e0732b36 Michael Hanselmann
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 0006af7d Michael Hanselmann
import tempfile
35 eda37a5a Michael Hanselmann
import logging
36 95e4a814 Michael Hanselmann
import errno
37 0006af7d Michael Hanselmann
38 95e4a814 Michael Hanselmann
from ganeti import constants
39 95e4a814 Michael Hanselmann
from ganeti import serializer
40 319856a9 Michael Hanselmann
from ganeti import utils
41 f97c7901 Michael Hanselmann
from ganeti import cli
42 0006af7d Michael Hanselmann
43 0006af7d Michael Hanselmann
44 95e4a814 Michael Hanselmann
# We need to keep filenames locally because they might be renamed between
45 95e4a814 Michael Hanselmann
# versions.
46 95e4a814 Michael Hanselmann
CONFIG_DATA_PATH = constants.DATA_DIR + "/config.data"
47 95e4a814 Michael Hanselmann
SERVER_PEM_PATH = constants.DATA_DIR + "/server.pem"
48 95e4a814 Michael Hanselmann
KNOWN_HOSTS_PATH = constants.DATA_DIR + "/known_hosts"
49 95e4a814 Michael Hanselmann
SSCONF_CLUSTER_NAME_PATH = constants.DATA_DIR + "/ssconf_cluster_name"
50 95e4a814 Michael Hanselmann
SSCONF_CONFIG_VERSION_PATH = constants.DATA_DIR + "/ssconf_config_version"
51 95e4a814 Michael Hanselmann
52 319856a9 Michael Hanselmann
options = None
53 319856a9 Michael Hanselmann
args = None
54 0006af7d Michael Hanselmann
55 95e4a814 Michael Hanselmann
# Unique object to identify calls without default value
56 95e4a814 Michael Hanselmann
NoDefault = object()
57 95e4a814 Michael Hanselmann
58 0006af7d Michael Hanselmann
59 319856a9 Michael Hanselmann
class Error(Exception):
60 319856a9 Michael Hanselmann
  """Generic exception"""
61 319856a9 Michael Hanselmann
  pass
62 0006af7d Michael Hanselmann
63 0006af7d Michael Hanselmann
64 95e4a814 Michael Hanselmann
def ReadFile(file_name, default=NoDefault):
65 95e4a814 Michael Hanselmann
  """Reads a file.
66 0006af7d Michael Hanselmann
67 0006af7d Michael Hanselmann
  """
68 95e4a814 Michael Hanselmann
  logging.debug("Reading %s", file_name)
69 319856a9 Michael Hanselmann
  try:
70 95e4a814 Michael Hanselmann
    fh = open(file_name, 'r')
71 95e4a814 Michael Hanselmann
  except IOError, err:
72 95e4a814 Michael Hanselmann
    if default is not NoDefault and err.errno == errno.ENOENT:
73 95e4a814 Michael Hanselmann
      return default
74 95e4a814 Michael Hanselmann
    raise
75 0006af7d Michael Hanselmann
76 0006af7d Michael Hanselmann
  try:
77 95e4a814 Michael Hanselmann
    return fh.read()
78 0006af7d Michael Hanselmann
  finally:
79 95e4a814 Michael Hanselmann
    fh.close()
80 0006af7d Michael Hanselmann
81 0006af7d Michael Hanselmann
82 95e4a814 Michael Hanselmann
def WriteFile(file_name, data):
83 95e4a814 Michael Hanselmann
  """Writes a configuration file.
84 0006af7d Michael Hanselmann
85 0006af7d Michael Hanselmann
  """
86 95e4a814 Michael Hanselmann
  logging.debug("Writing %s", file_name)
87 95e4a814 Michael Hanselmann
  utils.WriteFile(file_name=file_name, data=data, mode=0600,
88 95e4a814 Michael Hanselmann
                  dry_run=options.dry_run, backup=True)
89 0006af7d Michael Hanselmann
90 0006af7d Michael Hanselmann
91 eda37a5a Michael Hanselmann
def SetupLogging():
92 eda37a5a Michael Hanselmann
  """Configures the logging module.
93 eda37a5a Michael Hanselmann
94 eda37a5a Michael Hanselmann
  """
95 eda37a5a Michael Hanselmann
  formatter = logging.Formatter("%(asctime)s: %(message)s")
96 eda37a5a Michael Hanselmann
97 eda37a5a Michael Hanselmann
  stderr_handler = logging.StreamHandler()
98 eda37a5a Michael Hanselmann
  stderr_handler.setFormatter(formatter)
99 eda37a5a Michael Hanselmann
  if options.debug:
100 eda37a5a Michael Hanselmann
    stderr_handler.setLevel(logging.NOTSET)
101 eda37a5a Michael Hanselmann
  elif options.verbose:
102 eda37a5a Michael Hanselmann
    stderr_handler.setLevel(logging.INFO)
103 eda37a5a Michael Hanselmann
  else:
104 eda37a5a Michael Hanselmann
    stderr_handler.setLevel(logging.CRITICAL)
105 eda37a5a Michael Hanselmann
106 eda37a5a Michael Hanselmann
  root_logger = logging.getLogger("")
107 eda37a5a Michael Hanselmann
  root_logger.setLevel(logging.NOTSET)
108 eda37a5a Michael Hanselmann
  root_logger.addHandler(stderr_handler)
109 eda37a5a Michael Hanselmann
110 eda37a5a Michael Hanselmann
111 6d691282 Michael Hanselmann
def main():
112 6d691282 Michael Hanselmann
  """Main program.
113 6d691282 Michael Hanselmann
114 6d691282 Michael Hanselmann
  """
115 6d691282 Michael Hanselmann
  global options, args
116 6d691282 Michael Hanselmann
117 319856a9 Michael Hanselmann
  program = os.path.basename(sys.argv[0])
118 319856a9 Michael Hanselmann
119 0006af7d Michael Hanselmann
  # Option parsing
120 95e4a814 Michael Hanselmann
  parser = optparse.OptionParser(usage="%prog [--debug|--verbose] [--force]")
121 60edf71e Michael Hanselmann
  parser.add_option('--dry-run', dest='dry_run',
122 60edf71e Michael Hanselmann
                    action="store_true",
123 f4bc1f2c Michael Hanselmann
                    help="Try to do the conversion, but don't write"
124 f4bc1f2c Michael Hanselmann
                         " output file")
125 f97c7901 Michael Hanselmann
  parser.add_option(cli.FORCE_OPT)
126 eda37a5a Michael Hanselmann
  parser.add_option(cli.DEBUG_OPT)
127 0006af7d Michael Hanselmann
  parser.add_option('--verbose', dest='verbose',
128 0006af7d Michael Hanselmann
                    action="store_true",
129 0006af7d Michael Hanselmann
                    help="Verbose output")
130 0006af7d Michael Hanselmann
  (options, args) = parser.parse_args()
131 0006af7d Michael Hanselmann
132 eda37a5a Michael Hanselmann
  SetupLogging()
133 eda37a5a Michael Hanselmann
134 0006af7d Michael Hanselmann
  # Option checking
135 0006af7d Michael Hanselmann
  if args:
136 95e4a814 Michael Hanselmann
    raise Error("No arguments expected")
137 0006af7d Michael Hanselmann
138 319856a9 Michael Hanselmann
  if not options.force:
139 f4bc1f2c Michael Hanselmann
    usertext = ("%s MUST run on the master node. Is this the master"
140 f4bc1f2c Michael Hanselmann
                " node?" % program)
141 f97c7901 Michael Hanselmann
    if not cli.AskUser(usertext):
142 319856a9 Michael Hanselmann
      sys.exit(1)
143 319856a9 Michael Hanselmann
144 95e4a814 Michael Hanselmann
  # Check whether it's a Ganeti configuration directory
145 95e4a814 Michael Hanselmann
  if not (os.path.isfile(CONFIG_DATA_PATH) and
146 95e4a814 Michael Hanselmann
          os.path.isfile(SERVER_PEM_PATH) or
147 95e4a814 Michael Hanselmann
          os.path.isfile(KNOWN_HOSTS_PATH)):
148 95e4a814 Michael Hanselmann
    raise Error(("%s does not seem to be a known Ganeti configuration"
149 95e4a814 Michael Hanselmann
                 " directory") % constants.DATA_DIR)
150 95e4a814 Michael Hanselmann
151 95e4a814 Michael Hanselmann
  config_version = ReadFile(SSCONF_CONFIG_VERSION_PATH, "1.2").strip()
152 95e4a814 Michael Hanselmann
  logging.info("Found configuration version %s", config_version)
153 95e4a814 Michael Hanselmann
154 95e4a814 Michael Hanselmann
  config_data = serializer.LoadJson(ReadFile(CONFIG_DATA_PATH))
155 95e4a814 Michael Hanselmann
156 95e4a814 Michael Hanselmann
  # Ganeti 1.2?
157 95e4a814 Michael Hanselmann
  if config_version == "1.2":
158 95e4a814 Michael Hanselmann
    logging.info("Found a Ganeti 1.2 configuration")
159 95e4a814 Michael Hanselmann
160 95e4a814 Michael Hanselmann
    old_config_version = config_data["cluster"].get("config_version", None)
161 95e4a814 Michael Hanselmann
    logging.info("Found old configuration version %s", old_config_version)
162 95e4a814 Michael Hanselmann
    if old_config_version not in (3, ):
163 95e4a814 Michael Hanselmann
      raise Error("Unsupported configuration version: %s" %
164 95e4a814 Michael Hanselmann
                  old_config_version)
165 95e4a814 Michael Hanselmann
166 95e4a814 Michael Hanselmann
    # Make sure no instance uses remote_raid1 anymore
167 95e4a814 Michael Hanselmann
    remote_raid1_instances = []
168 95e4a814 Michael Hanselmann
    for instance in config_data["instances"]:
169 95e4a814 Michael Hanselmann
      if instance["disk_template"] == "remote_raid1":
170 95e4a814 Michael Hanselmann
        remote_raid1_instances.append(instance["name"])
171 95e4a814 Michael Hanselmann
    if remote_raid1_instances:
172 95e4a814 Michael Hanselmann
      for name in remote_raid1_instances:
173 95e4a814 Michael Hanselmann
        logging.error("Instance %s still using remote_raid1 disk template")
174 95e4a814 Michael Hanselmann
      raise Error("Unable to convert configuration as long as there are"
175 95e4a814 Michael Hanselmann
                  " instances using remote_raid1 disk template")
176 95e4a814 Michael Hanselmann
177 95e4a814 Michael Hanselmann
    # The configuration version will be stored in a ssconf file
178 95e4a814 Michael Hanselmann
    if 'config_version' in config_data['cluster']:
179 95e4a814 Michael Hanselmann
      del config_data['cluster']['config_version']
180 95e4a814 Michael Hanselmann
181 95e4a814 Michael Hanselmann
    # Build content of new known_hosts file
182 95e4a814 Michael Hanselmann
    cluster_name = ReadFile(SSCONF_CLUSTER_NAME_PATH).rstrip()
183 95e4a814 Michael Hanselmann
    cluster_key = config_data['cluster']['rsahostkeypub']
184 95e4a814 Michael Hanselmann
    known_hosts = "%s ssh-rsa %s\n" % (cluster_name, cluster_key)
185 0006af7d Michael Hanselmann
186 95e4a814 Michael Hanselmann
  else:
187 95e4a814 Michael Hanselmann
    logging.info("Found a Ganeti 2.0 configuration")
188 0006af7d Michael Hanselmann
189 95e4a814 Michael Hanselmann
    if "config_version" in config_data["cluster"]:
190 95e4a814 Michael Hanselmann
      raise Error("Inconsistent configuration: found config_data in"
191 95e4a814 Michael Hanselmann
                  " configuration file")
192 319856a9 Michael Hanselmann
193 95e4a814 Michael Hanselmann
    known_hosts = None
194 319856a9 Michael Hanselmann
195 95e4a814 Michael Hanselmann
  config_version_str = "%s\n" % constants.BuildVersion(2, 0, 0)
196 95e4a814 Michael Hanselmann
  try:
197 95e4a814 Michael Hanselmann
    logging.info("Writing configuration file")
198 95e4a814 Michael Hanselmann
    WriteFile(CONFIG_DATA_PATH, serializer.DumpJson(config_data))
199 95e4a814 Michael Hanselmann
200 95e4a814 Michael Hanselmann
    logging.info("Writing configuration version %s",
201 95e4a814 Michael Hanselmann
                 config_version_str.strip())
202 95e4a814 Michael Hanselmann
    WriteFile(SSCONF_CONFIG_VERSION_PATH, config_version_str)
203 95e4a814 Michael Hanselmann
204 95e4a814 Michael Hanselmann
    if known_hosts is not None:
205 95e4a814 Michael Hanselmann
      logging.info("Writing SSH known_hosts file (%s)", known_hosts.strip())
206 95e4a814 Michael Hanselmann
      WriteFile(KNOWN_HOSTS_PATH, known_hosts)
207 95e4a814 Michael Hanselmann
  except:
208 95e4a814 Michael Hanselmann
    logging.critical("Writing configuration failed. It is proably in an"
209 95e4a814 Michael Hanselmann
                     " inconsistent state and needs manual intervention.")
210 95e4a814 Michael Hanselmann
    raise
211 0006af7d Michael Hanselmann
212 6d691282 Michael Hanselmann
213 6d691282 Michael Hanselmann
if __name__ == "__main__":
214 6d691282 Michael Hanselmann
  main()
215 6d691282 Michael Hanselmann
216 319856a9 Michael Hanselmann
# vim: set foldmethod=marker :