Statistics
| Branch: | Tag: | Revision:

root / lib / client / gnt_network.py @ 4182a7ac

History | View | Annotate | Download (11.6 kB)

1
#
2
#
3

    
4
# Copyright (C) 2011, 2012, 2013 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
"""IP pool related commands"""
22

    
23
# pylint: disable=W0401,W0614
24
# W0401: Wildcard import ganeti.cli
25
# W0614: Unused import %s from wildcard import (since we need cli)
26

    
27
import textwrap
28
import itertools
29

    
30
from ganeti.cli import *
31
from ganeti import constants
32
from ganeti import opcodes
33
from ganeti import utils
34

    
35

    
36
#: default list of fields for L{ListNetworks}
37
_LIST_DEF_FIELDS = ["name", "network", "gateway",
38
                    "mac_prefix", "group_list", "tags"]
39

    
40

    
41
def _HandleReservedIPs(ips):
42
  if ips is None:
43
    return None
44
  elif not ips:
45
    return []
46
  else:
47
    return utils.UnescapeAndSplit(ips, sep=",")
48

    
49

    
50
def AddNetwork(opts, args):
51
  """Add a network to the cluster.
52

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

59
  """
60
  (network_name, ) = args
61

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

    
67
  reserved_ips = _HandleReservedIPs(opts.add_reserved_ips)
68

    
69
  op = opcodes.OpNetworkAdd(network_name=network_name,
70
                            gateway=opts.gateway,
71
                            network=opts.network,
72
                            gateway6=opts.gateway6,
73
                            network6=opts.network6,
74
                            mac_prefix=opts.mac_prefix,
75
                            add_reserved_ips=reserved_ips,
76
                            conflicts_check=opts.conflicts_check,
77
                            tags=tags)
78
  SubmitOrSend(op, opts)
79

    
80

    
81
def _GetDefaultGroups(cl, groups):
82
  """Gets list of groups to operate on.
83

84
  If C{groups} doesn't contain groups, a list of all groups in the cluster is
85
  returned.
86

87
  @type cl: L{luxi.Client}
88
  @type groups: list
89
  @rtype: list
90

91
  """
92
  if groups:
93
    return groups
94

    
95
  return list(itertools.chain(*cl.QueryGroups([], ["uuid"], False)))
96

    
97

    
98
def ConnectNetwork(opts, args):
99
  """Map a network to a node group.
100

101
  @param opts: the command line options selected by the user
102
  @type args: list
103
  @param args: Network, mode, physlink and node groups
104
  @rtype: int
105
  @return: the desired exit code
106

107
  """
108
  cl = GetClient()
109

    
110
  (network, mode, link) = args[:3]
111
  groups = _GetDefaultGroups(cl, args[3:])
112

    
113
  # TODO: Change logic to support "--submit"
114
  for group in groups:
115
    op = opcodes.OpNetworkConnect(group_name=group,
116
                                  network_name=network,
117
                                  network_mode=mode,
118
                                  network_link=link,
119
                                  conflicts_check=opts.conflicts_check)
120
    SubmitOpCode(op, opts=opts, cl=cl)
121

    
122

    
123
def DisconnectNetwork(opts, args):
124
  """Unmap a network from a node group.
125

126
  @param opts: the command line options selected by the user
127
  @type args: list
128
  @param args: Network and node groups
129
  @rtype: int
130
  @return: the desired exit code
131

132
  """
133
  cl = GetClient()
134

    
135
  (network, ) = args[:1]
136
  groups = _GetDefaultGroups(cl, args[1:])
137

    
138
  # TODO: Change logic to support "--submit"
139
  for group in groups:
140
    op = opcodes.OpNetworkDisconnect(group_name=group,
141
                                     network_name=network)
142
    SubmitOpCode(op, opts=opts, cl=cl)
143

    
144

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

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

154
  """
155
  desired_fields = ParseFields(opts.output, _LIST_DEF_FIELDS)
156
  fmtoverride = {
157
    "group_list":
158
      (lambda data: utils.CommaJoin("%s (%s, %s)" % (name, mode, link)
159
                                    for (name, mode, link) in data),
160
       False),
161
    "inst_list": (",".join, False),
162
    "tags": (",".join, False),
163
    }
164

    
165
  cl = GetClient(query=True)
166
  return GenericList(constants.QR_NETWORK, desired_fields, args, None,
167
                     opts.separator, not opts.no_headers,
168
                     verbose=opts.verbose, format_override=fmtoverride,
169
                     cl=cl)
170

    
171

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

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

181
  """
