diff --git a/lib/cli.py b/lib/cli.py
index d225bafed2ab9adce84ccd2f985a9bd9b50f6c47..0a27edd3543b35e9f99a2553bfc7bcdd9ddfa260 100644
--- a/lib/cli.py
+++ b/lib/cli.py
@@ -41,7 +41,7 @@ __all__ = ["DEBUG_OPT", "NOHDR_OPT", "SEP_OPT", "GenericMain", "SubmitOpCode",
            "cli_option", "GenerateTable", "AskUser",
            "ARGS_NONE", "ARGS_FIXED", "ARGS_ATLEAST", "ARGS_ANY", "ARGS_ONE",
            "USEUNITS_OPT", "FIELDS_OPT", "FORCE_OPT",
-           "ListTags", "AddTags", "RemoveTags",
+           "ListTags", "AddTags", "RemoveTags", "TAG_SRC_OPT",
            ]
 
 
@@ -66,6 +66,35 @@ def _ExtractTagsObject(opts, args):
   return retval
 
 
+def _ExtendTags(opts, args):
+  """Extend the args if a source file has been given.
+
+  This function will extend the tags with the contents of the file
+  passed in the 'tags_source' attribute of the opts parameter. A file
+  named '-' will be replaced by stdin.
+
+  """
+  fname = opts.tags_source
+  if fname is None:
+    return
+  if fname == "-":
+    new_fh = sys.stdin
+  else:
+    new_fh = open(fname, "r")
+  new_data = []
+  try:
+    # we don't use the nice 'new_data = [line.strip() for line in fh]'
+    # because of python bug 1633941
+    while True:
+      line = new_fh.readline()
+      if not line:
+        break
+      new_data.append(line.strip())
+  finally:
+    new_fh.close()
+  args.extend(new_data)
+
+
 def ListTags(opts, args):
   """List the tags on a given object.
 
@@ -94,6 +123,7 @@ def AddTags(opts, args):
 
   """
   kind, name = _ExtractTagsObject(opts, args)
+  _ExtendTags(opts, args)
   if not args:
     raise errors.OpPrereqError("No tags to be added")
   op = opcodes.OpAddTags(kind=kind, name=name, tags=args)
@@ -110,6 +140,7 @@ def RemoveTags(opts, args):
 
   """
   kind, name = _ExtractTagsObject(opts, args)
+  _ExtendTags(opts, args)
   if not args:
     raise errors.OpPrereqError("No tags to be removed")
   op = opcodes.OpDelTags(kind=kind, name=name, tags=args)
@@ -143,6 +174,9 @@ FORCE_OPT = make_option("-f", "--force", dest="force", action="store_true",
 _LOCK_OPT = make_option("--lock-retries", default=None,
                         type="int", help=SUPPRESS_HELP)
 
+TAG_SRC_OPT = make_option("--from", dest="tags_source",
+                          default=None, help="File with tag names")
+
 def ARGS_FIXED(val):
   """Macro-like function denoting a fixed number of arguments"""
   return -val
diff --git a/man/gnt-cluster.sgml b/man/gnt-cluster.sgml
index ef9e58f1f83d4e21081b774888c86429ef851fbd..5781c0949672e82afc57370c35e88b1b06cf330a 100644
--- a/man/gnt-cluster.sgml
+++ b/man/gnt-cluster.sgml
@@ -60,6 +60,7 @@
 
       <cmdsynopsis>
         <command>add-tags</command>
+        <arg choice="opt">--from <replaceable>file</replaceable></arg>
         <arg choice="req"
         rep="repeat"><replaceable>tag</replaceable></arg>
       </cmdsynopsis>
@@ -68,6 +69,14 @@
         Add tags to the cluster. If any of the tags contains invalid
         characters, the entire operation will abort.
       </para>
+
+      <para>
+        If the <option>--from</option> option is given, the list of
+        tags will be extended with the contents of that file (each
+        line becomes a tag). In this case, there is not need to pass
+        tags on the command line (if you do, both sources will be
+        used). A file name of - will be interpreted as stdin.
+      </para>
     </refsect2>
 
     <refsect2>
@@ -250,6 +259,7 @@
 
       <cmdsynopsis>
         <command>remove-tags</command>
+        <arg choice="opt">--from <replaceable>file</replaceable></arg>
         <arg choice="req"
         rep="repeat"><replaceable>tag</replaceable></arg>
       </cmdsynopsis>
@@ -258,6 +268,14 @@
         Remove tags from the cluster. If any of the tags are not
         existing on the cluster, the entire operation will abort.
       </para>
+
+      <para>
+        If the <option>--from</option> option is given, the list of
+        tags will be extended with the contents of that file (each
+        line becomes a tag). In this case, there is not need to pass
+        tags on the command line (if you do, both sources will be
+        used). A file name of - will be interpreted as stdin.
+      </para>
     </refsect2>
 
     <refsect2>
diff --git a/man/gnt-instance.sgml b/man/gnt-instance.sgml
index 53885e70b0fdf3e44020375c89435bd23913f9ee..9ada7c76f9ae9e1669a65208c75d28dc68887321 100644
--- a/man/gnt-instance.sgml
+++ b/man/gnt-instance.sgml
@@ -758,6 +758,7 @@ node1.example.com:sdb:/dev/md1
 
         <cmdsynopsis>
           <command>add-tags</command>
+          <arg choice="opt">--from <replaceable>file</replaceable></arg>
           <arg choice="req"><replaceable>instancename</replaceable></arg>
           <arg choice="req"
             rep="repeat"><replaceable>tag</replaceable></arg>
@@ -767,6 +768,13 @@ node1.example.com:sdb:/dev/md1
           Add tags to the given instance. If any of the tags contains
           invalid characters, the entire operation will abort.
         </para>
+        <para>
+          If the <option>--from</option> option is given, the list of
+          tags will be extended with the contents of that file (each
+          line becomes a tag). In this case, there is not need to pass
+          tags on the command line (if you do, both sources will be
+          used). A file name of - will be interpreted as stdin.
+        </para>
       </refsect3>
 
       <refsect3>
@@ -784,6 +792,7 @@ node1.example.com:sdb:/dev/md1
         <title>REMOVE-TAGS</title>
         <cmdsynopsis>
           <command>remove-tags</command>
+          <arg choice="opt">--from <replaceable>file</replaceable></arg>
           <arg choice="req"><replaceable>instancename</replaceable></arg>
           <arg choice="req"
             rep="repeat"><replaceable>tag</replaceable></arg>
@@ -793,6 +802,14 @@ node1.example.com:sdb:/dev/md1
           Remove tags from the given instance. If any of the tags are
           not existing on the node, the entire operation will abort.
         </para>
+
+        <para>
+          If the <option>--from</option> option is given, the list of
+          tags will be extended with the contents of that file (each
+          line becomes a tag). In this case, there is not need to pass
+          tags on the command line (if you do, both sources will be
+          used). A file name of - will be interpreted as stdin.
+        </para>
       </refsect3>
 
     </refsect2>
diff --git a/man/gnt-node.sgml b/man/gnt-node.sgml
index 1b71e59a1acf77a727f9d227bbd66106a440eb68..dfa75f7778dfafb9dbed4ef9c976079a374267a3 100644
--- a/man/gnt-node.sgml
+++ b/man/gnt-node.sgml
@@ -105,6 +105,7 @@
 
       <cmdsynopsis>
         <command>add-tags</command>
+        <arg choice="opt">--from <replaceable>file</replaceable></arg>
         <arg choice="req"><replaceable>nodename</replaceable></arg>
         <arg choice="req"
         rep="repeat"><replaceable>tag</replaceable></arg>
@@ -114,6 +115,14 @@
         Add tags to the given node. If any of the tags contains
         invalid characters, the entire operation will abort.
       </para>
+
+      <para>
+        If the <option>--from</option> option is given, the list of
+        tags will be extended with the contents of that file (each
+        line becomes a tag). In this case, there is not need to pass
+        tags on the command line (if you do, both sources will be
+        used). A file name of - will be interpreted as stdin.
+      </para>
     </refsect2>
 
     <refsect2>
@@ -292,6 +301,7 @@
       <title>REMOVE-TAGS</title>
       <cmdsynopsis>
         <command>remove-tags</command>
+        <arg choice="opt">--from <replaceable>file</replaceable></arg>
         <arg choice="req"><replaceable>nodename</replaceable></arg>
         <arg choice="req"
         rep="repeat"><replaceable>tag</replaceable></arg>
@@ -301,6 +311,14 @@
         Remove tags from the given node. If any of the tags are not
         existing on the node, the entire operation will abort.
       </para>
+
+      <para>
+        If the <option>--from</option> option is given, the list of
+        tags will be extended with the contents of that file (each
+        line becomes a tag). In this case, there is not need to pass
+        tags on the command line (if you do, both sources will be
+        used). A file name of - will be interpreted as stdin.
+      </para>
     </refsect2>
 
     <refsect2>
diff --git a/scripts/gnt-cluster b/scripts/gnt-cluster
index 6361c3bcdd3f1cb9668b66a966ba28da6ff7cf3b..9be9904a84c8605241aa58655cd204b6d7bce685 100755
--- a/scripts/gnt-cluster
+++ b/scripts/gnt-cluster
@@ -265,9 +265,9 @@ commands = {
                  "", "Show cluster configuration"),
   'list-tags': (ListTags, ARGS_NONE,
                 [DEBUG_OPT], "", "List the tags of the cluster"),
-  'add-tags': (AddTags, ARGS_ANY, [DEBUG_OPT],
+  'add-tags': (AddTags, ARGS_ANY, [DEBUG_OPT, TAG_SRC_OPT],
                "tag...", "Add tags to the cluster"),
-  'remove-tags': (RemoveTags, ARGS_ANY, [DEBUG_OPT],
+  'remove-tags': (RemoveTags, ARGS_ANY, [DEBUG_OPT, TAG_SRC_OPT],
                   "tag...", "Remove tags from the cluster"),
   }
 
diff --git a/scripts/gnt-instance b/scripts/gnt-instance
index 488ed3e2b20e8ecfc894b872e0b5c2f85c9a6cc0..be74647e657ef474a8b97f108c1d0cb8331fcdd4 100755
--- a/scripts/gnt-instance
+++ b/scripts/gnt-instance
@@ -756,9 +756,9 @@ commands = {
                        "Deactivate an instance's disks"),
   'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
                 "<node_name>", "List the tags of the given instance"),
-  'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT],
+  'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
                "<node_name> tag...", "Add tags to the given instance"),
-  'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT],
+  'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
                   "<node_name> tag...", "Remove tags from given instance"),
   }
 
diff --git a/scripts/gnt-node b/scripts/gnt-node
index c87070b6a355b9750b847931c69d6fbe56f2d367..e32d456db665775e3272bd2a417bb706ea8c1d7a 100755
--- a/scripts/gnt-node
+++ b/scripts/gnt-node
@@ -183,9 +183,9 @@ commands = {
               "[<node_name>...]", "List logical volumes on node(s)"),
   'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
                 "<node_name>", "List the tags of the given node"),
-  'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT],
+  'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
                "<node_name> tag...", "Add tags to the given node"),
-  'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT],
+  'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
                   "<node_name> tag...", "Remove tags from the given node"),
   }