import shutil
import socket
import sys
-import uuid
import netaddr
from oslo.config import cfg
from neutron.agent.linux import utils
from neutron.common import constants
from neutron.common import exceptions
+from neutron.common import utils as commonutils
from neutron.openstack.common import importutils
from neutron.openstack.common import jsonutils
from neutron.openstack.common import log as logging
"""Return a unique DHCP device ID for this host on the network."""
# There could be more than one dhcp server per network, so create
# a device id that combines host and network ids
-
- host_uuid = uuid.uuid5(uuid.NAMESPACE_DNS, socket.gethostname())
- return 'dhcp%s-%s' % (host_uuid, network.id)
+ return commonutils.get_dhcp_agent_device_id(network.id, self.conf.host)
def _set_default_route(self, network, device_name):
"""Sets the default gateway for this dhcp namespace.
# break since we found port that matches device_id
break
+ # check for a reserved DHCP port
+ if dhcp_port is None:
+ LOG.debug(_('DHCP port %(device_id)s on network %(network_id)s'
+ ' does not yet exist. Checking for a reserved port.'),
+ {'device_id': device_id, 'network_id': network.id})
+ for port in network.ports:
+ port_device_id = getattr(port, 'device_id', None)
+ if port_device_id == constants.DEVICE_ID_RESERVED_DHCP_PORT:
+ dhcp_port = self.plugin.update_dhcp_port(
+ port.id, {'port': {'device_id': device_id}})
+ if dhcp_port:
+ break
+
# DHCP port has not yet been created.
if dhcp_port is None:
LOG.debug(_('DHCP port %(device_id)s on network %(network_id)s'
DEVICE_OWNER_FLOATINGIP = "network:floatingip"
DEVICE_OWNER_DHCP = "network:dhcp"
+DEVICE_ID_RESERVED_DHCP_PORT = "reserved_dhcp_port"
+
FLOATINGIP_KEY = '_floatingips'
INTERFACE_KEY = '_interfaces'
METERING_LABEL_KEY = '_metering_labels'
import random
import signal
import socket
+import uuid
from eventlet.green import subprocess
from oslo.config import cfg
rndstr += hashlib.sha224(str(random.random())).hexdigest()
return rndstr[0:length]
+
+
+def get_dhcp_agent_device_id(network_id, host):
+ # Split host so as to always use only the hostname and
+ # not the domain name. This will guarantee consistentcy
+ # whether a local hostname or an fqdn is passed in.
+ local_hostname = host.split('.')[0]
+ host_uuid = uuid.uuid5(uuid.NAMESPACE_DNS, str(local_hostname))
+ return 'dhcp%s-%s' % (host_uuid, network_id)
from sqlalchemy.orm import joinedload
from neutron.common import constants
+from neutron.common import utils
from neutron.db import agents_db
from neutron.db import model_base
from neutron.extensions import dhcpagentscheduler
except exc.NoResultFound:
raise dhcpagentscheduler.NetworkNotHostedByDhcpAgent(
network_id=network_id, agent_id=id)
+
+ # reserve the port, so the ip is reused on a subsequent add
+ device_id = utils.get_dhcp_agent_device_id(network_id,
+ agent['host'])
+ filters = dict(device_id=[device_id])
+ ports = self.get_ports(context, filters=filters)
+ for port in ports:
+ port['device_id'] = constants.DEVICE_ID_RESERVED_DHCP_PORT
+ self.update_port(context, port['id'], dict(port=port))
+
context.session.delete(binding)
dhcp_notifier = self.agent_notifiers.get(constants.AGENT_TYPE_DHCP)
if dhcp_notifier:
self.assertEqual(1, num_before_remove)
self.assertEqual(0, num_after_remove)
+ def test_reserved_port_after_network_remove_from_dhcp_agent(self):
+ dhcp_hosta = {
+ 'binary': 'neutron-dhcp-agent',
+ 'host': DHCP_HOSTA,
+ 'topic': 'DHCP_AGENT',
+ 'configurations': {'dhcp_driver': 'dhcp_driver',
+ 'use_namespaces': True,
+ },
+ 'agent_type': constants.AGENT_TYPE_DHCP}
+ self._register_one_agent_state(dhcp_hosta)
+ hosta_id = self._get_agent_id(constants.AGENT_TYPE_DHCP,
+ DHCP_HOSTA)
+ with self.port(device_owner=constants.DEVICE_OWNER_DHCP,
+ host=DHCP_HOSTA) as port1:
+ self._remove_network_from_dhcp_agent(hosta_id,
+ port1['port']['network_id'])
+ port_res = self._list_ports(
+ 'json',
+ 200,
+ network_id=port1['port']['network_id'])
+ port_list = self.deserialize('json', port_res)
+ self.assertEqual(port_list['ports'][0]['device_id'],
+ constants.DEVICE_ID_RESERVED_DHCP_PORT)
+
def test_router_auto_schedule_with_invalid_router(self):
with self.router() as router:
l3_rpc = l3_rpc_base.L3RpcCallbackMixin()
from neutron.common import constants
from neutron.common import exceptions as n_exc
from neutron.common import test_lib
+from neutron.common import utils
from neutron import context
from neutron.db import api as db
from neutron.db import db_base_plugin_v2
# Arg must be present
if arg in kwargs:
data['port'][arg] = kwargs[arg]
+ # create a dhcp port device id if one hasn't been supplied
+ if ('device_owner' in kwargs and
+ kwargs['device_owner'] == constants.DEVICE_OWNER_DHCP and
+ 'host' in kwargs and
+ not 'device_id' in kwargs):
+ device_id = utils.get_dhcp_agent_device_id(net_id, kwargs['host'])
+ data['port']['device_id'] = device_id
port_req = self.new_create_request('ports', data, fmt)
if (kwargs.get('set_context') and 'tenant_id' in kwargs):
# create a specific auth context for this request
expected = ('dhcp1ae5f96c-c527-5079-82ea-371a01645457-12345678-1234-'
'5678-1234567890ab')
- with mock.patch('socket.gethostname') as get_host:
- with mock.patch('uuid.uuid5') as uuid5:
- uuid5.return_value = '1ae5f96c-c527-5079-82ea-371a01645457'
- get_host.return_value = 'localhost'
-
- dh = dhcp.DeviceManager(cfg.CONF, cfg.CONF.root_helper, None)
- self.assertEqual(dh.get_device_id(fake_net), expected)
- uuid5.assert_called_once_with(uuid.NAMESPACE_DNS, 'localhost')
+ with mock.patch('uuid.uuid5') as uuid5:
+ uuid5.return_value = '1ae5f96c-c527-5079-82ea-371a01645457'
+
+ dh = dhcp.DeviceManager(cfg.CONF, cfg.CONF.root_helper, None)
+ uuid5.called_once_with(uuid.NAMESPACE_DNS, cfg.CONF.host)
+ self.assertEqual(dh.get_device_id(fake_net), expected)
def test_update(self):
# Try with namespaces and no metadata network