Move classes out of l3_agent.py

The file l3_agent.py has become too large.  This patch is a simple
pure refactor to move some of the functionality in to other files
where things aren't too tangled up.  There is no functional change
with this patch and I avoided gratuitous other fixups in this patch in
order to make it easier to review.

I plan to follow up on the new l3_dvr and l3_agent_router modules with
more restructuring in the near future.

Partially-Implements: bp restructure-l3-agent

Change-Id: I3529fe4146c50c940f41eb26d0b5efc5870b3af9
This commit is contained in:
Carl Baldwin 2014-12-01 16:49:10 -05:00 committed by Brian Haley
parent e55e71524f
commit d7c5306360
14 changed files with 637 additions and 536 deletions

View File

View File

@ -15,7 +15,6 @@
import sys
import datetime
import eventlet
eventlet.monkey_patch()
@ -26,10 +25,12 @@ from oslo import messaging
from oslo.utils import excutils
from oslo.utils import importutils
from oslo.utils import timeutils
import Queue
from neutron.agent.common import config
from neutron.agent import l3_ha_agent
from neutron.agent.l3 import ha
from neutron.agent.l3 import link_local_allocator as lla
from neutron.agent.l3 import router_info
from neutron.agent.l3 import router_processing_queue as queue
from neutron.agent.linux import external_process
from neutron.agent.linux import interface
from neutron.agent.linux import ip_lib
@ -77,10 +78,6 @@ FIP_PR_START = 32768
FIP_PR_END = FIP_PR_START + 40000
RPC_LOOP_INTERVAL = 1
FLOATING_IP_CIDR_SUFFIX = '/32'
# Lower value is higher priority
PRIORITY_RPC = 0
PRIORITY_SYNC_ROUTERS_TASK = 1
DELETE_ROUTER = 1
class L3PluginApi(object):
@ -142,295 +139,8 @@ class L3PluginApi(object):
return cctxt.call(context, 'get_service_plugin_list')
class LinkLocalAddressPair(netaddr.IPNetwork):
def __init__(self, addr):
super(LinkLocalAddressPair, self).__init__(addr)
def get_pair(self):
"""Builds an address pair from the first and last addresses. """
return (netaddr.IPNetwork("%s/%s" % (self.network, self.prefixlen)),
netaddr.IPNetwork("%s/%s" % (self.broadcast, self.prefixlen)))
class LinkLocalAllocator(object):
"""Manages allocation of link local IP addresses.
These link local addresses are used for routing inside the fip namespaces.
The associations need to persist across agent restarts to maintain
consistency. Without this, there is disruption in network connectivity
as the agent rewires the connections with the new IP address assocations.
Persisting these in the database is unnecessary and would degrade
performance.
"""
def __init__(self, state_file, subnet):
"""Read the file with previous allocations recorded.
See the note in the allocate method for more detail.
"""
self.state_file = state_file
subnet = netaddr.IPNetwork(subnet)
self.allocations = {}
self.remembered = {}
for line in self._read():
key, cidr = line.strip().split(',')
self.remembered[key] = LinkLocalAddressPair(cidr)
self.pool = set(LinkLocalAddressPair(s) for s in subnet.subnet(31))
self.pool.difference_update(self.remembered.values())
def allocate(self, key):
"""Try to allocate a link local address pair.
I expect this to work in all cases because I expect the pool size to be
large enough for any situation. Nonetheless, there is some defensive
programming in here.
Since the allocations are persisted, there is the chance to leak
allocations which should have been released but were not. This leak
could eventually exhaust the pool.
So, if a new allocation is needed, the code first checks to see if
there are any remembered allocations for the key. If not, it checks
the free pool. If the free pool is empty then it dumps the remembered
allocations to free the pool. This final desparate step will not
happen often in practice.
"""
if key in self.remembered:
self.allocations[key] = self.remembered.pop(key)
return self.allocations[key]
if not self.pool:
# Desparate times. Try to get more in the pool.
self.pool.update(self.remembered.values())
self.remembered.clear()
if not self.pool:
# More than 256 routers on a compute node!
raise RuntimeError(_("Cannot allocate link local address"))
self.allocations[key] = self.pool.pop()
self._write_allocations()
return self.allocations[key]
def release(self, key):
self.pool.add(self.allocations.pop(key))
self._write_allocations()
def _write_allocations(self):
current = ["%s,%s\n" % (k, v) for k, v in self.allocations.items()]
remembered = ["%s,%s\n" % (k, v) for k, v in self.remembered.items()]
current.extend(remembered)
self._write(current)
def _write(self, lines):
with open(self.state_file, "w") as f:
f.writelines(lines)
def _read(self):
if not os.path.exists(self.state_file):
return []
with open(self.state_file) as f:
return f.readlines()
class RouterInfo(l3_ha_agent.RouterMixin):
def __init__(self, router_id, root_helper, router,
use_ipv6=False, ns_name=None):
self.router_id = router_id
self.ex_gw_port = None
self._snat_enabled = None
self._snat_action = None
self.internal_ports = []
self.snat_ports = []
self.floating_ips = set()
self.floating_ips_dict = {}
self.root_helper = root_helper
# Invoke the setter for establishing initial SNAT action
self.router = router
self.ns_name = ns_name
self.iptables_manager = iptables_manager.IptablesManager(
root_helper=root_helper,
use_ipv6=use_ipv6,
namespace=self.ns_name)
self.snat_iptables_manager = None
self.routes = []
# DVR Data
# Linklocal subnet for router and floating IP namespace link
self.rtr_fip_subnet = None
self.dist_fip_count = 0
super(RouterInfo, self).__init__()
@property
def router(self):
return self._router
@router.setter
def router(self, value):
self._router = value
if not self._router:
return
# enable_snat by default if it wasn't specified by plugin
self._snat_enabled = self._router.get('enable_snat', True)
# Set a SNAT action for the router
if self._router.get('gw_port'):
self._snat_action = ('add_rules' if self._snat_enabled
else 'remove_rules')
elif self.ex_gw_port:
# Gateway port was removed, remove rules
self._snat_action = 'remove_rules'
def perform_snat_action(self, snat_callback, *args):
# Process SNAT rules for attached subnets
if self._snat_action:
snat_callback(self, self._router.get('gw_port'),
*args, action=self._snat_action)
self._snat_action = None
class RouterUpdate(object):
"""Encapsulates a router update
An instance of this object carries the information necessary to prioritize
and process a request to update a router.
"""
def __init__(self, router_id, priority,
action=None, router=None, timestamp=None):
self.priority = priority
self.timestamp = timestamp
if not timestamp:
self.timestamp = timeutils.utcnow()
self.id = router_id
self.action = action
self.router = router
def __lt__(self, other):
"""Implements priority among updates
Lower numerical priority always gets precedence. When comparing two
updates of the same priority then the one with the earlier timestamp
gets procedence. In the unlikely event that the timestamps are also
equal it falls back to a simple comparison of ids meaning the
precedence is essentially random.
"""
if self.priority != other.priority:
return self.priority < other.priority
if self.timestamp != other.timestamp:
return self.timestamp < other.timestamp
return self.id < other.id
class ExclusiveRouterProcessor(object):
"""Manager for access to a router for processing
This class controls access to a router in a non-blocking way. The first
instance to be created for a given router_id is granted exclusive access to
the router.
Other instances may be created for the same router_id while the first
instance has exclusive access. If that happens then it doesn't block and
wait for access. Instead, it signals to the master instance that an update
came in with the timestamp.
This way, a thread will not block to wait for access to a router. Instead
it effectively signals to the thread that is working on the router that
something has changed since it started working on it. That thread will
simply finish its current iteration and then repeat.
This class keeps track of the last time that a router data was fetched and
processed. The timestamp that it keeps must be before when the data used
to process the router last was fetched from the database. But, as close as
possible. The timestamp should not be recorded, however, until the router
has been processed using the fetch data.
"""
_masters = {}
_router_timestamps = {}
def __init__(self, router_id):
self._router_id = router_id
if router_id not in self._masters:
self._masters[router_id] = self
self._queue = []
self._master = self._masters[router_id]
def _i_am_master(self):
return self == self._master
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
if self._i_am_master():
del self._masters[self._router_id]
def _get_router_data_timestamp(self):
return self._router_timestamps.get(self._router_id,
datetime.datetime.min)
def fetched_and_processed(self, timestamp):
"""Records the data timestamp after it is used to update the router"""
new_timestamp = max(timestamp, self._get_router_data_timestamp())
self._router_timestamps[self._router_id] = new_timestamp
def queue_update(self, update):
"""Queues an update from a worker
This is the queue used to keep new updates that come in while a router
is being processed. These updates have already bubbled to the front of
the RouterProcessingQueue.
"""
self._master._queue.append(update)
def updates(self):
"""Processes the router until updates stop coming
Only the master instance will process the router. However, updates may
come in from other workers while it is in progress. This method loops
until they stop coming.
"""
if self._i_am_master():
while self._queue:
# Remove the update from the queue even if it is old.
update = self._queue.pop(0)
# Process the update only if it is fresh.
if self._get_router_data_timestamp() < update.timestamp:
yield update
class RouterProcessingQueue(object):
"""Manager of the queue of routers to process."""
def __init__(self):
self._queue = Queue.PriorityQueue()
def add(self, update):
self._queue.put(update)
def each_update_to_next_router(self):
"""Grabs the next router from the queue and processes
This method uses a for loop to process the router repeatedly until
updates stop bubbling to the front of the queue.
"""
next_update = self._queue.get()
with ExclusiveRouterProcessor(next_update.id) as rp:
# Queue the update whether this worker is the master or not.
rp.queue_update(next_update)
# Here, if the current worker is not the master, the call to
# rp.updates() will not yield and so this will essentially be a
# noop.
for update in rp.updates():
yield (rp, update)
class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
l3_ha_agent.AgentMixin,
ha.AgentMixin,
manager.Manager):
"""Manager for L3NatAgent
@ -554,12 +264,12 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
# dvr data
self.agent_gateway_port = None
self.fip_ns_subscribers = set()
self.local_subnets = LinkLocalAllocator(
self.local_subnets = lla.LinkLocalAllocator(
os.path.join(self.conf.state_path, 'fip-linklocal-networks'),
FIP_LL_SUBNET)
self.fip_priorities = set(range(FIP_PR_START, FIP_PR_END))
self._queue = RouterProcessingQueue()
self._queue = queue.RouterProcessingQueue()
super(L3NATAgent, self).__init__(conf=self.conf)
self.target_ex_net_id = None
@ -745,7 +455,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
def _router_added(self, router_id, router):
ns_name = (self.get_ns_name(router_id)
if self.conf.use_namespaces else None)
ri = RouterInfo(router_id=router_id,
ri = router_info.RouterInfo(router_id=router_id,
root_helper=self.root_helper,
router=router,
use_ipv6=self.use_ipv6,
@ -1698,7 +1408,9 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
def router_deleted(self, context, router_id):
"""Deal with router deletion RPC message."""
LOG.debug('Got router deleted notification for %s', router_id)
update = RouterUpdate(router_id, PRIORITY_RPC, action=DELETE_ROUTER)
update = queue.RouterUpdate(router_id,
queue.PRIORITY_RPC,
action=queue.DELETE_ROUTER)
self._queue.add(update)
def _update_arp_entry(self, ri, ip, mac, subnet_id, operation):
@ -1751,13 +1463,15 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
if isinstance(routers[0], dict):
routers = [router['id'] for router in routers]
for id in routers:
update = RouterUpdate(id, PRIORITY_RPC)
update = queue.RouterUpdate(id, queue.PRIORITY_RPC)
self._queue.add(update)
def router_removed_from_agent(self, context, payload):
LOG.debug('Got router removed from agent :%r', payload)
router_id = payload['router_id']
update = RouterUpdate(router_id, PRIORITY_RPC, action=DELETE_ROUTER)
update = queue.RouterUpdate(router_id,
queue.PRIORITY_RPC,
action=queue.DELETE_ROUTER)
self._queue.add(update)
def router_added_to_agent(self, context, payload):
@ -1801,7 +1515,7 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
for rp, update in self._queue.each_update_to_next_router():
LOG.debug("Starting router update for %s", update.id)
router = update.router
if update.action != DELETE_ROUTER and not router:
if update.action != queue.DELETE_ROUTER and not router:
try:
update.timestamp = timeutils.utcnow()
routers = self.plugin_rpc.get_routers(self.context,
@ -1870,8 +1584,8 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
else:
LOG.debug('Processing :%r', routers)
for r in routers:
update = RouterUpdate(r['id'],
PRIORITY_SYNC_ROUTERS_TASK,
update = queue.RouterUpdate(r['id'],
queue.PRIORITY_SYNC_ROUTERS_TASK,
router=r,
timestamp=timestamp)
self._queue.add(update)
@ -1884,10 +1598,10 @@ class L3NATAgent(firewall_l3_agent.FWaaSL3AgentRpcCallback,
# Two kinds of stale routers: Routers for which info is cached in
# self.router_info and the others. First, handle the former.
for router_id in prev_router_ids - curr_router_ids:
update = RouterUpdate(router_id,
PRIORITY_SYNC_ROUTERS_TASK,
update = queue.RouterUpdate(router_id,
queue.PRIORITY_SYNC_ROUTERS_TASK,
timestamp=timestamp,
action=DELETE_ROUTER)
action=queue.DELETE_ROUTER)
self._queue.add(update)
# Next, one effort to clean out namespaces for which we don't have
@ -2001,7 +1715,7 @@ class L3NATAgentWithStateReport(L3NATAgent):
def _register_opts(conf):
conf.register_opts(L3NATAgent.OPTS)
conf.register_opts(l3_ha_agent.OPTS)
conf.register_opts(ha.OPTS)
config.register_interface_driver_opts_helper(conf)
config.register_use_namespaces_opts_helper(conf)
config.register_agent_state_opts_helper(conf)
@ -2010,7 +1724,7 @@ def _register_opts(conf):
conf.register_opts(external_process.OPTS)
def main(manager='neutron.agent.l3_agent.L3NATAgentWithStateReport'):
def main(manager='neutron.agent.l3.agent.L3NATAgentWithStateReport'):
_register_opts(cfg.CONF)
common_config.init(sys.argv[1:])
config.setup_logging()

View File

@ -0,0 +1,109 @@
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import netaddr
import os
class LinkLocalAddressPair(netaddr.IPNetwork):
def __init__(self, addr):
super(LinkLocalAddressPair, self).__init__(addr)
def get_pair(self):
"""Builds an address pair from the first and last addresses. """
return (netaddr.IPNetwork("%s/%s" % (self.network, self.prefixlen)),
netaddr.IPNetwork("%s/%s" % (self.broadcast, self.prefixlen)))
class LinkLocalAllocator(object):
"""Manages allocation of link local IP addresses.
These link local addresses are used for routing inside the fip namespaces.
The associations need to persist across agent restarts to maintain
consistency. Without this, there is disruption in network connectivity
as the agent rewires the connections with the new IP address assocations.
Persisting these in the database is unnecessary and would degrade
performance.
"""
def __init__(self, state_file, subnet):
"""Read the file with previous allocations recorded.
See the note in the allocate method for more detail.
"""
self.state_file = state_file
subnet = netaddr.IPNetwork(subnet)
self.allocations = {}
self.remembered = {}
for line in self._read():
key, cidr = line.strip().split(',')
self.remembered[key] = LinkLocalAddressPair(cidr)
self.pool = set(LinkLocalAddressPair(s) for s in subnet.subnet(31))
self.pool.difference_update(self.remembered.values())
def allocate(self, key):
"""Try to allocate a link local address pair.
I expect this to work in all cases because I expect the pool size to be
large enough for any situation. Nonetheless, there is some defensive
programming in here.
Since the allocations are persisted, there is the chance to leak
allocations which should have been released but were not. This leak
could eventually exhaust the pool.
So, if a new allocation is needed, the code first checks to see if
there are any remembered allocations for the key. If not, it checks
the free pool. If the free pool is empty then it dumps the remembered
allocations to free the pool. This final desparate step will not
happen often in practice.
"""
if key in self.remembered:
self.allocations[key] = self.remembered.pop(key)
return self.allocations[key]
if not self.pool:
# Desparate times. Try to get more in the pool.
self.pool.update(self.remembered.values())
self.remembered.clear()
if not self.pool:
# More than 256 routers on a compute node!
raise RuntimeError(_("Cannot allocate link local address"))
self.allocations[key] = self.pool.pop()
self._write_allocations()
return self.allocations[key]
def release(self, key):
self.pool.add(self.allocations.pop(key))
self._write_allocations()
def _write_allocations(self):
current = ["%s,%s\n" % (k, v) for k, v in self.allocations.items()]
remembered = ["%s,%s\n" % (k, v) for k, v in self.remembered.items()]
current.extend(remembered)
self._write(current)
def _write(self, lines):
with open(self.state_file, "w") as f:
f.writelines(lines)
def _read(self):
if not os.path.exists(self.state_file):
return []
with open(self.state_file) as f:
return f.readlines()

View File

@ -0,0 +1,72 @@
# Copyright (c) 2014 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.
from neutron.agent.l3 import ha
from neutron.agent.linux import iptables_manager
class RouterInfo(ha.RouterMixin):
def __init__(self, router_id, root_helper, router,
use_ipv6=False, ns_name=None):
self.router_id = router_id
self.ex_gw_port = None
self._snat_enabled = None
self._snat_action = None
self.internal_ports = []
self.snat_ports = []
self.floating_ips = set()
self.floating_ips_dict = {}
self.root_helper = root_helper
# Invoke the setter for establishing initial SNAT action
self.router = router
self.ns_name = ns_name
self.iptables_manager = iptables_manager.IptablesManager(
root_helper=root_helper,
use_ipv6=use_ipv6,
namespace=self.ns_name)
self.snat_iptables_manager = None
self.routes = []
# DVR Data
# Linklocal subnet for router and floating IP namespace link
self.rtr_fip_subnet = None
self.dist_fip_count = 0
super(RouterInfo, self).__init__()
@property
def router(self):
return self._router
@router.setter
def router(self, value):
self._router = value
if not self._router:
return
# enable_snat by default if it wasn't specified by plugin
self._snat_enabled = self._router.get('enable_snat', True)
# Set a SNAT action for the router
if self._router.get('gw_port'):
self._snat_action = ('add_rules' if self._snat_enabled
else 'remove_rules')
elif self.ex_gw_port:
# Gateway port was removed, remove rules
self._snat_action = 'remove_rules'
def perform_snat_action(self, snat_callback, *args):
# Process SNAT rules for attached subnets
if self._snat_action:
snat_callback(self, self._router.get('gw_port'),
*args, action=self._snat_action)
self._snat_action = None

View File

@ -0,0 +1,162 @@
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import datetime
import Queue
from oslo.utils import timeutils
# Lower value is higher priority
PRIORITY_RPC = 0
PRIORITY_SYNC_ROUTERS_TASK = 1
DELETE_ROUTER = 1
class RouterUpdate(object):
"""Encapsulates a router update
An instance of this object carries the information necessary to prioritize
and process a request to update a router.
"""
def __init__(self, router_id, priority,
action=None, router=None, timestamp=None):
self.priority = priority
self.timestamp = timestamp
if not timestamp:
self.timestamp = timeutils.utcnow()
self.id = router_id
self.action = action
self.router = router
def __lt__(self, other):
"""Implements priority among updates
Lower numerical priority always gets precedence. When comparing two
updates of the same priority then the one with the earlier timestamp
gets procedence. In the unlikely event that the timestamps are also
equal it falls back to a simple comparison of ids meaning the
precedence is essentially random.
"""
if self.priority != other.priority:
return self.priority < other.priority
if self.timestamp != other.timestamp:
return self.timestamp < other.timestamp
return self.id < other.id
class ExclusiveRouterProcessor(object):
"""Manager for access to a router for processing
This class controls access to a router in a non-blocking way. The first
instance to be created for a given router_id is granted exclusive access to
the router.
Other instances may be created for the same router_id while the first
instance has exclusive access. If that happens then it doesn't block and
wait for access. Instead, it signals to the master instance that an update
came in with the timestamp.
This way, a thread will not block to wait for access to a router. Instead
it effectively signals to the thread that is working on the router that
something has changed since it started working on it. That thread will
simply finish its current iteration and then repeat.
This class keeps track of the last time that a router data was fetched and
processed. The timestamp that it keeps must be before when the data used
to process the router last was fetched from the database. But, as close as
possible. The timestamp should not be recorded, however, until the router
has been processed using the fetch data.
"""
_masters = {}
_router_timestamps = {}
def __init__(self, router_id):
self._router_id = router_id
if router_id not in self._masters:
self._masters[router_id] = self
self._queue = []
self._master = self._masters[router_id]
def _i_am_master(self):
return self == self._master
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
if self._i_am_master():
del self._masters[self._router_id]
def _get_router_data_timestamp(self):
return self._router_timestamps.get(self._router_id,
datetime.datetime.min)
def fetched_and_processed(self, timestamp):
"""Records the data timestamp after it is used to update the router"""
new_timestamp = max(timestamp, self._get_router_data_timestamp())
self._router_timestamps[self._router_id] = new_timestamp
def queue_update(self, update):
"""Queues an update from a worker
This is the queue used to keep new updates that come in while a router
is being processed. These updates have already bubbled to the front of
the RouterProcessingQueue.
"""
self._master._queue.append(update)
def updates(self):
"""Processes the router until updates stop coming
Only the master instance will process the router. However, updates may
come in from other workers while it is in progress. This method loops
until they stop coming.
"""
if self._i_am_master():
while self._queue:
# Remove the update from the queue even if it is old.
update = self._queue.pop(0)
# Process the update only if it is fresh.
if self._get_router_data_timestamp() < update.timestamp:
yield update
class RouterProcessingQueue(object):
"""Manager of the queue of routers to process."""
def __init__(self):
self._queue = Queue.PriorityQueue()
def add(self, update):
self._queue.put(update)
def each_update_to_next_router(self):
"""Grabs the next router from the queue and processes
This method uses a for loop to process the router repeatedly until
updates stop bubbling to the front of the queue.
"""
next_update = self._queue.get()
with ExclusiveRouterProcessor(next_update.id) as rp:
# Queue the update whether this worker is the master or not.
rp.queue_update(next_update)
# Here, if the current worker is not the master, the call to
# rp.updates() will not yield and so this will essentially be a
# noop.
for update in rp.updates():
yield (rp, update)

View File

@ -23,7 +23,7 @@ from oslo.utils import importutils
from neutron.agent.common import config as agent_config
from neutron.agent import dhcp_agent
from neutron.agent import l3_agent
from neutron.agent.l3 import agent as l3_agent
from neutron.agent.linux import dhcp
from neutron.agent.linux import interface
from neutron.agent.linux import ip_lib

View File

@ -16,7 +16,7 @@
from oslo.config import cfg
from neutron.agent.common import config as agent_config
from neutron.agent import l3_agent
from neutron.agent.l3 import agent
from neutron.agent.linux import interface
from neutron.agent.linux import ip_lib
from neutron.agent.linux import ovs_lib
@ -45,7 +45,7 @@ def setup_conf():
conf = cfg.CONF
conf.register_cli_opts(opts)
conf.register_opts(l3_agent.L3NATAgent.OPTS)
conf.register_opts(agent.L3NATAgent.OPTS)
conf.register_opts(interface.OPTS)
agent_config.register_interface_driver_opts_helper(conf)
agent_config.register_use_namespaces_opts_helper(conf)

View File

@ -13,10 +13,10 @@
# under the License.
from neutron.agent import l3_agent
from neutron.agent.l3 import agent
class TestL3NATAgent(l3_agent.L3NATAgentWithStateReport):
class TestL3NATAgent(agent.L3NATAgentWithStateReport):
NESTED_NAMESPACE_SEPARATOR = '@'
def get_ns_name(self, router_id):

View File

@ -20,7 +20,7 @@ import mock
from oslo.config import cfg
from neutron.agent.common import config as agent_config
from neutron.agent import l3_agent
from neutron.agent.l3 import agent as l3_agent
from neutron.agent.linux import external_process
from neutron.agent.linux import ip_lib
from neutron.common import config as common_config
@ -39,7 +39,7 @@ class L3AgentTestFramework(base.BaseOVSLinuxTestCase):
def setUp(self):
super(L3AgentTestFramework, self).setUp()
self.check_sudo_enabled()
mock.patch('neutron.agent.l3_agent.L3PluginApi').start()
mock.patch('neutron.agent.l3.agent.L3PluginApi').start()
self.agent = self._configure_agent('agent1')
def _get_config_opts(self):

View File

@ -15,7 +15,6 @@
import contextlib
import copy
import datetime
import mock
import netaddr
@ -24,8 +23,10 @@ from oslo import messaging
from testtools import matchers
from neutron.agent.common import config as agent_config
from neutron.agent import l3_agent
from neutron.agent import l3_ha_agent
from neutron.agent.l3 import agent as l3_agent
from neutron.agent.l3 import ha
from neutron.agent.l3 import link_local_allocator as lla
from neutron.agent.l3 import router_info as l3router
from neutron.agent.linux import interface
from neutron.common import config as base_config
from neutron.common import constants as l3_constants
@ -49,161 +50,6 @@ class FakeDev(object):
self.name = name
class TestExclusiveRouterProcessor(base.BaseTestCase):
def setUp(self):
super(TestExclusiveRouterProcessor, self).setUp()
def test_i_am_master(self):
master = l3_agent.ExclusiveRouterProcessor(FAKE_ID)
not_master = l3_agent.ExclusiveRouterProcessor(FAKE_ID)
master_2 = l3_agent.ExclusiveRouterProcessor(FAKE_ID_2)
not_master_2 = l3_agent.ExclusiveRouterProcessor(FAKE_ID_2)
self.assertTrue(master._i_am_master())
self.assertFalse(not_master._i_am_master())
self.assertTrue(master_2._i_am_master())
self.assertFalse(not_master_2._i_am_master())
master.__exit__(None, None, None)
master_2.__exit__(None, None, None)
def test_master(self):
master = l3_agent.ExclusiveRouterProcessor(FAKE_ID)
not_master = l3_agent.ExclusiveRouterProcessor(FAKE_ID)
master_2 = l3_agent.ExclusiveRouterProcessor(FAKE_ID_2)
not_master_2 = l3_agent.ExclusiveRouterProcessor(FAKE_ID_2)
self.assertEqual(master._master, master)
self.assertEqual(not_master._master, master)
self.assertEqual(master_2._master, master_2)
self.assertEqual(not_master_2._master, master_2)
master.__exit__(None, None, None)
master_2.__exit__(None, None, None)
def test__enter__(self):
self.assertFalse(FAKE_ID in l3_agent.ExclusiveRouterProcessor._masters)
master = l3_agent.ExclusiveRouterProcessor(FAKE_ID)
master.__enter__()
self.assertTrue(FAKE_ID in l3_agent.ExclusiveRouterProcessor._masters)
master.__exit__(None, None, None)
def test__exit__(self):
master = l3_agent.ExclusiveRouterProcessor(FAKE_ID)
not_master = l3_agent.ExclusiveRouterProcessor(FAKE_ID)
master.__enter__()
self.assertTrue(FAKE_ID in l3_agent.ExclusiveRouterProcessor._masters)
not_master.__enter__()
not_master.__exit__(None, None, None)
self.assertTrue(FAKE_ID in l3_agent.ExclusiveRouterProcessor._masters)
master.__exit__(None, None, None)
self.assertFalse(FAKE_ID in l3_agent.ExclusiveRouterProcessor._masters)
def test_data_fetched_since(self):
master = l3_agent.ExclusiveRouterProcessor(FAKE_ID)
self.assertEqual(master._get_router_data_timestamp(),
datetime.datetime.min)
ts1 = datetime.datetime.utcnow() - datetime.timedelta(seconds=10)
ts2 = datetime.datetime.utcnow()
master.fetched_and_processed(ts2)
self.assertEqual(master._get_router_data_timestamp(), ts2)
master.fetched_and_processed(ts1)
self.assertEqual(master._get_router_data_timestamp(), ts2)
master.__exit__(None, None, None)
def test_updates(self):
master = l3_agent.ExclusiveRouterProcessor(FAKE_ID)
not_master = l3_agent.ExclusiveRouterProcessor(FAKE_ID)
master.queue_update(l3_agent.RouterUpdate(FAKE_ID, 0))
not_master.queue_update(l3_agent.RouterUpdate(FAKE_ID, 0))
for update in not_master.updates():
raise Exception("Only the master should process a router")
self.assertEqual(2, len([i for i in master.updates()]))
class TestLinkLocalAddrAllocator(base.BaseTestCase):
def setUp(self):
super(TestLinkLocalAddrAllocator, self).setUp()
self.subnet = netaddr.IPNetwork('169.254.31.0/24')
def test__init__(self):
a = l3_agent.LinkLocalAllocator('/file', self.subnet.cidr)
self.assertEqual('/file', a.state_file)
self.assertEqual({}, a.allocations)
def test__init__readfile(self):
with mock.patch.object(l3_agent.LinkLocalAllocator, '_read') as read:
read.return_value = ["da873ca2,169.254.31.28/31\n"]
a = l3_agent.LinkLocalAllocator('/file', self.subnet.cidr)
self.assertTrue('da873ca2' in a.remembered)
self.assertEqual({}, a.allocations)
def test_allocate(self):
a = l3_agent.LinkLocalAllocator('/file', self.subnet.cidr)
with mock.patch.object(l3_agent.LinkLocalAllocator, '_write') as write:
subnet = a.allocate('deadbeef')
self.assertTrue('deadbeef' in a.allocations)
self.assertTrue(subnet not in a.pool)
self._check_allocations(a.allocations)
write.assert_called_once_with(['deadbeef,%s\n' % subnet.cidr])
def test_allocate_from_file(self):
with mock.patch.object(l3_agent.LinkLocalAllocator, '_read') as read:
read.return_value = ["deadbeef,169.254.31.88/31\n"]
a = l3_agent.LinkLocalAllocator('/file', self.subnet.cidr)
with mock.patch.object(l3_agent.LinkLocalAllocator, '_write') as write:
subnet = a.allocate('deadbeef')
self.assertEqual(netaddr.IPNetwork('169.254.31.88/31'), subnet)
self.assertTrue(subnet not in a.pool)
self._check_allocations(a.allocations)
self.assertFalse(write.called)
def test_allocate_exhausted_pool(self):
subnet = netaddr.IPNetwork('169.254.31.0/31')
with mock.patch.object(l3_agent.LinkLocalAllocator, '_read') as read:
read.return_value = ["deadbeef,169.254.31.0/31\n"]
a = l3_agent.LinkLocalAllocator('/file', subnet.cidr)
with mock.patch.object(l3_agent.LinkLocalAllocator, '_write') as write:
allocation = a.allocate('abcdef12')
self.assertEqual(subnet, allocation)
self.assertFalse('deadbeef' in a.allocations)
self.assertTrue('abcdef12' in a.allocations)
self.assertTrue(allocation not in a.pool)
self._check_allocations(a.allocations)
write.assert_called_once_with(['abcdef12,%s\n' % allocation.cidr])
self.assertRaises(RuntimeError, a.allocate, 'deadbeef')
def test_release(self):
with mock.patch.object(l3_agent.LinkLocalAllocator, '_write') as write:
a = l3_agent.LinkLocalAllocator('/file', self.subnet.cidr)
subnet = a.allocate('deadbeef')
write.reset_mock()
a.release('deadbeef')
self.assertTrue('deadbeef' not in a.allocations)
self.assertTrue(subnet in a.pool)
self.assertEqual({}, a.allocations)
write.assert_called_once_with([])
def _check_allocations(self, allocations):
for key, subnet in allocations.items():
self.assertTrue(subnet in self.subnet)
self.assertEqual(subnet.prefixlen, 31)
def router_append_interface(router, count=1, ip_version=4, ra_mode=None,
addr_mode=None):
if ip_version == 4:
@ -318,7 +164,7 @@ class TestBasicRouterOperations(base.BaseTestCase):
self.conf = agent_config.setup_conf()
self.conf.register_opts(base_config.core_opts)
self.conf.register_opts(l3_agent.L3NATAgent.OPTS)
self.conf.register_opts(l3_ha_agent.OPTS)
self.conf.register_opts(ha.OPTS)
agent_config.register_interface_driver_opts_helper(self.conf)
agent_config.register_use_namespaces_opts_helper(self.conf)
agent_config.register_root_helper(self.conf)
@ -334,7 +180,7 @@ class TestBasicRouterOperations(base.BaseTestCase):
'neutron.agent.linux.ip_lib.device_exists')
self.device_exists = self.device_exists_p.start()
mock.patch('neutron.agent.l3_ha_agent.AgentMixin'
mock.patch('neutron.agent.l3.ha.AgentMixin'
'._init_ha_conf_path').start()
mock.patch('neutron.agent.linux.keepalived.KeepalivedNotifierMixin'
'._get_full_config_file_path').start()
@ -352,7 +198,7 @@ class TestBasicRouterOperations(base.BaseTestCase):
self.external_process = self.external_process_p.start()
self.send_arp_p = mock.patch(
'neutron.agent.l3_agent.L3NATAgent._send_gratuitous_arp_packet')
'neutron.agent.l3.agent.L3NATAgent._send_gratuitous_arp_packet')
self.send_arp = self.send_arp_p.start()
self.dvr_cls_p = mock.patch('neutron.agent.linux.interface.NullDriver')
@ -376,7 +222,7 @@ class TestBasicRouterOperations(base.BaseTestCase):
ip_dev.return_value = self.mock_ip_dev
self.l3pluginApi_cls_p = mock.patch(
'neutron.agent.l3_agent.L3PluginApi')
'neutron.agent.l3.agent.L3PluginApi')
l3pluginApi_cls = self.l3pluginApi_cls_p.start()
self.plugin_api = mock.MagicMock()
l3pluginApi_cls.return_value = self.plugin_api
@ -412,7 +258,7 @@ class TestBasicRouterOperations(base.BaseTestCase):
network_id = _uuid()
router = prepare_router_data(num_internal_ports=2)
router_id = router['id']
ri = l3_agent.RouterInfo(router_id, self.conf.root_helper,
ri = l3router.RouterInfo(router_id, self.conf.root_helper,
router=router)
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
cidr = '99.0.1.9/24'
@ -440,7 +286,7 @@ class TestBasicRouterOperations(base.BaseTestCase):
def test_router_info_create(self):
id = _uuid()
ns = "ns-" + id
ri = l3_agent.RouterInfo(id, self.conf.root_helper, {}, ns_name=ns)
ri = l3router.RouterInfo(id, self.conf.root_helper, {}, ns_name=ns)
self.assertTrue(ri.ns_name.endswith(id))
@ -458,7 +304,7 @@ class TestBasicRouterOperations(base.BaseTestCase):
'routes': [],
'gw_port': ex_gw_port}
ns = "ns-" + id
ri = l3_agent.RouterInfo(id, self.conf.root_helper, router, ns_name=ns)
ri = l3router.RouterInfo(id, self.conf.root_helper, router, ns_name=ns)
self.assertTrue(ri.ns_name.endswith(id))
self.assertEqual(ri.router, router)
@ -529,7 +375,7 @@ class TestBasicRouterOperations(base.BaseTestCase):
def _test_external_gateway_action(self, action, router):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router,
ns_name=agent.get_ns_name(router['id']))
# Special setup for dvr routers
@ -600,7 +446,7 @@ class TestBasicRouterOperations(base.BaseTestCase):
def test_external_gateway_updated(self):
router = prepare_router_data(num_internal_ports=2)
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router,
ns_name=agent.get_ns_name(router['id']))
interface_name, ex_gw_port = self._prepare_ext_gw_test(agent)
@ -627,7 +473,7 @@ class TestBasicRouterOperations(base.BaseTestCase):
def _test_ext_gw_updated_dvr_agent_mode(self, host,
agent_mode, expected_call_count):
router = prepare_router_data(num_internal_ports=2)
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router)
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
interface_name, ex_gw_port = self._prepare_ext_gw_test(agent)
@ -672,7 +518,7 @@ class TestBasicRouterOperations(base.BaseTestCase):
self.conf.set_override('use_namespaces', False)
router_id = _uuid()
ri = l3_agent.RouterInfo(router_id, self.conf.root_helper, {})
ri = l3router.RouterInfo(router_id, self.conf.root_helper, {})
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
floating_ip = '20.0.0.101'
interface_name = agent.get_external_device_name(router_id)
@ -711,7 +557,7 @@ class TestBasicRouterOperations(base.BaseTestCase):
self.conf.set_override('use_namespaces', False)
router_id = _uuid()
ri = l3_agent.RouterInfo(router_id, self.conf.root_helper, {})
ri = l3router.RouterInfo(router_id, self.conf.root_helper, {})
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
fake_route1 = {'destination': '135.207.0.0/16',
@ -757,7 +603,7 @@ class TestBasicRouterOperations(base.BaseTestCase):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
router_id = _uuid()
ri = l3_agent.RouterInfo(router_id, self.conf.root_helper, {})
ri = l3router.RouterInfo(router_id, self.conf.root_helper, {})
ri.router = {}
fake_old_routes = []
@ -815,7 +661,7 @@ class TestBasicRouterOperations(base.BaseTestCase):
def test__map_internal_interfaces(self):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
router = prepare_router_data(num_internal_ports=4)
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router)
test_port = {
'mac_address': '00:12:23:34:45:56',
@ -841,7 +687,7 @@ class TestBasicRouterOperations(base.BaseTestCase):
router = prepare_router_data(num_internal_ports=4)
subnet_ids = [_get_subnet_id(port) for port in
router[l3_constants.INTERFACE_KEY]]
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router)
# Test Basic cases
@ -870,7 +716,7 @@ class TestBasicRouterOperations(base.BaseTestCase):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
router = prepare_router_data(num_internal_ports=2)
router['distributed'] = True
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router)
ports = ri.router.get(l3_constants.INTERFACE_KEY, [])
test_ports = [{'mac_address': '00:11:22:33:44:55',
@ -921,7 +767,7 @@ class TestBasicRouterOperations(base.BaseTestCase):
def test__update_arp_entry_with_no_subnet(self):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
ri = l3_agent.RouterInfo(
ri = l3router.RouterInfo(
'foo_router_id', mock.ANY,
{'distributed': True, 'gw_port_host': HOSTNAME})
with mock.patch.object(l3_agent.ip_lib, 'IPDevice') as f:
@ -949,13 +795,13 @@ class TestBasicRouterOperations(base.BaseTestCase):
def test_process_cent_router(self):
router = prepare_router_data()
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router)
self._test_process_router(ri)
def test_process_dist_router(self):
router = prepare_router_data()
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router)
subnet_id = _get_subnet_id(router[l3_constants.INTERFACE_KEY][0])
ri.router['distributed'] = True
@ -1047,7 +893,7 @@ class TestBasicRouterOperations(base.BaseTestCase):
router['routes'] = [
{'destination': '8.8.8.8/32', 'nexthop': '35.4.0.10'},
{'destination': '8.8.4.4/32', 'nexthop': '35.4.0.11'}]
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router)
ri.router = router
with contextlib.nested(mock.patch.object(agent,
@ -1104,7 +950,7 @@ vrrp_instance VR_1 {
device.addr.list.return_value = []
ri.iptables_manager.ipv4['nat'] = mock.MagicMock()
with mock.patch.object(l3_agent.LinkLocalAllocator, '_write'):
with mock.patch.object(lla.LinkLocalAllocator, '_write'):
fip_statuses = agent.process_router_floating_ip_addresses(
ri, {'id': _uuid()})
self.assertEqual({fip_id: l3_constants.FLOATINGIP_STATUS_ACTIVE},
@ -1141,7 +987,7 @@ vrrp_instance VR_1 {
router = prepare_router_data(enable_snat=True)
router[l3_constants.FLOATINGIP_KEY] = fake_floatingips['floatingips']
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router)
ri.iptables_manager.ipv4['nat'] = mock.MagicMock()
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
@ -1165,7 +1011,7 @@ vrrp_instance VR_1 {
router = prepare_router_data(enable_snat=True)
router[l3_constants.FLOATINGIP_KEY] = fake_floatingips['floatingips']
router['distributed'] = True
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router)
ri.iptables_manager.ipv4['nat'] = mock.MagicMock()
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
@ -1288,7 +1134,7 @@ vrrp_instance VR_1 {
def test_process_router_snat_disabled(self):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
router = prepare_router_data(enable_snat=True)
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router)
agent.external_gateway_added = mock.Mock()
# Process with NAT
@ -1310,7 +1156,7 @@ vrrp_instance VR_1 {
def test_process_router_snat_enabled(self):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
router = prepare_router_data(enable_snat=False)
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router)
agent.external_gateway_added = mock.Mock()
# Process without NAT
@ -1332,7 +1178,7 @@ vrrp_instance VR_1 {
def test_process_router_interface_added(self):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
router = prepare_router_data()
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router)
agent.external_gateway_added = mock.Mock()
# Process with NAT
@ -1351,7 +1197,7 @@ vrrp_instance VR_1 {
# Get NAT rules without the gw_port
gw_port = router['gw_port']
router['gw_port'] = None
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router)
agent.external_gateway_added = mock.Mock()
agent.process_router(ri)
@ -1359,7 +1205,7 @@ vrrp_instance VR_1 {
# Get NAT rules with the gw_port
router['gw_port'] = gw_port
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router)
with mock.patch.object(
agent,
@ -1374,7 +1220,7 @@ vrrp_instance VR_1 {
def _process_router_ipv6_interface_added(
self, router, ra_mode=None, addr_mode=None):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router)
agent.external_gateway_added = mock.Mock()
# Process with NAT
@ -1432,7 +1278,7 @@ vrrp_instance VR_1 {
def test_process_router_ipv6v4_interface_added(self):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
router = prepare_router_data()
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router)
agent.external_gateway_added = mock.Mock()
# Process with NAT
@ -1448,7 +1294,7 @@ vrrp_instance VR_1 {
def test_process_router_interface_removed(self):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
router = prepare_router_data(num_internal_ports=2)
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router)
agent.external_gateway_added = mock.Mock()
# Process with NAT
@ -1464,7 +1310,7 @@ vrrp_instance VR_1 {
def test_process_router_ipv6_interface_removed(self):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
router = prepare_router_data()
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router)
agent.external_gateway_added = mock.Mock()
ri.router = router
@ -1483,7 +1329,7 @@ vrrp_instance VR_1 {
def test_process_router_internal_network_added_unexpected_error(self):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
router = prepare_router_data()
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router)
agent.external_gateway_added = mock.Mock()
with mock.patch.object(
@ -1509,7 +1355,7 @@ vrrp_instance VR_1 {
def test_process_router_internal_network_removed_unexpected_error(self):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
router = prepare_router_data()
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router)
agent.external_gateway_added = mock.Mock()
# add an internal port
@ -1550,7 +1396,7 @@ vrrp_instance VR_1 {
'fixed_ip_address': '7.7.7.7',
'port_id': router[l3_constants.INTERFACE_KEY][0]['id']}]
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router)
agent.external_gateway_added = mock.Mock()
agent.process_router(ri)
@ -1583,7 +1429,7 @@ vrrp_instance VR_1 {
'fixed_ip_address': '7.7.7.7',
'port_id': router[l3_constants.INTERFACE_KEY][0]['id']}]
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router)
agent.external_gateway_added = mock.Mock()
agent.process_router(ri)
@ -1594,7 +1440,7 @@ vrrp_instance VR_1 {
{fip_id: l3_constants.FLOATINGIP_STATUS_ERROR})
def test_handle_router_snat_rules_distributed_without_snat_manager(self):
ri = l3_agent.RouterInfo(
ri = l3router.RouterInfo(
'foo_router_id', mock.ANY, {'distributed': True})
ri.iptables_manager = mock.Mock()
@ -1626,7 +1472,7 @@ vrrp_instance VR_1 {
def test_handle_router_snat_rules_add_rules(self):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
ri = l3_agent.RouterInfo(_uuid(), self.conf.root_helper, {})
ri = l3router.RouterInfo(_uuid(), self.conf.root_helper, {})
ex_gw_port = {'fixed_ips': [{'ip_address': '192.168.1.4'}]}
ri.router = {'distributed': False}
agent._handle_router_snat_rules(ri, ex_gw_port,
@ -1657,7 +1503,7 @@ vrrp_instance VR_1 {
self.mock_ip.get_devices.return_value = get_devices_return
router = prepare_router_data(enable_snat=True, num_internal_ports=1)
ri = l3_agent.RouterInfo(router['id'],
ri = l3router.RouterInfo(router['id'],
self.conf.root_helper,
router=router)
@ -1700,7 +1546,7 @@ vrrp_instance VR_1 {
router = prepare_router_data(enable_snat=True, num_internal_ports=1)
del router['gw_port']
ri = l3_agent.RouterInfo(router['id'],
ri = l3router.RouterInfo(router['id'],
self.conf.root_helper,
router=router)
@ -2030,7 +1876,7 @@ vrrp_instance VR_1 {
def test_create_dvr_gateway(self):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
router = prepare_router_data()
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router)
port_id = _uuid()
@ -2091,14 +1937,14 @@ vrrp_instance VR_1 {
'floating_network_id': _uuid(),
'port_id': _uuid()}
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router)
rtr_2_fip_name = agent.get_rtr_int_device_name(ri.router_id)
fip_2_rtr_name = agent.get_fip_int_device_name(ri.router_id)
fip_ns_name = agent.get_fip_ns_name(str(fip['floating_network_id']))
with mock.patch.object(l3_agent.LinkLocalAllocator, '_write'):
with mock.patch.object(lla.LinkLocalAllocator, '_write'):
self.device_exists.return_value = False
agent.create_rtr_2_fip_link(ri, fip['floating_network_id'])
self.mock_ip.add_veth.assert_called_with(rtr_2_fip_name,
@ -2113,17 +1959,17 @@ vrrp_instance VR_1 {
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
router = prepare_router_data()
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router)
self.device_exists.return_value = True
with mock.patch.object(l3_agent.LinkLocalAllocator, '_write'):
with mock.patch.object(lla.LinkLocalAllocator, '_write'):
agent.create_rtr_2_fip_link(ri, {})
self.assertFalse(self.mock_ip.add_veth.called)
def test_floating_ip_added_dist(self):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
router = prepare_router_data()
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router)
agent_gw_port = {'fixed_ips': [{'ip_address': '20.0.0.30',
'subnet_id': _uuid()}],
@ -2140,14 +1986,14 @@ vrrp_instance VR_1 {
'floating_network_id': _uuid(),
'port_id': _uuid()}
agent.agent_gateway_port = agent_gw_port
ri.rtr_fip_subnet = l3_agent.LinkLocalAddressPair('169.254.30.42/31')
ri.rtr_fip_subnet = lla.LinkLocalAddressPair('169.254.30.42/31')
agent.floating_ip_added_dist(ri, fip)
self.mock_rule.add_rule_from.assert_called_with('192.168.0.1',
16, FIP_PRI)
# TODO(mrsmith): add more asserts
@mock.patch.object(l3_agent.L3NATAgent, '_fip_ns_unsubscribe')
@mock.patch.object(l3_agent.LinkLocalAllocator, '_write')
@mock.patch.object(lla.LinkLocalAllocator, '_write')
def test_floating_ip_removed_dist(self, write, unsubscribe):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
router = prepare_router_data()
@ -2160,7 +2006,7 @@ vrrp_instance VR_1 {
'ip_cidr': '20.0.0.30/24'}
fip_cidr = '11.22.33.44/24'
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper,
ri = l3router.RouterInfo(router['id'], self.conf.root_helper,
router=router)
ri.dist_fip_count = 2
agent.fip_ns_subscribers.add(ri.router_id)
@ -2168,7 +2014,7 @@ vrrp_instance VR_1 {
ri.fip_2_rtr = '11.22.33.42'
ri.rtr_2_fip = '11.22.33.40'
agent.agent_gateway_port = agent_gw_port
s = l3_agent.LinkLocalAddressPair('169.254.30.42/31')
s = lla.LinkLocalAddressPair('169.254.30.42/31')
ri.rtr_fip_subnet = s
agent.floating_ip_removed_dist(ri, fip_cidr)
self.mock_rule.delete_rule_priority.assert_called_with(FIP_PRI)
@ -2278,7 +2124,7 @@ vrrp_instance VR_1 {
router['distributed'] = True
router['gw_port_host'] = HOSTNAME
ri = l3_agent.RouterInfo(router['id'], self.conf.root_helper, router)
ri = l3router.RouterInfo(router['id'], self.conf.root_helper, router)
vm_floating_ip = '19.4.4.2'
ri.floating_ips_dict[vm_floating_ip] = FIP_PRI
ri.dist_fip_count = 1
@ -2321,7 +2167,7 @@ class TestL3AgentEventHandler(base.BaseTestCase):
def setUp(self):
super(TestL3AgentEventHandler, self).setUp()
cfg.CONF.register_opts(l3_agent.L3NATAgent.OPTS)
cfg.CONF.register_opts(l3_ha_agent.OPTS)
cfg.CONF.register_opts(ha.OPTS)
agent_config.register_interface_driver_opts_helper(cfg.CONF)
agent_config.register_use_namespaces_opts_helper(cfg.CONF)
cfg.CONF.set_override(
@ -2347,7 +2193,7 @@ class TestL3AgentEventHandler(base.BaseTestCase):
driver_cls.return_value = mock_driver
l3_plugin_p = mock.patch(
'neutron.agent.l3_agent.L3PluginApi')
'neutron.agent.l3.agent.L3PluginApi')
l3_plugin_cls = l3_plugin_p.start()
l3_plugin_cls.return_value = mock.MagicMock()
@ -2370,7 +2216,7 @@ class TestL3AgentEventHandler(base.BaseTestCase):
cfg.CONF.set_override('debug', True)
self.external_process_p.stop()
ri = l3_agent.RouterInfo(router_id, None, None)
ri = l3router.RouterInfo(router_id, None, None)
try:
with mock.patch(ip_class_path) as ip_mock:
self.agent._spawn_metadata_proxy(ri.router_id, ri.ns_name)

View File

@ -0,0 +1,96 @@
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
import netaddr
from neutron.agent.l3 import link_local_allocator as lla
from neutron.tests import base
class TestLinkLocalAddrAllocator(base.BaseTestCase):
def setUp(self):
super(TestLinkLocalAddrAllocator, self).setUp()
self.subnet = netaddr.IPNetwork('169.254.31.0/24')
def test__init__(self):
a = lla.LinkLocalAllocator('/file', self.subnet.cidr)
self.assertEqual('/file', a.state_file)
self.assertEqual({}, a.allocations)
def test__init__readfile(self):
with mock.patch.object(lla.LinkLocalAllocator, '_read') as read:
read.return_value = ["da873ca2,169.254.31.28/31\n"]
a = lla.LinkLocalAllocator('/file', self.subnet.cidr)
self.assertTrue('da873ca2' in a.remembered)
self.assertEqual({}, a.allocations)
def test_allocate(self):
a = lla.LinkLocalAllocator('/file', self.subnet.cidr)
with mock.patch.object(lla.LinkLocalAllocator, '_write') as write:
subnet = a.allocate('deadbeef')
self.assertTrue('deadbeef' in a.allocations)
self.assertTrue(subnet not in a.pool)
self._check_allocations(a.allocations)
write.assert_called_once_with(['deadbeef,%s\n' % subnet.cidr])
def test_allocate_from_file(self):
with mock.patch.object(lla.LinkLocalAllocator, '_read') as read:
read.return_value = ["deadbeef,169.254.31.88/31\n"]
a = lla.LinkLocalAllocator('/file', self.subnet.cidr)
with mock.patch.object(lla.LinkLocalAllocator, '_write') as write:
subnet = a.allocate('deadbeef')
self.assertEqual(netaddr.IPNetwork('169.254.31.88/31'), subnet)
self.assertTrue(subnet not in a.pool)
self._check_allocations(a.allocations)
self.assertFalse(write.called)
def test_allocate_exhausted_pool(self):
subnet = netaddr.IPNetwork('169.254.31.0/31')
with mock.patch.object(lla.LinkLocalAllocator, '_read') as read:
read.return_value = ["deadbeef,169.254.31.0/31\n"]
a = lla.LinkLocalAllocator('/file', subnet.cidr)
with mock.patch.object(lla.LinkLocalAllocator, '_write') as write:
allocation = a.allocate('abcdef12')
self.assertEqual(subnet, allocation)
self.assertFalse('deadbeef' in a.allocations)
self.assertTrue('abcdef12' in a.allocations)
self.assertTrue(allocation not in a.pool)
self._check_allocations(a.allocations)
write.assert_called_once_with(['abcdef12,%s\n' % allocation.cidr])
self.assertRaises(RuntimeError, a.allocate, 'deadbeef')
def test_release(self):
with mock.patch.object(lla.LinkLocalAllocator, '_write') as write:
a = lla.LinkLocalAllocator('/file', self.subnet.cidr)
subnet = a.allocate('deadbeef')
write.reset_mock()
a.release('deadbeef')
self.assertTrue('deadbeef' not in a.allocations)
self.assertTrue(subnet in a.pool)
self.assertEqual({}, a.allocations)
write.assert_called_once_with([])
def _check_allocations(self, allocations):
for key, subnet in allocations.items():
self.assertTrue(subnet in self.subnet)
self.assertEqual(subnet.prefixlen, 31)

View File

@ -0,0 +1,102 @@
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import datetime
from neutron.agent.l3 import router_processing_queue as l3_queue
from neutron.openstack.common import uuidutils
from neutron.tests import base
_uuid = uuidutils.generate_uuid
FAKE_ID = _uuid()
FAKE_ID_2 = _uuid()
class TestExclusiveRouterProcessor(base.BaseTestCase):
def setUp(self):
super(TestExclusiveRouterProcessor, self).setUp()
def test_i_am_master(self):
master = l3_queue.ExclusiveRouterProcessor(FAKE_ID)
not_master = l3_queue.ExclusiveRouterProcessor(FAKE_ID)
master_2 = l3_queue.ExclusiveRouterProcessor(FAKE_ID_2)
not_master_2 = l3_queue.ExclusiveRouterProcessor(FAKE_ID_2)
self.assertTrue(master._i_am_master())
self.assertFalse(not_master._i_am_master())
self.assertTrue(master_2._i_am_master())
self.assertFalse(not_master_2._i_am_master())
master.__exit__(None, None, None)
master_2.__exit__(None, None, None)
def test_master(self):
master = l3_queue.ExclusiveRouterProcessor(FAKE_ID)
not_master = l3_queue.ExclusiveRouterProcessor(FAKE_ID)
master_2 = l3_queue.ExclusiveRouterProcessor(FAKE_ID_2)
not_master_2 = l3_queue.ExclusiveRouterProcessor(FAKE_ID_2)
self.assertEqual(master._master, master)
self.assertEqual(not_master._master, master)
self.assertEqual(master_2._master, master_2)
self.assertEqual(not_master_2._master, master_2)
master.__exit__(None, None, None)
master_2.__exit__(None, None, None)
def test__enter__(self):
self.assertFalse(FAKE_ID in l3_queue.ExclusiveRouterProcessor._masters)
master = l3_queue.ExclusiveRouterProcessor(FAKE_ID)
master.__enter__()
self.assertTrue(FAKE_ID in l3_queue.ExclusiveRouterProcessor._masters)
master.__exit__(None, None, None)
def test__exit__(self):
master = l3_queue.ExclusiveRouterProcessor(FAKE_ID)
not_master = l3_queue.ExclusiveRouterProcessor(FAKE_ID)
master.__enter__()
self.assertTrue(FAKE_ID in l3_queue.ExclusiveRouterProcessor._masters)
not_master.__enter__()
not_master.__exit__(None, None, None)
self.assertTrue(FAKE_ID in l3_queue.ExclusiveRouterProcessor._masters)
master.__exit__(None, None, None)
self.assertFalse(FAKE_ID in l3_queue.ExclusiveRouterProcessor._masters)
def test_data_fetched_since(self):
master = l3_queue.ExclusiveRouterProcessor(FAKE_ID)
self.assertEqual(master._get_router_data_timestamp(),
datetime.datetime.min)
ts1 = datetime.datetime.utcnow() - datetime.timedelta(seconds=10)
ts2 = datetime.datetime.utcnow()
master.fetched_and_processed(ts2)
self.assertEqual(master._get_router_data_timestamp(), ts2)
master.fetched_and_processed(ts1)
self.assertEqual(master._get_router_data_timestamp(), ts2)
master.__exit__(None, None, None)
def test_updates(self):
master = l3_queue.ExclusiveRouterProcessor(FAKE_ID)
not_master = l3_queue.ExclusiveRouterProcessor(FAKE_ID)
master.queue_update(l3_queue.RouterUpdate(FAKE_ID, 0))
not_master.queue_update(l3_queue.RouterUpdate(FAKE_ID, 0))
for update in not_master.updates():
raise Exception("Only the master should process a router")
self.assertEqual(2, len([i for i in master.updates()]))

View File

@ -103,7 +103,7 @@ console_scripts =
neutron-dhcp-agent = neutron.agent.dhcp_agent:main
neutron-hyperv-agent = neutron.plugins.hyperv.agent.hyperv_neutron_agent:main
neutron-ibm-agent = neutron.plugins.ibm.agent.sdnve_neutron_agent:main
neutron-l3-agent = neutron.agent.l3_agent:main
neutron-l3-agent = neutron.agent.l3.agent:main
neutron-lbaas-agent = neutron.services.loadbalancer.agent.agent:main
neutron-linuxbridge-agent = neutron.plugins.linuxbridge.agent.linuxbridge_neutron_agent:main
neutron-metadata-agent = neutron.agent.metadata.agent:main