diff --git a/lib/cli.py b/lib/cli.py
index f46a0a05726fbde8d09f434d55d9126359e2f149..3ba016c8163601b7db3fc87d05c1469babdf7188 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 161059b4b180f931a424352fab6b8adf5a6d6369..207873ce5623fa47c161113dbbbb3c84b52cddd3 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 eececc5129ce14efa37707f24e124cd9d9951fe9..5536261ca2e7af027fd6e4b734503d25b95b608f 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 fb657e91f630219575bbb4472ee96b07ab6b9eb7..d2825dbd3adbe5431157e0c6c060960d9c5dca18 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 df7ac7ea8816bb73996050343ee27def0dc70e57..13378cda5ab53803ff83280134c8e0d74c45c05e 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 21f2db326e7c46c5c9c35e83aa419abc7831e89c..9436935c7116285c8a22628605fd229d0a84e848 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 b7ff5008b4af95dea6e4a6ba00b85896647af7fd..224e5b4a1dd77465227c9abcbaa26c15f9cd5799 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 7983c09c7bdb1e5c47713558b92b9bdd5365a916..a1d729fcd20a7e3a342b5e273e4dc9cc3857197c 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 584833ac07b75fb047ced2f372b1cff5c0c513bf..57fe6313cea3e0def71387f3ae1ad85ecdc2060d 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 = [