commit 8140e24f74b068db0905a2e113bb4769b7d7b766
Author: Dimitris Aragiorgis <dimara@grnet.gr>
Date:   Fri Jul 27 10:50:04 2012 +0300

    Add tags in network objects
    
    Support:
     - gnt-network add --tags.
     - gnt-network list-tags/add-tags/remove-tags/.
     - gnt-network list -o +tags
    
    Signed-off-by: Dimitris Aragiorgis <dimara@grnet.gr>
    Reviewed-by: Iustin Pop <iustin@google.com>

diff --git a/lib/cli.py b/lib/cli.py
index f46a0a0..3ba016c 100644
--- a/lib/cli.py
+++ b/lib/cli.py
@@ -435,6 +435,7 @@ def _ExtractTagsObject(opts, args):
     retval = kind, None
   elif kind in (constants.TAG_NODEGROUP,
                 constants.TAG_NODE,
+                constants.TAG_NETWORK,
                 constants.TAG_INSTANCE):
     if not args:
       raise errors.OpPrereqError("no arguments passed to the command",
diff --git a/lib/client/gnt_network.py b/lib/client/gnt_network.py
index 161059b..207873c 100644
--- a/lib/client/gnt_network.py
+++ b/lib/client/gnt_network.py
@@ -33,7 +33,7 @@ from textwrap import wrap
 
 #: default list of fields for L{ListNetworks}
 _LIST_DEF_FIELDS = ["name", "network", "gateway",
-                    "network_type", "mac_prefix", "group_list"]
+                    "network_type", "mac_prefix", "group_list", "tags"]
 
 
 def _HandleReservedIPs(ips):
