diff --git a/lib/cli.py b/lib/cli.py
index 9c83c419da851dabe72f6486eca66e105750a94d..2113308d146ce39600842314f0c48a231f1bfe2f 100644
--- a/lib/cli.py
+++ b/lib/cli.py
@@ -108,6 +108,7 @@ __all__ = [
   "IGNORE_REMOVE_FAILURES_OPT",
   "IGNORE_SECONDARIES_OPT",
   "IGNORE_SIZE_OPT",
+  "INCLUDEDEFAULTS_OPT",
   "INTERVAL_OPT",
   "MAC_PREFIX_OPT",
   "MAINTAIN_NODE_HEALTH_OPT",
@@ -237,6 +238,7 @@ __all__ = [
   "FormatQueryResult",
   "FormatParamsDictInfo",
   "FormatPolicyInfo",
+  "PrintIPolicyCommand",
   "PrintGenericInfo",
   "GenerateTable",
   "AskUser",
@@ -1621,6 +1623,10 @@ NOCONFLICTSCHECK_OPT = cli_option("--no-conflicts-check",
                                   action="store_false",
                                   help="Don't check for conflicting IPs")
 
+INCLUDEDEFAULTS_OPT = cli_option("--include-defaults", dest="include_defaults",
+                                 default=False, action="store_true",
+                                 help="Include default values")
+
 #: Options provided by all commands
 COMMON_OPTS = [DEBUG_OPT, REASON_OPT]
 
@@ -3751,6 +3757,41 @@ def FormatPolicyInfo(custom_ipolicy, eff_ipolicy, iscluster):
   return ret
 
 
+def _PrintSpecsParameters(buf, specs):
+  values = ("%s=%s" % (par, val) for (par, val) in sorted(specs.items()))
+  buf.write(",".join(values))
+
+
+def PrintIPolicyCommand(buf, ipolicy, isgroup):
+  """Print the command option used to generate the given instance policy.
+
+  Currently only the parts dealing with specs are supported.
+
+  @type buf: StringIO
+  @param buf: stream to write into
+  @type ipolicy: dict
+  @param ipolicy: instance policy
+  @type isgroup: bool
+  @param isgroup: whether the policy is at group level
+
+  """
+  if not isgroup:
+    stdspecs = ipolicy.get("std")
+    if stdspecs:
+      buf.write(" %s " % IPOLICY_STD_SPECS_STR)
+      _PrintSpecsParameters(buf, stdspecs)
+  minmax = ipolicy.get("minmax")
+  if minmax:
+    minspecs = minmax.get("min")
+    maxspecs = minmax.get("max")
+    if minspecs and maxspecs:
+      buf.write(" %s " % IPOLICY_BOUNDS_SPECS_STR)
+      buf.write("min:")
+      _PrintSpecsParameters(buf, minspecs)
+      buf.write("/max:")
+      _PrintSpecsParameters(buf, maxspecs)
+
+
 def ConfirmOperation(names, list_type, text, extra=""):
   """Ask the user to confirm an operation on a list of list_type.
 
diff --git a/lib/client/gnt_cluster.py b/lib/client/gnt_cluster.py
index e0e2d7fc23937d9c537a4ba3efb8cb211f5fe991..750d65510ea572decb0c2dd7d13fb4a9ab993c6f 100644
--- a/lib/client/gnt_cluster.py
+++ b/lib/client/gnt_cluster.py
@@ -26,6 +26,7 @@
 # W0614: Unused import %s from wildcard import (since we need cli)
 # C0103: Invalid name gnt-cluster
 
+from cStringIO import StringIO
 import os.path
 import time
 import OpenSSL
@@ -1491,6 +1492,26 @@ def Epo(opts, args, cl=None, _on_fn=_EpoOn, _off_fn=_EpoOff,
     return _off_fn(opts, node_list, inst_map)
 
 
+def _GetCreateCommand(info):
+  buf = StringIO()
+  buf.write("gnt-cluster init")
+  PrintIPolicyCommand(buf, info["ipolicy"], False)
+  buf.write(" ")
+  buf.write(info["name"])
+  return buf.getvalue()
+
+
+def ShowCreateCommand(opts, args):
+  """Shows the command that can be used to re-create the cluster.
+
+  Currently it works only for ipolicy specs.
+
+  """
+  cl = GetClient(query=True)
+  result = cl.QueryClusterInfo()
+  ToStdout(_GetCreateCommand(result))
+
+
 commands = {
   "init": (
     InitCluster, [ArgHost(min=1, max=1)],
@@ -1603,6 +1624,9 @@ commands = {
   "deactivate-master-ip": (
     DeactivateMasterIp, ARGS_NONE, [CONFIRM_OPT], "",
     "Deactivates the master IP"),
+  "show-ispecs-cmd": (
+    ShowCreateCommand, ARGS_NONE, [], "",
+    "Show the command line to re-create the cluster"),
   }
 
 
diff --git a/lib/client/gnt_group.py b/lib/client/gnt_group.py
index 2f8dcc9dc294ea98cc202d537ad3d31e297834ee..32e0e3a1493ddd13fe4b5cffadb9cda15c8c900a 100644
--- a/lib/client/gnt_group.py
+++ b/lib/client/gnt_group.py
@@ -24,6 +24,8 @@
 # W0401: Wildcard import ganeti.cli
 # W0614: Unused import %s from wildcard import (since we need cli)
 
+from cStringIO import StringIO
+
 from ganeti.cli import *
 from ganeti import constants
 from ganeti import opcodes
@@ -313,6 +315,35 @@ def GroupInfo(_, args):
     ])
 
 
+def _GetCreateCommand(group):
+  (name, ipolicy) = group
+  buf = StringIO()
+  buf.write("gnt-group add")
+  PrintIPolicyCommand(buf, ipolicy, True)
+  buf.write(" ")
+  buf.write(name)
+  return buf.getvalue()
+
+
+def ShowCreateCommand(opts, args):
+  """Shows the command that can be used to re-create a node group.
+
+  Currently it works only for ipolicy specs.
+
+  """
+  cl = GetClient(query=True)
+  selected_fields = ["name"]
+  if opts.include_defaults:
+    selected_fields += ["ipolicy"]
+  else:
+    selected_fields += ["custom_ipolicy"]
+  result = cl.QueryGroups(names=args, fields=selected_fields,
+                          use_locking=False)
+
+  for group in result:
+    ToStdout(_GetCreateCommand(group))
+
+
 commands = {
   "add": (
     AddGroup, ARGS_ONE_GROUP,
@@ -366,6 +397,10 @@ commands = {
   "info": (
     GroupInfo, ARGS_MANY_GROUPS, [], "[<group_name>...]",
     "Show group information"),
+  "show-ispecs-cmd": (
+    ShowCreateCommand, ARGS_MANY_GROUPS, [INCLUDEDEFAULTS_OPT],
+    "[--include-defaults] [<group_name>...]",
+    "Show the command line to re-create a group"),
   }
 
 
diff --git a/man/gnt-cluster.rst b/man/gnt-cluster.rst
index c42c5fc5e72c73b93824dcbab9b1592b03cb51e4..1e837ae25e406f433676652f2e8470fde3f91f1c 100644
--- a/man/gnt-cluster.rst
+++ b/man/gnt-cluster.rst
@@ -152,6 +152,14 @@ Passing the ``--roman`` option gnt-cluster info will try to print
 its integer fields in a latin friendly way. This allows further
 diffusion of Ganeti among ancient cultures.
 
+SHOW-ISPECS-CMD
+~~~~~~~~~~~~~~~
+
+**show-ispecs-cmd**
+
+Shows the command line that can be used to recreate the cluster with the
+same options relative to specs in the instance policies.
+
 INIT
 ~~~~
 
diff --git a/man/gnt-group.rst b/man/gnt-group.rst
index a633dc98c1f1a8ca0212030eba9a540b52762f96..6786e698d1eb4d9ff94270e17576a52f9da06dfa 100644
--- a/man/gnt-group.rst
+++ b/man/gnt-group.rst
@@ -257,10 +257,23 @@ be interpreted as stdin.
 INFO
 ~~~~
 
-**info** [group...]
+**info** [*group*...]
 
 Shows config information for all (or given) groups.
 
+SHOW-ISPECS-CMD
+~~~~~~~~~~~~~~~
+
+**show-ispecs-cmd** [\--include-defaults] [*group*...]
+
+Shows the command line that can be used to recreate the given groups (or
+all groups, if none is given) with the same options relative to specs in
+the instance policies.
+
+If ``--include-defaults`` is specified, include also the default values
+(i.e. the cluster-level settings), and not only the configuration items
+that a group overrides.
+
 
 .. vim: set textwidth=72 :
 .. Local Variables:
diff --git a/test/py/ganeti.cli_unittest.py b/test/py/ganeti.cli_unittest.py
index 96f84d60d61a1263358f89ef9d497d33952b6f8b..a90dd4abaeb3458c9b88f899bee3f6f181b3be92 100755
--- a/test/py/ganeti.cli_unittest.py
+++ b/test/py/ganeti.cli_unittest.py
@@ -1423,5 +1423,59 @@ class TestCreateIPolicyFromOpts(unittest.TestCase):
       self._TestFullISpecsInner(skel_ipolicy, exp_minmax1, None,
                                 False, fill_all)
 
+
+class TestPrintIPolicyCommand(unittest.TestCase):
+  """Test case for cli.PrintIPolicyCommand"""
+  _SPECS1 = {
+    "par1": 42,
+    "par2": "xyz",
+    }
+  _SPECS1_STR = "par1=42,par2=xyz"
+  _SPECS2 = {
+    "param": 10,
+    "another_param": 101,
+    }
+  _SPECS2_STR = "another_param=101,param=10"
+
+  def _CheckPrintIPolicyCommand(self, ipolicy, isgroup, expected):
+    buf = StringIO()
+    cli.PrintIPolicyCommand(buf, ipolicy, isgroup)
+    self.assertEqual(buf.getvalue(), expected)
+
+  def testIgnoreStdForGroup(self):
+    self._CheckPrintIPolicyCommand({"std": self._SPECS1}, True, "")
+
+  def testIgnoreEmpty(self):
+    policies = [
+      {},
+      {"std": {}},
+      {"minmax": {}},
+      {"minmax": {
+        "min": {},
+        "max": {},
+        }},
+      {"minmax": {
+        "min": self._SPECS1,
+        "max": {},
+        }},
+      ]
+    for pol in policies:
+      self._CheckPrintIPolicyCommand(pol, False, "")
+
+  def testFullPolicies(self):
+    cases = [
+      ({"std": self._SPECS1},
+       " %s %s" % (cli.IPOLICY_STD_SPECS_STR, self._SPECS1_STR)),
+      ({"minmax": {
+        "min": self._SPECS1,
+        "max": self._SPECS2,
+        }},
+       " %s min:%s/max:%s" % (cli.IPOLICY_BOUNDS_SPECS_STR,
+                              self._SPECS1_STR, self._SPECS2_STR)),
+      ]
+    for (pol, exp) in cases:
+      self._CheckPrintIPolicyCommand(pol, False, exp)
+
+
 if __name__ == "__main__":
   testutils.GanetiTestProgram()