Blob Blame History Raw
From 24f48d185b03b3bcff28d78a3be657f2a1295e4b Mon Sep 17 00:00:00 2001
From: Tomas Jelinek <tojeline@redhat.com>
Date: Fri, 6 Jan 2017 15:26:11 +0100
Subject: [PATCH] 'cluster cib-push' allows to obtain and push a diff

---
 pcs/cluster.py | 43 +++++++++++++++++++++++++++++++++++++------
 pcs/pcs.8      | 10 ++++++++--
 pcs/usage.py   | 14 +++++++++++---
 3 files changed, 56 insertions(+), 11 deletions(-)

diff --git a/pcs/cluster.py b/pcs/cluster.py
index 4572643..69e2852 100644
--- a/pcs/cluster.py
+++ b/pcs/cluster.py
@@ -1192,6 +1192,8 @@ def cluster_push(argv):
     filename = None
     scope = None
     timeout = None
+    diff_against = None
+
     if "--wait" in utils.pcs_options:
         timeout = utils.validate_wait_get_timeout()
     for arg in argv:
@@ -1204,6 +1206,8 @@ def cluster_push(argv):
                     utils.err("invalid CIB scope '%s'" % arg_value)
                 else:
                     scope = arg_value
+            if arg_name == "diff-against":
+                diff_against = arg_value
             else:
                 usage.cluster(["cib-push"])
                 sys.exit(1)
@@ -1212,6 +1216,8 @@ def cluster_push(argv):
     if not filename:
         usage.cluster(["cib-push"])
         sys.exit(1)
+    if diff_against and scope:
+        utils.err("Cannot use both scope and diff-against")
 
     try:
         new_cib_dom = xml.dom.minidom.parse(filename)
@@ -1223,13 +1229,38 @@ def cluster_push(argv):
     except (EnvironmentError, xml.parsers.expat.ExpatError) as e:
         utils.err("unable to parse new cib: %s" % e)
 
-    command = ["cibadmin", "--replace", "--xml-file", filename]
-    if scope:
-        command.append("--scope=%s" % scope)
-    output, retval = utils.run(command)
-    if retval != 0:
-        utils.err("unable to push cib\n" + output)
+    if diff_against:
+        try:
+            xml.dom.minidom.parse(diff_against)
+        except (EnvironmentError, xml.parsers.expat.ExpatError) as e:
+            utils.err("unable to parse original cib: %s" % e)
+        runner = utils.cmd_runner()
+        command = [
+            "crm_diff", "--original", diff_against, "--new", filename,
+            "--no-version"
+        ]
+        patch, error, dummy_retval = runner.run(command)
+        # dummy_retval == -1 means one of two things:
+        # a) an error has occured
+        # b) --original and --new differ
+        if error.strip():
+            utils.err("unable to diff the CIBs:\n" + error)
+
+        command = ["cibadmin", "--patch", "--xml-pipe"]
+        output, error, retval = runner.run(command, patch)
+        if retval != 0:
+            utils.err("unable to push cib\n" + error + output)
+
+    else:
+        command = ["cibadmin", "--replace", "--xml-file", filename]
+        if scope:
+            command.append("--scope=%s" % scope)
+        output, retval = utils.run(command)
+        if retval != 0:
+            utils.err("unable to push cib\n" + output)
+
     print("CIB updated")
+
     if "--wait" not in utils.pcs_options:
         return
     cmd = ["crm_resource", "--wait"]
diff --git a/pcs/pcs.8 b/pcs/pcs.8
index dffaddd..bf4b859 100644
--- a/pcs/pcs.8
+++ b/pcs/pcs.8
@@ -262,8 +262,14 @@ Sync corosync configuration to all nodes found from current corosync.conf file (
 cib [filename] [scope=<scope> | \fB\-\-config\fR]
 Get the raw xml from the CIB (Cluster Information Base).  If a filename is provided, we save the CIB to that file, otherwise the CIB is printed.  Specify scope to get a specific section of the CIB.  Valid values of the scope are: configuration, nodes, resources, constraints, crm_config, rsc_defaults, op_defaults, status.  \fB\-\-config\fR is the same as scope=configuration.  Do not specify a scope if you want to edit the saved CIB using pcs (pcs -f <command>).
 .TP
-cib-push <filename> [scope=<scope> | \fB\-\-config\fR] [\fB\-\-wait\fR[=<n>]]
-Push the raw xml from <filename> to the CIB (Cluster Information Base).  You can obtain the CIB by running the 'pcs cluster cib' command, which is recommended first step when you want to perform desired modifications (pcs \fB\-f\fR <command>) for the one-off push.  Specify scope to push a specific section of the CIB.  Valid values of the scope are: configuration, nodes, resources, constraints, crm_config, rsc_defaults, op_defaults.  \fB\-\-config\fR is the same as scope=configuration.  Use of \fB\-\-config\fR is recommended.  Do not specify a scope if you need to push the whole CIB or be warned in the case of outdated CIB.  If --wait is specified wait up to 'n' seconds for changes to be applied.  WARNING: the selected scope of the CIB will be overwritten by the current content of the specified file.
+cib-push <filename> [diff\-against=<filename_original> scope=<scope> | \fB\-\-config\fR] [\fB\-\-wait\fR[=<n>]]
+Push the raw xml from <filename> to the CIB (Cluster Information Base).  You can obtain the CIB by running the 'pcs cluster cib' command, which is recommended first step when you want to perform desired modifications (pcs \fB\-f\fR <command>) for the one\-off push.  If diff\-against is specified, pcs diffs contents of filename against contents of filename_original and pushes the result to the CIB.  Specify scope to push a specific section of the CIB.  Valid values of the scope are: configuration, nodes, resources, constraints, crm_config, rsc_defaults, op_defaults.  \fB\-\-config\fR is the same as scope=configuration.  Use of \fB\-\-config\fR is recommended.  Do not specify a scope if you need to push the whole CIB or be warned in the case of outdated CIB.  If --wait is specified wait up to 'n' seconds for changes to be applied.  WARNING: the selected scope of the CIB will be overwritten by the current content of the specified file.
+
+Example:
+    pcs cluster cib > original.xml
+    cp original.xml new.xml
+    pcs -f new.xml constraint location apache prefers node2
+    pcs cluster cib-push new.xml diff-against=original.xml
 .TP
 cib\-upgrade
 Upgrade the CIB to conform to the latest version of the document schema.
diff --git a/pcs/usage.py b/pcs/usage.py
index 0ebebe0..b33da35 100644
--- a/pcs/usage.py
+++ b/pcs/usage.py
@@ -656,20 +656,28 @@ Commands:
         scope=configuration.  Do not specify a scope if you want to edit
         the saved CIB using pcs (pcs -f <command>).
 
-    cib-push <filename> [scope=<scope> | --config] [--wait[=<n>]]
+    cib-push <filename> [--wait[=<n>]]
+            [diff-against=<filename_orignal> | scope=<scope> | --config]
         Push the raw xml from <filename> to the CIB (Cluster Information Base).
         You can obtain the CIB by running the 'pcs cluster cib' command, which
         is recommended first step when you want to perform desired
         modifications (pcs -f <command>) for the one-off push.
+        If diff-against is specified, pcs diffs contents of filename against
+        contents of filename_original and pushes the result to the CIB.
         Specify scope to push a specific section of the CIB.  Valid values
         of the scope are: configuration, nodes, resources, constraints,
         crm_config, rsc_defaults, op_defaults.  --config is the same as
         scope=configuration.  Use of --config is recommended.  Do not specify
         a scope if you need to push the whole CIB or be warned in the case
-        of outdated CIB. If --wait is specified wait up to 'n' seconds for
-        changes to be applied.
+        of outdated CIB.
+        If --wait is specified wait up to 'n' seconds for changes to be applied.
         WARNING: the selected scope of the CIB will be overwritten by the
         current content of the specified file.
+        Example:
+            pcs cluster cib > original.xml
+            cp original.xml new.xml
+            pcs -f new.xml constraint location apache prefers node2
+            pcs cluster cib-push new.xml diff-against=original.xml
 
     cib-upgrade
         Upgrade the CIB to conform to the latest version of the document schema.
-- 
1.8.3.1