<s> import collections <EOL> import os <EOL> import eventlet <EOL> from neutron_lib import constants <EOL> from neutron_lib import exceptions <EOL> from oslo_config import cfg <EOL> from oslo_log import log as logging <EOL> import oslo_messaging <EOL> from oslo_service import loopingcall <EOL> from oslo_utils import importutils <EOL> from neutron . _i18n import _ , _LE , _LI , _LW <EOL> from neutron . agent . linux import dhcp <EOL> from neutron . agent . linux import external_process <EOL> from neutron . agent . metadata import driver as metadata_driver <EOL> from neutron . agent import rpc as agent_rpc <EOL> from neutron . common import constants as n_const <EOL> from neutron . common import rpc as n_rpc <EOL> from neutron . common import topics <EOL> from neutron . common import utils <EOL> from neutron import context <EOL> from neutron import manager <EOL> LOG = logging . getLogger ( __name__ ) <EOL> class DhcpAgent ( manager . Manager ) : <EOL> """<STR_LIT>""" <EOL> target = oslo_messaging . Target ( version = '<STR_LIT:1.0>' ) <EOL> def __init__ ( self , host = None , conf = None ) : <EOL> super ( DhcpAgent , self ) . __init__ ( host = host ) <EOL> self . needs_resync_reasons = collections . defaultdict ( list ) <EOL> self . conf = conf or cfg . CONF <EOL> self . cache = NetworkCache ( ) <EOL> self . dhcp_driver_cls = importutils . import_class ( self . conf . dhcp_driver ) <EOL> ctx = context . get_admin_context_without_session ( ) <EOL> self . plugin_rpc = DhcpPluginApi ( topics . PLUGIN , ctx , self . conf . host ) <EOL> dhcp_dir = os . path . dirname ( "<STR_LIT>" % self . conf . state_path ) <EOL> utils . ensure_dir ( dhcp_dir ) <EOL> self . dhcp_version = self . dhcp_driver_cls . check_version ( ) <EOL> self . _populate_networks_cache ( ) <EOL> self . _metadata_routers = { } <EOL> self . _process_monitor = external_process . ProcessMonitor ( <EOL> config = self . conf , <EOL> resource_type = '<STR_LIT>' ) <EOL> def init_host ( self ) : <EOL> self . sync_state ( ) <EOL> def _populate_networks_cache ( self ) : <EOL> """<STR_LIT>""" <EOL> try : <EOL> existing_networks = self . dhcp_driver_cls . existing_dhcp_networks ( <EOL> self . conf <EOL> ) <EOL> for net_id in existing_networks : <EOL> net = dhcp . NetModel ( { "<STR_LIT:id>" : net_id , "<STR_LIT>" : [ ] , "<STR_LIT>" : [ ] } ) <EOL> self . cache . put ( net ) <EOL> except NotImplementedError : <EOL> LOG . debug ( "<STR_LIT>" <EOL> "<STR_LIT>" , <EOL> self . conf . dhcp_driver ) <EOL> def after_start ( self ) : <EOL> self . run ( ) <EOL> LOG . info ( _LI ( "<STR_LIT>" ) ) <EOL> def run ( self ) : <EOL> """<STR_LIT>""" <EOL> self . sync_state ( ) <EOL> self . periodic_resync ( ) <EOL> def call_driver ( self , action , network , ** action_kwargs ) : <EOL> """<STR_LIT>""" <EOL> LOG . debug ( '<STR_LIT>' , <EOL> { '<STR_LIT>' : network . id , '<STR_LIT:action>' : action } ) <EOL> try : <EOL> driver = self . dhcp_driver_cls ( self . conf , <EOL> network , <EOL> self . _process_monitor , <EOL> self . dhcp_version , <EOL> self . plugin_rpc ) <EOL> getattr ( driver , action ) ( ** action_kwargs ) <EOL> return True <EOL> except exceptions . Conflict : <EOL> LOG . warning ( _LW ( '<STR_LIT>' <EOL> '<STR_LIT>' <EOL> '<STR_LIT>' <EOL> '<STR_LIT>' ) , <EOL> { '<STR_LIT>' : network . id , '<STR_LIT:action>' : action } ) <EOL> except Exception as e : <EOL> if getattr ( e , '<STR_LIT>' , '<STR_LIT>' ) != '<STR_LIT>' : <EOL> self . schedule_resync ( e , network . id ) <EOL> if ( isinstance ( e , oslo_messaging . RemoteError ) <EOL> and e . exc_type == '<STR_LIT>' <EOL> or isinstance ( e , exceptions . NetworkNotFound ) ) : <EOL> LOG . debug ( "<STR_LIT>" , network . id ) <EOL> else : <EOL> LOG . exception ( _LE ( '<STR_LIT>' ) , <EOL> { '<STR_LIT>' : network . id , '<STR_LIT:action>' : action } ) <EOL> def schedule_resync ( self , reason , network_id = None ) : <EOL> """<STR_LIT>""" <EOL> self . needs_resync_reasons [ network_id ] . append ( reason ) <EOL> @ utils . synchronized ( '<STR_LIT>' ) <EOL> def sync_state ( self , networks = None ) : <EOL> """<STR_LIT>""" <EOL> only_nets = set ( [ ] if ( not networks or None in networks ) else networks ) <EOL> LOG . info ( _LI ( '<STR_LIT>' ) ) <EOL> pool = eventlet . GreenPool ( self . conf . num_sync_threads ) <EOL> known_network_ids = set ( self . cache . get_network_ids ( ) ) <EOL> try : <EOL> active_networks = self . plugin_rpc . get_active_networks_info ( ) <EOL> LOG . info ( _LI ( '<STR_LIT>' ) ) <EOL> active_network_ids = set ( network . id for network in active_networks ) <EOL> for deleted_id in known_network_ids - active_network_ids : <EOL> try : <EOL> self . disable_dhcp_helper ( deleted_id ) <EOL> except Exception as e : <EOL> self . schedule_resync ( e , deleted_id ) <EOL> LOG . exception ( _LE ( '<STR_LIT>' <EOL> '<STR_LIT>' ) , deleted_id ) <EOL> for network in active_networks : <EOL> if ( not only_nets or <EOL> network . id not in known_network_ids or <EOL> network . id in only_nets ) : <EOL> pool . spawn ( self . safe_configure_dhcp_for_network , network ) <EOL> pool . waitall ( ) <EOL> LOG . info ( _LI ( '<STR_LIT>' ) ) <EOL> except Exception as e : <EOL> if only_nets : <EOL> for network_id in only_nets : <EOL> self . schedule_resync ( e , network_id ) <EOL> else : <EOL> self . schedule_resync ( e ) <EOL> LOG . exception ( _LE ( '<STR_LIT>' ) ) <EOL> @ utils . exception_logger ( ) <EOL> def _periodic_resync_helper ( self ) : <EOL> """<STR_LIT>""" <EOL> while True : <EOL> eventlet . sleep ( self . conf . resync_interval ) <EOL> if self . needs_resync_reasons : <EOL> reasons = self . needs_resync_reasons <EOL> self . needs_resync_reasons = collections . defaultdict ( list ) <EOL> for net , r in reasons . items ( ) : <EOL> if not net : <EOL> net = "<STR_LIT:*>" <EOL> LOG . debug ( "<STR_LIT>" , <EOL> { "<STR_LIT>" : r , "<STR_LIT>" : net } ) <EOL> self . sync_state ( reasons . keys ( ) ) <EOL> def periodic_resync ( self ) : <EOL> """<STR_LIT>""" <EOL> eventlet . spawn ( self . _periodic_resync_helper ) <EOL> def safe_get_network_info ( self , network_id ) : <EOL> try : <EOL> network = self . plugin_rpc . get_network_info ( network_id ) <EOL> if not network : <EOL> LOG . debug ( '<STR_LIT>' , network_id ) <EOL> return network <EOL> except Exception as e : <EOL> self . schedule_resync ( e , network_id ) <EOL> LOG . exception ( _LE ( '<STR_LIT>' ) , network_id ) <EOL> def enable_dhcp_helper ( self , network_id ) : <EOL> """<STR_LIT>""" <EOL> network = self . safe_get_network_info ( network_id ) <EOL> if network : <EOL> self . configure_dhcp_for_network ( network ) <EOL> @ utils . exception_logger ( ) <EOL> def safe_configure_dhcp_for_network ( self , network ) : <EOL> try : <EOL> network_id = network . get ( '<STR_LIT:id>' ) <EOL> LOG . info ( _LI ( '<STR_LIT>' ) , network_id ) <EOL> self . configure_dhcp_for_network ( network ) <EOL> LOG . info ( _LI ( '<STR_LIT>' ) , network_id ) <EOL> except ( exceptions . NetworkNotFound , RuntimeError ) : <EOL> LOG . warning ( _LW ( '<STR_LIT>' <EOL> '<STR_LIT>' ) , <EOL> network . id ) <EOL> def configure_dhcp_for_network ( self , network ) : <EOL> if not network . admin_state_up : <EOL> return <EOL> enable_metadata = self . dhcp_driver_cls . should_enable_metadata ( <EOL> self . conf , network ) <EOL> dhcp_network_enabled = False <EOL> for subnet in network . subnets : <EOL> if subnet . enable_dhcp : <EOL> if self . call_driver ( '<STR_LIT>' , network ) : <EOL> dhcp_network_enabled = True <EOL> self . cache . put ( network ) <EOL> break <EOL> if enable_metadata and dhcp_network_enabled : <EOL> for subnet in network . subnets : <EOL> if subnet . ip_version == <NUM_LIT:4> and subnet . enable_dhcp : <EOL> self . enable_isolated_metadata_proxy ( network ) <EOL> break <EOL> elif ( not self . conf . force_metadata and <EOL> not self . conf . enable_isolated_metadata ) : <EOL> self . disable_isolated_metadata_proxy ( network ) <EOL> def disable_dhcp_helper ( self , network_id ) : <EOL> """<STR_LIT>""" <EOL> network = self . cache . get_network_by_id ( network_id ) <EOL> if network : <EOL> if self . conf . enable_isolated_metadata : <EOL> self . disable_isolated_metadata_proxy ( network ) <EOL> if self . call_driver ( '<STR_LIT>' , network ) : <EOL> self . cache . remove ( network ) <EOL> def refresh_dhcp_helper ( self , network_id ) : <EOL> """<STR_LIT>""" <EOL> old_network = self . cache . get_network_by_id ( network_id ) <EOL> if not old_network : <EOL> return self . enable_dhcp_helper ( network_id ) <EOL> network = self . safe_get_network_info ( network_id ) <EOL> if not network : <EOL> return <EOL> old_cidrs = set ( s . cidr for s in old_network . subnets if s . enable_dhcp ) <EOL> new_cidrs = set ( s . cidr for s in network . subnets if s . enable_dhcp ) <EOL> if new_cidrs and old_cidrs == new_cidrs : <EOL> self . call_driver ( '<STR_LIT>' , network ) <EOL> self . cache . put ( network ) <EOL> elif new_cidrs : <EOL> if self . call_driver ( '<STR_LIT>' , network ) : <EOL> self . cache . put ( network ) <EOL> else : <EOL> self . disable_dhcp_helper ( network . id ) <EOL> @ utils . synchronized ( '<STR_LIT>' ) <EOL> def network_create_end ( self , context , payload ) : <EOL> """<STR_LIT>""" <EOL> network_id = payload [ '<STR_LIT>' ] [ '<STR_LIT:id>' ] <EOL> self . enable_dhcp_helper ( network_id ) <EOL> @ utils . synchronized ( '<STR_LIT>' ) <EOL> def network_update_end ( self , context , payload ) : <EOL> """<STR_LIT>""" <EOL> network_id = payload [ '<STR_LIT>' ] [ '<STR_LIT:id>' ] <EOL> if payload [ '<STR_LIT>' ] [ '<STR_LIT>' ] : <EOL> self . enable_dhcp_helper ( network_id ) <EOL> else : <EOL> self . disable_dhcp_helper ( network_id ) <EOL> @ utils . synchronized ( '<STR_LIT>' ) <EOL> def network_delete_end ( self , context , payload ) : <EOL> """<STR_LIT>""" <EOL> self . disable_dhcp_helper ( payload [ '<STR_LIT>' ] ) <EOL> @ utils . synchronized ( '<STR_LIT>' ) <EOL> def subnet_update_end ( self , context , payload ) : <EOL> """<STR_LIT>""" <EOL> network_id = payload [ '<STR_LIT>' ] [ '<STR_LIT>' ] <EOL> self . refresh_dhcp_helper ( network_id ) <EOL> subnet_create_end = subnet_update_end <EOL> @ utils . synchronized ( '<STR_LIT>' ) <EOL> def subnet_delete_end ( self , context , payload ) : <EOL> """<STR_LIT>""" <EOL> subnet_id = payload [ '<STR_LIT>' ] <EOL> network = self . cache . get_network_by_subnet_id ( subnet_id ) <EOL> if network : <EOL> self . refresh_dhcp_helper ( network . id ) <EOL> @ utils . synchronized ( '<STR_LIT>' ) <EOL> def port_update_end ( self , context , payload ) : <EOL> """<STR_LIT>""" <EOL> updated_port = dhcp . DictModel ( payload [ '<STR_LIT:port>' ] ) <EOL> network = self . cache . get_network_by_id ( updated_port . network_id ) <EOL> if network : <EOL> LOG . info ( _LI ( "<STR_LIT>" ) , <EOL> updated_port ) <EOL> driver_action = '<STR_LIT>' <EOL> if self . _is_port_on_this_agent ( updated_port ) : <EOL> orig = self . cache . get_port_by_id ( updated_port [ '<STR_LIT:id>' ] ) <EOL> old_ips = { i [ '<STR_LIT>' ] for i in orig [ '<STR_LIT>' ] or [ ] } <EOL> new_ips = { i [ '<STR_LIT>' ] for i in updated_port [ '<STR_LIT>' ] } <EOL> if old_ips != new_ips : <EOL> driver_action = '<STR_LIT>' <EOL> self . cache . put_port ( updated_port ) <EOL> self . call_driver ( driver_action , network ) <EOL> def _is_port_on_this_agent ( self , port ) : <EOL> thishost = utils . get_dhcp_agent_device_id ( <EOL> port [ '<STR_LIT>' ] , self . conf . host ) <EOL> return port [ '<STR_LIT>' ] == thishost <EOL> port_create_end = port_update_end <EOL> @ utils . synchronized ( '<STR_LIT>' ) <EOL> def port_delete_end ( self , context , payload ) : <EOL> """<STR_LIT>""" <EOL> port = self . cache . get_port_by_id ( payload [ '<STR_LIT>' ] ) <EOL> if port : <EOL> network = self . cache . get_network_by_id ( port . network_id ) <EOL> self . cache . remove_port ( port ) <EOL> self . call_driver ( '<STR_LIT>' , network ) <EOL> def enable_isolated_metadata_proxy ( self , network ) : <EOL> kwargs = { '<STR_LIT>' : network . id } <EOL> if self . conf . enable_metadata_network : <EOL> router_ports = [ port for port in network . ports <EOL> if ( port . device_owner in <EOL> constants . ROUTER_INTERFACE_OWNERS ) ] <EOL> if router_ports : <EOL> if len ( router_ports ) > <NUM_LIT:1> : <EOL> LOG . warning ( _LW ( "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" ) , <EOL> { '<STR_LIT>' : len ( router_ports ) , <EOL> '<STR_LIT>' : router_ports [ <NUM_LIT:0> ] . id , <EOL> '<STR_LIT>' : router_ports [ <NUM_LIT:0> ] . device_id } ) <EOL> kwargs = { '<STR_LIT>' : router_ports [ <NUM_LIT:0> ] . device_id } <EOL> self . _metadata_routers [ network . id ] = router_ports [ <NUM_LIT:0> ] . device_id <EOL> metadata_driver . MetadataDriver . spawn_monitored_metadata_proxy ( <EOL> self . _process_monitor , network . namespace , dhcp . METADATA_PORT , <EOL> self . conf , ** kwargs ) <EOL> def disable_isolated_metadata_proxy ( self , network ) : <EOL> if ( self . conf . enable_metadata_network and <EOL> network . id in self . _metadata_routers ) : <EOL> uuid = self . _metadata_routers [ network . id ] <EOL> is_router_id = True <EOL> else : <EOL> uuid = network . id <EOL> is_router_id = False <EOL> metadata_driver . MetadataDriver . destroy_monitored_metadata_proxy ( <EOL> self . _process_monitor , uuid , self . conf ) <EOL> if is_router_id : <EOL> del self . _metadata_routers [ network . id ] <EOL> class DhcpPluginApi ( object ) : <EOL> """<STR_LIT>""" <EOL> def __init__ ( self , topic , context , host ) : <EOL> self . context = context <EOL> self . host = host <EOL> target = oslo_messaging . Target ( <EOL> topic = topic , <EOL> namespace = n_const . RPC_NAMESPACE_DHCP_PLUGIN , <EOL> version = '<STR_LIT:1.0>' ) <EOL> self . client = n_rpc . get_client ( target ) <EOL> def get_active_networks_info ( self ) : <EOL> """<STR_LIT>""" <EOL> cctxt = self . client . prepare ( version = '<STR_LIT>' ) <EOL> networks = cctxt . call ( self . context , '<STR_LIT>' , <EOL> host = self . host ) <EOL> return [ dhcp . NetModel ( n ) for n in networks ] <EOL> def get_network_info ( self , network_id ) : <EOL> """<STR_LIT>""" <EOL> cctxt = self . client . prepare ( ) <EOL> network = cctxt . call ( self . context , '<STR_LIT>' , <EOL> network_id = network_id , host = self . host ) <EOL> if network : <EOL> return dhcp . NetModel ( network ) <EOL> def create_dhcp_port ( self , port ) : <EOL> """<STR_LIT>""" <EOL> cctxt = self . client . prepare ( version = '<STR_LIT>' ) <EOL> port = cctxt . call ( self . context , '<STR_LIT>' , <EOL> port = port , host = self . host ) <EOL> if port : <EOL> return dhcp . DictModel ( port ) <EOL> def update_dhcp_port ( self , port_id , port ) : <EOL> """<STR_LIT>""" <EOL> cctxt = self . client . prepare ( version = '<STR_LIT>' ) <EOL> port = cctxt . call ( self . context , '<STR_LIT>' , <EOL> port_id = port_id , port = port , host = self . host ) <EOL> if port : <EOL> return dhcp . DictModel ( port ) <EOL> def release_dhcp_port ( self , network_id , device_id ) : <EOL> """<STR_LIT>""" <EOL> cctxt = self . client . prepare ( ) <EOL> return cctxt . call ( self . context , '<STR_LIT>' , <EOL> network_id = network_id , device_id = device_id , <EOL> host = self . host ) <EOL> class NetworkCache ( object ) : <EOL> """<STR_LIT>""" <EOL> def __init__ ( self ) : <EOL> self . cache = { } <EOL> self . subnet_lookup = { } <EOL> self . port_lookup = { } <EOL> def get_network_ids ( self ) : <EOL> return self . cache . keys ( ) <EOL> def get_network_by_id ( self , network_id ) : <EOL> return self . cache . get ( network_id ) <EOL> def get_network_by_subnet_id ( self , subnet_id ) : <EOL> return self . cache . get ( self . subnet_lookup . get ( subnet_id ) ) <EOL> def get_network_by_port_id ( self , port_id ) : <EOL> return self . cache . get ( self . port_lookup . get ( port_id ) ) <EOL> def put ( self , network ) : <EOL> if network . id in self . cache : <EOL> self . remove ( self . cache [ network . id ] ) <EOL> self . cache [ network . id ] = network <EOL> for subnet in network . subnets : <EOL> self . subnet_lookup [ subnet . id ] = network . id <EOL> for port in network . ports : <EOL> self . port_lookup [ port . id ] = network . id <EOL> def remove ( self , network ) : <EOL> del self . cache [ network . id ] <EOL> for subnet in network . subnets : <EOL> del self . subnet_lookup [ subnet . id ] <EOL> for port in network . ports : <EOL> del self . port_lookup [ port . id ] <EOL> def put_port ( self , port ) : <EOL> network = self . get_network_by_id ( port . network_id ) <EOL> for index in range ( len ( network . ports ) ) : <EOL> if network . ports [ index ] . id == port . id : <EOL> network . ports [ index ] = port <EOL> break <EOL> else : <EOL> network . ports . append ( port ) <EOL> self . port_lookup [ port . id ] = network . id <EOL> def remove_port ( self , port ) : <EOL> network = self . get_network_by_port_id ( port . id ) <EOL> for index in range ( len ( network . ports ) ) : <EOL> if network . ports [ index ] == port : <EOL> del network . ports [ index ] <EOL> del self . port_lookup [ port . id ] <EOL> break <EOL> def get_port_by_id ( self , port_id ) : <EOL> network = self . get_network_by_port_id ( port_id ) <EOL> if network : <EOL> for port in network . ports : <EOL> if port . id == port_id : <EOL> return port <EOL> def get_state ( self ) : <EOL> net_ids = self . get_network_ids ( ) <EOL> num_nets = len ( net_ids ) <EOL> num_subnets = <NUM_LIT:0> <EOL> num_ports = <NUM_LIT:0> <EOL> for net_id in net_ids : <EOL> network = self . get_network_by_id ( net_id ) <EOL> num_subnets += len ( network . subnets ) <EOL> num_ports += len ( network . ports ) <EOL> return { '<STR_LIT>' : num_nets , <EOL> '<STR_LIT>' : num_subnets , <EOL> '<STR_LIT>' : num_ports } <EOL> class DhcpAgentWithStateReport ( DhcpAgent ) : <EOL> def __init__ ( self , host = None , conf = None ) : <EOL> super ( DhcpAgentWithStateReport , self ) . __init__ ( host = host , conf = conf ) <EOL> self . state_rpc = agent_rpc . PluginReportStateAPI ( topics . REPORTS ) <EOL> self . agent_state = { <EOL> '<STR_LIT>' : '<STR_LIT>' , <EOL> '<STR_LIT:host>' : host , <EOL> '<STR_LIT>' : self . conf . AGENT . availability_zone , <EOL> '<STR_LIT>' : topics . DHCP_AGENT , <EOL> '<STR_LIT>' : { <EOL> '<STR_LIT>' : self . conf . dhcp_driver , <EOL> '<STR_LIT>' : self . conf . dhcp_lease_duration , <EOL> '<STR_LIT>' : self . conf . AGENT . log_agent_heartbeats } , <EOL> '<STR_LIT>' : True , <EOL> '<STR_LIT>' : constants . AGENT_TYPE_DHCP } <EOL> report_interval = self . conf . AGENT . report_interval <EOL> if report_interval : <EOL> self . heartbeat = loopingcall . FixedIntervalLoopingCall ( <EOL> self . _report_state ) <EOL> self . heartbeat . start ( interval = report_interval ) <EOL> def _report_state ( self ) : <EOL> try : <EOL> self . agent_state . get ( '<STR_LIT>' ) . update ( <EOL> self . cache . get_state ( ) ) <EOL> ctx = context . get_admin_context_without_session ( ) <EOL> agent_status = self . state_rpc . report_state ( <EOL> ctx , self . agent_state , True ) <EOL> if agent_status == n_const . AGENT_REVIVED : <EOL> LOG . info ( _LI ( "<STR_LIT>" <EOL> "<STR_LIT>" ) ) <EOL> self . schedule_resync ( "<STR_LIT>" ) <EOL> except AttributeError : <EOL> LOG . warning ( _LW ( "<STR_LIT>" <EOL> "<STR_LIT>" ) ) <EOL> self . heartbeat . stop ( ) <EOL> self . run ( ) <EOL> return <EOL> except Exception : <EOL> LOG . exception ( _LE ( "<STR_LIT>" ) ) <EOL> return <EOL> if self . agent_state . pop ( '<STR_LIT>' , None ) : <EOL> self . run ( ) <EOL> def agent_updated ( self , context , payload ) : <EOL> """<STR_LIT>""" <EOL> self . schedule_resync ( _ ( "<STR_LIT>" ) % <EOL> { "<STR_LIT>" : payload } ) <EOL> LOG . info ( _LI ( "<STR_LIT>" ) , payload ) <EOL> def after_start ( self ) : <EOL> LOG . info ( _LI ( "<STR_LIT>" ) ) </s>
