Remove two hlint overrides
[ganeti-local] / lib / client / gnt_network.py
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 from ganeti.cli import *
28 from ganeti import constants
29 from ganeti import opcodes
30 from ganeti import utils
31 from textwrap import wrap
32
33
34 #: 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 def _HandleReservedIPs(ips):
40   if ips is not None:
41     if ips == "":
42       return []
43     else:
44       return utils.UnescapeAndSplit(ips, sep=",")
45   return None
46
47
48 def AddNetwork(opts, args):
49   """Add a network to the cluster.
50
51   @param opts: the command line options selected by the user
52   @type args: list
53   @param args: a list of length 1 with the network name to create
54   @rtype: int
55   @return: the desired exit code
56
57   """
58   (network_name, ) = args
59
60   if opts.tags is not None:
61     tags = opts.tags.split(",")
62   else:
63     tags = []
64
65   op = opcodes.OpNetworkAdd(
66                     network_name=network_name,
67                     gateway=opts.gateway,
68                     network=opts.network,
69                     gateway6=opts.gateway6,
70                     network6=opts.network6,
71                     mac_prefix=opts.mac_prefix,
72                     network_type=opts.network_type,
73                     add_reserved_ips=_HandleReservedIPs(opts.add_reserved_ips),
74                     conflicts_check=opts.conflicts_check,
75                     tags=tags)
76   SubmitOpCode(op, opts=opts)
77
78
79 def MapNetwork(opts, args):
80   """Map a network to a node group.
81
82   @param opts: the command line options selected by the user
83   @type args: list
84   @param args: a list of length 3 with network, nodegroup, mode, physlink
85   @rtype: int
86   @return: the desired exit code
87
88   """
89   network = args[0]
90   groups = args[1]
91   mode = args[2]
92   link = args[3]
93
94   # TODO: allow comma separated group names
95   if groups == "all":
96     cl = GetClient()
97     (groups, ) = cl.QueryGroups([], ["name"], False)
98   else:
99     groups = [groups]
100
101   for group in groups:
102     op = opcodes.OpNetworkConnect(group_name=group,
103                                   network_name=network,
104                                   network_mode=mode,
105                                   network_link=link,
106                                   conflicts_check=opts.conflicts_check)
107     SubmitOpCode(op, opts=opts)
108
109
110 def UnmapNetwork(opts, args):
111   """Unmap a network from a node group.
112
113   @param opts: the command line options selected by the user
114   @type args: list
115   @param args: a list of length 3 with network, nodegorup
116   @rtype: int
117   @return: the desired exit code
118
119   """
120   network = args[0]
121   groups = args[1]
122
123   #TODO: allow comma separated group names
124   if groups == "all":
125     cl = GetClient()
126     (groups, ) = cl.QueryGroups([], ["name"], False)
127   else:
128     groups = [groups]
129
130   for group in groups:
131     op = opcodes.OpNetworkDisconnect(group_name=group,
132                                      network_name=network,
133                                      conflicts_check=opts.conflicts_check)
134     SubmitOpCode(op, opts=opts)
135
136
137 def ListNetworks(opts, args):
138   """List Ip pools and their properties.
139
140   @param opts: the command line options selected by the user
141   @type args: list
142   @param args: networks to list, or empty for all
143   @rtype: int
144   @return: the desired exit code
145
146   """
147   desired_fields = ParseFields(opts.output, _LIST_DEF_FIELDS)
148   fmtoverride = {
149     "group_list": (",".join, False),
150     "inst_list": (",".join, False),
151     "tags": (",".join, False),
152   }
153
154   return GenericList(constants.QR_NETWORK, desired_fields, args, None,
155                      opts.separator, not opts.no_headers,
156                      verbose=opts.verbose, format_override=fmtoverride)
157
158
159 def ListNetworkFields(opts, args):
160   """List network fields.
161
162   @param opts: the command line options selected by the user
163   @type args: list
164   @param args: fields to list, or empty for all
165   @rtype: int
166   @return: the desired exit code
167
168   """
169   return GenericListFields(constants.QR_NETWORK, args, opts.separator,
170                            not opts.no_headers)
171
172
173 def ShowNetworkConfig(_, args):
174   """Show network information.
175
176   @type args: list
177   @param args: should either be an empty list, in which case
178       we show information about all nodes, or should contain
179       a list of networks (names or UUIDs) to be queried for information
180   @rtype: int
181   @return: the desired exit code
182
183   """
184   cl = GetClient()
185   result = cl.QueryNetworks(fields=["name", "network", "gateway",
186                                     "network6", "gateway6",
187                                     "mac_prefix", "network_type",
188                                     "free_count", "reserved_count",
189                                     "map", "group_list", "inst_list",
190                                     "external_reservations",
191                                     "serial_no", "uuid"],
192                             names=args, use_locking=False)
193
194   for (name, network, gateway, network6, gateway6,
195        mac_prefix, network_type, free_count, reserved_count,
196        mapping, group_list, instances, ext_res, serial, uuid) in result:
197     size = free_count + reserved_count
198     ToStdout("Network name: %s", name)
199     ToStdout("UUID: %s", uuid)
200     ToStdout("Serial number: %d", serial)
201     ToStdout("  Subnet: %s", network)
202     ToStdout("  Gateway: %s", gateway)
203     ToStdout("  IPv6 Subnet: %s", network6)
204     ToStdout("  IPv6 Gateway: %s", gateway6)
205     ToStdout("  Mac Prefix: %s", mac_prefix)
206     ToStdout("  Type: %s", network_type)
207     ToStdout("  Size: %d", size)
208     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       ToStdout("     %s %s %d", str(idx).rjust(3), line.ljust(64), idx + 63)
214       idx += 64
215     ToStdout("         (X) used    (.) free")
216
217     if ext_res:
218       ToStdout("  externally reserved IPs:")
219       for line in wrap(ext_res, width=64):
220         ToStdout("    %s" % line)
221
222     if group_list:
223       ToStdout("  connected to node groups:")
224       for group in group_list:
225         ToStdout("    %s", group)
226     else:
227       ToStdout("  not connected to any node group")
228
229     if instances:
230       ToStdout("  used by %d instances:", len(instances))
231       for inst in instances:
232         ((ips, networks), ) = cl.QueryInstances([inst],
233                                                 ["nic.ips", "nic.networks"],
234                                                 use_locking=False)
235
236         l = lambda value: ", ".join(str(idx) + ":" + str(ip)
237                                     for idx, (ip, net) in enumerate(value)
238                                       if net == name)
239
240         ToStdout("    %s : %s", inst, l(zip(ips, networks)))
241     else:
242       ToStdout("  not used by any instances")
243
244
245 def SetNetworkParams(opts, args):
246   """Modifies an IP address pool's parameters.
247
248   @param opts: the command line options selected by the user
249   @type args: list
250   @param args: should contain only one element, the node group name
251
252   @rtype: int
253   @return: the desired exit code
254
255   """
256
257   # TODO: add "network": opts.network,
258   all_changes = {
259     "gateway": opts.gateway,
260     "add_reserved_ips": _HandleReservedIPs(opts.add_reserved_ips),
261     "remove_reserved_ips": _HandleReservedIPs(opts.remove_reserved_ips),
262     "mac_prefix": opts.mac_prefix,
263     "network_type": opts.network_type,
264     "gateway6": opts.gateway6,
265     "network6": opts.network6,
266   }
267
268   if all_changes.values().count(None) == len(all_changes):
269     ToStderr("Please give at least one of the parameters.")
270     return 1
271
272   # pylint: disable=W0142
273   op = opcodes.OpNetworkSetParams(network_name=args[0], **all_changes)
274
275   # TODO: add feedback to user, e.g. list the modifications
276   SubmitOrSend(op, opts)
277
278
279 def RemoveNetwork(opts, args):
280   """Remove an IP address pool from the cluster.
281
282   @param opts: the command line options selected by the user
283   @type args: list
284   @param args: a list of length 1 with the id of the IP address pool to remove
285   @rtype: int
286   @return: the desired exit code
287
288   """
289   (network_name,) = args
290   op = opcodes.OpNetworkRemove(network_name=network_name, force=opts.force)
291   SubmitOpCode(op, opts=opts)
292
293
294 commands = {
295   "add": (
296     AddNetwork, ARGS_ONE_NETWORK,
297     [DRY_RUN_OPT, NETWORK_OPT, GATEWAY_OPT, ADD_RESERVED_IPS_OPT,
298      MAC_PREFIX_OPT, NETWORK_TYPE_OPT, NETWORK6_OPT, GATEWAY6_OPT,
299      NOCONFLICTSCHECK_OPT, TAG_ADD_OPT],
300     "<network_name>", "Add a new IP network to the cluster"),
301   "list": (
302     ListNetworks, ARGS_MANY_NETWORKS,
303     [NOHDR_OPT, SEP_OPT, FIELDS_OPT, VERBOSE_OPT],
304     "[<network_id>...]",
305     "Lists the IP networks in the cluster. The available fields can be shown"
306     " using the \"list-fields\" command (see the man page for details)."
307     " The default list is (in order): %s." % utils.CommaJoin(_LIST_DEF_FIELDS)),
308   "list-fields": (
309     ListNetworkFields, [ArgUnknown()], [NOHDR_OPT, SEP_OPT], "[fields...]",
310     "Lists all available fields for networks"),
311   "info": (
312     ShowNetworkConfig, ARGS_MANY_NETWORKS, [],
313     "[<network_name>...]", "Show information about the network(s)"),
314   "modify": (
315     SetNetworkParams, ARGS_ONE_NETWORK,
316     [DRY_RUN_OPT, SUBMIT_OPT, ADD_RESERVED_IPS_OPT, REMOVE_RESERVED_IPS_OPT,
317      GATEWAY_OPT, MAC_PREFIX_OPT, NETWORK_TYPE_OPT, NETWORK6_OPT, GATEWAY6_OPT],
318     "<network_name>", "Alters the parameters of a network"),
319   "connect": (
320     MapNetwork,
321     [ArgNetwork(min=1, max=1), ArgGroup(min=1, max=1),
322      ArgUnknown(min=1, max=1), ArgUnknown(min=1, max=1)],
323     [NOCONFLICTSCHECK_OPT],
324     "<network_name> <node_group> <mode> <link>",
325     "Map a given network to the specified node group"
326     " with given mode and link (netparams)"),
327   "disconnect": (
328     UnmapNetwork,
329     [ArgNetwork(min=1, max=1), ArgGroup(min=1, max=1)],
330     [NOCONFLICTSCHECK_OPT],
331     "<network_name> <node_group>",
332     "Unmap a given network from a specified node group"),
333   "remove": (
334     RemoveNetwork, ARGS_ONE_NETWORK, [FORCE_OPT, DRY_RUN_OPT],
335     "[--dry-run] <network_id>",
336     "Remove an (empty) network from the cluster"),
337   "list-tags": (
338     ListTags, ARGS_ONE_NETWORK, [],
339     "<network_name>", "List the tags of the given network"),
340   "add-tags": (
341     AddTags, [ArgNetwork(min=1, max=1), ArgUnknown()],
342     [TAG_SRC_OPT, PRIORITY_OPT, SUBMIT_OPT],
343     "<network_name> tag...", "Add tags to the given network"),
344   "remove-tags": (
345     RemoveTags, [ArgNetwork(min=1, max=1), ArgUnknown()],
346     [TAG_SRC_OPT, PRIORITY_OPT, SUBMIT_OPT],
347     "<network_name> tag...", "Remove tags from given network"),
348 }
349
350
351 def Main():
352   return GenericMain(commands, override={"tag_type": constants.TAG_NETWORK})