~ubuntu-branches/ubuntu/utopic/pacemaker/utopic-proposed

« back to all changes in this revision

Viewing changes to shell/modules/ui.py.in

  • Committer: Package Import Robot
  • Author(s): Andres Rodriguez
  • Date: 2013-07-16 16:40:24 UTC
  • mfrom: (1.1.11) (2.2.3 experimental)
  • Revision ID: package-import@ubuntu.com-20130716164024-lvwrf4xivk1wdr3c
Tags: 1.1.9+git20130321-1ubuntu1
* Resync from debian expiremental.
* debian/control:
  - Use lower version for Build-Depends on libcorosync-dev
    and libqb-dev.
  - Build-Depends on libcfg-dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2008 Dejan Muhamedagic <dmuhamedagic@suse.de>
2
 
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.
7
 
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.
12
 
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
16
 
#
17
 
 
18
 
import sys
19
 
import re
20
 
import os
21
 
import shlex
22
 
import time
23
 
import bz2
24
 
 
25
 
from help import HelpSystem, cmd_help
26
 
from vars import Vars
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
32
 
from ra import *
33
 
from msg import *
34
 
from utils import *
35
 
from xmlutil import *
36
 
 
37
 
def cmd_end(cmd,dir = ".."):
38
 
    "Go up one level."
39
 
    levels.droplevel()
40
 
def cmd_exit(cmd,rc = 0):
41
 
    "Exit the crm program"
42
 
    cmd_end(cmd)
43
 
    if options.interactive and not options.batch:
44
 
        print "bye"
45
 
        try:
46
 
            from readline import write_history_file
47
 
            write_history_file(vars.hist_file)
48
 
        except:
49
 
            pass
50
 
    for f in vars.tmpfiles:
51
 
        os.unlink(f)
52
 
    sys.exit(rc)
53
 
 
54
 
class UserInterface(object):
55
 
    '''
56
 
    Stuff common to all user interface classes.
57
 
    '''
58
 
    global_cmd_aliases = {
59
 
        "quit": ("bye","exit"),
60
 
        "end": ("cd","up"),
61
 
    }
62
 
    def __init__(self):
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):
72
 
        pass
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 = ".."):
80
 
        "usage: end"
81
 
        self.end_game()
82
 
        cmd_end(cmd,dir)
83
 
    def exit(self,cmd):
84
 
        "usage: exit"
85
 
        self.end_game()
86
 
        cmd_exit(cmd)
87
 
 
88
 
class CliOptions(UserInterface):
89
 
    '''
90
 
    Manage user preferences
91
 
    '''
92
 
    lvl_name = "options"
93
 
    desc_short = "user preferences"
94
 
    desc_long = """
95
 
Several user preferences are available. Note that it is possible
96
 
to save the preferences to a startup file.
97
 
"""
98
 
    def __init__(self):
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)
112
 
        setup_aliases(self)
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)
142
 
            return True
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)
148
 
            return True
149
 
        return user_prefs.set_wait(opt)
150
 
    def show_options(self,cmd):
151
 
        "usage: show"
152
 
        return user_prefs.write_rc(sys.stdout)
153
 
    def save_options(self,cmd):
154
 
        "usage: save"
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")
159
 
 
160
 
class CibShadow(UserInterface):
161
 
    '''
162
 
    CIB shadow management class
163
 
    '''
164
 
    lvl_name = "cib"
165
 
    desc_short = "manage shadow CIBs"
166
 
    desc_long = """
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.
171
 
"""
172
 
    extcmd = ">/dev/null </dev/null crm_shadow"
173
 
    extcmd_stdout = "</dev/null crm_shadow"
174
 
    def __init__(self):
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
185
 
        self.chkcmd()
186
 
        setup_aliases(self)
187
 
    def chkcmd(self):
188
 
        try:
189
 
            ext_cmd("%s 2>&1" % self.extcmd)
190
 
        except os.error:
191
 
            no_prog_err(self.extcmd)
192
 
            return False
193
 
        return True
194
 
    def new(self,cmd,name,*args):
195
 
        "usage: new <shadow_cib> [withstatus] [force] [empty]"
196
 
        if not is_filename_sane(name):
197
 
            return False
198
 
        for par in args:
199
 
            if not par in ("force","--force","withstatus","empty"):
200
 
                syntax_err((cmd,name,par), context = 'new')
201
 
                return False
202
 
        if "empty" in args:
203
 
            new_cmd = "%s -e '%s'" % (self.extcmd,name)
204
 
        else:
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)
210
 
            self.use("use",name)
211
 
            if "withstatus" in args:
212
 
                cib_status.load("shadow:%s" % name)
213
 
    def _find_pe(self,infile):
214
 
        'Find a pe input'
215
 
        for p in ("%s/%s", "%s/%s.bz2", "%s/pe-*-%s.bz2"):
216
 
            fl = glob.glob(p % (vars.pe_dir,infile))
217
 
            if fl:
218
 
                break
219
 
        if not fl:
220
 
            common_err("no %s pe input file"%infile)
221
 
            return ''
222
 
        if len(fl) > 1:
223
 
            common_err("more than one %s pe input file: %s" % \
224
 
                (infile,' '.join(fl)))
225
 
            return ''
226
 
        return fl[0]
227
 
    def pe_import(self,cmd,infile,name = None):
