一、更新服務器數據庫
DhcpAgentWithStateReport類繼承DhcpAgent類,DhcpAgentWithStateReport類的作用主要是創建一個協程定時向neutron-server啓動時開啓的rpc-server上報neutron-dhcp-agent的服務或network狀態,然後通過neutron-server的core plugin將狀態更新到數據庫中。
其中self.cache = NetworkCache()主要保存底層的active的dhcp networks信息,這些信息會通過DhcpAgentWithStateReport類的_report_state方法上報到數據庫中。
#DhcpAgentWithStateReport繼承自DhcpAgent,用於彙報DHCPAgent的狀態
class DhcpAgentWithStateReport(DhcpAgent):
def __init__(self, host=None, conf=None):
super(DhcpAgentWithStateReport, self).__init__(host=host, conf=conf)
self.state_rpc = agent_rpc.PluginReportStateAPI(topics.REPORTS)
self.agent_state = {
'binary': 'neutron-dhcp-agent',
'host': host,
'availability_zone': self.conf.AGENT.availability_zone,
'topic': topics.DHCP_AGENT,
'configurations': {
'dhcp_driver': self.conf.dhcp_driver,
'dhcp_lease_duration': self.conf.dhcp_lease_duration,
'log_agent_heartbeats': self.conf.AGENT.log_agent_heartbeats},
'start_flag': True,
'agent_type': constants.AGENT_TYPE_DHCP}
report_interval = self.conf.AGENT.report_interval
if report_interval:
self.heartbeat = loopingcall.FixedIntervalLoopingCall(
self._report_state)
self.heartbeat.start(interval=report_interval)
def _report_state(self):
try:
self.agent_state.get('configurations').update(
self.cache.get_state())
ctx = context.get_admin_context_without_session()
agent_status = self.state_rpc.report_state(#彙報狀態
ctx, self.agent_state, True)
if agent_status == n_const.AGENT_REVIVED:
LOG.info("Agent has just been revived. "
"Scheduling full sync")
self.schedule_resync("Agent has just been revived")
except AttributeError:
# This means the server does not support report_state
LOG.warning("Neutron server does not support state report. "
"State report for this agent will be disabled.")
self.heartbeat.stop()
self.run()
return
except Exception:
LOG.exception("Failed reporting state!")
return
if self.agent_state.pop('start_flag', None):#在neutron-dhcp-agent開始運行時對網絡進行dhcp分配
self.run()
def agent_updated(self, context, payload):
"""Handle the agent_updated notification event."""
self.schedule_resync(_("Agent updated: %(payload)s") %
{"payload": payload})
LOG.info("agent_updated by server side %s!", payload)
def after_start(self):
LOG.info("DHCP agent started")
DhcpAgent類纔是爲neutron-dhcp-agent服務做主要工作的。初始化代碼如下:
#/neutron/agent/dhcp/agent.py: DhcpAgentWithStateReport
class DhcpAgent(manager.Manager):
"""DHCP agent service manager.
Note that the public methods of this class are exposed as the server side
of an rpc interface. The neutron server uses
neutron.api.rpc.agentnotifiers.dhcp_rpc_agent_api.DhcpAgentNotifyApi as the
client side to execute the methods here. For more information about
changing rpc interfaces, see doc/source/devref/rpc_api.rst.
"""
target = oslo_messaging.Target(version='1.0')
def __init__(self, host=None, conf=None):
super(DhcpAgent, self).__init__(host=host)
self.needs_resync_reasons = collections.defaultdict(list)
self.conf = conf or cfg.CONF
self.cache = NetworkCache()
self.dhcp_driver_cls = importutils.import_class(self.conf.dhcp_driver)
ctx = context.get_admin_context_without_session()
self.plugin_rpc = DhcpPluginApi(topics.PLUGIN, ctx, self.conf.host)
# create dhcp dir to store dhcp info
dhcp_dir = os.path.dirname("/%s/dhcp/" % self.conf.state_path)
utils.ensure_dir(dhcp_dir)
self.dhcp_version = self.dhcp_driver_cls.check_version()
self._populate_networks_cache()
# keep track of mappings between networks and routers for
# metadata processing
self._metadata_routers = {} # {network_id: router_id}
self._process_monitor = external_process.ProcessMonitor(
config=self.conf,
resource_type='dhcp')
其中self.cache = NetworkCache()主要保存底層的active的dhcp networks信息,這些信息會通過DhcpAgentWithStateReport類的_report_state方法上報到數據庫中。
二、更新配置文件
初始
接着首先看下rpc-service的start代碼,代碼路徑在neutron/service.py
#/neutron/service.py:Service
def start(self):
self.manager.init_host()
super(Service, self).start()
if self.report_interval:
pulse = loopingcall.FixedIntervalLoopingCall(self.report_state)
pulse.start(interval=self.report_interval,
initial_delay=self.report_interval)
self.timers.append(pulse)
if self.periodic_interval:
if self.periodic_fuzzy_delay:
initial_delay = random.randint(0, self.periodic_fuzzy_delay)
else:
initial_delay = None
periodic = loopingcall.FixedIntervalLoopingCall(
self.periodic_tasks)
periodic.start(interval=self.periodic_interval,
initial_delay=initial_delay)
self.timers.append(periodic)
self.manager.after_start()
rpc-service start的時候會調用agent的init_host函數。下面分析self.manager.init_host()
#/neutron/agent/dhcp/agent.py:DhcpAgent
def init_host(self):
self.sync_state()
#/neutron/agent/dhcp/agent.py:DhcpAgent
@utils.synchronized('dhcp-agent')
def sync_state(self, networks=None):
"""Sync the local DHCP state with Neutron. If no networks are passed,
or 'None' is one of the networks, sync all of the networks.
"""
only_nets = set([] if (not networks or None in networks) else networks)
LOG.info(_LI('Synchronizing state'))
pool = eventlet.GreenPool(self.conf.num_sync_threads)
known_network_ids = set(self.cache.get_network_ids())
try:
active_networks = self.plugin_rpc.get_active_networks_info()
LOG.info(_LI('All active networks have been fetched through RPC.'))
active_network_ids = set(network.id for network in active_networks)
for deleted_id in known_network_ids - active_network_ids:
try:
self.disable_dhcp_helper(deleted_id)
except Exception as e:
self.schedule_resync(e, deleted_id)
LOG.exception(_LE('Unable to sync network state on '
'deleted network %s'), deleted_id)
for network in active_networks:
if (not only_nets or # specifically resync all
network.id not in known_network_ids or # missing net
network.id in only_nets): # specific network to sync
pool.spawn(self.safe_configure_dhcp_for_network, network)
pool.waitall()
LOG.info(_LI('Synchronizing state complete'))
except Exception as e:
if only_nets:
for network_id in only_nets:
self.schedule_resync(e, network_id)
else:
self.schedule_resync(e)
LOG.exception(_LE('Unable to sync network state.'))
def get_active_networks_info(self):
"""Make a remote process call to retrieve all network info."""
cctxt = self.client.prepare(version='1.1')
networks = cctxt.call(self.context, 'get_active_networks_info',
host=self.host)
return [dhcp.NetModel(n) for n in networks]
sync_state函數主要功能是根據數據庫同步底層的networks信息,即將self.cache(底層保存的dhcpnetworks信息)與通過active_networks=self.plugin_rpc.get_active_networks_info()函數獲取的數據庫中的networks信息作比較,將未在數據庫中的底層networks從self.cache中進行移除。其中self.cache中的networks信息在創建DhcpAgent類的__init__函數的self._populate_networks_cache()代碼進行實現.
在將未在數據庫中的底層的dhcpnetworks從self.cache中進行移除後,將更新active的dhcp networks信息。
更新
DHCP agent會調用dhcp_driver.reload_allocations來更新配置文件。reload_allocations會根據新網絡配置重新生成配置文件。DHCP agent會在收到如下4種消息時調用reload_allocations
port_update_end
port_delete_end
subnet_update_end
subnet_delete_end
上面這些通知消息在neturon rest API的前端實現中發出,具體代碼在neutron.api.v2.base.py中
#neutron/api/v2/base.py
notifier_method = self._resource + '.create.end'
notifier_method = self._resource + '.delete.end'
notifier_method = self._resource + '.update.end'
可以看到針對每種resource(network, subnet, port),都會有相應的消息發出。dhcp agent根據這些消息來決定何時重新加載dnsmasp配置文件。
在dhcp.py中可見\neutron\agent\linux
def spawn_process(self):
"""Spawn the process, if it's not spawned already."""
# we only need to generate the lease file the first time dnsmasq starts
# rather than on every reload since dnsmasq will keep the file current
self._output_init_lease_file()
self._spawn_or_reload_process(reload_with_HUP=False)
def _spawn_or_reload_process(self, reload_with_HUP):
"""Spawns or reloads a Dnsmasq process for the network.
When reload_with_HUP is True, dnsmasq receives a HUP signal,
or it's reloaded if the process is not running.
"""
self._output_config_files()
pm = self._get_process_manager(
cmd_callback=self._build_cmdline_callback)
pm.enable(reload_cfg=reload_with_HUP)
self.process_monitor.register(uuid=self.network.id,
service_name=DNSMASQ_SERVICE_NAME,
monitored_process=pm)
def _output_config_files(self):
self._output_hosts_file()
self._output_addn_hosts_file()
self._output_opts_file()
DHCP agent對外的提供的API接口在neutron/api/agentnotifiers/dhcp_rpc_agent_api.py裏面,當網絡變化時,比如網絡創建和刪除,會通過API調用DHCP driver。
#neutron/api/agentnotifiers/dhcp_rpc_agent_api.py
class DhcpAgentNotifyAPI(object):
"""API for plugin to notify DHCP agent.
VALID_RESOURCES = ['network', 'subnet', 'port']
VALID_METHOD_NAMES = ['network.create.end',
'network.update.end',
'network.delete.end',
'subnet.create.end',
'subnet.update.end',
'subnet.delete.end',
'port.create.end',
'port.update.end',
'port.delete.end']
可以看network/subnet/port的創建、刪除和更新都會通知到DHCP agent, DHCPagent裏面有對應的回調函數。
#/neutron/agent/dhcp/agent.py:DhcpAgent
@utils.synchronized('dhcp-agent')
def network_create_end(self, context, payload):
"""Handle the network.create.end notification event."""
network_id = payload['network']['id']
self.enable_dhcp_helper(network_id)
#/neutron/agent/dhcp/agent.py:DhcpAgent
@utils.synchronized('dhcp-agent')
def network_update_end(self, context, payload):
"""Handle the network.update.end notification event."""
#/neutron/agent/dhcp/agent.py:DhcpAgent
@utils.synchronized('dhcp-agent')
def network_delete_end(self, context, payload):
"""Handle the network.delete.end notification event."""
#/neutron/agent/dhcp/agent.py:DhcpAgent
@utils.synchronized('dhcp-agent')
def subnet_update_end(self, context, payload):
"""Handle the subnet.update.end notification event."""
#/neutron/agent/dhcp/agent.py:DhcpAgent
@utils.synchronized('dhcp-agent')
def subnet_delete_end(self, context, payload):
"""Handle the subnet.delete.end notification event."""
#/neutron/agent/dhcp/agent.py:DhcpAgent
@utils.synchronized('dhcp-agent')
def port_update_end(self, context, payload):
"""Handle the port.update.end notification event."""
#/neutron/agent/dhcp/agent.py:DhcpAgent
@utils.synchronized('dhcp-agent')
def port_delete_end(self, context, payload):
"""Handle the port.delete.end notification event."""
我們以network創建爲例,分析HDCP agent如何調用DHCP driver 啓動DHCP server服務的
#/neutron/agent/dhcp/agent.py:DhcpAgent
@utils.synchronized('dhcp-agent')
def network_create_end(self, context, payload):
"""Handle the network.create.end notification event."""
network_id = payload['network']['id']
self.enable_dhcp_helper(network_id)
#/neutron/agent/dhcp/agent.py:DhcpAgent
def enable_dhcp_helper(self, network_id):
"""Enable DHCP for a network that meets enabling criteria."""
network = self.safe_get_network_info(network_id)
if network:
self.configure_dhcp_for_network(network)
#/neutron/agent/dhcp/agent.py:DhcpAgent
def configure_dhcp_for_network(self, network):
if not network.admin_state_up:
return
enable_metadata = self.dhcp_driver_cls.should_enable_metadata(
self.conf, network)
dhcp_network_enabled = False
for subnet in network.subnets:
if subnet.enable_dhcp:
if self.call_driver('enable', network):
dhcp_network_enabled = True
self.cache.put(network)
break
if enable_metadata and dhcp_network_enabled:
for subnet in network.subnets:
if subnet.ip_version == 4 and subnet.enable_dhcp:
self.enable_isolated_metadata_proxy(network)
break
elif (not self.conf.force_metadata and
not self.conf.enable_isolated_metadata):
# In the case that the dhcp agent ran with metadata enabled,
# and dhcp agent now starts with metadata disabled, check and
# delete any metadata_proxy.
self.disable_isolated_metadata_proxy(network)
DhcpAgent通過self.dhcp_driver_cls = importutils.import_class(self.conf.dhcp_driver)註冊了Dnsmasq這個Driver,並在需要的時候調用driver的相應接口。
當創建網絡的時候,發現子網裏面需要開啓DHCP server,則調用self.call_driver(‘enable’, network)。會創建一個DHCP服務,這個通過命令可以看到。
當刪除網絡時候,發現已經開啓了DHCP server,那麼就要刪除掉DHCP server,則調用接口self.call_driver(‘disable’, network)。
當子網和port添加和刪除的時候,與網絡有所不同,是調用的self.call_driver(‘reload_allocations’, network),進行更新配置操作數據。這塊代碼最終會調用DHCP driver部分。