9d5c540b47
Previously when starting the dhcp agent the sync_state() process would be extremely expensive as it would query quantum server for each network. In order to improve performance a get_active_networks_info() was added so this information could be retrieved in one query rather than doing a query for each active network. The second part of this patch optimizes the logic to avoid calling get_dhcp_port(). Previously, this method was called once for each network which makes a call to get_subnets() and get_ports() unnecessarily as the dhcp agent can determine itself if it needs to update a port or create a port for dhcp. This patch also threads the inital sync process and maintains backwards compatibility with the previous rpc api. There was also a trivial change to the nvp_plugin where filters are assumed to be a dict. implements blueprint improve-dhcp-agent-performance Change-Id: I3b631057f595250dad76516faa9b421789f60953
253 lines
10 KiB
Python
253 lines
10 KiB
Python
# Copyright (c) 2012 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 oslo.config import cfg
|
|
from sqlalchemy.orm import exc
|
|
|
|
from neutron.api.v2 import attributes
|
|
from neutron.common import constants
|
|
from neutron.common import utils
|
|
from neutron import manager
|
|
from neutron.openstack.common import log as logging
|
|
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class DhcpRpcCallbackMixin(object):
|
|
"""A mix-in that enable DHCP agent support in plugin implementations."""
|
|
|
|
def _get_active_networks(self, context, **kwargs):
|
|
"""Retrieve and return a list of the active networks."""
|
|
host = kwargs.get('host')
|
|
plugin = manager.NeutronManager.get_plugin()
|
|
if utils.is_extension_supported(
|
|
plugin, constants.DHCP_AGENT_SCHEDULER_EXT_ALIAS):
|
|
if cfg.CONF.network_auto_schedule:
|
|
plugin.auto_schedule_networks(context, host)
|
|
nets = plugin.list_active_networks_on_active_dhcp_agent(
|
|
context, host)
|
|
else:
|
|
filters = dict(admin_state_up=[True])
|
|
nets = plugin.get_networks(context, filters=filters)
|
|
return nets
|
|
|
|
def get_active_networks(self, context, **kwargs):
|
|
"""Retrieve and return a list of the active network ids."""
|
|
# NOTE(arosen): This method is no longer used by the DHCP agent but is
|
|
# left so that quantum-dhcp-agents will still continue to work if
|
|
# quantum-server is upgraded and not the agent.
|
|
host = kwargs.get('host')
|
|
LOG.debug(_('get_active_networks requested from %s'), host)
|
|
nets = self._get_active_networks(context, **kwargs)
|
|
return [net['id'] for net in nets]
|
|
|
|
def get_active_networks_info(self, context, **kwargs):
|
|
"""Returns all the networks/subnets/ports in system."""
|
|
host = kwargs.get('host')
|
|
LOG.debug(_('get_active_networks_info from %s'), host)
|
|
networks = self._get_active_networks(context, **kwargs)
|
|
plugin = manager.NeutronManager.get_plugin()
|
|
filters = {'network_id': [network['id'] for network in networks]}
|
|
ports = plugin.get_ports(context, filters=filters)
|
|
filters['enable_dhcp'] = [True]
|
|
subnets = plugin.get_subnets(context, filters=filters)
|
|
|
|
for network in networks:
|
|
network['subnets'] = [subnet for subnet in subnets
|
|
if subnet['network_id'] == network['id']]
|
|
network['ports'] = [port for port in ports
|
|
if port['network_id'] == network['id']]
|
|
|
|
return networks
|
|
|
|
def get_network_info(self, context, **kwargs):
|
|
"""Retrieve and return a extended information about a network."""
|
|
network_id = kwargs.get('network_id')
|
|
host = kwargs.get('host')
|
|
LOG.debug(_('Network %(network_id)s requested from '
|
|
'%(host)s'), {'network_id': network_id,
|
|
'host': host})
|
|
plugin = manager.NeutronManager.get_plugin()
|
|
network = plugin.get_network(context, network_id)
|
|
|
|
filters = dict(network_id=[network_id])
|
|
network['subnets'] = plugin.get_subnets(context, filters=filters)
|
|
network['ports'] = plugin.get_ports(context, filters=filters)
|
|
return network
|
|
|
|
def get_dhcp_port(self, context, **kwargs):
|
|
"""Allocate a DHCP port for the host and return port information.
|
|
|
|
This method will re-use an existing port if one already exists. When a
|
|
port is re-used, the fixed_ip allocation will be updated to the current
|
|
network state.
|
|
|
|
"""
|
|
# NOTE(arosen): This method is no longer used by the DHCP agent but is
|
|
# left so that quantum-dhcp-agents will still continue to work if
|
|
# quantum-server is upgraded and not the agent.
|
|
|
|
host = kwargs.get('host')
|
|
network_id = kwargs.get('network_id')
|
|
device_id = kwargs.get('device_id')
|
|
# There could be more than one dhcp server per network, so create
|
|
# a device id that combines host and network ids
|
|
|
|
LOG.debug(_('Port %(device_id)s for %(network_id)s requested from '
|
|
'%(host)s'), {'device_id': device_id,
|
|
'network_id': network_id,
|
|
'host': host})
|
|
plugin = manager.NeutronManager.get_plugin()
|
|
retval = None
|
|
|
|
filters = dict(network_id=[network_id])
|
|
subnets = dict([(s['id'], s) for s in
|
|
plugin.get_subnets(context, filters=filters)])
|
|
|
|
dhcp_enabled_subnet_ids = [s['id'] for s in
|
|
subnets.values() if s['enable_dhcp']]
|
|
|
|
try:
|
|
filters = dict(network_id=[network_id], device_id=[device_id])
|
|
ports = plugin.get_ports(context, filters=filters)
|
|
if ports:
|
|
# Ensure that fixed_ips cover all dhcp_enabled subnets.
|
|
port = ports[0]
|
|
for fixed_ip in port['fixed_ips']:
|
|
if fixed_ip['subnet_id'] in dhcp_enabled_subnet_ids:
|
|
dhcp_enabled_subnet_ids.remove(fixed_ip['subnet_id'])
|
|
port['fixed_ips'].extend(
|
|
[dict(subnet_id=s) for s in dhcp_enabled_subnet_ids])
|
|
|
|
retval = plugin.update_port(context, port['id'],
|
|
dict(port=port))
|
|
|
|
except exc.NoResultFound:
|
|
pass
|
|
|
|
if retval is None:
|
|
# No previous port exists, so create a new one.
|
|
LOG.debug(_('DHCP port %(device_id)s on network %(network_id)s '
|
|
'does not exist on %(host)s'),
|
|
{'device_id': device_id,
|
|
'network_id': network_id,
|
|
'host': host})
|
|
|
|
network = plugin.get_network(context, network_id)
|
|
|
|
port_dict = dict(
|
|
admin_state_up=True,
|
|
device_id=device_id,
|
|
network_id=network_id,
|
|
tenant_id=network['tenant_id'],
|
|
mac_address=attributes.ATTR_NOT_SPECIFIED,
|
|
name='',
|
|
device_owner='network:dhcp',
|
|
fixed_ips=[dict(subnet_id=s) for s in dhcp_enabled_subnet_ids])
|
|
|
|
retval = plugin.create_port(context, dict(port=port_dict))
|
|
|
|
# Convert subnet_id to subnet dict
|
|
for fixed_ip in retval['fixed_ips']:
|
|
subnet_id = fixed_ip.pop('subnet_id')
|
|
fixed_ip['subnet'] = subnets[subnet_id]
|
|
|
|
return retval
|
|
|
|
def release_dhcp_port(self, context, **kwargs):
|
|
"""Release the port currently being used by a DHCP agent."""
|
|
host = kwargs.get('host')
|
|
network_id = kwargs.get('network_id')
|
|
device_id = kwargs.get('device_id')
|
|
|
|
LOG.debug(_('DHCP port deletion for %(network_id)s request from '
|
|
'%(host)s'),
|
|
{'network_id': network_id, 'host': host})
|
|
plugin = manager.NeutronManager.get_plugin()
|
|
filters = dict(network_id=[network_id], device_id=[device_id])
|
|
ports = plugin.get_ports(context, filters=filters)
|
|
|
|
if ports:
|
|
plugin.delete_port(context, ports[0]['id'])
|
|
|
|
def release_port_fixed_ip(self, context, **kwargs):
|
|
"""Release the fixed_ip associated the subnet on a port."""
|
|
host = kwargs.get('host')
|
|
network_id = kwargs.get('network_id')
|
|
device_id = kwargs.get('device_id')
|
|
subnet_id = kwargs.get('subnet_id')
|
|
|
|
LOG.debug(_('DHCP port remove fixed_ip for %(subnet_id)s request '
|
|
'from %(host)s'),
|
|
{'subnet_id': subnet_id, 'host': host})
|
|
plugin = manager.NeutronManager.get_plugin()
|
|
filters = dict(network_id=[network_id], device_id=[device_id])
|
|
ports = plugin.get_ports(context, filters=filters)
|
|
|
|
if ports:
|
|
port = ports[0]
|
|
|
|
fixed_ips = port.get('fixed_ips', [])
|
|
for i in range(len(fixed_ips)):
|
|
if fixed_ips[i]['subnet_id'] == subnet_id:
|
|
del fixed_ips[i]
|
|
break
|
|
plugin.update_port(context, port['id'], dict(port=port))
|
|
|
|
def update_lease_expiration(self, context, **kwargs):
|
|
"""Release the fixed_ip associated the subnet on a port."""
|
|
host = kwargs.get('host')
|
|
network_id = kwargs.get('network_id')
|
|
ip_address = kwargs.get('ip_address')
|
|
lease_remaining = kwargs.get('lease_remaining')
|
|
|
|
LOG.debug(_('Updating lease expiration for %(ip_address)s on network '
|
|
'%(network_id)s from %(host)s.'),
|
|
{'ip_address': ip_address,
|
|
'network_id': network_id,
|
|
'host': host})
|
|
plugin = manager.NeutronManager.get_plugin()
|
|
|
|
plugin.update_fixed_ip_lease_expiration(context, network_id,
|
|
ip_address, lease_remaining)
|
|
|
|
def create_dhcp_port(self, context, **kwargs):
|
|
"""Create the dhcp port."""
|
|
host = kwargs.get('host')
|
|
port = kwargs.get('port')
|
|
LOG.debug(_('Create dhcp port %(port)s '
|
|
'from %(host)s.'),
|
|
{'port': port,
|
|
'host': host})
|
|
|
|
port['port']['device_owner'] = constants.DEVICE_OWNER_DHCP
|
|
if 'mac_address' not in port['port']:
|
|
port['port']['mac_address'] = attributes.ATTR_NOT_SPECIFIED
|
|
plugin = manager.NeutronManager.get_plugin()
|
|
return plugin.create_port(context, port)
|
|
|
|
def update_dhcp_port(self, context, **kwargs):
|
|
"""Update the dhcp port."""
|
|
host = kwargs.get('host')
|
|
port_id = kwargs.get('port_id')
|
|
port = kwargs.get('port')
|
|
LOG.debug(_('Update dhcp port %(port)s '
|
|
'from %(host)s.'),
|
|
{'port': port,
|
|
'host': host})
|
|
plugin = manager.NeutronManager.get_plugin()
|
|
return plugin.update_port(context, port_id, port)
|