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