~blamar/nova/gracefull-shutdown

« back to all changes in this revision

Viewing changes to nova/scheduler/zone_aware_scheduler.py

compute_api.get_all should be able to recurse zones (bug 744217).
Also, allow to build more than one instance at once with zone_aware_scheduler types.
Other cleanups with regards to zone aware scheduler...
 

Show diffs side-by-side

added added

removed removed

Lines of Context:
180
180
                                    request_spec, kwargs)
181
181
            return None
182
182
 
 
183
        num_instances = request_spec.get('num_instances', 1)
 
184
        LOG.debug(_("Attempting to build %(num_instances)d instance(s)") %
 
185
                locals())
 
186
 
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'))
187
191
 
188
 
        for num in xrange(request_spec['num_instances']):
 
192
        for num in xrange(num_instances):
189
193
            if not build_plan:
190
194
                break
191
195
 
192
 
            item = build_plan.pop(0)
193
 
            self._provision_resource(context, item, instance_id, request_spec,
194
 
                                    kwargs)
 
196
            build_plan_item = build_plan.pop(0)
 
197
            self._provision_resource(context, build_plan_item, instance_id,
 
198
                                     request_spec, kwargs)
195
199
 
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)"))
226
230
 
227
 
        #TODO(sandy): how to infer this from OS API params?
228
 
        num_instances = 1
229
 
 
230
 
        # Filter local hosts based on requirements ...
231
 
        host_list = self.filter_hosts(num_instances, request_spec)
232
 
 
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
235
 
 
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']
 
233
 
 
234
        weighted = []
 
235
        host_list = None
 
236
 
 
237
        for i in xrange(num_instances):
 
238
            # Filter local hosts based on requirements ...
 
239
            #
 
240
            # The first pass through here will pass 'None' as the
 
241
            # host_list.. which tells the filter to build the full
 
242
            # list of hosts.
 
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)
 
247
            if not host_list:
 
248
                LOG.warn(_("Filter returned no hosts after processing "
 
249
                        "%(i)d of %(num_instances)d instances") % locals())
 
250
                break
 
251
 
 
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'],
 
260
                    instance_type)
239
261
 
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'))
255
277
        return weighted
256
278
 
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.
260
 
        """
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()]
265
 
 
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.
 
283
        """
 
284
        instance_type = request_spec['instance_type']
 
285
        requested_mem = instance_type['memory_mb'] * 1024 * 1024
 
286
        return capabilities['host_memory_free'] >= requested_mem
 
287
 
 
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.
 
293
        """
 
294
 
 
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
 
298
            # AllHostsFilter
 
299
            return True
 
300
 
 
301
        filter_func = getattr(self, '%s_filter' % topic, _default_filter)
 
302
 
 
303
        if host_list is None:
 
304
            first_run = True
 
305
            host_list = self.zone_manager.service_states.iteritems()
 
306
        else:
 
307
            first_run = False
 
308
 
 
309
        filtered_hosts = []
 
310
        for host, services in host_list:
 
311
            if first_run:
 
312
                if topic not in services:
 
313
                    continue
 
314
                services = services[topic]
 
315
            if filter_func(host, services, request_spec):
 
316
                filtered_hosts.append((host, services))
 
317
        return filtered_hosts
 
318
 
 
319
    def weigh_hosts(self, topic, request_spec, hosts):
267
320
        """Derived classes may override this to provide more sophisticated
268
321
        scheduling objectives
269
322
        """
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]
 
326
 
 
327
    def compute_consume(self, capabilities, instance_type):
 
328
        """Consume compute resources for selected host"""
 
329
 
 
330
        requested_mem = max(instance_type['memory_mb'], 0) * 1024 * 1024
 
331
        capabilities['host_memory_free'] -= requested_mem
 
332
 
 
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"""
 
336
 
 
337
        consume_func = getattr(self, '%s_consume' % topic, None)
 
338
        if not consume_func:
 
339
            return
 
340
        consume_func(capabilities, instance_type)