openstack neuturon模塊 dhcp_agent向服務器彙報狀態更新數據庫與更新本地配置文件過程

一、更新服務器數據庫

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部分。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章