@@ -56,6 +56,11 @@ def AddNetwork(opts, args):
   """
   (network_name, ) = args
 
+  if opts.tags is not None:
+    tags = opts.tags.split(",")
+  else:
+    tags = []
+
   op = opcodes.OpNetworkAdd(network_name=network_name,
                             gateway=opts.gateway,
                             network=opts.network,
@@ -63,7 +68,8 @@ 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=_HandleReservedIPs(opts.add_reserved_ips),
+                            tags=tags)
   SubmitOpCode(op, opts=opts)
 
 
@@ -139,6 +145,7 @@ def ListNetworks(opts, args):
   fmtoverride = {
     "group_list": (",".join, False),
     "inst_list": (",".join, False),
+    "tags": (",".join, False),
   }
 
   return GenericList(constants.QR_NETWORK, desired_fields, args, None,
@@ -283,7 +290,7 @@ def RemoveNetwork(opts, args):
 commands = {
   "add": (
     AddNetwork, ARGS_ONE_NETWORK,
-    [DRY_RUN_OPT, NETWORK_OPT, GATEWAY_OPT, ADD_RESERVED_IPS_OPT,
+    [DRY_RUN_OPT, NETWORK_OPT, GATEWAY_OPT, ADD_RESERVED_IPS_OPT, TAG_ADD_OPT,
      MAC_PREFIX_OPT, NETWORK_TYPE_OPT, NETWORK6_OPT, GATEWAY6_OPT],
     "<network_name>", "Add a new IP network to the cluster"),
   "list": (
@@ -322,8 +329,19 @@ commands = {
     RemoveNetwork, ARGS_ONE_NETWORK, [FORCE_OPT, DRY_RUN_OPT],
     "[--dry-run] <network_id>",
     "Remove an (empty) network from the cluster"),
+  "list-tags": (
+    ListTags, ARGS_ONE_NETWORK, [],
+    "<network_name>", "List the tags of the given network"),
+  "add-tags": (
+    AddTags, [ArgNetwork(min=1, max=1), ArgUnknown()],
+    [TAG_SRC_OPT, PRIORITY_OPT, SUBMIT_OPT],
+    "<network_name> tag...", "Add tags to the given network"),
+  "remove-tags": (
+    RemoveTags, [ArgNetwork(min=1, max=1), ArgUnknown()],
+    [TAG_SRC_OPT, PRIORITY_OPT, SUBMIT_OPT],
+    "<network_name> tag...", "Remove tags from given network"),
 }
 
 
 def Main():
-  return GenericMain(commands)
+  return GenericMain(commands, override={"tag_type": constants.TAG_NETWORK})
diff --git a/lib/cmdlib.py b/lib/cmdlib.py
index eececc5..5536261 100644
--- a/lib/cmdlib.py
+++ b/lib/cmdlib.py
@@ -1316,7 +1316,7 @@ def _ExpandInstanceName(cfg, name):
   return _ExpandItemName(cfg.ExpandInstanceName, name, "Instance")
 
 def _BuildNetworkHookEnv(name, network, gateway, network6, gateway6,
-                         network_type, mac_prefix):
+                         network_type, mac_prefix, tags):
   env = dict()
   if name:
     env["NETWORK_NAME"] = name
@@ -1332,6 +1332,8 @@ def _BuildNetworkHookEnv(name, network, gateway, network6, gateway6,
     env["NETWORK_MAC_PREFIX"] = mac_prefix
   if network_type:
     env["NETWORK_TYPE"] = network_type
+  if tags:
+    env["NETWORK_TAGS"] = " ".join(tags)
 
   return env
 
@@ -1345,6 +1347,7 @@ def _BuildNetworkHookEnvByObject(lu, network):
     "gateway6": network.gateway6,
     "network_type": network.network_type,
     "mac_prefix": network.mac_prefix,
+    "tags" : network.tags,
   }
   return _BuildNetworkHookEnv(**args)
 
@@ -1431,6 +1434,8 @@ def _BuildInstanceHookEnv(name, primary_node, secondary_nodes, os_type, status,
             env["INSTANCE_NIC%d_NETWORK_MAC_PREFIX" % idx] = nobj.mac_prefix
           if nobj.network_type:
             env["INSTANCE_NIC%d_NETWORK_TYPE" % idx] = nobj.network_type
+          if nobj.tags:
+            env["INSTANCE_NIC%d_NETWORK_TAGS" % idx] = " ".join(nobj.tags)
       if mode == constants.NIC_MODE_BRIDGED:
         env["INSTANCE_NIC%d_BRIDGE" % idx] = link
   else:
@@ -15017,6 +15022,10 @@ class TagsLU(NoHooksLU): # pylint: disable=W0223
       self.group_uuid = self.cfg.LookupNodeGroup(self.op.name)
       lock_level = locking.LEVEL_NODEGROUP
       lock_name = self.group_uuid
+    elif self.op.kind == constants.TAG_NETWORK:
+      self.network_uuid = self.cfg.LookupNetwork(self.op.name)
+      lock_level = locking.LEVEL_NETWORK
+      lock_name = self.network_uuid
     else:
       lock_level = None
       lock_name = None
@@ -15039,6 +15048,8 @@ class TagsLU(NoHooksLU): # pylint: disable=W0223
       self.target = self.cfg.GetInstanceInfo(self.op.name)
     elif self.op.kind == constants.TAG_NODEGROUP:
       self.target = self.cfg.GetNodeGroup(self.group_uuid)
+    elif self.op.kind == constants.TAG_NETWORK:
+      self.target = self.cfg.GetNetwork(self.network_uuid)
     else:
       raise errors.OpPrereqError("Wrong tag type requested (%s)" %
                                  str(self.op.kind), errors.ECODE_INVAL)
@@ -15549,6 +15560,11 @@ class LUNetworkAdd(LogicalUnit):
     if self.op.mac_prefix:
       utils.NormalizeAndValidateMac(self.op.mac_prefix+":00:00:00")
 
+    # Check tag validity
+    for tag in self.op.tags:
+      objects.TaggableObject.ValidateTag(tag)
+
+
   def BuildHooksEnv(self):
     """Build hooks env.
 
@@ -15561,6 +15577,7 @@ class LUNetworkAdd(LogicalUnit):
       "gateway6": self.op.gateway6,
       "mac_prefix": self.op.mac_prefix,
       "network_type": self.op.network_type,
+      "tags": self.op.tags,
       }
     return _BuildNetworkHookEnv(**args)
 
@@ -15609,6 +15626,10 @@ class LUNetworkAdd(LogicalUnit):
         except errors.AddressPoolError, e:
           raise errors.OpExecError("Cannot reserve IP %s. %s " % (ip, e))
 
+    if self.op.tags:
+      for tag in self.op.tags:
+        nobj.AddTag(tag)
+
     self.cfg.AddNetwork(nobj, self.proc.GetECId(), check_uuid=False)
     del self.remove_locks[locking.LEVEL_NETWORK]
 
@@ -15714,6 +15735,7 @@ class LUNetworkSetParams(LogicalUnit):
     self.mac_prefix = self.network.mac_prefix
     self.network6 = self.network.network6
     self.gateway6 = self.network.gateway6
+    self.tags = self.network.tags
 
     self.pool = network.AddressPool(self.network)
 
@@ -15765,6 +15787,7 @@ class LUNetworkSetParams(LogicalUnit):
       "gateway6": self.gateway6,
       "mac_prefix": self.mac_prefix,
       "network_type": self.network_type,
+      "tags": self.tags,
       }
     return _BuildNetworkHookEnv(**args)
 
