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 |
|