在上一篇中,讲解了一下flatdhcp网络下nova-network服务在启动之前的准备工作:创建网桥、启动DNSmasq服务、添加iptables和ebtables规则等。
那这一篇,学习下创建虚拟机实例时,nova-network会进行的工作:
创建虚拟机实例时,nova-compute会通过rpc调用相应的(multi_host下通常就是自己所在主机上的)nova-network的allocate_for_instance API来为实例分配网络资源;
流程并不复杂,就是先为实例分配MAC地址然后分配固定IP,并在数据库进行记录;然后修改每个nova-network节点更新本地DNS的解析文件;最后修改DHCP服务的数据库文件,记录刚才分配的固定IP以及关联实例的主机名和MAC地址,并使DHCP优雅地重启;那么当实例启动后通过DHCP获取IP时,DHCP就把绑定其MAC地址的固定IP分配给它
class RPCAllocateFixedIP(object):
servicegroup_api = None
def _rpc_allocate_fixed_ip(self, context, instance_id, network_id,
**kwargs):
# 提高network_id获取network信息
network = self._get_network_by_id(context, network_id)
# 调用NetworkManager中allocate_fixed_ip来分配固定IP, 指定了address就使用address对应的固定IP;
# 如果没有指定, 就从固定IP池中选取一个未分配的与实例进行绑定
return self.allocate_fixed_ip(context, instance_id, network, **kwargs)
def _allocate_fixed_ips(self, context, instance_id, host, networks,
**kwargs):
green_threads = []
vpn = kwargs.get('vpn')
requested_networks = kwargs.get('requested_networks')
for network in networks:
address = None
if requested_networks is not None:
for address in (fixed_ip for (uuid, fixed_ip) in
requested_networks if network['uuid'] == uuid):
break
# 如果不是multi_host模式, 说明只有一个nova-network节点;
# 那么rpc调用的目标就是该节点
if not network['multi_host']:
host = network['host']
# 如果参数host为空并且数据库中的网络信息没有记录host, 那么进行一次rpc调用让存在的nova-network节点返回自己的host信息
if host is None:
network_p = obj_base.obj_to_primitive(network)
host = self.network_rpcapi.set_network_host(context,
network_p)
if host != self.host:
# 如果目标主机不是自己, 那么就意味着需要进行rpc调用;
# 因为可能需要针对多个网络来进行rpc调用, 为了提高响应速度, 使用协程来进行, 让多个调用能同时进行
green_threads.append(eventlet.spawn(
self.network_rpcapi._rpc_allocate_fixed_ip,
context, instance_id, network['id'], address, vpn,
host))
else:
# 调用NetworkManager中allocate_fixed_ip来分配固定IP
self.allocate_fixed_ip(context, instance_id, network,
vpn=vpn, address=address)
# 等待所有的协程处理完毕
for gt in green_threads:
gt.wait()
class NetworkManager(manager.Manager):
# 获取requested_networks中的网络信息或者当前租户的所有网络信息
def _get_networks_for_instance(self, context, instance_id, project_id,
requested_networks=None):
if requested_networks is not None and len(requested_networks) != 0:
network_uuids = [uuid for (uuid, fixed_ip) in requested_networks]
networks = self._get_networks_by_uuids(context, network_uuids)
else:
networks = network_obj.NetworkList.get_by_project(context,
project_id)
return networks
# 为实例分配一个固定IP, 要进行配额管理, 之前已经讲过, 大部分是数据库操作,
# 在此不做赘述, 重点关注下分配完后的操作_setup_network_on_host
def allocate_fixed_ip(self, context, instance_id, network, **kwargs):
if kwargs.get('vpn', None):
address = network['vpn_private_address']
fip = fixed_ip_obj.FixedIP.associate(context, address, instance_id,
network['id'], reserved=True)
else:
address = kwargs.get('address', None)
if address:
fip = fixed_ip_obj.FixedIP.associate(context, address,
instance_id,
network['id'])
else:
fip = fixed_ip_obj.FixedIP.associate_pool(context,
network['id'],
instance_id)
address = fip.address
vif = vif_obj.VirtualInterface.get_by_instance_and_network(
context, instance_id, network['id'])
fip.allocated = True
fip.virtual_interface_id = vif.id
fip.save()
if not kwargs.get('vpn', None):
self._do_trigger_security_group_members_refresh_for_instance(
instance_id)
instance = instance_obj.Instance.get_by_uuid(context, instance_id)
name = instance.display_name
if self._validate_instance_zone_for_dns_domain(context, instance):
self.instance_dns_manager.create_entry(name, address,
"A",
self.instance_dns_domain)
self.instance_dns_manager.create_entry(instance_id, address,
"A",
self.instance_dns_domain)
# 调用_setup_network_on_host, 需要具体的网络模式自行实现;
# 分配了固定IP后还需要在主机上进行相应的设置工作
self._setup_network_on_host(context, network)
return address
# nova-compute创建实例时, 会通过rpc调用此方法为实例分配网络资源
def allocate_for_instance(self, context, **kwargs):
instance_uuid = kwargs['instance_id']
if not uuidutils.is_uuid_like(instance_uuid):
instance_uuid = kwargs.get('instance_uuid')
host = kwargs['host']
project_id = kwargs['project_id']
rxtx_factor = kwargs['rxtx_factor']
requested_networks = kwargs.get('requested_networks')
vpn = kwargs['vpn']
macs = kwargs['macs']
admin_context = context.elevated()
LOG.debug(_("network allocations"), instance_uuid=instance_uuid,
context=context)
# 获取requested_networks中的网络信息或者当前租户的所有网络信息
networks = self._get_networks_for_instance(admin_context,
instance_uuid, project_id,
requested_networks=requested_networks)
# 从networks中抽取出一部分的必需网络信息
networks_list = [self._get_network_dict(network)
for network in networks]
LOG.debug(_('networks retrieved for instance: |%s|'),
networks_list, context=context, instance_uuid=instance_uuid)
try:
# 为实例的分配MAC地址并创建虚拟网卡的数据库记录
self._allocate_mac_addresses(context, instance_uuid, networks,
macs)
except Exception as e:
with excutils.save_and_reraise_exception():
# 遇到异常时进行回滚并抛出
vif_obj.VirtualInterface.delete_by_instance_uuid(context,
instance_uuid)
# 为实例分配固定IP
self._allocate_fixed_ips(admin_context, instance_uuid,
host, networks, vpn=vpn,
requested_networks=requested_networks)
# 如果需要更新DNS记录
if CONF.update_dns_entries:
network_ids = [network['id'] for network in networks]
# 通知全部的nova-network包括自己更新本地解析文件, 并优雅的重启DHCP服务
self.network_rpcapi.update_dns(context, network_ids)
# 返回实例的全部网络信息
return self.get_instance_nw_info(context, instance_uuid, rxtx_factor,
host)
class FlatDHCPManager(RPCAllocateFixedIP, floating_ips.FloatingIP,
NetworkManager):
# 该方法在准备工作中已经详细讲过了, 在分配固定IP后, 最主要的工作就是更新DHCP数据库记录
# 然后优雅重启DHCP服务
def _setup_network_on_host(self, context, network):
network['dhcp_server'] = self._get_dhcp_ip(context, network)
self.l3driver.initialize_network(network.get('cidr'))
self.l3driver.initialize_gateway(network)
if not CONF.fake_network:
dev = self.driver.get_dev(network)
elevated = context.elevated()
# !这里才是本次设置的重点所在:
# 更新DHCP数据库文件, 并优雅重启DHCP服务
self.driver.update_dhcp(elevated, dev, network)
if CONF.use_ipv6:
self.driver.update_ra(context, dev, network)
gateway = utils.get_my_linklocal(dev)
network.gateway_v6 = gateway
network.save()