Hello community,
here is the log from the commit of package crmsh for openSUSE:Factory checked in at 2019-11-11 13:01:06
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/crmsh (Old)
and /work/SRC/openSUSE:Factory/.crmsh.new.2990 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "crmsh"
Mon Nov 11 13:01:06 2019 rev:166 rq:747268 version:4.1.0+git.1573020742.a0b88227
Changes:
--------
--- /work/SRC/openSUSE:Factory/crmsh/crmsh.changes 2019-11-06 13:56:49.444203017 +0100
+++ /work/SRC/openSUSE:Factory/.crmsh.new.2990/crmsh.changes 2019-11-11 13:01:07.965737490 +0100
@@ -1,0 +2,7 @@
+Wed Nov 06 06:17:48 UTC 2019 - XLiang@suse.com
+
+- Update to version 4.1.0+git.1573020742.a0b88227:
+ * Test: unittest: test Parallax class
+ * Dev: parallax: create class Parallax to simplify using parallax
+
+-------------------------------------------------------------------
Old:
----
crmsh-4.1.0+git.1572504697.472361c5.tar.bz2
New:
----
crmsh-4.1.0+git.1573020742.a0b88227.tar.bz2
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ crmsh.spec ++++++
--- /var/tmp/diff_new_pack.IAcYTa/_old 2019-11-11 13:01:09.213738812 +0100
+++ /var/tmp/diff_new_pack.IAcYTa/_new 2019-11-11 13:01:09.229738829 +0100
@@ -36,7 +36,7 @@
Summary: High Availability cluster command-line interface
License: GPL-2.0-or-later
Group: %{pkg_group}
-Version: 4.1.0+git.1572504697.472361c5
+Version: 4.1.0+git.1573020742.a0b88227
Release: 0
Url: http://crmsh.github.io
Source0: %{name}-%{version}.tar.bz2
++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.IAcYTa/_old 2019-11-11 13:01:09.309738913 +0100
+++ /var/tmp/diff_new_pack.IAcYTa/_new 2019-11-11 13:01:09.309738913 +0100
@@ -1,4 +1,4 @@
<servicedata>
<service name="tar_scm">
<param name="url">git://github.com/ClusterLabs/crmsh.git</param>
- <param name="changesrevision">c8d41bd637dd03b4d60a9f35ca099e41ac32eec4</param></service></servicedata>
\ No newline at end of file
+ <param name="changesrevision">b8bb14dbbc9d6a0b1e79d696e64246bec0a98357</param></service></servicedata>
\ No newline at end of file
++++++ crmsh-4.1.0+git.1572504697.472361c5.tar.bz2 -> crmsh-4.1.0+git.1573020742.a0b88227.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.1.0+git.1572504697.472361c5/crmsh/parallax.py new/crmsh-4.1.0+git.1573020742.a0b88227/crmsh/parallax.py
--- old/crmsh-4.1.0+git.1572504697.472361c5/crmsh/parallax.py 1970-01-01 01:00:00.000000000 +0100
+++ new/crmsh-4.1.0+git.1573020742.a0b88227/crmsh/parallax.py 2019-11-06 07:12:22.000000000 +0100
@@ -0,0 +1,104 @@
+# Copyright (C) 2019 Xin Liang
+# See COPYING for license information.
+
+
+import os
+import parallax
+
+
+class Parallax(object):
+ """
+ # Parallax SSH API
+ # call: Executes the given command on a set of hosts, collecting the output
+ # copy: Copies files from the local machine to a set of remote hosts
+ # slurp: Copies files from a set of remote hosts to local folders
+ """
+ def __init__(self, nodes, cmd=None, localdir=None, filename=None,
+ src=None, dst=None, askpass=False, ssh_options=None):
+ self.nodes = nodes
+ self.askpass = askpass
+ self.ssh_options = ssh_options
+
+ # used for call
+ self.cmd = cmd
+ # used for slurp
+ self.localdir = localdir
+ self.filename = filename
+ # used for copy
+ self.src = src
+ self.dst = dst
+
+ self.opts = self.prepare()
+
+ def prepare(self):
+ opts = parallax.Options()
+ if self.ssh_options is None:
+ self.ssh_options = ['StrictHostKeyChecking=no', 'ConnectTimeout=10']
+ opts.ssh_options = self.ssh_options
+ opts.askpass = self.askpass
+ # warn_message will available from parallax-1.0.5
+ if hasattr(opts, 'warn_message'):
+ opts.warn_message = False
+ opts.localdir = self.localdir
+ return opts
+
+ def handle(self, results):
+ for host, result in results:
+ if isinstance(result, parallax.Error):
+ raise ValueError("Failed on {}: {}".format(host, result))
+ return results
+
+ def call(self):
+ results = parallax.call(self.nodes, self.cmd, self.opts)
+ return self.handle(list(results.items()))
+
+ def slurp(self):
+ dst = os.path.basename(self.filename)
+ results = parallax.slurp(self.nodes, self.filename, dst, self.opts)
+ return self.handle(list(results.items()))
+
+ def copy(self):
+ results = parallax.copy(self.nodes, self.src, self.dst, self.opts)
+ return self.handle(list(results.items()))
+
+
+def parallax_call(nodes, cmd, askpass=False, ssh_options=None):
+ """
+ Executes the given command on a set of hosts, collecting the output
+ nodes: a set of hosts
+ cmd: command
+ askpass: Ask for a password if passwordless not configured
+ ssh_options: Extra options to pass to SSH
+ Returns [(host, (rc, stdout, stdin)), ...] or ValueError exception
+ """
+ p = Parallax(nodes, cmd=cmd, askpass=askpass, ssh_options=ssh_options)
+ return p.call()
+
+
+def parallax_slurp(nodes, localdir, filename, askpass=False, ssh_options=None):
+ """
+ Copies from the remote node to the local node
+ nodes: a set of hosts
+ localdir: localpath
+ filename: remote filename want to slurp
+ askpass: Ask for a password if passwordless not configured
+ ssh_options: Extra options to pass to SSH
+ Returns [(host, (rc, stdout, stdin, localpath)), ...] or ValueError exception
+ """
+ p = Parallax(nodes, localdir=localdir, filename=filename,
+ askpass=askpass, ssh_options=ssh_options)
+ return p.slurp()
+
+
+def parallax_copy(nodes, src, dst, askpass=False, ssh_options=None):
+ """
+ Copies from the local node to a set of remote hosts
+ nodes: a set of hosts
+ src: local path
+ dst: remote path
+ askpass: Ask for a password if passwordless not configured
+ ssh_options: Extra options to pass to SSH
+ Returns [(host, (rc, stdout, stdin)), ...] or ValueError exception
+ """
+ p = Parallax(nodes, src=src, dst=dst, askpass=askpass, ssh_options=ssh_options)
+ return p.copy()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.1.0+git.1572504697.472361c5/data-manifest new/crmsh-4.1.0+git.1573020742.a0b88227/data-manifest
--- old/crmsh-4.1.0+git.1572504697.472361c5/data-manifest 2019-10-31 07:51:37.000000000 +0100
+++ new/crmsh-4.1.0+git.1573020742.a0b88227/data-manifest 2019-11-06 07:12:22.000000000 +0100
@@ -155,6 +155,7 @@
test/unittests/scripts/vipinc/main.yml
test/unittests/scripts/vip/main.yml
test/unittests/scripts/workflows/10-webserver.xml
+test/unittests/test_bootstrap.py
test/unittests/test_bugs.py
test/unittests/test_cib.py
test/unittests/test_cliformat.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-4.1.0+git.1572504697.472361c5/test/unittests/test_parallax.py new/crmsh-4.1.0+git.1573020742.a0b88227/test/unittests/test_parallax.py
--- old/crmsh-4.1.0+git.1572504697.472361c5/test/unittests/test_parallax.py 1970-01-01 01:00:00.000000000 +0100
+++ new/crmsh-4.1.0+git.1573020742.a0b88227/test/unittests/test_parallax.py 2019-11-06 07:12:22.000000000 +0100
@@ -0,0 +1,127 @@
+from __future__ import unicode_literals
+# Copyright (C) 2019 Xin Liang
+# See COPYING for license information.
+#
+# unit tests for parallax.py
+
+
+import os
+import unittest
+from unittest import mock
+import parallax
+from crmsh import parallax as cparallax
+
+
+class TestParallax(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ """
+ Global setUp.
+ """
+
+ def setUp(self):
+ """
+ Test setUp.
+ """
+ # Use the setup to create a fresh instance for each test
+ self.parallax_call_instance = cparallax.Parallax(["node1"], cmd="ls")
+ self.parallax_slurp_instance = cparallax.Parallax(["node1"], localdir="/opt", filename="/opt/file.c")
+ self.parallax_copy_instance = cparallax.Parallax(["node1", "node2"], src="/opt/file.c", dst="/tmp")
+
+ def tearDown(self):
+ """
+ Test tearDown.
+ """
+
+ @classmethod
+ def tearDownClass(cls):
+ """
+ Global tearDown.
+ """
+
+ @mock.patch("parallax.call")
+ @mock.patch("crmsh.parallax.Parallax.handle")
+ def test_call(self, mock_handle, mock_call):
+ mock_call.return_value = {"node1": (0, None, None)}
+ mock_handle.return_value = [("node1", (0, None, None))]
+
+ result = self.parallax_call_instance.call()
+ self.assertEqual(result, mock_handle.return_value)
+
+ mock_call.assert_called_once_with(["node1"], "ls", self.parallax_call_instance.opts)
+ mock_handle.assert_called_once_with(list(mock_call.return_value.items()))
+
+ @mock.patch("parallax.Error")
+ @mock.patch("parallax.call")
+ @mock.patch("crmsh.parallax.Parallax.handle")
+ def test_call_exception(self, mock_handle, mock_call, mock_error):
+ mock_error = mock.Mock()
+ mock_call.return_value = {"node1": mock_error}
+ mock_handle.side_effect = ValueError("error happen")
+
+ with self.assertRaises(ValueError) as err:
+ self.parallax_call_instance.call()
+ self.assertEqual("error happen", str(err.exception))
+
+ mock_call.assert_called_once_with(["node1"], "ls", self.parallax_call_instance.opts)
+ mock_handle.assert_called_once_with(list(mock_call.return_value.items()))
+
+ @mock.patch("crmsh.parallax.Parallax.handle")
+ @mock.patch("parallax.slurp")
+ @mock.patch("os.path.basename")
+ def test_slurp(self, mock_basename, mock_slurp, mock_handle):
+ mock_basename.return_value = "file.c"
+ mock_slurp.return_value = {"node1": (0, None, None, "/opt")}
+ mock_handle.return_value = [("node1", (0, None, None, "/opt"))]
+
+ result = self.parallax_slurp_instance.slurp()
+ self.assertEqual(result, mock_handle.return_value)
+
+ mock_basename.assert_called_once_with("/opt/file.c")
+ mock_slurp.assert_called_once_with(["node1"], "/opt/file.c", "file.c", self.parallax_slurp_instance.opts)
+ mock_handle.assert_called_once_with(list(mock_slurp.return_value.items()))
+
+ @mock.patch("parallax.Error")
+ @mock.patch("crmsh.parallax.Parallax.handle")
+ @mock.patch("parallax.slurp")
+ @mock.patch("os.path.basename")
+ def test_slurp_exception(self, mock_basename, mock_slurp, mock_handle, mock_error):
+ mock_basename.return_value = "file.c"
+ mock_error = mock.Mock()
+ mock_slurp.return_value = {"node1": mock_error}
+ mock_handle.side_effect = ValueError("error happen")
+
+ with self.assertRaises(ValueError) as err:
+ self.parallax_slurp_instance.slurp()
+ self.assertEqual("error happen", str(err.exception))
+
+ mock_basename.assert_called_once_with("/opt/file.c")
+ mock_slurp.assert_called_once_with(["node1"], "/opt/file.c", "file.c", self.parallax_slurp_instance.opts)
+ mock_handle.assert_called_once_with(list(mock_slurp.return_value.items()))
+
+ @mock.patch("parallax.copy")
+ @mock.patch("crmsh.parallax.Parallax.handle")
+ def test_copy(self, mock_handle, mock_copy):
+ mock_copy.return_value = {"node1": (0, None, None), "node2": (0, None, None)}
+ mock_handle.return_value = [("node1", (0, None, None)), ("node2", (0, None, None))]
+
+ result = self.parallax_copy_instance.copy()
+ self.assertEqual(result, mock_handle.return_value)
+
+ mock_copy.assert_called_once_with(["node1", "node2"], "/opt/file.c", "/tmp", self.parallax_copy_instance.opts)
+ mock_handle.assert_called_once_with(list(mock_copy.return_value.items()))
+
+ @mock.patch("parallax.Error")
+ @mock.patch("parallax.copy")
+ @mock.patch("crmsh.parallax.Parallax.handle")
+ def test_copy_exception(self, mock_handle, mock_copy, mock_error):
+ mock_error = mock.Mock()
+ mock_copy.return_value = {"node1": mock_error, "node2": (0, None, None)}
+ mock_handle.side_effect = ValueError("error happen")
+
+ with self.assertRaises(ValueError) as err:
+ self.parallax_copy_instance.copy()
+ self.assertEqual("error happen", str(err.exception))
+
+ mock_copy.assert_called_once_with(["node1", "node2"], "/opt/file.c", "/tmp", self.parallax_copy_instance.opts)
+ mock_handle.assert_called_once_with(list(mock_copy.return_value.items()))