Merge "[OVN] Add baremetal support without Neutron DHCP agent for IPv4"
This commit is contained in:
commit
75b95ad1c4
doc/source/ovn
neutron
common/ovn
conf/plugins/ml2/drivers/ovn
plugins/ml2/drivers/ovn/mech_driver/ovsdb
tests/unit
releasenotes/notes
@ -20,24 +20,25 @@ at [1]_.
|
|||||||
can announce host routes for both floating and fixed IP addresses. These
|
can announce host routes for both floating and fixed IP addresses. These
|
||||||
functions are not supported in OVN.
|
functions are not supported in OVN.
|
||||||
|
|
||||||
* Baremetal provisioning with iPXE
|
* Baremetal provisioning with iPXE without Neutron DHCP agent for IPv6
|
||||||
|
|
||||||
The core OVN DHCP server implementation does not have support for
|
The core OVN built-in DHCP server implementation does not
|
||||||
sending different boot options based on the ``gpxe`` DHCP Option
|
yet support PXE booting for IPv6. This can be achieved at
|
||||||
(no. 175). Also, Ironic uses dnsmasq syntax when configuring the DHCP
|
the moment if used with the Neutron DHCP agent by deploying it
|
||||||
options for Neutron [4]_ which is not understood by the OVN driver.
|
on OVN gateway nodes and disabling the OVN DHCP by setting the
|
||||||
Work on that is in progress currently, see [5]_ and [6]_.
|
``[ovn]/disable_ovn_dhcp_for_baremetal_ports`` configuration option
|
||||||
|
to True.
|
||||||
|
|
||||||
* QoS minimum bandwidth allocation in Placement API
|
* QoS minimum bandwidth allocation in Placement API
|
||||||
|
|
||||||
ML2/OVN integration with the Nova placement API to provide guaranteed
|
ML2/OVN integration with the Nova placement API to provide guaranteed
|
||||||
minimum bandwidth for ports [7]_. Work in progress, see [8]_
|
minimum bandwidth for ports [4]_. Work in progress, see [5]_
|
||||||
|
|
||||||
* IPv6 Prefix Delegation
|
* IPv6 Prefix Delegation
|
||||||
|
|
||||||
Currently ML2/OVN doesn't implement IPv6 prefix delegation. OVN logical
|
Currently ML2/OVN doesn't implement IPv6 prefix delegation. OVN logical
|
||||||
routers have this capability implemented in [9]_ and we have an open RFE to
|
routers have this capability implemented in [6]_ and we have an open RFE to
|
||||||
fill this gap [10]_.
|
fill this gap [7]_.
|
||||||
|
|
||||||
* East/West Fragmentation
|
* East/West Fragmentation
|
||||||
|
|
||||||
@ -51,12 +52,12 @@ at [1]_.
|
|||||||
from instances to reach the DHCP agent. For OVN this traffic has to be explicitly
|
from instances to reach the DHCP agent. For OVN this traffic has to be explicitly
|
||||||
allowed by security group rules attached to the instance. Note that the default
|
allowed by security group rules attached to the instance. Note that the default
|
||||||
security group does allow all outgoing traffic, so this only becomes relevant
|
security group does allow all outgoing traffic, so this only becomes relevant
|
||||||
when using custom security groups [11]_. Proposed patch is [12]_ but it
|
when using custom security groups [8]_. Proposed patch is [9]_ but it
|
||||||
needs to be revived and updated.
|
needs to be revived and updated.
|
||||||
|
|
||||||
* DNS resolution for instances
|
* DNS resolution for instances
|
||||||
|
|
||||||
OVN cannot use the host's networking for DNS resolution, so Case 2b in [13]_ can
|
OVN cannot use the host's networking for DNS resolution, so Case 2b in [10]_ can
|
||||||
only be used when additional DHCP agents are deployed. For Case 2a a different
|
only be used when additional DHCP agents are deployed. For Case 2a a different
|
||||||
configuration option has to be used in ``ml2_conf.ini``::
|
configuration option has to be used in ``ml2_conf.ini``::
|
||||||
|
|
||||||
@ -69,13 +70,10 @@ References
|
|||||||
.. [1] https://github.com/ovn-org/ovn/blob/master/TODO.rst
|
.. [1] https://github.com/ovn-org/ovn/blob/master/TODO.rst
|
||||||
.. [2] https://bugzilla.redhat.com/show_bug.cgi?id=2060310
|
.. [2] https://bugzilla.redhat.com/show_bug.cgi?id=2060310
|
||||||
.. [3] https://review.opendev.org/c/openstack/neutron/+/842292
|
.. [3] https://review.opendev.org/c/openstack/neutron/+/842292
|
||||||
.. [4] https://github.com/openstack/ironic/blob/123cb22c731f93d0c608d791b41e05884fe18c04/ironic/common/pxe_utils.py#L447-L462>
|
.. [4] https://specs.openstack.org/openstack/neutron-specs/specs/rocky/minimum-bandwidth-allocation-placement-api.html
|
||||||
.. [5] https://review.opendev.org/c/openstack/neutron/+/840287
|
.. [5] https://review.opendev.org/c/openstack/neutron/+/786478
|
||||||
.. [6] https://review.opendev.org/c/openstack/neutron/+/840316
|
.. [6] https://patchwork.ozlabs.org/project/openvswitch/patch/6aec0fb280f610a2083fbb6c61e251b1d237b21f.1576840560.git.lorenzo.bianconi@redhat.com/
|
||||||
.. [7] https://specs.openstack.org/openstack/neutron-specs/specs/rocky/minimum-bandwidth-allocation-placement-api.html
|
.. [7] https://bugs.launchpad.net/neutron/+bug/1895972
|
||||||
.. [8] https://review.opendev.org/c/openstack/neutron/+/786478
|
.. [8] https://bugs.launchpad.net/neutron/+bug/1926515
|
||||||
.. [9] https://patchwork.ozlabs.org/project/openvswitch/patch/6aec0fb280f610a2083fbb6c61e251b1d237b21f.1576840560.git.lorenzo.bianconi@redhat.com/
|
.. [9] https://review.opendev.org/c/openstack/neutron/+/788594
|
||||||
.. [10] https://bugs.launchpad.net/neutron/+bug/1895972
|
.. [10] https://docs.openstack.org/neutron/latest/admin/config-dns-res.html
|
||||||
.. [11] https://bugs.launchpad.net/neutron/+bug/1926515
|
|
||||||
.. [12] https://review.opendev.org/c/openstack/neutron/+/788594
|
|
||||||
.. [13] https://docs.openstack.org/neutron/latest/admin/config-dns-res.html
|
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
|
import copy
|
||||||
import re
|
import re
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
@ -131,10 +132,11 @@ SUPPORTED_DHCP_OPTS_MAPPING = {
|
|||||||
'T1': 'T1',
|
'T1': 'T1',
|
||||||
'T2': 'T2',
|
'T2': 'T2',
|
||||||
'bootfile-name': 'bootfile_name',
|
'bootfile-name': 'bootfile_name',
|
||||||
|
'bootfile-name-alt': 'bootfile_name_alt',
|
||||||
'wpad': 'wpad',
|
'wpad': 'wpad',
|
||||||
'path-prefix': 'path_prefix',
|
'path-prefix': 'path_prefix',
|
||||||
'tftp-server-address': 'tftp_server_address',
|
'tftp-server-address': 'tftp_server_address',
|
||||||
'server-ip-address': 'tftp_server_address',
|
'server-ip-address': 'next_server',
|
||||||
'1': 'netmask',
|
'1': 'netmask',
|
||||||
'3': 'router',
|
'3': 'router',
|
||||||
'6': 'dns_server',
|
'6': 'dns_server',
|
||||||
@ -175,10 +177,20 @@ SUPPORTED_DHCP_OPTS_MAPPING = {
|
|||||||
'23': 'dns_server'},
|
'23': 'dns_server'},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Baremetal specific DHCP options for VNIC_BAREMETAL ports
|
||||||
|
SUPPORTED_BM_DHCP_OPTS_MAPPING = copy.deepcopy(
|
||||||
|
SUPPORTED_DHCP_OPTS_MAPPING)
|
||||||
|
SUPPORTED_BM_DHCP_OPTS_MAPPING[4].update({
|
||||||
|
'tag:ipxe,bootfile-name': 'bootfile_name',
|
||||||
|
'tag:ipxe,67': 'bootfile_name',
|
||||||
|
'tag:!ipxe,bootfile-name': 'bootfile_name_alt',
|
||||||
|
'tag:!ipxe,67': 'bootfile_name_alt'})
|
||||||
|
|
||||||
# OVN string type DHCP options
|
# OVN string type DHCP options
|
||||||
OVN_STR_TYPE_DHCP_OPTS = [
|
OVN_STR_TYPE_DHCP_OPTS = [
|
||||||
'domain_name',
|
'domain_name',
|
||||||
'bootfile_name',
|
'bootfile_name',
|
||||||
|
'bootfile_name_alt',
|
||||||
'path_prefix',
|
'path_prefix',
|
||||||
'wpad',
|
'wpad',
|
||||||
'tftp_server']
|
'tftp_server']
|
||||||
|
@ -139,6 +139,12 @@ def validate_port_extra_dhcp_opts(port):
|
|||||||
:param port: A neutron port.
|
:param port: A neutron port.
|
||||||
:returns: A PortExtraDHCPValidation object.
|
:returns: A PortExtraDHCPValidation object.
|
||||||
"""
|
"""
|
||||||
|
# Get the right option mappings according to the port's vnic_type
|
||||||
|
vnic_type = port.get(portbindings.VNIC_TYPE, portbindings.VNIC_NORMAL)
|
||||||
|
mapping = constants.SUPPORTED_DHCP_OPTS_MAPPING
|
||||||
|
if vnic_type == portbindings.VNIC_BAREMETAL:
|
||||||
|
mapping = constants.SUPPORTED_BM_DHCP_OPTS_MAPPING
|
||||||
|
|
||||||
invalid = {const.IP_VERSION_4: [], const.IP_VERSION_6: []}
|
invalid = {const.IP_VERSION_4: [], const.IP_VERSION_6: []}
|
||||||
failed = False
|
failed = False
|
||||||
for edo in port.get(edo_ext.EXTRADHCPOPTS, []):
|
for edo in port.get(edo_ext.EXTRADHCPOPTS, []):
|
||||||
@ -151,7 +157,7 @@ def validate_port_extra_dhcp_opts(port):
|
|||||||
failed = False
|
failed = False
|
||||||
break
|
break
|
||||||
|
|
||||||
if opt_name not in constants.SUPPORTED_DHCP_OPTS_MAPPING[ip_version]:
|
if opt_name not in mapping[ip_version]:
|
||||||
invalid[ip_version].append(opt_name)
|
invalid[ip_version].append(opt_name)
|
||||||
failed = True
|
failed = True
|
||||||
|
|
||||||
@ -171,14 +177,16 @@ def get_lsp_dhcp_opts(port, ip_version):
|
|||||||
lsp_dhcp_disabled = False
|
lsp_dhcp_disabled = False
|
||||||
lsp_dhcp_opts = {}
|
lsp_dhcp_opts = {}
|
||||||
vnic_type = port.get(portbindings.VNIC_TYPE, portbindings.VNIC_NORMAL)
|
vnic_type = port.get(portbindings.VNIC_TYPE, portbindings.VNIC_NORMAL)
|
||||||
|
is_baremetal = vnic_type == portbindings.VNIC_BAREMETAL
|
||||||
|
|
||||||
# NOTE(lucasagomes): Baremetal does not yet work with OVN's built-in
|
if is_network_device_port(port):
|
||||||
# DHCP server, disable it for now
|
lsp_dhcp_disabled = True
|
||||||
if (is_network_device_port(port) or
|
elif is_baremetal and ovn_conf.is_ovn_dhcp_disabled_for_baremetal():
|
||||||
vnic_type == portbindings.VNIC_BAREMETAL):
|
|
||||||
lsp_dhcp_disabled = True
|
lsp_dhcp_disabled = True
|
||||||
else:
|
else:
|
||||||
mapping = constants.SUPPORTED_DHCP_OPTS_MAPPING[ip_version]
|
mapping = (constants.SUPPORTED_BM_DHCP_OPTS_MAPPING[ip_version]
|
||||||
|
if is_baremetal else
|
||||||
|
constants.SUPPORTED_DHCP_OPTS_MAPPING[ip_version])
|
||||||
for edo in port.get(edo_ext.EXTRADHCPOPTS, []):
|
for edo in port.get(edo_ext.EXTRADHCPOPTS, []):
|
||||||
if edo['ip_version'] != ip_version:
|
if edo['ip_version'] != ip_version:
|
||||||
continue
|
continue
|
||||||
|
@ -207,6 +207,12 @@ ovn_opts = [
|
|||||||
'or by checking the output of the following command: \n'
|
'or by checking the output of the following command: \n'
|
||||||
'ovs-appctl -t ovs-vswitchd dpif/show-dp-features '
|
'ovs-appctl -t ovs-vswitchd dpif/show-dp-features '
|
||||||
'br-int | grep "Check pkt length action".')),
|
'br-int | grep "Check pkt length action".')),
|
||||||
|
cfg.BoolOpt('disable_ovn_dhcp_for_baremetal_ports',
|
||||||
|
default=False,
|
||||||
|
help=_('Disable OVN\'s built-in DHCP for baremetal ports '
|
||||||
|
'(VNIC type "baremetal"). This alllow operators to '
|
||||||
|
'plug their own DHCP server of choice for PXE booting '
|
||||||
|
'baremetal nodes. Defaults to False.')),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -316,3 +322,7 @@ def is_ovn_emit_need_to_frag_enabled():
|
|||||||
|
|
||||||
def is_igmp_snooping_enabled():
|
def is_igmp_snooping_enabled():
|
||||||
return cfg.CONF.OVS.igmp_snooping_enable
|
return cfg.CONF.OVS.igmp_snooping_enable
|
||||||
|
|
||||||
|
|
||||||
|
def is_ovn_dhcp_disabled_for_baremetal():
|
||||||
|
return cfg.CONF.ovn.disable_ovn_dhcp_for_baremetal_ports
|
||||||
|
@ -20,6 +20,7 @@ import re
|
|||||||
import threading
|
import threading
|
||||||
|
|
||||||
from futurist import periodics
|
from futurist import periodics
|
||||||
|
from neutron_lib.api.definitions import portbindings
|
||||||
from neutron_lib.api.definitions import provider_net as pnet
|
from neutron_lib.api.definitions import provider_net as pnet
|
||||||
from neutron_lib import constants as n_const
|
from neutron_lib import constants as n_const
|
||||||
from neutron_lib import context as n_context
|
from neutron_lib import context as n_context
|
||||||
@ -730,6 +731,58 @@ class DBInconsistenciesPeriodics(SchemaAwarePeriodicsBase):
|
|||||||
txn.add(cmd)
|
txn.add(cmd)
|
||||||
raise periodics.NeverAgain()
|
raise periodics.NeverAgain()
|
||||||
|
|
||||||
|
# A static spacing value is used here, but this method will only run
|
||||||
|
# once per lock due to the use of periodics.NeverAgain().
|
||||||
|
@periodics.periodic(spacing=600, run_immediately=True)
|
||||||
|
def check_baremetal_ports_dhcp_options(self):
|
||||||
|
"""Update baremetal ports DHCP options
|
||||||
|
|
||||||
|
Update baremetal ports DHCP options based on the
|
||||||
|
"disable_ovn_dhcp_for_baremetal_ports" configuration option.
|
||||||
|
"""
|
||||||
|
# If external ports is not supported stop running
|
||||||
|
# this periodic task
|
||||||
|
if not self._ovn_client.is_external_ports_supported():
|
||||||
|
raise periodics.NeverAgain()
|
||||||
|
|
||||||
|
if not self.has_lock:
|
||||||
|
return
|
||||||
|
|
||||||
|
context = n_context.get_admin_context()
|
||||||
|
ports = self._ovn_client._plugin.get_ports(
|
||||||
|
context,
|
||||||
|
filters={portbindings.VNIC_TYPE: portbindings.VNIC_BAREMETAL})
|
||||||
|
if not ports:
|
||||||
|
raise periodics.NeverAgain()
|
||||||
|
|
||||||
|
with self._nb_idl.transaction(check_error=True) as txn:
|
||||||
|
for port in ports:
|
||||||
|
lsp = self._nb_idl.lsp_get(port['id']).execute(
|
||||||
|
check_error=True)
|
||||||
|
if not lsp:
|
||||||
|
continue
|
||||||
|
|
||||||
|
update_dhcp = False
|
||||||
|
if ovn_conf.is_ovn_dhcp_disabled_for_baremetal():
|
||||||
|
if lsp.dhcpv4_options or lsp.dhcpv6_options:
|
||||||
|
update_dhcp = True
|
||||||
|
else:
|
||||||
|
if not lsp.dhcpv4_options and not lsp.dhcpv6_options:
|
||||||
|
update_dhcp = True
|
||||||
|
|
||||||
|
if update_dhcp:
|
||||||
|
port_info = self._ovn_client._get_port_options(port)
|
||||||
|
dhcpv4_options, dhcpv6_options = (
|
||||||
|
self._ovn_client.update_port_dhcp_options(
|
||||||
|
port_info, txn))
|
||||||
|
txn.add(self._nb_idl.set_lswitch_port(
|
||||||
|
lport_name=port['id'],
|
||||||
|
dhcpv4_options=dhcpv4_options,
|
||||||
|
dhcpv6_options=dhcpv6_options,
|
||||||
|
if_exists=False))
|
||||||
|
|
||||||
|
raise periodics.NeverAgain()
|
||||||
|
|
||||||
|
|
||||||
class HashRingHealthCheckPeriodics(object):
|
class HashRingHealthCheckPeriodics(object):
|
||||||
|
|
||||||
|
@ -446,6 +446,24 @@ class OVNClient(object):
|
|||||||
|
|
||||||
return ha_ch_grp.uuid
|
return ha_ch_grp.uuid
|
||||||
|
|
||||||
|
def update_port_dhcp_options(self, port_info, txn):
|
||||||
|
dhcpv4_options = []
|
||||||
|
dhcpv6_options = []
|
||||||
|
if not port_info.dhcpv4_options:
|
||||||
|
dhcpv4_options = []
|
||||||
|
elif 'cmd' in port_info.dhcpv4_options:
|
||||||
|
dhcpv4_options = txn.add(port_info.dhcpv4_options['cmd'])
|
||||||
|
else:
|
||||||
|
dhcpv4_options = [port_info.dhcpv4_options['uuid']]
|
||||||
|
if not port_info.dhcpv6_options:
|
||||||
|
dhcpv6_options = []
|
||||||
|
elif 'cmd' in port_info.dhcpv6_options:
|
||||||
|
dhcpv6_options = txn.add(port_info.dhcpv6_options['cmd'])
|
||||||
|
else:
|
||||||
|
dhcpv6_options = [port_info.dhcpv6_options['uuid']]
|
||||||
|
|
||||||
|
return (dhcpv4_options, dhcpv6_options)
|
||||||
|
|
||||||
def create_port(self, context, port):
|
def create_port(self, context, port):
|
||||||
if utils.is_lsp_ignored(port):
|
if utils.is_lsp_ignored(port):
|
||||||
return
|
return
|
||||||
@ -476,18 +494,8 @@ class OVNClient(object):
|
|||||||
'Logical_Switch', 'name', lswitch_name)
|
'Logical_Switch', 'name', lswitch_name)
|
||||||
|
|
||||||
with self._nb_idl.transaction(check_error=True) as txn:
|
with self._nb_idl.transaction(check_error=True) as txn:
|
||||||
if not port_info.dhcpv4_options:
|
dhcpv4_options, dhcpv6_options = self.update_port_dhcp_options(
|
||||||
dhcpv4_options = []
|
port_info, txn=txn)
|
||||||
elif 'cmd' in port_info.dhcpv4_options:
|
|
||||||
dhcpv4_options = txn.add(port_info.dhcpv4_options['cmd'])
|
|
||||||
else:
|
|
||||||
dhcpv4_options = [port_info.dhcpv4_options['uuid']]
|
|
||||||
if not port_info.dhcpv6_options:
|
|
||||||
dhcpv6_options = []
|
|
||||||
elif 'cmd' in port_info.dhcpv6_options:
|
|
||||||
dhcpv6_options = txn.add(port_info.dhcpv6_options['cmd'])
|
|
||||||
else:
|
|
||||||
dhcpv6_options = [port_info.dhcpv6_options['uuid']]
|
|
||||||
# The lport_name *must* be neutron port['id']. It must match the
|
# The lport_name *must* be neutron port['id']. It must match the
|
||||||
# iface-id set in the Interfaces table of the Open_vSwitch
|
# iface-id set in the Interfaces table of the Open_vSwitch
|
||||||
# database which nova sets to be the port ID.
|
# database which nova sets to be the port ID.
|
||||||
@ -606,18 +614,9 @@ class OVNClient(object):
|
|||||||
else:
|
else:
|
||||||
columns_dict['type'] = port_info.type
|
columns_dict['type'] = port_info.type
|
||||||
columns_dict['addresses'] = port_info.addresses
|
columns_dict['addresses'] = port_info.addresses
|
||||||
if not port_info.dhcpv4_options:
|
|
||||||
dhcpv4_options = []
|
dhcpv4_options, dhcpv6_options = self.update_port_dhcp_options(
|
||||||
elif 'cmd' in port_info.dhcpv4_options:
|
port_info, txn=txn)
|
||||||
dhcpv4_options = txn.add(port_info.dhcpv4_options['cmd'])
|
|
||||||
else:
|
|
||||||
dhcpv4_options = [port_info.dhcpv4_options['uuid']]
|
|
||||||
if not port_info.dhcpv6_options:
|
|
||||||
dhcpv6_options = []
|
|
||||||
elif 'cmd' in port_info.dhcpv6_options:
|
|
||||||
dhcpv6_options = txn.add(port_info.dhcpv6_options['cmd'])
|
|
||||||
else:
|
|
||||||
dhcpv6_options = [port_info.dhcpv6_options['uuid']]
|
|
||||||
|
|
||||||
if self.is_metadata_port(port):
|
if self.is_metadata_port(port):
|
||||||
context = n_context.get_admin_context()
|
context = n_context.get_admin_context()
|
||||||
|
@ -281,6 +281,10 @@ class TestGateWayChassisValidity(base.BaseTestCase):
|
|||||||
|
|
||||||
class TestDHCPUtils(base.BaseTestCase):
|
class TestDHCPUtils(base.BaseTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
ovn_conf.register_opts()
|
||||||
|
super(TestDHCPUtils, self).setUp()
|
||||||
|
|
||||||
def test_validate_port_extra_dhcp_opts_empty(self):
|
def test_validate_port_extra_dhcp_opts_empty(self):
|
||||||
port = {edo_ext.EXTRADHCPOPTS: []}
|
port = {edo_ext.EXTRADHCPOPTS: []}
|
||||||
result = utils.validate_port_extra_dhcp_opts(port)
|
result = utils.validate_port_extra_dhcp_opts(port)
|
||||||
@ -384,11 +388,49 @@ class TestDHCPUtils(base.BaseTestCase):
|
|||||||
dhcp_disabled, options = utils.get_lsp_dhcp_opts(port, 4)
|
dhcp_disabled, options = utils.get_lsp_dhcp_opts(port, 4)
|
||||||
self.assertFalse(dhcp_disabled)
|
self.assertFalse(dhcp_disabled)
|
||||||
# Assert the names got translated to their OVN names
|
# Assert the names got translated to their OVN names
|
||||||
expected_options = {'tftp_server_address': '10.0.0.1',
|
expected_options = {'next_server': '10.0.0.1',
|
||||||
'ntp_server': '10.0.2.1',
|
'ntp_server': '10.0.2.1',
|
||||||
'bootfile_name': '"homer_simpson.bin"'}
|
'bootfile_name': '"homer_simpson.bin"'}
|
||||||
self.assertEqual(expected_options, options)
|
self.assertEqual(expected_options, options)
|
||||||
|
|
||||||
|
def test_get_lsp_dhcp_opts_for_baremetal(self):
|
||||||
|
opt0 = {'opt_name': 'tag:ipxe,bootfile-name',
|
||||||
|
'opt_value': 'http://172.7.27.29/ipxe',
|
||||||
|
'ip_version': 4}
|
||||||
|
opt1 = {'opt_name': 'tag:!ipxe,bootfile-name',
|
||||||
|
'opt_value': 'undionly.kpxe',
|
||||||
|
'ip_version': 4}
|
||||||
|
opt2 = {'opt_name': 'tftp-server',
|
||||||
|
'opt_value': '"172.7.27.29"',
|
||||||
|
'ip_version': 4}
|
||||||
|
port = {portbindings.VNIC_TYPE: portbindings.VNIC_BAREMETAL,
|
||||||
|
edo_ext.EXTRADHCPOPTS: [opt0, opt1, opt2]}
|
||||||
|
|
||||||
|
dhcp_disabled, options = utils.get_lsp_dhcp_opts(port, 4)
|
||||||
|
self.assertFalse(dhcp_disabled)
|
||||||
|
# Assert the names got translated to their OVN names and the
|
||||||
|
# options that weren't double-quoted are now double-quoted
|
||||||
|
expected_options = {'tftp_server': '"172.7.27.29"',
|
||||||
|
'bootfile_name': '"http://172.7.27.29/ipxe"',
|
||||||
|
'bootfile_name_alt': '"undionly.kpxe"'}
|
||||||
|
self.assertEqual(expected_options, options)
|
||||||
|
|
||||||
|
def test_get_lsp_dhcp_opts_dhcp_disabled_for_baremetal(self):
|
||||||
|
cfg.CONF.set_override(
|
||||||
|
'disable_ovn_dhcp_for_baremetal_ports', True, group='ovn')
|
||||||
|
|
||||||
|
opt = {'opt_name': 'tag:ipxe,bootfile-name',
|
||||||
|
'opt_value': 'http://172.7.27.29/ipxe',
|
||||||
|
'ip_version': 4}
|
||||||
|
port = {portbindings.VNIC_TYPE: portbindings.VNIC_BAREMETAL,
|
||||||
|
edo_ext.EXTRADHCPOPTS: [opt]}
|
||||||
|
|
||||||
|
dhcp_disabled, options = utils.get_lsp_dhcp_opts(port, 4)
|
||||||
|
# Assert DHCP is disabled for this port
|
||||||
|
self.assertTrue(dhcp_disabled)
|
||||||
|
# Assert no options were passed
|
||||||
|
self.assertEqual({}, options)
|
||||||
|
|
||||||
|
|
||||||
class TestConnectionConfigToTargetString(base.BaseTestCase):
|
class TestConnectionConfigToTargetString(base.BaseTestCase):
|
||||||
|
|
||||||
|
@ -616,3 +616,73 @@ class TestDBInconsistenciesPeriodics(testlib_api.SqlTestCaseLight,
|
|||||||
expected_calls = [mock.call('Logical_Router', lr0.uuid,
|
expected_calls = [mock.call('Logical_Router', lr0.uuid,
|
||||||
('external_ids', ext_ids))]
|
('external_ids', ext_ids))]
|
||||||
nb_idl.db_set.assert_has_calls(expected_calls)
|
nb_idl.db_set.assert_has_calls(expected_calls)
|
||||||
|
|
||||||
|
def _test_check_baremetal_ports_dhcp_options(self, dhcp_disabled=False):
|
||||||
|
cfg.CONF.set_override('disable_ovn_dhcp_for_baremetal_ports',
|
||||||
|
dhcp_disabled, group='ovn')
|
||||||
|
self.fake_ovn_client.is_external_ports_supported.return_value = True
|
||||||
|
nb_idl = self.fake_ovn_client._nb_idl
|
||||||
|
self.fake_ovn_client._get_port_options.return_value = 'fake-port-opts'
|
||||||
|
|
||||||
|
port0 = {'id': 'port0'}
|
||||||
|
port1 = {'id': 'port1'}
|
||||||
|
port2 = {'id': 'port2'}
|
||||||
|
port3 = {'id': 'port3'}
|
||||||
|
|
||||||
|
self.fake_ovn_client._plugin.get_ports.return_value = [
|
||||||
|
port0, port1, port2, port3]
|
||||||
|
|
||||||
|
lsp0 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
|
||||||
|
attrs={'type': constants.LSP_TYPE_EXTERNAL,
|
||||||
|
'name': 'lsp0',
|
||||||
|
'dhcpv4_options': ['fake-uuid'],
|
||||||
|
'dhcpv6_options': []})
|
||||||
|
lsp1 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
|
||||||
|
attrs={'type': constants.LSP_TYPE_EXTERNAL,
|
||||||
|
'name': 'lsp1',
|
||||||
|
'dhcpv4_options': [],
|
||||||
|
'dhcpv6_options': []})
|
||||||
|
lsp2 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
|
||||||
|
attrs={'type': constants.LSP_TYPE_EXTERNAL,
|
||||||
|
'name': 'lsp2',
|
||||||
|
'dhcpv4_options': [],
|
||||||
|
'dhcpv6_options': ['fake-uuid']})
|
||||||
|
lsp3 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
|
||||||
|
attrs={'type': constants.LSP_TYPE_EXTERNAL,
|
||||||
|
'name': 'lsp3',
|
||||||
|
'dhcpv4_options': ['fake-uuid'],
|
||||||
|
'dhcpv6_options': ['fake-uuid']})
|
||||||
|
|
||||||
|
nb_idl.lsp_get.return_value.execute.side_effect = [
|
||||||
|
lsp0, lsp1, lsp2, lsp3]
|
||||||
|
|
||||||
|
self.fake_ovn_client.update_port_dhcp_options.side_effect = [
|
||||||
|
(lsp0.dhcpv4_options, lsp0.dhcpv6_options),
|
||||||
|
(lsp1.dhcpv4_options, lsp1.dhcpv6_options),
|
||||||
|
(lsp2.dhcpv4_options, lsp2.dhcpv6_options),
|
||||||
|
(lsp3.dhcpv4_options, lsp3.dhcpv6_options)]
|
||||||
|
|
||||||
|
self.assertRaises(periodics.NeverAgain,
|
||||||
|
self.periodic.check_baremetal_ports_dhcp_options)
|
||||||
|
|
||||||
|
def test_check_baremetal_ports_dhcp_options(self):
|
||||||
|
self._test_check_baremetal_ports_dhcp_options()
|
||||||
|
self.fake_ovn_client._nb_idl.set_lswitch_port.assert_called_once_with(
|
||||||
|
lport_name='port1', dhcpv4_options=['fake-uuid'],
|
||||||
|
dhcpv6_options=[], if_exists=False)
|
||||||
|
|
||||||
|
def test_check_baremetal_ports_dhcp_options_dhcp_disabled(self):
|
||||||
|
self._test_check_baremetal_ports_dhcp_options(dhcp_disabled=True)
|
||||||
|
expected_calls = [
|
||||||
|
mock.call(lport_name='port0',
|
||||||
|
dhcpv4_options=['fake-uuid'],
|
||||||
|
dhcpv6_options=[], if_exists=False),
|
||||||
|
mock.call(lport_name='port2',
|
||||||
|
dhcpv4_options=[],
|
||||||
|
dhcpv6_options=[], if_exists=False),
|
||||||
|
mock.call(lport_name='port3',
|
||||||
|
dhcpv4_options=[],
|
||||||
|
dhcpv6_options=['fake-uuid'], if_exists=False)]
|
||||||
|
|
||||||
|
self.fake_ovn_client._nb_idl.set_lswitch_port.assert_has_calls(
|
||||||
|
expected_calls)
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Support for baremetal provisioning using OVN's built-in DHCP server
|
||||||
|
has been added for IPv4.
|
||||||
|
upgrade:
|
||||||
|
- |
|
||||||
|
A new configuration option called
|
||||||
|
``[ovn]/disable_ovn_dhcp_for_baremetal_ports`` has been added to
|
||||||
|
ML2/OVN for IPv4. Since PXE booting nodes can be very sensitive
|
||||||
|
depending on the hardware and some operators may prefer to use a
|
||||||
|
fully-fledged DHCP server instead of OVN's DHCP server this option
|
||||||
|
allows for disabling OVN's built-in DHCP server for baremetal ports
|
||||||
|
(vnic type "baremetal") when set to True. It defaults to False.
|
Loading…
x
Reference in New Issue
Block a user