diff --git a/neutron/agent/dhcp_agent.py b/neutron/agent/dhcp_agent.py index e9b972b70..251b835c4 100644 --- a/neutron/agent/dhcp_agent.py +++ b/neutron/agent/dhcp_agent.py @@ -16,8 +16,6 @@ # under the License. import os -import socket -import uuid import eventlet import netaddr @@ -27,10 +25,8 @@ from neutron.agent.common import config from neutron.agent.linux import dhcp from neutron.agent.linux import external_process from neutron.agent.linux import interface -from neutron.agent.linux import ip_lib from neutron.agent import rpc as agent_rpc from neutron.common import constants -from neutron.common import exceptions from neutron.common import legacy from neutron.common import topics from neutron.common import utils @@ -44,10 +40,6 @@ from neutron.openstack.common import service from neutron import service as neutron_service LOG = logging.getLogger(__name__) -NS_PREFIX = 'qdhcp-' -METADATA_DEFAULT_PREFIX = 16 -METADATA_DEFAULT_IP = '169.254.169.254/%d' % METADATA_DEFAULT_PREFIX -METADATA_PORT = 80 class DhcpAgent(manager.Manager): @@ -81,8 +73,8 @@ class DhcpAgent(manager.Manager): 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.device_manager = DeviceManager(self.conf, self.plugin_rpc) + 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): @@ -92,15 +84,16 @@ class DhcpAgent(manager.Manager): 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 = DictModel({"id": net_id, "subnets": [], "ports": []}) + 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 @@ -119,10 +112,6 @@ class DhcpAgent(manager.Manager): self.sync_state() self.periodic_resync() - def _ns_name(self, network): - if self.conf.use_namespaces: - return NS_PREFIX + network.id - def call_driver(self, action, network, **action_kwargs): """Invoke an action on a DHCP driver instance.""" try: @@ -131,9 +120,9 @@ class DhcpAgent(manager.Manager): driver = self.dhcp_driver_cls(self.conf, network, self.root_helper, - self.device_manager, - self._ns_name(network), - self.dhcp_version) + self.dhcp_version, + self.plugin_rpc) + getattr(driver, action)(**action_kwargs) return True @@ -238,9 +227,6 @@ class DhcpAgent(manager.Manager): else: self.disable_dhcp_helper(network.id) - if new_cidrs: - self.device_manager.update(network) - def release_lease_for_removed_ips(self, port, network): """Releases the dhcp lease for ips removed from a port.""" prev_port = self.cache.get_port_by_id(port.id) @@ -297,7 +283,7 @@ class DhcpAgent(manager.Manager): @utils.synchronized('dhcp-agent') def port_update_end(self, context, payload): """Handle the port.update.end notification event.""" - port = DictModel(payload['port']) + port = dhcp.DictModel(payload['port']) network = self.cache.get_network_by_id(port.network_id) if network: self.release_lease_for_removed_ips(port, network) @@ -328,7 +314,7 @@ class DhcpAgent(manager.Manager): # or all the networks connected via a router # to the one passed as a parameter neutron_lookup_param = '--network_id=%s' % network.id - meta_cidr = netaddr.IPNetwork(METADATA_DEFAULT_IP) + meta_cidr = netaddr.IPNetwork(dhcp.METADATA_DEFAULT_CIDR) has_metadata_subnet = any(netaddr.IPNetwork(s.cidr) in meta_cidr for s in network.subnets) if (self.conf.enable_metadata_network and has_metadata_subnet): @@ -355,7 +341,7 @@ class DhcpAgent(manager.Manager): '--metadata_proxy_socket=%s' % metadata_proxy_socket, neutron_lookup_param, '--state_path=%s' % self.conf.state_path, - '--metadata_port=%d' % METADATA_PORT] + '--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 @@ -364,7 +350,7 @@ class DhcpAgent(manager.Manager): self.conf, network.id, self.root_helper, - self._ns_name(network)) + network.namespace) pm.enable(callback) def disable_isolated_metadata_proxy(self, network): @@ -372,7 +358,7 @@ class DhcpAgent(manager.Manager): self.conf, network.id, self.root_helper, - self._ns_name(network)) + network.namespace) pm.disable() @@ -388,11 +374,12 @@ class DhcpPluginApi(proxy.RpcProxy): BASE_RPC_API_VERSION = '1.1' - def __init__(self, topic, context): + def __init__(self, topic, context, use_namespaces): super(DhcpPluginApi, self).__init__( topic=topic, default_version=self.BASE_RPC_API_VERSION) self.context = context self.host = cfg.CONF.host + self.use_namespaces = use_namespaces def get_active_networks_info(self): """Make a remote process call to retrieve all network info.""" @@ -400,41 +387,42 @@ class DhcpPluginApi(proxy.RpcProxy): self.make_msg('get_active_networks_info', host=self.host), topic=self.topic) - return [DictModel(n) for n in networks] + 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.""" - return DictModel(self.call(self.context, - self.make_msg('get_network_info', - network_id=network_id, - host=self.host), - topic=self.topic)) + return dhcp.NetModel(self.use_namespaces, + self.call(self.context, + self.make_msg('get_network_info', + network_id=network_id, + host=self.host), + topic=self.topic)) def get_dhcp_port(self, network_id, device_id): """Make a remote process call to get the dhcp port.""" - return DictModel(self.call(self.context, - self.make_msg('get_dhcp_port', - network_id=network_id, - device_id=device_id, - host=self.host), - topic=self.topic)) + return dhcp.DictModel(self.call(self.context, + self.make_msg('get_dhcp_port', + network_id=network_id, + device_id=device_id, + host=self.host), + topic=self.topic)) def create_dhcp_port(self, port): """Make a remote process call to create the dhcp port.""" - return DictModel(self.call(self.context, - self.make_msg('create_dhcp_port', - port=port, - host=self.host), - topic=self.topic)) + return dhcp.DictModel(self.call(self.context, + self.make_msg('create_dhcp_port', + port=port, + host=self.host), + topic=self.topic)) def update_dhcp_port(self, port_id, port): """Make a remote process call to update the dhcp port.""" - return DictModel(self.call(self.context, - self.make_msg('update_dhcp_port', - port_id=port_id, - port=port, - host=self.host), - topic=self.topic)) + return dhcp.DictModel(self.call(self.context, + self.make_msg('update_dhcp_port', + port_id=port_id, + port=port, + host=self.host), + topic=self.topic)) def release_dhcp_port(self, network_id, device_id): """Make a remote process call to release the dhcp port.""" @@ -537,226 +525,6 @@ class NetworkCache(object): 'ports': num_ports} -class DeviceManager(object): - OPTS = [ - cfg.StrOpt('interface_driver', - help=_("The driver used to manage the virtual interface.")) - ] - - def __init__(self, conf, plugin): - self.conf = conf - self.root_helper = config.get_root_helper(conf) - self.plugin = plugin - if not conf.interface_driver: - raise SystemExit(_('You must specify an interface driver')) - try: - self.driver = importutils.import_object( - conf.interface_driver, conf - ) - except Exception: - msg = _("Error importing interface driver " - "'%s'") % conf.interface_driver - raise SystemExit(msg) - - def get_interface_name(self, network, port): - """Return interface(device) name for use by the DHCP process.""" - return self.driver.get_device_name(port) - - def get_device_id(self, network): - """Return a unique DHCP device ID for this host on the network.""" - # There could be more than one dhcp server per network, so create - # a device id that combines host and network ids - - host_uuid = uuid.uuid5(uuid.NAMESPACE_DNS, socket.gethostname()) - return 'dhcp%s-%s' % (host_uuid, network.id) - - def _get_device(self, network): - """Return DHCP ip_lib device for this host on the network.""" - device_id = self.get_device_id(network) - port = self.plugin.get_dhcp_port(network.id, device_id) - interface_name = self.get_interface_name(network, port) - namespace = NS_PREFIX + network.id - return ip_lib.IPDevice(interface_name, - self.root_helper, - namespace) - - def _set_default_route(self, network): - """Sets the default gateway for this dhcp namespace. - - This method is idempotent and will only adjust the route if adjusting - it would change it from what it already is. This makes it safe to call - and avoids unnecessary perturbation of the system. - """ - device = self._get_device(network) - gateway = device.route.get_gateway() - if gateway: - gateway = gateway['gateway'] - - for subnet in network.subnets: - skip_subnet = ( - subnet.ip_version != 4 - or not subnet.enable_dhcp - or subnet.gateway_ip is None) - - if skip_subnet: - continue - - if gateway != subnet.gateway_ip: - m = _('Setting gateway for dhcp netns on net %(n)s to %(ip)s') - LOG.debug(m, {'n': network.id, 'ip': subnet.gateway_ip}) - - device.route.add_gateway(subnet.gateway_ip) - - return - - # No subnets on the network have a valid gateway. Clean it up to avoid - # confusion from seeing an invalid gateway here. - if gateway is not None: - msg = _('Removing gateway for dhcp netns on net %s') - LOG.debug(msg, network.id) - - device.route.delete_gateway(gateway) - - def setup_dhcp_port(self, network): - """Create/update DHCP port for the host if needed and return port.""" - - device_id = self.get_device_id(network) - subnets = {} - dhcp_enabled_subnet_ids = [] - for subnet in network.subnets: - if subnet.enable_dhcp: - dhcp_enabled_subnet_ids.append(subnet.id) - subnets[subnet.id] = subnet - - dhcp_port = None - for port in network.ports: - port_device_id = getattr(port, 'device_id', None) - if port_device_id == device_id: - port_fixed_ips = [] - for fixed_ip in port.fixed_ips: - port_fixed_ips.append({'subnet_id': fixed_ip.subnet_id, - 'ip_address': fixed_ip.ip_address}) - if fixed_ip.subnet_id in dhcp_enabled_subnet_ids: - dhcp_enabled_subnet_ids.remove(fixed_ip.subnet_id) - - # If there are dhcp_enabled_subnet_ids here that means that - # we need to add those to the port and call update. - if dhcp_enabled_subnet_ids: - port_fixed_ips.extend( - [dict(subnet_id=s) for s in dhcp_enabled_subnet_ids]) - dhcp_port = self.plugin.update_dhcp_port( - port.id, {'port': {'fixed_ips': port_fixed_ips}}) - else: - dhcp_port = port - # break since we found port that matches device_id - break - - # DHCP port has not yet been created. - if dhcp_port is None: - LOG.debug(_('DHCP port %(device_id)s on network %(network_id)s' - ' does not yet exist.'), {'device_id': device_id, - 'network_id': network.id}) - port_dict = dict( - name='', - admin_state_up=True, - device_id=device_id, - network_id=network.id, - tenant_id=network.tenant_id, - fixed_ips=[dict(subnet_id=s) for s in dhcp_enabled_subnet_ids]) - dhcp_port = self.plugin.create_dhcp_port({'port': port_dict}) - - # Convert subnet_id to subnet dict - fixed_ips = [dict(subnet_id=fixed_ip.subnet_id, - ip_address=fixed_ip.ip_address, - subnet=subnets[fixed_ip.subnet_id]) - for fixed_ip in dhcp_port.fixed_ips] - - ips = [DictModel(item) if isinstance(item, dict) else item - for item in fixed_ips] - dhcp_port.fixed_ips = ips - - return dhcp_port - - def setup(self, network, reuse_existing=False): - """Create and initialize a device for network's DHCP on this host.""" - port = self.setup_dhcp_port(network) - interface_name = self.get_interface_name(network, port) - - if self.conf.use_namespaces: - namespace = NS_PREFIX + network.id - else: - namespace = None - - if ip_lib.device_exists(interface_name, - self.root_helper, - namespace): - if not reuse_existing: - raise exceptions.PreexistingDeviceFailure( - dev_name=interface_name) - - LOG.debug(_('Reusing existing device: %s.'), interface_name) - else: - self.driver.plug(network.id, - port.id, - interface_name, - port.mac_address, - namespace=namespace) - ip_cidrs = [] - for fixed_ip in port.fixed_ips: - subnet = fixed_ip.subnet - net = netaddr.IPNetwork(subnet.cidr) - ip_cidr = '%s/%s' % (fixed_ip.ip_address, net.prefixlen) - ip_cidrs.append(ip_cidr) - - if (self.conf.enable_isolated_metadata and - self.conf.use_namespaces): - ip_cidrs.append(METADATA_DEFAULT_IP) - - self.driver.init_l3(interface_name, ip_cidrs, - namespace=namespace) - - # ensure that the dhcp interface is first in the list - if namespace is None: - device = ip_lib.IPDevice(interface_name, - self.root_helper) - device.route.pullup_route(interface_name) - - if self.conf.use_namespaces: - self._set_default_route(network) - - return interface_name - - def update(self, network): - """Update device settings for the network's DHCP on this host.""" - if self.conf.use_namespaces: - self._set_default_route(network) - - def destroy(self, network, device_name): - """Destroy the device used for the network's DHCP on this host.""" - if self.conf.use_namespaces: - namespace = NS_PREFIX + network.id - else: - namespace = None - - self.driver.unplug(device_name, namespace=namespace) - - self.plugin.release_dhcp_port(network.id, - self.get_device_id(network)) - - -class DictModel(object): - """Convert dict into an object that provides attribute access to values.""" - def __init__(self, d): - for key, value in d.iteritems(): - if isinstance(value, list): - value = [DictModel(item) if isinstance(item, dict) else item - for item in value] - elif isinstance(value, dict): - value = DictModel(value) - - setattr(self, key, value) - - class DhcpAgentWithStateReport(DhcpAgent): def __init__(self, host=None): super(DhcpAgentWithStateReport, self).__init__(host=host) @@ -811,7 +579,6 @@ def register_options(): cfg.CONF.register_opts(DhcpAgent.OPTS) config.register_agent_state_opts_helper(cfg.CONF) config.register_root_helper(cfg.CONF) - cfg.CONF.register_opts(DeviceManager.OPTS) cfg.CONF.register_opts(dhcp.OPTS) cfg.CONF.register_opts(interface.OPTS) diff --git a/neutron/agent/linux/dhcp.py b/neutron/agent/linux/dhcp.py index 9ea49bb7b..060ed3b18 100644 --- a/neutron/agent/linux/dhcp.py +++ b/neutron/agent/linux/dhcp.py @@ -22,12 +22,15 @@ import shutil import socket import StringIO import sys +import uuid import netaddr from oslo.config import cfg from neutron.agent.linux import ip_lib from neutron.agent.linux import utils +from neutron.common import exceptions +from neutron.openstack.common import importutils from neutron.openstack.common import jsonutils from neutron.openstack.common import log as logging from neutron.openstack.common import uuidutils @@ -47,6 +50,8 @@ OPTS = [ cfg.StrOpt('dnsmasq_dns_server', help=_('Use another DNS server before any in ' '/etc/resolv.conf.')), + cfg.StrOpt('interface_driver', + help=_("The driver used to manage the virtual interface.")), ] IPV4 = 4 @@ -56,20 +61,51 @@ TCP = 'tcp' DNS_PORT = 53 DHCPV4_PORT = 67 DHCPV6_PORT = 547 +METADATA_DEFAULT_PREFIX = 16 METADATA_DEFAULT_IP = '169.254.169.254' +METADATA_DEFAULT_CIDR = '%s/%d' % (METADATA_DEFAULT_IP, + METADATA_DEFAULT_PREFIX) +METADATA_PORT = 80 WIN2k3_STATIC_DNS = 249 +NS_PREFIX = 'qdhcp-' + + +class DictModel(object): + """Convert dict into an object that provides attribute access to values.""" + def __init__(self, d): + for key, value in d.iteritems(): + if isinstance(value, list): + value = [DictModel(item) if isinstance(item, dict) else item + for item in value] + elif isinstance(value, dict): + value = DictModel(value) + + setattr(self, key, value) + + +class NetModel(DictModel): + + def __init__(self, use_namespaces, d): + super(NetModel, self).__init__(d) + + self._ns_name = (use_namespaces and + "%s%s" % (NS_PREFIX, self.id) or None) + + @property + def namespace(self): + return self._ns_name class DhcpBase(object): __metaclass__ = abc.ABCMeta def __init__(self, conf, network, root_helper='sudo', - device_delegate=None, namespace=None, version=None): + version=None, plugin=None): self.conf = conf self.network = network self.root_helper = root_helper - self.device_delegate = device_delegate - self.namespace = namespace + self.device_manager = DeviceManager(self.conf, + self.root_helper, plugin) self.version = version @abc.abstractmethod @@ -84,6 +120,7 @@ class DhcpBase(object): """Restart the dhcp service for the network.""" self.disable(retain_port=True) self.enable() + self.device_manager.update(self.network) @abc.abstractproperty def active(self): @@ -122,8 +159,8 @@ class DhcpLocalProcess(DhcpBase): def enable(self): """Enables DHCP for this network by spawning a local process.""" - interface_name = self.device_delegate.setup(self.network, - reuse_existing=True) + interface_name = self.device_manager.setup(self.network, + reuse_existing=True) if self.active: self.restart() elif self._enable_dhcp(): @@ -138,7 +175,7 @@ class DhcpLocalProcess(DhcpBase): cmd = ['kill', '-9', pid] utils.execute(cmd, self.root_helper) if not retain_port: - self.device_delegate.destroy(self.network, self.interface_name) + self.device_manager.destroy(self.network, self.interface_name) elif pid: LOG.debug(_('DHCP for %(net_id)s pid %(pid)d is stale, ignoring ' @@ -311,8 +348,9 @@ class Dnsmasq(DhcpLocalProcess): if self.conf.dhcp_domain: cmd.append('--domain=%s' % self.conf.dhcp_domain) - if self.namespace: - ip_wrapper = ip_lib.IPWrapper(self.root_helper, self.namespace) + if self.network.namespace: + ip_wrapper = ip_lib.IPWrapper(self.root_helper, + self.network.namespace) ip_wrapper.netns.execute(cmd, addl_env=env) else: # For normal sudo prepend the env vars before command @@ -323,8 +361,9 @@ class Dnsmasq(DhcpLocalProcess): """Release a DHCP lease.""" for ip in removed_ips or []: cmd = ['dhcp_release', self.interface_name, ip, mac_address] - if self.namespace: - ip_wrapper = ip_lib.IPWrapper(self.root_helper, self.namespace) + if self.network.namespace: + ip_wrapper = ip_lib.IPWrapper(self.root_helper, + self.network.namespace) ip_wrapper.netns.execute(cmd) else: utils.execute(cmd, self.root_helper) @@ -347,6 +386,7 @@ class Dnsmasq(DhcpLocalProcess): else: LOG.debug(_('Pid %d is stale, relaunching dnsmasq'), self.pid) LOG.debug(_('Reloading allocations for network: %s'), self.network.id) + self.device_manager.update(self.network) def _output_hosts_file(self): """Writes a dnsmasq compatible hosts file.""" @@ -421,7 +461,7 @@ class Dnsmasq(DhcpLocalProcess): ip_dev = ip_lib.IPDevice( self.interface_name, self.root_helper, - self.namespace + self.network.namespace ) subnet_lookup = dict( @@ -476,3 +516,195 @@ class Dnsmasq(DhcpLocalProcess): sock.connect(dhcp_relay_socket) sock.send(jsonutils.dumps(data)) sock.close() + + +class DeviceManager(object): + + def __init__(self, conf, root_helper, plugin): + self.conf = conf + self.root_helper = root_helper + self.plugin = plugin + if not conf.interface_driver: + raise SystemExit(_('You must specify an interface driver')) + try: + self.driver = importutils.import_object( + conf.interface_driver, conf) + except Exception as e: + msg = (_("Error importing interface driver '%(driver)s': " + "%(inner)s") % {'driver': conf.interface_driver, + 'inner': e}) + raise SystemExit(msg) + + def get_interface_name(self, network, port): + """Return interface(device) name for use by the DHCP process.""" + return self.driver.get_device_name(port) + + def get_device_id(self, network): + """Return a unique DHCP device ID for this host on the network.""" + # There could be more than one dhcp server per network, so create + # a device id that combines host and network ids + + host_uuid = uuid.uuid5(uuid.NAMESPACE_DNS, socket.gethostname()) + return 'dhcp%s-%s' % (host_uuid, network.id) + + def _get_device(self, network): + """Return DHCP ip_lib device for this host on the network.""" + device_id = self.get_device_id(network) + port = self.plugin.get_dhcp_port(network.id, device_id) + interface_name = self.get_interface_name(network, port) + return ip_lib.IPDevice(interface_name, + self.root_helper, + network.namespace) + + def _set_default_route(self, network): + """Sets the default gateway for this dhcp namespace. + + This method is idempotent and will only adjust the route if adjusting + it would change it from what it already is. This makes it safe to call + and avoids unnecessary perturbation of the system. + """ + device = self._get_device(network) + gateway = device.route.get_gateway() + if gateway: + gateway = gateway['gateway'] + + for subnet in network.subnets: + skip_subnet = ( + subnet.ip_version != 4 + or not subnet.enable_dhcp + or subnet.gateway_ip is None) + + if skip_subnet: + continue + + if gateway != subnet.gateway_ip: + m = _('Setting gateway for dhcp netns on net %(n)s to %(ip)s') + LOG.debug(m, {'n': network.id, 'ip': subnet.gateway_ip}) + + device.route.add_gateway(subnet.gateway_ip) + + return + + # No subnets on the network have a valid gateway. Clean it up to avoid + # confusion from seeing an invalid gateway here. + if gateway is not None: + msg = _('Removing gateway for dhcp netns on net %s') + LOG.debug(msg, network.id) + + device.route.delete_gateway(gateway) + + def setup_dhcp_port(self, network): + """Create/update DHCP port for the host if needed and return port.""" + + device_id = self.get_device_id(network) + subnets = {} + dhcp_enabled_subnet_ids = [] + for subnet in network.subnets: + if subnet.enable_dhcp: + dhcp_enabled_subnet_ids.append(subnet.id) + subnets[subnet.id] = subnet + + dhcp_port = None + for port in network.ports: + port_device_id = getattr(port, 'device_id', None) + if port_device_id == device_id: + port_fixed_ips = [] + for fixed_ip in port.fixed_ips: + port_fixed_ips.append({'subnet_id': fixed_ip.subnet_id, + 'ip_address': fixed_ip.ip_address}) + if fixed_ip.subnet_id in dhcp_enabled_subnet_ids: + dhcp_enabled_subnet_ids.remove(fixed_ip.subnet_id) + + # If there are dhcp_enabled_subnet_ids here that means that + # we need to add those to the port and call update. + if dhcp_enabled_subnet_ids: + port_fixed_ips.extend( + [dict(subnet_id=s) for s in dhcp_enabled_subnet_ids]) + dhcp_port = self.plugin.update_dhcp_port( + port.id, {'port': {'fixed_ips': port_fixed_ips}}) + else: + dhcp_port = port + # break since we found port that matches device_id + break + + # DHCP port has not yet been created. + if dhcp_port is None: + LOG.debug(_('DHCP port %(device_id)s on network %(network_id)s' + ' does not yet exist.'), {'device_id': device_id, + 'network_id': network.id}) + port_dict = dict( + name='', + admin_state_up=True, + device_id=device_id, + network_id=network.id, + tenant_id=network.tenant_id, + fixed_ips=[dict(subnet_id=s) for s in dhcp_enabled_subnet_ids]) + dhcp_port = self.plugin.create_dhcp_port({'port': port_dict}) + + # Convert subnet_id to subnet dict + fixed_ips = [dict(subnet_id=fixed_ip.subnet_id, + ip_address=fixed_ip.ip_address, + subnet=subnets[fixed_ip.subnet_id]) + for fixed_ip in dhcp_port.fixed_ips] + + ips = [DictModel(item) if isinstance(item, dict) else item + for item in fixed_ips] + dhcp_port.fixed_ips = ips + + return dhcp_port + + def setup(self, network, reuse_existing=False): + """Create and initialize a device for network's DHCP on this host.""" + port = self.setup_dhcp_port(network) + interface_name = self.get_interface_name(network, port) + + if ip_lib.device_exists(interface_name, + self.root_helper, + network.namespace): + if not reuse_existing: + raise exceptions.PreexistingDeviceFailure( + dev_name=interface_name) + + LOG.debug(_('Reusing existing device: %s.'), interface_name) + else: + self.driver.plug(network.id, + port.id, + interface_name, + port.mac_address, + namespace=network.namespace) + ip_cidrs = [] + for fixed_ip in port.fixed_ips: + subnet = fixed_ip.subnet + net = netaddr.IPNetwork(subnet.cidr) + ip_cidr = '%s/%s' % (fixed_ip.ip_address, net.prefixlen) + ip_cidrs.append(ip_cidr) + + if (self.conf.enable_isolated_metadata and + self.conf.use_namespaces): + ip_cidrs.append(METADATA_DEFAULT_CIDR) + + self.driver.init_l3(interface_name, ip_cidrs, + namespace=network.namespace) + + # ensure that the dhcp interface is first in the list + if network.namespace is None: + device = ip_lib.IPDevice(interface_name, + self.root_helper) + device.route.pullup_route(interface_name) + + if self.conf.use_namespaces: + self._set_default_route(network) + + return interface_name + + def update(self, network): + """Update device settings for the network's DHCP on this host.""" + if self.conf.use_namespaces: + self._set_default_route(network) + + def destroy(self, network, device_name): + """Destroy the device used for the network's DHCP on this host.""" + self.driver.unplug(device_name, namespace=network.namespace) + + self.plugin.release_dhcp_port(network.id, + self.get_device_id(network)) diff --git a/neutron/agent/netns_cleanup_util.py b/neutron/agent/netns_cleanup_util.py index 78815f5fe..9c17f715f 100644 --- a/neutron/agent/netns_cleanup_util.py +++ b/neutron/agent/netns_cleanup_util.py @@ -21,7 +21,6 @@ import eventlet from oslo.config import cfg from neutron.agent.common import config as agent_config -from neutron.agent import dhcp_agent from neutron.agent import l3_agent from neutron.agent.linux import dhcp from neutron.agent.linux import ip_lib @@ -33,17 +32,10 @@ from neutron.openstack.common import log as logging LOG = logging.getLogger(__name__) -NS_MANGLING_PATTERN = ('(%s|%s)' % (dhcp_agent.NS_PREFIX, l3_agent.NS_PREFIX) + +NS_MANGLING_PATTERN = ('(%s|%s)' % (dhcp.NS_PREFIX, l3_agent.NS_PREFIX) + attributes.UUID_PATTERN) -class NullDelegate(object): - def __getattribute__(self, name): - def noop(*args, **kwargs): - pass - return noop - - class FakeNetwork(object): def __init__(self, id): self.id = id @@ -79,15 +71,13 @@ def setup_conf(): def kill_dhcp(conf, namespace): """Disable DHCP for a network if DHCP is still active.""" root_helper = agent_config.get_root_helper(conf) - network_id = namespace.replace(dhcp_agent.NS_PREFIX, '') + network_id = namespace.replace(dhcp.NS_PREFIX, '') - null_delegate = NullDelegate() dhcp_driver = importutils.import_object( conf.dhcp_driver, conf, FakeNetwork(network_id), - root_helper, - null_delegate) + root_helper) if dhcp_driver.active: dhcp_driver.disable() diff --git a/neutron/debug/debug_agent.py b/neutron/debug/debug_agent.py index 96706fdb7..5c0265f59 100644 --- a/neutron/debug/debug_agent.py +++ b/neutron/debug/debug_agent.py @@ -22,7 +22,7 @@ import netaddr from oslo.config import cfg from neutron.agent.common import config -from neutron.agent.dhcp_agent import DictModel +from neutron.agent.linux.dhcp import DictModel from neutron.agent.linux import ip_lib from neutron.agent.linux import utils from neutron.openstack.common import log as logging diff --git a/neutron/tests/unit/test_agent_netns_cleanup.py b/neutron/tests/unit/test_agent_netns_cleanup.py index 80e188f14..4c691151f 100644 --- a/neutron/tests/unit/test_agent_netns_cleanup.py +++ b/neutron/tests/unit/test_agent_netns_cleanup.py @@ -22,12 +22,6 @@ from neutron.agent import netns_cleanup_util as util from neutron.tests import base -class TestNullDelegate(base.BaseTestCase): - def test_getattribute(self): - null_delegate = util.NullDelegate() - self.assertIsNone(null_delegate.test()) - - class TestNetnsCleanup(base.BaseTestCase): def setUp(self): super(TestNetnsCleanup, self).setUp() diff --git a/neutron/tests/unit/test_dhcp_agent.py b/neutron/tests/unit/test_dhcp_agent.py index 4820676a9..eab75f1a4 100644 --- a/neutron/tests/unit/test_dhcp_agent.py +++ b/neutron/tests/unit/test_dhcp_agent.py @@ -30,7 +30,7 @@ from neutron.agent import dhcp_agent from neutron.agent.dhcp_agent import DhcpAgentWithStateReport from neutron.agent.linux import dhcp from neutron.agent.linux import interface -from neutron.common import constants +from neutron.common import constants as const from neutron.common import exceptions from neutron.tests import base @@ -38,92 +38,91 @@ from neutron.tests import base ROOTDIR = os.path.dirname(os.path.dirname(__file__)) ETCDIR = os.path.join(ROOTDIR, 'etc') HOSTNAME = 'hostname' +dev_man = dhcp.DeviceManager +rpc_api = dhcp_agent.DhcpPluginApi +DEVICE_MANAGER = '%s.%s' % (dev_man.__module__, dev_man.__name__) +DHCP_PLUGIN = '%s.%s' % (rpc_api.__module__, rpc_api.__name__) def etcdir(*p): return os.path.join(ETCDIR, *p) -class FakeModel: - def __init__(self, id_, **kwargs): - self.id = id_ - self.__dict__.update(kwargs) - - def __str__(self): - return str(self.__dict__) - fake_tenant_id = 'aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa' -fake_subnet1_allocation_pools = FakeModel('', start='172.9.9.2', - end='172.9.9.254') -fake_subnet1 = FakeModel('bbbbbbbb-bbbb-bbbb-bbbbbbbbbbbb', - network_id='12345678-1234-5678-1234567890ab', - cidr='172.9.9.0/24', enable_dhcp=True, name='', - tenant_id=fake_tenant_id, gateway_ip='172.9.9.1', - host_routes=[], dns_nameservers=[], ip_version=4, - allocation_pools=fake_subnet1_allocation_pools) +fake_subnet1_allocation_pools = dhcp.DictModel(dict(id='', start='172.9.9.2', + end='172.9.9.254')) +fake_subnet1 = dhcp.DictModel(dict(id='bbbbbbbb-bbbb-bbbb-bbbbbbbbbbbb', + network_id='12345678-1234-5678-1234567890ab', + cidr='172.9.9.0/24', enable_dhcp=True, name='', + tenant_id=fake_tenant_id, + gateway_ip='172.9.9.1', host_routes=[], + dns_nameservers=[], ip_version=4, + allocation_pools=fake_subnet1_allocation_pools)) -fake_subnet2_allocation_pools = FakeModel('', start='172.9.8.2', - end='172.9.8.254') -fake_subnet2 = FakeModel('dddddddd-dddd-dddd-dddddddddddd', - network_id='12345678-1234-5678-1234567890ab', - cidr='172.9.8.0/24', enable_dhcp=False, name='', - tenant_id=fake_tenant_id, gateway_ip='172.9.8.1', - host_routes=[], dns_nameservers=[], ip_version=4, - allocation_pools=fake_subnet2_allocation_pools) +fake_subnet2_allocation_pools = dhcp.DictModel(dict(id='', start='172.9.8.2', + end='172.9.8.254')) +fake_subnet2 = dhcp.DictModel(dict(id='dddddddd-dddd-dddd-dddddddddddd', + network_id='12345678-1234-5678-1234567890ab', + cidr='172.9.8.0/24', enable_dhcp=False, name='', + tenant_id=fake_tenant_id, gateway_ip='172.9.8.1', + host_routes=[], dns_nameservers=[], ip_version=4, + allocation_pools=fake_subnet2_allocation_pools)) -fake_subnet3 = FakeModel('bbbbbbbb-1111-2222-bbbbbbbbbbbb', - network_id='12345678-1234-5678-1234567890ab', - cidr='192.168.1.1/24', enable_dhcp=True) +fake_subnet3 = dhcp.DictModel(dict(id='bbbbbbbb-1111-2222-bbbbbbbbbbbb', + network_id='12345678-1234-5678-1234567890ab', + cidr='192.168.1.1/24', enable_dhcp=True)) -fake_meta_subnet = FakeModel('bbbbbbbb-1111-2222-bbbbbbbbbbbb', - network_id='12345678-1234-5678-1234567890ab', - cidr='169.254.169.252/30', - gateway_ip='169.254.169.253', enable_dhcp=True) +fake_meta_subnet = dhcp.DictModel(dict(id='bbbbbbbb-1111-2222-bbbbbbbbbbbb', + network_id='12345678-1234-5678-1234567890ab', + cidr='169.254.169.252/30', + gateway_ip='169.254.169.253', + enable_dhcp=True)) -fake_fixed_ip1 = FakeModel('', subnet_id=fake_subnet1.id, - ip_address='172.9.9.9') -fake_meta_fixed_ip = FakeModel('', subnet=fake_meta_subnet, - ip_address='169.254.169.254') -fake_allocation_pool_subnet1 = FakeModel('', start='172.9.9.2', - end='172.9.9.254') +fake_fixed_ip1 = dhcp.DictModel(dict(id='', subnet_id=fake_subnet1.id, + ip_address='172.9.9.9')) +fake_meta_fixed_ip = dhcp.DictModel(dict(id='', subnet=fake_meta_subnet, + ip_address='169.254.169.254')) +fake_allocation_pool_subnet1 = dhcp.DictModel(dict(id='', start='172.9.9.2', + end='172.9.9.254')) +fake_port1 = dhcp.DictModel(dict(id='12345678-1234-aaaa-1234567890ab', + device_id='dhcp-12345678-1234-aaaa-1234567890ab', + allocation_pools=fake_subnet1_allocation_pools, + mac_address='aa:bb:cc:dd:ee:ff', + network_id='12345678-1234-5678-1234567890ab', + fixed_ips=[fake_fixed_ip1])) -fake_port1 = FakeModel('12345678-1234-aaaa-1234567890ab', - device_id='dhcp-12345678-1234-aaaa-1234567890ab', - allocation_pools=fake_subnet1_allocation_pools, - mac_address='aa:bb:cc:dd:ee:ff', - network_id='12345678-1234-5678-1234567890ab', - fixed_ips=[fake_fixed_ip1]) +fake_port2 = dhcp.DictModel(dict(id='12345678-1234-aaaa-123456789000', + mac_address='aa:bb:cc:dd:ee:99', + network_id='12345678-1234-5678-1234567890ab', + fixed_ips=[])) -fake_port2 = FakeModel('12345678-1234-aaaa-123456789000', - mac_address='aa:bb:cc:dd:ee:99', - network_id='12345678-1234-5678-1234567890ab', - fixed_ips=[]) +fake_meta_port = dhcp.DictModel(dict(id='12345678-1234-aaaa-1234567890ab', + mac_address='aa:bb:cc:dd:ee:ff', + network_id='12345678-1234-5678-1234567890ab', + device_owner=const.DEVICE_OWNER_ROUTER_INTF, + device_id='forzanapoli', + fixed_ips=[fake_meta_fixed_ip])) -fake_meta_port = FakeModel('12345678-1234-aaaa-1234567890ab', - mac_address='aa:bb:cc:dd:ee:ff', - network_id='12345678-1234-5678-1234567890ab', - device_owner=constants.DEVICE_OWNER_ROUTER_INTF, - device_id='forzanapoli', - fixed_ips=[fake_meta_fixed_ip]) +fake_network = dhcp.NetModel(True, dict(id='12345678-1234-5678-1234567890ab', + tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa', + admin_state_up=True, + subnets=[fake_subnet1, fake_subnet2], + ports=[fake_port1])) -fake_network = FakeModel('12345678-1234-5678-1234567890ab', - tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa', - admin_state_up=True, - subnets=[fake_subnet1, fake_subnet2], - ports=[fake_port1]) +fake_meta_network = dhcp.NetModel(True, + dict(id='12345678-1234-5678-1234567890ab', + tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa', + admin_state_up=True, + subnets=[fake_meta_subnet], + ports=[fake_meta_port])) -fake_meta_network = FakeModel('12345678-1234-5678-1234567890ab', - tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa', - admin_state_up=True, - subnets=[fake_meta_subnet], - ports=[fake_meta_port]) - -fake_down_network = FakeModel('12345678-dddd-dddd-1234567890ab', - tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa', - admin_state_up=False, - subnets=[], - ports=[]) +fake_down_network = dhcp.NetModel(True, + dict(id='12345678-dddd-dddd-1234567890ab', + tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa', + admin_state_up=False, + subnets=[], + ports=[])) class TestDhcpAgent(base.BaseTestCase): @@ -163,8 +162,6 @@ class TestDhcpAgent(base.BaseTestCase): cfg.CONF.register_opts(dhcp_agent.DhcpAgent.OPTS) config.register_agent_state_opts_helper(cfg.CONF) config.register_root_helper(cfg.CONF) - cfg.CONF.register_opts( - dhcp_agent.DeviceManager.OPTS) cfg.CONF.register_opts(dhcp.OPTS) cfg.CONF.register_opts(interface.OPTS) cfg.CONF(project='neutron') @@ -192,7 +189,7 @@ class TestDhcpAgent(base.BaseTestCase): mock.call().wait()]) def test_run_completes_single_pass(self): - with mock.patch('neutron.agent.dhcp_agent.DeviceManager'): + with mock.patch(DEVICE_MANAGER): dhcp = dhcp_agent.DhcpAgent(HOSTNAME) attrs_to_mock = dict( [(a, mock.DEFAULT) for a in @@ -202,53 +199,34 @@ class TestDhcpAgent(base.BaseTestCase): mocks['sync_state'].assert_called_once_with() mocks['periodic_resync'].assert_called_once_with() - def test_ns_name(self): - with mock.patch('neutron.agent.dhcp_agent.DeviceManager'): - mock_net = mock.Mock(id='foo') - dhcp = dhcp_agent.DhcpAgent(HOSTNAME) - self.assertEqual(dhcp._ns_name(mock_net), 'qdhcp-foo') - - def test_ns_name_disabled_namespace(self): - with mock.patch('neutron.agent.dhcp_agent.DeviceManager'): - cfg.CONF.set_override('use_namespaces', False) - mock_net = mock.Mock(id='foo') - dhcp = dhcp_agent.DhcpAgent(HOSTNAME) - self.assertIsNone(dhcp._ns_name(mock_net)) - def test_call_driver(self): network = mock.Mock() network.id = '1' - with mock.patch('neutron.agent.dhcp_agent.DeviceManager') as dev_mgr: - dhcp = dhcp_agent.DhcpAgent(cfg.CONF) - self.assertTrue(dhcp.call_driver('foo', network)) - self.assertTrue(dev_mgr.called) - self.driver.assert_called_once_with(cfg.CONF, - mock.ANY, - 'sudo', - mock.ANY, - 'qdhcp-1', - mock.ANY) + dhcp = dhcp_agent.DhcpAgent(cfg.CONF) + self.assertTrue(dhcp.call_driver('foo', network)) + self.driver.assert_called_once_with(cfg.CONF, + mock.ANY, + 'sudo', + mock.ANY, + mock.ANY) def test_call_driver_failure(self): network = mock.Mock() network.id = '1' self.driver.return_value.foo.side_effect = Exception - with mock.patch('neutron.agent.dhcp_agent.DeviceManager') as dev_mgr: - with mock.patch.object(dhcp_agent.LOG, 'exception') as log: - dhcp = dhcp_agent.DhcpAgent(HOSTNAME) - self.assertIsNone(dhcp.call_driver('foo', network)) - self.assertTrue(dev_mgr.called) - self.driver.assert_called_once_with(cfg.CONF, - mock.ANY, - 'sudo', - mock.ANY, - 'qdhcp-1', - mock.ANY) - self.assertEqual(log.call_count, 1) - self.assertTrue(dhcp.needs_resync) + with mock.patch.object(dhcp_agent.LOG, 'exception') as log: + dhcp = dhcp_agent.DhcpAgent(HOSTNAME) + self.assertIsNone(dhcp.call_driver('foo', network)) + self.driver.assert_called_once_with(cfg.CONF, + mock.ANY, + 'sudo', + mock.ANY, + mock.ANY) + self.assertEqual(log.call_count, 1) + self.assertTrue(dhcp.needs_resync) def _test_sync_state_helper(self, known_networks, active_networks): - with mock.patch('neutron.agent.dhcp_agent.DhcpPluginApi') as plug: + with mock.patch(DHCP_PLUGIN) as plug: mock_plugin = mock.Mock() mock_plugin.get_active_networks_info.return_value = active_networks plug.return_value = mock_plugin @@ -283,7 +261,7 @@ class TestDhcpAgent(base.BaseTestCase): self._test_sync_state_helper(['b'], ['a']) def test_sync_state_plugin_error(self): - with mock.patch('neutron.agent.dhcp_agent.DhcpPluginApi') as plug: + with mock.patch(DHCP_PLUGIN) as plug: mock_plugin = mock.Mock() mock_plugin.get_active_networks_info.side_effect = Exception plug.return_value = mock_plugin @@ -351,7 +329,7 @@ class TestLogArgs(base.BaseTestCase): 'log_file': None, 'use_syslog': True, 'syslog_log_facility': 'LOG_USER'} - conf = dhcp_agent.DictModel(conf_dict) + conf = dhcp.DictModel(conf_dict) expected_args = ['--debug', '--use-syslog', '--syslog-log-facility=LOG_USER'] @@ -365,7 +343,7 @@ class TestLogArgs(base.BaseTestCase): 'log_file': None, 'use_syslog': False, 'syslog_log_facility': 'LOG_USER'} - conf = dhcp_agent.DictModel(conf_dict) + conf = dhcp.DictModel(conf_dict) expected_args = ['--debug', '--verbose', '--log-file=log_file_name', @@ -380,7 +358,7 @@ class TestLogArgs(base.BaseTestCase): 'log_file': 'tests/filelog', 'use_syslog': False, 'syslog_log_facility': 'LOG_USER'} - conf = dhcp_agent.DictModel(conf_dict) + conf = dhcp.DictModel(conf_dict) expected_args = ['--debug', '--log-file=log_file_name', '--log-dir=/etc/tests/tests'] @@ -394,7 +372,7 @@ class TestLogArgs(base.BaseTestCase): 'log_dir': None, 'use_syslog': False, 'syslog_log_facility': 'LOG_USER'} - conf = dhcp_agent.DictModel(conf_dict) + conf = dhcp.DictModel(conf_dict) expected_args = ['--debug', '--log-file=log_file_name', '--log-dir=tests'] @@ -408,7 +386,7 @@ class TestLogArgs(base.BaseTestCase): 'log_dir': '/etc/tests', 'use_syslog': True, 'syslog_log_facility': 'LOG_USER'} - conf = dhcp_agent.DictModel(conf_dict) + conf = dhcp.DictModel(conf_dict) expected_args = ['--debug', '--verbose', '--log-file=log_file_name', @@ -420,14 +398,13 @@ class TestLogArgs(base.BaseTestCase): class TestDhcpAgentEventHandler(base.BaseTestCase): def setUp(self): super(TestDhcpAgentEventHandler, self).setUp() - cfg.CONF.register_opts(dhcp_agent.DeviceManager.OPTS) 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) - self.plugin_p = mock.patch('neutron.agent.dhcp_agent.DhcpPluginApi') + self.plugin_p = mock.patch(DHCP_PLUGIN) plugin_cls = self.plugin_p.start() self.plugin = mock.Mock() plugin_cls.return_value = self.plugin @@ -665,11 +642,11 @@ class TestDhcpAgentEventHandler(base.BaseTestCase): disable.assertCalledOnceWith(fake_network.id) def test_refresh_dhcp_helper_no_dhcp_enabled_networks(self): - network = FakeModel('net-id', - tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa', - admin_state_up=True, - subnets=[], - ports=[]) + network = dhcp.NetModel(True, dict(id='net-id', + tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa', + admin_state_up=True, + subnets=[], + ports=[])) self.cache.get_network_by_id.return_value = network self.plugin.get_network_info.return_value = network @@ -682,11 +659,11 @@ class TestDhcpAgentEventHandler(base.BaseTestCase): [mock.call.get_network_by_id('net-id')]) def test_refresh_dhcp_helper_exception_during_rpc(self): - network = FakeModel('net-id', - tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa', - admin_state_up=True, - subnets=[], - ports=[]) + network = dhcp.NetModel(True, dict(id='net-id', + tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa', + admin_state_up=True, + subnets=[], + ports=[])) self.cache.get_network_by_id.return_value = network self.plugin.get_network_info.side_effect = Exception @@ -702,46 +679,41 @@ class TestDhcpAgentEventHandler(base.BaseTestCase): payload = dict(subnet=dict(network_id=fake_network.id)) self.cache.get_network_by_id.return_value = fake_network self.plugin.get_network_info.return_value = fake_network - self.dhcp.device_manager.update = mock.Mock() self.dhcp.subnet_update_end(None, payload) self.cache.assert_has_calls([mock.call.put(fake_network)]) self.call_driver.assert_called_once_with('reload_allocations', fake_network) - self.dhcp.device_manager.update.assert_called_once_with(fake_network) def test_subnet_update_end_restart(self): - new_state = FakeModel(fake_network.id, - tenant_id=fake_network.tenant_id, - admin_state_up=True, - subnets=[fake_subnet1, fake_subnet3], - ports=[fake_port1]) + new_state = dhcp.NetModel(True, dict(id=fake_network.id, + tenant_id=fake_network.tenant_id, + admin_state_up=True, + subnets=[fake_subnet1, fake_subnet3], + ports=[fake_port1])) payload = dict(subnet=dict(network_id=fake_network.id)) self.cache.get_network_by_id.return_value = fake_network self.plugin.get_network_info.return_value = new_state - self.dhcp.device_manager.update = mock.Mock() self.dhcp.subnet_update_end(None, payload) self.cache.assert_has_calls([mock.call.put(new_state)]) self.call_driver.assert_called_once_with('restart', new_state) - self.dhcp.device_manager.update.assert_called_once_with(new_state) def test_subnet_update_end_delete_payload(self): - prev_state = FakeModel(fake_network.id, - tenant_id=fake_network.tenant_id, - admin_state_up=True, - subnets=[fake_subnet1, fake_subnet3], - ports=[fake_port1]) + prev_state = dhcp.NetModel(True, dict(id=fake_network.id, + tenant_id=fake_network.tenant_id, + admin_state_up=True, + subnets=[fake_subnet1, fake_subnet3], + ports=[fake_port1])) payload = dict(subnet_id=fake_subnet1.id) self.cache.get_network_by_subnet_id.return_value = prev_state self.cache.get_network_by_id.return_value = prev_state self.plugin.get_network_info.return_value = fake_network - self.dhcp.device_manager.update = mock.Mock() self.dhcp.subnet_delete_end(None, payload) @@ -752,7 +724,6 @@ class TestDhcpAgentEventHandler(base.BaseTestCase): mock.call.put(fake_network)]) self.call_driver.assert_called_once_with('restart', fake_network) - self.dhcp.device_manager.update.assert_called_once_with(fake_network) def test_port_update_end(self): payload = dict(port=vars(fake_port2)) @@ -817,7 +788,7 @@ class TestDhcpAgentEventHandler(base.BaseTestCase): class TestDhcpPluginApiProxy(base.BaseTestCase): def setUp(self): super(TestDhcpPluginApiProxy, self).setUp() - self.proxy = dhcp_agent.DhcpPluginApi('foo', {}) + self.proxy = dhcp_agent.DhcpPluginApi('foo', {}, None) self.proxy.host = 'foo' self.call_p = mock.patch.object(self.proxy, 'call') @@ -962,40 +933,42 @@ class TestNetworkCache(base.BaseTestCase): fake_network) def test_put_port(self): - fake_network = FakeModel('12345678-1234-5678-1234567890ab', + fake_net = dhcp.NetModel(True, + dict(id='12345678-1234-5678-1234567890ab', tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa', subnets=[fake_subnet1], - ports=[fake_port1]) + ports=[fake_port1])) nc = dhcp_agent.NetworkCache() - nc.put(fake_network) + nc.put(fake_net) nc.put_port(fake_port2) self.assertEqual(len(nc.port_lookup), 2) - self.assertIn(fake_port2, fake_network.ports) + self.assertIn(fake_port2, fake_net.ports) def test_put_port_existing(self): - fake_network = FakeModel('12345678-1234-5678-1234567890ab', + fake_net = dhcp.NetModel(True, + dict(id='12345678-1234-5678-1234567890ab', tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa', subnets=[fake_subnet1], - ports=[fake_port1, fake_port2]) + ports=[fake_port1, fake_port2])) nc = dhcp_agent.NetworkCache() - nc.put(fake_network) + nc.put(fake_net) nc.put_port(fake_port2) self.assertEqual(len(nc.port_lookup), 2) - self.assertIn(fake_port2, fake_network.ports) + self.assertIn(fake_port2, fake_net.ports) def test_remove_port_existing(self): - fake_network = FakeModel('12345678-1234-5678-1234567890ab', + fake_net = dhcp.NetModel(True, + dict(id='12345678-1234-5678-1234567890ab', tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa', subnets=[fake_subnet1], - ports=[fake_port1, fake_port2]) - + ports=[fake_port1, fake_port2])) nc = dhcp_agent.NetworkCache() - nc.put(fake_network) + nc.put(fake_net) nc.remove_port(fake_port2) self.assertEqual(len(nc.port_lookup), 1) - self.assertNotIn(fake_port2, fake_network.ports) + self.assertNotIn(fake_port2, fake_net.ports) def test_get_port_by_id(self): nc = dhcp_agent.NetworkCache() @@ -1044,8 +1017,8 @@ class FakeV4NetworkNoGateway: class TestDeviceManager(base.BaseTestCase): def setUp(self): super(TestDeviceManager, self).setUp() - cfg.CONF.register_opts(dhcp_agent.DeviceManager.OPTS) cfg.CONF.register_opts(dhcp_agent.DhcpAgent.OPTS) + cfg.CONF.register_opts(dhcp.OPTS) cfg.CONF.set_override('interface_driver', 'neutron.agent.linux.interface.NullDriver') config.register_root_helper(cfg.CONF) @@ -1085,7 +1058,7 @@ class TestDeviceManager(base.BaseTestCase): self.device_exists.return_value = device_exists self.mock_driver.get_device_name.return_value = 'tap12345678-12' - dh = dhcp_agent.DeviceManager(cfg.CONF, plugin) + dh = dhcp.DeviceManager(cfg.CONF, cfg.CONF.root_helper, plugin) dh._set_default_route = mock.Mock() interface_name = dh.setup(net, reuse_existing) @@ -1099,20 +1072,21 @@ class TestDeviceManager(base.BaseTestCase): [{'subnet_id': fake_fixed_ip1.subnet_id}], 'device_id': mock.ANY}})]) - namespace = dhcp_agent.NS_PREFIX + net.id - expected_ips = ['172.9.9.9/24', '169.254.169.254/16'] - expected = [mock.call.init_l3('tap12345678-12', - expected_ips, - namespace=namespace)] + expected = [ + mock.call.get_device_name(port), + mock.call.init_l3( + 'tap12345678-12', + expected_ips, + namespace=net.namespace)] if not reuse_existing: - expected.insert(0, + expected.insert(1, mock.call.plug(net.id, port.id, 'tap12345678-12', 'aa:bb:cc:dd:ee:ff', - namespace=namespace)) + namespace=net.namespace)) self.mock_driver.assert_has_calls(expected) dh._set_default_route.assert_called_once_with(net) @@ -1132,7 +1106,7 @@ class TestDeviceManager(base.BaseTestCase): def test_create_dhcp_port_create_new(self): plugin = mock.Mock() - dh = dhcp_agent.DeviceManager(cfg.CONF, plugin) + dh = dhcp.DeviceManager(cfg.CONF, cfg.CONF.root_helper, plugin) plugin.create_dhcp_port.return_value = fake_network.ports[0] dh.setup_dhcp_port(fake_network) plugin.assert_has_calls([ @@ -1146,7 +1120,7 @@ class TestDeviceManager(base.BaseTestCase): def test_create_dhcp_port_update_add_subnet(self): plugin = mock.Mock() - dh = dhcp_agent.DeviceManager(cfg.CONF, plugin) + dh = dhcp.DeviceManager(cfg.CONF, cfg.CONF.root_helper, plugin) fake_network_copy = copy.deepcopy(fake_network) fake_network_copy.ports[0].device_id = dh.get_device_id(fake_network) fake_network_copy.subnets[1].enable_dhcp = True @@ -1163,7 +1137,7 @@ class TestDeviceManager(base.BaseTestCase): def test_create_dhcp_port_no_update_or_create(self): plugin = mock.Mock() - dh = dhcp_agent.DeviceManager(cfg.CONF, plugin) + dh = dhcp.DeviceManager(cfg.CONF, cfg.CONF.root_helper, plugin) fake_network_copy = copy.deepcopy(fake_network) fake_network_copy.ports[0].device_id = dh.get_device_id(fake_network) dh.setup_dhcp_port(fake_network_copy) @@ -1171,11 +1145,12 @@ class TestDeviceManager(base.BaseTestCase): self.assertFalse(plugin.update_dhcp_port.called) def test_destroy(self): - fake_network = FakeModel('12345678-1234-5678-1234567890ab', - tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa') + fake_net = dhcp.NetModel(True, + dict(id='12345678-1234-5678-1234567890ab', + tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa')) - fake_port = FakeModel('12345678-1234-aaaa-1234567890ab', - mac_address='aa:bb:cc:dd:ee:ff') + fake_port = dhcp.DictModel(dict(id='12345678-1234-aaaa-1234567890ab', + mac_address='aa:bb:cc:dd:ee:ff')) with mock.patch('neutron.agent.linux.interface.NullDriver') as dvr_cls: mock_driver = mock.MagicMock() @@ -1185,22 +1160,23 @@ class TestDeviceManager(base.BaseTestCase): plugin = mock.Mock() plugin.get_dhcp_port.return_value = fake_port - dh = dhcp_agent.DeviceManager(cfg.CONF, plugin) - dh.destroy(fake_network, 'tap12345678-12') + dh = dhcp.DeviceManager(cfg.CONF, cfg.CONF.root_helper, plugin) + dh.destroy(fake_net, 'tap12345678-12') dvr_cls.assert_called_once_with(cfg.CONF) mock_driver.assert_has_calls( [mock.call.unplug('tap12345678-12', - namespace='qdhcp-' + fake_network.id)]) + namespace='qdhcp-' + fake_net.id)]) plugin.assert_has_calls( - [mock.call.release_dhcp_port(fake_network.id, mock.ANY)]) + [mock.call.release_dhcp_port(fake_net.id, mock.ANY)]) def test_get_interface_name(self): - fake_network = FakeModel('12345678-1234-5678-1234567890ab', - tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa') + fake_net = dhcp.NetModel(True, + dict(id='12345678-1234-5678-1234567890ab', + tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa')) - fake_port = FakeModel('12345678-1234-aaaa-1234567890ab', - mac_address='aa:bb:cc:dd:ee:ff') + fake_port = dhcp.DictModel(dict(id='12345678-1234-aaaa-1234567890ab', + mac_address='aa:bb:cc:dd:ee:ff')) with mock.patch('neutron.agent.linux.interface.NullDriver') as dvr_cls: mock_driver = mock.MagicMock() @@ -1210,8 +1186,8 @@ class TestDeviceManager(base.BaseTestCase): plugin = mock.Mock() plugin.get_dhcp_port.return_value = fake_port - dh = dhcp_agent.DeviceManager(cfg.CONF, plugin) - dh.get_interface_name(fake_network, fake_port) + dh = dhcp.DeviceManager(cfg.CONF, cfg.CONF.root_helper, plugin) + dh.get_interface_name(fake_net, fake_port) dvr_cls.assert_called_once_with(cfg.CONF) mock_driver.assert_has_calls( @@ -1220,8 +1196,9 @@ class TestDeviceManager(base.BaseTestCase): self.assertEqual(len(plugin.mock_calls), 0) def test_get_device_id(self): - fake_network = FakeModel('12345678-1234-5678-1234567890ab', - tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa') + fake_net = dhcp.NetModel(True, + dict(id='12345678-1234-5678-1234567890ab', + tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa')) expected = ('dhcp1ae5f96c-c527-5079-82ea-371a01645457-12345678-1234-' '5678-1234567890ab') @@ -1230,12 +1207,12 @@ class TestDeviceManager(base.BaseTestCase): uuid5.return_value = '1ae5f96c-c527-5079-82ea-371a01645457' get_host.return_value = 'localhost' - dh = dhcp_agent.DeviceManager(cfg.CONF, None) + dh = dhcp.DeviceManager(cfg.CONF, cfg.CONF.root_helper, None) uuid5.called_once_with(uuid.NAMESPACE_DNS, 'localhost') - self.assertEqual(dh.get_device_id(fake_network), expected) + self.assertEqual(dh.get_device_id(fake_net), expected) def _get_device_manager_with_mock_device(self, conf, device): - dh = dhcp_agent.DeviceManager(conf, None) + dh = dhcp.DeviceManager(conf, cfg.CONF.root_helper, None) dh._get_device = mock.Mock(return_value=device) return dh @@ -1243,7 +1220,7 @@ class TestDeviceManager(base.BaseTestCase): # Try with namespaces and no metadata network cfg.CONF.set_override('use_namespaces', True) cfg.CONF.set_override('enable_metadata_network', False) - dh = dhcp_agent.DeviceManager(cfg.CONF, None) + dh = dhcp.DeviceManager(cfg.CONF, cfg.CONF.root_helper, None) dh._set_default_route = mock.Mock() dh.update(True) @@ -1253,7 +1230,7 @@ class TestDeviceManager(base.BaseTestCase): # No namespaces, shouldn't set default route. cfg.CONF.set_override('use_namespaces', False) cfg.CONF.set_override('enable_metadata_network', False) - dh = dhcp_agent.DeviceManager(cfg.CONF, None) + dh = dhcp.DeviceManager(cfg.CONF, cfg.CONF.root_helper, None) dh._set_default_route = mock.Mock() dh.update(FakeV4Network()) @@ -1263,7 +1240,7 @@ class TestDeviceManager(base.BaseTestCase): # Meta data network enabled, don't interfere with its gateway. cfg.CONF.set_override('use_namespaces', True) cfg.CONF.set_override('enable_metadata_network', True) - dh = dhcp_agent.DeviceManager(cfg.CONF, None) + dh = dhcp.DeviceManager(cfg.CONF, cfg.CONF.root_helper, None) dh._set_default_route = mock.Mock() dh.update(FakeV4Network()) @@ -1273,7 +1250,7 @@ class TestDeviceManager(base.BaseTestCase): # For completeness cfg.CONF.set_override('use_namespaces', False) cfg.CONF.set_override('enable_metadata_network', True) - dh = dhcp_agent.DeviceManager(cfg.CONF, None) + dh = dhcp.DeviceManager(cfg.CONF, cfg.CONF.root_helper, None) dh._set_default_route = mock.Mock() dh.update(FakeV4Network()) @@ -1386,24 +1363,38 @@ class TestDictModel(base.BaseTestCase): def test_basic_dict(self): d = dict(a=1, b=2) - m = dhcp_agent.DictModel(d) + m = dhcp.DictModel(d) self.assertEqual(m.a, 1) self.assertEqual(m.b, 2) def test_dict_has_sub_dict(self): d = dict(a=dict(b=2)) - m = dhcp_agent.DictModel(d) + m = dhcp.DictModel(d) self.assertEqual(m.a.b, 2) def test_dict_contains_list(self): d = dict(a=[1, 2]) - m = dhcp_agent.DictModel(d) + m = dhcp.DictModel(d) self.assertEqual(m.a, [1, 2]) def test_dict_contains_list_of_dicts(self): d = dict(a=[dict(b=2), dict(c=3)]) - m = dhcp_agent.DictModel(d) + m = dhcp.DictModel(d) self.assertEqual(m.a[0].b, 2) self.assertEqual(m.a[1].c, 3) + + +class TestNetModel(base.BaseTestCase): + def test_ns_name(self): + network = dhcp.NetModel(True, {'id': 'foo'}) + self.assertEqual(network.namespace, 'qdhcp-foo') + + def test_ns_name_false_namespace(self): + network = dhcp.NetModel(False, {'id': 'foo'}) + self.assertIsNone(network.namespace) + + def test_ns_name_none_namespace(self): + network = dhcp.NetModel(None, {'id': 'foo'}) + self.assertIsNone(network.namespace) diff --git a/neutron/tests/unit/test_linux_dhcp.py b/neutron/tests/unit/test_linux_dhcp.py index a4471b409..6be5c954c 100644 --- a/neutron/tests/unit/test_linux_dhcp.py +++ b/neutron/tests/unit/test_linux_dhcp.py @@ -134,18 +134,21 @@ class FakeDualNetwork: id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' subnets = [FakeV4Subnet(), FakeV6Subnet()] ports = [FakePort1(), FakePort2(), FakePort3()] + namespace = 'qdhcp-ns' class FakeDualNetworkGatewayRoute: id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' subnets = [FakeV4SubnetGatewayRoute(), FakeV6Subnet()] ports = [FakePort1(), FakePort2(), FakePort3()] + namespace = 'qdhcp-ns' class FakeDualNetworkSingleDHCP: id = 'cccccccc-cccc-cccc-cccc-cccccccccccc' subnets = [FakeV4Subnet(), FakeV4SubnetNoDHCP()] ports = [FakePort1(), FakePort2(), FakePort3()] + namespace = 'qdhcp-ns' class FakeV4NoGatewayNetwork: @@ -154,46 +157,6 @@ class FakeV4NoGatewayNetwork: ports = [FakePort1()] -class TestDhcpBase(base.BaseTestCase): - def test_existing_dhcp_networks_abstract_error(self): - self.assertRaises(NotImplementedError, - dhcp.DhcpBase.existing_dhcp_networks, - None, None) - - def test_check_version_abstract_error(self): - self.assertRaises(NotImplementedError, - dhcp.DhcpBase.check_version) - - def test_base_abc_error(self): - self.assertRaises(TypeError, dhcp.DhcpBase, None) - - def test_restart(self): - class SubClass(dhcp.DhcpBase): - def __init__(self): - dhcp.DhcpBase.__init__(self, None, None, None) - self.called = [] - - def enable(self): - self.called.append('enable') - - def disable(self, retain_port=False): - self.called.append('disable %s' % retain_port) - - def reload_allocations(self): - pass - - def release_lease(self): - pass - - @property - def active(self): - return True - - c = SubClass() - c.restart() - self.assertEqual(c.called, ['disable True', 'enable']) - - class LocalChild(dhcp.DhcpLocalProcess): PORTS = {4: [4], 6: [6]} @@ -223,6 +186,9 @@ class TestBase(base.BaseTestCase): self.conf = config.setup_conf() self.conf.register_opts(base_config.core_opts) self.conf.register_opts(dhcp.OPTS) + instance = mock.patch("neutron.agent.linux.dhcp.DeviceManager") + self.mock_mgr = instance.start() + self.addCleanup(self.mock_mgr.stop) self.conf.register_opt(cfg.BoolOpt('enable_isolated_metadata', default=True)) self.conf(args=args) @@ -237,6 +203,47 @@ class TestBase(base.BaseTestCase): self.execute = self.execute_p.start() +class TestDhcpBase(TestBase): + + def test_existing_dhcp_networks_abstract_error(self): + self.assertRaises(NotImplementedError, + dhcp.DhcpBase.existing_dhcp_networks, + None, None) + + def test_check_version_abstract_error(self): + self.assertRaises(NotImplementedError, + dhcp.DhcpBase.check_version) + + def test_base_abc_error(self): + self.assertRaises(TypeError, dhcp.DhcpBase, None) + + def test_restart(self): + class SubClass(dhcp.DhcpBase): + def __init__(self): + dhcp.DhcpBase.__init__(self, cfg.CONF, FakeV4Network(), None) + self.called = [] + + def enable(self): + self.called.append('enable') + + def disable(self, retain_port=False): + self.called.append('disable %s' % retain_port) + + def reload_allocations(self): + pass + + def release_lease(self): + pass + + @property + def active(self): + return True + + c = SubClass() + c.restart() + self.assertEqual(c.called, ['disable True', 'enable']) + + class TestDhcpLocalProcess(TestBase): def test_active(self): dummy_cmd_line = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa' @@ -285,18 +292,14 @@ class TestDhcpLocalProcess(TestBase): self.assertTrue(makedirs.called) def test_enable_already_active(self): - delegate = mock.Mock() - delegate.setup.return_value = 'tap0' with mock.patch.object(LocalChild, 'active') as patched: patched.__get__ = mock.Mock(return_value=True) - lp = LocalChild(self.conf, FakeV4Network(), - device_delegate=delegate) + lp = LocalChild(self.conf, FakeV4Network()) lp.enable() self.assertEqual(lp.called, ['restart']) def test_enable(self): - delegate = mock.Mock(return_value='tap0') attrs_to_mock = dict( [(a, mock.DEFAULT) for a in ['active', 'get_conf_file_name', 'interface_name']] @@ -307,12 +310,12 @@ class TestDhcpLocalProcess(TestBase): mocks['get_conf_file_name'].return_value = '/dir' mocks['interface_name'].__set__ = mock.Mock() lp = LocalChild(self.conf, - FakeDualNetwork(), - device_delegate=delegate) + FakeDualNetwork()) lp.enable() - delegate.assert_has_calls( - [mock.call.setup(mock.ANY, reuse_existing=True)]) + self.mock_mgr.assert_has_calls( + [mock.call(self.conf, 'sudo', None), + mock.call().setup(mock.ANY, reuse_existing=True)]) self.assertEqual(lp.called, ['spawn']) self.assertTrue(mocks['interface_name'].__set__.called) @@ -345,34 +348,30 @@ class TestDhcpLocalProcess(TestBase): def test_disable_retain_port(self): attrs_to_mock = dict([(a, mock.DEFAULT) for a in ['active', 'interface_name', 'pid']]) - delegate = mock.Mock() network = FakeDualNetwork() with mock.patch.multiple(LocalChild, **attrs_to_mock) as mocks: mocks['active'].__get__ = mock.Mock(return_value=True) mocks['pid'].__get__ = mock.Mock(return_value=5) mocks['interface_name'].__get__ = mock.Mock(return_value='tap0') - lp = LocalChild(self.conf, network, device_delegate=delegate, - namespace='qdhcp-ns') + lp = LocalChild(self.conf, network) lp.disable(retain_port=True) - self.assertFalse(delegate.called) - exp_args = ['kill', '-9', 5] - self.execute.assert_called_once_with(exp_args, 'sudo') + exp_args = ['kill', '-9', 5] + self.execute.assert_called_once_with(exp_args, 'sudo') def test_disable(self): attrs_to_mock = dict([(a, mock.DEFAULT) for a in ['active', 'interface_name', 'pid']]) - delegate = mock.Mock() network = FakeDualNetwork() with mock.patch.multiple(LocalChild, **attrs_to_mock) as mocks: mocks['active'].__get__ = mock.Mock(return_value=True) mocks['pid'].__get__ = mock.Mock(return_value=5) mocks['interface_name'].__get__ = mock.Mock(return_value='tap0') - lp = LocalChild(self.conf, network, device_delegate=delegate, - namespace='qdhcp-ns') + lp = LocalChild(self.conf, network) lp.disable() - delegate.assert_has_calls([mock.call.destroy(network, 'tap0')]) + self.mock_mgr.assert_has_calls([mock.call(self.conf, 'sudo', None), + mock.call().destroy(network, 'tap0')]) exp_args = ['kill', '-9', 5] self.execute.assert_called_once_with(exp_args, 'sudo') @@ -451,8 +450,6 @@ class TestDnsmasq(TestBase): expected.extend(extra_options) self.execute.return_value = ('', '') - delegate = mock.Mock() - delegate.get_interface_name.return_value = 'tap0' attrs_to_mock = dict( [(a, mock.DEFAULT) for a in @@ -469,8 +466,6 @@ class TestDnsmasq(TestBase): with mock.patch.object(dhcp.sys, 'argv') as argv: argv.__getitem__.side_effect = fake_argv dm = dhcp.Dnsmasq(self.conf, FakeDualNetwork(), - device_delegate=delegate, - namespace='qdhcp-ns', version=float(2.59)) dm.spawn_process() self.assertTrue(mocks['_output_opts_file'].called) @@ -584,8 +579,7 @@ tag:tag0,option:router""".lstrip() self.safe.assert_called_once_with('/foo/opts', expected) def test_release_lease(self): - dm = dhcp.Dnsmasq(self.conf, FakeDualNetwork(), namespace='qdhcp-ns', - version=float(2.59)) + dm = dhcp.Dnsmasq(self.conf, FakeDualNetwork(), version=float(2.59)) dm.release_lease(mac_address=FakePort2.mac_address, removed_ips=[FakePort2.fixed_ips[0].ip_address]) exp_args = ['ip', 'netns', 'exec', 'qdhcp-ns', 'dhcp_release', @@ -628,7 +622,6 @@ tag:tag1,249,%s,%s""".lstrip() % (fake_v6, with mock.patch.object(dhcp.Dnsmasq, 'pid') as pid: pid.__get__ = mock.Mock(return_value=5) dm = dhcp.Dnsmasq(self.conf, FakeDualNetwork(), - namespace='qdhcp-ns', version=float(2.59)) method_name = '_make_subnet_interface_ip_map' @@ -675,7 +668,7 @@ tag:tag1,249,%s,%s""".lstrip() % (fake_v6, with mock.patch.object(dhcp.Dnsmasq, 'pid') as pid: pid.__get__ = mock.Mock(return_value=5) dm = dhcp.Dnsmasq(self.conf, FakeDualNetwork(), - namespace='qdhcp-ns', version=float(2.59)) + version=float(2.59)) method_name = '_make_subnet_interface_ip_map' with mock.patch.object(dhcp.Dnsmasq, method_name) as ip_map: @@ -694,8 +687,7 @@ tag:tag1,249,%s,%s""".lstrip() % (fake_v6, ] dm = dhcp.Dnsmasq(self.conf, - FakeDualNetwork(), - namespace='qdhcp-ns') + FakeDualNetwork()) self.assertEqual( dm._make_subnet_interface_ip_map(), diff --git a/neutron/tests/unit/test_linux_interface.py b/neutron/tests/unit/test_linux_interface.py index 3ca628d74..2b5eb4516 100644 --- a/neutron/tests/unit/test_linux_interface.py +++ b/neutron/tests/unit/test_linux_interface.py @@ -18,7 +18,7 @@ import mock from neutron.agent.common import config -from neutron.agent.dhcp_agent import DeviceManager +from neutron.agent.linux import dhcp from neutron.agent.linux import interface from neutron.agent.linux import ip_lib from neutron.agent.linux import utils @@ -330,7 +330,7 @@ class TestBridgeInterfaceDriver(TestBase): class TestMetaInterfaceDriver(TestBase): def setUp(self): super(TestMetaInterfaceDriver, self).setUp() - self.conf.register_opts(DeviceManager.OPTS) + 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.addCleanup(self.client_cls_p.stop)