diff --git a/lib/constants.py b/lib/constants.py
index fb657e9..d2825db 100644
--- a/lib/constants.py
+++ b/lib/constants.py
@@ -538,11 +538,13 @@ TAG_CLUSTER = "cluster"
 TAG_NODEGROUP = "nodegroup"
 TAG_NODE = "node"
 TAG_INSTANCE = "instance"
+TAG_NETWORK = "network"
 VALID_TAG_TYPES = frozenset([
   TAG_CLUSTER,
   TAG_NODEGROUP,
   TAG_NODE,
   TAG_INSTANCE,
+  TAG_NETWORK,
   ])
 MAX_TAG_LEN = 128
 MAX_TAGS_PER_OBJ = 4096
diff --git a/lib/objects.py b/lib/objects.py
index df7ac7e..13378cd 100644
--- a/lib/objects.py
+++ b/lib/objects.py
@@ -1997,7 +1997,7 @@ class InstanceConsole(ConfigObject):
     return True
 
 
-class Network(ConfigObject):
+class Network(TaggableObject):
   """Object representing a network definition for ganeti.
 
   """
diff --git a/lib/opcodes.py b/lib/opcodes.py
index 21f2db3..9436935 100644
--- a/lib/opcodes.py
+++ b/lib/opcodes.py
@@ -2018,6 +2018,7 @@ class OpNetworkAdd(OpCode):
     ("mac_prefix", None, ht.TMaybeString, None),
     ("add_reserved_ips", None,
      ht.TOr(ht.TNone, ht.TListOf(_CheckCIDRAddrNotation)), None),
+    ("tags", ht.EmptyList, ht.TListOf(ht.TNonEmptyString), "Network tags"),
     ]
 
 class OpNetworkRemove(OpCode):
diff --git a/lib/query.py b/lib/query.py
index b7ff500..224e5b4 100644
--- a/lib/query.py
+++ b/lib/query.py
@@ -2532,11 +2532,17 @@ def _BuildNetworkFields():
   """Builds list of fields for network queries.
 
   """
-  # Add simple fields
   fields = [
+    (_MakeField("tags", "Tags", QFT_OTHER, "Tags"), IQ_CONFIG, 0,
+     lambda ctx, inst: list(inst.GetTags())),
+    ]
+
+  # Add simple fields
+  fields.extend([
     (_MakeField(name, title, kind, doc),
      NETQ_CONFIG, 0, _GetItemAttr(name))
-    for (name, (title, kind, flags, doc)) in _NETWORK_SIMPLE_FIELDS.items()]
+     for (name, (title, kind, flags, doc)) in _NETWORK_SIMPLE_FIELDS.items()
+    ])
 
   def _GetLength(getter):
     return lambda ctx, network: len(getter(ctx)[network.uuid])
diff --git a/lib/rapi/client.py b/lib/rapi/client.py
index 7983c09..a1d729f 100644
--- a/lib/rapi/client.py
+++ b/lib/rapi/client.py
@@ -1725,7 +1725,7 @@ class GanetiRapiClient(object): # pylint: disable=R0904
 
   def CreateNetwork(self, network_name, network, gateway=None, network6=None,
                     gateway6=None, mac_prefix=None, network_type=None,
-                    add_reserved_ips=None, dry_run=False):
+                    add_reserved_ips=None, tags=None, dry_run=False):
     """Creates a new network.
 
     @type name: str
@@ -1740,6 +1740,12 @@ class GanetiRapiClient(object): # pylint: disable=R0904
     query = []
     _AppendDryRunIf(query, dry_run)
 
+    if add_reserved_ips:
+      add_reserved_ips = add_reserved_ips.split(',')
+
+    if tags:
+      tags = tags.split(',')
+
     body = {
       "network_name": network_name,
       "gateway": gateway,
@@ -1748,7 +1754,8 @@ class GanetiRapiClient(object): # pylint: disable=R0904
       "network6": network6,
       "mac_prefix": mac_prefix,
       "network_type": network_type,
-      "add_reserved_ips": add_reserved_ips
+      "add_reserved_ips": add_reserved_ips,
+      "tags": tags,
       }
 
     return self._SendRequest(HTTP_POST, "/%s/networks" % GANETI_RAPI_VERSION,
diff --git a/lib/rapi/rlib2.py b/lib/rapi/rlib2.py
index 584833a..57fe631 100644
--- a/lib/rapi/rlib2.py
+++ b/lib/rapi/rlib2.py
@@ -97,7 +97,7 @@ NET_FIELDS = ["name", "network", "gateway",
               "mac_prefix", "network_type",
               "free_count", "reserved_count",
               "map", "group_list", "inst_list",
-              "external_reservations",
+              "external_reservations", "tags",
              ] 
 
 G_FIELDS = [