228
 
        "usage: import {<file>|<number>} [<shadow>]"
229
 
        if name and not is_filename_sane(name):
230
 
            return False
231
 
        # where's the input?
232
 
        if not os.access(infile,os.F_OK):
233
 
            if "/" in infile:
234
 
                common_err("%s: no such file"%infile)
235
 
                return False
236
 
            infile = self._find_pe(infile)
237
 
            if not infile:
238
 
                return False
239
 
        if not name:
240
 
            name = os.path.basename(infile)
241
 
        # read input
242
 
        try:
243
 
            f = open(infile)
244
 
        except IOError,msg:
245
 
            common_err("open: %s"%msg)
246
 
            return
247
 
        s = ''.join(f)
248
 
        f.close()
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
254
 
        try:
255
 
            f = open(shadowfile(name), "w")
256
 
        except IOError,msg:
257
 
            common_err("open: %s"%msg)
258
 
            return
259
 
        f.write(s)
260
 
        f.close()
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):
266
 
            return False
267
 
        if vars.cib_in_use == name:
268
 
            common_err("%s shadow CIB is in use"%name)
269
 
            return False
270
 
        if ext_cmd("%s -D '%s' --force" % (self.extcmd,name)) == 0:
271
 
            common_info("%s shadow CIB deleted"%name)
272
 
        else:
273
 
            common_err("failed to delete %s shadow CIB"%name)
274
 
            return False
275
 
    def reset(self,cmd,name):
276
 
        "usage: reset <shadow_cib>"
277
 
        if not is_filename_sane(name):
278
 
            return False
279
 
        if ext_cmd("%s -r '%s'" % (self.extcmd,name)) == 0:
280
 
            common_info("copied live CIB to %s"%name)
281
 
        else:
282
 
            common_err("failed to copy live CIB to %s"%name)
283
 
            return False
284
 
    def commit(self,cmd,name):
285
 
        "usage: commit <shadow_cib>"
286
 
        if not is_filename_sane(name):
287
 
            return False
288
 
        if ext_cmd("%s -C '%s' --force" % (self.extcmd,name)) == 0:
289
 
            common_info("commited '%s' shadow CIB to the cluster"%name)
290
 
        else:
291
 
            common_err("failed to commit the %s shadow CIB"%name)
292
 
            return False
293
 
    def diff(self,cmd):
294
 
        "usage: diff"
295
 
        s = get_stdout(add_sudo("%s -d" % self.extcmd_stdout))
296
 
        page_string(s)
297
 
    def list(self,cmd):
298
 
        "usage: list"
299
 
        if options.regression_tests:
300
 
            for t in listshadows():
301
 
                print t
302
 
        else:
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)
310
 
            vars.cib_in_use = ""
311
 
            if withstatus:
312
 
                cib_status.load("live")
313
 
        else:
314
 
            os.putenv(vars.shadow_envvar,name)
315
 
            vars.cib_in_use = name
316
 
            if withstatus:
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):
322
 
            return False
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)
326
 
                return False
327
 
        if withstatus and withstatus != "withstatus":
328
 
            syntax_err((cmd,withstatus), context = 'use')
329
 
            return False
330
 
        # If invoked from configure
331
 
        # take special precautions
332
 
        try:
333
 
            prev_level = levels.previous().myname()
334
 
        except:
335
 
            prev_level = ''
336
 
        if prev_level != "cibconfig":
337
 
            self._use(name,withstatus)
338
 
            return True
339
 
        if not cib_factory.has_cib_changed():
340
 
            self._use(name,withstatus)
341
 
            # new CIB: refresh the CIB factory
342
 
            cib_factory.refresh()
343
 
            return True
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
354
 
                return False
355
 
        self._use(name,withstatus) # now load the status too
356
 
        return True
357
 
 
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))
361
 
        return False
362
 
    return True
363
 
class Template(UserInterface):
364
 
    '''
365
 
    Configuration templates.
366
 
    '''
367
 
    lvl_name = "template"
368
 
    def __init__(self):
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)
377
 
        setup_aliases(self)
378
 
        self.init_dir()
379
 
        self.curr_conf = ''
380
 
    def init_dir(self):
381
 
        '''Create the conf directory, link to templates'''
382
 
        if not os.path.isdir(vars.tmpl_conf_dir):
383
 
            try:
384
 
                os.makedirs(vars.tmpl_conf_dir)
385
 
            except os.error,msg:
386
 
                common_err("makedirs: %s"%msg)
387
 
                return
388
 
    def get_depends(self,tmpl):
389
 
        '''return a list of required templates'''
390
 
        # Not used. May need it later.
391
 
        try:
392
 
            tf = open("%s/%s" % (vars.tmpl_dir, tmpl),"r")
393
 
        except IOError,msg:
394
 
            common_err("open: %s"%msg)
395
 
            return
396
 
        l = []
397
 
        for s in tf:
398
 
            a = s.split()
399
 
            if len(a) >= 2 and a[0] == '%depends_on':
400
 
                l += a[1:]
401
 
        tf.close()
402
 
        return l
403
 
    def replace_params(self,s,user_data):
404
 
        change = False
405
 
        for i in range(len(s)):
406
 
            word = s[i]
407
 
            for p in user_data:
408
 
                # is parameter in the word?
409
 
                pos = word.find('%' + p) 
410
 
                if pos < 0:
