Statistics
| Branch: | Tag: | Revision:

root / tools / sanitize-config @ 36c70d4d

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