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):