411
 
                    continue
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]):
415
 
                    continue
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] + '"'
421
 
                else:
422
 
                    v = user_data[p]
423
 
                word = word.replace('%' + p, v)
424
 
                change = True # we did replace something
425
 
            if change:
426
 
                s[i] = word
427
 
        if 'opt' in s:
428
 
            if not change:
429
 
                s = []
430
 
            else:
431
 
                s.remove('opt')
432
 
        return s
433
 
    def generate(self,l,user_data):
434
 
        '''replace parameters (user_data) and generate output
435
 
        '''
436
 
        l2 = []
437
 
        for piece in l:
438
 
            piece2 = []
439
 
            for s in piece:
440
 
                s = self.replace_params(s,user_data)
441
 
                if s:
442
 
                    piece2.append(' '.join(s))
443
 
            if piece2:
444
 
                l2.append(' \\\n\t'.join(piece2))
445
 
        return '\n'.join(l2)
446
 
    def process(self,config = ''):
447
 
        '''Create a cli configuration from the current config'''
448
 
        try:
449
 
            f = open("%s/%s" % (vars.tmpl_conf_dir, config or self.curr_conf),'r')
450
 
        except IOError,msg:
451
 
            common_err("open: %s"%msg)
452
 
            return ''
453
 
        l = []
454
 
        piece = []
455
 
        user_data = {}
456
 
        # states
457
 
        START = 0; PFX = 1; DATA = 2; GENERATE = 3
458
 
        state = START
459
 
        err_buf.start_tmp_lineno()
460
 
        rc = True
461
 
        for inp in f:
462
 
            err_buf.incr_lineno()
463
 
            if inp.startswith('#'):
464
 
                continue
465
 
            if type(inp) == type(u''):
466
 
                inp = inp.encode('ascii')
467
 
            inp = inp.strip()
468
 
            try:
469
 
                s = shlex.split(inp)
470
 
            except ValueError, msg:
471
 
                common_err(msg)
472
 
                continue
473
 
            while '\n' in s:
474
 
                s.remove('\n')
475
 
            if not s:
476
 
                if state == GENERATE and piece:
477
 
                    l.append(piece)
478
 
                    piece = []
479
 
            elif s[0] in ("%name","%depends_on","%suggests"):
480
 
                continue
481
 
            elif s[0] == "%pfx":
482
 
                if check_transition(inp,state,(START,DATA)) and len(s) == 2:
483
 
                    pfx = s[1]
484
 
                    state = PFX
485
 
            elif s[0] == "%required":
486
 
                if check_transition(inp,state,(PFX,)):
487
 
                    state = DATA
488
 
                    data_reqd = True
489
 
            elif s[0] == "%optional":
490
 
                if check_transition(inp,state,(PFX,DATA)):
491
 
                    state = DATA
492
 
                    data_reqd = False
493
 
            elif s[0] == "%%":
494
 
                if state != DATA:
495
 
                    common_warn("user data in wrong state %s" % state)
496
 
                if len(s) < 2:
497
 
                    common_warn("parameter name missing")
498
 
                elif len(s) == 2:
499
 
                    if data_reqd:
500
 
                        common_err("required parameter %s not set" % s[1])
501
 
                        rc = False
502
 
                elif len(s) == 3:
503
 
                    user_data["%s:%s" % (pfx,s[1])] = s[2]
504
 
                else:
505
 
                    common_err("%s: syntax error" % inp)
506
 
            elif s[0] == "%generate":
507
 
                if check_transition(inp,state,(DATA,)):
508
 
                    state = GENERATE
509
 
                    piece = []
510
 
            elif state == GENERATE:
511
 
                if s:
512
 
                    piece.append(s)
513
 
            else:
514
 
                common_err("<%s> unexpected" % inp)
515
 
        if piece:
516
 
            l.append(piece)
517
 
        err_buf.stop_tmp_lineno()
518
 
        f.close()
519
 
        if not rc:
520
 
            return ''
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):
525
 
            return False
526
 
        if os.path.isfile("%s/%s" % (vars.tmpl_conf_dir, name)):
527
 
            common_err("config %s exists; delete it first" % name)
528
 
            return False
529
 
        lt = LoadTemplate(name)
530
 
        rc = True
531
 
        mode = 0
532
 
        params = {}
533
 
        for s in args:
534
 
            if mode == 0 and s == "params":
535
 
                params["id"] = name
536
 
                mode = 1
537
 
            elif mode == 1:
538
 
                a = s.split('=')
539
 
                if len(a) != 2:
540
 
                    syntax_err(args, context = 'new')
541
 
                    rc = False
542
 
                else:
543
 
                    params[a[0]] = a[1]
544
 
            elif not lt.load_template(s):
545
 
                rc = False
546
 
        if rc:
547
 
            lt.post_process(params)
548
 
        if not rc or not lt.write_config(name):
549
 
            return False
550
 
        self.curr_conf = name
551
 
    def config_exists(self,name):
552
 
        if not is_filename_sane(name):
553
 
            return False
554
 
        if not os.path.isfile("%s/%s" % (vars.tmpl_conf_dir, name)):
555
 
            common_err("%s: no such config" % name)
556
 
            return False
557
 
        return True
558
 
    def delete(self,cmd,name,force = ''):
559
 
        "usage: delete <config> [force]"
560
 
        if force:
561
 
            if force != "force" and force != "--force":
562
 
                syntax_err((cmd,force), context = 'delete')
563
 
                return False
564
 
        if not self.config_exists(name):
565
 
            return False
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):
569
 
                return False
570
 
            else:
571
 
                self.curr_conf = ''
572
 
        os.remove("%s/%s" % (vars.tmpl_conf_dir, name))
573
 
    def load(self,cmd,name = ''):
574
 
        "usage: load [<config>]"
575
 
        if not name:
576
 
            self.curr_conf = ''
577
 
            return True
578
 
        if not self.config_exists(name):
579
 
            return False
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")
585
 
            return False
586
 
        if name:
587
 
            if not self.config_exists(name):
588
 
                return False
589
 
            edit_file("%s/%s" % (vars.tmpl_conf_dir, name))
590
 
        else:
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")
596
 
            return False
597
 
        if name:
598
 
            if not self.config_exists(name):
599
 
                return False
600
 
            print self.process(name)
601
 
        else:
602
 
            print self.process()
603
 
    def apply(self,cmd,*args):
604
 
        "usage: apply [<method>] [<config>]"
605
 
        method = "replace"
606
 
        name = ''
607
 
        if len(args) > 0:
608
 
            i = 0
609
 
            if args[0] in ("replace","update"):
610
 
                method = args[0]
611
 
                i += 1
612
 
            if len(args) > i:
613
 
                name = args[i]
614
 
        if not name and not self.curr_conf:
615
 
            common_err("please load a config first")
616
 
            return False
617
 
        if name:
618
 
            if not self.config_exists(name):
619
 
                return False
620
 
            s = self.process(name)
621
 
        else:
622
 
            s = self.process()
623
 
        if not s:
624
 
            return False
625
 
        tmp = str2tmp(s)
626
 
        if not tmp:
627
 
            return False
628
 
        set_obj = mkset_obj("NOOBJ")
629
 
        rc = set_obj.import_file(method,tmp)
630
 
        try: os.unlink(tmp)
631
 
        except: pass
632
 
        return rc
633
 
    def list(self,cmd,templates = ''):
634
 
        "usage: list [templates]"
635
 
        if templates == "templates":
636
 
            multicolumn(listtemplates())
637
 
        else:
638
 
            multicolumn(listconfigs())
639
 
 
640
 
def manage_attr(cmd,attr_ext_commands,*args):
641
 
    if len(args) < 3:
642
 
        bad_usage(cmd,' '.join(args))
643
 
        return False
644
 
    attr_cmd = None
645
 
    try:
646
 
        attr_cmd = attr_ext_commands[args[1]]
647
 
    except KeyError:
648
 
        bad_usage(cmd,' '.join(args))
649
 
        return False
650
 
    if not attr_cmd:
651
 
        bad_usage(cmd,' '.join(args))
652
 
        return False
653
 
    if args[1] == 'set':
654
 
        if len(args) == 4:
655
 
            if not is_name_sane(args[0]) \
656
 
                    or not is_name_sane(args[2]) \
657
 
                    or not is_value_sane(args[3]):
658
 
                return False
659
 
            return ext_cmd(attr_cmd%(args[0],args[2],args[3])) == 0
660
 
        else:
661
 
            bad_usage(cmd,' '.join(args))
662
 
            return False
663
 
    elif args[1] in ('delete','show'):
664
 
        if len(args) == 3:
665
 
            if not is_name_sane(args[0]) \
666
 
                    or not is_name_sane(args[2]):
667
 
                return False
668
 
            return ext_cmd(attr_cmd%(args[0],args[2])) == 0
669
 
        else:
670
 
            bad_usage(cmd,' '.join(args))
671
 
            return False
672
 
    else:
673
 
        bad_usage(cmd,' '.join(args))
674
 
        return False
675
 
 
676
 
def rm_meta_attribute(node,attr,l):
677
 
    '''
678
 
    Build a list of nvpair nodes which contain attribute
679
 
    (recursively in all children resources)
680
 
    '''
681
 
    for c in node.childNodes:
682
 
        if not is_element(c):
683
 
            continue
684
 
        if c.tagName == "meta_attributes":
685
 
            nvpair = get_attr_in_set(c,attr)
686
 
            if nvpair:
687
 
                l.append(nvpair)
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'
692
 
    l = []
693
 
    for c in node.childNodes:
694
 
        if is_child_rsc(c):
695
 
            rm_meta_attribute(c,attr,l)
696
 
    rmnodes(l)
697
 
def set_deep_meta_attr(attr,value,rsc_id):
698
 
    '''
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.
704
 
    '''
705
 
    target_node = rsc2node(rsc_id)
706
 
    if not target_node:
707
 
        common_error("resource %s does not exist" % rsc_id)
708
 
        return False
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)
716
 
 
717
 
def get_max_clone(id):
718
 
    v = get_meta_param(id,"clone-max")
719
 
    try:
720
 
        cnt = int(v)
721
 
    except:
722
 
        cnt = len(listnodes())
723
 
    return cnt
724
 
def cleanup_resource(rsc,node = ''):
725
 
    if not is_name_sane(rsc) or not is_name_sane(node):
726
 
        return False
727
 
    if not node:
728
 
        rc = ext_cmd(RscMgmt.rsc_cleanup_all%(rsc)) == 0
729
 
    else:
730
 
        rc = ext_cmd(RscMgmt.rsc_cleanup%(rsc,node)) == 0
731
 
    return rc
732
 
 
733
 
class RscMgmt(UserInterface):
734
 
    '''
735
 
    Resources management class
736
 
    '''
737
 
    lvl_name = "resource"
738
 
    desc_short = "resources management"
739
 
    desc_long = """
740
 
Everything related to resources management is available at this
741
 
level. Most commands are implemented using the crm_resource(8)
742
 
program.
743
 
"""
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'"
752
 
    rsc_param =  {
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'",
756
 
    }
757
 
    rsc_meta =  {
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'",
761
 
    }
762
 
    rsc_failcount = {
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",
766
 
    }
767
 
    rsc_utilization =  {
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'",
771
 
    }
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'"
776
 
    def __init__(self):
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",),
799
 
        })
800
 
        setup_aliases(self)
801
 
    def status(self,cmd,rsc = None):
802
 
        "usage: status [<rsc>]"
803
 
        if rsc:
804
 
            if not is_name_sane(rsc):
805
 
                return False
806
 
            return ext_cmd(self.rsc_status % rsc) == 0
807
 
        else:
808
 
            return ext_cmd(self.rsc_status_all) == 0
809
 
    def start(self,cmd,rsc):
810
 
        "usage: start <rsc>"
811
 
        if not is_name_sane(rsc):
812
 
            return False
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):
817
 
            return False
818
 
        common_info("ordering %s to stop" % rsc)
819
 
        if not self.stop("stop",rsc):
820
 
            return False
821
 
        if not wait4dc("stop", not options.batch):
822
 
            return False
823
 
        common_info("ordering %s to start" % rsc)
824
 
        return self.start("start",rsc)
825
 
    def stop(self,cmd,rsc):
826
 
        "usage: stop <rsc>"
827
 
        if not is_name_sane(rsc):
828
 
            return False
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):
833
 
            return False
834
 
        if not is_rsc_ms(rsc):
835
 
            common_err("%s is not a master-slave resource" % rsc)
836
 
            return False
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):
841
 
            return False
842
 
        if not is_rsc_ms(rsc):
843
 
            common_err("%s is not a master-slave resource" % rsc)
844
 
            return False
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):
849
 
            return False
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):
854
 
            return False
855
 
        return set_deep_meta_attr("is-managed","false",rsc)
856
 
    def migrate(self,cmd,*args):
857
 
        """usage: migrate <rsc> [<node>] [<lifetime>] [force]"""
858
 
        rsc = args[0]
859
 
        if not is_name_sane(rsc):
860
 
            return False
861
 
        node = None
862
 
        lifetime = None
863
 
        force = False
864
 
        if len(args) == 2:
865
 
            if not args[1] in listnodes():
866
 
                if args[1] == "force":
867
 
                    force = True
868
 
                else:
869
 
                    lifetime = args[1]
870
 
            else:
871
 
                node = args[1]
872
 
        elif len(args) == 3:
873
 
            if not args[1] in listnodes():
874
 
                lifetime = args[1]
875
 
                force = True
876
 
            else:
877
 
                node = args[1]
878
 
                if args[2] == "force":
879
 
                    force = True
880
 
                else:
881
 
                    lifetime = args[2]
882
 
        elif len(args) == 4:
883
 
            if not is_name_sane(args[1]):
884
 
                return False
885
 
            node = args[1]
886
 
            lifetime = args[2]
887
 
            if not args[3] == "force":
888
 
                syntax_err((cmd,force))
889
 
                return False
890
 
            force = True
891
 
        opts = ''
892
 
        if node:
893
 
            opts = "--node='%s'" % node
894
 
        if lifetime:
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):
902
 
            return False
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
907
 
        # all live nodes.
908
 
        if len(args) == 2: # remove
909
 
            return cleanup_resource(args[0],args[1])
910
 
        else:
911
 
            return cleanup_resource(args[0])
912
 
    def failcount(self,cmd,*args):
913
 
        """usage:
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)
918
 
        return d()
919
 
    def param(self,cmd,*args):
920
 
        """usage:
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)
925
 
        return d()
926
 
    def meta(self,cmd,*args):
927
 
        """usage:
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)
932
 
        return d()
933
 
    def utilization(self,cmd,*args):
934
 
        """usage:
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)
939
 
        return d()
940
 
    def refresh(self,cmd,*args):
941
 
        'usage: refresh [<node>]'
942
 
        if len(args) == 1:
943
 
            if not is_name_sane(args[0]):
944
 
                return False
945
 
            return ext_cmd(self.rsc_refresh_node%args[0]) == 0
946
 
        else:
947
 
            return ext_cmd(self.rsc_refresh) == 0
948
 
    def reprobe(self,cmd,*args):
949
 
        'usage: reprobe [<node>]'
950
 
        if len(args) == 1:
951
 
            if not is_name_sane(args[0]):
952
 
                return False
953
 
            return ext_cmd(self.rsc_reprobe_node%args[0]) == 0
954
 
        else:
955
 
            return ext_cmd(self.rsc_reprobe) == 0
956
 
 
957
 
def print_node(uname,id,node_type,other,inst_attr,offline):
958
 
    """
959
 
    Try to pretty print a node from the cib. Sth like:
960
 
    uname(id): node_type
961
 
        attr1: v1
962
 
        attr2: v2
963
 
    """
