【Error Message】
-------------------------------------------------------------------------------Log Info--------------------------------------------------------------------------------------------------------------------------
Code: 500
Message: No valid host was found
Details: File "/usr/lib/python2.7/dist-packages/nova/scheduler/filter_scheduler.py", line108, in schedule_run_instance raise exception.NoValidHost(reason="")
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
原因:集群中没有节点可以满足launch instance指定需要大小的资源,比如虚拟内存/磁盘空间等。导致scheduler在
#这部分源代码解析转自:http://blog.csdn.net/gaoxingnengjisuan/article/details/15734437
******************************************附源代码解析*******************************************************
*/nova/scheduler/filter_scheduler.py //实现了基于主机过滤器选取主机节点方式的调度器
def schedule_run_instance(self, context, request_spec,
admin_password, injected_files,
requested_networks, is_first_time,
filter_properties):
"""
这个方法被从nova.compute.api调用,来提供实例;
返回创建的实例的列表;
获取要建立实例的数目;
循环每一个实例,获取分配给这个实例的主机;
拨备创建实例请求的各种资源;
远程发送运行实例的消息到队列;
发布调度运行实例结束这个事件的通知;
"""
# request_spec:请求规范;
# 这个参数的格式字典化;
payload = dict(request_spec=request_spec)
# notifier.publisher_id("scheduler"):获取信息发布者ID;
# 返回值为"scheduler.host";
# notify:使用指定的驱动程序发布通知;(这个方法需要进一步分析它的具体实现过程)
notifier.notify(context, notifier.publisher_id("scheduler"),'scheduler.run_instance.start', notifier.INFO, payload)
# 从request_spec当中获取instance_uuids值;
instance_uuids = request_spec.pop('instance_uuids')
# 获取要建立实例的数目;
num_instances = len(instance_uuids)
LOG.debug(_("Attempting to build %(num_instances)d instance(s)") % locals())
# 返回满足要求规格的主机列表;
# 循环为每一个实例获取合适的主机后,返回选择的主机列表;
weighed_hosts = self._schedule(context, request_spec, filter_properties, instance_uuids)
# @@@这个语句没有弄明白,后面要继续看一下;
filter_properties.pop('context', None)
# 循环遍历instance_uuids;
for num, instance_uuid in enumerate(instance_uuids):
request_spec['instance_properties']['launch_index'] = num
try:
# 获取分配给这个实例的主机;
try:
weighed_host = weighed_hosts.pop(0) #############问题就出在这
except IndexError:
raise exception.NoValidHost(reason="")
# context:上下文信息;
# weighed_host:获取分配给这个实例的主机;
# request_spec:请求规范;
# filter_properties:过滤器属性信息;
# requested_networks:请求的网络信息;
# injected_files:注入文件;
# admin_password:admin密码;
# is_first_time:标志是否是第一次;
# instance_uuid:每个实例的UUID;
# _provision_resource:在这个区域内拨备创建请求的各种资源;
# 远程发送建立实例的消息到队列;
self._provision_resource(context, weighed_host,
request_spec,
filter_properties,
requested_networks,
injected_files, admin_password,
is_first_time,
instance_uuid=instance_uuid)
except Exception as ex:
driver.handle_schedule_error(context, ex, instance_uuid, request_spec)
retry = filter_properties.get('retry', {})
retry['hosts'] = []
# @@@notify:使用指定的驱动程序发布通知;
# 应该是发布调度运行实例结束这个事件的通知;
notifier.notify(context, notifier.publisher_id("scheduler"),
'scheduler.run_instance.end', notifier.INFO, payload)
<pre name="code" class="python">def _schedule(self, context, request_spec, filter_properties, instance_uuids=None):
"""
返回满足要求规格的主机列表;
循环为每一个实例获取合适的主机后,返回选择的主机列表;
对主机进行过滤;
FilterManager类继承自Scheduler类;
在Scheduler类的初始化中,加载了所有可用的filter类;
根据配置文件中scheduler_default_filters字段的定义选择默认使用的一个或多个filter;
依次对每个主机调用filter类的host_passes()方法,如果返回都为True,则主机通过过滤;
对所有通过过滤的主机计算权值;
"""
# elevated:返回带有admin标志设置的context的版本;
elevated = context.elevated()
# 获取实例属性信息(instance_properties);
instance_properties = request_spec['instance_properties']
# 获取实例类型信息(instance_type);
instance_type = request_spec.get("instance_type", None)
update_group_hosts = False
# 获取scheduler_hints;
scheduler_hints = filter_properties.get('scheduler_hints') or {}
# 获取group信息;
group = scheduler_hints.get('group', None)
if group:
# group_hosts:获取group中所有具有经过过滤后符合过滤条件的主机的列表;
group_hosts = self.group_hosts(elevated, group)
# 表示已经更新过group_hosts;
update_group_hosts = True
# 如果filter_properties中不包含'group_hosts',则增加'group_hosts'到filter_properties中;
if 'group_hosts' not in filter_properties:
filter_properties.update({'group_hosts': []})
# 获取filter_properties中原有的'group_hosts';
# 把两部分'group_hosts'加在一起;(@@@应该都是符合条件的,自己的理解;)
configured_hosts = filter_properties['group_hosts']
filter_properties['group_hosts'] = configured_hosts + group_hosts
# 获取配置选项;
config_options = self._get_configuration_options()
# 要建立实例的属性信息对象拷贝;
properties = instance_properties.copy()
# 从instance_uuids获取properties['uuid'];
if instance_uuids:
properties['uuid'] = instance_uuids[0]
# 记录尝试次数;
self._populate_retry(filter_properties, properties)
# 更新过滤器属性信息;
filter_properties.update({'context': context,
'request_spec': request_spec,
'config_options': config_options,
'instance_type': instance_type})
# 从request_spec获取有用的信息,填充到过滤器属性当中;
# 分别是filter_properties['os_type']和filter_properties['project_id'];
self.populate_filter_properties(request_spec, filter_properties)
# 寻找可以接受的本地主机列表通过对我们的选项进行反复的过滤和称重;
# 这里我们使用了迭代,所以只遍历了一次这个列表;
# 获取并返回HostStates列表;
# 过滤掉不满足要求的主机;
hosts = self.host_manager.get_all_host_states(elevated)
selected_hosts = []
# 获取要建立的实例数目;
if instance_uuids:
num_instances = len(instance_uuids)
else:
num_instances = request_spec.get('num_instances', 1)
# 遍历num_instances个实例,为每个实例选取合适的运行主机;
for num in xrange(num_instances):
# Filter local hosts based on requirements ...
# 基于具体要求过滤本地主机;
# get_filtered_hosts:过滤主机,返回那些通过所有过滤器的主机;
hosts = self.host_manager.get_filtered_hosts(hosts, filter_properties)
if not hosts:
break
LOG.debug(_("Filtered %(hosts)s") % locals())
# 对主机进行称重;
# 获取并返回一个WeighedObjects的主机排序列表(最高分排在第一);
weighed_hosts = self.host_manager.get_weighed_hosts(hosts, filter_properties)
# scheduler_host_subset_size:这个参数定义了新的实例将会被调度到一个主机上,这个主机是随机的从最好的(分数最高的)N个主机组成的子集中选择出来的;
# 这个参数定义了这个子集的大小,供选择最好的主机使用;
# 如果值为1,则由称重函数返回第一个主机;
# 这个值至少为1,任何小于1的值都会被忽略,而且会被1所代替;
# 这个参数的默认值为1;
scheduler_host_subset_size = CONF.scheduler_host_subset_size
if scheduler_host_subset_size > len(weighed_hosts):
scheduler_host_subset_size = len(weighed_hosts)
if scheduler_host_subset_size < 1:
scheduler_host_subset_size = 1
# 按照上面的解释,从分数最高的若干主机组成的子集中,随机的选择一个主机出来;
# 新的实例将会被调度到这个主机上;
chosen_host = random.choice(
weighed_hosts[0:scheduler_host_subset_size])
LOG.debug(_("Choosing host %(chosen_host)s") % locals())
# 把选好的主机增加到selected_hosts列表中;
selected_hosts.append(chosen_host)
# 因为已经选好了一个主机,所以要在下一个实例选择主机前,更新主机资源信息;
chosen_host.obj.consume_from_instance(instance_properties)
if update_group_hosts is True:
filter_properties['group_hosts'].append(chosen_host.obj.host)
# 循环为每一个实例获取合适的主机后,返回选择的主机列表;
return selected_hosts