Statistics
| Branch: | Tag: | Revision:

root / tools / sanitize-config @ 1fe10404

History | View | Annotate | Download (8.5 kB)

1 ea5fd476 Iustin Pop
#!/usr/bin/python
2 ea5fd476 Iustin Pop
#
3 ea5fd476 Iustin Pop
4 ea5fd476 Iustin Pop
# Copyright (C) 2010 Google Inc.
5 ea5fd476 Iustin Pop
#
6 ea5fd476 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 ea5fd476 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 ea5fd476 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 ea5fd476 Iustin Pop
# (at your option) any later version.
10 ea5fd476 Iustin Pop
#
11 ea5fd476 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 ea5fd476 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 ea5fd476 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 ea5fd476 Iustin Pop
# General Public License for more details.
15 ea5fd476 Iustin Pop
#
16 ea5fd476 Iustin Pop
# You should have received a copy of the GNU General Public License
17 ea5fd476 Iustin Pop
# along with this program; if not, write to the Free Software
18 ea5fd476 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 ea5fd476 Iustin Pop
# 02110-1301, USA.
20 ea5fd476 Iustin Pop
21 ea5fd476 Iustin Pop
22 b459a848 Andrea Spadaccini
# pylint: disable=C0103
23 ea5fd476 Iustin Pop
24 ea5fd476 Iustin Pop
"""Tool to sanitize/randomize the configuration file.
25 ea5fd476 Iustin Pop
26 ea5fd476 Iustin Pop
"""
27 ea5fd476 Iustin Pop
28 ea5fd476 Iustin Pop
import sys
29 ea5fd476 Iustin Pop
import os
30 ea5fd476 Iustin Pop
import os.path
31 ea5fd476 Iustin Pop
import optparse
32 ea5fd476 Iustin Pop
33 ea5fd476 Iustin Pop
from ganeti import constants
34 ea5fd476 Iustin Pop
from ganeti import serializer
35 ea5fd476 Iustin Pop
from ganeti import utils
36 09bf5d24 Michael Hanselmann
from ganeti import pathutils
37 ea5fd476 Iustin Pop
from ganeti import cli
38 ea5fd476 Iustin Pop
from ganeti.cli import cli_option
39 ea5fd476 Iustin Pop
40 ea5fd476 Iustin Pop
41 ea5fd476 Iustin Pop
OPTS = [
42 ea5fd476 Iustin Pop
  cli.VERBOSE_OPT,
43 ea5fd476 Iustin Pop
  cli_option("--path", help="Convert this configuration file"
44 09bf5d24 Michael Hanselmann
             " instead of '%s'" % pathutils.CLUSTER_CONF_FILE,
45 09bf5d24 Michael Hanselmann
             default=pathutils.CLUSTER_CONF_FILE, dest="CONFIG_DATA_PATH"),
46 ea5fd476 Iustin Pop
  cli_option("--sanitize-names", default="yes", type="bool",
47 ea5fd476 Iustin Pop
             help="Randomize the cluster, node and instance names [yes]"),
48 ea5fd476 Iustin Pop
  cli_option("--sanitize-ips", default="yes", type="bool",
49 ea5fd476 Iustin Pop
             help="Randomize the cluster, node and instance IPs [yes]"),
50 ea5fd476 Iustin Pop
  cli_option("--sanitize-lvs", default="no", type="bool",
51 ea5fd476 Iustin Pop
             help="Randomize the LV names (for old clusters) [no]"),
52 ea5fd476 Iustin Pop
  cli_option("--sanitize-os-names", default="yes", type="bool",
53 ea5fd476 Iustin Pop
             help="Randomize the OS names [yes]"),
54 ea5fd476 Iustin Pop
  cli_option("--no-randomization", default=False, action="store_true",
55 ea5fd476 Iustin Pop
             help="Disable all name randomization (only randomize secrets)"),
56 ea5fd476 Iustin Pop
  cli_option("--base-domain", default="example.com",
57 ea5fd476 Iustin Pop
             help="The base domain used for new names [example.com]"),
58 ea5fd476 Iustin Pop
  ]
59 ea5fd476 Iustin Pop
60 ea5fd476 Iustin Pop
61 ea5fd476 Iustin Pop
def Error(txt, *args):
62 ea5fd476 Iustin Pop
  """Writes a message to standard error and exits.
63 ea5fd476 Iustin Pop
64 ea5fd476 Iustin Pop
  """
65 ea5fd476 Iustin Pop
  cli.ToStderr(txt, *args)