964
 
    s_offline = offline and "(offline)" or ""
965
 
    if uname == id:
966
 
        print "%s: %s%s" % (uname,node_type,s_offline)
967
 
    else:
968
 
        print "%s(%s): %s%s" % (uname,id,node_type,s_offline)
969
 
    for a in other:
970
 
        print "\t%s: %s" % (a,other[a])
971
 
    for a,v in inst_attr:
972
 
        print "\t%s: %s" % (a,v)
973
 
 
974
 
class NodeMgmt(UserInterface):
975
 
    '''
976
 
    Nodes management class
977
 
    '''
978
 
    lvl_name = "node"
979
 
    desc_short = "nodes management"
980
 
    desc_long = """
981
 
A few node related tasks such as node standby are implemented
982
 
here.
983
 
"""
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"
991
 
    dc = "crmadmin -D"
992
 
    node_attr = {
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'",
996
 
    }
997
 
    node_status = {
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'",
1001
 
    }
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'",
1006
 
    }
1007
 
    def __init__(self):
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({
1020
 
            "show": ("list",),
1021
 
        })
1022
 
        setup_aliases(self)
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>]'
1028
 
        doc = cibdump2doc()
1029
 
        if not doc:
1030
 
            return False
1031
 
        nodes_node = get_conf_elem(doc, "nodes")
1032
 
        status = get_conf_elem(doc, "status")
1033
 
        if not nodes_node:
1034
 
            return False
1035
 
        for c in nodes_node.childNodes:
1036
 
            if not is_element(c) or c.tagName != "node":
1037
 
                continue
1038
 
            if node and c.getAttribute("uname") != node:
1039
 
                continue
1040
 
            type = uname = id = ""
1041
 
            inst_attr = []
1042
 
            other = {}
1043
 
            for attr in c.attributes.keys():
1044
 
                v = c.getAttribute(attr)
1045
 
                if attr == "type":
1046
 
                    type = v
1047
 
                elif attr == "uname":
1048
 
                    uname = v
1049
 
                elif attr == "id":
1050
 
                    id = v
1051
 
                else:
1052
 
                    other[attr] = v
1053
 
            for c2 in c.childNodes:
1054
 
                if not is_element(c2):
1055
 
                    continue
1056
 
                if c2.tagName == "instance_attributes":
1057
 
                    inst_attr += nvpairs2list(c2)
1058
 
            offline = False
1059
 
            for c2 in status.getElementsByTagName("node_state"):
1060
 
                if uname != c2.getAttribute("uname"):
1061
 
                    continue
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>]'
1066
 
        node = None
1067
 
        lifetime = None
1068
 
        if not args:
1069
 
            node = vars.this_node
1070
 
        if len(args) == 1:
1071
 
            if not args[0] in listnodes():
1072
 
                node = vars.this_node
1073
 
                lifetime = args[0]
1074
 
            else:
1075
 
                node = args[0]
1076
 
        elif len(args) == 2:
1077
 
            node = args[0]
1078
 
            lifetime = args[1]
1079
 
        if lifetime not in (None,"reboot","forever"):
1080
 
            common_err("bad lifetime: %s" % lifetime)
1081
 
            return False
1082
 
        if not is_name_sane(node):
1083
 
            return False
1084
 
        opts = ''
1085
 
        if lifetime:
1086
 
            opts = "--lifetime='%s'" % lifetime
1087
 
        else:
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>]'
1092
 
        if not node:
1093
 
            node = vars.this_node
1094
 
        if not is_name_sane(node):
1095
 
            return False
1096
 
        return ext_cmd(self.node_standby%(node,"off","--lifetime='forever'")) == 0
1097
 
    def fence(self,cmd,node):
1098
 
        'usage: fence <node>'
1099
 
        if not node:
1100
 
            node = vars.this_node
1101
 
        if not is_name_sane(node):
1102
 
            return False
1103
 
        if not user_prefs.get_force() and \
1104
 
                not ask("Do you really want to shoot %s?" % node):
1105
 
            return False
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):
1110
 
            return False
1111
 
        if not user_prefs.get_force() and \
1112
 
                not ask("Do you really want to drop state for node %s?" % node):
1113
 
            return False
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):
1119
 
            return False
1120
 
        if not node in listnodes():
1121
 
            common_err("node %s not found in the CIB" % node)
1122
 
            return False
1123
 
        rc = True
1124
 
        if cluster_stack() == "heartbeat":
1125
 
            rc = ext_cmd(self.hb_delnode%node) == 0
1126
 
        else:
1127
 
            node_states = {}
1128
 
            for s in stdout2list("%s -l" % self.crm_node):
1129
 
                a = s.split()
1130
 
                if len(a) != 3:
1131
 
                    common_warn("%s bad format: %s" % (self.crm_node,s))
1132
 
                    continue
1133
 
                # fmt: id uname status
1134
 
                # remove only those in state "lost"
1135
 
                if a[1] == node:
1136
 
                    node_states[a[2]] = 1
1137
 
                    if a[2] == "lost":
1138
 
                        ec = ext_cmd("%s --force -R %s" % (self.crm_node,a[0]))
1139
 
                        if ec != 0:
1140
 
                            common_warn('"%s --force -R %s" failed, rc=%d' % (self.crm_node,a[0],ec))
