Merge "Remove DHCP lease logic"
This commit is contained in:
commit
436e25f957
@ -1,20 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright (c) 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.
|
||||
|
||||
from neutron.agent.linux import dhcp
|
||||
dhcp.Dnsmasq.lease_update()
|
@ -69,7 +69,7 @@ lock_path = $state_path/lock
|
||||
# mac_generation_retries = 16
|
||||
|
||||
# DHCP Lease duration (in seconds)
|
||||
# dhcp_lease_duration = 120
|
||||
# dhcp_lease_duration = 86400
|
||||
|
||||
# Allow sending resource operation notification to DHCP agent
|
||||
# dhcp_agent_notification = True
|
||||
|
@ -9,7 +9,7 @@
|
||||
[Filters]
|
||||
|
||||
# dhcp-agent
|
||||
dnsmasq: EnvFilter, dnsmasq, root, NEUTRON_RELAY_SOCKET_PATH=, NEUTRON_NETWORK_ID=
|
||||
dnsmasq: EnvFilter, dnsmasq, root, NEUTRON_NETWORK_ID=
|
||||
# dhcp-agent uses kill as well, that's handled by the generic KillFilter
|
||||
# it looks like these are the only signals needed, per
|
||||
# neutron/agent/linux/dhcp.py
|
||||
@ -20,6 +20,7 @@ kill_dnsmasq_usr: KillFilter, root, /usr/sbin/dnsmasq, -9, -HUP
|
||||
cat: RegExpFilter, cat, root, cat, /proc/\d+/cmdline
|
||||
ovs-vsctl: CommandFilter, ovs-vsctl, root
|
||||
ivs-ctl: CommandFilter, ivs-ctl, root
|
||||
dhcp_release: CommandFilter, dhcp_release, root
|
||||
|
||||
# metadata proxy
|
||||
metadata_proxy: CommandFilter, neutron-ns-metadata-proxy, root
|
||||
|
@ -37,12 +37,10 @@ from neutron.common import utils
|
||||
from neutron import context
|
||||
from neutron import manager
|
||||
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 loopingcall
|
||||
from neutron.openstack.common.rpc import proxy
|
||||
from neutron.openstack.common import service
|
||||
from neutron.openstack.common import uuidutils
|
||||
from neutron import service as neutron_service
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -81,8 +79,10 @@ class DhcpAgent(manager.Manager):
|
||||
ctx = context.get_admin_context_without_session()
|
||||
self.plugin_rpc = DhcpPluginApi(topics.PLUGIN, ctx)
|
||||
self.device_manager = DeviceManager(self.conf, self.plugin_rpc)
|
||||
self.lease_relay = DhcpLeaseRelay(self.update_lease)
|
||||
|
||||
# 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()
|
||||
|
||||
@ -114,13 +114,12 @@ class DhcpAgent(manager.Manager):
|
||||
"""Activate the DHCP agent."""
|
||||
self.sync_state()
|
||||
self.periodic_resync()
|
||||
self.lease_relay.start()
|
||||
|
||||
def _ns_name(self, network):
|
||||
if self.conf.use_namespaces:
|
||||
return NS_PREFIX + network.id
|
||||
|
||||
def call_driver(self, action, network):
|
||||
def call_driver(self, action, network, **action_kwargs):
|
||||
"""Invoke an action on a DHCP driver instance."""
|
||||
try:
|
||||
# the Driver expects something that is duck typed similar to
|
||||
@ -131,21 +130,13 @@ class DhcpAgent(manager.Manager):
|
||||
self.device_manager,
|
||||
self._ns_name(network),
|
||||
self.dhcp_version)
|
||||
getattr(driver, action)()
|
||||
getattr(driver, action)(**action_kwargs)
|
||||
return True
|
||||
|
||||
except Exception:
|
||||
self.needs_resync = True
|
||||
LOG.exception(_('Unable to %s dhcp.'), action)
|
||||
|
||||
def update_lease(self, network_id, ip_address, time_remaining):
|
||||
try:
|
||||
self.plugin_rpc.update_lease_expiration(network_id, ip_address,
|
||||
time_remaining)
|
||||
except Exception:
|
||||
self.needs_resync = True
|
||||
LOG.exception(_('Unable to update lease'))
|
||||
|
||||
def sync_state(self):
|
||||
"""Sync the local DHCP state with Neutron."""
|
||||
LOG.info(_('Synchronizing state'))
|
||||
@ -246,6 +237,22 @@ class DhcpAgent(manager.Manager):
|
||||
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)
|
||||
if prev_port:
|
||||
previous_ips = set(fixed_ip.ip_address
|
||||
for fixed_ip in prev_port.fixed_ips)
|
||||
current_ips = set(fixed_ip.ip_address
|
||||
for fixed_ip in port.fixed_ips)
|
||||
# pass in port with removed ips on it
|
||||
removed_ips = previous_ips - current_ips
|
||||
if removed_ips:
|
||||
self.call_driver('release_lease',
|
||||
network,
|
||||
mac_address=port.mac_address,
|
||||
removed_ips=removed_ips)
|
||||
|
||||
@utils.synchronized('dhcp-agent')
|
||||
def network_create_end(self, context, payload):
|
||||
"""Handle the network.create.end notification event."""
|
||||
@ -289,6 +296,7 @@ class DhcpAgent(manager.Manager):
|
||||
port = DictModel(payload['port'])
|
||||
network = self.cache.get_network_by_id(port.network_id)
|
||||
if network:
|
||||
self.release_lease_for_removed_ips(port, network)
|
||||
self.cache.put_port(port)
|
||||
self.call_driver('reload_allocations', network)
|
||||
|
||||
@ -302,6 +310,12 @@ class DhcpAgent(manager.Manager):
|
||||
if port:
|
||||
network = self.cache.get_network_by_id(port.network_id)
|
||||
self.cache.remove_port(port)
|
||||
removed_ips = [fixed_ip.ip_address
|
||||
for fixed_ip in port.fixed_ips]
|
||||
self.call_driver('release_lease',
|
||||
network,
|
||||
mac_address=port.mac_address,
|
||||
removed_ips=removed_ips)
|
||||
self.call_driver('reload_allocations', network)
|
||||
|
||||
def enable_isolated_metadata_proxy(self, network):
|
||||
@ -435,16 +449,6 @@ class DhcpPluginApi(proxy.RpcProxy):
|
||||
host=self.host),
|
||||
topic=self.topic)
|
||||
|
||||
def update_lease_expiration(self, network_id, ip_address, lease_remaining):
|
||||
"""Make a remote process call to update the ip lease expiration."""
|
||||
self.cast(self.context,
|
||||
self.make_msg('update_lease_expiration',
|
||||
network_id=network_id,
|
||||
ip_address=ip_address,
|
||||
lease_remaining=lease_remaining,
|
||||
host=self.host),
|
||||
topic=self.topic)
|
||||
|
||||
|
||||
class NetworkCache(object):
|
||||
"""Agent cache of the current network state."""
|
||||
@ -747,67 +751,6 @@ class DictModel(object):
|
||||
setattr(self, key, value)
|
||||
|
||||
|
||||
class DhcpLeaseRelay(object):
|
||||
"""UNIX domain socket server for processing lease updates.
|
||||
|
||||
Network namespace isolation prevents the DHCP process from notifying
|
||||
Neutron directly. This class works around the limitation by using the
|
||||
domain socket to pass the information. This class handles message.
|
||||
receiving and then calls the callback method.
|
||||
"""
|
||||
|
||||
OPTS = [
|
||||
cfg.StrOpt('dhcp_lease_relay_socket',
|
||||
default='$state_path/dhcp/lease_relay',
|
||||
help=_('Location to DHCP lease relay UNIX domain socket'))
|
||||
]
|
||||
|
||||
def __init__(self, lease_update_callback):
|
||||
self.callback = lease_update_callback
|
||||
|
||||
dirname = os.path.dirname(cfg.CONF.dhcp_lease_relay_socket)
|
||||
if os.path.isdir(dirname):
|
||||
try:
|
||||
os.unlink(cfg.CONF.dhcp_lease_relay_socket)
|
||||
except OSError:
|
||||
if os.path.exists(cfg.CONF.dhcp_lease_relay_socket):
|
||||
raise
|
||||
else:
|
||||
os.makedirs(dirname, 0o755)
|
||||
|
||||
def _handler(self, client_sock, client_addr):
|
||||
"""Handle incoming lease relay stream connection.
|
||||
|
||||
This method will only read the first 1024 bytes and then close the
|
||||
connection. The limit exists to limit the impact of misbehaving
|
||||
clients.
|
||||
"""
|
||||
try:
|
||||
msg = client_sock.recv(1024)
|
||||
data = jsonutils.loads(msg)
|
||||
client_sock.close()
|
||||
|
||||
network_id = data['network_id']
|
||||
if not uuidutils.is_uuid_like(network_id):
|
||||
raise ValueError(_("Network ID %s is not a valid UUID") %
|
||||
network_id)
|
||||
ip_address = str(netaddr.IPAddress(data['ip_address']))
|
||||
lease_remaining = int(data['lease_remaining'])
|
||||
self.callback(network_id, ip_address, lease_remaining)
|
||||
except ValueError as e:
|
||||
LOG.warn(_('Unable to parse lease relay msg to dict.'))
|
||||
LOG.warn(_('Exception value: %s'), e)
|
||||
LOG.warn(_('Message representation: %s'), repr(msg))
|
||||
except Exception as e:
|
||||
LOG.exception(_('Unable update lease. Exception'))
|
||||
|
||||
def start(self):
|
||||
"""Spawn a green thread to run the lease relay unix socket server."""
|
||||
listener = eventlet.listen(cfg.CONF.dhcp_lease_relay_socket,
|
||||
family=socket.AF_UNIX)
|
||||
eventlet.spawn(eventlet.serve, listener, self._handler)
|
||||
|
||||
|
||||
class DhcpAgentWithStateReport(DhcpAgent):
|
||||
def __init__(self, host=None):
|
||||
super(DhcpAgentWithStateReport, self).__init__(host=host)
|
||||
@ -863,7 +806,6 @@ def register_options():
|
||||
config.register_agent_state_opts_helper(cfg.CONF)
|
||||
config.register_root_helper(cfg.CONF)
|
||||
cfg.CONF.register_opts(DeviceManager.OPTS)
|
||||
cfg.CONF.register_opts(DhcpLeaseRelay.OPTS)
|
||||
cfg.CONF.register_opts(dhcp.OPTS)
|
||||
cfg.CONF.register_opts(interface.OPTS)
|
||||
|
||||
|
@ -89,6 +89,10 @@ class DhcpBase(object):
|
||||
def active(self):
|
||||
"""Boolean representing the running state of the DHCP server."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def release_lease(self, mac_address, removed_ips):
|
||||
"""Release a DHCP lease."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def reload_allocations(self):
|
||||
"""Force the DHCP server to reload the assignment database."""
|
||||
@ -261,8 +265,6 @@ class Dnsmasq(DhcpLocalProcess):
|
||||
"""Spawns a Dnsmasq process for the network."""
|
||||
env = {
|
||||
self.NEUTRON_NETWORK_ID_KEY: self.network.id,
|
||||
self.NEUTRON_RELAY_SOCKET_PATH_KEY:
|
||||
self.conf.dhcp_lease_relay_socket
|
||||
}
|
||||
|
||||
cmd = [
|
||||
@ -279,7 +281,6 @@ class Dnsmasq(DhcpLocalProcess):
|
||||
#'--dhcp-lease-max=%s' % ?,
|
||||
'--dhcp-hostsfile=%s' % self._output_hosts_file(),
|
||||
'--dhcp-optsfile=%s' % self._output_opts_file(),
|
||||
'--dhcp-script=%s' % self._lease_relay_script_path(),
|
||||
'--leasefile-ro',
|
||||
]
|
||||
|
||||
@ -318,6 +319,16 @@ class Dnsmasq(DhcpLocalProcess):
|
||||
cmd = ['%s=%s' % pair for pair in env.items()] + cmd
|
||||
utils.execute(cmd, self.root_helper)
|
||||
|
||||
def release_lease(self, mac_address, removed_ips):
|
||||
"""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)
|
||||
ip_wrapper.netns.execute(cmd)
|
||||
else:
|
||||
utils.execute(cmd, self.root_helper)
|
||||
|
||||
def reload_allocations(self):
|
||||
"""Rebuild the dnsmasq config and signal the dnsmasq to reload."""
|
||||
|
||||
@ -428,10 +439,6 @@ class Dnsmasq(DhcpLocalProcess):
|
||||
|
||||
return retval
|
||||
|
||||
def _lease_relay_script_path(self):
|
||||
return os.path.join(os.path.dirname(sys.argv[0]),
|
||||
'neutron-dhcp-agent-dnsmasq-lease-update')
|
||||
|
||||
def _format_option(self, index, option, *args):
|
||||
"""Format DHCP option by option name or code."""
|
||||
if self.version >= self.MINIMUM_VERSION:
|
||||
|
@ -71,7 +71,7 @@ core_opts = [
|
||||
help=_("Maximum number of host routes per subnet")),
|
||||
cfg.IntOpt('max_fixed_ips_per_port', default=5,
|
||||
help=_("Maximum number of fixed ips per port")),
|
||||
cfg.IntOpt('dhcp_lease_duration', default=120,
|
||||
cfg.IntOpt('dhcp_lease_duration', default=86400,
|
||||
deprecated_name='dhcp_lease_time',
|
||||
help=_("DHCP lease duration")),
|
||||
cfg.BoolOpt('dhcp_agent_notification', default=True,
|
||||
|
@ -293,54 +293,6 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
|
||||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def _hold_ip(context, network_id, subnet_id, port_id, ip_address):
|
||||
alloc_qry = context.session.query(
|
||||
models_v2.IPAllocation).with_lockmode('update')
|
||||
allocated = alloc_qry.filter_by(network_id=network_id,
|
||||
port_id=port_id,
|
||||
ip_address=ip_address,
|
||||
subnet_id=subnet_id).one()
|
||||
|
||||
if not allocated:
|
||||
return
|
||||
if allocated.expiration < timeutils.utcnow():
|
||||
# immediately delete expired allocations
|
||||
NeutronDbPluginV2._recycle_ip(
|
||||
context, network_id, subnet_id, ip_address)
|
||||
else:
|
||||
LOG.debug(_("Hold allocated IP %(ip_address)s "
|
||||
"(%(network_id)s/%(subnet_id)s/%(port_id)s)"),
|
||||
{'ip_address': ip_address,
|
||||
'network_id': network_id,
|
||||
'subnet_id': subnet_id,
|
||||
'port_id': port_id})
|
||||
allocated.port_id = None
|
||||
|
||||
@staticmethod
|
||||
def _recycle_expired_ip_allocations(context, network_id):
|
||||
"""Return held ip allocations with expired leases back to the pool."""
|
||||
if network_id in getattr(context, '_recycled_networks', set()):
|
||||
return
|
||||
|
||||
expired_qry = context.session.query(
|
||||
models_v2.IPAllocation).with_lockmode('update')
|
||||
expired_qry = expired_qry.filter_by(network_id=network_id,
|
||||
port_id=None)
|
||||
expired_qry = expired_qry.filter(
|
||||
models_v2.IPAllocation.expiration <= timeutils.utcnow())
|
||||
|
||||
for expired in expired_qry:
|
||||
NeutronDbPluginV2._recycle_ip(context,
|
||||
network_id,
|
||||
expired['subnet_id'],
|
||||
expired['ip_address'])
|
||||
|
||||
if hasattr(context, '_recycled_networks'):
|
||||
context._recycled_networks.add(network_id)
|
||||
else:
|
||||
context._recycled_networks = set([network_id])
|
||||
|
||||
@staticmethod
|
||||
def _recycle_ip(context, network_id, subnet_id, ip_address):
|
||||
"""Return an IP address to the pool of free IP's on the network
|
||||
@ -424,11 +376,6 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
|
||||
NeutronDbPluginV2._delete_ip_allocation(context, network_id, subnet_id,
|
||||
ip_address)
|
||||
|
||||
@staticmethod
|
||||
def _default_allocation_expiration():
|
||||
return (timeutils.utcnow() +
|
||||
datetime.timedelta(seconds=cfg.CONF.dhcp_lease_duration))
|
||||
|
||||
def update_fixed_ip_lease_expiration(self, context, network_id,
|
||||
ip_address, lease_remaining):
|
||||
|
||||
@ -690,11 +637,10 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
|
||||
to_add = self._test_fixed_ips_for_port(context, network_id, new_ips)
|
||||
for ip in original_ips:
|
||||
LOG.debug(_("Port update. Hold %s"), ip)
|
||||
NeutronDbPluginV2._hold_ip(context,
|
||||
network_id,
|
||||
ip['subnet_id'],
|
||||
port_id,
|
||||
ip['ip_address'])
|
||||
NeutronDbPluginV2._recycle_ip(context,
|
||||
network_id,
|
||||
ip['subnet_id'],
|
||||
ip['ip_address'])
|
||||
|
||||
if to_add:
|
||||
LOG.debug(_("Port update. Adding %s"), to_add)
|
||||
@ -1321,7 +1267,6 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
|
||||
tenant_id = self._get_tenant_id_for_create(context, p)
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
self._recycle_expired_ip_allocations(context, network_id)
|
||||
network = self._get_network(context, network_id)
|
||||
|
||||
# Ensure that a MAC address is defined and it is unique on the
|
||||
@ -1372,7 +1317,6 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
|
||||
port_id=port_id,
|
||||
ip_address=ip_address,
|
||||
subnet_id=subnet_id,
|
||||
expiration=self._default_allocation_expiration()
|
||||
)
|
||||
context.session.add(allocated)
|
||||
|
||||
@ -1387,8 +1331,6 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
|
||||
# Check if the IPs need to be updated
|
||||
if 'fixed_ips' in p:
|
||||
changed_ips = True
|
||||
self._recycle_expired_ip_allocations(context,
|
||||
port['network_id'])
|
||||
original = self._make_port_dict(port, process_extensions=False)
|
||||
added_ips, prev_ips = self._update_ips_for_port(
|
||||
context, port["network_id"], id, original["fixed_ips"],
|
||||
@ -1398,8 +1340,7 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
|
||||
for ip in added_ips:
|
||||
allocated = models_v2.IPAllocation(
|
||||
network_id=port['network_id'], port_id=port.id,
|
||||
ip_address=ip['ip_address'], subnet_id=ip['subnet_id'],
|
||||
expiration=self._default_allocation_expiration())
|
||||
ip_address=ip['ip_address'], subnet_id=ip['subnet_id'])
|
||||
context.session.add(allocated)
|
||||
# Remove all attributes in p which are not in the port DB model
|
||||
# and then update the port
|
||||
@ -1428,11 +1369,10 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
|
||||
if NeutronDbPluginV2._check_ip_in_allocation_pool(
|
||||
context, a['subnet_id'], subnet['gateway_ip'],
|
||||
a['ip_address']):
|
||||
NeutronDbPluginV2._hold_ip(context,
|
||||
a['network_id'],
|
||||
a['subnet_id'],
|
||||
id,
|
||||
a['ip_address'])
|
||||
NeutronDbPluginV2._recycle_ip(context,
|
||||
a['network_id'],
|
||||
a['subnet_id'],
|
||||
a['ip_address'])
|
||||
else:
|
||||
# IPs out of allocation pool will not be recycled, but
|
||||
# we do need to delete the allocation from the DB
|
||||
|
@ -47,8 +47,8 @@ class DhcpRpcCallbackMixin(object):
|
||||
def get_active_networks(self, context, **kwargs):
|
||||
"""Retrieve and return a list of the active network ids."""
|
||||
# NOTE(arosen): This method is no longer used by the DHCP agent but is
|
||||
# left so that quantum-dhcp-agents will still continue to work if
|
||||
# quantum-server is upgraded and not the agent.
|
||||
# left so that neutron-dhcp-agents will still continue to work if
|
||||
# neutron-server is upgraded and not the agent.
|
||||
host = kwargs.get('host')
|
||||
LOG.debug(_('get_active_networks requested from %s'), host)
|
||||
nets = self._get_active_networks(context, **kwargs)
|
||||
@ -97,8 +97,8 @@ class DhcpRpcCallbackMixin(object):
|
||||
|
||||
"""
|
||||
# NOTE(arosen): This method is no longer used by the DHCP agent but is
|
||||
# left so that quantum-dhcp-agents will still continue to work if
|
||||
# quantum-server is upgraded and not the agent.
|
||||
# left so that neutron-dhcp-agents will still continue to work if
|
||||
# neutron-server is upgraded and not the agent.
|
||||
|
||||
host = kwargs.get('host')
|
||||
network_id = kwargs.get('network_id')
|
||||
@ -209,20 +209,13 @@ class DhcpRpcCallbackMixin(object):
|
||||
|
||||
def update_lease_expiration(self, context, **kwargs):
|
||||
"""Release the fixed_ip associated the subnet on a port."""
|
||||
# NOTE(arosen): This method is no longer used by the DHCP agent but is
|
||||
# left so that neutron-dhcp-agents will still continue to work if
|
||||
# neutron-server is upgraded and not the agent.
|
||||
host = kwargs.get('host')
|
||||
network_id = kwargs.get('network_id')
|
||||
ip_address = kwargs.get('ip_address')
|
||||
lease_remaining = kwargs.get('lease_remaining')
|
||||
|
||||
LOG.debug(_('Updating lease expiration for %(ip_address)s on network '
|
||||
'%(network_id)s from %(host)s.'),
|
||||
{'ip_address': ip_address,
|
||||
'network_id': network_id,
|
||||
'host': host})
|
||||
plugin = manager.NeutronManager.get_plugin()
|
||||
|
||||
plugin.update_fixed_ip_lease_expiration(context, network_id,
|
||||
ip_address, lease_remaining)
|
||||
LOG.warning(_('Updating lease expiration is now deprecated. Issued '
|
||||
'from host %(host)s.') % host)
|
||||
|
||||
def create_dhcp_port(self, context, **kwargs):
|
||||
"""Create the dhcp port."""
|
||||
|
@ -0,0 +1,46 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright 2013 OpenStack Foundation
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
"""remove_dhcp_lease
|
||||
|
||||
Revision ID: f9263d6df56
|
||||
Revises: c88b6b5fea3
|
||||
Create Date: 2013-07-17 12:31:33.731197
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'f9263d6df56'
|
||||
down_revision = 'c88b6b5fea3'
|
||||
|
||||
# Change to ['*'] if this migration applies to all plugins
|
||||
|
||||
migration_for_plugins = [
|
||||
'*'
|
||||
]
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade(active_plugins=None, options=None):
|
||||
op.drop_column('ipallocations', u'expiration')
|
||||
|
||||
|
||||
def downgrade(active_plugins=None, options=None):
|
||||
op.add_column('ipallocations', sa.Column(u'expiration', sa.DATETIME(),
|
||||
nullable=True))
|
@ -99,7 +99,6 @@ class IPAllocation(model_base.BASEV2):
|
||||
network_id = sa.Column(sa.String(36), sa.ForeignKey("networks.id",
|
||||
ondelete="CASCADE"),
|
||||
nullable=False, primary_key=True)
|
||||
expiration = sa.Column(sa.DateTime, nullable=True)
|
||||
|
||||
|
||||
class Route(object):
|
||||
|
@ -41,6 +41,6 @@ class ConfigurationTest(base.BaseTestCase):
|
||||
'..', '..', '..')
|
||||
absolute_dir = os.path.abspath(relative_dir)
|
||||
self.assertEqual(absolute_dir, cfg.CONF.state_path)
|
||||
self.assertEqual(120, cfg.CONF.dhcp_lease_duration)
|
||||
self.assertEqual(86400, cfg.CONF.dhcp_lease_duration)
|
||||
self.assertFalse(cfg.CONF.allow_overlapping_ips)
|
||||
self.assertEqual('neutron', cfg.CONF.control_exchange)
|
||||
|
@ -1191,7 +1191,7 @@ fixed_ips=ip_address%%3D%s&fixed_ips=ip_address%%3D%s&fixed_ips=subnet_id%%3D%s
|
||||
self.assertEqual(ips[1]['subnet_id'], subnet['subnet']['id'])
|
||||
|
||||
def test_update_port_update_ips(self):
|
||||
"""Update IP and generate new IP on port.
|
||||
"""Update IP and associate new IP on port.
|
||||
|
||||
Check a port update with the specified subnet_id's. A IP address
|
||||
will be allocated for each subnet_id.
|
||||
@ -1200,7 +1200,8 @@ fixed_ips=ip_address%%3D%s&fixed_ips=ip_address%%3D%s&fixed_ips=subnet_id%%3D%s
|
||||
with self.port(subnet=subnet) as port:
|
||||
data = {'port': {'admin_state_up': False,
|
||||
'fixed_ips': [{'subnet_id':
|
||||
subnet['subnet']['id']}]}}
|
||||
subnet['subnet']['id'],
|
||||
'ip_address': '10.0.0.3'}]}}
|
||||
req = self.new_update_request('ports', data,
|
||||
port['port']['id'])
|
||||
res = self.deserialize(self.fmt, req.get_response(self.api))
|
||||
@ -1227,9 +1228,9 @@ fixed_ips=ip_address%%3D%s&fixed_ips=ip_address%%3D%s&fixed_ips=subnet_id%%3D%s
|
||||
data['port']['admin_state_up'])
|
||||
ips = res['port']['fixed_ips']
|
||||
self.assertEqual(len(ips), 2)
|
||||
self.assertEqual(ips[0]['ip_address'], '10.0.0.3')
|
||||
self.assertEqual(ips[0]['ip_address'], '10.0.0.2')
|
||||
self.assertEqual(ips[0]['subnet_id'], subnet['subnet']['id'])
|
||||
self.assertEqual(ips[1]['ip_address'], '10.0.0.4')
|
||||
self.assertEqual(ips[1]['ip_address'], '10.0.0.3')
|
||||
self.assertEqual(ips[1]['subnet_id'], subnet['subnet']['id'])
|
||||
|
||||
def test_requested_duplicate_mac(self):
|
||||
@ -1634,57 +1635,6 @@ fixed_ips=ip_address%%3D%s&fixed_ips=ip_address%%3D%s&fixed_ips=subnet_id%%3D%s
|
||||
res = port_req.get_response(self.api)
|
||||
self.assertEqual(res.status_int, 400)
|
||||
|
||||
def test_default_allocation_expiration(self):
|
||||
cfg.CONF.set_override('dhcp_lease_duration', 120)
|
||||
reference = datetime.datetime(2012, 8, 13, 23, 11, 0)
|
||||
|
||||
with mock.patch.object(timeutils, 'utcnow') as mock_utcnow:
|
||||
mock_utcnow.return_value = reference
|
||||
|
||||
plugin = NeutronManager.get_plugin()
|
||||
expires = plugin._default_allocation_expiration()
|
||||
self.assertEqual(expires,
|
||||
reference + datetime.timedelta(seconds=120))
|
||||
|
||||
def test_update_fixed_ip_lease_expiration(self):
|
||||
cfg.CONF.set_override('dhcp_lease_duration', 10)
|
||||
plugin = NeutronManager.get_plugin()
|
||||
with self.subnet() as subnet:
|
||||
with self.port(subnet=subnet) as port:
|
||||
update_context = context.Context('', port['port']['tenant_id'])
|
||||
plugin.update_fixed_ip_lease_expiration(
|
||||
update_context,
|
||||
subnet['subnet']['network_id'],
|
||||
port['port']['fixed_ips'][0]['ip_address'],
|
||||
500)
|
||||
|
||||
q = update_context.session.query(models_v2.IPAllocation)
|
||||
q = q.filter_by(
|
||||
port_id=port['port']['id'],
|
||||
ip_address=port['port']['fixed_ips'][0]['ip_address'])
|
||||
|
||||
ip_allocation = q.one()
|
||||
|
||||
self.assertThat(
|
||||
ip_allocation.expiration - timeutils.utcnow(),
|
||||
matchers.GreaterThan(datetime.timedelta(seconds=10)))
|
||||
|
||||
def test_port_delete_holds_ip(self):
|
||||
base_class = db_base_plugin_v2.NeutronDbPluginV2
|
||||
with mock.patch.object(base_class, '_hold_ip') as hold_ip:
|
||||
with self.subnet() as subnet:
|
||||
with self.port(subnet=subnet, no_delete=True) as port:
|
||||
req = self.new_delete_request('ports', port['port']['id'])
|
||||
res = req.get_response(self.api)
|
||||
self.assertEqual(res.status_int, 204)
|
||||
|
||||
hold_ip.assert_called_once_with(
|
||||
mock.ANY,
|
||||
port['port']['network_id'],
|
||||
port['port']['fixed_ips'][0]['subnet_id'],
|
||||
port['port']['id'],
|
||||
port['port']['fixed_ips'][0]['ip_address'])
|
||||
|
||||
def test_update_fixed_ip_lease_expiration_invalid_address(self):
|
||||
cfg.CONF.set_override('dhcp_lease_duration', 10)
|
||||
plugin = NeutronManager.get_plugin()
|
||||
@ -1699,27 +1649,6 @@ fixed_ips=ip_address%%3D%s&fixed_ips=ip_address%%3D%s&fixed_ips=subnet_id%%3D%s
|
||||
120)
|
||||
self.assertTrue(log.mock_calls)
|
||||
|
||||
def test_hold_ip_address(self):
|
||||
plugin = NeutronManager.get_plugin()
|
||||
with self.subnet() as subnet:
|
||||
with self.port(subnet=subnet) as port:
|
||||
update_context = context.Context('', port['port']['tenant_id'])
|
||||
port_id = port['port']['id']
|
||||
with mock.patch.object(db_base_plugin_v2, 'LOG') as log:
|
||||
ip_address = port['port']['fixed_ips'][0]['ip_address']
|
||||
plugin._hold_ip(
|
||||
update_context,
|
||||
subnet['subnet']['network_id'],
|
||||
subnet['subnet']['id'],
|
||||
port_id,
|
||||
ip_address)
|
||||
self.assertTrue(log.mock_calls)
|
||||
|
||||
q = update_context.session.query(models_v2.IPAllocation)
|
||||
q = q.filter_by(port_id=None, ip_address=ip_address)
|
||||
|
||||
self.assertEqual(q.count(), 1)
|
||||
|
||||
def test_recycle_ip_address_without_allocation_pool(self):
|
||||
plugin = NeutronManager.get_plugin()
|
||||
allocation_pools = [{"start": '10.0.0.10',
|
||||
@ -1742,47 +1671,6 @@ fixed_ips=ip_address%%3D%s&fixed_ips=ip_address%%3D%s&fixed_ips=subnet_id%%3D%s
|
||||
q = q.filter_by(subnet_id=subnet_id)
|
||||
self.assertEqual(q.count(), 0)
|
||||
|
||||
def test_recycle_held_ip_address(self):
|
||||
plugin = NeutronManager.get_plugin()
|
||||
with self.subnet() as subnet:
|
||||
with self.port(subnet=subnet) as port:
|
||||
update_context = context.Context('', port['port']['tenant_id'])
|
||||
port_id = port['port']['id']
|
||||
port_obj = plugin._get_port(update_context, port_id)
|
||||
|
||||
for fixed_ip in port_obj.fixed_ips:
|
||||
fixed_ip.active = False
|
||||
fixed_ip.expiration = datetime.datetime.utcnow()
|
||||
|
||||
with mock.patch.object(plugin, '_recycle_ip') as rc:
|
||||
plugin._recycle_expired_ip_allocations(
|
||||
update_context, subnet['subnet']['network_id'])
|
||||
rc.assertEqual(len(rc.mock_calls), 1)
|
||||
self.assertEqual(update_context._recycled_networks,
|
||||
set([subnet['subnet']['network_id']]))
|
||||
|
||||
def test_recycle_expired_previously_run_within_context(self):
|
||||
plugin = NeutronManager.get_plugin()
|
||||
with self.subnet() as subnet:
|
||||
with self.port(subnet=subnet) as port:
|
||||
update_context = context.Context('', port['port']['tenant_id'])
|
||||
port_id = port['port']['id']
|
||||
port_obj = plugin._get_port(update_context, port_id)
|
||||
|
||||
update_context._recycled_networks = set(
|
||||
[subnet['subnet']['network_id']])
|
||||
|
||||
for fixed_ip in port_obj.fixed_ips:
|
||||
fixed_ip.active = False
|
||||
fixed_ip.expiration = datetime.datetime.utcnow()
|
||||
|
||||
with mock.patch.object(plugin, '_recycle_ip') as rc:
|
||||
plugin._recycle_expired_ip_allocations(
|
||||
update_context, subnet['subnet']['network_id'])
|
||||
rc.assertFalse(rc.called)
|
||||
self.assertEqual(update_context._recycled_networks,
|
||||
set([subnet['subnet']['network_id']]))
|
||||
|
||||
def test_max_fixed_ips_exceeded(self):
|
||||
with self.subnet(gateway_ip='10.0.0.3',
|
||||
cidr='10.0.0.0/24') as subnet:
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
import copy
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
import uuid
|
||||
|
||||
@ -33,7 +32,6 @@ from neutron.agent.linux import dhcp
|
||||
from neutron.agent.linux import interface
|
||||
from neutron.common import constants
|
||||
from neutron.common import exceptions
|
||||
from neutron.openstack.common import jsonutils
|
||||
from neutron.tests import base
|
||||
|
||||
|
||||
@ -99,7 +97,8 @@ fake_port1 = FakeModel('12345678-1234-aaaa-1234567890ab',
|
||||
|
||||
fake_port2 = FakeModel('12345678-1234-aaaa-123456789000',
|
||||
mac_address='aa:bb:cc:dd:ee:99',
|
||||
network_id='12345678-1234-5678-1234567890ab')
|
||||
network_id='12345678-1234-5678-1234567890ab',
|
||||
fixed_ips=[])
|
||||
|
||||
fake_meta_port = FakeModel('12345678-1234-aaaa-1234567890ab',
|
||||
mac_address='aa:bb:cc:dd:ee:ff',
|
||||
@ -147,7 +146,6 @@ class TestDhcpAgent(base.BaseTestCase):
|
||||
|
||||
def test_dhcp_agent_manager(self):
|
||||
state_rpc_str = 'neutron.agent.rpc.PluginReportStateAPI'
|
||||
lease_relay_str = 'neutron.agent.dhcp_agent.DhcpLeaseRelay'
|
||||
with mock.patch.object(DhcpAgentWithStateReport,
|
||||
'sync_state',
|
||||
autospec=True) as mock_sync_state:
|
||||
@ -155,34 +153,27 @@ class TestDhcpAgent(base.BaseTestCase):
|
||||
'periodic_resync',
|
||||
autospec=True) as mock_periodic_resync:
|
||||
with mock.patch(state_rpc_str) as state_rpc:
|
||||
with mock.patch(lease_relay_str) as mock_lease_relay:
|
||||
with mock.patch.object(sys, 'argv') as sys_argv:
|
||||
sys_argv.return_value = [
|
||||
'dhcp', '--config-file',
|
||||
etcdir('neutron.conf.test')]
|
||||
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_agent.DhcpLeaseRelay.OPTS)
|
||||
cfg.CONF.register_opts(dhcp.OPTS)
|
||||
cfg.CONF.register_opts(interface.OPTS)
|
||||
cfg.CONF(project='neutron')
|
||||
agent_mgr = DhcpAgentWithStateReport('testhost')
|
||||
eventlet.greenthread.sleep(1)
|
||||
agent_mgr.after_start()
|
||||
mock_sync_state.assert_called_once_with(agent_mgr)
|
||||
mock_periodic_resync.assert_called_once_with(
|
||||
agent_mgr)
|
||||
state_rpc.assert_has_calls(
|
||||
[mock.call(mock.ANY),
|
||||
mock.call().report_state(mock.ANY, mock.ANY,
|
||||
mock.ANY)])
|
||||
mock_lease_relay.assert_has_calls(
|
||||
[mock.call(mock.ANY),
|
||||
mock.call().start()])
|
||||
with mock.patch.object(sys, 'argv') as sys_argv:
|
||||
sys_argv.return_value = [
|
||||
'dhcp', '--config-file',
|
||||
etcdir('neutron.conf.test')]
|
||||
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')
|
||||
agent_mgr = DhcpAgentWithStateReport('testhost')
|
||||
eventlet.greenthread.sleep(1)
|
||||
agent_mgr.after_start()
|
||||
mock_sync_state.assert_called_once_with(agent_mgr)
|
||||
mock_periodic_resync.assert_called_once_with(agent_mgr)
|
||||
state_rpc.assert_has_calls(
|
||||
[mock.call(mock.ANY),
|
||||
mock.call().report_state(mock.ANY, mock.ANY,
|
||||
mock.ANY)])
|
||||
|
||||
def test_dhcp_agent_main_agent_manager(self):
|
||||
logging_str = 'neutron.agent.common.config.setup_logging'
|
||||
@ -202,13 +193,11 @@ class TestDhcpAgent(base.BaseTestCase):
|
||||
dhcp = dhcp_agent.DhcpAgent(HOSTNAME)
|
||||
attrs_to_mock = dict(
|
||||
[(a, mock.DEFAULT) for a in
|
||||
['sync_state', 'lease_relay', 'periodic_resync']])
|
||||
['sync_state', 'periodic_resync']])
|
||||
with mock.patch.multiple(dhcp, **attrs_to_mock) as mocks:
|
||||
dhcp.run()
|
||||
mocks['sync_state'].assert_called_once_with()
|
||||
mocks['periodic_resync'].assert_called_once_with()
|
||||
mocks['lease_relay'].assert_has_mock_calls(
|
||||
[mock.call.start()])
|
||||
|
||||
def test_ns_name(self):
|
||||
with mock.patch('neutron.agent.dhcp_agent.DeviceManager'):
|
||||
@ -255,28 +244,6 @@ class TestDhcpAgent(base.BaseTestCase):
|
||||
self.assertEqual(log.call_count, 1)
|
||||
self.assertTrue(dhcp.needs_resync)
|
||||
|
||||
def test_update_lease(self):
|
||||
with mock.patch('neutron.agent.dhcp_agent.DhcpPluginApi') as plug:
|
||||
dhcp = dhcp_agent.DhcpAgent(HOSTNAME)
|
||||
dhcp.update_lease('net_id', '192.168.1.1', 120)
|
||||
plug.assert_has_calls(
|
||||
[mock.call().update_lease_expiration(
|
||||
'net_id', '192.168.1.1', 120)])
|
||||
|
||||
def test_update_lease_failure(self):
|
||||
with mock.patch('neutron.agent.dhcp_agent.DhcpPluginApi') as plug:
|
||||
plug.return_value.update_lease_expiration.side_effect = Exception
|
||||
|
||||
with mock.patch.object(dhcp_agent.LOG, 'exception') as log:
|
||||
dhcp = dhcp_agent.DhcpAgent(HOSTNAME)
|
||||
dhcp.update_lease('net_id', '192.168.1.1', 120)
|
||||
plug.assert_has_calls(
|
||||
[mock.call().update_lease_expiration(
|
||||
'net_id', '192.168.1.1', 120)])
|
||||
|
||||
self.assertTrue(log.called)
|
||||
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:
|
||||
mock_plugin = mock.Mock()
|
||||
@ -425,7 +392,6 @@ class TestDhcpAgentEventHandler(base.BaseTestCase):
|
||||
def setUp(self):
|
||||
super(TestDhcpAgentEventHandler, self).setUp()
|
||||
cfg.CONF.register_opts(dhcp_agent.DeviceManager.OPTS)
|
||||
cfg.CONF.register_opts(dhcp_agent.DhcpLeaseRelay.OPTS)
|
||||
cfg.CONF.register_opts(dhcp.OPTS)
|
||||
cfg.CONF.set_override('interface_driver',
|
||||
'neutron.agent.linux.interface.NullDriver')
|
||||
@ -754,26 +720,52 @@ class TestDhcpAgentEventHandler(base.BaseTestCase):
|
||||
def test_port_update_end(self):
|
||||
payload = dict(port=vars(fake_port2))
|
||||
self.cache.get_network_by_id.return_value = fake_network
|
||||
self.cache.get_port_by_id.return_value = fake_port2
|
||||
self.dhcp.port_update_end(None, payload)
|
||||
self.cache.assert_has_calls(
|
||||
[mock.call.get_network_by_id(fake_port2.network_id),
|
||||
mock.call.get_port_by_id(fake_port2.id),
|
||||
mock.call.put_port(mock.ANY)])
|
||||
self.call_driver.assert_called_once_with('reload_allocations',
|
||||
fake_network)
|
||||
|
||||
def test_port_update_change_ip_on_port(self):
|
||||
payload = dict(port=vars(fake_port1))
|
||||
self.cache.get_network_by_id.return_value = fake_network
|
||||
updated_fake_port1 = copy.deepcopy(fake_port1)
|
||||
updated_fake_port1.fixed_ips[0].ip_address = '172.9.9.99'
|
||||
self.cache.get_port_by_id.return_value = updated_fake_port1
|
||||
self.dhcp.port_update_end(None, payload)
|
||||
self.cache.assert_has_calls(
|
||||
[mock.call.get_network_by_id(fake_port1.network_id),
|
||||
mock.call.get_port_by_id(fake_port1.id),
|
||||
mock.call.put_port(mock.ANY)])
|
||||
self.call_driver.assert_has_calls(
|
||||
[mock.call.call_driver(
|
||||
'release_lease',
|
||||
fake_network,
|
||||
mac_address=fake_port1.mac_address,
|
||||
removed_ips=set([updated_fake_port1.fixed_ips[0].ip_address])),
|
||||
mock.call.call_driver('reload_allocations', fake_network)])
|
||||
|
||||
def test_port_delete_end(self):
|
||||
payload = dict(port_id=fake_port2.id)
|
||||
self.cache.get_network_by_id.return_value = fake_network
|
||||
self.cache.get_port_by_id.return_value = fake_port2
|
||||
|
||||
self.dhcp.port_delete_end(None, payload)
|
||||
|
||||
removed_ips = [fixed_ip.ip_address
|
||||
for fixed_ip in fake_port2.fixed_ips]
|
||||
self.cache.assert_has_calls(
|
||||
[mock.call.get_port_by_id(fake_port2.id),
|
||||
mock.call.get_network_by_id(fake_network.id),
|
||||
mock.call.remove_port(fake_port2)])
|
||||
self.call_driver.assert_called_once_with('reload_allocations',
|
||||
fake_network)
|
||||
self.call_driver.assert_has_calls(
|
||||
[mock.call.call_driver('release_lease',
|
||||
fake_network,
|
||||
mac_address=fake_port2.mac_address,
|
||||
removed_ips=removed_ips),
|
||||
mock.call.call_driver('reload_allocations', fake_network)])
|
||||
|
||||
def test_port_delete_end_unknown_port(self):
|
||||
payload = dict(port_id='unknown')
|
||||
@ -865,16 +857,6 @@ class TestDhcpPluginApiProxy(base.BaseTestCase):
|
||||
device_id='devid',
|
||||
host='foo')
|
||||
|
||||
def test_update_lease_expiration(self):
|
||||
with mock.patch.object(self.proxy, 'cast') as mock_cast:
|
||||
self.proxy.update_lease_expiration('netid', 'ipaddr', 1)
|
||||
self.assertTrue(mock_cast.called)
|
||||
self.make_msg.assert_called_once_with('update_lease_expiration',
|
||||
network_id='netid',
|
||||
ip_address='ipaddr',
|
||||
lease_remaining=1,
|
||||
host='foo')
|
||||
|
||||
|
||||
class TestNetworkCache(base.BaseTestCase):
|
||||
def test_put_network(self):
|
||||
@ -1363,123 +1345,6 @@ class TestDeviceManager(base.BaseTestCase):
|
||||
device.route.add_gateway.assert_called_once_with('192.168.1.1')
|
||||
|
||||
|
||||
class TestDhcpLeaseRelay(base.BaseTestCase):
|
||||
def setUp(self):
|
||||
super(TestDhcpLeaseRelay, self).setUp()
|
||||
cfg.CONF.register_opts(dhcp_agent.DhcpLeaseRelay.OPTS)
|
||||
self.unlink_p = mock.patch('os.unlink')
|
||||
self.unlink = self.unlink_p.start()
|
||||
|
||||
def tearDown(self):
|
||||
self.unlink_p.stop()
|
||||
super(TestDhcpLeaseRelay, self).tearDown()
|
||||
|
||||
def test_init_relay_socket_path_no_prev_socket(self):
|
||||
with mock.patch('os.path.exists') as exists:
|
||||
exists.return_value = False
|
||||
self.unlink.side_effect = OSError
|
||||
|
||||
dhcp_agent.DhcpLeaseRelay(None)
|
||||
|
||||
self.unlink.assert_called_once_with(
|
||||
cfg.CONF.dhcp_lease_relay_socket)
|
||||
exists.assert_called_once_with(cfg.CONF.dhcp_lease_relay_socket)
|
||||
|
||||
def test_init_relay_socket_path_prev_socket_exists(self):
|
||||
with mock.patch('os.path.exists') as exists:
|
||||
exists.return_value = False
|
||||
|
||||
dhcp_agent.DhcpLeaseRelay(None)
|
||||
|
||||
self.unlink.assert_called_once_with(
|
||||
cfg.CONF.dhcp_lease_relay_socket)
|
||||
self.assertFalse(exists.called)
|
||||
|
||||
def test_init_relay_socket_path_prev_socket_unlink_failure(self):
|
||||
self.unlink.side_effect = OSError
|
||||
with mock.patch('os.path.exists') as exists:
|
||||
exists.return_value = True
|
||||
with testtools.ExpectedException(OSError):
|
||||
dhcp_agent.DhcpLeaseRelay(None)
|
||||
|
||||
self.unlink.assert_called_once_with(
|
||||
cfg.CONF.dhcp_lease_relay_socket)
|
||||
exists.assert_called_once_with(
|
||||
cfg.CONF.dhcp_lease_relay_socket)
|
||||
|
||||
def test_handler_valid_data(self):
|
||||
network_id = 'cccccccc-cccc-cccc-cccc-cccccccccccc'
|
||||
ip_address = '192.168.1.9'
|
||||
lease_remaining = 120
|
||||
|
||||
json_rep = jsonutils.dumps(dict(network_id=network_id,
|
||||
lease_remaining=lease_remaining,
|
||||
ip_address=ip_address))
|
||||
handler = mock.Mock()
|
||||
mock_sock = mock.Mock()
|
||||
mock_sock.recv.return_value = json_rep
|
||||
|
||||
relay = dhcp_agent.DhcpLeaseRelay(handler)
|
||||
|
||||
relay._handler(mock_sock, mock.Mock())
|
||||
mock_sock.assert_has_calls([mock.call.recv(1024), mock.call.close()])
|
||||
handler.called_once_with(network_id, ip_address, lease_remaining)
|
||||
|
||||
def test_handler_invalid_data(self):
|
||||
network_id = 'cccccccc-cccc-cccc-cccc-cccccccccccc'
|
||||
ip_address = '192.168.x.x'
|
||||
lease_remaining = 120
|
||||
|
||||
json_rep = jsonutils.dumps(
|
||||
dict(network_id=network_id,
|
||||
lease_remaining=lease_remaining,
|
||||
ip_address=ip_address))
|
||||
|
||||
handler = mock.Mock()
|
||||
mock_sock = mock.Mock()
|
||||
mock_sock.recv.return_value = json_rep
|
||||
|
||||
relay = dhcp_agent.DhcpLeaseRelay(handler)
|
||||
|
||||
with mock.patch('neutron.openstack.common.'
|
||||
'uuidutils.is_uuid_like') as validate:
|
||||
validate.return_value = False
|
||||
|
||||
with mock.patch.object(dhcp_agent.LOG, 'warn') as log:
|
||||
|
||||
relay._handler(mock_sock, mock.Mock())
|
||||
mock_sock.assert_has_calls(
|
||||
[mock.call.recv(1024), mock.call.close()])
|
||||
self.assertFalse(handler.called)
|
||||
self.assertTrue(log.called)
|
||||
|
||||
def test_handler_other_exception(self):
|
||||
handler = mock.Mock()
|
||||
mock_sock = mock.Mock()
|
||||
mock_sock.recv.side_effect = Exception
|
||||
|
||||
relay = dhcp_agent.DhcpLeaseRelay(handler)
|
||||
|
||||
with mock.patch.object(dhcp_agent.LOG, 'exception') as log:
|
||||
relay._handler(mock_sock, mock.Mock())
|
||||
mock_sock.assert_has_calls([mock.call.recv(1024)])
|
||||
self.assertFalse(handler.called)
|
||||
self.assertTrue(log.called)
|
||||
|
||||
def test_start(self):
|
||||
with mock.patch.object(dhcp_agent, 'eventlet') as mock_eventlet:
|
||||
handler = mock.Mock()
|
||||
relay = dhcp_agent.DhcpLeaseRelay(handler)
|
||||
relay.start()
|
||||
|
||||
mock_eventlet.assert_has_calls(
|
||||
[mock.call.listen(cfg.CONF.dhcp_lease_relay_socket,
|
||||
family=socket.AF_UNIX),
|
||||
mock.call.spawn(mock_eventlet.serve,
|
||||
mock.call.listen.return_value,
|
||||
relay._handler)])
|
||||
|
||||
|
||||
class TestDictModel(base.BaseTestCase):
|
||||
def test_basic_dict(self):
|
||||
d = dict(a=1, b=2)
|
||||
|
@ -16,7 +16,6 @@
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
import socket
|
||||
|
||||
import mock
|
||||
from oslo.config import cfg
|
||||
@ -24,7 +23,6 @@ from oslo.config import cfg
|
||||
from neutron.agent.common import config
|
||||
from neutron.agent.linux import dhcp
|
||||
from neutron.common import config as base_config
|
||||
from neutron.openstack.common import jsonutils
|
||||
from neutron.tests import base
|
||||
|
||||
|
||||
@ -184,6 +182,9 @@ class TestDhcpBase(base.BaseTestCase):
|
||||
def reload_allocations(self):
|
||||
pass
|
||||
|
||||
def release_lease(self):
|
||||
pass
|
||||
|
||||
@property
|
||||
def active(self):
|
||||
return True
|
||||
@ -209,6 +210,9 @@ class LocalChild(dhcp.DhcpLocalProcess):
|
||||
def spawn_process(self):
|
||||
self.called.append('spawn')
|
||||
|
||||
def release_lease(self):
|
||||
self.called.append('release_lease')
|
||||
|
||||
|
||||
class TestBase(base.BaseTestCase):
|
||||
def setUp(self):
|
||||
@ -219,9 +223,6 @@ class TestBase(base.BaseTestCase):
|
||||
self.conf = config.setup_conf()
|
||||
self.conf.register_opts(base_config.core_opts)
|
||||
self.conf.register_opts(dhcp.OPTS)
|
||||
self.conf.register_opt(
|
||||
cfg.StrOpt('dhcp_lease_relay_socket',
|
||||
default='$state_path/dhcp/lease_relay'))
|
||||
self.conf.register_opt(cfg.BoolOpt('enable_isolated_metadata',
|
||||
default=True))
|
||||
self.conf(args=args)
|
||||
@ -230,9 +231,9 @@ class TestBase(base.BaseTestCase):
|
||||
|
||||
self.replace_p = mock.patch('neutron.agent.linux.utils.replace_file')
|
||||
self.execute_p = mock.patch('neutron.agent.linux.utils.execute')
|
||||
self.addCleanup(self.replace_p.stop)
|
||||
self.addCleanup(self.execute_p.stop)
|
||||
self.safe = self.replace_p.start()
|
||||
self.addCleanup(self.replace_p.stop)
|
||||
self.execute = self.execute_p.start()
|
||||
|
||||
|
||||
@ -433,7 +434,6 @@ class TestDnsmasq(TestBase):
|
||||
'exec',
|
||||
'qdhcp-ns',
|
||||
'env',
|
||||
'NEUTRON_RELAY_SOCKET_PATH=/dhcp/lease_relay',
|
||||
'NEUTRON_NETWORK_ID=cccccccc-cccc-cccc-cccc-cccccccccccc',
|
||||
'dnsmasq',
|
||||
'--no-hosts',
|
||||
@ -445,11 +445,9 @@ class TestDnsmasq(TestBase):
|
||||
'--pid-file=/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/pid',
|
||||
'--dhcp-hostsfile=/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/host',
|
||||
'--dhcp-optsfile=/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/opts',
|
||||
('--dhcp-script=/usr/local/bin/neutron-dhcp-agent-'
|
||||
'dnsmasq-lease-update'),
|
||||
'--leasefile-ro',
|
||||
'--dhcp-range=set:tag0,192.168.0.0,static,120s',
|
||||
'--dhcp-range=set:tag1,fdca:3ba5:a17a:4ba3::,static,120s']
|
||||
'--dhcp-range=set:tag0,192.168.0.0,static,86400s',
|
||||
'--dhcp-range=set:tag1,fdca:3ba5:a17a:4ba3::,static,86400s']
|
||||
expected.extend(extra_options)
|
||||
|
||||
self.execute.return_value = ('', '')
|
||||
@ -585,6 +583,17 @@ 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.release_lease(mac_address=FakePort2.mac_address,
|
||||
removed_ips=[FakePort2.fixed_ips[0].ip_address])
|
||||
exp_args = ['ip', 'netns', 'exec', 'qdhcp-ns', 'dhcp_release',
|
||||
dm.interface_name, FakePort2.fixed_ips[0].ip_address,
|
||||
FakePort2.mac_address]
|
||||
self.execute.assert_called_once_with(exp_args, root_helper='sudo',
|
||||
check_exit_code=True)
|
||||
|
||||
def test_reload_allocations(self):
|
||||
exp_host_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/host'
|
||||
exp_host_data = ('00:00:80:aa:bb:cc,host-192-168-0-2.openstacklocal,'
|
||||
@ -693,69 +702,6 @@ tag:tag1,249,%s,%s""".lstrip() % (fake_v6,
|
||||
{FakeV4Subnet.id: '192.168.0.1'}
|
||||
)
|
||||
|
||||
def _test_lease_relay_script_helper(self, action, lease_remaining,
|
||||
path_exists=True):
|
||||
relay_path = '/dhcp/relay_socket'
|
||||
network_id = 'cccccccc-cccc-cccc-cccc-cccccccccccc'
|
||||
mac_address = 'aa:bb:cc:dd:ee:ff'
|
||||
ip_address = '192.168.1.9'
|
||||
|
||||
json_rep = jsonutils.dumps(dict(network_id=network_id,
|
||||
lease_remaining=lease_remaining,
|
||||
mac_address=mac_address,
|
||||
ip_address=ip_address))
|
||||
|
||||
environ = {
|
||||
'NEUTRON_NETWORK_ID': network_id,
|
||||
'NEUTRON_RELAY_SOCKET_PATH': relay_path,
|
||||
'DNSMASQ_TIME_REMAINING': '120',
|
||||
}
|
||||
|
||||
def fake_environ(name, default=None):
|
||||
return environ.get(name, default)
|
||||
|
||||
with mock.patch('os.environ') as mock_environ:
|
||||
mock_environ.get.side_effect = fake_environ
|
||||
|
||||
with mock.patch.object(dhcp, 'sys') as mock_sys:
|
||||
mock_sys.argv = [
|
||||
'lease-update',
|
||||
action,
|
||||
mac_address,
|
||||
ip_address,
|
||||
]
|
||||
|
||||
with mock.patch('socket.socket') as mock_socket:
|
||||
mock_conn = mock.Mock()
|
||||
mock_socket.return_value = mock_conn
|
||||
|
||||
with mock.patch('os.path.exists') as mock_exists:
|
||||
mock_exists.return_value = path_exists
|
||||
|
||||
dhcp.Dnsmasq.lease_update()
|
||||
|
||||
mock_exists.assert_called_once_with(relay_path)
|
||||
if path_exists:
|
||||
mock_socket.assert_called_once_with(
|
||||
socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
|
||||
mock_conn.assert_has_calls(
|
||||
[mock.call.connect(relay_path),
|
||||
mock.call.send(json_rep),
|
||||
mock.call.close()])
|
||||
|
||||
def test_lease_relay_script_add(self):
|
||||
self._test_lease_relay_script_helper('add', 120)
|
||||
|
||||
def test_lease_relay_script_old(self):
|
||||
self._test_lease_relay_script_helper('old', 120)
|
||||
|
||||
def test_lease_relay_script_del(self):
|
||||
self._test_lease_relay_script_helper('del', 0)
|
||||
|
||||
def test_lease_relay_script_add_socket_missing(self):
|
||||
self._test_lease_relay_script_helper('add', 120, False)
|
||||
|
||||
def test_remove_config_files(self):
|
||||
net = FakeV4Network()
|
||||
path = '/opt/data/neutron/dhcp'
|
||||
|
@ -73,7 +73,6 @@ console_scripts =
|
||||
neutron-db-manage = neutron.db.migration.cli:main
|
||||
neutron-debug = neutron.debug.shell:main
|
||||
neutron-dhcp-agent = neutron.agent.dhcp_agent:main
|
||||
neutron-dhcp-agent-dnsmasq-lease-update = neutron.agent.linux.dhcp:Dnsmasq.lease_update
|
||||
neutron-hyperv-agent = neutron.plugins.hyperv.agent.hyperv_neutron_agent:main
|
||||
neutron-l3-agent = neutron.agent.l3_agent:main
|
||||
neutron-lbaas-agent = neutron.services.loadbalancer.drivers.haproxy.agent:main
|
||||
@ -91,7 +90,6 @@ console_scripts =
|
||||
quantum-db-manage = neutron.db.migration.cli:main
|
||||
quantum-debug = neutron.debug.shell:main
|
||||
quantum-dhcp-agent = neutron.agent.dhcp_agent:main
|
||||
quantum-dhcp-agent-dnsmasq-lease-update = neutron.agent.linux.dhcp:Dnsmasq.lease_update
|
||||
quantum-hyperv-agent = neutron.plugins.hyperv.agent.hyperv_neutron_agent:main
|
||||
quantum-l3-agent = neutron.agent.l3_agent:main
|
||||
quantum-lbaas-agent = neutron.services.loadbalancer.drivers.haproxy.agent:main
|
||||
|
Loading…
Reference in New Issue
Block a user