66 ea5fd476 Iustin Pop
  sys.exit(1)
67 ea5fd476 Iustin Pop
68 ea5fd476 Iustin Pop
69 ea5fd476 Iustin Pop
def GenerateNameMap(opts, names, base):
70 ea5fd476 Iustin Pop
  """For a given set of names, generate a list of sane new names.
71 ea5fd476 Iustin Pop
72 ea5fd476 Iustin Pop
  """
73 ea5fd476 Iustin Pop
  names = utils.NiceSort(names)
74 ea5fd476 Iustin Pop
  name_map = {}
75 ea5fd476 Iustin Pop
  for idx, old_name in enumerate(names):
76 ea5fd476 Iustin Pop
    new_name = "%s%d.%s" % (base, idx + 1, opts.base_domain)
77 ea5fd476 Iustin Pop
    if new_name in names:
78 ea5fd476 Iustin Pop
      Error("Name conflict for %s: %s already exists", base, new_name)
79 ea5fd476 Iustin Pop
    name_map[old_name] = new_name
80 ea5fd476 Iustin Pop
  return name_map
81 ea5fd476 Iustin Pop
82 ea5fd476 Iustin Pop
83 b459a848 Andrea Spadaccini
def SanitizeSecrets(opts, cfg): # pylint: disable=W0613
84 ea5fd476 Iustin Pop
  """Cleanup configuration secrets.
85 ea5fd476 Iustin Pop
86 ea5fd476 Iustin Pop
  """
87 ea5fd476 Iustin Pop
  cfg["cluster"]["rsahostkeypub"] = ""
88 ea5fd476 Iustin Pop
  for instance in cfg["instances"].values():
89 ea5fd476 Iustin Pop
    for disk in instance["disks"]:
90 ea5fd476 Iustin Pop
      RandomizeDiskSecrets(disk)
91 ea5fd476 Iustin Pop
92 ea5fd476 Iustin Pop
93 ea5fd476 Iustin Pop
def SanitizeCluster(opts, cfg):
94 ea5fd476 Iustin Pop
  """Sanitize the cluster names.
95 ea5fd476 Iustin Pop
96 ea5fd476 Iustin Pop
  """
97 ea5fd476 Iustin Pop
  cfg["cluster"]["cluster_name"] = "cluster." + opts.base_domain
98 ea5fd476 Iustin Pop
99 ea5fd476 Iustin Pop
100 ea5fd476 Iustin Pop
def SanitizeNodes(opts, cfg):
101 ea5fd476 Iustin Pop
  """Sanitize node names.
102 ea5fd476 Iustin Pop
103 ea5fd476 Iustin Pop
  """
104 ea5fd476 Iustin Pop
  old_names = cfg["nodes"].keys()
105 ea5fd476 Iustin Pop
  old_map = GenerateNameMap(opts, old_names, "node")
106 ea5fd476 Iustin Pop
107 ea5fd476 Iustin Pop
  # rename nodes
108 ea5fd476 Iustin Pop
  RenameDictKeys(cfg["nodes"], old_map, True)
109 ea5fd476 Iustin Pop
110 ea5fd476 Iustin Pop
  # update master node
111 ea5fd476 Iustin Pop
  cfg["cluster"]["master_node"] = old_map[cfg["cluster"]["master_node"]]
112 ea5fd476 Iustin Pop
113 ea5fd476 Iustin Pop
  # update instance configuration
114 ea5fd476 Iustin Pop
  for instance in cfg["instances"].values():
115 ea5fd476 Iustin Pop
    instance["primary_node"] = old_map[instance["primary_node"]]
116 ea5fd476 Iustin Pop
    for disk in instance["disks"]:
117 ea5fd476 Iustin Pop
      RenameDiskNodes(disk, old_map)
118 ea5fd476 Iustin Pop
119 ea5fd476 Iustin Pop
120 ea5fd476 Iustin Pop
def SanitizeInstances(opts, cfg):
121 ea5fd476 Iustin Pop
  """Sanitize instance names.
122 ea5fd476 Iustin Pop
123 ea5fd476 Iustin Pop
  """
124 ea5fd476 Iustin Pop
  old_names = cfg["instances"].keys()
125 ea5fd476 Iustin Pop
  old_map = GenerateNameMap(opts, old_names, "instance")
126 ea5fd476 Iustin Pop
127 ea5fd476 Iustin Pop
  RenameDictKeys(cfg["instances"], old_map, True)