1141
 
            if not node_states:
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)
1146
 
                rc = False
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)
1150
 
                rc = False
1151
 
        if rc:
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)
1155
 
                rc = False
1156
 
        if rc:
1157
 
            common_info("node %s deleted" % node)
1158
 
        else:
1159
 
            common_err('failed to delete node %s' % node)
1160
 
        return rc
1161
 
    def attribute(self,cmd,*args):
1162
 
        """usage:
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)
1167
 
        return d()
1168
 
    def utilization(self,cmd,*args):
1169
 
        """usage:
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)
1174
 
        return d()
1175
 
    def status_attr(self,cmd,*args):
1176
 
        """usage:
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)
1181
 
        return d()
1182
 
 
1183
 
class RA(UserInterface):
1184
 
    '''
1185
 
    CIB shadow management class
1186
 
    '''
1187
 
    lvl_name = "ra"
1188
 
    desc_short = "resource agents information center"
1189
 
    desc_long = """
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.
1193
 
"""
1194
 
    provider_classes = ["ocf"]
1195
 
    def __init__(self):
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({
1202
 
            "meta": ("info",),
1203
 
        })
1204
 
        setup_aliases(self)
1205
 
    def classes(self,cmd):
1206
 
        "usage: classes"
1207
 
        for c in ra_classes():
1208
 
            if c in self.provider_classes:
1209
 
                print "%s / %s" % (c,' '.join(ra_providers_all(c)))
1210
 
            else:
1211
 
                print "%s" % 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)
1219
 
            return False
1220
 
        if p and not p in ra_providers_all(c):
1221
 
            common_err("there is no provider %s for class %s" % (p,c))
1222
 
            return False
1223
 
        if options.regression_tests:
1224
 
            for t in ra_types(c,p):
1225
 
                print t
1226
 
        else:
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
1231
 
            ra_type = args[0]
1232
 
            ra_class = args[1]
1233
 
            if len(args) < 3:
1234
 
                ra_provider = "heartbeat"
1235
 
            else:
1236
 
                ra_provider = args[2]
1237
 
        else:
1238
 
            if args[0] in vars.meta_progs:
1239
 
                ra_class = args[0]
1240
 
                ra_provider = ra_type = None
1241
 
            else:
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():
1245
 
            return False
1246
 
        try:
1247
 
            page_string(ra.meta_pretty())
1248
 
        except:
1249
 
            return False
1250
 
 
1251
 
def ptestlike(simfun,def_verb,cmd,*args):
1252
 
    verbosity = def_verb  # default verbosity
1253
 
    nograph = False
1254
 
    scores = False
1255
 
    utilization = False
1256
 
    actions = False
1257
 
    for p in args:
1258
 
        if p == "nograph":
1259
 
            nograph = True
1260
 
        elif p == "scores":
1261
 
            scores = True
1262
 
        elif p == "utilization":
1263
 
            utilization = True
1264
 
        elif p == "actions":
1265
 
            actions = True
1266
 
        elif re.match("^vv*$", p):
1267
 
            verbosity = p
1268
 
        else:
1269
 
            bad_usage(cmd,' '.join(args))
1270
 
            return False
1271
 
    return simfun(nograph, scores, utilization, actions, verbosity)
1272
 
 
1273
 
class StatusMgmt(UserInterface):
1274
 
    '''
1275
 
    The CIB status section management user interface class
1276
 
    '''
1277
 
    lvl_name = "cibstatus"
1278
 
    def __init__(self):
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)
1289
 
        setup_aliases(self)
1290
 
    def myname(self):
1291
 
        '''Just return some id.'''
1292
 
        return "cibstatus"
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):
1300
 
        "usage: origin"
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]"
1305
 
        if changed:
1306
 
            if changed != "changed":
1307
 
                syntax_err((cmd,changed))
1308
 
                return False
1309
 
            else:
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)
1316
 
            return False
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]
1325
 
        else:
1326
 
            num_rc = rc
1327
 
        if not num_rc.isdigit():
1328
 
            common_err("%s exit code invalid" % num_rc)
1329
 
            return False
1330
 
        num_op_status = op_status
1331
 
        if 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)
1336
 
                return False
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)
1344
 
 
1345
 
class CibConfig(UserInterface):
1346
 
    '''
1347
 
    The configuration class
1348
 
    '''
1349
 
    lvl_name = "configure"
1350
 
    desc_short = "CRM cluster configuration"
1351
 
    desc_long = """
1352
 
The configuration level.
1353
 
 
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
1356
 
cluster.
1357
 
