# HG changeset patch # User Dejan Muhamedagic # 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 : [:] [[:] ...] + [loss-policy=] + + 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 score-type: [:] [:] [symmetrical=]""" return self.__conf_object(cmd,*args) + def conf_rsc_ticket(self,cmd,*args): + """usage: rsc_ticket : [:] [[:] ...] + [loss-policy=]""" + return self.__conf_object(cmd,*args) def conf_property(self,cmd,*args): "usage: property [$id=]