summaryrefslogtreecommitdiffstats
path: root/crmsh/ui_assist.py
blob: f5d1b68a2a081ba7f7273e283c8247660971aec4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# Copyright (C) 2014 Kristoffer Gronlund <kgronlund@suse.com>
# See COPYING for license information.

from . import utils
from . import command
from . import completers as compl
from . import xmlutil
from .cibconfig import cib_factory


def rmattrs(e, *attrs):
    "remove the given attributes from an XML element"
    for attr in attrs:
        if attr in e.attrib:
            del e.attrib[attr]


class Assist(command.UI):
    '''
    The assist UI collects what could be called
    configuration macros. Things like merging
    multiple resources into a template, or building
    a colocated set with a relation to a dummy
    resource.
    '''
    name = "assist"

    def __init__(self):
        command.UI.__init__(self)

    def requires(self):
        return cib_factory.initialize()

    @command.skill_level('administrator')
    @command.completers_repeating(compl.call(cib_factory.prim_id_list))
    def do_template(self, context, *primitives):
        '''
        Create a shared template for the given primitives
        '''
        if len(primitives) < 1:
            context.fatal_error("Expected at least one primitive argument")
        objs = [cib_factory.find_resource(p) for p in primitives]
        for prim, obj in zip(primitives, objs):
            if obj is None:
                context.fatal_error("Primitive %s not found" % (prim))
        if objs and all(obj.obj_type == 'primitive' for obj in objs):
            return self._template_primitives(context, objs)
        context.fatal_error("Cannot create a template for the given resources")

    def _template_primitives(self, context, primitives):
        """
        Try to template the given primitives:
        Templating means creating a rsc_template and moving
        shared attributes and other commonalities into that template
        (this second step is currently not available)
        """
        shared_template = None
        if all('template' in obj.node.attrib for obj in primitives):
            return True
        if len(set(xmlutil.mk_rsc_type(obj.node) for obj in primitives)) != 1:
            context.fatal_error("Cannot template the given primitives")

        node = primitives[0].node
        template_name = self.make_unique_name('template-%s-' % (node.get('type').lower()))
        shared_template = cib_factory.create_object('rsc_template', template_name,
                                                    xmlutil.mk_rsc_type(node))
        if not shared_template:
            context.fatal_error("Error creating template")
        for obj in primitives:
            obj.node.set('template', template_name)
            rmattrs(obj.node, 'class', 'provider', 'type')
            obj.set_updated()

        if not self._pull_attributes(context, shared_template, primitives):
            context.fatal_error("Error when copying attributes into template")

        context.info("Template created: %s" % (template_name))
        return True

    def _pull_attributes(self, context, template, primitives):
        '''
        TODO: take any attributes shared by all primitives and
        move them into the shared template
        '''
        return True

    @command.skill_level('administrator')
    @command.completers_repeating(compl.call(cib_factory.prim_id_list))
    @command.name('weak-bond')
    @command.alias('weak_bond')
    def do_weak_bond(self, context, *nodes):
        '''
        Create a 'weak' colocation:
        Colocating a non-sequential resource set with
        a dummy resource which is not monitored creates,
        in effect, a colocation which does not imply any
        internal relationship between resources.
        '''
        if len(nodes) < 2:
            context.fatal_error("Need at least two arguments")

        for node in nodes:
            obj = cib_factory.find_resource(node)
            if not obj:
                context.fatal_error("Object not found: %s" % (node))
            if not xmlutil.is_primitive(obj.node):
                context.fatal_error("Object not primitive: %s" % (node))

        constraint_name = self.make_unique_name('place-constraint-')
        dummy_name = self.make_unique_name('place-dummy-')
        print("Create weak bond / independent colocation")
        print("The following elements will be created:")
        print("   * Colocation constraint, ID: %s" % (constraint_name))
        print("   * Dummy resource, ID: %s" % (dummy_name))
        if not utils.can_ask() or utils.ask("Create resources?"):
            cib_factory.create_object('primitive', dummy_name, 'ocf:heartbeat:Dummy')
            colo = ['colocation', constraint_name, 'inf:', '(']
            colo.extend(nodes)
            colo.append(')')
            colo.append(dummy_name)
            cib_factory.create_object(*colo)

    def make_unique_name(self, prefix):
        n = 0
        while n < 1000:
            n += 1
            name = "%s%s" % (prefix, n)
            for _id in cib_factory.id_list():
                if name == _id.lower():
                    continue
            return name
        raise ValueError("Failed to generate unique resource ID with prefix '%s'" % (prefix))