"""
1358
 
    def __init__(self):
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",),
1398
 
            "ms": ("master",),
1399
 
        })
1400
 
        setup_aliases(self)
1401
 
        cib_factory.initialize()
1402
 
    def myname(self):
1403
 
        '''Just return some id.'''
1404
 
        return "cibconfig"
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():
1414
 
            return False
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():
1422
 
            return False
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():
1430
 
            return False
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)
1439
 
        else:
1440
 
            rc2 = 0
1441
 
        return rc1 and rc2 <= 1
1442
 
    def verify(self,cmd):
1443
 
        "usage: verify"
1444
 
        if not cib_factory.is_cib_sane():
1445
 
            return False
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():
1451
 
            return False
1452
 
        if args[0] == "xml":
1453
 
            f = args[1]
1454
 
            set_obj = mkset_obj("xml")
1455
 
        else:
1456
 
            f = args[0]
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():
1462
 
            return False
1463
 
        if args[0] == "xml":
1464
 
            if len(args) != 3:
1465
 
                syntax_err(args, context = 'load')
1466
 
                return False
1467
 
            url = args[2]
1468
 
            method = args[1]
1469
 
            set_obj = mkset_obj("xml","NOOBJ")
1470
 
        else:
1471
 
            if len(args) != 2:
1472
 
                syntax_err(args, context = 'load')
1473
 
                return False
1474
 
            url = args[1]
1475
 
            method = args[0]
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():
1481
 
            return False
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():
1486
 
            return False
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():
1491
 
            return False
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():
1496
 
            return False
1497
 
        if nodes:
1498
 
            if nodes == "nodes":
1499
 
                return cib_factory.erase_nodes()
1500
 
            else:
1501
 
                syntax_err((cmd,nodes), context = 'erase')
1502
 
        else:
1503
 
            return cib_factory.erase()
1504
 
    def refresh(self,cmd):
1505
 
        "usage: refresh"
1506
 
        if not cib_factory.is_cib_sane():
1507
 
            return False
1508
 
        if options.interactive and cib_factory.has_cib_changed():
1509
 
            if not ask("All changes will be dropped. Do you want to proceed?"):
1510
 
                return
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():
1515
 
            return False
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))
1522
 
            return False
1523
 
        if not cib_factory.is_cib_sane():
1524
 
            return False
1525
 
        if not cib_factory.has_cib_changed():
1526
 
            common_info("apparently there is nothing to commit")
1527
 
            common_info("try changing something first")
1528
 
            return
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"))
1532
 
        if rc1 and rc2:
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)
1539
 
        return False
1540
 
    def upgrade(self,cmd,force = None):
1541
 
        "usage: upgrade [force]"
1542
 
        if not cib_factory.is_cib_sane():
1543
 
            return False
1544
 
        if force and force != "force":
1545
 
            syntax_err((cmd,force))
1546
 
            return False
1547
 
        if user_prefs.get_force() or force:
1548
 
            return cib_factory.upgrade_cib_06to10(True)
1549
 
        else:
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():
1554
 
            return False
1555
 
        f = lambda: cib_factory.create_object(cmd,*args)
1556
 
        return f()
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>...]]
1567
 
        [operations id_spec
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}
1587
 
 
1588
 
        node_pref :: <score>: <node>
1589
 
 
1590
 
        rules ::
1591
 
          rule [id_spec] [$role=<role>] <score>: <expression>
1592
 
          [rule [id_spec] [$role=<role>] <score>: <expression> ...]
1593
 
 
1594
 
        id_spec :: $id=<id> | $id-ref=<id>
1595
 
        score :: <number> | <attribute> | [-]inf
1596
 
        expression :: <simple_exp> [bool_op <simple_exp> ...]
1597
 
        bool_op :: or | and
1598
 
        simple_exp :: <attribute> [type:]<binary_op> <value>
1599
 
                      | <unary_op> <attribute>
1600
 
                      | date <date_expr>
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>]
1607
 
        """
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):
1623
 
        "usage: xml <xml>"
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}
1630
 
 
1631
 
        roles :: role:<role-ref> [role:<role-ref> ...]
1632
 
        rules :: rule [rule ...]
1633
 
 
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 ...]
1638
 
 
1639
 
        rule :: acl-right cib-spec [attribute:<attribute>]
1640
 
 
1641
 
        acl-right :: read | write | deny
1642
 
 
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>
1646
 
 
1647
 
        shortcut :: meta:<rsc>[:<attr>]
1648
 
                    params:<rsc>[:<attr>]
1649
 
                    utilization:<rsc>
1650
 
                    location:<rsc>
1651
 
                    property[:<attr>]
1652
 
                    node[:<node>]
1653
 
                    nodeattr[:<attr>]
1654
 
                    nodeutil[:<node>]
1655
 
                    status"""
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")
1662
 
        cib_factory.reset()
1663
 
 
1664
 
class TopLevel(UserInterface):
1665
 
    '''
1666
 
    The top level.
1667
 
    '''
1668
 
    lvl_name = "."
1669
 
    crm_mon = "crm_mon -1"
1670
 
    status_opts = {
1671
 
        "bynode": "-n",
1672
 
        "inactive": "-r",
1673
 
        "ops": "-o",
1674
 
        "timing": "-t",
1675
 
        "failcounts": "-f",
1676
 
    }
1677
 
    def __init__(self):
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
1686
 
        setup_aliases(self)
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)
1691
 
            except: pass
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.
1696
 
 
1697
 
Usage:
1698
 
...............
1699
 
        status [<option> ...]
1700
 
 
1701
 
        option :: bynode | inactive | ops | timing | failcounts
1702
 
...............
1703
 
""")
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
1711
 
        """
1712
 
        status_cmd = self.crm_mon
1713
 
        for par in args:
1714
 
            if par in self.status_opts:
1715
 
                status_cmd = "%s %s" % (status_cmd, self.status_opts[par])
1716
 
            else:
1717
 
                syntax_err((cmd,par), context = 'status')
1718
 
                return False
1719
 
        return ext_cmd(status_cmd) == 0
1720
 
 
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()
1729
 
 
1730
 
# vim:ts=4:sw=4:et: