Hello community,

here is the log from the commit of package crmsh for openSUSE:Factory checked 
in at 2014-03-18 14:13:40
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/crmsh (Old)
 and      /work/SRC/openSUSE:Factory/.crmsh.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "crmsh"

Changes:
--------
--- /work/SRC/openSUSE:Factory/crmsh/crmsh.changes      2014-03-10 
12:31:24.000000000 +0100
+++ /work/SRC/openSUSE:Factory/.crmsh.new/crmsh.changes 2014-03-18 
14:13:42.000000000 +0100
@@ -1,0 +2,21 @@
+Mon Mar 17 12:53:00 UTC 2014 - kgronl...@suse.com
+
+- medium: command: Propagate error from auto-commit (bnc#868533)
+- medium: crm_pkg: Add --no-refresh to zypper commands
+- medium: scripts: Improved setup in cluster init/add (bnc#868008)
+- upstream cs: 053db64d9cca
+
+-------------------------------------------------------------------
+Thu Mar 13 14:51:40 UTC 2014 - kgronl...@suse.com
+
+- medium: scripts: Improved debug output from cluster scripts (bnc#866636)
+- medium: scripts: corosync uses mcastport - 1 (bnc#868008)
+- medium: xmlutil: fencing-topology used broken comparison (bnc#866639)
+- medium: utils: ask() did not respect force flag in all cases (bnc#868007)
+- low: utils: Nicer warning when crm_simulate fails
+- low: ui: Don't call nonexistent function on unsupported cluster stack
+- low: main: Better descriptions for -d and -R flags.
+- low: parse: More liberal parsing of role assignment in constraint rules
+- upstream cs: dbf8e3b7538 
+
+-------------------------------------------------------------------

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

Other differences:
------------------
++++++ crmsh.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/.hg_archival.txt new/crmsh/.hg_archival.txt
--- old/crmsh/.hg_archival.txt  2014-03-08 14:13:37.000000000 +0100
+++ new/crmsh/.hg_archival.txt  2014-03-17 13:09:30.000000000 +0100
@@ -1,5 +1,5 @@
 repo: 13c3bd69e935090cd25213c474cafc3f01b5910b
-node: 4bb659cf287e386310ecb8894f2c4f5ab95755d4
+node: 053db64d9cca33de61567a117573c69079345628
 branch: default
 latesttag: 1.2.6-rc1
-latesttagdistance: 462
+latesttagdistance: 476
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/modules/command.py new/crmsh/modules/command.py
--- old/crmsh/modules/command.py        2014-03-08 14:13:37.000000000 +0100
+++ new/crmsh/modules/command.py        2014-03-17 13:09:30.000000000 +0100
@@ -247,8 +247,9 @@
         TODO: Implement full cd navigation. cd ../configure, for example
         Also implement ls to list commands / levels from current location
         '''
-        context.up()
+        ok = context.up()
         context.save_stack()
+        return ok
 
     @help('''List levels and commands
 Lists the available sublevels and commands
@@ -289,22 +290,26 @@
 ''')
     @completer(_cd_completer)
     def do_cd(self, context, optarg='..'):
+        ok = True
         path = optarg.split('/', 1)
         if len(path) == 1:
             path = path[0]
             if path == '..':
-                context.up()
+                ok = context.up()
             elif path == '.' or not path:
-                return
+                return ok
             else:
                 info = self.get_child(path)
                 if not info or not info.level:
                     context.fatal_error("No such level: " + path)
                 context.enter_level(info.level)
         else:
-            self.do_cd(context, path[0])
-            self.do_cd(context, path[1])
+            if not self.do_cd(context, path[0]):
+                ok = False
+            if not self.do_cd(context, path[1]):
+                ok = False
         context.save_stack()
+        return True
 
     @alias('bye', 'exit')
     @help('''Exit the interactive shell
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/modules/main.py new/crmsh/modules/main.py
--- old/crmsh/modules/main.py   2014-03-08 14:13:37.000000000 +0100
+++ new/crmsh/modules/main.py   2014-03-17 13:09:30.000000000 +0100
@@ -180,13 +180,12 @@
         Print Pacemaker version and build information (Mercurial Hg
         changeset hash).
 
-    -R, --regression-tests::
-        Run in the regression test mode. Used mainly by the
-        regression testing suite.
-
     -d, --debug::
-        Print some debug information. Used by developers. [Not yet
-        refined enough to print useful information for other users.]
+        Print verbose debugging information.
+
+    -R, --regression-tests::
+        Enables extra verbose trace logging used by the regression
+        tests. Logs all external calls made by crmsh.
 
     --scriptdir='DIR'::
         Extra directory where crm looks for cluster scripts. Can be
@@ -217,7 +216,7 @@
 
 def set_interactive():
     '''Set the interactive option only if we're on a tty.'''
-    if sys.stdin.isatty():
+    if utils.can_ask():
         options.interactive = True
 
 
@@ -422,7 +421,7 @@
 
         load_rc(context, userdir.RC_FILE)
         atexit.register(exit_handler)
-        options.interactive = sys.stdin.isatty()
+        options.interactive = utils.can_ask()
         if not options.interactive:
             err_buf.reset_lineno()
             options.batch = True
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/modules/parse.py new/crmsh/modules/parse.py
--- old/crmsh/modules/parse.py  2014-03-08 14:13:37.000000000 +0100
+++ new/crmsh/modules/parse.py  2014-03-17 13:09:30.000000000 +0100
@@ -459,7 +459,7 @@
 
 class ConstraintParser(BaseParser):
     _SCORE_RE = re.compile(r"([^:]+):$")
-    _ROLE_RE = re.compile(r"\$role=(.+)$", re.IGNORECASE)
+    _ROLE_RE = re.compile(r"\$?role=(.+)$", re.IGNORECASE)
     _ROLE2_RE = re.compile(r"role=(.+)$", re.IGNORECASE)
     _BOOLOP_RE = re.compile(r'(%s)$' % ('|'.join(vars.boolean_ops)), 
re.IGNORECASE)
     _UNARYOP_RE = re.compile(r'(%s)$' % ('|'.join(vars.unary_ops)), 
re.IGNORECASE)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/modules/scripts.py new/crmsh/modules/scripts.py
--- old/crmsh/modules/scripts.py        2014-03-08 14:13:37.000000000 +0100
+++ new/crmsh/modules/scripts.py        2014-03-17 13:09:30.000000000 +0100
@@ -67,6 +67,20 @@
     return "Bad configuration option" not in err
 
 
+def _pssh_call(hosts, cmd, opts):
+    "pssh.call with debug logging"
+    if config.core.debug or options.regression_tests:
+        err_buf.debug("pssh.call(%s, %s)" % (repr(hosts), cmd))
+    return pssh.call(hosts, cmd, opts)
+
+
+def _pssh_copy(hosts, src, dst, opts):
+    "pssh.copy with debug logging"
+    if config.core.debug or options.regression_tests:
+        err_buf.debug("pssh.copy(%s, %s, %s)" % (repr(hosts), src, dst))
+    return pssh.copy(hosts, src, dst, opts)
+
+
 def _generate_workdir_name():
     '''
     Generate a temporary folder name to use while
@@ -250,7 +264,7 @@
         'PasswordAuthentication=no',
         'StrictHostKeyChecking=no',
         'ControlPersist=no']
-    if config.core.debug:
+    if options.regression_tests:
         opts.ssh_extra += ['-vvv']
     return opts
 
@@ -322,6 +336,9 @@
             hosts2.append((h, p, u))
         else:
             local_node = (h, p, u)
+    err_buf.debug("Local node: %s, Remote hosts: %s" % (
+        local_node,
+        ', '.join(h[0] for h in hosts2)))
     return local_node, hosts2
 
 
@@ -366,22 +383,24 @@
 def _create_remote_workdirs(hosts, path, opts):
     "Create workdirs on remote hosts"
     ok = True
-    for host, result in pssh.call(hosts,
-                                  "mkdir -p %s" % (os.path.dirname(path)),
-                                  opts).iteritems():
+    for host, result in _pssh_call(hosts,
+                                   "mkdir -p %s" % (os.path.dirname(path)),
+                                   opts).iteritems():
         if isinstance(result, pssh.Error):
-            err_buf.error("[%s]: %s" % (host, result))
+            err_buf.error("[%s]: Start: %s" % (host, result))
             ok = False
     if not ok:
-        raise ValueError("Failed to create working folders, aborting.")
+        msg = "Failed to connect to one or more of these hosts via SSH: %s" % (
+            ', '.join(h[0] for h in hosts))
+        raise ValueError(msg)
 
 
 def _copy_to_remote_dirs(hosts, path, opts):
     "Copy a local folder to same location on remote hosts"
     ok = True
-    for host, result in pssh.copy(hosts,
-                                  path,
-                                  path, opts).iteritems():
+    for host, result in _pssh_copy(hosts,
+                                   path,
+                                   path, opts).iteritems():
         if isinstance(result, pssh.Error):
             err_buf.error("[%s]: %s" % (host, result))
             ok = False
@@ -394,7 +413,7 @@
     Copy src to dst both locally and remotely
     """
     ok = True
-    ret = pssh.copy(hosts, src, dst, opts)
+    ret = _pssh_copy(hosts, src, dst, opts)
     for host, result in ret.iteritems():
         if isinstance(result, pssh.Error):
             err_buf.error("[%s]: %s" % (host, result))
@@ -412,7 +431,7 @@
                 else:
                     shutil.copytree(src, dst)
         except (IOError, OSError, shutil.Error), e:
-            err_buf.error("[%s]: %s" % (utils.this_node(), e))
+            err_buf.error("[%s]: %s" % (local_node, e))
             ok = False
     return ok
 
@@ -577,9 +596,9 @@
         else:
             self.opts.input_stream = None
 
-        for host, result in pssh.call(self.hosts,
-                                      cmdline,
-                                      self.opts).iteritems():
+        for host, result in _pssh_call(self.hosts,
+                                       cmdline,
+                                       self.opts).iteritems():
             if isinstance(result, pssh.Error):
                 self.error("[%s]: %s", host, result)
                 ok = False
@@ -617,34 +636,54 @@
         return out
 
 
-def _run_cleanup(hosts, workdir, opts):
+def _cleanup_local(workdir):
+    "clean up the local tmp dir"
+    if workdir and os.path.isdir(workdir):
+        cleanscript = os.path.join(workdir, 'crm_clean.py')
+        if os.path.isfile(cleanscript):
+            if subprocess.call([cleanscript, workdir], shell=False) != 0:
+                shutil.rmtree(workdir)
+        else:
+            shutil.rmtree(workdir)
+
+
+def _print_output(host, rc, out, err):
+    "Print the output from a process that ran on host"
+    if out:
+        err_buf.ok("[%s]: %s" % (host, out))
+    if err:
+        err_buf.error("[%s]: %s" % (host, err))
+
+
+def _run_cleanup(local_node, hosts, workdir, opts):
     "Clean up after the cluster script"
     if hosts and workdir:
-        for host, result in pssh.call(hosts,
-                                      "%s %s" % (os.path.join(workdir, 
'crm_clean.py'),
-                                                 workdir),
-                                      opts).iteritems():
+        cleanscript = os.path.join(workdir, 'crm_clean.py')
+        for host, result in _pssh_call(hosts,
+                                       "%s %s" % (cleanscript,
+                                                  workdir),
+                                       opts).iteritems():
             if isinstance(result, pssh.Error):
-                err_buf.warning("[%s]: Failed to clean up %s: %s" % (host, 
workdir, result))
+                err_buf.debug("[%s]: Failed to clean up %s" % (host, workdir))
+                err_buf.error("[%s]: Clean: %s" % (host, result))
             else:
-                rc, out, err = result
-                print out
-    if workdir and os.path.isdir(workdir):
-        shutil.rmtree(workdir)
+                _print_output(host, *result)
+    _cleanup_local(workdir)
 
 
-def _print_debug(hosts, workdir, opts):
+def _print_debug(local_node, hosts, workdir, opts):
     "Print debug output (if any)"
     dbglog = os.path.join(workdir, 'crm_script.debug')
-    for host, result in pssh.call(hosts,
-                                  "[ -f '%s' ] && cat '%s'" % (dbglog, dbglog),
-                                  opts).iteritems():
+    for host, result in _pssh_call(hosts,
+                                   "[ -f '%s' ] && cat '%s'" % (dbglog, 
dbglog),
+                                   opts).iteritems():
         if isinstance(result, pssh.Error):
             err_buf.error("[%s]: %s" % (host, result))
         else:
-            rc, out, err = result
-            print out
-            print err
+            _print_output(host, *result)
+    if os.path.isfile(dbglog):
+        f = open(dbglog).read()
+        err_buf.ok("[%s]: %s" % (local_node, f))
 
 
 def run(name, args):
@@ -689,6 +728,6 @@
         raise ValueError("Internal error while running %s: %s" % (name, e))
     finally:
         if not config.core.debug:
-            _run_cleanup(hosts, workdir, opts)
+            _run_cleanup(local_node, hosts, workdir, opts)
         else:
-            _print_debug(hosts, workdir, opts)
+            _print_debug(local_node, hosts, workdir, opts)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/modules/ui_assist.py 
new/crmsh/modules/ui_assist.py
--- old/crmsh/modules/ui_assist.py      2014-03-08 14:13:37.000000000 +0100
+++ new/crmsh/modules/ui_assist.py      2014-03-17 13:09:30.000000000 +0100
@@ -83,7 +83,7 @@
         print "The following elements will be created:"
         print "   * Colocation constraint, ID: %s" % (constraint_name)
         print "   * Dummy resource, ID: %s" % (dummy_name)
-        if not sys.stdin.isatty() or utils.ask("Create resources?"):
+        if not utils.can_ask() or utils.ask("Create resources?"):
             cib_factory.create_object('primitive', dummy_name, 
'ocf:heartbeat:Dummy')
             colo = ['colocation', constraint_name, 'inf:', '(']
             colo.extend(nodes)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/modules/ui_cluster.py 
new/crmsh/modules/ui_cluster.py
--- old/crmsh/modules/ui_cluster.py     2014-03-08 14:13:37.000000000 +0100
+++ new/crmsh/modules/ui_cluster.py     2014-03-17 13:09:30.000000000 +0100
@@ -45,7 +45,7 @@
     def requires(self):
         stack = utils.cluster_stack()
         if len(stack) > 0 and stack != 'corosync':
-            err_buf.warn("Unsupported cluster stack %s detected." % (stack))
+            err_buf.warning("Unsupported cluster stack %s detected." % (stack))
             return False
         return True
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/modules/ui_configure.py 
new/crmsh/modules/ui_configure.py
--- old/crmsh/modules/ui_configure.py   2014-03-08 14:13:37.000000000 +0100
+++ new/crmsh/modules/ui_configure.py   2014-03-17 13:09:30.000000000 +0100
@@ -792,9 +792,11 @@
         return cib_factory.has_cib_changed()
 
     def end_game(self, no_questions_asked=False):
+        ok = True
         if cib_factory.has_cib_changed():
             if no_questions_asked or not options.interactive:
-                self._commit()
+                ok = self._commit()
             elif utils.ask("There are changes pending. Do you want to commit 
them?"):
-                self._commit()
+                ok = self._commit()
         cib_factory.reset()
+        return ok
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/modules/ui_context.py 
new/crmsh/modules/ui_context.py
--- old/crmsh/modules/ui_context.py     2014-03-08 14:13:37.000000000 +0100
+++ new/crmsh/modules/ui_context.py     2014-03-17 13:09:30.000000000 +0100
@@ -92,7 +92,7 @@
             common_err("%s: %s" % (self.get_qualified_name(), msg))
             rv = False
         if cmd or (rv is False):
-            self._back_out()
+            rv = self._back_out() and rv
         return rv
 
     def complete(self, line):
@@ -232,7 +232,7 @@
 
     def _set_interactive(self):
         '''Set the interactive option only if we're on a tty.'''
-        if sys.stdin.isatty():
+        if utils.can_ask():
             options.interactive = True
 
     def execute_command(self):
@@ -293,17 +293,21 @@
         '''
         Navigate up in the levels hierarchy
         '''
+        ok = True
         if len(self.stack) > 1:
-            self.current_level().end_game(no_questions_asked=self._in_transit)
+            ok = 
self.current_level().end_game(no_questions_asked=self._in_transit) is not False
             self.stack.pop()
             self.clear_readline_cache()
+        return ok
 
     def _back_out(self):
         '''
         Restore the stack to the marked position
         '''
+        ok = True
         while self._mark > 0 and len(self.stack) > self._mark:
-            self.up()
+            ok = self.up() and ok
+        return ok
 
     def save_stack(self):
         self._mark = len(self.stack)
@@ -312,9 +316,11 @@
         '''
         Exit from the top level
         '''
-        self.current_level().end_game()
+        ok = self.current_level().end_game()
         if options.interactive and not options.batch:
             print "bye"
+        if ok is False and rc == 0:
+            rc = 1
         sys.exit(rc)
 
     def level_name(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/modules/ui_corosync.py 
new/crmsh/modules/ui_corosync.py
--- old/crmsh/modules/ui_corosync.py    2014-03-08 14:13:37.000000000 +0100
+++ new/crmsh/modules/ui_corosync.py    2014-03-17 13:09:30.000000000 +0100
@@ -50,7 +50,7 @@
     def requires(self):
         stack = utils.cluster_stack()
         if len(stack) > 0 and stack != 'corosync':
-            err_buf.warn("Unsupported cluster stack %s detected." % (stack))
+            err_buf.warning("Unsupported cluster stack %s detected." % (stack))
             return False
         return True
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/modules/utils.py new/crmsh/modules/utils.py
--- old/crmsh/modules/utils.py  2014-03-08 14:13:37.000000000 +0100
+++ new/crmsh/modules/utils.py  2014-03-17 13:09:30.000000000 +0100
@@ -73,22 +73,38 @@
             return True
 
 
+def can_ask():
+    """
+    Is user-interactivity possible?
+    Checks if connected to a TTY.
+    """
+    return sys.stdin.isatty()
+
+
 def ask(msg):
-    # if there's no terminal, no use asking and default to "no"
-    if not sys.stdin.isatty():
+    """
+    Ask for user confirmation.
+    If core.force is true, always return true.
+    If not interactive and core.force is false, always return false.
+    """
+    if config.core.force:
+        common_info("%s [YES]" % (msg))
+        return True
+    if not can_ask():
         return False
-    print_msg = True
+
+    msg += ' '
+    if msg.endswith('? '):
+        msg = msg[:-2] + ' ([y]/n)? '
+
     while True:
         try:
-            ans = raw_input(msg + ' ')
+            ans = raw_input(msg)
         except EOFError:
             ans = 'n'
-        if not ans or ans[0].lower() not in ('n', 'y'):
-            if print_msg:
-                print "Please answer with y[es] or n[o]"
-                print_msg = False
-        else:
-            return ans[0].lower() == 'y'
+        ans = ans[0].lower()
+        if ans in 'yn':
+            return ans == 'y'
 
 # holds part of line before \ split
 # for a multi-line input
@@ -650,7 +666,11 @@
     common_debug("invoke: %s" % ptest)
     rc, s = get_stdout(ptest, input_s=graph_s)
     if rc != 0:
-        common_warn("%s exited with %d" % (ptest, rc))
+        common_debug("%s exited with %d" % (ptest, rc))
+        if actions and rc == 1:
+            common_warn("No actions found.")
+        else:
+            common_warn("Simulation was unsuccessful (RC=%d)." % (rc))
     if dotfile:
         if os.path.getsize(dotfile) > 0:
             show_dot_graph(dotfile)
@@ -872,7 +892,7 @@
     w, h = get_winsize()
     if not need_pager(s, w, h):
         print term_render(s)
-    elif not config.core.pager or not sys.stdout.isatty() or options.batch:
+    elif not config.core.pager or not can_ask() or options.batch:
         print term_render(s)
     else:
         opts = ""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/modules/xmlutil.py new/crmsh/modules/xmlutil.py
--- old/crmsh/modules/xmlutil.py        2014-03-08 14:13:37.000000000 +0100
+++ new/crmsh/modules/xmlutil.py        2014-03-17 13:09:30.000000000 +0100
@@ -1270,15 +1270,7 @@
     return l
 
 
-def xml_noorder_hash(n):
-    return sorted([hash(etree.tostring(x))
-                   for x in n.iterchildren()])
-
-xml_hash_d = {
-    "fencing-topology": xml_noorder_hash,
-}
-
-checker = doctestcompare.LXMLOutputChecker()
+_checker = doctestcompare.LXMLOutputChecker()
 
 
 def xml_equals_unordered(a, b):
@@ -1291,7 +1283,7 @@
         return isinstance(x.tag, basestring) and x.tag or x.text
 
     def sortby(v):
-        return tagflat(v) + ''.join(sorted(v.attrib.keys()))
+        return tagflat(v) + ''.join(sorted(v.attrib.keys() + 
v.attrib.values()))
 
     def safe_strip(text):
         return text is not None and text.strip() or ''
@@ -1313,24 +1305,13 @@
 
 
 def xml_equals(n, m, show=False):
-    if n.tag in xml_hash_d:
-        n_hash_l = xml_hash_d[n.tag](n)
-        m_hash_l = xml_hash_d[n.tag](m)
-        rc = len(n_hash_l) == len(m_hash_l)
-        for i in range(len(n_hash_l)):
-            if not rc:
-                break
-            if n_hash_l[i] != m_hash_l[i]:
-                rc = False
-    else:
-        #rc = checker.compare_docs(n, m)
-        rc = xml_equals_unordered(n, m)
+    rc = xml_equals_unordered(n, m)
     if not rc and show and config.core.debug:
         # somewhat strange, but that's how this works
         from doctest import Example
         example = Example("etree.tostring(n)", etree.tostring(n))
         got = etree.tostring(m)
-        print checker.output_difference(example, got, 0)
+        print _checker.output_difference(example, got, 0)
     return rc
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/scripts/add/add.py new/crmsh/scripts/add/add.py
--- old/crmsh/scripts/add/add.py        2014-03-08 14:13:37.000000000 +0100
+++ new/crmsh/scripts/add/add.py        2014-03-17 13:09:30.000000000 +0100
@@ -2,6 +2,7 @@
 import sys
 import os
 import crm_script
+import crm_init
 
 COROSYNC_AUTH = '/etc/corosync/authkey'
 COROSYNC_CONF = '/etc/corosync/corosync.conf'
@@ -9,9 +10,6 @@
 host = crm_script.host()
 add_nodes = crm_script.param('node').split(',')
 
-PACKAGES = ['pacemaker', 'corosync', 'crmsh']
-
-
 def run_collect():
     if host not in add_nodes:
         crm_script.exit_ok(host)
@@ -19,7 +17,7 @@
     rc, out, err = crm_script.service('pacemaker', 'is-active')
     if rc == 0 and out.strip() == 'active':
         crm_script.exit_fail("Pacemaker already running on %s" % (host))
-    crm_script.exit_ok(host)
+    crm_script.exit_ok(crm_init.info())
 
 
 def make_opts():
@@ -49,11 +47,8 @@
 def run_install():
     if host not in add_nodes:
         crm_script.exit_ok(host)
-    for pkg in PACKAGES:
-        try:
-            crm_script.package(pkg, 'latest')
-        except Exception, e:
-            crm_script.exit_fail("Error installing package '%s': %s" % (pkg, 
e))
+    packages = ['cluster-glue', 'corosync', 'crmsh', 'pacemaker', 
'resource-agents']
+    crm_init.install_packages(packages)
     crm_script.exit_ok(host)
 
 
@@ -98,6 +93,13 @@
     crm_script.exit_ok(host)
 
 
+def run_firewall():
+    if host not in add_nodes:
+        crm_script.exit_ok(host)
+    crm_init.configure_firewall()
+    crm_script.exit_ok(host)
+
+
 def start_new_node():
     if host not in add_nodes:
         crm_script.exit_ok(host)
@@ -118,6 +120,8 @@
         run_install()
     elif sys.argv[1] == 'copy':
         run_copy()
+    elif sys.argv[1] == 'firewall':
+        run_firewall()
     elif sys.argv[1] == 'start':
         start_new_node()
     else:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/scripts/add/main.yml 
new/crmsh/scripts/add/main.yml
--- old/crmsh/scripts/add/main.yml      2014-03-08 14:13:37.000000000 +0100
+++ new/crmsh/scripts/add/main.yml      2014-03-17 13:09:30.000000000 +0100
@@ -26,6 +26,9 @@
     - name: Copy configuration files
       apply_local: add.py copy
 
+    - name: Configure firewall
+      apply: add.py firewall
+
     - name: Start cluster on new node
       apply: add.py start
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/scripts/init/Makefile.am 
new/crmsh/scripts/init/Makefile.am
--- old/crmsh/scripts/init/Makefile.am  2014-03-08 14:13:37.000000000 +0100
+++ new/crmsh/scripts/init/Makefile.am  2014-03-17 13:09:30.000000000 +0100
@@ -21,7 +21,7 @@
 
 scriptinitdir          = $(datadir)/@PACKAGE@/scripts/init
 
-scriptinit_DATA        = main.yml corosync.conf.template init_common.py 
basic.cib.template
+scriptinit_DATA        = main.yml corosync.conf.template basic.cib.template
 scriptinit_SCRIPTS     = collect.py verify.py configure.py authkey.py init.py
 
 EXTRA_DIST     = $(scriptinit_DATA) $(scriptinit_SCRIPTS)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/scripts/init/collect.py 
new/crmsh/scripts/init/collect.py
--- old/crmsh/scripts/init/collect.py   2014-03-08 14:13:37.000000000 +0100
+++ new/crmsh/scripts/init/collect.py   2014-03-17 13:09:30.000000000 +0100
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 import crm_script
-import init_common as init
+import crm_init
 try:
-    crm_script.exit_ok(init.info())
+    crm_script.exit_ok(crm_init.info())
 except Exception, e:
     crm_script.exit_fail(str(e))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/scripts/init/configure.py 
new/crmsh/scripts/init/configure.py
--- old/crmsh/scripts/init/configure.py 2014-03-08 14:13:37.000000000 +0100
+++ new/crmsh/scripts/init/configure.py 2014-03-17 13:09:30.000000000 +0100
@@ -1,77 +1,19 @@
 #!/usr/bin/env python
 import sys
-import os
 import crm_script
-
-host = crm_script.host()
-hostinfo = crm_script.output(1)[host]
-
-
-def service_enabled(name):
-    for svc in hostinfo['services']:
-        if svc['name'] == name and svc['enabled'] == 'enabled':
-            return True
-    return False
-
-
-def service_active(name):
-    for svc in hostinfo['services']:
-        if svc['name'] == name and svc['active'] == 'active':
-            return True
-    return False
-
-SUSE_FW_TEMPLATE = """## Name: HAE cluster ports
-## Description: opens ports for HAE cluster services
-TCP="%(tcp)s"
-UDP="%(udp)s"
-"""
-
-
-def configure_firewall():
-    corosync_mcastport = crm_script.param('mcastport')
-    FW_CLUSTER = '/etc/sysconfig/SuSEfirewall2.d/services/cluster'
-
-    tcp_ports = '30865 5560 7630 21064'
-    udp_ports = corosync_mcastport
-
-    if service_enabled('SuSEfirewall2_init'):
-        if os.path.isfile(FW_CLUSTER):
-            import re
-            tmpl = open(FW_CLUSTER).read()
-            tmpl = re.sub(r'^TCP="(.*)"', 'TCP="%s"' % (tcp_ports), tmpl, 
flags=re.M)
-            tmpl = re.sub(r'^UDP="(.*)"', 'UDP="%s"' % (udp_ports), tmpl, 
flags=re.M)
-            with open(FW_CLUSTER, 'w') as f:
-                f.write(tmpl)
-        elif os.path.isdir(os.path.dirname(FW_CLUSTER)):
-            with open(FW_CLUSTER, 'w') as fwc:
-                fwc.write(SUSE_FW_TEMPLATE % {'tcp': tcp_ports,
-                                              'udp': udp_ports})
-        else:
-            # neither the cluster file nor the services
-            # directory exists
-            crm_script.exit_fail("SUSE firewall is configured but %s does not 
exist" %
-                                 os.path.dirname(FW_CLUSTER))
-        if service_active('SuSEfirewall2_init'):
-            crm_script.service('SuSEfirewall2_init', 'restart')
-
-    # TODO: other platforms
+import crm_init
 
 
 def run_install():
-    # install packages
-    PACKAGES = ['cluster-glue', 'corosync', 'crmsh', 'pacemaker', 
'resource-agents']
-    for pkg in PACKAGES:
-        try:
-            crm_script.package(pkg, 'latest')
-        except Exception, e:
-            crm_script.exit_fail("Failed to install %s: %s" % (pkg, e))
-
-    configure_firewall()
-
+    packages = ['cluster-glue', 'corosync', 'crmsh', 'pacemaker', 
'resource-agents']
+    crm_init.install_packages(packages)
+    crm_init.configure_firewall()
     crm_script.exit_ok(True)
 
 
 def make_bindnetaddr():
+    host = crm_script.host()
+    hostinfo = crm_script.output(1)[host]
     ba = crm_script.param('bindnetaddr')
     if ba:
         return ba
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/scripts/init/init_common.py 
new/crmsh/scripts/init/init_common.py
--- old/crmsh/scripts/init/init_common.py       2014-03-08 14:13:37.000000000 
+0100
+++ new/crmsh/scripts/init/init_common.py       1970-01-01 01:00:00.000000000 
+0100
@@ -1,132 +0,0 @@
-import os
-import platform
-import socket
-import crm_script
-
-PACKAGES = ['booth', 'cluster-glue', 'corosync', 'crmsh', 'csync2', 'drbd',
-            'fence-agents', 'gfs2', 'gfs2-utils', 'hawk', 'ocfs2',
-            'ocfs2-tools', 'pacemaker', 'pacemaker-mgmt',
-            'resource-agents', 'sbd']
-SERVICES = ['sshd', 'ntp', 'corosync', 'pacemaker', 'hawk', 
'SuSEfirewall2_init']
-SSH_KEY = os.path.expanduser('~/.ssh/id_rsa')
-CSYNC2_KEY = '/etc/csync2/key_hagroup'
-CSYNC2_CFG = '/etc/csync2/csync2.cfg'
-COROSYNC_CONF = '/etc/corosync/corosync.conf'
-SYSCONFIG_SBD = '/etc/sysconfig/sbd'
-SYSCONFIG_FW = '/etc/sysconfig/SuSEfirewall2'
-SYSCONFIG_FW_CLUSTER = '/etc/sysconfig/SuSEfirewall2.d/services/cluster'
-
-
-def rpm_info():
-    'check installed packages'
-    return crm_script.rpmcheck(PACKAGES)
-
-
-def svc_info():
-    'check enabled/active services'
-    state = []
-    for service in SERVICES:
-        active, enabled = 'unknown', 'unknown'
-        rc, out, err = crm_script.call(["systemctl", "is-enabled", 
"%s.service" % (service)])
-        if rc in (0, 1, 3) and out:
-            enabled = out.strip()
-        else:
-            state.append({'name': service, 'error': err.strip()})
-            continue
-        rc, out, err = crm_script.call(["systemctl", "is-active", "%s.service" 
% (service)])
-        if rc in (0, 1, 3) and out:
-            active = out.strip()
-        else:
-            state.append({'name': service, 'error': err.strip()})
-            continue
-        state.append({'name': service, 'active': active, 'enabled': enabled})
-    return state
-
-
-def sys_info():
-    'system information'
-    system, node, release, version, machine, processor = platform.uname()
-    distname, distver, distid = platform.linux_distribution()
-    hostname = platform.node().split('.')[0]
-    return {'system': system,
-            'node': node,
-            'release': release,
-            'version': version,
-            'machine': machine,
-            'processor': processor,
-            'distname': distname,
-            'distver': distver,
-            'distid': distid,
-            'user': os.getlogin(),
-            'hostname': hostname,
-            'fqdn': socket.getfqdn()}
-
-
-def net_info():
-    ret = {}
-    interfaces = []
-    rc, out, err = crm_script.call(['netstat', '-nr'])
-    if rc == 0:
-        data = [l.split() for l in out.split('\n')]
-        if len(data) < 3:
-            return {'error': "Failed to parse netstat output"}
-        keys = data[1]
-        for line in data[2:]:
-            if len(line) == len(keys):
-                interfaces.append(dict(zip(keys, line)))
-    else:
-        interfaces.append({'error': err.strip()})
-    ret['interfaces'] = interfaces
-    hostname = platform.node().split('.')[0]
-    try:
-        ip = socket.gethostbyname(hostname)
-        ret['hostname'] = {'name': hostname, 'ip': ip}
-    except Exception, e:
-        ret['hostname'] = {'error': str(e)}
-    return ret
-
-
-def files_info():
-    def check(fn):
-        if os.path.isfile(os.path.expanduser(fn)):
-            return os.path.expanduser(fn)
-        return ''
-    return {'ssh_key': check(SSH_KEY),
-            'csync2_key': check(CSYNC2_KEY),
-            'csync2_cfg': check(CSYNC2_CFG),
-            'corosync_conf': check(COROSYNC_CONF),
-            'sysconfig_sbd': check(SYSCONFIG_SBD),
-            'sysconfig_fw': check(SYSCONFIG_FW),
-            'sysconfig_fw_cluster': check(SYSCONFIG_FW_CLUSTER),
-            }
-
-
-def logrotate_info():
-    rc, _, _ = crm_script.call(
-        'grep -r corosync.conf /etc/logrotate.d',
-        shell=True)
-    return {'corosync.conf': rc == 0}
-
-
-def disk_info():
-    rc, out, err = crm_script.call(['df'], shell=False)
-    if rc == 0:
-        disk_use = []
-        for line in out.split('\n')[1:]:
-            line = line.strip()
-            if line:
-                data = line.split()
-                if len(data) >= 6:
-                    disk_use.append((data[5], int(data[4][:-1])))
-        return disk_use
-    return []
-
-
-def info():
-    return {'rpm': rpm_info(),
-            'services': svc_info(),
-            'system': sys_info(),
-            'net': net_info(),
-            'files': files_info(),
-            'logrotate': logrotate_info(),
-            'disk': disk_info()}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/scripts/init/verify.py 
new/crmsh/scripts/init/verify.py
--- old/crmsh/scripts/init/verify.py    2014-03-08 14:13:37.000000000 +0100
+++ new/crmsh/scripts/init/verify.py    2014-03-17 13:09:30.000000000 +0100
@@ -1,13 +1,6 @@
 #!/usr/bin/env python
 import crm_script
-
-data = crm_script.output(1)
-
-
-def verify_host(host, info):
-    if host != info['system']['hostname']:
-        crm_script.exit_fail("Hostname mismatch: %s is not %s" %
-                             (host, info['system']['hostname']))
+import crm_init
 
 
 def select_interfaces(user_iface, data):
@@ -32,39 +25,6 @@
     return user_iface
 
 
-def compare_system(systems):
-    def check(value, msg):
-        vals = set([system[value] for host, system in systems])
-        if len(vals) > 1:
-            info = ', '.join('%s: %s' % (h, system[value]) for h, system in 
systems)
-            crm_script.exit_fail("%s: %s" % (msg, info))
-
-    check('machine', 'Architecture differs')
-    #check('release', 'Kernel release differs')
-    check('distname', 'Distribution differs')
-    check('distver', 'Distribution version differs')
-    #check('version', 'Kernel version differs')
-
-
-def check_diskspace():
-    for host, info in data.iteritems():
-        for mount, percent in info['disk']:
-            interesting = (mount == '/' or
-                           mount.startswith('/var/log') or
-                           mount.startswith('/tmp'))
-            if interesting and percent > 90:
-                crm_script.exit_fail("Not enough space on %s:%s" % (host, 
mount))
-
-
-def check_services():
-    for host, info in data.iteritems():
-        for svc in info['services']:
-            if svc['name'] == 'pacemaker' and svc['active'] == 'active':
-                crm_script.exit_fail("%s already running pacemaker" % (host))
-            if svc['name'] == 'corosync' and svc['active'] == 'active':
-                crm_script.exit_fail("%s already running corosync" % (host))
-
-
 def make_mcastaddr():
     import random
     random.seed()
@@ -72,12 +32,9 @@
     return "%d.%d.%d.%d" % (239, b, c, d)
 
 try:
-    for host, info in data.iteritems():
-        verify_host(host, info)
-    compare_system((h, info['system']) for h, info in data.iteritems())
+    data = crm_script.output(1)
 
-    check_diskspace()
-    check_services()
+    crm_init.verify(data)
 
     ret = {}
     ret['iface'] = select_interfaces(crm_script.param('iface'), data)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/test/unittests/test_bugs.py 
new/crmsh/test/unittests/test_bugs.py
--- old/crmsh/test/unittests/test_bugs.py       2014-03-08 14:13:37.000000000 
+0100
+++ new/crmsh/test/unittests/test_bugs.py       2014-03-17 13:09:30.000000000 
+0100
@@ -225,3 +225,32 @@
     xmlutil.sanitize_cib(elems)
     assert etree.tostring(elems).count("COMMENT TEXT") == 3
 
+
+def test_eq1():
+    xml1 = """<cluster_property_set id="cib-bootstrap-options">
+    <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" 
value="true"></nvpair>
+    <nvpair id="cib-bootstrap-options-stonith-timeout" name="stonith-timeout" 
value="180"></nvpair>
+    <nvpair id="cib-bootstrap-options-symmetric-cluster" 
name="symmetric-cluster" value="false"></nvpair>
+    <nvpair id="cib-bootstrap-options-no-quorum-policy" 
name="no-quorum-policy" value="freeze"></nvpair>
+    <nvpair id="cib-bootstrap-options-batch-limit" name="batch-limit" 
value="20"></nvpair>
+    <nvpair id="cib-bootstrap-options-dc-version" name="dc-version" 
value="1.1.10-c1a326d"></nvpair>
+    <nvpair id="cib-bootstrap-options-cluster-infrastructure" 
name="cluster-infrastructure" value="corosync"></nvpair>
+    <nvpair id="cib-bootstrap-options-last-lrm-refresh" 
name="last-lrm-refresh" value="1391433789"></nvpair>
+    <nvpair id="cib-bootstrap-options-is-managed-default" 
name="is-managed-default" value="true"></nvpair>
+  </cluster_property_set>
+  """
+    xml2 = """<cluster_property_set id="cib-bootstrap-options">
+    <nvpair id="cib-bootstrap-options-stonith-enabled" name="stonith-enabled" 
value="true"></nvpair>
+    <nvpair id="cib-bootstrap-options-stonith-timeout" name="stonith-timeout" 
value="180"></nvpair>
+    <nvpair id="cib-bootstrap-options-symmetric-cluster" 
name="symmetric-cluster" value="false"></nvpair>
+    <nvpair id="cib-bootstrap-options-no-quorum-policy" 
name="no-quorum-policy" value="freeze"></nvpair>
+    <nvpair id="cib-bootstrap-options-batch-limit" name="batch-limit" 
value="20"></nvpair>
+    <nvpair id="cib-bootstrap-options-dc-version" name="dc-version" 
value="1.1.10-c1a326d"></nvpair>
+    <nvpair id="cib-bootstrap-options-cluster-infrastructure" 
name="cluster-infrastructure" value="corosync"></nvpair>
+    <nvpair id="cib-bootstrap-options-last-lrm-refresh" 
name="last-lrm-refresh" value="1391433789"></nvpair>
+    <nvpair id="cib-bootstrap-options-is-managed-default" 
name="is-managed-default" value="true"></nvpair>
+  </cluster_property_set>
+    """
+    e1 = etree.fromstring(xml1)
+    e2 = etree.fromstring(xml2)
+    assert xmlutil.xml_equals(e1, e2, show=True)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/test/unittests/test_cliformat.py 
new/crmsh/test/unittests/test_cliformat.py
--- old/crmsh/test/unittests/test_cliformat.py  2014-03-08 14:13:37.000000000 
+0100
+++ new/crmsh/test/unittests/test_cliformat.py  2014-03-17 13:09:30.000000000 
+0100
@@ -19,6 +19,7 @@
 import utils
 import parse
 import cibconfig
+from lxml import etree
 from test_parse import MockValidation
 
 factory = cibconfig.cib_factory
@@ -99,7 +100,6 @@
     <resource_ref id="apache"/>
   </resource_set>
 </rsc_colocation>"""
-    from lxml import etree
     data = etree.fromstring(xml)
     obj = factory.new_object('colocation', 'colo-2')
     assert obj is not None
@@ -127,7 +127,6 @@
 value="Stopped"/> \
   </meta_attributes> \
 </primitive>"""
-    from lxml import etree
     data = etree.fromstring(xml)
     obj = factory.new_object('primitive', 'dummy')
     assert obj is not None
@@ -152,7 +151,6 @@
     <op name="monitor" interval="60" timeout="30" id="dummy2-monitor-60"/> \
   </operations> \
 </primitive>"""
-    from lxml import etree
     data = etree.fromstring(xml)
     obj = factory.new_object('primitive', 'dummy2')
     assert obj is not None
@@ -163,3 +161,24 @@
     exp = 'primitive dummy2 ocf:pacemaker:Dummy meta target-role="Stopped" op 
start timeout="60" interval="0" op stop timeout="60" interval="0" op monitor 
interval="60" timeout="30"'
     assert data == exp
     assert obj.cli_use_validate()
+
+def test_fencing():
+    xml = """<fencing-topology>
+    <fencing-level devices="st1" id="fencing" index="1"
+target="ha-three"></fencing-level>
+    <fencing-level devices="st1" id="fencing-0" index="1"
+target="ha-two"></fencing-level>
+    <fencing-level devices="st1" id="fencing-1" index="1"
+target="ha-one"></fencing-level>
+  </fencing-topology>"""
+    data = etree.fromstring(xml)
+    obj = factory.new_object('fencing_topology', 'st1')
+    assert obj is not None
+    obj.node = data
+    obj.set_id()
+    data = obj.repr_cli(format=-1)
+    print data
+    exp = 'fencing_topology st1'
+    assert data == exp
+    assert obj.cli_use_validate()
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/test/unittests/test_parse.py 
new/crmsh/test/unittests/test_parse.py
--- old/crmsh/test/unittests/test_parse.py      2014-03-08 14:13:37.000000000 
+0100
+++ new/crmsh/test/unittests/test_parse.py      2014-03-17 13:09:30.000000000 
+0100
@@ -203,6 +203,11 @@
         self.assertEqual(out.node, 'bar')
         #print out.to_list()
 
+        out = self.parser.parse('location loc-1 thing rule role=slave -inf: 
#uname eq madrid')
+        self.assertEqual(out.id, 'loc-1')
+        self.assertEqual(out.resource, 'thing')
+        self.assertEqual(out.score, None)
+
     def test_colocation(self):
         out = self.parser.parse('colocation col-1 inf: foo:master ( bar wiz 
sequential=yes )')
         self.assertEqual(out.id, 'col-1')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/utils/Makefile.am new/crmsh/utils/Makefile.am
--- old/crmsh/utils/Makefile.am 2014-03-08 14:13:37.000000000 +0100
+++ new/crmsh/utils/Makefile.am 2014-03-17 13:09:30.000000000 +0100
@@ -21,7 +21,7 @@
 
 utilsdir               = $(datadir)/@PACKAGE@/utils
 
-utils_DATA     = crm_script.py
+utils_DATA     = crm_script.py crm_init.py
 utils_SCRIPTS  = crm_clean.py crm_rpmcheck.py crm_pkg.py
 
 EXTRA_DIST     = $(utils_DATA) $(utils_SCRIPTS)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/utils/crm_init.py new/crmsh/utils/crm_init.py
--- old/crmsh/utils/crm_init.py 1970-01-01 01:00:00.000000000 +0100
+++ new/crmsh/utils/crm_init.py 2014-03-17 13:09:30.000000000 +0100
@@ -0,0 +1,263 @@
+import os
+import re
+import platform
+import socket
+import crm_script
+
+PACKAGES = ['booth', 'cluster-glue', 'corosync', 'crmsh', 'csync2', 'drbd',
+            'fence-agents', 'gfs2', 'gfs2-utils', 'hawk', 'ocfs2',
+            'ocfs2-tools', 'pacemaker', 'pacemaker-mgmt',
+            'resource-agents', 'sbd']
+SERVICES = ['sshd', 'ntp', 'corosync', 'pacemaker', 'hawk', 
'SuSEfirewall2_init']
+SSH_KEY = os.path.expanduser('~/.ssh/id_rsa')
+CSYNC2_KEY = '/etc/csync2/key_hagroup'
+CSYNC2_CFG = '/etc/csync2/csync2.cfg'
+COROSYNC_CONF = '/etc/corosync/corosync.conf'
+SYSCONFIG_SBD = '/etc/sysconfig/sbd'
+SYSCONFIG_FW = '/etc/sysconfig/SuSEfirewall2'
+SYSCONFIG_FW_CLUSTER = '/etc/sysconfig/SuSEfirewall2.d/services/cluster'
+
+
+def rpm_info():
+    'check installed packages'
+    return crm_script.rpmcheck(PACKAGES)
+
+
+def service_info(service):
+    "Returns information about a given service"
+    active, enabled = 'unknown', 'unknown'
+    rc, out, err = crm_script.call(["/usr/bin/systemctl", "is-enabled", 
"%s.service" % (service)])
+    if rc in (0, 1, 3) and out:
+        enabled = out.strip()
+    else:
+        return {'name': service, 'error': err.strip()}
+    rc, out, err = crm_script.call(["/usr/bin/systemctl", "is-active", 
"%s.service" % (service)])
+    if rc in (0, 1, 3) and out:
+        active = out.strip()
+    else:
+        return {'name': service, 'error': err.strip()}
+    return {'name': service, 'active': active, 'enabled': enabled}
+
+
+def services_info():
+    'check enabled/active services'
+    return [service_info(service) for service in SERVICES]
+
+
+def sys_info():
+    'system information'
+    system, node, release, version, machine, processor = platform.uname()
+    distname, distver, distid = platform.linux_distribution()
+    hostname = platform.node().split('.')[0]
+    return {'system': system,
+            'node': node,
+            'release': release,
+            'version': version,
+            'machine': machine,
+            'processor': processor,
+            'distname': distname,
+            'distver': distver,
+            'distid': distid,
+            'user': os.getlogin(),
+            'hostname': hostname,
+            'fqdn': socket.getfqdn()}
+
+
+def net_info():
+    ret = {}
+    interfaces = []
+    rc, out, err = crm_script.call(['netstat', '-nr'])
+    if rc == 0:
+        data = [l.split() for l in out.split('\n')]
+        if len(data) < 3:
+            return {'error': "Failed to parse netstat output"}
+        keys = data[1]
+        for line in data[2:]:
+            if len(line) == len(keys):
+                interfaces.append(dict(zip(keys, line)))
+    else:
+        interfaces.append({'error': err.strip()})
+    ret['interfaces'] = interfaces
+    hostname = platform.node().split('.')[0]
+    try:
+        ip = socket.gethostbyname(hostname)
+        ret['hostname'] = {'name': hostname, 'ip': ip}
+    except Exception, e:
+        ret['hostname'] = {'error': str(e)}
+    return ret
+
+
+def files_info():
+    def check(fn):
+        if os.path.isfile(os.path.expanduser(fn)):
+            return os.path.expanduser(fn)
+        return ''
+    return {'ssh_key': check(SSH_KEY),
+            'csync2_key': check(CSYNC2_KEY),
+            'csync2_cfg': check(CSYNC2_CFG),
+            'corosync_conf': check(COROSYNC_CONF),
+            'sysconfig_sbd': check(SYSCONFIG_SBD),
+            'sysconfig_fw': check(SYSCONFIG_FW),
+            'sysconfig_fw_cluster': check(SYSCONFIG_FW_CLUSTER),
+            }
+
+
+def logrotate_info():
+    rc, _, _ = crm_script.call(
+        'grep -r corosync.conf /etc/logrotate.d',
+        shell=True)
+    return {'corosync.conf': rc == 0}
+
+
+def disk_info():
+    rc, out, err = crm_script.call(['df'], shell=False)
+    if rc == 0:
+        disk_use = []
+        for line in out.split('\n')[1:]:
+            line = line.strip()
+            if line:
+                data = line.split()
+                if len(data) >= 6:
+                    disk_use.append((data[5], int(data[4][:-1])))
+        return disk_use
+    return []
+
+
+def info():
+    return {'rpm': rpm_info(),
+            'services': services_info(),
+            'system': sys_info(),
+            'net': net_info(),
+            'files': files_info(),
+            'logrotate': logrotate_info(),
+            'disk': disk_info()}
+
+
+def verify(data):
+    """
+    Given output from info(), verifies
+    as much as possible before init/add.
+    """
+    def check_diskspace():
+        for host, info in data.iteritems():
+            for mount, percent in info['disk']:
+                interesting = (mount == '/' or
+                               mount.startswith('/var/log') or
+                               mount.startswith('/tmp'))
+                if interesting and percent > 90:
+                    crm_script.exit_fail("Not enough space on %s:%s" % (host, 
mount))
+
+    def check_services():
+        for host, info in data.iteritems():
+            for svc in info['services']:
+                if svc['name'] == 'pacemaker' and svc['active'] == 'active':
+                    crm_script.exit_fail("%s already running pacemaker" % 
(host))
+                if svc['name'] == 'corosync' and svc['active'] == 'active':
+                    crm_script.exit_fail("%s already running corosync" % 
(host))
+
+    def verify_host(host, info):
+        if host != info['system']['hostname']:
+            crm_script.exit_fail("Hostname mismatch: %s is not %s" %
+                                 (host, info['system']['hostname']))
+
+    def compare_system(systems):
+        def check(value, msg):
+            vals = set([system[value] for host, system in systems])
+            if len(vals) > 1:
+                info = ', '.join('%s: %s' % (h, system[value]) for h, system 
in systems)
+                crm_script.exit_fail("%s: %s" % (msg, info))
+
+        check('machine', 'Architecture differs')
+        #check('release', 'Kernel release differs')
+        check('distname', 'Distribution differs')
+        check('distver', 'Distribution version differs')
+        #check('version', 'Kernel version differs')
+
+    for host, info in data.iteritems():
+        verify_host(host, info)
+
+    compare_system((h, info['system']) for h, info in data.iteritems())
+
+    check_diskspace()
+    check_services()
+
+
+# common functions to initialize a cluster node
+
+
+def is_service_enabled(name):
+    info = service_info(name)
+    if info.get('name') == name and info.get('enabled') == 'enabled':
+        return True
+    return False
+
+
+def is_service_active(name):
+    info = service_info(name)
+    if info.get('name') == name and info.get('active') == 'active':
+        return True
+    return False
+
+
+def install_packages(packages):
+    for pkg in packages:
+        try:
+            crm_script.package(pkg, 'latest')
+        except Exception, e:
+            crm_script.exit_fail("Failed to install %s: %s" % (pkg, e))
+
+
+def configure_firewall():
+    _SUSE_FW_TEMPLATE = """## Name: HAE cluster ports
+## Description: opens ports for HAE cluster services
+TCP="%(tcp)s"
+UDP="%(udp)s"
+"""
+    corosync_mcastport = crm_script.param('mcastport')
+    if not corosync_mcastport:
+        rc, out, err = crm_script.call(['crm', 'corosync', 'get', 
'totem.interface.mcastport'])
+        if rc == 0:
+            corosync_mcastport = out.strip()
+    FW = '/etc/sysconfig/SuSEfirewall2'
+    FW_CLUSTER = '/etc/sysconfig/SuSEfirewall2.d/services/cluster'
+
+    tcp_ports = '30865 5560 7630 21064'
+    udp_ports = '%s %s' % (corosync_mcastport, int(corosync_mcastport) - 1)
+
+    if is_service_enabled('SuSEfirewall2'):
+        if os.path.isfile(FW_CLUSTER):
+            tmpl = open(FW_CLUSTER).read()
+            tmpl = re.sub(r'^TCP="(.*)"', 'TCP="%s"' % (tcp_ports), tmpl, 
flags=re.M)
+            tmpl = re.sub(r'^UDP="(.*)"', 'UDP="%s"' % (udp_ports), tmpl, 
flags=re.M)
+            with open(FW_CLUSTER, 'w') as f:
+                f.write(tmpl)
+        elif os.path.isdir(os.path.dirname(FW_CLUSTER)):
+            with open(FW_CLUSTER, 'w') as fwc:
+                fwc.write(_SUSE_FW_TEMPLATE % {'tcp': tcp_ports,
+                                               'udp': udp_ports})
+        else:
+            # neither the cluster file nor the services
+            # directory exists
+            crm_script.exit_fail("SUSE firewall is configured but %s does not 
exist" %
+                                 os.path.dirname(FW_CLUSTER))
+
+        # add cluster to FW_CONFIGURATIONS_EXT
+        if os.path.isfile(FW):
+            txt = open(FW).read()
+            m = re.search(r'^FW_CONFIGURATIONS_EXT="(.*)"', txt, re.M)
+            if m:
+                services = m.group(1).split()
+                if 'cluster' not in services:
+                    services.append('cluster')
+                txt = re.sub(r'^FW_CONFIGURATIONS_EXT="(.*)"',
+                             r'FW_CONFIGURATIONS_EXT="%s"' % (' 
'.join(services)),
+                             txt,
+                             flags=re.M)
+            else:
+                txt += '\nFW_CONFIGURATIONS_EXT="cluster"'
+            with open(FW, 'w') as fw:
+                fw.write(txt)
+        if is_service_active('SuSEfirewall2'):
+            crm_script.service('SuSEfirewall2', 'restart')
+
+    # TODO: other platforms
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/utils/crm_pkg.py new/crmsh/utils/crm_pkg.py
--- old/crmsh/utils/crm_pkg.py  2014-03-08 14:13:37.000000000 +0100
+++ new/crmsh/utils/crm_pkg.py  2014-03-17 13:09:30.000000000 +0100
@@ -107,6 +107,7 @@
 
         cmd = [self._zyp,
                '--non-interactive',
+               '--no-refresh',
                'install',
                '--auto-agree-with-licenses',
                name]
@@ -124,6 +125,7 @@
         pre_version = self.get_version(name)
         cmd = [self._zyp,
                '--non-interactive',
+               '--no-refresh',
                'update',
                '--auto-agree-with-licenses',
                name]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/crmsh/utils/crm_script.py 
new/crmsh/utils/crm_script.py
--- old/crmsh/utils/crm_script.py       2014-03-08 14:13:37.000000000 +0100
+++ new/crmsh/utils/crm_script.py       2014-03-17 13:09:30.000000000 +0100
@@ -171,3 +171,4 @@
     except Exception, e:
         raise IOError("Failed to write %s from template %s: %s" % (dest, 
template, e))
     debug("crm_script(save_template): wrote %s" % (dest))
+

-- 
To unsubscribe, e-mail: opensuse-commit+unsubscr...@opensuse.org
For additional commands, e-mail: opensuse-commit+h...@opensuse.org

Reply via email to