Hello community,

here is the log from the commit of package crmsh for openSUSE:Factory checked 
in at 2019-12-16 15:20:21
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/crmsh (Old)
 and      /work/SRC/openSUSE:Factory/.crmsh.new.4691 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "crmsh"

Mon Dec 16 15:20:21 2019 rev:169 rq:757217 version:4.1.0+git.1576228931.ae559358

Changes:
--------
--- /work/SRC/openSUSE:Factory/crmsh/crmsh.changes      2019-12-10 
22:42:33.409806142 +0100
+++ /work/SRC/openSUSE:Factory/.crmsh.new.4691/crmsh.changes    2019-12-16 
15:20:28.531136492 +0100
@@ -1,0 +2,14 @@
+Fri Dec 13 09:33:44 UTC 2019 - xli...@suse.com
+
+- Update to version 4.1.0+git.1576228931.ae559358:
+  * Dev: bootstrap: support multi disk sbd configure
+
+-------------------------------------------------------------------
+Wed Dec 11 07:05:30 UTC 2019 - xli...@suse.com
+
+- Update to version 4.1.0+git.1576047267.d87652bb:
+  * Dev: behave: functional test for resource failcount set subcommand
+  * Low: unittest: add ut for utils.get_nodeid_from_name
+  * Fix: ui_resource: set resource failcount correctly(bsc#1144241)
+
+-------------------------------------------------------------------

Old:
----
  crmsh-4.1.0+git.1575875711.41d65be4.tar.bz2

New:
----
  crmsh-4.1.0+git.1576228931.ae559358.tar.bz2

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

Other differences:
------------------
++++++ crmsh.spec ++++++
--- /var/tmp/diff_new_pack.e2UqJ2/_old  2019-12-16 15:20:29.819135955 +0100
+++ /var/tmp/diff_new_pack.e2UqJ2/_new  2019-12-16 15:20:29.827135952 +0100
@@ -36,7 +36,7 @@
 Summary:        High Availability cluster command-line interface
 License:        GPL-2.0-or-later
 Group:          %{pkg_group}
-Version:        4.1.0+git.1575875711.41d65be4
+Version:        4.1.0+git.1576228931.ae559358
 Release:        0
 Url:            http://crmsh.github.io
 Source0:        %{name}-%{version}.tar.bz2

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.e2UqJ2/_old  2019-12-16 15:20:29.887135926 +0100
+++ /var/tmp/diff_new_pack.e2UqJ2/_new  2019-12-16 15:20:29.887135926 +0100
@@ -5,4 +5,4 @@
                 <param 
name="url">https://github.com/liangxin1300/crmsh.git</param>
               <param 
name="changesrevision">d8dc51b4cb34964aa72e918999ebc7f03b48f3c9</param></service><service
 name="tar_scm">
                 <param 
name="url">https://github.com/ClusterLabs/crmsh.git</param>
-              <param 
name="changesrevision">000f2a79d35ac8d8642f7c93cf75b6d002f3c6e9</param></service></servicedata>
\ No newline at end of file
+              <param 
name="changesrevision">ae559358d1933de123c43a2f86d2d588810d8045</param></service></servicedata>
\ No newline at end of file

++++++ crmsh-4.1.0+git.1575875711.41d65be4.tar.bz2 -> 
crmsh-4.1.0+git.1576228931.ae559358.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-4.1.0+git.1575875711.41d65be4/.travis.yml 
new/crmsh-4.1.0+git.1576228931.ae559358/.travis.yml
--- old/crmsh-4.1.0+git.1575875711.41d65be4/.travis.yml 2019-12-09 
08:15:11.000000000 +0100
+++ new/crmsh-4.1.0+git.1576228931.ae559358/.travis.yml 2019-12-13 
10:22:11.000000000 +0100
@@ -65,6 +65,12 @@
       script:
         - $FUNCTIONAL_TEST qdevice run validate
 
+    - name: "functional test for resource subcommand"
+      before_install:
+        - $FUNCTIONAL_TEST resource before_install
+      script:
+        - $FUNCTIONAL_TEST resource run
+
     - stage: delivery
       if: type != pull_request AND branch = master
       env:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.1.0+git.1575875711.41d65be4/crmsh/bootstrap.py 
new/crmsh-4.1.0+git.1576228931.ae559358/crmsh/bootstrap.py
--- old/crmsh-4.1.0+git.1575875711.41d65be4/crmsh/bootstrap.py  2019-12-09 
08:15:11.000000000 +0100
+++ new/crmsh-4.1.0+git.1576228931.ae559358/crmsh/bootstrap.py  2019-12-13 
10:22:11.000000000 +0100
@@ -1362,38 +1362,44 @@
             if not confirm("SBD is already configured to use %s - overwrite?" 
% (configured_dev)):
                 return
 
-        dev = ""
         dev_looks_sane = False
         while not dev_looks_sane:
-            dev = prompt_for_string('Path to storage device (e.g. 
/dev/disk/by-id/...), or "none"', r'none|\/.*', dev)
+            dev = prompt_for_string('Path to storage device (e.g. 
/dev/disk/by-id/...), or "none", use ";" as separator for multi path', 
r'none|\/.*')
             if dev == "none":
                 _context.diskless_sbd = True
                 init_sbd_diskless()
                 return
