Add SCTP support in API
Add SCTP support in the API for listeners, pools, health-monitors resources. Story: 2007884 Task: 40255 Change-Id: I57a3c528a20943724bdcd36422c689f496068330
This commit is contained in:
@@ -760,8 +760,8 @@ healthmonitor-timeout-optional:
|
|||||||
type: integer
|
type: integer
|
||||||
healthmonitor-type:
|
healthmonitor-type:
|
||||||
description: |
|
description: |
|
||||||
The type of health monitor. One of ``HTTP``, ``HTTPS``, ``PING``, ``TCP``,
|
The type of health monitor. One of ``HTTP``, ``HTTPS``, ``PING``,
|
||||||
``TLS-HELLO``, or ``UDP-CONNECT``.
|
``SCTP``, ``TCP``, ``TLS-HELLO``, or ``UDP-CONNECT``.
|
||||||
in: body
|
in: body
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
@@ -1228,15 +1228,15 @@ project_id-optional-deprecated:
|
|||||||
type: string
|
type: string
|
||||||
protocol:
|
protocol:
|
||||||
description: |
|
description: |
|
||||||
The protocol for the resource. One of ``HTTP``, ``HTTPS``, ``TCP``,
|
The protocol for the resource. One of ``HTTP``, ``HTTPS``, ``SCTP``,
|
||||||
``TERMINATED_HTTPS``, or ``UDP``.
|
``TCP``, ``TERMINATED_HTTPS``, or ``UDP``.
|
||||||
in: body
|
in: body
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
protocol-pools:
|
protocol-pools:
|
||||||
description: |
|
description: |
|
||||||
The protocol for the resource. One of ``HTTP``, ``HTTPS``, ``PROXY``,
|
The protocol for the resource. One of ``HTTP``, ``HTTPS``, ``PROXY``,
|
||||||
``PROXYV2``, ``TCP``, or ``UDP``.
|
``PROXYV2``, ``SCTP``, ``TCP``, or ``UDP``.
|
||||||
in: body
|
in: body
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
@@ -1421,18 +1421,18 @@ session_persistence_cookie:
|
|||||||
type: string
|
type: string
|
||||||
session_persistence_granularity:
|
session_persistence_granularity:
|
||||||
description: |
|
description: |
|
||||||
The netmask used to determine UDP session persistence. Currently only
|
The netmask used to determine SCTP or UDP session persistence. Currently
|
||||||
valid for UDP pools with session persistence of SOURCE_IP. Default netmask
|
only valid for SCTP or UDP pools with session persistence of SOURCE_IP.
|
||||||
is 255.255.255.255, meaning per client full IP.
|
Default netmask is 255.255.255.255, meaning per client full IP.
|
||||||
in: body
|
in: body
|
||||||
min_version: 2.2
|
min_version: 2.2
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
session_persistence_timeout:
|
session_persistence_timeout:
|
||||||
description: |
|
description: |
|
||||||
The timeout, in seconds, after which a UDP flow may be rescheduled to a
|
The timeout, in seconds, after which a SCTP or UDP flow may be rescheduled
|
||||||
different member. Currently only applies to UDP pools with session
|
to a different member. Currently only applies to SCTP or UDP pools with
|
||||||
persistence of SOURCE_IP. Default is 360.
|
session persistence of SOURCE_IP. Default is 360.
|
||||||
in: body
|
in: body
|
||||||
min_version: 2.2
|
min_version: 2.2
|
||||||
required: false
|
required: false
|
||||||
|
|||||||
@@ -594,20 +594,22 @@ Valid protocol combinations
|
|||||||
.. |8Y| replace:: |2| |2| |2| |2| Y
|
.. |8Y| replace:: |2| |2| |2| |2| Y
|
||||||
.. |8N| replace:: |2| |2| |2| |2| N
|
.. |8N| replace:: |2| |2| |2| |2| N
|
||||||
|
|
||||||
+-------------+-------+--------+------+-------------------+------+
|
+-------------+-------+--------+-------+------+-------------------+------+
|
||||||
|| |listener| || HTTP || HTTPS || TCP || TERMINATED_HTTPS || UDP |
|
|| |listener| || HTTP || HTTPS || SCTP || TCP || TERMINATED_HTTPS || UDP |
|
||||||
|| Pool || || || || || |
|
|| Pool || || || || || || |
|
||||||
+=============+=======+========+======+===================+======+
|
+=============+=======+========+=======+======+===================+======+
|
||||||
| HTTP | |2Y| | |2N| | |1Y| | |8Y| | |1N| |
|
| HTTP | |2Y| | |2N| | |2N| | |1Y| | |8Y| | |1N| |
|
||||||
+-------------+-------+--------+------+-------------------+------+
|
+-------------+-------+--------+-------+------+-------------------+------+
|
||||||
| HTTPS | |2N| | |2Y| | |1Y| | |8N| | |1N| |
|
| HTTPS | |2N| | |2Y| | |2N| | |1Y| | |8N| | |1N| |
|
||||||
+-------------+-------+--------+------+-------------------+------+
|
+-------------+-------+--------+-------+------+-------------------+------+
|
||||||
| PROXY | |2Y| | |2Y| | |1Y| | |8Y| | |1N| |
|
| PROXY | |2Y| | |2Y| | |2N| | |1Y| | |8Y| | |1N| |
|
||||||
+-------------+-------+--------+------+-------------------+------+
|
+-------------+-------+--------+-------+------+-------------------+------+
|
||||||
| TCP | |2N| | |2Y| | |1Y| | |8N| | |1N| |
|
| SCTP | |2N| | |2N| | |2Y| | |1N| | |8N| | |1N| |
|
||||||
+-------------+-------+--------+------+-------------------+------+
|
+-------------+-------+--------+-------+------+-------------------+------+
|
||||||
| UDP | |2N| | |2N| | |1N| | |8N| | |1Y| |
|
| TCP | |2N| | |2Y| | |2N| | |1Y| | |8N| | |1N| |
|
||||||
+-------------+-------+--------+------+-------------------+------+
|
+-------------+-------+--------+-------+------+-------------------+------+
|
||||||
|
| UDP | |2N| | |2N| | |2N| | |1N| | |8N| | |1Y| |
|
||||||
|
+-------------+-------+--------+-------+------+-------------------+------+
|
||||||
|
|
||||||
"Y" means the combination is valid and "N" means invalid.
|
"Y" means the combination is valid and "N" means invalid.
|
||||||
|
|
||||||
@@ -640,25 +642,28 @@ Valid protocol combinations
|
|||||||
.. |5Y| replace:: |2| |2| |1| Y
|
.. |5Y| replace:: |2| |2| |1| Y
|
||||||
.. |5N| replace:: |2| |2| |1| N
|
.. |5N| replace:: |2| |2| |1| N
|
||||||
|
|
||||||
+-------------------+-------+--------+-------+------+------------+---------------+
|
+-------------------+-------+--------+-------+-------+------+------------+---------------+
|
||||||
|| |Health Monitor| || HTTP || HTTPS || PING || TCP || TLS-HELLO || |UDPCONNECT| |
|
|| |Health Monitor| || HTTP || HTTPS || PING || SCTP || TCP || TLS-HELLO || |UDPCONNECT| |
|
||||||
|| Pool || || || || || || |
|
|| Pool || || || || || || || |
|
||||||
+===================+=======+========+=======+======+============+===============+
|
+===================+=======+========+=======+=======+======+============+===============+
|
||||||
| HTTP | |2Y| | |2Y| | |1Y| | |1Y| | |4Y| | |5N| |
|
| HTTP | |2Y| | |2Y| | |1Y| | |1N| | |1Y| | |4Y| | |5N| |
|
||||||
+-------------------+-------+--------+-------+------+------------+---------------+
|
+-------------------+-------+--------+-------+-------+------+------------+---------------+
|
||||||
| HTTPS | |2Y| | |2Y| | |1Y| | |1Y| | |4Y| | |5N| |
|
| HTTPS | |2Y| | |2Y| | |1Y| | |1N| | |1Y| | |4Y| | |5N| |
|
||||||
+-------------------+-------+--------+-------+------+------------+---------------+
|
+-------------------+-------+--------+-------+-------+------+------------+---------------+
|
||||||
| PROXY | |2Y| | |2Y| | |1Y| | |1Y| | |4Y| | |5N| |
|
| PROXY | |2Y| | |2Y| | |1Y| | |1N| | |1Y| | |4Y| | |5N| |
|
||||||
+-------------------+-------+--------+-------+------+------------+---------------+
|
+-------------------+-------+--------+-------+-------+------+------------+---------------+
|
||||||
| TCP | |2Y| | |2Y| | |1Y| | |1Y| | |4Y| | |5N| |
|
| SCTP | |2Y| | |2N| | |1N| | |1Y| | |1Y| | |4N| | |5Y| |
|
||||||
+-------------------+-------+--------+-------+------+------------+---------------+
|
+-------------------+-------+--------+-------+-------+------+------------+---------------+
|
||||||
| UDP | |2Y| | |2N| | |1N| | |1Y| | |4N| | |5Y| |
|
| TCP | |2Y| | |2Y| | |1Y| | |1N| | |1Y| | |4Y| | |5N| |
|
||||||
+-------------------+-------+--------+-------+------+------------+---------------+
|
+-------------------+-------+--------+-------+-------+------+------------+---------------+
|
||||||
|
| UDP | |2Y| | |2N| | |1N| | |1Y| | |1Y| | |4N| | |5Y| |
|
||||||
|
+-------------------+-------+--------+-------+-------+------+------------+---------------+
|
||||||
|
|
||||||
"Y" means the combination is valid and "N" means invalid.
|
"Y" means the combination is valid and "N" means invalid.
|
||||||
|
|
||||||
These combinations are mostly as you'd expect for all non-UDP pool protocols:
|
These combinations are mostly as you'd expect for all non-UDP/SCTP pool
|
||||||
non-UDP pools can have health monitors with any check type besides UDP-CONNECT.
|
protocols: non-UDP/SCTP pools can have health monitors with any check type
|
||||||
For UDP pools however, things are a little more complicated. UDP Pools support
|
besides UDP-CONNECT and SCTP.
|
||||||
UDP-CONNECT but also HTTP and TCP checks. HTTPS checks are technically feasible
|
For UDP or SCTP pools however, things are a little more complicated. UDP and
|
||||||
but have not yet been implemented.
|
SCTP Pools support UDP-CONNECT and SCTP but also HTTP and TCP checks. HTTPS
|
||||||
|
checks are technically feasible but have not yet been implemented.
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ At a minimum, you must specify these health monitor attributes:
|
|||||||
times out.
|
times out.
|
||||||
|
|
||||||
- ``type`` The type of health monitor. One of ``HTTP``, ``HTTPS``, ``PING``,
|
- ``type`` The type of health monitor. One of ``HTTP``, ``HTTPS``, ``PING``,
|
||||||
``TCP``, ``TLS-HELLO``, or ``UDP-CONNECT``.
|
``SCTP``, ``TCP``, ``TLS-HELLO``, or ``UDP-CONNECT``.
|
||||||
|
|
||||||
Some attributes receive default values if you omit them from the request:
|
Some attributes receive default values if you omit them from the request:
|
||||||
|
|
||||||
|
|||||||
@@ -115,7 +115,9 @@ L7 policies with ``action`` of ``REDIRECT_TO_URL`` will return the default HTTP
|
|||||||
L7 policies with ``action`` of ``REJECT`` will return a ``Forbidden (403)``
|
L7 policies with ``action`` of ``REJECT`` will return a ``Forbidden (403)``
|
||||||
response code to the requester.
|
response code to the requester.
|
||||||
|
|
||||||
.. note:: Pools of type ``UDP`` cannot be used in L7 policies at this time.
|
.. note::
|
||||||
|
Pools of type ``SCTP``, ``TCP`` or ``UDP`` cannot be used in L7
|
||||||
|
policies at this time.
|
||||||
|
|
||||||
.. rest_status_code:: success ../http-status.yaml
|
.. rest_status_code:: success ../http-status.yaml
|
||||||
|
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ At a minimum, you must specify these pool attributes:
|
|||||||
|
|
||||||
- ``protocol`` The protocol for which this pool and its members
|
- ``protocol`` The protocol for which this pool and its members
|
||||||
listen. A valid value is ``HTTP``, ``HTTPS``, ``PROXY``, ``PROXYV2``,
|
listen. A valid value is ``HTTP``, ``HTTPS``, ``PROXY``, ``PROXYV2``,
|
||||||
``TCP``, or ``UDP``.
|
``SCTP``, ``TCP``, or ``UDP``.
|
||||||
|
|
||||||
- ``lb_algorithm`` The load-balancer algorithm, such as
|
- ``lb_algorithm`` The load-balancer algorithm, such as
|
||||||
``ROUND_ROBIN``, ``LEAST_CONNECTIONS``, ``SOURCE_IP`` and ``SOURCE_IP_PORT``,
|
``ROUND_ROBIN``, ``LEAST_CONNECTIONS``, ``SOURCE_IP`` and ``SOURCE_IP_PORT``,
|
||||||
|
|||||||
@@ -1201,7 +1201,7 @@ and validated with the following exceptions:
|
|||||||
| | | be less than the delay value. |
|
| | | be less than the delay value. |
|
||||||
+-----------------------+--------+------------------------------------------+
|
+-----------------------+--------+------------------------------------------+
|
||||||
| type | string | The type of health monitor. One of HTTP, |
|
| type | string | The type of health monitor. One of HTTP, |
|
||||||
| | | HTTPS, PING, TCP, TLS-HELLO or |
|
| | | HTTPS, PING, SCTP, TCP, TLS-HELLO or |
|
||||||
| | | UDP-CONNECT. |
|
| | | UDP-CONNECT. |
|
||||||
+-----------------------+--------+------------------------------------------+
|
+-----------------------+--------+------------------------------------------+
|
||||||
| url_path | string | The HTTP URL path of the request sent by |
|
| url_path | string | The HTTP URL path of the request sent by |
|
||||||
|
|||||||
@@ -159,6 +159,14 @@ cli=openstack loadbalancer healthmonitor create --type UDP-CONNECT <pool>
|
|||||||
driver.amphora=complete
|
driver.amphora=complete
|
||||||
driver.ovn=missing
|
driver.ovn=missing
|
||||||
|
|
||||||
|
[operation.type.SCTP]
|
||||||
|
title=type - SCTP
|
||||||
|
status=optional
|
||||||
|
notes=Use SCTP for the health monitor.
|
||||||
|
cli=openstack loadbalancer healthmonitor create --type SCTP <pool>
|
||||||
|
driver.amphora=missing
|
||||||
|
driver.ovn=missing
|
||||||
|
|
||||||
[operation.url_path]
|
[operation.url_path]
|
||||||
title=url_path
|
title=url_path
|
||||||
status=optional
|
status=optional
|
||||||
|
|||||||
@@ -230,6 +230,14 @@ cli=openstack loadbalancer listener create --protocol UDP <loadbalancer>
|
|||||||
driver.amphora=complete
|
driver.amphora=complete
|
||||||
driver.ovn=complete
|
driver.ovn=complete
|
||||||
|
|
||||||
|
[operation.protocol.SCTP]
|
||||||
|
title=protocol - SCTP
|
||||||
|
status=optional
|
||||||
|
notes=SCTP protocol support for the listener.
|
||||||
|
cli=openstack loadbalancer listener create --protocol SCTP <loadbalancer>
|
||||||
|
driver.amphora=missing
|
||||||
|
driver.ovn=missing
|
||||||
|
|
||||||
[operation.protocol_port]
|
[operation.protocol_port]
|
||||||
title=protocol_port
|
title=protocol_port
|
||||||
status=mandatory
|
status=mandatory
|
||||||
|
|||||||
@@ -138,6 +138,14 @@ cli=openstack loadbalancer pool create --protocol UDP --listener <listener>
|
|||||||
driver.amphora=complete
|
driver.amphora=complete
|
||||||
driver.ovn=complete
|
driver.ovn=complete
|
||||||
|
|
||||||
|
[operation.protocol.SCTP]
|
||||||
|
title=protocol - SCTP
|
||||||
|
status=optional
|
||||||
|
notes=SCTP protocol support for the pool.
|
||||||
|
cli=openstack loadbalancer pool create --protocol SCTP --listener <listener>
|
||||||
|
driver.amphora=missing
|
||||||
|
driver.ovn=missing
|
||||||
|
|
||||||
[operation.session_persistence.APP_COOKIE]
|
[operation.session_persistence.APP_COOKIE]
|
||||||
title=session_persistence - APP_COOKIE
|
title=session_persistence - APP_COOKIE
|
||||||
status=optional
|
status=optional
|
||||||
@@ -165,7 +173,7 @@ driver.ovn=missing
|
|||||||
[operation.session_persistence.persistence_timeout]
|
[operation.session_persistence.persistence_timeout]
|
||||||
title=session_persistence - persistence_timeout
|
title=session_persistence - persistence_timeout
|
||||||
status=optional
|
status=optional
|
||||||
notes=The timeout, in seconds, after which a UDP flow may be rescheduled to a different member.
|
notes=The timeout, in seconds, after which a SCTP or UDP flow may be rescheduled to a different member.
|
||||||
cli=openstack loadbalancer pool create --session-persistence persistence_timeout=360 --listener <listener>
|
cli=openstack loadbalancer pool create --session-persistence persistence_timeout=360 --listener <listener>
|
||||||
driver.amphora=complete
|
driver.amphora=complete
|
||||||
driver.ovn=missing
|
driver.ovn=missing
|
||||||
@@ -173,7 +181,7 @@ driver.ovn=missing
|
|||||||
[operation.session_persistence.persistence_granularity]
|
[operation.session_persistence.persistence_granularity]
|
||||||
title=session_persistence - persistence_granularity
|
title=session_persistence - persistence_granularity
|
||||||
status=optional
|
status=optional
|
||||||
notes=The netmask used to determine UDP SOURCE_IP session persistence.
|
notes=The netmask used to determine SCTP or UDP SOURCE_IP session persistence.
|
||||||
cli=openstack loadbalancer pool create --session-persistence persistence_granularity=255.255.255.255 --listener <listener>
|
cli=openstack loadbalancer pool create --session-persistence persistence_granularity=255.255.255.255 --listener <listener>
|
||||||
driver.amphora=complete
|
driver.amphora=complete
|
||||||
driver.ovn=missing
|
driver.ovn=missing
|
||||||
|
|||||||
@@ -856,8 +856,8 @@ generates the health check in your web application:
|
|||||||
|
|
||||||
Other health monitors
|
Other health monitors
|
||||||
---------------------
|
---------------------
|
||||||
Other health monitor types include ``PING``, ``TCP``, ``HTTPS``, ``TLS-HELLO``,
|
Other health monitor types include ``PING``, ``TCP``, ``HTTPS``, ``SCTP``,
|
||||||
and ``UDP-CONNECT``.
|
``TLS-HELLO``, and ``UDP-CONNECT``.
|
||||||
|
|
||||||
``PING`` health monitors send periodic ICMP PING requests to the back-end
|
``PING`` health monitors send periodic ICMP PING requests to the back-end
|
||||||
servers. Obviously, your back-end servers must be configured to allow PINGs in
|
servers. Obviously, your back-end servers must be configured to allow PINGs in
|
||||||
@@ -881,6 +881,13 @@ ssl back-end servers. Unfortunately, this causes problems if the servers are
|
|||||||
performing client certificate validation, as HAProxy won't have a valid cert.
|
performing client certificate validation, as HAProxy won't have a valid cert.
|
||||||
In this case, using ``TLS-HELLO`` type monitoring is an alternative.
|
In this case, using ``TLS-HELLO`` type monitoring is an alternative.
|
||||||
|
|
||||||
|
``SCTP`` health monitors send an INIT packet to the back-end server's port.
|
||||||
|
If an application is listening on this port, the Operating System should reply
|
||||||
|
with an INIT ACK packet, but if the port is closed, it replies with an ABORT
|
||||||
|
packet.
|
||||||
|
If the health monitor receives an INIT ACK packet, it immediatly closes the
|
||||||
|
connection with an ABORT packet, and considers that the server is ONLINE.
|
||||||
|
|
||||||
``TLS-HELLO`` health monitors simply ensure the back-end server responds to
|
``TLS-HELLO`` health monitors simply ensure the back-end server responds to
|
||||||
SSLv3 client hello messages. It will not check any other health metrics, like
|
SSLv3 client hello messages. It will not check any other health metrics, like
|
||||||
status code or body contents.
|
status code or body contents.
|
||||||
|
|||||||
@@ -131,6 +131,9 @@ class RootController(object):
|
|||||||
self._add_a_version(versions, 'v2.21', 'v2', 'SUPPORTED',
|
self._add_a_version(versions, 'v2.21', 'v2', 'SUPPORTED',
|
||||||
'2020-09-03T00:00:00Z', host_url)
|
'2020-09-03T00:00:00Z', host_url)
|
||||||
# Add PROXYV2 pool protocol
|
# Add PROXYV2 pool protocol
|
||||||
self._add_a_version(versions, 'v2.22', 'v2', 'CURRENT',
|
self._add_a_version(versions, 'v2.22', 'v2', 'SUPPORTED',
|
||||||
'2020-09-04T00:00:00Z', host_url)
|
'2020-09-04T00:00:00Z', host_url)
|
||||||
|
# SCTP protocol
|
||||||
|
self._add_a_version(versions, 'v2.23', 'v2', 'CURRENT',
|
||||||
|
'2020-09-07T00:00:00Z', host_url)
|
||||||
return {'versions': versions}
|
return {'versions': versions}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from octavia_lib.api.drivers import data_models as driver_dm
|
from octavia_lib.api.drivers import data_models as driver_dm
|
||||||
|
from octavia_lib.common import constants as lib_consts
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_db import exception as odb_exceptions
|
from oslo_db import exception as odb_exceptions
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
@@ -164,16 +165,19 @@ class HealthMonitorController(base.BaseController):
|
|||||||
# do not give any information as to what constraint failed
|
# do not give any information as to what constraint failed
|
||||||
raise exceptions.InvalidOption(value='', option='') from e
|
raise exceptions.InvalidOption(value='', option='') from e
|
||||||
|
|
||||||
def _validate_healthmonitor_request_for_udp(self, request):
|
def _validate_healthmonitor_request_for_udp_sctp(self, request,
|
||||||
|
pool_protocol):
|
||||||
if request.type not in (
|
if request.type not in (
|
||||||
consts.HEALTH_MONITOR_UDP_CONNECT,
|
consts.HEALTH_MONITOR_UDP_CONNECT,
|
||||||
|
lib_consts.HEALTH_MONITOR_SCTP,
|
||||||
consts.HEALTH_MONITOR_TCP,
|
consts.HEALTH_MONITOR_TCP,
|
||||||
consts.HEALTH_MONITOR_HTTP):
|
consts.HEALTH_MONITOR_HTTP):
|
||||||
raise exceptions.ValidationException(detail=_(
|
raise exceptions.ValidationException(detail=_(
|
||||||
"The associated pool protocol is %(pool_protocol)s, so only "
|
"The associated pool protocol is %(pool_protocol)s, so only "
|
||||||
"a %(types)s health monitor is supported.") % {
|
"a %(types)s health monitor is supported.") % {
|
||||||
'pool_protocol': consts.PROTOCOL_UDP,
|
'pool_protocol': pool_protocol,
|
||||||
'types': '/'.join((consts.HEALTH_MONITOR_UDP_CONNECT,
|
'types': '/'.join((consts.HEALTH_MONITOR_UDP_CONNECT,
|
||||||
|
lib_consts.HEALTH_MONITOR_SCTP,
|
||||||
consts.HEALTH_MONITOR_TCP,
|
consts.HEALTH_MONITOR_TCP,
|
||||||
consts.HEALTH_MONITOR_HTTP))})
|
consts.HEALTH_MONITOR_HTTP))})
|
||||||
# check the delay value if the HM type is UDP-CONNECT
|
# check the delay value if the HM type is UDP-CONNECT
|
||||||
@@ -209,14 +213,19 @@ class HealthMonitorController(base.BaseController):
|
|||||||
raise exceptions.DisabledOption(
|
raise exceptions.DisabledOption(
|
||||||
option='type', value=consts.HEALTH_MONITOR_PING)
|
option='type', value=consts.HEALTH_MONITOR_PING)
|
||||||
|
|
||||||
if pool.protocol == consts.PROTOCOL_UDP:
|
if pool.protocol in (lib_consts.PROTOCOL_UDP,
|
||||||
self._validate_healthmonitor_request_for_udp(health_monitor)
|
lib_consts.PROTOCOL_SCTP):
|
||||||
|
self._validate_healthmonitor_request_for_udp_sctp(health_monitor,
|
||||||
|
pool.protocol)
|
||||||
else:
|
else:
|
||||||
if health_monitor.type == consts.HEALTH_MONITOR_UDP_CONNECT:
|
if health_monitor.type in (consts.HEALTH_MONITOR_UDP_CONNECT,
|
||||||
|
lib_consts.HEALTH_MONITOR_SCTP):
|
||||||
raise exceptions.ValidationException(detail=_(
|
raise exceptions.ValidationException(detail=_(
|
||||||
"The %(type)s type is only supported for pools of type "
|
"The %(type)s type is only supported for pools of type "
|
||||||
"%(protocol)s.") % {'type': health_monitor.type,
|
"%(protocols)s.") % {
|
||||||
'protocol': consts.PROTOCOL_UDP})
|
'type': health_monitor.type,
|
||||||
|
'protocols': '/'.join((consts.PROTOCOL_UDP,
|
||||||
|
lib_consts.PROTOCOL_SCTP))})
|
||||||
|
|
||||||
# Load the driver early as it also provides validation
|
# Load the driver early as it also provides validation
|
||||||
driver = driver_factory.get_driver(provider)
|
driver = driver_factory.get_driver(provider)
|
||||||
@@ -342,11 +351,12 @@ class HealthMonitorController(base.BaseController):
|
|||||||
self._auth_validate_action(context, project_id, consts.RBAC_PUT)
|
self._auth_validate_action(context, project_id, consts.RBAC_PUT)
|
||||||
|
|
||||||
self._validate_update_hm(db_hm, health_monitor)
|
self._validate_update_hm(db_hm, health_monitor)
|
||||||
# Validate health monitor update options for UDP-CONNECT type.
|
# Validate health monitor update options for UDP/SCTP
|
||||||
if (pool.protocol == consts.PROTOCOL_UDP and
|
if pool.protocol in (lib_consts.PROTOCOL_UDP,
|
||||||
db_hm.type == consts.HEALTH_MONITOR_UDP_CONNECT):
|
lib_consts.PROTOCOL_SCTP):
|
||||||
health_monitor.type = db_hm.type
|
health_monitor.type = db_hm.type
|
||||||
self._validate_healthmonitor_request_for_udp(health_monitor)
|
self._validate_healthmonitor_request_for_udp_sctp(health_monitor,
|
||||||
|
pool.protocol)
|
||||||
|
|
||||||
self._set_default_on_none(health_monitor)
|
self._set_default_on_none(health_monitor)
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from octavia_lib.api.drivers import data_models as driver_dm
|
from octavia_lib.api.drivers import data_models as driver_dm
|
||||||
|
from octavia_lib.common import constants as lib_consts
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_db import exception as odb_exceptions
|
from oslo_db import exception as odb_exceptions
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
@@ -175,12 +176,13 @@ class ListenersController(base.BaseController):
|
|||||||
self._validate_insert_headers(
|
self._validate_insert_headers(
|
||||||
listener_dict['insert_headers'].keys(), listener_protocol)
|
listener_dict['insert_headers'].keys(), listener_protocol)
|
||||||
|
|
||||||
# Check for UDP compatibility
|
# Check for UDP/SCTP compatibility
|
||||||
if (listener_protocol == constants.PROTOCOL_UDP and
|
if (listener_protocol in (constants.PROTOCOL_UDP,
|
||||||
|
lib_consts.PROTOCOL_SCTP) and
|
||||||
self._is_tls_or_insert_header(listener_dict)):
|
self._is_tls_or_insert_header(listener_dict)):
|
||||||
raise exceptions.ValidationException(
|
raise exceptions.ValidationException(
|
||||||
detail=_("%s protocol listener does not "
|
detail=_("%s protocol listener does not "
|
||||||
"support TLS.") % constants.PROTOCOL_UDP)
|
"support TLS.") % listener_protocol)
|
||||||
|
|
||||||
# Check for TLS disabled
|
# Check for TLS disabled
|
||||||
if (not CONF.api_settings.allow_tls_terminated_listeners and
|
if (not CONF.api_settings.allow_tls_terminated_listeners and
|
||||||
@@ -251,8 +253,8 @@ class ListenersController(base.BaseController):
|
|||||||
listener_dict.get('client_ca_tls_certificate_id'),
|
listener_dict.get('client_ca_tls_certificate_id'),
|
||||||
listener_dict.get('client_crl_container_id', None))
|
listener_dict.get('client_crl_container_id', None))
|
||||||
|
|
||||||
# Validate that the L4 protocol (UDP or TCP) is not already used for
|
# Validate that the L4 protocol (UDP, TCP or SCTP) is not already used
|
||||||
# the specified protocol_port in this load balancer
|
# for the specified protocol_port in this load balancer
|
||||||
pcontext = pecan_request.context
|
pcontext = pecan_request.context
|
||||||
query_filter = {
|
query_filter = {
|
||||||
'project_id': listener_dict['project_id'],
|
'project_id': listener_dict['project_id'],
|
||||||
@@ -435,12 +437,13 @@ class ListenersController(base.BaseController):
|
|||||||
raise exceptions.ValidationException(
|
raise exceptions.ValidationException(
|
||||||
detail='No listener object supplied.')
|
detail='No listener object supplied.')
|
||||||
|
|
||||||
# Check for UDP compatibility
|
# Check for UDP/SCTP compatibility
|
||||||
if (db_listener.protocol == constants.PROTOCOL_UDP and
|
if (db_listener.protocol in (constants.PROTOCOL_UDP,
|
||||||
|
lib_consts.PROTOCOL_SCTP) and
|
||||||
self._is_tls_or_insert_header(listener.to_dict())):
|
self._is_tls_or_insert_header(listener.to_dict())):
|
||||||
raise exceptions.ValidationException(detail=_(
|
raise exceptions.ValidationException(detail=_(
|
||||||
"%s protocol listener does not support TLS or header "
|
"%s protocol listener does not support TLS or header "
|
||||||
"insertion.") % constants.PROTOCOL_UDP)
|
"insertion.") % db_listener.protocol)
|
||||||
|
|
||||||
# Check for certs when not TERMINATED_HTTPS
|
# Check for certs when not TERMINATED_HTTPS
|
||||||
if (db_listener.protocol != constants.PROTOCOL_TERMINATED_HTTPS and
|
if (db_listener.protocol != constants.PROTOCOL_TERMINATED_HTTPS and
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from octavia_lib.api.drivers import data_models as driver_dm
|
from octavia_lib.api.drivers import data_models as driver_dm
|
||||||
|
from octavia_lib.common import constants as lib_consts
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_db import exception as odb_exceptions
|
from oslo_db import exception as odb_exceptions
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
@@ -165,7 +166,7 @@ class PoolsController(base.BaseController):
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _validate_pool_request_for_udp(self, request):
|
def _validate_pool_request_for_udp_sctp(self, request):
|
||||||
if request.session_persistence:
|
if request.session_persistence:
|
||||||
if (request.session_persistence.type ==
|
if (request.session_persistence.type ==
|
||||||
constants.SESSION_PERSISTENCE_SOURCE_IP and
|
constants.SESSION_PERSISTENCE_SOURCE_IP and
|
||||||
@@ -174,14 +175,15 @@ class PoolsController(base.BaseController):
|
|||||||
check_exist_attrs=['type', 'persistence_timeout',
|
check_exist_attrs=['type', 'persistence_timeout',
|
||||||
'persistence_granularity'])):
|
'persistence_granularity'])):
|
||||||
raise exceptions.ValidationException(detail=_(
|
raise exceptions.ValidationException(detail=_(
|
||||||
"session_persistence %s type for UDP protocol "
|
"session_persistence %s type for UDP and SCTP protocols "
|
||||||
"only accepts: type, persistence_timeout, "
|
"only accepts: type, persistence_timeout, "
|
||||||
"persistence_granularity.") % (
|
"persistence_granularity.") % (
|
||||||
constants.SESSION_PERSISTENCE_SOURCE_IP))
|
constants.SESSION_PERSISTENCE_SOURCE_IP))
|
||||||
if request.session_persistence.cookie_name:
|
if request.session_persistence.cookie_name:
|
||||||
raise exceptions.ValidationException(detail=_(
|
raise exceptions.ValidationException(detail=_(
|
||||||
"Cookie names are not supported for %s pools.") %
|
"Cookie names are not supported for %s pools.") %
|
||||||
constants.PROTOCOL_UDP)
|
"/".join((constants.PROTOCOL_UDP,
|
||||||
|
lib_consts.PROTOCOL_SCTP)))
|
||||||
if request.session_persistence.type in [
|
if request.session_persistence.type in [
|
||||||
constants.SESSION_PERSISTENCE_HTTP_COOKIE,
|
constants.SESSION_PERSISTENCE_HTTP_COOKIE,
|
||||||
constants.SESSION_PERSISTENCE_APP_COOKIE]:
|
constants.SESSION_PERSISTENCE_APP_COOKIE]:
|
||||||
@@ -189,7 +191,8 @@ class PoolsController(base.BaseController):
|
|||||||
"Session persistence of type %(type)s is not supported "
|
"Session persistence of type %(type)s is not supported "
|
||||||
"for %(protocol)s protocol pools.") % {
|
"for %(protocol)s protocol pools.") % {
|
||||||
'type': request.session_persistence.type,
|
'type': request.session_persistence.type,
|
||||||
'protocol': constants.PROTOCOL_UDP})
|
'protocol': "/".join((constants.PROTOCOL_UDP,
|
||||||
|
lib_consts.PROTOCOL_SCTP))})
|
||||||
|
|
||||||
@wsme_pecan.wsexpose(pool_types.PoolRootResponse,
|
@wsme_pecan.wsexpose(pool_types.PoolRootResponse,
|
||||||
body=pool_types.PoolRootPOST, status_code=201)
|
body=pool_types.PoolRootPOST, status_code=201)
|
||||||
@@ -226,15 +229,16 @@ class PoolsController(base.BaseController):
|
|||||||
if pool.listener_id and listener:
|
if pool.listener_id and listener:
|
||||||
self._validate_protocol(listener.protocol, pool.protocol)
|
self._validate_protocol(listener.protocol, pool.protocol)
|
||||||
|
|
||||||
if pool.protocol == constants.PROTOCOL_UDP:
|
if pool.protocol in (constants.PROTOCOL_UDP,
|
||||||
self._validate_pool_request_for_udp(pool)
|
lib_consts.PROTOCOL_SCTP):
|
||||||
|
self._validate_pool_request_for_udp_sctp(pool)
|
||||||
else:
|
else:
|
||||||
if (pool.session_persistence and (
|
if (pool.session_persistence and (
|
||||||
pool.session_persistence.persistence_timeout or
|
pool.session_persistence.persistence_timeout or
|
||||||
pool.session_persistence.persistence_granularity)):
|
pool.session_persistence.persistence_granularity)):
|
||||||
raise exceptions.ValidationException(detail=_(
|
raise exceptions.ValidationException(detail=_(
|
||||||
"persistence_timeout and persistence_granularity "
|
"persistence_timeout and persistence_granularity "
|
||||||
"is only for UDP protocol pools."))
|
"is only for UDP and SCTP protocol pools."))
|
||||||
|
|
||||||
if pool.session_persistence:
|
if pool.session_persistence:
|
||||||
sp_dict = pool.session_persistence.to_dict(render_unsets=False)
|
sp_dict = pool.session_persistence.to_dict(render_unsets=False)
|
||||||
@@ -311,16 +315,20 @@ class PoolsController(base.BaseController):
|
|||||||
hm[constants.PROJECT_ID] = db_pool.project_id
|
hm[constants.PROJECT_ID] = db_pool.project_id
|
||||||
new_hm = health_monitor.HealthMonitorController()._graph_create(
|
new_hm = health_monitor.HealthMonitorController()._graph_create(
|
||||||
lock_session, hm)
|
lock_session, hm)
|
||||||
if db_pool.protocol == constants.PROTOCOL_UDP:
|
if db_pool.protocol in (constants.PROTOCOL_UDP,
|
||||||
|
lib_consts.PROTOCOL_SCTP):
|
||||||
health_monitor.HealthMonitorController(
|
health_monitor.HealthMonitorController(
|
||||||
)._validate_healthmonitor_request_for_udp(new_hm)
|
)._validate_healthmonitor_request_for_udp_sctp(new_hm,
|
||||||
|
db_pool)
|
||||||
else:
|
else:
|
||||||
if new_hm.type == constants.HEALTH_MONITOR_UDP_CONNECT:
|
if new_hm.type in (constants.HEALTH_MONITOR_UDP_CONNECT,
|
||||||
|
lib_consts.HEALTH_MONITOR_SCTP):
|
||||||
raise exceptions.ValidationException(detail=_(
|
raise exceptions.ValidationException(detail=_(
|
||||||
"The %(type)s type is only supported for pools of "
|
"The %(type)s type is only supported for pools of "
|
||||||
"type %(protocol)s.") % {
|
"type %(protocol)s.") % {
|
||||||
'type': new_hm.type,
|
'type': new_hm.type,
|
||||||
'protocol': constants.PROTOCOL_UDP})
|
'protocol': '/'.join((constants.PROTOCOL_UDP,
|
||||||
|
lib_consts.PROTOCOL_SCTP))})
|
||||||
db_pool.health_monitor = new_hm
|
db_pool.health_monitor = new_hm
|
||||||
|
|
||||||
# Now check quotas for members
|
# Now check quotas for members
|
||||||
@@ -344,8 +352,9 @@ class PoolsController(base.BaseController):
|
|||||||
|
|
||||||
def _validate_pool_PUT(self, pool, db_pool):
|
def _validate_pool_PUT(self, pool, db_pool):
|
||||||
|
|
||||||
if db_pool.protocol == constants.PROTOCOL_UDP:
|
if db_pool.protocol in (constants.PROTOCOL_UDP,
|
||||||
self._validate_pool_request_for_udp(pool)
|
lib_consts.PROTOCOL_SCTP):
|
||||||
|
self._validate_pool_request_for_udp_sctp(pool)
|
||||||
else:
|
else:
|
||||||
if (pool.session_persistence and (
|
if (pool.session_persistence and (
|
||||||
pool.session_persistence.persistence_timeout or
|
pool.session_persistence.persistence_timeout or
|
||||||
|
|||||||
@@ -214,7 +214,8 @@ VALID_LISTENER_POOL_PROTOCOL_MAP = {
|
|||||||
lib_consts.PROTOCOL_PROXYV2, PROTOCOL_TCP],
|
lib_consts.PROTOCOL_PROXYV2, PROTOCOL_TCP],
|
||||||
PROTOCOL_TERMINATED_HTTPS: [PROTOCOL_HTTP, PROTOCOL_PROXY,
|
PROTOCOL_TERMINATED_HTTPS: [PROTOCOL_HTTP, PROTOCOL_PROXY,
|
||||||
lib_consts.PROTOCOL_PROXYV2],
|
lib_consts.PROTOCOL_PROXYV2],
|
||||||
PROTOCOL_UDP: [PROTOCOL_UDP]}
|
PROTOCOL_UDP: [PROTOCOL_UDP],
|
||||||
|
lib_consts.PROTOCOL_SCTP: [lib_consts.PROTOCOL_SCTP]}
|
||||||
|
|
||||||
# API Integer Ranges
|
# API Integer Ranges
|
||||||
MIN_PORT_NUMBER = 1
|
MIN_PORT_NUMBER = 1
|
||||||
@@ -815,6 +816,7 @@ L4_PROTOCOL_MAP = {
|
|||||||
PROTOCOL_PROXY: PROTOCOL_TCP,
|
PROTOCOL_PROXY: PROTOCOL_TCP,
|
||||||
lib_consts.PROTOCOL_PROXYV2: PROTOCOL_TCP,
|
lib_consts.PROTOCOL_PROXYV2: PROTOCOL_TCP,
|
||||||
PROTOCOL_UDP: PROTOCOL_UDP,
|
PROTOCOL_UDP: PROTOCOL_UDP,
|
||||||
|
lib_consts.PROTOCOL_SCTP: lib_consts.PROTOCOL_SCTP,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Image drivers
|
# Image drivers
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# Copyright 2020 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""sctp support
|
||||||
|
|
||||||
|
Revision ID: 8b47b2546312
|
||||||
|
Revises: e6ee84f0abf3
|
||||||
|
Create Date: 2020-06-26 09:26:45.397873
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy import sql
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '8b47b2546312'
|
||||||
|
down_revision = 'e6ee84f0abf3'
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
for table in ['protocol', 'health_monitor_type']:
|
||||||
|
insert_table = sql.table(
|
||||||
|
table,
|
||||||
|
sql.column(u'name', sa.String),
|
||||||
|
sql.column(u'description', sa.String)
|
||||||
|
)
|
||||||
|
|
||||||
|
op.bulk_insert(
|
||||||
|
insert_table,
|
||||||
|
[
|
||||||
|
{'name': 'SCTP'}
|
||||||
|
]
|
||||||
|
)
|
||||||
@@ -16,6 +16,7 @@ import time
|
|||||||
|
|
||||||
from neutronclient.common import exceptions as neutron_client_exceptions
|
from neutronclient.common import exceptions as neutron_client_exceptions
|
||||||
from novaclient import exceptions as nova_client_exceptions
|
from novaclient import exceptions as nova_client_exceptions
|
||||||
|
from octavia_lib.common import constants as lib_consts
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from stevedore import driver as stevedore_driver
|
from stevedore import driver as stevedore_driver
|
||||||
@@ -159,6 +160,8 @@ class AllowedAddressPairsDriver(neutron_base.BaseNeutronDriver):
|
|||||||
protocol = constants.PROTOCOL_TCP.lower()
|
protocol = constants.PROTOCOL_TCP.lower()
|
||||||
if listener.protocol == constants.PROTOCOL_UDP:
|
if listener.protocol == constants.PROTOCOL_UDP:
|
||||||
protocol = constants.PROTOCOL_UDP.lower()
|
protocol = constants.PROTOCOL_UDP.lower()
|
||||||
|
elif listener.protocol == lib_consts.PROTOCOL_SCTP:
|
||||||
|
protocol = lib_consts.PROTOCOL_SCTP.lower()
|
||||||
|
|
||||||
if listener.allowed_cidrs:
|
if listener.allowed_cidrs:
|
||||||
for ac in listener.allowed_cidrs:
|
for ac in listener.allowed_cidrs:
|
||||||
@@ -183,7 +186,8 @@ class AllowedAddressPairsDriver(neutron_base.BaseNeutronDriver):
|
|||||||
# None ports with the egress rules. VRRP uses protocol 51 and 112
|
# None ports with the egress rules. VRRP uses protocol 51 and 112
|
||||||
if (rule.get('direction') == 'egress' or
|
if (rule.get('direction') == 'egress' or
|
||||||
rule.get('protocol').upper() not in
|
rule.get('protocol').upper() not in
|
||||||
[constants.PROTOCOL_TCP, constants.PROTOCOL_UDP]):
|
[constants.PROTOCOL_TCP, constants.PROTOCOL_UDP,
|
||||||
|
lib_consts.PROTOCOL_SCTP]):
|
||||||
continue
|
continue
|
||||||
old_ports.append((rule.get('port_range_max'),
|
old_ports.append((rule.get('port_range_max'),
|
||||||
rule.get('protocol').lower(),
|
rule.get('protocol').lower(),
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class TestRootController(base_db_test.OctaviaDBTestBase):
|
|||||||
def test_api_versions(self):
|
def test_api_versions(self):
|
||||||
versions = self._get_versions_with_config()
|
versions = self._get_versions_with_config()
|
||||||
version_ids = tuple(v.get('id') for v in versions)
|
version_ids = tuple(v.get('id') for v in versions)
|
||||||
self.assertEqual(23, len(version_ids))
|
self.assertEqual(24, len(version_ids))
|
||||||
self.assertIn('v2.0', version_ids)
|
self.assertIn('v2.0', version_ids)
|
||||||
self.assertIn('v2.1', version_ids)
|
self.assertIn('v2.1', version_ids)
|
||||||
self.assertIn('v2.2', version_ids)
|
self.assertIn('v2.2', version_ids)
|
||||||
@@ -69,6 +69,7 @@ class TestRootController(base_db_test.OctaviaDBTestBase):
|
|||||||
self.assertIn('v2.20', version_ids)
|
self.assertIn('v2.20', version_ids)
|
||||||
self.assertIn('v2.21', version_ids)
|
self.assertIn('v2.21', version_ids)
|
||||||
self.assertIn('v2.22', version_ids)
|
self.assertIn('v2.22', version_ids)
|
||||||
|
self.assertIn('v2.23', version_ids)
|
||||||
|
|
||||||
# Each version should have a 'self' 'href' to the API version URL
|
# Each version should have a 'self' 'href' to the API version URL
|
||||||
# [{u'rel': u'self', u'href': u'http://localhost/v2'}]
|
# [{u'rel': u'self', u'href': u'http://localhost/v2'}]
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ from octavia.common import data_models
|
|||||||
from octavia.common import exceptions
|
from octavia.common import exceptions
|
||||||
from octavia.db import repositories
|
from octavia.db import repositories
|
||||||
from octavia.tests.functional.api.v2 import base
|
from octavia.tests.functional.api.v2 import base
|
||||||
|
from octavia_lib.common import constants as lib_consts
|
||||||
|
|
||||||
|
|
||||||
class TestHealthMonitor(base.BaseAPITest):
|
class TestHealthMonitor(base.BaseAPITest):
|
||||||
@@ -56,6 +57,7 @@ class TestHealthMonitor(base.BaseAPITest):
|
|||||||
self.set_lb_status(self.lb_id)
|
self.set_lb_status(self.lb_id)
|
||||||
self.pool_repo = repositories.PoolRepository()
|
self.pool_repo = repositories.PoolRepository()
|
||||||
self._setup_udp_lb_resources()
|
self._setup_udp_lb_resources()
|
||||||
|
self._setup_sctp_lb_resources()
|
||||||
|
|
||||||
def _setup_udp_lb_resources(self):
|
def _setup_udp_lb_resources(self):
|
||||||
self.udp_lb = self.create_load_balancer(uuidutils.generate_uuid()).get(
|
self.udp_lb = self.create_load_balancer(uuidutils.generate_uuid()).get(
|
||||||
@@ -80,6 +82,25 @@ class TestHealthMonitor(base.BaseAPITest):
|
|||||||
group='api_settings',
|
group='api_settings',
|
||||||
udp_connect_min_interval_health_monitor='3')
|
udp_connect_min_interval_health_monitor='3')
|
||||||
|
|
||||||
|
def _setup_sctp_lb_resources(self):
|
||||||
|
self.sctp_lb = self.create_load_balancer(
|
||||||
|
uuidutils.generate_uuid()).get('loadbalancer')
|
||||||
|
self.sctp_lb_id = self.sctp_lb.get('id')
|
||||||
|
self.set_lb_status(self.sctp_lb_id)
|
||||||
|
|
||||||
|
self.sctp_listener = self.create_listener(
|
||||||
|
lib_consts.PROTOCOL_SCTP, 8888,
|
||||||
|
self.sctp_lb_id).get('listener')
|
||||||
|
self.sctp_listener_id = self.sctp_listener.get('id')
|
||||||
|
self.set_lb_status(self.sctp_lb_id)
|
||||||
|
|
||||||
|
self.sctp_pool_with_listener = self.create_pool(
|
||||||
|
None, lib_consts.PROTOCOL_SCTP, constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||||
|
listener_id=self.sctp_listener_id)
|
||||||
|
self.sctp_pool_with_listener_id = (
|
||||||
|
self.sctp_pool_with_listener.get('pool').get('id'))
|
||||||
|
self.set_lb_status(self.sctp_lb_id)
|
||||||
|
|
||||||
def test_get(self):
|
def test_get(self):
|
||||||
api_hm = self.create_health_monitor(
|
api_hm = self.create_health_monitor(
|
||||||
self.pool_id, constants.HEALTH_MONITOR_HTTP,
|
self.pool_id, constants.HEALTH_MONITOR_HTTP,
|
||||||
@@ -936,6 +957,32 @@ class TestHealthMonitor(base.BaseAPITest):
|
|||||||
self.assertEqual('/test.html', api_hm.get('url_path'))
|
self.assertEqual('/test.html', api_hm.get('url_path'))
|
||||||
self.assertEqual('200-201', api_hm.get('expected_codes'))
|
self.assertEqual('200-201', api_hm.get('expected_codes'))
|
||||||
|
|
||||||
|
def test_create_udp_case_with_sctp_type(self):
|
||||||
|
# create with SCTP type
|
||||||
|
api_hm = self.create_health_monitor(
|
||||||
|
self.udp_pool_with_listener_id,
|
||||||
|
lib_consts.HEALTH_MONITOR_SCTP,
|
||||||
|
3, 1, 1, 1).get(self.root_tag)
|
||||||
|
self.assert_correct_status(
|
||||||
|
lb_id=self.udp_lb_id, listener_id=self.udp_listener_id,
|
||||||
|
pool_id=self.udp_pool_with_listener_id, hm_id=api_hm.get('id'),
|
||||||
|
lb_prov_status=constants.PENDING_UPDATE,
|
||||||
|
listener_prov_status=constants.PENDING_UPDATE,
|
||||||
|
pool_prov_status=constants.PENDING_UPDATE,
|
||||||
|
hm_prov_status=constants.PENDING_CREATE,
|
||||||
|
hm_op_status=constants.OFFLINE)
|
||||||
|
self.set_lb_status(self.udp_lb_id)
|
||||||
|
self.assertEqual(lib_consts.HEALTH_MONITOR_SCTP,
|
||||||
|
api_hm.get('type'))
|
||||||
|
self.assertEqual(3, api_hm.get('delay'))
|
||||||
|
self.assertEqual(1, api_hm.get('timeout'))
|
||||||
|
self.assertEqual(1, api_hm.get('max_retries_down'))
|
||||||
|
self.assertEqual(1, api_hm.get('max_retries'))
|
||||||
|
# Verify the L7 fields is None
|
||||||
|
self.assertIsNone(api_hm.get('http_method'))
|
||||||
|
self.assertIsNone(api_hm.get('url_path'))
|
||||||
|
self.assertIsNone(api_hm.get('expected_codes'))
|
||||||
|
|
||||||
def test_udp_case_when_udp_connect_min_interval_health_monitor_set(self):
|
def test_udp_case_when_udp_connect_min_interval_health_monitor_set(self):
|
||||||
# negative case first
|
# negative case first
|
||||||
req_dict = {'pool_id': self.udp_pool_with_listener_id,
|
req_dict = {'pool_id': self.udp_pool_with_listener_id,
|
||||||
@@ -981,6 +1028,7 @@ class TestHealthMonitor(base.BaseAPITest):
|
|||||||
"monitor is supported.") % {
|
"monitor is supported.") % {
|
||||||
'pool_protocol': constants.PROTOCOL_UDP,
|
'pool_protocol': constants.PROTOCOL_UDP,
|
||||||
'types': '/'.join([constants.HEALTH_MONITOR_UDP_CONNECT,
|
'types': '/'.join([constants.HEALTH_MONITOR_UDP_CONNECT,
|
||||||
|
lib_consts.HEALTH_MONITOR_SCTP,
|
||||||
constants.HEALTH_MONITOR_TCP,
|
constants.HEALTH_MONITOR_TCP,
|
||||||
constants.HEALTH_MONITOR_HTTP])}
|
constants.HEALTH_MONITOR_HTTP])}
|
||||||
|
|
||||||
@@ -1005,7 +1053,8 @@ class TestHealthMonitor(base.BaseAPITest):
|
|||||||
"supported for pools of type "
|
"supported for pools of type "
|
||||||
"%(protocol)s.") % {
|
"%(protocol)s.") % {
|
||||||
'type': constants.HEALTH_MONITOR_UDP_CONNECT,
|
'type': constants.HEALTH_MONITOR_UDP_CONNECT,
|
||||||
'protocol': constants.PROTOCOL_UDP}
|
'protocol': '/'.join((constants.PROTOCOL_UDP,
|
||||||
|
lib_consts.PROTOCOL_SCTP))}
|
||||||
res = self.post(self.HMS_PATH, self._build_body(req_dict),
|
res = self.post(self.HMS_PATH, self._build_body(req_dict),
|
||||||
status=400,
|
status=400,
|
||||||
expect_errors=True)
|
expect_errors=True)
|
||||||
@@ -1014,6 +1063,108 @@ class TestHealthMonitor(base.BaseAPITest):
|
|||||||
lb_id=self.udp_lb_id, listener_id=self.udp_listener_id,
|
lb_id=self.udp_lb_id, listener_id=self.udp_listener_id,
|
||||||
pool_id=self.udp_pool_with_listener_id)
|
pool_id=self.udp_pool_with_listener_id)
|
||||||
|
|
||||||
|
def test_create_sctp_case_with_udp_connect_type(self):
|
||||||
|
# create with UDP-CONNECT type
|
||||||
|
api_hm = self.create_health_monitor(
|
||||||
|
self.sctp_pool_with_listener_id,
|
||||||
|
constants.HEALTH_MONITOR_UDP_CONNECT,
|
||||||
|
3, 1, 1, 1).get(self.root_tag)
|
||||||
|
self.assert_correct_status(
|
||||||
|
lb_id=self.sctp_lb_id, listener_id=self.sctp_listener_id,
|
||||||
|
pool_id=self.sctp_pool_with_listener_id, hm_id=api_hm.get('id'),
|
||||||
|
lb_prov_status=constants.PENDING_UPDATE,
|
||||||
|
listener_prov_status=constants.PENDING_UPDATE,
|
||||||
|
pool_prov_status=constants.PENDING_UPDATE,
|
||||||
|
hm_prov_status=constants.PENDING_CREATE,
|
||||||
|
hm_op_status=constants.OFFLINE)
|
||||||
|
self.set_lb_status(self.sctp_lb_id)
|
||||||
|
self.assertEqual(constants.HEALTH_MONITOR_UDP_CONNECT,
|
||||||
|
api_hm.get('type'))
|
||||||
|
self.assertEqual(3, api_hm.get('delay'))
|
||||||
|
self.assertEqual(1, api_hm.get('timeout'))
|
||||||
|
self.assertEqual(1, api_hm.get('max_retries_down'))
|
||||||
|
self.assertEqual(1, api_hm.get('max_retries'))
|
||||||
|
# Verify the L7 fields is None
|
||||||
|
self.assertIsNone(api_hm.get('http_method'))
|
||||||
|
self.assertIsNone(api_hm.get('url_path'))
|
||||||
|
self.assertIsNone(api_hm.get('expected_codes'))
|
||||||
|
|
||||||
|
def test_create_sctp_case_with_tcp_type(self):
|
||||||
|
# create with TCP type
|
||||||
|
api_hm = self.create_health_monitor(
|
||||||
|
self.sctp_pool_with_listener_id, constants.HEALTH_MONITOR_TCP,
|
||||||
|
3, 1, 1, 1).get(self.root_tag)
|
||||||
|
self.assert_correct_status(
|
||||||
|
lb_id=self.sctp_lb_id, listener_id=self.sctp_listener_id,
|
||||||
|
pool_id=self.sctp_pool_with_listener_id, hm_id=api_hm.get('id'),
|
||||||
|
lb_prov_status=constants.PENDING_UPDATE,
|
||||||
|
listener_prov_status=constants.PENDING_UPDATE,
|
||||||
|
pool_prov_status=constants.PENDING_UPDATE,
|
||||||
|
hm_prov_status=constants.PENDING_CREATE,
|
||||||
|
hm_op_status=constants.OFFLINE)
|
||||||
|
self.set_lb_status(self.sctp_lb_id)
|
||||||
|
self.assertEqual(constants.HEALTH_MONITOR_TCP, api_hm.get('type'))
|
||||||
|
self.assertEqual(3, api_hm.get('delay'))
|
||||||
|
self.assertEqual(1, api_hm.get('timeout'))
|
||||||
|
self.assertEqual(1, api_hm.get('max_retries_down'))
|
||||||
|
self.assertEqual(1, api_hm.get('max_retries'))
|
||||||
|
self.assertIsNone(api_hm.get('http_method'))
|
||||||
|
self.assertIsNone(api_hm.get('url_path'))
|
||||||
|
self.assertIsNone(api_hm.get('expected_codes'))
|
||||||
|
|
||||||
|
def test_create_sctp_case_with_http_type(self):
|
||||||
|
# create with HTTP type
|
||||||
|
api_hm = self.create_health_monitor(
|
||||||
|
self.sctp_pool_with_listener_id, constants.HEALTH_MONITOR_HTTP,
|
||||||
|
3, 1, 1, 1, url_path='/test.html',
|
||||||
|
http_method=constants.HEALTH_MONITOR_HTTP_METHOD_GET,
|
||||||
|
expected_codes='200-201').get(self.root_tag)
|
||||||
|
self.assert_correct_status(
|
||||||
|
lb_id=self.sctp_lb_id, listener_id=self.sctp_listener_id,
|
||||||
|
pool_id=self.sctp_pool_with_listener_id, hm_id=api_hm.get('id'),
|
||||||
|
lb_prov_status=constants.PENDING_UPDATE,
|
||||||
|
listener_prov_status=constants.PENDING_UPDATE,
|
||||||
|
pool_prov_status=constants.PENDING_UPDATE,
|
||||||
|
hm_prov_status=constants.PENDING_CREATE,
|
||||||
|
hm_op_status=constants.OFFLINE)
|
||||||
|
self.set_lb_status(self.sctp_lb_id)
|
||||||
|
self.assertEqual(constants.HEALTH_MONITOR_HTTP, api_hm.get('type'))
|
||||||
|
self.assertEqual(3, api_hm.get('delay'))
|
||||||
|
self.assertEqual(1, api_hm.get('timeout'))
|
||||||
|
self.assertEqual(1, api_hm.get('max_retries_down'))
|
||||||
|
self.assertEqual(1, api_hm.get('max_retries'))
|
||||||
|
self.assertEqual(3, api_hm.get('delay'))
|
||||||
|
self.assertEqual(constants.HEALTH_MONITOR_HTTP_METHOD_GET,
|
||||||
|
api_hm.get('http_method'))
|
||||||
|
self.assertEqual('/test.html', api_hm.get('url_path'))
|
||||||
|
self.assertEqual('200-201', api_hm.get('expected_codes'))
|
||||||
|
|
||||||
|
def test_create_sctp_case_with_sctp_type(self):
|
||||||
|
# create with SCTP type
|
||||||
|
api_hm = self.create_health_monitor(
|
||||||
|
self.sctp_pool_with_listener_id,
|
||||||
|
lib_consts.HEALTH_MONITOR_SCTP,
|
||||||
|
3, 1, 1, 1).get(self.root_tag)
|
||||||
|
self.assert_correct_status(
|
||||||
|
lb_id=self.sctp_lb_id, listener_id=self.sctp_listener_id,
|
||||||
|
pool_id=self.sctp_pool_with_listener_id, hm_id=api_hm.get('id'),
|
||||||
|
lb_prov_status=constants.PENDING_UPDATE,
|
||||||
|
listener_prov_status=constants.PENDING_UPDATE,
|
||||||
|
pool_prov_status=constants.PENDING_UPDATE,
|
||||||
|
hm_prov_status=constants.PENDING_CREATE,
|
||||||
|
hm_op_status=constants.OFFLINE)
|
||||||
|
self.set_lb_status(self.sctp_lb_id)
|
||||||
|
self.assertEqual(lib_consts.HEALTH_MONITOR_SCTP,
|
||||||
|
api_hm.get('type'))
|
||||||
|
self.assertEqual(3, api_hm.get('delay'))
|
||||||
|
self.assertEqual(1, api_hm.get('timeout'))
|
||||||
|
self.assertEqual(1, api_hm.get('max_retries_down'))
|
||||||
|
self.assertEqual(1, api_hm.get('max_retries'))
|
||||||
|
# Verify the L7 fields is None
|
||||||
|
self.assertIsNone(api_hm.get('http_method'))
|
||||||
|
self.assertIsNone(api_hm.get('url_path'))
|
||||||
|
self.assertIsNone(api_hm.get('expected_codes'))
|
||||||
|
|
||||||
def test_ensure_L7_fields_filled_during_create(self):
|
def test_ensure_L7_fields_filled_during_create(self):
|
||||||
# Create a health monitor with a load balancer pool
|
# Create a health monitor with a load balancer pool
|
||||||
api_hm = self.create_health_monitor(
|
api_hm = self.create_health_monitor(
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ from octavia.db import api as db_api
|
|||||||
from octavia.tests.common import constants as c_const
|
from octavia.tests.common import constants as c_const
|
||||||
from octavia.tests.common import sample_certs
|
from octavia.tests.common import sample_certs
|
||||||
from octavia.tests.functional.api.v2 import base
|
from octavia.tests.functional.api.v2 import base
|
||||||
|
from octavia_lib.common import constants as lib_consts
|
||||||
|
|
||||||
|
|
||||||
class TestPool(base.BaseAPITest):
|
class TestPool(base.BaseAPITest):
|
||||||
@@ -1026,7 +1027,9 @@ class TestPool(base.BaseAPITest):
|
|||||||
'lb_algorithm': constants.LB_ALGORITHM_ROUND_ROBIN,
|
'lb_algorithm': constants.LB_ALGORITHM_ROUND_ROBIN,
|
||||||
'session_persistence': sp}
|
'session_persistence': sp}
|
||||||
expect_error_msg = ("Validation failure: Cookie names are not "
|
expect_error_msg = ("Validation failure: Cookie names are not "
|
||||||
"supported for %s pools.") % constants.PROTOCOL_UDP
|
"supported for %s pools.") % (
|
||||||
|
"/".join((constants.PROTOCOL_UDP,
|
||||||
|
lib_consts.PROTOCOL_SCTP)))
|
||||||
res = self.post(self.POOLS_PATH, self._build_body(req_dict),
|
res = self.post(self.POOLS_PATH, self._build_body(req_dict),
|
||||||
status=400, expect_errors=True)
|
status=400, expect_errors=True)
|
||||||
self.assertEqual(expect_error_msg, res.json['faultstring'])
|
self.assertEqual(expect_error_msg, res.json['faultstring'])
|
||||||
@@ -1047,7 +1050,10 @@ class TestPool(base.BaseAPITest):
|
|||||||
constants.SESSION_PERSISTENCE_APP_COOKIE]:
|
constants.SESSION_PERSISTENCE_APP_COOKIE]:
|
||||||
expect_error_msg = ("Validation failure: Session persistence of "
|
expect_error_msg = ("Validation failure: Session persistence of "
|
||||||
"type %s is not supported for %s protocol "
|
"type %s is not supported for %s protocol "
|
||||||
"pools.") % (type, constants.PROTOCOL_UDP)
|
"pools.") % (
|
||||||
|
type,
|
||||||
|
"/".join((constants.PROTOCOL_UDP,
|
||||||
|
lib_consts.PROTOCOL_SCTP)))
|
||||||
sp.update({'type': type})
|
sp.update({'type': type})
|
||||||
req_dict['session_persistence'] = sp
|
req_dict['session_persistence'] = sp
|
||||||
res = self.post(self.POOLS_PATH, self._build_body(req_dict),
|
res = self.post(self.POOLS_PATH, self._build_body(req_dict),
|
||||||
@@ -1070,9 +1076,11 @@ class TestPool(base.BaseAPITest):
|
|||||||
'session_persistence': sp}
|
'session_persistence': sp}
|
||||||
expect_error_msg = (
|
expect_error_msg = (
|
||||||
"Validation failure: session_persistence %s type for %s "
|
"Validation failure: session_persistence %s type for %s "
|
||||||
"protocol only accepts: type, persistence_timeout, "
|
"protocols only accepts: type, persistence_timeout, "
|
||||||
"persistence_granularity.") % (
|
"persistence_granularity.") % (
|
||||||
constants.SESSION_PERSISTENCE_SOURCE_IP, constants.PROTOCOL_UDP)
|
constants.SESSION_PERSISTENCE_SOURCE_IP,
|
||||||
|
" and ".join((constants.PROTOCOL_UDP,
|
||||||
|
lib_consts.PROTOCOL_SCTP)))
|
||||||
res = self.post(self.POOLS_PATH, self._build_body(req_dict),
|
res = self.post(self.POOLS_PATH, self._build_body(req_dict),
|
||||||
status=400, expect_errors=True)
|
status=400, expect_errors=True)
|
||||||
self.assertEqual(expect_error_msg, res.json['faultstring'])
|
self.assertEqual(expect_error_msg, res.json['faultstring'])
|
||||||
@@ -1092,7 +1100,9 @@ class TestPool(base.BaseAPITest):
|
|||||||
'lb_algorithm': constants.LB_ALGORITHM_ROUND_ROBIN}
|
'lb_algorithm': constants.LB_ALGORITHM_ROUND_ROBIN}
|
||||||
expect_error_msg = ("Validation failure: persistence_timeout and "
|
expect_error_msg = ("Validation failure: persistence_timeout and "
|
||||||
"persistence_granularity is only for %s protocol "
|
"persistence_granularity is only for %s protocol "
|
||||||
"pools.") % constants.PROTOCOL_UDP
|
"pools.") % (
|
||||||
|
" and ".join((constants.PROTOCOL_UDP,
|
||||||
|
lib_consts.PROTOCOL_SCTP)))
|
||||||
for s in sps:
|
for s in sps:
|
||||||
req_dict.update({'session_persistence': s})
|
req_dict.update({'session_persistence': s})
|
||||||
res = self.post(self.POOLS_PATH, self._build_body(req_dict),
|
res = self.post(self.POOLS_PATH, self._build_body(req_dict),
|
||||||
@@ -1320,7 +1330,8 @@ class TestPool(base.BaseAPITest):
|
|||||||
# Error during update pool with non-UDP type and cookie_name.
|
# Error during update pool with non-UDP type and cookie_name.
|
||||||
expect_error_msg = (
|
expect_error_msg = (
|
||||||
"Validation failure: Cookie names are not supported for %s"
|
"Validation failure: Cookie names are not supported for %s"
|
||||||
" pools.") % constants.PROTOCOL_UDP
|
" pools.") % ("/".join((constants.PROTOCOL_UDP,
|
||||||
|
lib_consts.PROTOCOL_SCTP)))
|
||||||
sess_p['type'] = constants.SESSION_PERSISTENCE_HTTP_COOKIE
|
sess_p['type'] = constants.SESSION_PERSISTENCE_HTTP_COOKIE
|
||||||
sess_p['cookie_name'] = 'test-cookie-name'
|
sess_p['cookie_name'] = 'test-cookie-name'
|
||||||
new_pool = {'session_persistence': sess_p}
|
new_pool = {'session_persistence': sess_p}
|
||||||
@@ -1333,10 +1344,11 @@ class TestPool(base.BaseAPITest):
|
|||||||
|
|
||||||
# Error during update pool with source ip type and more options.
|
# Error during update pool with source ip type and more options.
|
||||||
expect_error_msg = (
|
expect_error_msg = (
|
||||||
"Validation failure: session_persistence %s type for %s protocol "
|
"Validation failure: session_persistence %s type for %s protocols "
|
||||||
"only accepts: type, persistence_timeout, "
|
"only accepts: type, persistence_timeout, "
|
||||||
"persistence_granularity.") % (
|
"persistence_granularity.") % (
|
||||||
constants.SESSION_PERSISTENCE_SOURCE_IP, constants.PROTOCOL_UDP)
|
constants.SESSION_PERSISTENCE_SOURCE_IP,
|
||||||
|
" and ".join((constants.PROTOCOL_UDP, lib_consts.PROTOCOL_SCTP)))
|
||||||
sess_p['type'] = constants.SESSION_PERSISTENCE_SOURCE_IP
|
sess_p['type'] = constants.SESSION_PERSISTENCE_SOURCE_IP
|
||||||
sess_p['cookie_name'] = 'test-cookie-name'
|
sess_p['cookie_name'] = 'test-cookie-name'
|
||||||
sess_p['persistence_timeout'] = 4
|
sess_p['persistence_timeout'] = 4
|
||||||
@@ -1354,7 +1366,10 @@ class TestPool(base.BaseAPITest):
|
|||||||
constants.SESSION_PERSISTENCE_HTTP_COOKIE]:
|
constants.SESSION_PERSISTENCE_HTTP_COOKIE]:
|
||||||
expect_error_msg = ("Validation failure: Session persistence of "
|
expect_error_msg = ("Validation failure: Session persistence of "
|
||||||
"type %s is not supported for %s protocol "
|
"type %s is not supported for %s protocol "
|
||||||
"pools.") % (ty, constants.PROTOCOL_UDP)
|
"pools.") % (
|
||||||
|
ty,
|
||||||
|
"/".join((constants.PROTOCOL_UDP,
|
||||||
|
lib_consts.PROTOCOL_SCTP)))
|
||||||
sess_p['type'] = ty
|
sess_p['type'] = ty
|
||||||
res = self.put(self.POOL_PATH.format(pool_id=api_pool.get('id')),
|
res = self.put(self.POOL_PATH.format(pool_id=api_pool.get('id')),
|
||||||
self._build_body(new_pool), status=400,
|
self._build_body(new_pool), status=400,
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Add support for SCTP protocol. SCTP support has been added in the Octavia
|
||||||
|
API for listener, pool, and health-monitor resources.
|
||||||
Reference in New Issue
Block a user