128 ea5fd476 Iustin Pop
129 ea5fd476 Iustin Pop
130 b459a848 Andrea Spadaccini
def SanitizeIps(opts, cfg): # pylint: disable=W0613
131 ea5fd476 Iustin Pop
  """Sanitize the IP names.
132 ea5fd476 Iustin Pop
133 ea5fd476 Iustin Pop
  @note: we're interested in obscuring the old IPs, not in generating
134 ea5fd476 Iustin Pop
      actually valid new IPs, so we chose to simply put IPv4
135 ea5fd476 Iustin Pop
      addresses, irrelevant of whether IPv6 or IPv4 addresses existed
136 ea5fd476 Iustin Pop
      before.
137 ea5fd476 Iustin Pop
138 ea5fd476 Iustin Pop
  """
139 ea5fd476 Iustin Pop
  def _Get(old):
140 ea5fd476 Iustin Pop
    if old in ip_map:
141 ea5fd476 Iustin Pop
      return ip_map[old]
142 ea5fd476 Iustin Pop
    idx = len(ip_map) + 1
143 ea5fd476 Iustin Pop
    rest, d_octet = divmod(idx, 256)
144 ea5fd476 Iustin Pop
    rest, c_octet = divmod(rest, 256)
145 ea5fd476 Iustin Pop
    rest, b_octet = divmod(rest, 256)
146 ea5fd476 Iustin Pop
    if rest > 0:
147 ea5fd476 Iustin Pop
      Error("Too many IPs!")
148 ea5fd476 Iustin Pop
    new_ip = "%d.%d.%d.%d" % (10, b_octet, c_octet, d_octet)
149 ea5fd476 Iustin Pop
    ip_map[old] = new_ip
150 ea5fd476 Iustin Pop
    return new_ip
151 ea5fd476 Iustin Pop
152 ea5fd476 Iustin Pop
  ip_map = {}
153 ea5fd476 Iustin Pop
154 ea5fd476 Iustin Pop
  cfg["cluster"]["master_ip"] = _Get(cfg["cluster"]["master_ip"])
155 ea5fd476 Iustin Pop
  for node in cfg["nodes"].values():
156 ea5fd476 Iustin Pop
    node["primary_ip"] = _Get(node["primary_ip"])
157 ea5fd476 Iustin Pop
    node["secondary_ip"] = _Get(node["secondary_ip"])
158 ea5fd476 Iustin Pop
159 ea5fd476 Iustin Pop
  for instance in cfg["instances"].values():
160 ea5fd476 Iustin Pop
    for nic in instance["nics"]:
161 ea5fd476 Iustin Pop
      if "ip" in nic and nic["ip"]:
162 ea5fd476 Iustin Pop
        nic["ip"] = _Get(nic["ip"])
163 ea5fd476 Iustin Pop
164 ea5fd476 Iustin Pop
165 b459a848 Andrea Spadaccini
def SanitizeOsNames(opts, cfg): # pylint: disable=W0613
166 ea5fd476 Iustin Pop
  """Sanitize the OS names.
167 ea5fd476 Iustin Pop
168 ea5fd476 Iustin Pop
  """
169 ea5fd476 Iustin Pop
  def _Get(old):
170 ea5fd476 Iustin Pop
    if old in os_map:
171 ea5fd476 Iustin Pop
      return os_map[old]
172 ea5fd476 Iustin Pop
    os_map[old] = "ganeti-os%d" % (len(os_map) + 1)
173 ea5fd476 Iustin Pop
    return os_map[old]
174 ea5fd476 Iustin Pop
175 ea5fd476 Iustin Pop
  os_map = {}
176 ea5fd476 Iustin Pop
  for instance in cfg["instances"].values():
177 ea5fd476 Iustin Pop
    instance["os"] = _Get(instance["os"])
178 ea5fd476 Iustin Pop
179 ea5fd476 Iustin Pop
  if "os_hvp" in cfg["cluster"]:
180 ea5fd476 Iustin Pop
    for os_name in cfg["cluster"]["os_hvp"]:
181 ea5fd476 Iustin Pop
      # force population of the entire os map
182 ea5fd476 Iustin Pop
      _Get(os_name)
183 ea5fd476 Iustin Pop
    RenameDictKeys(cfg["cluster"]["os_hvp"], os_map, False)