-            if not is_block_device(dev):
-                print("    That doesn't look like a block device", 
file=sys.stderr)
-                dev = ""
-            else:
-                warn("All data on {} will be destroyed!".format(dev))
-                if confirm('Are you sure you wish to use this device?'):
-                    dev_looks_sane = True
+            dev_list = dev.strip(';').split(';')
+            for dev_item in dev_list:
+                if not is_block_device(dev_item):
+                    print("    {} doesn't look like a block 
device".format(dev_item), file=sys.stderr)
+                    dev_looks_sane = False
+                    break
                 else:
-                    dev = ""
-        _context.sbd_device = dev
+                    warn("All data on {} will be destroyed!".format(dev_item))
+                    if confirm('Are you sure you wish to use this device?'):
+                        dev_looks_sane = True
+                    else:
+                        dev_looks_sane = False
+                        break
 
-    if not is_block_device(_context.sbd_device):
-        error("SBD device %s doesn't seem to exist" % (_context.sbd_device))
+        _context.sbd_device = dev_list
+
+    for dev in _context.sbd_device:
+        if not is_block_device(dev):
+            error("SBD device %s doesn't seem to exist" % (dev))
 
     # TODO: need to ensure watchdog is available
     # (actually, should work if watchdog unavailable, it'll just whine in the 
logs...)
     # TODO: what about timeouts for multipath devices?
     status_long('Initializing SBD...')
