Merge "Improve port dhcp Provisioning" into stable/queens
This commit is contained in:
commit
8c4431562e
@ -105,7 +105,7 @@ class ExclusiveResourceProcessor(object):
|
|||||||
|
|
||||||
if id not in self._masters:
|
if id not in self._masters:
|
||||||
self._masters[id] = self
|
self._masters[id] = self
|
||||||
self._queue = []
|
self._queue = Queue.PriorityQueue(-1)
|
||||||
|
|
||||||
self._master = self._masters[id]
|
self._master = self._masters[id]
|
||||||
|
|
||||||
@ -135,7 +135,7 @@ class ExclusiveResourceProcessor(object):
|
|||||||
resource is being processed. These updates have already bubbled to
|
resource is being processed. These updates have already bubbled to
|
||||||
the front of the ResourceProcessingQueue.
|
the front of the ResourceProcessingQueue.
|
||||||
"""
|
"""
|
||||||
self._master._queue.append(update)
|
self._master._queue.put(update)
|
||||||
|
|
||||||
def updates(self):
|
def updates(self):
|
||||||
"""Processes the resource until updates stop coming
|
"""Processes the resource until updates stop coming
|
||||||
@ -144,13 +144,14 @@ class ExclusiveResourceProcessor(object):
|
|||||||
may come in from other workers while it is in progress. This method
|
may come in from other workers while it is in progress. This method
|
||||||
loops until they stop coming.
|
loops until they stop coming.
|
||||||
"""
|
"""
|
||||||
if self._i_am_master():
|
while self._i_am_master():
|
||||||
while self._queue:
|
if self._queue.empty():
|
||||||
# Remove the update from the queue even if it is old.
|
return
|
||||||
update = self._queue.pop(0)
|
# Get the update from the queue even if it is old.
|
||||||
# Process the update only if it is fresh.
|
update = self._queue.get()
|
||||||
if self._get_resource_data_timestamp() < update.timestamp:
|
# Process the update only if it is fresh.
|
||||||
yield update
|
if self._get_resource_data_timestamp() < update.timestamp:
|
||||||
|
yield update
|
||||||
|
|
||||||
|
|
||||||
class ResourceProcessingQueue(object):
|
class ResourceProcessingQueue(object):
|
||||||
|
@ -21,7 +21,6 @@ from neutron_lib.agent import constants as agent_consts
|
|||||||
from neutron_lib import constants
|
from neutron_lib import constants
|
||||||
from neutron_lib import context
|
from neutron_lib import context
|
||||||
from neutron_lib import exceptions
|
from neutron_lib import exceptions
|
||||||
from neutron_lib.utils import runtime
|
|
||||||
from oslo_concurrency import lockutils
|
from oslo_concurrency import lockutils
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
@ -32,6 +31,7 @@ from oslo_utils import importutils
|
|||||||
import six
|
import six
|
||||||
|
|
||||||
from neutron._i18n import _
|
from neutron._i18n import _
|
||||||
|
from neutron.agent.common import resource_processing_queue as queue
|
||||||
from neutron.agent.linux import dhcp
|
from neutron.agent.linux import dhcp
|
||||||
from neutron.agent.linux import external_process
|
from neutron.agent.linux import external_process
|
||||||
from neutron.agent.metadata import driver as metadata_driver
|
from neutron.agent.metadata import driver as metadata_driver
|
||||||
@ -45,6 +45,8 @@ from neutron import manager
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
_SYNC_STATE_LOCK = lockutils.ReaderWriterLock()
|
_SYNC_STATE_LOCK = lockutils.ReaderWriterLock()
|
||||||
|
|
||||||
|
DEFAULT_PRIORITY = 255
|
||||||
|
|
||||||
|
|
||||||
def _sync_lock(f):
|
def _sync_lock(f):
|
||||||
"""Decorator to block all operations for a global sync call."""
|
"""Decorator to block all operations for a global sync call."""
|
||||||
@ -64,12 +66,6 @@ def _wait_if_syncing(f):
|
|||||||
return wrapped
|
return wrapped
|
||||||
|
|
||||||
|
|
||||||
def _net_lock(network_id):
|
|
||||||
"""Returns a context manager lock based on network_id."""
|
|
||||||
lock_name = 'dhcp-agent-network-lock-%s' % network_id
|
|
||||||
return lockutils.lock(lock_name, runtime.SYNCHRONIZED_PREFIX)
|
|
||||||
|
|
||||||
|
|
||||||
class DhcpAgent(manager.Manager):
|
class DhcpAgent(manager.Manager):
|
||||||
"""DHCP agent service manager.
|
"""DHCP agent service manager.
|
||||||
|
|
||||||
@ -100,6 +96,7 @@ class DhcpAgent(manager.Manager):
|
|||||||
self._process_monitor = external_process.ProcessMonitor(
|
self._process_monitor = external_process.ProcessMonitor(
|
||||||
config=self.conf,
|
config=self.conf,
|
||||||
resource_type='dhcp')
|
resource_type='dhcp')
|
||||||
|
self._queue = queue.ResourceProcessingQueue()
|
||||||
|
|
||||||
def init_host(self):
|
def init_host(self):
|
||||||
self.sync_state()
|
self.sync_state()
|
||||||
@ -128,6 +125,7 @@ class DhcpAgent(manager.Manager):
|
|||||||
"""Activate the DHCP agent."""
|
"""Activate the DHCP agent."""
|
||||||
self.periodic_resync()
|
self.periodic_resync()
|
||||||
self.start_ready_ports_loop()
|
self.start_ready_ports_loop()
|
||||||
|
eventlet.spawn_n(self._process_loop)
|
||||||
|
|
||||||
def call_driver(self, action, network, **action_kwargs):
|
def call_driver(self, action, network, **action_kwargs):
|
||||||
"""Invoke an action on a DHCP driver instance."""
|
"""Invoke an action on a DHCP driver instance."""
|
||||||
@ -354,36 +352,64 @@ class DhcpAgent(manager.Manager):
|
|||||||
# Update the metadata proxy after the dhcp driver has been updated
|
# Update the metadata proxy after the dhcp driver has been updated
|
||||||
self.update_isolated_metadata_proxy(network)
|
self.update_isolated_metadata_proxy(network)
|
||||||
|
|
||||||
@_wait_if_syncing
|
|
||||||
def network_create_end(self, context, payload):
|
def network_create_end(self, context, payload):
|
||||||
"""Handle the network.create.end notification event."""
|
"""Handle the network.create.end notification event."""
|
||||||
network_id = payload['network']['id']
|
update = queue.ResourceUpdate(payload['network']['id'],
|
||||||
with _net_lock(network_id):
|
payload.get('priority',
|
||||||
self.enable_dhcp_helper(network_id)
|
DEFAULT_PRIORITY),
|
||||||
|
action='_network_create',
|
||||||
|
resource=payload)
|
||||||
|
self._queue.add(update)
|
||||||
|
|
||||||
@_wait_if_syncing
|
@_wait_if_syncing
|
||||||
|
def _network_create(self, payload):
|
||||||
|
network_id = payload['network']['id']
|
||||||
|
self.enable_dhcp_helper(network_id)
|
||||||
|
|
||||||
def network_update_end(self, context, payload):
|
def network_update_end(self, context, payload):
|
||||||
"""Handle the network.update.end notification event."""
|
"""Handle the network.update.end notification event."""
|
||||||
network_id = payload['network']['id']
|
update = queue.ResourceUpdate(payload['network']['id'],
|
||||||
with _net_lock(network_id):
|
payload.get('priority',
|
||||||
if payload['network']['admin_state_up']:
|
DEFAULT_PRIORITY),
|
||||||
self.enable_dhcp_helper(network_id)
|
action='_network_update',
|
||||||
else:
|
resource=payload)
|
||||||
self.disable_dhcp_helper(network_id)
|
self._queue.add(update)
|
||||||
|
|
||||||
@_wait_if_syncing
|
@_wait_if_syncing
|
||||||
def network_delete_end(self, context, payload):
|
def _network_update(self, payload):
|
||||||
"""Handle the network.delete.end notification event."""
|
network_id = payload['network']['id']
|
||||||
network_id = payload['network_id']
|
if payload['network']['admin_state_up']:
|
||||||
with _net_lock(network_id):
|
self.enable_dhcp_helper(network_id)
|
||||||
|
else:
|
||||||
self.disable_dhcp_helper(network_id)
|
self.disable_dhcp_helper(network_id)
|
||||||
|
|
||||||
|
def network_delete_end(self, context, payload):
|
||||||
|
"""Handle the network.delete.end notification event."""
|
||||||
|
update = queue.ResourceUpdate(payload['network_id'],
|
||||||
|
payload.get('priority',
|
||||||
|
DEFAULT_PRIORITY),
|
||||||
|
action='_network_delete',
|
||||||
|
resource=payload)
|
||||||
|
self._queue.add(update)
|
||||||
|
|
||||||
@_wait_if_syncing
|
@_wait_if_syncing
|
||||||
|
def _network_delete(self, payload):
|
||||||
|
network_id = payload['network_id']
|
||||||
|
self.disable_dhcp_helper(network_id)
|
||||||
|
|
||||||
def subnet_update_end(self, context, payload):
|
def subnet_update_end(self, context, payload):
|
||||||
"""Handle the subnet.update.end notification event."""
|
"""Handle the subnet.update.end notification event."""
|
||||||
|
update = queue.ResourceUpdate(payload['subnet']['network_id'],
|
||||||
|
payload.get('priority',
|
||||||
|
DEFAULT_PRIORITY),
|
||||||
|
action='_subnet_update',
|
||||||
|
resource=payload)
|
||||||
|
self._queue.add(update)
|
||||||
|
|
||||||
|
@_wait_if_syncing
|
||||||
|
def _subnet_update(self, payload):
|
||||||
network_id = payload['subnet']['network_id']
|
network_id = payload['subnet']['network_id']
|
||||||
with _net_lock(network_id):
|
self.refresh_dhcp_helper(network_id)
|
||||||
self.refresh_dhcp_helper(network_id)
|
|
||||||
|
|
||||||
# Use the update handler for the subnet create event.
|
# Use the update handler for the subnet create event.
|
||||||
subnet_create_end = subnet_update_end
|
subnet_create_end = subnet_update_end
|
||||||
@ -406,31 +432,63 @@ class DhcpAgent(manager.Manager):
|
|||||||
port = self.cache.get_port_by_id(port_id)
|
port = self.cache.get_port_by_id(port_id)
|
||||||
return port.network_id if port else None
|
return port.network_id if port else None
|
||||||
|
|
||||||
@_wait_if_syncing
|
|
||||||
def subnet_delete_end(self, context, payload):
|
def subnet_delete_end(self, context, payload):
|
||||||
"""Handle the subnet.delete.end notification event."""
|
"""Handle the subnet.delete.end notification event."""
|
||||||
network_id = self._get_network_lock_id(payload)
|
network_id = self._get_network_lock_id(payload)
|
||||||
if not network_id:
|
if not network_id:
|
||||||
return
|
return
|
||||||
with _net_lock(network_id):
|
update = queue.ResourceUpdate(network_id,
|
||||||
subnet_id = payload['subnet_id']
|
payload.get('priority',
|
||||||
network = self.cache.get_network_by_subnet_id(subnet_id)
|
DEFAULT_PRIORITY),
|
||||||
if not network:
|
action='_subnet_delete',
|
||||||
return
|
resource=payload)
|
||||||
self.refresh_dhcp_helper(network.id)
|
self._queue.add(update)
|
||||||
|
|
||||||
@_wait_if_syncing
|
@_wait_if_syncing
|
||||||
|
def _subnet_delete(self, payload):
|
||||||
|
network_id = self._get_network_lock_id(payload)
|
||||||
|
if not network_id:
|
||||||
|
return
|
||||||
|
subnet_id = payload['subnet_id']
|
||||||
|
network = self.cache.get_network_by_subnet_id(subnet_id)
|
||||||
|
if not network:
|
||||||
|
return
|
||||||
|
self.refresh_dhcp_helper(network.id)
|
||||||
|
|
||||||
|
def _process_loop(self):
|
||||||
|
LOG.debug("Starting _process_loop")
|
||||||
|
|
||||||
|
pool = eventlet.GreenPool(size=8)
|
||||||
|
while True:
|
||||||
|
pool.spawn_n(self._process_resource_update)
|
||||||
|
|
||||||
|
def _process_resource_update(self):
|
||||||
|
for tmp, update in self._queue.each_update_to_next_resource():
|
||||||
|
method = getattr(self, update.action)
|
||||||
|
method(update.resource)
|
||||||
|
|
||||||
def port_update_end(self, context, payload):
|
def port_update_end(self, context, payload):
|
||||||
"""Handle the port.update.end notification event."""
|
"""Handle the port.update.end notification event."""
|
||||||
updated_port = dhcp.DictModel(payload['port'])
|
updated_port = dhcp.DictModel(payload['port'])
|
||||||
with _net_lock(updated_port.network_id):
|
if self.cache.is_port_message_stale(updated_port):
|
||||||
if self.cache.is_port_message_stale(payload['port']):
|
LOG.debug("Discarding stale port update: %s", updated_port)
|
||||||
LOG.debug("Discarding stale port update: %s", updated_port)
|
return
|
||||||
return
|
update = queue.ResourceUpdate(updated_port.network_id,
|
||||||
network = self.cache.get_network_by_id(updated_port.network_id)
|
payload.get('priority',
|
||||||
if not network:
|
DEFAULT_PRIORITY),
|
||||||
return
|
action='_port_update',
|
||||||
self.reload_allocations(updated_port, network)
|
resource=updated_port)
|
||||||
|
self._queue.add(update)
|
||||||
|
|
||||||
|
@_wait_if_syncing
|
||||||
|
def _port_update(self, updated_port):
|
||||||
|
if self.cache.is_port_message_stale(updated_port):
|
||||||
|
LOG.debug("Discarding stale port update: %s", updated_port)
|
||||||
|
return
|
||||||
|
network = self.cache.get_network_by_id(updated_port.network_id)
|
||||||
|
if not network:
|
||||||
|
return
|
||||||
|
self.reload_allocations(updated_port, network)
|
||||||
|
|
||||||
def reload_allocations(self, port, network):
|
def reload_allocations(self, port, network):
|
||||||
LOG.info("Trigger reload_allocations for port %s", port)
|
LOG.info("Trigger reload_allocations for port %s", port)
|
||||||
@ -466,50 +524,67 @@ class DhcpAgent(manager.Manager):
|
|||||||
port['network_id'], self.conf.host)
|
port['network_id'], self.conf.host)
|
||||||
return port['device_id'] == thishost
|
return port['device_id'] == thishost
|
||||||
|
|
||||||
@_wait_if_syncing
|
|
||||||
def port_create_end(self, context, payload):
|
def port_create_end(self, context, payload):
|
||||||
"""Handle the port.create.end notification event."""
|
"""Handle the port.create.end notification event."""
|
||||||
created_port = dhcp.DictModel(payload['port'])
|
created_port = dhcp.DictModel(payload['port'])
|
||||||
with _net_lock(created_port.network_id):
|
update = queue.ResourceUpdate(created_port.network_id,
|
||||||
network = self.cache.get_network_by_id(created_port.network_id)
|
payload.get('priority',
|
||||||
if not network:
|
DEFAULT_PRIORITY),
|
||||||
return
|
action='_port_create',
|
||||||
new_ips = {i['ip_address'] for i in created_port['fixed_ips']}
|
resource=created_port)
|
||||||
for port_cached in network.ports:
|
self._queue.add(update)
|
||||||
# if there are other ports cached with the same ip address in
|
|
||||||
# the same network this indicate that the cache is out of sync
|
|
||||||
cached_ips = {i['ip_address']
|
|
||||||
for i in port_cached['fixed_ips']}
|
|
||||||
if new_ips.intersection(cached_ips):
|
|
||||||
self.schedule_resync("Duplicate IP addresses found, "
|
|
||||||
"DHCP cache is out of sync",
|
|
||||||
created_port.network_id)
|
|
||||||
return
|
|
||||||
self.reload_allocations(created_port, network)
|
|
||||||
|
|
||||||
@_wait_if_syncing
|
@_wait_if_syncing
|
||||||
|
def _port_create(self, created_port):
|
||||||
|
network = self.cache.get_network_by_id(created_port.network_id)
|
||||||
|
if not network:
|
||||||
|
return
|
||||||
|
new_ips = {i['ip_address'] for i in created_port['fixed_ips']}
|
||||||
|
for port_cached in network.ports:
|
||||||
|
# if there are other ports cached with the same ip address in
|
||||||
|
# the same network this indicate that the cache is out of sync
|
||||||
|
cached_ips = {i['ip_address']
|
||||||
|
for i in port_cached['fixed_ips']}
|
||||||
|
if new_ips.intersection(cached_ips):
|
||||||
|
self.schedule_resync("Duplicate IP addresses found, "
|
||||||
|
"DHCP cache is out of sync",
|
||||||
|
created_port.network_id)
|
||||||
|
return
|
||||||
|
self.reload_allocations(created_port, network)
|
||||||
|
|
||||||
def port_delete_end(self, context, payload):
|
def port_delete_end(self, context, payload):
|
||||||
"""Handle the port.delete.end notification event."""
|
"""Handle the port.delete.end notification event."""
|
||||||
network_id = self._get_network_lock_id(payload)
|
network_id = self._get_network_lock_id(payload)
|
||||||
if not network_id:
|
if not network_id:
|
||||||
return
|
return
|
||||||
with _net_lock(network_id):
|
update = queue.ResourceUpdate(network_id,
|
||||||
port_id = payload['port_id']
|
payload.get('priority',
|
||||||
port = self.cache.get_port_by_id(port_id)
|
DEFAULT_PRIORITY),
|
||||||
self.cache.deleted_ports.add(port_id)
|
action='_port_delete',
|
||||||
if not port:
|
resource=payload)
|
||||||
return
|
self._queue.add(update)
|
||||||
network = self.cache.get_network_by_id(port.network_id)
|
|
||||||
self.cache.remove_port(port)
|
@_wait_if_syncing
|
||||||
if self._is_port_on_this_agent(port):
|
def _port_delete(self, payload):
|
||||||
# the agent's port has been deleted. disable the service
|
network_id = self._get_network_lock_id(payload)
|
||||||
# and add the network to the resync list to create
|
if not network_id:
|
||||||
# (or acquire a reserved) port.
|
return
|
||||||
self.call_driver('disable', network)
|
port_id = payload['port_id']
|
||||||
self.schedule_resync("Agent port was deleted", port.network_id)
|
port = self.cache.get_port_by_id(port_id)
|
||||||
else:
|
self.cache.deleted_ports.add(port_id)
|
||||||
self.call_driver('reload_allocations', network)
|
if not port:
|
||||||
self.update_isolated_metadata_proxy(network)
|
return
|
||||||
|
network = self.cache.get_network_by_id(port.network_id)
|
||||||
|
self.cache.remove_port(port)
|
||||||
|
if self._is_port_on_this_agent(port):
|
||||||
|
# the agent's port has been deleted. disable the service
|
||||||
|
# and add the network to the resync list to create
|
||||||
|
# (or acquire a reserved) port.
|
||||||
|
self.call_driver('disable', network)
|
||||||
|
self.schedule_resync("Agent port was deleted", port.network_id)
|
||||||
|
else:
|
||||||
|
self.call_driver('reload_allocations', network)
|
||||||
|
self.update_isolated_metadata_proxy(network)
|
||||||
|
|
||||||
def update_isolated_metadata_proxy(self, network):
|
def update_isolated_metadata_proxy(self, network):
|
||||||
"""Spawn or kill metadata proxy.
|
"""Spawn or kill metadata proxy.
|
||||||
|
@ -13,6 +13,9 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import copy
|
||||||
|
import random
|
||||||
|
|
||||||
from neutron_lib.callbacks import events
|
from neutron_lib.callbacks import events
|
||||||
from neutron_lib.callbacks import registry
|
from neutron_lib.callbacks import registry
|
||||||
from neutron_lib.callbacks import resources
|
from neutron_lib.callbacks import resources
|
||||||
@ -26,6 +29,36 @@ from neutron.common import rpc as n_rpc
|
|||||||
from neutron.common import topics
|
from neutron.common import topics
|
||||||
from neutron.common import utils
|
from neutron.common import utils
|
||||||
|
|
||||||
|
# Priorities - lower value is higher priority
|
||||||
|
PRIORITY_NETWORK_CREATE = 0
|
||||||
|
PRIORITY_NETWORK_UPDATE = 1
|
||||||
|
PRIORITY_NETWORK_DELETE = 2
|
||||||
|
PRIORITY_SUBNET_UPDATE = 3
|
||||||
|
PRIORITY_SUBNET_DELETE = 4
|
||||||
|
# In order to improve port dhcp provisioning when nova concurrently create
|
||||||
|
# multiple vms, I classify the port_create_end message to two levels, the
|
||||||
|
# high-level message only cast to one agent, the low-level message cast to all
|
||||||
|
# other agent. In this way, When there are a large number of ports that need to
|
||||||
|
# be processed, we can dispatch the high priority message of port to different
|
||||||
|
# agent, so that the processed port will not block other port's processing in
|
||||||
|
# other dhcp agents.
|
||||||
|
PRIORITY_PORT_CREATE_HIGH = 5
|
||||||
|
PRIORITY_PORT_CREATE_LOW = 6
|
||||||
|
PRIORITY_PORT_UPDATE = 7
|
||||||
|
PRIORITY_PORT_DELETE = 8
|
||||||
|
|
||||||
|
METHOD_PRIORITY_MAP = {
|
||||||
|
'network_create_end': PRIORITY_NETWORK_CREATE,
|
||||||
|
'network_update_end': PRIORITY_NETWORK_UPDATE,
|
||||||
|
'network_delete_end': PRIORITY_NETWORK_DELETE,
|
||||||
|
'subnet_create_end': PRIORITY_SUBNET_UPDATE,
|
||||||
|
'subnet_update_end': PRIORITY_SUBNET_UPDATE,
|
||||||
|
'subnet_delete_end': PRIORITY_SUBNET_DELETE,
|
||||||
|
'port_create_end': PRIORITY_PORT_CREATE_LOW,
|
||||||
|
'port_update_end': PRIORITY_PORT_UPDATE,
|
||||||
|
'port_delete_end': PRIORITY_PORT_DELETE
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -101,7 +134,8 @@ class DhcpAgentNotifyAPI(object):
|
|||||||
for agent in new_agents:
|
for agent in new_agents:
|
||||||
self._cast_message(
|
self._cast_message(
|
||||||
context, 'network_create_end',
|
context, 'network_create_end',
|
||||||
{'network': {'id': network['id']}}, agent['host'])
|
{'network': {'id': network['id']},
|
||||||
|
'priority': PRIORITY_NETWORK_CREATE}, agent['host'])
|
||||||
elif not existing_agents:
|
elif not existing_agents:
|
||||||
LOG.warning('Unable to schedule network %s: no agents '
|
LOG.warning('Unable to schedule network %s: no agents '
|
||||||
'available; will retry on subsequent port '
|
'available; will retry on subsequent port '
|
||||||
@ -147,6 +181,7 @@ class DhcpAgentNotifyAPI(object):
|
|||||||
|
|
||||||
def _notify_agents(self, context, method, payload, network_id):
|
def _notify_agents(self, context, method, payload, network_id):
|
||||||
"""Notify all the agents that are hosting the network."""
|
"""Notify all the agents that are hosting the network."""
|
||||||
|
payload['priority'] = METHOD_PRIORITY_MAP.get(method)
|
||||||
# fanout is required as we do not know who is "listening"
|
# fanout is required as we do not know who is "listening"
|
||||||
no_agents = not utils.is_extension_supported(
|
no_agents = not utils.is_extension_supported(
|
||||||
self.plugin, constants.DHCP_AGENT_SCHEDULER_EXT_ALIAS)
|
self.plugin, constants.DHCP_AGENT_SCHEDULER_EXT_ALIAS)
|
||||||
@ -184,10 +219,21 @@ class DhcpAgentNotifyAPI(object):
|
|||||||
return
|
return
|
||||||
enabled_agents = self._get_enabled_agents(
|
enabled_agents = self._get_enabled_agents(
|
||||||
context, network, agents, method, payload)
|
context, network, agents, method, payload)
|
||||||
|
|
||||||
|
if method == 'port_create_end':
|
||||||
|
high_agent = enabled_agents.pop(
|
||||||
|
random.randint(0, len(enabled_agents) - 1))
|
||||||
|
self._notify_high_priority_agent(
|
||||||
|
context, copy.deepcopy(payload), high_agent)
|
||||||
for agent in enabled_agents:
|
for agent in enabled_agents:
|
||||||
self._cast_message(
|
self._cast_message(
|
||||||
context, method, payload, agent.host, agent.topic)
|
context, method, payload, agent.host, agent.topic)
|
||||||
|
|
||||||
|
def _notify_high_priority_agent(self, context, payload, agent):
|
||||||
|
payload['priority'] = PRIORITY_PORT_CREATE_HIGH
|
||||||
|
self._cast_message(context, "port_create_end",
|
||||||
|
payload, agent.host, agent.topic)
|
||||||
|
|
||||||
def _cast_message(self, context, method, payload, host,
|
def _cast_message(self, context, method, payload, host,
|
||||||
topic=topics.DHCP_AGENT):
|
topic=topics.DHCP_AGENT):
|
||||||
"""Cast the payload to the dhcp agent running on the host."""
|
"""Cast the payload to the dhcp agent running on the host."""
|
||||||
@ -201,11 +247,13 @@ class DhcpAgentNotifyAPI(object):
|
|||||||
|
|
||||||
def network_removed_from_agent(self, context, network_id, host):
|
def network_removed_from_agent(self, context, network_id, host):
|
||||||
self._cast_message(context, 'network_delete_end',
|
self._cast_message(context, 'network_delete_end',
|
||||||
{'network_id': network_id}, host)
|
{'network_id': network_id,
|
||||||
|
'priority': PRIORITY_NETWORK_DELETE}, host)
|
||||||
|
|
||||||
def network_added_to_agent(self, context, network_id, host):
|
def network_added_to_agent(self, context, network_id, host):
|
||||||
self._cast_message(context, 'network_create_end',
|
self._cast_message(context, 'network_create_end',
|
||||||
{'network': {'id': network_id}}, host)
|
{'network': {'id': network_id},
|
||||||
|
'priority': PRIORITY_NETWORK_CREATE}, host)
|
||||||
|
|
||||||
def agent_updated(self, context, admin_state_up, host):
|
def agent_updated(self, context, admin_state_up, host):
|
||||||
self._cast_message(context, 'agent_updated',
|
self._cast_message(context, 'agent_updated',
|
||||||
|
@ -48,6 +48,7 @@ DHCP_PLUGIN = '%s.%s' % (rpc_api.__module__, rpc_api.__name__)
|
|||||||
FAKE_NETWORK_UUID = '12345678-1234-5678-1234567890ab'
|
FAKE_NETWORK_UUID = '12345678-1234-5678-1234567890ab'
|
||||||
FAKE_NETWORK_DHCP_NS = "qdhcp-%s" % FAKE_NETWORK_UUID
|
FAKE_NETWORK_DHCP_NS = "qdhcp-%s" % FAKE_NETWORK_UUID
|
||||||
FAKE_TENANT_ID = 'aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa'
|
FAKE_TENANT_ID = 'aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa'
|
||||||
|
FAKE_PRIORITY = 6
|
||||||
|
|
||||||
|
|
||||||
fake_subnet1_allocation_pools = dhcp.DictModel(dict(id='', start='172.9.9.2',
|
fake_subnet1_allocation_pools = dhcp.DictModel(dict(id='', start='172.9.9.2',
|
||||||
@ -284,11 +285,15 @@ class TestDhcpAgent(base.BaseTestCase):
|
|||||||
dhcp = dhcp_agent.DhcpAgent(HOSTNAME)
|
dhcp = dhcp_agent.DhcpAgent(HOSTNAME)
|
||||||
attrs_to_mock = dict(
|
attrs_to_mock = dict(
|
||||||
[(a, mock.DEFAULT) for a in
|
[(a, mock.DEFAULT) for a in
|
||||||
['periodic_resync', 'start_ready_ports_loop']])
|
['periodic_resync', 'start_ready_ports_loop',
|
||||||
|
'_process_loop']])
|
||||||
with mock.patch.multiple(dhcp, **attrs_to_mock) as mocks:
|
with mock.patch.multiple(dhcp, **attrs_to_mock) as mocks:
|
||||||
dhcp.run()
|
with mock.patch.object(dhcp_agent.eventlet,
|
||||||
mocks['periodic_resync'].assert_called_once_with()
|
'spawn_n') as spawn_n:
|
||||||
mocks['start_ready_ports_loop'].assert_called_once_with()
|
dhcp.run()
|
||||||
|
mocks['periodic_resync'].assert_called_once_with()
|
||||||
|
mocks['start_ready_ports_loop'].assert_called_once_with()
|
||||||
|
spawn_n.assert_called_once_with(mocks['_process_loop'])
|
||||||
|
|
||||||
def test_call_driver(self):
|
def test_call_driver(self):
|
||||||
network = mock.Mock()
|
network = mock.Mock()
|
||||||
@ -891,29 +896,36 @@ class TestDhcpAgentEventHandler(base.BaseTestCase):
|
|||||||
self._test_disable_isolated_metadata_proxy(fake_dist_network)
|
self._test_disable_isolated_metadata_proxy(fake_dist_network)
|
||||||
|
|
||||||
def test_network_create_end(self):
|
def test_network_create_end(self):
|
||||||
payload = dict(network=dict(id=fake_network.id))
|
payload = dict(network=dict(id=fake_network.id),
|
||||||
|
priority=FAKE_PRIORITY)
|
||||||
|
|
||||||
with mock.patch.object(self.dhcp, 'enable_dhcp_helper') as enable:
|
with mock.patch.object(self.dhcp, 'enable_dhcp_helper') as enable:
|
||||||
self.dhcp.network_create_end(None, payload)
|
self.dhcp.network_create_end(None, payload)
|
||||||
|
self.dhcp._process_resource_update()
|
||||||
enable.assert_called_once_with(fake_network.id)
|
enable.assert_called_once_with(fake_network.id)
|
||||||
|
|
||||||
def test_network_update_end_admin_state_up(self):
|
def test_network_update_end_admin_state_up(self):
|
||||||
payload = dict(network=dict(id=fake_network.id, admin_state_up=True))
|
payload = dict(network=dict(id=fake_network.id, admin_state_up=True),
|
||||||
|
priority=FAKE_PRIORITY)
|
||||||
with mock.patch.object(self.dhcp, 'enable_dhcp_helper') as enable:
|
with mock.patch.object(self.dhcp, 'enable_dhcp_helper') as enable:
|
||||||
self.dhcp.network_update_end(None, payload)
|
self.dhcp.network_update_end(None, payload)
|
||||||
|
self.dhcp._process_resource_update()
|
||||||
enable.assert_called_once_with(fake_network.id)
|
enable.assert_called_once_with(fake_network.id)
|
||||||
|
|
||||||
def test_network_update_end_admin_state_down(self):
|
def test_network_update_end_admin_state_down(self):
|
||||||
payload = dict(network=dict(id=fake_network.id, admin_state_up=False))
|
payload = dict(network=dict(id=fake_network.id, admin_state_up=False),
|
||||||
|
priority=FAKE_PRIORITY)
|
||||||
with mock.patch.object(self.dhcp, 'disable_dhcp_helper') as disable:
|
with mock.patch.object(self.dhcp, 'disable_dhcp_helper') as disable:
|
||||||
self.dhcp.network_update_end(None, payload)
|
self.dhcp.network_update_end(None, payload)
|
||||||
|
self.dhcp._process_resource_update()
|
||||||
disable.assert_called_once_with(fake_network.id)
|
disable.assert_called_once_with(fake_network.id)
|
||||||
|
|
||||||
def test_network_delete_end(self):
|
def test_network_delete_end(self):
|
||||||
payload = dict(network_id=fake_network.id)
|
payload = dict(network_id=fake_network.id, priority=FAKE_PRIORITY)
|
||||||
|
|
||||||
with mock.patch.object(self.dhcp, 'disable_dhcp_helper') as disable:
|
with mock.patch.object(self.dhcp, 'disable_dhcp_helper') as disable:
|
||||||
self.dhcp.network_delete_end(None, payload)
|
self.dhcp.network_delete_end(None, payload)
|
||||||
|
self.dhcp._process_resource_update()
|
||||||
disable.assert_called_once_with(fake_network.id)
|
disable.assert_called_once_with(fake_network.id)
|
||||||
|
|
||||||
def test_refresh_dhcp_helper_no_dhcp_enabled_networks(self):
|
def test_refresh_dhcp_helper_no_dhcp_enabled_networks(self):
|
||||||
@ -955,13 +967,15 @@ class TestDhcpAgentEventHandler(base.BaseTestCase):
|
|||||||
# attribute isn't True.
|
# attribute isn't True.
|
||||||
payload = dict(subnet=dhcp.DictModel(
|
payload = dict(subnet=dhcp.DictModel(
|
||||||
dict(network_id=fake_network.id, enable_dhcp=False,
|
dict(network_id=fake_network.id, enable_dhcp=False,
|
||||||
cidr='99.99.99.0/24', ip_version=4)))
|
cidr='99.99.99.0/24', ip_version=4)),
|
||||||
|
priority=FAKE_PRIORITY)
|
||||||
self.cache.get_network_by_id.return_value = fake_network
|
self.cache.get_network_by_id.return_value = fake_network
|
||||||
new_net = copy.deepcopy(fake_network)
|
new_net = copy.deepcopy(fake_network)
|
||||||
new_net.subnets.append(payload['subnet'])
|
new_net.subnets.append(payload['subnet'])
|
||||||
self.plugin.get_network_info.return_value = new_net
|
self.plugin.get_network_info.return_value = new_net
|
||||||
|
|
||||||
self.dhcp.subnet_create_end(None, payload)
|
self.dhcp.subnet_create_end(None, payload)
|
||||||
|
self.dhcp._process_resource_update()
|
||||||
|
|
||||||
self.cache.assert_has_calls([mock.call.put(new_net)])
|
self.cache.assert_has_calls([mock.call.put(new_net)])
|
||||||
self.call_driver.assert_called_once_with('reload_allocations', new_net)
|
self.call_driver.assert_called_once_with('reload_allocations', new_net)
|
||||||
@ -970,20 +984,24 @@ class TestDhcpAgentEventHandler(base.BaseTestCase):
|
|||||||
self.call_driver.reset_mock()
|
self.call_driver.reset_mock()
|
||||||
payload = dict(subnet=dhcp.DictModel(
|
payload = dict(subnet=dhcp.DictModel(
|
||||||
dict(network_id=fake_network.id, enable_dhcp=True,
|
dict(network_id=fake_network.id, enable_dhcp=True,
|
||||||
cidr='99.99.88.0/24', ip_version=const.IP_VERSION_4)))
|
cidr='99.99.88.0/24', ip_version=4)),
|
||||||
|
priority=FAKE_PRIORITY)
|
||||||
new_net = copy.deepcopy(fake_network)
|
new_net = copy.deepcopy(fake_network)
|
||||||
new_net.subnets.append(payload['subnet'])
|
new_net.subnets.append(payload['subnet'])
|
||||||
self.plugin.get_network_info.return_value = new_net
|
self.plugin.get_network_info.return_value = new_net
|
||||||
self.dhcp.subnet_create_end(None, payload)
|
self.dhcp.subnet_create_end(None, payload)
|
||||||
|
self.dhcp._process_resource_update()
|
||||||
self.cache.assert_has_calls([mock.call.put(new_net)])
|
self.cache.assert_has_calls([mock.call.put(new_net)])
|
||||||
self.call_driver.assert_called_once_with('restart', new_net)
|
self.call_driver.assert_called_once_with('restart', new_net)
|
||||||
|
|
||||||
def test_subnet_update_end(self):
|
def test_subnet_update_end(self):
|
||||||
payload = dict(subnet=dict(network_id=fake_network.id))
|
payload = dict(subnet=dict(network_id=fake_network.id),
|
||||||
|
priority=FAKE_PRIORITY)
|
||||||
self.cache.get_network_by_id.return_value = fake_network
|
self.cache.get_network_by_id.return_value = fake_network
|
||||||
self.plugin.get_network_info.return_value = fake_network
|
self.plugin.get_network_info.return_value = fake_network
|
||||||
|
|
||||||
self.dhcp.subnet_update_end(None, payload)
|
self.dhcp.subnet_update_end(None, payload)
|
||||||
|
self.dhcp._process_resource_update()
|
||||||
|
|
||||||
self.cache.assert_has_calls([mock.call.put(fake_network)])
|
self.cache.assert_has_calls([mock.call.put(fake_network)])
|
||||||
self.call_driver.assert_called_once_with('reload_allocations',
|
self.call_driver.assert_called_once_with('reload_allocations',
|
||||||
@ -993,7 +1011,8 @@ class TestDhcpAgentEventHandler(base.BaseTestCase):
|
|||||||
self.dhcp.dhcp_ready_ports)
|
self.dhcp.dhcp_ready_ports)
|
||||||
|
|
||||||
def test_subnet_update_dhcp(self):
|
def test_subnet_update_dhcp(self):
|
||||||
payload = dict(subnet=dict(network_id=fake_network.id))
|
payload = dict(subnet=dict(network_id=fake_network.id),
|
||||||
|
priority=FAKE_PRIORITY)
|
||||||
self.cache.get_network_by_id.return_value = fake_network
|
self.cache.get_network_by_id.return_value = fake_network
|
||||||
new_net = copy.deepcopy(fake_network)
|
new_net = copy.deepcopy(fake_network)
|
||||||
new_subnet1 = copy.deepcopy(fake_subnet1)
|
new_subnet1 = copy.deepcopy(fake_subnet1)
|
||||||
@ -1002,6 +1021,7 @@ class TestDhcpAgentEventHandler(base.BaseTestCase):
|
|||||||
new_net.subnets = [new_subnet1, new_subnet2]
|
new_net.subnets = [new_subnet1, new_subnet2]
|
||||||
self.plugin.get_network_info.return_value = new_net
|
self.plugin.get_network_info.return_value = new_net
|
||||||
self.dhcp.subnet_update_end(None, payload)
|
self.dhcp.subnet_update_end(None, payload)
|
||||||
|
self.dhcp._process_resource_update()
|
||||||
self.call_driver.assert_called_once_with('restart', new_net)
|
self.call_driver.assert_called_once_with('restart', new_net)
|
||||||
|
|
||||||
self.call_driver.reset_mock()
|
self.call_driver.reset_mock()
|
||||||
@ -1013,6 +1033,7 @@ class TestDhcpAgentEventHandler(base.BaseTestCase):
|
|||||||
new_net2.subnets = [new_subnet1, new_subnet2]
|
new_net2.subnets = [new_subnet1, new_subnet2]
|
||||||
self.plugin.get_network_info.return_value = new_net2
|
self.plugin.get_network_info.return_value = new_net2
|
||||||
self.dhcp.subnet_update_end(None, payload)
|
self.dhcp.subnet_update_end(None, payload)
|
||||||
|
self.dhcp._process_resource_update()
|
||||||
self.call_driver.assert_called_once_with('restart', new_net2)
|
self.call_driver.assert_called_once_with('restart', new_net2)
|
||||||
|
|
||||||
def test_subnet_update_end_restart(self):
|
def test_subnet_update_end_restart(self):
|
||||||
@ -1022,11 +1043,13 @@ class TestDhcpAgentEventHandler(base.BaseTestCase):
|
|||||||
subnets=[fake_subnet1, fake_subnet3],
|
subnets=[fake_subnet1, fake_subnet3],
|
||||||
ports=[fake_port1]))
|
ports=[fake_port1]))
|
||||||
|
|
||||||
payload = dict(subnet=dict(network_id=fake_network.id))
|
payload = dict(subnet=dict(network_id=fake_network.id),
|
||||||
|
priority=FAKE_PRIORITY)
|
||||||
self.cache.get_network_by_id.return_value = fake_network
|
self.cache.get_network_by_id.return_value = fake_network
|
||||||
self.plugin.get_network_info.return_value = new_state
|
self.plugin.get_network_info.return_value = new_state
|
||||||
|
|
||||||
self.dhcp.subnet_update_end(None, payload)
|
self.dhcp.subnet_update_end(None, payload)
|
||||||
|
self.dhcp._process_resource_update()
|
||||||
|
|
||||||
self.cache.assert_has_calls([mock.call.put(new_state)])
|
self.cache.assert_has_calls([mock.call.put(new_state)])
|
||||||
self.call_driver.assert_called_once_with('restart',
|
self.call_driver.assert_called_once_with('restart',
|
||||||
@ -1039,12 +1062,13 @@ class TestDhcpAgentEventHandler(base.BaseTestCase):
|
|||||||
subnets=[fake_subnet1, fake_subnet3],
|
subnets=[fake_subnet1, fake_subnet3],
|
||||||
ports=[fake_port1]))
|
ports=[fake_port1]))
|
||||||
|
|
||||||
payload = dict(subnet_id=fake_subnet1.id)
|
payload = dict(subnet_id=fake_subnet1.id, priority=FAKE_PRIORITY)
|
||||||
self.cache.get_network_by_subnet_id.return_value = prev_state
|
self.cache.get_network_by_subnet_id.return_value = prev_state
|
||||||
self.cache.get_network_by_id.return_value = prev_state
|
self.cache.get_network_by_id.return_value = prev_state
|
||||||
self.plugin.get_network_info.return_value = fake_network
|
self.plugin.get_network_info.return_value = fake_network
|
||||||
|
|
||||||
self.dhcp.subnet_delete_end(None, payload)
|
self.dhcp.subnet_delete_end(None, payload)
|
||||||
|
self.dhcp._process_resource_update()
|
||||||
|
|
||||||
self.cache.assert_has_calls([
|
self.cache.assert_has_calls([
|
||||||
mock.call.get_network_by_subnet_id(
|
mock.call.get_network_by_subnet_id(
|
||||||
@ -1063,12 +1087,14 @@ class TestDhcpAgentEventHandler(base.BaseTestCase):
|
|||||||
subnets=[fake_subnet1, fake_subnet3],
|
subnets=[fake_subnet1, fake_subnet3],
|
||||||
ports=[fake_port1]))
|
ports=[fake_port1]))
|
||||||
|
|
||||||
payload = dict(subnet_id=fake_subnet1.id, network_id=fake_network.id)
|
payload = dict(subnet_id=fake_subnet1.id, network_id=fake_network.id,
|
||||||
|
priority=FAKE_PRIORITY)
|
||||||
self.cache.get_network_by_subnet_id.return_value = prev_state
|
self.cache.get_network_by_subnet_id.return_value = prev_state
|
||||||
self.cache.get_network_by_id.return_value = prev_state
|
self.cache.get_network_by_id.return_value = prev_state
|
||||||
self.plugin.get_network_info.return_value = fake_network
|
self.plugin.get_network_info.return_value = fake_network
|
||||||
|
|
||||||
self.dhcp.subnet_delete_end(None, payload)
|
self.dhcp.subnet_delete_end(None, payload)
|
||||||
|
self.dhcp._process_resource_update()
|
||||||
|
|
||||||
self.cache.assert_has_calls([
|
self.cache.assert_has_calls([
|
||||||
mock.call.get_network_by_subnet_id(
|
mock.call.get_network_by_subnet_id(
|
||||||
@ -1085,6 +1111,7 @@ class TestDhcpAgentEventHandler(base.BaseTestCase):
|
|||||||
payload = dict(port=copy.deepcopy(fake_port2))
|
payload = dict(port=copy.deepcopy(fake_port2))
|
||||||
self.cache.get_network_by_id.return_value = fake_network
|
self.cache.get_network_by_id.return_value = fake_network
|
||||||
self.dhcp.port_update_end(None, payload)
|
self.dhcp.port_update_end(None, payload)
|
||||||
|
self.dhcp._process_resource_update()
|
||||||
self.reload_allocations.assert_called_once_with(fake_port2,
|
self.reload_allocations.assert_called_once_with(fake_port2,
|
||||||
fake_network)
|
fake_network)
|
||||||
|
|
||||||
@ -1105,19 +1132,12 @@ class TestDhcpAgentEventHandler(base.BaseTestCase):
|
|||||||
payload = dict(port=copy.deepcopy(fake_port2))
|
payload = dict(port=copy.deepcopy(fake_port2))
|
||||||
self.cache.get_network_by_id.return_value = fake_network
|
self.cache.get_network_by_id.return_value = fake_network
|
||||||
self.dhcp.port_create_end(None, payload)
|
self.dhcp.port_create_end(None, payload)
|
||||||
|
self.dhcp._process_resource_update()
|
||||||
self.reload_allocations.assert_called_once_with(fake_port2,
|
self.reload_allocations.assert_called_once_with(fake_port2,
|
||||||
fake_network)
|
fake_network)
|
||||||
|
|
||||||
def test_port_update_end_grabs_lock(self):
|
|
||||||
payload = dict(port=fake_port2)
|
|
||||||
self.cache.get_network_by_id.return_value = None
|
|
||||||
self.cache.get_port_by_id.return_value = fake_port2
|
|
||||||
with mock.patch('neutron.agent.dhcp.agent._net_lock') as nl:
|
|
||||||
self.dhcp.port_update_end(None, payload)
|
|
||||||
nl.assert_called_once_with(fake_port2.network_id)
|
|
||||||
|
|
||||||
def test_port_update_change_ip_on_port(self):
|
def test_port_update_change_ip_on_port(self):
|
||||||
payload = dict(port=fake_port1)
|
payload = dict(port=fake_port1, priority=FAKE_PRIORITY)
|
||||||
self.cache.get_network_by_id.return_value = fake_network
|
self.cache.get_network_by_id.return_value = fake_network
|
||||||
updated_fake_port1 = copy.deepcopy(fake_port1)
|
updated_fake_port1 = copy.deepcopy(fake_port1)
|
||||||
updated_fake_port1.fixed_ips[0].ip_address = '172.9.9.99'
|
updated_fake_port1.fixed_ips[0].ip_address = '172.9.9.99'
|
||||||
@ -1125,6 +1145,7 @@ class TestDhcpAgentEventHandler(base.BaseTestCase):
|
|||||||
with mock.patch.object(
|
with mock.patch.object(
|
||||||
self.dhcp, 'update_isolated_metadata_proxy') as ump:
|
self.dhcp, 'update_isolated_metadata_proxy') as ump:
|
||||||
self.dhcp.port_update_end(None, payload)
|
self.dhcp.port_update_end(None, payload)
|
||||||
|
self.dhcp._process_resource_update()
|
||||||
self.cache.assert_has_calls(
|
self.cache.assert_has_calls(
|
||||||
[mock.call.get_network_by_id(fake_port1.network_id),
|
[mock.call.get_network_by_id(fake_port1.network_id),
|
||||||
mock.call.put_port(mock.ANY)])
|
mock.call.put_port(mock.ANY)])
|
||||||
@ -1135,35 +1156,38 @@ class TestDhcpAgentEventHandler(base.BaseTestCase):
|
|||||||
def test_port_update_change_subnet_on_dhcp_agents_port(self):
|
def test_port_update_change_subnet_on_dhcp_agents_port(self):
|
||||||
self.cache.get_network_by_id.return_value = fake_network
|
self.cache.get_network_by_id.return_value = fake_network
|
||||||
self.cache.get_port_by_id.return_value = fake_port1
|
self.cache.get_port_by_id.return_value = fake_port1
|
||||||
payload = dict(port=copy.deepcopy(fake_port1))
|
payload = dict(port=copy.deepcopy(fake_port1), priority=FAKE_PRIORITY)
|
||||||
device_id = utils.get_dhcp_agent_device_id(
|
device_id = utils.get_dhcp_agent_device_id(
|
||||||
payload['port']['network_id'], self.dhcp.conf.host)
|
payload['port']['network_id'], self.dhcp.conf.host)
|
||||||
payload['port']['fixed_ips'][0]['subnet_id'] = '77777-7777'
|
payload['port']['fixed_ips'][0]['subnet_id'] = '77777-7777'
|
||||||
payload['port']['device_id'] = device_id
|
payload['port']['device_id'] = device_id
|
||||||
self.dhcp.port_update_end(None, payload)
|
self.dhcp.port_update_end(None, payload)
|
||||||
|
self.dhcp._process_resource_update()
|
||||||
self.assertFalse(self.call_driver.called)
|
self.assertFalse(self.call_driver.called)
|
||||||
|
|
||||||
def test_port_update_change_ip_on_dhcp_agents_port(self):
|
def test_port_update_change_ip_on_dhcp_agents_port(self):
|
||||||
self.cache.get_network_by_id.return_value = fake_network
|
self.cache.get_network_by_id.return_value = fake_network
|
||||||
self.cache.get_port_by_id.return_value = fake_port1
|
self.cache.get_port_by_id.return_value = fake_port1
|
||||||
payload = dict(port=copy.deepcopy(fake_port1))
|
payload = dict(port=copy.deepcopy(fake_port1), priority=FAKE_PRIORITY)
|
||||||
device_id = utils.get_dhcp_agent_device_id(
|
device_id = utils.get_dhcp_agent_device_id(
|
||||||
payload['port']['network_id'], self.dhcp.conf.host)
|
payload['port']['network_id'], self.dhcp.conf.host)
|
||||||
payload['port']['fixed_ips'][0]['ip_address'] = '172.9.9.99'
|
payload['port']['fixed_ips'][0]['ip_address'] = '172.9.9.99'
|
||||||
payload['port']['device_id'] = device_id
|
payload['port']['device_id'] = device_id
|
||||||
self.dhcp.port_update_end(None, payload)
|
self.dhcp.port_update_end(None, payload)
|
||||||
|
self.dhcp._process_resource_update()
|
||||||
self.call_driver.assert_has_calls(
|
self.call_driver.assert_has_calls(
|
||||||
[mock.call.call_driver('restart', fake_network)])
|
[mock.call.call_driver('restart', fake_network)])
|
||||||
|
|
||||||
def test_port_update_change_ip_on_dhcp_agents_port_cache_miss(self):
|
def test_port_update_change_ip_on_dhcp_agents_port_cache_miss(self):
|
||||||
self.cache.get_network_by_id.return_value = fake_network
|
self.cache.get_network_by_id.return_value = fake_network
|
||||||
self.cache.get_port_by_id.return_value = None
|
self.cache.get_port_by_id.return_value = None
|
||||||
payload = dict(port=copy.deepcopy(fake_port1))
|
payload = dict(port=copy.deepcopy(fake_port1), priority=FAKE_PRIORITY)
|
||||||
device_id = utils.get_dhcp_agent_device_id(
|
device_id = utils.get_dhcp_agent_device_id(
|
||||||
payload['port']['network_id'], self.dhcp.conf.host)
|
payload['port']['network_id'], self.dhcp.conf.host)
|
||||||
payload['port']['fixed_ips'][0]['ip_address'] = '172.9.9.99'
|
payload['port']['fixed_ips'][0]['ip_address'] = '172.9.9.99'
|
||||||
payload['port']['device_id'] = device_id
|
payload['port']['device_id'] = device_id
|
||||||
self.dhcp.port_update_end(None, payload)
|
self.dhcp.port_update_end(None, payload)
|
||||||
|
self.dhcp._process_resource_update()
|
||||||
self.schedule_resync.assert_called_once_with(mock.ANY,
|
self.schedule_resync.assert_called_once_with(mock.ANY,
|
||||||
fake_port1.network_id)
|
fake_port1.network_id)
|
||||||
|
|
||||||
@ -1173,28 +1197,31 @@ class TestDhcpAgentEventHandler(base.BaseTestCase):
|
|||||||
duplicate_ip = fake_port1['fixed_ips'][0]['ip_address']
|
duplicate_ip = fake_port1['fixed_ips'][0]['ip_address']
|
||||||
payload['port']['fixed_ips'][0]['ip_address'] = duplicate_ip
|
payload['port']['fixed_ips'][0]['ip_address'] = duplicate_ip
|
||||||
self.dhcp.port_create_end(None, payload)
|
self.dhcp.port_create_end(None, payload)
|
||||||
|
self.dhcp._process_resource_update()
|
||||||
self.schedule_resync.assert_called_once_with(mock.ANY,
|
self.schedule_resync.assert_called_once_with(mock.ANY,
|
||||||
fake_port2.network_id)
|
fake_port2.network_id)
|
||||||
|
|
||||||
def test_port_update_on_dhcp_agents_port_no_ip_change(self):
|
def test_port_update_on_dhcp_agents_port_no_ip_change(self):
|
||||||
self.cache.get_network_by_id.return_value = fake_network
|
self.cache.get_network_by_id.return_value = fake_network
|
||||||
self.cache.get_port_by_id.return_value = fake_port1
|
self.cache.get_port_by_id.return_value = fake_port1
|
||||||
payload = dict(port=fake_port1)
|
payload = dict(port=fake_port1, priority=FAKE_PRIORITY)
|
||||||
device_id = utils.get_dhcp_agent_device_id(
|
device_id = utils.get_dhcp_agent_device_id(
|
||||||
payload['port']['network_id'], self.dhcp.conf.host)
|
payload['port']['network_id'], self.dhcp.conf.host)
|
||||||
payload['port']['device_id'] = device_id
|
payload['port']['device_id'] = device_id
|
||||||
self.dhcp.port_update_end(None, payload)
|
self.dhcp.port_update_end(None, payload)
|
||||||
|
self.dhcp._process_resource_update()
|
||||||
self.call_driver.assert_has_calls(
|
self.call_driver.assert_has_calls(
|
||||||
[mock.call.call_driver('reload_allocations', fake_network)])
|
[mock.call.call_driver('reload_allocations', fake_network)])
|
||||||
|
|
||||||
def test_port_delete_end_no_network_id(self):
|
def test_port_delete_end_no_network_id(self):
|
||||||
payload = dict(port_id=fake_port2.id)
|
payload = dict(port_id=fake_port2.id, priority=FAKE_PRIORITY)
|
||||||
self.cache.get_network_by_id.return_value = fake_network
|
self.cache.get_network_by_id.return_value = fake_network
|
||||||
self.cache.get_port_by_id.return_value = fake_port2
|
self.cache.get_port_by_id.return_value = fake_port2
|
||||||
|
|
||||||
with mock.patch.object(
|
with mock.patch.object(
|
||||||
self.dhcp, 'update_isolated_metadata_proxy') as ump:
|
self.dhcp, 'update_isolated_metadata_proxy') as ump:
|
||||||
self.dhcp.port_delete_end(None, payload)
|
self.dhcp.port_delete_end(None, payload)
|
||||||
|
self.dhcp._process_resource_update()
|
||||||
self.cache.assert_has_calls(
|
self.cache.assert_has_calls(
|
||||||
[mock.call.get_port_by_id(fake_port2.id),
|
[mock.call.get_port_by_id(fake_port2.id),
|
||||||
mock.call.get_port_by_id(fake_port2.id),
|
mock.call.get_port_by_id(fake_port2.id),
|
||||||
@ -1206,13 +1233,15 @@ class TestDhcpAgentEventHandler(base.BaseTestCase):
|
|||||||
self.assertTrue(ump.called)
|
self.assertTrue(ump.called)
|
||||||
|
|
||||||
def test_port_delete_end(self):
|
def test_port_delete_end(self):
|
||||||
payload = dict(port_id=fake_port2.id, network_id=fake_network.id)
|
payload = dict(port_id=fake_port2.id, network_id=fake_network.id,
|
||||||
|
priority=FAKE_PRIORITY)
|
||||||
self.cache.get_network_by_id.return_value = fake_network
|
self.cache.get_network_by_id.return_value = fake_network
|
||||||
self.cache.get_port_by_id.return_value = fake_port2
|
self.cache.get_port_by_id.return_value = fake_port2
|
||||||
|
|
||||||
with mock.patch.object(
|
with mock.patch.object(
|
||||||
self.dhcp, 'update_isolated_metadata_proxy') as ump:
|
self.dhcp, 'update_isolated_metadata_proxy') as ump:
|
||||||
self.dhcp.port_delete_end(None, payload)
|
self.dhcp.port_delete_end(None, payload)
|
||||||
|
self.dhcp._process_resource_update()
|
||||||
self.cache.assert_has_calls(
|
self.cache.assert_has_calls(
|
||||||
[mock.call.get_port_by_id(fake_port2.id),
|
[mock.call.get_port_by_id(fake_port2.id),
|
||||||
mock.call.deleted_ports.add(fake_port2.id),
|
mock.call.deleted_ports.add(fake_port2.id),
|
||||||
@ -1223,10 +1252,12 @@ class TestDhcpAgentEventHandler(base.BaseTestCase):
|
|||||||
self.assertTrue(ump.called)
|
self.assertTrue(ump.called)
|
||||||
|
|
||||||
def test_port_delete_end_unknown_port(self):
|
def test_port_delete_end_unknown_port(self):
|
||||||
payload = dict(port_id='unknown', network_id='unknown')
|
payload = dict(port_id='unknown', network_id='unknown',
|
||||||
|
priority=FAKE_PRIORITY)
|
||||||
self.cache.get_port_by_id.return_value = None
|
self.cache.get_port_by_id.return_value = None
|
||||||
|
|
||||||
self.dhcp.port_delete_end(None, payload)
|
self.dhcp.port_delete_end(None, payload)
|
||||||
|
self.dhcp._process_resource_update()
|
||||||
|
|
||||||
self.cache.assert_has_calls([mock.call.get_port_by_id('unknown')])
|
self.cache.assert_has_calls([mock.call.get_port_by_id('unknown')])
|
||||||
self.assertEqual(self.call_driver.call_count, 0)
|
self.assertEqual(self.call_driver.call_count, 0)
|
||||||
@ -1239,7 +1270,9 @@ class TestDhcpAgentEventHandler(base.BaseTestCase):
|
|||||||
self.cache.get_network_by_id.return_value = fake_network
|
self.cache.get_network_by_id.return_value = fake_network
|
||||||
self.cache.get_port_by_id.return_value = port
|
self.cache.get_port_by_id.return_value = port
|
||||||
self.dhcp.port_delete_end(None, {'port_id': port.id,
|
self.dhcp.port_delete_end(None, {'port_id': port.id,
|
||||||
'network_id': fake_network.id})
|
'network_id': fake_network.id,
|
||||||
|
'priority': FAKE_PRIORITY})
|
||||||
|
self.dhcp._process_resource_update()
|
||||||
self.call_driver.assert_has_calls(
|
self.call_driver.assert_has_calls(
|
||||||
[mock.call.call_driver('disable', fake_network)])
|
[mock.call.call_driver('disable', fake_network)])
|
||||||
|
|
||||||
|
@ -13,12 +13,14 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import copy
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
from neutron_lib.callbacks import events
|
from neutron_lib.callbacks import events
|
||||||
from neutron_lib.callbacks import registry
|
from neutron_lib.callbacks import registry
|
||||||
from neutron_lib.callbacks import resources
|
from neutron_lib.callbacks import resources
|
||||||
|
from neutron_lib import constants
|
||||||
from neutron_lib.plugins import directory
|
from neutron_lib.plugins import directory
|
||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
@ -138,10 +140,43 @@ class TestDhcpAgentNotifyAPI(base.BaseTestCase):
|
|||||||
network = {'id': 'foo_network_id'}
|
network = {'id': 'foo_network_id'}
|
||||||
self._test__get_enabled_agents(network, agents=[agent1, agent2])
|
self._test__get_enabled_agents(network, agents=[agent1, agent2])
|
||||||
|
|
||||||
|
def test__notify_agents_allocate_priority(self):
|
||||||
|
mock_context = mock.MagicMock()
|
||||||
|
mock_context.is_admin = True
|
||||||
|
methods = ['network_create_end', 'network_update_end',
|
||||||
|
'network_delete_end', 'subnet_create_end',
|
||||||
|
'subnet_update_end', 'subnet_delete_end',
|
||||||
|
'port_create_end', 'port_update_end', 'port_delete_end']
|
||||||
|
with mock.patch.object(self.notifier, '_schedule_network') as f:
|
||||||
|
with mock.patch.object(self.notifier, '_get_enabled_agents') as g:
|
||||||
|
for method in methods:
|
||||||
|
f.return_value = [mock.MagicMock()]
|
||||||
|
g.return_value = [mock.MagicMock()]
|
||||||
|
payload = {}
|
||||||
|
if method.startswith('port'):
|
||||||
|
payload['port'] = \
|
||||||
|
{'device_id':
|
||||||
|
constants.DEVICE_ID_RESERVED_DHCP_PORT}
|
||||||
|
expected_payload = copy.deepcopy(payload)
|
||||||
|
expected_payload['priority'] = \
|
||||||
|
dhcp_rpc_agent_api.METHOD_PRIORITY_MAP.get(method)
|
||||||
|
self.notifier._notify_agents(mock_context, method, payload,
|
||||||
|
'fake_network_id')
|
||||||
|
if method == 'network_delete_end':
|
||||||
|
self.mock_fanout.assert_called_with(mock.ANY, method,
|
||||||
|
expected_payload)
|
||||||
|
elif method != 'network_create_end':
|
||||||
|
if method == 'port_create_end':
|
||||||
|
expected_payload['priority'] = \
|
||||||
|
dhcp_rpc_agent_api.PRIORITY_PORT_CREATE_HIGH
|
||||||
|
self.mock_cast.assert_called_with(mock.ANY, method,
|
||||||
|
expected_payload,
|
||||||
|
mock.ANY, mock.ANY)
|
||||||
|
|
||||||
def test__notify_agents_fanout_required(self):
|
def test__notify_agents_fanout_required(self):
|
||||||
self.notifier._notify_agents(mock.ANY,
|
self.notifier._notify_agents(mock.ANY,
|
||||||
'network_delete_end',
|
'network_delete_end',
|
||||||
mock.ANY, 'foo_network_id')
|
{}, 'foo_network_id')
|
||||||
self.assertEqual(1, self.mock_fanout.call_count)
|
self.assertEqual(1, self.mock_fanout.call_count)
|
||||||
|
|
||||||
def _test__notify_agents_with_function(
|
def _test__notify_agents_with_function(
|
||||||
|
@ -27,6 +27,7 @@ from oslo_utils import uuidutils
|
|||||||
from webob import exc
|
from webob import exc
|
||||||
|
|
||||||
from neutron.api import extensions
|
from neutron.api import extensions
|
||||||
|
from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api
|
||||||
from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
|
from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
|
||||||
from neutron.api.rpc.handlers import dhcp_rpc
|
from neutron.api.rpc.handlers import dhcp_rpc
|
||||||
from neutron.api.rpc.handlers import l3_rpc
|
from neutron.api.rpc.handlers import l3_rpc
|
||||||
@ -46,10 +47,12 @@ from neutron.tests.unit.extensions import test_l3
|
|||||||
from neutron.tests.unit import testlib_api
|
from neutron.tests.unit import testlib_api
|
||||||
from neutron import wsgi
|
from neutron import wsgi
|
||||||
|
|
||||||
|
|
||||||
L3_HOSTA = 'hosta'
|
L3_HOSTA = 'hosta'
|
||||||
DHCP_HOSTA = 'hosta'
|
DHCP_HOSTA = 'hosta'
|
||||||
L3_HOSTB = 'hostb'
|
L3_HOSTB = 'hostb'
|
||||||
DHCP_HOSTC = 'hostc'
|
DHCP_HOSTC = 'hostc'
|
||||||
|
DHCP_HOSTD = 'hostd'
|
||||||
|
|
||||||
DEVICE_OWNER_COMPUTE = ''.join([constants.DEVICE_OWNER_COMPUTE_PREFIX,
|
DEVICE_OWNER_COMPUTE = ''.join([constants.DEVICE_OWNER_COMPUTE_PREFIX,
|
||||||
'test:',
|
'test:',
|
||||||
@ -1313,7 +1316,9 @@ class OvsDhcpAgentNotifierTestCase(test_agent.AgentDBTestMixIn,
|
|||||||
network_id)
|
network_id)
|
||||||
self.dhcp_notifier_cast.assert_called_with(
|
self.dhcp_notifier_cast.assert_called_with(
|
||||||
mock.ANY, 'network_create_end',
|
mock.ANY, 'network_create_end',
|
||||||
{'network': {'id': network_id}}, DHCP_HOSTA)
|
{'network': {'id': network_id},
|
||||||
|
'priority': dhcp_rpc_agent_api.PRIORITY_NETWORK_CREATE},
|
||||||
|
DHCP_HOSTA)
|
||||||
notifications = fake_notifier.NOTIFICATIONS
|
notifications = fake_notifier.NOTIFICATIONS
|
||||||
expected_event_type = 'dhcp_agent.network.add'
|
expected_event_type = 'dhcp_agent.network.add'
|
||||||
self._assert_notify(notifications, expected_event_type)
|
self._assert_notify(notifications, expected_event_type)
|
||||||
@ -1331,7 +1336,9 @@ class OvsDhcpAgentNotifierTestCase(test_agent.AgentDBTestMixIn,
|
|||||||
network_id)
|
network_id)
|
||||||
self.dhcp_notifier_cast.assert_called_with(
|
self.dhcp_notifier_cast.assert_called_with(
|
||||||
mock.ANY, 'network_delete_end',
|
mock.ANY, 'network_delete_end',
|
||||||
{'network_id': network_id}, DHCP_HOSTA)
|
{'network_id': network_id,
|
||||||
|
'priority': dhcp_rpc_agent_api.PRIORITY_NETWORK_DELETE},
|
||||||
|
DHCP_HOSTA)
|
||||||
notifications = fake_notifier.NOTIFICATIONS
|
notifications = fake_notifier.NOTIFICATIONS
|
||||||
expected_event_type = 'dhcp_agent.network.remove'
|
expected_event_type = 'dhcp_agent.network.remove'
|
||||||
self._assert_notify(notifications, expected_event_type)
|
self._assert_notify(notifications, expected_event_type)
|
||||||
@ -1374,17 +1381,19 @@ class OvsDhcpAgentNotifierTestCase(test_agent.AgentDBTestMixIn,
|
|||||||
ctx = context.get_admin_context()
|
ctx = context.get_admin_context()
|
||||||
net['network'] = self.plugin.get_network(ctx, net['network']['id'])
|
net['network'] = self.plugin.get_network(ctx, net['network']['id'])
|
||||||
sub['subnet'] = self.plugin.get_subnet(ctx, sub['subnet']['id'])
|
sub['subnet'] = self.plugin.get_subnet(ctx, sub['subnet']['id'])
|
||||||
|
sub['priority'] = dhcp_rpc_agent_api.PRIORITY_SUBNET_UPDATE
|
||||||
port['port'] = self.plugin.get_port(ctx, port['port']['id'])
|
port['port'] = self.plugin.get_port(ctx, port['port']['id'])
|
||||||
return net, sub, port
|
return net, sub, port
|
||||||
|
|
||||||
def _notification_mocks(self, hosts, net, subnet, port):
|
def _notification_mocks(self, hosts, net, subnet, port, port_priority):
|
||||||
host_calls = {}
|
host_calls = {}
|
||||||
for host in hosts:
|
for host in hosts:
|
||||||
expected_calls = [
|
expected_calls = [
|
||||||
mock.call(
|
mock.call(
|
||||||
mock.ANY,
|
mock.ANY,
|
||||||
'network_create_end',
|
'network_create_end',
|
||||||
{'network': {'id': net['network']['id']}},
|
{'priority': dhcp_rpc_agent_api.PRIORITY_NETWORK_CREATE,
|
||||||
|
'network': {'id': net['network']['id']}},
|
||||||
host),
|
host),
|
||||||
mock.call(
|
mock.call(
|
||||||
mock.ANY,
|
mock.ANY,
|
||||||
@ -1394,7 +1403,8 @@ class OvsDhcpAgentNotifierTestCase(test_agent.AgentDBTestMixIn,
|
|||||||
mock.call(
|
mock.call(
|
||||||
mock.ANY,
|
mock.ANY,
|
||||||
'port_create_end',
|
'port_create_end',
|
||||||
{'port': port['port']},
|
{'port': port['port'],
|
||||||
|
'priority': port_priority},
|
||||||
host, 'dhcp_agent')]
|
host, 'dhcp_agent')]
|
||||||
host_calls[host] = expected_calls
|
host_calls[host] = expected_calls
|
||||||
return host_calls
|
return host_calls
|
||||||
@ -1402,19 +1412,46 @@ class OvsDhcpAgentNotifierTestCase(test_agent.AgentDBTestMixIn,
|
|||||||
def test_network_port_create_notification(self):
|
def test_network_port_create_notification(self):
|
||||||
hosts = [DHCP_HOSTA]
|
hosts = [DHCP_HOSTA]
|
||||||
net, subnet, port = self._network_port_create(hosts)
|
net, subnet, port = self._network_port_create(hosts)
|
||||||
expected_calls = self._notification_mocks(hosts, net, subnet, port)
|
expected_calls = self._notification_mocks(
|
||||||
|
hosts, net, subnet, port,
|
||||||
|
dhcp_rpc_agent_api.PRIORITY_PORT_CREATE_HIGH)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
expected_calls[DHCP_HOSTA], self.dhcp_notifier_cast.call_args_list)
|
expected_calls[DHCP_HOSTA], self.dhcp_notifier_cast.call_args_list)
|
||||||
|
|
||||||
def test_network_ha_port_create_notification(self):
|
def test_network_ha_port_create_notification(self):
|
||||||
cfg.CONF.set_override('dhcp_agents_per_network', 2)
|
cfg.CONF.set_override('dhcp_agents_per_network', 3)
|
||||||
hosts = [DHCP_HOSTA, DHCP_HOSTC]
|
hosts = [DHCP_HOSTA, DHCP_HOSTC, DHCP_HOSTD]
|
||||||
net, subnet, port = self._network_port_create(hosts)
|
net, subnet, port = self._network_port_create(hosts)
|
||||||
expected_calls = self._notification_mocks(hosts, net, subnet, port)
|
for host_call in self.dhcp_notifier_cast.call_args_list:
|
||||||
for expected in expected_calls[DHCP_HOSTA]:
|
if ("'priority': " + str(
|
||||||
self.assertIn(expected, self.dhcp_notifier_cast.call_args_list)
|
dhcp_rpc_agent_api.PRIORITY_PORT_CREATE_HIGH)
|
||||||
for expected in expected_calls[DHCP_HOSTC]:
|
in str(host_call)):
|
||||||
|
if DHCP_HOSTA in str(host_call):
|
||||||
|
expected_high_calls = self._notification_mocks(
|
||||||
|
[DHCP_HOSTA], net, subnet, port,
|
||||||
|
dhcp_rpc_agent_api.PRIORITY_PORT_CREATE_HIGH)
|
||||||
|
high_host = DHCP_HOSTA
|
||||||
|
hosts.pop(0)
|
||||||
|
elif DHCP_HOSTC in str(host_call):
|
||||||
|
expected_high_calls = self._notification_mocks(
|
||||||
|
[DHCP_HOSTC], net, subnet, port,
|
||||||
|
dhcp_rpc_agent_api.PRIORITY_PORT_CREATE_HIGH)
|
||||||
|
high_host = DHCP_HOSTC
|
||||||
|
hosts.pop(1)
|
||||||
|
elif DHCP_HOSTD in str(host_call):
|
||||||
|
expected_high_calls = self._notification_mocks(
|
||||||
|
[DHCP_HOSTD], net, subnet, port,
|
||||||
|
dhcp_rpc_agent_api.PRIORITY_PORT_CREATE_HIGH)
|
||||||
|
high_host = DHCP_HOSTD
|
||||||
|
hosts.pop(2)
|
||||||
|
expected_low_calls = self._notification_mocks(
|
||||||
|
hosts, net, subnet, port,
|
||||||
|
dhcp_rpc_agent_api.PRIORITY_PORT_CREATE_LOW)
|
||||||
|
for expected in expected_high_calls[high_host]:
|
||||||
self.assertIn(expected, self.dhcp_notifier_cast.call_args_list)
|
self.assertIn(expected, self.dhcp_notifier_cast.call_args_list)
|
||||||
|
for host, low_expecteds in expected_low_calls.items():
|
||||||
|
for expected in low_expecteds:
|
||||||
|
self.assertIn(expected, self.dhcp_notifier_cast.call_args_list)
|
||||||
|
|
||||||
def _is_schedule_network_called(self, device_id):
|
def _is_schedule_network_called(self, device_id):
|
||||||
dhcp_notifier_schedule = mock.patch(
|
dhcp_notifier_schedule = mock.patch(
|
||||||
|
Loading…
Reference in New Issue
Block a user