1
|
|
2
|
|
3
|
|
4
|
|
5
|
|
6
|
|
7
|
|
8
|
|
9
|
|
10
|
|
11
|
|
12
|
|
13
|
|
14
|
|
15
|
|
16
|
|
17
|
|
18
|
|
19
|
"""Haizea uses a policy manager that allows certain scheduling decisions to
|
20
|
be delegated to pluggable policies. This is done so scheduling policies
|
21
|
can be (1) modified without having to modify core components of Haizea, and
|
22
|
(2) implemented by writing a single Python class that implements a given
|
23
|
interface for pluggable policies.
|
24
|
|
25
|
Three policies are currently pluggable: lease preemptability ("Can lease X
|
26
|
preempt lease Y?"), host selection ("I want to deploy a VM, what host should
|
27
|
I select for this?") and lease admission ("Should I accept/reject this lease
|
28
|
request?"). Haizea provides several simple policy modules in the
|
29
|
haizea.policies package. The policy to use is selected in the configuration
|
30
|
file. See the Haizea Documentation for more details on how this is done.
|
31
|
|
32
|
This module provides Haizea's policy manager and the base classes for
|
33
|
pluggable policies.
|
34
|
"""
|
35
|
|
36
|
|
37
|
from haizea.common.utils import abstract
|
38
|
from mx.DateTime import DateTimeDelta
|
39
|
import operator
|
40
|
|
41
|
class PolicyManager(object):
|
42
|
"""The Policy Manager
|
43
|
|
44
|
This class manages the policy modules and provides methods to
|
45
|
access these modules.
|
46
|
|
47
|
"""
|
48
|
def __init__(self, admission, preemption, host_selection, pricing):
|
49
|
"""Constructor
|
50
|
|
51
|
Expects fully-constructed policies (these are currently
|
52
|
loaded in the Manager class, based on the config file).
|
53
|
|
54
|
Arguments:
|
55
|
admission -- A child of LeaseAdmissionPolicy
|
56
|
preemption -- A child of PreemptabilityPolicy
|
57
|
host_selection -- A child of HostSelectionPolicy
|
58
|
pricing -- A child of PricingPolicy
|
59
|
|
60
|
"""
|
61
|
self.admission = admission
|
62
|
self.preemption = preemption
|
63
|
self.host_selection = host_selection
|
64
|
self.pricing = pricing
|
65
|
|
66
|
def sort_leases(self, preemptor, preemptees, time):
|
67
|
"""Sorts a list of leases by their preemptability
|
68
|
|
69
|
Takes a list of leases (the "preemptees"), determines their preemptability
|
70
|
by another lease (the "preemptor"), and returns a list with the
|
71
|
leases sorted by decreasing preemptability score (most preemptable
|
72
|
leases first)
|
73
|
|
74
|
See documentation of PreemptabilityPolicy.get_lease_preemptability_score
|
75
|
for more details on the preemptability score.
|
76
|
|
77
|
Argument
|
78
|
preemptor -- Preemptor lease
|
79
|
preemptees -- List of preemptee leases
|
80
|
time -- Time at which preemption would take place
|
81
|
"""
|
82
|
leases_score = [(preemptee, self.get_lease_preemptability_score(preemptor,preemptee, time)) for preemptee in preemptees]
|
83
|
leases_score = [(preemptee,score) for preemptee,score in leases_score if score != -1]
|
84
|
leases_score.sort(key=operator.itemgetter(1), reverse=True)
|
85
|
return [preemptee for preemptee,score in leases_score]
|
86
|
|
87
|
|
88
|
def sort_hosts(self, nodes, time, lease):
|
89
|
"""Sorts a list of hosts by their score
|
90
|
|
91
|
Takes a list of hosts, determines their score, and sorts them in
|
92
|
order of decreasing score (most desireable hosts first)
|
93
|
|
94
|
See documentation of HostSelectionPolicy.get_host_score for more details.
|
95
|
|
96
|
Arguments:
|
97
|
nodes -- List of physical node (the integer identifier used in the slot table)
|
98
|
time -- Time at which the lease might be scheduled
|
99
|
lease -- Lease that is being scheduled.
|
100
|
"""
|
101
|
nodes_score = [(node, self.get_host_score(node, time, lease)) for node in nodes]
|
102
|
nodes_score.sort(key=operator.itemgetter(1), reverse=True)
|
103
|
return [node for node,score in nodes_score]
|
104
|
|
105
|
|
106
|
def accept_lease(self, lease):
|
107
|
"""Lease admission function
|
108
|
|
109
|
Returns True if the lease can be accepted, False if it should be rejected.
|
110
|
|
111
|
Argument
|
112
|
lease -- Lease request
|
113
|
"""
|
114
|
return self.admission.accept_lease(lease)
|
115
|
|
116
|
|
117
|
def get_lease_preemptability_score(self, preemptor, preemptee, time):
|
118
|
"""Computes the lease preemptability score
|
119
|
|
120
|
See documentation of PreemptabilityPolicy.get_lease_preemptability_score
|
121
|
for more details.
|
122
|
|
123
|
Arguments:
|
124
|
preemptor -- Preemptor lease
|
125
|
preemptee -- Preemptee lease
|
126
|
time -- Time at which preemption would take place
|
127
|
"""
|
128
|
return self.preemption.get_lease_preemptability_score(preemptor, preemptee, time)
|
129
|
|
130
|
|
131
|
def get_host_score(self, node, time, lease):
|
132
|
"""Computes the score of a host
|
133
|
|
134
|
See documentation of HostSelectionPolicy.get_host_score for more details.
|
135
|
|
136
|
Arguments:
|
137
|
node -- Physical node (the integer identifier used in the slot table)
|
138
|
time -- Time at which the lease might be scheduled
|
139
|
lease -- Lease that is being scheduled.
|
140
|
"""
|
141
|
return self.host_selection.get_host_score(node, time, lease)
|
142
|
|
143
|
|
144
|
def price_lease(self, lease, preempted_leases):
|
145
|
"""Computes the price of a lease
|
146
|
|
147
|
See documentation of PricingPolicy.price_lease for more details.
|
148
|
|
149
|
Arguments:
|
150
|
lease -- Lease that is being scheduled.
|
151
|
preempted_leases -- Leases that would have to be preempted to support this lease.
|
152
|
"""
|
153
|
return self.pricing.price_lease(lease, preempted_leases)
|
154
|
|
155
|
|
156
|
class LeaseAdmissionPolicy(object):
|
157
|
"""Lease Admission policy
|
158
|
|
159
|
This is the parent class of lease admission policies. A lease admission
|
160
|
policy determines whether a given lease request should be accepted or not
|
161
|
by Haizea. Note that this is distinct from whether the lease can be
|
162
|
scheduled or not (although this could certainly be a part of the
|
163
|
policy); the policy simply decides whether the lease can be considered for
|
164
|
scheduling or not. For example, a user could submit an AR lease that must
|
165
|
start in 5 hours, but the policy could dictate that all ARs must be notified
|
166
|
at least 24 hours in advance (and the lease would be rejected, regardless of
|
167
|
whether there was resources available for it in 5 hours). Similarly, an
|
168
|
AR lease could be requested 48 hours in advance, be accepted by the lease
|
169
|
admission policy, but then be rejected by the scheduler if there are no
|
170
|
resources available.
|
171
|
|
172
|
"""
|
173
|
def __init__(self, slottable):
|
174
|
"""Constructor
|
175
|
|
176
|
Argument
|
177
|
slottable -- A fully constructed SlotTable
|
178
|
"""
|
179
|
self.slottable = slottable
|
180
|
|
181
|
|
182
|
def accept_lease(self, lease):
|
183
|
"""Lease admission function
|
184
|
|
185
|
Returns True if the lease can be accepted, False if it should be rejected.
|
186
|
|
187
|
Argument
|
188
|
lease -- Lease request
|
189
|
"""
|
190
|
abstract()
|
191
|
|
192
|
|
193
|
|
194
|
class PreemptabilityPolicy(object):
|
195
|
"""Lease Preemptability policy
|
196
|
|
197
|
This is the parent class of lease preemptability policies. This type of
|
198
|
policy is used to determine whether a lease can be preempted by another
|
199
|
lease at a given time. However, the policy doesn't return True or False but,
|
200
|
rather, a "preemptability score" (see get_lease_preemptability_score for
|
201
|
more details)
|
202
|
|
203
|
"""
|
204
|
def __init__(self, slottable):
|
205
|
"""Constructor
|
206
|
|
207
|
Argument
|
208
|
slottable -- A fully constructed SlotTable
|
209
|
"""
|
210
|
self.slottable = slottable
|
211
|
|
212
|
|
213
|
def get_lease_preemptability_score(self, preemptor, preemptee, time):
|
214
|
"""Computes the lease preemptability score
|
215
|
|
216
|
Given a lease that needs to preempt resources (the "preemptor"),
|
217
|
another lease (the "preemptee") that may be preempted by it, and a time,
|
218
|
this method determines the preemptability score of the preemptee or
|
219
|
"how preemptable is the preemptee by the preemptor at the given time".
|
220
|
The score can be the following:
|
221
|
|
222
|
-1 : Cannot be preempted under any circumstances
|
223
|
0.0 <= x <= 1.0: Lease can be preempted. The higher the score,
|
224
|
the "more preemptable" it is (this is a relative measure; the score
|
225
|
should be used to determine which of several leases is a better
|
226
|
candidate for preemption)
|
227
|
|
228
|
Arguments:
|
229
|
preemptor -- Preemptor lease
|
230
|
preemptee -- Preemptee lease
|
231
|
time -- Time at which preemption would take place
|
232
|
"""
|
233
|
abstract()
|
234
|
|
235
|
|
236
|
def _get_aging_factor(self, lease, time):
|
237
|
"""Returns an aging factor for the preemptability score
|
238
|
|
239
|
This is a convenience function that can be used to "age" a
|
240
|
preemptability score (allowing leases that have been submitted
|
241
|
long ago to avoid preemption). The method returns a factor
|
242
|
between 0 and 1 that can be multiplied by the score, reducing
|
243
|
the score based on the lease's "age".
|
244
|
|
245
|
Currently, this method uses a hard-coded horizon of 7 days
|
246
|
(any lease older than 7 days cannot be preempted, and leases
|
247
|
less than 7 days are assigned a factor proportional to their age)
|
248
|
|
249
|
Arguments:
|
250
|
lease -- Lease that is going to be preempted
|
251
|
time -- Time at which preemption would take place
|
252
|
"""
|
253
|
|
254
|
horizon = time - DateTimeDelta(7)
|
255
|
if lease.submit_time <= horizon:
|
256
|
return -1
|
257
|
else:
|
258
|
seconds = (time - lease.submit_time).seconds
|
259
|
horizon_seconds = DateTimeDelta(31).seconds
|
260
|
return float(horizon_seconds - seconds) / horizon_seconds
|
261
|
|
262
|
|
263
|
class HostSelectionPolicy(object):
|
264
|
"""Host Selection policy
|
265
|
|
266
|
This is the parent class of host selection policies. When mapping VMs
|
267
|
to physical hosts, this policy determines what hosts are more desireable.
|
268
|
For example, an energy-saving policy might value hosts that already have
|
269
|
VMs running (to leave as many empty machines as possible, which could then
|
270
|
be turned off), whereas another policy might prefer empty hosts to make
|
271
|
sure that VMs are spread out across nodes.
|
272
|
|
273
|
To do this, the policy will assign a score to each host. See the documentation
|
274
|
for get_host_score for more details.
|
275
|
|
276
|
"""
|
277
|
def __init__(self, slottable):
|
278
|
"""Constructor
|
279
|
|
280
|
Argument
|
281
|
slottable -- A fully constructed SlotTable
|
282
|
"""
|
283
|
self.slottable = slottable
|
284
|
|
285
|
|
286
|
def get_host_score(self, node, time, lease):
|
287
|
"""Computes the score of a host
|
288
|
|
289
|
|
290
|
|
291
|
Given a physical host, a time, and a lease we would like to
|
292
|
schedule at that time, this method returns a score indicating
|
293
|
how desireable that host is for that lease at that time.
|
294
|
The score can be between 0.0 and 1.0. The higher the score,
|
295
|
the "more desireable" the physical host is (this is a relative measure;
|
296
|
the score should be used to determine which of several physical hosts
|
297
|
is more desireable for this lease).
|
298
|
|
299
|
Arguments:
|
300
|
node -- Physical node (the integer identifier used in the slot table)
|
301
|
time -- Time at which the lease might be scheduled
|
302
|
lease -- Lease that is being scheduled.
|
303
|
"""
|
304
|
abstract()
|
305
|
|
306
|
|
307
|
class PricingPolicy(object):
|
308
|
"""Pricing policy
|
309
|
|
310
|
...
|
311
|
|
312
|
"""
|
313
|
def __init__(self, slottable):
|
314
|
"""Constructor
|
315
|
|
316
|
Argument
|
317
|
slottable -- A fully constructed SlotTable
|
318
|
"""
|
319
|
self.slottable = slottable
|
320
|
|
321
|
|
322
|
def price_lease(self, lease, preempted_leases):
|
323
|
"""Computes the price of a lease
|
324
|
|
325
|
Arguments:
|
326
|
lease -- Lease that is being scheduled.
|
327
|
preempted_leases -- Leases that would have to be preempted to support this lease.
|
328
|
"""
|
329
|
abstract()
|
330
|
|
331
|
def feedback(self, lease):
|
332
|
"""Called after a lease has been accepted or rejected, to provide
|
333
|
feeback to the pricing policy.
|
334
|
|
335
|
Arguments:
|
336
|
lease -- Lease that has been accepted/rejected
|
337
|
"""
|
338
|
pass
|