182
  cl = GetClient(query=True)
183

    
184
  return GenericListFields(constants.QR_NETWORK, args, opts.separator,
185
                           not opts.no_headers, cl=cl)
186

    
187

    
188
def ShowNetworkConfig(_, args):
189
  """Show network information.
190

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

198
  """
199
  cl = GetClient()
200
  result = cl.QueryNetworks(fields=["name", "network", "gateway",
201
                                    "network6", "gateway6",
202
                                    "mac_prefix",
203
                                    "free_count", "reserved_count",
204
                                    "map", "group_list", "inst_list",
205
                                    "external_reservations",
206
                                    "serial_no", "uuid"],
207
                            names=args, use_locking=False)
208

    
209
  for (name, network, gateway, network6, gateway6,
210
       mac_prefix, free_count, reserved_count,
211
       mapping, group_list, instances, ext_res, serial, uuid) in result:
212
    pool = free_count or reserved_count or mapping or reserved_count
213
    ToStdout("Network name: %s", name)
214
    ToStdout("UUID: %s", uuid)
215
    ToStdout("Serial number: %d", serial)
216
    ToStdout("  Subnet: %s", network)
217
    ToStdout("  Gateway: %s", gateway)
218
    ToStdout("  IPv6 Subnet: %s", network6)
219
    ToStdout("  IPv6 Gateway: %s", gateway6)
220
    ToStdout("  Mac Prefix: %s", mac_prefix)
221
    if pool:
222
      size = free_count + reserved_count
223
      ToStdout("  Size: %d", size)
224
      ToStdout("  Free: %d (%.2f%%)", free_count,
225
               100 * float(free_count) / float(size))
226
      ToStdout("  Usage map:")
227
      idx = 0
228
      for line in textwrap.wrap(mapping, width=64):
229
        ToStdout("     %s %s %d", str(idx).rjust(3), line.ljust(64), idx + 63)
230
        idx += 64
231
      ToStdout("         (X) used    (.) free")
232

    
233
      if ext_res:
234
        ToStdout("  externally reserved IPs:")
235
        for line in textwrap.wrap(ext_res, width=64):
236
          ToStdout("    %s" % line)
237

    
238
    if group_list:
239
      ToStdout("  connected to node groups:")
240
      for group in group_list:
241
        ToStdout("    %s", group)
242
    else:
243
      ToStdout("  not connected to any node group")
244

    
245
    if instances:
246
      ToStdout("  used by %d instances:", len(instances))
247
      for inst in instances:
248
        ((ips, networks), ) = cl.QueryInstances([inst],
249
                                                ["nic.ips", "nic.networks"],
250
                                                use_locking=False)
251

    
252
        l = lambda value: ", ".join(str(idx) + ":" + str(ip)
253
                                    for idx, (ip, net) in enumerate(value)
254
                                      if net == uuid)
255

    
256
        ToStdout("    %s : %s", inst, l(zip(ips, networks)))
257
    else:
258
      ToStdout("  not used by any instances")
259

    
260

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

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

268
  @rtype: int
269
  @return: the desired exit code
270

271
  """
272
  # TODO: add "network": opts.network,
273
  all_changes = {
274
    "gateway": opts.gateway,
275
    "add_reserved_ips": _HandleReservedIPs(opts.add_reserved_ips),
276
    "remove_reserved_ips": _HandleReservedIPs(opts.remove_reserved_ips),
277
    "mac_prefix": opts.mac_prefix,
278
    "gateway6": opts.gateway6,
279
    "network6": opts.network6,
280
  }
281

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

    
286
  # pylint: disable=W0142
287
  op = opcodes.OpNetworkSetParams(network_name=args[0], **all_changes)
288

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

    
292

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

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

302
  """
303
  (network_name,) = args
304
  op = opcodes.OpNetworkRemove(network_name=network_name, force=opts.force)
305
  SubmitOrSend(op, opts)
306

    
307

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

    
368

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