184 ea5fd476 Iustin Pop
185 ea5fd476 Iustin Pop
186 b459a848 Andrea Spadaccini
def SanitizeDisks(opts, cfg): # pylint: disable=W0613
187 ea5fd476 Iustin Pop
  """Cleanup disks disks.
188 ea5fd476 Iustin Pop
189 ea5fd476 Iustin Pop
  """
190 ea5fd476 Iustin Pop
  def _Get(old):
191 ea5fd476 Iustin Pop
    if old in lv_map:
192 ea5fd476 Iustin Pop
      return old
193 ea5fd476 Iustin Pop
    lv_map[old] = utils.NewUUID()
194 ea5fd476 Iustin Pop
    return lv_map[old]
195 ea5fd476 Iustin Pop
196 ea5fd476 Iustin Pop
  def helper(disk):
197 ea5fd476 Iustin Pop
    if "children" in disk and disk["children"]:
198 ea5fd476 Iustin Pop
      for child in disk["children"]:
199 ea5fd476 Iustin Pop
        helper(child)
200 ea5fd476 Iustin Pop
201 ea5fd476 Iustin Pop
    if disk["dev_type"] == constants.LD_DRBD8:
202 ea5fd476 Iustin Pop
      if "physical_id" in disk:
203 ea5fd476 Iustin Pop
        del disk["physical_id"]
204 ea5fd476 Iustin Pop
205 ea5fd476 Iustin Pop
    if disk["dev_type"] == constants.LD_LV and opts.sanitize_lvs:
206 ea5fd476 Iustin Pop
      disk["logical_id"][1] = _Get(disk["logical_id"][1])
207 ea5fd476 Iustin Pop
      disk["physical_id"][1] = disk["logical_id"][1]
208 ea5fd476 Iustin Pop
209 ea5fd476 Iustin Pop
  lv_map = {}
210 ea5fd476 Iustin Pop
211 ea5fd476 Iustin Pop
  for instance in cfg["instances"].values():
212 ea5fd476 Iustin Pop
    for disk in instance["disks"]:
213 ea5fd476 Iustin Pop
      helper(disk)
214 ea5fd476 Iustin Pop
215 ea5fd476 Iustin Pop
216 ea5fd476 Iustin Pop
def RandomizeDiskSecrets(disk):
217 ea5fd476 Iustin Pop
  """Randomize a disks' secrets (if any).
218 ea5fd476 Iustin Pop
219 ea5fd476 Iustin Pop
  """
220 ea5fd476 Iustin Pop
  if "children" in disk and disk["children"]:
221 ea5fd476 Iustin Pop
    for child in disk["children"]:
222 ea5fd476 Iustin Pop
      RandomizeDiskSecrets(child)
223 ea5fd476 Iustin Pop
224 ea5fd476 Iustin Pop
  # only disk type to contain secrets is the drbd one
225 ea5fd476 Iustin Pop
  if disk["dev_type"] == constants.LD_DRBD8:
226 ea5fd476 Iustin Pop
    disk["logical_id"][5] = utils.GenerateSecret()
227 ea5fd476 Iustin Pop
228 ea5fd476 Iustin Pop
229 ea5fd476 Iustin Pop
def RenameDiskNodes(disk, node_map):
230 ea5fd476 Iustin Pop
  """Rename nodes in the disk config.
231 ea5fd476 Iustin Pop
232 ea5fd476 Iustin Pop
  """
233 ea5fd476 Iustin Pop
  if "children" in disk and disk["children"]:
234 ea5fd476 Iustin Pop
    for child in disk["children"]:
235 ea5fd476 Iustin Pop
      RenameDiskNodes(child, node_map)
236 ea5fd476 Iustin Pop
237 ea5fd476 Iustin Pop
  # only disk type to contain nodes is the drbd one
238 ea5fd476 Iustin Pop
  if disk["dev_type"] == constants.LD_DRBD8:
239 ea5fd476 Iustin Pop
    lid = disk["logical_id"]
240 ea5fd476 Iustin Pop
    lid[0] = node_map[lid[0]]
241 ea5fd476 Iustin Pop
    lid[1] = node_map[lid[1]]
242 ea5fd476 Iustin Pop
243 ea5fd476 Iustin Pop
244 ea5fd476 Iustin Pop
def RenameDictKeys(a_dict, name_map, update_name):
245 ea5fd476 Iustin Pop
  """Rename the dictionary keys based on a name map.
246 ea5fd476 Iustin Pop
247 ea5fd476 Iustin Pop
  """
248 ea5fd476 Iustin Pop
  for old_name in a_dict.keys():
