1
# Copyright (C) 2008 Dejan Muhamedagic <dmuhamedagic@suse.de>
3
# This program is free software; you can redistribute it and/or
4
# modify it under the terms of the GNU General Public
5
# License as published by the Free Software Foundation; either
6
# version 2.1 of the License, or (at your option) any later version.
8
# This software is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
# General Public License for more details.
13
# You should have received a copy of the GNU General Public
14
# License along with this library; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25
from help import HelpSystem, cmd_help
27
from levels import Levels
28
from cibconfig import mkset_obj, CibFactory
29
from cibstatus import CibStatus
30
from template import LoadTemplate
31
from cliformat import nvpairs2list
37
def cmd_end(cmd,dir = ".."):
40
def cmd_exit(cmd,rc = 0):
41
"Exit the crm program"
43
if options.interactive and not options.batch:
46
from readline import write_history_file
47
write_history_file(vars.hist_file)
50
for f in vars.tmpfiles:
54
class UserInterface(object):
56
Stuff common to all user interface classes.
58
global_cmd_aliases = {
59
"quit": ("bye","exit"),
63
self.help_table = odict()
64
self.cmd_table = odict()
65
self.cmd_table["help"] = (self.help,(0,1),0,0)
66
self.cmd_table["quit"] = (self.exit,(0,0),0,0)
67
self.cmd_table["end"] = (self.end,(0,1),0,0)
68
self.cmd_aliases = self.global_cmd_aliases.copy()
69
if options.interactive:
70
self.help_table = help_sys.load_level(self.lvl_name)
71
def end_game(self, no_questions_asked = False):
73
def help(self,cmd,topic = ''):
74
"usage: help [<topic>]"
75
if not self.help_table:
76
self.help_table = help_sys.load_level(self.lvl_name)
77
setup_help_aliases(self)
78
cmd_help(self.help_table,topic)
79
def end(self,cmd,dir = ".."):
88
class CliOptions(UserInterface):
90
Manage user preferences
93
desc_short = "user preferences"
95
Several user preferences are available. Note that it is possible
96
to save the preferences to a startup file.
99
UserInterface.__init__(self)
100
self.cmd_table["skill-level"] = (self.set_skill_level,(1,1),0,0)
101
self.cmd_table["editor"] = (self.set_editor,(1,1),0,0)
102
self.cmd_table["pager"] = (self.set_pager,(1,1),0,0)
103
self.cmd_table["user"] = (self.set_crm_user,(0,1),0,0)
104
self.cmd_table["output"] = (self.set_output,(1,1),0,0)
105
self.cmd_table["colorscheme"] = (self.set_colors,(1,1),0,0)
106
self.cmd_table["check-frequency"] = (self.set_check_frequency,(1,1),0,0)
107
self.cmd_table["check-mode"] = (self.set_check_mode,(1,1),0,0)
108
self.cmd_table["sort-elements"] = (self.set_sort_elements,(1,1),0,0)
109
self.cmd_table["wait"] = (self.set_wait,(1,1),0,0)
110
self.cmd_table["save"] = (self.save_options,(0,0),0,0)
111
self.cmd_table["show"] = (self.show_options,(0,0),0,0)
113
def set_skill_level(self,cmd,skill_level):
114
"""usage: skill-level <level>
115
level: operator | administrator | expert"""
116
return user_prefs.set_skill_level(skill_level)
117
def set_editor(self,cmd,prog):
118
"usage: editor <program>"
119
return user_prefs.set_editor(prog)
120
def set_pager(self,cmd,prog):
121
"usage: pager <program>"
122
return user_prefs.set_pager(prog)
123
def set_crm_user(self,cmd,user = ''):
124
"usage: user [<crm_user>]"
125
return user_prefs.set_crm_user(user)
126
def set_output(self,cmd,otypes):
127
"usage: output <type>"
128
return user_prefs.set_output(otypes)
129
def set_colors(self,cmd,scheme):
130
"usage: colorscheme <colors>"
131
return user_prefs.set_colors(scheme)
132
def set_check_frequency(self,cmd,freq):
133
"usage: check-frequence <freq>"
134
return user_prefs.set_check_freq(freq)
135
def set_check_mode(self,cmd,mode):
136
"usage: check-mode <mode>"
137
return user_prefs.set_check_mode(mode)
138
def set_sort_elements(self,cmd,opt):
139
"usage: sort-elements {yes|no}"
140
if not verify_boolean(opt):
141
common_err("%s: bad boolean option"%opt)
143
return user_prefs.set_sort_elems(opt)
144
def set_wait(self,cmd,opt):
145
"usage: wait {yes|no}"
146
if not verify_boolean(opt):
147
common_err("%s: bad boolean option"%opt)
149
return user_prefs.set_wait(opt)
150
def show_options(self,cmd):
152
return user_prefs.write_rc(sys.stdout)
153
def save_options(self,cmd):
155
return user_prefs.save_options(vars.rc_file)
156
def end_game(self, no_questions_asked = False):
157
if no_questions_asked and not options.interactive:
158
self.save_options("save")
160
class CibShadow(UserInterface):
162
CIB shadow management class
165
desc_short = "manage shadow CIBs"
167
A shadow CIB is a regular cluster configuration which is kept in
168
a file. The CRM and the CRM tools may manage a shadow CIB in the
169
same way as the live CIB (i.e. the current cluster configuration).
170
A shadow CIB may be applied to the cluster in one step.
172
extcmd = ">/dev/null </dev/null crm_shadow"
173
extcmd_stdout = "</dev/null crm_shadow"
175
UserInterface.__init__(self)
176
self.cmd_table["new"] = (self.new,(1,3),1,0)
177
self.cmd_table["delete"] = (self.delete,(1,1),1,0)
178
self.cmd_table["reset"] = (self.reset,(1,1),1,0)
179
self.cmd_table["commit"] = (self.commit,(1,1),1,1)
180
self.cmd_table["use"] = (self.use,(0,2),1,0)
181
self.cmd_table["diff"] = (self.diff,(0,0),1,0)
182
self.cmd_table["list"] = (self.list,(0,0),1,0)
183
self.cmd_table["import"] = (self.pe_import,(1,2),1,0)
184
self.cmd_table["cibstatus"] = StatusMgmt
189
ext_cmd("%s 2>&1" % self.extcmd)
191
no_prog_err(self.extcmd)
194
def new(self,cmd,name,*args):
195
"usage: new <shadow_cib> [withstatus] [force] [empty]"
196
if not is_filename_sane(name):
199
if not par in ("force","--force","withstatus","empty"):
200
syntax_err((cmd,name,par), context = 'new')
203
new_cmd = "%s -e '%s'" % (self.extcmd,name)
205
new_cmd = "%s -c '%s'" % (self.extcmd,name)
206
if user_prefs.get_force() or "force" in args or "--force" in args:
207
new_cmd = "%s --force" % new_cmd
208
if ext_cmd(new_cmd) == 0:
209
common_info("%s shadow CIB created"%name)
211
if "withstatus" in args:
212
cib_status.load("shadow:%s" % name)
213
def _find_pe(self,infile):
215
for p in ("%s/%s", "%s/%s.bz2", "%s/pe-*-%s.bz2"):
216
fl = glob.glob(p % (vars.pe_dir,infile))
220
common_err("no %s pe input file"%infile)
223
common_err("more than one %s pe input file: %s" % \
224
(infile,' '.join(fl)))
227
def pe_import(self,cmd,infile,name = None):
228
"usage: import {<file>|<number>} [<shadow>]"
229
if name and not is_filename_sane(name):
232
if not os.access(infile,os.F_OK):
234
common_err("%s: no such file"%infile)
236
infile = self._find_pe(infile)
240
name = os.path.basename(infile)
245
common_err("open: %s"%msg)
249
# decompresed and rename shadow if it ends with .bz2
250
if infile.endswith(".bz2"):
251
name = name.replace(".bz2","")
252
s = bz2.decompress(s)
253
# copy input to the shadow
255
f = open(shadowfile(name), "w")
257
common_err("open: %s"%msg)
261
# use the shadow and load the status from there
262
return self.use("use",name,"withstatus")
263
def delete(self,cmd,name):
264
"usage: delete <shadow_cib>"
265
if not is_filename_sane(name):
267
if vars.cib_in_use == name:
268
common_err("%s shadow CIB is in use"%name)
270
if ext_cmd("%s -D '%s' --force" % (self.extcmd,name)) == 0:
271
common_info("%s shadow CIB deleted"%name)
273
common_err("failed to delete %s shadow CIB"%name)
275
def reset(self,cmd,name):
276
"usage: reset <shadow_cib>"
277
if not is_filename_sane(name):
279
if ext_cmd("%s -r '%s'" % (self.extcmd,name)) == 0:
280
common_info("copied live CIB to %s"%name)
282
common_err("failed to copy live CIB to %s"%name)
284
def commit(self,cmd,name):
285
"usage: commit <shadow_cib>"
286
if not is_filename_sane(name):
288
if ext_cmd("%s -C '%s' --force" % (self.extcmd,name)) == 0:
289
common_info("commited '%s' shadow CIB to the cluster"%name)
291
common_err("failed to commit the %s shadow CIB"%name)
295
s = get_stdout(add_sudo("%s -d" % self.extcmd_stdout))
299
if options.regression_tests:
300
for t in listshadows():
303
multicolumn(listshadows())
304
def _use(self,name,withstatus):
305
# Choose a shadow cib for further changes. If the name
306
# provided is empty, then choose the live (cluster) cib.
307
# Don't allow ' in shadow names
308
if not name or name == "live":
309
os.unsetenv(vars.shadow_envvar)
312
cib_status.load("live")
314
os.putenv(vars.shadow_envvar,name)
315
vars.cib_in_use = name
317
cib_status.load("shadow:%s" % name)
318
def use(self,cmd,name = '', withstatus = ''):
319
"usage: use [<shadow_cib>] [withstatus]"
320
# check the name argument
321
if name and not is_filename_sane(name):
323
if name and name != "live":
324
if not os.access(shadowfile(name),os.F_OK):
325
common_err("%s: no such shadow CIB"%name)
327
if withstatus and withstatus != "withstatus":
328
syntax_err((cmd,withstatus), context = 'use')
330
# If invoked from configure
331
# take special precautions
333
prev_level = levels.previous().myname()
336
if prev_level != "cibconfig":
337
self._use(name,withstatus)
339
if not cib_factory.has_cib_changed():
340
self._use(name,withstatus)
341
# new CIB: refresh the CIB factory
342
cib_factory.refresh()
344
saved_cib = vars.cib_in_use
345
self._use(name,'') # don't load the status yet
346
if not cib_factory.is_current_cib_equal(silent = True):
347
# user made changes and now wants to switch to a
348
# different and unequal CIB; we refuse to cooperate
349
common_err("the requested CIB is different from the current one")
350
if user_prefs.get_force():
351
common_info("CIB overwrite forced")
352
elif not ask("All changes will be dropped. Do you want to proceed?"):
353
self._use(saved_cib,'') # revert to the previous CIB
355
self._use(name,withstatus) # now load the status too
358
def check_transition(inp,state,possible_l):
359
if not state in possible_l:
360
common_err("input (%s) in wrong state %s" % (inp,state))
363
class Template(UserInterface):
365
Configuration templates.
367
lvl_name = "template"
369
UserInterface.__init__(self)
370
self.cmd_table["new"] = (self.new,(2,),1,0)
371
self.cmd_table["load"] = (self.load,(0,1),1,0)
372
self.cmd_table["edit"] = (self.edit,(0,1),1,0)
373
self.cmd_table["delete"] = (self.delete,(1,2),1,0)
374
self.cmd_table["show"] = (self.show,(0,1),0,0)
375
self.cmd_table["apply"] = (self.apply,(0,2),1,0)
376
self.cmd_table["list"] = (self.list,(0,1),0,0)
381
'''Create the conf directory, link to templates'''
382
if not os.path.isdir(vars.tmpl_conf_dir):
384
os.makedirs(vars.tmpl_conf_dir)
386
common_err("makedirs: %s"%msg)
388
def get_depends(self,tmpl):
389
'''return a list of required templates'''
390
# Not used. May need it later.
392
tf = open("%s/%s" % (vars.tmpl_dir, tmpl),"r")
394
common_err("open: %s"%msg)
399
if len(a) >= 2 and a[0] == '%depends_on':
403
def replace_params(self,s,user_data):
405
for i in range(len(s)):
408
# is parameter in the word?
409
pos = word.find('%' + p)
412
endpos = pos + len('%' + p)
413
# and it isn't part of another word?
414
if re.match("[A-Za-z0-9]", word[endpos:endpos+1]):
416
# if the value contains a space or
417
# it is a value of an attribute
418
# put quotes around it
419
if user_data[p].find(' ') >= 0 or word[pos-1:pos] == '=':
420
v = '"' + user_data[p] + '"'
423
word = word.replace('%' + p, v)
424
change = True # we did replace something
433
def generate(self,l,user_data):
434
'''replace parameters (user_data) and generate output
440
s = self.replace_params(s,user_data)
442
piece2.append(' '.join(s))
444
l2.append(' \\\n\t'.join(piece2))
446
def process(self,config = ''):
447
'''Create a cli configuration from the current config'''
449
f = open("%s/%s" % (vars.tmpl_conf_dir, config or self.curr_conf),'r')
451
common_err("open: %s"%msg)
457
START = 0; PFX = 1; DATA = 2; GENERATE = 3
459
err_buf.start_tmp_lineno()
462
err_buf.incr_lineno()
463
if inp.startswith('#'):
465
if type(inp) == type(u''):
466
inp = inp.encode('ascii')
470
except ValueError, msg:
476
if state == GENERATE and piece:
479
elif s[0] in ("%name","%depends_on","%suggests"):
482
if check_transition(inp,state,(START,DATA)) and len(s) == 2:
485
elif s[0] == "%required":
486
if check_transition(inp,state,(PFX,)):
489
elif s[0] == "%optional":
490
if check_transition(inp,state,(PFX,DATA)):
495
common_warn("user data in wrong state %s" % state)
497
common_warn("parameter name missing")
500
common_err("required parameter %s not set" % s[1])
503
user_data["%s:%s" % (pfx,s[1])] = s[2]
505
common_err("%s: syntax error" % inp)
506
elif s[0] == "%generate":
507
if check_transition(inp,state,(DATA,)):
510
elif state == GENERATE:
514
common_err("<%s> unexpected" % inp)
517
err_buf.stop_tmp_lineno()
521
return self.generate(l,user_data)
522
def new(self,cmd,name,*args):
523
"usage: new <config> <template> [<template> ...] [params name=value ...]"
524
if not is_filename_sane(name):
526
if os.path.isfile("%s/%s" % (vars.tmpl_conf_dir, name)):
527
common_err("config %s exists; delete it first" % name)
529
lt = LoadTemplate(name)
534
if mode == 0 and s == "params":
540
syntax_err(args, context = 'new')
544
elif not lt.load_template(s):
547
lt.post_process(params)
548
if not rc or not lt.write_config(name):
550
self.curr_conf = name
551
def config_exists(self,name):
552
if not is_filename_sane(name):
554
if not os.path.isfile("%s/%s" % (vars.tmpl_conf_dir, name)):
555
common_err("%s: no such config" % name)
558
def delete(self,cmd,name,force = ''):
559
"usage: delete <config> [force]"
561
if force != "force" and force != "--force":
562
syntax_err((cmd,force), context = 'delete')
564
if not self.config_exists(name):
566
if name == self.curr_conf:
567
if not force and not user_prefs.get_force() and \
568
not ask("Do you really want to remove config %s which is in use?" % self.curr_conf):
572
os.remove("%s/%s" % (vars.tmpl_conf_dir, name))
573
def load(self,cmd,name = ''):
574
"usage: load [<config>]"
578
if not self.config_exists(name):
580
self.curr_conf = name
581
def edit(self,cmd,name = ''):
582
"usage: edit [<config>]"
583
if not name and not self.curr_conf:
584
common_err("please load a config first")
587
if not self.config_exists(name):
589
edit_file("%s/%s" % (vars.tmpl_conf_dir, name))
591
edit_file("%s/%s" % (vars.tmpl_conf_dir, self.curr_conf))
592
def show(self,cmd,name = ''):
593
"usage: show [<config>]"
594
if not name and not self.curr_conf:
595
common_err("please load a config first")
598
if not self.config_exists(name):
600
print self.process(name)
603
def apply(self,cmd,*args):
604
"usage: apply [<method>] [<config>]"
609
if args[0] in ("replace","update"):
614
if not name and not self.curr_conf:
615
common_err("please load a config first")
618
if not self.config_exists(name):
620
s = self.process(name)
628
set_obj = mkset_obj("NOOBJ")
629
rc = set_obj.import_file(method,tmp)
633
def list(self,cmd,templates = ''):
634
"usage: list [templates]"
635
if templates == "templates":
636
multicolumn(listtemplates())
638
multicolumn(listconfigs())
640
def manage_attr(cmd,attr_ext_commands,*args):
642
bad_usage(cmd,' '.join(args))
646
attr_cmd = attr_ext_commands[args[1]]
648
bad_usage(cmd,' '.join(args))
651
bad_usage(cmd,' '.join(args))
655
if not is_name_sane(args[0]) \
656
or not is_name_sane(args[2]) \
657
or not is_value_sane(args[3]):
659
return ext_cmd(attr_cmd%(args[0],args[2],args[3])) == 0
661
bad_usage(cmd,' '.join(args))
663
elif args[1] in ('delete','show'):
665
if not is_name_sane(args[0]) \
666
or not is_name_sane(args[2]):
668
return ext_cmd(attr_cmd%(args[0],args[2])) == 0
670
bad_usage(cmd,' '.join(args))
673
bad_usage(cmd,' '.join(args))
676
def rm_meta_attribute(node,attr,l):
678
Build a list of nvpair nodes which contain attribute
679
(recursively in all children resources)
681
for c in node.childNodes:
682
if not is_element(c):
684
if c.tagName == "meta_attributes":
685
nvpair = get_attr_in_set(c,attr)
688
elif is_child_rsc(c) and not c.parentNode.tagName == "group":
689
rm_meta_attribute(c,attr,l)
690
def clean_inferior_attributes(node,attr):
691
'Remove attr from all resources below'
693
for c in node.childNodes:
695
rm_meta_attribute(c,attr,l)
697
def set_deep_meta_attr(attr,value,rsc_id):
699
If the referenced rsc is a primitive that belongs to a group,
700
then set its attribute.
701
Otherwise, go up to the topmost resource which contains this
702
resource and set the attribute there and remove all the
703
attributes which may be set in its children.
705
target_node = rsc2node(rsc_id)
707
common_error("resource %s does not exist" % rsc_id)
709
if not (target_node.tagName == "primitive" and \
710
target_node.parentNode.tagName == "group"):
711
target_node = get_topmost_rsc(target_node)
712
clean_inferior_attributes(target_node,attr)
713
for n in get_set_nodes(target_node,"meta_attributes",1):
714
set_attr(n,attr,value)
715
return commit_rsc(target_node)
717
def get_max_clone(id):
718
v = get_meta_param(id,"clone-max")
722
cnt = len(listnodes())
724
def cleanup_resource(rsc,node = ''):
725
if not is_name_sane(rsc) or not is_name_sane(node):
728
rc = ext_cmd(RscMgmt.rsc_cleanup_all%(rsc)) == 0
730
rc = ext_cmd(RscMgmt.rsc_cleanup%(rsc,node)) == 0
733
class RscMgmt(UserInterface):
735
Resources management class
737
lvl_name = "resource"
738
desc_short = "resources management"
740
Everything related to resources management is available at this
741
level. Most commands are implemented using the crm_resource(8)
744
rsc_status_all = "crm_resource -L"
745
rsc_status = "crm_resource -W -r '%s'"
746
rsc_showxml = "crm_resource -q -r '%s'"
747
rsc_setrole = "crm_resource --meta -r '%s' -p target-role -v '%s'"
748
rsc_migrate = "crm_resource -M -r '%s' %s"
749
rsc_unmigrate = "crm_resource -U -r '%s'"
750
rsc_cleanup = "crm_resource -C -r '%s' -H '%s'"
751
rsc_cleanup_all = "crm_resource -C -r '%s'"
753
'set': "crm_resource -r '%s' -p '%s' -v '%s'",
754
'delete': "crm_resource -r '%s' -d '%s'",
755
'show': "crm_resource -r '%s' -g '%s'",
758
'set': "crm_resource --meta -r '%s' -p '%s' -v '%s'",
759
'delete': "crm_resource --meta -r '%s' -d '%s'",
760
'show': "crm_resource --meta -r '%s' -g '%s'",
763
'set': "crm_attribute -t status -n 'fail-count-%s' -N '%s' -v '%s' -d 0",
764
'delete': "crm_attribute -t status -n 'fail-count-%s' -N '%s' -D -d 0",
765
'show': "crm_attribute -t status -n 'fail-count-%s' -N '%s' -G -d 0",
768
'set': "crm_resource -z -r '%s' -p '%s' -v '%s'",
769
'delete': "crm_resource -z -r '%s' -d '%s'",
770
'show': "crm_resource -z -r '%s' -g '%s'",
772
rsc_refresh = "crm_resource -R"
773
rsc_refresh_node = "crm_resource -R -H '%s'"
774
rsc_reprobe = "crm_resource -P"
775
rsc_reprobe_node = "crm_resource -P -H '%s'"
777
UserInterface.__init__(self)
778
self.cmd_table["status"] = (self.status,(0,1),0,0)
779
self.cmd_table["start"] = (self.start,(1,1),0,1)
780
self.cmd_table["stop"] = (self.stop,(1,1),0,1)
781
self.cmd_table["restart"] = (self.restart,(1,1),0,1)
782
self.cmd_table["promote"] = (self.promote,(1,1),0,1)
783
self.cmd_table["demote"] = (self.demote,(1,1),0,1)
784
self.cmd_table["manage"] = (self.manage,(1,1),0,0)
785
self.cmd_table["unmanage"] = (self.unmanage,(1,1),0,0)
786
self.cmd_table["migrate"] = (self.migrate,(1,4),0,1)
787
self.cmd_table["unmigrate"] = (self.unmigrate,(1,1),0,1)
788
self.cmd_table["param"] = (self.param,(3,4),1,1)
789
self.cmd_table["meta"] = (self.meta,(3,4),1,1)
790
self.cmd_table["utilization"] = (self.utilization,(3,4),1,1)
791
self.cmd_table["failcount"] = (self.failcount,(3,4),0,0)
792
self.cmd_table["cleanup"] = (self.cleanup,(1,2),1,1)
793
self.cmd_table["refresh"] = (self.refresh,(0,1),0,0)
794
self.cmd_table["reprobe"] = (self.reprobe,(0,1),0,1)
795
self.cmd_aliases.update({
796
"status": ("show","list",),
797
"migrate": ("move",),
798
"unmigrate": ("unmove",),
801
def status(self,cmd,rsc = None):
802
"usage: status [<rsc>]"
804
if not is_name_sane(rsc):
806
return ext_cmd(self.rsc_status % rsc) == 0
808
return ext_cmd(self.rsc_status_all) == 0
809
def start(self,cmd,rsc):
811
if not is_name_sane(rsc):
813
return set_deep_meta_attr("target-role","Started",rsc)
814
def restart(self,cmd,rsc):
815
"usage: restart <rsc>"
816
if not is_name_sane(rsc):
818
common_info("ordering %s to stop" % rsc)
819
if not self.stop("stop",rsc):
821
if not wait4dc("stop", not options.batch):
823
common_info("ordering %s to start" % rsc)
824
return self.start("start",rsc)
825
def stop(self,cmd,rsc):
827
if not is_name_sane(rsc):
829
return set_deep_meta_attr("target-role","Stopped",rsc)
830
def promote(self,cmd,rsc):
831
"usage: promote <rsc>"
832
if not is_name_sane(rsc):
834
if not is_rsc_ms(rsc):
835
common_err("%s is not a master-slave resource" % rsc)
837
return ext_cmd(self.rsc_setrole%(rsc,"Master")) == 0
838
def demote(self,cmd,rsc):
839
"usage: demote <rsc>"
840
if not is_name_sane(rsc):
842
if not is_rsc_ms(rsc):
843
common_err("%s is not a master-slave resource" % rsc)
845
return ext_cmd(self.rsc_setrole%(rsc,"Slave")) == 0
846
def manage(self,cmd,rsc):
847
"usage: manage <rsc>"
848
if not is_name_sane(rsc):
850
return set_deep_meta_attr("is-managed","true",rsc)
851
def unmanage(self,cmd,rsc):
852
"usage: unmanage <rsc>"
853
if not is_name_sane(rsc):
855
return set_deep_meta_attr("is-managed","false",rsc)
856
def migrate(self,cmd,*args):
857
"""usage: migrate <rsc> [<node>] [<lifetime>] [force]"""
859
if not is_name_sane(rsc):
865
if not args[1] in listnodes():
866
if args[1] == "force":
873
if not args[1] in listnodes():
878
if args[2] == "force":
883
if not is_name_sane(args[1]):
887
if not args[3] == "force":
888
syntax_err((cmd,force))
893
opts = "--node='%s'" % node
895
opts = "%s --lifetime='%s'" % (opts,lifetime)
896
if force or user_prefs.get_force():
897
opts = "%s --force" % opts
898
return ext_cmd(self.rsc_migrate % (rsc,opts)) == 0
899
def unmigrate(self,cmd,rsc):
900
"usage: unmigrate <rsc>"
901
if not is_name_sane(rsc):
903
return ext_cmd(self.rsc_unmigrate%rsc) == 0
904
def cleanup(self,cmd,*args):
905
"usage: cleanup <rsc> [<node>]"
906
# Cleanup a resource on a node. Omit node to cleanup on
908
if len(args) == 2: # remove
909
return cleanup_resource(args[0],args[1])
911
return cleanup_resource(args[0])
912
def failcount(self,cmd,*args):
914
failcount <rsc> set <node> <value>
915
failcount <rsc> delete <node>
916
failcount <rsc> show <node>"""
917
d = lambda: manage_attr(cmd,self.rsc_failcount,*args)
919
def param(self,cmd,*args):
921
param <rsc> set <param> <value>
922
param <rsc> delete <param>
923
param <rsc> show <param>"""
924
d = lambda: manage_attr(cmd,self.rsc_param,*args)
926
def meta(self,cmd,*args):
928
meta <rsc> set <attr> <value>
929
meta <rsc> delete <attr>
930
meta <rsc> show <attr>"""
931
d = lambda: manage_attr(cmd,self.rsc_meta,*args)
933
def utilization(self,cmd,*args):
935
utilization <rsc> set <attr> <value>
936
utilization <rsc> delete <attr>
937
utilization <rsc> show <attr>"""
938
d = lambda: manage_attr(cmd,self.rsc_utilization,*args)
940
def refresh(self,cmd,*args):
941
'usage: refresh [<node>]'
943
if not is_name_sane(args[0]):
945
return ext_cmd(self.rsc_refresh_node%args[0]) == 0
947
return ext_cmd(self.rsc_refresh) == 0
948
def reprobe(self,cmd,*args):
949
'usage: reprobe [<node>]'
951
if not is_name_sane(args[0]):
953
return ext_cmd(self.rsc_reprobe_node%args[0]) == 0
955
return ext_cmd(self.rsc_reprobe) == 0
957
def print_node(uname,id,node_type,other,inst_attr,offline):
959
Try to pretty print a node from the cib. Sth like:
964
s_offline = offline and "(offline)" or ""
966
print "%s: %s%s" % (uname,node_type,s_offline)
968
print "%s(%s): %s%s" % (uname,id,node_type,s_offline)
970
print "\t%s: %s" % (a,other[a])
971
for a,v in inst_attr:
972
print "\t%s: %s" % (a,v)
974
class NodeMgmt(UserInterface):
976
Nodes management class
979
desc_short = "nodes management"
981
A few node related tasks such as node standby are implemented
984
node_standby = "crm_attribute -N '%s' -n standby -v '%s' %s"
985
node_delete = "cibadmin -D -o nodes -X '<node uname=\"%s\"/>'"
986
node_delete_status = "cibadmin -D -o status -X '<node_state uname=\"%s\"/>'"
987
node_clear_state = "cibadmin %s -o status --xml-text '<node_state id=\"%s\" uname=\"%s\" ha=\"active\" in_ccm=\"false\" crmd=\"offline\" join=\"member\" expected=\"down\" crm-debug-origin=\"manual_clear\" shutdown=\"0\"/>'"
988
hb_delnode = "@datadir@/heartbeat/hb_delnode '%s'"
989
crm_node = "crm_node"
990
node_fence = "crm_attribute -t status -U '%s' -n terminate -v true"
993
'set': "crm_attribute -t nodes -U '%s' -n '%s' -v '%s'",
994
'delete': "crm_attribute -D -t nodes -U '%s' -n '%s'",
995
'show': "crm_attribute -G -t nodes -U '%s' -n '%s'",
998
'set': "crm_attribute -t status -U '%s' -n '%s' -v '%s'",
999
'delete': "crm_attribute -D -t status -U '%s' -n '%s'",
1000
'show': "crm_attribute -G -t status -U '%s' -n '%s'",
1002
node_utilization = {
1003
'set': "crm_attribute -z -t nodes -U '%s' -n '%s' -v '%s'",
1004
'delete': "crm_attribute -z -D -t nodes -U '%s' -n '%s'",
1005
'show': "crm_attribute -z -G -t nodes -U '%s' -n '%s'",
1008
UserInterface.__init__(self)
1009
self.cmd_table["status"] = (self.status,(0,1),0,0)
1010
self.cmd_table["show"] = (self.show,(0,1),0,0)
1011
self.cmd_table["standby"] = (self.standby,(0,2),0,1)
1012
self.cmd_table["online"] = (self.online,(0,1),0,1)
1013
self.cmd_table["fence"] = (self.fence,(1,1),0,1)
1014
self.cmd_table["delete"] = (self.delete,(1,1),0,0)
1015
self.cmd_table["clearstate"] = (self.clearstate,(1,1),0,1)
1016
self.cmd_table["attribute"] = (self.attribute,(3,4),0,1)
1017
self.cmd_table["utilization"] = (self.utilization,(3,4),0,1)
1018
self.cmd_table["status-attr"] = (self.status_attr,(3,4),0,1)
1019
self.cmd_aliases.update({
1023
def status(self,cmd,node = None):
1024
'usage: status [<node>]'
1025
return ext_cmd("%s -o nodes"%cib_dump) == 0
1026
def show(self,cmd,node = None):
1027
'usage: show [<node>]'
1031
nodes_node = get_conf_elem(doc, "nodes")
1032
status = get_conf_elem(doc, "status")
1035
for c in nodes_node.childNodes:
1036
if not is_element(c) or c.tagName != "node":
1038
if node and c.getAttribute("uname") != node:
1040
type = uname = id = ""
1043
for attr in c.attributes.keys():
1044
v = c.getAttribute(attr)
1047
elif attr == "uname":
1053
for c2 in c.childNodes:
1054
if not is_element(c2):
1056
if c2.tagName == "instance_attributes":
1057
inst_attr += nvpairs2list(c2)
1059
for c2 in status.getElementsByTagName("node_state"):
1060
if uname != c2.getAttribute("uname"):
1062
offline = c2.getAttribute("crmd") == "offline"
1063
print_node(uname,id,type,other,inst_attr,offline)
1064
def standby(self,cmd,*args):
1065
'usage: standby [<node>] [<lifetime>]'
1069
node = vars.this_node
1071
if not args[0] in listnodes():
1072
node = vars.this_node
1076
elif len(args) == 2:
1079
if lifetime not in (None,"reboot","forever"):
1080
common_err("bad lifetime: %s" % lifetime)
1082
if not is_name_sane(node):
1086
opts = "--lifetime='%s'" % lifetime
1088
opts = "--lifetime='forever'"
1089
return ext_cmd(self.node_standby%(node,"on",opts)) == 0
1090
def online(self,cmd,node = None):
1091
'usage: online [<node>]'
1093
node = vars.this_node
1094
if not is_name_sane(node):
1096
return ext_cmd(self.node_standby%(node,"off","--lifetime='forever'")) == 0
1097
def fence(self,cmd,node):
1098
'usage: fence <node>'
1100
node = vars.this_node
1101
if not is_name_sane(node):
1103
if not user_prefs.get_force() and \
1104
not ask("Do you really want to shoot %s?" % node):
1106
return ext_cmd(self.node_fence%(node)) == 0
1107
def clearstate(self,cmd,node):
1108
'usage: clearstate <node>'
1109
if not is_name_sane(node):
1111
if not user_prefs.get_force() and \
1112
not ask("Do you really want to drop state for node %s?" % node):
1114
return ext_cmd(self.node_clear_state%("-M -c",node,node)) == 0 and \
1115
ext_cmd(self.node_clear_state%("-R",node,node)) == 0
1116
def delete(self,cmd,node):
1117
'usage: delete <node>'
1118
if not is_name_sane(node):
1120
if not node in listnodes():
1121
common_err("node %s not found in the CIB" % node)
1124
if cluster_stack() == "heartbeat":
1125
rc = ext_cmd(self.hb_delnode%node) == 0
1128
for s in stdout2list("%s -l" % self.crm_node):
1131
common_warn("%s bad format: %s" % (self.crm_node,s))
1133
# fmt: id uname status
1134
# remove only those in state "lost"
1136
node_states[a[2]] = 1
1138
ec = ext_cmd("%s --force -R %s" % (self.crm_node,a[0]))
1140
common_warn('"%s --force -R %s" failed, rc=%d' % (self.crm_node,a[0],ec))
1142
common_info("node %s not found by %s" % (node,self.crm_node))
1143
elif "member" in node_states:
1144
common_info("node %s appears to be still active" % node)
1145
common_info("check output of %s -l" % self.crm_node)
1147
elif not "lost" in node_states:
1148
common_err("node %s's state not recognized: %s" % (node,node_states.keys()))
1149
common_info("check output of %s -l" % self.crm_node)
1152
if ext_cmd(self.node_delete%node) != 0 or \
1153
ext_cmd(self.node_delete_status%node) != 0:
1154
common_err("failed to remove %s from the CIB" % node)
1157
common_info("node %s deleted" % node)
1159
common_err('failed to delete node %s' % node)
1161
def attribute(self,cmd,*args):
1163
attribute <node> set <rsc> <value>
1164
attribute <node> delete <rsc>
1165
attribute <node> show <rsc>"""
1166
d = lambda: manage_attr(cmd,self.node_attr,*args)
1168
def utilization(self,cmd,*args):
1170
utilization <node> set <rsc> <value>
1171
utilization <node> delete <rsc>
1172
utilization <node> show <rsc>"""
1173
d = lambda: manage_attr(cmd,self.node_utilization,*args)
1175
def status_attr(self,cmd,*args):
1177
status-attr <node> set <rsc> <value>
1178
status-attr <node> delete <rsc>
1179
status-attr <node> show <rsc>"""
1180
d = lambda: manage_attr(cmd,self.node_status,*args)
1183
class RA(UserInterface):
1185
CIB shadow management class
1188
desc_short = "resource agents information center"
1190
This level contains commands which show various information about
1191
the installed resource agents. It is available both at the top
1192
level and at the `configure` level.
1194
provider_classes = ["ocf"]
1196
UserInterface.__init__(self)
1197
self.cmd_table["classes"] = (self.classes,(0,0),0,0)
1198
self.cmd_table["list"] = (self.list,(1,2),1,0)
1199
self.cmd_table["providers"] = (self.providers,(1,2),1,0)
1200
self.cmd_table["meta"] = (self.meta,(1,3),1,0)
1201
self.cmd_aliases.update({
1205
def classes(self,cmd):
1207
for c in ra_classes():
1208
if c in self.provider_classes:
1209
print "%s / %s" % (c,' '.join(ra_providers_all(c)))
1212
def providers(self,cmd,ra_type,ra_class = "ocf"):
1213
"usage: providers <ra> [<class>]"
1214
print ' '.join(ra_providers(ra_type,ra_class))
1215
def list(self,cmd,c,p = None):
1216
"usage: list <class> [<provider>]"
1217
if not c in ra_classes():
1218
common_err("class %s does not exist" % c)
1220
if p and not p in ra_providers_all(c):
1221
common_err("there is no provider %s for class %s" % (p,c))
1223
if options.regression_tests:
1224
for t in ra_types(c,p):
1227
multicolumn(ra_types(c,p))
1228
def meta(self,cmd,*args):
1229
"usage: meta [<class>:[<provider>:]]<type>"
1230
if len(args) > 1: # obsolete syntax
1234
ra_provider = "heartbeat"
1236
ra_provider = args[2]
1238
if args[0] in vars.meta_progs:
1240
ra_provider = ra_type = None
1242
ra_class,ra_provider,ra_type = disambiguate_ra_type(args[0])
1243
ra = RAInfo(ra_class,ra_type,ra_provider)
1244
if not ra.mk_ra_node():
1247
page_string(ra.meta_pretty())
1251
def ptestlike(simfun,def_verb,cmd,*args):
1252
verbosity = def_verb # default verbosity
1262
elif p == "utilization":
1264
elif p == "actions":
1266
elif re.match("^vv*$", p):
1269
bad_usage(cmd,' '.join(args))
1271
return simfun(nograph, scores, utilization, actions, verbosity)
1273
class StatusMgmt(UserInterface):
1275
The CIB status section management user interface class
1277
lvl_name = "cibstatus"
1279
UserInterface.__init__(self)
1280
self.cmd_table["show"] = (self.show,(0,1),1,0)
1281
self.cmd_table["save"] = (self.save,(0,1),2,0)
1282
self.cmd_table["load"] = (self.load,(1,1),2,0)
1283
self.cmd_table["origin"] = (self.origin,(0,0),1,0)
1284
self.cmd_table["node"] = (self.edit_node,(2,2),2,0)
1285
self.cmd_table["op"] = (self.edit_op,(3,5),2,0)
1286
self.cmd_table["run"] = (self.run,(0,3),1,0)
1287
self.cmd_table["simulate"] = (self.simulate,(0,3),1,0)
1288
self.cmd_table["quorum"] = (self.quorum,(1,1),1,0)
1291
'''Just return some id.'''
1293
def load(self,cmd,org):
1294
"usage: load {<file>|shadow:<cib>|live}"
1295
return cib_status.load(org)
1296
def save(self,cmd,dest = None):
1297
"usage: save [<file>|shadow:<cib>]"
1298
return cib_status.save(dest)
1299
def origin(self,cmd):
1301
state = cib_status.modified and " (modified)" or ""
1302
print "%s%s" % (cib_status.origin,state)
1303
def show(self,cmd,changed = ""):
1304
"usage: show [changed]"
1306
if changed != "changed":
1307
syntax_err((cmd,changed))
1310
return cib_status.list_changes()
1311
return cib_status.show()
1312
def quorum(self,cmd,opt):
1313
"usage: quorum <bool>"
1314
if not verify_boolean(opt):
1315
common_err("%s: bad boolean option"%opt)
1317
return cib_status.set_quorum(is_boolean_true(opt))
1318
def edit_node(self,cmd,node,state):
1319
"usage: node <node> {online|offline|unclean}"
1320
return cib_status.edit_node(node,state)
1321
def edit_op(self,cmd,op,rsc,rc,op_status = None,node = ''):
1322
"usage: op <operation> <resource> <exit_code> [<op_status>] [<node>]"
1323
if rc in vars.lrm_exit_codes:
1324
num_rc = vars.lrm_exit_codes[rc]
1327
if not num_rc.isdigit():
1328
common_err("%s exit code invalid" % num_rc)
1330
num_op_status = op_status
1332
if op_status in vars.lrm_status_codes:
1333
num_op_status = vars.lrm_status_codes[op_status]
1334
if not num_op_status.isdigit():
1335
common_err("%s operation status invalid" % num_op_status)
1337
return cib_status.edit_op(op,rsc,num_rc,num_op_status,node)
1338
def run(self,cmd,*args):
1339
"usage: run [nograph] [v...] [scores] [utilization]"
1340
return ptestlike(cib_status.run,'',cmd,*args)
1341
def simulate(self,cmd,*args):
1342
"usage: simulate [nograph] [v...] [scores] [utilization]"
1343
return ptestlike(cib_status.simulate,'',cmd,*args)
1345
class CibConfig(UserInterface):
1347
The configuration class
1349
lvl_name = "configure"
1350
desc_short = "CRM cluster configuration"
1352
The configuration level.
1354
Note that you can change the working CIB at the cib level. It is
1355
advisable to configure shadow CIBs and then commit them to the
1359
UserInterface.__init__(self)
1360
self.cmd_table["erase"] = (self.erase,(0,1),1,0)
1361
self.cmd_table["verify"] = (self.verify,(0,0),1,0)
1362
self.cmd_table["refresh"] = (self.refresh,(0,0),1,0)
1363
self.cmd_table["ptest"] = (self.ptest,(0,3),1,0)
1364
self.cmd_table["commit"] = (self.commit,(0,1),1,1)
1365
self.cmd_table["upgrade"] = (self.upgrade,(0,1),1,0)
1366
self.cmd_table["show"] = (self.show,(0,),1,0)
1367
self.cmd_table["edit"] = (self.edit,(0,),1,0)
1368
self.cmd_table["filter"] = (self.filter,(1,),1,0)
1369
self.cmd_table["delete"] = (self.delete,(1,),1,0)
1370
self.cmd_table["default-timeouts"] = (self.default_timeouts,(1,),1,0)
1371
self.cmd_table["rename"] = (self.rename,(2,2),1,0)
1372
self.cmd_table["save"] = (self.save,(1,2),1,0)
1373
self.cmd_table["load"] = (self.load,(2,3),1,0)
1374
self.cmd_table["node"] = (self.conf_node,(1,),1,0)
1375
self.cmd_table["primitive"] = (self.conf_primitive,(2,),1,0)
1376
self.cmd_table["group"] = (self.conf_group,(2,),1,0)
1377
self.cmd_table["clone"] = (self.conf_clone,(2,),1,0)
1378
self.cmd_table["ms"] = (self.conf_ms,(2,),1,0)
1379
self.cmd_table["location"] = (self.conf_location,(2,),1,0)
1380
self.cmd_table["colocation"] = (self.conf_colocation,(2,),1,0)
1381
self.cmd_table["order"] = (self.conf_order,(2,),1,0)
1382
self.cmd_table["property"] = (self.conf_property,(1,),1,0)
1383
self.cmd_table["rsc_defaults"] = (self.conf_rsc_defaults,(1,),1,0)
1384
self.cmd_table["op_defaults"] = (self.conf_op_defaults,(1,),1,0)
1385
self.cmd_table["xml"] = (self.conf_xml,(1,),1,0)
1386
self.cmd_table["monitor"] = (self.conf_monitor,(2,2),1,0)
1387
self.cmd_table["role"] = (self.conf_role,(2,),2,0)
1388
self.cmd_table["user"] = (self.conf_user,(2,),2,0)
1389
self.cmd_table["ra"] = RA
1390
self.cmd_table["cib"] = CibShadow
1391
self.cmd_table["cibstatus"] = StatusMgmt
1392
self.cmd_table["template"] = Template
1393
self.cmd_table["_test"] = (self.check_structure,(0,0),1,0)
1394
self.cmd_table["_regtest"] = (self.regression_testing,(1,1),1,0)
1395
self.cmd_table["_objects"] = (self.showobjects,(0,0),1,0)
1396
self.cmd_aliases.update({
1397
"colocation": ("collocation",),
1401
cib_factory.initialize()
1403
'''Just return some id.'''
1405
def check_structure(self,cmd):
1406
return cib_factory.check_structure()
1407
def regression_testing(self,cmd,param):
1408
return cib_factory.regression_testing(param)
1409
def showobjects(self,cmd):
1410
cib_factory.showobjects()
1411
def show(self,cmd,*args):
1412
"usage: show [xml] [<id>...]"
1413
if not cib_factory.is_cib_sane():
1415
err_buf.buffer() # keep error messages
1416
set_obj = mkset_obj(*args)
1417
err_buf.release() # show them, but get an ack from the user
1418
return set_obj.show()
1419
def filter(self,cmd,filter,*args):
1420
"usage: filter <prog> [xml] [<id>...]"
1421
if not cib_factory.is_cib_sane():
1423
err_buf.buffer() # keep error messages
1424
set_obj = mkset_obj(*args)
1425
err_buf.release() # show them, but get an ack from the user
1426
return set_obj.filter(filter)
1427
def edit(self,cmd,*args):
1428
"usage: edit [xml] [<id>...]"
1429
if not cib_factory.is_cib_sane():
1431
err_buf.buffer() # keep error messages
1432
set_obj = mkset_obj(*args)
1433
err_buf.release() # show them, but get an ack from the user
1434
return set_obj.edit()
1435
def _verify(self, set_obj_semantic, set_obj_all):
1436
rc1 = set_obj_all.verify()
1437
if user_prefs.check_frequency != "never":
1438
rc2 = set_obj_semantic.semantic_check(set_obj_all)
1441
return rc1 and rc2 <= 1
1442
def verify(self,cmd):
1444
if not cib_factory.is_cib_sane():
1446
set_obj_all = mkset_obj("xml")
1447
return self._verify(set_obj_all, set_obj_all)
1448
def save(self,cmd,*args):
1449
"usage: save [xml] <filename>"
1450
if not cib_factory.is_cib_sane():
1452
if args[0] == "xml":
1454
set_obj = mkset_obj("xml")
1457
set_obj = mkset_obj()
1458
return set_obj.save_to_file(f)
1459
def load(self,cmd,*args):
1460
"usage: load [xml] {replace|update} {<url>|<path>}"
1461
if not cib_factory.is_cib_sane():
1463
if args[0] == "xml":
1465
syntax_err(args, context = 'load')
1469
set_obj = mkset_obj("xml","NOOBJ")
1472
syntax_err(args, context = 'load')
1476
set_obj = mkset_obj("NOOBJ")
1477
return set_obj.import_file(method,url)
1478
def delete(self,cmd,*args):
1479
"usage: delete <id> [<id>...]"
1480
if not cib_factory.is_cib_sane():
1482
return cib_factory.delete(*args)
1483
def default_timeouts(self,cmd,*args):
1484
"usage: default-timeouts <id> [<id>...]"
1485
if not cib_factory.is_cib_sane():
1487
return cib_factory.default_timeouts(*args)
1488
def rename(self,cmd,old_id,new_id):
1489
"usage: rename <old_id> <new_id>"
1490
if not cib_factory.is_cib_sane():
1492
return cib_factory.rename(old_id,new_id)
1493
def erase(self,cmd,nodes = None):
1494
"usage: erase [nodes]"
1495
if not cib_factory.is_cib_sane():
1498
if nodes == "nodes":
1499
return cib_factory.erase_nodes()
1501
syntax_err((cmd,nodes), context = 'erase')
1503
return cib_factory.erase()
1504
def refresh(self,cmd):
1506
if not cib_factory.is_cib_sane():
1508
if options.interactive and cib_factory.has_cib_changed():
1509
if not ask("All changes will be dropped. Do you want to proceed?"):
1511
cib_factory.refresh()
1512
def ptest(self,cmd,*args):
1513
"usage: ptest [nograph] [v...] [scores] [utilization] [actions]"
1514
if not cib_factory.is_cib_sane():
1516
set_obj = mkset_obj("xml")
1517
return ptestlike(set_obj.ptest,'vv',cmd,*args)
1518
def commit(self,cmd,force = None):
1519
"usage: commit [force]"
1520
if force and force != "force":
1521
syntax_err((cmd,force))
1523
if not cib_factory.is_cib_sane():
1525
if not cib_factory.has_cib_changed():
1526
common_info("apparently there is nothing to commit")
1527
common_info("try changing something first")
1529
rc1 = cib_factory.is_current_cib_equal()
1530
rc2 = cib_factory.is_cib_empty() or \
1531
self._verify(mkset_obj("xml","changed"),mkset_obj("xml"))
1533
return cib_factory.commit()
1534
if force or user_prefs.get_force():
1535
common_info("commit forced")
1536
return cib_factory.commit(True)
1537
if ask("Do you still want to commit?"):
1538
return cib_factory.commit(True)
1540
def upgrade(self,cmd,force = None):
1541
"usage: upgrade [force]"
1542
if not cib_factory.is_cib_sane():
1544
if force and force != "force":
1545
syntax_err((cmd,force))
1547
if user_prefs.get_force() or force:
1548
return cib_factory.upgrade_cib_06to10(True)
1550
return cib_factory.upgrade_cib_06to10()
1551
def __conf_object(self,cmd,*args):
1552
"The configure object command."
1553
if not cib_factory.is_cib_sane():
1555
f = lambda: cib_factory.create_object(cmd,*args)
1557
def conf_node(self,cmd,*args):
1558
"""usage: node <uname>[:<type>]
1559
[attributes <param>=<value> [<param>=<value>...]]
1560
[utilization <param>=<value> [<param>=<value>...]]"""
1561
return self.__conf_object(cmd,*args)
1562
def conf_primitive(self,cmd,*args):
1563
"""usage: primitive <rsc> [<class>:[<provider>:]]<type>
1564
[params <param>=<value> [<param>=<value>...]]
1565
[meta <attribute>=<value> [<attribute>=<value>...]]
1566
[utilization <attribute>=<value> [<attribute>=<value>...]]
1568
[op op_type [<attribute>=<value>...] ...]]"""
1569
return self.__conf_object(cmd,*args)
1570
def conf_group(self,cmd,*args):
1571
"""usage: group <name> <rsc> [<rsc>...]
1572
[params <param>=<value> [<param>=<value>...]]
1573
[meta <attribute>=<value> [<attribute>=<value>...]]"""
1574
return self.__conf_object(cmd,*args)
1575
def conf_clone(self,cmd,*args):
1576
"""usage: clone <name> <rsc>
1577
[params <param>=<value> [<param>=<value>...]]
1578
[meta <attribute>=<value> [<attribute>=<value>...]]"""
1579
return self.__conf_object(cmd,*args)
1580
def conf_ms(self,cmd,*args):
1581
"""usage: ms <name> <rsc>
1582
[params <param>=<value> [<param>=<value>...]]
1583
[meta <attribute>=<value> [<attribute>=<value>...]]"""
1584
return self.__conf_object(cmd,*args)
1585
def conf_location(self,cmd,*args):
1586
"""usage: location <id> <rsc> {node_pref|rules}
1588
node_pref :: <score>: <node>
1591
rule [id_spec] [$role=<role>] <score>: <expression>
1592
[rule [id_spec] [$role=<role>] <score>: <expression> ...]
1594
id_spec :: $id=<id> | $id-ref=<id>
1595
score :: <number> | <attribute> | [-]inf
1596
expression :: <simple_exp> [bool_op <simple_exp> ...]
1598
simple_exp :: <attribute> [type:]<binary_op> <value>
1599
| <unary_op> <attribute>
1601
type :: string | version | number
1602
binary_op :: lt | gt | lte | gte | eq | ne
1603
unary_op :: defined | not_defined"""
1604
return self.__conf_object(cmd,*args)
1605
def conf_colocation(self,cmd,*args):
1606
"""usage: colocation <id> <score>: <rsc>[:<role>] <rsc>[:<role>]
1608
return self.__conf_object(cmd,*args)
1609
def conf_order(self,cmd,*args):
1610
"""usage: order <id> score-type: <first-rsc>[:<action>] <then-rsc>[:<action>]
1611
[symmetrical=<bool>]"""
1612
return self.__conf_object(cmd,*args)
1613
def conf_property(self,cmd,*args):
1614
"usage: property [$id=<set_id>] <option>=<value>"
1615
return self.__conf_object(cmd,*args)
1616
def conf_rsc_defaults(self,cmd,*args):
1617
"usage: rsc_defaults [$id=<set_id>] <option>=<value>"
1618
return self.__conf_object(cmd,*args)
1619
def conf_op_defaults(self,cmd,*args):
1620
"usage: op_defaults [$id=<set_id>] <option>=<value>"
1621
return self.__conf_object(cmd,*args)
1622
def conf_xml(self,cmd,*args):
1624
return self.__conf_object(cmd,*args)
1625
def conf_monitor(self,cmd,*args):
1626
"usage: monitor <rsc>[:<role>] <interval>[:<timeout>]"
1627
return self.__conf_object(cmd,*args)
1628
def conf_user(self,cmd,*args):
1629
"""user <uid> {roles|rules}
1631
roles :: role:<role-ref> [role:<role-ref> ...]
1632
rules :: rule [rule ...]
1634
(See the role command for details on rules.)"""
1635
return self.__conf_object(cmd,*args)
1636
def conf_role(self,cmd,*args):
1637
"""role <role-id> rule [rule ...]
1639
rule :: acl-right cib-spec [attribute:<attribute>]
1641
acl-right :: read | write | deny
1643
cib-spec :: xpath-spec | tag-ref-spec
1644
xpath-spec :: xpath:<xpath> | shortcut
1645
tag-ref-spec :: tag:<tag> | ref:<id> | tag:<tag> ref:<id>
1647
shortcut :: meta:<rsc>[:<attr>]
1648
params:<rsc>[:<attr>]
1656
return self.__conf_object(cmd,*args)
1657
def end_game(self, no_questions_asked = False):
1658
if cib_factory.has_cib_changed():
1659
if no_questions_asked or not options.interactive or \
1660
ask("There are changes pending. Do you want to commit them?"):
1661
self.commit("commit")
1664
class TopLevel(UserInterface):
1669
crm_mon = "crm_mon -1"
1678
UserInterface.__init__(self)
1679
self.cmd_table['cib'] = CibShadow
1680
self.cmd_table['resource'] = RscMgmt
1681
self.cmd_table['configure'] = CibConfig
1682
self.cmd_table['node'] = NodeMgmt
1683
self.cmd_table['options'] = CliOptions
1684
self.cmd_table['status'] = (self.status,(0,5),0,0)
1685
self.cmd_table['ra'] = RA
1687
self.help_table["."] = ("","""This is the CRM command line interface program.""")
1688
for lvl in self.cmd_table.keys():
1689
try: self.help_table[lvl] = (self.cmd_table[lvl].desc_short, \
1690
self.cmd_table[lvl].desc_long)
1692
self.help_table["status"] = ("show cluster status", """
1693
Show cluster status. The status is displayed by crm_mon. Supply
1694
additional arguments for more information or different format.
1695
See crm_mon(8) for more details.
1699
status [<option> ...]
1701
option :: bynode | inactive | ops | timing | failcounts
1704
self.help_table["quit"] = ("exit the program", "")
1705
self.help_table["help"] = ("show help", "")
1706
self.help_table["end"] = ("go back one level", "")
1707
setup_help_aliases(self)
1708
def status(self,cmd,*args):
1709
"""usage: status [<option> ...]
1710
option :: bynode | inactive | ops | timing | failcounts
1712
status_cmd = self.crm_mon
1714
if par in self.status_opts:
1715
status_cmd = "%s %s" % (status_cmd, self.status_opts[par])
1717
syntax_err((cmd,par), context = 'status')
1719
return ext_cmd(status_cmd) == 0
1721
help_sys = HelpSystem()
1722
user_prefs = UserPrefs.getInstance()
1723
options = Options.getInstance()
1724
err_buf = ErrorBuffer.getInstance()
1725
vars = Vars.getInstance()
1726
levels = Levels.getInstance(TopLevel)
1727
cib_status = CibStatus.getInstance()
1728
cib_factory = CibFactory.getInstance()