-    if not invoke("sbd -d %s create" % (_context.sbd_device)):
-        error("Failed to initialize SBD device %s" % (_context.sbd_device))
+    for dev in _context.sbd_device:
+        if not invoke("sbd -d %s create" % (dev)):
+            error("Failed to initialize SBD device %s" % (dev))
     status_done()
 
     utils.sysconfig_set(SYSCONFIG_SBD,
-                        SBD_DEVICE=_context.sbd_device,
+                        SBD_DEVICE=';'.join(_context.sbd_device),
                         SBD_PACEMAKER="yes",
                         SBD_STARTMODE="always",
                         SBD_DELAY_START="no",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.1.0+git.1575875711.41d65be4/crmsh/completers.py 
new/crmsh-4.1.0+git.1576228931.ae559358/crmsh/completers.py
--- old/crmsh-4.1.0+git.1575875711.41d65be4/crmsh/completers.py 2019-12-09 
08:15:11.000000000 +0100
+++ new/crmsh-4.1.0+git.1576228931.ae559358/crmsh/completers.py 2019-12-13 
10:22:11.000000000 +0100
@@ -44,7 +44,7 @@
 booleans = choice(['yes', 'no', 'true', 'false', 'on', 'off'])
 
 
-def resources(args):
+def resources(args=None):
     cib_el = xmlutil.resources_xml()
     if cib_el is None:
         return []
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.1.0+git.1575875711.41d65be4/crmsh/ui_cluster.py 
new/crmsh-4.1.0+git.1576228931.ae559358/crmsh/ui_cluster.py
--- old/crmsh-4.1.0+git.1575875711.41d65be4/crmsh/ui_cluster.py 2019-12-09 
08:15:11.000000000 +0100
+++ new/crmsh-4.1.0+git.1576228931.ae559358/crmsh/ui_cluster.py 2019-12-13 
10:22:11.000000000 +0100
@@ -243,8 +243,8 @@
         storage_group = optparse.OptionGroup(parser, "Storage configuration", 
"Options for configuring shared storage.")
         storage_group.add_option("-p", "--partition-device", 
dest="shared_device", metavar="DEVICE",
                                  help='Partition this shared storage device 
(only used in "storage" stage)')
-        storage_group.add_option("-s", "--sbd-device", dest="sbd_device", 
metavar="DEVICE",
-                                 help="Block device to use for SBD fencing")
+        storage_group.add_option("-s", "--sbd-device", dest="sbd_device", 
metavar="DEVICE", action="append",
+                                 help="Block device to use for SBD fencing, 
use \";\" as separator for multi path")
         storage_group.add_option("-o", "--ocfs2-device", dest="ocfs2_device", 
metavar="DEVICE",
                                  help='Block device to use for OCFS2 (only 
used in "vgfs" stage)')
         parser.add_option_group(storage_group)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.1.0+git.1575875711.41d65be4/crmsh/ui_resource.py 
new/crmsh-4.1.0+git.1576228931.ae559358/crmsh/ui_resource.py
--- old/crmsh-4.1.0+git.1575875711.41d65be4/crmsh/ui_resource.py        
2019-12-09 08:15:11.000000000 +0100
+++ new/crmsh-4.1.0+git.1576228931.ae559358/crmsh/ui_resource.py        
2019-12-13 10:22:11.000000000 +0100
@@ -203,7 +203,8 @@
         'get': "crm_resource --meta --resource '%s' --get-parameter '%s'",
     }
     rsc_failcount = {
-        'set': "crm_attribute -t status -n 'fail-count-%s' -N '%s' -v '%s' -d 
0",
+        'set': "crm_attribute -t status -n 'fail-count-%s' -N '%s' -v '%s'",
+        'set_p': "crm_attribute -t status -P 'fail-count-%s' -N '%s' -v '%s'",
         'delete': "crm_failcount -D -r %s -N %s",
         'show': "crm_failcount -G -r %s -N %s",
         'get': "crm_failcount -G -r %s -N %s",
@@ -483,11 +484,75 @@
 
     @command.wait
     @command.completers(compl.resources, _attrcmds, compl.nodes)
-    def do_failcount(self, context, rsc, cmd, node, value=None):
+    def do_failcount(self, context, rsc, cmd, node, value=None, 
operation=None, interval=None):
         """usage:
-        failcount <rsc> set <node> <value>
+        failcount <rsc> set <node> <value> [operation] [interval]
         failcount <rsc> delete <node>
         failcount <rsc> show <node>"""
+        def sec_to_ms(s):
+            return s + '000'
+
+        def ms_to_sec(m):
+            return m[:len(m)-3]
+
+        if rsc not in compl.resources():
+            context.fatal_error("Resource {} not exists in this 
cluster".format(rsc))
+        valid_cmd_list = ["set", "delete", "show"]
+        if cmd not in valid_cmd_list:
+            context.fatal_error("{} is not valid command(should be one of 
{})".format(cmd, valid_cmd_list))
+        nodeid = utils.get_nodeid_from_name(node)
+        if nodeid is None:
+            context.fatal_error("Node {} not in this cluster".format(node))
+
+        if cmd == "set":
+            # query the current failcount status
+            query_cmd = "cibadmin -Ql --xpath 
'/cib/status/node_state[@id='{}']'".format(nodeid)
+            rc, out, err = utils.get_stdout_stderr(query_cmd)
+            if rc != 0:
+                context.fatal_error(err)
+
+            # try to get failcount dict {operation:interval}
+            import re
+            failcount_res = 
re.findall(r'fail-count-{}#(.*)_([0-9]+)'.format(rsc), out)
+            if not failcount_res:
+                context.fatal_error("No failcount on node {} for resource 
{}".format(node, rsc))
+            failcount_dict = dict(failcount_res)
+
+            # validate for operation and interval
+            if operation and operation not in failcount_dict.keys():
+                context.fatal_error("Usage: failcount <rsc> set <node> <value> 
[operation] [interval]\n\
+                           Should specify operation between \"{}\"".format(' 
'.join(failcount_dict.keys())))
+            if (operation and interval) and (operation, sec_to_ms(interval)) 
not in failcount_res:
+                context.fatal_error("Usage: failcount <rsc> set <node> <value> 
[operation] [interval]\n\
+                           Should specify (operation, interval) between {}".\
+                           format([(op, ms_to_sec(inter)) for op, inter in 
failcount_res]))
+
+            # just one failcount entry
+            if len(failcount_res) == 1:
+                operation = failcount_res[0][0]
+                interval = failcount_dict[operation]
+                rsc = '{}#{}_{}'.format(rsc, operation, interval)
+
+            # multiple failcount entries for this resource and node
+            if len(failcount_res) > 1:
+                if operation and interval:
+                    rsc = '{}#{}_{}'.format(rsc, operation, 
sec_to_ms(interval))
+                elif int(value) == 0:
+                    # using '-P' option of 'crm_attribute' command
+                    cmd = "set_p"
+                    if operation:
+                        op_interval_str = '|'.join(['{}_{}'.format(operation, 
inter) for op, inter in failcount_res if op==operation])
+                    else:
+                        op_interval_str = '|'.join(['{}_{}'.format(op, inter) 
for op, inter in failcount_res])
+                    rsc = '{}#({})'.format(rsc, op_interval_str)
+                else:
+                    # value != 0
+                    if operation and len([op for op, _ in failcount_res if op 
== operation]) == 1:
+                        rsc = '{}#{}_{}'.format(rsc, operation, 
failcount_dict[operation])
+                    else:
+                        context.fatal_error("Should specify (operation, 
interval) between {}".
+                                format([(op, ms_to_sec(inter)) for op, inter 
in failcount_res]))
+
         return ui_utils.manage_attr(context.get_command_name(), 
self.rsc_failcount,
                                     rsc, cmd, node, value)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.1.0+git.1575875711.41d65be4/crmsh/ui_utils.py 
new/crmsh-4.1.0+git.1576228931.ae559358/crmsh/ui_utils.py
--- old/crmsh-4.1.0+git.1575875711.41d65be4/crmsh/ui_utils.py   2019-12-09 
08:15:11.000000000 +0100
+++ new/crmsh-4.1.0+git.1576228931.ae559358/crmsh/ui_utils.py   2019-12-13 
10:22:11.000000000 +0100
@@ -22,7 +22,7 @@
     def sanity_check(arg):
         if not utils.is_name_sane(arg):
             raise ValueError("Expected valid name, got '%s'" % (arg))
-    if subcmd == 'set':
+    if subcmd in ['set', 'set_p']:
         if value is None:
             raise ValueError("Missing value argument to set")
         sanity_check(rsc)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-4.1.0+git.1575875711.41d65be4/crmsh/utils.py 
new/crmsh-4.1.0+git.1576228931.ae559358/crmsh/utils.py
--- old/crmsh-4.1.0+git.1575875711.41d65be4/crmsh/utils.py      2019-12-09 
08:15:11.000000000 +0100
+++ new/crmsh-4.1.0+git.1576228931.ae559358/crmsh/utils.py      2019-12-13 
10:22:11.000000000 +0100
@@ -2253,4 +2253,15 @@
 def is_unicast():
     from . import corosync
     return corosync.get_value("totem.transport") == "udpu"
+
+
+def get_nodeid_from_name(name):
+    rc, out = get_stdout('crm_node -l')
+    if rc != 0:
+        return None
+    res = re.search(r'^([0-9]+) {} '.format(name), out, re.M)
+    if res:
+        return res.group(1)
+    else:
+        return None
 # vim:ts=4:sw=4:et:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-4.1.0+git.1575875711.41d65be4/data-manifest 
new/crmsh-4.1.0+git.1576228931.ae559358/data-manifest
--- old/crmsh-4.1.0+git.1575875711.41d65be4/data-manifest       2019-12-09 
08:15:11.000000000 +0100
+++ new/crmsh-4.1.0+git.1576228931.ae559358/data-manifest       2019-12-13 
10:22:11.000000000 +0100
@@ -71,6 +71,7 @@
 test/features/qdevice_options.feature
 test/features/qdevice_setup_remove.feature
 test/features/qdevice_validate.feature
+test/features/resource_failcount.feature
 test/features/steps/__init__.py
 test/features/steps/step_implenment.py
 test/features/steps/utils.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh-4.1.0+git.1575875711.41d65be4/doc/crm.8.adoc 
new/crmsh-4.1.0+git.1576228931.ae559358/doc/crm.8.adoc
--- old/crmsh-4.1.0+git.1575875711.41d65be4/doc/crm.8.adoc      2019-12-09 
08:15:11.000000000 +0100
+++ new/crmsh-4.1.0+git.1576228931.ae559358/doc/crm.8.adoc      2019-12-13 
10:22:11.000000000 +0100
@@ -2011,10 +2011,13 @@
 ==== `failcount`
 
 Show/edit/delete the failcount of a resource.
+When `set` a non-zero value, `operation` and `interval` should be 
+provided when multiple operation failcount entries exist.
+`interval` is a value in seconds.
 
 Usage:
 ...............
-failcount <rsc> set <node> <value>
+failcount <rsc> set <node> <value> [operation] [interval]
 failcount <rsc> delete <node>
 failcount <rsc> show <node>
 ...............
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.1.0+git.1575875711.41d65be4/test/features/environment.py 
new/crmsh-4.1.0+git.1576228931.ae559358/test/features/environment.py
--- old/crmsh-4.1.0+git.1575875711.41d65be4/test/features/environment.py        
2019-12-09 08:15:11.000000000 +0100
+++ new/crmsh-4.1.0+git.1576228931.ae559358/test/features/environment.py        
2019-12-13 10:22:11.000000000 +0100
@@ -11,6 +11,10 @@
         return None
 
 
+def resource_cleanup():
+    utils.get_stdout_stderr('crm resource cleanup')
+
+
 def before_step(context, step):
     context.logger = logging.getLogger("Step:{}".format(step.name))
 
@@ -20,6 +24,7 @@
     if tag == "clean":
         online_nodes = get_online_nodes()
         if online_nodes:
+            resource_cleanup()
             try:
                 parallax.parallax_call(online_nodes, 'crm cluster stop')
             except ValueError as err:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.1.0+git.1575875711.41d65be4/test/features/resource_failcount.feature
 
new/crmsh-4.1.0+git.1576228931.ae559358/test/features/resource_failcount.feature
--- 
old/crmsh-4.1.0+git.1575875711.41d65be4/test/features/resource_failcount.feature
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/crmsh-4.1.0+git.1576228931.ae559358/test/features/resource_failcount.feature
    2019-12-13 10:22:11.000000000 +0100
@@ -0,0 +1,64 @@
+@resource
+Feature: Use "crm resource failcount" to manage failcounts
+
+  Tag @clean means need to stop cluster service if the service is available
+
+  Background: Setup one node cluster and configure a Dummy resource
+    Given     Cluster service is "stopped" on "hanode1"
+    When      Run "crm cluster init -y --no-overwrite-sshkey" on "hanode1"
+    Then      Cluster service is "started" on "hanode1"
+    When      Run "crm configure primitive d Dummy op monitor interval=3s" on 
"hanode1"
+    Then      Resource "d" type "Dummy" is "Started"
+
+  @clean
+  Scenario: Validation, input the wrong parameters
+    When    Try "crm resource failcount ddd show hanode1"
+    Then    Except "ERROR: resource.failcount: Resource ddd not exists in this 
cluster"
+    When    Try "crm resource failcount d showss hanode1"
+    Then    Except "ERROR: resource.failcount: showss is not valid 
command(should be one of ['set', 'delete', 'show'])"
+    When    Try "crm resource failcount d set hanode11 0"
+    Then    Except "ERROR: resource.failcount: Node hanode11 not in this 
cluster"
+    When    Try "crm resource failcount d set hanode1 0"
+    Then    Except "ERROR: resource.failcount: No failcount on node hanode1 
for resource d"
+
+  @clean
+  Scenario: Set the failcount to 0
+    When    Run "rm -f /run/resource-agents/Dummy-d.state" on "hanode1"
+    And     Wait "5" seconds
+    Then    Resource "d" failcount on "hanode1" is "1"
+    When    Run "crm resource failcount d set hanode1 0" on "hanode1"
+    Then    Resource "d" failcount on "hanode1" is "0"
+
+  @clean
+  Scenario: Set multiple failcounts to 0
+    When    Run "sed -i -e '/rm \${OCF_RESKEY_state}/a\' -e "else\nreturn 
\$OCF_ERR_GENERIC" /usr/lib/ocf/resource.d/heartbeat/Dummy" on "hanode1"
+    And     Run "rm -f /run/resource-agents/Dummy-d.state" on "hanode1"
+    And     Wait "5" seconds
+    Then    Resource "d" failcount on "hanode1" is "INFINITY"
+        """
+        now have two failcount entries, one is monitor, another is stop
+       """
+    When    Run "crm resource failcount d set hanode1 0" on "hanode1"
+        """
+        set all failcounts to 0
+       """
+    Then    Resource "d" failcount on "hanode1" is "0"
+    When    Run "crm resource cleanup" on "hanode1"
+    And     Wait "5" seconds
+    And     Run "rm -f /run/resource-agents/Dummy-d.state" on "hanode1"
+    And     Wait "5" seconds
+    Then    Resource "d" failcount on "hanode1" is "INFINITY"
+        """
+        now have two failcount entries, one is monitor, another is stop
+       """
+    When    Run "crm resource failcount d set hanode1 0 stop" on "hanode1"
+        """
+        set stop failcounts to 0
+       """
+    Then    Resource "d" failcount on "hanode1" is "1"
+    When    Run "crm resource failcount d set hanode1 0 monitor" on "hanode1"
+        """
+        set monitor failcounts to 0
+       """
+    Then    Resource "d" failcount on "hanode1" is "0"
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.1.0+git.1575875711.41d65be4/test/features/steps/step_implenment.py 
new/crmsh-4.1.0+git.1576228931.ae559358/test/features/steps/step_implenment.py
--- 
old/crmsh-4.1.0+git.1575875711.41d65be4/test/features/steps/step_implenment.py  
    2019-12-09 08:15:11.000000000 +0100
+++ 
new/crmsh-4.1.0+git.1576228931.ae559358/test/features/steps/step_implenment.py  
    2019-12-13 10:22:11.000000000 +0100
@@ -1,4 +1,5 @@
 import re
+import time
 from behave import given, when, then
 from crmsh import corosync, parallax
 from utils import check_cluster_state, check_service_state, online, 
run_command, me, \
@@ -37,6 +38,11 @@
     run_command(context, cmd, err_record=True)
 
 
+@when('Wait "{second}" seconds')
+def step_impl(context, second):
+    time.sleep(int(second))
+
+
 @then('Except "{msg}"')
 def step_impl(context, msg):
     assert context.command_error_output == msg
@@ -109,3 +115,28 @@
     out = run_command(context, "sed -n -e '/quorum/,/^}/ p' 
/etc/corosync/corosync.conf")
     if out:
         context.logger.info("\n{}".format(out))
+
+
+@then('Resource "{res}" type "{res_type}" is "{state}"')
+def step_impl(context, res, res_type, state):
+    try_count = 0
+    result = None
+    while try_count < 5:
+        time.sleep(1)
+        out = run_command(context, "crm_mon -1")
+        if out:
+            result = re.search(r'\s{}\s+.*:{}\):\s+{} '.format(res, res_type, 
state), out)
+            if not result:
+                try_count += 1
+            else:
+                break
+    assert result is not None
+
+
+@then('Resource "{res}" failcount on "{node}" is "{number}"')
+def step_impl(context, res, node, number):
+    cmd = "crm resource failcount {} show {}".format(res, node)
+    out = run_command(context, cmd)
+    if out:
+        result = re.search(r'name=fail-count-{} value={}'.format(res, number), 
out)
+        assert result is not None
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.1.0+git.1575875711.41d65be4/test/run-in-travis.sh 
new/crmsh-4.1.0+git.1576228931.ae559358/test/run-in-travis.sh
--- old/crmsh-4.1.0+git.1575875711.41d65be4/test/run-in-travis.sh       
2019-12-09 08:15:11.000000000 +0100
+++ new/crmsh-4.1.0+git.1576228931.ae559358/test/run-in-travis.sh       
2019-12-13 10:22:11.000000000 +0100
@@ -32,7 +32,7 @@
                configure
                make_install
                exit $?;;
-       bootstrap|qdevice|hb_report)
+       bootstrap|qdevice|hb_report|resource)
                functional_tests $1 $2
                exit $?;;
        *)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/crmsh-4.1.0+git.1575875711.41d65be4/test/unittests/test_utils.py 
new/crmsh-4.1.0+git.1576228931.ae559358/test/unittests/test_utils.py
--- old/crmsh-4.1.0+git.1575875711.41d65be4/test/unittests/test_utils.py        
2019-12-09 08:15:11.000000000 +0100
+++ new/crmsh-4.1.0+git.1576228931.ae559358/test/unittests/test_utils.py        
2019-12-13 10:22:11.000000000 +0100
@@ -6,13 +6,52 @@
 
 import os
 import socket
+import re
 from unittest import mock
+from nose.tools import eq_
 from itertools import chain
 from crmsh import utils
 from crmsh import config
 from crmsh import tmpfiles
 
 
+@mock.patch('re.search')
+@mock.patch('crmsh.utils.get_stdout')
+def test_get_nodeid_from_name_run_None1(mock_get_stdout, mock_re_search):
+    mock_get_stdout.return_value = (1, None)
+    mock_re_search_inst = mock.Mock()
+    mock_re_search.return_value = mock_re_search_inst
+    res = utils.get_nodeid_from_name("node1")
+    eq_(res, None)
+    mock_get_stdout.assert_called_once_with('crm_node -l')
+    mock_re_search.assert_not_called()
+
+
+@mock.patch('re.search')
+@mock.patch('crmsh.utils.get_stdout')
+def test_get_nodeid_from_name_run_None2(mock_get_stdout, mock_re_search):
+    mock_get_stdout.return_value = (0, "172167901 node1 member\n172168231 
node2 member")
+    mock_re_search.return_value = None
+    res = utils.get_nodeid_from_name("node111")
+    eq_(res, None)
+    mock_get_stdout.assert_called_once_with('crm_node -l')
+    mock_re_search.assert_called_once_with(r'^([0-9]+) node111 ', 
mock_get_stdout.return_value[1], re.M)
+
+
+@mock.patch('re.search')
+@mock.patch('crmsh.utils.get_stdout')
+def test_get_nodeid_from_name(mock_get_stdout, mock_re_search):
+    mock_get_stdout.return_value = (0, "172167901 node1 member\n172168231 
node2 member")
+    mock_re_search_inst = mock.Mock()
+    mock_re_search.return_value = mock_re_search_inst
+    mock_re_search_inst.group.return_value = '172168231'
+    res = utils.get_nodeid_from_name("node2")
+    eq_(res, '172168231')
+    mock_get_stdout.assert_called_once_with('crm_node -l')
+    mock_re_search.assert_called_once_with(r'^([0-9]+) node2 ', 
mock_get_stdout.return_value[1], re.M)
+    mock_re_search_inst.group.assert_called_once_with(1)
+
+
 def test_check_ssh_passwd_need_True():
     with mock.patch('crmsh.utils.get_stdout_stderr') as mock_get_stdout_stderr:
         mock_get_stdout_stderr.side_effect = [(0, None, None), (1, None, None)]


Reply via email to