249 ea5fd476 Iustin Pop
    new_name = name_map[old_name]
250 ea5fd476 Iustin Pop
    a_dict[new_name] = a_dict[old_name]
251 ea5fd476 Iustin Pop
    del a_dict[old_name]
252 ea5fd476 Iustin Pop
    if update_name:
253 ea5fd476 Iustin Pop
      a_dict[new_name]["name"] = new_name
254 ea5fd476 Iustin Pop
255 ea5fd476 Iustin Pop
256 ea5fd476 Iustin Pop
def main():
257 ea5fd476 Iustin Pop
  """Main program.
258 ea5fd476 Iustin Pop
259 ea5fd476 Iustin Pop
  """
260 ea5fd476 Iustin Pop
  # Option parsing
261 ea5fd476 Iustin Pop
  parser = optparse.OptionParser(usage="%prog [--verbose] output_file")
262 ea5fd476 Iustin Pop
263 ea5fd476 Iustin Pop
  for o in OPTS:
264 ea5fd476 Iustin Pop
    parser.add_option(o)
265 ea5fd476 Iustin Pop
266 ea5fd476 Iustin Pop
  (opts, args) = parser.parse_args()
267 ea5fd476 Iustin Pop
  if opts.no_randomization:
268 ea5fd476 Iustin Pop
    opts.sanitize_names = opts.sanitize_ips = opts.sanitize_os_names = \
269 ea5fd476 Iustin Pop
        opts.sanitize_lvs = False
270 ea5fd476 Iustin Pop
271 ea5fd476 Iustin Pop
  # Option checking
272 ea5fd476 Iustin Pop
  if len(args) != 1:
273 ea5fd476 Iustin Pop
    Error("Usage: sanitize-config [options] {<output_file> | -}")
274 ea5fd476 Iustin Pop
275 ea5fd476 Iustin Pop
  # Check whether it's a Ganeti configuration directory
276 ea5fd476 Iustin Pop
  if not os.path.isfile(opts.CONFIG_DATA_PATH):
277 ea5fd476 Iustin Pop
    Error("Cannot find Ganeti configuration file %s", opts.CONFIG_DATA_PATH)
278 ea5fd476 Iustin Pop
279 ea5fd476 Iustin Pop
  config_data = serializer.LoadJson(utils.ReadFile(opts.CONFIG_DATA_PATH))
280 ea5fd476 Iustin Pop
281 ea5fd476 Iustin Pop
  # first, do some disk cleanup: remove DRBD physical_ids, since it
282 ea5fd476 Iustin Pop
  # contains both IPs (which we want changed) and the DRBD secret, and
283 ea5fd476 Iustin Pop
  # it's not needed for normal functioning, and randomize LVM names
284 ea5fd476 Iustin Pop
  SanitizeDisks(opts, config_data)
285 ea5fd476 Iustin Pop
286 ea5fd476 Iustin Pop
  SanitizeSecrets(opts, config_data)
287 ea5fd476 Iustin Pop
288 ea5fd476 Iustin Pop
  if opts.sanitize_names:
289 ea5fd476 Iustin Pop
    SanitizeCluster(opts, config_data)
290 ea5fd476 Iustin Pop
    SanitizeNodes(opts, config_data)
291 ea5fd476 Iustin Pop
    SanitizeInstances(opts, config_data)
292 ea5fd476 Iustin Pop
293 ea5fd476 Iustin Pop
  if opts.sanitize_ips:
294 ea5fd476 Iustin Pop
    SanitizeIps(opts, config_data)
295 ea5fd476 Iustin Pop
296 ea5fd476 Iustin Pop
  if opts.sanitize_os_names:
297 ea5fd476 Iustin Pop
    SanitizeOsNames(opts, config_data)
298 ea5fd476 Iustin Pop
299 ea5fd476 Iustin Pop
  data = serializer.DumpJson(config_data)
300 ea5fd476 Iustin Pop
  if args[0] == "-":
301 ea5fd476 Iustin Pop
    sys.stdout.write(data)
302 ea5fd476 Iustin Pop
  else:
303 ea5fd476 Iustin Pop
    utils.WriteFile(file_name=args[0],
304 ea5fd476 Iustin Pop
                    data=data,
305 ea5fd476 Iustin Pop
                    mode=0600,
306 ea5fd476 Iustin Pop
                    backup=True)
307 ea5fd476 Iustin Pop
308 ea5fd476 Iustin Pop
if __name__ == "__main__":
309 ea5fd476 Iustin Pop
  main()