Statistics
| Branch: | Tag: | Revision:

root / commissioning / api / controller.py @ 9f1a1bd0

History | View | Annotate | Download (7 kB)

1
from .exception import CorruptedError
2
from .callpoint import Callpoint
3
from .physical import Physical
4

    
5
class Controller(object):
6

    
7
    def __init__(self, quotaholder, physical):
8
        self.quotaholder = quotaholder
9
        self.physical = physical
10
        self.controller_init()
11

    
12
    def controller_init(self):
13
        pass
14

    
15
    def get_commission_issue(self, commission_spec):
16
        """Prepare and return the arguments for the
17
           quotaholder's issue_commission call,
18
           containing the provisions required and
19
           the target entity for their allocation.
20
        """
21
        raise NotImplementedError
22

    
23
    def register_commission(self,   serial,
24
                                    clientkey,
25
                                    physical_description    ):
26

    
27
        """Register a commission to the controller's stable storage,
28
           along with the quotaholder serial and clientkey,
29
           and the target physical description.
30
           This information is needed to co-ordinate the commission
31
           execution among the quotaholder server, the controller,
32
           and the physical layer implementing the resource.
33
        """
34
        raise NotImplementedError
35

    
36
    def get_commission(self, serial):
37
        """Retrieve the commission registered with serial"""
38
        raise NotImplementedError
39

    
40
    def complete_commission(self, serial):
41
        """Mark and commit in stable storage the commission identified by
42
           a serial as to-be-completed-successfully,
43
           i.e that it has succeeded in producing a physical resource
44
           and is to be removed from being tracked by the holder server,
45
           controller, and physical layers.
46
        """
47
        raise NotImplementedError
48

    
49
    def is_commission_complete(self, serial):
50
        """Return true if the serial is marked as
51
           completed by complete_commission()
52
        """
53
        raise NotImplementedError
54

    
55
    def fail_commission(self, serial):
56
        """Mark and commit in stable storage the commission identified by
57
           a serial as to-be-completed-unsuccessfully,
58
           i.e. that it has failed in producing a physical resource
59
           and is to be removed from being tracked by the holder server,
60
           controller, and physical layers.
61
        """
62
        raise NotImplementedError
63

    
64
    def is_commission_failing(self, serial):
65
        """Return true if the serial is marked as
66
           failing by fail_commission()
67
        """
68
        raise NotImplementedError
69

    
70
    def retire_commission(self, serial):
71
        """Stop tracking the commission identified by a serial"""
72
        raise NotImplementedError
73

    
74
    def undertake_commission(self, commission_spec):
75
        """Initiate and start tracking and co-ordinating a commission
76
           from a commission spec.
77
        """
78
        holder = self.quotaholder
79
        physical = self.physical
80

    
81
        commission_issue = self.get_commission_issue(commission_spec)
82
        entity = commission_issue['entity']
83
        clientkey = commission_issue['clientkey']
84
        physical_description = self.derive_description(commission_spec)
85

    
86
        serial = holder.issue_commission(entity, provisions)
87
        self.register_commission(   serial,
88
                                    clientkey,
89
                                    physical_description    )
90

    
91
        self.process_controller(serial)
92
        return serial
93

    
94
    def process_controller(self, serial):
95
        """Consider the current state of a commission in the controller layer,
96
           and schedule next actions.
97
        """
98
        holder = self.quotaholder
99
        physical = self.physical
100
        controller = self
101

    
102
        r = controller.get_commission(serial)
103
        if not r:
104
            return
105

    
106
        serial, clientkey, physical_description, status = r
107

    
108
        if controller.is_commission_complete(serial):
109
            holder.accept_commission(serial=serial, clientkey=clientkey)
110
            physical.end_commission(serial, physical_description)
111
            controller.retire_commission(serial)
112

    
113
        elif controller.is_commission_failing(serial):
114
            holder.recall_commission(serial=serial, clientkey=clientkey)
115
            physical.end_commission(serial, physical_description)
116
            controller.retire_commission(serial)
117

    
118
        else:
119
            controller.process_physical(serial)
120

    
121
    def process_physical(self, serial):
122
        """Consider the current state of a commission in the physical layer,
123
           and schedule next actions.
124
        """
125
        physical = self.physical
126

    
127
        r = self.get_commission(serial)
128
        if not r:
129
            m = "Unknown serial %d in process_physical!" % (serial,)
130
            raise CorruptedError(m)
131

    
132
        target_description = r[2]
133
        current_state = physical.get_current_state(serial, target_description)
134

    
135
        if not current_state:
136

    
137
            physical.initiate_commission(serial, target_description)
138

    
139
        elif physical.complies(current_state, target_description):
140

    
141
            self.complete_commission(serial)
142
            self.process_controller(serial)
143

    
144
        elif physical.attainable(current_state, target_description):
145

    
146
            physical.continue_commission(serial, target_description)
147

    
148
        else:
149
            self.fail_commission(serial)
150
            physical.end_commission(serial, target_description)
151
            self.process_controller(serial)
152

    
153

    
154
class ControlledCallpoint(Callpoint):
155

    
156
    controllables = set()
157

    
158
    def __init__(self, *args):
159
        self.controllables = set()
160
        super(ControlledCallpoint, self).__init__(*args)
161

    
162
    def commission_from_call(self, call_name, call_data):
163
        commission_spec = {}
164
        commission_spec['call_name'] = call_name
165
        commission_spec['call_data'] = call_data
166
        commission_spec['provisions'] = ()
167
        return commission_spec, True
168

    
169
    def register_controllable(self, call_name):
170
        controllable = self.controllables
171
        if call_name in controllables:
172
            return
173

    
174
        try:
175
            canonify_output = self.api_spec.canonify_output
176
            s = canonify_output(None) is None
177
            s += isinstance(canonify_output(1), int)
178
            s += isinstance(canonify_output(2**63), int)
179
            if s != 3:
180
                raise CanonifyException
181
        except CanonifyException, e:
182
            m = ("Attempt to register controllable call '%s', but"
183
                 "the api spec does not define a nullable int (serial) output!"
184
                                                                % (call_name,))
185
            raise CanonifyException(m, e)
186

    
187
        controllables.add(call_name)
188

    
189
    def do_make_call(self, call_name, call_data):
190
        r = self.commission_from_call(call_name, call_data)
191
        commission_spec, controllable = r
192
        controller = self.controller
193

    
194
        if not controllable:
195
            return controller.forward_commission(commission_spec)
196

    
197
        if call_name not in self.controllables:
198
            self.register_controllable(call_name)
199

    
200
        serial = controller.undertake_commission(commission_spec)
201
        return serial
202