Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package crmsh for openSUSE:Factory checked 
in at 2022-12-09 13:18:11
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/crmsh (Old)
 and      /work/SRC/openSUSE:Factory/.crmsh.new.1835 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "crmsh"

Fri Dec  9 13:18:11 2022 rev:270 rq:1041757 version:4.4.1+20221207.84e6ea16

Changes:
--------
--- /work/SRC/openSUSE:Factory/crmsh/crmsh.changes      2022-12-05 
18:01:38.652726341 +0100
+++ /work/SRC/openSUSE:Factory/.crmsh.new.1835/crmsh.changes    2022-12-09 
13:19:37.663590267 +0100
@@ -1,0 +2,16 @@
+Wed Dec 07 02:27:46 UTC 2022 - xli...@suse.com
+
+- Update to version 4.4.1+20221207.84e6ea16:
+  * Dev: parse: Don't set timeout value when is not set by user meanwhile no 
value is advised in the metadata
+  * Dev: parse: complete advised operation values for other actions beside 
monitor
+  * Dev: unittest: Add unit test for utils.compatible_role
+  * Dev: parse: Consider compatibility for role when complete operation 
actions with advised values
+
+-------------------------------------------------------------------
+Tue Dec 06 09:17:16 UTC 2022 - xli...@suse.com
+
+- Update to version 4.4.1+20221206.b25bc04c:
+  * Dev: unittest: Adjust unit test based on previous changes
+  * Dev: qdevice: Refactor qdevice validation code
+
+-------------------------------------------------------------------

Old:
----
  crmsh-4.4.1+20221203.9bb5442e.tar.bz2

New:
----
  crmsh-4.4.1+20221207.84e6ea16.tar.bz2

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

Other differences:
------------------
++++++ crmsh.spec ++++++
--- /var/tmp/diff_new_pack.BOUFht/_old  2022-12-09 13:19:38.183593030 +0100
+++ /var/tmp/diff_new_pack.BOUFht/_new  2022-12-09 13:19:38.187593051 +0100
@@ -36,7 +36,7 @@
 Summary:        High Availability cluster command-line interface
 License:        GPL-2.0-or-later
 Group:          %{pkg_group}
-Version:        4.4.1+20221203.9bb5442e
+Version:        4.4.1+20221207.84e6ea16
 Release:        0
 URL:            http://crmsh.github.io
 Source0:        %{name}-%{version}.tar.bz2

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.BOUFht/_old  2022-12-09 13:19:38.231593285 +0100
+++ /var/tmp/diff_new_pack.BOUFht/_new  2022-12-09 13:19:38.235593306 +0100
@@ -9,7 +9,7 @@
 </service>
 <service name="tar_scm">
   <param name="url">https://github.com/ClusterLabs/crmsh.git</param>
-  <param 
name="changesrevision">d450e2c49093db2e6ba718b6e92800bd3f843128</param>
+  <param 
name="changesrevision">84e6ea16faf7d379e21af00ba934a6b644723309</param>
 </service>
 </servicedata>
 (No newline at EOF)

++++++ crmsh-4.4.1+20221203.9bb5442e.tar.bz2 -> 
crmsh-4.4.1+20221207.84e6ea16.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-4.4.1+20221203.9bb5442e/crmsh/bootstrap.py 
new/crmsh-4.4.1+20221207.84e6ea16/crmsh/bootstrap.py
--- old/crmsh-4.4.1+20221203.9bb5442e/crmsh/bootstrap.py        2022-12-03 
15:47:59.000000000 +0100
+++ new/crmsh-4.4.1+20221207.84e6ea16/crmsh/bootstrap.py        2022-12-07 
03:10:51.000000000 +0100
@@ -293,7 +293,7 @@
         readline.remove_history_item(hlen - 1)
 
 
-def prompt_for_string(msg, match=None, default='', valid_func=None, 
prev_value=[]):
+def prompt_for_string(msg, match=None, default='', valid_func=None, 
prev_value=[], allow_empty=False):
     if _context.yes_to_all:
         return default
 
@@ -303,8 +303,10 @@
         enable_completion()
         if val:
             drop_last_history()
-        else:
+        elif allow_empty:
             return None
+        else:
+            continue
         if not match and not valid_func:
             return val
         if match and not re.match(match, val):
@@ -312,7 +314,10 @@
             continue
         if valid_func:
             try:
-                valid_func(val, prev_value)
+                if prev_value:
+                    valid_func(val, prev_value)
+                else:
+                    valid_func(val)
             except ValueError as err:
                 logger.error(err)
                 continue
@@ -1336,8 +1341,6 @@
             return
 
         adminaddr = prompt_for_string('Virtual IP', 
valid_func=Validation.valid_admin_ip)
-        if not adminaddr:
-            utils.fatal("Expected an IP address")
 
     crm_configure_load("update", 'primitive admin-ip IPaddr2 ip=%s op monitor 
interval=10 timeout=20' % (utils.doublequote(adminaddr)))
     wait_for_resource("Configuring virtual IP ({})".format(adminaddr), 
"admin-ip")
@@ -1352,15 +1355,33 @@
     logger.info("Configure Qdevice/Qnetd:\n" + QDEVICE_HELP_INFO + "\n")
     if not confirm("Do you want to configure QDevice?"):
         return
-    qnetd_addr = prompt_for_string("HOST or IP of the QNetd server to be used")
-    if not qnetd_addr:
-        utils.fatal("Address of QNetd is required")
-    qdevice_port = prompt_for_string("TCP PORT of QNetd server", default=5403)
-    qdevice_algo = prompt_for_string("QNetd decision ALGORITHM (ffsplit/lms)", 
default="ffsplit")
-    qdevice_tie_breaker = prompt_for_string("QNetd TIE_BREAKER 
(lowest/highest/valid node id)", default="lowest")
-    qdevice_tls = prompt_for_string("Whether using TLS on QDevice/QNetd 
(on/off/required)", default="on")
-    qdevice_heuristics = prompt_for_string("Heuristics COMMAND to run with 
absolute path; For multiple commands, use \";\" to separate")
-    qdevice_heuristics_mode = prompt_for_string("MODE of operation of 
heuristics (on/sync/off)", default="sync") if qdevice_heuristics else None
+    while True:
+        try:
+            qdevice.QDevice.check_package_installed("corosync-qdevice")
+            break
+        except ValueError as err:
+            logger.error(err)
+            if confirm("Please install the package manually and press 'y' to 
continue"):
+                continue
+            else:
+                return
+
+    qnetd_addr = prompt_for_string("HOST or IP of the QNetd server to be used",
+            valid_func=qdevice.QDevice.check_qnetd_addr)
+    qdevice_port = prompt_for_string("TCP PORT of QNetd server", default=5403,
+            valid_func=qdevice.QDevice.check_qdevice_port)
+    qdevice_algo = prompt_for_string("QNetd decision ALGORITHM (ffsplit/lms)", 
default="ffsplit",
+            valid_func=qdevice.QDevice.check_qdevice_algo)
+    qdevice_tie_breaker = prompt_for_string("QNetd TIE_BREAKER 
(lowest/highest/valid node id)", default="lowest",
+            valid_func=qdevice.QDevice.check_qdevice_tie_breaker)
+    qdevice_tls = prompt_for_string("Whether using TLS on QDevice/QNetd 
(on/off/required)", default="on",
+            valid_func=qdevice.QDevice.check_qdevice_tls)
+    qdevice_heuristics = prompt_for_string("Heuristics COMMAND to run with 
absolute path; For multiple commands, use \";\" to separate",
+            valid_func=qdevice.QDevice.check_qdevice_heuristics,
+            allow_empty=True)
+    qdevice_heuristics_mode = prompt_for_string("MODE of operation of 
heuristics (on/sync/off)", default="sync",
+            valid_func=qdevice.QDevice.check_qdevice_heuristics_mode) if 
qdevice_heuristics else None
+
     _context.qdevice_inst = qdevice.QDevice(
             qnetd_addr,
             port=qdevice_port,
@@ -1370,7 +1391,6 @@
             cmds=qdevice_heuristics,
             mode=qdevice_heuristics_mode,
             is_stage=_context.stage == "qdevice")
-    _context.qdevice_inst.valid_qdevice_options()
 
 
 def init_qdevice():
@@ -1787,16 +1807,16 @@
     if is_unicast:
         ringXaddr_res = []
         for i in 0, 1:
-            while True:
-                ringXaddr = prompt_for_string(
-                        'Address for ring{}'.format(i),
-                        default=pick_default_value(_context.default_ip_list, 
ringXaddr_res),
-                        valid_func=Validation.valid_ucast_ip,
-                        prev_value=ringXaddr_res)
-                if not ringXaddr:
-                    utils.fatal("No value for ring{}".format(i))
-                ringXaddr_res.append(ringXaddr)
-                break
+            ringXaddr = prompt_for_string(
+                    'Address for ring{}'.format(i),
+                    default=pick_default_value(_context.default_ip_list, 
ringXaddr_res),
+                    valid_func=Validation.valid_ucast_ip,
+                    prev_value=ringXaddr_res)
+            # The ringXaddr here still might be empty on non-interactive mode
+            # when don't have default ip addresses(_context.default_ip_list is 
empty or just one)
+            if not ringXaddr:
+                utils.fatal("No value for ring{}".format(i))
+            ringXaddr_res.append(ringXaddr)
             if not rrp_flag:
                 break
         invoke("rm -f /var/lib/heartbeat/crm/* /var/lib/pacemaker/cib/*")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-4.4.1+20221203.9bb5442e/crmsh/constants.py 
new/crmsh-4.4.1+20221207.84e6ea16/crmsh/constants.py
--- old/crmsh-4.4.1+20221203.9bb5442e/crmsh/constants.py        2022-12-03 
15:47:59.000000000 +0100
+++ new/crmsh-4.4.1+20221207.84e6ea16/crmsh/constants.py        2022-12-07 
03:10:51.000000000 +0100
@@ -241,8 +241,6 @@
 date_spec_names = '''hours monthdays weekdays yearsdays months \
 weeks years weekyears moon'''.split()
 in_range_attrs = ('start', 'end')
-roles_names = ('Stopped', 'Started', 'Master', 'Slave')
-actions_names = ('start', 'promote', 'demote', 'stop')
 node_default_type = "normal"
 node_attributes_keyw = ("attributes", "utilization")
 shadow_envvar = "CIB_shadow"
@@ -527,7 +525,11 @@
 ADVISED_ACTION_LIST = ['monitor', 'start', 'stop', 'promote', 'demote']
 ADVISED_KEY_LIST = ['timeout', 'interval', 'role']
 DEFAULT_INTERVAL_IN_ACTION = "20s"
-DEFAULT_TIMEOUT_IN_ACTION = "60s"
 
 WAIT_TIMEOUT_MS_DEFAULT = 120000
+
+RSC_ROLE_PROMOTED = "Promoted"
+RSC_ROLE_UNPROMOTED = "Unpromoted"
+RSC_ROLE_PROMOTED_LEGACY = "Master"
+RSC_ROLE_UNPROMOTED_LEGACY = "Slave"
 # vim:ts=4:sw=4:et:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-4.4.1+20221203.9bb5442e/crmsh/parse.py 
new/crmsh-4.4.1+20221207.84e6ea16/crmsh/parse.py
--- old/crmsh-4.4.1+20221203.9bb5442e/crmsh/parse.py    2022-12-03 
15:47:59.000000000 +0100
+++ new/crmsh-4.4.1+20221207.84e6ea16/crmsh/parse.py    2022-12-07 
03:10:51.000000000 +0100
@@ -12,7 +12,7 @@
 from . import schema
 from .utils import keyword_cmp, verify_boolean, lines2cli
 from .utils import get_boolean, olist, canonical_boolean
-from .utils import handle_role_for_ocf_1_1
+from .utils import handle_role_for_ocf_1_1, compatible_role
 from . import xmlutil
 from . import log
 
@@ -668,15 +668,18 @@
         if not ra_actions_dict:
             return
 
-        def extract_advised_monitor_value(advised_dict, attr, role=None):
+        def extract_advised_value(advised_dict, action, attr, role=None):
             adv_attr_value = None
             try:
-                if role:
-                    for monitor_item in advised_dict['monitor']:
-                        if role == monitor_item['role']:
-                            adv_attr_value = monitor_item[attr]
+                if action == "monitor":
+                    if role:
+                        for monitor_item in advised_dict[action]:
+                            if compatible_role(role, monitor_item['role']):
+                                adv_attr_value = monitor_item[attr]
+                    else:
+                        adv_attr_value = advised_dict[action][0][attr]
                 else:
-                    adv_attr_value = advised_dict['monitor'][0][attr]
+                    adv_attr_value = advised_dict[action][attr]
             except KeyError:
                 pass
             return adv_attr_value
@@ -693,15 +696,13 @@
             for op_node in op_nodes_list:
                 action = op_node.get('name')
                 # complete advised value if interval or timeout not configured
-                if action == "monitor":
-                    adv_interval = 
extract_advised_monitor_value(action_advised_attr_dict, 'interval', 
op_node.get('role')) or \
-                            constants.DEFAULT_INTERVAL_IN_ACTION
-                    adv_timeout = 
extract_advised_monitor_value(action_advised_attr_dict, 'timeout', 
op_node.get('role')) or \
-                            constants.DEFAULT_TIMEOUT_IN_ACTION
-                    if op_node.get('interval') is None:
-                        op_node.set('interval', adv_interval)
-                    if op_node.get('timeout') is None:
-                        op_node.set('timeout', adv_timeout)
+                adv_interval = extract_advised_value(action_advised_attr_dict, 
action, 'interval', op_node.get('role')) or \
+                        constants.DEFAULT_INTERVAL_IN_ACTION
+                adv_timeout = extract_advised_value(action_advised_attr_dict, 
action, 'timeout', op_node.get('role'))
+                if op_node.get('interval') is None:
+                    op_node.set('interval', adv_interval)
+                if op_node.get('timeout') is None and adv_timeout:
+                    op_node.set('timeout', adv_timeout)
                 configured_action_list.append(action)
 
         for action in action_advised_attr_dict:
@@ -716,7 +717,7 @@
                     for k, v in v_dict.items():
                         # set normal attributes
                         if k in constants.ADVISED_KEY_LIST:
-                            op_node.set(k, v)
+                            op_node.set(k, handle_role_for_ocf_1_1(v))
                     operations_node.append(op_node)
             else:
                 op_node = xmlutil.new('op', name=action, **value)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-4.4.1+20221203.9bb5442e/crmsh/qdevice.py 
new/crmsh-4.4.1+20221207.84e6ea16/crmsh/qdevice.py
--- old/crmsh-4.4.1+20221203.9bb5442e/crmsh/qdevice.py  2022-12-03 
15:47:59.000000000 +0100
+++ new/crmsh-4.4.1+20221207.84e6ea16/crmsh/qdevice.py  2022-12-07 
03:10:51.000000000 +0100
@@ -201,52 +201,82 @@
         """
         return "{}/{}/{}".format(self.qdevice_path, self.cluster_node, 
self.qdevice_p12_filename)
 
-    def valid_qdevice_options(self):
-        """
-        Validate qdevice related options
-        """
+    @staticmethod
+    def check_qnetd_addr(qnetd_addr):
         qnetd_ip = None
-
-        if not utils.package_is_installed("corosync-qdevice"):
-            raise ValueError("Package \"corosync-qdevice\" not installed on 
this node")
-
         try:
             # socket.getaddrinfo works for both ipv4 and ipv6 address
             # The function returns a list of 5-tuples with the following 
structure:
             # (family, type, proto, canonname, sockaddr)
             # sockaddr is a tuple describing a socket address, whose format 
depends on the returned family
             # (a (address, port) 2-tuple for AF_INET, a (address, port, flow 
info, scope id) 4-tuple for AF_INET6)
-            res = socket.getaddrinfo(self.qnetd_addr, None)
+            res = socket.getaddrinfo(qnetd_addr, None)
             qnetd_ip = res[0][-1][0]
         except socket.error:
-            raise ValueError("host \"{}\" is 
unreachable".format(self.qnetd_addr))
+            raise ValueError("host \"{}\" is unreachable".format(qnetd_addr))
 
-        utils.ping_node(self.qnetd_addr)
+        utils.ping_node(qnetd_addr)
 
         if utils.InterfacesInfo.ip_in_local(qnetd_ip):
             raise ValueError("host for qnetd must be a remote one")
 
         if not utils.check_port_open(qnetd_ip, 22):
-            raise ValueError("ssh service on \"{}\" not 
available".format(self.qnetd_addr))
+            raise ValueError("ssh service on \"{}\" not 
available".format(qnetd_addr))
 
-        if not utils.valid_port(self.port):
+    @staticmethod
+    def check_qdevice_port(qdevice_port):
+        if not utils.valid_port(qdevice_port):
             raise ValueError("invalid qdevice port range(1024 - 65535)")
 
-        if self.algo not in ("ffsplit", "lms"):
-            raise ValueError("invalid ALGORITHM choice: '{}' (choose from 
'ffsplit', 'lms')".format(self.algo))
-
-        if self.tie_breaker not in ("lowest", "highest") and not 
utils.valid_nodeid(self.tie_breaker):
+    @staticmethod
+    def check_qdevice_algo(qdevice_algo):
+        if qdevice_algo not in ("ffsplit", "lms"):
+            raise ValueError("invalid ALGORITHM choice: '{}' (choose from 
'ffsplit', 'lms')".format(qdevice_algo))
+
+    @staticmethod
+    def check_qdevice_tie_breaker(qdevice_tie_breaker):
+        if qdevice_tie_breaker not in ("lowest", "highest") and not 
utils.valid_nodeid(qdevice_tie_breaker):
             raise ValueError("invalid qdevice 
tie_breaker(lowest/highest/valid_node_id)")
 
-        if self.tls not in ("on", "off", "required"):
-            raise ValueError("invalid TLS choice: '{}' (choose from 'on', 
'off', 'required')".format(self.tls))
+    @staticmethod
+    def check_qdevice_tls(qdevice_tls):
+        if qdevice_tls not in ("on", "off", "required"):
+            raise ValueError("invalid TLS choice: '{}' (choose from 'on', 
'off', 'required')".format(qdevice_tls))
+
+    @staticmethod
+    def check_qdevice_heuristics_mode(mode):
+        if not mode:
+            return
+        if mode not in ("on", "sync", "off"):
+            raise ValueError("invalid MODE choice: '{}' (choose from 'on', 
'sync', 'off')".format(mode))
+
+    @staticmethod
+    def check_qdevice_heuristics(cmds):
+        if not cmds:
+            return
+        for cmd in cmds.strip(';').split(';'):
+            if not cmd.startswith('/'):
+                raise ValueError("commands for heuristics should be absolute 
path")
+            if not os.path.exists(cmd.split()[0]):
+                raise ValueError("command {} not exist".format(cmd.split()[0]))
+
+    @staticmethod
+    def check_package_installed(pkg, remote=None):
+        if not utils.package_is_installed(pkg, remote_addr=remote):
+            raise ValueError("Package \"{}\" not installed on {}".format(pkg, 
remote if remote else "this node"))
 
-        if self.cmds:
-            for cmd in self.cmds.strip(';').split(';'):
-                if not cmd.startswith('/'):
-                    raise ValueError("commands for heuristics should be 
absolute path")
-                if not os.path.exists(cmd.split()[0]):
-                    raise ValueError("command {} not 
exist".format(cmd.split()[0]))
+    def valid_qdevice_options(self):
+        """
+        Validate qdevice related options
+        """
+        self.check_package_installed("corosync-qdevice")
+        self.check_qnetd_addr(self.qnetd_addr)
+        self.check_qdevice_port(self.port)
+        self.check_qdevice_algo(self.algo)
+        self.check_qdevice_tie_breaker(self.tie_breaker)
+        self.check_qdevice_tls(self.tls)
+        self.check_qdevice_heuristics(self.cmds)
+        self.check_qdevice_heuristics_mode(self.mode)
 
     def valid_qnetd(self):
         """
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-4.4.1+20221203.9bb5442e/crmsh/sbd.py 
new/crmsh-4.4.1+20221207.84e6ea16/crmsh/sbd.py
--- old/crmsh-4.4.1+20221203.9bb5442e/crmsh/sbd.py      2022-12-03 
15:47:59.000000000 +0100
+++ new/crmsh-4.4.1+20221207.84e6ea16/crmsh/sbd.py      2022-12-07 
03:10:51.000000000 +0100
@@ -352,8 +352,6 @@
         dev_looks_sane = False
         while not dev_looks_sane:
             dev = bootstrap.prompt_for_string('Path to storage device (e.g. 
/dev/disk/by-id/...), or "none" for diskless sbd, use ";" as separator for 
multi path', r'none|\/.*')
-            if not dev:
-                continue
             if dev == "none":
                 self.diskless_sbd = True
                 return
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-4.4.1+20221203.9bb5442e/crmsh/ui_resource.py 
new/crmsh-4.4.1+20221207.84e6ea16/crmsh/ui_resource.py
--- old/crmsh-4.4.1+20221203.9bb5442e/crmsh/ui_resource.py      2022-12-03 
15:47:59.000000000 +0100
+++ new/crmsh-4.4.1+20221207.84e6ea16/crmsh/ui_resource.py      2022-12-07 
03:10:51.000000000 +0100
@@ -338,7 +338,7 @@
         if not xmlutil.RscState().is_ms_or_promotable_clone(rsc):
             logger.error("%s is not a promotable resource", rsc)
             return False
-        role = "Promoted" if config.core.OCF_1_1_SUPPORT else "Master"
+        role = 
utils.handle_role_for_ocf_1_1(constants.RSC_ROLE_PROMOTED_LEGACY)
         return utils.ext_cmd(self.rsc_setrole % (rsc, role)) == 0
 
     def do_scores(self, context):
@@ -367,7 +367,7 @@
         if not xmlutil.RscState().is_ms_or_promotable_clone(rsc):
             logger.error("%s is not a promotable resource", rsc)
             return False
-        role = "Unpromoted" if config.core.OCF_1_1_SUPPORT else "Slave"
+        role = 
utils.handle_role_for_ocf_1_1(constants.RSC_ROLE_UNPROMOTED_LEGACY)
         return utils.ext_cmd(self.rsc_setrole % (rsc, role)) == 0
 
     @command.completers(compl.resources)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-4.4.1+20221203.9bb5442e/crmsh/utils.py 
new/crmsh-4.4.1+20221207.84e6ea16/crmsh/utils.py
--- old/crmsh-4.4.1+20221203.9bb5442e/crmsh/utils.py    2022-12-03 
15:47:59.000000000 +0100
+++ new/crmsh-4.4.1+20221207.84e6ea16/crmsh/utils.py    2022-12-07 
03:10:51.000000000 +0100
@@ -3059,16 +3059,28 @@
     Only turn on ocf_1_1 feature the cib schema version is pacemaker-3.7 or 
above
     """
     from .cibconfig import cib_factory
+    cib_factory.get_cib()
     return is_larger_than_min_version(cib_factory.get_schema(), 
constants.SCHEMA_MIN_VER_SUPPORT_OCF_1_1)
 
 
+def compatible_role(role1, role2):
+    master_or_promoted = (constants.RSC_ROLE_PROMOTED_LEGACY, 
constants.RSC_ROLE_PROMOTED)
+    slave_or_unpromoted = (constants.RSC_ROLE_UNPROMOTED_LEGACY, 
constants.RSC_ROLE_UNPROMOTED)
+    res1 = role1 in master_or_promoted and role2 in master_or_promoted
+    res2 = role1 in slave_or_unpromoted and role2 in slave_or_unpromoted
+    return res1 or res2
+
+
 def handle_role_for_ocf_1_1(value, name='role'):
     """
     * Convert role from Promoted/Unpromoted to Master/Slave if schema doesn't 
support OCF 1.1
     * Convert role from Master/Slave to Promoted/Unpromoted if ocf1.1 cib 
schema detected and OCF_1_1_SUPPORT is yes
     """
     role_names = ["role", "target-role"]
-    downgrade_dict = {"Promoted": "Master", "Unpromoted": "Slave"}
+    downgrade_dict = {
+            constants.RSC_ROLE_PROMOTED: constants.RSC_ROLE_PROMOTED_LEGACY,
+            constants.RSC_ROLE_UNPROMOTED: constants.RSC_ROLE_UNPROMOTED_LEGACY
+            }
     upgrade_dict = {v: k for k, v in downgrade_dict.items()}
 
     if name not in role_names:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.4.1+20221203.9bb5442e/test/unittests/test_bootstrap.py 
new/crmsh-4.4.1+20221207.84e6ea16/test/unittests/test_bootstrap.py
--- old/crmsh-4.4.1+20221203.9bb5442e/test/unittests/test_bootstrap.py  
2022-12-03 15:47:59.000000000 +0100
+++ new/crmsh-4.4.1+20221207.84e6ea16/test/unittests/test_bootstrap.py  
2022-12-07 03:10:51.000000000 +0100
@@ -870,26 +870,34 @@
         bootstrap.configure_qdevice_interactive()
         mock_prompt.assert_not_called()
 
-    @mock.patch('crmsh.utils.fatal')
-    @mock.patch('crmsh.bootstrap.prompt_for_string')
+    @mock.patch('logging.Logger.info')
     @mock.patch('crmsh.bootstrap.confirm')
-    def test_configure_qdevice_interactive_error(self, mock_confirm, 
mock_prompt, mock_error):
+    def test_configure_qdevice_interactive_not_confirm(self, mock_confirm, 
mock_info):
         bootstrap._context = mock.Mock(yes_to_all=False)
-        mock_confirm.return_value = True
-        mock_prompt.return_value = None
-        mock_error.side_effect = SystemExit
-
-        with self.assertRaises(SystemExit):
-            bootstrap.configure_qdevice_interactive()
-
-        mock_error.assert_called_once_with("Address of QNetd is required")
+        mock_confirm.return_value = False
+        bootstrap.configure_qdevice_interactive()
         mock_confirm.assert_called_once_with("Do you want to configure 
QDevice?")
-        mock_prompt.assert_called_once_with("HOST or IP of the QNetd server to 
be used")
+
+    @mock.patch('logging.Logger.error')
+    @mock.patch('crmsh.qdevice.QDevice.check_package_installed')
+    @mock.patch('logging.Logger.info')
+    @mock.patch('crmsh.bootstrap.confirm')
+    def test_configure_qdevice_interactive_not_installed(self, mock_confirm, 
mock_info, mock_installed, mock_error):
+        bootstrap._context = mock.Mock(yes_to_all=False)
+        mock_confirm.side_effect = [True, False]
+        mock_installed.side_effect = ValueError("corosync-qdevice not 
installed")
+        bootstrap.configure_qdevice_interactive()
+        mock_confirm.assert_has_calls([
+            mock.call("Do you want to configure QDevice?"),
+            mock.call("Please install the package manually and press 'y' to 
continue")
+            ])
 
     @mock.patch('crmsh.qdevice.QDevice')
     @mock.patch('crmsh.bootstrap.prompt_for_string')
+    @mock.patch('crmsh.qdevice.QDevice.check_package_installed')
+    @mock.patch('logging.Logger.info')
     @mock.patch('crmsh.bootstrap.confirm')
-    def test_configure_qdevice_interactive(self, mock_confirm, mock_prompt, 
mock_qdevice):
+    def test_configure_qdevice_interactive(self, mock_confirm, mock_info, 
mock_installed, mock_prompt, mock_qdevice):
         bootstrap._context = mock.Mock(yes_to_all=False)
         mock_confirm.return_value = True
         mock_prompt.side_effect = ["qnetd-node", 5403, "ffsplit", "lowest", 
"on", None]
@@ -899,15 +907,21 @@
         bootstrap.configure_qdevice_interactive()
         mock_confirm.assert_called_once_with("Do you want to configure 
QDevice?")
         mock_prompt.assert_has_calls([
-            mock.call("HOST or IP of the QNetd server to be used"),
-            mock.call("TCP PORT of QNetd server", default=5403),
-            mock.call("QNetd decision ALGORITHM (ffsplit/lms)", 
default="ffsplit"),
-            mock.call("QNetd TIE_BREAKER (lowest/highest/valid node id)", 
default="lowest"),
-            mock.call("Whether using TLS on QDevice/QNetd (on/off/required)", 
default="on"),
-            mock.call("Heuristics COMMAND to run with absolute path; For 
multiple commands, use \";\" to separate")
+            mock.call("HOST or IP of the QNetd server to be used",
+                valid_func=qdevice.QDevice.check_qnetd_addr),
+            mock.call("TCP PORT of QNetd server", default=5403,
+                valid_func=qdevice.QDevice.check_qdevice_port),
+            mock.call("QNetd decision ALGORITHM (ffsplit/lms)", 
default="ffsplit",
+                valid_func=qdevice.QDevice.check_qdevice_algo),
+            mock.call("QNetd TIE_BREAKER (lowest/highest/valid node id)", 
default="lowest",
+                valid_func=qdevice.QDevice.check_qdevice_tie_breaker),
+            mock.call("Whether using TLS on QDevice/QNetd (on/off/required)", 
default="on",
+                valid_func=qdevice.QDevice.check_qdevice_tls),
+            mock.call("Heuristics COMMAND to run with absolute path; For 
multiple commands, use \";\" to separate",
+                valid_func=qdevice.QDevice.check_qdevice_heuristics,
+                allow_empty=True)
             ])
         mock_qdevice.assert_called_once_with('qnetd-node', port=5403, 
algo='ffsplit', tie_breaker='lowest', tls='on', cmds=None, mode=None, 
is_stage=False)
-        mock_qdevice_inst.valid_qdevice_options.assert_called_once_with()
 
     @mock.patch('crmsh.utils.fatal')
     @mock.patch('crmsh.utils.is_qdevice_configured')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.4.1+20221203.9bb5442e/test/unittests/test_qdevice.py 
new/crmsh-4.4.1+20221207.84e6ea16/test/unittests/test_qdevice.py
--- old/crmsh-4.4.1+20221203.9bb5442e/test/unittests/test_qdevice.py    
2022-12-03 15:47:59.000000000 +0100
+++ new/crmsh-4.4.1+20221207.84e6ea16/test/unittests/test_qdevice.py    
2022-12-07 03:10:51.000000000 +0100
@@ -198,175 +198,105 @@
         res = self.qdevice_with_ip_cluster_node.qdevice_p12_on_cluster
         self.assertEqual(res, 
"/etc/corosync/qdevice/net/node1.com/qdevice-net-node.p12")
 
-    @mock.patch("crmsh.utils.package_is_installed")
-    def test_valid_qdevice_options_not_installed(self, mock_installed):
-        mock_installed.return_value = False
-        with self.assertRaises(ValueError) as err:
-            self.qdevice_with_ip.valid_qdevice_options()
-        self.assertEqual("Package \"corosync-qdevice\" not installed on this 
node", str(err.exception))
-        mock_installed.assert_called_once_with("corosync-qdevice")
-
-    @mock.patch("crmsh.utils.InterfacesInfo.ip_in_local")
-    @mock.patch("crmsh.utils.ping_node")
-    @mock.patch("socket.getaddrinfo")
-    @mock.patch("crmsh.utils.package_is_installed")
-    def test_valid_qdevice_options_remote_exception(self, mock_installed, 
mock_getaddrinfo, mock_ping, mock_ip_local):
-        mock_installed.return_value = True
-        mock_getaddrinfo.return_value = [(None, ("10.10.10.123",)),]
-        mock_ip_local.return_value = True
+    @mock.patch('crmsh.utils.check_port_open')
+    @mock.patch('crmsh.utils.InterfacesInfo.ip_in_local')
+    @mock.patch('crmsh.utils.ping_node')
+    @mock.patch('socket.getaddrinfo')
+    def test_check_qnetd_addr_port_error(self, mock_getaddrinfo, mock_ping, 
mock_in_local, mock_check):
+        mock_getaddrinfo.return_value = [(None, ("10.10.10.123",)),]
+        mock_in_local.return_value = False
+        mock_check.return_value = False
+        with self.assertRaises(ValueError) as err:
+            qdevice.QDevice.check_qnetd_addr("qnetd-node")
+        excepted_err_string = "ssh service on \"qnetd-node\" not available"
+        self.assertEqual(excepted_err_string, str(err.exception))
+
+    @mock.patch('crmsh.utils.InterfacesInfo.ip_in_local')
+    @mock.patch('crmsh.utils.ping_node')
+    @mock.patch('socket.getaddrinfo')
+    def test_check_qnetd_addr_local(self, mock_getaddrinfo, mock_ping, 
mock_in_local):
+        mock_getaddrinfo.return_value = [(None, ("10.10.10.123",)),]
+        mock_in_local.return_value = True
+        with self.assertRaises(ValueError) as err:
+            qdevice.QDevice.check_qnetd_addr("qnetd-node")
+        excepted_err_string = "host for qnetd must be a remote one"
+        self.assertEqual(excepted_err_string, str(err.exception))
 
-        with self.assertRaises(ValueError) as err:
-            self.qdevice_with_ip.valid_qdevice_options()
-        self.assertEqual("host for qnetd must be a remote one", 
str(err.exception))
-
-        mock_installed.assert_called_once_with("corosync-qdevice")
-        mock_getaddrinfo.assert_called_once_with("10.10.10.123", None)
-        mock_ping.assert_called_once_with("10.10.10.123")
-        mock_ip_local.assert_called_once_with("10.10.10.123")
-
-    @mock.patch("socket.getaddrinfo")
-    @mock.patch("crmsh.utils.package_is_installed")
-    def test_valid_qdevice_options_getaddrinfo_exception(self, mock_installed, 
mock_getaddrinfo):
-        mock_installed.return_value = True
+    @mock.patch('socket.getaddrinfo')
+    def test_check_qnetd_addr(self, mock_getaddrinfo):
         mock_getaddrinfo.side_effect = socket.error
-
         with self.assertRaises(ValueError) as err:
-            self.qdevice_with_hostname.valid_qdevice_options()
-        self.assertEqual("host \"node.qnetd\" is unreachable", 
str(err.exception))
-
-        mock_installed.assert_called_once_with("corosync-qdevice")
-        mock_getaddrinfo.assert_called_once_with("node.qnetd", None)
-
-    @mock.patch("crmsh.utils.check_port_open")
-    @mock.patch("crmsh.utils.InterfacesInfo.ip_in_local")
-    @mock.patch("crmsh.utils.ping_node")
-    @mock.patch("socket.getaddrinfo")
-    @mock.patch("crmsh.utils.package_is_installed")
-    def test_valid_qdevice_options_ssh_service_exception(self, mock_installed, 
mock_getaddrinfo,
-            mock_ping, mock_ip_local, mock_port_open):
-        mock_installed.return_value = True
-        mock_getaddrinfo.return_value = [(None, ("10.10.10.123",)),]
-        mock_ip_local.return_value = False
-        mock_port_open.return_value = False
+            qdevice.QDevice.check_qnetd_addr("qnetd-node")
+        excepted_err_string = "host \"qnetd-node\" is unreachable"
+        self.assertEqual(excepted_err_string, str(err.exception))
 
+    @mock.patch('crmsh.utils.valid_port')
+    def test_check_qdevice_port(self, mock_port):
+        mock_port.return_value = False
         with self.assertRaises(ValueError) as err:
-            self.qdevice_with_ip.valid_qdevice_options()
-        self.assertEqual("ssh service on \"10.10.10.123\" not available", 
str(err.exception))
-
-        mock_installed.assert_called_once_with("corosync-qdevice")
-        mock_getaddrinfo.assert_called_once_with("10.10.10.123", None)
-        mock_ping.assert_called_once_with("10.10.10.123")
-        mock_port_open.assert_called_once_with("10.10.10.123", 22)
-        mock_ip_local.assert_called_once_with("10.10.10.123")
-
-    @mock.patch("crmsh.utils.valid_port")
-    @mock.patch("crmsh.utils.check_port_open")
-    @mock.patch("crmsh.utils.InterfacesInfo.ip_in_local")
-    @mock.patch("crmsh.utils.ping_node")
-    @mock.patch("socket.getaddrinfo")
-    @mock.patch("crmsh.utils.package_is_installed")
-    def test_valid_qdevice_options_invalid_port_exception(self, 
mock_installed, mock_getaddrinfo,
-            mock_ping, mock_ip_local, mock_port_open, mock_valid_port):
-        mock_installed.return_value = True
-        mock_getaddrinfo.return_value = [(None, ("10.10.10.123",)),]
-        mock_ip_local.return_value = False
-        mock_port_open.return_value = True
-        mock_valid_port.return_value = False
+            qdevice.QDevice.check_qdevice_port("1")
+        excepted_err_string = "invalid qdevice port range(1024 - 65535)"
+        self.assertEqual(excepted_err_string, str(err.exception))
 
+    def test_check_qdevice_algo(self):
         with self.assertRaises(ValueError) as err:
-            self.qdevice_with_invalid_port.valid_qdevice_options()
-        self.assertEqual("invalid qdevice port range(1024 - 65535)", 
str(err.exception))
-
-        mock_installed.assert_called_once_with("corosync-qdevice")
-        mock_getaddrinfo.assert_called_once_with("10.10.10.123", None)
-        mock_ping.assert_called_once_with("10.10.10.123")
-        mock_ip_local.assert_called_once_with("10.10.10.123")
-        mock_port_open.assert_called_once_with("10.10.10.123", 22)
-        mock_valid_port.assert_called_once_with(100)
-
-    @mock.patch("crmsh.utils.valid_nodeid")
-    @mock.patch("crmsh.utils.valid_port")
-    @mock.patch("crmsh.utils.check_port_open")
-    @mock.patch("crmsh.utils.InterfacesInfo.ip_in_local")
-    @mock.patch("crmsh.utils.ping_node")
-    @mock.patch("socket.getaddrinfo")
-    @mock.patch("crmsh.utils.package_is_installed")
-    def test_valid_qdevice_options_invalid_nodeid_exception(self, 
mock_installed, mock_getaddrinfo,
-            mock_ping, mock_ip_local, mock_port_open, mock_valid_port, 
mock_valid_nodeid):
-        mock_installed.return_value = True
-        mock_getaddrinfo.return_value = [(None, ("10.10.10.123",)),]
-        mock_ip_local.return_value = False
-        mock_port_open.return_value = True
-        mock_valid_port.return_value = True
-        mock_valid_nodeid.return_value = False
+            qdevice.QDevice.check_qdevice_algo("1")
+        excepted_err_string = "invalid ALGORITHM choice: '1' (choose from 
'ffsplit', 'lms')"
+        self.assertEqual(excepted_err_string, str(err.exception))
 
+    def test_check_qdevice_tie_breaker(self):
         with self.assertRaises(ValueError) as err:
-            self.qdevice_with_invalid_tie_breaker.valid_qdevice_options()
-        self.assertEqual("invalid qdevice 
tie_breaker(lowest/highest/valid_node_id)", str(err.exception))
-
-        mock_installed.assert_called_once_with("corosync-qdevice")
-        mock_ip_local.assert_called_once_with("10.10.10.123")
-        mock_getaddrinfo.assert_called_once_with("10.10.10.123", None)
-        mock_ping.assert_called_once_with("10.10.10.123")
-        mock_port_open.assert_called_once_with("10.10.10.123", 22)
-        mock_valid_port.assert_called_once_with(5403)
-        mock_valid_nodeid.assert_called_once_with("wrong")
-
-    @mock.patch("crmsh.utils.valid_nodeid")
-    @mock.patch("crmsh.utils.valid_port")
-    @mock.patch("crmsh.utils.check_port_open")
-    @mock.patch("crmsh.utils.InterfacesInfo.ip_in_local")
-    @mock.patch("crmsh.utils.ping_node")
-    @mock.patch("socket.getaddrinfo")
-    @mock.patch("crmsh.utils.package_is_installed")
-    def test_valid_qdevice_options_invalid_cmds_relative_path(self, 
mock_installed, mock_getaddrinfo,
-            mock_ping, mock_ip_local, mock_port_open, mock_valid_port, 
mock_valid_nodeid):
-        mock_installed.return_value = True
-        mock_getaddrinfo.return_value = [(None, ("10.10.10.123",)),]
-        mock_ip_local.return_value = False
-        mock_port_open.return_value = True
-        mock_valid_port.return_value = True
-        mock_valid_nodeid.return_value = True
+            qdevice.QDevice.check_qdevice_tie_breaker("1")
+        excepted_err_string = "invalid qdevice 
tie_breaker(lowest/highest/valid_node_id)"
+        self.assertEqual(excepted_err_string, str(err.exception))
 
+    def test_check_qdevice_tls(self):
         with self.assertRaises(ValueError) as err:
-            
self.qdevice_with_invalid_cmds_relative_path.valid_qdevice_options()
-        self.assertEqual("commands for heuristics should be absolute path", 
str(err.exception))
+            qdevice.QDevice.check_qdevice_tls("1")
+        excepted_err_string = "invalid TLS choice: '1' (choose from 'on', 
'off', 'required')"
+        self.assertEqual(excepted_err_string, str(err.exception))
 
-        mock_installed.assert_called_once_with("corosync-qdevice")
-        mock_getaddrinfo.assert_called_once_with("10.10.10.123", None)
-        mock_ping.assert_called_once_with("10.10.10.123")
-        mock_ip_local.assert_called_once_with("10.10.10.123")
-        mock_port_open.assert_called_once_with("10.10.10.123", 22)
-        mock_valid_port.assert_called_once_with(5403)
-        mock_valid_nodeid.assert_not_called()
-
-    @mock.patch("crmsh.utils.valid_nodeid")
-    @mock.patch("crmsh.utils.valid_port")
-    @mock.patch("crmsh.utils.check_port_open")
-    @mock.patch("crmsh.utils.InterfacesInfo.ip_in_local")
-    @mock.patch("crmsh.utils.ping_node")
-    @mock.patch("socket.getaddrinfo")
-    @mock.patch("crmsh.utils.package_is_installed")
-    def test_valid_qdevice_options_invalid_cmds_not_exist(self, 
mock_installed, mock_getaddrinfo,
-            mock_ping, mock_ip_local, mock_port_open, mock_valid_port, 
mock_valid_nodeid):
-        mock_installed.return_value = True
-        mock_getaddrinfo.return_value = [(None, ("10.10.10.123",)),]
-        mock_ip_local.return_value = False
-        mock_port_open.return_value = True
-        mock_valid_port.return_value = True
-        mock_valid_nodeid.return_value = True
+    def test_check_qdevice_hm(self):
+        with self.assertRaises(ValueError) as err:
+            qdevice.QDevice.check_qdevice_heuristics_mode("1")
+        excepted_err_string = "invalid MODE choice: '1' (choose from 'on', 
'sync', 'off')"
+        self.assertEqual(excepted_err_string, str(err.exception))
 
+    def test_check_qdevice_he_path_error(self):
         with self.assertRaises(ValueError) as err:
-            self.qdevice_with_invalid_cmds_not_exist.valid_qdevice_options()
-        self.assertEqual("command /not_exist not exist", str(err.exception))
+            qdevice.QDevice.check_qdevice_heuristics("command1")
+        excepted_err_string = "commands for heuristics should be absolute path"
+        self.assertEqual(excepted_err_string, str(err.exception))
 
+    @mock.patch('os.path.exists')
+    def test_check_qdevice_he_not_exist_erro(self, mock_exists):
+        mock_exists.return_value = False
+        with self.assertRaises(ValueError) as err:
+            qdevice.QDevice.check_qdevice_heuristics("/usr/bin/testst")
+        excepted_err_string = "command /usr/bin/testst not exist"
+        self.assertEqual(excepted_err_string, str(err.exception))
+    
+    @mock.patch('crmsh.utils.package_is_installed')
+    def test_check_package_installed(self, mock_installed):
+        mock_installed.return_value = False
+        with self.assertRaises(ValueError) as err:
+            qdevice.QDevice.check_package_installed("corosync-qdevice")
+        excepted_err_string = "Package \"corosync-qdevice\" not installed on 
this node"
+        self.assertEqual(excepted_err_string, str(err.exception))
+
+    @mock.patch('crmsh.qdevice.QDevice.check_qdevice_heuristics_mode')
+    @mock.patch('crmsh.qdevice.QDevice.check_qdevice_heuristics')
+    @mock.patch('crmsh.qdevice.QDevice.check_qdevice_tls')
+    @mock.patch('crmsh.qdevice.QDevice.check_qdevice_tie_breaker')
+    @mock.patch('crmsh.qdevice.QDevice.check_qdevice_algo')
+    @mock.patch('crmsh.qdevice.QDevice.check_qdevice_port')
+    @mock.patch('crmsh.qdevice.QDevice.check_qnetd_addr')
+    @mock.patch('crmsh.qdevice.QDevice.check_package_installed')
+    def test_valid_qdevice_options(self, mock_installed, mock_check_qnetd, 
mock_check_port,
+            mock_check_algo, mock_check_tie, mock_check_tls, mock_check_h, 
mock_check_hm):
+        self.qdevice_with_ip.valid_qdevice_options()
         mock_installed.assert_called_once_with("corosync-qdevice")
-        mock_getaddrinfo.assert_called_once_with("10.10.10.123", None)
-        mock_ping.assert_called_once_with("10.10.10.123")
-        mock_ip_local.assert_called_once_with("10.10.10.123")
-        mock_port_open.assert_called_once_with("10.10.10.123", 22)
-        mock_valid_port.assert_called_once_with(5403)
-        mock_valid_nodeid.assert_not_called()
+        mock_check_qnetd.assert_called_once_with("10.10.10.123")
 
     @mock.patch("crmsh.utils.package_is_installed")
     def test_valid_qnetd_not_installed(self, mock_installed):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.4.1+20221203.9bb5442e/test/unittests/test_sbd.py 
new/crmsh-4.4.1+20221207.84e6ea16/test/unittests/test_sbd.py
--- old/crmsh-4.4.1+20221203.9bb5442e/test/unittests/test_sbd.py        
2022-12-03 15:47:59.000000000 +0100
+++ new/crmsh-4.4.1+20221207.84e6ea16/test/unittests/test_sbd.py        
2022-12-07 03:10:51.000000000 +0100
@@ -383,7 +383,7 @@
         mock_confirm.return_value = True
         mock_no_overwrite.return_value = False
         mock_from_config.return_value = []
-        mock_prompt.side_effect = [None, "none"]
+        mock_prompt.return_value = "none"
 
         self.sbd_inst._get_sbd_device_interactive()
 
@@ -391,7 +391,7 @@
         mock_confirm.assert_called_once_with("Do you wish to use SBD?")
         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)
+            mock.call('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.utils.re_split_string')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.4.1+20221203.9bb5442e/test/unittests/test_utils.py 
new/crmsh-4.4.1+20221207.84e6ea16/test/unittests/test_utils.py
--- old/crmsh-4.4.1+20221203.9bb5442e/test/unittests/test_utils.py      
2022-12-03 15:47:59.000000000 +0100
+++ new/crmsh-4.4.1+20221207.84e6ea16/test/unittests/test_utils.py      
2022-12-07 03:10:51.000000000 +0100
@@ -1729,3 +1729,7 @@
 
 def test_handle_role_for_ocf_1_1_return_not_role():
     assert utils.handle_role_for_ocf_1_1("test", name='other') == "test"
+
+
+def test_compatible_role():
+    assert utils.compatible_role("Slave", "Unpromoted") is True

Reply via email to