Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package crmsh for openSUSE:Factory checked 
in at 2021-01-20 18:27:22
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/crmsh (Old)
 and      /work/SRC/openSUSE:Factory/.crmsh.new.28504 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "crmsh"

Wed Jan 20 18:27:22 2021 rev:200 rq:864474 version:4.2.0+git.1610961380.fc379403

Changes:
--------
--- /work/SRC/openSUSE:Factory/crmsh/crmsh.changes      2021-01-13 
18:31:05.601881218 +0100
+++ /work/SRC/openSUSE:Factory/.crmsh.new.28504/crmsh.changes   2021-01-20 
18:27:52.103542149 +0100
@@ -1,0 +2,13 @@
+Mon Jan 18 09:27:22 UTC 2021 - xli...@suse.com
+
+- Update to version 4.2.0+git.1610961380.fc379403:
+  * Dev: unittest: unit test for bootstrap.Watchdog class
+  * Dev: behave: functional test for bootstrap.Watchdog class
+  * Fix: bootstrap: Use class Watchdog to simplify watchdog 
config(bsc#1154927, bsc#1178869)
+  * Dev: Polish the sbd feature.
+  * Dev: Replace -f with -c and run check when no parameter provide.
+  * Fix: Fix the yes option not working
+  * Fix: Remove useless import and show help when no input.
+  * Dev: Correct SBD device id inconsistenc during ASR
+
+-------------------------------------------------------------------

Old:
----
  crmsh-4.2.0+git.1610410636.fef21a41.tar.bz2

New:
----
  crmsh-4.2.0+git.1610961380.fc379403.tar.bz2

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ crmsh.spec ++++++
--- /var/tmp/diff_new_pack.ulYbu9/_old  2021-01-20 18:27:53.031543000 +0100
+++ /var/tmp/diff_new_pack.ulYbu9/_new  2021-01-20 18:27:53.035543004 +0100
@@ -36,7 +36,7 @@
 Summary:        High Availability cluster command-line interface
 License:        GPL-2.0-or-later
 Group:          %{pkg_group}
-Version:        4.2.0+git.1610410636.fef21a41
+Version:        4.2.0+git.1610961380.fc379403
 Release:        0
 Url:            http://crmsh.github.io
 Source0:        %{name}-%{version}.tar.bz2

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.ulYbu9/_old  2021-01-20 18:27:53.075543040 +0100
+++ /var/tmp/diff_new_pack.ulYbu9/_new  2021-01-20 18:27:53.075543040 +0100
@@ -9,6 +9,6 @@
 </service>
 <service name="tar_scm">
   <param name="url">https://github.com/ClusterLabs/crmsh.git</param>
-  <param 
name="changesrevision">fef21a418a6443c040814c782618a95afcfbfe85</param>
+  <param 
name="changesrevision">fc379403bff248fe7acb1dc5373551ae57a93ef4</param>
 </service>
 </servicedata>
\ No newline at end of file

++++++ crmsh-4.2.0+git.1610410636.fef21a41.tar.bz2 -> 
crmsh-4.2.0+git.1610961380.fc379403.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.2.0+git.1610410636.fef21a41/crmsh/bootstrap.py 
new/crmsh-4.2.0+git.1610961380.fc379403/crmsh/bootstrap.py
--- old/crmsh-4.2.0+git.1610410636.fef21a41/crmsh/bootstrap.py  2021-01-12 
01:17:16.000000000 +0100
+++ new/crmsh-4.2.0+git.1610961380.fc379403/crmsh/bootstrap.py  2021-01-18 
10:16:20.000000000 +0100
@@ -59,6 +59,7 @@
         '''
         Initialize attributes
         '''
+        self.type = None # init or join
         self.quiet = None
         self.yes_to_all = None
         self.template = None
@@ -144,11 +145,185 @@
                 error("Duplicated input")
         if self.no_overwrite_sshkey:
             warn("--no-overwrite-sshkey option is deprecated since crmsh does 
not overwrite ssh keys by default anymore and will be removed in future 
versions")
+        if self.type == "join" and self.watchdog:
+            warn("-w option is deprecated and will be removed in future 
versions")
 
     def init_sbd_manager(self):
         self.sbd_manager = SBDManager(self.sbd_devices, self.diskless_sbd)
 
 
+class Watchdog(object):
+    """
+    Class to find valid watchdog device name
+    """
+    QUERY_CMD = "sbd query-watchdog"
+    DEVICE_FIND_REGREX = "\[[0-9]+\] (/dev/.*)\n.*\nDriver: (.*)"
+
+    def __init__(self, _input=None, peer_host=None):
+        """
+        Init function
+        """
+        self._input = _input
+        self._peer_host = peer_host
+        self._watchdog_info_dict = {}
+        self._watchdog_device_name = None
+    
+    @property
+    def watchdog_device_name(self):
+        return self._watchdog_device_name
+
+    @staticmethod
+    def _verify_watchdog_device(dev, ignore_error=False):
+        """
+        Use wdctl to verify watchdog device
+        """
+        rc, _, err = utils.get_stdout_stderr("wdctl {}".format(dev))
+        if rc != 0:
+            if ignore_error:
+                return False
+            else:
+                error("Invalid watchdog device {}: {}".format(dev, err))
+        return True
+
+    @staticmethod
+    def _load_watchdog_driver(driver):
+        """
+        Load specific watchdog driver
+        """
+        invoke("echo {} > /etc/modules-load.d/watchdog.conf".format(driver))
+        invoke("systemctl restart systemd-modules-load")
+
+    @staticmethod
+    def _get_watchdog_device_from_sbd_config():
+        """
+        Try to get watchdog device name from sbd config file
+        """
+        conf = utils.parse_sysconfig(SYSCONFIG_SBD)
+        return conf.get("SBD_WATCHDOG_DEV")
+
+    @staticmethod
+    def _driver_is_loaded(driver):
+        """
+        Check if driver was already loaded
+        """
+        _, out, _ = utils.get_stdout_stderr("lsmod")
+        return re.search("\n{}\s+".format(driver), out)
+
+    def _set_watchdog_info(self):
+        """
+        Set watchdog info through sbd query-watchdog command
+        Content in self._watchdog_info_dict: {device_name: driver_name}
+        """
+        rc, out, err = utils.get_stdout_stderr(self.QUERY_CMD)
+        if rc == 0 and out:
+            # output format might like:
+            #   [1] /dev/watchdog\nIdentity: Software Watchdog\nDriver: 
softdog\n
+            self._watchdog_info_dict = 
dict(re.findall(self.DEVICE_FIND_REGREX, out))
+        else:
+            error("Failed to run {}: {}".format(self.QUERY_CMD, err))
+
+    def _get_device_through_driver(self, driver_name):
+        """
+        Get watchdog device name which has driver_name
+        """
+        for device, driver in self._watchdog_info_dict.items():
+            if driver == driver_name and self._verify_watchdog_device(device):
+                return device
+        return None
+
+    def _get_driver_through_device_remotely(self, dev_name):
+        """
+        Given watchdog device name, get driver name on remote node
+        """
+        cmd = "ssh -o StrictHostKeyChecking=no root@{} 
{}".format(self._peer_host, self.QUERY_CMD)
+        rc, out, err = utils.get_stdout_stderr(cmd)
+        if rc == 0 and out:
+            # output format might like:
+            #   [1] /dev/watchdog\nIdentity: Software Watchdog\nDriver: 
softdog\n
+            device_driver_dict = dict(re.findall(self.DEVICE_FIND_REGREX, out))
+            if device_driver_dict and dev_name in device_driver_dict:
+                return device_driver_dict[dev_name]
+            else:
+                return None
+        else:
+            error("Failed to run {} remotely: {}".format(self.QUERY_CMD, err))
+
+    def _get_first_unused_device(self):
+        """
+        Get first unused watchdog device name
+        """
+        for dev in self._watchdog_info_dict:
+            if self._verify_watchdog_device(dev, ignore_error=True):
+                return dev
+        return None
+
+    def _set_input(self):
+        """
+        If self._input was not provided by option:
+          1. Try to get it from sbd config file
+          2. Try to get the first valid device from result of sbd 
query-watchdog
+          3. Set the self._input as softdog
+        """
+        if not self._input:
+            dev = self._get_watchdog_device_from_sbd_config()
+            if dev and self._verify_watchdog_device(dev, ignore_error=True):
+                self._input = dev
+                return
+            first_unused = self._get_first_unused_device()
+            self._input = first_unused if first_unused else "softdog"
+
+    def _valid_device(self, dev):
+        """
+        Is an unused watchdog device
+        """
+        if dev in self._watchdog_info_dict and 
self._verify_watchdog_device(dev):
+            return True
+        return False
+
+    def join_watchdog(self):
+        """
+        In join proces, get watchdog device from config
+        If that device not exist, get driver name from init node, and load 
that driver
+        """
+        self._set_watchdog_info()
+
+        res = self._get_watchdog_device_from_sbd_config()
+        if not res:
+            error("Failed to get watchdog device from 
{}".format(SYSCONFIG_SBD))
+        self._input = res
+
+        if not self._valid_device(self._input):
+            driver = self._get_driver_through_device_remotely(self._input)
+            self._load_watchdog_driver(driver)
+
+    def init_watchdog(self):
+        """
+        In init process, find valid watchdog device
+        """
+        self._set_watchdog_info()
+        self._set_input()
+
+        # self._input is a device name
+        if self._valid_device(self._input):
+            self._watchdog_device_name = self._input
+            return
+
+        # self._input is invalid, exit
+        if not invokerc("modinfo {}".format(self._input)):
+            error("Should provide valid watchdog device or driver name by -w 
option")
+
+        # self._input is a driver name, load it if it was unloaded
+        if not self._driver_is_loaded(self._input):
+            self._load_watchdog_driver(self._input)
+            self._set_watchdog_info()
+
+        # self._input is a loaded driver name, find corresponding device name
+        res = self._get_device_through_driver(self._input)
+        if res:
+            self._watchdog_device_name = res
+            return
+
+
 class SBDManager(object):
     """
     Class to manage sbd configuration and services
@@ -175,16 +350,7 @@
         self.sbd_devices_input = sbd_devices
         self.diskless_sbd = diskless_sbd
         self._sbd_devices = None
-
-    @staticmethod
-    def _check_environment():
-        """
-        Check prerequisites for SBD
-        """
-        if not check_watchdog():
-            error("Watchdog device must be configured in order to use SBD")
-        if not utils.is_program("sbd"):
-            error("sbd executable not found! Cannot configure SBD")
+        self._watchdog_inst = None
 
     def _parse_sbd_device(self):
         """
@@ -255,8 +421,6 @@
             warn("Not configuring SBD - STONITH will be disabled.")
             return
 
-        self._check_environment()
-
         configured_dev_list = self._get_sbd_device_from_config()
         if configured_dev_list and not confirm("SBD is already configured to 
use {} - overwrite?".format(';'.join(configured_dev_list))):
             return configured_dev_list
@@ -294,10 +458,7 @@
         if self.sbd_devices_input:
             dev_list = self._parse_sbd_device()
             self._verify_sbd_device(dev_list)
-            self._check_environment()
-        elif self.diskless_sbd:
-            self._check_environment()
-        else:
+        elif not self.diskless_sbd:
             dev_list = self._get_sbd_device_interactive()
         self._sbd_devices = dev_list
 
@@ -321,7 +482,7 @@
                 "SBD_PACEMAKER": "yes",
                 "SBD_STARTMODE": "always",
                 "SBD_DELAY_START": "no",
-                "SBD_WATCHDOG_DEV": detect_watchdog_device()
+                "SBD_WATCHDOG_DEV": self._watchdog_inst.watchdog_device_name
                 }
         if self._sbd_devices:
             sbd_config_dict["SBD_DEVICE"] = ';'.join(self._sbd_devices)
@@ -349,6 +510,8 @@
         """
         if not utils.package_is_installed("sbd"):
             return
+        self._watchdog_inst = Watchdog(_input=_context.watchdog)
+        self._watchdog_inst.init_watchdog()
         self._get_sbd_device()
         if not self._sbd_devices and not self.diskless_sbd:
             invoke("systemctl disable sbd.service")
@@ -386,7 +549,8 @@
         if not os.path.exists(SYSCONFIG_SBD) or not 
utils.service_is_enabled("sbd.service", peer_host):
             invoke("systemctl disable sbd.service")
             return
-        self._check_environment()
+        self._watchdog_inst = Watchdog(peer_host=peer_host)
+        self._watchdog_inst.join_watchdog()
         dev_list = self._get_sbd_device_from_config()
         if dev_list:
             self._verify_sbd_device(dev_list, [peer_host])
@@ -738,11 +902,6 @@
         warn("{} is not configured to start at system 
boot.".format(timekeeper))
         warned = True
 
-    if stage in ("", "join", "sbd"):
-        if not check_watchdog():
-            warn("No watchdog device found. If SBD is used, the cluster will 
be unable to start without a watchdog.")
-            warned = True
-
     if warned:
         if not confirm("Do you want to continue anyway?"):
             return False
@@ -751,19 +910,6 @@
     return True
 
 
-def init_watchdog():
-    """
-    If a watchdog device was provided on the command line, configure it
-    """
-    if _context.watchdog:
-        _, outp = utils.get_stdout("lsmod")
-        if not any(re.match(r"^{}\b", d) is not None for d in 
outp.splitlines()):
-            invoke("modprobe {}".format(_context.watchdog))
-            with open("/etc/modules-load.d/watchdog.conf", "w") as f:
-                f.write("{}\n".format(_context.watchdog))
-            invoke("systemctl restart systemd-modules-load")
-
-
 def log_start():
     """
     Convenient side-effect: this will die immediately if the log file
@@ -1524,30 +1670,6 @@
     status("Created %s for OCFS2 partition" % (_context.ocfs2_device))
 
 
-def detect_watchdog_device():
-    """
-    Find the watchdog device. Fall back to /dev/watchdog.
-    """
-    wdconf = "/etc/modules-load.d/watchdog.conf"
-    watchdog_dev = "/dev/watchdog"
-    if os.path.exists(wdconf):
-        txt = open(wdconf, "r").read()
-        for line in txt.splitlines():
-            m = re.match(r'^\s*watchdog-device\s*=\s*(.*)$', line)
-            if m:
-                watchdog_dev = m.group(1)
-    return watchdog_dev
-
-
-def check_watchdog():
-    """
-    Verify watchdog device. Fall back to /dev/watchdog.
-    """
-    watchdog_dev = detect_watchdog_device()
-    rc, _out, _err = utils.get_stdout_stderr("wdctl %s" % (watchdog_dev))
-    return rc == 0
-
-
 def init_sbd():
     """
     Configure SBD (Storage-based fencing).
@@ -2328,8 +2450,6 @@
     if stage != "":
         globals()["init_" + stage]()
     else:
-        if _context.watchdog is not None:
-            init_watchdog()
         init_ssh()
         init_csync2()
         init_corosync()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.2.0+git.1610410636.fef21a41/crmsh/ui_cluster.py 
new/crmsh-4.2.0+git.1610961380.fc379403/crmsh/ui_cluster.py
--- old/crmsh-4.2.0+git.1610410636.fef21a41/crmsh/ui_cluster.py 2021-01-12 
01:17:16.000000000 +0100
+++ new/crmsh-4.2.0+git.1610961380.fc379403/crmsh/ui_cluster.py 2021-01-18 
10:16:20.000000000 +0100
@@ -222,7 +222,7 @@
         parser.add_argument("-S", "--enable-sbd", dest="diskless_sbd", 
action="store_true",
                             help="Enable SBD even if no SBD device is 
configured (diskless mode)")
         parser.add_argument("-w", "--watchdog", dest="watchdog", 
metavar="WATCHDOG",
-                            help="Use the given watchdog device")
+                            help="Use the given watchdog device or driver 
name")
         parser.add_argument("--no-overwrite-sshkey", action="store_true", 
dest="no_overwrite_sshkey",
                             help='Avoid "/root/.ssh/id_rsa" overwrite if "-y" 
option is used (False by default)')
 
@@ -289,6 +289,7 @@
         boot_context.ui_context = context
         boot_context.stage = stage
         boot_context.args = args
+        boot_context.type = "init"
 
         bootstrap.bootstrap_init(boot_context)
 
@@ -344,6 +345,7 @@
         join_context = bootstrap.Context.set_context(options)
         join_context.ui_context = context
         join_context.stage = stage
+        join_context.type = "join"
 
         bootstrap.bootstrap_join(join_context)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.2.0+git.1610410636.fef21a41/preflight_check/check.py 
new/crmsh-4.2.0+git.1610961380.fc379403/preflight_check/check.py
--- old/crmsh-4.2.0+git.1610410636.fef21a41/preflight_check/check.py    
2021-01-12 01:17:16.000000000 +0100
+++ new/crmsh-4.2.0+git.1610961380.fc379403/preflight_check/check.py    
2021-01-18 10:16:20.000000000 +0100
@@ -1,17 +1,95 @@
 import re
-from . import utils
-from . import task
+import os
+import sys
+
 from crmsh import utils as crmshutils
 from crmsh import bootstrap as crmshboot
 from crmsh import completers
 
+from . import utils
+from . import task
+from . import config
+
+
+def fix(context):
+    """
+    Check configuration and fix the abnormal options
+    """
+    if context.check_conf:
+        candidate = check_sbd()
+        if candidate != "":
+            correct_sbd(context, candidate)
+    print()
+
+
+def check_sbd():
+    """
+    Check the sbd device and find a possible fix for incorrect disk
+
+    Only support one path SBD_DEVICE in the current version
+    """
+    print("\n============ Checking the SBD device ============")
+    task_inst = task.TaskCheck("Checking SBD device")
+
+    with task_inst.run():
+
+        if not os.path.exists(config.SBD_CONF):
+            utils.msg_info("SBD configuration file {} not found.".
+                           format(config.SBD_CONF))
+            return ""
+
+        sbd_options = crmshutils.parse_sysconfig(config.SBD_CONF)
+
+        if not "SBD_DEVICE" in sbd_options:
+            utils.msg_info("SBD DEVICE not used.")
+            return ""
+
+        dev = sbd_options["SBD_DEVICE"]
+
+        if not os.path.exists(dev):
+            task_inst.warn("SBD device '{}' is not exist.".format(dev))
+        else:
+            if utils.is_valid_sbd(dev):
+                task_inst.info("'{}' is a valid SBD device.".format(dev))
+                return ""
+            else:
+                task_inst.warn("Device '{}' is not valid for SBD, may need 
initialize."
+                               .format(dev))
+
+        candidate = utils.find_candidate_sbd(dev)
+
+        if candidate == "":
+            task_inst.warn("Fail to find a valid candidate SBD device.")
+            return ""
+
+        task_inst.info("Found '{}' with SBD header exist.".format(candidate))
+
+    return candidate
+
+
+def correct_sbd(context, can):
+    """
+    Fix the sbd device conf with candidate device
+
+    Only support one path SBD_DEVICE in the current version
+    """
+
+    task_inst = task.TaskFixSBD(can, context.yes)
+    try:
+        task_inst.pre_check()
+        task_inst.print_header()
+        with task_inst.backup():
+            task_inst.run()
+        task_inst.verify()
+    except task.TaskError as err:
+        task_inst.error(str(err))
+        sys.exit(1)
+
 
 def check(context):
     """
     Check environment and cluster state if related options are enabled
     """
-    if context.env_check:
-        check_environment()
     if context.cluster_check:
         check_cluster()
     print()
@@ -105,7 +183,7 @@
                     task_inst.warn("{}.service is not active".format(item))
                 break
         else:
-           task_inst.warn("Failed to detect firewall")
+            task_inst.warn("Failed to detect firewall")
 
 
 def check_cluster():
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.2.0+git.1610410636.fef21a41/preflight_check/config.py 
new/crmsh-4.2.0+git.1610961380.fc379403/preflight_check/config.py
--- old/crmsh-4.2.0+git.1610410636.fef21a41/preflight_check/config.py   
2021-01-12 01:17:16.000000000 +0100
+++ new/crmsh-4.2.0+git.1610961380.fc379403/preflight_check/config.py   
2021-01-18 10:16:20.000000000 +0100
@@ -5,3 +5,5 @@
 REMOVE_PORT = "firewall-cmd --zone=public --remove-port={port}/udp"
 ADD_PORT = "firewall-cmd --zone=public --add-port={port}/udp"
 FENCE_HISTORY = "stonith_admin -h {node}"
+SBD_CONF = "/etc/sysconfig/sbd"
+SBD_CHECK_CMD = "sbd -d {dev} dump"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.2.0+git.1610410636.fef21a41/preflight_check/main.py 
new/crmsh-4.2.0+git.1610961380.fc379403/preflight_check/main.py
--- old/crmsh-4.2.0+git.1610410636.fef21a41/preflight_check/main.py     
2021-01-12 01:17:16.000000000 +0100
+++ new/crmsh-4.2.0+git.1610961380.fc379403/preflight_check/main.py     
2021-01-18 10:16:20.000000000 +0100
@@ -6,10 +6,8 @@
 from argparse import RawTextHelpFormatter
 
 from . import check
-from . import config
 from . import utils
 from . import task
-from crmsh import utils as crmshutils
 
 
 logger = logging.getLogger('cpc')
@@ -31,8 +29,8 @@
         self.logfile = None
         self.current_case = None
 
-        # set by argparse
-        self.env_check = None
+        # set by argparse(functions)
+        self.check_conf = None
         self.cluster_check = None
         self.sbd = None
         self.corosync = None
@@ -40,6 +38,8 @@
         self.fence_node = None
         self.sp_iptables = None
         self.loop = None
+
+        # set by argument(additional options)
         self.yes = None
         self.help = None
 
@@ -123,7 +123,7 @@
     if not context.sp_iptables:
         return
 
-    task_inst = task.TaskSplitBrain()
+    task_inst = task.TaskSplitBrain(context.yes)
     try:
         task_inst.pre_check()
         task_inst.print_header()
@@ -142,7 +142,7 @@
     if not context.fence_node:
         return
 
-    task_inst = task.TaskFence(context.fence_node)
+    task_inst = task.TaskFence(context)
     try:
         task_inst.pre_check()
         task_inst.print_header()
@@ -154,8 +154,8 @@
 
 
 class MyArgParseFormatter(RawTextHelpFormatter):
-    def __init__(self,prog):
-        super(MyArgParseFormatter,self).__init__(prog, max_help_position=50)
+    def __init__(self, prog):
+        super(MyArgParseFormatter, self).__init__(prog, max_help_position=50)
 
 
 def parse_argument(context):
@@ -173,10 +173,8 @@
                                                            context.jsonfile,
                                                            
context.report_path))
 
-    parser.add_argument('-e', '--env-check', dest='env_check', 
action='store_true',
-                        help='Check environment')
-    parser.add_argument('-c', '--cluster-check', dest='cluster_check', 
action='store_true',
-                        help='Check cluster state')
+    parser.add_argument('-c', '--check-conf', dest='check_conf', 
action='store_true',
+                        help='Valid the configurations')
 
     group_mutual = parser.add_mutually_exclusive_group()
     group_mutual.add_argument('--kill-sbd', dest='sbd', action='store_true',
@@ -202,9 +200,13 @@
     if args.help:
         parser.print_help()
         sys.exit(0)
+
     for arg in vars(args):
         setattr(context, arg, getattr(args, arg))
 
+    if len(sys.argv) == 1:
+        setattr(context, 'cluster_check', True)
+
 
 def setup_logging(context):
     """
@@ -239,6 +241,7 @@
     setup_logging(context)
 
     try:
+        check.fix(context)
         check.check(context)
         kill_process(context)
         fence_node(context)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.2.0+git.1610410636.fef21a41/preflight_check/task.py 
new/crmsh-4.2.0+git.1610961380.fc379403/preflight_check/task.py
--- old/crmsh-4.2.0+git.1610410636.fef21a41/preflight_check/task.py     
2021-01-12 01:17:16.000000000 +0100
+++ new/crmsh-4.2.0+git.1610961380.fc379403/preflight_check/task.py     
2021-01-18 10:16:20.000000000 +0100
@@ -4,11 +4,13 @@
 import time
 import threading
 import logging
+import shutil
+import tempfile
 from contextlib import contextmanager
+from crmsh import utils as crmshutils
 
 from . import utils
 from . import config
-from crmsh import utils as crmshutils
 
 
 logger = logging.getLogger('cpc')
@@ -29,6 +31,7 @@
         flush, to print the message immediately
         """
         self.passed = True
+        self.yes = False
         self.quiet = quiet
         self.messages = []
         self.timestamp = utils.now()
@@ -86,7 +89,7 @@
         Print testcase header
         """
         print(self.header())
-        if not crmshutils.ask("Run?"):
+        if not self.yes and not crmshutils.ask("Run?"):
             self.info("Testcase cancelled")
             sys.exit()
 
@@ -157,13 +160,14 @@
     """
     Class to fence node
     """
-    def  __init__(self, target):
+    def  __init__(self, context):
         """
         Init function
         """
-        self.target_node = target
+        self.target_node = context.fence_node
         description = "Fence node {}".format(self.target_node)
         super(self.__class__, self).__init__(description, flush=True)
+        self.yes = context.yes
 
     def header(self):
         """
@@ -309,6 +313,7 @@
         super(self.__class__, self).__init__(self.description, flush=True)
         self.cmd = "killall -9 {}".format(self.target_kill)
         self.looping = context.loop
+        self.yes = context.yes
         if not self.looping:
             self.expected = self.EXPECTED[self.target_kill][0]
         else:
@@ -432,7 +437,7 @@
     Class to define how to simulate split brain by blocking corosync ports
     """
 
-    def  __init__(self):
+    def  __init__(self, yes=False):
         """
         Init function
         """
@@ -441,6 +446,7 @@
         self.ports = []
         self.peer_nodelist = []
         super(self.__class__, self).__init__(self.description, flush=True)
+        self.yes = yes
 
     def header(self):
         """
@@ -558,3 +564,97 @@
         if not result:
             self.thread_stop_event.set()
             # should be an error here
+
+
+class TaskFixSBD(Task):
+    """
+    Class to fix SBD DEVICE incorrect issue
+    """
+
+    def  __init__(self, candidate, yes=False):
+        self.new = candidate
+        self.description = "Replace SBD_DEVICE with candidate 
{}".format(self.new)
+        self.conf = config.SBD_CONF
+        super(self.__class__, self).__init__(self.description, flush=True)
+        self.bak = tempfile.mktemp()
+        self.edit = tempfile.mktemp()
+        self.yes = yes
+
+        sbd_options = crmshutils.parse_sysconfig(self.conf)
+        self.old = sbd_options["SBD_DEVICE"]
+
+    def header(self):
+        """
+        Case header
+        """
+        h = '''==============================================
+Case:                {}
+Original SBD device: {}
+New SBD device:      {}
+'''.format(self.description, self.old, self.new)
+        return h
+
+    def to_json(self): # pragma: no cover
+        """
+        Generate json output
+        """
+        self.build_base_result()
+        self.result['Original SBD device'] = self.old
+        self.result['New SBD device'] = self.new
+        from . import main
+        main.ctx.task_list = self.prev_task_list + [self.result]
+        utils.json_dumps()
+
+    def pre_check(self):
+        """
+        Check the prerequisite
+        """
+        if not os.path.exists(self.conf):
+            raise TaskError("Configure file {} not exist!".format(self.conf))
+
+        if not os.path.exists(self.new):
+            raise TaskError("Device {} not exist!".format(self.new))
+
+    @contextmanager
+    def backup(self):
+        """
+        Backup the configuration file before modify
+        """
+        shutil.copyfile(self.conf, self.bak)
+        try:
+            yield
+        finally:
+            if self.bak:
+                shutil.copyfile(self.bak, self.conf)
+
+    def run(self):
+        """
+        Change the SBD DEVICE of configuration file
+        """
+        with open(self.edit, "w") as editfd:
+            with open(self.conf, "r") as oldfd:
+                for line in oldfd.readlines():
+                    if line.strip().startswith("SBD_DEVICE"):
+                        line = "SBD_DEVICE='" + self.new +"'\n"
+                    editfd.write(line)
+
+        try:
+            shutil.copymode(self.conf, self.edit)
+            os.remove(self.conf)
+            shutil.move(self.edit, self.conf)
+            os.remove(self.bak)
+            self.bak = None
+        except:
+            raise TaskError("Fail to modify file {}".format(self.conf))
+
+    def verify(self):
+        """
+        Verify the modification is working
+        """
+        sbd_options = crmshutils.parse_sysconfig(self.conf)
+
+        if sbd_options["SBD_DEVICE"] == self.new:
+            self.info("SBD DEVICE change succeed")
+        else:
+            raise TaskError("Fail to replace SBD device {} in {}!".
+                            format(self.new, config.SBD_CONF))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.2.0+git.1610410636.fef21a41/preflight_check/utils.py 
new/crmsh-4.2.0+git.1610961380.fc379403/preflight_check/utils.py
--- old/crmsh-4.2.0+git.1610410636.fef21a41/preflight_check/utils.py    
2021-01-12 01:17:16.000000000 +0100
+++ new/crmsh-4.2.0+git.1610961380.fc379403/preflight_check/utils.py    
2021-01-18 10:16:20.000000000 +0100
@@ -1,5 +1,6 @@
 import os
 import re
+import glob
 import json
 import logging
 from datetime import datetime
@@ -232,3 +233,69 @@
             # a process may have died since we got the list of pids
             pass
     return False, -1
+
+
+def _find_match_count(str1, str2):
+    """
+    Find the max match number of s1 and s2
+    """
+    leng = min(len(str1), len(str2))
+    num = 0
+
+    for i in range(leng):
+        if str1[i] == str2[i]:
+            num += 1
+        else:
+            break
+
+    return num
+
+
+def is_valid_sbd(dev):
+    """
+    Check whether the device is a initialized SBD device
+
+    dev: dev path
+    return 'True' if 'dev' is a initialized SBD device
+    """
+    if not os.path.exists(dev):
+        return False
+
+    rc, out, err = 
crmshutils.get_stdout_stderr(config.SBD_CHECK_CMD.format(dev=dev))
+    if rc != 0 and err:
+        msg_error(err)
+        return False
+
+    return True
+
+
+def find_candidate_sbd(dev):
+    """
+    Find the devices that already have SBD header
+
+    return the path of candidate SBD device
+    """
+    ddir = os.path.dirname(dev)
+    dname = os.path.basename(dev)
+
+    dev_list = glob.glob(ddir + "/*")
+    if len(dev_list) == 0:
+        return ""
+
+    can_filter = filter(is_valid_sbd, dev_list)
+    candidates = list(can_filter)
+
+    if len(candidates) == 0:
+        return ""
+
+    index = 0
+    i = 0
+    max_match = -1
+    num_list = map(_find_match_count, [dev] * len(candidates), candidates)
+    for num in num_list:
+        if num > max_match:
+            max_match = num
+            index = i
+        i += 1
+
+    return candidates[index]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.2.0+git.1610410636.fef21a41/test/features/bootstrap_sbd.feature 
new/crmsh-4.2.0+git.1610961380.fc379403/test/features/bootstrap_sbd.feature
--- old/crmsh-4.2.0+git.1610410636.fef21a41/test/features/bootstrap_sbd.feature 
2021-01-12 01:17:16.000000000 +0100
+++ new/crmsh-4.2.0+git.1610961380.fc379403/test/features/bootstrap_sbd.feature 
2021-01-18 10:16:20.000000000 +0100
@@ -4,20 +4,6 @@
   Tag @clean means need to stop cluster service if the service is available
 
   @clean
-  Scenario: Check prerequisites for SBD
-    Given   Has disk "/dev/sda1" on "hanode1"
-
-    When    Run "mv /usr/sbin/sbd /tmp" on "hanode1"
-    And     Try "crm cluster init -s /dev/sda1 -y"
-    Then    Except "ERROR: cluster.init: sbd executable not found! Cannot 
configure SBD"
-    When    Run "mv /tmp/sbd /usr/sbin" on "hanode1"
-
-    When    Run "mv /dev/watchdog /tmp" on "hanode1"
-    And     Try "crm cluster init -s /dev/sda1 -y"
-    Then    Except "ERROR: cluster.init: Watchdog device must be configured in 
order to use SBD"
-    When    Run "mv /tmp/watchdog /dev" on "hanode1"
-
-  @clean
   Scenario: Verify sbd device
     When    Try "crm cluster init -s "/dev/sda1;/dev/sda2;/dev/sda3;/dev/sda4" 
-y"
     Then    Except "ERROR: cluster.init: Maximum number of SBD device is 3"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.2.0+git.1610410636.fef21a41/test/features/steps/const.py 
new/crmsh-4.2.0+git.1610961380.fc379403/test/features/steps/const.py
--- old/crmsh-4.2.0+git.1610410636.fef21a41/test/features/steps/const.py        
2021-01-12 01:17:16.000000000 +0100
+++ new/crmsh-4.2.0+git.1610961380.fc379403/test/features/steps/const.py        
2021-01-18 10:16:20.000000000 +0100
@@ -73,7 +73,7 @@
   -S, --enable-sbd      Enable SBD even if no SBD device is configured
                         (diskless mode)
   -w WATCHDOG, --watchdog WATCHDOG
-                        Use the given watchdog device
+                        Use the given watchdog device or driver name
   --no-overwrite-sshkey
                         Avoid "/root/.ssh/id_rsa" overwrite if "-y" option is
                         used (False by default)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.2.0+git.1610410636.fef21a41/test/unittests/test_bootstrap.py 
new/crmsh-4.2.0+git.1610961380.fc379403/test/unittests/test_bootstrap.py
--- old/crmsh-4.2.0+git.1610410636.fef21a41/test/unittests/test_bootstrap.py    
2021-01-12 01:17:16.000000000 +0100
+++ new/crmsh-4.2.0+git.1610961380.fc379403/test/unittests/test_bootstrap.py    
2021-01-18 10:16:20.000000000 +0100
@@ -22,6 +22,303 @@
 from crmsh import corosync
 
 
+class TestWatchdog(unittest.TestCase):
+    """
+    Unitary tests for crmsh.bootstrap.Watchdog
+    """
+
+    @classmethod
+    def setUpClass(cls):
+        """
+        Global setUp.
+        """
+
+    def setUp(self):
+        """
+        Test setUp.
+        """
+        self.watchdog_inst = bootstrap.Watchdog()
+        self.watchdog_join_inst = bootstrap.Watchdog(peer_host="node1")
+
+    def tearDown(self):
+        """
+        Test tearDown.
+        """
+
+    @classmethod
+    def tearDownClass(cls):
+        """
+        Global tearDown.
+        """
+
+    @mock.patch('crmsh.utils.get_stdout_stderr')
+    def test_verify_watchdog_device_ignore_error(self, mock_run):
+        mock_run.return_value = (1, None, "error")
+        res = self.watchdog_inst._verify_watchdog_device("/dev/watchdog", True)
+        self.assertEqual(res, False)
+        mock_run.assert_called_once_with("wdctl /dev/watchdog")
+
+    @mock.patch('crmsh.bootstrap.error')
+    @mock.patch('crmsh.utils.get_stdout_stderr')
+    def test_verify_watchdog_device_error(self, mock_run, mock_error):
+        mock_run.return_value = (1, None, "error")
+        mock_error.side_effect = ValueError
+        with self.assertRaises(ValueError) as err:
+            self.watchdog_inst._verify_watchdog_device("/dev/watchdog")
+        mock_error.assert_called_once_with("Invalid watchdog device 
/dev/watchdog: error")
+        mock_run.assert_called_once_with("wdctl /dev/watchdog")
+
+    @mock.patch('crmsh.utils.get_stdout_stderr')
+    def test_verify_watchdog_device(self, mock_run):
+        mock_run.return_value = (0, None, None)
+        res = self.watchdog_inst._verify_watchdog_device("/dev/watchdog")
+        self.assertEqual(res, True)
+
+    @mock.patch('crmsh.bootstrap.invoke')
+    def test_load_watchdog_driver(self, mock_invoke):
+        self.watchdog_inst._load_watchdog_driver("softdog")
+        mock_invoke.assert_has_calls([
+            mock.call("echo softdog > /etc/modules-load.d/watchdog.conf"),
+            mock.call("systemctl restart systemd-modules-load")
+            ])
+
+    @mock.patch('crmsh.utils.parse_sysconfig')
+    def test_get_watchdog_device_from_sbd_config(self, mock_parse):
+        mock_parse_inst = mock.Mock()
+        mock_parse.return_value = mock_parse_inst
+        mock_parse_inst.get.return_value = "/dev/watchdog"
+        res = self.watchdog_inst._get_watchdog_device_from_sbd_config()
+        self.assertEqual(res, "/dev/watchdog")
+        mock_parse.assert_called_once_with(bootstrap.SYSCONFIG_SBD)
+
+    @mock.patch('crmsh.utils.get_stdout_stderr')
+    def test_driver_is_loaded(self, mock_run):
+        output = """
+button                 24576  0
+softdog                16384  2
+btrfs                1474560  1
+        """
+        mock_run.return_value = (0, output, None)
+        res = self.watchdog_inst._driver_is_loaded("softdog")
+        assert res is not None
+        mock_run.assert_called_once_with("lsmod")
+
+    @mock.patch('crmsh.bootstrap.error')
+    @mock.patch('crmsh.utils.get_stdout_stderr')
+    def test_set_watchdog_info_error(self, mock_run, mock_error):
+        mock_run.return_value = (1, None, "error")
+        mock_error.side_effect = ValueError
+        with self.assertRaises(ValueError):
+            self.watchdog_inst._set_watchdog_info()
+        mock_run.assert_called_once_with(bootstrap.Watchdog.QUERY_CMD)
+        mock_error.assert_called_once_with("Failed to run {}: 
error".format(bootstrap.Watchdog.QUERY_CMD))
+
+    @mock.patch('crmsh.utils.get_stdout_stderr')
+    def test_set_watchdog_info(self, mock_run):
+        output = """
+Discovered 3 watchdog devices:
+
+[1] /dev/watchdog
+Identity: Software Watchdog
+Driver: softdog
+CAUTION: Not recommended for use with sbd.
+
+[2] /dev/watchdog0
+Identity: Software Watchdog
+Driver: softdog
+CAUTION: Not recommended for use with sbd.
+
+[3] /dev/watchdog1
+Identity: iTCO_wdt
+Driver: iTCO_wdt
+        """
+        mock_run.return_value = (0, output, None)
+        self.watchdog_inst._set_watchdog_info()
+        self.assertEqual(self.watchdog_inst._watchdog_info_dict, 
{'/dev/watchdog': 'softdog', '/dev/watchdog0': 'softdog', '/dev/watchdog1': 
'iTCO_wdt'})
+
+    @mock.patch('crmsh.bootstrap.Watchdog._verify_watchdog_device')
+    def test_get_device_through_driver_none(self, mock_verify):
+        self.watchdog_inst._watchdog_info_dict = {'/dev/watchdog': 'softdog', 
'/dev/watchdog0': 'softdog', '/dev/watchdog1': 'iTCO_wdt'}
+        mock_verify.return_value = False
+        res = self.watchdog_inst._get_device_through_driver("iTCO_wdt")
+        self.assertEqual(res, None)
+        mock_verify.assert_called_once_with("/dev/watchdog1")
+
+    @mock.patch('crmsh.bootstrap.Watchdog._verify_watchdog_device')
+    def test_get_device_through_driver(self, mock_verify):
+        self.watchdog_inst._watchdog_info_dict = {'/dev/watchdog': 'softdog', 
'/dev/watchdog0': 'softdog', '/dev/watchdog1': 'iTCO_wdt'}
+        mock_verify.return_value = True
+        res = self.watchdog_inst._get_device_through_driver("iTCO_wdt")
+        self.assertEqual(res, "/dev/watchdog1")
+        mock_verify.assert_called_once_with("/dev/watchdog1")
+
+    @mock.patch('crmsh.bootstrap.error')
+    @mock.patch('crmsh.utils.get_stdout_stderr')
+    def test_get_driver_through_device_remotely_error(self, mock_run, 
mock_error):
+        mock_run.return_value = (1, None, "error")
+        self.watchdog_join_inst._get_driver_through_device_remotely("test")
+        mock_run.assert_called_once_with("ssh -o StrictHostKeyChecking=no 
root@node1 sbd query-watchdog")
+        mock_error.assert_called_once_with("Failed to run sbd query-watchdog 
remotely: error")
+
+    @mock.patch('crmsh.utils.get_stdout_stderr')
+    def test_get_driver_through_device_remotely_none(self, mock_run):
+        mock_run.return_value = (0, "data", None)
+        res = 
self.watchdog_join_inst._get_driver_through_device_remotely("/dev/watchdog")
+        self.assertEqual(res, None)
+        mock_run.assert_called_once_with("ssh -o StrictHostKeyChecking=no 
root@node1 sbd query-watchdog")
+
+    @mock.patch('crmsh.utils.get_stdout_stderr')
+    def test_get_driver_through_device_remotely(self, mock_run):
+        output = """
+Discovered 3 watchdog devices:
+
+[1] /dev/watchdog
+Identity: Software Watchdog
+Driver: softdog
+CAUTION: Not recommended for use with sbd.
+
+[2] /dev/watchdog0
+Identity: Software Watchdog
+Driver: softdog
+CAUTION: Not recommended for use with sbd.
+
+[3] /dev/watchdog1
+Identity: iTCO_wdt
+Driver: iTCO_wdt
+        """
+        mock_run.return_value = (0, output, None)
+        res = 
self.watchdog_join_inst._get_driver_through_device_remotely("/dev/watchdog")
+        self.assertEqual(res, "softdog")
+        mock_run.assert_called_once_with("ssh -o StrictHostKeyChecking=no 
root@node1 sbd query-watchdog")
+
+    def test_get_first_unused_device_none(self):
+        res = self.watchdog_inst._get_first_unused_device()
+        self.assertEqual(res, None)
+
+    @mock.patch('crmsh.bootstrap.Watchdog._verify_watchdog_device')
+    def test_get_first_unused_device(self, mock_verify):
+        mock_verify.return_value = True
+        self.watchdog_inst._watchdog_info_dict = {'/dev/watchdog': 'softdog', 
'/dev/watchdog0': 'softdog', '/dev/watchdog1': 'iTCO_wdt'}
+        res = self.watchdog_inst._get_first_unused_device()
+        self.assertEqual(res, "/dev/watchdog")
+        mock_verify.assert_called_once_with("/dev/watchdog", ignore_error=True)
+
+    @mock.patch('crmsh.bootstrap.Watchdog._get_first_unused_device')
+    @mock.patch('crmsh.bootstrap.Watchdog._verify_watchdog_device')
+    
@mock.patch('crmsh.bootstrap.Watchdog._get_watchdog_device_from_sbd_config')
+    def test_set_input_from_config(self, mock_from_config, mock_verify, 
mock_first):
+        mock_from_config.return_value = "/dev/watchdog"
+        mock_verify.return_value = True
+        self.watchdog_inst._set_input()
+        mock_first.assert_not_called()
+        mock_from_config.assert_called_once_with()
+
+    @mock.patch('crmsh.bootstrap.Watchdog._get_first_unused_device')
+    @mock.patch('crmsh.bootstrap.Watchdog._verify_watchdog_device')
+    
@mock.patch('crmsh.bootstrap.Watchdog._get_watchdog_device_from_sbd_config')
+    def test_set_input(self, mock_from_config, mock_verify, mock_first):
+        mock_from_config.return_value = None
+        mock_first.return_value = None
+        self.watchdog_inst._set_input()
+        self.assertEqual(self.watchdog_inst._input, "softdog")
+        mock_from_config.assert_called_once_with()
+        mock_verify.assert_not_called()
+        mock_first.assert_called_once_with()
+
+    def test_valid_device_false(self):
+        res = self.watchdog_inst._valid_device("test")
+        self.assertEqual(res, False)
+
+    @mock.patch('crmsh.bootstrap.Watchdog._verify_watchdog_device')
+    def test_valid_device(self, mock_verify):
+        mock_verify.return_value = True
+        self.watchdog_inst._watchdog_info_dict = {'/dev/watchdog': 'softdog', 
'/dev/watchdog0': 'softdog', '/dev/watchdog1': 'iTCO_wdt'}
+        res = self.watchdog_inst._valid_device("/dev/watchdog")
+        self.assertEqual(res, True)
+
+    @mock.patch('crmsh.bootstrap.error')
+    
@mock.patch('crmsh.bootstrap.Watchdog._get_watchdog_device_from_sbd_config')
+    @mock.patch('crmsh.bootstrap.Watchdog._set_watchdog_info')
+    def test_join_watchdog_error(self, mock_set_info, mock_from_config, 
mock_error):
+        mock_from_config.return_value = None
+        mock_error.side_effect = SystemExit
+        with self.assertRaises(SystemExit):
+            self.watchdog_join_inst.join_watchdog()
+        mock_set_info.assert_called_once_with()
+        mock_from_config.assert_called_once_with()
+        mock_error.assert_called_once_with("Failed to get watchdog device from 
{}".format(bootstrap.SYSCONFIG_SBD))
+
+    @mock.patch('crmsh.bootstrap.Watchdog._load_watchdog_driver')
+    @mock.patch('crmsh.bootstrap.Watchdog._get_driver_through_device_remotely')
+    @mock.patch('crmsh.bootstrap.Watchdog._valid_device')
+    
@mock.patch('crmsh.bootstrap.Watchdog._get_watchdog_device_from_sbd_config')
+    @mock.patch('crmsh.bootstrap.Watchdog._set_watchdog_info')
+    def test_join_watchdog(self, mock_set_info, mock_from_config, mock_valid, 
mock_get_driver_remotely, mock_load):
+        mock_from_config.return_value = "/dev/watchdog"
+        mock_valid.return_value = False
+        mock_get_driver_remotely.return_value = "softdog"
+
+        self.watchdog_join_inst.join_watchdog()
+
+        mock_set_info.assert_called_once_with()
+        mock_from_config.assert_called_once_with()
+        mock_valid.assert_called_once_with("/dev/watchdog")
+        mock_get_driver_remotely.assert_called_once_with("/dev/watchdog")
+        mock_load.assert_called_once_with("softdog")
+
+    @mock.patch('crmsh.bootstrap.invokerc')
+    @mock.patch('crmsh.bootstrap.Watchdog._valid_device')
+    @mock.patch('crmsh.bootstrap.Watchdog._set_input')
+    @mock.patch('crmsh.bootstrap.Watchdog._set_watchdog_info')
+    def test_init_watchdog_valid(self, mock_set_info, mock_set_input, 
mock_valid, mock_invokerc):
+        mock_valid.return_value = True
+        self.watchdog_inst._input = "/dev/watchdog"
+        self.watchdog_inst.init_watchdog()
+        mock_invokerc.assert_not_called()
+        mock_valid.assert_called_once_with("/dev/watchdog")
+
+    @mock.patch('crmsh.bootstrap.error')
+    @mock.patch('crmsh.bootstrap.invokerc')
+    @mock.patch('crmsh.bootstrap.Watchdog._valid_device')
+    @mock.patch('crmsh.bootstrap.Watchdog._set_input')
+    @mock.patch('crmsh.bootstrap.Watchdog._set_watchdog_info')
+    def test_init_watchdog_error(self, mock_set_info, mock_set_input, 
mock_valid, mock_invokerc, mock_error):
+        mock_valid.return_value = False
+        mock_invokerc.return_value = False
+        self.watchdog_inst._input = "test"
+        mock_error.side_effect = SystemExit
+
+        with self.assertRaises(SystemExit):
+            self.watchdog_inst.init_watchdog()
+
+        mock_valid.assert_called_once_with("test")
+        mock_invokerc.assert_called_once_with("modinfo test")
+        mock_error.assert_called_once_with("Should provide valid watchdog 
device or driver name by -w option")
+
+    @mock.patch('crmsh.bootstrap.Watchdog._get_device_through_driver')
+    @mock.patch('crmsh.bootstrap.Watchdog._load_watchdog_driver')
+    @mock.patch('crmsh.bootstrap.Watchdog._driver_is_loaded')
+    @mock.patch('crmsh.bootstrap.invokerc')
+    @mock.patch('crmsh.bootstrap.Watchdog._valid_device')
+    @mock.patch('crmsh.bootstrap.Watchdog._set_input')
+    @mock.patch('crmsh.bootstrap.Watchdog._set_watchdog_info')
+    def test_init_watchdog(self, mock_set_info, mock_set_input, mock_valid, 
mock_invokerc, mock_is_loaded, mock_load, mock_get_device):
+        mock_valid.return_value = False
+        self.watchdog_inst._input = "softdog"
+        mock_invokerc.return_value = True
+        mock_is_loaded.return_value = False
+        mock_get_device.return_value = "/dev/watchdog"
+
+        self.watchdog_inst.init_watchdog()
+
+        mock_valid.assert_called_once_with("softdog")
+        mock_invokerc.assert_called_once_with("modinfo softdog")
+        mock_is_loaded.assert_called_once_with("softdog")
+        mock_load.assert_called_once_with("softdog")
+        mock_set_info.assert_has_calls([mock.call(), mock.call()])
+        mock_get_device.assert_called_once_with("softdog")
+
+
 class TestSBDManager(unittest.TestCase):
     """
     Unitary tests for crmsh.bootstrap.SBDManager
@@ -70,10 +367,9 @@
         mock_warn.assert_called_once_with("Not configuring SBD - STONITH will 
be disabled.")
 
     @mock.patch('crmsh.bootstrap.SBDManager._get_sbd_device_from_config')
-    @mock.patch('crmsh.bootstrap.SBDManager._check_environment')
     @mock.patch('crmsh.bootstrap.confirm')
     @mock.patch('crmsh.bootstrap.status')
-    def test__get_sbd_device_interactive_already_configured(self, mock_status, 
mock_confirm, mock_check, mock_from_config):
+    def test_get_sbd_device_interactive_already_configured(self, mock_status, 
mock_confirm, mock_from_config):
         bootstrap._context = mock.Mock(yes_to_all=False)
         mock_confirm.side_effect = [True, False]
         mock_from_config.return_value = ["/dev/sda1"]
@@ -87,15 +383,13 @@
             mock.call("SBD is already configured to use /dev/sda1 - 
overwrite?")
             ])
         
mock_status.assert_called_once_with(bootstrap.SBDManager.SBD_STATUS_DESCRIPTION)
-        mock_check.assert_called_once_with()
         mock_from_config.assert_called_once_with()
 
     @mock.patch('crmsh.bootstrap.prompt_for_string')
     @mock.patch('crmsh.bootstrap.SBDManager._get_sbd_device_from_config')
-    @mock.patch('crmsh.bootstrap.SBDManager._check_environment')
     @mock.patch('crmsh.bootstrap.confirm')
     @mock.patch('crmsh.bootstrap.status')
-    def test_get_sbd_device_interactive_diskless(self, mock_status, 
mock_confirm, mock_check, mock_from_config, mock_prompt):
+    def test_get_sbd_device_interactive_diskless(self, mock_status, 
mock_confirm, mock_from_config, mock_prompt):
         bootstrap._context = mock.Mock(yes_to_all=False)
         mock_confirm.return_value = True
         mock_from_config.return_value = None
@@ -104,16 +398,14 @@
         self.sbd_inst._get_sbd_device_interactive()
 
         
mock_status.assert_called_once_with(bootstrap.SBDManager.SBD_STATUS_DESCRIPTION)
-        mock_check.assert_called_once_with()
         mock_from_config.assert_called_once_with()
         mock_prompt.assert_called_once_with('Path to storage device (e.g. 
/dev/disk/by-id/...), or "none" for diskless sbd, use ";" as separator for 
multi path', 'none|\\/.*')
 
     @mock.patch('crmsh.bootstrap.prompt_for_string')
     @mock.patch('crmsh.bootstrap.SBDManager._get_sbd_device_from_config')
-    @mock.patch('crmsh.bootstrap.SBDManager._check_environment')
     @mock.patch('crmsh.bootstrap.confirm')
     @mock.patch('crmsh.bootstrap.status')
-    def test_get_sbd_device_interactive_null_and_diskless(self, mock_status, 
mock_confirm, mock_check, mock_from_config, mock_prompt):
+    def test_get_sbd_device_interactive_null_and_diskless(self, mock_status, 
mock_confirm, mock_from_config, mock_prompt):
         bootstrap._context = mock.Mock(yes_to_all=False)
         mock_confirm.return_value = True
         mock_from_config.return_value = None
@@ -123,7 +415,6 @@
 
         
mock_status.assert_called_once_with(bootstrap.SBDManager.SBD_STATUS_DESCRIPTION)
         mock_confirm.assert_called_once_with("Do you wish to use SBD?")
-        mock_check.assert_called_once_with()
         mock_from_config.assert_called_once_with()
         mock_prompt.assert_has_calls([
             mock.call('Path to storage device (e.g. /dev/disk/by-id/...), or 
"none" for diskless sbd, use ";" as separator for multi path', 'none|\\/.*') 
for x in range(2)
@@ -134,10 +425,9 @@
     @mock.patch('crmsh.bootstrap.SBDManager._verify_sbd_device')
     @mock.patch('crmsh.bootstrap.prompt_for_string')
     @mock.patch('crmsh.bootstrap.SBDManager._get_sbd_device_from_config')
-    @mock.patch('crmsh.bootstrap.SBDManager._check_environment')
     @mock.patch('crmsh.bootstrap.confirm')
     @mock.patch('crmsh.bootstrap.status')
-    def test_get_sbd_device_interactive(self, mock_status, mock_confirm, 
mock_check, mock_from_config, mock_prompt, mock_verify, mock_error_msg, 
mock_warn):
+    def test_get_sbd_device_interactive(self, mock_status, mock_confirm, 
mock_from_config, mock_prompt, mock_verify, mock_error_msg, mock_warn):
         bootstrap._context = mock.Mock(yes_to_all=False)
         mock_confirm.side_effect = [True, False, True]
         mock_from_config.return_value = None
@@ -152,7 +442,6 @@
             mock.call("Do you wish to use SBD?"),
             mock.call("Are you sure you wish to use this device?")
             ])
-        mock_check.assert_called_once_with()
         mock_from_config.assert_called_once_with()
         mock_error_msg.assert_called_once_with("/dev/test1 error")
         mock_warn.assert_has_calls([
@@ -163,33 +452,6 @@
             mock.call('Path to storage device (e.g. /dev/disk/by-id/...), or 
"none" for diskless sbd, use ";" as separator for multi path', 'none|\\/.*') 
for x in range(3)
             ])
 
-    @mock.patch('crmsh.bootstrap.error')
-    @mock.patch('crmsh.bootstrap.check_watchdog')
-    def test_check_environment_no_watchdog(self, mock_watchdog, mock_error):
-        mock_watchdog.return_value = False
-        mock_error.side_effect = ValueError
-
-        with self.assertRaises(ValueError):
-            self.sbd_inst._check_environment()
-
-        mock_error.assert_called_once_with("Watchdog device must be configured 
in order to use SBD")
-        mock_watchdog.assert_called_once_with()
-
-    @mock.patch('crmsh.utils.is_program')
-    @mock.patch('crmsh.bootstrap.error')
-    @mock.patch('crmsh.bootstrap.check_watchdog')
-    def test_check_environment_no_sbd(self, mock_watchdog, mock_error, 
mock_is_program):
-        mock_watchdog.return_value = True
-        mock_is_program.return_value = False
-        mock_error.side_effect = ValueError
-
-        with self.assertRaises(ValueError):
-            self.sbd_inst._check_environment()
-
-        mock_error.assert_called_once_with("sbd executable not found! Cannot 
configure SBD")
-        mock_watchdog.assert_called_once_with()
-        mock_is_program.assert_called_once_with("sbd")
-
     def test_parse_sbd_device(self):
         res = self.sbd_inst._parse_sbd_device()
         assert res == ["/dev/sdb1", "/dev/sdc1"]
@@ -215,15 +477,13 @@
         mock_block_device.assert_has_calls([mock.call("/dev/sdb1"), 
mock.call("/dev/sdc1")])
         mock_compare.assert_called_once_with("/dev/sdb1", [])
 
-    @mock.patch('crmsh.bootstrap.SBDManager._check_environment')
     @mock.patch('crmsh.bootstrap.SBDManager._verify_sbd_device')
     @mock.patch('crmsh.bootstrap.SBDManager._parse_sbd_device')
-    def test_get_sbd_device_from_option(self, mock_parse, mock_verify, 
mock_check):
+    def test_get_sbd_device_from_option(self, mock_parse, mock_verify):
         mock_parse.return_value = ["/dev/sdb1", "/dev/sdc1"]
         self.sbd_inst._get_sbd_device()
         mock_parse.assert_called_once_with()
         mock_verify.assert_called_once_with(mock_parse.return_value)
-        mock_check.assert_called_once_with()
 
     @mock.patch('crmsh.bootstrap.SBDManager._get_sbd_device_interactive')
     def test_get_sbd_device_from_interactive(self, mock_interactive):
@@ -231,10 +491,8 @@
         self.sbd_inst_interactive._get_sbd_device()
         mock_interactive.assert_called_once_with()
 
-    @mock.patch('crmsh.bootstrap.SBDManager._check_environment')
-    def test_get_sbd_device_diskless(self, mock_check):
+    def test_get_sbd_device_diskless(self):
         self.sbd_inst_diskless._get_sbd_device()
-        mock_check.assert_called_once_with()
 
     @mock.patch('crmsh.bootstrap.error')
     @mock.patch('crmsh.bootstrap.invoke')
@@ -254,16 +512,14 @@
 
     @mock.patch('crmsh.bootstrap.csync2_update')
     @mock.patch('crmsh.utils.sysconfig_set')
-    @mock.patch('crmsh.bootstrap.detect_watchdog_device')
     @mock.patch('shutil.copyfile')
-    def test_update_configuration(self, mock_copy, mock_detect, 
mock_sysconfig, mock_update):
+    def test_update_configuration(self, mock_copy, mock_sysconfig, 
mock_update):
         self.sbd_inst._sbd_devices = ["/dev/sdb1", "/dev/sdc1"]
-        mock_detect.return_value = "/dev/watchdog"
+        self.sbd_inst._watchdog_inst = 
mock.Mock(watchdog_device_name="/dev/watchdog")
 
         self.sbd_inst._update_configuration()
 
         
mock_copy.assert_called_once_with("/usr/share/fillup-templates/sysconfig.sbd", 
"/etc/sysconfig/sbd")
-        mock_detect.assert_called_once_with()
         mock_sysconfig.assert_called_once_with("/etc/sysconfig/sbd", 
SBD_PACEMAKER='yes', SBD_STARTMODE='always', SBD_DELAY_START='no', 
SBD_WATCHDOG_DEV='/dev/watchdog', SBD_DEVICE='/dev/sdb1;/dev/sdc1')
         mock_update.assert_called_once_with("/etc/sysconfig/sbd")
 
@@ -297,16 +553,21 @@
         self.sbd_inst.sbd_init()
         mock_package.assert_called_once_with("sbd")
 
+    @mock.patch('crmsh.bootstrap.invoke')
     @mock.patch('crmsh.bootstrap.status_done')
     @mock.patch('crmsh.bootstrap.SBDManager._update_configuration')
     @mock.patch('crmsh.bootstrap.SBDManager._initialize_sbd')
     @mock.patch('crmsh.bootstrap.status_long')
     @mock.patch('crmsh.bootstrap.SBDManager._get_sbd_device')
+    @mock.patch('crmsh.bootstrap.Watchdog')
     @mock.patch('crmsh.utils.package_is_installed')
-    def test_sbd_init_return(self, mock_package, mock_get_device, mock_status, 
mock_initialize, mock_update, mock_status_done):
+    def test_sbd_init_return(self, mock_package, mock_watchdog, 
mock_get_device, mock_status, mock_initialize, mock_update, mock_status_done, 
mock_invoke):
         mock_package.return_value = True
         self.sbd_inst._sbd_devices = None
         self.sbd_inst.diskless_sbd = False
+        mock_watchdog_inst = mock.Mock()
+        mock_watchdog.return_value = mock_watchdog_inst
+        mock_watchdog_inst.init_watchdog = mock.Mock()
 
         self.sbd_inst.sbd_init()
 
@@ -316,15 +577,23 @@
         mock_initialize.assert_not_called()
         mock_update.assert_not_called()
         mock_status_done.assert_not_called()
+        mock_watchdog.assert_called_once_with(_input=None)
+        mock_watchdog_inst.init_watchdog.assert_called_once_with()
+        mock_invoke.assert_called_once_with("systemctl disable sbd.service")
 
     @mock.patch('crmsh.bootstrap.status_done')
     @mock.patch('crmsh.bootstrap.SBDManager._update_configuration')
     @mock.patch('crmsh.bootstrap.SBDManager._initialize_sbd')
     @mock.patch('crmsh.bootstrap.status_long')
     @mock.patch('crmsh.bootstrap.SBDManager._get_sbd_device')
+    @mock.patch('crmsh.bootstrap.Watchdog')
     @mock.patch('crmsh.utils.package_is_installed')
-    def test_sbd_init(self, mock_package, mock_get_device, mock_status, 
mock_initialize, mock_update, mock_status_done):
+    def test_sbd_init(self, mock_package, mock_watchdog, mock_get_device, 
mock_status, mock_initialize, mock_update, mock_status_done):
+        bootstrap._context = mock.Mock(watchdog=None)
         mock_package.return_value = True
+        mock_watchdog_inst = mock.Mock()
+        mock_watchdog.return_value = mock_watchdog_inst
+        mock_watchdog_inst.init_watchdog = mock.Mock()
         self.sbd_inst_diskless.sbd_init()
 
         mock_package.assert_called_once_with("sbd")
@@ -333,6 +602,8 @@
         mock_initialize.assert_called_once_with()
         mock_update.assert_called_once_with()
         mock_status_done.assert_called_once_with()
+        mock_watchdog.assert_called_once_with(_input=None)
+        mock_watchdog_inst.init_watchdog.assert_called_once_with()
 
     @mock.patch('crmsh.utils.package_is_installed')
     def test_configure_sbd_resource_not_installed(self, mock_package):
@@ -423,12 +694,11 @@
         mock_exists.assert_called_once_with("/etc/sysconfig/sbd")
         mock_invoke.assert_called_once_with("systemctl disable sbd.service")
 
-    @mock.patch('crmsh.bootstrap.SBDManager._check_environment')
     @mock.patch('crmsh.bootstrap.invoke')
     @mock.patch('crmsh.utils.service_is_enabled')
     @mock.patch('os.path.exists')
     @mock.patch('crmsh.utils.package_is_installed')
-    def test_join_sbd_config_disabled(self, mock_package, mock_exists, 
mock_enabled, mock_invoke, mock_check):
+    def test_join_sbd_config_disabled(self, mock_package, mock_exists, 
mock_enabled, mock_invoke):
         mock_package.return_value = True
         mock_exists.return_value = True
         mock_enabled.return_value = False
@@ -439,32 +709,35 @@
         mock_exists.assert_called_once_with("/etc/sysconfig/sbd")
         mock_invoke.assert_called_once_with("systemctl disable sbd.service")
         mock_enabled.assert_called_once_with("sbd.service", "node1")
-        mock_check.assert_not_called()
 
     @mock.patch('crmsh.bootstrap.status')
     @mock.patch('crmsh.bootstrap.SBDManager._verify_sbd_device')
     @mock.patch('crmsh.bootstrap.SBDManager._get_sbd_device_from_config')
-    @mock.patch('crmsh.bootstrap.SBDManager._check_environment')
+    @mock.patch('crmsh.bootstrap.Watchdog')
     @mock.patch('crmsh.bootstrap.invoke')
     @mock.patch('crmsh.utils.service_is_enabled')
     @mock.patch('os.path.exists')
     @mock.patch('crmsh.utils.package_is_installed')
-    def test_join_sbd(self, mock_package, mock_exists, mock_enabled, 
mock_invoke, mock_check, mock_get_device, mock_verify, mock_status):
+    def test_join_sbd(self, mock_package, mock_exists, mock_enabled, 
mock_invoke, mock_watchdog, mock_get_device, mock_verify, mock_status):
         mock_package.return_value = True
         mock_exists.return_value = True
         mock_enabled.return_value = True
         mock_get_device.return_value = ["/dev/sdb1"]
+        mock_watchdog_inst = mock.Mock()
+        mock_watchdog.return_value = mock_watchdog_inst
+        mock_watchdog_inst.join_watchdog = mock.Mock()
 
         self.sbd_inst.join_sbd("node1")
 
         mock_package.assert_called_once_with("sbd")
         mock_exists.assert_called_once_with("/etc/sysconfig/sbd")
         mock_invoke.assert_called_once_with("systemctl enable sbd.service")
-        mock_check.assert_called_once_with()
         mock_get_device.assert_called_once_with()
         mock_verify.assert_called_once_with(["/dev/sdb1"], ["node1"])
         mock_enabled.assert_called_once_with("sbd.service", "node1")
         mock_status.assert_called_once_with("Got SBD configuration")
+        mock_watchdog.assert_called_once_with(peer_host="node1")
+        mock_watchdog_inst.join_watchdog.assert_called_once_with()
 
     @mock.patch('crmsh.bootstrap.SBDManager._get_sbd_device_from_config')
     def test_verify_sbd_device_classmethod_exception(self, mock_get_config):

Reply via email to