Statistics
| Branch: | Tag: | Revision:

root / tools / sanitize-config @ 33bff17b

History | View | Annotate | Download (8.5 kB)

1
#!/usr/bin/python
2
#
3

    
4
# Copyright (C) 2010 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
# pylint: disable=C0103
23

    
24
"""Tool to sanitize/randomize the configuration file.
25

    
26
"""
27

    
28
import sys
29
import os
30
import os.path
31
import optparse
32

    
33
from ganeti import constants
34
from ganeti import serializer
35
from ganeti import utils
36
from ganeti import pathutils
37
from ganeti import cli
38
from ganeti.cli import cli_option
39

    
40

    
41
OPTS = [
42
  cli.VERBOSE_OPT,
43
  cli_option("--path", help="Convert this configuration file"
44
             " instead of '%s'" % pathutils.CLUSTER_CONF_FILE,
45
             default=pathutils.CLUSTER_CONF_FILE, dest="CONFIG_DATA_PATH"),
46
  cli_option("--sanitize-names", default="yes", type="bool",
47
             help="Randomize the cluster, node and instance names [yes]"),
48
  cli_option("--sanitize-ips", default="yes", type="bool",
49
             help="Randomize the cluster, node and instance IPs [yes]"),
50
  cli_option("--sanitize-lvs", default="no", type="bool",
51
             help="Randomize the LV names (for old clusters) [no]"),
52
  cli_option("--sanitize-os-names", default="yes", type="bool",
53
             help="Randomize the OS names [yes]"),
54
  cli_option("--no-randomization", default=False, action="store_true",
55
             help="Disable all name randomization (only randomize secrets)"),
56
  cli_option("--base-domain", default="example.com",
57
             help="The base domain used for new names [example.com]"),
58
  ]
59

    
60

    
61
def Error(txt, *args):
62
  """Writes a message to standard error and exits.
63

    
64
  """
65
  cli.ToStderr(txt, *args)
66
  sys.exit(1)
67

    
68

    
69
def GenerateNameMap(opts, names, base):
70
  """For a given set of names, generate a list of sane new names.
71

    
72
  """
73
  names = utils.NiceSort(names)
74
  name_map = {}
75
  for idx, old_name in enumerate(names):
76
    new_name = "%s%d.%s" % (base, idx + 1, opts.base_domain)
77
    if new_name in names:
78
      Error("Name conflict for %s: %s already exists", base, new_name)
79
    name_map[old_name] = new_name
80
  return name_map
81

    
82

    
83
def SanitizeSecrets(opts, cfg): # pylint: disable=W0613
84
  """Cleanup configuration secrets.
85

    
86
  """
87
  cfg["cluster"]["rsahostkeypub"] = ""
88
  for instance in cfg["instances"].values():
89
    for disk in instance["disks"]:
90
      RandomizeDiskSecrets(disk)
91

    
92

    
93
def SanitizeCluster(opts, cfg):
94
  """Sanitize the cluster names.
95

    
96
  """
97
  cfg["cluster"]["cluster_name"] = "cluster." + opts.base_domain
98

    
99

    
100
def SanitizeNodes(opts, cfg):
101
  """Sanitize node names.
102

    
103
  """
104
  old_names = cfg["nodes"].keys()
105
  old_map = GenerateNameMap(opts, old_names, "node")
106

    
107
  # rename nodes
108
  RenameDictKeys(cfg["nodes"], old_map, True)
109

    
110
  # update master node
111
  cfg["cluster"]["master_node"] = old_map[cfg["cluster"]["master_node"]]
112

    
113
  # update instance configuration
114
  for instance in cfg["instances"].values():
115
    instance["primary_node"] = old_map[instance["primary_node"]]
116
    for disk in instance["disks"]:
117
      RenameDiskNodes(disk, old_map)
118

    
119

    
120
def SanitizeInstances(opts, cfg):
121
  """Sanitize instance names.
122

    
123
  """
124
  old_names = cfg["instances"].keys()
125
  old_map = GenerateNameMap(opts, old_names, "instance")
126

    
127
  RenameDictKeys(cfg["instances"], old_map, True)
128

    
129

    
130
def SanitizeIps(opts, cfg): # pylint: disable=W0613
131
  """Sanitize the IP names.
132

    
133
  @note: we're interested in obscuring the old IPs, not in generating
134
      actually valid new IPs, so we chose to simply put IPv4
135
      addresses, irrelevant of whether IPv6 or IPv4 addresses existed
136
      before.
137

    
138
  """
139
  def _Get(old):
140
    if old in ip_map:
141
      return ip_map[old]
142
    idx = len(ip_map) + 1
143
    rest, d_octet = divmod(idx, 256)
144
    rest, c_octet = divmod(rest, 256)
145
    rest, b_octet = divmod(rest, 256)
146
    if rest > 0:
147
      Error("Too many IPs!")
148
    new_ip = "%d.%d.%d.%d" % (10, b_octet, c_octet, d_octet)
149
    ip_map[old] = new_ip
150
    return new_ip
151

    
152
  ip_map = {}
153

    
154
  cfg["cluster"]["master_ip"] = _Get(cfg["cluster"]["master_ip"])
155
  for node in cfg["nodes"].values():
156
    node["primary_ip"] = _Get(node["primary_ip"])
157
    node["secondary_ip"] = _Get(node["secondary_ip"])
158

    
159
  for instance in cfg["instances"].values():
