Merge branch 'devel-2.7'
[ganeti-local] / lib / client / gnt_network.py
index cde1358..b07aa7a 100644 (file)
@@ -1,7 +1,7 @@
 #
 #
 
-# Copyright (C) 2011 Google Inc.
+# Copyright (C) 2011, 2012 Google Inc.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # W0401: Wildcard import ganeti.cli
 # W0614: Unused import %s from wildcard import (since we need cli)
 
+import textwrap
+import itertools
+
 from ganeti.cli import *
 from ganeti import constants
 from ganeti import opcodes
 from ganeti import utils
-from textwrap import wrap
+from ganeti import errors
 
 
 #: default list of fields for L{ListNetworks}
@@ -37,12 +40,13 @@ _LIST_DEF_FIELDS = ["name", "network", "gateway",
 
 
 def _HandleReservedIPs(ips):
-  if ips is not None:
-    if ips == "":
-      return []
-    else:
-      return utils.UnescapeAndSplit(ips, sep=",")
-  return None
+  if ips is None:
+    return None
+  elif not ips:
+    return []
+  else:
+    return utils.UnescapeAndSplit(ips, sep=",")
+
 
 def AddNetwork(opts, args):
   """Add a network to the cluster.
@@ -56,11 +60,17 @@ def AddNetwork(opts, args):
   """
   (network_name, ) = args
 
+  if opts.network is None:
+    raise errors.OpPrereqError("The --network option must be given",
+                               errors.ECODE_INVAL)
+
   if opts.tags is not None:
     tags = opts.tags.split(",")
   else:
     tags = []
 
+  reserved_ips = _HandleReservedIPs(opts.add_reserved_ips)
+
   op = opcodes.OpNetworkAdd(network_name=network_name,
                             gateway=opts.gateway,
                             network=opts.network,
@@ -68,68 +78,75 @@ def AddNetwork(opts, args):
                             network6=opts.network6,
                             mac_prefix=opts.mac_prefix,
                             network_type=opts.network_type,
-                            add_reserved_ips=\
-                              _HandleReservedIPs(opts.add_reserved_ips),
+                            add_reserved_ips=reserved_ips,
+                            conflicts_check=opts.conflicts_check,
                             tags=tags)
-  SubmitOpCode(op, opts=opts)
+  SubmitOrSend(op, opts)
+
+
+def _GetDefaultGroups(cl, groups):
+  """Gets list of groups to operate on.
+
+  If C{groups} doesn't contain groups, a list of all groups in the cluster is
+  returned.
+
+  @type cl: L{luxi.Client}
+  @type groups: list
+  @rtype: list
+
+  """
+  if groups:
+    return groups
+
+  return list(itertools.chain(*cl.QueryGroups([], ["uuid"], False)))
 
 
-def MapNetwork(opts, args):
+def ConnectNetwork(opts, args):
   """Map a network to a node group.
 
   @param opts: the command line options selected by the user
   @type args: list
-  @param args: a list of length 3 with network, nodegroup, mode, physlink
+  @param args: Network, mode, physlink and node groups
   @rtype: int
   @return: the desired exit code
 
   """
-  network = args[0]
-  groups = args[1]
-  mode = args[2]
-  link = args[3]
-
-  #TODO: allow comma separated group names
-  if groups == 'all':
-    cl = GetClient()
-    (groups, ) = cl.QueryGroups([], ['name'], False)
-  else:
-    groups = [groups]
+  cl = GetClient()
+
+  (network, mode, link) = args[:3]
+  groups = _GetDefaultGroups(cl, args[3:])
 
+  # TODO: Change logic to support "--submit"
   for group in groups:
     op = opcodes.OpNetworkConnect(group_name=group,
                                   network_name=network,
                                   network_mode=mode,
                                   network_link=link,
                                   conflicts_check=opts.conflicts_check)
-    SubmitOpCode(op, opts=opts)
+    SubmitOpCode(op, opts=opts, cl=cl)
 
 
-def UnmapNetwork(opts, args):
+def DisconnectNetwork(opts, args):
   """Unmap a network from a node group.
 
   @param opts: the command line options selected by the user
   @type args: list
-  @param args: a list of length 3 with network, nodegorup
+  @param args: Network and node groups
   @rtype: int
   @return: the desired exit code
 
   """
-  network = args[0]
-  groups = args[1]
+  cl = GetClient()
 
-  #TODO: allow comma separated group names
-  if groups == 'all':
-    cl = GetClient()
-    (groups, ) = cl.QueryGroups([], ['name'], False)
-  else:
-    groups = [groups]
+  (network, ) = args[:1]
+  groups = _GetDefaultGroups(cl, args[1:])
 
+  # TODO: Change logic to support "--submit"
   for group in groups:
     op = opcodes.OpNetworkDisconnect(group_name=group,
                                      network_name=network,
                                      conflicts_check=opts.conflicts_check)
-    SubmitOpCode(op, opts=opts)
+    SubmitOpCode(op, opts=opts, cl=cl)
 
 
 def ListNetworks(opts, args):
@@ -144,14 +161,19 @@ def ListNetworks(opts, args):
   """
   desired_fields = ParseFields(opts.output, _LIST_DEF_FIELDS)
   fmtoverride = {
-    "group_list": (",".join, False),
+    "group_list":
+      (lambda data: utils.CommaJoin("%s (%s, %s)" % (name, mode, link)
+                                    for (name, mode, link) in data),
+       False),
     "inst_list": (",".join, False),
     "tags": (",".join, False),
-  }
+    }
 
+  cl = GetClient()
   return GenericList(constants.QR_NETWORK, desired_fields, args, None,
                      opts.separator, not opts.no_headers,
-                     verbose=opts.verbose, format_override=fmtoverride)
+                     verbose=opts.verbose, format_override=fmtoverride,
+                     cl=cl)
 
 
 def ListNetworkFields(opts, args):
@@ -171,7 +193,6 @@ def ListNetworkFields(opts, args):
 def ShowNetworkConfig(_, args):
   """Show network information.
 
-  @param opts: the command line options selected by the user
   @type args: list
   @param args: should either be an empty list, in which case
       we show information about all nodes, or should contain
@@ -186,33 +207,36 @@ def ShowNetworkConfig(_, args):
                                     "mac_prefix", "network_type",
                                     "free_count", "reserved_count",
                                     "map", "group_list", "inst_list",
-                                    "external_reservations"],
+                                    "external_reservations",
+                                    "serial_no", "uuid"],
                             names=args, use_locking=False)
 
   for (name, network, gateway, network6, gateway6,
        mac_prefix, network_type, free_count, reserved_count,
-       mapping, group_list, instances, ext_res) in result:
+       mapping, group_list, instances, ext_res, serial, uuid) in result:
     size = free_count + reserved_count
     ToStdout("Network name: %s", name)
-    ToStdout("  subnet: %s", network)
-    ToStdout("  gateway: %s", gateway)
-    ToStdout("  subnet6: %s", network6)
-    ToStdout("  gateway6: %s", gateway6)
-    ToStdout("  mac prefix: %s", mac_prefix)
-    ToStdout("  type: %s", network_type)
-    ToStdout("  size: %d", size)
-    ToStdout("  free: %d (%.2f%%)", free_count,
-             100 * float(free_count)/float(size))
-    ToStdout("  usage map:")
+    ToStdout("UUID: %s", uuid)
+    ToStdout("Serial number: %d", serial)
+    ToStdout("  Subnet: %s", network)
+    ToStdout("  Gateway: %s", gateway)
+    ToStdout("  IPv6 Subnet: %s", network6)
+    ToStdout("  IPv6 Gateway: %s", gateway6)
+    ToStdout("  Mac Prefix: %s", mac_prefix)
+    ToStdout("  Type: %s", network_type)
+    ToStdout("  Size: %d", size)
+    ToStdout("  Free: %d (%.2f%%)", free_count,
+             100 * float(free_count) / float(size))
+    ToStdout("  Usage map:")
     idx = 0
-    for line in wrap(mapping, width=64):
+    for line in textwrap.wrap(mapping, width=64):
       ToStdout("     %s %s %d", str(idx).rjust(3), line.ljust(64), idx + 63)
       idx += 64
     ToStdout("         (X) used    (.) free")
 
     if ext_res:
       ToStdout("  externally reserved IPs:")
-      for line in wrap(ext_res, width=64):
+      for line in textwrap.wrap(ext_res, width=64):
         ToStdout("    %s" % line)
 
     if group_list:
@@ -229,7 +253,7 @@ def ShowNetworkConfig(_, args):
                                                 ["nic.ips", "nic.networks"],
                                                 use_locking=False)
 
-        l = lambda value: ", ".join(str(idx)+":"+str(ip)
+        l = lambda value: ", ".join(str(idx) + ":" + str(ip)
                                     for idx, (ip, net) in enumerate(value)
                                       if net == name)
 
@@ -249,7 +273,6 @@ def SetNetworkParams(opts, args):
   @return: the desired exit code
 
   """
-
   # TODO: add "network": opts.network,
   all_changes = {
     "gateway": opts.gateway,
@@ -284,14 +307,15 @@ def RemoveNetwork(opts, args):
   """
   (network_name,) = args
   op = opcodes.OpNetworkRemove(network_name=network_name, force=opts.force)
-  SubmitOpCode(op, opts=opts)
+  SubmitOrSend(op, opts)
 
 
 commands = {
   "add": (
     AddNetwork, ARGS_ONE_NETWORK,
-    [DRY_RUN_OPT, NETWORK_OPT, GATEWAY_OPT, ADD_RESERVED_IPS_OPT, TAG_ADD_OPT,
-     MAC_PREFIX_OPT, NETWORK_TYPE_OPT, NETWORK6_OPT, GATEWAY6_OPT],
+    [DRY_RUN_OPT, NETWORK_OPT, GATEWAY_OPT, ADD_RESERVED_IPS_OPT,
+     MAC_PREFIX_OPT, NETWORK_TYPE_OPT, NETWORK6_OPT, GATEWAY6_OPT,
+     NOCONFLICTSCHECK_OPT, TAG_ADD_OPT, PRIORITY_OPT, SUBMIT_OPT],
     "<network_name>", "Add a new IP network to the cluster"),
   "list": (
     ListNetworks, ARGS_MANY_NETWORKS,
@@ -309,24 +333,28 @@ commands = {
   "modify": (
     SetNetworkParams, ARGS_ONE_NETWORK,
     [DRY_RUN_OPT, SUBMIT_OPT, ADD_RESERVED_IPS_OPT, REMOVE_RESERVED_IPS_OPT,
-     GATEWAY_OPT, MAC_PREFIX_OPT, NETWORK_TYPE_OPT, NETWORK6_OPT, GATEWAY6_OPT],
+     GATEWAY_OPT, MAC_PREFIX_OPT, NETWORK_TYPE_OPT, NETWORK6_OPT, GATEWAY6_OPT,
+     PRIORITY_OPT],
     "<network_name>", "Alters the parameters of a network"),
   "connect": (
-    MapNetwork,
-    [ArgNetwork(min=1, max=1), ArgGroup(min=1, max=1),
-     ArgUnknown(min=1, max=1), ArgUnknown(min=1, max=1)],
-    [NOCONFLICTSCHECK_OPT],
-    "<network_name> <node_group> <mode> <link>",
+    ConnectNetwork,
+    [ArgNetwork(min=1, max=1),
+     ArgChoice(min=1, max=1, choices=constants.NIC_VALID_MODES),
+     ArgUnknown(min=1, max=1),
+     ArgGroup()],
+    [NOCONFLICTSCHECK_OPT, PRIORITY_OPT],
+    "<network_name> <mode> <link> [<node_group>...]",
     "Map a given network to the specified node group"
     " with given mode and link (netparams)"),
   "disconnect": (
-    UnmapNetwork,
-    [ArgNetwork(min=1, max=1), ArgGroup(min=1, max=1)],
-    [NOCONFLICTSCHECK_OPT],
-    "<network_name> <node_group>",
+    DisconnectNetwork,
+    [ArgNetwork(min=1, max=1), ArgGroup()],
+    [NOCONFLICTSCHECK_OPT, PRIORITY_OPT],
+    "<network_name> [<node_group>...]",
     "Unmap a given network from a specified node group"),
   "remove": (
-    RemoveNetwork, ARGS_ONE_NETWORK, [FORCE_OPT, DRY_RUN_OPT],
+    RemoveNetwork, ARGS_ONE_NETWORK,
+    [FORCE_OPT, DRY_RUN_OPT, SUBMIT_OPT, PRIORITY_OPT],
     "[--dry-run] <network_id>",
     "Remove an (empty) network from the cluster"),
   "list-tags": (