4 # Copyright (C) 2011, 2012 Google Inc.
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.
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.
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
21 """IP pool related commands"""
23 # pylint: disable=W0401,W0614
24 # W0401: Wildcard import ganeti.cli
25 # W0614: Unused import %s from wildcard import (since we need cli)
30 from ganeti.cli import *
31 from ganeti import constants
32 from ganeti import opcodes
33 from ganeti import utils
34 from ganeti import errors
37 #: default list of fields for L{ListNetworks}
38 _LIST_DEF_FIELDS = ["name", "network", "gateway",
39 "network_type", "mac_prefix", "group_list", "tags"]
42 def _HandleReservedIPs(ips):
48 return utils.UnescapeAndSplit(ips, sep=",")
51 def AddNetwork(opts, args):
52 """Add a network to the cluster.
54 @param opts: the command line options selected by the user
56 @param args: a list of length 1 with the network name to create
58 @return: the desired exit code
61 (network_name, ) = args
63 if opts.network is None:
64 raise errors.OpPrereqError("The --network option must be given",
67 if opts.tags is not None:
68 tags = opts.tags.split(",")
72 reserved_ips = _HandleReservedIPs(opts.add_reserved_ips)
74 op = opcodes.OpNetworkAdd(network_name=network_name,
77 gateway6=opts.gateway6,
78 network6=opts.network6,
79 mac_prefix=opts.mac_prefix,
80 network_type=opts.network_type,
81 add_reserved_ips=reserved_ips,
82 conflicts_check=opts.conflicts_check,
84 SubmitOrSend(op, opts)
87 def _GetDefaultGroups(cl, groups):
88 """Gets list of groups to operate on.
90 If C{groups} doesn't contain groups, a list of all groups in the cluster is
93 @type cl: L{luxi.Client}
101 return list(itertools.chain(*cl.QueryGroups([], ["uuid"], False)))
104 def ConnectNetwork(opts, args):
105 """Map a network to a node group.
107 @param opts: the command line options selected by the user
109 @param args: Network, mode, physlink and node groups
111 @return: the desired exit code
116 (network, mode, link) = args[:3]
117 groups = _GetDefaultGroups(cl, args[3:])
119 # TODO: Change logic to support "--submit"
121 op = opcodes.OpNetworkConnect(group_name=group,
122 network_name=network,
125 conflicts_check=opts.conflicts_check)
126 SubmitOpCode(op, opts=opts, cl=cl)
129 def DisconnectNetwork(opts, args):
130 """Unmap a network from a node group.
132 @param opts: the command line options selected by the user
134 @param args: Network and node groups
136 @return: the desired exit code
141 (network, ) = args[:1]
142 groups = _GetDefaultGroups(cl, args[1:])
144 # TODO: Change logic to support "--submit"
146 op = opcodes.OpNetworkDisconnect(group_name=group,
147 network_name=network,
148 conflicts_check=opts.conflicts_check)
149 SubmitOpCode(op, opts=opts, cl=cl)
152 def ListNetworks(opts, args):
153 """List Ip pools and their properties.
155 @param opts: the command line options selected by the user
157 @param args: networks to list, or empty for all
159 @return: the desired exit code
162 desired_fields = ParseFields(opts.output, _LIST_DEF_FIELDS)
165 (lambda data: utils.CommaJoin("%s (%s, %s)" % (name, mode, link)
166 for (name, mode, link) in data),
168 "inst_list": (",".join, False),
169 "tags": (",".join, False),
172 return GenericList(constants.QR_NETWORK, desired_fields, args, None,
173 opts.separator, not opts.no_headers,
174 verbose=opts.verbose, format_override=fmtoverride)
177 def ListNetworkFields(opts, args):
178 """List network fields.
180 @param opts: the command line options selected by the user
182 @param args: fields to list, or empty for all
184 @return: the desired exit code
187 return GenericListFields(constants.QR_NETWORK, args, opts.separator,
191 def ShowNetworkConfig(_, args):
192 """Show network information.
195 @param args: should either be an empty list, in which case
196 we show information about all nodes, or should contain
197 a list of networks (names or UUIDs) to be queried for information
199 @return: the desired exit code
203 result = cl.QueryNetworks(fields=["name", "network", "gateway",
204 "network6", "gateway6",
205 "mac_prefix", "network_type",
206 "free_count", "reserved_count",
207 "map", "group_list", "inst_list",
208 "external_reservations",
209 "serial_no", "uuid"],
210 names=args, use_locking=False)
212 for (name, network, gateway, network6, gateway6,
213 mac_prefix, network_type, free_count, reserved_count,
214 mapping, group_list, instances, ext_res, serial, uuid) in result:
215 size = free_count + reserved_count
216 ToStdout("Network name: %s", name)
217 ToStdout("UUID: %s", uuid)
218 ToStdout("Serial number: %d", serial)
219 ToStdout(" Subnet: %s", network)
220 ToStdout(" Gateway: %s", gateway)
221 ToStdout(" IPv6 Subnet: %s", network6)
222 ToStdout(" IPv6 Gateway: %s", gateway6)
223 ToStdout(" Mac Prefix: %s", mac_prefix)
224 ToStdout(" Type: %s", network_type)
225 ToStdout(" Size: %d", size)
226 ToStdout(" Free: %d (%.2f%%)", free_count,
227 100 * float(free_count) / float(size))
228 ToStdout(" Usage map:")
230 for line in textwrap.wrap(mapping, width=64):
231 ToStdout(" %s %s %d", str(idx).rjust(3), line.ljust(64), idx + 63)
233 ToStdout(" (X) used (.) free")
236 ToStdout(" externally reserved IPs:")
237 for line in textwrap.wrap(ext_res, width=64):
238 ToStdout(" %s" % line)
241 ToStdout(" connected to node groups:")
242 for group in group_list:
243 ToStdout(" %s", group)
245 ToStdout(" not connected to any node group")
248 ToStdout(" used by %d instances:", len(instances))
249 for inst in instances:
250 ((ips, networks), ) = cl.QueryInstances([inst],
251 ["nic.ips", "nic.networks"],
254 l = lambda value: ", ".join(str(idx) + ":" + str(ip)
255 for idx, (ip, net) in enumerate(value)
258 ToStdout(" %s : %s", inst, l(zip(ips, networks)))
260 ToStdout(" not used by any instances")
263 def SetNetworkParams(opts, args):
264 """Modifies an IP address pool's parameters.
266 @param opts: the command line options selected by the user
268 @param args: should contain only one element, the node group name
271 @return: the desired exit code
274 # TODO: add "network": opts.network,
276 "gateway": opts.gateway,
277 "add_reserved_ips": _HandleReservedIPs(opts.add_reserved_ips),
278 "remove_reserved_ips": _HandleReservedIPs(opts.remove_reserved_ips),
279 "mac_prefix": opts.mac_prefix,
280 "network_type": opts.network_type,
281 "gateway6": opts.gateway6,
282 "network6": opts.network6,
285 if all_changes.values().count(None) == len(all_changes):
286 ToStderr("Please give at least one of the parameters.")
289 # pylint: disable=W0142
290 op = opcodes.OpNetworkSetParams(network_name=args[0], **all_changes)
292 # TODO: add feedback to user, e.g. list the modifications
293 SubmitOrSend(op, opts)
296 def RemoveNetwork(opts, args):
297 """Remove an IP address pool from the cluster.
299 @param opts: the command line options selected by the user
301 @param args: a list of length 1 with the id of the IP address pool to remove
303 @return: the desired exit code
306 (network_name,) = args
307 op = opcodes.OpNetworkRemove(network_name=network_name, force=opts.force)
308 SubmitOrSend(op, opts)
313 AddNetwork, ARGS_ONE_NETWORK,
314 [DRY_RUN_OPT, NETWORK_OPT, GATEWAY_OPT, ADD_RESERVED_IPS_OPT,
315 MAC_PREFIX_OPT, NETWORK_TYPE_OPT, NETWORK6_OPT, GATEWAY6_OPT,
316 NOCONFLICTSCHECK_OPT, TAG_ADD_OPT, PRIORITY_OPT, SUBMIT_OPT],
317 "<network_name>", "Add a new IP network to the cluster"),
319 ListNetworks, ARGS_MANY_NETWORKS,
320 [NOHDR_OPT, SEP_OPT, FIELDS_OPT, VERBOSE_OPT],
322 "Lists the IP networks in the cluster. The available fields can be shown"
323 " using the \"list-fields\" command (see the man page for details)."
324 " The default list is (in order): %s." % utils.CommaJoin(_LIST_DEF_FIELDS)),
326 ListNetworkFields, [ArgUnknown()], [NOHDR_OPT, SEP_OPT], "[fields...]",
327 "Lists all available fields for networks"),
329 ShowNetworkConfig, ARGS_MANY_NETWORKS, [],
330 "[<network_name>...]", "Show information about the network(s)"),
332 SetNetworkParams, ARGS_ONE_NETWORK,
333 [DRY_RUN_OPT, SUBMIT_OPT, ADD_RESERVED_IPS_OPT, REMOVE_RESERVED_IPS_OPT,
334 GATEWAY_OPT, MAC_PREFIX_OPT, NETWORK_TYPE_OPT, NETWORK6_OPT, GATEWAY6_OPT,
336 "<network_name>", "Alters the parameters of a network"),
339 [ArgNetwork(min=1, max=1),
340 ArgChoice(min=1, max=1, choices=constants.NIC_VALID_MODES),
341 ArgUnknown(min=1, max=1),
343 [NOCONFLICTSCHECK_OPT, PRIORITY_OPT],
344 "<network_name> <mode> <link> [<node_group>...]",
345 "Map a given network to the specified node group"
346 " with given mode and link (netparams)"),
349 [ArgNetwork(min=1, max=1), ArgGroup()],
350 [NOCONFLICTSCHECK_OPT, PRIORITY_OPT],
351 "<network_name> [<node_group>...]",
352 "Unmap a given network from a specified node group"),
354 RemoveNetwork, ARGS_ONE_NETWORK,
355 [FORCE_OPT, DRY_RUN_OPT, SUBMIT_OPT, PRIORITY_OPT],
356 "[--dry-run] <network_id>",
357 "Remove an (empty) network from the cluster"),
359 ListTags, ARGS_ONE_NETWORK, [],
360 "<network_name>", "List the tags of the given network"),
362 AddTags, [ArgNetwork(min=1, max=1), ArgUnknown()],
363 [TAG_SRC_OPT, PRIORITY_OPT, SUBMIT_OPT],
364 "<network_name> tag...", "Add tags to the given network"),
366 RemoveTags, [ArgNetwork(min=1, max=1), ArgUnknown()],
367 [TAG_SRC_OPT, PRIORITY_OPT, SUBMIT_OPT],
368 "<network_name> tag...", "Remove tags from given network"),
373 return GenericMain(commands, override={"tag_type": constants.TAG_NETWORK})