From b35c004204206c7c92b0450b80a812bf21b2af71 Mon Sep 17 00:00:00 2001 From: armando-migliaccio Date: Thu, 8 Jan 2015 17:16:50 -0800 Subject: [PATCH] DHCP agent restructuring Wrap dhcp agent into its own module and break out configurations and entry point for better seperation. This lead to some test cleanup that revealed that options were registered unnecessarily. When/if the dhcp agent goes through a restructuring along the same lines of the L3 agent's, this would be the step to start from. Related-blueprint: restructure-l3-agent Related-blueprint: core-vendor-decomposition Change-Id: I87d9f1079ed4e71c731984ec00e2f785024fd5f8 --- doc/source/devref/rpc_api.rst | 4 +- neutron/agent/dhcp/__init__.py | 0 neutron/agent/dhcp/agent.py | 596 ++++++++++++++++++++ neutron/agent/dhcp/config.py | 64 +++ neutron/agent/dhcp_agent.py | 606 +-------------------- neutron/agent/linux/dhcp.py | 25 - neutron/api/rpc/handlers/dhcp_rpc.py | 2 +- neutron/cmd/netns_cleanup.py | 7 +- neutron/tests/unit/test_dhcp_agent.py | 23 +- neutron/tests/unit/test_linux_dhcp.py | 4 +- neutron/tests/unit/test_linux_interface.py | 2 - 11 files changed, 688 insertions(+), 645 deletions(-) create mode 100644 neutron/agent/dhcp/__init__.py create mode 100644 neutron/agent/dhcp/agent.py create mode 100644 neutron/agent/dhcp/config.py diff --git a/doc/source/devref/rpc_api.rst b/doc/source/devref/rpc_api.rst index 15a800746..866086af2 100644 --- a/doc/source/devref/rpc_api.rst +++ b/doc/source/devref/rpc_api.rst @@ -154,7 +154,7 @@ that indicates where the corresponding server or client code is located. Example: DHCP ------------- -The DHCP agent includes a client API, neutron.agent.dhcp_agent.DhcpPluginAPI. +The DHCP agent includes a client API, neutron.agent.dhcp.agent.DhcpPluginAPI. The DHCP agent uses this class to call remote methods back in the Neutron server. The server side is defined in neutron.api.rpc.handlers.dhcp_rpc.DhcpRpcCallback. It is up to the Neutron @@ -165,7 +165,7 @@ Similarly, there is an RPC interface defined that allows the Neutron plugin to remotely invoke methods in the DHCP agent. The client side is defined in neutron.api.rpc.agentnotifiers.dhcp_rpc_agent_api.DhcpAgentNotifyApi. The server side of this interface that runs in the DHCP agent is -neutron.agent.dhcp_agent.DhcpAgent. +neutron.agent.dhcp.agent.DhcpAgent. More Info ========= diff --git a/neutron/agent/dhcp/__init__.py b/neutron/agent/dhcp/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/neutron/agent/dhcp/agent.py b/neutron/agent/dhcp/agent.py new file mode 100644 index 000000000..594a78909 --- /dev/null +++ b/neutron/agent/dhcp/agent.py @@ -0,0 +1,596 @@ +# Copyright 2012 OpenStack Foundation +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import collections +import os + +import eventlet + +from oslo.config import cfg +from oslo import messaging +from oslo.utils import importutils + +from neutron.agent.common import config +from neutron.agent.linux import dhcp +from neutron.agent.linux import external_process +from neutron.agent import rpc as agent_rpc +from neutron.common import constants +from neutron.common import exceptions +from neutron.common import rpc as n_rpc +from neutron.common import topics +from neutron.common import utils +from neutron import context +from neutron.i18n import _LE, _LI, _LW +from neutron import manager +from neutron.openstack.common import log as logging +from neutron.openstack.common import loopingcall + +LOG = logging.getLogger(__name__) + + +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 = messaging.Target(version='1.0') + + def __init__(self, host=None): + super(DhcpAgent, self).__init__(host=host) + self.needs_resync_reasons = collections.defaultdict(list) + self.conf = cfg.CONF + self.cache = NetworkCache() + self.root_helper = config.get_root_helper(self.conf) + 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.use_namespaces) + # create dhcp dir to store dhcp info + dhcp_dir = os.path.dirname("/%s/dhcp/" % self.conf.state_path) + if not os.path.isdir(dhcp_dir): + os.makedirs(dhcp_dir, 0o755) + self.dhcp_version = self.dhcp_driver_cls.check_version() + self._populate_networks_cache() + + def _populate_networks_cache(self): + """Populate the networks cache when the DHCP-agent starts.""" + try: + existing_networks = self.dhcp_driver_cls.existing_dhcp_networks( + self.conf, + self.root_helper + ) + for net_id in existing_networks: + net = dhcp.NetModel(self.conf.use_namespaces, + {"id": net_id, + "subnets": [], + "ports": []}) + self.cache.put(net) + except NotImplementedError: + # just go ahead with an empty networks cache + LOG.debug("The '%s' DHCP-driver does not support retrieving of a " + "list of existing networks", + self.conf.dhcp_driver) + + def after_start(self): + self.run() + LOG.info(_LI("DHCP agent started")) + + def run(self): + """Activate the DHCP agent.""" + self.sync_state() + self.periodic_resync() + + def call_driver(self, action, network, **action_kwargs): + """Invoke an action on a DHCP driver instance.""" + LOG.debug('Calling driver for network: %(net)s action: %(action)s', + {'net': network.id, 'action': action}) + try: + # the Driver expects something that is duck typed similar to + # the base models. + driver = self.dhcp_driver_cls(self.conf, + network, + self.root_helper, + self.dhcp_version, + self.plugin_rpc) + + getattr(driver, action)(**action_kwargs) + return True + except exceptions.Conflict: + # No need to resync here, the agent will receive the event related + # to a status update for the network + LOG.warning(_LW('Unable to %(action)s dhcp for %(net_id)s: there ' + 'is a conflict with its current state; please ' + 'check that the network and/or its subnet(s) ' + 'still exist.'), + {'net_id': network.id, 'action': action}) + except Exception as e: + self.schedule_resync(e, network.id) + if (isinstance(e, messaging.RemoteError) + and e.exc_type == 'NetworkNotFound' + or isinstance(e, exceptions.NetworkNotFound)): + LOG.warning(_LW("Network %s has been deleted."), network.id) + else: + LOG.exception(_LE('Unable to %(action)s dhcp for %(net_id)s.'), + {'net_id': network.id, 'action': action}) + + def schedule_resync(self, reason, network=None): + """Schedule a resync for a given network and reason. If no network is + specified, resync all networks. + """ + self.needs_resync_reasons[network].append(reason) + + @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(cfg.CONF.num_sync_threads) + known_network_ids = set(self.cache.get_network_ids()) + + try: + active_networks = self.plugin_rpc.get_active_networks_info() + 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: + self.schedule_resync(e) + LOG.exception(_LE('Unable to sync network state.')) + + @utils.exception_logger() + def _periodic_resync_helper(self): + """Resync the dhcp state at the configured interval.""" + while True: + eventlet.sleep(self.conf.resync_interval) + if self.needs_resync_reasons: + # be careful to avoid a race with additions to list + # from other threads + reasons = self.needs_resync_reasons + self.needs_resync_reasons = collections.defaultdict(list) + for net, r in reasons.items(): + if not net: + net = "*" + LOG.debug("resync (%(network)s): %(reason)s", + {"reason": r, "network": net}) + self.sync_state(reasons.keys()) + + def periodic_resync(self): + """Spawn a thread to periodically resync the dhcp state.""" + eventlet.spawn(self._periodic_resync_helper) + + def safe_get_network_info(self, network_id): + try: + network = self.plugin_rpc.get_network_info(network_id) + if not network: + LOG.warn(_LW('Network %s has been deleted.'), network_id) + return network + except Exception as e: + self.schedule_resync(e, network_id) + LOG.exception(_LE('Network %s info call failed.'), network_id) + + 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) + + @utils.exception_logger() + def safe_configure_dhcp_for_network(self, network): + try: + self.configure_dhcp_for_network(network) + except (exceptions.NetworkNotFound, RuntimeError): + LOG.warn(_LW('Network %s may have been deleted and its resources ' + 'may have already been disposed.'), network.id) + + 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 + + def disable_dhcp_helper(self, network_id): + """Disable DHCP for a network known to the agent.""" + network = self.cache.get_network_by_id(network_id) + if network: + if (self.conf.use_namespaces and + self.conf.enable_isolated_metadata): + # NOTE(jschwarz): In the case where a network is deleted, all + # the subnets and ports are deleted before this function is + # called, so checking if 'should_enable_metadata' is True + # for any subnet is false logic here. + self.disable_isolated_metadata_proxy(network) + if self.call_driver('disable', network): + self.cache.remove(network) + + def refresh_dhcp_helper(self, network_id): + """Refresh or disable DHCP for a network depending on the current state + of the network. + """ + old_network = self.cache.get_network_by_id(network_id) + if not old_network: + # DHCP current not running for network. + return self.enable_dhcp_helper(network_id) + + network = self.safe_get_network_info(network_id) + if not network: + return + + old_cidrs = set(s.cidr for s in old_network.subnets if s.enable_dhcp) + new_cidrs = set(s.cidr for s in network.subnets if s.enable_dhcp) + + if new_cidrs and old_cidrs == new_cidrs: + self.call_driver('reload_allocations', network) + self.cache.put(network) + elif new_cidrs: + if self.call_driver('restart', network): + self.cache.put(network) + else: + self.disable_dhcp_helper(network.id) + + @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) + + @utils.synchronized('dhcp-agent') + def network_update_end(self, context, payload): + """Handle the network.update.end notification event.""" + network_id = payload['network']['id'] + if payload['network']['admin_state_up']: + self.enable_dhcp_helper(network_id) + else: + self.disable_dhcp_helper(network_id) + + @utils.synchronized('dhcp-agent') + def network_delete_end(self, context, payload): + """Handle the network.delete.end notification event.""" + self.disable_dhcp_helper(payload['network_id']) + + @utils.synchronized('dhcp-agent') + def subnet_update_end(self, context, payload): + """Handle the subnet.update.end notification event.""" + network_id = payload['subnet']['network_id'] + self.refresh_dhcp_helper(network_id) + + # Use the update handler for the subnet create event. + subnet_create_end = subnet_update_end + + @utils.synchronized('dhcp-agent') + def subnet_delete_end(self, context, payload): + """Handle the subnet.delete.end notification event.""" + subnet_id = payload['subnet_id'] + network = self.cache.get_network_by_subnet_id(subnet_id) + if network: + self.refresh_dhcp_helper(network.id) + + @utils.synchronized('dhcp-agent') + def port_update_end(self, context, payload): + """Handle the port.update.end notification event.""" + updated_port = dhcp.DictModel(payload['port']) + network = self.cache.get_network_by_id(updated_port.network_id) + if network: + self.cache.put_port(updated_port) + self.call_driver('reload_allocations', network) + + # Use the update handler for the port create event. + port_create_end = port_update_end + + @utils.synchronized('dhcp-agent') + def port_delete_end(self, context, payload): + """Handle the port.delete.end notification event.""" + port = self.cache.get_port_by_id(payload['port_id']) + if port: + network = self.cache.get_network_by_id(port.network_id) + self.cache.remove_port(port) + self.call_driver('reload_allocations', network) + + def enable_isolated_metadata_proxy(self, network): + + # The proxy might work for either a single network + # or all the networks connected via a router + # to the one passed as a parameter + neutron_lookup_param = '--network_id=%s' % network.id + # When the metadata network is enabled, the proxy might + # be started for the router attached to the network + if self.conf.enable_metadata_network: + router_ports = [port for port in network.ports + if (port.device_owner == + constants.DEVICE_OWNER_ROUTER_INTF)] + if router_ports: + # Multiple router ports should not be allowed + if len(router_ports) > 1: + LOG.warning(_LW("%(port_num)d router ports found on the " + "metadata access network. Only the port " + "%(port_id)s, for router %(router_id)s " + "will be considered"), + {'port_num': len(router_ports), + 'port_id': router_ports[0].id, + 'router_id': router_ports[0].device_id}) + neutron_lookup_param = ('--router_id=%s' % + router_ports[0].device_id) + + def callback(pid_file): + metadata_proxy_socket = cfg.CONF.metadata_proxy_socket + proxy_cmd = ['neutron-ns-metadata-proxy', + '--pid_file=%s' % pid_file, + '--metadata_proxy_socket=%s' % metadata_proxy_socket, + neutron_lookup_param, + '--state_path=%s' % self.conf.state_path, + '--metadata_port=%d' % dhcp.METADATA_PORT] + proxy_cmd.extend(config.get_log_args( + cfg.CONF, 'neutron-ns-metadata-proxy-%s.log' % network.id)) + return proxy_cmd + + pm = external_process.ProcessManager( + self.conf, + network.id, + self.root_helper, + network.namespace) + pm.enable(callback) + + def disable_isolated_metadata_proxy(self, network): + pm = external_process.ProcessManager( + self.conf, + network.id, + self.root_helper, + network.namespace) + pm.disable() + + +class DhcpPluginApi(object): + """Agent side of the dhcp rpc API. + + This class implements the client side of an rpc interface. The server side + of this interface can be found in + neutron.api.rpc.handlers.dhcp_rpc.DhcpRpcCallback. For more information + about changing rpc interfaces, see doc/source/devref/rpc_api.rst. + + API version history: + 1.0 - Initial version. + 1.1 - Added get_active_networks_info, create_dhcp_port, + and update_dhcp_port methods. + + """ + + def __init__(self, topic, context, use_namespaces): + self.context = context + self.host = cfg.CONF.host + self.use_namespaces = use_namespaces + target = messaging.Target( + topic=topic, + namespace=constants.RPC_NAMESPACE_DHCP_PLUGIN, + version='1.0') + self.client = n_rpc.get_client(target) + + 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(self.use_namespaces, n) for n in networks] + + def get_network_info(self, network_id): + """Make a remote process call to retrieve network info.""" + cctxt = self.client.prepare() + network = cctxt.call(self.context, 'get_network_info', + network_id=network_id, host=self.host) + if network: + return dhcp.NetModel(self.use_namespaces, network) + + def get_dhcp_port(self, network_id, device_id): + """Make a remote process call to get the dhcp port.""" + cctxt = self.client.prepare() + port = cctxt.call(self.context, 'get_dhcp_port', + network_id=network_id, device_id=device_id, + host=self.host) + if port: + return dhcp.DictModel(port) + + def create_dhcp_port(self, port): + """Make a remote process call to create the dhcp port.""" + cctxt = self.client.prepare(version='1.1') + port = cctxt.call(self.context, 'create_dhcp_port', + port=port, host=self.host) + if port: + return dhcp.DictModel(port) + + def update_dhcp_port(self, port_id, port): + """Make a remote process call to update the dhcp port.""" + cctxt = self.client.prepare(version='1.1') + port = cctxt.call(self.context, 'update_dhcp_port', + port_id=port_id, port=port, host=self.host) + if port: + return dhcp.DictModel(port) + + def release_dhcp_port(self, network_id, device_id): + """Make a remote process call to release the dhcp port.""" + cctxt = self.client.prepare() + return cctxt.call(self.context, 'release_dhcp_port', + network_id=network_id, device_id=device_id, + host=self.host) + + def release_port_fixed_ip(self, network_id, device_id, subnet_id): + """Make a remote process call to release a fixed_ip on the port.""" + cctxt = self.client.prepare() + return cctxt.call(self.context, 'release_port_fixed_ip', + network_id=network_id, subnet_id=subnet_id, + device_id=device_id, host=self.host) + + +class NetworkCache(object): + """Agent cache of the current network state.""" + def __init__(self): + self.cache = {} + self.subnet_lookup = {} + self.port_lookup = {} + + def get_network_ids(self): + return self.cache.keys() + + def get_network_by_id(self, network_id): + return self.cache.get(network_id) + + def get_network_by_subnet_id(self, subnet_id): + return self.cache.get(self.subnet_lookup.get(subnet_id)) + + def get_network_by_port_id(self, port_id): + return self.cache.get(self.port_lookup.get(port_id)) + + def put(self, network): + if network.id in self.cache: + self.remove(self.cache[network.id]) + + self.cache[network.id] = network + + for subnet in network.subnets: + self.subnet_lookup[subnet.id] = network.id + + for port in network.ports: + self.port_lookup[port.id] = network.id + + def remove(self, network): + del self.cache[network.id] + + for subnet in network.subnets: + del self.subnet_lookup[subnet.id] + + for port in network.ports: + del self.port_lookup[port.id] + + def put_port(self, port): + network = self.get_network_by_id(port.network_id) + for index in range(len(network.ports)): + if network.ports[index].id == port.id: + network.ports[index] = port + break + else: + network.ports.append(port) + + self.port_lookup[port.id] = network.id + + def remove_port(self, port): + network = self.get_network_by_port_id(port.id) + + for index in range(len(network.ports)): + if network.ports[index] == port: + del network.ports[index] + del self.port_lookup[port.id] + break + + def get_port_by_id(self, port_id): + network = self.get_network_by_port_id(port_id) + if network: + for port in network.ports: + if port.id == port_id: + return port + + def get_state(self): + net_ids = self.get_network_ids() + num_nets = len(net_ids) + num_subnets = 0 + num_ports = 0 + for net_id in net_ids: + network = self.get_network_by_id(net_id) + num_subnets += len(network.subnets) + num_ports += len(network.ports) + return {'networks': num_nets, + 'subnets': num_subnets, + 'ports': num_ports} + + +class DhcpAgentWithStateReport(DhcpAgent): + def __init__(self, host=None): + super(DhcpAgentWithStateReport, self).__init__(host=host) + self.state_rpc = agent_rpc.PluginReportStateAPI(topics.PLUGIN) + self.agent_state = { + 'binary': 'neutron-dhcp-agent', + 'host': host, + 'topic': topics.DHCP_AGENT, + 'configurations': { + 'dhcp_driver': cfg.CONF.dhcp_driver, + 'use_namespaces': cfg.CONF.use_namespaces, + 'dhcp_lease_duration': cfg.CONF.dhcp_lease_duration}, + 'start_flag': True, + 'agent_type': constants.AGENT_TYPE_DHCP} + report_interval = cfg.CONF.AGENT.report_interval + self.use_call = True + 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() + self.state_rpc.report_state(ctx, self.agent_state, self.use_call) + self.use_call = False + except AttributeError: + # This means the server does not support report_state + LOG.warn(_LW("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(_LE("Failed reporting state!")) + return + if self.agent_state.pop('start_flag', None): + 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(_LI("agent_updated by server side %s!"), payload) + + def after_start(self): + LOG.info(_LI("DHCP agent started")) diff --git a/neutron/agent/dhcp/config.py b/neutron/agent/dhcp/config.py new file mode 100644 index 000000000..e8a6c7b79 --- /dev/null +++ b/neutron/agent/dhcp/config.py @@ -0,0 +1,64 @@ +# Copyright 2015 OpenStack Foundation +# +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from oslo.config import cfg + +DHCP_AGENT_OPTS = [ + cfg.IntOpt('resync_interval', default=5, + help=_("Interval to resync.")), + cfg.StrOpt('dhcp_driver', + default='neutron.agent.linux.dhcp.Dnsmasq', + help=_("The driver used to manage the DHCP server.")), + cfg.BoolOpt('enable_isolated_metadata', default=False, + help=_("Support Metadata requests on isolated networks.")), + cfg.BoolOpt('enable_metadata_network', default=False, + help=_("Allows for serving metadata requests from a " + "dedicated network. Requires " + "enable_isolated_metadata = True")), + cfg.IntOpt('num_sync_threads', default=4, + help=_('Number of threads to use during sync process.')), + cfg.StrOpt('metadata_proxy_socket', + default='$state_path/metadata_proxy', + help=_('Location of Metadata Proxy UNIX domain ' + 'socket')), +] + +DHCP_OPTS = [ + cfg.StrOpt('dhcp_confs', + default='$state_path/dhcp', + help=_('Location to store DHCP server config files')), + cfg.StrOpt('dhcp_domain', + default='openstacklocal', + help=_('Domain to use for building the hostnames')), +] + +DNSMASQ_OPTS = [ + cfg.StrOpt('dnsmasq_config_file', + default='', + help=_('Override the default dnsmasq settings with this file')), + cfg.ListOpt('dnsmasq_dns_servers', + help=_('Comma-separated list of the DNS servers which will be ' + 'used as forwarders.'), + deprecated_name='dnsmasq_dns_server'), + cfg.BoolOpt('dhcp_delete_namespaces', default=False, + help=_("Delete namespace after removing a dhcp server.")), + cfg.IntOpt( + 'dnsmasq_lease_max', + default=(2 ** 24), + help=_('Limit number of leases to prevent a denial-of-service.')), + cfg.BoolOpt('dhcp_broadcast_reply', default=False, + help=_("Use broadcast in DHCP replies")), +] diff --git a/neutron/agent/dhcp_agent.py b/neutron/agent/dhcp_agent.py index 4e2063aea..4f5b98de0 100644 --- a/neutron/agent/dhcp_agent.py +++ b/neutron/agent/dhcp_agent.py @@ -1,4 +1,5 @@ -# Copyright 2012 OpenStack Foundation +# Copyright 2015 OpenStack Foundation +# # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -13,623 +14,30 @@ # License for the specific language governing permissions and limitations # under the License. -import collections -import os import sys import eventlet eventlet.monkey_patch() from oslo.config import cfg -from oslo import messaging -from oslo.utils import importutils from neutron.agent.common import config -from neutron.agent.linux import dhcp -from neutron.agent.linux import external_process +from neutron.agent.dhcp import config as dhcp_config from neutron.agent.linux import interface -from neutron.agent.linux import ovs_lib # noqa -from neutron.agent import rpc as agent_rpc from neutron.common import config as common_config -from neutron.common import constants -from neutron.common import exceptions -from neutron.common import rpc as n_rpc from neutron.common import topics -from neutron.common import utils -from neutron import context -from neutron.i18n import _LE, _LI, _LW -from neutron import manager -from neutron.openstack.common import log as logging -from neutron.openstack.common import loopingcall from neutron.openstack.common import service from neutron import service as neutron_service -LOG = logging.getLogger(__name__) - - -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 = messaging.Target(version='1.0') - - OPTS = [ - cfg.IntOpt('resync_interval', default=5, - help=_("Interval to resync.")), - cfg.StrOpt('dhcp_driver', - default='neutron.agent.linux.dhcp.Dnsmasq', - help=_("The driver used to manage the DHCP server.")), - cfg.BoolOpt('enable_isolated_metadata', default=False, - help=_("Support Metadata requests on isolated networks.")), - cfg.BoolOpt('enable_metadata_network', default=False, - help=_("Allows for serving metadata requests from a " - "dedicated network. Requires " - "enable_isolated_metadata = True")), - cfg.IntOpt('num_sync_threads', default=4, - help=_('Number of threads to use during sync process.')), - cfg.StrOpt('metadata_proxy_socket', - default='$state_path/metadata_proxy', - help=_('Location of Metadata Proxy UNIX domain ' - 'socket')), - ] - - def __init__(self, host=None): - super(DhcpAgent, self).__init__(host=host) - self.needs_resync_reasons = collections.defaultdict(list) - self.conf = cfg.CONF - self.cache = NetworkCache() - self.root_helper = config.get_root_helper(self.conf) - 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.use_namespaces) - # create dhcp dir to store dhcp info - dhcp_dir = os.path.dirname("/%s/dhcp/" % self.conf.state_path) - if not os.path.isdir(dhcp_dir): - os.makedirs(dhcp_dir, 0o755) - self.dhcp_version = self.dhcp_driver_cls.check_version() - self._populate_networks_cache() - - def _populate_networks_cache(self): - """Populate the networks cache when the DHCP-agent starts.""" - try: - existing_networks = self.dhcp_driver_cls.existing_dhcp_networks( - self.conf, - self.root_helper - ) - for net_id in existing_networks: - net = dhcp.NetModel(self.conf.use_namespaces, - {"id": net_id, - "subnets": [], - "ports": []}) - self.cache.put(net) - except NotImplementedError: - # just go ahead with an empty networks cache - LOG.debug("The '%s' DHCP-driver does not support retrieving of a " - "list of existing networks", - self.conf.dhcp_driver) - - def after_start(self): - self.run() - LOG.info(_LI("DHCP agent started")) - - def run(self): - """Activate the DHCP agent.""" - self.sync_state() - self.periodic_resync() - - def call_driver(self, action, network, **action_kwargs): - """Invoke an action on a DHCP driver instance.""" - LOG.debug('Calling driver for network: %(net)s action: %(action)s', - {'net': network.id, 'action': action}) - try: - # the Driver expects something that is duck typed similar to - # the base models. - driver = self.dhcp_driver_cls(self.conf, - network, - self.root_helper, - self.dhcp_version, - self.plugin_rpc) - - getattr(driver, action)(**action_kwargs) - return True - except exceptions.Conflict: - # No need to resync here, the agent will receive the event related - # to a status update for the network - LOG.warning(_LW('Unable to %(action)s dhcp for %(net_id)s: there ' - 'is a conflict with its current state; please ' - 'check that the network and/or its subnet(s) ' - 'still exist.'), - {'net_id': network.id, 'action': action}) - except Exception as e: - self.schedule_resync(e, network.id) - if (isinstance(e, messaging.RemoteError) - and e.exc_type == 'NetworkNotFound' - or isinstance(e, exceptions.NetworkNotFound)): - LOG.warning(_LW("Network %s has been deleted."), network.id) - else: - LOG.exception(_LE('Unable to %(action)s dhcp for %(net_id)s.'), - {'net_id': network.id, 'action': action}) - - def schedule_resync(self, reason, network=None): - """Schedule a resync for a given network and reason. If no network is - specified, resync all networks. - """ - self.needs_resync_reasons[network].append(reason) - - @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(cfg.CONF.num_sync_threads) - known_network_ids = set(self.cache.get_network_ids()) - - try: - active_networks = self.plugin_rpc.get_active_networks_info() - 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: - self.schedule_resync(e) - LOG.exception(_LE('Unable to sync network state.')) - - @utils.exception_logger() - def _periodic_resync_helper(self): - """Resync the dhcp state at the configured interval.""" - while True: - eventlet.sleep(self.conf.resync_interval) - if self.needs_resync_reasons: - # be careful to avoid a race with additions to list - # from other threads - reasons = self.needs_resync_reasons - self.needs_resync_reasons = collections.defaultdict(list) - for net, r in reasons.items(): - if not net: - net = "*" - LOG.debug("resync (%(network)s): %(reason)s", - {"reason": r, "network": net}) - self.sync_state(reasons.keys()) - - def periodic_resync(self): - """Spawn a thread to periodically resync the dhcp state.""" - eventlet.spawn(self._periodic_resync_helper) - - def safe_get_network_info(self, network_id): - try: - network = self.plugin_rpc.get_network_info(network_id) - if not network: - LOG.warn(_LW('Network %s has been deleted.'), network_id) - return network - except Exception as e: - self.schedule_resync(e, network_id) - LOG.exception(_LE('Network %s info call failed.'), network_id) - - 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) - - @utils.exception_logger() - def safe_configure_dhcp_for_network(self, network): - try: - self.configure_dhcp_for_network(network) - except (exceptions.NetworkNotFound, RuntimeError): - LOG.warn(_LW('Network %s may have been deleted and its resources ' - 'may have already been disposed.'), network.id) - - 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 - - def disable_dhcp_helper(self, network_id): - """Disable DHCP for a network known to the agent.""" - network = self.cache.get_network_by_id(network_id) - if network: - if (self.conf.use_namespaces and - self.conf.enable_isolated_metadata): - # NOTE(jschwarz): In the case where a network is deleted, all - # the subnets and ports are deleted before this function is - # called, so checking if 'should_enable_metadata' is True - # for any subnet is false logic here. - self.disable_isolated_metadata_proxy(network) - if self.call_driver('disable', network): - self.cache.remove(network) - - def refresh_dhcp_helper(self, network_id): - """Refresh or disable DHCP for a network depending on the current state - of the network. - """ - old_network = self.cache.get_network_by_id(network_id) - if not old_network: - # DHCP current not running for network. - return self.enable_dhcp_helper(network_id) - - network = self.safe_get_network_info(network_id) - if not network: - return - - old_cidrs = set(s.cidr for s in old_network.subnets if s.enable_dhcp) - new_cidrs = set(s.cidr for s in network.subnets if s.enable_dhcp) - - if new_cidrs and old_cidrs == new_cidrs: - self.call_driver('reload_allocations', network) - self.cache.put(network) - elif new_cidrs: - if self.call_driver('restart', network): - self.cache.put(network) - else: - self.disable_dhcp_helper(network.id) - - @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) - - @utils.synchronized('dhcp-agent') - def network_update_end(self, context, payload): - """Handle the network.update.end notification event.""" - network_id = payload['network']['id'] - if payload['network']['admin_state_up']: - self.enable_dhcp_helper(network_id) - else: - self.disable_dhcp_helper(network_id) - - @utils.synchronized('dhcp-agent') - def network_delete_end(self, context, payload): - """Handle the network.delete.end notification event.""" - self.disable_dhcp_helper(payload['network_id']) - - @utils.synchronized('dhcp-agent') - def subnet_update_end(self, context, payload): - """Handle the subnet.update.end notification event.""" - network_id = payload['subnet']['network_id'] - self.refresh_dhcp_helper(network_id) - - # Use the update handler for the subnet create event. - subnet_create_end = subnet_update_end - - @utils.synchronized('dhcp-agent') - def subnet_delete_end(self, context, payload): - """Handle the subnet.delete.end notification event.""" - subnet_id = payload['subnet_id'] - network = self.cache.get_network_by_subnet_id(subnet_id) - if network: - self.refresh_dhcp_helper(network.id) - - @utils.synchronized('dhcp-agent') - def port_update_end(self, context, payload): - """Handle the port.update.end notification event.""" - updated_port = dhcp.DictModel(payload['port']) - network = self.cache.get_network_by_id(updated_port.network_id) - if network: - self.cache.put_port(updated_port) - self.call_driver('reload_allocations', network) - - # Use the update handler for the port create event. - port_create_end = port_update_end - - @utils.synchronized('dhcp-agent') - def port_delete_end(self, context, payload): - """Handle the port.delete.end notification event.""" - port = self.cache.get_port_by_id(payload['port_id']) - if port: - network = self.cache.get_network_by_id(port.network_id) - self.cache.remove_port(port) - self.call_driver('reload_allocations', network) - - def enable_isolated_metadata_proxy(self, network): - - # The proxy might work for either a single network - # or all the networks connected via a router - # to the one passed as a parameter - neutron_lookup_param = '--network_id=%s' % network.id - # When the metadata network is enabled, the proxy might - # be started for the router attached to the network - if self.conf.enable_metadata_network: - router_ports = [port for port in network.ports - if (port.device_owner == - constants.DEVICE_OWNER_ROUTER_INTF)] - if router_ports: - # Multiple router ports should not be allowed - if len(router_ports) > 1: - LOG.warning(_LW("%(port_num)d router ports found on the " - "metadata access network. Only the port " - "%(port_id)s, for router %(router_id)s " - "will be considered"), - {'port_num': len(router_ports), - 'port_id': router_ports[0].id, - 'router_id': router_ports[0].device_id}) - neutron_lookup_param = ('--router_id=%s' % - router_ports[0].device_id) - - def callback(pid_file): - metadata_proxy_socket = cfg.CONF.metadata_proxy_socket - proxy_cmd = ['neutron-ns-metadata-proxy', - '--pid_file=%s' % pid_file, - '--metadata_proxy_socket=%s' % metadata_proxy_socket, - neutron_lookup_param, - '--state_path=%s' % self.conf.state_path, - '--metadata_port=%d' % dhcp.METADATA_PORT] - proxy_cmd.extend(config.get_log_args( - cfg.CONF, 'neutron-ns-metadata-proxy-%s.log' % network.id)) - return proxy_cmd - - pm = external_process.ProcessManager( - self.conf, - network.id, - self.root_helper, - network.namespace) - pm.enable(callback) - - def disable_isolated_metadata_proxy(self, network): - pm = external_process.ProcessManager( - self.conf, - network.id, - self.root_helper, - network.namespace) - pm.disable() - - -class DhcpPluginApi(object): - """Agent side of the dhcp rpc API. - - This class implements the client side of an rpc interface. The server side - of this interface can be found in - neutron.api.rpc.handlers.dhcp_rpc.DhcpRpcCallback. For more information - about changing rpc interfaces, see doc/source/devref/rpc_api.rst. - - API version history: - 1.0 - Initial version. - 1.1 - Added get_active_networks_info, create_dhcp_port, - and update_dhcp_port methods. - - """ - - def __init__(self, topic, context, use_namespaces): - self.context = context - self.host = cfg.CONF.host - self.use_namespaces = use_namespaces - target = messaging.Target( - topic=topic, - namespace=constants.RPC_NAMESPACE_DHCP_PLUGIN, - version='1.0') - self.client = n_rpc.get_client(target) - - 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(self.use_namespaces, n) for n in networks] - - def get_network_info(self, network_id): - """Make a remote process call to retrieve network info.""" - cctxt = self.client.prepare() - network = cctxt.call(self.context, 'get_network_info', - network_id=network_id, host=self.host) - if network: - return dhcp.NetModel(self.use_namespaces, network) - - def get_dhcp_port(self, network_id, device_id): - """Make a remote process call to get the dhcp port.""" - cctxt = self.client.prepare() - port = cctxt.call(self.context, 'get_dhcp_port', - network_id=network_id, device_id=device_id, - host=self.host) - if port: - return dhcp.DictModel(port) - - def create_dhcp_port(self, port): - """Make a remote process call to create the dhcp port.""" - cctxt = self.client.prepare(version='1.1') - port = cctxt.call(self.context, 'create_dhcp_port', - port=port, host=self.host) - if port: - return dhcp.DictModel(port) - - def update_dhcp_port(self, port_id, port): - """Make a remote process call to update the dhcp port.""" - cctxt = self.client.prepare(version='1.1') - port = cctxt.call(self.context, 'update_dhcp_port', - port_id=port_id, port=port, host=self.host) - if port: - return dhcp.DictModel(port) - - def release_dhcp_port(self, network_id, device_id): - """Make a remote process call to release the dhcp port.""" - cctxt = self.client.prepare() - return cctxt.call(self.context, 'release_dhcp_port', - network_id=network_id, device_id=device_id, - host=self.host) - - def release_port_fixed_ip(self, network_id, device_id, subnet_id): - """Make a remote process call to release a fixed_ip on the port.""" - cctxt = self.client.prepare() - return cctxt.call(self.context, 'release_port_fixed_ip', - network_id=network_id, subnet_id=subnet_id, - device_id=device_id, host=self.host) - - -class NetworkCache(object): - """Agent cache of the current network state.""" - def __init__(self): - self.cache = {} - self.subnet_lookup = {} - self.port_lookup = {} - - def get_network_ids(self): - return self.cache.keys() - - def get_network_by_id(self, network_id): - return self.cache.get(network_id) - - def get_network_by_subnet_id(self, subnet_id): - return self.cache.get(self.subnet_lookup.get(subnet_id)) - - def get_network_by_port_id(self, port_id): - return self.cache.get(self.port_lookup.get(port_id)) - - def put(self, network): - if network.id in self.cache: - self.remove(self.cache[network.id]) - - self.cache[network.id] = network - - for subnet in network.subnets: - self.subnet_lookup[subnet.id] = network.id - - for port in network.ports: - self.port_lookup[port.id] = network.id - - def remove(self, network): - del self.cache[network.id] - - for subnet in network.subnets: - del self.subnet_lookup[subnet.id] - - for port in network.ports: - del self.port_lookup[port.id] - - def put_port(self, port): - network = self.get_network_by_id(port.network_id) - for index in range(len(network.ports)): - if network.ports[index].id == port.id: - network.ports[index] = port - break - else: - network.ports.append(port) - - self.port_lookup[port.id] = network.id - - def remove_port(self, port): - network = self.get_network_by_port_id(port.id) - - for index in range(len(network.ports)): - if network.ports[index] == port: - del network.ports[index] - del self.port_lookup[port.id] - break - - def get_port_by_id(self, port_id): - network = self.get_network_by_port_id(port_id) - if network: - for port in network.ports: - if port.id == port_id: - return port - - def get_state(self): - net_ids = self.get_network_ids() - num_nets = len(net_ids) - num_subnets = 0 - num_ports = 0 - for net_id in net_ids: - network = self.get_network_by_id(net_id) - num_subnets += len(network.subnets) - num_ports += len(network.ports) - return {'networks': num_nets, - 'subnets': num_subnets, - 'ports': num_ports} - - -class DhcpAgentWithStateReport(DhcpAgent): - def __init__(self, host=None): - super(DhcpAgentWithStateReport, self).__init__(host=host) - self.state_rpc = agent_rpc.PluginReportStateAPI(topics.PLUGIN) - self.agent_state = { - 'binary': 'neutron-dhcp-agent', - 'host': host, - 'topic': topics.DHCP_AGENT, - 'configurations': { - 'dhcp_driver': cfg.CONF.dhcp_driver, - 'use_namespaces': cfg.CONF.use_namespaces, - 'dhcp_lease_duration': cfg.CONF.dhcp_lease_duration}, - 'start_flag': True, - 'agent_type': constants.AGENT_TYPE_DHCP} - report_interval = cfg.CONF.AGENT.report_interval - self.use_call = True - 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() - self.state_rpc.report_state(ctx, self.agent_state, self.use_call) - self.use_call = False - except AttributeError: - # This means the server does not support report_state - LOG.warn(_LW("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(_LE("Failed reporting state!")) - return - if self.agent_state.pop('start_flag', None): - 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(_LI("agent_updated by server side %s!"), payload) - - def after_start(self): - LOG.info(_LI("DHCP agent started")) - def register_options(): - cfg.CONF.register_opts(DhcpAgent.OPTS) config.register_interface_driver_opts_helper(cfg.CONF) config.register_use_namespaces_opts_helper(cfg.CONF) config.register_agent_state_opts_helper(cfg.CONF) config.register_root_helper(cfg.CONF) - cfg.CONF.register_opts(dhcp.OPTS) + cfg.CONF.register_opts(dhcp_config.DHCP_AGENT_OPTS) + cfg.CONF.register_opts(dhcp_config.DHCP_OPTS) + cfg.CONF.register_opts(dhcp_config.DNSMASQ_OPTS) cfg.CONF.register_opts(interface.OPTS) @@ -641,5 +49,5 @@ def main(): binary='neutron-dhcp-agent', topic=topics.DHCP_AGENT, report_interval=cfg.CONF.AGENT.report_interval, - manager='neutron.agent.dhcp_agent.DhcpAgentWithStateReport') + manager='neutron.agent.dhcp.agent.DhcpAgentWithStateReport') service.launch(server).wait() diff --git a/neutron/agent/linux/dhcp.py b/neutron/agent/linux/dhcp.py index 37e53e482..7889a7039 100644 --- a/neutron/agent/linux/dhcp.py +++ b/neutron/agent/linux/dhcp.py @@ -22,7 +22,6 @@ import socket import sys import netaddr -from oslo.config import cfg from oslo.serialization import jsonutils from oslo.utils import importutils import six @@ -38,30 +37,6 @@ from neutron.openstack.common import uuidutils LOG = logging.getLogger(__name__) -OPTS = [ - cfg.StrOpt('dhcp_confs', - default='$state_path/dhcp', - help=_('Location to store DHCP server config files')), - cfg.StrOpt('dhcp_domain', - default='openstacklocal', - help=_('Domain to use for building the hostnames')), - cfg.StrOpt('dnsmasq_config_file', - default='', - help=_('Override the default dnsmasq settings with this file')), - cfg.ListOpt('dnsmasq_dns_servers', - help=_('Comma-separated list of the DNS servers which will be ' - 'used as forwarders.'), - deprecated_name='dnsmasq_dns_server'), - cfg.BoolOpt('dhcp_delete_namespaces', default=False, - help=_("Delete namespace after removing a dhcp server.")), - cfg.IntOpt( - 'dnsmasq_lease_max', - default=(2 ** 24), - help=_('Limit number of leases to prevent a denial-of-service.')), - cfg.BoolOpt('dhcp_broadcast_reply', default=False, - help=_("Use broadcast in DHCP replies")), -] - IPV4 = 4 IPV6 = 6 UDP = 'udp' diff --git a/neutron/api/rpc/handlers/dhcp_rpc.py b/neutron/api/rpc/handlers/dhcp_rpc.py index 59561fd8a..07dc3c811 100644 --- a/neutron/api/rpc/handlers/dhcp_rpc.py +++ b/neutron/api/rpc/handlers/dhcp_rpc.py @@ -39,7 +39,7 @@ class DhcpRpcCallback(object): This class implements the server side of an rpc interface. The client side of this interface can be found in - neutron.agent.dhcp_agent.DhcpPluginApi. For more information about + neutron.agent.dhcp.agent.DhcpPluginApi. For more information about changing rpc interfaces, see doc/source/devref/rpc_api.rst. """ diff --git a/neutron/cmd/netns_cleanup.py b/neutron/cmd/netns_cleanup.py index 5f3d6dca6..89c85bda9 100644 --- a/neutron/cmd/netns_cleanup.py +++ b/neutron/cmd/netns_cleanup.py @@ -22,7 +22,7 @@ from oslo.config import cfg from oslo.utils import importutils from neutron.agent.common import config as agent_config -from neutron.agent import dhcp_agent +from neutron.agent.dhcp import config as dhcp_config from neutron.agent.l3 import agent as l3_agent from neutron.agent.linux import dhcp from neutron.agent.linux import interface @@ -65,8 +65,9 @@ def setup_conf(): agent_config.register_interface_driver_opts_helper(conf) agent_config.register_use_namespaces_opts_helper(conf) agent_config.register_root_helper(conf) - conf.register_opts(dhcp.OPTS) - conf.register_opts(dhcp_agent.DhcpAgent.OPTS) + conf.register_opts(dhcp_config.DHCP_AGENT_OPTS) + conf.register_opts(dhcp_config.DHCP_OPTS) + conf.register_opts(dhcp_config.DNSMASQ_OPTS) conf.register_opts(interface.OPTS) return conf diff --git a/neutron/tests/unit/test_dhcp_agent.py b/neutron/tests/unit/test_dhcp_agent.py index b18f3e679..415e9cbb6 100644 --- a/neutron/tests/unit/test_dhcp_agent.py +++ b/neutron/tests/unit/test_dhcp_agent.py @@ -25,7 +25,9 @@ from oslo import messaging import testtools from neutron.agent.common import config -from neutron.agent import dhcp_agent +from neutron.agent.dhcp import agent as dhcp_agent +from neutron.agent.dhcp import config as dhcp_config +from neutron.agent import dhcp_agent as entry from neutron.agent.linux import dhcp from neutron.agent.linux import interface from neutron.common import config as common_config @@ -183,14 +185,14 @@ fake_down_network = dhcp.NetModel( class TestDhcpAgent(base.BaseTestCase): def setUp(self): super(TestDhcpAgent, self).setUp() - dhcp_agent.register_options() + entry.register_options() cfg.CONF.set_override('interface_driver', 'neutron.agent.linux.interface.NullDriver') # disable setting up periodic state reporting cfg.CONF.set_override('report_interval', 0, 'AGENT') self.driver_cls_p = mock.patch( - 'neutron.agent.dhcp_agent.importutils.import_class') + 'neutron.agent.dhcp.agent.importutils.import_class') self.driver = mock.Mock(name='driver') self.driver.existing_dhcp_networks.return_value = [] self.driver_cls = self.driver_cls_p.start() @@ -213,11 +215,10 @@ class TestDhcpAgent(base.BaseTestCase): sys_argv.return_value = [ 'dhcp', '--config-file', base.etcdir('neutron.conf.test')] - cfg.CONF.register_opts(dhcp_agent.DhcpAgent.OPTS) + cfg.CONF.register_opts(dhcp_config.DHCP_AGENT_OPTS) config.register_interface_driver_opts_helper(cfg.CONF) config.register_agent_state_opts_helper(cfg.CONF) config.register_root_helper(cfg.CONF) - cfg.CONF.register_opts(dhcp.OPTS) cfg.CONF.register_opts(interface.OPTS) common_config.init(sys.argv[1:]) agent_mgr = dhcp_agent.DhcpAgentWithStateReport( @@ -239,7 +240,7 @@ class TestDhcpAgent(base.BaseTestCase): with mock.patch(launcher_str) as launcher: sys_argv.return_value = ['dhcp', '--config-file', base.etcdir('neutron.conf.test')] - dhcp_agent.main() + entry.main() launcher.assert_has_calls( [mock.call(), mock.call().launch_service(mock.ANY), mock.call().wait()]) @@ -514,24 +515,23 @@ class TestDhcpAgentEventHandler(base.BaseTestCase): def setUp(self): super(TestDhcpAgentEventHandler, self).setUp() config.register_interface_driver_opts_helper(cfg.CONF) - cfg.CONF.register_opts(dhcp.OPTS) cfg.CONF.set_override('interface_driver', 'neutron.agent.linux.interface.NullDriver') config.register_root_helper(cfg.CONF) - cfg.CONF.register_opts(dhcp_agent.DhcpAgent.OPTS) + cfg.CONF.register_opts(dhcp_config.DHCP_AGENT_OPTS) self.plugin_p = mock.patch(DHCP_PLUGIN) plugin_cls = self.plugin_p.start() self.plugin = mock.Mock() plugin_cls.return_value = self.plugin - self.cache_p = mock.patch('neutron.agent.dhcp_agent.NetworkCache') + self.cache_p = mock.patch('neutron.agent.dhcp.agent.NetworkCache') cache_cls = self.cache_p.start() self.cache = mock.Mock() cache_cls.return_value = self.cache self.mock_makedirs_p = mock.patch("os.makedirs") self.mock_makedirs = self.mock_makedirs_p.start() - self.mock_init_p = mock.patch('neutron.agent.dhcp_agent.' + self.mock_init_p = mock.patch('neutron.agent.dhcp.agent.' 'DhcpAgent._populate_networks_cache') self.mock_init = self.mock_init_p.start() with mock.patch.object(dhcp.Dnsmasq, @@ -1161,8 +1161,7 @@ class TestDeviceManager(base.BaseTestCase): super(TestDeviceManager, self).setUp() config.register_interface_driver_opts_helper(cfg.CONF) config.register_use_namespaces_opts_helper(cfg.CONF) - cfg.CONF.register_opts(dhcp_agent.DhcpAgent.OPTS) - cfg.CONF.register_opts(dhcp.OPTS) + cfg.CONF.register_opts(dhcp_config.DHCP_AGENT_OPTS) cfg.CONF.set_override('interface_driver', 'neutron.agent.linux.interface.NullDriver') config.register_root_helper(cfg.CONF) diff --git a/neutron/tests/unit/test_linux_dhcp.py b/neutron/tests/unit/test_linux_dhcp.py index faabbd3a9..e1caa56c1 100644 --- a/neutron/tests/unit/test_linux_dhcp.py +++ b/neutron/tests/unit/test_linux_dhcp.py @@ -22,6 +22,7 @@ from oslo.config import cfg import testtools from neutron.agent.common import config +from neutron.agent.dhcp import config as dhcp_config from neutron.agent.linux import dhcp from neutron.common import config as base_config from neutron.common import constants @@ -494,7 +495,8 @@ class TestBase(base.BaseTestCase): super(TestBase, self).setUp() self.conf = config.setup_conf() self.conf.register_opts(base_config.core_opts) - self.conf.register_opts(dhcp.OPTS) + self.conf.register_opts(dhcp_config.DHCP_OPTS) + self.conf.register_opts(dhcp_config.DNSMASQ_OPTS) config.register_interface_driver_opts_helper(self.conf) config.register_use_namespaces_opts_helper(self.conf) instance = mock.patch("neutron.agent.linux.dhcp.DeviceManager") diff --git a/neutron/tests/unit/test_linux_interface.py b/neutron/tests/unit/test_linux_interface.py index df7b1970f..78b098e88 100644 --- a/neutron/tests/unit/test_linux_interface.py +++ b/neutron/tests/unit/test_linux_interface.py @@ -16,7 +16,6 @@ import mock from neutron.agent.common import config -from neutron.agent.linux import dhcp from neutron.agent.linux import interface from neutron.agent.linux import ip_lib from neutron.agent.linux import ovs_lib @@ -418,7 +417,6 @@ class TestMetaInterfaceDriver(TestBase): def setUp(self): super(TestMetaInterfaceDriver, self).setUp() config.register_interface_driver_opts_helper(self.conf) - self.conf.register_opts(dhcp.OPTS) self.client_cls_p = mock.patch('neutronclient.v2_0.client.Client') client_cls = self.client_cls_p.start() self.client_inst = mock.Mock() -- 2.45.2