Statistics
| Branch: | Tag: | Revision:

root / tools / sanitize-config @ 4fe43605

History | View | Annotate | Download (8.2 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 a9542a4f Thomas Thrainer
  cfg["cluster"]["dsahostkeypub"] = ""
89 ea5fd476 Iustin Pop
  for instance in cfg["instances"].values():
90 ea5fd476 Iustin Pop
    for disk in instance["disks"]:
91 ea5fd476 Iustin Pop
      RandomizeDiskSecrets(disk)
92 ea5fd476 Iustin Pop
93 ea5fd476 Iustin Pop
94 ea5fd476 Iustin Pop
def SanitizeCluster(opts, cfg):
95 ea5fd476 Iustin Pop
  """Sanitize the cluster names.
96 ea5fd476 Iustin Pop
97 ea5fd476 Iustin Pop
  """
98 ea5fd476 Iustin Pop
  cfg["cluster"]["cluster_name"] = "cluster." + opts.base_domain
99 ea5fd476 Iustin Pop
100 ea5fd476 Iustin Pop
101 ea5fd476 Iustin Pop
def SanitizeNodes(opts, cfg):
102 ea5fd476 Iustin Pop
  """Sanitize node names.
103 ea5fd476 Iustin Pop
104 ea5fd476 Iustin Pop
  """
105 ea5fd476 Iustin Pop
  old_names = cfg["nodes"].keys()
106 ea5fd476 Iustin Pop
  old_map = GenerateNameMap(opts, old_names, "node")
107 ea5fd476 Iustin Pop
108 ea5fd476 Iustin Pop
  # rename nodes
109 ea5fd476 Iustin Pop
  RenameDictKeys(cfg["nodes"], old_map, True)
110 ea5fd476 Iustin Pop
111 ea5fd476 Iustin Pop
  # update master node
112 ea5fd476 Iustin Pop
  cfg["cluster"]["master_node"] = old_map[cfg["cluster"]["master_node"]]
113 ea5fd476 Iustin Pop
114 ea5fd476 Iustin Pop
  # update instance configuration
115 ea5fd476 Iustin Pop
  for instance in cfg["instances"].values():
116 ea5fd476 Iustin Pop
    instance["primary_node"] = old_map[instance["primary_node"]]
117 ea5fd476 Iustin Pop
    for disk in instance["disks"]:
118 ea5fd476 Iustin Pop
      RenameDiskNodes(disk, old_map)
119 ea5fd476 Iustin Pop
120 ea5fd476 Iustin Pop
121 ea5fd476 Iustin Pop
def SanitizeInstances(opts, cfg):
122 ea5fd476 Iustin Pop
  """Sanitize instance names.
123 ea5fd476 Iustin Pop
124 ea5fd476 Iustin Pop
  """
125 ea5fd476 Iustin Pop
  old_names = cfg["instances"].keys()
126 ea5fd476 Iustin Pop
  old_map = GenerateNameMap(opts, old_names, "instance")
127 ea5fd476 Iustin Pop
128 ea5fd476 Iustin Pop
  RenameDictKeys(cfg["instances"], old_map, True)
129 ea5fd476 Iustin Pop
130 ea5fd476 Iustin Pop
131 b459a848 Andrea Spadaccini
def SanitizeIps(opts, cfg): # pylint: disable=W0613
132 ea5fd476 Iustin Pop
  """Sanitize the IP names.
133 ea5fd476 Iustin Pop
134 ea5fd476 Iustin Pop
  @note: we're interested in obscuring the old IPs, not in generating
135 ea5fd476 Iustin Pop
      actually valid new IPs, so we chose to simply put IPv4
136 ea5fd476 Iustin Pop
      addresses, irrelevant of whether IPv6 or IPv4 addresses existed
137 ea5fd476 Iustin Pop
      before.
138 ea5fd476 Iustin Pop
139 ea5fd476 Iustin Pop
  """
140 ea5fd476 Iustin Pop
  def _Get(old):
141 ea5fd476 Iustin Pop
    if old in ip_map:
142 ea5fd476 Iustin Pop
      return ip_map[old]
143 ea5fd476 Iustin Pop
    idx = len(ip_map) + 1
144 ea5fd476 Iustin Pop
    rest, d_octet = divmod(idx, 256)
145 ea5fd476 Iustin Pop
    rest, c_octet = divmod(rest, 256)
146 ea5fd476 Iustin Pop
    rest, b_octet = divmod(rest, 256)
147 ea5fd476 Iustin Pop
    if rest > 0:
148 ea5fd476 Iustin Pop
      Error("Too many IPs!")
149 ea5fd476 Iustin Pop
    new_ip = "%d.%d.%d.%d" % (10, b_octet, c_octet, d_octet)
150 ea5fd476 Iustin Pop
    ip_map[old] = new_ip
151 ea5fd476 Iustin Pop
    return new_ip
152 ea5fd476 Iustin Pop
153 ea5fd476 Iustin Pop
  ip_map = {}
154 ea5fd476 Iustin Pop
155 ea5fd476 Iustin Pop
  cfg["cluster"]["master_ip"] = _Get(cfg["cluster"]["master_ip"])
156 ea5fd476 Iustin Pop
  for node in cfg["nodes"].values():
157 ea5fd476 Iustin Pop
    node["primary_ip"] = _Get(node["primary_ip"])
158 ea5fd476 Iustin Pop
    node["secondary_ip"] = _Get(node["secondary_ip"])
159 ea5fd476 Iustin Pop
160 ea5fd476 Iustin Pop
  for instance in cfg["instances"].values():
161 ea5fd476 Iustin Pop
    for nic in instance["nics"]:
162 ea5fd476 Iustin Pop
      if "ip" in nic and nic["ip"]:
163 ea5fd476 Iustin Pop
        nic["ip"] = _Get(nic["ip"])
164 ea5fd476 Iustin Pop
165 ea5fd476 Iustin Pop
166 b459a848 Andrea Spadaccini
def SanitizeOsNames(opts, cfg): # pylint: disable=W0613
167 ea5fd476 Iustin Pop
  """Sanitize the OS names.
168 ea5fd476 Iustin Pop
169 ea5fd476 Iustin Pop
  """
170 ea5fd476 Iustin Pop
  def _Get(old):
171 ea5fd476 Iustin Pop
    if old in os_map:
172 ea5fd476 Iustin Pop
      return os_map[old]
173 ea5fd476 Iustin Pop
    os_map[old] = "ganeti-os%d" % (len(os_map) + 1)
174 ea5fd476 Iustin Pop
    return os_map[old]
175 ea5fd476 Iustin Pop
176 ea5fd476 Iustin Pop
  os_map = {}
177 ea5fd476 Iustin Pop
  for instance in cfg["instances"].values():
178 ea5fd476 Iustin Pop
    instance["os"] = _Get(instance["os"])
179 ea5fd476 Iustin Pop
180 ea5fd476 Iustin Pop
  if "os_hvp" in cfg["cluster"]:
181 ea5fd476 Iustin Pop
    for os_name in cfg["cluster"]["os_hvp"]:
182 ea5fd476 Iustin Pop
      # force population of the entire os map
183 ea5fd476 Iustin Pop
      _Get(os_name)
184 ea5fd476 Iustin Pop
    RenameDictKeys(cfg["cluster"]["os_hvp"], os_map, False)
185 ea5fd476 Iustin Pop
186 ea5fd476 Iustin Pop
187 b459a848 Andrea Spadaccini
def SanitizeDisks(opts, cfg): # pylint: disable=W0613
188 ea5fd476 Iustin Pop
  """Cleanup disks disks.
189 ea5fd476 Iustin Pop
190 ea5fd476 Iustin Pop
  """
191 ea5fd476 Iustin Pop
  def _Get(old):
192 ea5fd476 Iustin Pop
    if old in lv_map:
193 ea5fd476 Iustin Pop
      return old
194 ea5fd476 Iustin Pop
    lv_map[old] = utils.NewUUID()
195 ea5fd476 Iustin Pop
    return lv_map[old]
196 ea5fd476 Iustin Pop
197 ea5fd476 Iustin Pop
  def helper(disk):
198 ea5fd476 Iustin Pop
    if "children" in disk and disk["children"]:
199 ea5fd476 Iustin Pop
      for child in disk["children"]:
200 ea5fd476 Iustin Pop
        helper(child)
201 ea5fd476 Iustin Pop
202 cd3b4ff4 Helga Velroyen
    if disk["dev_type"] == constants.DT_PLAIN and opts.sanitize_lvs:
203 ea5fd476 Iustin Pop
      disk["logical_id"][1] = _Get(disk["logical_id"][1])
204 ea5fd476 Iustin Pop
205 ea5fd476 Iustin Pop
  lv_map = {}
206 ea5fd476 Iustin Pop
207 ea5fd476 Iustin Pop
  for instance in cfg["instances"].values():
208 ea5fd476 Iustin Pop
    for disk in instance["disks"]:
209 ea5fd476 Iustin Pop
      helper(disk)
210 ea5fd476 Iustin Pop
211 ea5fd476 Iustin Pop
212 ea5fd476 Iustin Pop
def RandomizeDiskSecrets(disk):
213 ea5fd476 Iustin Pop
  """Randomize a disks' secrets (if any).
214 ea5fd476 Iustin Pop
215 ea5fd476 Iustin Pop
  """
216 ea5fd476 Iustin Pop
  if "children" in disk and disk["children"]:
217 ea5fd476 Iustin Pop
    for child in disk["children"]:
218 ea5fd476 Iustin Pop
      RandomizeDiskSecrets(child)
219 ea5fd476 Iustin Pop
220 ea5fd476 Iustin Pop
  # only disk type to contain secrets is the drbd one
221 cd3b4ff4 Helga Velroyen
  if disk["dev_type"] == constants.DT_DRBD8:
222 ea5fd476 Iustin Pop
    disk["logical_id"][5] = utils.GenerateSecret()
223 ea5fd476 Iustin Pop
224 ea5fd476 Iustin Pop
225 ea5fd476 Iustin Pop
def RenameDiskNodes(disk, node_map):
226 ea5fd476 Iustin Pop
  """Rename nodes in the disk config.
227 ea5fd476 Iustin Pop
228 ea5fd476 Iustin Pop
  """
229 ea5fd476 Iustin Pop
  if "children" in disk and disk["children"]:
230 ea5fd476 Iustin Pop
    for child in disk["children"]:
231 ea5fd476 Iustin Pop
      RenameDiskNodes(child, node_map)
232 ea5fd476 Iustin Pop
233 ea5fd476 Iustin Pop
  # only disk type to contain nodes is the drbd one
234 cd3b4ff4 Helga Velroyen
  if disk["dev_type"] == constants.DT_DRBD8:
235 ea5fd476 Iustin Pop
    lid = disk["logical_id"]
236 ea5fd476 Iustin Pop
    lid[0] = node_map[lid[0]]
237 ea5fd476 Iustin Pop
    lid[1] = node_map[lid[1]]
238 ea5fd476 Iustin Pop
239 ea5fd476 Iustin Pop
240 ea5fd476 Iustin Pop
def RenameDictKeys(a_dict, name_map, update_name):
241 ea5fd476 Iustin Pop
  """Rename the dictionary keys based on a name map.
242 ea5fd476 Iustin Pop
243 ea5fd476 Iustin Pop
  """
244 ea5fd476 Iustin Pop
  for old_name in a_dict.keys():
245 ea5fd476 Iustin Pop
    new_name = name_map[old_name]
246 ea5fd476 Iustin Pop
    a_dict[new_name] = a_dict[old_name]
247 ea5fd476 Iustin Pop
    del a_dict[old_name]
248 ea5fd476 Iustin Pop
    if update_name:
249 ea5fd476 Iustin Pop
      a_dict[new_name]["name"] = new_name
250 ea5fd476 Iustin Pop
251 ea5fd476 Iustin Pop
252 ea5fd476 Iustin Pop
def main():
253 ea5fd476 Iustin Pop
  """Main program.
254 ea5fd476 Iustin Pop
255 ea5fd476 Iustin Pop
  """
256 ea5fd476 Iustin Pop
  # Option parsing
257 ea5fd476 Iustin Pop
  parser = optparse.OptionParser(usage="%prog [--verbose] output_file")
258 ea5fd476 Iustin Pop
259 ea5fd476 Iustin Pop
  for o in OPTS:
260 ea5fd476 Iustin Pop
    parser.add_option(o)
261 ea5fd476 Iustin Pop
262 ea5fd476 Iustin Pop
  (opts, args) = parser.parse_args()
263 ea5fd476 Iustin Pop
  if opts.no_randomization:
264 ea5fd476 Iustin Pop
    opts.sanitize_names = opts.sanitize_ips = opts.sanitize_os_names = \
265 ea5fd476 Iustin Pop
        opts.sanitize_lvs = False
266 ea5fd476 Iustin Pop
267 ea5fd476 Iustin Pop
  # Option checking
268 ea5fd476 Iustin Pop
  if len(args) != 1:
269 ea5fd476 Iustin Pop
    Error("Usage: sanitize-config [options] {<output_file> | -}")
270 ea5fd476 Iustin Pop
271 ea5fd476 Iustin Pop
  # Check whether it's a Ganeti configuration directory
272 ea5fd476 Iustin Pop
  if not os.path.isfile(opts.CONFIG_DATA_PATH):
273 ea5fd476 Iustin Pop
    Error("Cannot find Ganeti configuration file %s", opts.CONFIG_DATA_PATH)
274 ea5fd476 Iustin Pop
275 ea5fd476 Iustin Pop
  config_data = serializer.LoadJson(utils.ReadFile(opts.CONFIG_DATA_PATH))
276 ea5fd476 Iustin Pop
277 204eb58c Thomas Thrainer
  # Randomize LVM names
278 ea5fd476 Iustin Pop
  SanitizeDisks(opts, config_data)
279 ea5fd476 Iustin Pop
280 ea5fd476 Iustin Pop
  SanitizeSecrets(opts, config_data)
281 ea5fd476 Iustin Pop
282 ea5fd476 Iustin Pop
  if opts.sanitize_names:
283 ea5fd476 Iustin Pop
    SanitizeCluster(opts, config_data)
284 ea5fd476 Iustin Pop
    SanitizeNodes(opts, config_data)
285 ea5fd476 Iustin Pop
    SanitizeInstances(opts, config_data)
286 ea5fd476 Iustin Pop
287 ea5fd476 Iustin Pop
  if opts.sanitize_ips:
288 ea5fd476 Iustin Pop
    SanitizeIps(opts, config_data)
289 ea5fd476 Iustin Pop
290 ea5fd476 Iustin Pop
  if opts.sanitize_os_names:
291 ea5fd476 Iustin Pop
    SanitizeOsNames(opts, config_data)
292 ea5fd476 Iustin Pop
293 ea5fd476 Iustin Pop
  data = serializer.DumpJson(config_data)
294 ea5fd476 Iustin Pop
  if args[0] == "-":
295 ea5fd476 Iustin Pop
    sys.stdout.write(data)
296 ea5fd476 Iustin Pop
  else:
297 ea5fd476 Iustin Pop
    utils.WriteFile(file_name=args[0],
298 ea5fd476 Iustin Pop
                    data=data,
299 ea5fd476 Iustin Pop
                    mode=0600,
300 ea5fd476 Iustin Pop
                    backup=True)
301 ea5fd476 Iustin Pop
302 ea5fd476 Iustin Pop
if __name__ == "__main__":
303 ea5fd476 Iustin Pop
  main()