Hello community,
here is the log from the commit of package crmsh for openSUSE:Factory checked in at 2015-10-19 22:51:28
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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 2015-10-12 10:02:44.000000000 +0200
+++ /work/SRC/openSUSE:Factory/.crmsh.new/crmsh.changes 2015-10-20 00:05:26.000000000 +0200
@@ -1,0 +2,22 @@
+Thu Oct 15 05:18:06 UTC 2015 - kgronlund@suse.com
+
+- Update to version 2.2.0~rc3+git.1444854254.fc37f7f:
+ + high: utils: Handle time zones in parse_time (bsc#949511)
+ + medium: cibconfig: Fix sanity check for attribute-based fencing topology (#110)
+ + medium: ui_script: Optionally print common params
+ + medium: hb_report: Remove reference to function name in event patterns (bsc#942906)
+ + medium: report: Make transitions without end stretch to 2525
+ + doc: add missing <> to fencing_topology syntax
+ + doc: add missing backslash in fencing_topology example
+ + doc: add explanatory comments to fencing_topology
+ + doc: Update the scripts documentation
+ + doc: Fix unclosed block in scripts documentation
+
+-------------------------------------------------------------------
+Mon Oct 12 14:51:37 UTC 2015 - kgronlund@suse.com
+
+- Update to version 2.2.0~rc3+git.1444661352.14fa72b:
+ + high: scripts: Determine output format of script correctly (bsc#949980)
+ + high: cibconfig: Fix bug with node/resource collision
+
+-------------------------------------------------------------------
Old:
----
crmsh-2.2.0~rc3+git.1444340345.59850ca.tar.bz2
New:
----
crmsh-2.2.0~rc3+git.1444854254.fc37f7f.tar.bz2
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ crmsh.spec ++++++
--- /var/tmp/diff_new_pack.mvUPdr/_old 2015-10-20 00:05:27.000000000 +0200
+++ /var/tmp/diff_new_pack.mvUPdr/_new 2015-10-20 00:05:27.000000000 +0200
@@ -36,7 +36,7 @@
Summary: High Availability cluster command-line interface
License: GPL-2.0+
Group: %{pkg_group}
-Version: 2.2.0~rc3+git.1444340345.59850ca
+Version: 2.2.0~rc3+git.1444854254.fc37f7f
Release: 0
Url: http://crmsh.github.io
Source0: %{name}-%{version}.tar.bz2
++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.mvUPdr/_old 2015-10-20 00:05:27.000000000 +0200
+++ /var/tmp/diff_new_pack.mvUPdr/_new 2015-10-20 00:05:27.000000000 +0200
@@ -1,4 +1,4 @@
<servicedata>
<service name="tar_scm">
<param name="url">git://github.com/ClusterLabs/crmsh.git</param>
- <param name="changesrevision">59850ca9ed07b3e965170b1fb50712ea1bfa502f</param></service></servicedata>
\ No newline at end of file
+ <param name="changesrevision">fc37f7f872a065147e3c0c32e962a924cad109b3</param></service></servicedata>
\ No newline at end of file
++++++ crmsh-2.2.0~rc3+git.1444340345.59850ca.tar.bz2 -> crmsh-2.2.0~rc3+git.1444854254.fc37f7f.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1444340345.59850ca/doc/crm.8.adoc new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/doc/crm.8.adoc
--- old/crmsh-2.2.0~rc3+git.1444340345.59850ca/doc/crm.8.adoc 2015-10-08 23:44:05.000000000 +0200
+++ new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/doc/crm.8.adoc 2015-10-15 07:18:06.000000000 +0200
@@ -2761,20 +2761,26 @@
Usage:
...............
-fencing_topology stonith_resources [stonith_resources ...]
-fencing_topology fencing_order [fencing_order ...]
+fencing_topology [ ...]
+fencing_topology [ ...]
-fencing_order :: target stonith_resources [stonith_resources ...]
+fencing_order :: <target> [ ...]
stonith_resources :: <rsc>[,<rsc>...]
target :: <node>: | attr:<node-attribute>=<value>
...............
Example:
...............
+# Only kill the power if poison-pill fails
fencing_topology poison-pill power
+
+# As above for node-a, but a different strategy for node-b
fencing_topology \
- node-a: poison-pill power
+ node-a: poison-pill power \
node-b: ipmi serial
+
+# Fencing anything on rack 1 requires fencing via both APC 1 and 2,
+# to defeat the redundancy provided by two separate UPS units.
fencing_topology attr:rack=1 apc01,apc02
...............
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1444340345.59850ca/doc/website-v1/scripts.adoc new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/doc/website-v1/scripts.adoc
--- old/crmsh-2.2.0~rc3+git.1444340345.59850ca/doc/website-v1/scripts.adoc 2015-10-08 23:44:05.000000000 +0200
+++ new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/doc/website-v1/scripts.adoc 2015-10-15 07:18:06.000000000 +0200
@@ -2,7 +2,7 @@
:source-highlighter: pygments
.Version information
-NOTE: This section applies to `crmsh 2.0+` only.
+NOTE: This section applies to `crmsh 2.2+` only.
== Introduction ==
@@ -36,16 +36,52 @@
function through the usual SSH channels used for system maintenance,
requiring no additional software to be installed or maintained.
+For many scripts that only configure cluster resources or only perform
+changes on the local machine, the use of SSH is not necessary. These
+scripts can be used even if there is no way for `crmsh` to reach the
+other nodes other than through the cluster configuration.
+
+NOTE: The scripts functionality in `crmsh` has been greatly expanded
+and improved in `crmsh` 2.2. Many new scripts have been added, and in
+addition the scripts are now used as the backend for the wizards
+functionality in HAWK, the HA web interface. For more information, see
+https://github.com/ClusterLabs/hawk.
+
== Usage ==
Scripts are available through the `cluster` sub-level in the crm
shell. Some scripts have custom commands linked to them for
-convenience, such as the `init`, `join` and `remove` commands for
-creating new clusters, introducing new nodes into the cluster and for
-removing nodes from a running cluster.
-
-Other scripts can be accessed through the `script` sub-level inside
-`cluster`.
+convenience, such as the `init`, `add` and `remove` commands available
+in the `cluster` sublevel, for creating new clusters, introducing new
+nodes into the cluster and for removing nodes from a running cluster.
+
+Other scripts can be accessed through the `script` sub-level.
+
+=== Common Parameters ===
+
+Which parameters a script accepts varies from script to
+script. However, there is a set of parameters that are common to all
+scripts. These parameters can be passed to any script.
+
+`nodes`::
+ List of nodes to execute the script for
+`dry_run`::
+ If set, simulate execution only
+ (default: no)
+`action`::
+ If set, only execute a single action (index, as returned by verify)
+`statefile`::
+ When single-stepping, the state is saved in the given file
+`user`::
+ Run script as the given user
+`sudo`::
+ If set, crm will prompt for a sudo password and use sudo when appropriate
+ (default: no)
+`port`::
+ Port to connect on
+`timeout`::
+ Execution timeout in seconds
+ (default: 600)
=== List available scripts ===
@@ -56,37 +92,88 @@
list
.........
-The available scripts are listed along with a short description.
+The available scripts are listed along with a short
+description. Optionally, the arguments +all+ or +names+ can be
+used. Without the +all+ flag, some scripts that are used by `crmsh` to
+implement certain commands are hidden from view. With the +names+
+flag, only a plain list of script names is printed.
=== Script description ===
-To get more details about a script, run the `describe` command. For
-example, to get more information about what the `health` script does
+To get more details about a script, run the `show` command. For
+example, to get more information about what the `virtual-ip` script does
and what parameters it accepts, use the following command:
.........
# crm script
-describe health
+show virtual-ip
.........
-`describe` will print a longer explanation for the script, along with
+`show` will print a longer description of the script, along with a
+list of parameters divided into _steps_. Each script is divided into a
+series of steps which are performed in order. Some steps may not
+accept any parameters, but for those that do, the available parameters
+are listed here.
+
+By default, only a basic subset of the available parameters is printed
+in order to make the scripts easier to use. By passing `all` to the
+`show` command, the advanced parameters are also shown. In addition,
+there is a list of common parameters
+
+`show` will print a longer explanation for the script, along with
a list of parameters, each parameter having a description, a note
saying if it is an optional or required parameter, and if optional,
what the default value is.
+=== Verifying parameters ===
+
+Since a script potentially performs a series of actions and may fail
+for various reasons at any point, it is advisable to review the
+actions that a script will perform before actually running it. To do
+this, the `verify` command can be used.
+
+Pass the parameters that you would pass to `run`, and `verify` will
+check that the parameter values are OK, as well as print the sequence
+of steps that will be performed given the particular parameter values
+given.
+
+The following is an example showing how to verify the creation of a
+Virtual IP resource, using the `virtual-ip` script:
+
+..........
+# crm script
+verify virtual-ip id=my-virtual-ip ip=192.168.0.10
+..........
+
+`crmsh` will print something similar to the following output:
+
+...........
+1. Configure cluster resources
+
+ primitive my-virtual-ip ocf:heartbeat:IPaddr2
+ ip="192.168.0.10"
+ op start timeout="20" op stop timeout="20"
+ op monitor interval="10" timeout="20"
+...........
+
+In this particular case, there is only a single step, and that step
+configures a primitive resource. Other scripts may configure multiple
+resources and constraints, or may perform multiple steps in sequence.
+
=== Running a script ===
To run a script, all required parameters and any optional parameters
that should have values other than the default should be provided as
-`key=value` pairs on the command line. The following example shows how
-to call the `health` script with verbose output enabled:
+`key=value` pairs on the command line.
+
+The following example shows how to create a Virtual IP resource using
+the `virtual-ip` script:
........
# crm script
-run health verbose=true
+run virtual-ip id=my-virtual-ip ip=192.168.0.10
........
-
==== Single-stepping a script ====
It is possible to run a script action-by-action, with manual intervention
@@ -135,6 +222,17 @@
=== How scripts work, in detail ===
+NOTE: The implementation of cluster scripts was revised between
+`crmsh` 2.0 and `crmsh` 2.2. This section describes the revised
+cluster script format. The old format is still accepted by `crmsh`.
+
+A cluster script consists of four main sections:
+
+. The name and description of the script.
+. Any other scripts or agents included by this script, and any parameter value overrides to those provided by the included script.
+. A set of parameters accepted by the script itself, in addition to those accepted by any scripts or agents included in the script.
+. A sequence of actions which the script will perform.
+
When the script runs, the actions defined in `main.yml` as described
below are executed one at a time. Each action prescribes a
modification that is applied to the cluster. Some actions work by
@@ -230,8 +328,8 @@
to read and modify, while at the same time be compatible with JSON. To
learn more, see http:://yaml.org/[yaml.org].
-Here is an example `main.yml` file, heavily commented to explain what
-each section means.
+Here is an example `main.yml` file which wraps the resource agent
+`ocf:heartbeat:IPaddr2`.
[source,yaml]
----
@@ -243,83 +341,112 @@
# is less than 2.2, the script is assumed to be a legacy
# script (specified in the format used before crmsh 2.2).
- version: 2.2
- shortdesc: Check uptime of nodes
- longdesc: >
- This script will fetch the uptime of
- all nodes and report which node has been
- up the longest.
+ shortdesc: Virtual IP
+ category: Basic
+ include:
+ - agent: ocf:heartbeat:IPaddr2
+ name: virtual-ip
+ parameters:
+ - name: id
+ type: resource
+ required: true
+ - name: ip
+ type: ip_address
+ required: true
+ - name: cidr_netmask
+ type: integer
+ required: false
+ - name: broadcast
+ type: ip_address
+ required: false
+ ops: |
+ op start timeout="20" op stop timeout="20"
+ op monitor interval="10" timeout="20"
+ actions:
+ - include: virtual-ip
+----
+
+For a bigger example, here is the `apache` agent which includes
+multiple optional steps, the optional installation of packages,
+defines multiple cluster resources and potentially calls bash commands
+on each of the cluster nodes.
+
+[source,yaml]
+----
+# Copyright (C) 2009 Dejan Muhamedagic
+# Copyright (C) 2015 Kristoffer Gronlund
+#
+# License: GNU General Public License (GPL)
+---
+- version: 2.2
+ category: Server
+ shortdesc: Apache Webserver
+ longdesc: |
+ Configure a resource group containing a virtual IP address and
+ an instance of the Apache web server.
+
+ You can optionally configure a Filesystem resource which will be
+ mounted before the web server is started.
+
+ You can also optionally configure a database resource which will
+ be started before the web server but after mounting the optional
+ filesystem.
+ include:
+ - agent: ocf:heartbeat:apache
+ name: apache
+ longdesc: |
+ The Apache configuration file specified here must be available via the
+ same path on all cluster nodes, and Apache must be configured with
+ mod_status enabled. If in doubt, try running Apache manually via
+ its init script first, and ensure http://localhost:80/server-status is
+ accessible.
+ ops: |
+ op start timeout="40"
+ op stop timeout="60"
+ op monitor interval="10" timeout="20"
+ - script: virtual-ip
+ shortdesc: The IP address configured here will start before the Apache instance.
+ parameters:
+ - name: id
+ value: "{{id}}-vip"
+ - script: filesystem
+ shortdesc: Optional filesystem mounted before the web server is started.
+ required: false
+ - script: database
+ shortdesc: Optional database started before the web server is started.
+ required: false
parameters:
- # Parameters must have a name.
- # If a default value is provided, the parameter
- # is considered optional. Parameters without a
- # default value must be provided when running the
- # script.
- # To require a parameter to be explicitly provided
- # by the user, set required to true.
- # To require the value of the parameter to be unique
- # across the cluster, set unique to true. This setting
- # is not enforced by crmsh, but can be useful as
- # documentation.
- - name: show_all
- shortdesc: Show all uptimes
- longdesc: Enable to print all uptimes, not only a summary.
+ - name: install
+ type: boolean
+ shortdesc: Install and configure apache
value: false
- required: true
- unique: false
- steps:
- # Steps consist of a descriptive name and an action which
- # calls a script to do its work. The script should be an
- # executable file located in the same folder as main.yml.
- #
- # Script files can be written in any language, as long as
- # the cluster nodes know how to execute them.
- #
- # These are the valid actions:
- # cib:
- # Apply the given CIB configuration. The configuration
- # can refer to script variables using a mustaschioed
- # syntax described in the documentation.
- # install:
- # Install the given space-separated list of packages
- # using the system package manager.
- # service:
- # Manages system services using the system init tools.
- # The argument should be a space-separated list of
- # <service>:<state> pairs.
- # call:
- # Runs a shell command either on the current node or
- # on all nodes in the cluster. If the shell command
- # fails, the action fails as well.
- # crm:
- # Runs the given crm command line.
- # copy:
- # Copy a file to all of the cluster nodes.
- # collect:
- # Runs on all nodes. Should not perform changes, only
- # gather and return information.
- # validate:
- # Runs on the local node only. Should report problems
- # that would prevent further progress. If validate returns
- # a map of values, matching script parameters are updated
- # to reflect those values.
- # apply:
- # Runs on all nodes. Applies changes.
- # If the dry_run flag is set, script execution stops
- # before the first apply action.
- #
- # apply_local:
- # Runs on the local node only. Otherwise same as apply.
- #
- # report:
- # Runs on the local node only. Output from this step is
- # printed, not saved as input to the following steps.
- # This output does not have to be in JSON format.
- - name: Fetch uptime
- collect: fetch.py
- - name: Report uptime
- report: report.py
+ actions:
+ - install:
+ - apache2
+ shortdesc: Install the apache package
+ when: install
+ - service:
+ - apache: disable
+ shortdesc: Let cluster manage apache
+ when: install
+ - call: a2enmod status; true
+ shortdesc: Enable status module
+ when: install
+ - include: filesystem
+ - include: database
+ - include: virtual-ip
+ - include: apache
+ - cib: |
+ group g-{{id}}
+ {{filesystem:id}}
+ {{database:id}}
+ {{virtual-ip:id}}
+ {{id}}
----
+The language for referring to parameter values in `cib` actions is
+described below.
+
=== Command arguments ===
The actions that accept a command as argument must not refer to
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1444340345.59850ca/hb_report/hb_report.in new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/hb_report/hb_report.in
--- old/crmsh-2.2.0~rc3+git.1444340345.59850ca/hb_report/hb_report.in 2015-10-08 23:44:05.000000000 +0200
+++ new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/hb_report/hb_report.in 2015-10-15 07:18:06.000000000 +0200
@@ -32,15 +32,15 @@
# Important events
#
# Patterns format:
-# title extended_regexp
+# title extended_regexp
# NB: don't use spaces in titles or regular expressions!
EVENT_PATTERNS="
-membership crmd.*ccm_event.*(NEW|LOST)|pcmk_peer_update.*(lost|memb):
-quorum crmd.*crm_update_quorum:.Updating.quorum.status|crmd.*ais.disp.*quorum.(lost|ac?quir)
-pause Process.pause.detected
-resources lrmd.*rsc:(start|stop)
-stonith crmd.*te_fence_node.*Exec|stonith-ng.*log_oper.*reboot|stonithd.*(requests|(Succeeded|Failed).to.STONITH|result=)
-start_stop Configuration.validated..Starting.heartbeat|Corosync.Cluster.Engine|Executive.Service.RELEASE|crm_shutdown:.Requesting.shutdown|pcmk_shutdown:.Shutdown.complete
+membership crmd.*(NEW|LOST)|pcmk_peer_update.*(lost|memb):
+quorum crmd.*Updating.quorum.status|crmd.*ais.disp.*quorum.(lost|ac?quir)
+pause Process.pause.detected
+resources lrmd.*(start|stop)
+stonith crmd.*Exec|stonith-ng.*log_oper.*reboot|stonithd.*(requests|(Succeeded|Failed).to.STONITH|result=)
+start_stop Configuration.validated..Starting.heartbeat|Corosync.Cluster.Engine|Executive.Service.RELEASE|Requesting.shutdown|Shutdown.complete
"
init_tmpfiles
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1444340345.59850ca/modules/cibconfig.py new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/modules/cibconfig.py
--- old/crmsh-2.2.0~rc3+git.1444340345.59850ca/modules/cibconfig.py 2015-10-08 23:44:05.000000000 +0200
+++ new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/modules/cibconfig.py 2015-10-15 07:18:06.000000000 +0200
@@ -1957,7 +1957,7 @@
return utils.get_check_rc()
rc = 0
nl = self.node.findall("fencing-level")
- for target in [x.get("target") for x in nl]:
+ for target in [x.get("target") for x in nl if x.get("target") is not None]:
if target.lower() not in [id.lower() for id in cib_factory.node_id_list()]:
common_warn("%s: target %s not a node" % (self.obj_id, target))
rc = 1
@@ -2127,13 +2127,25 @@
return obj.obj_type
return None
+ def _is_node(self, nid):
+ for obj in self.objset.all_set:
+ if obj.obj_id == nid and obj.obj_type == 'node':
+ return True
+ return False
+
+ def _is_resource(self, nid):
+ for obj in self.objset.all_set:
+ if obj.obj_id == nid and obj.obj_type != 'node':
+ return True
+ return False
+
def _obj_nodes(self):
return oset([n for n in self.objset.obj_ids
- if self._obj_type(n) == 'node'])
+ if self._is_node(n)])
def _obj_resources(self):
return oset([n for n in self.objset.obj_ids
- if self._obj_type(n) != 'node'])
+ if self._is_resource(n)])
def _is_edit_valid(self, id_set, existing):
'''
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1444340345.59850ca/modules/report.py new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/modules/report.py
--- old/crmsh-2.2.0~rc3+git.1444340345.59850ca/modules/report.py 2015-10-08 23:44:05.000000000 +0200
+++ new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/modules/report.py 2015-10-15 07:18:06.000000000 +0200
@@ -16,6 +16,7 @@
from .utils import file2str, shortdate, acquire_lock, append_file, ext_cmd, shorttime
from .utils import page_string, release_lock, rmdir_r, parse_time, get_cib_attributes
from .utils import is_pcmk_118, pipe_cmd_nosudo, file_find_by_name, get_stdout, quote
+from .utils import make_datetime_naive, datetime_to_timestamp
_HAS_PARALLAX = False
try:
@@ -67,7 +68,7 @@
if t is None:
return None
elif isinstance(t, datetime.datetime):
- return convert_dt(t)
+ return datetime_to_timestamp(t)
return t
@@ -81,13 +82,14 @@
# strptime returns a time_struct
tm = time.strptime(' '.join([YEAR] + s.split()[0:3]),
"%Y %b %d %H:%M:%S")
- return time.mktime(tm)
+ ts = time.mktime(tm)
except: # try the rfc5424
try:
- return convert_dt(parse_time(s.split()[0]))
+ ts = datetime_to_timestamp(parse_time(s.split()[0]))
except Exception:
common_debug("malformed line: %s" % s)
return None
+ return ts
_syslog2node_formats = (re.compile(r'\w+ \d+ \d+:\d+:\d+ (?:\[\d+\])? (\w+)'),
@@ -274,18 +276,6 @@
return l
-def convert_dt(dt):
- """
- Convert a datetime object into a floating-point second value
- """
- try:
- ts = time.mktime(dt.timetuple())
- ts += dt.microsecond / 1000000.0
- return ts
- except:
- return None
-
-
class LogSyslog(object):
'''
Slice log, search log.
@@ -337,8 +327,10 @@
start = log_seek(f, self.from_ts)
end = log_seek(f, self.to_ts, to_end=True)
if start == -1 or end == -1:
+ common_debug("%s is a bad log" % (log))
bad_logs.append(log)
else:
+ common_debug("%s start=%s, end=%s" % (log, start, end))
self.startpos[f] = start
self.endpos[f] = end
for log in bad_logs:
@@ -435,7 +427,7 @@
def human_date(dt):
'Some human date representation. Date defaults to now.'
if not dt:
- dt = datetime.datetime.now()
+ dt = make_datetime_naive(datetime.datetime.now())
# drop microseconds
return re.sub("[.].*", "", "%s %s" % (dt.date(), dt.time()))
@@ -595,7 +587,7 @@
self.end_ts = syslog_ts(end_msg)
else:
common_warn("end of transition %s not found in logs (transition not complete yet?)" % self)
- self.end_ts = self.start_ts
+ self.end_ts = (datetime.datetime(2525, 1, 1) - datetime.datetime(1970, 1, 1)).total_seconds()
def __str__(self):
return self.get_node_file()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1444340345.59850ca/modules/scripts.py new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/modules/scripts.py
--- old/crmsh-2.2.0~rc3+git.1444340345.59850ca/modules/scripts.py 2015-10-08 23:44:05.000000000 +0200
+++ new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/modules/scripts.py 2015-10-15 07:18:06.000000000 +0200
@@ -262,27 +262,27 @@
def collect(self):
"input: shell command"
- self._run.run_command(self._nodes or 'all', self._value)
+ self._run.run_command(self._nodes or 'all', self._value, True)
self._run.record_json()
def validate(self):
"input: shell command"
- self._run.run_command(self._nodes, self._value)
+ self._run.run_command(None, self._value, True)
self._run.validate_json()
def apply(self):
"input: shell command"
- self._run.run_command(self._nodes or 'all', self._value)
+ self._run.run_command(self._nodes or 'all', self._value, True)
self._run.record_json()
def apply_local(self):
"input: shell command"
- self._run.run_command(self._nodes, self._value)
+ self._run.run_command(None, self._value, True)
self._run.record_json()
def report(self):
"input: shell command"
- self._run.run_command(self._nodes, self._value)
+ self._run.run_command(None, self._value, False)
self._run.report_result()
def call(self):
@@ -1160,7 +1160,7 @@
# TODO: remove common params?
# Pass them in a separate list of options?
# Right now these names are basically reserved..
-def _common_params():
+def common_params():
"Parameters common to all cluster scripts"
return [('nodes', None, 'List of nodes to execute the script for'),
('dry_run', 'no', 'If set, simulate execution only'),
@@ -1174,7 +1174,7 @@
def _common_param_default(name):
- for param, default, _ in _common_params():
+ for param, default, _ in common_params():
if param == name:
return default
return None
@@ -1403,7 +1403,7 @@
# pass as flags to command line
def _split_commons(params):
- ret, cdict = {}, dict([(c, d) for c, d, _ in _common_params()])
+ ret, cdict = {}, dict([(c, d) for c, d, _ in common_params()])
for key, value in params.iteritems():
if key in cdict:
cdict[key] = value
@@ -1769,15 +1769,15 @@
self.dstfile,
self.opts)
- def run_command(self, nodes, command):
+ def run_command(self, nodes, command, is_json_output):
"called by Actions"
cmdline = 'cd "%s"; ./%s' % (self.workdir, command)
if not self._update_state():
raise ValueError("Failed when updating input, aborting.")
- self.call(nodes, cmdline)
+ self.call(nodes, cmdline, is_json_output)
def copy_file(self, nodes, src, dst):
- if nodes == 'all':
+ if not self._is_local(nodes):
ok = _copy_to_all(self.printer,
self.workdir,
self.hosts,
@@ -1798,7 +1798,7 @@
"called by Actions"
if self.result is not None:
if not self.result:
- self.result = ''
+ self.result = {}
self.data.append(self.result)
self.rc = True
else:
@@ -1851,11 +1851,22 @@
prompt = "sudo password: "
self.sudo_pass = getpass.getpass(prompt=prompt)
- def call(self, nodes, cmdline):
+ def _is_local(self, nodes):
+ islocal = False
if nodes == 'all':
- self.result = self._process_remote(cmdline)
+ pass
+ elif nodes is not None and nodes != []:
+ islocal = nodes == [self.local_node_name()]
else:
- self.result = self._process_local(cmdline)
+ islocal = True
+ self.printer.debug("is_local (%s): %s" % (nodes, islocal))
+ return islocal
+
+ def call(self, nodes, cmdline, is_json_output=False):
+ if not self._is_local(nodes):
+ self.result = self._process_remote(cmdline, is_json_output)
+ else:
+ self.result = self._process_local(cmdline, is_json_output)
self.rc = self.result not in (False, None)
def execute_shell(self, nodes, cmdscript):
@@ -1872,7 +1883,7 @@
tmpf = self.str2tmp(cmdscript)
_chmodx(tmpf)
- if nodes == 'all':
+ if not self._is_local(nodes):
ok = _copy_to_remote_dirs(self.printer,
self.hosts,
tmpf,
@@ -1881,10 +1892,10 @@
self.result = False
else:
cmdline = 'cd "%s"; %s' % (self.workdir, tmpf)
- self.result = self._process_remote(cmdline)
+ self.result = self._process_remote(cmdline, False)
else:
cmdline = 'cd "%s"; %s' % (self.workdir, tmpf)
- self.result = self._process_local(cmdline)
+ self.result = self._process_local(cmdline, False)
self.rc = self.result not in (None, False)
def str2tmp(self, s):
@@ -1908,7 +1919,7 @@
return
return fn
- def _process_remote(self, cmdline):
+ def _process_remote(self, cmdline, is_json_output):
"""
Handle an action that executes on all nodes
"""
@@ -1938,20 +1949,24 @@
if rc != 0:
self.printer.error(host, "Remote error (rc=%s) %s%s" % (rc, out, err))
ok = False
- else:
+ elif is_json_output:
action_result[host] = json.loads(out)
+ else:
+ action_result[host] = out
if self.local_node:
- ret = self._process_local(cmdline)
+ ret = self._process_local(cmdline, False)
if ret is None:
ok = False
- else:
+ elif is_json_output:
action_result[self.local_node_name()] = json.loads(ret)
+ else:
+ action_result[self.local_node_name()] = ret
if ok:
self.printer.debug("Result: %s" % repr(action_result))
return action_result
return None
- def _process_local(self, cmdline):
+ def _process_local(self, cmdline, is_json_output):
"""
Handle an action that executes locally
"""
@@ -1968,7 +1983,9 @@
if rc != 0:
self.printer.error(self.local_node_name(), "Error (%d): %s" % (rc, err))
return None
- self.printer.debug("%s" % repr(out))
+ self.printer.debug("Result(local): %s" % repr(out))
+ if is_json_output:
+ out = json.loads(out)
return out
def local_node_name(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1444340345.59850ca/modules/ui_history.py new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/modules/ui_history.py
--- old/crmsh-2.2.0~rc3+git.1444340345.59850ca/modules/ui_history.py 2015-10-08 23:44:05.000000000 +0200
+++ new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/modules/ui_history.py 2015-10-15 07:18:06.000000000 +0200
@@ -18,7 +18,7 @@
from . import options
from .cibconfig import mkset_obj, cib_factory
from .msg import common_err, common_debug, common_info
-from .msg import syntax_err, bad_usage
+from .msg import syntax_err
from . import report
from . import cmd_status
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1444340345.59850ca/modules/ui_script.py new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/modules/ui_script.py
--- old/crmsh-2.2.0~rc3+git.1444340345.59850ca/modules/ui_script.py 2015-10-08 23:44:05.000000000 +0200
+++ new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/modules/ui_script.py 2015-10-15 07:18:06.000000000 +0200
@@ -138,7 +138,6 @@
ret += describe_param(p, _scoped_name(context, p['name']), all)
for i, step in enumerate(s.get('steps', [])):
ret += describe_step(icontext + [i], context, step, all)
- ret += '\n'
return ret
@@ -238,13 +237,21 @@
'shortdesc': str(script['shortdesc']),
'longdesc': scripts.format_desc(script['longdesc']),
'steps': "\n".join((describe_step([i], [], s, all) for i, s in enumerate(script['steps'])))}
- print("""%(name)s (%(category)s)
+ output = """%(name)s (%(category)s)
%(shortdesc)s
%(longdesc)s
%(steps)s
-""" % vals)
+""" % vals
+ if all:
+ output += "Common Parameters\n\n"
+ for name, defval, desc in scripts.common_params():
+ output += " %s\n" % (name)
+ output += " %s\n" % (desc)
+ if defval is not None:
+ output += " (default: %s)\n" % (defval)
+ utils.page_string(output)
@command.completers(compl.call(scripts.list_scripts))
def do_verify(self, context, name, *args):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1444340345.59850ca/modules/utils.py new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/modules/utils.py
--- old/crmsh-2.2.0~rc3+git.1444340345.59850ca/modules/utils.py 2015-10-08 23:44:05.000000000 +0200
+++ new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/modules/utils.py 2015-10-15 07:18:06.000000000 +0200
@@ -1013,11 +1013,45 @@
return [x for x in cl if x]
+def datetime_is_aware(dt):
+ """
+ Determines if a given datetime.datetime is aware.
+
+ The logic is described in Python's docs:
+ http://docs.python.org/library/datetime.html#datetime.tzinfo
+ """
+ return dt and dt.tzinfo is not None and dt.tzinfo.utcoffset(dt) is not None
+
+
+def make_datetime_naive(dt):
+ """
+ Ensures that the datetime is
+ not time zone-aware
+ """
+ if dt and datetime_is_aware(dt):
+ return dt.replace(tzinfo=None) - dt.utcoffset()
+ return dt
+
+
+def datetime_to_timestamp(dt):
+ """
+ Convert a datetime object into a floating-point second value
+ """
+ try:
+ return (make_datetime_naive(dt) - datetime.datetime(1970, 1, 1)).total_seconds()
+ except Exception as e:
+ common_err("datetime_to_timestamp error: %s" % (e))
+ return None
+
+
def parse_time(t):
'''
Try to make sense of the user provided time spec.
Use dateutil if available, otherwise strptime.
Return the datetime value.
+
+ Also does time zone elimination by passing the datetime
+ through a timestamp conversion if necessary
'''
try:
import dateutil.parser
@@ -1032,6 +1066,11 @@
except ValueError, msg:
common_err("no dateutil, please provide times as printed by date(1)")
return None
+ if datetime_is_aware(dt):
+ ts = datetime_to_timestamp(dt)
+ if ts is None:
+ return None
+ dt = datetime.datetime.fromtimestamp(ts)
return dt
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/testcases/commit.exp new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/test/testcases/commit.exp
--- old/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/testcases/commit.exp 2015-10-08 23:44:05.000000000 +0200
+++ new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/test/testcases/commit.exp 2015-10-15 07:18:06.000000000 +0200
@@ -27,17 +27,17 @@
.INP: commit
.EXT crm_resource --show-metadata ocf:heartbeat:Dummy
.INP: rename p3 pp3
-INFO: 21: resource references in colocation:cl1 updated
-INFO: 21: resource references in location:l1 updated
-INFO: 21: resource references in order:o1 updated
+INFO: 21: modified colocation:cl1 from p3 to pp3
+INFO: 21: modified location:l1 from p3 to pp3
+INFO: 21: modified order:o1 from p3 to pp3
.INP: commit
.INP: rename pp3 p3
-INFO: 23: resource references in colocation:cl1 updated
-INFO: 23: resource references in location:l1 updated
-INFO: 23: resource references in order:o1 updated
+INFO: 23: modified colocation:cl1 from pp3 to p3
+INFO: 23: modified location:l1 from pp3 to p3
+INFO: 23: modified order:o1 from pp3 to p3
.INP: delete c1
-INFO: 24: resource references in colocation:cl1 updated
-INFO: 24: resource references in order:o1 updated
+INFO: 24: modified colocation:cl1 from c1 to g1
+INFO: 24: modified order:o1 from c1 to g1
.INP: commit
.INP: group g2 d1 d2
.INP: commit
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/testcases/delete.exp new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/test/testcases/delete.exp
--- old/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/testcases/delete.exp 2015-10-08 23:44:05.000000000 +0200
+++ new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/test/testcases/delete.exp 2015-10-15 07:18:06.000000000 +0200
@@ -21,7 +21,7 @@
location d1-pref d1 100: node1
.INP: _test
.INP: rename d1 p1
-INFO: 13: resource references in location:d1-pref updated
+INFO: 13: modified location:d1-pref from d1 to p1
.INP: show
node node1
primitive d2 ocf:pacemaker:Dummy
@@ -62,7 +62,7 @@
.INP: primitive d2 ocf:pacemaker:Dummy
.INP: _test
.INP: group g1 d2 d1
-INFO: 29: resource references in location:d1-pref updated
+INFO: 29: modified location:d1-pref from d1 to g1
.INP: delete d2
.INP: show
node node1
@@ -76,7 +76,7 @@
location d1-pref g1 100: node1
.INP: _test
.INP: delete g1
-INFO: 33: resource references in location:d1-pref updated
+INFO: 33: modified location:d1-pref from g1 to d1
.INP: show
node node1
primitive d1 ocf:pacemaker:Dummy
@@ -94,12 +94,12 @@
.INP: # delete a group which is in a clone
.INP: primitive d2 ocf:pacemaker:Dummy
.INP: group g1 d2 d1
-INFO: 38: resource references in location:d1-pref updated
+INFO: 38: modified location:d1-pref from d1 to g1
.INP: clone c1 g1
-INFO: 39: resource references in location:d1-pref updated
+INFO: 39: modified location:d1-pref from g1 to c1
.INP: delete g1
-INFO: 40: resource references in location:d1-pref updated
-INFO: 40: resource references in location:d1-pref updated
+INFO: 40: modified location:d1-pref from c1 to g1
+INFO: 40: modified location:d1-pref from g1 to d2
.INP: show
node node1
primitive d1 ocf:pacemaker:Dummy
@@ -112,14 +112,14 @@
location d1-pref d2 100: node1
.INP: _test
.INP: group g1 d2 d1
-INFO: 43: resource references in location:d1-pref updated
+INFO: 43: modified location:d1-pref from d2 to g1
.INP: clone c1 g1
-INFO: 44: resource references in location:d1-pref updated
+INFO: 44: modified location:d1-pref from g1 to c1
.INP: _test
.INP: # delete group from a clone (again)
.INP: delete g1
-INFO: 47: resource references in location:d1-pref updated
-INFO: 47: resource references in location:d1-pref updated
+INFO: 47: modified location:d1-pref from c1 to g1
+INFO: 47: modified location:d1-pref from g1 to d2
.INP: show
node node1
primitive d1 ocf:pacemaker:Dummy
@@ -132,13 +132,13 @@
location d1-pref d2 100: node1
.INP: _test
.INP: group g1 d2 d1
-INFO: 50: resource references in location:d1-pref updated
+INFO: 50: modified location:d1-pref from d2 to g1
.INP: clone c1 g1
-INFO: 51: resource references in location:d1-pref updated
+INFO: 51: modified location:d1-pref from g1 to c1
.INP: # delete primitive and its group and their clone
.INP: delete d2 d1 c1 g1
-INFO: 53: resource references in location:d1-pref updated
-INFO: 53: resource references in location:d1-pref updated
+INFO: 53: modified location:d1-pref from c1 to g1
+INFO: 53: modified location:d1-pref from g1 to d2
INFO: 53: hanging location:d1-pref deleted
.INP: show
node node1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/testcases/resource.exp new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/test/testcases/resource.exp
--- old/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/testcases/resource.exp 2015-10-08 23:44:05.000000000 +0200
+++ new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/test/testcases/resource.exp 2015-10-15 07:18:06.000000000 +0200
@@ -130,6 +130,7 @@
.SETENV showobj=p0
.TRY resource param p0 set a0 "1 2 3"
.EXT crm_resource -r 'p0' -p 'a0' -v '1 2 3'
+
Set 'p0' option: id=p0-instance_attributes-a0 set=p0-instance_attributes name=a0=1 2 3
.INP: configure
.INP: _regtest on
@@ -194,6 +195,7 @@
.TRY resource meta p0 set m0 123
.EXT crm_resource --meta -r 'p0' -p 'm0' -v '123'
+
Set 'p0' option: id=p0-meta_attributes-m0 set=p0-meta_attributes name=m0=123
.INP: configure
.INP: _regtest on
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/testcases/scripts.exp new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/test/testcases/scripts.exp
--- old/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/testcases/scripts.exp 2015-10-08 23:44:05.000000000 +0200
+++ new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/test/testcases/scripts.exp 2015-10-15 07:18:06.000000000 +0200
@@ -245,7 +245,6 @@
Broadcast address
-
.INP: verify virtual-ip id=foo ip=10.0.0.5
1. Configure cluster resources
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/unittests/test_bugs.py new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/test/unittests/test_bugs.py
--- old/crmsh-2.2.0~rc3+git.1444340345.59850ca/test/unittests/test_bugs.py 2015-10-08 23:44:05.000000000 +0200
+++ new/crmsh-2.2.0~rc3+git.1444854254.fc37f7f/test/unittests/test_bugs.py 2015-10-15 07:18:06.000000000 +0200
@@ -655,6 +655,50 @@
@with_setup(setup_func, teardown_func)
+def test_id_collision_breakage_3():
+ from crmsh import clidisplay
+
+ obj = cibconfig.mkset_obj()
+ assert obj is not None
+ with clidisplay.nopretty():
+ original_cib = obj.repr()
+ print original_cib
+
+ obj = cibconfig.mkset_obj()
+ assert obj is not None
+ ok = obj.save("""node node1
+primitive node1 Dummy params fake=something
+ """)
+ assert ok
+
+ print "** baseline"
+ obj = cibconfig.mkset_obj()
+ assert obj is not None
+ with clidisplay.nopretty():
+ print obj.repr()
+
+ obj = cibconfig.mkset_obj()
+ assert obj is not None
+ ok = obj.save("""primitive node1 Dummy params fake=something-else
+ """, no_remove=True, method='update')
+ assert ok
+
+ print "** end"
+
+ obj = cibconfig.mkset_obj()
+ assert obj is not None
+ ok = obj.save(original_cib, no_remove=False, method='replace')
+ assert ok
+ obj = cibconfig.mkset_obj()
+ with clidisplay.nopretty():
+ print "*** ORIGINAL"
+ print original_cib
+ print "*** NOW"
+ print obj.repr()
+ assert original_cib == obj.repr()
+
+
+@with_setup(setup_func, teardown_func)
def test_id_collision_breakage_2():
from crmsh import clidisplay
@@ -747,3 +791,22 @@
print "*** NOW"
print obj.repr()
assert original_cib == obj.repr()
+
+
+@with_setup(setup_func, teardown_func)
+def test_bug_110():
+ """
+ configuring attribute-based fencing-topology
+ """
+ factory.create_object(*"primitive stonith-libvirt stonith:null".split())
+ factory.create_object(*"primitive fence-nova stonith:null".split())
+ cmd = "fencing_topology attr:OpenStack-role=compute stonith-libvirt,fence-nova".split()
+ ok = factory.create_object(*cmd)
+ assert ok
+ obj = cibconfig.mkset_obj()
+ assert obj is not None
+
+ for o in obj.obj_set:
+ if o.node.tag == 'fencing-topology':
+ assert o.check_sanity() == 0
+