summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xqpid/cpp/etc/qpidd-primary.in2
-rwxr-xr-xqpid/cpp/src/tests/ha_test.py2
-rw-r--r--qpid/doc/book/src/cpp-broker/Active-Passive-Cluster.xml39
-rwxr-xr-xqpid/tools/src/py/qpid-ha68
4 files changed, 76 insertions, 35 deletions
diff --git a/qpid/cpp/etc/qpidd-primary.in b/qpid/cpp/etc/qpidd-primary.in
index e79d8cc09c..86bc76d5e1 100755
--- a/qpid/cpp/etc/qpidd-primary.in
+++ b/qpid/cpp/etc/qpidd-primary.in
@@ -76,7 +76,7 @@ start() {
lock
$QPID_INIT start primary || return $?
echo -n $"Promoting to primary: "
- err=$($QPID_HA promote 2>&1)
+ err=$($QPID_HA promote --cluster-manager 2>&1)
RETVAL=$?
[ $RETVAL = 0 ] && success || { echo -n "$err: "; failure; }
echo
diff --git a/qpid/cpp/src/tests/ha_test.py b/qpid/cpp/src/tests/ha_test.py
index f09a4cd573..9adad45ed4 100755
--- a/qpid/cpp/src/tests/ha_test.py
+++ b/qpid/cpp/src/tests/ha_test.py
@@ -176,7 +176,7 @@ acl allow all all
except Exception, e:
raise Exception("Error in qpid_ha -b %s %s: %s"%(url, args,e))
- def promote(self): self.ready(); self.qpid_ha(["promote"])
+ def promote(self): self.ready(); self.qpid_ha(["promote", "--cluster-manager"])
def replicate(self, from_broker, queue): self.qpid_ha(["replicate", from_broker, queue])
@property
def agent(self):
diff --git a/qpid/doc/book/src/cpp-broker/Active-Passive-Cluster.xml b/qpid/doc/book/src/cpp-broker/Active-Passive-Cluster.xml
index fbdf321c56..26d701df64 100644
--- a/qpid/doc/book/src/cpp-broker/Active-Passive-Cluster.xml
+++ b/qpid/doc/book/src/cpp-broker/Active-Passive-Cluster.xml
@@ -927,29 +927,35 @@ acl allow <replaceable>USER</replaceable>@QPID all all
</itemizedlist>
</para>
<para>
- The <command>qpid-ha</command> command allows you to check if a broker is primary,
- and to promote a backup to primary.
+ The <command>qpid-ha</command> command allows you to check if a broker is
+ primary, and to promote a backup to primary.
</para>
<para>
To test if a broker is the primary:
- <programlisting>
-qpid-ha -b <replaceable>broker-address</replaceable> status --expect=primary
- </programlisting>
- This command will return 0 if the broker at <replaceable>broker-address</replaceable>
- is the primary, non-0 otherwise.
+ </para>
+ <programlisting>qpid-ha -b <replaceable>broker-address</replaceable> status --expect=primary</programlisting>
+ <para>
+ This will return 0 if the broker at <replaceable>broker-address</replaceable> is the primary,
+ non-0 otherwise.
</para>
<para>
To promote a broker to primary:
- <programlisting>
-qpid-ha -b <replaceable>broker-address</replaceable> promote
- </programlisting>
+ <programlisting>qpid-ha --cluster-manager -b <replaceable>broker-address</replaceable> promote</programlisting>
+ </para>
+ <para>
+ Note that <literal>promote</literal> is considered a "cluster manager
+ only" command. Incorrect use of <literal>promote</literal> outside of the
+ cluster manager could create a cluster with multiple primaries. Such a
+ cluster will malfunction and lose data. "Cluster manager only" commands
+ are not accessible in <command>qpid-ha</command> without the
+ <literal>--cluster-manager</literal> option.
</para>
<para>
- <command>qpid-ha --help</command> gives information on other commands and options available.
- You can also use <command>qpid-ha</command> to manually examine and promote brokers. This
- can be useful for testing failover scenarios without having to set up a full resource manager,
- or to simulate a cluster on a single node. For deployment, a resource manager is required.
+ To list the full set of commands use:
</para>
+ <programlisting>
+qpid-ha --cluster-manager --help
+ </programlisting>
</section>
<section id ="ha-store">
@@ -999,8 +1005,9 @@ warning Client closed connection with 320: ACL denied anonymous@QPID creating a
<para>
Set the HA security configuration and ACL file as described in <xref
linkend="ha-security"/>. Once the cluster is running and the primary is
- promoted , run <literal>qpid-ha</literal> to make sure that the brokers
- are running as one cluster.
+ promoted , run:
+ <programlisting>qpid-ha status --all</programlisting>
+ to make sure that the brokers are running as one cluster.
</para>
</section>
<section id="ha-troubleshoot-slow-recovery">
diff --git a/qpid/tools/src/py/qpid-ha b/qpid/tools/src/py/qpid-ha
index 640463c09a..d7b2cb48f6 100755
--- a/qpid/tools/src/py/qpid-ha
+++ b/qpid/tools/src/py/qpid-ha
@@ -50,12 +50,17 @@ def find_qpidd_conf():
if p.isfile(conf): return conf
return None
-class Command:
- commands = {}
+class Command(object):
+ """
+ Common options and logic for all commands. Subclasses provide additional
+ options and execution logic.
+ """
+
+ commands = []
def __init__(self, name, help, arg_names=[], connect_agent=True):
"""@param connect_agent true if we should establish a QMF agent connection"""
- Command.commands[name] = self
+ Command.commands.append(self)
self.name = name
self.connect_agent = connect_agent
self.arg_names = arg_names
@@ -141,6 +146,21 @@ class Command:
def do_execute(self, qmf_broker, opts, args):
raise Exception("Command '%s' is not yet implemented"%self.name)
+class ManagerCommand(Command):
+ """
+ Base for commands that should only be used by a cluster manager tool that ensures
+ cluster consistency.
+ """
+
+ manager_commands = [] # Cluster manager commands
+
+ def __init__(self, name, help, arg_names=[], connect_agent=True):
+ """@param connect_agent true if we should establish a QMF agent connection"""
+ super(ManagerCommand, self).__init__(name, "[Cluster manager only] "+help, arg_names, connect_agent)
+ self.commands.remove(self) # Not a user command
+ self.manager_commands.append(self)
+
+
class PingCmd(Command):
def __init__(self):
Command.__init__(self, "ping","Check if the broker is alive and responding", connect_agent=False)
@@ -148,11 +168,13 @@ class PingCmd(Command):
self.connection.session() # Make sure we can establish a session.
PingCmd()
-class PromoteCmd(Command):
+class PromoteCmd(ManagerCommand):
def __init__(self):
- Command.__init__(self, "promote","Promote a backup broker to primary. Note this command will not detect if another broker is already primary, and creating a second primary will make the cluster inconsistent. It is up to the caller (normally the cluster resource manager) to ensure there is only one primary.")
+ super(PromoteCmd, self).__init__("promote", "Promote a backup broker to primary. This command should *only* be used by a cluster manager (such as rgmanager) that ensures only one broker is primary at a time. Promoting more than one broker to primary at the same time will make the cluster inconsistent and will cause data loss and unexpected behavior.")
+
def do_execute(self, qmf_broker, ha_broker, opts, args):
qmf_broker._method("promote", {}, HA_BROKER, timeout=opts.timeout)
+
PromoteCmd()
@@ -226,25 +248,37 @@ def print_usage(prog):
print " %-12s %s."%(name, help.split(".")[0])
print "\nFor help with a command type: %s <command> --help\n"%prog
-def find_command(args):
+def find_command(args, commands):
"""Find a command among the arguments and options"""
for arg in args:
- if arg in Command.commands:
- return Command.commands[arg]
+ cmds = [cmd for cmd in commands if cmd.name == arg]
+ if cmds: return cmds[0]
return None
def main_except(argv):
"""This version of main raises exceptions"""
- args=argv[1:]
- if args and args[0] == "--help-all":
- for c in Command.commands.itervalues():
- c.op.print_help(); print
+ args = argv[1:]
+ commands = Command.commands
+ if "--cluster-manager" in args:
+ commands += ManagerCommand.manager_commands
+ args.remove("--cluster-manager")
+ if "--help-all" in args:
+ for c in commands: c.op.print_help(); print
else:
- command = find_command(args)
- if not command:
- print_usage(os.path.basename(argv[0]));
- raise Exception("Command not found")
- command.execute(args)
+ command = find_command(args, commands)
+ if command:
+ command.execute(args)
+ else:
+ # Check for attempt to use a manager command without --cluster-manager
+ command = find_command(args, ManagerCommand.manager_commands)
+ if command:
+ message="""'%s' should only be called by the cluster manager.
+Incorrect use of '%s' will cause cluster malfunction.
+To call from a cluster manager use '%s --cluster-manager'. """
+ raise Exception(message%((command.name,)*3))
+ else:
+ print_usage(os.path.basename(argv[0]));
+ raise Exception("No valid command")
def main(argv):
try: