180
180
request_spec, kwargs)
183
num_instances = request_spec.get('num_instances', 1)
184
LOG.debug(_("Attempting to build %(num_instances)d instance(s)") %
183
187
# Create build plan and provision ...
184
188
build_plan = self.select(context, request_spec)
185
189
if not build_plan:
186
190
raise driver.NoValidHost(_('No hosts were available'))
188
for num in xrange(request_spec['num_instances']):
192
for num in xrange(num_instances):
189
193
if not build_plan:
192
item = build_plan.pop(0)
193
self._provision_resource(context, item, instance_id, request_spec,
196
build_plan_item = build_plan.pop(0)
197
self._provision_resource(context, build_plan_item, instance_id,
198
request_spec, kwargs)
196
200
# Returning None short-circuits the routing to Compute (since
197
201
# we've already done it here)
224
228
raise NotImplemented(_("Zone Aware Scheduler only understands "
225
229
"Compute nodes (for now)"))
227
#TODO(sandy): how to infer this from OS API params?
230
# Filter local hosts based on requirements ...
231
host_list = self.filter_hosts(num_instances, request_spec)
233
# TODO(sirp): weigh_hosts should also be a function of 'topic' or
234
# resources, so that we can apply different objective functions to it
236
# then weigh the selected hosts.
237
# weighted = [{weight=weight, name=hostname}, ...]
238
weighted = self.weigh_hosts(num_instances, request_spec, host_list)
231
num_instances = request_spec.get('num_instances', 1)
232
instance_type = request_spec['instance_type']
237
for i in xrange(num_instances):
238
# Filter local hosts based on requirements ...
240
# The first pass through here will pass 'None' as the
241
# host_list.. which tells the filter to build the full
243
# On a 2nd pass, the filter can modify the host_list with
244
# any updates it needs to make based on resources that
245
# may have been consumed from a previous build..
246
host_list = self.filter_hosts(topic, request_spec, host_list)
248
LOG.warn(_("Filter returned no hosts after processing "
249
"%(i)d of %(num_instances)d instances") % locals())
252
# then weigh the selected hosts.
253
# weighted = [{weight=weight, hostname=hostname,
254
# capabilities=capabs}, ...]
255
weights = self.weigh_hosts(topic, request_spec, host_list)
256
weights.sort(key=operator.itemgetter('weight'))
257
best_weight = weights[0]
258
weighted.append(best_weight)
259
self.consume_resources(topic, best_weight['capabilities'],
240
262
# Next, tack on the best weights from the child zones ...
241
263
json_spec = json.dumps(request_spec)
254
276
weighted.sort(key=operator.itemgetter('weight'))
257
def filter_hosts(self, num, request_spec):
258
"""Derived classes must override this method and return
259
a list of hosts in [(hostname, capability_dict)] format.
261
# NOTE(sirp): The default logic is the equivalent to AllHostsFilter
262
service_states = self.zone_manager.service_states
263
return [(host, services)
264
for host, services in service_states.iteritems()]
266
def weigh_hosts(self, num, request_spec, hosts):
279
def compute_filter(self, hostname, capabilities, request_spec):
280
"""Return whether or not we can schedule to this compute node.
281
Derived classes should override this and return True if the host
282
is acceptable for scheduling.
284
instance_type = request_spec['instance_type']
285
requested_mem = instance_type['memory_mb'] * 1024 * 1024
286
return capabilities['host_memory_free'] >= requested_mem
288
def filter_hosts(self, topic, request_spec, host_list=None):
289
"""Return a list of hosts which are acceptable for scheduling.
290
Return value should be a list of (hostname, capability_dict)s.
291
Derived classes may override this, but may find the
292
'<topic>_filter' function more appropriate.
295
def _default_filter(self, hostname, capabilities, request_spec):
296
"""Default filter function if there's no <topic>_filter"""
297
# NOTE(sirp): The default logic is the equivalent to
301
filter_func = getattr(self, '%s_filter' % topic, _default_filter)
303
if host_list is None:
305
host_list = self.zone_manager.service_states.iteritems()
310
for host, services in host_list:
312
if topic not in services:
314
services = services[topic]
315
if filter_func(host, services, request_spec):
316
filtered_hosts.append((host, services))
317
return filtered_hosts
319
def weigh_hosts(self, topic, request_spec, hosts):
267
320
"""Derived classes may override this to provide more sophisticated
268
321
scheduling objectives
270
323
# NOTE(sirp): The default logic is the same as the NoopCostFunction
271
return [dict(weight=1, hostname=host) for host, caps in hosts]
324
return [dict(weight=1, hostname=hostname, capabilities=capabilities)
325
for hostname, capabilities in hosts]
327
def compute_consume(self, capabilities, instance_type):
328
"""Consume compute resources for selected host"""
330
requested_mem = max(instance_type['memory_mb'], 0) * 1024 * 1024
331
capabilities['host_memory_free'] -= requested_mem
333
def consume_resources(self, topic, capabilities, instance_type):
334
"""Consume resources for a specific host. 'host' is a tuple
335
of the hostname and the services"""
337
consume_func = getattr(self, '%s_consume' % topic, None)
340
consume_func(capabilities, instance_type)