gnt_network.py 11.2 KB
Newer Older
1
2
3
#
#

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

"""IP pool related commands"""

23
# pylint: disable=W0401,W0614
24
25
26
27
28
29
30
31
32
33
34
# W0401: Wildcard import ganeti.cli
# W0614: Unused import %s from wildcard import (since we need cli)

from ganeti.cli import *
from ganeti import constants
from ganeti import opcodes
from ganeti import utils
from textwrap import wrap


#: default list of fields for L{ListNetworks}
35
_LIST_DEF_FIELDS = ["name", "network", "gateway",
36
                    "network_type", "mac_prefix", "group_list", "tags"]
37
38
39
40
41
42
43
44
45
46


def _HandleReservedIPs(ips):
  if ips is not None:
    if ips == "":
      return []
    else:
      return utils.UnescapeAndSplit(ips, sep=",")
  return None

47

48
49
50
51
52
53
54
55
56
57
58
59
def AddNetwork(opts, args):
  """Add a network to the cluster.

  @param opts: the command line options selected by the user
  @type args: list
  @param args: a list of length 1 with the network name to create
  @rtype: int
  @return: the desired exit code

  """
  (network_name, ) = args

60
61
62
63
64
  if opts.tags is not None:
    tags = opts.tags.split(",")
  else:
    tags = []

65
66
67
68
69
70
71
72
73
  op = opcodes.OpNetworkAdd(
                    network_name=network_name,
                    gateway=opts.gateway,
                    network=opts.network,
                    gateway6=opts.gateway6,
                    network6=opts.network6,
                    mac_prefix=opts.mac_prefix,
                    network_type=opts.network_type,
                    add_reserved_ips=_HandleReservedIPs(opts.add_reserved_ips),
Dimitris Aragiorgis's avatar
Dimitris Aragiorgis committed
74
                    conflicts_check=opts.conflicts_check,
75
                    tags=tags)
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
  SubmitOpCode(op, opts=opts)


def MapNetwork(opts, args):
  """Map a network to a node group.

  @param opts: the command line options selected by the user
  @type args: list
  @param args: a list of length 3 with network, nodegroup, mode, physlink
  @rtype: int
  @return: the desired exit code

  """
  network = args[0]
  groups = args[1]
  mode = args[2]
  link = args[3]

94
95
  # TODO: allow comma separated group names
  if groups == "all":
96
    cl = GetClient()
97
    (groups, ) = cl.QueryGroups([], ["name"], False)
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
  else:
    groups = [groups]

  for group in groups:
    op = opcodes.OpNetworkConnect(group_name=group,
                                  network_name=network,
                                  network_mode=mode,
                                  network_link=link,
                                  conflicts_check=opts.conflicts_check)
    SubmitOpCode(op, opts=opts)


def UnmapNetwork(opts, args):
  """Unmap a network from a node group.

  @param opts: the command line options selected by the user
  @type args: list
  @param args: a list of length 3 with network, nodegorup
  @rtype: int
  @return: the desired exit code

  """
  network = args[0]
  groups = args[1]

  #TODO: allow comma separated group names
124
  if groups == "all":
125
    cl = GetClient()
126
    (groups, ) = cl.QueryGroups([], ["name"], False)
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
  else:
    groups = [groups]

  for group in groups:
    op = opcodes.OpNetworkDisconnect(group_name=group,
                                     network_name=network,
                                     conflicts_check=opts.conflicts_check)
    SubmitOpCode(op, opts=opts)


def ListNetworks(opts, args):
  """List Ip pools and their properties.

  @param opts: the command line options selected by the user
  @type args: list
  @param args: networks to list, or empty for all
  @rtype: int
  @return: the desired exit code

  """
  desired_fields = ParseFields(opts.output, _LIST_DEF_FIELDS)
  fmtoverride = {
    "group_list": (",".join, False),
    "inst_list": (",".join, False),
151
    "tags": (",".join, False),
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
  }

  return GenericList(constants.QR_NETWORK, desired_fields, args, None,
                     opts.separator, not opts.no_headers,
                     verbose=opts.verbose, format_override=fmtoverride)


