0440703030
- PE: Demote from Master does not clear previous errors - crmd: Prevent secondary DC fencing resulting from CIB updates that are lost due to elections - crmd: Log duplicate DC detection as a WARNING not ERROR - crmd: Bug lf#2632 - Correctly handle nodes that return faster than stonith - Core: Treat GNUTLS_E_UNEXPECTED_PACKET_LENGTH as normal termination of a TLS session - cib: Call gnutls_bye() and shutdown() when disconnecting from remote TLS connections - cib: Remove disconnected remote connections from mainloop - cib: Attempt a graceful sign-off for remote TLS connections - Core: Ensure there is sufficient space for EOS when building short-form option strings (prevents segfault) - Core: Fix variable expansion in pkg-config files - PE: Resolve memory leak reported by valgrind - PE: Fix memory leak for re-allocated resources reported by valgrind - PE: Improve the merging with template's operations - crmd: Allow nodes to fence themselves if they're the last one standing (lf#2584) - stonith: Add an API call for listing installed agents - stonith: Allow the fencing history to be queried - stonith: Ensure completed operations are recorded as such in the history - stonith: Support --quiet to display just the seconds since epoch at which a node was last shot - stonith: Serialize actions for a given device - stonith: Add missing entries to stonith_error2string() (missing OBS-URL: https://build.opensuse.org/package/show/network:ha-clustering:Factory/pacemaker?expand=0&rev=18
327 lines
13 KiB
Diff
327 lines
13 KiB
Diff
# HG changeset patch
|
|
# User Dejan Muhamedagic <dejan@hello-penguin.com>
|
|
# Date 1314783705 -7200
|
|
# Node ID 825cb3e79d7bc1c4ac30468f8c028c9129d00541
|
|
# Parent f77e52725f2d98c219d8b22208da0b89b3d42112
|
|
High: Shell: support for rsc_ticket
|
|
|
|
diff --git a/doc/crm.8.txt b/doc/crm.8.txt
|
|
--- a/doc/crm.8.txt
|
|
+++ b/doc/crm.8.txt
|
|
@@ -1639,6 +1639,34 @@ Example:
|
|
order o1 inf: A ( B C )
|
|
...............
|
|
|
|
+[[cmdhelp_configure_rsc_ticket,resources ticket dependency]]
|
|
+==== `rsc_ticket`
|
|
+
|
|
+This constraint expresses dependency of resources on cluster-wide
|
|
+attributes, also known as tickets. Tickets are mainly used in
|
|
+geo-clusters, which consist of multiple sites. A ticket may be
|
|
+granted to a site, thus allowing resources to run there.
|
|
+
|
|
+The `loss-policy` attribute specifies what happens to the
|
|
+resource (or resources) if the ticket is revoked. The default is
|
|
+either `stop` or `demote` depending on whether a resource is
|
|
+multi-state.
|
|
+
|
|
+Usage:
|
|
+...............
|
|
+ rsc_ticket <id> <ticket_id>: <rsc>[:<role>] [<rsc>[:<role>] ...]
|
|
+ [loss-policy=<loss_policy_action>]
|
|
+
|
|
+ loss_policy_action :: stop | demote | fence | freeze
|
|
+...............
|
|
+Example:
|
|
+...............
|
|
+ rsc_ticket ticket-A_public-ip ticket-A: public-ip
|
|
+ rsc_ticket ticket-A_bigdb ticket-A: bigdb loss-policy=fence
|
|
+ rsc_ticket ticket-B_storage ticket-B: drbd-a:Master drbd-b:Master
|
|
+...............
|
|
+
|
|
+
|
|
[[cmdhelp_configure_property,set a cluster property]]
|
|
==== `property`
|
|
|
|
diff --git a/shell/modules/cibconfig.py b/shell/modules/cibconfig.py
|
|
--- a/shell/modules/cibconfig.py
|
|
+++ b/shell/modules/cibconfig.py
|
|
@@ -1243,7 +1243,7 @@ class CibSimpleConstraint(CibObject):
|
|
if node.getElementsByTagName("resource_set"):
|
|
col = rsc_set_constraint(node,obj_type)
|
|
else:
|
|
- col = two_rsc_constraint(node,obj_type)
|
|
+ col = simple_rsc_constraint(node,obj_type)
|
|
if not col:
|
|
return None
|
|
symm = node.getAttribute("symmetrical")
|
|
@@ -1264,6 +1264,27 @@ class CibSimpleConstraint(CibObject):
|
|
remove_id_used_attributes(oldnode)
|
|
return headnode
|
|
|
|
+class CibRscTicket(CibSimpleConstraint):
|
|
+ '''
|
|
+ rsc_ticket constraint.
|
|
+ '''
|
|
+ def repr_cli_head(self,node):
|
|
+ obj_type = vars.cib_cli_map[node.tagName]
|
|
+ node_id = node.getAttribute("id")
|
|
+ s = cli_display.keyword(obj_type)
|
|
+ id = cli_display.id(node_id)
|
|
+ ticket = cli_display.ticket(node.getAttribute("ticket"))
|
|
+ if node.getElementsByTagName("resource_set"):
|
|
+ col = rsc_set_constraint(node,obj_type)
|
|
+ else:
|
|
+ col = simple_rsc_constraint(node,obj_type)
|
|
+ if not col:
|
|
+ return None
|
|
+ a = node.getAttribute("loss-policy")
|
|
+ if a:
|
|
+ col.append("loss-policy=%s" % a)
|
|
+ return "%s %s %s: %s" % (s,id,ticket,' '.join(col))
|
|
+
|
|
class CibProperty(CibObject):
|
|
'''
|
|
Cluster properties.
|
|
@@ -1371,6 +1392,7 @@ cib_object_map = {
|
|
"rsc_location": ( "location", CibLocation, "constraints" ),
|
|
"rsc_colocation": ( "colocation", CibSimpleConstraint, "constraints" ),
|
|
"rsc_order": ( "order", CibSimpleConstraint, "constraints" ),
|
|
+ "rsc_ticket": ( "rsc_ticket", CibRscTicket, "constraints" ),
|
|
"cluster_property_set": ( "property", CibProperty, "crm_config", "cib-bootstrap-options" ),
|
|
"rsc_defaults": ( "rsc_defaults", CibProperty, "rsc_defaults", "rsc-options" ),
|
|
"op_defaults": ( "op_defaults", CibProperty, "op_defaults", "op-options" ),
|
|
diff --git a/shell/modules/clidisplay.py b/shell/modules/clidisplay.py
|
|
--- a/shell/modules/clidisplay.py
|
|
+++ b/shell/modules/clidisplay.py
|
|
@@ -62,6 +62,8 @@ class CliDisplay(Singleton):
|
|
return self.otherword(4, s)
|
|
def score(self, s):
|
|
return self.otherword(5, s)
|
|
+ def ticket(self, s):
|
|
+ return self.otherword(5, s)
|
|
|
|
user_prefs = UserPrefs.getInstance()
|
|
vars = Vars.getInstance()
|
|
diff --git a/shell/modules/cliformat.py b/shell/modules/cliformat.py
|
|
--- a/shell/modules/cliformat.py
|
|
+++ b/shell/modules/cliformat.py
|
|
@@ -226,22 +226,25 @@ def rsc_set_constraint(node,obj_type):
|
|
action = n.getAttribute("action")
|
|
for r in n.getElementsByTagName("resource_ref"):
|
|
rsc = cli_display.rscref(r.getAttribute("id"))
|
|
- q = (obj_type == "colocation") and role or action
|
|
+ q = (obj_type == "order") and action or role
|
|
col.append(q and "%s:%s"%(rsc,q) or rsc)
|
|
cnt += 1
|
|
if not sequential:
|
|
col.append(")")
|
|
- if cnt <= 2: # a degenerate thingie
|
|
+ if (obj_type != "rsc_ticket" and cnt <= 2) or \
|
|
+ (obj_type == "rsc_ticket" and cnt <= 1): # a degenerate thingie
|
|
col.insert(0,"_rsc_set_")
|
|
return col
|
|
-def two_rsc_constraint(node,obj_type):
|
|
+def simple_rsc_constraint(node,obj_type):
|
|
col = []
|
|
if obj_type == "colocation":
|
|
col.append(mkrscrole(node,"rsc"))
|
|
col.append(mkrscrole(node,"with-rsc"))
|
|
- else:
|
|
+ elif obj_type == "order":
|
|
col.append(mkrscaction(node,"first"))
|
|
col.append(mkrscaction(node,"then"))
|
|
+ else: # rsc_ticket
|
|
+ col.append(mkrscrole(node,"rsc"))
|
|
return col
|
|
|
|
# this pre (or post)-processing is oversimplified
|
|
diff --git a/shell/modules/completion.py b/shell/modules/completion.py
|
|
--- a/shell/modules/completion.py
|
|
+++ b/shell/modules/completion.py
|
|
@@ -467,6 +467,7 @@ completer_lists = {
|
|
"location" : (null_list,rsc_id_list),
|
|
"colocation" : (null_list,null_list,rsc_id_list,loop),
|
|
"order" : (null_list,null_list,rsc_id_list,loop),
|
|
+ "rsc_ticket" : (null_list,null_list,rsc_id_list,loop),
|
|
"property" : (property_complete,loop),
|
|
"rsc_defaults" : (prim_complete_meta,loop),
|
|
"op_defaults" : (op_attr_list,loop),
|
|
diff --git a/shell/modules/parse.py b/shell/modules/parse.py
|
|
--- a/shell/modules/parse.py
|
|
+++ b/shell/modules/parse.py
|
|
@@ -178,6 +178,15 @@ def parse_op(s):
|
|
head_pl.append(["name",s[0]])
|
|
return cli_list
|
|
|
|
+def cli_parse_ticket(ticket,pl):
|
|
+ if ticket.endswith(':'):
|
|
+ ticket = ticket.rstrip(':')
|
|
+ else:
|
|
+ syntax_err(ticket, context = 'rsc_ticket')
|
|
+ return False
|
|
+ pl.append(["ticket",ticket])
|
|
+ return True
|
|
+
|
|
def cli_parse_score(score,pl,noattr = False):
|
|
if score.endswith(':'):
|
|
score = score.rstrip(':')
|
|
@@ -197,6 +206,7 @@ def cli_parse_score(score,pl,noattr = Fa
|
|
else:
|
|
pl.append(["score-attribute",score])
|
|
return True
|
|
+
|
|
def is_binary_op(s):
|
|
l = s.split(':')
|
|
if len(l) == 2:
|
|
@@ -302,13 +312,13 @@ def parse_location(s):
|
|
return False
|
|
return cli_list
|
|
|
|
-def cli_opt_symmetrical(p,pl):
|
|
+def cli_opt_attribute(type, p, pl, attr):
|
|
if not p:
|
|
return True
|
|
pl1 = []
|
|
cli_parse_attr([p],pl1)
|
|
- if len(pl1) != 1 or not find_value(pl1,"symmetrical"):
|
|
- syntax_err(p,context = "order")
|
|
+ if len(pl1) != 1 or not find_value(pl1, attr):
|
|
+ syntax_err(p,context = type)
|
|
return False
|
|
pl += pl1
|
|
return True
|
|
@@ -490,7 +500,33 @@ def parse_order(s):
|
|
resource_set_obj = ResourceSet(type,s[3:],cli_list)
|
|
if not resource_set_obj.parse():
|
|
return False
|
|
- if not cli_opt_symmetrical(symm,head_pl):
|
|
+ if not cli_opt_attribute(type, symm, head_pl, "symmetrical"):
|
|
+ return False
|
|
+ return cli_list
|
|
+
|
|
+def parse_rsc_ticket(s):
|
|
+ cli_list = []
|
|
+ head_pl = []
|
|
+ type = "rsc_ticket"
|
|
+ cli_list.append([s[0],head_pl])
|
|
+ if len(s) < 4:
|
|
+ syntax_err(s,context = "rsc_ticket")
|
|
+ return False
|
|
+ head_pl.append(["id",s[1]])
|
|
+ if not cli_parse_ticket(s[2],head_pl):
|
|
+ return False
|
|
+ # save loss-policy for later (if it exists)
|
|
+ loss_policy = ""
|
|
+ if is_attribute(s[len(s)-1],"loss-policy"):
|
|
+ loss_policy = s.pop()
|
|
+ if len(s) == 4:
|
|
+ if not cli_parse_rsc_role(s[3], head_pl):
|
|
+ return False
|
|
+ else:
|
|
+ resource_set_obj = ResourceSet(type, s[3:], cli_list)
|
|
+ if not resource_set_obj.parse():
|
|
+ return False
|
|
+ if not cli_opt_attribute(type, loss_policy, head_pl, attr = "loss-policy"):
|
|
return False
|
|
return cli_list
|
|
|
|
@@ -501,6 +537,8 @@ def parse_constraint(s):
|
|
return parse_colocation(s)
|
|
elif keyword_cmp(s[0], "order"):
|
|
return parse_order(s)
|
|
+ elif keyword_cmp(s[0], "rsc_ticket"):
|
|
+ return parse_rsc_ticket(s)
|
|
def parse_property(s):
|
|
cli_list = []
|
|
head_pl = []
|
|
@@ -708,6 +746,7 @@ class CliParser(object):
|
|
"colocation": (3,parse_constraint),
|
|
"collocation": (3,parse_constraint),
|
|
"order": (3,parse_constraint),
|
|
+ "rsc_ticket": (3,parse_constraint),
|
|
"monitor": (3,parse_op),
|
|
"node": (2,parse_node),
|
|
"property": (2,parse_property),
|
|
diff --git a/shell/modules/ui.py.in b/shell/modules/ui.py.in
|
|
--- a/shell/modules/ui.py.in
|
|
+++ b/shell/modules/ui.py.in
|
|
@@ -1400,6 +1400,7 @@ cluster.
|
|
self.cmd_table["location"] = (self.conf_location,(2,),1,0)
|
|
self.cmd_table["colocation"] = (self.conf_colocation,(2,),1,0)
|
|
self.cmd_table["order"] = (self.conf_order,(2,),1,0)
|
|
+ self.cmd_table["rsc_ticket"] = (self.conf_rsc_ticket,(2,),1,0)
|
|
self.cmd_table["property"] = (self.conf_property,(1,),1,0)
|
|
self.cmd_table["rsc_defaults"] = (self.conf_rsc_defaults,(1,),1,0)
|
|
self.cmd_table["op_defaults"] = (self.conf_op_defaults,(1,),1,0)
|
|
@@ -1632,6 +1633,10 @@ cluster.
|
|
"""usage: order <id> score-type: <first-rsc>[:<action>] <then-rsc>[:<action>]
|
|
[symmetrical=<bool>]"""
|
|
return self.__conf_object(cmd,*args)
|
|
+ def conf_rsc_ticket(self,cmd,*args):
|
|
+ """usage: rsc_ticket <id> <ticket_id>: <rsc>[:<role>] [<rsc>[:<role>] ...]
|
|
+ [loss-policy=<loss_policy_action>]"""
|
|
+ return self.__conf_object(cmd,*args)
|
|
def conf_property(self,cmd,*args):
|
|
"usage: property [$id=<set_id>] <option>=<value>"
|
|
return self.__conf_object(cmd,*args)
|
|
diff --git a/shell/modules/vars.py.in b/shell/modules/vars.py.in
|
|
--- a/shell/modules/vars.py.in
|
|
+++ b/shell/modules/vars.py.in
|
|
@@ -53,6 +53,7 @@ class Vars(Singleton):
|
|
"rsc_location": "location",
|
|
"rsc_colocation": "colocation",
|
|
"rsc_order": "order",
|
|
+ "rsc_ticket": "rsc_ticket",
|
|
"cluster_property_set": "property",
|
|
"rsc_defaults": "rsc_defaults",
|
|
"op_defaults": "op_defaults",
|
|
@@ -62,13 +63,13 @@ class Vars(Singleton):
|
|
container_tags = ("group", "clone", "ms", "master")
|
|
clonems_tags = ("clone", "ms", "master")
|
|
resource_tags = ("primitive","group","clone","ms","master")
|
|
- constraint_tags = ("rsc_location","rsc_colocation","rsc_order")
|
|
+ constraint_tags = ("rsc_location","rsc_colocation","rsc_order","rsc_ticket")
|
|
constraint_rsc_refs = ("rsc","with-rsc","first","then")
|
|
children_tags = ("group", "primitive")
|
|
nvpairs_tags = ("meta_attributes", "instance_attributes", "utilization")
|
|
defaults_tags = ("rsc_defaults","op_defaults")
|
|
resource_cli_names = ("primitive","group","clone","ms","master")
|
|
- constraint_cli_names = ("location","colocation","collocation","order")
|
|
+ constraint_cli_names = ("location","colocation","collocation","order","rsc_ticket")
|
|
nvset_cli_names = ("property","rsc_defaults","op_defaults")
|
|
op_cli_names = ("monitor", "start", "stop", "migrate_to", "migrate_from","promote","demote","notify")
|
|
ra_operations = ("probe", "monitor", "start", "stop",
|
|
diff --git a/shell/modules/xmlutil.py b/shell/modules/xmlutil.py
|
|
--- a/shell/modules/xmlutil.py
|
|
+++ b/shell/modules/xmlutil.py
|
|
@@ -520,7 +520,8 @@ def mss(node_list):
|
|
def constraints(node_list):
|
|
return filter_on_tag(node_list,"rsc_location") \
|
|
+ filter_on_tag(node_list,"rsc_colocation") \
|
|
- + filter_on_tag(node_list,"rsc_order")
|
|
+ + filter_on_tag(node_list,"rsc_order") \
|
|
+ + filter_on_tag(node_list,"rsc_ticket")
|
|
def properties(node_list):
|
|
return filter_on_tag(node_list,"cluster_property_set") \
|
|
+ filter_on_tag(node_list,"rsc_defaults") \
|
|
@@ -562,7 +563,8 @@ def constraints_cli(node_list):
|
|
return filter_on_type(node_list,"location") \
|
|
+ filter_on_type(node_list,"colocation") \
|
|
+ filter_on_type(node_list,"collocation") \
|
|
- + filter_on_type(node_list,"order")
|
|
+ + filter_on_type(node_list,"order") \
|
|
+ + filter_on_type(node_list,"rsc_ticket")
|
|
def properties_cli(cl):
|
|
return filter_on_type(cl,"property") \
|
|
+ filter_on_type(cl,"rsc_defaults") \
|
|
@@ -601,6 +603,8 @@ def referenced_resources(node):
|
|
elif xml_obj_type == "rsc_order":
|
|
node_list = node.getElementsByTagName("first") + \
|
|
node.getElementsByTagName("then")
|
|
+ elif xml_obj_type == "rsc_ticket":
|
|
+ node_list = node.getElementsByTagName("rsc")
|
|
return [x.getAttribute("id") for x in node_list]
|
|
|
|
def rename_id(node,old_id,new_id):
|