160
    for nic in instance["nics"]:
161
      if "ip" in nic and nic["ip"]:
162
        nic["ip"] = _Get(nic["ip"])
163

    
164

    
165
def SanitizeOsNames(opts, cfg): # pylint: disable=W0613
166
  """Sanitize the OS names.
167

    
168
  """
169
  def _Get(old):
170
    if old in os_map:
171
      return os_map[old]
172
    os_map[old] = "ganeti-os%d" % (len(os_map) + 1)
173
    return os_map[old]
174

    
175
  os_map = {}
176
  for instance in cfg["instances"].values():
177
    instance["os"] = _Get(instance["os"])
178

    
179
  if "os_hvp" in cfg["cluster"]:
180
    for os_name in cfg["cluster"]["os_hvp"]:
181
      # force population of the entire os map
182
      _Get(os_name)
183
    RenameDictKeys(cfg["cluster"]["os_hvp"], os_map, False)
184

    
185

    
186
def SanitizeDisks(opts, cfg): # pylint: disable=W0613
187
  """Cleanup disks disks.
188

    
189
  """
190
  def _Get(old):
191
    if old in lv_map:
192
      return old
193
    lv_map[old] = utils.NewUUID()
194
    return lv_map[old]
195

    
196
  def helper(disk):
197
    if "children" in disk and disk["children"]:
198
      for child in disk["children"]:
199
        helper(child)
200

    
201
    if disk["dev_type"] == constants.LD_DRBD8:
202
      if "physical_id" in disk:
203
        del disk["physical_id"]
204

    
205
    if disk["dev_type"] == constants.LD_LV and opts.sanitize_lvs:
206
      disk["logical_id"][1] = _Get(disk["logical_id"][1])
207
      disk["physical_id"][1] = disk["logical_id"][1]
208

    
209
  lv_map = {}
210

    
211
  for instance in cfg["instances"].values():
212
    for disk in instance["disks"]:
213
      helper(disk)
214

    
215

    
216
def RandomizeDiskSecrets(disk):
217
  """Randomize a disks' secrets (if any).
218

    
219
  """
220
  if "children" in disk and disk["children"]:
221
    for child in disk["children"]:
222
      RandomizeDiskSecrets(child)
223

    
224
  # only disk type to contain secrets is the drbd one
225
  if disk["dev_type"] == constants.LD_DRBD8:
226
    disk["logical_id"][5] = utils.GenerateSecret()
227

    
228

    
229
def RenameDiskNodes(disk, node_map):
230
  """Rename nodes in the disk config.
231

    
232
  """
233
  if "children" in disk and disk["children"]:
234
    for child in disk["children"]:
235
      RenameDiskNodes(child, node_map)
236

    
237
  # only disk type to contain nodes is the drbd one
238
  if disk["dev_type"] == constants.LD_DRBD8:
239
    lid = disk["logical_id"]
240
    lid[0] = node_map[lid[0]]
241
    lid[1] = node_map[lid[1]]
242

    
243

    
244
def RenameDictKeys(a_dict, name_map, update_name):
245
  """Rename the dictionary keys based on a name map.
246

    
247
  """
248
  for old_name in a_dict.keys():
249
    new_name = name_map[old_name]
250
    a_dict[new_name] = a_dict[old_name]
251
    del a_dict[old_name]
252
    if update_name:
253
      a_dict[new_name]["name"] = new_name
254

    
255

    
256
def main():
257
  """Main program.
258

    
259
  """
260
  # Option parsing
261
  parser = optparse.OptionParser(usage="%prog [--verbose] output_file")
262

    
263
  for o in OPTS:
264
    parser.add_option(o)
265

    
266
  (opts, args) = parser.parse_args()
267
  if opts.no_randomization:
268
    opts.sanitize_names = opts.sanitize_ips = opts.sanitize_os_names = \
269
        opts.sanitize_lvs = False
270

    
271
  # Option checking
272
  if len(args) != 1:
273
    Error("Usage: sanitize-config [options] {<output_file> | -}")
274

    
275
  # Check whether it's a Ganeti configuration directory
276
  if not os.path.isfile(opts.CONFIG_DATA_PATH):
277
    Error("Cannot find Ganeti configuration file %s", opts.CONFIG_DATA_PATH)
278

    
279
  config_data = serializer.LoadJson(utils.ReadFile(opts.CONFIG_DATA_PATH))
280

    
281
  # first, do some disk cleanup: remove DRBD physical_ids, since it
282
  # contains both IPs (which we want changed) and the DRBD secret, and
283
  # it's not needed for normal functioning, and randomize LVM names
284
  SanitizeDisks(opts, config_data)
285

    
286
  SanitizeSecrets(opts, config_data)
287

    
288
  if opts.sanitize_names:
289
    SanitizeCluster(opts, config_data)
290
    SanitizeNodes(opts, config_data)
291
    SanitizeInstances(opts, config_data)
292

    
293
  if opts.sanitize_ips:
294
    SanitizeIps(opts, config_data)
295

    
296
  if opts.sanitize_os_names:
297
    SanitizeOsNames(opts, config_data)
298

    
299
  data = serializer.DumpJson(config_data)
300
  if args[0] == "-":
301
    sys.stdout.write(data)
302
  else:
303
    utils.WriteFile(file_name=args[0],
304
                    data=data,
305
                    mode=0600,
306
                    backup=True)
307

    
308
if __name__ == "__main__":
309
  main()