def ListNetworkFields(opts, args):
  """List network fields.

  @param opts: the command line options selected by the user
  @type args: list
  @param args: fields to list, or empty for all
  @rtype: int
  @return: the desired exit code

  """
  return GenericListFields(constants.QR_NETWORK, args, opts.separator,
                           not opts.no_headers)


173
def ShowNetworkConfig(_, args):
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
  """Show network information.

  @type args: list
  @param args: should either be an empty list, in which case
      we show information about all nodes, or should contain
      a list of networks (names or UUIDs) to be queried for information
  @rtype: int
  @return: the desired exit code

  """
  cl = GetClient()
  result = cl.QueryNetworks(fields=["name", "network", "gateway",
                                    "network6", "gateway6",
                                    "mac_prefix", "network_type",
                                    "free_count", "reserved_count",
                                    "map", "group_list", "inst_list",
190
191
                                    "external_reservations",
                                    "serial_no", "uuid"],
192
193
194
195
                            names=args, use_locking=False)

  for (name, network, gateway, network6, gateway6,
       mac_prefix, network_type, free_count, reserved_count,
196
       mapping, group_list, instances, ext_res, serial, uuid) in result:
197
198
    size = free_count + reserved_count
    ToStdout("Network name: %s", name)
199
200
201
202
203
204
205
206
207
208
    ToStdout("UUID: %s", uuid)
    ToStdout("Serial number: %d", serial)
    ToStdout("  Subnet: %s", network)
    ToStdout("  Gateway: %s", gateway)
    ToStdout("  IPv6 Subnet: %s", network6)
    ToStdout("  IPv6 Gateway: %s", gateway6)
    ToStdout("  Mac Prefix: %s", mac_prefix)
    ToStdout("  Type: %s", network_type)
    ToStdout("  Size: %d", size)
    ToStdout("  Free: %d (%.2f%%)", free_count,
209
             100 * float(free_count) / float(size))
210
    ToStdout("  Usage map:")
211
    idx = 0
212
    for line in wrap(mapping, width=64):
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
      ToStdout("     %s %s %d", str(idx).rjust(3), line.ljust(64), idx + 63)
      idx += 64
    ToStdout("         (X) used    (.) free")

    if ext_res:
      ToStdout("  externally reserved IPs:")
      for line in wrap(ext_res, width=64):
        ToStdout("    %s" % line)

    if group_list:
      ToStdout("  connected to node groups:")
      for group in group_list:
        ToStdout("    %s", group)
    else:
      ToStdout("  not connected to any node group")

    if instances:
      ToStdout("  used by %d instances:", len(instances))
      for inst in instances:
        ((ips, networks), ) = cl.QueryInstances([inst],
                                                ["nic.ips", "nic.networks"],
                                                use_locking=False)

236
        l = lambda value: ", ".join(str(idx) + ":" + str(ip)
237
238
239
                                    for idx, (ip, net) in enumerate(value)
                                      if net == name)

240
        ToStdout("    %s : %s", inst, l(zip(ips, networks)))
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
    else:
      ToStdout("  not used by any instances")


def SetNetworkParams(opts, args):
  """Modifies an IP address pool's parameters.

  @param opts: the command line options selected by the user
  @type args: list
  @param args: should contain only one element, the node group name

  @rtype: int
  @return: the desired exit code

  """

  # TODO: add "network": opts.network,
  all_changes = {
    "gateway": opts.gateway,
    "add_reserved_ips": _HandleReservedIPs(opts.add_reserved_ips),
    "remove_reserved_ips": _HandleReservedIPs(opts.remove_reserved_ips),
    "mac_prefix": opts.mac_prefix,
    "network_type": opts.network_type,
    "gateway6": opts.gateway6,
    "network6": opts.network6,
  }

  if all_changes.values().count(None) == len(all_changes):
    ToStderr("Please give at least one of the parameters.")
    return 1

272
273
  # pylint: disable=W0142
  op = opcodes.OpNetworkSetParams(network_name=args[0], **all_changes)
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296

  # TODO: add feedback to user, e.g. list the modifications
  SubmitOrSend(op, opts)


