Statistics
| Branch: | Tag: | Revision:

root / lib / client / gnt_network.py @ 84e110aa

History | View | Annotate | Download (11.5 kB)

1
#
2
#
3

    
4
# Copyright (C) 2011, 2012 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

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

    
35

    
36
#: default list of fields for L{ListNetworks}
37
_LIST_DEF_FIELDS = ["name", "network", "gateway",
38
                    "network_type", "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.network is None:
63
    raise errors.OpPrereqError("The --network option must be given",
64
                               errors.ECODE_INVAL)
65

    
66
  if opts.tags is not None:
67
    tags = opts.tags.split(",")
68
  else:
69
    tags = []
70

    
71
  reserved_ips = _HandleReservedIPs(opts.add_reserved_ips)
72

    
73
  op = opcodes.OpNetworkAdd(network_name=network_name,
74
                            gateway=opts.gateway,
75
                            network=opts.network,
76
                            gateway6=opts.gateway6,
77
                            network6=opts.network6,
78
                            mac_prefix=opts.mac_prefix,
79
                            network_type=opts.network_type,
80
                            add_reserved_ips=reserved_ips,
81
                            conflicts_check=opts.conflicts_check,
82
                            tags=tags)
83
  SubmitOpCode(op, opts=opts)
84

    
85

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

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

95
  """
96
  network = args[0]
97
  groups = args[1]
98
  mode = args[2]
99
  link = args[3]
100

    
101
  # TODO: allow comma separated group names
102
  if groups == "all":
103
    cl = GetClient()
104
    (groups, ) = cl.QueryGroups([], ["name"], False)
105
  else:
106
    groups = [groups]
107

    
108
  for group in groups:
109
    op = opcodes.OpNetworkConnect(group_name=group,
110
                                  network_name=network,
111
                                  network_mode=mode,
112
                                  network_link=link,
113
                                  conflicts_check=opts.conflicts_check)
114
    SubmitOpCode(op, opts=opts)
115

    
116

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

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

126
  """
127
  network = args[0]
128
  groups = args[1]
129

    
130
  #TODO: allow comma separated group names
131
  if groups == "all":
132
    cl = GetClient()
133
    (groups, ) = cl.QueryGroups([], ["name"], False)
134
  else:
135
    groups = [groups]
136

    
137
  for group in groups:
138
    op = opcodes.OpNetworkDisconnect(group_name=group,
139
                                     network_name=network,
140
                                     conflicts_check=opts.conflicts_check)
141
    SubmitOpCode(op, opts=opts)
142

    
143

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

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

153
  """
154
  desired_fields = ParseFields(opts.output, _LIST_DEF_FIELDS)
155
  fmtoverride = {
156
    "group_list": (",".join, False),
157
    "inst_list": (",".join, False),
158
    "tags": (",".join, False),
159
  }
160

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

    
165

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

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

175
  """
176
  return GenericListFields(constants.QR_NETWORK, args, opts.separator,
177
                           not opts.no_headers)
178

    
179

    
180
def ShowNetworkConfig(_, args):
181
  """Show network information.
182

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

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

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

    
224
    if ext_res:
225
      ToStdout("  externally reserved IPs:")
226
      for line in textwrap.wrap(ext_res, width=64):
227
        ToStdout("    %s" % line)
228

    
229
    if group_list:
230
      ToStdout("  connected to node groups:")
231
      for group in group_list:
232
        ToStdout("    %s", group)
233
    else:
234
      ToStdout("  not connected to any node group")
235

    
236
    if instances:
237
      ToStdout("  used by %d instances:", len(instances))
238
      for inst in instances:
239
        ((ips, networks), ) = cl.QueryInstances([inst],
240
                                                ["nic.ips", "nic.networks"],
241
                                                use_locking=False)
242

    
243
        l = lambda value: ", ".join(str(idx) + ":" + str(ip)
244
                                    for idx, (ip, net) in enumerate(value)
245
                                      if net == name)
246

    
247
        ToStdout("    %s : %s", inst, l(zip(ips, networks)))
248
    else:
249
      ToStdout("  not used by any instances")
250

    
251

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

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

259
  @rtype: int
260
  @return: the desired exit code
261

262
  """
263

    
264
  # TODO: add "network": opts.network,
265
  all_changes = {
266
    "gateway": opts.gateway,
267
    "add_reserved_ips": _HandleReservedIPs(opts.add_reserved_ips),
268
    "remove_reserved_ips": _HandleReservedIPs(opts.remove_reserved_ips),
269
    "mac_prefix": opts.mac_prefix,
270
    "network_type": opts.network_type,
271
    "gateway6": opts.gateway6,
272
    "network6": opts.network6,
273
  }
274

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

    
279
  # pylint: disable=W0142
280
  op = opcodes.OpNetworkSetParams(network_name=args[0], **all_changes)
281

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

    
285

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

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

295
  """
296
  (network_name,) = args
297
  op = opcodes.OpNetworkRemove(network_name=network_name, force=opts.force)
298
  SubmitOpCode(op, opts=opts)
299

    
300

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

    
358

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