def RemoveNetwork(opts, args):
  """Remove an IP address pool from the cluster.

  @param opts: the command line options selected by the user
  @type args: list
  @param args: a list of length 1 with the id of the IP address pool to remove
  @rtype: int
  @return: the desired exit code

  """
  (network_name,) = args
  op = opcodes.OpNetworkRemove(network_name=network_name, force=opts.force)
  SubmitOpCode(op, opts=opts)


commands = {
  "add": (
    AddNetwork, ARGS_ONE_NETWORK,
Dimitris Aragiorgis's avatar
Dimitris Aragiorgis committed
297
298
299
    [DRY_RUN_OPT, NETWORK_OPT, GATEWAY_OPT, ADD_RESERVED_IPS_OPT,
     MAC_PREFIX_OPT, NETWORK_TYPE_OPT, NETWORK6_OPT, GATEWAY6_OPT,
     NOCONFLICTSCHECK_OPT, TAG_ADD_OPT],
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
    "<network_name>", "Add a new IP network to the cluster"),
  "list": (
    ListNetworks, ARGS_MANY_NETWORKS,
    [NOHDR_OPT, SEP_OPT, FIELDS_OPT, VERBOSE_OPT],
    "[<network_id>...]",
    "Lists the IP networks in the cluster. The available fields can be shown"
    " using the \"list-fields\" command (see the man page for details)."
    " The default list is (in order): %s." % utils.CommaJoin(_LIST_DEF_FIELDS)),
  "list-fields": (
    ListNetworkFields, [ArgUnknown()], [NOHDR_OPT, SEP_OPT], "[fields...]",
    "Lists all available fields for networks"),
  "info": (
    ShowNetworkConfig, ARGS_MANY_NETWORKS, [],
    "[<network_name>...]", "Show information about the network(s)"),
  "modify": (
    SetNetworkParams, ARGS_ONE_NETWORK,
    [DRY_RUN_OPT, SUBMIT_OPT, ADD_RESERVED_IPS_OPT, REMOVE_RESERVED_IPS_OPT,
     GATEWAY_OPT, MAC_PREFIX_OPT, NETWORK_TYPE_OPT, NETWORK6_OPT, GATEWAY6_OPT],
    "<network_name>", "Alters the parameters of a network"),
  "connect": (
    MapNetwork,
    [ArgNetwork(min=1, max=1), ArgGroup(min=1, max=1),
     ArgUnknown(min=1, max=1), ArgUnknown(min=1, max=1)],
    [NOCONFLICTSCHECK_OPT],
    "<network_name> <node_group> <mode> <link>",
    "Map a given network to the specified node group"
    " with given mode and link (netparams)"),
  "disconnect": (
    UnmapNetwork,
    [ArgNetwork(min=1, max=1), ArgGroup(min=1, max=1)],
    [NOCONFLICTSCHECK_OPT],
    "<network_name> <node_group>",
    "Unmap a given network from a specified node group"),
  "remove": (
    RemoveNetwork, ARGS_ONE_NETWORK, [FORCE_OPT, DRY_RUN_OPT],
    "[--dry-run] <network_id>",
    "Remove an (empty) network from the cluster"),
337
338
339
340
341
342
343
344
345
346
347
  "list-tags": (
    ListTags, ARGS_ONE_NETWORK, [],
    "<network_name>", "List the tags of the given network"),
  "add-tags": (
    AddTags, [ArgNetwork(min=1, max=1), ArgUnknown()],
    [TAG_SRC_OPT, PRIORITY_OPT, SUBMIT_OPT],
    "<network_name> tag...", "Add tags to the given network"),
  "remove-tags": (
    RemoveTags, [ArgNetwork(min=1, max=1), ArgUnknown()],
    [TAG_SRC_OPT, PRIORITY_OPT, SUBMIT_OPT],
    "<network_name> tag...", "Remove tags from given network"),
348
349
350
351
}


def Main():
352
  return GenericMain(commands, override={"tag_type": constants.TAG_NETWORK})