Merge "Fix multi-listener load balancers"

This commit is contained in:
Zuul 2019-07-29 22:14:04 +00:00 committed by Gerrit Code Review
commit 37799137a3
80 changed files with 6752 additions and 2136 deletions

View File

@ -29,9 +29,7 @@ communication is limited to fail-over protocols.)
Versioning Versioning
---------- ----------
All Octavia APIs (including internal APIs like this one) are versioned. For the All Octavia APIs (including internal APIs like this one) are versioned. For the
purposes of this document, the initial version of this API shall be v0.5. (So, purposes of this document, the initial version of this API shall be 1.0.
any reference to a *:version* variable should be replaced with the literal
string 'v0.5'.)
Response codes Response codes
-------------- --------------
@ -74,136 +72,6 @@ a secure way (ex. memory filesystem).
API API
=== ===
Get amphora topology
--------------------
* **URL:** /*:version*/topology
* **Method:** GET
* **URL params:** none
* **Data params:** none
* **Success Response:**
* Code: 200
* Content: JSON formatted listing of this amphora's configured topology.
* **Error Response:**
* none
JSON Response attributes:
* *hostname* - hostname of amphora
* *uuid* - uuid of amphora
* *topology* - One of: SINGLE, ACTIVE-STANDBY, ACTIVE-ACTIVE
* *role* - One of ACTIVE, STANDBY (only applicable to ACTIVE-STANDBY)
* *ha_ip* - only applicable to ACTIVE-STANDBY topology: Highly-available
routing IP address for the ACTIVE-STANDBY pair.
**Examples**
* Success code 200:
::
JSON response:
{
'hostname': 'octavia-haproxy-img-00328',
'uuid': '6e2bc8a0-2548-4fb7-a5f0-fb1ef4a696ce',
'topology': 'SINGLE',
'role': 'ACTIVE',
'ha_ip': '',
}
Set amphora topology
--------------------
* **URL:** /*:version*/topology
* **Method:** POST
* **URL params:** none
* **Data params:**
* *topology*: One of: SINGLE, ACTIVE-STANDBY, ACTIVE-ACTIVE
* *role*: One of: ACTIVE, STANDBY (only applicable to ACTIVE-STANDBY)
* *ha_ip*: (only applicable to ACTIVE-STANDBY) Highly-available IP for the
HA pair
* *secret*: (only applicable to ACTIVE-STANDBY topology) Shared secret used
for authentication with other HA pair member
* **Success Response:**
* Code: 200
* Content: OK
* Code: 202
* Content: OK
* **Error Response:**
* Code: 400
* Content: Invalid request.
* *(Response will also include information on which parameters did not*
*pass either a syntax check or other topology logic test)*
* Code: 503
* Content: Topology transition in progress
* **Response:**
| OK
**Notes:** In an ACTIVE-STANDBY configuration, the 'role' parameter might
change spontaneously due to a failure of one node. In other topologies, the
role is not used.
Also note that some topology changes can take several minutes to enact, yet
we want all API commands to return in a matter of seconds. In this case, a
topology change is initiated, and the amphora status changes from "OK" to
"TOPOLOGY-CHANGE". The controller should not try to change any resources during
this transition. (Any attempts will be met with an error.) Once the
topology change is complete, amphora status should return to "OK". (When the
UDP communication from amphorae to controller is defined, a 'transition
complete' message is probably one good candidate for this type of UDP
communication.)
**Examples**
* Success code 200:
::
JSON POST parameters:
{
'topology': 'ACTIVE-STANDBY',
'role': 'ACTIVE',
'ha_ip': ' 203.0.113.2',
'secret': 'b20e06cf1abcf29c708d3b437f4a29892a0921d0',
}
Response:
OK
* Error code 400:
::
Response:
{
'message': 'Invalid request',
'details': 'Unknown topology: BAD_TEST_DATA',
}
* Error code 503:
::
Response:
{
'message': 'Topology transition in progress',
}
Get amphora info Get amphora info
---------------- ----------------
* **URL:** /info * **URL:** /info
@ -249,7 +117,7 @@ version string prepended to it.
Get amphora details Get amphora details
------------------- -------------------
* **URL:** /*:version*/details * **URL:** /1.0/details
* **Method:** GET * **Method:** GET
* **URL params:** none * **URL params:** none
* **Data params:** none * **Data params:** none
@ -372,7 +240,7 @@ health of the amphora, currently-configured topology and role, etc.
Get interface Get interface
------------- -------------
* **URL:** /*:version*/interface/*:ip* * **URL:** /1.0/interface/*:ip*
* **Method:** GET * **Method:** GET
* **URL params:** * **URL params:**
@ -408,7 +276,7 @@ Get interface
:: ::
GET URL: GET URL:
https://octavia-haproxy-img-00328.local/v0.5/interface/10.0.0.1 https://octavia-haproxy-img-00328.local/1.0/interface/10.0.0.1
JSON Response: JSON Response:
{ {
@ -422,7 +290,7 @@ Get interface
:: ::
GET URL: GET URL:
https://octavia-haproxy-img-00328.local/v0.5/interface/10.5.0.1 https://octavia-haproxy-img-00328.local/1.0/interface/10.5.0.1
JSON Response: JSON Response:
{ {
@ -435,7 +303,7 @@ Get interface
:: ::
GET URL: GET URL:
https://octavia-haproxy-img-00328.local/v0.5/interface/10.6.0.1.1 https://octavia-haproxy-img-00328.local/1.0/interface/10.6.0.1.1
JSON Response: JSON Response:
{ {
@ -446,7 +314,7 @@ Get interface
Get all listeners' statuses Get all listeners' statuses
--------------------------- ---------------------------
* **URL:** /*:version*/listeners * **URL:** /1.0/listeners
* **Method:** GET * **Method:** GET
* **URL params:** none * **URL params:** none
* **Data params:** none * **Data params:** none
@ -492,91 +360,14 @@ a valid haproxy configuration).
'type': 'TERMINATED_HTTPS', 'type': 'TERMINATED_HTTPS',
}] }]
Get a listener's status Start or Stop a load balancer
----------------------- -----------------------------
* **URL:** /*:version*/listeners/*:listener* * **URL:** /1.0/loadbalancer/*:object_id*/*:action*
* **Method:** GET
* **URL params:**
* *:listener* = Listener UUID
* **Data params:** none
* **Success Response:**
* Code: 200
* Content: JSON-formatted listener status
* **Error Response:**
* Code: 404
* Content: Not Found
JSON Response attributes:
* *status* - One of the operational status: ACTIVE, STOPPED, ERROR -
future versions might support provisioning status:
PENDING_CREATE, PENDING_UPDATE, PENDING_DELETE, DELETED
* *uuid* - Listener UUID
* *type* - One of: TCP, HTTP, TERMINATED_HTTPS
* *pools* - Map of pool UUIDs and their overall UP / DOWN / DEGRADED status
* *members* - Map of member UUIDs and their overall UP / DOWN status
**Notes:** Note that this returns a status if: the pid file exists,
the stats socket exists, or an haproxy configuration is present (not
just if there is a valid haproxy configuration).
**Examples**
* Success code 200:
::
JSON Response:
{
'status': 'ACTIVE',
'uuid': 'e2dfddc0-5b9e-11e4-8ed6-0800200c9a66',
'type': 'HTTP',
'pools':[
{
'uuid': '399bbf4b-5f6c-4370-a61e-ed2ff2fc9387',
'status': 'UP',
'members':[
{'73f6d278-ae1c-4248-ad02-0bfd50d69aab': 'UP'},
{'2edca57c-5890-4bcb-ae67-4ef75776cc67': 'DOWN'},
],
},
{
'uuid': '2250eb21-16ca-44bd-9b12-0b4eb3d18140',
'status': 'DOWN',
'members':[
{'130dff11-4aab-4ba8-a39b-8d77caa7a1ad': 'DOWN'},
],
},
],
}
* Error code 404:
::
JSON Response:
{
'message': 'Listener Not Found',
'details': 'No listener with UUID: 04bff5c3-5862-4a13-b9e3-9b440d0ed50a',
}
Start or Stop a listener
------------------------
* **URL:** /*:version*/listeners/*:listener*/*:action*
* **Method:** PUT * **Method:** PUT
* **URL params:** * **URL params:**
* *:listener* = Listener UUID * *:object_id* = Object UUID
* *:action* = One of: start, stop, reload * *:action* = One of: start, stop, reload
* **Data params:** none * **Data params:** none
@ -612,7 +403,7 @@ Start or Stop a listener
| OK | OK
| Configuration file is valid | Configuration file is valid
| haproxy daemon for 7e9f91eb-b3e6-4e3b-a1a7-d6f7fdc1de7c started (pid 32428) | haproxy daemon for 85e2111b-29c4-44be-94f3-e72045805801 started (pid 32428)
**Examples:** **Examples:**
@ -621,12 +412,12 @@ Start or Stop a listener
:: ::
PUT URL: PUT URL:
https://octavia-haproxy-img-00328.local/v0.5/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/start https://octavia-haproxy-img-00328.local/1.0/loadbalancer/85e2111b-29c4-44be-94f3-e72045805801/start
JSON Response: JSON Response:
{ {
'message': 'OK', 'message': 'OK',
'details': 'Configuration file is valid\nhaproxy daemon for 04bff5c3-5862-4a13-b9e3-9b440d0ed50a started', 'details': 'Configuration file is valid\nhaproxy daemon for 85e2111b-29c4-44be-94f3-e72045805801 started',
} }
* Error code 400: * Error code 400:
@ -634,7 +425,7 @@ Start or Stop a listener
:: ::
PUT URL: PUT URL:
https://octavia-haproxy-img-00328.local/v0.5/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/BAD_TEST_DATA https://octavia-haproxy-img-00328.local/1.0/loadbalancer/85e2111b-29c4-44be-94f3-e72045805801/BAD_TEST_DATA
JSON Response: JSON Response:
{ {
@ -647,12 +438,12 @@ Start or Stop a listener
:: ::
PUT URL: PUT URL:
https://octavia-haproxy-img-00328.local/v0.5/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/stop https://octavia-haproxy-img-00328.local/1.0/loadbalancer/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/stop
JSON Response: JSON Response:
{ {
'message': 'Listener Not Found', 'message': 'Listener Not Found',
'details': 'No listener with UUID: 04bff5c3-5862-4a13-b9e3-9b440d0ed50a', 'details': 'No loadbalancer with UUID: 04bff5c3-5862-4a13-b9e3-9b440d0ed50a',
} }
* Error code 500: * Error code 500:
@ -660,7 +451,7 @@ Start or Stop a listener
:: ::
PUT URL: PUT URL:
https://octavia-haproxy-img-00328.local/v0.5/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/stop https://octavia-haproxy-img-00328.local/1.0/loadbalancer/85e2111b-29c4-44be-94f3-e72045805801/stop
Response: Response:
{ {
@ -680,7 +471,7 @@ Start or Stop a listener
Delete a listener Delete a listener
----------------- -----------------
* **URL:** /*:version*/listeners/*:listener* * **URL:** /1.0/listeners/*:listener*
* **Method:** DELETE * **Method:** DELETE
* **URL params:** * **URL params:**
@ -724,7 +515,7 @@ Delete a listener
:: ::
DELETE URL: DELETE URL:
https://octavia-haproxy-img-00328.local/v0.5/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a https://octavia-haproxy-img-00328.local/1.0/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a
JSON Response: JSON Response:
{ {
@ -736,7 +527,7 @@ Delete a listener
:: ::
DELETE URL: DELETE URL:
https://octavia-haproxy-img-00328.local/v0.5/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a https://octavia-haproxy-img-00328.local/1.0/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a
JSON Response: JSON Response:
{ {
@ -756,11 +547,11 @@ Delete a listener
Upload SSL certificate PEM file Upload SSL certificate PEM file
------------------------------- -------------------------------
* **URL:** /*:version*/listeners/*:listener*/certificates/*:filename.pem* * **URL:** /1.0/loadbalancer/*:loadbalancer_id*/certificates/*:filename.pem*
* **Method:** PUT * **Method:** PUT
* **URL params:** * **URL params:**
* *:listener* = Listener UUID * *:loadbalancer_id* = Load balancer UUID
* *:filename* = PEM filename (see notes below for naming convention) * *:filename* = PEM filename (see notes below for naming convention)
* **Data params:** Certificate data. (PEM file should be a concatenation of * **Data params:** Certificate data. (PEM file should be a concatenation of
@ -812,7 +603,7 @@ explicitly restarted
:: ::
PUT URI: PUT URI:
https://octavia-haproxy-img-00328.local/v0.5/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/certificates/www.example.com.pem https://octavia-haproxy-img-00328.local/1.0/loadbalancer/85e2111b-29c4-44be-94f3-e72045805801/certificates/www.example.com.pem
(Put data should contain the certificate information, concatenated as (Put data should contain the certificate information, concatenated as
described above) described above)
@ -826,7 +617,7 @@ explicitly restarted
:: ::
PUT URI: PUT URI:
https://octavia-haproxy-img-00328.local/v0.5/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/certificates/www.example.com.pem https://octavia-haproxy-img-00328.local/1.0/loadbalancer/85e2111b-29c4-44be-94f3-e72045805801/certificates/www.example.com.pem
(If PUT data does not contain a certificate) (If PUT data does not contain a certificate)
JSON Response: JSON Response:
@ -839,7 +630,7 @@ explicitly restarted
:: ::
PUT URI: PUT URI:
https://octavia-haproxy-img-00328.local/v0.5/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/certificates/www.example.com.pem https://octavia-haproxy-img-00328.local/1.0/loadbalancer/85e2111b-29c4-44be-94f3-e72045805801/certificates/www.example.com.pem
(If PUT data does not contain an RSA key) (If PUT data does not contain an RSA key)
JSON Response: JSON Response:
@ -852,7 +643,7 @@ explicitly restarted
:: ::
PUT URI: PUT URI:
https://octavia-haproxy-img-00328.local/v0.5/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/certificates/www.example.com.pem https://octavia-haproxy-img-00328.local/1.0/loadbalancer/85e2111b-29c4-44be-94f3-e72045805801/certificates/www.example.com.pem
(If the first certificate and the RSA key do not have the same modulus.) (If the first certificate and the RSA key do not have the same modulus.)
JSON Response: JSON Response:
@ -865,15 +656,14 @@ explicitly restarted
:: ::
PUT URI: PUT URI:
https://octavia-haproxy-img-00328.local/v0.5/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/certificates/www.example.com.pem https://octavia-haproxy-img-00328.local/1.0/loadbalancer/85e2111b-29c4-44be-94f3-e72045805801/certificates/www.example.com.pem
JSON Response: JSON Response:
{ {
'message': 'Listener Not Found', 'message': 'Listener Not Found',
'details': 'No listener with UUID: 04bff5c3-5862-4a13-b9e3-9b440d0ed50a', 'details': 'No loadbalancer with UUID: 04bff5c3-5862-4a13-b9e3-9b440d0ed50a',
} }
* Error code 503: * Error code 503:
:: ::
@ -883,15 +673,14 @@ explicitly restarted
'message': 'Topology transition in progress', 'message': 'Topology transition in progress',
} }
Get SSL certificate md5sum Get SSL certificate md5sum
-------------------------- --------------------------
* **URL:** /*:version*/listeners/*:listener*/certificates/*:filename.pem* * **URL:** /1.0/loadbalancer/*:loadbalancer_id*/certificates/*:filename.pem*
* **Method:** GET * **Method:** GET
* **URL params:** * **URL params:**
* *:listener* = Listener UUID * *:loadbalancer_id* = Load balancer UUID
* *:filename* = PEM filename (see notes below for naming convention) * *:filename* = PEM filename (see notes below for naming convention)
* **Data params:** none * **Data params:** none
@ -937,7 +726,7 @@ disclosing it over the wire from the amphora is a security risk.
JSON Response: JSON Response:
{ {
'message': 'Listener Not Found', 'message': 'Listener Not Found',
'details': 'No listener with UUID: 04bff5c3-5862-4a13-b9e3-9b440d0ed50a', 'details': 'No loadbalancer with UUID: 04bff5c3-5862-4a13-b9e3-9b440d0ed50a',
} }
* Error code 404: * Error code 404:
@ -953,11 +742,11 @@ disclosing it over the wire from the amphora is a security risk.
Delete SSL certificate PEM file Delete SSL certificate PEM file
------------------------------- -------------------------------
* **URL:** /*:version*/listeners/*:listener*/certificates/*:filename.pem* * **URL:** /1.0/loadbalancer/*:loadbalancer_id*/certificates/*:filename.pem*
* **Method:** DELETE * **Method:** DELETE
* **URL params:** * **URL params:**
* *:listener* = Listener UUID * *:loadbalancer_id* = Load balancer UUID
* *:filename* = PEM filename (see notes below for naming convention) * *:filename* = PEM filename (see notes below for naming convention)
* **Data params:** none * **Data params:** none
@ -988,7 +777,7 @@ Delete SSL certificate PEM file
:: ::
DELETE URL: DELETE URL:
https://octavia-haproxy-img-00328.local/v0.5/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/certificates/www.example.com.pem https://octavia-haproxy-img-00328.local/1.0/loadbalancer/85e2111b-29c4-44be-94f3-e72045805801/certificates/www.example.com.pem
JSON Response: JSON Response:
{ {
@ -1000,7 +789,7 @@ Delete SSL certificate PEM file
:: ::
DELETE URL: DELETE URL:
https://octavia-haproxy-img-00328.local/v0.5/listeners/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/certificates/www.example.com.pem https://octavia-haproxy-img-00328.local/1.0/loadbalancer/85e2111b-29c4-44be-94f3-e72045805801/certificates/www.example.com.pem
JSON Response: JSON Response:
{ {
@ -1017,14 +806,14 @@ Delete SSL certificate PEM file
'message': 'Topology transition in progress', 'message': 'Topology transition in progress',
} }
Upload listener haproxy configuration Upload load balancer haproxy configuration
------------------------------------- ------------------------------------------
* **URL:** /*:version*/listeners/*:amphora_id*/*:listener*/haproxy * **URL:** /1.0/loadbalancer/*:amphora_id*/*:loadbalancer_id*/haproxy
* **Method:** PUT * **Method:** PUT
* **URL params:** * **URL params:**
* *:listener* = Listener UUID * *:loadbalancer_id* = Load Balancer UUID
* *:amphora_id* = Amphora UUID * *:amphora_id* = Amphora UUID
* **Data params:** haproxy configuration file for the listener * **Data params:** haproxy configuration file for the listener
@ -1071,7 +860,7 @@ out of the haproxy daemon status interface for tracking health and stats).
:: ::
PUT URL: PUT URL:
https://octavia-haproxy-img-00328.local/v0.5/listeners/d459b1c8-54b0-4030-9bec-4f449e73b1ef/04bff5c3-5862-4a13-b9e3-9b440d0ed50a/haproxy https://octavia-haproxy-img-00328.local/1.0/loadbalancer/d459b1c8-54b0-4030-9bec-4f449e73b1ef/85e2111b-29c4-44be-94f3-e72045805801/haproxy
(Upload PUT data should be a raw haproxy.conf file.) (Upload PUT data should be a raw haproxy.conf file.)
JSON Response: JSON Response:
@ -1098,14 +887,14 @@ out of the haproxy daemon status interface for tracking health and stats).
'message': 'Topology transition in progress', 'message': 'Topology transition in progress',
} }
Get listener haproxy configuration Get loadbalancer haproxy configuration
---------------------------------- --------------------------------------
* **URL:** /*:version*/listeners/*:listener*/haproxy * **URL:** /1.0/loadbalancer/*:loadbalancer_id*/haproxy
* **Method:** GET * **Method:** GET
* **URL params:** * **URL params:**
* *:listener* = Listener UUID * *:loadbalancer_id* = Load balancer UUID
* **Data params:** none * **Data params:** none
* **Success Response:** * **Success Response:**
@ -1122,7 +911,7 @@ Get listener haproxy configuration
* **Response:** * **Response:**
| # Config file for 7e9f91eb-b3e6-4e3b-a1a7-d6f7fdc1de7c | # Config file for 85e2111b-29c4-44be-94f3-e72045805801
| (cut for brevity) | (cut for brevity)
* **Implied actions:** none * **Implied actions:** none
@ -1134,11 +923,11 @@ Get listener haproxy configuration
:: ::
GET URL: GET URL:
https://octavia-haproxy-img-00328.local/v0.5/listeners/7e9f91eb-b3e6-4e3b-a1a7-d6f7fdc1de7c/haproxy https://octavia-haproxy-img-00328.local/1.0/loadbalancer/85e2111b-29c4-44be-94f3-e72045805801/haproxy
Response is the raw haproxy.cfg: Response is the raw haproxy.cfg:
# Config file for 7e9f91eb-b3e6-4e3b-a1a7-d6f7fdc1de7c # Config file for 85e2111b-29c4-44be-94f3-e72045805801
(cut for brevity) (cut for brevity)
* Error code 404: * Error code 404:
@ -1147,15 +936,14 @@ Get listener haproxy configuration
JSON Response: JSON Response:
{ {
'message': 'Listener Not Found', 'message': 'Loadbalancer Not Found',
'details': 'No listener with UUID: 04bff5c3-5862-4a13-b9e3-9b440d0ed50a', 'details': 'No loadbalancer with UUID: 04bff5c3-5862-4a13-b9e3-9b440d0ed50a',
} }
Plug VIP Plug VIP
-------- --------
* **URL:** /*:version*/plug/vip/*:ip* * **URL:** /1.0/plug/vip/*:ip*
* **Method:** Post * **Method:** Post
* **URL params:** * **URL params:**
@ -1210,7 +998,7 @@ Plug VIP
:: ::
POST URL: POST URL:
https://octavia-haproxy-img-00328.local/v0.5/plug/vip/203.0.113.2 https://octavia-haproxy-img-00328.local/1.0/plug/vip/203.0.113.2
JSON POST parameters: JSON POST parameters:
{ {
@ -1225,10 +1013,6 @@ Plug VIP
'details': 'VIP 203.0.113.2 plugged on interface eth1' 'details': 'VIP 203.0.113.2 plugged on interface eth1'
} }
* Error code 400: * Error code 400:
:: ::
@ -1251,7 +1035,7 @@ Plug VIP
Plug Network Plug Network
------------ ------------
* **URL:** /*:version*/plug/network/ * **URL:** /1.0/plug/network/
* **Method:** POST * **Method:** POST
* **URL params:** none * **URL params:** none
@ -1292,7 +1076,7 @@ Plug Network
:: ::
POST URL: POST URL:
https://octavia-haproxy-img-00328.local/v0.5/plug/network/ https://octavia-haproxy-img-00328.local/1.0/plug/network/
JSON POST parameters: JSON POST parameters:
{ {
@ -1319,7 +1103,7 @@ Plug Network
Upload SSL server certificate PEM file for Controller Communication Upload SSL server certificate PEM file for Controller Communication
------------------------------------------------------------------- -------------------------------------------------------------------
* **URL:** /*:version*/certificate * **URL:** /1.0/certificate
* **Method:** PUT * **Method:** PUT
* **Data params:** Certificate data. (PEM file should be a concatenation of * **Data params:** Certificate data. (PEM file should be a concatenation of
@ -1362,7 +1146,7 @@ not be available for some time.
:: ::
PUT URI: PUT URI:
https://octavia-haproxy-img-00328.local/v0.5/certificate https://octavia-haproxy-img-00328.local/1.0/certificate
(Put data should contain the certificate information, concatenated as (Put data should contain the certificate information, concatenated as
described above) described above)
@ -1376,7 +1160,7 @@ not be available for some time.
:: ::
PUT URI: PUT URI:
https://octavia-haproxy-img-00328.local/v0.5/certificates https://octavia-haproxy-img-00328.local/1.0/certificates
(If PUT data does not contain a certificate) (If PUT data does not contain a certificate)
JSON Response: JSON Response:
@ -1389,7 +1173,7 @@ not be available for some time.
:: ::
PUT URI: PUT URI:
https://octavia-haproxy-img-00328.local/v0.5/certificate https://octavia-haproxy-img-00328.local/1.0/certificate
(If PUT data does not contain an RSA key) (If PUT data does not contain an RSA key)
JSON Response: JSON Response:
@ -1402,7 +1186,7 @@ not be available for some time.
:: ::
PUT URI: PUT URI:
https://octavia-haproxy-img-00328.local/v0.5/certificate https://octavia-haproxy-img-00328.local/1.0/certificate
(If the first certificate and the RSA key do not have the same modulus.) (If the first certificate and the RSA key do not have the same modulus.)
JSON Response: JSON Response:
@ -1414,7 +1198,7 @@ not be available for some time.
Upload keepalived configuration Upload keepalived configuration
------------------------------- -------------------------------
* **URL:** /*:version*/vrrp/upload * **URL:** /1.0/vrrp/upload
* **Method:** PUT * **Method:** PUT
* **URL params:** none * **URL params:** none
* **Data params:** none * **Data params:** none
@ -1441,7 +1225,7 @@ OK
:: ::
PUT URI: PUT URI:
https://octavia-haproxy-img-00328.local/v0.5/vrrp/upload https://octavia-haproxy-img-00328.local/1.0/vrrp/upload
JSON Response: JSON Response:
{ {
@ -1452,7 +1236,7 @@ OK
Start, Stop, or Reload keepalived Start, Stop, or Reload keepalived
--------------------------------- ---------------------------------
* **URL:** /*:version*/vrrp/*:action* * **URL:** /1.0/vrrp/*:action*
* **Method:** PUT * **Method:** PUT
* **URL params:** * **URL params:**
@ -1489,7 +1273,7 @@ Start, Stop, or Reload keepalived
:: ::
PUT URL: PUT URL:
https://octavia-haproxy-img-00328.local/v0.5/vrrp/start https://octavia-haproxy-img-00328.local/1.0/vrrp/start
JSON Response: JSON Response:
{ {
@ -1502,7 +1286,7 @@ Start, Stop, or Reload keepalived
:: ::
PUT URL: PUT URL:
https://octavia-haproxy-img-00328.local/v0.5/vrrp/BAD_TEST_DATA https://octavia-haproxy-img-00328.local/1.0/vrrp/BAD_TEST_DATA
JSON Response: JSON Response:
{ {
@ -1515,7 +1299,7 @@ Start, Stop, or Reload keepalived
:: ::
PUT URL: PUT URL:
https://octavia-haproxy-img-00328.local/v0.5/vrrp/stop https://octavia-haproxy-img-00328.local/1.0/vrrp/stop
JSON Response: JSON Response:
{ {
@ -1526,7 +1310,7 @@ Start, Stop, or Reload keepalived
Update the amphora agent configuration Update the amphora agent configuration
-------------------------------------- --------------------------------------
* **URL:** /*:version*/config * **URL:** /1.0/config
* **Method:** PUT * **Method:** PUT
* **Data params:** A amphora-agent configuration file * **Data params:** A amphora-agent configuration file
@ -1561,7 +1345,7 @@ will be updated.
:: ::
PUT URL: PUT URL:
https://octavia-haproxy-img-00328.local/v0.5/config https://octavia-haproxy-img-00328.local/1.0/config
(Upload PUT data should be a raw amphora-agent.conf file.) (Upload PUT data should be a raw amphora-agent.conf file.)
JSON Response: JSON Response:

View File

@ -12,4 +12,4 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
VERSION = '0.5' VERSION = '1.0'

View File

@ -46,7 +46,7 @@ class AmphoraInfo(object):
return webob.Response(json=body) return webob.Response(json=body)
def compile_amphora_details(self, extend_udp_driver=None): def compile_amphora_details(self, extend_udp_driver=None):
haproxy_listener_list = util.get_listeners() haproxy_listener_list = sorted(util.get_listeners())
extend_body = {} extend_body = {}
udp_listener_list = [] udp_listener_list = []
if extend_udp_driver: if extend_udp_driver:
@ -87,8 +87,8 @@ class AmphoraInfo(object):
'load': self._load(), 'load': self._load(),
'topology': consts.TOPOLOGY_SINGLE, 'topology': consts.TOPOLOGY_SINGLE,
'topology_status': consts.TOPOLOGY_STATUS_OK, 'topology_status': consts.TOPOLOGY_STATUS_OK,
'listeners': list( 'listeners': sorted(list(
set(haproxy_listener_list + udp_listener_list)) set(haproxy_listener_list + udp_listener_list)))
if udp_listener_list else haproxy_listener_list, if udp_listener_list else haproxy_listener_list,
'packages': {}} 'packages': {}}
if extend_body: if extend_body:
@ -101,10 +101,10 @@ class AmphoraInfo(object):
version = subprocess.check_output(cmd.split()) version = subprocess.check_output(cmd.split())
return version return version
def _count_haproxy_processes(self, listener_list): def _count_haproxy_processes(self, lb_list):
num = 0 num = 0
for listener_id in listener_list: for lb_id in lb_list:
if util.is_listener_running(listener_id): if util.is_lb_running(lb_id):
# optional check if it's still running # optional check if it's still running
num += 1 num += 1
return num return num

View File

@ -22,7 +22,7 @@ from oslo_config import cfg
from oslo_log import log as logging from oslo_log import log as logging
import webob import webob
from octavia.amphorae.backends.agent.api_server import listener from octavia.amphorae.backends.agent.api_server import loadbalancer
from octavia.amphorae.backends.agent.api_server import util from octavia.amphorae.backends.agent.api_server import util
from octavia.common import constants as consts from octavia.common import constants as consts
@ -43,7 +43,7 @@ check_script_template = j2_env.get_template(consts.CHECK_SCRIPT_CONF)
class Keepalived(object): class Keepalived(object):
def upload_keepalived_config(self): def upload_keepalived_config(self):
stream = listener.Wrapped(flask.request.stream) stream = loadbalancer.Wrapped(flask.request.stream)
if not os.path.exists(util.keepalived_dir()): if not os.path.exists(util.keepalived_dir()):
os.makedirs(util.keepalived_dir()) os.makedirs(util.keepalived_dir())

View File

@ -25,10 +25,9 @@ from oslo_log import log as logging
import webob import webob
from werkzeug import exceptions from werkzeug import exceptions
from octavia.amphorae.backends.agent.api_server import listener from octavia.amphorae.backends.agent.api_server import loadbalancer
from octavia.amphorae.backends.agent.api_server import udp_listener_base from octavia.amphorae.backends.agent.api_server import udp_listener_base
from octavia.amphorae.backends.agent.api_server import util from octavia.amphorae.backends.agent.api_server import util
from octavia.amphorae.backends.utils import keepalivedlvs_query
from octavia.common import constants as consts from octavia.common import constants as consts
BUFFER = 100 BUFFER = 100
@ -51,7 +50,7 @@ class KeepalivedLvs(udp_listener_base.UdpListenerApiServerBase):
_SUBSCRIBED_AMP_COMPILE = ['keepalived', 'ipvsadm'] _SUBSCRIBED_AMP_COMPILE = ['keepalived', 'ipvsadm']
def upload_udp_listener_config(self, listener_id): def upload_udp_listener_config(self, listener_id):
stream = listener.Wrapped(flask.request.stream) stream = loadbalancer.Wrapped(flask.request.stream)
NEED_CHECK = True NEED_CHECK = True
if not os.path.exists(util.keepalived_lvs_dir()): if not os.path.exists(util.keepalived_lvs_dir()):
@ -256,45 +255,6 @@ class KeepalivedLvs(udp_listener_base.UdpListenerApiServerBase):
}) })
return listeners return listeners
def get_udp_listener_status(self, listener_id):
"""Gets the status of a UDP listener
This method will consult the stats socket
so calling this method will interfere with
the health daemon with the risk of the amphora
shut down
:param listener_id: The id of the listener
"""
self._check_udp_listener_exists(listener_id)
status = self._check_udp_listener_status(listener_id)
if status != consts.ACTIVE:
stats = dict(
status=status,
uuid=listener_id,
type='UDP'
)
return webob.Response(json=stats)
stats = dict(
status=status,
uuid=listener_id,
type='UDP'
)
try:
pool = keepalivedlvs_query.get_udp_listener_pool_status(
listener_id)
except subprocess.CalledProcessError as e:
return webob.Response(json=dict(
message="Error getting kernel lvs status for udp listener "
"{}".format(listener_id),
details=e.output), status=500)
stats['pools'] = [pool]
return webob.Response(json=stats)
def delete_udp_listener(self, listener_id): def delete_udp_listener(self, listener_id):
try: try:
self._check_udp_listener_exists(listener_id) self._check_udp_listener_exists(listener_id)

View File

@ -31,7 +31,6 @@ from werkzeug import exceptions
from octavia.amphorae.backends.agent.api_server import haproxy_compatibility from octavia.amphorae.backends.agent.api_server import haproxy_compatibility
from octavia.amphorae.backends.agent.api_server import osutils from octavia.amphorae.backends.agent.api_server import osutils
from octavia.amphorae.backends.agent.api_server import util from octavia.amphorae.backends.agent.api_server import util
from octavia.amphorae.backends.utils import haproxy_query as query
from octavia.common import constants as consts from octavia.common import constants as consts
from octavia.common import utils as octavia_utils from octavia.common import utils as octavia_utils
@ -54,10 +53,6 @@ SYSVINIT_TEMPLATE = JINJA_ENV.get_template(SYSVINIT_CONF)
SYSTEMD_TEMPLATE = JINJA_ENV.get_template(SYSTEMD_CONF) SYSTEMD_TEMPLATE = JINJA_ENV.get_template(SYSTEMD_CONF)
class ParsingError(Exception):
pass
# Wrap a stream so we can compute the md5 while reading # Wrap a stream so we can compute the md5 while reading
class Wrapped(object): class Wrapped(object):
def __init__(self, stream_): def __init__(self, stream_):
@ -77,37 +72,37 @@ class Wrapped(object):
return getattr(self.stream, attr) return getattr(self.stream, attr)
class Listener(object): class Loadbalancer(object):
def __init__(self): def __init__(self):
self._osutils = osutils.BaseOS.get_os_util() self._osutils = osutils.BaseOS.get_os_util()
def get_haproxy_config(self, listener_id): def get_haproxy_config(self, lb_id):
"""Gets the haproxy config """Gets the haproxy config
:param listener_id: the id of the listener :param listener_id: the id of the listener
""" """
self._check_listener_exists(listener_id) self._check_lb_exists(lb_id)
with open(util.config_path(listener_id), 'r') as file: with open(util.config_path(lb_id), 'r') as file:
cfg = file.read() cfg = file.read()
resp = webob.Response(cfg, content_type='text/plain') resp = webob.Response(cfg, content_type='text/plain')
resp.headers['ETag'] = hashlib.md5(six.b(cfg)).hexdigest() # nosec resp.headers['ETag'] = hashlib.md5(six.b(cfg)).hexdigest() # nosec
return resp return resp
def upload_haproxy_config(self, amphora_id, listener_id): def upload_haproxy_config(self, amphora_id, lb_id):
"""Upload the haproxy config """Upload the haproxy config
:param amphora_id: The id of the amphora to update :param amphora_id: The id of the amphora to update
:param listener_id: The id of the listener :param lb_id: The id of the loadbalancer
""" """
stream = Wrapped(flask.request.stream) stream = Wrapped(flask.request.stream)
# We have to hash here because HAProxy has a string length limitation # We have to hash here because HAProxy has a string length limitation
# in the configuration file "peer <peername>" lines # in the configuration file "peer <peername>" lines
peer_name = octavia_utils.base64_sha1_string(amphora_id).rstrip('=') peer_name = octavia_utils.base64_sha1_string(amphora_id).rstrip('=')
if not os.path.exists(util.haproxy_dir(listener_id)): if not os.path.exists(util.haproxy_dir(lb_id)):
os.makedirs(util.haproxy_dir(listener_id)) os.makedirs(util.haproxy_dir(lb_id))
name = os.path.join(util.haproxy_dir(listener_id), 'haproxy.cfg.new') name = os.path.join(util.haproxy_dir(lb_id), 'haproxy.cfg.new')
flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
# mode 00600 # mode 00600
mode = stat.S_IRUSR | stat.S_IWUSR mode = stat.S_IRUSR | stat.S_IWUSR
@ -148,7 +143,7 @@ class Listener(object):
status=400) status=400)
# file ok - move it # file ok - move it
os.rename(name, util.config_path(listener_id)) os.rename(name, util.config_path(lb_id))
try: try:
@ -156,7 +151,7 @@ class Listener(object):
LOG.debug('Found init system: %s', init_system) LOG.debug('Found init system: %s', init_system)
init_path = util.init_path(listener_id, init_system) init_path = util.init_path(lb_id, init_system)
if init_system == consts.INIT_SYSTEMD: if init_system == consts.INIT_SYSTEMD:
template = SYSTEMD_TEMPLATE template = SYSTEMD_TEMPLATE
@ -194,9 +189,9 @@ class Listener(object):
text = template.render( text = template.render(
peer_name=peer_name, peer_name=peer_name,
haproxy_pid=util.pid_path(listener_id), haproxy_pid=util.pid_path(lb_id),
haproxy_cmd=util.CONF.haproxy_amphora.haproxy_cmd, haproxy_cmd=util.CONF.haproxy_amphora.haproxy_cmd,
haproxy_cfg=util.config_path(listener_id), haproxy_cfg=util.config_path(lb_id),
haproxy_user_group_cfg=consts.HAPROXY_USER_GROUP_CFG, haproxy_user_group_cfg=consts.HAPROXY_USER_GROUP_CFG,
respawn_count=util.CONF.haproxy_amphora.respawn_count, respawn_count=util.CONF.haproxy_amphora.respawn_count,
respawn_interval=(util.CONF.haproxy_amphora. respawn_interval=(util.CONF.haproxy_amphora.
@ -212,25 +207,25 @@ class Listener(object):
# Make sure the new service is enabled on boot # Make sure the new service is enabled on boot
if init_system == consts.INIT_SYSTEMD: if init_system == consts.INIT_SYSTEMD:
util.run_systemctl_command( util.run_systemctl_command(
consts.ENABLE, "haproxy-{list}".format(list=listener_id)) consts.ENABLE, "haproxy-{lb_id}".format(lb_id=lb_id))
elif init_system == consts.INIT_SYSVINIT: elif init_system == consts.INIT_SYSVINIT:
try: try:
subprocess.check_output(init_enable_cmd.split(), subprocess.check_output(init_enable_cmd.split(),
stderr=subprocess.STDOUT) stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
LOG.error("Failed to enable haproxy-%(list)s service: " LOG.error("Failed to enable haproxy-%(lb_id)s service: "
"%(err)s %(out)s", {'list': listener_id, 'err': e, "%(err)s %(out)s", {'lb_id': lb_id, 'err': e,
'out': e.output}) 'out': e.output})
return webob.Response(json=dict( return webob.Response(json=dict(
message="Error enabling haproxy-{0} service".format( message="Error enabling haproxy-{0} service".format(
listener_id), details=e.output), status=500) lb_id), details=e.output), status=500)
res = webob.Response(json={'message': 'OK'}, status=202) res = webob.Response(json={'message': 'OK'}, status=202)
res.headers['ETag'] = stream.get_md5() res.headers['ETag'] = stream.get_md5()
return res return res
def start_stop_listener(self, listener_id, action): def start_stop_lb(self, lb_id, action):
action = action.lower() action = action.lower()
if action not in [consts.AMP_ACTION_START, if action not in [consts.AMP_ACTION_START,
consts.AMP_ACTION_STOP, consts.AMP_ACTION_STOP,
@ -239,30 +234,30 @@ class Listener(object):
message='Invalid Request', message='Invalid Request',
details="Unknown action: {0}".format(action)), status=400) details="Unknown action: {0}".format(action)), status=400)
self._check_listener_exists(listener_id) self._check_lb_exists(lb_id)
# Since this script should be created at LB create time # Since this script should be created at LB create time
# we can check for this path to see if VRRP is enabled # we can check for this path to see if VRRP is enabled
# on this amphora and not write the file if VRRP is not in use # on this amphora and not write the file if VRRP is not in use
if os.path.exists(util.keepalived_check_script_path()): if os.path.exists(util.keepalived_check_script_path()):
self.vrrp_check_script_update(listener_id, action) self.vrrp_check_script_update(lb_id, action)
# HAProxy does not start the process when given a reload # HAProxy does not start the process when given a reload
# so start it if haproxy is not already running # so start it if haproxy is not already running
if action == consts.AMP_ACTION_RELOAD: if action == consts.AMP_ACTION_RELOAD:
if consts.OFFLINE == self._check_haproxy_status(listener_id): if consts.OFFLINE == self._check_haproxy_status(lb_id):
action = consts.AMP_ACTION_START action = consts.AMP_ACTION_START
cmd = ("/usr/sbin/service haproxy-{listener_id} {action}".format( cmd = ("/usr/sbin/service haproxy-{lb_id} {action}".format(
listener_id=listener_id, action=action)) lb_id=lb_id, action=action))
try: try:
subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT) subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
if b'Job is already running' not in e.output: if b'Job is already running' not in e.output:
LOG.debug( LOG.debug(
"Failed to %(action)s haproxy-%(list)s service: %(err)s " "Failed to %(action)s haproxy-%(lb_id)s service: %(err)s "
"%(out)s", {'action': action, 'list': listener_id, "%(out)s", {'action': action, 'lb_id': lb_id,
'err': e, 'out': e.output}) 'err': e, 'out': e.output})
return webob.Response(json=dict( return webob.Response(json=dict(
message="Error {0}ing haproxy".format(action), message="Error {0}ing haproxy".format(action),
@ -271,40 +266,40 @@ class Listener(object):
consts.AMP_ACTION_RELOAD]: consts.AMP_ACTION_RELOAD]:
return webob.Response(json=dict( return webob.Response(json=dict(
message='OK', message='OK',
details='Listener {listener_id} {action}ed'.format( details='Listener {lb_id} {action}ed'.format(
listener_id=listener_id, action=action)), status=202) lb_id=lb_id, action=action)), status=202)
details = ( details = (
'Configuration file is valid\n' 'Configuration file is valid\n'
'haproxy daemon for {0} started'.format(listener_id) 'haproxy daemon for {0} started'.format(lb_id)
) )
return webob.Response(json=dict(message='OK', details=details), return webob.Response(json=dict(message='OK', details=details),
status=202) status=202)
def delete_listener(self, listener_id): def delete_lb(self, lb_id):
try: try:
self._check_listener_exists(listener_id) self._check_lb_exists(lb_id)
except exceptions.HTTPException: except exceptions.HTTPException:
return webob.Response(json={'message': 'OK'}) return webob.Response(json={'message': 'OK'})
# check if that haproxy is still running and if stop it # check if that haproxy is still running and if stop it
if os.path.exists(util.pid_path(listener_id)) and os.path.exists( if os.path.exists(util.pid_path(lb_id)) and os.path.exists(
os.path.join('/proc', util.get_haproxy_pid(listener_id))): os.path.join('/proc', util.get_haproxy_pid(lb_id))):
cmd = "/usr/sbin/service haproxy-{0} stop".format(listener_id) cmd = "/usr/sbin/service haproxy-{0} stop".format(lb_id)
try: try:
subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT) subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
LOG.error("Failed to stop haproxy-%s service: %s %s", LOG.error("Failed to stop haproxy-%s service: %s %s",
listener_id, e, e.output) lb_id, e, e.output)
return webob.Response(json=dict( return webob.Response(json=dict(
message="Error stopping haproxy", message="Error stopping haproxy",
details=e.output), status=500) details=e.output), status=500)
# parse config and delete stats socket # parse config and delete stats socket
try: try:
cfg = self._parse_haproxy_file(listener_id) stats_socket = util.parse_haproxy_file(lb_id)[0]
os.remove(cfg['stats_socket']) os.remove(stats_socket)
except Exception: except Exception:
pass pass
@ -313,22 +308,22 @@ class Listener(object):
# on this amphora and not write the file if VRRP is not in use # on this amphora and not write the file if VRRP is not in use
if os.path.exists(util.keepalived_check_script_path()): if os.path.exists(util.keepalived_check_script_path()):
self.vrrp_check_script_update( self.vrrp_check_script_update(
listener_id, action=consts.AMP_ACTION_STOP) lb_id, action=consts.AMP_ACTION_STOP)
# delete the ssl files # delete the ssl files
try: try:
shutil.rmtree(self._cert_dir(listener_id)) shutil.rmtree(self._cert_dir(lb_id))
except Exception: except Exception:
pass pass
# disable the service # disable the service
init_system = util.get_os_init_system() init_system = util.get_os_init_system()
init_path = util.init_path(listener_id, init_system) init_path = util.init_path(lb_id, init_system)
if init_system == consts.INIT_SYSTEMD: if init_system == consts.INIT_SYSTEMD:
util.run_systemctl_command( util.run_systemctl_command(
consts.DISABLE, "haproxy-{list}".format( consts.DISABLE, "haproxy-{lb_id}".format(
list=listener_id)) lb_id=lb_id))
elif init_system == consts.INIT_SYSVINIT: elif init_system == consts.INIT_SYSVINIT:
init_disable_cmd = "insserv -r {file}".format(file=init_path) init_disable_cmd = "insserv -r {file}".format(file=init_path)
elif init_system != consts.INIT_UPSTART: elif init_system != consts.INIT_UPSTART:
@ -339,15 +334,15 @@ class Listener(object):
subprocess.check_output(init_disable_cmd.split(), subprocess.check_output(init_disable_cmd.split(),
stderr=subprocess.STDOUT) stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
LOG.error("Failed to disable haproxy-%(list)s service: " LOG.error("Failed to disable haproxy-%(lb_id)s service: "
"%(err)s %(out)s", {'list': listener_id, 'err': e, "%(err)s %(out)s", {'lb_id': lb_id, 'err': e,
'out': e.output}) 'out': e.output})
return webob.Response(json=dict( return webob.Response(json=dict(
message="Error disabling haproxy-{0} service".format( message="Error disabling haproxy-{0} service".format(
listener_id), details=e.output), status=500) lb_id), details=e.output), status=500)
# delete the directory + init script for that listener # delete the directory + init script for that listener
shutil.rmtree(util.haproxy_dir(listener_id)) shutil.rmtree(util.haproxy_dir(lb_id))
if os.path.exists(init_path): if os.path.exists(init_path):
os.remove(init_path) os.remove(init_path)
@ -364,68 +359,29 @@ class Listener(object):
""" """
listeners = list() listeners = list()
for listener in util.get_listeners(): for lb in util.get_loadbalancers():
status = self._check_listener_status(listener) stats_socket, listeners_on_lb = util.parse_haproxy_file(lb)
listener_type = ''
if status == consts.ACTIVE: for listener_id, listener in listeners_on_lb.items():
listener_type = self._parse_haproxy_file(listener)['mode'] listeners.append({
'status': consts.ACTIVE,
listeners.append({ 'uuid': listener_id,
'status': status, 'type': listener['mode'],
'uuid': listener, })
'type': listener_type,
})
if other_listeners: if other_listeners:
listeners = listeners + other_listeners listeners = listeners + other_listeners
return webob.Response(json=listeners, content_type='application/json') return webob.Response(json=listeners, content_type='application/json')
def get_listener_status(self, listener_id): def upload_certificate(self, lb_id, filename):
"""Gets the status of a listener
This method will consult the stats socket
so calling this method will interfere with
the health daemon with the risk of the amphora
shut down
Currently type==SSL is not detected
:param listener_id: The id of the listener
"""
self._check_listener_exists(listener_id)
status = self._check_listener_status(listener_id)
if status != consts.ACTIVE:
stats = dict(
status=status,
uuid=listener_id,
type=''
)
return webob.Response(json=stats)
cfg = self._parse_haproxy_file(listener_id)
stats = dict(
status=status,
uuid=listener_id,
type=cfg['mode']
)
# read stats socket
q = query.HAProxyQuery(cfg['stats_socket'])
servers = q.get_pool_status()
stats['pools'] = list(servers.values())
return webob.Response(json=stats)
def upload_certificate(self, listener_id, filename):
self._check_ssl_filename_format(filename) self._check_ssl_filename_format(filename)
# create directory if not already there # create directory if not already there
if not os.path.exists(self._cert_dir(listener_id)): if not os.path.exists(self._cert_dir(lb_id)):
os.makedirs(self._cert_dir(listener_id)) os.makedirs(self._cert_dir(lb_id))
stream = Wrapped(flask.request.stream) stream = Wrapped(flask.request.stream)
file = self._cert_file_path(listener_id, filename) file = self._cert_file_path(lb_id, filename)
flags = os.O_WRONLY | os.O_CREAT flags = os.O_WRONLY | os.O_CREAT
# mode 00600 # mode 00600
mode = stat.S_IRUSR | stat.S_IWUSR mode = stat.S_IRUSR | stat.S_IWUSR
@ -439,10 +395,10 @@ class Listener(object):
resp.headers['ETag'] = stream.get_md5() resp.headers['ETag'] = stream.get_md5()
return resp return resp
def get_certificate_md5(self, listener_id, filename): def get_certificate_md5(self, lb_id, filename):
self._check_ssl_filename_format(filename) self._check_ssl_filename_format(filename)
cert_path = self._cert_file_path(listener_id, filename) cert_path = self._cert_file_path(lb_id, filename)
path_exists = os.path.exists(cert_path) path_exists = os.path.exists(cert_path)
if not path_exists: if not path_exists:
return webob.Response(json=dict( return webob.Response(json=dict(
@ -457,60 +413,34 @@ class Listener(object):
resp.headers['ETag'] = md5 resp.headers['ETag'] = md5
return resp return resp
def delete_certificate(self, listener_id, filename): def delete_certificate(self, lb_id, filename):
self._check_ssl_filename_format(filename) self._check_ssl_filename_format(filename)
if os.path.exists(self._cert_file_path(listener_id, filename)): if os.path.exists(self._cert_file_path(lb_id, filename)):
os.remove(self._cert_file_path(listener_id, filename)) os.remove(self._cert_file_path(lb_id, filename))
return webob.Response(json=dict(message='OK')) return webob.Response(json=dict(message='OK'))
def _check_listener_status(self, listener_id): def _get_listeners_on_lb(self, lb_id):
if os.path.exists(util.pid_path(listener_id)): if os.path.exists(util.pid_path(lb_id)):
if os.path.exists( if os.path.exists(
os.path.join('/proc', util.get_haproxy_pid(listener_id))): os.path.join('/proc', util.get_haproxy_pid(lb_id))):
# Check if the listener is disabled # Check if the listener is disabled
with open(util.config_path(listener_id), 'r') as file: with open(util.config_path(lb_id), 'r') as file:
cfg = file.read() cfg = file.read()
m = re.search('frontend {}'.format(listener_id), cfg) m = re.findall('^frontend (.*)$', cfg, re.MULTILINE)
if m: return m or []
return consts.ACTIVE
return consts.OFFLINE
else: # pid file but no process... else: # pid file but no process...
return consts.ERROR return []
else: else:
return consts.OFFLINE return []
def _parse_haproxy_file(self, listener_id): def _check_lb_exists(self, lb_id):
with open(util.config_path(listener_id), 'r') as file: # check if we know about that lb
cfg = file.read() if lb_id not in util.get_loadbalancers():
m = re.search(r'mode\s+(http|tcp)', cfg)
if not m:
raise ParsingError()
mode = m.group(1).upper()
m = re.search(r'stats socket\s+(\S+)', cfg)
if not m:
raise ParsingError()
stats_socket = m.group(1)
m = re.search(r'ssl crt\s+(\S+)', cfg)
ssl_crt = None
if m:
ssl_crt = m.group(1)
mode = 'TERMINATED_HTTPS'
return dict(mode=mode,
stats_socket=stats_socket,
ssl_crt=ssl_crt)
def _check_listener_exists(self, listener_id):
# check if we know about that listener
if not os.path.exists(util.config_path(listener_id)):
raise exceptions.HTTPException( raise exceptions.HTTPException(
response=webob.Response(json=dict( response=webob.Response(json=dict(
message='Listener Not Found', message='Loadbalancer Not Found',
details="No listener with UUID: {0}".format( details="No loadbalancer with UUID: {0}".format(
listener_id)), status=404)) lb_id)), status=404))
def _check_ssl_filename_format(self, filename): def _check_ssl_filename_format(self, filename):
# check if the format is (xxx.)*xxx.pem # check if the format is (xxx.)*xxx.pem
@ -519,20 +449,19 @@ class Listener(object):
response=webob.Response(json=dict( response=webob.Response(json=dict(
message='Filename has wrong format'), status=400)) message='Filename has wrong format'), status=400))
def _cert_dir(self, listener_id): def _cert_dir(self, lb_id):
return os.path.join(util.CONF.haproxy_amphora.base_cert_dir, return os.path.join(util.CONF.haproxy_amphora.base_cert_dir, lb_id)
listener_id)
def _cert_file_path(self, listener_id, filename): def _cert_file_path(self, lb_id, filename):
return os.path.join(self._cert_dir(listener_id), filename) return os.path.join(self._cert_dir(lb_id), filename)
def vrrp_check_script_update(self, listener_id, action): def vrrp_check_script_update(self, lb_id, action):
listener_ids = util.get_listeners() lb_ids = util.get_loadbalancers()
if action == consts.AMP_ACTION_STOP: if action == consts.AMP_ACTION_STOP:
listener_ids.remove(listener_id) lb_ids.remove(lb_id)
args = [] args = []
for lid in listener_ids: for lbid in lb_ids:
args.append(util.haproxy_sock_path(lid)) args.append(util.haproxy_sock_path(lbid))
if not os.path.exists(util.keepalived_dir()): if not os.path.exists(util.keepalived_dir()):
os.makedirs(util.keepalived_dir()) os.makedirs(util.keepalived_dir())
@ -542,9 +471,9 @@ class Listener(object):
with open(util.haproxy_check_script_path(), 'w') as text_file: with open(util.haproxy_check_script_path(), 'w') as text_file:
text_file.write(cmd) text_file.write(cmd)
def _check_haproxy_status(self, listener_id): def _check_haproxy_status(self, lb_id):
if os.path.exists(util.pid_path(listener_id)): if os.path.exists(util.pid_path(lb_id)):
if os.path.exists( if os.path.exists(
os.path.join('/proc', util.get_haproxy_pid(listener_id))): os.path.join('/proc', util.get_haproxy_pid(lb_id))):
return consts.ACTIVE return consts.ACTIVE
return consts.OFFLINE return consts.OFFLINE

View File

@ -26,7 +26,7 @@ from octavia.amphorae.backends.agent import api_server
from octavia.amphorae.backends.agent.api_server import amphora_info from octavia.amphorae.backends.agent.api_server import amphora_info
from octavia.amphorae.backends.agent.api_server import certificate_update from octavia.amphorae.backends.agent.api_server import certificate_update
from octavia.amphorae.backends.agent.api_server import keepalived from octavia.amphorae.backends.agent.api_server import keepalived
from octavia.amphorae.backends.agent.api_server import listener from octavia.amphorae.backends.agent.api_server import loadbalancer
from octavia.amphorae.backends.agent.api_server import osutils from octavia.amphorae.backends.agent.api_server import osutils
from octavia.amphorae.backends.agent.api_server import plug from octavia.amphorae.backends.agent.api_server import plug
from octavia.amphorae.backends.agent.api_server import udp_listener_base from octavia.amphorae.backends.agent.api_server import udp_listener_base
@ -56,7 +56,7 @@ class Server(object):
self.app = flask.Flask(__name__) self.app = flask.Flask(__name__)
self._osutils = osutils.BaseOS.get_os_util() self._osutils = osutils.BaseOS.get_os_util()
self._keepalived = keepalived.Keepalived() self._keepalived = keepalived.Keepalived()
self._listener = listener.Listener() self._loadbalancer = loadbalancer.Loadbalancer()
self._udp_listener = (udp_listener_base.UdpListenerApiServerBase. self._udp_listener = (udp_listener_base.UdpListenerApiServerBase.
get_server_driver()) get_server_driver())
self._plug = plug.Plug(self._osutils) self._plug = plug.Plug(self._osutils)
@ -64,8 +64,10 @@ class Server(object):
register_app_error_handler(self.app) register_app_error_handler(self.app)
self.app.add_url_rule(rule='/', view_func=self.version_discovery,
methods=['GET'])
self.app.add_url_rule(rule=PATH_PREFIX + self.app.add_url_rule(rule=PATH_PREFIX +
'/listeners/<amphora_id>/<listener_id>/haproxy', '/loadbalancer/<amphora_id>/<lb_id>/haproxy',
view_func=self.upload_haproxy_config, view_func=self.upload_haproxy_config,
methods=['PUT']) methods=['PUT'])
self.app.add_url_rule(rule=PATH_PREFIX + self.app.add_url_rule(rule=PATH_PREFIX +
@ -74,7 +76,7 @@ class Server(object):
view_func=self.upload_udp_listener_config, view_func=self.upload_udp_listener_config,
methods=['PUT']) methods=['PUT'])
self.app.add_url_rule(rule=PATH_PREFIX + self.app.add_url_rule(rule=PATH_PREFIX +
'/listeners/<listener_id>/haproxy', '/loadbalancer/<lb_id>/haproxy',
view_func=self.get_haproxy_config, view_func=self.get_haproxy_config,
methods=['GET']) methods=['GET'])
self.app.add_url_rule(rule=PATH_PREFIX + self.app.add_url_rule(rule=PATH_PREFIX +
@ -82,11 +84,11 @@ class Server(object):
view_func=self.get_udp_listener_config, view_func=self.get_udp_listener_config,
methods=['GET']) methods=['GET'])
self.app.add_url_rule(rule=PATH_PREFIX + self.app.add_url_rule(rule=PATH_PREFIX +
'/listeners/<listener_id>/<action>', '/loadbalancer/<object_id>/<action>',
view_func=self.start_stop_listener, view_func=self.start_stop_lb_object,
methods=['PUT']) methods=['PUT'])
self.app.add_url_rule(rule=PATH_PREFIX + '/listeners/<listener_id>', self.app.add_url_rule(rule=PATH_PREFIX + '/listeners/<object_id>',
view_func=self.delete_listener, view_func=self.delete_lb_object,
methods=['DELETE']) methods=['DELETE'])
self.app.add_url_rule(rule=PATH_PREFIX + '/config', self.app.add_url_rule(rule=PATH_PREFIX + '/config',
view_func=self.upload_config, view_func=self.upload_config,
@ -100,18 +102,15 @@ class Server(object):
self.app.add_url_rule(rule=PATH_PREFIX + '/listeners', self.app.add_url_rule(rule=PATH_PREFIX + '/listeners',
view_func=self.get_all_listeners_status, view_func=self.get_all_listeners_status,
methods=['GET']) methods=['GET'])
self.app.add_url_rule(rule=PATH_PREFIX + '/listeners/<listener_id>', self.app.add_url_rule(rule=PATH_PREFIX + '/loadbalancer/<lb_id>'
view_func=self.get_listener_status,
methods=['GET'])
self.app.add_url_rule(rule=PATH_PREFIX + '/listeners/<listener_id>'
'/certificates/<filename>', '/certificates/<filename>',
view_func=self.upload_certificate, view_func=self.upload_certificate,
methods=['PUT']) methods=['PUT'])
self.app.add_url_rule(rule=PATH_PREFIX + '/listeners/<listener_id>' self.app.add_url_rule(rule=PATH_PREFIX + '/loadbalancer/<lb_id>'
'/certificates/<filename>', '/certificates/<filename>',
view_func=self.get_certificate_md5, view_func=self.get_certificate_md5,
methods=['GET']) methods=['GET'])
self.app.add_url_rule(rule=PATH_PREFIX + '/listeners/<listener_id>' self.app.add_url_rule(rule=PATH_PREFIX + '/loadbalancer/<lb_id>'
'/certificates/<filename>', '/certificates/<filename>',
view_func=self.delete_certificate, view_func=self.delete_certificate,
methods=['DELETE']) methods=['DELETE'])
@ -133,30 +132,30 @@ class Server(object):
view_func=self.get_interface, view_func=self.get_interface,
methods=['GET']) methods=['GET'])
def upload_haproxy_config(self, amphora_id, listener_id): def upload_haproxy_config(self, amphora_id, lb_id):
return self._listener.upload_haproxy_config(amphora_id, listener_id) return self._loadbalancer.upload_haproxy_config(amphora_id, lb_id)
def upload_udp_listener_config(self, amphora_id, listener_id): def upload_udp_listener_config(self, amphora_id, listener_id):
return self._udp_listener.upload_udp_listener_config(listener_id) return self._udp_listener.upload_udp_listener_config(listener_id)
def get_haproxy_config(self, listener_id): def get_haproxy_config(self, lb_id):
return self._listener.get_haproxy_config(listener_id) return self._loadbalancer.get_haproxy_config(lb_id)
def get_udp_listener_config(self, listener_id): def get_udp_listener_config(self, listener_id):
return self._udp_listener.get_udp_listener_config(listener_id) return self._udp_listener.get_udp_listener_config(listener_id)
def start_stop_listener(self, listener_id, action): def start_stop_lb_object(self, object_id, action):
protocol = util.get_listener_protocol(listener_id) protocol = util.get_protocol_for_lb_object(object_id)
if protocol == 'UDP': if protocol == 'UDP':
return self._udp_listener.manage_udp_listener( return self._udp_listener.manage_udp_listener(
listener_id, action) listener_id=object_id, action=action)
return self._listener.start_stop_listener(listener_id, action) return self._loadbalancer.start_stop_lb(lb_id=object_id, action=action)
def delete_listener(self, listener_id): def delete_lb_object(self, object_id):
protocol = util.get_listener_protocol(listener_id) protocol = util.get_protocol_for_lb_object(object_id)
if protocol == 'UDP': if protocol == 'UDP':
return self._udp_listener.delete_udp_listener(listener_id) return self._udp_listener.delete_udp_listener(object_id)
return self._listener.delete_listener(listener_id) return self._loadbalancer.delete_lb(object_id)
def get_details(self): def get_details(self):
return self._amphora_info.compile_amphora_details( return self._amphora_info.compile_amphora_details(
@ -168,23 +167,17 @@ class Server(object):
def get_all_listeners_status(self): def get_all_listeners_status(self):
udp_listeners = self._udp_listener.get_all_udp_listeners_status() udp_listeners = self._udp_listener.get_all_udp_listeners_status()
return self._listener.get_all_listeners_status( return self._loadbalancer.get_all_listeners_status(
other_listeners=udp_listeners) other_listeners=udp_listeners)
def get_listener_status(self, listener_id): def upload_certificate(self, lb_id, filename):
protocol = util.get_listener_protocol(listener_id) return self._loadbalancer.upload_certificate(lb_id, filename)
if protocol == 'UDP':
return self._udp_listener.get_udp_listener_status(listener_id)
return self._listener.get_listener_status(listener_id)
def upload_certificate(self, listener_id, filename): def get_certificate_md5(self, lb_id, filename):
return self._listener.upload_certificate(listener_id, filename) return self._loadbalancer.get_certificate_md5(lb_id, filename)
def get_certificate_md5(self, listener_id, filename): def delete_certificate(self, lb_id, filename):
return self._listener.get_certificate_md5(listener_id, filename) return self._loadbalancer.delete_certificate(lb_id, filename)
def delete_certificate(self, listener_id, filename):
return self._listener.delete_certificate(listener_id, filename)
def plug_vip(self, vip): def plug_vip(self, vip):
# Catch any issues with the subnet info json # Catch any issues with the subnet info json
@ -251,3 +244,6 @@ class Server(object):
details=str(e)), status=500) details=str(e)), status=500)
return webob.Response(json={'message': 'OK'}, status=202) return webob.Response(json={'message': 'OK'}, status=202)
def version_discovery(self):
return webob.Response(json={'api_version': api_server.VERSION})

View File

@ -94,17 +94,6 @@ class UdpListenerApiServerBase(object):
""" """
@abc.abstractmethod
def get_udp_listener_status(self, listener_id):
"""Gets the status of a UDP listener
:param listener_id: The id of the listener
:returns: HTTP response with status code.
:raises Exception: If the listener is failed to find.
"""
@abc.abstractmethod @abc.abstractmethod
def delete_udp_listener(self, listener_id): def delete_udp_listener(self, listener_id):
"""Delete a UDP Listener from a amphora """Delete a UDP Listener from a amphora

View File

@ -28,21 +28,31 @@ from octavia.common import constants as consts
CONF = cfg.CONF CONF = cfg.CONF
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
FRONTEND_BACKEND_PATTERN = re.compile(r'\n(frontend|backend)\s+(\S+)\n')
LISTENER_MODE_PATTERN = re.compile(r'^\s+mode\s+(.*)$', re.MULTILINE)
TLS_CERT_PATTERN = re.compile(r'^\s+bind\s+\S+\s+ssl crt\s+(.*)$',
re.MULTILINE)
STATS_SOCKET_PATTERN = re.compile(r'stats socket\s+(\S+)')
class ParsingError(Exception):
pass
class UnknownInitError(Exception): class UnknownInitError(Exception):
pass pass
def init_path(listener_id, init_system): def init_path(lb_id, init_system):
if init_system == consts.INIT_SYSTEMD: if init_system == consts.INIT_SYSTEMD:
return os.path.join(consts.SYSTEMD_DIR, return os.path.join(consts.SYSTEMD_DIR,
'haproxy-{0}.service'.format(listener_id)) 'haproxy-{0}.service'.format(lb_id))
if init_system == consts.INIT_UPSTART: if init_system == consts.INIT_UPSTART:
return os.path.join(consts.UPSTART_DIR, return os.path.join(consts.UPSTART_DIR,
'haproxy-{0}.conf'.format(listener_id)) 'haproxy-{0}.conf'.format(lb_id))
if init_system == consts.INIT_SYSVINIT: if init_system == consts.INIT_SYSVINIT:
return os.path.join(consts.SYSVINIT_DIR, return os.path.join(consts.SYSVINIT_DIR,
'haproxy-{0}'.format(listener_id)) 'haproxy-{0}'.format(lb_id))
raise UnknownInitError() raise UnknownInitError()
@ -91,20 +101,20 @@ def keepalived_lvs_cfg_path(listener_id):
str(listener_id)) str(listener_id))
def haproxy_dir(listener_id): def haproxy_dir(lb_id):
return os.path.join(CONF.haproxy_amphora.base_path, listener_id) return os.path.join(CONF.haproxy_amphora.base_path, lb_id)
def pid_path(listener_id): def pid_path(lb_id):
return os.path.join(haproxy_dir(listener_id), listener_id + '.pid') return os.path.join(haproxy_dir(lb_id), lb_id + '.pid')
def config_path(listener_id): def config_path(lb_id):
return os.path.join(haproxy_dir(listener_id), 'haproxy.cfg') return os.path.join(haproxy_dir(lb_id), 'haproxy.cfg')
def get_haproxy_pid(listener_id): def get_haproxy_pid(lb_id):
with open(pid_path(listener_id), 'r') as f: with open(pid_path(lb_id), 'r') as f:
return f.readline().rstrip() return f.readline().rstrip()
@ -114,8 +124,8 @@ def get_keepalivedlvs_pid(listener_id):
return f.readline().rstrip() return f.readline().rstrip()
def haproxy_sock_path(listener_id): def haproxy_sock_path(lb_id):
return os.path.join(CONF.haproxy_amphora.base_path, listener_id + '.sock') return os.path.join(CONF.haproxy_amphora.base_path, lb_id + '.sock')
def haproxy_check_script_path(): def haproxy_check_script_path():
@ -168,16 +178,28 @@ def get_listeners():
:returns: An array with the ids of all listeners, e.g. ['123', '456', ...] :returns: An array with the ids of all listeners, e.g. ['123', '456', ...]
or [] if no listeners exist or [] if no listeners exist
""" """
listeners = []
for lb_id in get_loadbalancers():
listeners_on_lb = parse_haproxy_file(lb_id)[1]
listeners.extend(list(listeners_on_lb.keys()))
return listeners
def get_loadbalancers():
"""Get Load balancers
:returns: An array with the ids of all load balancers,
e.g. ['123', '456', ...] or [] if no loadbalancers exist
"""
if os.path.exists(CONF.haproxy_amphora.base_path): if os.path.exists(CONF.haproxy_amphora.base_path):
return [f for f in os.listdir(CONF.haproxy_amphora.base_path) return [f for f in os.listdir(CONF.haproxy_amphora.base_path)
if os.path.exists(config_path(f))] if os.path.exists(config_path(f))]
return [] return []
def is_listener_running(listener_id): def is_lb_running(lb_id):
return os.path.exists(pid_path(listener_id)) and os.path.exists( return os.path.exists(pid_path(lb_id)) and os.path.exists(
os.path.join('/proc', get_haproxy_pid(listener_id))) os.path.join('/proc', get_haproxy_pid(lb_id)))
def get_udp_listeners(): def get_udp_listeners():
@ -251,7 +273,7 @@ def run_systemctl_command(command, service):
'err': e, 'out': e.output}) 'err': e, 'out': e.output})
def get_listener_protocol(listener_id): def get_protocol_for_lb_object(object_id):
"""Returns the L4 protocol for a listener. """Returns the L4 protocol for a listener.
If the listener is a TCP based listener (haproxy) return TCP. If the listener is a TCP based listener (haproxy) return TCP.
@ -261,8 +283,52 @@ def get_listener_protocol(listener_id):
:param listener_id: The ID of the listener to identify. :param listener_id: The ID of the listener to identify.
:returns: TCP, UDP, or None :returns: TCP, UDP, or None
""" """
if os.path.exists(config_path(listener_id)): if os.path.exists(config_path(object_id)):
return consts.PROTOCOL_TCP return consts.PROTOCOL_TCP
if os.path.exists(keepalived_lvs_cfg_path(listener_id)): if os.path.exists(keepalived_lvs_cfg_path(object_id)):
return consts.PROTOCOL_UDP return consts.PROTOCOL_UDP
return None return None
def parse_haproxy_file(lb_id):
with open(config_path(lb_id), 'r') as file:
cfg = file.read()
listeners = {}
m = FRONTEND_BACKEND_PATTERN.split(cfg)
last_token = None
last_id = None
for section in m:
if last_token is None:
# We aren't in a section yet, see if this line starts one
if section == 'frontend':
last_token = section
elif last_token == 'frontend':
# We're in a frontend section, save the id for later
last_token = last_token + "_id"
last_id = section
elif last_token == 'frontend_id':
# We're in a frontend section and already have the id
# Look for the mode
mode_matcher = LISTENER_MODE_PATTERN.search(section)
if not mode_matcher:
raise ParsingError()
listeners[last_id] = {
'mode': mode_matcher.group(1).upper(),
}
# Now see if this is a TLS frontend
tls_matcher = TLS_CERT_PATTERN.search(section)
if tls_matcher:
# TODO(rm_work): Can't we have terminated tcp?
listeners[last_id]['mode'] = 'TERMINATED_HTTPS'
listeners[last_id]['ssl_crt'] = tls_matcher.group(1)
# Clear out the token and id and start over
last_token = last_id = None
m = STATS_SOCKET_PATTERN.search(cfg)
if not m:
raise ParsingError()
stats_socket = m.group(1)
return stats_socket, listeners

View File

@ -43,18 +43,19 @@ SEQ = 0
# incompatible changes. # incompatible changes.
# #
# ver 1 - Adds UDP listener status when no pool or members are present # ver 1 - Adds UDP listener status when no pool or members are present
# ver 2 - Switch to all listeners in a single combined haproxy config
# #
MSG_VER = 1 MSG_VER = 2
def list_sock_stat_files(hadir=None): def list_sock_stat_files(hadir=None):
stat_sock_files = {} stat_sock_files = {}
if hadir is None: if hadir is None:
hadir = CONF.haproxy_amphora.base_path hadir = CONF.haproxy_amphora.base_path
listener_ids = util.get_listeners() lb_ids = util.get_loadbalancers()
for listener_id in listener_ids: for lb_id in lb_ids:
sock_file = listener_id + ".sock" sock_file = lb_id + ".sock"
stat_sock_files[listener_id] = os.path.join(hadir, sock_file) stat_sock_files[lb_id] = os.path.join(hadir, sock_file)
return stat_sock_files return stat_sock_files
@ -115,39 +116,55 @@ def get_stats(stat_sock_file):
def build_stats_message(): def build_stats_message():
# Example version 2 message without UDP:
# {
# "id": "<amphora_id>",
# "seq": 67,
# "listeners": {
# "<listener_id>": {
# "status": "OPEN",
# "stats": {
# "tx": 0,
# "rx": 0,
# "conns": 0,
# "totconns": 0,
# "ereq": 0
# }
# }
# },
# "pools": {
# "<pool_id>:<listener_id>": {
# "status": "UP",
# "members": {
# "<member_id>": "no check"
# }
# }
# },
# "ver": 2
# }
global SEQ global SEQ
msg = {'id': CONF.amphora_agent.amphora_id, msg = {'id': CONF.amphora_agent.amphora_id,
'seq': SEQ, "listeners": {}, 'seq': SEQ, 'listeners': {}, 'pools': {},
'ver': MSG_VER} 'ver': MSG_VER}
SEQ += 1 SEQ += 1
stat_sock_files = list_sock_stat_files() stat_sock_files = list_sock_stat_files()
for listener_id, stat_sock_file in stat_sock_files.items(): # TODO(rm_work) There should only be one of these in the new config system
listener_dict = {'pools': {}, for lb_id, stat_sock_file in stat_sock_files.items():
'status': 'DOWN', if util.is_lb_running(lb_id):
'stats': {
'tx': 0,
'rx': 0,
'conns': 0,
'totconns': 0,
'ereq': 0}}
msg['listeners'][listener_id] = listener_dict
if util.is_listener_running(listener_id):
(stats, pool_status) = get_stats(stat_sock_file) (stats, pool_status) = get_stats(stat_sock_file)
listener_dict = msg['listeners'][listener_id]
for row in stats: for row in stats:
if row['svname'] == 'FRONTEND': if row['svname'] == 'FRONTEND':
listener_dict['stats']['tx'] = int(row['bout']) listener_id = row['pxname']
listener_dict['stats']['rx'] = int(row['bin']) msg['listeners'][listener_id] = {
listener_dict['stats']['conns'] = int(row['scur']) 'status': row['status'],
listener_dict['stats']['totconns'] = int(row['stot']) 'stats': {'tx': int(row['bout']),
listener_dict['stats']['ereq'] = int(row['ereq']) 'rx': int(row['bin']),
listener_dict['status'] = row['status'] 'conns': int(row['scur']),
for oid, pool in pool_status.items(): 'totconns': int(row['stot']),
if oid != listener_id: 'ereq': int(row['ereq'])}}
pool_id = oid for pool_id, pool in pool_status.items():
pools = listener_dict['pools'] msg['pools'][pool_id] = {"status": pool['status'],
pools[pool_id] = {"status": pool['status'], "members": pool['members']}
"members": pool['members']}
# UDP listener part # UDP listener part
udp_listener_ids = util.get_udp_listeners() udp_listener_ids = util.get_udp_listeners()

View File

@ -124,7 +124,9 @@ class HAProxyQuery(object):
final_results[line['pxname']] = dict(members={}) final_results[line['pxname']] = dict(members={})
if line['svname'] == 'BACKEND': if line['svname'] == 'BACKEND':
final_results[line['pxname']]['uuid'] = line['pxname'] pool_id, listener_id = line['pxname'].split(':')
final_results[line['pxname']]['pool_uuid'] = pool_id
final_results[line['pxname']]['listener_uuid'] = listener_id
final_results[line['pxname']]['status'] = line['status'] final_results[line['pxname']]['status'] = line['status']
else: else:
final_results[line['pxname']]['members'][line['svname']] = ( final_results[line['pxname']]['members'][line['svname']] = (

View File

@ -22,17 +22,19 @@ import six
class AmphoraLoadBalancerDriver(object): class AmphoraLoadBalancerDriver(object):
@abc.abstractmethod @abc.abstractmethod
def update_amphora_listeners(self, listeners, amphora_id, timeout_dict): def update_amphora_listeners(self, loadbalancer, amphora,
timeout_dict):
"""Update the amphora with a new configuration. """Update the amphora with a new configuration.
:param listeners: List of listeners to update. :param loadbalancer: List of listeners to update.
:type listener: list :type loadbalancer: list(octavia.db.models.Listener)
:param amphora_id: The ID of the amphora to update :param amphora: The index of the specific amphora to update
:type amphora_id: string :type amphora: octavia.db.models.Amphora
:param timeout_dict: Dictionary of timeout values for calls to the :param timeout_dict: Dictionary of timeout values for calls to the
amphora. May contain: req_conn_timeout, amphora. May contain: req_conn_timeout,
req_read_timeout, conn_max_retries, req_read_timeout, conn_max_retries,
conn_retry_interval conn_retry_interval
:type timeout_dict: dict
:returns: None :returns: None
Builds a new configuration, pushes it to the amphora, and reloads Builds a new configuration, pushes it to the amphora, and reloads
@ -40,14 +42,12 @@ class AmphoraLoadBalancerDriver(object):
""" """
@abc.abstractmethod @abc.abstractmethod
def update(self, listener, vip): def update(self, loadbalancer):
"""Update the amphora with a new configuration. """Update the amphora with a new configuration.
:param listener: listener object, :param loadbalancer: loadbalancer object, need to use its
need to use its protocol_port property vip.ip_address property
:type listener: object :type loadbalancer: octavia.db.models.LoadBalancer
:param vip: vip object, need to use its ip_address property
:type vip: object
:returns: None :returns: None
At this moment, we just build the basic structure for testing, will At this moment, we just build the basic structure for testing, will
@ -55,31 +55,13 @@ class AmphoraLoadBalancerDriver(object):
""" """
@abc.abstractmethod @abc.abstractmethod
def stop(self, listener, vip): def start(self, loadbalancer, amphora):
"""Stop the listener on the vip. """Start the listeners on the amphora.
:param listener: listener object, :param loadbalancer: loadbalancer object to start listeners
need to use its protocol_port property :type loadbalancer: octavia.db.models.LoadBalancer
:type listener: object
:param vip: vip object, need to use its ip_address property
:type vip: object
:returns: return a value list (listener, vip, status flag--suspend)
At this moment, we just build the basic structure for testing, will
add more function along with the development.
"""
@abc.abstractmethod
def start(self, listener, vip, amphora):
"""Start the listener on the vip.
:param listener: listener object,
need to use its protocol_port property
:type listener: object
:param vip: vip object, need to use its ip_address property
:type vip: object
:param amphora: Amphora to start. If None, start on all amphora :param amphora: Amphora to start. If None, start on all amphora
:type amphora: object :type amphora: octavia.db.models.Amphora
:returns: return a value list (listener, vip, status flag--enable) :returns: return a value list (listener, vip, status flag--enable)
At this moment, we just build the basic structure for testing, will At this moment, we just build the basic structure for testing, will
@ -87,14 +69,12 @@ class AmphoraLoadBalancerDriver(object):
""" """
@abc.abstractmethod @abc.abstractmethod
def delete(self, listener, vip): def delete(self, listener):
"""Delete the listener on the vip. """Delete the listener on the vip.
:param listener: listener object, :param listener: listener object,
need to use its protocol_port property need to use its protocol_port property
:type listener: object :type listener: octavia.db.models.Listener
:param vip: vip object, need to use its ip_address property
:type vip: object
:returns: return a value list (listener, vip, status flag--delete) :returns: return a value list (listener, vip, status flag--delete)
At this moment, we just build the basic structure for testing, will At this moment, we just build the basic structure for testing, will
@ -106,7 +86,7 @@ class AmphoraLoadBalancerDriver(object):
"""Returns information about the amphora. """Returns information about the amphora.
:param amphora: amphora object, need to use its id property :param amphora: amphora object, need to use its id property
:type amphora: object :type amphora: octavia.db.models.Amphora
:returns: return a value list (amphora.id, status flag--'info') :returns: return a value list (amphora.id, status flag--'info')
At this moment, we just build the basic structure for testing, will At this moment, we just build the basic structure for testing, will
@ -122,7 +102,7 @@ class AmphoraLoadBalancerDriver(object):
"""Return ceilometer ready diagnostic data. """Return ceilometer ready diagnostic data.
:param amphora: amphora object, need to use its id property :param amphora: amphora object, need to use its id property
:type amphora: object :type amphora: octavia.db.models.Amphora
:returns: return a value list (amphora.id, status flag--'ge :returns: return a value list (amphora.id, status flag--'ge
t_diagnostics') t_diagnostics')
@ -138,7 +118,7 @@ class AmphoraLoadBalancerDriver(object):
"""Finalize the amphora before any listeners are configured. """Finalize the amphora before any listeners are configured.
:param amphora: amphora object, need to use its id property :param amphora: amphora object, need to use its id property
:type amphora: object :type amphora: octavia.db.models.Amphora
:returns: None :returns: None
At this moment, we just build the basic structure for testing, will At this moment, we just build the basic structure for testing, will
@ -151,6 +131,8 @@ class AmphoraLoadBalancerDriver(object):
def post_vip_plug(self, amphora, load_balancer, amphorae_network_config): def post_vip_plug(self, amphora, load_balancer, amphorae_network_config):
"""Called after network driver has allocated and plugged the VIP """Called after network driver has allocated and plugged the VIP
:param amphora:
:type amphora: octavia.db.models.Amphora
:param load_balancer: A load balancer that just had its vip allocated :param load_balancer: A load balancer that just had its vip allocated
and plugged in the network driver. and plugged in the network driver.
:type load_balancer: octavia.common.data_models.LoadBalancer :type load_balancer: octavia.common.data_models.LoadBalancer
@ -168,7 +150,7 @@ class AmphoraLoadBalancerDriver(object):
"""Called after amphora added to network """Called after amphora added to network
:param amphora: amphora object, needs id and network ip(s) :param amphora: amphora object, needs id and network ip(s)
:type amphora: object :type amphora: octavia.db.models.Amphora
:param port: contains information of the plugged port :param port: contains information of the plugged port
:type port: octavia.network.data_models.Port :type port: octavia.network.data_models.Port
@ -182,7 +164,7 @@ class AmphoraLoadBalancerDriver(object):
"""Start health checks. """Start health checks.
:param health_mixin: health mixin object :param health_mixin: health mixin object
:type amphora: object :type health_mixin: HealthMixin
Starts listener process and calls HealthMixin to update Starts listener process and calls HealthMixin to update
databases information. databases information.
@ -199,7 +181,7 @@ class AmphoraLoadBalancerDriver(object):
"""Upload cert info to the amphora. """Upload cert info to the amphora.
:param amphora: amphora object, needs id and network ip(s) :param amphora: amphora object, needs id and network ip(s)
:type amphora: object :type amphora: octavia.db.models.Amphora
:param pem_file: a certificate file :param pem_file: a certificate file
:type pem_file: file object :type pem_file: file object
@ -210,7 +192,7 @@ class AmphoraLoadBalancerDriver(object):
"""Upload and update the amphora agent configuration. """Upload and update the amphora agent configuration.
:param amphora: amphora object, needs id and network ip(s) :param amphora: amphora object, needs id and network ip(s)
:type amphora: object :type amphora: octavia.db.models.Amphora
:param agent_config: The new amphora agent configuration file. :param agent_config: The new amphora agent configuration file.
:type agent_config: string :type agent_config: string
""" """

View File

@ -32,7 +32,8 @@ from octavia.amphorae.drivers.haproxy import exceptions as exc
from octavia.amphorae.drivers.keepalived import vrrp_rest_driver from octavia.amphorae.drivers.keepalived import vrrp_rest_driver
from octavia.common.config import cfg from octavia.common.config import cfg
from octavia.common import constants as consts from octavia.common import constants as consts
from octavia.common.jinja.haproxy import jinja_cfg import octavia.common.jinja.haproxy.combined_listeners.jinja_cfg as jinja_combo
import octavia.common.jinja.haproxy.split_listeners.jinja_cfg as jinja_split
from octavia.common.jinja.lvs import jinja_cfg as jinja_udp_cfg from octavia.common.jinja.lvs import jinja_cfg as jinja_udp_cfg
from octavia.common.tls_utils import cert_parser from octavia.common.tls_utils import cert_parser
from octavia.common import utils from octavia.common import utils
@ -51,14 +52,23 @@ class HaproxyAmphoraLoadBalancerDriver(
def __init__(self): def __init__(self):
super(HaproxyAmphoraLoadBalancerDriver, self).__init__() super(HaproxyAmphoraLoadBalancerDriver, self).__init__()
self.client = AmphoraAPIClient() self.clients = {
'base': AmphoraAPIClientBase(),
'0.5': AmphoraAPIClient0_5(),
'1.0': AmphoraAPIClient1_0(),
}
self.cert_manager = stevedore_driver.DriverManager( self.cert_manager = stevedore_driver.DriverManager(
namespace='octavia.cert_manager', namespace='octavia.cert_manager',
name=CONF.certificates.cert_manager, name=CONF.certificates.cert_manager,
invoke_on_load=True, invoke_on_load=True,
).driver ).driver
self.jinja = jinja_cfg.JinjaTemplater( self.jinja_combo = jinja_combo.JinjaTemplater(
base_amp_path=CONF.haproxy_amphora.base_path,
base_crt_dir=CONF.haproxy_amphora.base_cert_dir,
haproxy_template=CONF.haproxy_amphora.haproxy_template,
connection_logging=CONF.haproxy_amphora.connection_logging)
self.jinja_split = jinja_split.JinjaTemplater(
base_amp_path=CONF.haproxy_amphora.base_path, base_amp_path=CONF.haproxy_amphora.base_path,
base_crt_dir=CONF.haproxy_amphora.base_cert_dir, base_crt_dir=CONF.haproxy_amphora.base_cert_dir,
haproxy_template=CONF.haproxy_amphora.haproxy_template, haproxy_template=CONF.haproxy_amphora.haproxy_template,
@ -72,21 +82,39 @@ class HaproxyAmphoraLoadBalancerDriver(
:returns version_list: A list with the major and minor numbers :returns version_list: A list with the major and minor numbers
""" """
self._populate_amphora_api_version(amphora)
amp_info = self.clients[amphora.api_version].get_info(amphora)
haproxy_version_string = amp_info['haproxy_version']
version_string = self.client.get_info(amphora)['haproxy_version'] return haproxy_version_string.split('.')[:2]
return version_string.split('.')[:2] def _populate_amphora_api_version(self, amphora):
"""Populate the amphora object with the api_version
def update_amphora_listeners(self, listeners, amphora_index, This will query the amphora for version discovery and populate
amphorae, timeout_dict=None): the api_version string attribute on the amphora object.
:returns: None
"""
if not getattr(amphora, 'api_version', None):
try:
amphora.api_version = self.clients['base'].get_api_version(
amphora)['api_version']
except exc.NotFound:
# Amphora is too old for version discovery, default to 0.5
amphora.api_version = '0.5'
LOG.debug('Amphora %s has API version %s',
amphora.id, amphora.api_version)
return list(map(int, amphora.api_version.split('.')))
def update_amphora_listeners(self, loadbalancer, amphora,
timeout_dict=None):
"""Update the amphora with a new configuration. """Update the amphora with a new configuration.
:param listeners: List of listeners to update. :param loadbalancer: The load balancer to update
:type listener: list :type loadbalancer: object
:param amphora_index: The index of the amphora to update :param amphora: The amphora to update
:type amphora_index: integer :type amphora: object
:param amphorae: List of amphorae
:type amphorae: list
:param timeout_dict: Dictionary of timeout values for calls to the :param timeout_dict: Dictionary of timeout values for calls to the
amphora. May contain: req_conn_timeout, amphora. May contain: req_conn_timeout,
req_read_timeout, conn_max_retries, req_read_timeout, conn_max_retries,
@ -96,46 +124,82 @@ class HaproxyAmphoraLoadBalancerDriver(
Updates the configuration of the listeners on a single amphora. Updates the configuration of the listeners on a single amphora.
""" """
# if the amphora does not yet have listeners, no need to update them. # if the amphora does not yet have listeners, no need to update them.
if not listeners: if not loadbalancer.listeners:
LOG.debug('No listeners found to update.') LOG.debug('No listeners found to update.')
return return
amp = amphorae[amphora_index] if amphora is None or amphora.status == consts.DELETED:
if amp is None or amp.status == consts.DELETED:
return return
haproxy_versions = self._get_haproxy_versions(amp) # Check which HAProxy version is on the amp
haproxy_versions = self._get_haproxy_versions(amphora)
# Check which config style to use
api_version = self._populate_amphora_api_version(amphora)
if api_version[0] == 0 and api_version[1] <= 5: # 0.5 or earlier
split_config = True
LOG.warning(
'Amphora %s for loadbalancer %s needs upgrade to single '
'process mode.', amphora.id, loadbalancer.id)
else:
split_config = False
LOG.debug('Amphora %s for loadbalancer %s is already in single '
'process mode.', amphora.id, loadbalancer.id)
# TODO(johnsom) remove when we don't have a process per listener has_tcp = False
for listener in listeners: for listener in loadbalancer.listeners:
LOG.debug("%s updating listener %s on amphora %s", LOG.debug("%s updating listener %s on amphora %s",
self.__class__.__name__, listener.id, amp.id) self.__class__.__name__, listener.id, amphora.id)
if listener.protocol == 'UDP': if listener.protocol == 'UDP':
# Generate Keepalived LVS configuration from listener object # Generate Keepalived LVS configuration from listener object
config = self.udp_jinja.build_config(listener=listener) config = self.udp_jinja.build_config(listener=listener)
self.client.upload_udp_config(amp, listener.id, config, self.clients[amphora.api_version].upload_udp_config(
timeout_dict=timeout_dict) amphora, listener.id, config, timeout_dict=timeout_dict)
self.client.reload_listener(amp, listener.id, self.clients[amphora.api_version].reload_listener(
timeout_dict=timeout_dict) amphora, listener.id, timeout_dict=timeout_dict)
else: else:
certs = self._process_tls_certificates(listener) has_tcp = True
client_ca_filename = self._process_secret( if split_config:
listener, listener.client_ca_tls_certificate_id) obj_id = listener.id
crl_filename = self._process_secret( else:
listener, listener.client_crl_container_id) obj_id = loadbalancer.id
pool_tls_certs = self._process_listener_pool_certs(listener)
# Generate HaProxy configuration from listener object certs = self._process_tls_certificates(
config = self.jinja.build_config( listener, amphora, obj_id)
host_amphora=amp, listener=listener, client_ca_filename = self._process_secret(
tls_cert=certs['tls_cert'], listener, listener.client_ca_tls_certificate_id,
haproxy_versions=haproxy_versions, amphora, obj_id)
client_ca_filename=client_ca_filename, crl_filename = self._process_secret(
client_crl=crl_filename, listener, listener.client_crl_container_id,
pool_tls_certs=pool_tls_certs) amphora, obj_id)
self.client.upload_config(amp, listener.id, config, pool_tls_certs = self._process_listener_pool_certs(
timeout_dict=timeout_dict) listener, amphora, obj_id)
self.client.reload_listener(amp, listener.id,
timeout_dict=timeout_dict) if split_config:
config = self.jinja_split.build_config(
host_amphora=amphora, listener=listener,
tls_cert=certs['tls_cert'],
haproxy_versions=haproxy_versions,
client_ca_filename=client_ca_filename,
client_crl=crl_filename,
pool_tls_certs=pool_tls_certs)
self.clients[amphora.api_version].upload_config(
amphora, listener.id, config,
timeout_dict=timeout_dict)
self.clients[amphora.api_version].reload_listener(
amphora, listener.id, timeout_dict=timeout_dict)
if has_tcp and not split_config:
# Generate HaProxy configuration from listener object
config = self.jinja_combo.build_config(
host_amphora=amphora, listeners=loadbalancer.listeners,
tls_cert=certs['tls_cert'],
haproxy_versions=haproxy_versions,
client_ca_filename=client_ca_filename,
client_crl=crl_filename,
pool_tls_certs=pool_tls_certs)
self.clients[amphora.api_version].upload_config(
amphora, loadbalancer.id, config, timeout_dict=timeout_dict)
self.clients[amphora.api_version].reload_listener(
amphora, loadbalancer.id, timeout_dict=timeout_dict)
def _udp_update(self, listener, vip): def _udp_update(self, listener, vip):
LOG.debug("Amphora %s keepalivedlvs, updating " LOG.debug("Amphora %s keepalivedlvs, updating "
@ -146,69 +210,132 @@ class HaproxyAmphoraLoadBalancerDriver(
for amp in listener.load_balancer.amphorae: for amp in listener.load_balancer.amphorae:
if amp.status != consts.DELETED: if amp.status != consts.DELETED:
# Generate Keepalived LVS configuration from listener object # Generate Keepalived LVS configuration from listener object
self._populate_amphora_api_version(amp)
config = self.udp_jinja.build_config(listener=listener) config = self.udp_jinja.build_config(listener=listener)
self.client.upload_udp_config(amp, listener.id, config) self.clients[amp.api_version].upload_udp_config(
self.client.reload_listener(amp, listener.id) amp, listener.id, config)
self.clients[amp.api_version].reload_listener(
amp, listener.id)
def update(self, listener, vip): def update(self, loadbalancer):
if listener.protocol == 'UDP': for amphora in loadbalancer.amphorae:
self._udp_update(listener, vip) if amphora.status != consts.DELETED:
else: self.update_amphora_listeners(loadbalancer, amphora)
LOG.debug("Amphora %s haproxy, updating listener %s, "
"vip %s", self.__class__.__name__,
listener.protocol_port,
vip.ip_address)
# Process listener certificate info
certs = self._process_tls_certificates(listener)
client_ca_filename = self._process_secret(
listener, listener.client_ca_tls_certificate_id)
crl_filename = self._process_secret(
listener, listener.client_crl_container_id)
pool_tls_certs = self._process_listener_pool_certs(listener)
for amp in listener.load_balancer.amphorae:
if amp.status != consts.DELETED:
haproxy_versions = self._get_haproxy_versions(amp)
# Generate HaProxy configuration from listener object
config = self.jinja.build_config(
host_amphora=amp, listener=listener,
tls_cert=certs['tls_cert'],
haproxy_versions=haproxy_versions,
client_ca_filename=client_ca_filename,
client_crl=crl_filename,
pool_tls_certs=pool_tls_certs)
self.client.upload_config(amp, listener.id, config)
self.client.reload_listener(amp, listener.id)
def upload_cert_amp(self, amp, pem): def upload_cert_amp(self, amp, pem):
LOG.debug("Amphora %s updating cert in REST driver " LOG.debug("Amphora %s updating cert in REST driver "
"with amphora id %s,", "with amphora id %s,",
self.__class__.__name__, amp.id) self.__class__.__name__, amp.id)
self.client.update_cert_for_rotation(amp, pem) self._populate_amphora_api_version(amp)
self.clients[amp.api_version].update_cert_for_rotation(amp, pem)
def _apply(self, func, listener=None, amphora=None, *args): def _apply(self, func_name, loadbalancer, amphora=None, *args):
if amphora is None: if amphora is None:
for amp in listener.load_balancer.amphorae: amphorae = loadbalancer.amphorae
if amp.status != consts.DELETED:
func(amp, listener.id, *args)
else: else:
if amphora.status != consts.DELETED: amphorae = [amphora]
func(amphora, listener.id, *args)
def stop(self, listener, vip): for amp in amphorae:
self._apply(self.client.stop_listener, listener) if amp.status != consts.DELETED:
api_version = self._populate_amphora_api_version(amp)
# Check which config style to use
if api_version[0] == 0 and api_version[1] <= 5:
# 0.5 or earlier
LOG.warning(
'Amphora %s for loadbalancer %s needs upgrade to '
'single process mode.', amp.id, loadbalancer.id)
for listener in loadbalancer.listeners:
getattr(self.clients[amp.api_version], func_name)(
amp, listener.id, *args)
else:
LOG.debug(
'Amphora %s for loadbalancer %s is already in single '
'process mode.', amp.id, loadbalancer.id)
has_tcp = False
for listener in loadbalancer.listeners:
if listener.protocol == consts.PROTOCOL_UDP:
getattr(self.clients[amp.api_version], func_name)(
amp, listener.id, *args)
else:
has_tcp = True
if has_tcp:
getattr(self.clients[amp.api_version], func_name)(
amp, loadbalancer.id, *args)
def start(self, listener, vip, amphora=None): def start(self, loadbalancer, amphora=None):
self._apply(self.client.start_listener, listener, amphora) self._apply('start_listener', loadbalancer, amphora)
def delete(self, listener, vip): def delete(self, listener):
self._apply(self.client.delete_listener, listener) # Delete any UDP listeners the old way (we didn't update the way they
# are configured)
loadbalancer = listener.load_balancer
if listener.protocol == consts.PROTOCOL_UDP:
for amp in loadbalancer.amphorae:
if amp.status != consts.DELETED:
self._populate_amphora_api_version(amp)
self.clients[amp.api_version].delete_listener(
amp, listener.id)
return
# In case the listener is not UDP, things get more complicated.
# We need to do this individually for each amphora in case some are
# using split config and others are using combined config.
for amp in loadbalancer.amphorae:
if amp.status != consts.DELETED:
api_version = self._populate_amphora_api_version(amp)
# Check which config style to use
if api_version[0] == 0 and api_version[1] <= 5:
# 0.5 or earlier
LOG.warning(
'Amphora %s for loadbalancer %s needs upgrade to '
'single process mode.', amp.id, loadbalancer.id)
self.clients[amp.api_version].delete_listener(
amp, listener.id)
else:
LOG.debug(
'Amphora %s for loadbalancer %s is already in single '
'process mode.', amp.id, loadbalancer.id)
self._combined_config_delete(amp, listener)
def _combined_config_delete(self, amphora, listener):
# Remove the listener from the listener list on the LB before
# passing the whole thing over to update (so it'll actually delete)
listener.load_balancer.listeners.remove(listener)
# Check if there's any certs that we need to delete
certs = self._process_tls_certificates(listener)
certs_to_delete = set()
if certs['tls_cert']:
certs_to_delete.add(certs['tls_cert'].id)
for sni_cert in certs['sni_certs']:
certs_to_delete.add(sni_cert.id)
# Delete them (they'll be recreated before the reload if they are
# needed for other listeners anyway)
self._populate_amphora_api_version(amphora)
for cert_id in certs_to_delete:
self.clients[amphora.api_version].delete_cert_pem(
amphora, listener.load_balancer.id,
'{id}.pem'.format(id=cert_id))
# See how many non-UDP listeners we have left
non_udp_listener_count = len([
1 for l in listener.load_balancer.listeners
if l.protocol != consts.PROTOCOL_UDP])
if non_udp_listener_count > 0:
# We have other listeners, so just update is fine.
# TODO(rm_work): This is a little inefficient since this duplicates
# a lot of the detection logic that has already been done, but it
# is probably safer to re-use the existing code-path.
self.update_amphora_listeners(listener.load_balancer, amphora)
else:
# Deleting the last listener, so really do the delete
self.clients[amphora.api_version].delete_listener(
amphora, listener.load_balancer.id)
def get_info(self, amphora): def get_info(self, amphora):
return self.client.get_info(amphora) self._populate_amphora_api_version(amphora)
return self.clients[amphora.api_version].get_info(amphora)
def get_diagnostics(self, amphora): def get_diagnostics(self, amphora):
pass pass
@ -218,6 +345,7 @@ class HaproxyAmphoraLoadBalancerDriver(
def post_vip_plug(self, amphora, load_balancer, amphorae_network_config): def post_vip_plug(self, amphora, load_balancer, amphorae_network_config):
if amphora.status != consts.DELETED: if amphora.status != consts.DELETED:
self._populate_amphora_api_version(amphora)
subnet = amphorae_network_config.get(amphora.id).vip_subnet subnet = amphorae_network_config.get(amphora.id).vip_subnet
# NOTE(blogan): using the vrrp port here because that # NOTE(blogan): using the vrrp port here because that
# is what the allowed address pairs network driver sets # is what the allowed address pairs network driver sets
@ -242,9 +370,8 @@ class HaproxyAmphoraLoadBalancerDriver(
'mtu': port.network.mtu, 'mtu': port.network.mtu,
'host_routes': host_routes} 'host_routes': host_routes}
try: try:
self.client.plug_vip(amphora, self.clients[amphora.api_version].plug_vip(
load_balancer.vip.ip_address, amphora, load_balancer.vip.ip_address, net_info)
net_info)
except exc.Conflict: except exc.Conflict:
LOG.warning('VIP with MAC %(mac)s already exists on amphora, ' LOG.warning('VIP with MAC %(mac)s already exists on amphora, '
'skipping post_vip_plug', 'skipping post_vip_plug',
@ -264,13 +391,14 @@ class HaproxyAmphoraLoadBalancerDriver(
'fixed_ips': fixed_ips, 'fixed_ips': fixed_ips,
'mtu': port.network.mtu} 'mtu': port.network.mtu}
try: try:
self.client.plug_network(amphora, port_info) self._populate_amphora_api_version(amphora)
self.clients[amphora.api_version].plug_network(amphora, port_info)
except exc.Conflict: except exc.Conflict:
LOG.warning('Network with MAC %(mac)s already exists on amphora, ' LOG.warning('Network with MAC %(mac)s already exists on amphora, '
'skipping post_network_plug', 'skipping post_network_plug',
{'mac': port.mac_address}) {'mac': port.mac_address})
def _process_tls_certificates(self, listener): def _process_tls_certificates(self, listener, amphora=None, obj_id=None):
"""Processes TLS data from the listener. """Processes TLS data from the listener.
Converts and uploads PEM data to the Amphora API Converts and uploads PEM data to the Amphora API
@ -290,15 +418,15 @@ class HaproxyAmphoraLoadBalancerDriver(
sni_certs = data['sni_certs'] sni_certs = data['sni_certs']
certs.extend(sni_certs) certs.extend(sni_certs)
for cert in certs: if amphora and obj_id:
pem = cert_parser.build_pem(cert) for cert in certs:
md5 = hashlib.md5(pem).hexdigest() # nosec pem = cert_parser.build_pem(cert)
name = '{id}.pem'.format(id=cert.id) md5 = hashlib.md5(pem).hexdigest() # nosec
self._apply(self._upload_cert, listener, None, pem, md5, name) name = '{id}.pem'.format(id=cert.id)
self._upload_cert(amphora, obj_id, pem, md5, name)
return {'tls_cert': tls_cert, 'sni_certs': sni_certs} return {'tls_cert': tls_cert, 'sni_certs': sni_certs}
def _process_secret(self, listener, secret_ref): def _process_secret(self, listener, secret_ref, amphora=None, obj_id=None):
"""Get the secret from the cert manager and upload it to the amp. """Get the secret from the cert manager and upload it to the amp.
:returns: The filename of the secret in the amp. :returns: The filename of the secret in the amp.
@ -314,10 +442,14 @@ class HaproxyAmphoraLoadBalancerDriver(
md5 = hashlib.md5(secret).hexdigest() # nosec md5 = hashlib.md5(secret).hexdigest() # nosec
id = hashlib.sha1(secret).hexdigest() # nosec id = hashlib.sha1(secret).hexdigest() # nosec
name = '{id}.pem'.format(id=id) name = '{id}.pem'.format(id=id)
self._apply(self._upload_cert, listener, None, secret, md5, name)
if amphora and obj_id:
self._upload_cert(
amphora, obj_id, pem=secret, md5=md5, name=name)
return name return name
def _process_listener_pool_certs(self, listener): def _process_listener_pool_certs(self, listener, amphora=None,
obj_id=None):
# {'POOL-ID': { # {'POOL-ID': {
# 'client_cert': client_full_filename, # 'client_cert': client_full_filename,
# 'ca_cert': ca_cert_full_filename, # 'ca_cert': ca_cert_full_filename,
@ -325,19 +457,20 @@ class HaproxyAmphoraLoadBalancerDriver(
pool_certs_dict = dict() pool_certs_dict = dict()
for pool in listener.pools: for pool in listener.pools:
if pool.id not in pool_certs_dict: if pool.id not in pool_certs_dict:
pool_certs_dict[pool.id] = self._process_pool_certs(listener, pool_certs_dict[pool.id] = self._process_pool_certs(
pool) listener, pool, amphora, obj_id)
for l7policy in listener.l7policies: for l7policy in listener.l7policies:
if (l7policy.redirect_pool and if (l7policy.redirect_pool and
l7policy.redirect_pool.id not in pool_certs_dict): l7policy.redirect_pool.id not in pool_certs_dict):
pool_certs_dict[l7policy.redirect_pool.id] = ( pool_certs_dict[l7policy.redirect_pool.id] = (
self._process_pool_certs(listener, l7policy.redirect_pool)) self._process_pool_certs(listener, l7policy.redirect_pool,
amphora, obj_id))
return pool_certs_dict return pool_certs_dict
def _process_pool_certs(self, listener, pool): def _process_pool_certs(self, listener, pool, amphora=None, obj_id=None):
pool_cert_dict = dict() pool_cert_dict = dict()
# Handle the cleint cert(s) and key # Handle the client cert(s) and key
if pool.tls_certificate_id: if pool.tls_certificate_id:
data = cert_parser.load_certificates_data(self.cert_manager, pool) data = cert_parser.load_certificates_data(self.cert_manager, pool)
pem = cert_parser.build_pem(data) pem = cert_parser.build_pem(data)
@ -347,15 +480,18 @@ class HaproxyAmphoraLoadBalancerDriver(
pass pass
md5 = hashlib.md5(pem).hexdigest() # nosec md5 = hashlib.md5(pem).hexdigest() # nosec
name = '{id}.pem'.format(id=data.id) name = '{id}.pem'.format(id=data.id)
self._apply(self._upload_cert, listener, None, pem, md5, name) if amphora and obj_id:
self._upload_cert(amphora, obj_id, pem=pem, md5=md5, name=name)
pool_cert_dict['client_cert'] = os.path.join( pool_cert_dict['client_cert'] = os.path.join(
CONF.haproxy_amphora.base_cert_dir, listener.id, name) CONF.haproxy_amphora.base_cert_dir, listener.id, name)
if pool.ca_tls_certificate_id: if pool.ca_tls_certificate_id:
name = self._process_secret(listener, pool.ca_tls_certificate_id) name = self._process_secret(listener, pool.ca_tls_certificate_id,
amphora, obj_id)
pool_cert_dict['ca_cert'] = os.path.join( pool_cert_dict['ca_cert'] = os.path.join(
CONF.haproxy_amphora.base_cert_dir, listener.id, name) CONF.haproxy_amphora.base_cert_dir, listener.id, name)
if pool.crl_container_id: if pool.crl_container_id:
name = self._process_secret(listener, pool.crl_container_id) name = self._process_secret(listener, pool.crl_container_id,
amphora, obj_id)
pool_cert_dict['crl'] = os.path.join( pool_cert_dict['crl'] = os.path.join(
CONF.haproxy_amphora.base_cert_dir, listener.id, name) CONF.haproxy_amphora.base_cert_dir, listener.id, name)
@ -363,13 +499,14 @@ class HaproxyAmphoraLoadBalancerDriver(
def _upload_cert(self, amp, listener_id, pem, md5, name): def _upload_cert(self, amp, listener_id, pem, md5, name):
try: try:
if self.client.get_cert_md5sum( if self.clients[amp.api_version].get_cert_md5sum(
amp, listener_id, name, ignore=(404,)) == md5: amp, listener_id, name, ignore=(404,)) == md5:
return return
except exc.NotFound: except exc.NotFound:
pass pass
self.client.upload_cert_pem(amp, listener_id, name, pem) self.clients[amp.api_version].upload_cert_pem(
amp, listener_id, name, pem)
def update_amphora_agent_config(self, amphora, agent_config, def update_amphora_agent_config(self, amphora, agent_config,
timeout_dict=None): timeout_dict=None):
@ -389,8 +526,9 @@ class HaproxyAmphoraLoadBalancerDriver(
new values. new values.
""" """
try: try:
self.client.update_agent_config(amphora, agent_config, self._populate_amphora_api_version(amphora)
timeout_dict=timeout_dict) self.clients[amphora.api_version].update_agent_config(
amphora, agent_config, timeout_dict=timeout_dict)
except exc.NotFound: except exc.NotFound:
LOG.debug('Amphora {} does not support the update_agent_config ' LOG.debug('Amphora {} does not support the update_agent_config '
'API.'.format(amphora.id)) 'API.'.format(amphora.id))
@ -411,10 +549,9 @@ class CustomHostNameCheckingAdapter(requests.adapters.HTTPAdapter):
self).init_poolmanager(*pool_args, **pool_kwargs) self).init_poolmanager(*pool_args, **pool_kwargs)
class AmphoraAPIClient(object): class AmphoraAPIClientBase(object):
def __init__(self): def __init__(self):
super(AmphoraAPIClient, self).__init__() super(AmphoraAPIClientBase, self).__init__()
self.secure = False
self.get = functools.partial(self.request, 'get') self.get = functools.partial(self.request, 'get')
self.post = functools.partial(self.request, 'post') self.post = functools.partial(self.request, 'post')
@ -422,38 +559,29 @@ class AmphoraAPIClient(object):
self.delete = functools.partial(self.request, 'delete') self.delete = functools.partial(self.request, 'delete')
self.head = functools.partial(self.request, 'head') self.head = functools.partial(self.request, 'head')
self.start_listener = functools.partial(self._action,
consts.AMP_ACTION_START)
self.stop_listener = functools.partial(self._action,
consts.AMP_ACTION_STOP)
self.reload_listener = functools.partial(self._action,
consts.AMP_ACTION_RELOAD)
self.start_vrrp = functools.partial(self._vrrp_action,
consts.AMP_ACTION_START)
self.stop_vrrp = functools.partial(self._vrrp_action,
consts.AMP_ACTION_STOP)
self.reload_vrrp = functools.partial(self._vrrp_action,
consts.AMP_ACTION_RELOAD)
self.session = requests.Session() self.session = requests.Session()
self.session.cert = CONF.haproxy_amphora.client_cert self.session.cert = CONF.haproxy_amphora.client_cert
self.ssl_adapter = CustomHostNameCheckingAdapter() self.ssl_adapter = CustomHostNameCheckingAdapter()
self.session.mount('https://', self.ssl_adapter) self.session.mount('https://', self.ssl_adapter)
def _base_url(self, ip): def _base_url(self, ip, api_version=None):
if utils.is_ipv6_lla(ip): if utils.is_ipv6_lla(ip):
ip = '[{ip}%{interface}]'.format( ip = '[{ip}%{interface}]'.format(
ip=ip, ip=ip,
interface=CONF.haproxy_amphora.lb_network_interface) interface=CONF.haproxy_amphora.lb_network_interface)
elif utils.is_ipv6(ip): elif utils.is_ipv6(ip):
ip = '[{ip}]'.format(ip=ip) ip = '[{ip}]'.format(ip=ip)
return "https://{ip}:{port}/{version}/".format( if api_version:
return "https://{ip}:{port}/{version}/".format(
ip=ip,
port=CONF.haproxy_amphora.bind_port,
version=api_version)
return "https://{ip}:{port}/".format(
ip=ip, ip=ip,
port=CONF.haproxy_amphora.bind_port, port=CONF.haproxy_amphora.bind_port)
version=API_VERSION)
def request(self, method, amp, path='/', timeout_dict=None, **kwargs): def request(self, method, amp, path='/', timeout_dict=None,
retry_404=True, **kwargs):
cfg_ha_amp = CONF.haproxy_amphora cfg_ha_amp = CONF.haproxy_amphora
if timeout_dict is None: if timeout_dict is None:
timeout_dict = {} timeout_dict = {}
@ -468,7 +596,7 @@ class AmphoraAPIClient(object):
LOG.debug("request url %s", path) LOG.debug("request url %s", path)
_request = getattr(self.session, method.lower()) _request = getattr(self.session, method.lower())
_url = self._base_url(amp.lb_network_ip) + path _url = self._base_url(amp.lb_network_ip, amp.api_version) + path
LOG.debug("request url %s", _url) LOG.debug("request url %s", _url)
reqargs = { reqargs = {
'verify': CONF.haproxy_amphora.server_ca, 'verify': CONF.haproxy_amphora.server_ca,
@ -481,7 +609,7 @@ class AmphoraAPIClient(object):
self.ssl_adapter.uuid = amp.id self.ssl_adapter.uuid = amp.id
exception = None exception = None
# Keep retrying # Keep retrying
for a in six.moves.xrange(conn_max_retries): for dummy in six.moves.xrange(conn_max_retries):
try: try:
with warnings.catch_warnings(): with warnings.catch_warnings():
warnings.filterwarnings( warnings.filterwarnings(
@ -497,6 +625,8 @@ class AmphoraAPIClient(object):
# amphora is not yet up, in which case retry. # amphora is not yet up, in which case retry.
# Otherwise return the response quickly. # Otherwise return the response quickly.
if r.status_code == 404: if r.status_code == 404:
if not retry_404:
raise exc.NotFound()
LOG.debug('Got a 404 (content-type: %(content_type)s) -- ' LOG.debug('Got a 404 (content-type: %(content_type)s) -- '
'connection data: %(content)s', 'connection data: %(content)s',
{'content_type': content_type, {'content_type': content_type,
@ -524,6 +654,32 @@ class AmphoraAPIClient(object):
'exception': exception}) 'exception': exception})
raise driver_except.TimeOutException() raise driver_except.TimeOutException()
def get_api_version(self, amp):
amp.api_version = None
r = self.get(amp, retry_404=False)
# Handle 404 special as we don't want to log an ERROR on 404
exc.check_exception(r, (404,))
if r.status_code == 404:
raise exc.NotFound()
return r.json()
class AmphoraAPIClient0_5(AmphoraAPIClientBase):
def __init__(self):
super(AmphoraAPIClient0_5, self).__init__()
self.start_listener = functools.partial(self._action,
consts.AMP_ACTION_START)
self.reload_listener = functools.partial(self._action,
consts.AMP_ACTION_RELOAD)
self.start_vrrp = functools.partial(self._vrrp_action,
consts.AMP_ACTION_START)
self.stop_vrrp = functools.partial(self._vrrp_action,
consts.AMP_ACTION_STOP)
self.reload_vrrp = functools.partial(self._vrrp_action,
consts.AMP_ACTION_RELOAD)
def upload_config(self, amp, listener_id, config, timeout_dict=None): def upload_config(self, amp, listener_id, config, timeout_dict=None):
r = self.put( r = self.put(
amp, amp,
@ -532,14 +688,6 @@ class AmphoraAPIClient(object):
data=config) data=config)
return exc.check_exception(r) return exc.check_exception(r)
def get_listener_status(self, amp, listener_id):
r = self.get(
amp,
'listeners/{listener_id}'.format(listener_id=listener_id))
if exc.check_exception(r):
return r.json()
return None
def _action(self, action, amp, listener_id, timeout_dict=None): def _action(self, action, amp, listener_id, timeout_dict=None):
r = self.put(amp, 'listeners/{listener_id}/{action}'.format( r = self.put(amp, 'listeners/{listener_id}/{action}'.format(
listener_id=listener_id, action=action), timeout_dict=timeout_dict) listener_id=listener_id, action=action), timeout_dict=timeout_dict)
@ -630,3 +778,133 @@ class AmphoraAPIClient(object):
def update_agent_config(self, amp, agent_config, timeout_dict=None): def update_agent_config(self, amp, agent_config, timeout_dict=None):
r = self.put(amp, 'config', timeout_dict, data=agent_config) r = self.put(amp, 'config', timeout_dict, data=agent_config)
return exc.check_exception(r) return exc.check_exception(r)
class AmphoraAPIClient1_0(AmphoraAPIClientBase):
def __init__(self):
super(AmphoraAPIClient1_0, self).__init__()
self.start_listener = functools.partial(self._action,
consts.AMP_ACTION_START)
self.reload_listener = functools.partial(self._action,
consts.AMP_ACTION_RELOAD)
self.start_vrrp = functools.partial(self._vrrp_action,
consts.AMP_ACTION_START)
self.stop_vrrp = functools.partial(self._vrrp_action,
consts.AMP_ACTION_STOP)
self.reload_vrrp = functools.partial(self._vrrp_action,
consts.AMP_ACTION_RELOAD)
def upload_config(self, amp, loadbalancer_id, config, timeout_dict=None):
r = self.put(
amp,
'loadbalancer/{amphora_id}/{loadbalancer_id}/haproxy'.format(
amphora_id=amp.id, loadbalancer_id=loadbalancer_id),
timeout_dict, data=config)
return exc.check_exception(r)
def get_listener_status(self, amp, listener_id):
r = self.get(
amp,
'listeners/{listener_id}'.format(listener_id=listener_id))
if exc.check_exception(r):
return r.json()
return None
def _action(self, action, amp, object_id, timeout_dict=None):
r = self.put(
amp, 'loadbalancer/{object_id}/{action}'.format(
object_id=object_id, action=action),
timeout_dict=timeout_dict)
return exc.check_exception(r)
def upload_cert_pem(self, amp, loadbalancer_id, pem_filename, pem_file):
r = self.put(
amp,
'loadbalancer/{loadbalancer_id}/certificates/{filename}'.format(
loadbalancer_id=loadbalancer_id, filename=pem_filename),
data=pem_file)
return exc.check_exception(r)
def get_cert_md5sum(self, amp, loadbalancer_id, pem_filename,
ignore=tuple()):
r = self.get(
amp,
'loadbalancer/{loadbalancer_id}/certificates/{filename}'.format(
loadbalancer_id=loadbalancer_id, filename=pem_filename))
if exc.check_exception(r, ignore):
return r.json().get("md5sum")
return None
def delete_cert_pem(self, amp, loadbalancer_id, pem_filename):
r = self.delete(
amp,
'loadbalancer/{loadbalancer_id}/certificates/{filename}'.format(
loadbalancer_id=loadbalancer_id, filename=pem_filename))
return exc.check_exception(r, (404,))
def update_cert_for_rotation(self, amp, pem_file):
r = self.put(amp, 'certificate', data=pem_file)
return exc.check_exception(r)
def delete_listener(self, amp, object_id):
r = self.delete(
amp, 'listeners/{object_id}'.format(object_id=object_id))
return exc.check_exception(r, (404,))
def get_info(self, amp):
r = self.get(amp, "info")
if exc.check_exception(r):
return r.json()
return None
def get_details(self, amp):
r = self.get(amp, "details")
if exc.check_exception(r):
return r.json()
return None
def get_all_listeners(self, amp):
r = self.get(amp, "listeners")
if exc.check_exception(r):
return r.json()
return None
def plug_network(self, amp, port):
r = self.post(amp, 'plug/network',
json=port)
return exc.check_exception(r)
def plug_vip(self, amp, vip, net_info):
r = self.post(amp,
'plug/vip/{vip}'.format(vip=vip),
json=net_info)
return exc.check_exception(r)
def upload_vrrp_config(self, amp, config):
r = self.put(amp, 'vrrp/upload', data=config)
return exc.check_exception(r)
def _vrrp_action(self, action, amp):
r = self.put(amp, 'vrrp/{action}'.format(action=action))
return exc.check_exception(r)
def get_interface(self, amp, ip_addr, timeout_dict=None):
r = self.get(amp, 'interface/{ip_addr}'.format(ip_addr=ip_addr),
timeout_dict=timeout_dict)
if exc.check_exception(r):
return r.json()
return None
def upload_udp_config(self, amp, listener_id, config, timeout_dict=None):
r = self.put(
amp,
'listeners/{amphora_id}/{listener_id}/udp_listener'.format(
amphora_id=amp.id, listener_id=listener_id), timeout_dict,
data=config)
return exc.check_exception(r)
def update_agent_config(self, amp, agent_config, timeout_dict=None):
r = self.put(amp, 'config', timeout_dict, data=agent_config)
return exc.check_exception(r)

View File

@ -45,13 +45,14 @@ class KeepalivedAmphoraDriverMixin(driver_base.VRRPDriverMixin):
lambda amp: amp.status == constants.AMPHORA_ALLOCATED, lambda amp: amp.status == constants.AMPHORA_ALLOCATED,
loadbalancer.amphorae): loadbalancer.amphorae):
self._populate_amphora_api_version(amp)
# Get the VIP subnet prefix for the amphora # Get the VIP subnet prefix for the amphora
vip_cidr = amphorae_network_config[amp.id].vip_subnet.cidr vip_cidr = amphorae_network_config[amp.id].vip_subnet.cidr
# Generate Keepalived configuration from loadbalancer object # Generate Keepalived configuration from loadbalancer object
config = templater.build_keepalived_config( config = templater.build_keepalived_config(
loadbalancer, amp, vip_cidr) loadbalancer, amp, vip_cidr)
self.client.upload_vrrp_config(amp, config) self.clients[amp.api_version].upload_vrrp_config(amp, config)
def stop_vrrp_service(self, loadbalancer): def stop_vrrp_service(self, loadbalancer):
"""Stop the vrrp services running on the loadbalancer's amphorae """Stop the vrrp services running on the loadbalancer's amphorae
@ -65,7 +66,8 @@ class KeepalivedAmphoraDriverMixin(driver_base.VRRPDriverMixin):
lambda amp: amp.status == constants.AMPHORA_ALLOCATED, lambda amp: amp.status == constants.AMPHORA_ALLOCATED,
loadbalancer.amphorae): loadbalancer.amphorae):
self.client.stop_vrrp(amp) self._populate_amphora_api_version(amp)
self.clients[amp.api_version].stop_vrrp(amp)
def start_vrrp_service(self, loadbalancer): def start_vrrp_service(self, loadbalancer):
"""Start the VRRP services of all amphorae of the loadbalancer """Start the VRRP services of all amphorae of the loadbalancer
@ -80,7 +82,8 @@ class KeepalivedAmphoraDriverMixin(driver_base.VRRPDriverMixin):
loadbalancer.amphorae): loadbalancer.amphorae):
LOG.debug("Start VRRP Service on amphora %s .", amp.lb_network_ip) LOG.debug("Start VRRP Service on amphora %s .", amp.lb_network_ip)
self.client.start_vrrp(amp) self._populate_amphora_api_version(amp)
self.clients[amp.api_version].start_vrrp(amp)
def reload_vrrp_service(self, loadbalancer): def reload_vrrp_service(self, loadbalancer):
"""Reload the VRRP services of all amphorae of the loadbalancer """Reload the VRRP services of all amphorae of the loadbalancer
@ -94,8 +97,10 @@ class KeepalivedAmphoraDriverMixin(driver_base.VRRPDriverMixin):
lambda amp: amp.status == constants.AMPHORA_ALLOCATED, lambda amp: amp.status == constants.AMPHORA_ALLOCATED,
loadbalancer.amphorae): loadbalancer.amphorae):
self.client.reload_vrrp(amp) self._populate_amphora_api_version(amp)
self.clients[amp.api_version].reload_vrrp(amp)
def get_vrrp_interface(self, amphora, timeout_dict=None): def get_vrrp_interface(self, amphora, timeout_dict=None):
return self.client.get_interface( self._populate_amphora_api_version(amphora)
return self.clients[amphora.api_version].get_interface(
amphora, amphora.vrrp_ip, timeout_dict=timeout_dict)['interface'] amphora, amphora.vrrp_ip, timeout_dict=timeout_dict)['interface']

View File

@ -37,44 +37,41 @@ class NoopManager(object):
super(NoopManager, self).__init__() super(NoopManager, self).__init__()
self.amphoraconfig = {} self.amphoraconfig = {}
def update_amphora_listeners(self, listeners, amphora_index, def update_amphora_listeners(self, loadbalancer, amphora, timeout_dict):
amphorae, timeout_dict): amphora_id = amphora.id
amphora_id = amphorae[amphora_index].id for listener in loadbalancer.listeners:
for listener in listeners:
LOG.debug("Amphora noop driver update_amphora_listeners, " LOG.debug("Amphora noop driver update_amphora_listeners, "
"listener %s, amphora %s, timeouts %s", listener.id, "listener %s, amphora %s, timeouts %s", listener.id,
amphora_id, timeout_dict) amphora_id, timeout_dict)
self.amphoraconfig[(listener.id, amphora_id)] = ( self.amphoraconfig[(listener.id, amphora_id)] = (
listener, amphora_id, timeout_dict, "update_amp") listener, amphora_id, timeout_dict, "update_amp")
def update(self, listener, vip): def update(self, loadbalancer):
LOG.debug("Amphora %s no-op, update listener %s, vip %s", LOG.debug("Amphora %s no-op, update listener %s, vip %s",
self.__class__.__name__, listener.protocol_port,
vip.ip_address)
self.amphoraconfig[(listener.protocol_port,
vip.ip_address)] = (listener, vip, 'active')
def stop(self, listener, vip):
LOG.debug("Amphora %s no-op, stop listener %s, vip %s",
self.__class__.__name__, self.__class__.__name__,
listener.protocol_port, vip.ip_address) tuple(l.protocol_port for l in loadbalancer.listeners),
self.amphoraconfig[(listener.protocol_port, loadbalancer.vip.ip_address)
vip.ip_address)] = (listener, vip, 'stop') self.amphoraconfig[
(tuple(l.protocol_port for l in loadbalancer.listeners),
loadbalancer.vip.ip_address)] = (loadbalancer.listeners,
loadbalancer.vip,
'active')
def start(self, listener, vip, amphora=None): def start(self, loadbalancer, amphora=None):
LOG.debug("Amphora %s no-op, start listener %s, vip %s, amp %s", LOG.debug("Amphora %s no-op, start listeners, lb %s, amp %s",
self.__class__.__name__, self.__class__.__name__, loadbalancer.id, amphora)
listener.protocol_port, vip.ip_address, amphora) self.amphoraconfig[
self.amphoraconfig[(listener.protocol_port, (loadbalancer.id, amphora.id)] = (loadbalancer, amphora,
vip.ip_address, amphora)] = (listener, vip, 'start')
amphora, 'start')
def delete(self, listener, vip): def delete(self, listener):
LOG.debug("Amphora %s no-op, delete listener %s, vip %s", LOG.debug("Amphora %s no-op, delete listener %s, vip %s",
self.__class__.__name__, self.__class__.__name__,
listener.protocol_port, vip.ip_address) listener.protocol_port,
listener.load_balancer.vip.ip_address)
self.amphoraconfig[(listener.protocol_port, self.amphoraconfig[(listener.protocol_port,
vip.ip_address)] = (listener, vip, 'delete') listener.load_balancer.vip.ip_address)] = (
listener, listener.load_balancer.vip, 'delete')
def get_info(self, amphora): def get_info(self, amphora):
LOG.debug("Amphora %s no-op, info amphora %s", LOG.debug("Amphora %s no-op, info amphora %s",
@ -124,27 +121,22 @@ class NoopAmphoraLoadBalancerDriver(
super(NoopAmphoraLoadBalancerDriver, self).__init__() super(NoopAmphoraLoadBalancerDriver, self).__init__()
self.driver = NoopManager() self.driver = NoopManager()
def update_amphora_listeners(self, listeners, amphora_index, def update_amphora_listeners(self, loadbalancer, amphora, timeout_dict):
amphorae, timeout_dict):
self.driver.update_amphora_listeners(listeners, amphora_index, self.driver.update_amphora_listeners(loadbalancer, amphora,
amphorae, timeout_dict) timeout_dict)
def update(self, listener, vip): def update(self, loadbalancer):
self.driver.update(listener, vip) self.driver.update(loadbalancer)
def stop(self, listener, vip): def start(self, loadbalancer, amphora=None):
self.driver.stop(listener, vip) self.driver.start(loadbalancer, amphora)
def start(self, listener, vip, amphora=None): def delete(self, listener):
self.driver.start(listener, vip, amphora) self.driver.delete(listener)
def delete(self, listener, vip):
self.driver.delete(listener, vip)
def get_info(self, amphora): def get_info(self, amphora):

View File

@ -122,3 +122,6 @@ def main():
process.join() process.join()
except KeyboardInterrupt: except KeyboardInterrupt:
process_cleanup() process_cleanup()
if __name__ == "__main__":
main()

View File

@ -39,3 +39,6 @@ def main():
workers=CONF.controller_worker.workers, args=(CONF,)) workers=CONF.controller_worker.workers, args=(CONF,))
oslo_config_glue.setup(sm, CONF, reload_method="mutate") oslo_config_glue.setup(sm, CONF, reload_method="mutate")
sm.run() sm.run()
if __name__ == "__main__":
main()

View File

@ -0,0 +1,499 @@
# Copyright (c) 2015 Rackspace
#
# 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.
import os
import re
import jinja2
import six
from octavia.common.config import cfg
from octavia.common import constants
from octavia.common import utils as octavia_utils
PROTOCOL_MAP = {
constants.PROTOCOL_TCP: 'tcp',
constants.PROTOCOL_HTTP: 'http',
constants.PROTOCOL_HTTPS: 'tcp',
constants.PROTOCOL_PROXY: 'proxy',
constants.PROTOCOL_TERMINATED_HTTPS: 'http'
}
BALANCE_MAP = {
constants.LB_ALGORITHM_ROUND_ROBIN: 'roundrobin',
constants.LB_ALGORITHM_LEAST_CONNECTIONS: 'leastconn',
constants.LB_ALGORITHM_SOURCE_IP: 'source'
}
CLIENT_AUTH_MAP = {constants.CLIENT_AUTH_NONE: 'none',
constants.CLIENT_AUTH_OPTIONAL: 'optional',
constants.CLIENT_AUTH_MANDATORY: 'required'}
ACTIVE_PENDING_STATUSES = constants.SUPPORTED_PROVISIONING_STATUSES + (
constants.DEGRADED,)
BASE_PATH = '/var/lib/octavia'
BASE_CRT_DIR = BASE_PATH + '/certs'
HAPROXY_TEMPLATE = os.path.abspath(
os.path.join(os.path.dirname(__file__),
'templates/haproxy.cfg.j2'))
CONF = cfg.CONF
JINJA_ENV = None
class JinjaTemplater(object):
def __init__(self,
base_amp_path=None,
base_crt_dir=None,
haproxy_template=None,
log_http=None,
log_server=None,
connection_logging=True):
"""HaProxy configuration generation
:param base_amp_path: Base path for amphora data
:param base_crt_dir: Base directory for certificate storage
:param haproxy_template: Absolute path to Jinja template
:param log_http: Haproxy HTTP logging path
:param log_server: Haproxy Server logging path
:param connection_logging: enable logging connections in haproxy
"""
self.base_amp_path = base_amp_path or BASE_PATH
self.base_crt_dir = base_crt_dir or BASE_CRT_DIR
self.haproxy_template = haproxy_template or HAPROXY_TEMPLATE
self.log_http = log_http
self.log_server = log_server
self.connection_logging = connection_logging
def build_config(self, host_amphora, listeners, tls_cert,
haproxy_versions, socket_path=None,
client_ca_filename=None, client_crl=None,
pool_tls_certs=None):
"""Convert a logical configuration to the HAProxy version
:param host_amphora: The Amphora this configuration is hosted on
:param listener: The listener configuration
:param tls_cert: The TLS certificates for the listener
:param socket_path: The socket path for Haproxy process
:return: Rendered configuration
"""
# Check for any backward compatibility items we need to check
# This is done here for upgrade scenarios where one amp in a
# pair might be running an older amphora version.
feature_compatibility = {}
# Is it newer than haproxy 1.5?
if not (int(haproxy_versions[0]) < 2 and int(haproxy_versions[1]) < 6):
feature_compatibility[constants.HTTP_REUSE] = True
return self.render_loadbalancer_obj(
host_amphora, listeners, tls_cert=tls_cert,
socket_path=socket_path,
feature_compatibility=feature_compatibility,
client_ca_filename=client_ca_filename, client_crl=client_crl,
pool_tls_certs=pool_tls_certs)
def _get_template(self):
"""Returns the specified Jinja configuration template."""
global JINJA_ENV
if not JINJA_ENV:
template_loader = jinja2.FileSystemLoader(
searchpath=os.path.dirname(self.haproxy_template))
JINJA_ENV = jinja2.Environment(
autoescape=True,
loader=template_loader,
trim_blocks=True,
lstrip_blocks=True)
JINJA_ENV.filters['hash_amp_id'] = octavia_utils.base64_sha1_string
return JINJA_ENV.get_template(os.path.basename(self.haproxy_template))
def _format_log_string(self, load_balancer, protocol):
log_format = CONF.haproxy_amphora.user_log_format.replace(
'{project_id}', load_balancer.project_id)
log_format = log_format.replace('{lb_id}', load_balancer.id)
# Order of these filters matter.
# TODO(johnsom) Remove when HAProxy handles the format string
# with HTTP variables in TCP listeners.
# Currently it either throws an error or just fails
# to log the message.
if protocol not in constants.HAPROXY_HTTP_PROTOCOLS:
log_format = log_format.replace('%{+Q}r', '-')
log_format = log_format.replace('%r', '-')
log_format = log_format.replace('%{+Q}ST', '-')
log_format = log_format.replace('%ST', '-')
log_format = log_format.replace(' ', '\ ')
return log_format
def render_loadbalancer_obj(self, host_amphora, listeners,
tls_cert=None, socket_path=None,
feature_compatibility=None,
client_ca_filename=None, client_crl=None,
pool_tls_certs=None):
"""Renders a templated configuration from a load balancer object
:param host_amphora: The Amphora this configuration is hosted on
:param listener: The listener configuration
:param tls_cert: The TLS certificates for the listener
:param client_ca_filename: The CA certificate for client authorization
:param socket_path: The socket path for Haproxy process
:return: Rendered configuration
"""
feature_compatibility = feature_compatibility or {}
loadbalancer = self._transform_loadbalancer(
host_amphora,
listeners[0].load_balancer,
listeners,
tls_cert,
feature_compatibility,
client_ca_filename=client_ca_filename,
client_crl=client_crl,
pool_tls_certs=pool_tls_certs)
if not socket_path:
socket_path = '%s/%s.sock' % (self.base_amp_path,
listeners[0].load_balancer.id)
return self._get_template().render(
{'loadbalancer': loadbalancer,
'stats_sock': socket_path,
'log_http': self.log_http,
'log_server': self.log_server,
'administrative_log_facility':
CONF.amphora_agent.administrative_log_facility,
'user_log_facility': CONF.amphora_agent.user_log_facility,
'connection_logging': self.connection_logging},
constants=constants)
def _transform_loadbalancer(self, host_amphora, loadbalancer, listeners,
tls_cert, feature_compatibility,
client_ca_filename=None, client_crl=None,
pool_tls_certs=None):
"""Transforms a load balancer into an object that will
be processed by the templating system
"""
listener_transforms = []
for listener in listeners:
if listener.protocol == constants.PROTOCOL_UDP:
continue
listener_transforms.append(self._transform_listener(
listener, tls_cert, feature_compatibility, loadbalancer,
client_ca_filename=client_ca_filename, client_crl=client_crl,
pool_tls_certs=pool_tls_certs))
ret_value = {
'id': loadbalancer.id,
'vip_address': loadbalancer.vip.ip_address,
'listeners': listener_transforms,
'topology': loadbalancer.topology,
'enabled': loadbalancer.enabled,
'peer_port': listeners[0].peer_port,
'host_amphora': self._transform_amphora(
host_amphora, feature_compatibility),
'amphorae': loadbalancer.amphorae
}
# NOTE(sbalukoff): Global connection limit should be a sum of all
# listeners' connection limits.
connection_limit_sum = 0
for listener in listeners:
if listener.protocol == constants.PROTOCOL_UDP:
continue
if listener.connection_limit and listener.connection_limit > -1:
connection_limit_sum += listener.connection_limit
else:
# If *any* listener has no connection limit, global = MAX
connection_limit_sum = constants.HAPROXY_MAX_MAXCONN
# If there's a limit between 0 and MAX, set it, otherwise just set MAX
if 0 < connection_limit_sum < constants.HAPROXY_MAX_MAXCONN:
ret_value['global_connection_limit'] = connection_limit_sum
else:
ret_value['global_connection_limit'] = (
constants.HAPROXY_MAX_MAXCONN)
return ret_value
def _transform_amphora(self, amphora, feature_compatibility):
"""Transform an amphora into an object that will
be processed by the templating system.
"""
return {
'id': amphora.id,
'lb_network_ip': amphora.lb_network_ip,
'vrrp_ip': amphora.vrrp_ip,
'ha_ip': amphora.ha_ip,
'vrrp_port_id': amphora.vrrp_port_id,
'ha_port_id': amphora.ha_port_id,
'role': amphora.role,
'status': amphora.status,
'vrrp_interface': amphora.vrrp_interface,
'vrrp_priority': amphora.vrrp_priority
}
def _transform_listener(self, listener, tls_cert, feature_compatibility,
loadbalancer, client_ca_filename=None,
client_crl=None, pool_tls_certs=None):
"""Transforms a listener into an object that will
be processed by the templating system
"""
ret_value = {
'id': listener.id,
'protocol_port': listener.protocol_port,
'protocol_mode': PROTOCOL_MAP[listener.protocol],
'protocol': listener.protocol,
'insert_headers': listener.insert_headers,
'enabled': listener.enabled,
'user_log_format': self._format_log_string(loadbalancer,
listener.protocol),
'timeout_client_data': (
listener.timeout_client_data or
CONF.haproxy_amphora.timeout_client_data),
'timeout_member_connect': (
listener.timeout_member_connect or
CONF.haproxy_amphora.timeout_member_connect),
'timeout_member_data': (
listener.timeout_member_data or
CONF.haproxy_amphora.timeout_member_data),
'timeout_tcp_inspect': (listener.timeout_tcp_inspect or
CONF.haproxy_amphora.timeout_tcp_inspect),
}
if listener.connection_limit and listener.connection_limit > -1:
ret_value['connection_limit'] = listener.connection_limit
else:
ret_value['connection_limit'] = constants.HAPROXY_MAX_MAXCONN
if listener.tls_certificate_id:
ret_value['default_tls_path'] = '%s.pem' % (
os.path.join(self.base_crt_dir,
loadbalancer.id,
tls_cert.id))
if listener.sni_containers:
ret_value['crt_dir'] = os.path.join(
self.base_crt_dir, loadbalancer.id)
if listener.client_ca_tls_certificate_id:
ret_value['client_ca_tls_path'] = '%s' % (
os.path.join(self.base_crt_dir, loadbalancer.id,
client_ca_filename))
ret_value['client_auth'] = CLIENT_AUTH_MAP.get(
listener.client_authentication)
if listener.client_crl_container_id:
ret_value['client_crl_path'] = '%s' % (
os.path.join(self.base_crt_dir, loadbalancer.id, client_crl))
pools = []
for x in listener.pools:
kwargs = {}
if pool_tls_certs and pool_tls_certs.get(x.id):
kwargs = {'pool_tls_certs': pool_tls_certs.get(x.id)}
pools.append(self._transform_pool(
x, feature_compatibility, **kwargs))
ret_value['pools'] = pools
if listener.default_pool:
for pool in pools:
if pool['id'] == listener.default_pool.id:
ret_value['default_pool'] = pool
break
l7policies = [self._transform_l7policy(
x, feature_compatibility, pool_tls_certs)
for x in listener.l7policies]
ret_value['l7policies'] = l7policies
return ret_value
def _transform_pool(self, pool, feature_compatibility,
pool_tls_certs=None):
"""Transforms a pool into an object that will
be processed by the templating system
"""
ret_value = {
'id': pool.id,
'protocol': PROTOCOL_MAP[pool.protocol],
'proxy_protocol': pool.protocol == constants.PROTOCOL_PROXY,
'lb_algorithm': BALANCE_MAP.get(pool.lb_algorithm, 'roundrobin'),
'members': [],
'health_monitor': '',
'session_persistence': '',
'enabled': pool.enabled,
'operating_status': pool.operating_status,
'stick_size': CONF.haproxy_amphora.haproxy_stick_size,
constants.HTTP_REUSE: feature_compatibility.get(
constants.HTTP_REUSE, False),
'ca_tls_path': '',
'crl_path': '',
'tls_enabled': pool.tls_enabled
}
members = [self._transform_member(x, feature_compatibility)
for x in pool.members]
ret_value['members'] = members
if pool.health_monitor:
ret_value['health_monitor'] = self._transform_health_monitor(
pool.health_monitor, feature_compatibility)
if pool.session_persistence:
ret_value[
'session_persistence'] = self._transform_session_persistence(
pool.session_persistence, feature_compatibility)
if (pool.tls_certificate_id and pool_tls_certs and
pool_tls_certs.get('client_cert')):
ret_value['client_cert'] = pool_tls_certs.get('client_cert')
if (pool.ca_tls_certificate_id and pool_tls_certs and
pool_tls_certs.get('ca_cert')):
ret_value['ca_cert'] = pool_tls_certs.get('ca_cert')
if (pool.crl_container_id and pool_tls_certs and
pool_tls_certs.get('crl')):
ret_value['crl'] = pool_tls_certs.get('crl')
return ret_value
@staticmethod
def _transform_session_persistence(persistence, feature_compatibility):
"""Transforms session persistence into an object that will
be processed by the templating system
"""
return {
'type': persistence.type,
'cookie_name': persistence.cookie_name
}
@staticmethod
def _transform_member(member, feature_compatibility):
"""Transforms a member into an object that will
be processed by the templating system
"""
return {
'id': member.id,
'address': member.ip_address,
'protocol_port': member.protocol_port,
'weight': member.weight,
'enabled': member.enabled,
'subnet_id': member.subnet_id,
'operating_status': member.operating_status,
'monitor_address': member.monitor_address,
'monitor_port': member.monitor_port,
'backup': member.backup
}
def _transform_health_monitor(self, monitor, feature_compatibility):
"""Transforms a health monitor into an object that will
be processed by the templating system
"""
codes = None
if monitor.expected_codes:
codes = '|'.join(self._expand_expected_codes(
monitor.expected_codes))
return {
'id': monitor.id,
'type': monitor.type,
'delay': monitor.delay,
'timeout': monitor.timeout,
'fall_threshold': monitor.fall_threshold,
'rise_threshold': monitor.rise_threshold,
'http_method': monitor.http_method,
'url_path': monitor.url_path,
'expected_codes': codes,
'enabled': monitor.enabled,
'http_version': monitor.http_version,
'domain_name': monitor.domain_name,
}
def _transform_l7policy(self, l7policy, feature_compatibility,
pool_tls_certs=None):
"""Transforms an L7 policy into an object that will
be processed by the templating system
"""
ret_value = {
'id': l7policy.id,
'action': l7policy.action,
'redirect_url': l7policy.redirect_url,
'redirect_prefix': l7policy.redirect_prefix,
'enabled': l7policy.enabled
}
if l7policy.redirect_pool:
kwargs = {}
if pool_tls_certs and pool_tls_certs.get(
l7policy.redirect_pool.id):
kwargs = {'pool_tls_certs':
pool_tls_certs.get(l7policy.redirect_pool.id)}
ret_value['redirect_pool'] = self._transform_pool(
l7policy.redirect_pool, feature_compatibility, **kwargs)
else:
ret_value['redirect_pool'] = None
if (l7policy.action in [constants.L7POLICY_ACTION_REDIRECT_TO_URL,
constants.L7POLICY_ACTION_REDIRECT_PREFIX] and
l7policy.redirect_http_code):
ret_value['redirect_http_code'] = l7policy.redirect_http_code
else:
ret_value['redirect_http_code'] = None
l7rules = [self._transform_l7rule(x, feature_compatibility)
for x in l7policy.l7rules if x.enabled]
ret_value['l7rules'] = l7rules
return ret_value
def _transform_l7rule(self, l7rule, feature_compatibility):
"""Transforms an L7 rule into an object that will
be processed by the templating system
"""
return {
'id': l7rule.id,
'type': l7rule.type,
'compare_type': l7rule.compare_type,
'key': l7rule.key,
'value': self._escape_haproxy_config_string(l7rule.value),
'invert': l7rule.invert,
'enabled': l7rule.enabled
}
@staticmethod
def _escape_haproxy_config_string(value):
"""Escapes certain characters in a given string such that
haproxy will parse the string as a single value
"""
# Escape backslashes first
value = re.sub(r'\\', r'\\\\', value)
# Spaces next
value = re.sub(' ', '\\ ', value)
return value
@staticmethod
def _expand_expected_codes(codes):
"""Expand the expected code string in set of codes.
200-204 -> 200, 201, 202, 204
200, 203 -> 200, 203
"""
retval = set()
for code in codes.replace(',', ' ').split(' '):
code = code.strip()
if not code:
continue
elif '-' in code:
low, hi = code.split('-')[:2]
retval.update(
str(i) for i in six.moves.xrange(int(low), int(hi) + 1))
else:
retval.add(code)
return sorted(retval)

View File

@ -0,0 +1,52 @@
{# Copyright (c) 2015 Rackspace
#
# 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.
#
#}
# Configuration for loadbalancer {{ loadbalancer_id }}
global
daemon
user nobody
log {{ log_http | default('/dev/log', true)}} local{{ user_log_facility }}
log {{ log_server | default('/dev/log', true)}} local{{ administrative_log_facility }} notice
stats socket {{ sock_path }} mode 0666 level user
{% if loadbalancer.global_connection_limit is defined %}
maxconn {{ loadbalancer.global_connection_limit }}
{% endif %}
{% set found_ns = namespace(found=false) %}
{% for listener in loadbalancer.listeners if listener.enabled %}
{% for pool in listener.pools if pool.enabled %}
{% if pool.health_monitor and pool.health_monitor.enabled and
pool.health_monitor.type == constants.HEALTH_MONITOR_PING and
found_ns.found == false %}
{% set found_ns.found = true %}
external-check
{% endif %}
{% endfor %}
{% endfor %}
defaults
{% if connection_logging %}
log global
{% else %}
no log
{% endif %}
retries 3
option redispatch
option splice-request
option splice-response
option http-keep-alive
{% block peers %}{% endblock peers %}
{% block proxies %}{% endblock proxies %}

View File

@ -0,0 +1,40 @@
{# Copyright (c) 2015 Rackspace
#
# 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.
#
#}
{% extends 'base.j2' %}
{% from 'macros.j2' import frontend_macro, backend_macro %}
{% from 'macros.j2' import peers_macro %}
{% set loadbalancer_id = loadbalancer.id %}
{% set sock_path = stats_sock %}
{% block peers %}
{{ peers_macro(constants, loadbalancer) }}
{% endblock peers %}
{% block proxies %}
{% if loadbalancer.enabled %}
{% for listener in loadbalancer.listeners if listener.enabled %}
{{- frontend_macro(constants, listener, loadbalancer.vip_address) }}
{% for pool in listener.pools if pool.enabled %}
{{- backend_macro(constants, listener, pool, loadbalancer) }}
{% endfor %}
{% endfor %}
{% endif %}
{% endblock proxies %}

View File

@ -0,0 +1,370 @@
{# Copyright (c) 2015 Rackspace
#
# 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.
#
#}
{% macro peers_macro(constants, loadbalancer) %}
{% if loadbalancer.topology == constants.TOPOLOGY_ACTIVE_STANDBY %}
peers {{ "%s_peers"|format(loadbalancer.id.replace("-", ""))|trim() }}
{% for amp in loadbalancer.amphorae if (
amp.status == constants.AMPHORA_ALLOCATED) %}
{# HAProxy has peer name limitations, thus the hash filter #}
peer {{ amp.id|hash_amp_id|replace('=', '') }} {{
amp.vrrp_ip }}:{{ constants.HAPROXY_BASE_PEER_PORT }}
{% endfor %}
{% endif %}
{% endmacro %}
{% macro bind_macro(constants, listener, lb_vip_address) %}
{% if listener.default_tls_path %}
{% set def_crt_opt = ("ssl crt %s"|format(
listener.default_tls_path)|trim()) %}
{% else %}
{% set def_crt_opt = "" %}
{% endif %}
{% if listener.crt_dir %}
{% set crt_dir_opt = "crt %s"|format(listener.crt_dir)|trim() %}
{% else %}
{% set crt_dir_opt = "" %}
{% endif %}
{% if listener.client_ca_tls_path and listener.client_auth %}
{% set client_ca_opt = "ca-file %s verify %s"|format(listener.client_ca_tls_path, listener.client_auth)|trim() %}
{% else %}
{% set client_ca_opt = "" %}
{% endif %}
{% if listener.client_crl_path and listener.client_ca_tls_path %}
{% set ca_crl_opt = "crl-file %s"|format(listener.client_crl_path)|trim() %}
{% else %}
{% set ca_crl_opt = "" %}
{% endif %}
bind {{ lb_vip_address }}:{{ listener.protocol_port }} {{
"%s %s %s %s"|format(def_crt_opt, crt_dir_opt, client_ca_opt, ca_crl_opt)|trim() }}
{% endmacro %}
{% macro l7rule_compare_type_macro(constants, ctype) %}
{% if ctype == constants.L7RULE_COMPARE_TYPE_REGEX %}
{{- "-m reg" -}}
{% elif ctype == constants.L7RULE_COMPARE_TYPE_STARTS_WITH %}
{{- "-m beg" -}}
{% elif ctype == constants.L7RULE_COMPARE_TYPE_ENDS_WITH %}
{{- "-m end" -}}
{% elif ctype == constants.L7RULE_COMPARE_TYPE_CONTAINS %}
{{- "-m sub" -}}
{% elif ctype == constants.L7RULE_COMPARE_TYPE_EQUAL_TO %}
{{- "-m str" -}}
{% endif %}
{% endmacro %}
{% macro l7rule_macro(constants, l7rule) %}
{% if l7rule.type == constants.L7RULE_TYPE_HOST_NAME %}
acl {{ l7rule.id }} req.hdr(host) -i {{ l7rule_compare_type_macro(
constants, l7rule.compare_type) }} {{ l7rule.value }}
{% elif l7rule.type == constants.L7RULE_TYPE_PATH %}
acl {{ l7rule.id }} path {{ l7rule_compare_type_macro(
constants, l7rule.compare_type) }} {{ l7rule.value }}
{% elif l7rule.type == constants.L7RULE_TYPE_FILE_TYPE %}
acl {{ l7rule.id }} path_end {{ l7rule_compare_type_macro(
constants, l7rule.compare_type) }} {{ l7rule.value }}
{% elif l7rule.type == constants.L7RULE_TYPE_HEADER %}
acl {{ l7rule.id }} req.hdr({{ l7rule.key }}) {{
l7rule_compare_type_macro(
constants, l7rule.compare_type) }} {{ l7rule.value }}
{% elif l7rule.type == constants.L7RULE_TYPE_COOKIE %}
acl {{ l7rule.id }} req.cook({{ l7rule.key }}) {{
l7rule_compare_type_macro(
constants, l7rule.compare_type) }} {{ l7rule.value }}
{% elif l7rule.type == constants.L7RULE_TYPE_SSL_CONN_HAS_CERT %}
acl {{ l7rule.id }} ssl_c_used
{% elif l7rule.type == constants.L7RULE_TYPE_SSL_VERIFY_RESULT %}
acl {{ l7rule.id }} ssl_c_verify eq {{ l7rule.value }}
{% elif l7rule.type == constants.L7RULE_TYPE_SSL_DN_FIELD %}
acl {{ l7rule.id }} ssl_c_s_dn({{ l7rule.key }}) {{
l7rule_compare_type_macro(
constants, l7rule.compare_type) }} {{ l7rule.value }}
{% endif %}
{% endmacro %}
{% macro l7rule_invert_macro(invert) %}
{% if invert %}
{{- "!" -}}
{% endif %}
{% endmacro %}
{% macro l7rule_list_macro(l7policy) %}
{% for l7rule in l7policy.l7rules %}
{{- " " -}}{{- l7rule_invert_macro(l7rule.invert) -}}{{- l7rule.id -}}
{% endfor %}
{% endmacro %}
{% macro l7policy_macro(constants, l7policy, listener) %}
{% for l7rule in l7policy.l7rules %}
{{- l7rule_macro(constants, l7rule) -}}
{% endfor %}
{% if l7policy.redirect_http_code %}
{% set redirect_http_code_opt = " code %s"|format(
l7policy.redirect_http_code) %}
{% else %}
{% set redirect_http_code_opt = "" %}
{% endif %}
{% if l7policy.action == constants.L7POLICY_ACTION_REJECT %}
http-request deny if{{ l7rule_list_macro(l7policy) }}
{% elif l7policy.action == constants.L7POLICY_ACTION_REDIRECT_TO_URL %}
redirect {{- redirect_http_code_opt }} location {{ l7policy.redirect_url }} if{{ l7rule_list_macro(l7policy) }}
{% elif l7policy.action == constants.L7POLICY_ACTION_REDIRECT_TO_POOL and l7policy.redirect_pool.enabled %}
use_backend {{ l7policy.redirect_pool.id }}:{{ listener.id }} if{{ l7rule_list_macro(l7policy) }}
{% elif l7policy.action == constants.L7POLICY_ACTION_REDIRECT_PREFIX %}
redirect {{- redirect_http_code_opt }} prefix {{ l7policy.redirect_prefix }} if{{ l7rule_list_macro(l7policy) }}
{% endif %}
{% endmacro %}
{% macro frontend_macro(constants, listener, lb_vip_address) %}
frontend {{ listener.id }}
log-format {{ listener.user_log_format }}
{% if listener.connection_limit is defined %}
maxconn {{ listener.connection_limit }}
{% endif %}
{% if (listener.protocol.lower() ==
constants.PROTOCOL_TERMINATED_HTTPS.lower()) %}
redirect scheme https if !{ ssl_fc }
{% endif %}
{{ bind_macro(constants, listener, lb_vip_address)|trim() }}
mode {{ listener.protocol_mode }}
{% for l7policy in listener.l7policies if (l7policy.enabled and
l7policy.l7rules|length > 0) %}
{{- l7policy_macro(constants, l7policy, listener) -}}
{% endfor %}
{% if listener.default_pool and listener.default_pool.enabled %}
default_backend {{ listener.default_pool.id }}:{{ listener.id }}
{% endif %}
timeout client {{ listener.timeout_client_data }}
{% if listener.timeout_tcp_inspect %}
tcp-request inspect-delay {{ listener.timeout_tcp_inspect }}
{% endif %}
{% endmacro %}
{% macro member_macro(constants, pool, member) %}
{% if pool.health_monitor and pool.health_monitor.enabled %}
{% if member.monitor_address %}
{% set monitor_addr_opt = " addr %s"|format(member.monitor_address) %}
{% else %}
{% set monitor_addr_opt = "" %}
{% endif %}
{% if member.monitor_port %}
{% set monitor_port_opt = " port %s"|format(member.monitor_port) %}
{% else %}
{% set monitor_port_opt = "" %}
{% endif %}
{% if pool.health_monitor.type == constants.HEALTH_MONITOR_HTTPS %}
{% set monitor_ssl_opt = " check-ssl verify none" %}
{% else %}
{% set monitor_ssl_opt = "" %}
{% endif %}
{% set hm_opt = " check%s inter %ds fall %d rise %d%s%s"|format(
monitor_ssl_opt, pool.health_monitor.delay,
pool.health_monitor.fall_threshold,
pool.health_monitor.rise_threshold, monitor_addr_opt,
monitor_port_opt) %}
{% else %}
{% set hm_opt = "" %}
{% endif %}
{% if (pool.session_persistence.type ==
constants.SESSION_PERSISTENCE_HTTP_COOKIE) %}
{% set persistence_opt = " cookie %s"|format(member.id) %}
{% else %}
{% set persistence_opt = "" %}
{% endif %}
{% if pool.proxy_protocol %}
{% set proxy_protocol_opt = " send-proxy" %}
{% else %}
{% set proxy_protocol_opt = "" %}
{% endif %}
{% if member.backup %}
{% set member_backup_opt = " backup" %}
{% else %}
{% set member_backup_opt = "" %}
{% endif %}
{% if member.enabled %}
{% set member_enabled_opt = "" %}
{% else %}
{% set member_enabled_opt = " disabled" %}
{% endif %}
{% if pool.tls_enabled %}
{% set def_opt_prefix = " ssl" %}
{% set def_sni_opt = " sni ssl_fc_sni" %}
{% else %}
{% set def_opt_prefix = "" %}
{% set def_sni_opt = "" %}
{% endif %}
{% if pool.client_cert and pool.tls_enabled %}
{% set def_crt_opt = " crt %s"|format(pool.client_cert) %}
{% else %}
{% set def_crt_opt = "" %}
{% endif %}
{% if pool.ca_cert and pool.tls_enabled %}
{% set ca_opt = " ca-file %s"|format(pool.ca_cert) %}
{% set def_verify_opt = " verify required" %}
{% if pool.crl %}
{% set crl_opt = " crl-file %s"|format(pool.crl) %}
{% else %}
{% set def_verify_opt = "" %}
{% endif %}
{% elif pool.tls_enabled %}
{% set def_verify_opt = " verify none" %}
{% endif %}
{{ "server %s %s:%d weight %s%s%s%s%s%s%s%s%s%s%s%s"|e|format(
member.id, member.address, member.protocol_port, member.weight,
hm_opt, persistence_opt, proxy_protocol_opt, member_backup_opt,
member_enabled_opt, def_opt_prefix, def_crt_opt, ca_opt, crl_opt,
def_verify_opt, def_sni_opt)|trim() }}
{% endmacro %}
{% macro backend_macro(constants, listener, pool, loadbalancer) %}
backend {{ pool.id }}:{{ listener.id }}
{% if pool.protocol.lower() == constants.PROTOCOL_PROXY.lower() %}
mode {{ listener.protocol_mode }}
{% else %}
mode {{ pool.protocol }}
{% endif %}
{% if pool.get(constants.HTTP_REUSE, False) and (
pool.protocol.lower() == constants.PROTOCOL_HTTP.lower() or
(pool.protocol.lower() == constants.PROTOCOL_PROXY.lower() and
listener.protocol_mode.lower() ==
constants.PROTOCOL_HTTP.lower()))%}
http-reuse safe
{% endif %}
balance {{ pool.lb_algorithm }}
{% if pool.session_persistence %}
{% if (pool.session_persistence.type ==
constants.SESSION_PERSISTENCE_SOURCE_IP) %}
{% if loadbalancer.topology == constants.TOPOLOGY_ACTIVE_STANDBY %}
stick-table type ip size {{ pool.stick_size }} peers {{
"%s_peers"|format(loadbalancer.id.replace("-", ""))|trim() }}
{% else %}
stick-table type ip size {{ pool.stick_size }}
{% endif %}
stick on src
{% elif (pool.session_persistence.type ==
constants.SESSION_PERSISTENCE_APP_COOKIE) %}
{% if loadbalancer.topology == constants.TOPOLOGY_ACTIVE_STANDBY %}
stick-table type string len 64 size {{
pool.stick_size }} peers {{
"%s_peers"|format(loadbalancer.id.replace("-", ""))|trim() }}
{% else %}
stick-table type string len 64 size {{ pool.stick_size }}
{% endif %}
stick store-response res.cook({{ pool.session_persistence.cookie_name }})
stick match req.cook({{ pool.session_persistence.cookie_name }})
{% elif (pool.session_persistence.type ==
constants.SESSION_PERSISTENCE_HTTP_COOKIE) %}
cookie SRV insert indirect nocache
{% endif %}
{% endif %}
{% if pool.health_monitor and pool.health_monitor.enabled %}
timeout check {{ pool.health_monitor.timeout }}s
{% if (pool.health_monitor.type ==
constants.HEALTH_MONITOR_HTTP or pool.health_monitor.type ==
constants.HEALTH_MONITOR_HTTPS) %}
{% if (pool.health_monitor.http_version and
pool.health_monitor.http_version == 1.1 and
pool.health_monitor.domain_name) %}
option httpchk {{ pool.health_monitor.http_method }} {{ pool.health_monitor.url_path }} HTTP/
{{- pool.health_monitor.http_version -}}{{- "\\r\\n" | safe -}}
Host:\ {{ pool.health_monitor.domain_name }}
{% elif pool.health_monitor.http_version %}
option httpchk {{ pool.health_monitor.http_method }} {{ pool.health_monitor.url_path }} HTTP/
{{- pool.health_monitor.http_version -}}{{- "\\r\\n" | safe }}
{% else %}
option httpchk {{ pool.health_monitor.http_method }} {{ pool.health_monitor.url_path }}
{% endif %}
http-check expect rstatus {{ pool.health_monitor.expected_codes }}
{% endif %}
{% if pool.health_monitor.type == constants.HEALTH_MONITOR_TLS_HELLO %}
option ssl-hello-chk
{% endif %}
{% if pool.health_monitor.type == constants.HEALTH_MONITOR_PING %}
option external-check
external-check command /var/lib/octavia/ping-wrapper.sh
{% endif %}
{% endif %}
{% if pool.protocol.lower() == constants.PROTOCOL_HTTP.lower() %}
{% if listener.insert_headers.get('X-Forwarded-For',
'False').lower() == 'true' %}
option forwardfor
{% endif %}
{% if listener.insert_headers.get('X-Forwarded-Port',
'False').lower() == 'true' %}
http-request set-header X-Forwarded-Port %[dst_port]
{% endif %}
{% endif %}
{% if listener.insert_headers.get('X-Forwarded-Proto',
'False').lower() == 'true' %}
{% if listener.protocol.lower() == constants.PROTOCOL_HTTP.lower() %}
http-request set-header X-Forwarded-Proto http
{% elif listener.protocol.lower() ==
constants.PROTOCOL_TERMINATED_HTTPS.lower() %}
http-request set-header X-Forwarded-Proto https
{% endif %}
{% endif %}
{% if listener.protocol.lower() == constants.PROTOCOL_TERMINATED_HTTPS.lower() %}
{% if listener.insert_headers.get('X-SSL-Client-Verify',
'False').lower() == 'true' %}
http-request set-header X-SSL-Client-Verify %[ssl_c_verify]
{% endif %}
{% if listener.insert_headers.get('X-SSL-Client-Has-Cert',
'False').lower() == 'true' %}
http-request set-header X-SSL-Client-Has-Cert %[ssl_c_used]
{% endif %}
{% if listener.insert_headers.get('X-SSL-Client-DN',
'False').lower() == 'true' %}
http-request set-header X-SSL-Client-DN %{+Q}[ssl_c_s_dn]
{% endif %}
{% if listener.insert_headers.get('X-SSL-Client-CN',
'False').lower() == 'true' %}
http-request set-header X-SSL-Client-CN %{+Q}[ssl_c_s_dn(cn)]
{% endif %}
{% if listener.insert_headers.get('X-SSL-Issuer',
'False').lower() == 'true' %}
http-request set-header X-SSL-Issuer %{+Q}[ssl_c_i_dn]
{% endif %}
{% if listener.insert_headers.get('X-SSL-Client-SHA1',
'False').lower() == 'true' %}
http-request set-header X-SSL-Client-SHA1 %{+Q}[ssl_c_sha1,hex]
{% endif %}
{% if listener.insert_headers.get('X-SSL-Client-Not-Before',
'False').lower() == 'true' %}
http-request set-header X-SSL-Client-Not-Before %{+Q}[ssl_c_notbefore]
{% endif %}
{% if listener.insert_headers.get('X-SSL-Client-Not-After',
'False').lower() == 'true' %}
http-request set-header X-SSL-Client-Not-After %{+Q}[ssl_c_notafter]
{% endif %}
{% endif %}
{% if listener.connection_limit is defined %}
fullconn {{ listener.connection_limit }}
{% endif %}
option allbackups
timeout connect {{ listener.timeout_member_connect }}
timeout server {{ listener.timeout_member_data }}
{% for member in pool.members %}
{{- member_macro(constants, pool, member) -}}
{% endfor %}
{% endmacro %}

View File

@ -110,7 +110,7 @@ class UpdateHealthDb(update_base.HealthUpdateBase):
:type map: string :type map: string
:returns: null :returns: null
The input health data structure is shown as below:: The input v1 health data structure is shown as below::
health = { health = {
"id": self.FAKE_UUID_1, "id": self.FAKE_UUID_1,
@ -125,6 +125,33 @@ class UpdateHealthDb(update_base.HealthUpdateBase):
} }
} }
Example V2 message::
{"id": "<amphora_id>",
"seq": 67,
"listeners": {
"<listener_id>": {
"status": "OPEN",
"stats": {
"tx": 0,
"rx": 0,
"conns": 0,
"totconns": 0,
"ereq": 0
}
}
},
"pools": {
"<pool_id>:<listener_id>": {
"status": "UP",
"members": {
"<member_id>": "no check"
}
}
},
"ver": 2
}
""" """
session = db_api.get_session() session = db_api.get_session()
@ -134,10 +161,15 @@ class UpdateHealthDb(update_base.HealthUpdateBase):
ignore_listener_count = False ignore_listener_count = False
if db_lb: if db_lb:
expected_listener_count = len(db_lb.get('listeners', {})) expected_listener_count = 0
if 'PENDING' in db_lb['provisioning_status']: if 'PENDING' in db_lb['provisioning_status']:
ignore_listener_count = True ignore_listener_count = True
else: else:
for key, listener in db_lb.get('listeners', {}).items():
# disabled listeners don't report from the amphora
if listener['enabled']:
expected_listener_count += 1
# If this is a heartbeat older than versioning, handle # If this is a heartbeat older than versioning, handle
# UDP special for backward compatibility. # UDP special for backward compatibility.
if 'ver' not in health: if 'ver' not in health:
@ -231,6 +263,8 @@ class UpdateHealthDb(update_base.HealthUpdateBase):
else: else:
lb_status = constants.ONLINE lb_status = constants.ONLINE
health_msg_version = health.get('ver', 0)
for listener_id in db_lb.get('listeners', {}): for listener_id in db_lb.get('listeners', {}):
db_op_status = db_lb['listeners'][listener_id]['operating_status'] db_op_status = db_lb['listeners'][listener_id]['operating_status']
listener_status = None listener_status = None
@ -267,11 +301,36 @@ class UpdateHealthDb(update_base.HealthUpdateBase):
if not listener: if not listener:
continue continue
pools = listener['pools'] if health_msg_version < 2:
raw_pools = listener['pools']
# normalize the pool IDs. Single process listener pools
# have the listener id appended with an ':' seperator.
# Old multi-process listener pools only have a pool ID.
# This makes sure the keys are only pool IDs.
pools = {(k + ' ')[:k.rfind(':')]: v for k, v in
raw_pools.items()}
for db_pool_id in db_lb.get('pools', {}):
# If we saw this pool already on another listener, skip it.
if db_pool_id in processed_pools:
continue
db_pool_dict = db_lb['pools'][db_pool_id]
lb_status = self._process_pool_status(
session, db_pool_id, db_pool_dict, pools,
lb_status, processed_pools, potential_offline_pools)
if health_msg_version >= 2:
raw_pools = health['pools']
# normalize the pool IDs. Single process listener pools
# have the listener id appended with an ':' seperator.
# Old multi-process listener pools only have a pool ID.
# This makes sure the keys are only pool IDs.
pools = {(k + ' ')[:k.rfind(':')]: v for k, v in raw_pools.items()}
for db_pool_id in db_lb.get('pools', {}): for db_pool_id in db_lb.get('pools', {}):
# If we saw this pool already on another listener # If we saw this pool already, skip it.
# skip it.
if db_pool_id in processed_pools: if db_pool_id in processed_pools:
continue continue
db_pool_dict = db_lb['pools'][db_pool_id] db_pool_dict = db_lb['pools'][db_pool_id]
@ -416,7 +475,7 @@ class UpdateStatsDb(update_base.StatsUpdateBase, stats.StatsMixin):
:type map: string :type map: string
:returns: null :returns: null
Example:: Example V1 message::
health = { health = {
"id": self.FAKE_UUID_1, "id": self.FAKE_UUID_1,
@ -440,6 +499,33 @@ class UpdateStatsDb(update_base.StatsUpdateBase, stats.StatsMixin):
} }
} }
Example V2 message::
{"id": "<amphora_id>",
"seq": 67,
"listeners": {
"<listener_id>": {
"status": "OPEN",
"stats": {
"tx": 0,
"rx": 0,
"conns": 0,
"totconns": 0,
"ereq": 0
}
}
},
"pools": {
"<pool_id>:<listener_id>": {
"status": "UP",
"members": {
"<member_id>": "no check"
}
}
},
"ver": 2
}
""" """
session = db_api.get_session() session = db_api.get_session()

View File

@ -235,13 +235,14 @@ class ControllerWorker(base_taskflow.BaseTaskFlowEngine):
raise db_exceptions.NoResultFound raise db_exceptions.NoResultFound
load_balancer = listener.load_balancer load_balancer = listener.load_balancer
listeners = load_balancer.listeners
create_listener_tf = self._taskflow_load(self._listener_flows. create_listener_tf = self._taskflow_load(self._listener_flows.
get_create_listener_flow(), get_create_listener_flow(),
store={constants.LOADBALANCER: store={constants.LOADBALANCER:
load_balancer, load_balancer,
constants.LISTENERS: constants.LISTENERS:
[listener]}) listeners})
with tf_logging.DynamicLoggingListener(create_listener_tf, with tf_logging.DynamicLoggingListener(create_listener_tf,
log=LOG): log=LOG):
create_listener_tf.run() create_listener_tf.run()

View File

@ -470,7 +470,7 @@ class AmphoraFlows(object):
update_amps_subflow.add( update_amps_subflow.add(
amphora_driver_tasks.AmpListenersUpdate( amphora_driver_tasks.AmpListenersUpdate(
name=constants.AMP_LISTENER_UPDATE + '-' + str(amp_index), name=constants.AMP_LISTENER_UPDATE + '-' + str(amp_index),
requires=(constants.LISTENERS, constants.AMPHORAE), requires=(constants.LOADBALANCER, constants.AMPHORAE),
inject={constants.AMPHORA_INDEX: amp_index, inject={constants.AMPHORA_INDEX: amp_index,
constants.TIMEOUT_DICT: timeout_dict})) constants.TIMEOUT_DICT: timeout_dict}))
amp_index += 1 amp_index += 1
@ -514,8 +514,7 @@ class AmphoraFlows(object):
requires=constants.AMPHORA)) requires=constants.AMPHORA))
failover_amphora_flow.add(amphora_driver_tasks.ListenersStart( failover_amphora_flow.add(amphora_driver_tasks.ListenersStart(
requires=(constants.LOADBALANCER, constants.LISTENERS, requires=(constants.LOADBALANCER, constants.AMPHORA)))
constants.AMPHORA)))
failover_amphora_flow.add( failover_amphora_flow.add(
database_tasks.DisableAmphoraHealthMonitoring( database_tasks.DisableAmphoraHealthMonitoring(
rebind={constants.AMPHORA: constants.FAILED_AMPHORA}, rebind={constants.AMPHORA: constants.FAILED_AMPHORA},

View File

@ -37,7 +37,7 @@ class HealthMonitorFlows(object):
create_hm_flow.add(database_tasks.MarkHealthMonitorPendingCreateInDB( create_hm_flow.add(database_tasks.MarkHealthMonitorPendingCreateInDB(
requires=constants.HEALTH_MON)) requires=constants.HEALTH_MON))
create_hm_flow.add(amphora_driver_tasks.ListenersUpdate( create_hm_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
create_hm_flow.add(database_tasks.MarkHealthMonitorActiveInDB( create_hm_flow.add(database_tasks.MarkHealthMonitorActiveInDB(
requires=constants.HEALTH_MON)) requires=constants.HEALTH_MON))
create_hm_flow.add(database_tasks.MarkPoolActiveInDB( create_hm_flow.add(database_tasks.MarkPoolActiveInDB(
@ -63,7 +63,7 @@ class HealthMonitorFlows(object):
DeleteModelObject(rebind={constants.OBJECT: DeleteModelObject(rebind={constants.OBJECT:
constants.HEALTH_MON})) constants.HEALTH_MON}))
delete_hm_flow.add(amphora_driver_tasks.ListenersUpdate( delete_hm_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
delete_hm_flow.add(database_tasks.DeleteHealthMonitorInDB( delete_hm_flow.add(database_tasks.DeleteHealthMonitorInDB(
requires=constants.HEALTH_MON)) requires=constants.HEALTH_MON))
delete_hm_flow.add(database_tasks.DecrementHealthMonitorQuota( delete_hm_flow.add(database_tasks.DecrementHealthMonitorQuota(
@ -92,7 +92,7 @@ class HealthMonitorFlows(object):
update_hm_flow.add(database_tasks.MarkHealthMonitorPendingUpdateInDB( update_hm_flow.add(database_tasks.MarkHealthMonitorPendingUpdateInDB(
requires=constants.HEALTH_MON)) requires=constants.HEALTH_MON))
update_hm_flow.add(amphora_driver_tasks.ListenersUpdate( update_hm_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
update_hm_flow.add(database_tasks.UpdateHealthMonInDB( update_hm_flow.add(database_tasks.UpdateHealthMonInDB(
requires=[constants.HEALTH_MON, constants.UPDATE_DICT])) requires=[constants.HEALTH_MON, constants.UPDATE_DICT]))
update_hm_flow.add(database_tasks.MarkHealthMonitorActiveInDB( update_hm_flow.add(database_tasks.MarkHealthMonitorActiveInDB(

View File

@ -37,7 +37,7 @@ class L7PolicyFlows(object):
create_l7policy_flow.add(database_tasks.MarkL7PolicyPendingCreateInDB( create_l7policy_flow.add(database_tasks.MarkL7PolicyPendingCreateInDB(
requires=constants.L7POLICY)) requires=constants.L7POLICY))
create_l7policy_flow.add(amphora_driver_tasks.ListenersUpdate( create_l7policy_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
create_l7policy_flow.add(database_tasks.MarkL7PolicyActiveInDB( create_l7policy_flow.add(database_tasks.MarkL7PolicyActiveInDB(
requires=constants.L7POLICY)) requires=constants.L7POLICY))
create_l7policy_flow.add(database_tasks.MarkLBAndListenersActiveInDB( create_l7policy_flow.add(database_tasks.MarkLBAndListenersActiveInDB(
@ -60,7 +60,7 @@ class L7PolicyFlows(object):
delete_l7policy_flow.add(model_tasks.DeleteModelObject( delete_l7policy_flow.add(model_tasks.DeleteModelObject(
rebind={constants.OBJECT: constants.L7POLICY})) rebind={constants.OBJECT: constants.L7POLICY}))
delete_l7policy_flow.add(amphora_driver_tasks.ListenersUpdate( delete_l7policy_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
delete_l7policy_flow.add(database_tasks.DeleteL7PolicyInDB( delete_l7policy_flow.add(database_tasks.DeleteL7PolicyInDB(
requires=constants.L7POLICY)) requires=constants.L7POLICY))
delete_l7policy_flow.add(database_tasks.MarkLBAndListenersActiveInDB( delete_l7policy_flow.add(database_tasks.MarkLBAndListenersActiveInDB(
@ -81,7 +81,7 @@ class L7PolicyFlows(object):
update_l7policy_flow.add(database_tasks.MarkL7PolicyPendingUpdateInDB( update_l7policy_flow.add(database_tasks.MarkL7PolicyPendingUpdateInDB(
requires=constants.L7POLICY)) requires=constants.L7POLICY))
update_l7policy_flow.add(amphora_driver_tasks.ListenersUpdate( update_l7policy_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
update_l7policy_flow.add(database_tasks.UpdateL7PolicyInDB( update_l7policy_flow.add(database_tasks.UpdateL7PolicyInDB(
requires=[constants.L7POLICY, constants.UPDATE_DICT])) requires=[constants.L7POLICY, constants.UPDATE_DICT]))
update_l7policy_flow.add(database_tasks.MarkL7PolicyActiveInDB( update_l7policy_flow.add(database_tasks.MarkL7PolicyActiveInDB(

View File

@ -37,7 +37,7 @@ class L7RuleFlows(object):
create_l7rule_flow.add(database_tasks.MarkL7RulePendingCreateInDB( create_l7rule_flow.add(database_tasks.MarkL7RulePendingCreateInDB(
requires=constants.L7RULE)) requires=constants.L7RULE))
create_l7rule_flow.add(amphora_driver_tasks.ListenersUpdate( create_l7rule_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
create_l7rule_flow.add(database_tasks.MarkL7RuleActiveInDB( create_l7rule_flow.add(database_tasks.MarkL7RuleActiveInDB(
requires=constants.L7RULE)) requires=constants.L7RULE))
create_l7rule_flow.add(database_tasks.MarkL7PolicyActiveInDB( create_l7rule_flow.add(database_tasks.MarkL7PolicyActiveInDB(
@ -62,7 +62,7 @@ class L7RuleFlows(object):
delete_l7rule_flow.add(model_tasks.DeleteModelObject( delete_l7rule_flow.add(model_tasks.DeleteModelObject(
rebind={constants.OBJECT: constants.L7RULE})) rebind={constants.OBJECT: constants.L7RULE}))
delete_l7rule_flow.add(amphora_driver_tasks.ListenersUpdate( delete_l7rule_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
delete_l7rule_flow.add(database_tasks.DeleteL7RuleInDB( delete_l7rule_flow.add(database_tasks.DeleteL7RuleInDB(
requires=constants.L7RULE)) requires=constants.L7RULE))
delete_l7rule_flow.add(database_tasks.MarkL7PolicyActiveInDB( delete_l7rule_flow.add(database_tasks.MarkL7PolicyActiveInDB(
@ -85,7 +85,7 @@ class L7RuleFlows(object):
update_l7rule_flow.add(database_tasks.MarkL7RulePendingUpdateInDB( update_l7rule_flow.add(database_tasks.MarkL7RulePendingUpdateInDB(
requires=constants.L7RULE)) requires=constants.L7RULE))
update_l7rule_flow.add(amphora_driver_tasks.ListenersUpdate( update_l7rule_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
update_l7rule_flow.add(database_tasks.UpdateL7RuleInDB( update_l7rule_flow.add(database_tasks.UpdateL7RuleInDB(
requires=[constants.L7RULE, constants.UPDATE_DICT])) requires=[constants.L7RULE, constants.UPDATE_DICT]))
update_l7rule_flow.add(database_tasks.MarkL7RuleActiveInDB( update_l7rule_flow.add(database_tasks.MarkL7RuleActiveInDB(

View File

@ -33,7 +33,7 @@ class ListenerFlows(object):
create_listener_flow.add(lifecycle_tasks.ListenersToErrorOnRevertTask( create_listener_flow.add(lifecycle_tasks.ListenersToErrorOnRevertTask(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=[constants.LOADBALANCER, constants.LISTENERS]))
create_listener_flow.add(amphora_driver_tasks.ListenersUpdate( create_listener_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
create_listener_flow.add(network_tasks.UpdateVIP( create_listener_flow.add(network_tasks.UpdateVIP(
requires=constants.LOADBALANCER)) requires=constants.LOADBALANCER))
create_listener_flow.add(database_tasks. create_listener_flow.add(database_tasks.
@ -57,7 +57,7 @@ class ListenerFlows(object):
requires=constants.LOADBALANCER_ID, requires=constants.LOADBALANCER_ID,
provides=constants.LOADBALANCER)) provides=constants.LOADBALANCER))
create_all_listeners_flow.add(amphora_driver_tasks.ListenersUpdate( create_all_listeners_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
create_all_listeners_flow.add(network_tasks.UpdateVIP( create_all_listeners_flow.add(network_tasks.UpdateVIP(
requires=constants.LOADBALANCER)) requires=constants.LOADBALANCER))
return create_all_listeners_flow return create_all_listeners_flow
@ -71,7 +71,7 @@ class ListenerFlows(object):
delete_listener_flow.add(lifecycle_tasks.ListenerToErrorOnRevertTask( delete_listener_flow.add(lifecycle_tasks.ListenerToErrorOnRevertTask(
requires=constants.LISTENER)) requires=constants.LISTENER))
delete_listener_flow.add(amphora_driver_tasks.ListenerDelete( delete_listener_flow.add(amphora_driver_tasks.ListenerDelete(
requires=[constants.LOADBALANCER, constants.LISTENER])) requires=constants.LISTENER))
delete_listener_flow.add(network_tasks.UpdateVIPForDelete( delete_listener_flow.add(network_tasks.UpdateVIPForDelete(
requires=constants.LOADBALANCER)) requires=constants.LOADBALANCER))
delete_listener_flow.add(database_tasks.DeleteListenerInDB( delete_listener_flow.add(database_tasks.DeleteListenerInDB(
@ -115,7 +115,7 @@ class ListenerFlows(object):
update_listener_flow.add(lifecycle_tasks.ListenersToErrorOnRevertTask( update_listener_flow.add(lifecycle_tasks.ListenersToErrorOnRevertTask(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=[constants.LOADBALANCER, constants.LISTENERS]))
update_listener_flow.add(amphora_driver_tasks.ListenersUpdate( update_listener_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
update_listener_flow.add(database_tasks.UpdateListenerInDB( update_listener_flow.add(database_tasks.UpdateListenerInDB(
requires=[constants.LISTENER, constants.UPDATE_DICT])) requires=[constants.LISTENER, constants.UPDATE_DICT]))
update_listener_flow.add(database_tasks. update_listener_flow.add(database_tasks.

View File

@ -332,7 +332,7 @@ class LoadBalancerFlows(object):
update_LB_flow.add(network_tasks.ApplyQos( update_LB_flow.add(network_tasks.ApplyQos(
requires=(constants.LOADBALANCER, constants.UPDATE_DICT))) requires=(constants.LOADBALANCER, constants.UPDATE_DICT)))
update_LB_flow.add(amphora_driver_tasks.ListenersUpdate( update_LB_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
update_LB_flow.add(database_tasks.UpdateLoadbalancerInDB( update_LB_flow.add(database_tasks.UpdateLoadbalancerInDB(
requires=[constants.LOADBALANCER, constants.UPDATE_DICT])) requires=[constants.LOADBALANCER, constants.UPDATE_DICT]))
update_LB_flow.add(database_tasks.MarkLBActiveInDB( update_LB_flow.add(database_tasks.MarkLBActiveInDB(

View File

@ -48,7 +48,7 @@ class MemberFlows(object):
requires=(constants.LOADBALANCER, constants.ADDED_PORTS) requires=(constants.LOADBALANCER, constants.ADDED_PORTS)
)) ))
create_member_flow.add(amphora_driver_tasks.ListenersUpdate( create_member_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=(constants.LOADBALANCER, constants.LISTENERS))) requires=constants.LOADBALANCER))
create_member_flow.add(database_tasks.MarkMemberActiveInDB( create_member_flow.add(database_tasks.MarkMemberActiveInDB(
requires=constants.MEMBER)) requires=constants.MEMBER))
create_member_flow.add(database_tasks.MarkPoolActiveInDB( create_member_flow.add(database_tasks.MarkPoolActiveInDB(
@ -79,7 +79,7 @@ class MemberFlows(object):
delete_member_flow.add(database_tasks.DeleteMemberInDB( delete_member_flow.add(database_tasks.DeleteMemberInDB(
requires=constants.MEMBER)) requires=constants.MEMBER))
delete_member_flow.add(amphora_driver_tasks.ListenersUpdate( delete_member_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
delete_member_flow.add(database_tasks.DecrementMemberQuota( delete_member_flow.add(database_tasks.DecrementMemberQuota(
requires=constants.MEMBER)) requires=constants.MEMBER))
delete_member_flow.add(database_tasks.MarkPoolActiveInDB( delete_member_flow.add(database_tasks.MarkPoolActiveInDB(
@ -105,7 +105,7 @@ class MemberFlows(object):
update_member_flow.add(database_tasks.MarkMemberPendingUpdateInDB( update_member_flow.add(database_tasks.MarkMemberPendingUpdateInDB(
requires=constants.MEMBER)) requires=constants.MEMBER))
update_member_flow.add(amphora_driver_tasks.ListenersUpdate( update_member_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
update_member_flow.add(database_tasks.UpdateMemberInDB( update_member_flow.add(database_tasks.UpdateMemberInDB(
requires=[constants.MEMBER, constants.UPDATE_DICT])) requires=[constants.MEMBER, constants.UPDATE_DICT]))
update_member_flow.add(database_tasks.MarkMemberActiveInDB( update_member_flow.add(database_tasks.MarkMemberActiveInDB(
@ -195,7 +195,7 @@ class MemberFlows(object):
# Update the Listener (this makes the changes active on the Amp) # Update the Listener (this makes the changes active on the Amp)
batch_update_members_flow.add(amphora_driver_tasks.ListenersUpdate( batch_update_members_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=(constants.LOADBALANCER, constants.LISTENERS))) requires=constants.LOADBALANCER))
# Mark all the members ACTIVE here, then pool then LB/Listeners # Mark all the members ACTIVE here, then pool then LB/Listeners
batch_update_members_flow.add(unordered_members_active_flow) batch_update_members_flow.add(unordered_members_active_flow)

View File

@ -37,7 +37,7 @@ class PoolFlows(object):
create_pool_flow.add(database_tasks.MarkPoolPendingCreateInDB( create_pool_flow.add(database_tasks.MarkPoolPendingCreateInDB(
requires=constants.POOL)) requires=constants.POOL))
create_pool_flow.add(amphora_driver_tasks.ListenersUpdate( create_pool_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
create_pool_flow.add(database_tasks.MarkPoolActiveInDB( create_pool_flow.add(database_tasks.MarkPoolActiveInDB(
requires=constants.POOL)) requires=constants.POOL))
create_pool_flow.add(database_tasks.MarkLBAndListenersActiveInDB( create_pool_flow.add(database_tasks.MarkLBAndListenersActiveInDB(
@ -62,7 +62,7 @@ class PoolFlows(object):
delete_pool_flow.add(model_tasks.DeleteModelObject( delete_pool_flow.add(model_tasks.DeleteModelObject(
rebind={constants.OBJECT: constants.POOL})) rebind={constants.OBJECT: constants.POOL}))
delete_pool_flow.add(amphora_driver_tasks.ListenersUpdate( delete_pool_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
delete_pool_flow.add(database_tasks.DeletePoolInDB( delete_pool_flow.add(database_tasks.DeletePoolInDB(
requires=constants.POOL)) requires=constants.POOL))
delete_pool_flow.add(database_tasks.DecrementPoolQuota( delete_pool_flow.add(database_tasks.DecrementPoolQuota(
@ -116,7 +116,7 @@ class PoolFlows(object):
update_pool_flow.add(database_tasks.MarkPoolPendingUpdateInDB( update_pool_flow.add(database_tasks.MarkPoolPendingUpdateInDB(
requires=constants.POOL)) requires=constants.POOL))
update_pool_flow.add(amphora_driver_tasks.ListenersUpdate( update_pool_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
update_pool_flow.add(database_tasks.UpdatePoolInDB( update_pool_flow.add(database_tasks.UpdatePoolInDB(
requires=[constants.POOL, constants.UPDATE_DICT])) requires=[constants.POOL, constants.UPDATE_DICT]))
update_pool_flow.add(database_tasks.MarkPoolActiveInDB( update_pool_flow.add(database_tasks.MarkPoolActiveInDB(

View File

@ -52,13 +52,13 @@ class BaseAmphoraTask(task.Task):
class AmpListenersUpdate(BaseAmphoraTask): class AmpListenersUpdate(BaseAmphoraTask):
"""Task to update the listeners on one amphora.""" """Task to update the listeners on one amphora."""
def execute(self, listeners, amphora_index, amphorae, timeout_dict=()): def execute(self, loadbalancer, amphora_index, amphorae, timeout_dict=()):
# Note, we don't want this to cause a revert as it may be used # Note, we don't want this to cause a revert as it may be used
# in a failover flow with both amps failing. Skip it and let # in a failover flow with both amps failing. Skip it and let
# health manager fix it. # health manager fix it.
try: try:
self.amphora_driver.update_amphora_listeners( self.amphora_driver.update_amphora_listeners(
listeners, amphora_index, amphorae, timeout_dict) loadbalancer, amphorae[amphora_index], timeout_dict)
except Exception as e: except Exception as e:
amphora_id = amphorae[amphora_index].id amphora_id = amphorae[amphora_index].id
LOG.error('Failed to update listeners on amphora %s. Skipping ' LOG.error('Failed to update listeners on amphora %s. Skipping '
@ -71,11 +71,9 @@ class AmpListenersUpdate(BaseAmphoraTask):
class ListenersUpdate(BaseAmphoraTask): class ListenersUpdate(BaseAmphoraTask):
"""Task to update amphora with all specified listeners' configurations.""" """Task to update amphora with all specified listeners' configurations."""
def execute(self, loadbalancer, listeners): def execute(self, loadbalancer):
"""Execute updates per listener for an amphora.""" """Execute updates per listener for an amphora."""
for listener in listeners: self.amphora_driver.update(loadbalancer)
listener.load_balancer = loadbalancer
self.amphora_driver.update(listener, loadbalancer.vip)
def revert(self, loadbalancer, *args, **kwargs): def revert(self, loadbalancer, *args, **kwargs):
"""Handle failed listeners updates.""" """Handle failed listeners updates."""
@ -86,61 +84,30 @@ class ListenersUpdate(BaseAmphoraTask):
self.task_utils.mark_listener_prov_status_error(listener.id) self.task_utils.mark_listener_prov_status_error(listener.id)
class ListenerStop(BaseAmphoraTask):
"""Task to stop the listener on the vip."""
def execute(self, loadbalancer, listener):
"""Execute listener stop routines for an amphora."""
self.amphora_driver.stop(listener, loadbalancer.vip)
LOG.debug("Stopped the listener on the vip")
def revert(self, listener, *args, **kwargs):
"""Handle a failed listener stop."""
LOG.warning("Reverting listener stop.")
self.task_utils.mark_listener_prov_status_error(listener.id)
class ListenerStart(BaseAmphoraTask):
"""Task to start the listener on the vip."""
def execute(self, loadbalancer, listener):
"""Execute listener start routines for an amphora."""
self.amphora_driver.start(listener, loadbalancer.vip)
LOG.debug("Started the listener on the vip")
def revert(self, listener, *args, **kwargs):
"""Handle a failed listener start."""
LOG.warning("Reverting listener start.")
self.task_utils.mark_listener_prov_status_error(listener.id)
class ListenersStart(BaseAmphoraTask): class ListenersStart(BaseAmphoraTask):
"""Task to start all listeners on the vip.""" """Task to start all listeners on the vip."""
def execute(self, loadbalancer, listeners, amphora=None): def execute(self, loadbalancer, amphora=None):
"""Execute listener start routines for listeners on an amphora.""" """Execute listener start routines for listeners on an amphora."""
for listener in listeners: if loadbalancer.listeners:
self.amphora_driver.start(listener, loadbalancer.vip, amphora) self.amphora_driver.start(loadbalancer, amphora)
LOG.debug("Started the listeners on the vip") LOG.debug("Started the listeners on the vip")
def revert(self, listeners, *args, **kwargs): def revert(self, loadbalancer, *args, **kwargs):
"""Handle failed listeners starts.""" """Handle failed listeners starts."""
LOG.warning("Reverting listeners starts.") LOG.warning("Reverting listeners starts.")
for listener in listeners: for listener in loadbalancer.listeners:
self.task_utils.mark_listener_prov_status_error(listener.id) self.task_utils.mark_listener_prov_status_error(listener.id)
class ListenerDelete(BaseAmphoraTask): class ListenerDelete(BaseAmphoraTask):
"""Task to delete the listener on the vip.""" """Task to delete the listener on the vip."""
def execute(self, loadbalancer, listener): def execute(self, listener):
"""Execute listener delete routines for an amphora.""" """Execute listener delete routines for an amphora."""
self.amphora_driver.delete(listener, loadbalancer.vip) # TODO(rm_work): This is only relevant because of UDP listeners now.
self.amphora_driver.delete(listener)
LOG.debug("Deleted the listener on the vip") LOG.debug("Deleted the listener on the vip")
def revert(self, listener, *args, **kwargs): def revert(self, listener, *args, **kwargs):

View File

@ -235,13 +235,14 @@ class ControllerWorker(base_taskflow.BaseTaskFlowEngine):
raise db_exceptions.NoResultFound raise db_exceptions.NoResultFound
load_balancer = listener.load_balancer load_balancer = listener.load_balancer
listeners = load_balancer.listeners
create_listener_tf = self._taskflow_load(self._listener_flows. create_listener_tf = self._taskflow_load(self._listener_flows.
get_create_listener_flow(), get_create_listener_flow(),
store={constants.LOADBALANCER: store={constants.LOADBALANCER:
load_balancer, load_balancer,
constants.LISTENERS: constants.LISTENERS:
[listener]}) listeners})
with tf_logging.DynamicLoggingListener(create_listener_tf, with tf_logging.DynamicLoggingListener(create_listener_tf,
log=LOG): log=LOG):
create_listener_tf.run() create_listener_tf.run()

View File

@ -470,7 +470,7 @@ class AmphoraFlows(object):
update_amps_subflow.add( update_amps_subflow.add(
amphora_driver_tasks.AmpListenersUpdate( amphora_driver_tasks.AmpListenersUpdate(
name=constants.AMP_LISTENER_UPDATE + '-' + str(amp_index), name=constants.AMP_LISTENER_UPDATE + '-' + str(amp_index),
requires=(constants.LISTENERS, constants.AMPHORAE), requires=(constants.LOADBALANCER, constants.AMPHORAE),
inject={constants.AMPHORA_INDEX: amp_index, inject={constants.AMPHORA_INDEX: amp_index,
constants.TIMEOUT_DICT: timeout_dict})) constants.TIMEOUT_DICT: timeout_dict}))
amp_index += 1 amp_index += 1
@ -514,8 +514,7 @@ class AmphoraFlows(object):
requires=constants.AMPHORA)) requires=constants.AMPHORA))
failover_amphora_flow.add(amphora_driver_tasks.ListenersStart( failover_amphora_flow.add(amphora_driver_tasks.ListenersStart(
requires=(constants.LOADBALANCER, constants.LISTENERS, requires=(constants.LOADBALANCER, constants.AMPHORA)))
constants.AMPHORA)))
failover_amphora_flow.add( failover_amphora_flow.add(
database_tasks.DisableAmphoraHealthMonitoring( database_tasks.DisableAmphoraHealthMonitoring(
rebind={constants.AMPHORA: constants.FAILED_AMPHORA}, rebind={constants.AMPHORA: constants.FAILED_AMPHORA},

View File

@ -37,7 +37,7 @@ class HealthMonitorFlows(object):
create_hm_flow.add(database_tasks.MarkHealthMonitorPendingCreateInDB( create_hm_flow.add(database_tasks.MarkHealthMonitorPendingCreateInDB(
requires=constants.HEALTH_MON)) requires=constants.HEALTH_MON))
create_hm_flow.add(amphora_driver_tasks.ListenersUpdate( create_hm_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
create_hm_flow.add(database_tasks.MarkHealthMonitorActiveInDB( create_hm_flow.add(database_tasks.MarkHealthMonitorActiveInDB(
requires=constants.HEALTH_MON)) requires=constants.HEALTH_MON))
create_hm_flow.add(database_tasks.MarkPoolActiveInDB( create_hm_flow.add(database_tasks.MarkPoolActiveInDB(
@ -63,7 +63,7 @@ class HealthMonitorFlows(object):
DeleteModelObject(rebind={constants.OBJECT: DeleteModelObject(rebind={constants.OBJECT:
constants.HEALTH_MON})) constants.HEALTH_MON}))
delete_hm_flow.add(amphora_driver_tasks.ListenersUpdate( delete_hm_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
delete_hm_flow.add(database_tasks.DeleteHealthMonitorInDB( delete_hm_flow.add(database_tasks.DeleteHealthMonitorInDB(
requires=constants.HEALTH_MON)) requires=constants.HEALTH_MON))
delete_hm_flow.add(database_tasks.DecrementHealthMonitorQuota( delete_hm_flow.add(database_tasks.DecrementHealthMonitorQuota(
@ -92,7 +92,7 @@ class HealthMonitorFlows(object):
update_hm_flow.add(database_tasks.MarkHealthMonitorPendingUpdateInDB( update_hm_flow.add(database_tasks.MarkHealthMonitorPendingUpdateInDB(
requires=constants.HEALTH_MON)) requires=constants.HEALTH_MON))
update_hm_flow.add(amphora_driver_tasks.ListenersUpdate( update_hm_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
update_hm_flow.add(database_tasks.UpdateHealthMonInDB( update_hm_flow.add(database_tasks.UpdateHealthMonInDB(
requires=[constants.HEALTH_MON, constants.UPDATE_DICT])) requires=[constants.HEALTH_MON, constants.UPDATE_DICT]))
update_hm_flow.add(database_tasks.MarkHealthMonitorActiveInDB( update_hm_flow.add(database_tasks.MarkHealthMonitorActiveInDB(

View File

@ -37,7 +37,7 @@ class L7PolicyFlows(object):
create_l7policy_flow.add(database_tasks.MarkL7PolicyPendingCreateInDB( create_l7policy_flow.add(database_tasks.MarkL7PolicyPendingCreateInDB(
requires=constants.L7POLICY)) requires=constants.L7POLICY))
create_l7policy_flow.add(amphora_driver_tasks.ListenersUpdate( create_l7policy_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
create_l7policy_flow.add(database_tasks.MarkL7PolicyActiveInDB( create_l7policy_flow.add(database_tasks.MarkL7PolicyActiveInDB(
requires=constants.L7POLICY)) requires=constants.L7POLICY))
create_l7policy_flow.add(database_tasks.MarkLBAndListenersActiveInDB( create_l7policy_flow.add(database_tasks.MarkLBAndListenersActiveInDB(
@ -60,7 +60,7 @@ class L7PolicyFlows(object):
delete_l7policy_flow.add(model_tasks.DeleteModelObject( delete_l7policy_flow.add(model_tasks.DeleteModelObject(
rebind={constants.OBJECT: constants.L7POLICY})) rebind={constants.OBJECT: constants.L7POLICY}))
delete_l7policy_flow.add(amphora_driver_tasks.ListenersUpdate( delete_l7policy_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
delete_l7policy_flow.add(database_tasks.DeleteL7PolicyInDB( delete_l7policy_flow.add(database_tasks.DeleteL7PolicyInDB(
requires=constants.L7POLICY)) requires=constants.L7POLICY))
delete_l7policy_flow.add(database_tasks.MarkLBAndListenersActiveInDB( delete_l7policy_flow.add(database_tasks.MarkLBAndListenersActiveInDB(
@ -81,7 +81,7 @@ class L7PolicyFlows(object):
update_l7policy_flow.add(database_tasks.MarkL7PolicyPendingUpdateInDB( update_l7policy_flow.add(database_tasks.MarkL7PolicyPendingUpdateInDB(
requires=constants.L7POLICY)) requires=constants.L7POLICY))
update_l7policy_flow.add(amphora_driver_tasks.ListenersUpdate( update_l7policy_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
update_l7policy_flow.add(database_tasks.UpdateL7PolicyInDB( update_l7policy_flow.add(database_tasks.UpdateL7PolicyInDB(
requires=[constants.L7POLICY, constants.UPDATE_DICT])) requires=[constants.L7POLICY, constants.UPDATE_DICT]))
update_l7policy_flow.add(database_tasks.MarkL7PolicyActiveInDB( update_l7policy_flow.add(database_tasks.MarkL7PolicyActiveInDB(

View File

@ -37,7 +37,7 @@ class L7RuleFlows(object):
create_l7rule_flow.add(database_tasks.MarkL7RulePendingCreateInDB( create_l7rule_flow.add(database_tasks.MarkL7RulePendingCreateInDB(
requires=constants.L7RULE)) requires=constants.L7RULE))
create_l7rule_flow.add(amphora_driver_tasks.ListenersUpdate( create_l7rule_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
create_l7rule_flow.add(database_tasks.MarkL7RuleActiveInDB( create_l7rule_flow.add(database_tasks.MarkL7RuleActiveInDB(
requires=constants.L7RULE)) requires=constants.L7RULE))
create_l7rule_flow.add(database_tasks.MarkL7PolicyActiveInDB( create_l7rule_flow.add(database_tasks.MarkL7PolicyActiveInDB(
@ -62,7 +62,7 @@ class L7RuleFlows(object):
delete_l7rule_flow.add(model_tasks.DeleteModelObject( delete_l7rule_flow.add(model_tasks.DeleteModelObject(
rebind={constants.OBJECT: constants.L7RULE})) rebind={constants.OBJECT: constants.L7RULE}))
delete_l7rule_flow.add(amphora_driver_tasks.ListenersUpdate( delete_l7rule_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
delete_l7rule_flow.add(database_tasks.DeleteL7RuleInDB( delete_l7rule_flow.add(database_tasks.DeleteL7RuleInDB(
requires=constants.L7RULE)) requires=constants.L7RULE))
delete_l7rule_flow.add(database_tasks.MarkL7PolicyActiveInDB( delete_l7rule_flow.add(database_tasks.MarkL7PolicyActiveInDB(
@ -85,7 +85,7 @@ class L7RuleFlows(object):
update_l7rule_flow.add(database_tasks.MarkL7RulePendingUpdateInDB( update_l7rule_flow.add(database_tasks.MarkL7RulePendingUpdateInDB(
requires=constants.L7RULE)) requires=constants.L7RULE))
update_l7rule_flow.add(amphora_driver_tasks.ListenersUpdate( update_l7rule_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
update_l7rule_flow.add(database_tasks.UpdateL7RuleInDB( update_l7rule_flow.add(database_tasks.UpdateL7RuleInDB(
requires=[constants.L7RULE, constants.UPDATE_DICT])) requires=[constants.L7RULE, constants.UPDATE_DICT]))
update_l7rule_flow.add(database_tasks.MarkL7RuleActiveInDB( update_l7rule_flow.add(database_tasks.MarkL7RuleActiveInDB(

View File

@ -33,7 +33,7 @@ class ListenerFlows(object):
create_listener_flow.add(lifecycle_tasks.ListenersToErrorOnRevertTask( create_listener_flow.add(lifecycle_tasks.ListenersToErrorOnRevertTask(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=[constants.LOADBALANCER, constants.LISTENERS]))
create_listener_flow.add(amphora_driver_tasks.ListenersUpdate( create_listener_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
create_listener_flow.add(network_tasks.UpdateVIP( create_listener_flow.add(network_tasks.UpdateVIP(
requires=constants.LOADBALANCER)) requires=constants.LOADBALANCER))
create_listener_flow.add(database_tasks. create_listener_flow.add(database_tasks.
@ -57,7 +57,7 @@ class ListenerFlows(object):
requires=constants.LOADBALANCER_ID, requires=constants.LOADBALANCER_ID,
provides=constants.LOADBALANCER)) provides=constants.LOADBALANCER))
create_all_listeners_flow.add(amphora_driver_tasks.ListenersUpdate( create_all_listeners_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
create_all_listeners_flow.add(network_tasks.UpdateVIP( create_all_listeners_flow.add(network_tasks.UpdateVIP(
requires=constants.LOADBALANCER)) requires=constants.LOADBALANCER))
return create_all_listeners_flow return create_all_listeners_flow
@ -71,7 +71,7 @@ class ListenerFlows(object):
delete_listener_flow.add(lifecycle_tasks.ListenerToErrorOnRevertTask( delete_listener_flow.add(lifecycle_tasks.ListenerToErrorOnRevertTask(
requires=constants.LISTENER)) requires=constants.LISTENER))
delete_listener_flow.add(amphora_driver_tasks.ListenerDelete( delete_listener_flow.add(amphora_driver_tasks.ListenerDelete(
requires=[constants.LOADBALANCER, constants.LISTENER])) requires=constants.LISTENER))
delete_listener_flow.add(network_tasks.UpdateVIPForDelete( delete_listener_flow.add(network_tasks.UpdateVIPForDelete(
requires=constants.LOADBALANCER)) requires=constants.LOADBALANCER))
delete_listener_flow.add(database_tasks.DeleteListenerInDB( delete_listener_flow.add(database_tasks.DeleteListenerInDB(
@ -115,7 +115,7 @@ class ListenerFlows(object):
update_listener_flow.add(lifecycle_tasks.ListenersToErrorOnRevertTask( update_listener_flow.add(lifecycle_tasks.ListenersToErrorOnRevertTask(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=[constants.LOADBALANCER, constants.LISTENERS]))
update_listener_flow.add(amphora_driver_tasks.ListenersUpdate( update_listener_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
update_listener_flow.add(database_tasks.UpdateListenerInDB( update_listener_flow.add(database_tasks.UpdateListenerInDB(
requires=[constants.LISTENER, constants.UPDATE_DICT])) requires=[constants.LISTENER, constants.UPDATE_DICT]))
update_listener_flow.add(database_tasks. update_listener_flow.add(database_tasks.

View File

@ -332,7 +332,7 @@ class LoadBalancerFlows(object):
update_LB_flow.add(network_tasks.ApplyQos( update_LB_flow.add(network_tasks.ApplyQos(
requires=(constants.LOADBALANCER, constants.UPDATE_DICT))) requires=(constants.LOADBALANCER, constants.UPDATE_DICT)))
update_LB_flow.add(amphora_driver_tasks.ListenersUpdate( update_LB_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
update_LB_flow.add(database_tasks.UpdateLoadbalancerInDB( update_LB_flow.add(database_tasks.UpdateLoadbalancerInDB(
requires=[constants.LOADBALANCER, constants.UPDATE_DICT])) requires=[constants.LOADBALANCER, constants.UPDATE_DICT]))
update_LB_flow.add(database_tasks.MarkLBActiveInDB( update_LB_flow.add(database_tasks.MarkLBActiveInDB(

View File

@ -48,7 +48,7 @@ class MemberFlows(object):
requires=(constants.LOADBALANCER, constants.ADDED_PORTS) requires=(constants.LOADBALANCER, constants.ADDED_PORTS)
)) ))
create_member_flow.add(amphora_driver_tasks.ListenersUpdate( create_member_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=(constants.LOADBALANCER, constants.LISTENERS))) requires=constants.LOADBALANCER))
create_member_flow.add(database_tasks.MarkMemberActiveInDB( create_member_flow.add(database_tasks.MarkMemberActiveInDB(
requires=constants.MEMBER)) requires=constants.MEMBER))
create_member_flow.add(database_tasks.MarkPoolActiveInDB( create_member_flow.add(database_tasks.MarkPoolActiveInDB(
@ -79,7 +79,7 @@ class MemberFlows(object):
delete_member_flow.add(database_tasks.DeleteMemberInDB( delete_member_flow.add(database_tasks.DeleteMemberInDB(
requires=constants.MEMBER)) requires=constants.MEMBER))
delete_member_flow.add(amphora_driver_tasks.ListenersUpdate( delete_member_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
delete_member_flow.add(database_tasks.DecrementMemberQuota( delete_member_flow.add(database_tasks.DecrementMemberQuota(
requires=constants.MEMBER)) requires=constants.MEMBER))
delete_member_flow.add(database_tasks.MarkPoolActiveInDB( delete_member_flow.add(database_tasks.MarkPoolActiveInDB(
@ -105,7 +105,7 @@ class MemberFlows(object):
update_member_flow.add(database_tasks.MarkMemberPendingUpdateInDB( update_member_flow.add(database_tasks.MarkMemberPendingUpdateInDB(
requires=constants.MEMBER)) requires=constants.MEMBER))
update_member_flow.add(amphora_driver_tasks.ListenersUpdate( update_member_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
update_member_flow.add(database_tasks.UpdateMemberInDB( update_member_flow.add(database_tasks.UpdateMemberInDB(
requires=[constants.MEMBER, constants.UPDATE_DICT])) requires=[constants.MEMBER, constants.UPDATE_DICT]))
update_member_flow.add(database_tasks.MarkMemberActiveInDB( update_member_flow.add(database_tasks.MarkMemberActiveInDB(
@ -195,7 +195,7 @@ class MemberFlows(object):
# Update the Listener (this makes the changes active on the Amp) # Update the Listener (this makes the changes active on the Amp)
batch_update_members_flow.add(amphora_driver_tasks.ListenersUpdate( batch_update_members_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=(constants.LOADBALANCER, constants.LISTENERS))) requires=constants.LOADBALANCER))
# Mark all the members ACTIVE here, then pool then LB/Listeners # Mark all the members ACTIVE here, then pool then LB/Listeners
batch_update_members_flow.add(unordered_members_active_flow) batch_update_members_flow.add(unordered_members_active_flow)

View File

@ -37,7 +37,7 @@ class PoolFlows(object):
create_pool_flow.add(database_tasks.MarkPoolPendingCreateInDB( create_pool_flow.add(database_tasks.MarkPoolPendingCreateInDB(
requires=constants.POOL)) requires=constants.POOL))
create_pool_flow.add(amphora_driver_tasks.ListenersUpdate( create_pool_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
create_pool_flow.add(database_tasks.MarkPoolActiveInDB( create_pool_flow.add(database_tasks.MarkPoolActiveInDB(
requires=constants.POOL)) requires=constants.POOL))
create_pool_flow.add(database_tasks.MarkLBAndListenersActiveInDB( create_pool_flow.add(database_tasks.MarkLBAndListenersActiveInDB(
@ -62,7 +62,7 @@ class PoolFlows(object):
delete_pool_flow.add(model_tasks.DeleteModelObject( delete_pool_flow.add(model_tasks.DeleteModelObject(
rebind={constants.OBJECT: constants.POOL})) rebind={constants.OBJECT: constants.POOL}))
delete_pool_flow.add(amphora_driver_tasks.ListenersUpdate( delete_pool_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
delete_pool_flow.add(database_tasks.DeletePoolInDB( delete_pool_flow.add(database_tasks.DeletePoolInDB(
requires=constants.POOL)) requires=constants.POOL))
delete_pool_flow.add(database_tasks.DecrementPoolQuota( delete_pool_flow.add(database_tasks.DecrementPoolQuota(
@ -116,7 +116,7 @@ class PoolFlows(object):
update_pool_flow.add(database_tasks.MarkPoolPendingUpdateInDB( update_pool_flow.add(database_tasks.MarkPoolPendingUpdateInDB(
requires=constants.POOL)) requires=constants.POOL))
update_pool_flow.add(amphora_driver_tasks.ListenersUpdate( update_pool_flow.add(amphora_driver_tasks.ListenersUpdate(
requires=[constants.LOADBALANCER, constants.LISTENERS])) requires=constants.LOADBALANCER))
update_pool_flow.add(database_tasks.UpdatePoolInDB( update_pool_flow.add(database_tasks.UpdatePoolInDB(
requires=[constants.POOL, constants.UPDATE_DICT])) requires=[constants.POOL, constants.UPDATE_DICT]))
update_pool_flow.add(database_tasks.MarkPoolActiveInDB( update_pool_flow.add(database_tasks.MarkPoolActiveInDB(

View File

@ -52,13 +52,13 @@ class BaseAmphoraTask(task.Task):
class AmpListenersUpdate(BaseAmphoraTask): class AmpListenersUpdate(BaseAmphoraTask):
"""Task to update the listeners on one amphora.""" """Task to update the listeners on one amphora."""
def execute(self, listeners, amphora_index, amphorae, timeout_dict=()): def execute(self, loadbalancer, amphora_index, amphorae, timeout_dict=()):
# Note, we don't want this to cause a revert as it may be used # Note, we don't want this to cause a revert as it may be used
# in a failover flow with both amps failing. Skip it and let # in a failover flow with both amps failing. Skip it and let
# health manager fix it. # health manager fix it.
try: try:
self.amphora_driver.update_amphora_listeners( self.amphora_driver.update_amphora_listeners(
listeners, amphora_index, amphorae, timeout_dict) loadbalancer, amphorae[amphora_index], timeout_dict)
except Exception as e: except Exception as e:
amphora_id = amphorae[amphora_index].id amphora_id = amphorae[amphora_index].id
LOG.error('Failed to update listeners on amphora %s. Skipping ' LOG.error('Failed to update listeners on amphora %s. Skipping '
@ -71,11 +71,9 @@ class AmpListenersUpdate(BaseAmphoraTask):
class ListenersUpdate(BaseAmphoraTask): class ListenersUpdate(BaseAmphoraTask):
"""Task to update amphora with all specified listeners' configurations.""" """Task to update amphora with all specified listeners' configurations."""
def execute(self, loadbalancer, listeners): def execute(self, loadbalancer):
"""Execute updates per listener for an amphora.""" """Execute updates per listener for an amphora."""
for listener in listeners: self.amphora_driver.update(loadbalancer)
listener.load_balancer = loadbalancer
self.amphora_driver.update(listener, loadbalancer.vip)
def revert(self, loadbalancer, *args, **kwargs): def revert(self, loadbalancer, *args, **kwargs):
"""Handle failed listeners updates.""" """Handle failed listeners updates."""
@ -86,61 +84,30 @@ class ListenersUpdate(BaseAmphoraTask):
self.task_utils.mark_listener_prov_status_error(listener.id) self.task_utils.mark_listener_prov_status_error(listener.id)
class ListenerStop(BaseAmphoraTask):
"""Task to stop the listener on the vip."""
def execute(self, loadbalancer, listener):
"""Execute listener stop routines for an amphora."""
self.amphora_driver.stop(listener, loadbalancer.vip)
LOG.debug("Stopped the listener on the vip")
def revert(self, listener, *args, **kwargs):
"""Handle a failed listener stop."""
LOG.warning("Reverting listener stop.")
self.task_utils.mark_listener_prov_status_error(listener.id)
class ListenerStart(BaseAmphoraTask):
"""Task to start the listener on the vip."""
def execute(self, loadbalancer, listener):
"""Execute listener start routines for an amphora."""
self.amphora_driver.start(listener, loadbalancer.vip)
LOG.debug("Started the listener on the vip")
def revert(self, listener, *args, **kwargs):
"""Handle a failed listener start."""
LOG.warning("Reverting listener start.")
self.task_utils.mark_listener_prov_status_error(listener.id)
class ListenersStart(BaseAmphoraTask): class ListenersStart(BaseAmphoraTask):
"""Task to start all listeners on the vip.""" """Task to start all listeners on the vip."""
def execute(self, loadbalancer, listeners, amphora=None): def execute(self, loadbalancer, amphora=None):
"""Execute listener start routines for listeners on an amphora.""" """Execute listener start routines for listeners on an amphora."""
for listener in listeners: if loadbalancer.listeners:
self.amphora_driver.start(listener, loadbalancer.vip, amphora) self.amphora_driver.start(loadbalancer, amphora)
LOG.debug("Started the listeners on the vip") LOG.debug("Started the listeners on the vip")
def revert(self, listeners, *args, **kwargs): def revert(self, loadbalancer, *args, **kwargs):
"""Handle failed listeners starts.""" """Handle failed listeners starts."""
LOG.warning("Reverting listeners starts.") LOG.warning("Reverting listeners starts.")
for listener in listeners: for listener in loadbalancer.listeners:
self.task_utils.mark_listener_prov_status_error(listener.id) self.task_utils.mark_listener_prov_status_error(listener.id)
class ListenerDelete(BaseAmphoraTask): class ListenerDelete(BaseAmphoraTask):
"""Task to delete the listener on the vip.""" """Task to delete the listener on the vip."""
def execute(self, loadbalancer, listener): def execute(self, listener):
"""Execute listener delete routines for an amphora.""" """Execute listener delete routines for an amphora."""
self.amphora_driver.delete(listener, loadbalancer.vip) # TODO(rm_work): This is only relevant because of UDP listeners now.
self.amphora_driver.delete(listener)
LOG.debug("Deleted the listener on the vip") LOG.debug("Deleted the listener on the vip")
def revert(self, listener, *args, **kwargs): def revert(self, listener, *args, **kwargs):

View File

@ -1253,6 +1253,7 @@ class AmphoraRepository(BaseRepository):
"load_balancer.operating_status AS lb_op_status, " "load_balancer.operating_status AS lb_op_status, "
"listener.id AS list_id, " "listener.id AS list_id, "
"listener.operating_status AS list_op_status, " "listener.operating_status AS list_op_status, "
"listener.enabled AS list_enabled, "
"listener.protocol AS list_protocol, " "listener.protocol AS list_protocol, "
"pool.id AS pool_id, " "pool.id AS pool_id, "
"pool.operating_status AS pool_op_status, " "pool.operating_status AS pool_op_status, "
@ -1278,7 +1279,8 @@ class AmphoraRepository(BaseRepository):
lb['operating_status'] = row['lb_op_status'] lb['operating_status'] = row['lb_op_status']
if row['list_id'] and row['list_id'] not in listeners: if row['list_id'] and row['list_id'] not in listeners:
listener = {'operating_status': row['list_op_status'], listener = {'operating_status': row['list_op_status'],
'protocol': row['list_protocol']} 'protocol': row['list_protocol'],
'enabled': row['list_enabled']}
listeners[row['list_id']] = listener listeners[row['list_id']] = listener
if row['pool_id']: if row['pool_id']:
if row['pool_id'] in pools and row['member_id']: if row['pool_id'] in pools and row['member_id']:

View File

@ -18,7 +18,6 @@ import subprocess
import flask import flask
import mock import mock
from werkzeug import exceptions
from oslo_utils import uuidutils from oslo_utils import uuidutils
@ -315,67 +314,6 @@ class KeepalivedLvsTestCase(base.TestCase):
'start') 'start')
self.assertEqual(500, res.status_code) self.assertEqual(500, res.status_code)
@mock.patch('octavia.amphorae.backends.utils.keepalivedlvs_query.'
'get_listener_realserver_mapping')
@mock.patch('subprocess.check_output', return_value=PROC_CONTENT)
@mock.patch('os.path.exists')
def test_get_udp_listener_status(self, m_exist, m_check_output,
mget_mapping):
mget_mapping.return_value = (
True, {'10.0.0.99:82': {'status': 'UP',
'Weight': '13',
'InActConn': '0',
'ActiveConn': '0'},
'10.0.0.98:82': {'status': 'UP',
'Weight': '13',
'InActConn': '0',
'ActiveConn': '0'}})
pid_path = ('/var/lib/octavia/lvs/octavia-'
'keepalivedlvs-%s.pid' % self.FAKE_ID)
self.useFixture(test_utils.OpenFixture(pid_path,
self.NORMAL_PID_CONTENT))
cfg_path = ('/var/lib/octavia/lvs/octavia-'
'keepalivedlvs-%s.conf' % self.FAKE_ID)
self.useFixture(test_utils.OpenFixture(cfg_path,
self.NORMAL_CFG_CONTENT))
m_exist.return_value = True
expected = {'status': 'ACTIVE',
'pools': [{'lvs': {
'members': {self.MEMBER_ID1: 'UP',
self.MEMBER_ID2: 'UP'},
'status': 'UP',
'uuid': self.POOL_ID}}],
'type': 'UDP', 'uuid': self.FAKE_ID}
res = self.test_keepalivedlvs.get_udp_listener_status(self.FAKE_ID)
self.assertEqual(200, res.status_code)
self.assertEqual(expected, res.json)
@mock.patch('os.path.exists')
def test_get_udp_listener_status_no_exists(self, m_exist):
m_exist.return_value = False
self.assertRaises(exceptions.HTTPException,
self.test_keepalivedlvs.get_udp_listener_status,
self.FAKE_ID)
@mock.patch('os.path.exists')
def test_get_udp_listener_status_offline_status(self, m_exist):
m_exist.return_value = True
pid_path = ('/var/lib/octavia/lvs/octavia-'
'keepalivedlvs-%s.pid' % self.FAKE_ID)
self.useFixture(test_utils.OpenFixture(pid_path,
self.NORMAL_PID_CONTENT))
cfg_path = ('/var/lib/octavia/lvs/octavia-'
'keepalivedlvs-%s.conf' % self.FAKE_ID)
self.useFixture(test_utils.OpenFixture(cfg_path, 'NO VS CONFIG'))
expected = {'status': 'OFFLINE',
'type': 'UDP',
'uuid': self.FAKE_ID}
res = self.test_keepalivedlvs.get_udp_listener_status(self.FAKE_ID)
self.assertEqual(200, res.status_code)
self.assertEqual(expected, res.json)
@mock.patch('octavia.amphorae.backends.agent.api_server.util.' @mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'get_udp_listeners', return_value=[LISTENER_ID]) 'get_udp_listeners', return_value=[LISTENER_ID])
@mock.patch('octavia.amphorae.backends.agent.api_server.util.' @mock.patch('octavia.amphorae.backends.agent.api_server.util.'

View File

@ -118,11 +118,11 @@ class TestServerTestCase(base.TestCase):
mock_distro_id.return_value = distro mock_distro_id.return_value = distro
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.put('/' + api_server.VERSION + rv = self.ubuntu_app.put('/' + api_server.VERSION +
'/listeners/amp_123/123/haproxy', '/loadbalancer/amp_123/123/haproxy',
data='test') data='test')
elif distro == consts.CENTOS: elif distro == consts.CENTOS:
rv = self.centos_app.put('/' + api_server.VERSION + rv = self.centos_app.put('/' + api_server.VERSION +
'/listeners/amp_123/123/haproxy', '/loadbalancer/amp_123/123/haproxy',
data='test') data='test')
mode = stat.S_IRUSR | stat.S_IWUSR mode = stat.S_IRUSR | stat.S_IWUSR
mock_open.assert_called_with(file_name, flags, mode) mock_open.assert_called_with(file_name, flags, mode)
@ -159,11 +159,11 @@ class TestServerTestCase(base.TestCase):
mock_distro_id.return_value = distro mock_distro_id.return_value = distro
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.put('/' + api_server.VERSION + rv = self.ubuntu_app.put('/' + api_server.VERSION +
'/listeners/amp_123/123/haproxy', '/loadbalancer/amp_123/123/haproxy',
data='test') data='test')
elif distro == consts.CENTOS: elif distro == consts.CENTOS:
rv = self.centos_app.put('/' + api_server.VERSION + rv = self.centos_app.put('/' + api_server.VERSION +
'/listeners/amp_123/123/haproxy', '/loadbalancer/amp_123/123/haproxy',
data='test') data='test')
self.assertEqual(500, rv.status_code) self.assertEqual(500, rv.status_code)
@ -188,11 +188,11 @@ class TestServerTestCase(base.TestCase):
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.put('/' + api_server.VERSION + rv = self.ubuntu_app.put('/' + api_server.VERSION +
'/listeners/amp_123/123/haproxy', '/loadbalancer/amp_123/123/haproxy',
data='test') data='test')
elif distro == consts.CENTOS: elif distro == consts.CENTOS:
rv = self.centos_app.put('/' + api_server.VERSION + rv = self.centos_app.put('/' + api_server.VERSION +
'/listeners/amp_123/123/haproxy', '/loadbalancer/amp_123/123/haproxy',
data='test') data='test')
self.assertEqual(202, rv.status_code) self.assertEqual(202, rv.status_code)
@ -220,11 +220,11 @@ class TestServerTestCase(base.TestCase):
mock_distro_id.return_value = distro mock_distro_id.return_value = distro
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.put('/' + api_server.VERSION + rv = self.ubuntu_app.put('/' + api_server.VERSION +
'/listeners/amp_123/123/haproxy', '/loadbalancer/amp_123/123/haproxy',
data='test') data='test')
elif distro == consts.CENTOS: elif distro == consts.CENTOS:
rv = self.centos_app.put('/' + api_server.VERSION + rv = self.centos_app.put('/' + api_server.VERSION +
'/listeners/amp_123/123/haproxy', '/loadbalancer/amp_123/123/haproxy',
data='test') data='test')
self.assertEqual(400, rv.status_code) self.assertEqual(400, rv.status_code)
self.assertEqual( self.assertEqual(
@ -255,11 +255,11 @@ class TestServerTestCase(base.TestCase):
mock_distro_id.return_value = distro mock_distro_id.return_value = distro
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.put('/' + api_server.VERSION + rv = self.ubuntu_app.put('/' + api_server.VERSION +
'/listeners/amp_123/123/haproxy', '/loadbalancer/amp_123/123/haproxy',
data='test') data='test')
elif distro == consts.CENTOS: elif distro == consts.CENTOS:
rv = self.ubuntu_app.put('/' + api_server.VERSION + rv = self.ubuntu_app.put('/' + api_server.VERSION +
'/listeners/amp_123/123/haproxy', '/loadbalancer/amp_123/123/haproxy',
data='test') data='test')
self.assertEqual(500, rv.status_code) self.assertEqual(500, rv.status_code)
@ -269,45 +269,49 @@ class TestServerTestCase(base.TestCase):
def test_centos_start(self): def test_centos_start(self):
self._test_start(consts.CENTOS) self._test_start(consts.CENTOS)
@mock.patch('os.listdir')
@mock.patch('os.path.exists') @mock.patch('os.path.exists')
@mock.patch('octavia.amphorae.backends.agent.api_server.listener.Listener.' @mock.patch('octavia.amphorae.backends.agent.api_server.loadbalancer.'
'vrrp_check_script_update') 'Loadbalancer.vrrp_check_script_update')
@mock.patch('subprocess.check_output') @mock.patch('subprocess.check_output')
def _test_start(self, distro, mock_subprocess, mock_vrrp, mock_exists): def _test_start(self, distro, mock_subprocess, mock_vrrp, mock_exists,
mock_listdir):
self.assertIn(distro, [consts.UBUNTU, consts.CENTOS]) self.assertIn(distro, [consts.UBUNTU, consts.CENTOS])
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.put('/' + api_server.VERSION + rv = self.ubuntu_app.put('/' + api_server.VERSION +
'/listeners/123/error') '/loadbalancer/123/error')
elif distro == consts.CENTOS: elif distro == consts.CENTOS:
rv = self.centos_app.put('/' + api_server.VERSION + rv = self.centos_app.put('/' + api_server.VERSION +
'/listeners/123/error') '/loadbalancer/123/error')
self.assertEqual(400, rv.status_code) self.assertEqual(400, rv.status_code)
self.assertEqual( self.assertEqual(
{'message': 'Invalid Request', {'message': 'Invalid Request',
'details': 'Unknown action: error', }, 'details': 'Unknown action: error', },
jsonutils.loads(rv.data.decode('utf-8'))) jsonutils.loads(rv.data.decode('utf-8')))
mock_exists.reset_mock()
mock_exists.return_value = False mock_exists.return_value = False
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.put('/' + api_server.VERSION + rv = self.ubuntu_app.put('/' + api_server.VERSION +
'/listeners/123/start') '/loadbalancer/123/start')
elif distro == consts.CENTOS: elif distro == consts.CENTOS:
rv = self.centos_app.put('/' + api_server.VERSION + rv = self.centos_app.put('/' + api_server.VERSION +
'/listeners/123/start') '/loadbalancer/123/start')
self.assertEqual(404, rv.status_code) self.assertEqual(404, rv.status_code)
self.assertEqual( self.assertEqual(
{'message': 'Listener Not Found', {'message': 'Loadbalancer Not Found',
'details': 'No listener with UUID: 123'}, 'details': 'No loadbalancer with UUID: 123'},
jsonutils.loads(rv.data.decode('utf-8'))) jsonutils.loads(rv.data.decode('utf-8')))
mock_exists.assert_called_with('/var/lib/octavia/123/haproxy.cfg') mock_exists.assert_called_with('/var/lib/octavia')
mock_exists.return_value = True mock_exists.return_value = True
mock_listdir.return_value = ['123']
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.put('/' + api_server.VERSION + rv = self.ubuntu_app.put('/' + api_server.VERSION +
'/listeners/123/start') '/loadbalancer/123/start')
elif distro == consts.CENTOS: elif distro == consts.CENTOS:
rv = self.centos_app.put('/' + api_server.VERSION + rv = self.centos_app.put('/' + api_server.VERSION +
'/listeners/123/start') '/loadbalancer/123/start')
self.assertEqual(202, rv.status_code) self.assertEqual(202, rv.status_code)
self.assertEqual( self.assertEqual(
{'message': 'OK', {'message': 'OK',
@ -322,10 +326,10 @@ class TestServerTestCase(base.TestCase):
7, 'test', RANDOM_ERROR) 7, 'test', RANDOM_ERROR)
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.put('/' + api_server.VERSION + rv = self.ubuntu_app.put('/' + api_server.VERSION +
'/listeners/123/start') '/loadbalancer/123/start')
elif distro == consts.CENTOS: elif distro == consts.CENTOS:
rv = self.centos_app.put('/' + api_server.VERSION + rv = self.centos_app.put('/' + api_server.VERSION +
'/listeners/123/start') '/loadbalancer/123/start')
self.assertEqual(500, rv.status_code) self.assertEqual(500, rv.status_code)
self.assertEqual( self.assertEqual(
{ {
@ -341,26 +345,28 @@ class TestServerTestCase(base.TestCase):
def test_centos_reload(self): def test_centos_reload(self):
self._test_reload(consts.CENTOS) self._test_reload(consts.CENTOS)
@mock.patch('os.listdir')
@mock.patch('os.path.exists') @mock.patch('os.path.exists')
@mock.patch('octavia.amphorae.backends.agent.api_server.listener.Listener.' @mock.patch('octavia.amphorae.backends.agent.api_server.loadbalancer.'
'vrrp_check_script_update') 'Loadbalancer.vrrp_check_script_update')
@mock.patch('octavia.amphorae.backends.agent.api_server.listener.Listener.' @mock.patch('octavia.amphorae.backends.agent.api_server.loadbalancer.'
'_check_haproxy_status') 'Loadbalancer._check_haproxy_status')
@mock.patch('subprocess.check_output') @mock.patch('subprocess.check_output')
def _test_reload(self, distro, mock_subprocess, mock_haproxy_status, def _test_reload(self, distro, mock_subprocess, mock_haproxy_status,
mock_vrrp, mock_exists): mock_vrrp, mock_exists, mock_listdir):
self.assertIn(distro, [consts.UBUNTU, consts.CENTOS]) self.assertIn(distro, [consts.UBUNTU, consts.CENTOS])
# Process running so reload # Process running so reload
mock_exists.return_value = True mock_exists.return_value = True
mock_listdir.return_value = ['123']
mock_haproxy_status.return_value = consts.ACTIVE mock_haproxy_status.return_value = consts.ACTIVE
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.put('/' + api_server.VERSION + rv = self.ubuntu_app.put('/' + api_server.VERSION +
'/listeners/123/reload') '/loadbalancer/123/reload')
elif distro == consts.CENTOS: elif distro == consts.CENTOS:
rv = self.centos_app.put('/' + api_server.VERSION + rv = self.centos_app.put('/' + api_server.VERSION +
'/listeners/123/reload') '/loadbalancer/123/reload')
self.assertEqual(202, rv.status_code) self.assertEqual(202, rv.status_code)
self.assertEqual( self.assertEqual(
{'message': 'OK', {'message': 'OK',
@ -374,10 +380,10 @@ class TestServerTestCase(base.TestCase):
mock_haproxy_status.return_value = consts.OFFLINE mock_haproxy_status.return_value = consts.OFFLINE
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.put('/' + api_server.VERSION + rv = self.ubuntu_app.put('/' + api_server.VERSION +
'/listeners/123/reload') '/loadbalancer/123/reload')
elif distro == consts.CENTOS: elif distro == consts.CENTOS:
rv = self.centos_app.put('/' + api_server.VERSION + rv = self.centos_app.put('/' + api_server.VERSION +
'/listeners/123/reload') '/loadbalancer/123/reload')
self.assertEqual(202, rv.status_code) self.assertEqual(202, rv.status_code)
self.assertEqual( self.assertEqual(
{'message': 'OK', {'message': 'OK',
@ -411,13 +417,13 @@ class TestServerTestCase(base.TestCase):
self.assertEqual(200, rv.status_code) self.assertEqual(200, rv.status_code)
self.assertEqual(dict( self.assertEqual(dict(
api_version='0.5', api_version='1.0',
haproxy_version='9.9.99-9', haproxy_version='9.9.99-9',
hostname='test-host'), hostname='test-host'),
jsonutils.loads(rv.data.decode('utf-8'))) jsonutils.loads(rv.data.decode('utf-8')))
@mock.patch('octavia.amphorae.backends.agent.api_server.util.' @mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'get_listener_protocol', return_value='TCP') 'get_protocol_for_lb_object', return_value='TCP')
@mock.patch('octavia.amphorae.backends.agent.api_server.util.' @mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'get_os_init_system', return_value=consts.INIT_SYSTEMD) 'get_os_init_system', return_value=consts.INIT_SYSTEMD)
def test_delete_ubuntu_listener_systemd(self, mock_init_system, def test_delete_ubuntu_listener_systemd(self, mock_init_system,
@ -426,7 +432,7 @@ class TestServerTestCase(base.TestCase):
mock_init_system) mock_init_system)
@mock.patch('octavia.amphorae.backends.agent.api_server.util.' @mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'get_listener_protocol', return_value='TCP') 'get_protocol_for_lb_object', return_value='TCP')
@mock.patch('octavia.amphorae.backends.agent.api_server.util.' @mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'get_os_init_system', return_value=consts.INIT_SYSTEMD) 'get_os_init_system', return_value=consts.INIT_SYSTEMD)
def test_delete_centos_listener_systemd(self, mock_init_system, def test_delete_centos_listener_systemd(self, mock_init_system,
@ -435,7 +441,7 @@ class TestServerTestCase(base.TestCase):
mock_init_system) mock_init_system)
@mock.patch('octavia.amphorae.backends.agent.api_server.util.' @mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'get_listener_protocol', return_value='TCP') 'get_protocol_for_lb_object', return_value='TCP')
@mock.patch('octavia.amphorae.backends.agent.api_server.util.' @mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'get_os_init_system', return_value=consts.INIT_SYSVINIT) 'get_os_init_system', return_value=consts.INIT_SYSVINIT)
def test_delete_ubuntu_listener_sysvinit(self, mock_init_system, def test_delete_ubuntu_listener_sysvinit(self, mock_init_system,
@ -444,7 +450,7 @@ class TestServerTestCase(base.TestCase):
mock_init_system) mock_init_system)
@mock.patch('octavia.amphorae.backends.agent.api_server.util.' @mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'get_listener_protocol', return_value='TCP') 'get_protocol_for_lb_object', return_value='TCP')
@mock.patch('octavia.amphorae.backends.agent.api_server.util.' @mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'get_os_init_system', return_value=consts.INIT_UPSTART) 'get_os_init_system', return_value=consts.INIT_UPSTART)
def test_delete_ubuntu_listener_upstart(self, mock_init_system, def test_delete_ubuntu_listener_upstart(self, mock_init_system,
@ -452,20 +458,22 @@ class TestServerTestCase(base.TestCase):
self._test_delete_listener(consts.INIT_UPSTART, consts.UBUNTU, self._test_delete_listener(consts.INIT_UPSTART, consts.UBUNTU,
mock_init_system) mock_init_system)
@mock.patch('os.listdir')
@mock.patch('os.path.exists') @mock.patch('os.path.exists')
@mock.patch('subprocess.check_output') @mock.patch('subprocess.check_output')
@mock.patch('octavia.amphorae.backends.agent.api_server.listener.Listener.' @mock.patch('octavia.amphorae.backends.agent.api_server.loadbalancer.'
'vrrp_check_script_update') 'Loadbalancer.vrrp_check_script_update')
@mock.patch('octavia.amphorae.backends.agent.api_server.util.' + @mock.patch('octavia.amphorae.backends.agent.api_server.util.' +
'get_haproxy_pid') 'get_haproxy_pid')
@mock.patch('shutil.rmtree') @mock.patch('shutil.rmtree')
@mock.patch('os.remove') @mock.patch('os.remove')
def _test_delete_listener(self, init_system, distro, mock_init_system, def _test_delete_listener(self, init_system, distro, mock_init_system,
mock_remove, mock_rmtree, mock_pid, mock_vrrp, mock_remove, mock_rmtree, mock_pid, mock_vrrp,
mock_check_output, mock_exists): mock_check_output, mock_exists, mock_listdir):
self.assertIn(distro, [consts.UBUNTU, consts.CENTOS]) self.assertIn(distro, [consts.UBUNTU, consts.CENTOS])
# no listener # no listener
mock_exists.return_value = False mock_exists.return_value = False
mock_listdir.return_value = ['123']
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.delete('/' + api_server.VERSION + rv = self.ubuntu_app.delete('/' + api_server.VERSION +
'/listeners/123') '/listeners/123')
@ -474,10 +482,10 @@ class TestServerTestCase(base.TestCase):
'/listeners/123') '/listeners/123')
self.assertEqual(200, rv.status_code) self.assertEqual(200, rv.status_code)
self.assertEqual(OK, jsonutils.loads(rv.data.decode('utf-8'))) self.assertEqual(OK, jsonutils.loads(rv.data.decode('utf-8')))
mock_exists.assert_called_with('/var/lib/octavia/123/haproxy.cfg') mock_exists.assert_called_once_with('/var/lib/octavia')
# service is stopped + no upstart script + no vrrp # service is stopped + no upstart script + no vrrp
mock_exists.side_effect = [True, False, False, False] mock_exists.side_effect = [True, True, False, False, False]
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.delete('/' + api_server.VERSION + rv = self.ubuntu_app.delete('/' + api_server.VERSION +
'/listeners/123') '/listeners/123')
@ -504,7 +512,7 @@ class TestServerTestCase(base.TestCase):
mock_exists.assert_any_call('/var/lib/octavia/123/123.pid') mock_exists.assert_any_call('/var/lib/octavia/123/123.pid')
# service is stopped + no upstart script + vrrp # service is stopped + no upstart script + vrrp
mock_exists.side_effect = [True, False, True, False] mock_exists.side_effect = [True, True, False, True, False]
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.delete('/' + api_server.VERSION + rv = self.ubuntu_app.delete('/' + api_server.VERSION +
'/listeners/123') '/listeners/123')
@ -531,7 +539,7 @@ class TestServerTestCase(base.TestCase):
mock_exists.assert_any_call('/var/lib/octavia/123/123.pid') mock_exists.assert_any_call('/var/lib/octavia/123/123.pid')
# service is stopped + upstart script + no vrrp # service is stopped + upstart script + no vrrp
mock_exists.side_effect = [True, False, False, True] mock_exists.side_effect = [True, True, False, False, True]
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.delete('/' + api_server.VERSION + rv = self.ubuntu_app.delete('/' + api_server.VERSION +
'/listeners/123') '/listeners/123')
@ -555,7 +563,7 @@ class TestServerTestCase(base.TestCase):
self.assertIn(init_system, consts.VALID_INIT_SYSTEMS) self.assertIn(init_system, consts.VALID_INIT_SYSTEMS)
# service is stopped + upstart script + vrrp # service is stopped + upstart script + vrrp
mock_exists.side_effect = [True, False, True, True] mock_exists.side_effect = [True, True, False, True, True]
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.delete('/' + api_server.VERSION + rv = self.ubuntu_app.delete('/' + api_server.VERSION +
'/listeners/123') '/listeners/123')
@ -579,7 +587,7 @@ class TestServerTestCase(base.TestCase):
self.assertIn(init_system, consts.VALID_INIT_SYSTEMS) self.assertIn(init_system, consts.VALID_INIT_SYSTEMS)
# service is running + upstart script + no vrrp # service is running + upstart script + no vrrp
mock_exists.side_effect = [True, True, True, False, True] mock_exists.side_effect = [True, True, True, True, False, True]
mock_pid.return_value = '456' mock_pid.return_value = '456'
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.delete('/' + api_server.VERSION + rv = self.ubuntu_app.delete('/' + api_server.VERSION +
@ -609,7 +617,7 @@ class TestServerTestCase(base.TestCase):
self.assertIn(init_system, consts.VALID_INIT_SYSTEMS) self.assertIn(init_system, consts.VALID_INIT_SYSTEMS)
# service is running + upstart script + vrrp # service is running + upstart script + vrrp
mock_exists.side_effect = [True, True, True, True, True] mock_exists.side_effect = [True, True, True, True, True, True]
mock_pid.return_value = '456' mock_pid.return_value = '456'
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.delete('/' + api_server.VERSION + rv = self.ubuntu_app.delete('/' + api_server.VERSION +
@ -639,7 +647,7 @@ class TestServerTestCase(base.TestCase):
self.assertIn(init_system, consts.VALID_INIT_SYSTEMS) self.assertIn(init_system, consts.VALID_INIT_SYSTEMS)
# service is running + stopping fails # service is running + stopping fails
mock_exists.side_effect = [True, True, True] mock_exists.side_effect = [True, True, True, True]
mock_check_output.side_effect = subprocess.CalledProcessError( mock_check_output.side_effect = subprocess.CalledProcessError(
7, 'test', RANDOM_ERROR) 7, 'test', RANDOM_ERROR)
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
@ -661,8 +669,9 @@ class TestServerTestCase(base.TestCase):
def test_centos_get_haproxy(self): def test_centos_get_haproxy(self):
self._test_get_haproxy(consts.CENTOS) self._test_get_haproxy(consts.CENTOS)
@mock.patch('os.listdir')
@mock.patch('os.path.exists') @mock.patch('os.path.exists')
def _test_get_haproxy(self, distro, mock_exists): def _test_get_haproxy(self, distro, mock_exists, mock_listdir):
self.assertIn(distro, [consts.UBUNTU, consts.CENTOS]) self.assertIn(distro, [consts.UBUNTU, consts.CENTOS])
@ -670,23 +679,24 @@ class TestServerTestCase(base.TestCase):
mock_exists.side_effect = [False] mock_exists.side_effect = [False]
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.get('/' + api_server.VERSION + rv = self.ubuntu_app.get('/' + api_server.VERSION +
'/listeners/123/haproxy') '/loadbalancer/123/haproxy')
elif distro == consts.CENTOS: elif distro == consts.CENTOS:
rv = self.centos_app.get('/' + api_server.VERSION + rv = self.centos_app.get('/' + api_server.VERSION +
'/listeners/123/haproxy') '/loadbalancer/123/haproxy')
self.assertEqual(404, rv.status_code) self.assertEqual(404, rv.status_code)
mock_exists.side_effect = [True] mock_exists.side_effect = [True, True]
path = util.config_path('123') path = util.config_path('123')
self.useFixture(test_utils.OpenFixture(path, CONTENT)) self.useFixture(test_utils.OpenFixture(path, CONTENT))
mock_listdir.return_value = ['123']
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.get('/' + api_server.VERSION + rv = self.ubuntu_app.get('/' + api_server.VERSION +
'/listeners/123/haproxy') '/loadbalancer/123/haproxy')
elif distro == consts.CENTOS: elif distro == consts.CENTOS:
rv = self.centos_app.get('/' + api_server.VERSION + rv = self.centos_app.get('/' + api_server.VERSION +
'/listeners/123/haproxy') '/loadbalancer/123/haproxy')
self.assertEqual(200, rv.status_code) self.assertEqual(200, rv.status_code)
self.assertEqual(six.b(CONTENT), rv.data) self.assertEqual(six.b(CONTENT), rv.data)
self.assertEqual('text/plain; charset=utf-8', self.assertEqual('text/plain; charset=utf-8',
@ -699,17 +709,17 @@ class TestServerTestCase(base.TestCase):
self._test_get_all_listeners(consts.CENTOS) self._test_get_all_listeners(consts.CENTOS)
@mock.patch('octavia.amphorae.backends.agent.api_server.util.' @mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'get_listeners') 'get_loadbalancers')
@mock.patch('octavia.amphorae.backends.agent.api_server.listener.Listener.' @mock.patch('octavia.amphorae.backends.agent.api_server.loadbalancer.'
'_check_listener_status') 'Loadbalancer._check_haproxy_status')
@mock.patch('octavia.amphorae.backends.agent.api_server.listener.Listener.' @mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'_parse_haproxy_file') 'parse_haproxy_file')
def _test_get_all_listeners(self, distro, mock_parse, mock_status, def _test_get_all_listeners(self, distro, mock_parse, mock_status,
mock_listener): mock_lbs):
self.assertIn(distro, [consts.UBUNTU, consts.CENTOS]) self.assertIn(distro, [consts.UBUNTU, consts.CENTOS])
# no listeners # no listeners
mock_listener.side_effect = [[]] mock_lbs.side_effect = [[]]
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.get('/' + api_server.VERSION + '/listeners') rv = self.ubuntu_app.get('/' + api_server.VERSION + '/listeners')
elif distro == consts.CENTOS: elif distro == consts.CENTOS:
@ -719,8 +729,8 @@ class TestServerTestCase(base.TestCase):
self.assertFalse(jsonutils.loads(rv.data.decode('utf-8'))) self.assertFalse(jsonutils.loads(rv.data.decode('utf-8')))
# one listener ACTIVE # one listener ACTIVE
mock_listener.side_effect = [['123']] mock_lbs.side_effect = [['123']]
mock_parse.side_effect = [{'mode': 'test'}] mock_parse.side_effect = [['fake_socket', {'123': {'mode': 'test'}}]]
mock_status.side_effect = [consts.ACTIVE] mock_status.side_effect = [consts.ACTIVE]
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.get('/' + api_server.VERSION + '/listeners') rv = self.ubuntu_app.get('/' + api_server.VERSION + '/listeners')
@ -732,10 +742,11 @@ class TestServerTestCase(base.TestCase):
[{'status': consts.ACTIVE, 'type': 'test', 'uuid': '123'}], [{'status': consts.ACTIVE, 'type': 'test', 'uuid': '123'}],
jsonutils.loads(rv.data.decode('utf-8'))) jsonutils.loads(rv.data.decode('utf-8')))
# two listener one ACTIVE, one ERROR # two listeners, two modes
mock_listener.side_effect = [['123', '456']] mock_lbs.side_effect = [['123', '456']]
mock_parse.side_effect = [{'mode': 'test'}, {'mode': 'http'}] mock_parse.side_effect = [['fake_socket', {'123': {'mode': 'test'}}],
mock_status.side_effect = [consts.ACTIVE, consts.ERROR] ['fake_socket', {'456': {'mode': 'http'}}]]
mock_status.return_value = consts.ACTIVE
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.get('/' + api_server.VERSION + '/listeners') rv = self.ubuntu_app.get('/' + api_server.VERSION + '/listeners')
elif distro == consts.CENTOS: elif distro == consts.CENTOS:
@ -744,86 +755,7 @@ class TestServerTestCase(base.TestCase):
self.assertEqual(200, rv.status_code) self.assertEqual(200, rv.status_code)
self.assertEqual( self.assertEqual(
[{'status': consts.ACTIVE, 'type': 'test', 'uuid': '123'}, [{'status': consts.ACTIVE, 'type': 'test', 'uuid': '123'},
{'status': consts.ERROR, 'type': '', 'uuid': '456'}], {'status': consts.ACTIVE, 'type': 'http', 'uuid': '456'}],
jsonutils.loads(rv.data.decode('utf-8')))
def test_ubuntu_get_listener(self):
self._test_get_listener(consts.UBUNTU)
def test_centos_get_listener(self):
self._test_get_listener(consts.CENTOS)
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'get_listener_protocol', return_value='TCP')
@mock.patch('octavia.amphorae.backends.agent.api_server.listener.Listener.'
'_check_listener_status')
@mock.patch('octavia.amphorae.backends.agent.api_server.listener.Listener.'
'_parse_haproxy_file')
@mock.patch('octavia.amphorae.backends.utils.haproxy_query.HAProxyQuery')
@mock.patch('os.path.exists')
def _test_get_listener(self, distro, mock_exists, mock_query, mock_parse,
mock_status, mock_get_proto):
self.assertIn(distro, [consts.UBUNTU, consts.CENTOS])
# Listener not found
mock_exists.side_effect = [False]
if distro == consts.UBUNTU:
rv = self.ubuntu_app.get('/' + api_server.VERSION +
'/listeners/123')
elif distro == consts.CENTOS:
rv = self.centos_app.get('/' + api_server.VERSION +
'/listeners/123')
self.assertEqual(404, rv.status_code)
self.assertEqual(
{'message': 'Listener Not Found',
'details': 'No listener with UUID: 123'},
jsonutils.loads(rv.data.decode('utf-8')))
# Listener not ACTIVE
mock_parse.side_effect = [dict(mode='test')]
mock_status.side_effect = [consts.ERROR]
mock_exists.side_effect = [True]
if distro == consts.UBUNTU:
rv = self.ubuntu_app.get('/' + api_server.VERSION +
'/listeners/123')
elif distro == consts.CENTOS:
rv = self.centos_app.get('/' + api_server.VERSION +
'/listeners/123')
self.assertEqual(200, rv.status_code)
self.assertEqual(dict(
status=consts.ERROR,
type='',
uuid='123'), jsonutils.loads(rv.data.decode('utf-8')))
# Listener ACTIVE
mock_parse.side_effect = [dict(mode='test', stats_socket='blah')]
mock_status.side_effect = [consts.ACTIVE]
mock_exists.side_effect = [True]
mock_pool = mock.Mock()
mock_query.side_effect = [mock_pool]
mock_pool.get_pool_status.side_effect = [
{'tcp-servers': {
'status': 'DOWN',
'uuid': 'tcp-servers',
'members': [
{'id-34833': 'DOWN'},
{'id-34836': 'DOWN'}]}}]
if distro == consts.UBUNTU:
rv = self.ubuntu_app.get('/' + api_server.VERSION +
'/listeners/123')
elif distro == consts.CENTOS:
rv = self.centos_app.get('/' + api_server.VERSION +
'/listeners/123')
self.assertEqual(200, rv.status_code)
self.assertEqual(dict(
status=consts.ACTIVE,
type='test',
uuid='123',
pools=[dict(
status=consts.DOWN,
uuid='tcp-servers',
members=[
{u'id-34833': u'DOWN'},
{u'id-34836': u'DOWN'}])]),
jsonutils.loads(rv.data.decode('utf-8'))) jsonutils.loads(rv.data.decode('utf-8')))
def test_ubuntu_delete_cert(self): def test_ubuntu_delete_cert(self):
@ -838,11 +770,13 @@ class TestServerTestCase(base.TestCase):
self.assertIn(distro, [consts.UBUNTU, consts.CENTOS]) self.assertIn(distro, [consts.UBUNTU, consts.CENTOS])
mock_exists.side_effect = [False] mock_exists.side_effect = [False]
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.delete('/' + api_server.VERSION + rv = self.ubuntu_app.delete(
'/listeners/123/certificates/test.pem') '/' + api_server.VERSION +
'/loadbalancer/123/certificates/test.pem')
elif distro == consts.CENTOS: elif distro == consts.CENTOS:
rv = self.centos_app.delete('/' + api_server.VERSION + rv = self.centos_app.delete(
'/listeners/123/certificates/test.pem') '/' + api_server.VERSION +
'/loadbalancer/123/certificates/test.pem')
self.assertEqual(200, rv.status_code) self.assertEqual(200, rv.status_code)
self.assertEqual(OK, jsonutils.loads(rv.data.decode('utf-8'))) self.assertEqual(OK, jsonutils.loads(rv.data.decode('utf-8')))
mock_exists.assert_called_once_with( mock_exists.assert_called_once_with(
@ -851,20 +785,24 @@ class TestServerTestCase(base.TestCase):
# wrong file name # wrong file name
mock_exists.side_effect = [True] mock_exists.side_effect = [True]
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.delete('/' + api_server.VERSION + rv = self.ubuntu_app.delete(
'/listeners/123/certificates/test.bla') '/' + api_server.VERSION +
'/loadbalancer/123/certificates/test.bla')
elif distro == consts.CENTOS: elif distro == consts.CENTOS:
rv = self.centos_app.delete('/' + api_server.VERSION + rv = self.centos_app.delete(
'/listeners/123/certificates/test.bla') '/' + api_server.VERSION +
'/loadbalancer/123/certificates/test.bla')
self.assertEqual(400, rv.status_code) self.assertEqual(400, rv.status_code)
mock_exists.side_effect = [True] mock_exists.side_effect = [True]
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.delete('/' + api_server.VERSION + rv = self.ubuntu_app.delete(
'/listeners/123/certificates/test.pem') '/' + api_server.VERSION +
'/loadbalancer/123/certificates/test.pem')
elif distro == consts.CENTOS: elif distro == consts.CENTOS:
rv = self.centos_app.delete('/' + api_server.VERSION + rv = self.centos_app.delete(
'/listeners/123/certificates/test.pem') '/' + api_server.VERSION +
'/loadbalancer/123/certificates/test.pem')
self.assertEqual(200, rv.status_code) self.assertEqual(200, rv.status_code)
self.assertEqual(OK, jsonutils.loads(rv.data.decode('utf-8'))) self.assertEqual(OK, jsonutils.loads(rv.data.decode('utf-8')))
mock_remove.assert_called_once_with( mock_remove.assert_called_once_with(
@ -886,10 +824,10 @@ class TestServerTestCase(base.TestCase):
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.get('/' + api_server.VERSION + rv = self.ubuntu_app.get('/' + api_server.VERSION +
'/listeners/123/certificates/test.pem') '/loadbalancer/123/certificates/test.pem')
elif distro == consts.CENTOS: elif distro == consts.CENTOS:
rv = self.centos_app.get('/' + api_server.VERSION + rv = self.centos_app.get('/' + api_server.VERSION +
'/listeners/123/certificates/test.pem') '/loadbalancer/123/certificates/test.pem')
self.assertEqual(404, rv.status_code) self.assertEqual(404, rv.status_code)
self.assertEqual(dict( self.assertEqual(dict(
details='No certificate with filename: test.pem', details='No certificate with filename: test.pem',
@ -901,29 +839,29 @@ class TestServerTestCase(base.TestCase):
mock_exists.side_effect = [True] mock_exists.side_effect = [True]
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.put('/' + api_server.VERSION + rv = self.ubuntu_app.put('/' + api_server.VERSION +
'/listeners/123/certificates/test.bla', '/loadbalancer/123/certificates/test.bla',
data='TestTest') data='TestTest')
elif distro == consts.CENTOS: elif distro == consts.CENTOS:
rv = self.centos_app.put('/' + api_server.VERSION + rv = self.centos_app.put('/' + api_server.VERSION +
'/listeners/123/certificates/test.bla', '/loadbalancer/123/certificates/test.bla',
data='TestTest') data='TestTest')
self.assertEqual(400, rv.status_code) self.assertEqual(400, rv.status_code)
mock_exists.return_value = True mock_exists.return_value = True
mock_exists.side_effect = None mock_exists.side_effect = None
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
path = self.ubuntu_test_server._listener._cert_file_path( path = self.ubuntu_test_server._loadbalancer._cert_file_path(
'123', 'test.pem') '123', 'test.pem')
elif distro == consts.CENTOS: elif distro == consts.CENTOS:
path = self.centos_test_server._listener._cert_file_path( path = self.centos_test_server._loadbalancer._cert_file_path(
'123', 'test.pem') '123', 'test.pem')
self.useFixture(test_utils.OpenFixture(path, CONTENT)) self.useFixture(test_utils.OpenFixture(path, CONTENT))
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.get('/' + api_server.VERSION + rv = self.ubuntu_app.get('/' + api_server.VERSION +
'/listeners/123/certificates/test.pem') '/loadbalancer/123/certificates/test.pem')
elif distro == consts.CENTOS: elif distro == consts.CENTOS:
rv = self.centos_app.get('/' + api_server.VERSION + rv = self.centos_app.get('/' + api_server.VERSION +
'/listeners/123/certificates/test.pem') '/loadbalancer/123/certificates/test.pem')
self.assertEqual(200, rv.status_code) self.assertEqual(200, rv.status_code)
self.assertEqual(dict(md5sum=hashlib.md5(six.b(CONTENT)).hexdigest()), self.assertEqual(dict(md5sum=hashlib.md5(six.b(CONTENT)).hexdigest()),
jsonutils.loads(rv.data.decode('utf-8'))) jsonutils.loads(rv.data.decode('utf-8')))
@ -942,20 +880,20 @@ class TestServerTestCase(base.TestCase):
# wrong file name # wrong file name
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.put('/' + api_server.VERSION + rv = self.ubuntu_app.put('/' + api_server.VERSION +
'/listeners/123/certificates/test.bla', '/loadbalancer/123/certificates/test.bla',
data='TestTest') data='TestTest')
elif distro == consts.CENTOS: elif distro == consts.CENTOS:
rv = self.centos_app.put('/' + api_server.VERSION + rv = self.centos_app.put('/' + api_server.VERSION +
'/listeners/123/certificates/test.bla', '/loadbalancer/123/certificates/test.bla',
data='TestTest') data='TestTest')
self.assertEqual(400, rv.status_code) self.assertEqual(400, rv.status_code)
mock_exists.return_value = True mock_exists.return_value = True
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
path = self.ubuntu_test_server._listener._cert_file_path( path = self.ubuntu_test_server._loadbalancer._cert_file_path(
'123', 'test.pem') '123', 'test.pem')
elif distro == consts.CENTOS: elif distro == consts.CENTOS:
path = self.centos_test_server._listener._cert_file_path( path = self.centos_test_server._loadbalancer._cert_file_path(
'123', 'test.pem') '123', 'test.pem')
m = self.useFixture(test_utils.OpenFixture(path)).mock_open m = self.useFixture(test_utils.OpenFixture(path)).mock_open
@ -963,11 +901,11 @@ class TestServerTestCase(base.TestCase):
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.put('/' + api_server.VERSION + rv = self.ubuntu_app.put('/' + api_server.VERSION +
'/listeners/123/certificates/' '/loadbalancer/123/certificates/'
'test.pem', data='TestTest') 'test.pem', data='TestTest')
elif distro == consts.CENTOS: elif distro == consts.CENTOS:
rv = self.centos_app.put('/' + api_server.VERSION + rv = self.centos_app.put('/' + api_server.VERSION +
'/listeners/123/certificates/' '/loadbalancer/123/certificates/'
'test.pem', data='TestTest') 'test.pem', data='TestTest')
self.assertEqual(200, rv.status_code) self.assertEqual(200, rv.status_code)
self.assertEqual(OK, jsonutils.loads(rv.data.decode('utf-8'))) self.assertEqual(OK, jsonutils.loads(rv.data.decode('utf-8')))
@ -980,11 +918,11 @@ class TestServerTestCase(base.TestCase):
with mock.patch('os.open'), mock.patch.object(os, 'fdopen', m): with mock.patch('os.open'), mock.patch.object(os, 'fdopen', m):
if distro == consts.UBUNTU: if distro == consts.UBUNTU:
rv = self.ubuntu_app.put('/' + api_server.VERSION + rv = self.ubuntu_app.put('/' + api_server.VERSION +
'/listeners/123/certificates/' '/loadbalancer/123/certificates/'
'test.pem', data='TestTest') 'test.pem', data='TestTest')
elif distro == consts.CENTOS: elif distro == consts.CENTOS:
rv = self.centos_app.put('/' + api_server.VERSION + rv = self.centos_app.put('/' + api_server.VERSION +
'/listeners/123/certificates/' '/loadbalancer/123/certificates/'
'test.pem', data='TestTest') 'test.pem', data='TestTest')
self.assertEqual(200, rv.status_code) self.assertEqual(200, rv.status_code)
self.assertEqual(OK, jsonutils.loads(rv.data.decode('utf-8'))) self.assertEqual(OK, jsonutils.loads(rv.data.decode('utf-8')))
@ -2626,7 +2564,7 @@ class TestServerTestCase(base.TestCase):
haproxy_count = random.randrange(0, 100) haproxy_count = random.randrange(0, 100)
mock_count_haproxy.return_value = haproxy_count mock_count_haproxy.return_value = haproxy_count
expected_dict = {'active': True, 'api_version': '0.5', expected_dict = {'active': True, 'api_version': '1.0',
'cpu': {'soft_irq': cpu_softirq, 'system': cpu_system, 'cpu': {'soft_irq': cpu_softirq, 'system': cpu_system,
'total': cpu_total, 'user': cpu_user}, 'total': cpu_total, 'user': cpu_user},
'disk': {'available': disk_available, 'disk': {'available': disk_available,
@ -2699,3 +2637,11 @@ class TestServerTestCase(base.TestCase):
rv = self.centos_app.put('/' + api_server.VERSION + rv = self.centos_app.put('/' + api_server.VERSION +
'/config', data='TestTest') '/config', data='TestTest')
self.assertEqual(500, rv.status_code) self.assertEqual(500, rv.status_code)
def test_version_discovery(self):
self.test_client = server.Server().app.test_client()
expected_dict = {'api_version': api_server.VERSION}
rv = self.test_client.get('/')
self.assertEqual(200, rv.status_code)
self.assertEqual(expected_dict,
jsonutils.loads(rv.data.decode('utf-8')))

View File

@ -3274,7 +3274,8 @@ class AmphoraRepositoryTest(BaseRepositoryTest):
enabled=True, peer_port=1025, default_pool_id=pool.id) enabled=True, peer_port=1025, default_pool_id=pool.id)
listener_ref = {listener.id: {'operating_status': constants.ONLINE, listener_ref = {listener.id: {'operating_status': constants.ONLINE,
'protocol': constants.PROTOCOL_HTTP}} 'protocol': constants.PROTOCOL_HTTP,
'enabled': 1}}
lb_ref['listeners'] = listener_ref lb_ref['listeners'] = listener_ref
# Test with an LB, pool, and listener (no members) # Test with an LB, pool, and listener (no members)

View File

@ -19,13 +19,18 @@ from oslo_utils import uuidutils
from octavia.amphorae.backends.agent import api_server from octavia.amphorae.backends.agent import api_server
from octavia.amphorae.backends.agent.api_server import amphora_info from octavia.amphorae.backends.agent.api_server import amphora_info
from octavia.amphorae.backends.agent.api_server import util
from octavia.common.jinja.haproxy.combined_listeners import jinja_cfg
from octavia.tests.common import utils as test_utils from octavia.tests.common import utils as test_utils
import octavia.tests.unit.base as base import octavia.tests.unit.base as base
from octavia.tests.unit.common.sample_configs import sample_configs_combined
class TestAmphoraInfo(base.TestCase): class TestAmphoraInfo(base.TestCase):
API_VERSION = random.randrange(0, 10000) API_VERSION = random.randrange(0, 10000)
BASE_AMP_PATH = '/var/lib/octavia'
BASE_CRT_PATH = BASE_AMP_PATH + '/certs'
HAPROXY_VERSION = random.randrange(0, 10000) HAPROXY_VERSION = random.randrange(0, 10000)
KEEPALIVED_VERSION = random.randrange(0, 10000) KEEPALIVED_VERSION = random.randrange(0, 10000)
IPVSADM_VERSION = random.randrange(0, 10000) IPVSADM_VERSION = random.randrange(0, 10000)
@ -33,6 +38,7 @@ class TestAmphoraInfo(base.TestCase):
FAKE_LISTENER_ID_2 = uuidutils.generate_uuid() FAKE_LISTENER_ID_2 = uuidutils.generate_uuid()
FAKE_LISTENER_ID_3 = uuidutils.generate_uuid() FAKE_LISTENER_ID_3 = uuidutils.generate_uuid()
FAKE_LISTENER_ID_4 = uuidutils.generate_uuid() FAKE_LISTENER_ID_4 = uuidutils.generate_uuid()
LB_ID_1 = uuidutils.generate_uuid()
def setUp(self): def setUp(self):
super(TestAmphoraInfo, self).setUp() super(TestAmphoraInfo, self).setUp()
@ -40,6 +46,23 @@ class TestAmphoraInfo(base.TestCase):
self.amp_info = amphora_info.AmphoraInfo(self.osutils_mock) self.amp_info = amphora_info.AmphoraInfo(self.osutils_mock)
self.udp_driver = mock.MagicMock() self.udp_driver = mock.MagicMock()
# setup a fake haproxy config file
templater = jinja_cfg.JinjaTemplater(
base_amp_path=self.BASE_AMP_PATH,
base_crt_dir=self.BASE_CRT_PATH)
tls_tupel = sample_configs_combined.sample_tls_container_tuple(
id='tls_container_id',
certificate='imaCert1', private_key='imaPrivateKey1',
primary_cn='FakeCN')
self.rendered_haproxy_cfg = templater.render_loadbalancer_obj(
sample_configs_combined.sample_amphora_tuple(),
[sample_configs_combined.sample_listener_tuple(
proto='TERMINATED_HTTPS', tls=True, sni=True)],
tls_tupel)
path = util.config_path(self.LB_ID_1)
self.useFixture(test_utils.OpenFixture(path,
self.rendered_haproxy_cfg))
def _return_version(self, package_name): def _return_version(self, package_name):
if package_name == 'ipvsadm': if package_name == 'ipvsadm':
return self.IPVSADM_VERSION return self.IPVSADM_VERSION
@ -138,8 +161,8 @@ class TestAmphoraInfo(base.TestCase):
u'haproxy_count': 5, u'haproxy_count': 5,
u'haproxy_version': self.HAPROXY_VERSION, u'haproxy_version': self.HAPROXY_VERSION,
u'hostname': u'FAKE_HOST', u'hostname': u'FAKE_HOST',
u'listeners': [self.FAKE_LISTENER_ID_1, u'listeners': sorted([self.FAKE_LISTENER_ID_1,
self.FAKE_LISTENER_ID_2], self.FAKE_LISTENER_ID_2]),
u'load': [u'0.09', u'0.11', u'0.10'], u'load': [u'0.09', u'0.11', u'0.10'],
u'memory': {u'buffers': 344792, u'memory': {u'buffers': 344792,
u'cached': 4271856, u'cached': 4271856,
@ -163,8 +186,7 @@ class TestAmphoraInfo(base.TestCase):
'get_udp_listeners', 'get_udp_listeners',
return_value=[FAKE_LISTENER_ID_3, FAKE_LISTENER_ID_4]) return_value=[FAKE_LISTENER_ID_3, FAKE_LISTENER_ID_4])
@mock.patch('octavia.amphorae.backends.agent.api_server.util.' @mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'get_listeners', return_value=[FAKE_LISTENER_ID_1, 'get_loadbalancers')
FAKE_LISTENER_ID_2])
@mock.patch('octavia.amphorae.backends.agent.api_server.' @mock.patch('octavia.amphorae.backends.agent.api_server.'
'amphora_info.AmphoraInfo._get_meminfo') 'amphora_info.AmphoraInfo._get_meminfo')
@mock.patch('octavia.amphorae.backends.agent.api_server.' @mock.patch('octavia.amphorae.backends.agent.api_server.'
@ -182,7 +204,7 @@ class TestAmphoraInfo(base.TestCase):
def test_compile_amphora_details_for_udp(self, mhostname, m_count, def test_compile_amphora_details_for_udp(self, mhostname, m_count,
m_pkg_version, m_load, m_get_nets, m_pkg_version, m_load, m_get_nets,
m_os, m_cpu, mget_mem, m_os, m_cpu, mget_mem,
mget_listener, mget_udp_listener): mock_get_lb, mget_udp_listener):
mget_mem.return_value = {'SwapCached': 0, 'Buffers': 344792, mget_mem.return_value = {'SwapCached': 0, 'Buffers': 344792,
'MemTotal': 21692784, 'Cached': 4271856, 'MemTotal': 21692784, 'Cached': 4271856,
'Slab': 534384, 'MemFree': 12685624, 'Slab': 534384, 'MemFree': 12685624,
@ -205,6 +227,7 @@ class TestAmphoraInfo(base.TestCase):
self.udp_driver.get_subscribed_amp_compile_info.return_value = [ self.udp_driver.get_subscribed_amp_compile_info.return_value = [
'keepalived', 'ipvsadm'] 'keepalived', 'ipvsadm']
self.udp_driver.is_listener_running.side_effect = [True, False] self.udp_driver.is_listener_running.side_effect = [True, False]
mock_get_lb.return_value = [self.LB_ID_1]
original_version = api_server.VERSION original_version = api_server.VERSION
api_server.VERSION = self.API_VERSION api_server.VERSION = self.API_VERSION
expected_dict = {u'active': True, expected_dict = {u'active': True,
@ -221,10 +244,10 @@ class TestAmphoraInfo(base.TestCase):
u'ipvsadm_version': self.IPVSADM_VERSION, u'ipvsadm_version': self.IPVSADM_VERSION,
u'udp_listener_process_count': 1, u'udp_listener_process_count': 1,
u'hostname': u'FAKE_HOST', u'hostname': u'FAKE_HOST',
u'listeners': list(set([self.FAKE_LISTENER_ID_1, u'listeners': sorted(list(set(
self.FAKE_LISTENER_ID_2, [self.FAKE_LISTENER_ID_3,
self.FAKE_LISTENER_ID_3, self.FAKE_LISTENER_ID_4,
self.FAKE_LISTENER_ID_4])), 'sample_listener_id_1']))),
u'load': [u'0.09', u'0.11', u'0.10'], u'load': [u'0.09', u'0.11', u'0.10'],
u'memory': {u'buffers': 344792, u'memory': {u'buffers': 344792,
u'cached': 4271856, u'cached': 4271856,
@ -245,7 +268,7 @@ class TestAmphoraInfo(base.TestCase):
api_server.VERSION = original_version api_server.VERSION = original_version
@mock.patch('octavia.amphorae.backends.agent.api_server.util.' @mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'is_listener_running') 'is_lb_running')
def test__count_haproxy_process(self, mock_is_running): def test__count_haproxy_process(self, mock_is_running):
# Test no listeners passed in # Test no listeners passed in

View File

@ -17,7 +17,7 @@ import mock
from octavia.amphorae.backends.agent.api_server import haproxy_compatibility from octavia.amphorae.backends.agent.api_server import haproxy_compatibility
from octavia.common import constants from octavia.common import constants
import octavia.tests.unit.base as base import octavia.tests.unit.base as base
from octavia.tests.unit.common.sample_configs import sample_configs from octavia.tests.unit.common.sample_configs import sample_configs_combined
class HAProxyCompatTestCase(base.TestCase): class HAProxyCompatTestCase(base.TestCase):
@ -30,7 +30,7 @@ class HAProxyCompatTestCase(base.TestCase):
" user nobody\n" " user nobody\n"
" log /dev/log local0\n" " log /dev/log local0\n"
" log /dev/log local1 notice\n" " log /dev/log local1 notice\n"
" stats socket /var/lib/octavia/sample_listener_id_1.sock" " stats socket /var/lib/octavia/sample_loadbalancer_id_1.sock"
" mode 0666 level user\n" " mode 0666 level user\n"
" maxconn {maxconn}\n\n" " maxconn {maxconn}\n\n"
"defaults\n" "defaults\n"
@ -47,11 +47,11 @@ class HAProxyCompatTestCase(base.TestCase):
" maxconn {maxconn}\n" " maxconn {maxconn}\n"
" bind 10.0.0.2:80\n" " bind 10.0.0.2:80\n"
" mode http\n" " mode http\n"
" default_backend sample_pool_id_1\n" " default_backend sample_pool_id_1:sample_listener_id_1\n"
" timeout client 50000\n\n").format( " timeout client 50000\n\n").format(
maxconn=constants.HAPROXY_MAX_MAXCONN) maxconn=constants.HAPROXY_MAX_MAXCONN)
self.backend_without_external = ( self.backend_without_external = (
"backend sample_pool_id_1\n" "backend sample_pool_id_1:sample_listener_id_1\n"
" mode http\n" " mode http\n"
" balance roundrobin\n" " balance roundrobin\n"
" cookie SRV insert indirect nocache\n" " cookie SRV insert indirect nocache\n"
@ -69,7 +69,7 @@ class HAProxyCompatTestCase(base.TestCase):
"sample_member_id_2\n").format( "sample_member_id_2\n").format(
maxconn=constants.HAPROXY_MAX_MAXCONN) maxconn=constants.HAPROXY_MAX_MAXCONN)
self.backend_with_external = ( self.backend_with_external = (
"backend sample_pool_id_1\n" "backend sample_pool_id_1:sample_listener_id_1\n"
" mode http\n" " mode http\n"
" balance roundrobin\n" " balance roundrobin\n"
" cookie SRV insert indirect nocache\n" " cookie SRV insert indirect nocache\n"
@ -103,7 +103,7 @@ class HAProxyCompatTestCase(base.TestCase):
def test_process_cfg_for_version_compat(self, mock_get_version): def test_process_cfg_for_version_compat(self, mock_get_version):
# Test 1.6 version path, no change to config expected # Test 1.6 version path, no change to config expected
mock_get_version.return_value = [1, 6] mock_get_version.return_value = [1, 6]
test_config = sample_configs.sample_base_expected_config( test_config = sample_configs_combined.sample_base_expected_config(
backend=self.backend_with_external) backend=self.backend_with_external)
result_config = haproxy_compatibility.process_cfg_for_version_compat( result_config = haproxy_compatibility.process_cfg_for_version_compat(
test_config) test_config)
@ -111,7 +111,7 @@ class HAProxyCompatTestCase(base.TestCase):
# Test 1.5 version path, external-check should be removed # Test 1.5 version path, external-check should be removed
mock_get_version.return_value = [1, 5] mock_get_version.return_value = [1, 5]
test_config = sample_configs.sample_base_expected_config( test_config = sample_configs_combined.sample_base_expected_config(
backend=self.backend_with_external) backend=self.backend_with_external)
result_config = haproxy_compatibility.process_cfg_for_version_compat( result_config = haproxy_compatibility.process_cfg_for_version_compat(
test_config) test_config)

View File

@ -13,7 +13,6 @@
# under the License. # under the License.
import mock import mock
from werkzeug import exceptions
from oslo_utils import uuidutils from oslo_utils import uuidutils
@ -29,13 +28,6 @@ class KeepalivedLvsTestCase(base.TestCase):
super(KeepalivedLvsTestCase, self).setUp() super(KeepalivedLvsTestCase, self).setUp()
self.test_keepalivedlvs = keepalivedlvs.KeepalivedLvs() self.test_keepalivedlvs = keepalivedlvs.KeepalivedLvs()
@mock.patch('os.path.exists')
def test_get_udp_listener_status_no_exists(self, m_exist):
m_exist.return_value = False
self.assertRaises(exceptions.HTTPException,
self.test_keepalivedlvs.get_udp_listener_status,
self.FAKE_ID)
@mock.patch.object(keepalivedlvs, "webob") @mock.patch.object(keepalivedlvs, "webob")
@mock.patch('os.path.exists') @mock.patch('os.path.exists')
def test_delete_udp_listener_not_exist(self, m_exist, m_webob): def test_delete_udp_listener_not_exist(self, m_exist, m_webob):

View File

@ -17,141 +17,35 @@ import subprocess
import mock import mock
from oslo_utils import uuidutils from oslo_utils import uuidutils
from octavia.amphorae.backends.agent.api_server import listener from octavia.amphorae.backends.agent.api_server import loadbalancer
from octavia.amphorae.backends.agent.api_server import util as agent_util from octavia.amphorae.backends.agent.api_server import util as agent_util
from octavia.common import constants as consts from octavia.common import constants as consts
from octavia.common.jinja.haproxy import jinja_cfg
from octavia.tests.common import utils as test_utils from octavia.tests.common import utils as test_utils
import octavia.tests.unit.base as base import octavia.tests.unit.base as base
from octavia.tests.unit.common.sample_configs import sample_configs
BASE_AMP_PATH = '/var/lib/octavia'
BASE_CRT_PATH = BASE_AMP_PATH + '/certs'
LISTENER_ID1 = uuidutils.generate_uuid() LISTENER_ID1 = uuidutils.generate_uuid()
LB_ID1 = uuidutils.generate_uuid()
class ListenerTestCase(base.TestCase): class ListenerTestCase(base.TestCase):
def setUp(self): def setUp(self):
super(ListenerTestCase, self).setUp() super(ListenerTestCase, self).setUp()
self.jinja_cfg = jinja_cfg.JinjaTemplater(
base_amp_path=BASE_AMP_PATH,
base_crt_dir=BASE_CRT_PATH)
self.mock_platform = mock.patch("distro.id").start() self.mock_platform = mock.patch("distro.id").start()
self.mock_platform.return_value = "ubuntu" self.mock_platform.return_value = "ubuntu"
self.test_listener = listener.Listener() self.test_loadbalancer = loadbalancer.Loadbalancer()
def test_parse_haproxy_config(self):
# template_tls
tls_tupe = sample_configs.sample_tls_container_tuple(
id='tls_container_id',
certificate='imaCert1', private_key='imaPrivateKey1',
primary_cn='FakeCN')
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(),
sample_configs.sample_listener_tuple(proto='TERMINATED_HTTPS',
tls=True, sni=True),
tls_tupe)
path = agent_util.config_path(LISTENER_ID1)
self.useFixture(test_utils.OpenFixture(path, rendered_obj))
res = self.test_listener._parse_haproxy_file(LISTENER_ID1)
self.assertEqual('TERMINATED_HTTPS', res['mode'])
self.assertEqual('/var/lib/octavia/sample_listener_id_1.sock',
res['stats_socket'])
self.assertEqual(
'/var/lib/octavia/certs/sample_listener_id_1/tls_container_id.pem',
res['ssl_crt'])
# render_template_tls_no_sni
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(),
sample_configs.sample_listener_tuple(
proto='TERMINATED_HTTPS', tls=True),
tls_cert=sample_configs.sample_tls_container_tuple(
id='tls_container_id',
certificate='ImAalsdkfjCert',
private_key='ImAsdlfksdjPrivateKey',
primary_cn="FakeCN"))
self.useFixture(test_utils.OpenFixture(path, rendered_obj))
res = self.test_listener._parse_haproxy_file(LISTENER_ID1)
self.assertEqual('TERMINATED_HTTPS', res['mode'])
self.assertEqual(BASE_AMP_PATH + '/sample_listener_id_1.sock',
res['stats_socket'])
self.assertEqual(
BASE_CRT_PATH + '/sample_listener_id_1/tls_container_id.pem',
res['ssl_crt'])
# render_template_http
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(),
sample_configs.sample_listener_tuple())
self.useFixture(test_utils.OpenFixture(path, rendered_obj))
res = self.test_listener._parse_haproxy_file(LISTENER_ID1)
self.assertEqual('HTTP', res['mode'])
self.assertEqual(BASE_AMP_PATH + '/sample_listener_id_1.sock',
res['stats_socket'])
self.assertIsNone(res['ssl_crt'])
# template_https
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(),
sample_configs.sample_listener_tuple(proto='HTTPS'))
self.useFixture(test_utils.OpenFixture(path, rendered_obj))
res = self.test_listener._parse_haproxy_file(LISTENER_ID1)
self.assertEqual('TCP', res['mode'])
self.assertEqual(BASE_AMP_PATH + '/sample_listener_id_1.sock',
res['stats_socket'])
self.assertIsNone(res['ssl_crt'])
# Bogus format
self.useFixture(test_utils.OpenFixture(path, 'Bogus'))
try:
res = self.test_listener._parse_haproxy_file(LISTENER_ID1)
self.fail("No Exception?")
except listener.ParsingError:
pass
@mock.patch('os.path.exists')
@mock.patch('octavia.amphorae.backends.agent.api_server' +
'.util.get_haproxy_pid')
def test_check_listener_status(self, mock_pid, mock_exists):
mock_pid.return_value = '1245'
mock_exists.side_effect = [True, True]
config_path = agent_util.config_path(LISTENER_ID1)
file_contents = 'frontend {}'.format(LISTENER_ID1)
self.useFixture(test_utils.OpenFixture(config_path, file_contents))
self.assertEqual(
consts.ACTIVE,
self.test_listener._check_listener_status(LISTENER_ID1))
mock_exists.side_effect = [True, False]
self.assertEqual(
consts.ERROR,
self.test_listener._check_listener_status(LISTENER_ID1))
mock_exists.side_effect = [False]
self.assertEqual(
consts.OFFLINE,
self.test_listener._check_listener_status(LISTENER_ID1))
@mock.patch('os.makedirs') @mock.patch('os.makedirs')
@mock.patch('os.path.exists') @mock.patch('os.path.exists')
@mock.patch('os.listdir') @mock.patch('os.listdir')
@mock.patch('os.path.join') @mock.patch('os.path.join')
@mock.patch('octavia.amphorae.backends.agent.api_server.util.' @mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'get_listeners') 'get_loadbalancers')
@mock.patch('octavia.amphorae.backends.agent.api_server.util' @mock.patch('octavia.amphorae.backends.agent.api_server.util'
'.haproxy_sock_path') '.haproxy_sock_path')
def test_vrrp_check_script_update(self, mock_sock_path, mock_get_listeners, def test_vrrp_check_script_update(self, mock_sock_path, mock_get_lbs,
mock_join, mock_listdir, mock_exists, mock_join, mock_listdir, mock_exists,
mock_makedirs): mock_makedirs):
mock_get_listeners.return_value = ['abc', LISTENER_ID1] mock_get_lbs.return_value = ['abc', LB_ID1]
mock_sock_path.return_value = 'listener.sock' mock_sock_path.return_value = 'listener.sock'
mock_exists.return_value = False mock_exists.return_value = False
cmd = 'haproxy-vrrp-check ' + ' '.join(['listener.sock']) + '; exit $?' cmd = 'haproxy-vrrp-check ' + ' '.join(['listener.sock']) + '; exit $?'
@ -159,17 +53,17 @@ class ListenerTestCase(base.TestCase):
path = agent_util.keepalived_dir() path = agent_util.keepalived_dir()
m = self.useFixture(test_utils.OpenFixture(path)).mock_open m = self.useFixture(test_utils.OpenFixture(path)).mock_open
self.test_listener.vrrp_check_script_update(LISTENER_ID1, 'stop') self.test_loadbalancer.vrrp_check_script_update(LB_ID1, 'stop')
handle = m() handle = m()
handle.write.assert_called_once_with(cmd) handle.write.assert_called_once_with(cmd)
mock_get_listeners.return_value = ['abc', LISTENER_ID1] mock_get_lbs.return_value = ['abc', LB_ID1]
cmd = ('haproxy-vrrp-check ' + ' '.join(['listener.sock', cmd = ('haproxy-vrrp-check ' + ' '.join(['listener.sock',
'listener.sock']) + '; exit ' 'listener.sock']) + '; exit '
'$?') '$?')
m = self.useFixture(test_utils.OpenFixture(path)).mock_open m = self.useFixture(test_utils.OpenFixture(path)).mock_open
self.test_listener.vrrp_check_script_update(LISTENER_ID1, 'start') self.test_loadbalancer.vrrp_check_script_update(LB_ID1, 'start')
handle = m() handle = m()
handle.write.assert_called_once_with(cmd) handle.write.assert_called_once_with(cmd)
@ -181,29 +75,29 @@ class ListenerTestCase(base.TestCase):
mock_exists.side_effect = [True, True] mock_exists.side_effect = [True, True]
self.assertEqual( self.assertEqual(
consts.ACTIVE, consts.ACTIVE,
self.test_listener._check_haproxy_status(LISTENER_ID1)) self.test_loadbalancer._check_haproxy_status(LISTENER_ID1))
mock_exists.side_effect = [True, False] mock_exists.side_effect = [True, False]
self.assertEqual( self.assertEqual(
consts.OFFLINE, consts.OFFLINE,
self.test_listener._check_haproxy_status(LISTENER_ID1)) self.test_loadbalancer._check_haproxy_status(LISTENER_ID1))
mock_exists.side_effect = [False] mock_exists.side_effect = [False]
self.assertEqual( self.assertEqual(
consts.OFFLINE, consts.OFFLINE,
self.test_listener._check_haproxy_status(LISTENER_ID1)) self.test_loadbalancer._check_haproxy_status(LISTENER_ID1))
@mock.patch('octavia.amphorae.backends.agent.api_server.listener.Listener.' @mock.patch('octavia.amphorae.backends.agent.api_server.loadbalancer.'
'_check_haproxy_status') 'Loadbalancer._check_haproxy_status')
@mock.patch('octavia.amphorae.backends.agent.api_server.listener.Listener.' @mock.patch('octavia.amphorae.backends.agent.api_server.loadbalancer.'
'vrrp_check_script_update') 'Loadbalancer.vrrp_check_script_update')
@mock.patch('os.path.exists') @mock.patch('os.path.exists')
@mock.patch('octavia.amphorae.backends.agent.api_server.listener.Listener.' @mock.patch('octavia.amphorae.backends.agent.api_server.loadbalancer.'
'_check_listener_exists') 'Loadbalancer._check_lb_exists')
@mock.patch('subprocess.check_output') @mock.patch('subprocess.check_output')
def test_start_stop_listener(self, mock_check_output, mock_list_exists, def test_start_stop_lb(self, mock_check_output, mock_lb_exists,
mock_path_exists, mock_vrrp_update, mock_path_exists, mock_vrrp_update,
mock_check_status): mock_check_status):
listener_id = uuidutils.generate_uuid() listener_id = uuidutils.generate_uuid()
mock_path_exists.side_effect = [False, True, True, False, False] mock_path_exists.side_effect = [False, True, True, False, False]
@ -214,12 +108,12 @@ class ListenerTestCase(base.TestCase):
ref_command_split.append('haproxy-{}'.format(listener_id)) ref_command_split.append('haproxy-{}'.format(listener_id))
ref_command_split.append(consts.AMP_ACTION_START) ref_command_split.append(consts.AMP_ACTION_START)
result = self.test_listener.start_stop_listener( result = self.test_loadbalancer.start_stop_lb(
listener_id, consts.AMP_ACTION_START) listener_id, consts.AMP_ACTION_START)
mock_check_output.assert_called_once_with(ref_command_split, mock_check_output.assert_called_once_with(ref_command_split,
stderr=subprocess.STDOUT) stderr=subprocess.STDOUT)
mock_list_exists.assert_called_once_with(listener_id) mock_lb_exists.assert_called_once_with(listener_id)
mock_vrrp_update.assert_not_called() mock_vrrp_update.assert_not_called()
self.assertEqual(202, result.status_code) self.assertEqual(202, result.status_code)
self.assertEqual('OK', result.json['message']) self.assertEqual('OK', result.json['message'])
@ -228,7 +122,7 @@ class ListenerTestCase(base.TestCase):
self.assertEqual(ref_details, result.json['details']) self.assertEqual(ref_details, result.json['details'])
# Happy path - VRRP - RELOAD # Happy path - VRRP - RELOAD
mock_list_exists.reset_mock() mock_lb_exists.reset_mock()
mock_vrrp_update.reset_mock() mock_vrrp_update.reset_mock()
mock_check_output.reset_mock() mock_check_output.reset_mock()
@ -236,12 +130,12 @@ class ListenerTestCase(base.TestCase):
ref_command_split.append('haproxy-{}'.format(listener_id)) ref_command_split.append('haproxy-{}'.format(listener_id))
ref_command_split.append(consts.AMP_ACTION_RELOAD) ref_command_split.append(consts.AMP_ACTION_RELOAD)
result = self.test_listener.start_stop_listener( result = self.test_loadbalancer.start_stop_lb(
listener_id, consts.AMP_ACTION_RELOAD) listener_id, consts.AMP_ACTION_RELOAD)
mock_check_output.assert_called_once_with(ref_command_split, mock_check_output.assert_called_once_with(ref_command_split,
stderr=subprocess.STDOUT) stderr=subprocess.STDOUT)
mock_list_exists.assert_called_once_with(listener_id) mock_lb_exists.assert_called_once_with(listener_id)
mock_vrrp_update.assert_called_once_with(listener_id, mock_vrrp_update.assert_called_once_with(listener_id,
consts.AMP_ACTION_RELOAD) consts.AMP_ACTION_RELOAD)
self.assertEqual(202, result.status_code) self.assertEqual(202, result.status_code)
@ -251,7 +145,7 @@ class ListenerTestCase(base.TestCase):
self.assertEqual(ref_details, result.json['details']) self.assertEqual(ref_details, result.json['details'])
# Happy path - VRRP - RELOAD - OFFLINE # Happy path - VRRP - RELOAD - OFFLINE
mock_list_exists.reset_mock() mock_lb_exists.reset_mock()
mock_vrrp_update.reset_mock() mock_vrrp_update.reset_mock()
mock_check_output.reset_mock() mock_check_output.reset_mock()
@ -259,12 +153,12 @@ class ListenerTestCase(base.TestCase):
ref_command_split.append('haproxy-{}'.format(listener_id)) ref_command_split.append('haproxy-{}'.format(listener_id))
ref_command_split.append(consts.AMP_ACTION_START) ref_command_split.append(consts.AMP_ACTION_START)
result = self.test_listener.start_stop_listener( result = self.test_loadbalancer.start_stop_lb(
listener_id, consts.AMP_ACTION_RELOAD) listener_id, consts.AMP_ACTION_RELOAD)
mock_check_output.assert_called_once_with(ref_command_split, mock_check_output.assert_called_once_with(ref_command_split,
stderr=subprocess.STDOUT) stderr=subprocess.STDOUT)
mock_list_exists.assert_called_once_with(listener_id) mock_lb_exists.assert_called_once_with(listener_id)
mock_vrrp_update.assert_called_once_with(listener_id, mock_vrrp_update.assert_called_once_with(listener_id,
consts.AMP_ACTION_RELOAD) consts.AMP_ACTION_RELOAD)
self.assertEqual(202, result.status_code) self.assertEqual(202, result.status_code)
@ -274,7 +168,7 @@ class ListenerTestCase(base.TestCase):
self.assertEqual(ref_details, result.json['details']) self.assertEqual(ref_details, result.json['details'])
# Unhappy path - Not already running # Unhappy path - Not already running
mock_list_exists.reset_mock() mock_lb_exists.reset_mock()
mock_vrrp_update.reset_mock() mock_vrrp_update.reset_mock()
mock_check_output.reset_mock() mock_check_output.reset_mock()
@ -285,12 +179,12 @@ class ListenerTestCase(base.TestCase):
mock_check_output.side_effect = subprocess.CalledProcessError( mock_check_output.side_effect = subprocess.CalledProcessError(
output=b'bogus', returncode=-2, cmd='sit') output=b'bogus', returncode=-2, cmd='sit')
result = self.test_listener.start_stop_listener( result = self.test_loadbalancer.start_stop_lb(
listener_id, consts.AMP_ACTION_START) listener_id, consts.AMP_ACTION_START)
mock_check_output.assert_called_once_with(ref_command_split, mock_check_output.assert_called_once_with(ref_command_split,
stderr=subprocess.STDOUT) stderr=subprocess.STDOUT)
mock_list_exists.assert_called_once_with(listener_id) mock_lb_exists.assert_called_once_with(listener_id)
mock_vrrp_update.assert_not_called() mock_vrrp_update.assert_not_called()
self.assertEqual(500, result.status_code) self.assertEqual(500, result.status_code)
self.assertEqual('Error {}ing haproxy'.format(consts.AMP_ACTION_START), self.assertEqual('Error {}ing haproxy'.format(consts.AMP_ACTION_START),
@ -298,7 +192,7 @@ class ListenerTestCase(base.TestCase):
self.assertEqual('bogus', result.json['details']) self.assertEqual('bogus', result.json['details'])
# Unhappy path - Already running # Unhappy path - Already running
mock_list_exists.reset_mock() mock_lb_exists.reset_mock()
mock_vrrp_update.reset_mock() mock_vrrp_update.reset_mock()
mock_check_output.reset_mock() mock_check_output.reset_mock()
@ -309,12 +203,12 @@ class ListenerTestCase(base.TestCase):
mock_check_output.side_effect = subprocess.CalledProcessError( mock_check_output.side_effect = subprocess.CalledProcessError(
output=b'Job is already running', returncode=-2, cmd='sit') output=b'Job is already running', returncode=-2, cmd='sit')
result = self.test_listener.start_stop_listener( result = self.test_loadbalancer.start_stop_lb(
listener_id, consts.AMP_ACTION_START) listener_id, consts.AMP_ACTION_START)
mock_check_output.assert_called_once_with(ref_command_split, mock_check_output.assert_called_once_with(ref_command_split,
stderr=subprocess.STDOUT) stderr=subprocess.STDOUT)
mock_list_exists.assert_called_once_with(listener_id) mock_lb_exists.assert_called_once_with(listener_id)
mock_vrrp_update.assert_not_called() mock_vrrp_update.assert_not_called()
self.assertEqual(202, result.status_code) self.assertEqual(202, result.status_code)
self.assertEqual('OK', result.json['message']) self.assertEqual('OK', result.json['message'])
@ -324,14 +218,62 @@ class ListenerTestCase(base.TestCase):
# Invalid action # Invalid action
mock_check_output.reset_mock() mock_check_output.reset_mock()
mock_list_exists.reset_mock() mock_lb_exists.reset_mock()
mock_path_exists.reset_mock() mock_path_exists.reset_mock()
mock_vrrp_update.reset_mock() mock_vrrp_update.reset_mock()
result = self.test_listener.start_stop_listener(listener_id, 'bogus') result = self.test_loadbalancer.start_stop_lb(listener_id, 'bogus')
self.assertEqual(400, result.status_code) self.assertEqual(400, result.status_code)
self.assertEqual('Invalid Request', result.json['message']) self.assertEqual('Invalid Request', result.json['message'])
self.assertEqual('Unknown action: bogus', result.json['details']) self.assertEqual('Unknown action: bogus', result.json['details'])
mock_list_exists.assert_not_called() mock_lb_exists.assert_not_called()
mock_path_exists.assert_not_called() mock_path_exists.assert_not_called()
mock_vrrp_update.assert_not_called() mock_vrrp_update.assert_not_called()
mock_check_output.assert_not_called() mock_check_output.assert_not_called()
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'config_path')
@mock.patch('octavia.amphorae.backends.agent.api_server.util.'
'get_haproxy_pid')
@mock.patch('os.path.exists')
def test_get_listeners_on_lb(self, mock_exists, mock_get_haproxy_pid,
mock_config_path):
fake_cfg_path = '/some/fake/cfg/file.cfg'
mock_config_path.return_value = fake_cfg_path
mock_get_haproxy_pid.return_value = 'fake_pid'
# Finds two listeners
mock_exists.side_effect = [True, True]
fake_cfg_data = 'frontend list1\nbackend foo\nfrontend list2'
self.useFixture(
test_utils.OpenFixture(fake_cfg_path, fake_cfg_data)).mock_open
result = self.test_loadbalancer._get_listeners_on_lb(LB_ID1)
self.assertEqual(['list1', 'list2'], result)
mock_exists.assert_has_calls([mock.call(agent_util.pid_path(LB_ID1)),
mock.call('/proc/fake_pid')])
# No PID file, no listeners
mock_exists.reset_mock()
mock_exists.side_effect = [False]
result = self.test_loadbalancer._get_listeners_on_lb(LB_ID1)
self.assertEqual([], result)
mock_exists.assert_called_once_with(agent_util.pid_path(LB_ID1))
# PID file, no running process, no listeners
mock_exists.reset_mock()
mock_exists.side_effect = [True, False]
result = self.test_loadbalancer._get_listeners_on_lb(LB_ID1)
self.assertEqual([], result)
mock_exists.assert_has_calls([mock.call(agent_util.pid_path(LB_ID1)),
mock.call('/proc/fake_pid')])
# PID file, running process, no listeners
mock_exists.reset_mock()
mock_exists.side_effect = [True, True]
fake_cfg_data = 'backend only'
self.useFixture(
test_utils.OpenFixture(fake_cfg_path, fake_cfg_data)).mock_open
result = self.test_loadbalancer._get_listeners_on_lb(LB_ID1)
self.assertEqual([], result)
mock_exists.assert_has_calls([mock.call(agent_util.pid_path(LB_ID1)),
mock.call('/proc/fake_pid')])

View File

@ -22,11 +22,15 @@ from oslo_utils import uuidutils
from octavia.amphorae.backends.agent.api_server import util from octavia.amphorae.backends.agent.api_server import util
from octavia.common import constants as consts from octavia.common import constants as consts
from octavia.common.jinja.haproxy.combined_listeners import jinja_cfg
from octavia.tests.common import utils as test_utils from octavia.tests.common import utils as test_utils
import octavia.tests.unit.base as base import octavia.tests.unit.base as base
from octavia.tests.unit.common.sample_configs import sample_configs_combined
BASE_AMP_PATH = '/var/lib/octavia'
BASE_CRT_PATH = BASE_AMP_PATH + '/certs'
CONF = cfg.CONF CONF = cfg.CONF
LISTENER_ID1 = uuidutils.generate_uuid()
class TestUtil(base.TestCase): class TestUtil(base.TestCase):
@ -34,6 +38,9 @@ class TestUtil(base.TestCase):
super(TestUtil, self).setUp() super(TestUtil, self).setUp()
self.CONF = self.useFixture(oslo_fixture.Config(cfg.CONF)) self.CONF = self.useFixture(oslo_fixture.Config(cfg.CONF))
self.listener_id = uuidutils.generate_uuid() self.listener_id = uuidutils.generate_uuid()
self.jinja_cfg = jinja_cfg.JinjaTemplater(
base_amp_path=BASE_AMP_PATH,
base_crt_dir=BASE_CRT_PATH)
def test_keepalived_lvs_dir(self): def test_keepalived_lvs_dir(self):
fake_path = '/fake/path' fake_path = '/fake/path'
@ -171,7 +178,7 @@ class TestUtil(base.TestCase):
mock_cfg_path.return_value = '/there' mock_cfg_path.return_value = '/there'
mock_path_exists.side_effect = [True, False, True, False, False] mock_path_exists.side_effect = [True, False, True, False, False]
result = util.get_listener_protocol('1') result = util.get_protocol_for_lb_object('1')
mock_cfg_path.assert_called_once_with('1') mock_cfg_path.assert_called_once_with('1')
mock_path_exists.assert_called_once_with('/there') mock_path_exists.assert_called_once_with('/there')
@ -180,7 +187,7 @@ class TestUtil(base.TestCase):
mock_cfg_path.reset_mock() mock_cfg_path.reset_mock()
result = util.get_listener_protocol('2') result = util.get_protocol_for_lb_object('2')
mock_cfg_path.assert_called_once_with('2') mock_cfg_path.assert_called_once_with('2')
mock_lvs_path.assert_called_once_with('2') mock_lvs_path.assert_called_once_with('2')
@ -189,8 +196,97 @@ class TestUtil(base.TestCase):
mock_cfg_path.reset_mock() mock_cfg_path.reset_mock()
mock_lvs_path.reset_mock() mock_lvs_path.reset_mock()
result = util.get_listener_protocol('3') result = util.get_protocol_for_lb_object('3')
mock_cfg_path.assert_called_once_with('3') mock_cfg_path.assert_called_once_with('3')
mock_lvs_path.assert_called_once_with('3') mock_lvs_path.assert_called_once_with('3')
self.assertIsNone(result) self.assertIsNone(result)
def test_parse_haproxy_config(self):
# template_tls
tls_tupe = sample_configs_combined.sample_tls_container_tuple(
id='tls_container_id',
certificate='imaCert1', private_key='imaPrivateKey1',
primary_cn='FakeCN')
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs_combined.sample_amphora_tuple(),
[sample_configs_combined.sample_listener_tuple(
proto='TERMINATED_HTTPS', tls=True, sni=True)],
tls_tupe)
path = util.config_path(LISTENER_ID1)
self.useFixture(test_utils.OpenFixture(path, rendered_obj))
res = util.parse_haproxy_file(LISTENER_ID1)
listener_dict = res[1]['sample_listener_id_1']
self.assertEqual('TERMINATED_HTTPS', listener_dict['mode'])
self.assertEqual('/var/lib/octavia/sample_loadbalancer_id_1.sock',
res[0])
self.assertEqual(
'/var/lib/octavia/certs/sample_loadbalancer_id_1/'
'tls_container_id.pem crt /var/lib/octavia/certs/'
'sample_loadbalancer_id_1',
listener_dict['ssl_crt'])
# render_template_tls_no_sni
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs_combined.sample_amphora_tuple(),
[sample_configs_combined.sample_listener_tuple(
proto='TERMINATED_HTTPS', tls=True)],
tls_cert=sample_configs_combined.sample_tls_container_tuple(
id='tls_container_id',
certificate='ImAalsdkfjCert',
private_key='ImAsdlfksdjPrivateKey',
primary_cn="FakeCN"))
self.useFixture(test_utils.OpenFixture(path, rendered_obj))
res = util.parse_haproxy_file(LISTENER_ID1)
listener_dict = res[1]['sample_listener_id_1']
self.assertEqual('TERMINATED_HTTPS', listener_dict['mode'])
self.assertEqual(BASE_AMP_PATH + '/sample_loadbalancer_id_1.sock',
res[0])
self.assertEqual(
BASE_CRT_PATH + '/sample_loadbalancer_id_1/tls_container_id.pem',
listener_dict['ssl_crt'])
# render_template_http
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs_combined.sample_amphora_tuple(),
[sample_configs_combined.sample_listener_tuple()])
self.useFixture(test_utils.OpenFixture(path, rendered_obj))
res = util.parse_haproxy_file(LISTENER_ID1)
listener_dict = res[1]['sample_listener_id_1']
self.assertEqual('HTTP', listener_dict['mode'])
self.assertEqual(BASE_AMP_PATH + '/sample_loadbalancer_id_1.sock',
res[0])
self.assertIsNone(listener_dict.get('ssl_crt', None))
# template_https
rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs_combined.sample_amphora_tuple(),
[sample_configs_combined.sample_listener_tuple(proto='HTTPS')])
self.useFixture(test_utils.OpenFixture(path, rendered_obj))
res = util.parse_haproxy_file(LISTENER_ID1)
listener_dict = res[1]['sample_listener_id_1']
self.assertEqual('TCP', listener_dict['mode'])
self.assertEqual(BASE_AMP_PATH + '/sample_loadbalancer_id_1.sock',
res[0])
self.assertIsNone(listener_dict.get('ssl_crt', None))
# Bogus format
self.useFixture(test_utils.OpenFixture(path, 'Bogus'))
try:
res = util.parse_haproxy_file(LISTENER_ID1)
self.fail("No Exception?")
except util.ParsingError:
pass
# Bad listener mode
fake_cfg = 'stats socket foo\nfrontend {}\nmode\n'.format(LISTENER_ID1)
self.useFixture(test_utils.OpenFixture(path, fake_cfg))
self.assertRaises(util.ParsingError, util.parse_haproxy_file,
LISTENER_ID1)

View File

@ -52,7 +52,7 @@ SAMPLE_STATS = ({'': '', 'status': 'OPEN', 'lastchg': '',
'rate': '0', 'req_rate': '0', 'check_status': '', 'rate': '0', 'req_rate': '0', 'check_status': '',
'econ': '', 'comp_out': '0', 'wredis': '', 'dresp': '0', 'econ': '', 'comp_out': '0', 'wredis': '', 'dresp': '0',
'ereq': '0', 'tracked': '', 'comp_in': '0', 'ereq': '0', 'tracked': '', 'comp_in': '0',
'pxname': '490b6ae7-21aa-43f1-b82a-68ddcd2ca2fb', 'pxname': LISTENER_ID1,
'dreq': '0', 'hrsp_5xx': '0', 'last_chk': '', 'dreq': '0', 'hrsp_5xx': '0', 'last_chk': '',
'check_code': '', 'sid': '0', 'bout': '0', 'hrsp_1xx': '0', 'check_code': '', 'sid': '0', 'bout': '0', 'hrsp_1xx': '0',
'qlimit': '', 'hrsp_other': '0', 'bin': '0', 'rtime': '', 'qlimit': '', 'hrsp_other': '0', 'bin': '0', 'rtime': '',
@ -107,25 +107,16 @@ SAMPLE_STATS = ({'': '', 'status': 'OPEN', 'lastchg': '',
SAMPLE_STATS_MSG = { SAMPLE_STATS_MSG = {
'listeners': { 'listeners': {
LISTENER_ID1: { LISTENER_ID1: {
'pools': {
'432fc8b3-d446-48d4-bb64-13beb90e22bc': {
'members': {
'302e33d9-dee1-4de9-98d5-36329a06fb58': 'DOWN'},
'status': 'UP'}},
'stats': { 'stats': {
'totconns': 0, 'conns': 0, 'totconns': 0, 'conns': 0,
'tx': 0, 'rx': 0, 'ereq': 0}, 'tx': 0, 'rx': 0, 'ereq': 0},
'status': 'OPEN'}, 'status': 'OPEN'},
LISTENER_ID2: { },
'pools': { 'pools': {'432fc8b3-d446-48d4-bb64-13beb90e22bc': {
'432fc8b3-d446-48d4-bb64-13beb90e22bc': { 'members': {'302e33d9-dee1-4de9-98d5-36329a06fb58': 'DOWN'},
'members': { 'status': 'UP'}, '432fc8b3-d446-48d4-bb64-13beb90e22bc': {
'302e33d9-dee1-4de9-98d5-36329a06fb58': 'DOWN'}, 'members': {'302e33d9-dee1-4de9-98d5-36329a06fb58': 'DOWN'},
'status': 'UP'}}, 'status': 'UP'},
'stats': {
'totconns': 0, 'conns': 0,
'tx': 0, 'rx': 0, 'ereq': 0},
'status': 'OPEN'}
}, },
'id': None, 'id': None,
'seq': 0, 'seq': 0,
@ -141,7 +132,7 @@ class TestHealthDaemon(base.TestCase):
conf.config(group="haproxy_amphora", base_path=BASE_PATH) conf.config(group="haproxy_amphora", base_path=BASE_PATH)
@mock.patch('octavia.amphorae.backends.agent.' @mock.patch('octavia.amphorae.backends.agent.'
'api_server.util.get_listeners') 'api_server.util.get_loadbalancers')
def test_list_sock_stat_files(self, mock_get_listener): def test_list_sock_stat_files(self, mock_get_listener):
mock_get_listener.return_value = LISTENER_IDS mock_get_listener.return_value = LISTENER_IDS
@ -297,7 +288,7 @@ class TestHealthDaemon(base.TestCase):
stats_query_mock.get_pool_status.assert_called_once_with() stats_query_mock.get_pool_status.assert_called_once_with()
@mock.patch('octavia.amphorae.backends.agent.api_server.' @mock.patch('octavia.amphorae.backends.agent.api_server.'
'util.is_listener_running') 'util.is_lb_running')
@mock.patch('octavia.amphorae.backends.health_daemon.' @mock.patch('octavia.amphorae.backends.health_daemon.'
'health_daemon.get_stats') 'health_daemon.get_stats')
@mock.patch('octavia.amphorae.backends.health_daemon.' @mock.patch('octavia.amphorae.backends.health_daemon.'
@ -318,7 +309,7 @@ class TestHealthDaemon(base.TestCase):
mock_get_stats.assert_any_call('TEST2') mock_get_stats.assert_any_call('TEST2')
@mock.patch('octavia.amphorae.backends.agent.api_server.' @mock.patch('octavia.amphorae.backends.agent.api_server.'
'util.is_listener_running') 'util.is_lb_running')
@mock.patch('octavia.amphorae.backends.health_daemon.' @mock.patch('octavia.amphorae.backends.health_daemon.'
'health_daemon.get_stats') 'health_daemon.get_stats')
@mock.patch('octavia.amphorae.backends.health_daemon.' @mock.patch('octavia.amphorae.backends.health_daemon.'
@ -336,25 +327,6 @@ class TestHealthDaemon(base.TestCase):
self.assertEqual(1, mock_get_stats.call_count) self.assertEqual(1, mock_get_stats.call_count)
@mock.patch('octavia.amphorae.backends.agent.api_server.'
'util.is_listener_running')
@mock.patch('octavia.amphorae.backends.health_daemon.'
'health_daemon.get_stats')
@mock.patch('octavia.amphorae.backends.health_daemon.'
'health_daemon.list_sock_stat_files')
def test_build_stats_message_mismatch_pool(self, mock_list_files,
mock_get_stats,
mock_is_running):
mock_list_files.return_value = {LISTENER_ID1: 'TEST',
LISTENER_ID2: 'TEST2'}
mock_is_running.return_value = True
mock_get_stats.return_value = SAMPLE_STATS, SAMPLE_BOGUS_POOL_STATUS
msg = health_daemon.build_stats_message()
self.assertEqual({}, msg['listeners'][LISTENER_ID1]['pools'])
@mock.patch("octavia.amphorae.backends.utils.keepalivedlvs_query." @mock.patch("octavia.amphorae.backends.utils.keepalivedlvs_query."
"get_udp_listener_pool_status") "get_udp_listener_pool_status")
@mock.patch("octavia.amphorae.backends.utils.keepalivedlvs_query." @mock.patch("octavia.amphorae.backends.utils.keepalivedlvs_query."
@ -411,7 +383,7 @@ class TestHealthDaemon(base.TestCase):
'status': constants.DOWN, 'status': constants.DOWN,
'pools': {}, 'pools': {},
'stats': {'conns': 0, 'totconns': 0, 'ereq': 0, 'stats': {'conns': 0, 'totconns': 0, 'ereq': 0,
'rx': 0, 'tx': 0}}}, 'id': None, 'rx': 0, 'tx': 0}}}, 'pools': {}, 'id': None,
'seq': mock.ANY, 'ver': health_daemon.MSG_VER} 'seq': mock.ANY, 'ver': health_daemon.MSG_VER}
msg = health_daemon.build_stats_message() msg = health_daemon.build_stats_message()
self.assertEqual(expected, msg) self.assertEqual(expected, msg)

View File

@ -29,22 +29,29 @@ STATS_SOCKET_SAMPLE = (
"_3xx,hrsp_4xx,hrsp_5xx,hrsp_other,hanafail,req_rate,req_rate_max,req_tot" "_3xx,hrsp_4xx,hrsp_5xx,hrsp_other,hanafail,req_rate,req_rate_max,req_tot"
",cli_abrt,srv_abrt,comp_in,comp_out,comp_byp,comp_rsp,lastsess,last_chk," ",cli_abrt,srv_abrt,comp_in,comp_out,comp_byp,comp_rsp,lastsess,last_chk,"
"last_agt,qtime,ctime,rtime,ttime,\n" "last_agt,qtime,ctime,rtime,ttime,\n"
"http-servers,id-34821,0,0,0,0,,0,0,0,,0,,0,0,0,0,DOWN,1,1,0,1,1,575,575" "http-servers:listener-id,id-34821,0,0,0,0,,0,0,0,,0,,0,0,0,0,DOWN,1,1,0,"
",,1,3,1,,0,,2,0,,0,L4TOUT,,30001,0,0,0,0,0,0,0,,,,0,0,,,,,-1,,,0,0,0,0,\n" "1,1,575,575,,1,3,1,,0,,2,0,,0,L4TOUT,,30001,0,0,0,0,0,0,0,,,,0,0,,,,,-1,,"
"http-servers,id-34824,0,0,0,0,,0,0,0,,0,,0,0,0,0,DOWN,1,1,0,1,1,567,567," ",0,0,0,0,\n"
",1,3,2,,0,,2,0,,0,L4TOUT,,30001,0,0,0,0,0,0,0,,,,0,0,,,,,-1,,,0,0,0,0,\n" "http-servers:listener-id,id-34824,0,0,0,0,,0,0,0,,0,,0,0,0,0,DOWN,1,1,0,"
"http-servers,BACKEND,0,0,0,0,200,0,0,0,0,0,,0,0,0,0,DOWN,0,0,0,,1,567,567" "1,1,567,567,,1,3,2,,0,,2,0,,0,L4TOUT,,30001,0,0,0,0,0,0,0,,,,0,0,,,,,-1,,"
",,1,3,0,,0,,1,0,,0,,,,0,0,0,0,0,0,,,,,0,0,0,0,0,0,-1,,,0,0,0,0,\n" ",0,0,0,0,\n"
"tcp-servers,id-34833,0,0,0,0,,0,0,0,,0,,0,0,0,0,UP,1,1,0,1,1,560,560,," "http-servers:listener-id,BACKEND,0,0,0,0,200,0,0,0,0,0,,0,0,0,0,DOWN,0,0,"
"1,5,1,,0,,2,0,,0,L4TOUT,,30000,,,,,,,0,,,,0,0,,,,,-1,,,0,0,0,0,\n" "0,,1,567,567,,1,3,0,,0,,1,0,,0,,,,0,0,0,0,0,0,,,,,0,0,0,0,0,0,-1,,,0,0,0,"
"tcp-servers,id-34836,0,0,0,0,,0,0,0,,0,,0,0,0,0,UP,1,1,0,1,1,552,552,," "0,\n"
"1,5,2,,0,,2,0,,0,L4TOUT,,30001,,,,,,,0,,,,0,0,,,,,-1,,,0,0,0,0,\n" "tcp-servers:listener-id,id-34833,0,0,0,0,,0,0,0,,0,,0,0,0,0,UP,1,1,0,1,1,"
"tcp-servers,id-34839,0,0,0,0,,0,0,0,,0,,0,0,0,0,DRAIN,0,1,0,0,0,552,0,," "560,560,,1,5,1,,0,,2,0,,0,L4TOUT,,30000,,,,,,,0,,,,0,0,,,,,-1,,,0,0,0,0,"
"1,5,2,,0,,2,0,,0,L7OK,,30001,,,,,,,0,,,,0,0,,,,,-1,,,0,0,0,0,\n" "\n"
"tcp-servers,id-34842,0,0,0,0,,0,0,0,,0,,0,0,0,0,MAINT,0,1,0,0,0,552,0,," "tcp-servers:listener-id,id-34836,0,0,0,0,,0,0,0,,0,,0,0,0,0,UP,1,1,0,1,1,"
"1,5,2,,0,,2,0,,0,L7OK,,30001,,,,,,,0,,,,0,0,,,,,-1,,,0,0,0,0,\n" "552,552,,1,5,2,,0,,2,0,,0,L4TOUT,,30001,,,,,,,0,,,,0,0,,,,,-1,,,0,0,0,0,"
"tcp-servers,BACKEND,0,0,0,0,200,0,0,0,0,0,,0,0,0,0,UP,0,0,0,,1,552,552" "\n"
",,1,5,0,,0,,1,0,,0,,,,,,,,,,,,,,0,0,0,0,0,0,-1,,,0,0,0,0," "tcp-servers:listener-id,id-34839,0,0,0,0,,0,0,0,,0,,0,0,0,0,DRAIN,0,1,0,"
"0,0,552,0,,1,5,2,,0,,2,0,,0,L7OK,,30001,,,,,,,0,,,,0,0,,,,,-1,,,0,0,0,0,"
"\n"
"tcp-servers:listener-id,id-34842,0,0,0,0,,0,0,0,,0,,0,0,0,0,MAINT,0,1,0,"
"0,0,552,0,,1,5,2,,0,,2,0,,0,L7OK,,30001,,,,,,,0,,,,0,0,,,,,-1,,,0,0,0,0,"
"\n"
"tcp-servers:listener-id,BACKEND,0,0,0,0,200,0,0,0,0,0,,0,0,0,0,UP,0,0,0,,"
"1,552,552,,1,5,0,,0,,1,0,,0,,,,,,,,,,,,,,0,0,0,0,0,0,-1,,,0,0,0,0,"
) )
INFO_SOCKET_SAMPLE = ( INFO_SOCKET_SAMPLE = (
@ -94,17 +101,19 @@ class QueryTestCase(base.TestCase):
self.q._query = query_mock self.q._query = query_mock
query_mock.return_value = STATS_SOCKET_SAMPLE query_mock.return_value = STATS_SOCKET_SAMPLE
self.assertEqual( self.assertEqual(
{'tcp-servers': { {'tcp-servers:listener-id': {
'status': constants.UP, 'status': constants.UP,
'uuid': 'tcp-servers', 'listener_uuid': 'listener-id',
'pool_uuid': 'tcp-servers',
'members': 'members':
{'id-34833': constants.UP, {'id-34833': constants.UP,
'id-34836': constants.UP, 'id-34836': constants.UP,
'id-34839': constants.DRAIN, 'id-34839': constants.DRAIN,
'id-34842': constants.MAINT}}, 'id-34842': constants.MAINT}},
'http-servers': { 'http-servers:listener-id': {
'status': constants.DOWN, 'status': constants.DOWN,
'uuid': 'http-servers', 'listener_uuid': 'listener-id',
'pool_uuid': 'http-servers',
'members': 'members':
{'id-34821': constants.DOWN, {'id-34821': constants.DOWN,
'id-34824': constants.DOWN}}}, 'id-34824': constants.DOWN}}},

File diff suppressed because it is too large Load Diff

View File

@ -20,18 +20,25 @@ from octavia.amphorae.drivers.keepalived import vrrp_rest_driver
from octavia.common import constants from octavia.common import constants
import octavia.tests.unit.base as base import octavia.tests.unit.base as base
# Version 1.0 is functionally identical to all versions before it
API_VERSION = '1.0'
class TestVRRPRestDriver(base.TestCase): class TestVRRPRestDriver(base.TestCase):
def setUp(self): def setUp(self):
self.keepalived_mixin = vrrp_rest_driver.KeepalivedAmphoraDriverMixin() self.keepalived_mixin = vrrp_rest_driver.KeepalivedAmphoraDriverMixin()
self.keepalived_mixin.client = mock.MagicMock() self.keepalived_mixin.clients = {
self.client = self.keepalived_mixin.client 'base': mock.MagicMock(),
API_VERSION: mock.MagicMock()}
self.keepalived_mixin._populate_amphora_api_version = mock.MagicMock()
self.clients = self.keepalived_mixin.clients
self.FAKE_CONFIG = 'FAKE CONFIG' self.FAKE_CONFIG = 'FAKE CONFIG'
self.lb_mock = mock.MagicMock() self.lb_mock = mock.MagicMock()
self.amphora_mock = mock.MagicMock() self.amphora_mock = mock.MagicMock()
self.amphora_mock.id = uuidutils.generate_uuid() self.amphora_mock.id = uuidutils.generate_uuid()
self.amphora_mock.status = constants.AMPHORA_ALLOCATED self.amphora_mock.status = constants.AMPHORA_ALLOCATED
self.amphora_mock.api_version = API_VERSION
self.lb_mock.amphorae = [self.amphora_mock] self.lb_mock.amphorae = [self.amphora_mock]
self.amphorae_network_config = {} self.amphorae_network_config = {}
vip_subnet = mock.MagicMock() vip_subnet = mock.MagicMock()
@ -49,7 +56,7 @@ class TestVRRPRestDriver(base.TestCase):
self.keepalived_mixin.update_vrrp_conf(self.lb_mock, self.keepalived_mixin.update_vrrp_conf(self.lb_mock,
self.amphorae_network_config) self.amphorae_network_config)
self.client.upload_vrrp_config.assert_called_once_with( self.clients[API_VERSION].upload_vrrp_config.assert_called_once_with(
self.amphora_mock, self.amphora_mock,
self.FAKE_CONFIG) self.FAKE_CONFIG)
@ -57,16 +64,19 @@ class TestVRRPRestDriver(base.TestCase):
self.keepalived_mixin.stop_vrrp_service(self.lb_mock) self.keepalived_mixin.stop_vrrp_service(self.lb_mock)
self.client.stop_vrrp.assert_called_once_with(self.amphora_mock) self.clients[API_VERSION].stop_vrrp.assert_called_once_with(
self.amphora_mock)
def test_start_vrrp_service(self): def test_start_vrrp_service(self):
self.keepalived_mixin.start_vrrp_service(self.lb_mock) self.keepalived_mixin.start_vrrp_service(self.lb_mock)
self.client.start_vrrp.assert_called_once_with(self.amphora_mock) self.clients[API_VERSION].start_vrrp.assert_called_once_with(
self.amphora_mock)
def test_reload_vrrp_service(self): def test_reload_vrrp_service(self):
self.keepalived_mixin.reload_vrrp_service(self.lb_mock) self.keepalived_mixin.reload_vrrp_service(self.lb_mock)
self.client.reload_vrrp.assert_called_once_with(self.amphora_mock) self.clients[API_VERSION].reload_vrrp.assert_called_once_with(
self.amphora_mock)

View File

@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import mock
from oslo_utils import uuidutils from oslo_utils import uuidutils
from octavia.amphorae.drivers.noop_driver import driver from octavia.amphorae.drivers.noop_driver import driver
@ -54,6 +55,7 @@ class TestNoopAmphoraLoadBalancerDriver(base.TestCase):
self.load_balancer = data_models.LoadBalancer( self.load_balancer = data_models.LoadBalancer(
id=FAKE_UUID_1, amphorae=[self.amphora], vip=self.vip, id=FAKE_UUID_1, amphorae=[self.amphora], vip=self.vip,
listeners=[self.listener]) listeners=[self.listener])
self.listener.load_balancer = self.load_balancer
self.network = network_models.Network(id=self.FAKE_UUID_1) self.network = network_models.Network(id=self.FAKE_UUID_1)
self.port = network_models.Port(id=uuidutils.generate_uuid()) self.port = network_models.Port(id=uuidutils.generate_uuid())
self.amphorae_net_configs = { self.amphorae_net_configs = {
@ -70,8 +72,7 @@ class TestNoopAmphoraLoadBalancerDriver(base.TestCase):
constants.CONN_RETRY_INTERVAL: 4} constants.CONN_RETRY_INTERVAL: 4}
def test_update_amphora_listeners(self): def test_update_amphora_listeners(self):
amphorae = [self.amphora] self.driver.update_amphora_listeners(self.load_balancer, self.amphora,
self.driver.update_amphora_listeners([self.listener], 0, amphorae,
self.timeout_dict) self.timeout_dict)
self.assertEqual((self.listener, self.amphora.id, self.timeout_dict, self.assertEqual((self.listener, self.amphora.id, self.timeout_dict,
'update_amp'), 'update_amp'),
@ -80,28 +81,22 @@ class TestNoopAmphoraLoadBalancerDriver(base.TestCase):
self.amphora.id)]) self.amphora.id)])
def test_update(self): def test_update(self):
self.driver.update(self.listener, self.vip) self.driver.update(self.load_balancer)
self.assertEqual((self.listener, self.vip, 'active'), self.assertEqual(([self.listener], self.vip, 'active'),
self.driver.driver.amphoraconfig[( self.driver.driver.amphoraconfig[(
self.listener.protocol_port, (self.listener.protocol_port,),
self.vip.ip_address)])
def test_stop(self):
self.driver.stop(self.listener, self.vip)
self.assertEqual((self.listener, self.vip, 'stop'),
self.driver.driver.amphoraconfig[(
self.listener.protocol_port,
self.vip.ip_address)]) self.vip.ip_address)])
def test_start(self): def test_start(self):
self.driver.start(self.listener, self.vip, amphora='amp1') mock_amphora = mock.MagicMock()
self.assertEqual((self.listener, self.vip, 'amp1', 'start'), mock_amphora.id = '321'
self.driver.start(self.load_balancer, amphora=mock_amphora)
self.assertEqual((self.load_balancer, mock_amphora, 'start'),
self.driver.driver.amphoraconfig[( self.driver.driver.amphoraconfig[(
self.listener.protocol_port, self.load_balancer.id, '321')])
self.vip.ip_address, 'amp1')])
def test_delete(self): def test_delete(self):
self.driver.delete(self.listener, self.vip) self.driver.delete(self.listener)
self.assertEqual((self.listener, self.vip, 'delete'), self.assertEqual((self.listener, self.vip, 'delete'),
self.driver.driver.amphoraconfig[( self.driver.driver.amphoraconfig[(
self.listener.protocol_port, self.listener.protocol_port,

View File

@ -134,7 +134,8 @@ class TestBarbicanManager(base.TestCase):
self.assertIsInstance(data, cert.Cert) self.assertIsInstance(data, cert.Cert)
self.assertEqual(sample.X509_CERT_KEY, data.get_private_key()) self.assertEqual(sample.X509_CERT_KEY, data.get_private_key())
self.assertEqual(sample.X509_CERT, data.get_certificate()) self.assertEqual(sample.X509_CERT, data.get_certificate())
self.assertItemsEqual(sample.X509_IMDS_LIST, data.get_intermediates()) self.assertEqual(sorted(sample.X509_IMDS_LIST),
sorted(data.get_intermediates()))
self.assertIsNone(data.get_private_key_passphrase()) self.assertIsNone(data.get_private_key_passphrase())
def test_delete_cert_legacy(self): def test_delete_cert_legacy(self):

File diff suppressed because it is too large Load Diff

View File

@ -17,9 +17,9 @@ import copy
import os import os
from octavia.common import constants from octavia.common import constants
from octavia.common.jinja.haproxy import jinja_cfg from octavia.common.jinja.haproxy.split_listeners import jinja_cfg
from octavia.tests.unit import base from octavia.tests.unit import base
from octavia.tests.unit.common.sample_configs import sample_configs from octavia.tests.unit.common.sample_configs import sample_configs_split
class TestHaproxyCfg(base.TestCase): class TestHaproxyCfg(base.TestCase):
@ -70,20 +70,19 @@ class TestHaproxyCfg(base.TestCase):
"weight 13 check inter 30s fall 3 rise 2 cookie " "weight 13 check inter 30s fall 3 rise 2 cookie "
"sample_member_id_2\n\n").format( "sample_member_id_2\n\n").format(
maxconn=constants.HAPROXY_MAX_MAXCONN) maxconn=constants.HAPROXY_MAX_MAXCONN)
tls_tupe = sample_configs.sample_tls_container_tuple( tls_tupe = sample_configs_split.sample_tls_container_tuple(
id='tls_container_id', id='tls_container_id',
certificate='imaCert1', private_key='imaPrivateKey1', certificate='imaCert1', private_key='imaPrivateKey1',
primary_cn='FakeCN') primary_cn='FakeCN')
rendered_obj = self.jinja_cfg.render_loadbalancer_obj( rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple(proto='TERMINATED_HTTPS', sample_configs_split.sample_listener_tuple(
tls=True, sni=True, proto='TERMINATED_HTTPS', tls=True, sni=True,
client_ca_cert=True, client_ca_cert=True, client_crl_cert=True),
client_crl_cert=True),
tls_tupe, client_ca_filename='client_ca.pem', tls_tupe, client_ca_filename='client_ca.pem',
client_crl='SHA_ID.pem') client_crl='SHA_ID.pem')
self.assertEqual( self.assertEqual(
sample_configs.sample_base_expected_config( sample_configs_split.sample_base_expected_config(
frontend=fe, backend=be), frontend=fe, backend=be),
rendered_obj) rendered_obj)
@ -121,16 +120,16 @@ class TestHaproxyCfg(base.TestCase):
"cookie sample_member_id_2\n\n").format( "cookie sample_member_id_2\n\n").format(
maxconn=constants.HAPROXY_MAX_MAXCONN) maxconn=constants.HAPROXY_MAX_MAXCONN)
rendered_obj = self.jinja_cfg.render_loadbalancer_obj( rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple( sample_configs_split.sample_listener_tuple(
proto='TERMINATED_HTTPS', tls=True), proto='TERMINATED_HTTPS', tls=True),
tls_cert=sample_configs.sample_tls_container_tuple( tls_cert=sample_configs_split.sample_tls_container_tuple(
id='tls_container_id', id='tls_container_id',
certificate='ImAalsdkfjCert', certificate='ImAalsdkfjCert',
private_key='ImAsdlfksdjPrivateKey', private_key='ImAsdlfksdjPrivateKey',
primary_cn="FakeCN")) primary_cn="FakeCN"))
self.assertEqual( self.assertEqual(
sample_configs.sample_base_expected_config( sample_configs_split.sample_base_expected_config(
frontend=fe, backend=be), frontend=fe, backend=be),
rendered_obj) rendered_obj)
@ -154,10 +153,10 @@ class TestHaproxyCfg(base.TestCase):
"cookie sample_member_id_2\n\n").format( "cookie sample_member_id_2\n\n").format(
maxconn=constants.HAPROXY_MAX_MAXCONN) maxconn=constants.HAPROXY_MAX_MAXCONN)
rendered_obj = self.jinja_cfg.render_loadbalancer_obj( rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple()) sample_configs_split.sample_listener_tuple())
self.assertEqual( self.assertEqual(
sample_configs.sample_base_expected_config(backend=be), sample_configs_split.sample_base_expected_config(backend=be),
rendered_obj) rendered_obj)
def test_render_template_member_backup(self): def test_render_template_member_backup(self):
@ -182,11 +181,11 @@ class TestHaproxyCfg(base.TestCase):
"cookie sample_member_id_2 backup\n\n").format( "cookie sample_member_id_2 backup\n\n").format(
maxconn=constants.HAPROXY_MAX_MAXCONN) maxconn=constants.HAPROXY_MAX_MAXCONN)
rendered_obj = self.jinja_cfg.render_loadbalancer_obj( rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple(monitor_ip_port=True, sample_configs_split.sample_listener_tuple(
backup_member=True)) monitor_ip_port=True, backup_member=True))
self.assertEqual( self.assertEqual(
sample_configs.sample_base_expected_config(backend=be), sample_configs_split.sample_base_expected_config(backend=be),
rendered_obj) rendered_obj)
def test_render_template_custom_timeouts(self): def test_render_template_custom_timeouts(self):
@ -219,13 +218,13 @@ class TestHaproxyCfg(base.TestCase):
"sample_member_id_2\n\n").format( "sample_member_id_2\n\n").format(
maxconn=constants.HAPROXY_MAX_MAXCONN) maxconn=constants.HAPROXY_MAX_MAXCONN)
rendered_obj = self.jinja_cfg.render_loadbalancer_obj( rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple(timeout_member_connect=1, sample_configs_split.sample_listener_tuple(
timeout_client_data=2, timeout_member_connect=1, timeout_client_data=2,
timeout_member_data=3)) timeout_member_data=3))
self.assertEqual( self.assertEqual(
sample_configs.sample_base_expected_config(frontend=fe, sample_configs_split.sample_base_expected_config(
backend=be), frontend=fe, backend=be),
rendered_obj) rendered_obj)
def test_render_template_null_timeouts(self): def test_render_template_null_timeouts(self):
@ -258,13 +257,13 @@ class TestHaproxyCfg(base.TestCase):
"sample_member_id_2\n\n").format( "sample_member_id_2\n\n").format(
maxconn=constants.HAPROXY_MAX_MAXCONN) maxconn=constants.HAPROXY_MAX_MAXCONN)
rendered_obj = self.jinja_cfg.render_loadbalancer_obj( rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple(timeout_member_connect=None, sample_configs_split.sample_listener_tuple(
timeout_client_data=None, timeout_member_connect=None, timeout_client_data=None,
timeout_member_data=None)) timeout_member_data=None))
self.assertEqual( self.assertEqual(
sample_configs.sample_base_expected_config(frontend=fe, sample_configs_split.sample_base_expected_config(
backend=be), frontend=fe, backend=be),
rendered_obj) rendered_obj)
def test_render_template_member_monitor_addr_port(self): def test_render_template_member_monitor_addr_port(self):
@ -289,10 +288,10 @@ class TestHaproxyCfg(base.TestCase):
"cookie sample_member_id_2\n\n").format( "cookie sample_member_id_2\n\n").format(
maxconn=constants.HAPROXY_MAX_MAXCONN) maxconn=constants.HAPROXY_MAX_MAXCONN)
rendered_obj = self.jinja_cfg.render_loadbalancer_obj( rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple(monitor_ip_port=True)) sample_configs_split.sample_listener_tuple(monitor_ip_port=True))
self.assertEqual( self.assertEqual(
sample_configs.sample_base_expected_config(backend=be), sample_configs_split.sample_base_expected_config(backend=be),
rendered_obj) rendered_obj)
def test_render_template_https_real_monitor(self): def test_render_template_https_real_monitor(self):
@ -326,9 +325,9 @@ class TestHaproxyCfg(base.TestCase):
"cookie sample_member_id_2\n\n").format( "cookie sample_member_id_2\n\n").format(
maxconn=constants.HAPROXY_MAX_MAXCONN) maxconn=constants.HAPROXY_MAX_MAXCONN)
rendered_obj = self.jinja_cfg.render_loadbalancer_obj( rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple(proto='HTTPS')) sample_configs_split.sample_listener_tuple(proto='HTTPS'))
self.assertEqual(sample_configs.sample_base_expected_config( self.assertEqual(sample_configs_split.sample_base_expected_config(
frontend=fe, backend=be), rendered_obj) frontend=fe, backend=be), rendered_obj)
def test_render_template_https_hello_monitor(self): def test_render_template_https_hello_monitor(self):
@ -361,10 +360,10 @@ class TestHaproxyCfg(base.TestCase):
"cookie sample_member_id_2\n\n").format( "cookie sample_member_id_2\n\n").format(
maxconn=constants.HAPROXY_MAX_MAXCONN) maxconn=constants.HAPROXY_MAX_MAXCONN)
rendered_obj = self.jinja_cfg.render_loadbalancer_obj( rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple( sample_configs_split.sample_listener_tuple(
proto='HTTPS', monitor_proto='TLS-HELLO')) proto='HTTPS', monitor_proto='TLS-HELLO'))
self.assertEqual(sample_configs.sample_base_expected_config( self.assertEqual(sample_configs_split.sample_base_expected_config(
frontend=fe, backend=be), rendered_obj) frontend=fe, backend=be), rendered_obj)
def test_render_template_no_monitor_http(self): def test_render_template_no_monitor_http(self):
@ -382,9 +381,10 @@ class TestHaproxyCfg(base.TestCase):
"cookie sample_member_id_2\n\n").format( "cookie sample_member_id_2\n\n").format(
maxconn=constants.HAPROXY_MAX_MAXCONN) maxconn=constants.HAPROXY_MAX_MAXCONN)
rendered_obj = self.jinja_cfg.render_loadbalancer_obj( rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple(proto='HTTP', monitor=False)) sample_configs_split.sample_listener_tuple(
self.assertEqual(sample_configs.sample_base_expected_config( proto='HTTP', monitor=False))
self.assertEqual(sample_configs_split.sample_base_expected_config(
backend=be), rendered_obj) backend=be), rendered_obj)
def test_render_template_disabled_member(self): def test_render_template_disabled_member(self):
@ -402,10 +402,10 @@ class TestHaproxyCfg(base.TestCase):
"cookie sample_member_id_2 disabled\n\n").format( "cookie sample_member_id_2 disabled\n\n").format(
maxconn=constants.HAPROXY_MAX_MAXCONN) maxconn=constants.HAPROXY_MAX_MAXCONN)
rendered_obj = self.jinja_cfg.render_loadbalancer_obj( rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple(proto='HTTP', monitor=False, sample_configs_split.sample_listener_tuple(
disabled_member=True)) proto='HTTP', monitor=False, disabled_member=True))
self.assertEqual(sample_configs.sample_base_expected_config( self.assertEqual(sample_configs_split.sample_base_expected_config(
backend=be), rendered_obj) backend=be), rendered_obj)
def test_render_template_ping_monitor_http(self): def test_render_template_ping_monitor_http(self):
@ -430,10 +430,10 @@ class TestHaproxyCfg(base.TestCase):
go = " maxconn {maxconn}\n external-check\n\n".format( go = " maxconn {maxconn}\n external-check\n\n".format(
maxconn=constants.HAPROXY_MAX_MAXCONN) maxconn=constants.HAPROXY_MAX_MAXCONN)
rendered_obj = self.jinja_cfg.render_loadbalancer_obj( rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple(proto='HTTP', sample_configs_split.sample_listener_tuple(
monitor_proto='PING')) proto='HTTP', monitor_proto='PING'))
self.assertEqual(sample_configs.sample_base_expected_config( self.assertEqual(sample_configs_split.sample_base_expected_config(
backend=be, global_opts=go), rendered_obj) backend=be, global_opts=go), rendered_obj)
def test_render_template_no_monitor_https(self): def test_render_template_no_monitor_https(self):
@ -462,9 +462,10 @@ class TestHaproxyCfg(base.TestCase):
"cookie sample_member_id_2\n\n").format( "cookie sample_member_id_2\n\n").format(
maxconn=constants.HAPROXY_MAX_MAXCONN) maxconn=constants.HAPROXY_MAX_MAXCONN)
rendered_obj = self.jinja_cfg.render_loadbalancer_obj( rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple(proto='HTTPS', monitor=False)) sample_configs_split.sample_listener_tuple(
self.assertEqual(sample_configs.sample_base_expected_config( proto='HTTPS', monitor=False))
self.assertEqual(sample_configs_split.sample_base_expected_config(
frontend=fe, backend=be), rendered_obj) frontend=fe, backend=be), rendered_obj)
def test_render_template_health_monitor_http_check(self): def test_render_template_health_monitor_http_check(self):
@ -488,11 +489,10 @@ class TestHaproxyCfg(base.TestCase):
"cookie sample_member_id_2\n\n").format( "cookie sample_member_id_2\n\n").format(
maxconn=constants.HAPROXY_MAX_MAXCONN) maxconn=constants.HAPROXY_MAX_MAXCONN)
rendered_obj = self.jinja_cfg.render_loadbalancer_obj( rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple(proto='HTTP', sample_configs_split.sample_listener_tuple(
monitor_proto='HTTP', proto='HTTP', monitor_proto='HTTP', hm_host_http_check=True))
hm_host_http_check=True)) self.assertEqual(sample_configs_split.sample_base_expected_config(
self.assertEqual(sample_configs.sample_base_expected_config(
backend=be), rendered_obj) backend=be), rendered_obj)
def test_render_template_no_persistence_https(self): def test_render_template_no_persistence_https(self):
@ -518,10 +518,10 @@ class TestHaproxyCfg(base.TestCase):
" server sample_member_id_2 10.0.0.98:82 " " server sample_member_id_2 10.0.0.98:82 "
"weight 13\n\n").format(maxconn=constants.HAPROXY_MAX_MAXCONN) "weight 13\n\n").format(maxconn=constants.HAPROXY_MAX_MAXCONN)
rendered_obj = self.jinja_cfg.render_loadbalancer_obj( rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple(proto='HTTPS', monitor=False, sample_configs_split.sample_listener_tuple(
persistence=False)) proto='HTTPS', monitor=False, persistence=False))
self.assertEqual(sample_configs.sample_base_expected_config( self.assertEqual(sample_configs_split.sample_base_expected_config(
frontend=fe, backend=be), rendered_obj) frontend=fe, backend=be), rendered_obj)
def test_render_template_no_persistence_http(self): def test_render_template_no_persistence_http(self):
@ -536,10 +536,10 @@ class TestHaproxyCfg(base.TestCase):
" server sample_member_id_2 10.0.0.98:82 " " server sample_member_id_2 10.0.0.98:82 "
"weight 13\n\n").format(maxconn=constants.HAPROXY_MAX_MAXCONN) "weight 13\n\n").format(maxconn=constants.HAPROXY_MAX_MAXCONN)
rendered_obj = self.jinja_cfg.render_loadbalancer_obj( rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple(proto='HTTP', monitor=False, sample_configs_split.sample_listener_tuple(
persistence=False)) proto='HTTP', monitor=False, persistence=False))
self.assertEqual(sample_configs.sample_base_expected_config( self.assertEqual(sample_configs_split.sample_base_expected_config(
backend=be), rendered_obj) backend=be), rendered_obj)
def test_render_template_sourceip_persistence(self): def test_render_template_sourceip_persistence(self):
@ -561,11 +561,11 @@ class TestHaproxyCfg(base.TestCase):
"weight 13 check inter 30s fall 3 rise 2\n\n").format( "weight 13 check inter 30s fall 3 rise 2\n\n").format(
maxconn=constants.HAPROXY_MAX_MAXCONN) maxconn=constants.HAPROXY_MAX_MAXCONN)
rendered_obj = self.jinja_cfg.render_loadbalancer_obj( rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple( sample_configs_split.sample_listener_tuple(
persistence_type='SOURCE_IP')) persistence_type='SOURCE_IP'))
self.assertEqual( self.assertEqual(
sample_configs.sample_base_expected_config(backend=be), sample_configs_split.sample_base_expected_config(backend=be),
rendered_obj) rendered_obj)
def test_render_template_appcookie_persistence(self): def test_render_template_appcookie_persistence(self):
@ -588,12 +588,12 @@ class TestHaproxyCfg(base.TestCase):
"weight 13 check inter 30s fall 3 rise 2\n\n").format( "weight 13 check inter 30s fall 3 rise 2\n\n").format(
maxconn=constants.HAPROXY_MAX_MAXCONN) maxconn=constants.HAPROXY_MAX_MAXCONN)
rendered_obj = self.jinja_cfg.render_loadbalancer_obj( rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple( sample_configs_split.sample_listener_tuple(
persistence_type='APP_COOKIE', persistence_type='APP_COOKIE',
persistence_cookie='JSESSIONID')) persistence_cookie='JSESSIONID'))
self.assertEqual( self.assertEqual(
sample_configs.sample_base_expected_config(backend=be), sample_configs_split.sample_base_expected_config(backend=be),
rendered_obj) rendered_obj)
def test_render_template_unlimited_connections(self): def test_render_template_unlimited_connections(self):
@ -622,9 +622,10 @@ class TestHaproxyCfg(base.TestCase):
"cookie sample_member_id_2\n\n").format( "cookie sample_member_id_2\n\n").format(
maxconn=constants.HAPROXY_MAX_MAXCONN) maxconn=constants.HAPROXY_MAX_MAXCONN)
rendered_obj = self.jinja_cfg.render_loadbalancer_obj( rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple(proto='HTTPS', monitor=False)) sample_configs_split.sample_listener_tuple(
self.assertEqual(sample_configs.sample_base_expected_config( proto='HTTPS', monitor=False))
self.assertEqual(sample_configs_split.sample_base_expected_config(
frontend=fe, backend=be), rendered_obj) frontend=fe, backend=be), rendered_obj)
def test_render_template_limited_connections(self): def test_render_template_limited_connections(self):
@ -652,10 +653,10 @@ class TestHaproxyCfg(base.TestCase):
"cookie sample_member_id_2\n\n") "cookie sample_member_id_2\n\n")
g_opts = " maxconn 2014\n\n" g_opts = " maxconn 2014\n\n"
rendered_obj = self.jinja_cfg.render_loadbalancer_obj( rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple(proto='HTTPS', monitor=False, sample_configs_split.sample_listener_tuple(
connection_limit=2014)) proto='HTTPS', monitor=False, connection_limit=2014))
self.assertEqual(sample_configs.sample_base_expected_config( self.assertEqual(sample_configs_split.sample_base_expected_config(
frontend=fe, backend=be, global_opts=g_opts), rendered_obj) frontend=fe, backend=be, global_opts=g_opts), rendered_obj)
def test_render_template_l7policies(self): def test_render_template_l7policies(self):
@ -720,9 +721,9 @@ class TestHaproxyCfg(base.TestCase):
"inter 30s fall 3 rise 2 cookie sample_member_id_3\n\n").format( "inter 30s fall 3 rise 2 cookie sample_member_id_3\n\n").format(
maxconn=constants.HAPROXY_MAX_MAXCONN) maxconn=constants.HAPROXY_MAX_MAXCONN)
rendered_obj = self.jinja_cfg.render_loadbalancer_obj( rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple(l7=True)) sample_configs_split.sample_listener_tuple(l7=True))
self.assertEqual(sample_configs.sample_base_expected_config( self.assertEqual(sample_configs_split.sample_base_expected_config(
frontend=fe, backend=be), rendered_obj) frontend=fe, backend=be), rendered_obj)
def test_render_template_http_xff(self): def test_render_template_http_xff(self):
@ -746,11 +747,11 @@ class TestHaproxyCfg(base.TestCase):
"cookie sample_member_id_2\n\n").format( "cookie sample_member_id_2\n\n").format(
maxconn=constants.HAPROXY_MAX_MAXCONN) maxconn=constants.HAPROXY_MAX_MAXCONN)
rendered_obj = self.jinja_cfg.render_loadbalancer_obj( rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple( sample_configs_split.sample_listener_tuple(
insert_headers={'X-Forwarded-For': 'true'})) insert_headers={'X-Forwarded-For': 'true'}))
self.assertEqual( self.assertEqual(
sample_configs.sample_base_expected_config(backend=be), sample_configs_split.sample_base_expected_config(backend=be),
rendered_obj) rendered_obj)
def test_render_template_http_xff_xfport(self): def test_render_template_http_xff_xfport(self):
@ -775,12 +776,12 @@ class TestHaproxyCfg(base.TestCase):
"cookie sample_member_id_2\n\n").format( "cookie sample_member_id_2\n\n").format(
maxconn=constants.HAPROXY_MAX_MAXCONN) maxconn=constants.HAPROXY_MAX_MAXCONN)
rendered_obj = self.jinja_cfg.render_loadbalancer_obj( rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple( sample_configs_split.sample_listener_tuple(
insert_headers={'X-Forwarded-For': 'true', insert_headers={'X-Forwarded-For': 'true',
'X-Forwarded-Port': 'true'})) 'X-Forwarded-Port': 'true'}))
self.assertEqual( self.assertEqual(
sample_configs.sample_base_expected_config(backend=be), sample_configs_split.sample_base_expected_config(backend=be),
rendered_obj) rendered_obj)
def test_render_template_pool_proxy_protocol(self): def test_render_template_pool_proxy_protocol(self):
@ -801,11 +802,11 @@ class TestHaproxyCfg(base.TestCase):
"cookie sample_member_id_2 send-proxy\n\n").format( "cookie sample_member_id_2 send-proxy\n\n").format(
maxconn=constants.HAPROXY_MAX_MAXCONN) maxconn=constants.HAPROXY_MAX_MAXCONN)
rendered_obj = self.jinja_cfg.render_loadbalancer_obj( rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple( sample_configs_split.sample_listener_tuple(
be_proto='PROXY')) be_proto='PROXY'))
self.assertEqual( self.assertEqual(
sample_configs.sample_base_expected_config(backend=be), sample_configs_split.sample_base_expected_config(backend=be),
rendered_obj) rendered_obj)
def test_render_template_pool_cert(self): def test_render_template_pool_cert(self):
@ -831,15 +832,15 @@ class TestHaproxyCfg(base.TestCase):
maxconn=constants.HAPROXY_MAX_MAXCONN, maxconn=constants.HAPROXY_MAX_MAXCONN,
opts="ssl crt %s verify none sni ssl_fc_sni" % cert_file_path) opts="ssl crt %s verify none sni ssl_fc_sni" % cert_file_path)
rendered_obj = self.jinja_cfg.render_loadbalancer_obj( rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple( sample_configs_split.sample_listener_tuple(
pool_cert=True, tls_enabled=True), pool_cert=True, tls_enabled=True),
pool_tls_certs={ pool_tls_certs={
'sample_pool_id_1': 'sample_pool_id_1':
{'client_cert': cert_file_path, {'client_cert': cert_file_path,
'ca_cert': None, 'crl': None}}) 'ca_cert': None, 'crl': None}})
self.assertEqual( self.assertEqual(
sample_configs.sample_base_expected_config(backend=be), sample_configs_split.sample_base_expected_config(backend=be),
rendered_obj) rendered_obj)
def test_render_template_with_full_pool_cert(self): def test_render_template_with_full_pool_cert(self):
@ -870,8 +871,8 @@ class TestHaproxyCfg(base.TestCase):
"crl-file %s" % pool_crl, "crl-file %s" % pool_crl,
"verify required sni ssl_fc_sni")) "verify required sni ssl_fc_sni"))
rendered_obj = self.jinja_cfg.render_loadbalancer_obj( rendered_obj = self.jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple( sample_configs_split.sample_listener_tuple(
pool_cert=True, pool_ca_cert=True, pool_crl=True, pool_cert=True, pool_ca_cert=True, pool_crl=True,
tls_enabled=True), tls_enabled=True),
pool_tls_certs={ pool_tls_certs={
@ -880,107 +881,109 @@ class TestHaproxyCfg(base.TestCase):
'ca_cert': pool_ca_cert, 'ca_cert': pool_ca_cert,
'crl': pool_crl}}) 'crl': pool_crl}})
self.assertEqual( self.assertEqual(
sample_configs.sample_base_expected_config(backend=be), sample_configs_split.sample_base_expected_config(backend=be),
rendered_obj) rendered_obj)
def test_transform_session_persistence(self): def test_transform_session_persistence(self):
in_persistence = sample_configs.sample_session_persistence_tuple() in_persistence = (
ret = self.jinja_cfg._transform_session_persistence(in_persistence, {}) sample_configs_split.sample_session_persistence_tuple())
self.assertEqual(sample_configs.RET_PERSISTENCE, ret) ret = self.jinja_cfg._transform_session_persistence(
in_persistence, {})
self.assertEqual(sample_configs_split.RET_PERSISTENCE, ret)
def test_transform_health_monitor(self): def test_transform_health_monitor(self):
in_persistence = sample_configs.sample_health_monitor_tuple() in_persistence = sample_configs_split.sample_health_monitor_tuple()
ret = self.jinja_cfg._transform_health_monitor(in_persistence, {}) ret = self.jinja_cfg._transform_health_monitor(in_persistence, {})
self.assertEqual(sample_configs.RET_MONITOR_1, ret) self.assertEqual(sample_configs_split.RET_MONITOR_1, ret)
def test_transform_member(self): def test_transform_member(self):
in_member = sample_configs.sample_member_tuple('sample_member_id_1', in_member = sample_configs_split.sample_member_tuple(
'10.0.0.99') 'sample_member_id_1', '10.0.0.99')
ret = self.jinja_cfg._transform_member(in_member, {}) ret = self.jinja_cfg._transform_member(in_member, {})
self.assertEqual(sample_configs.RET_MEMBER_1, ret) self.assertEqual(sample_configs_split.RET_MEMBER_1, ret)
def test_transform_pool(self): def test_transform_pool(self):
in_pool = sample_configs.sample_pool_tuple() in_pool = sample_configs_split.sample_pool_tuple()
ret = self.jinja_cfg._transform_pool(in_pool, {}) ret = self.jinja_cfg._transform_pool(in_pool, {})
self.assertEqual(sample_configs.RET_POOL_1, ret) self.assertEqual(sample_configs_split.RET_POOL_1, ret)
def test_transform_pool_2(self): def test_transform_pool_2(self):
in_pool = sample_configs.sample_pool_tuple(sample_pool=2) in_pool = sample_configs_split.sample_pool_tuple(sample_pool=2)
ret = self.jinja_cfg._transform_pool(in_pool, {}) ret = self.jinja_cfg._transform_pool(in_pool, {})
self.assertEqual(sample_configs.RET_POOL_2, ret) self.assertEqual(sample_configs_split.RET_POOL_2, ret)
def test_transform_pool_http_reuse(self): def test_transform_pool_http_reuse(self):
in_pool = sample_configs.sample_pool_tuple(sample_pool=2) in_pool = sample_configs_split.sample_pool_tuple(sample_pool=2)
ret = self.jinja_cfg._transform_pool( ret = self.jinja_cfg._transform_pool(
in_pool, {constants.HTTP_REUSE: True}) in_pool, {constants.HTTP_REUSE: True})
expected_config = copy.copy(sample_configs.RET_POOL_2) expected_config = copy.copy(sample_configs_split.RET_POOL_2)
expected_config[constants.HTTP_REUSE] = True expected_config[constants.HTTP_REUSE] = True
self.assertEqual(expected_config, ret) self.assertEqual(expected_config, ret)
def test_transform_pool_cert(self): def test_transform_pool_cert(self):
in_pool = sample_configs.sample_pool_tuple(pool_cert=True) in_pool = sample_configs_split.sample_pool_tuple(pool_cert=True)
cert_path = os.path.join(self.jinja_cfg.base_crt_dir, cert_path = os.path.join(self.jinja_cfg.base_crt_dir,
'test_listener_id', 'pool_cert.pem') 'test_listener_id', 'pool_cert.pem')
ret = self.jinja_cfg._transform_pool( ret = self.jinja_cfg._transform_pool(
in_pool, {}, pool_tls_certs={'client_cert': cert_path}) in_pool, {}, pool_tls_certs={'client_cert': cert_path})
expected_config = copy.copy(sample_configs.RET_POOL_1) expected_config = copy.copy(sample_configs_split.RET_POOL_1)
expected_config['client_cert'] = cert_path expected_config['client_cert'] = cert_path
self.assertEqual(expected_config, ret) self.assertEqual(expected_config, ret)
def test_transform_listener(self): def test_transform_listener(self):
in_listener = sample_configs.sample_listener_tuple() in_listener = sample_configs_split.sample_listener_tuple()
ret = self.jinja_cfg._transform_listener(in_listener, None, {}, ret = self.jinja_cfg._transform_listener(in_listener, None, {},
in_listener.load_balancer) in_listener.load_balancer)
self.assertEqual(sample_configs.RET_LISTENER, ret) self.assertEqual(sample_configs_split.RET_LISTENER, ret)
def test_transform_listener_with_l7(self): def test_transform_listener_with_l7(self):
in_listener = sample_configs.sample_listener_tuple(l7=True) in_listener = sample_configs_split.sample_listener_tuple(l7=True)
ret = self.jinja_cfg._transform_listener(in_listener, None, {}, ret = self.jinja_cfg._transform_listener(in_listener, None, {},
in_listener.load_balancer) in_listener.load_balancer)
self.assertEqual(sample_configs.RET_LISTENER_L7, ret) self.assertEqual(sample_configs_split.RET_LISTENER_L7, ret)
def test_transform_loadbalancer(self): def test_transform_loadbalancer(self):
in_amphora = sample_configs.sample_amphora_tuple() in_amphora = sample_configs_split.sample_amphora_tuple()
in_listener = sample_configs.sample_listener_tuple() in_listener = sample_configs_split.sample_listener_tuple()
ret = self.jinja_cfg._transform_loadbalancer( ret = self.jinja_cfg._transform_loadbalancer(
in_amphora, in_listener.load_balancer, in_listener, None, {}) in_amphora, in_listener.load_balancer, in_listener, None, {})
self.assertEqual(sample_configs.RET_LB, ret) self.assertEqual(sample_configs_split.RET_LB, ret)
def test_transform_amphora(self): def test_transform_amphora(self):
in_amphora = sample_configs.sample_amphora_tuple() in_amphora = sample_configs_split.sample_amphora_tuple()
ret = self.jinja_cfg._transform_amphora(in_amphora, {}) ret = self.jinja_cfg._transform_amphora(in_amphora, {})
self.assertEqual(sample_configs.RET_AMPHORA, ret) self.assertEqual(sample_configs_split.RET_AMPHORA, ret)
def test_transform_loadbalancer_with_l7(self): def test_transform_loadbalancer_with_l7(self):
in_amphora = sample_configs.sample_amphora_tuple() in_amphora = sample_configs_split.sample_amphora_tuple()
in_listener = sample_configs.sample_listener_tuple(l7=True) in_listener = sample_configs_split.sample_listener_tuple(l7=True)
ret = self.jinja_cfg._transform_loadbalancer( ret = self.jinja_cfg._transform_loadbalancer(
in_amphora, in_listener.load_balancer, in_listener, None, {}) in_amphora, in_listener.load_balancer, in_listener, None, {})
self.assertEqual(sample_configs.RET_LB_L7, ret) self.assertEqual(sample_configs_split.RET_LB_L7, ret)
def test_transform_l7policy(self): def test_transform_l7policy(self):
in_l7policy = sample_configs.sample_l7policy_tuple( in_l7policy = sample_configs_split.sample_l7policy_tuple(
'sample_l7policy_id_1') 'sample_l7policy_id_1')
ret = self.jinja_cfg._transform_l7policy(in_l7policy, {}) ret = self.jinja_cfg._transform_l7policy(in_l7policy, {})
self.assertEqual(sample_configs.RET_L7POLICY_1, ret) self.assertEqual(sample_configs_split.RET_L7POLICY_1, ret)
def test_transform_l7policy_2_8(self): def test_transform_l7policy_2_8(self):
in_l7policy = sample_configs.sample_l7policy_tuple( in_l7policy = sample_configs_split.sample_l7policy_tuple(
'sample_l7policy_id_2', sample_policy=2) 'sample_l7policy_id_2', sample_policy=2)
ret = self.jinja_cfg._transform_l7policy(in_l7policy, {}) ret = self.jinja_cfg._transform_l7policy(in_l7policy, {})
self.assertEqual(sample_configs.RET_L7POLICY_2, ret) self.assertEqual(sample_configs_split.RET_L7POLICY_2, ret)
# test invalid action without redirect_http_code # test invalid action without redirect_http_code
in_l7policy = sample_configs.sample_l7policy_tuple( in_l7policy = sample_configs_split.sample_l7policy_tuple(
'sample_l7policy_id_8', sample_policy=2, redirect_http_code=None) 'sample_l7policy_id_8', sample_policy=2, redirect_http_code=None)
ret = self.jinja_cfg._transform_l7policy(in_l7policy, {}) ret = self.jinja_cfg._transform_l7policy(in_l7policy, {})
self.assertEqual(sample_configs.RET_L7POLICY_8, ret) self.assertEqual(sample_configs_split.RET_L7POLICY_8, ret)
def test_transform_l7policy_disabled_rule(self): def test_transform_l7policy_disabled_rule(self):
in_l7policy = sample_configs.sample_l7policy_tuple( in_l7policy = sample_configs_split.sample_l7policy_tuple(
'sample_l7policy_id_6', sample_policy=6) 'sample_l7policy_id_6', sample_policy=6)
ret = self.jinja_cfg._transform_l7policy(in_l7policy, {}) ret = self.jinja_cfg._transform_l7policy(in_l7policy, {})
self.assertEqual(sample_configs.RET_L7POLICY_6, ret) self.assertEqual(sample_configs_split.RET_L7POLICY_6, ret)
def test_escape_haproxy_config_string(self): def test_escape_haproxy_config_string(self):
self.assertEqual(self.jinja_cfg._escape_haproxy_config_string( self.assertEqual(self.jinja_cfg._escape_haproxy_config_string(
@ -1034,11 +1037,12 @@ class TestHaproxyCfg(base.TestCase):
" option splice-response\n" " option splice-response\n"
" option http-keep-alive\n\n") " option http-keep-alive\n\n")
rendered_obj = j_cfg.render_loadbalancer_obj( rendered_obj = j_cfg.render_loadbalancer_obj(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple() sample_configs_split.sample_listener_tuple()
) )
self.assertEqual( self.assertEqual(
sample_configs.sample_base_expected_config(defaults=defaults), sample_configs_split.sample_base_expected_config(
defaults=defaults),
rendered_obj) rendered_obj)
def test_http_reuse(self): def test_http_reuse(self):
@ -1065,12 +1069,12 @@ class TestHaproxyCfg(base.TestCase):
"cookie sample_member_id_2 send-proxy\n\n").format( "cookie sample_member_id_2 send-proxy\n\n").format(
maxconn=constants.HAPROXY_MAX_MAXCONN) maxconn=constants.HAPROXY_MAX_MAXCONN)
rendered_obj = j_cfg.build_config( rendered_obj = j_cfg.build_config(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple(be_proto='PROXY'), sample_configs_split.sample_listener_tuple(be_proto='PROXY'),
tls_cert=None, tls_cert=None,
haproxy_versions=("1", "8", "1")) haproxy_versions=("1", "8", "1"))
self.assertEqual( self.assertEqual(
sample_configs.sample_base_expected_config(backend=be), sample_configs_split.sample_base_expected_config(backend=be),
rendered_obj) rendered_obj)
# Without http-reuse # Without http-reuse
@ -1091,12 +1095,12 @@ class TestHaproxyCfg(base.TestCase):
"cookie sample_member_id_2 send-proxy\n\n").format( "cookie sample_member_id_2 send-proxy\n\n").format(
maxconn=constants.HAPROXY_MAX_MAXCONN) maxconn=constants.HAPROXY_MAX_MAXCONN)
rendered_obj = j_cfg.build_config( rendered_obj = j_cfg.build_config(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_configs.sample_listener_tuple(be_proto='PROXY'), sample_configs_split.sample_listener_tuple(be_proto='PROXY'),
tls_cert=None, tls_cert=None,
haproxy_versions=("1", "5", "18")) haproxy_versions=("1", "5", "18"))
self.assertEqual( self.assertEqual(
sample_configs.sample_base_expected_config(backend=be), sample_configs_split.sample_base_expected_config(backend=be),
rendered_obj) rendered_obj)
def test_ssl_types_l7rules(self): def test_ssl_types_l7rules(self):
@ -1172,15 +1176,15 @@ class TestHaproxyCfg(base.TestCase):
" timeout server 50000\n" " timeout server 50000\n"
" server sample_member_id_3 10.0.0.97:82 weight 13 check " " server sample_member_id_3 10.0.0.97:82 weight 13 check "
"inter 30s fall 3 rise 2 cookie sample_member_id_3\n\n") "inter 30s fall 3 rise 2 cookie sample_member_id_3\n\n")
sample_listener = sample_configs.sample_listener_tuple( sample_listener = sample_configs_split.sample_listener_tuple(
proto=constants.PROTOCOL_TERMINATED_HTTPS, l7=True, proto=constants.PROTOCOL_TERMINATED_HTTPS, l7=True,
ssl_type_l7=True) ssl_type_l7=True)
rendered_obj = j_cfg.build_config( rendered_obj = j_cfg.build_config(
sample_configs.sample_amphora_tuple(), sample_configs_split.sample_amphora_tuple(),
sample_listener, sample_listener,
tls_cert=None, tls_cert=None,
haproxy_versions=("1", "5", "18")) haproxy_versions=("1", "5", "18"))
self.assertEqual( self.assertEqual(
sample_configs.sample_base_expected_config( sample_configs_split.sample_base_expected_config(
frontend=fe, backend=be), frontend=fe, backend=be),
rendered_obj) rendered_obj)

View File

@ -16,7 +16,7 @@
from octavia.common import constants from octavia.common import constants
from octavia.common.jinja.lvs import jinja_cfg from octavia.common.jinja.lvs import jinja_cfg
from octavia.tests.unit import base from octavia.tests.unit import base
from octavia.tests.unit.common.sample_configs import sample_configs from octavia.tests.unit.common.sample_configs import sample_configs_combined
from oslo_config import cfg from oslo_config import cfg
from oslo_config import fixture as oslo_fixture from oslo_config import fixture as oslo_fixture
@ -76,7 +76,7 @@ class TestLvsCfg(base.TestCase):
" }\n\n" " }\n\n"
"}\n\n") "}\n\n")
rendered_obj = self.udp_jinja_cfg.render_loadbalancer_obj( rendered_obj = self.udp_jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_listener_tuple( sample_configs_combined.sample_listener_tuple(
proto=constants.PROTOCOL_UDP, proto=constants.PROTOCOL_UDP,
persistence_type=constants.SESSION_PERSISTENCE_SOURCE_IP, persistence_type=constants.SESSION_PERSISTENCE_SOURCE_IP,
persistence_timeout=33, persistence_timeout=33,
@ -124,7 +124,7 @@ class TestLvsCfg(base.TestCase):
" }\n\n" " }\n\n"
"}\n\n") "}\n\n")
listener = sample_configs.sample_listener_tuple( listener = sample_configs_combined.sample_listener_tuple(
proto=constants.PROTOCOL_UDP, proto=constants.PROTOCOL_UDP,
monitor_proto=constants.HEALTH_MONITOR_UDP_CONNECT, monitor_proto=constants.HEALTH_MONITOR_UDP_CONNECT,
connection_limit=98, connection_limit=98,
@ -172,7 +172,7 @@ class TestLvsCfg(base.TestCase):
"}\n\n") "}\n\n")
rendered_obj = self.udp_jinja_cfg.render_loadbalancer_obj( rendered_obj = self.udp_jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_listener_tuple( sample_configs_combined.sample_listener_tuple(
proto=constants.PROTOCOL_UDP, proto=constants.PROTOCOL_UDP,
monitor_proto=constants.HEALTH_MONITOR_UDP_CONNECT, monitor_proto=constants.HEALTH_MONITOR_UDP_CONNECT,
persistence=False, persistence=False,
@ -185,56 +185,57 @@ class TestLvsCfg(base.TestCase):
"net_namespace amphora-haproxy\n\n\n") "net_namespace amphora-haproxy\n\n\n")
rendered_obj = self.udp_jinja_cfg.render_loadbalancer_obj( rendered_obj = self.udp_jinja_cfg.render_loadbalancer_obj(
sample_configs.sample_listener_tuple( sample_configs_combined.sample_listener_tuple(
proto=constants.PROTOCOL_UDP, monitor=False, proto=constants.PROTOCOL_UDP, monitor=False,
persistence=False, alloc_default_pool=False)) persistence=False, alloc_default_pool=False))
self.assertEqual(exp, rendered_obj) self.assertEqual(exp, rendered_obj)
def test_udp_transform_session_persistence(self): def test_udp_transform_session_persistence(self):
persistence_src_ip = sample_configs.sample_session_persistence_tuple( persistence_src_ip = (
persistence_type=constants.SESSION_PERSISTENCE_SOURCE_IP, sample_configs_combined.sample_session_persistence_tuple(
persistence_cookie=None, persistence_type=constants.SESSION_PERSISTENCE_SOURCE_IP,
persistence_timeout=33, persistence_cookie=None,
persistence_granularity='255.0.0.0' persistence_timeout=33,
) persistence_granularity='255.0.0.0'
exp = sample_configs.UDP_SOURCE_IP_BODY ))
exp = sample_configs_combined.UDP_SOURCE_IP_BODY
ret = self.udp_jinja_cfg._transform_session_persistence( ret = self.udp_jinja_cfg._transform_session_persistence(
persistence_src_ip) persistence_src_ip)
self.assertEqual(exp, ret) self.assertEqual(exp, ret)
def test_udp_transform_health_monitor(self): def test_udp_transform_health_monitor(self):
in_hm = sample_configs.sample_health_monitor_tuple( in_hm = sample_configs_combined.sample_health_monitor_tuple(
proto=constants.HEALTH_MONITOR_UDP_CONNECT proto=constants.HEALTH_MONITOR_UDP_CONNECT
) )
ret = self.udp_jinja_cfg._transform_health_monitor(in_hm) ret = self.udp_jinja_cfg._transform_health_monitor(in_hm)
self.assertEqual(sample_configs.RET_UDP_HEALTH_MONITOR, ret) self.assertEqual(sample_configs_combined.RET_UDP_HEALTH_MONITOR, ret)
def test_udp_transform_member(self): def test_udp_transform_member(self):
in_member = sample_configs.sample_member_tuple('member_id_1', in_member = sample_configs_combined.sample_member_tuple(
'192.0.2.10') 'member_id_1', '192.0.2.10')
ret = self.udp_jinja_cfg._transform_member(in_member) ret = self.udp_jinja_cfg._transform_member(in_member)
self.assertEqual(sample_configs.RET_UDP_MEMBER, ret) self.assertEqual(sample_configs_combined.RET_UDP_MEMBER, ret)
def test_udp_transform_pool(self): def test_udp_transform_pool(self):
in_pool = sample_configs.sample_pool_tuple( in_pool = sample_configs_combined.sample_pool_tuple(
proto=constants.PROTOCOL_UDP, proto=constants.PROTOCOL_UDP,
persistence_type=constants.SESSION_PERSISTENCE_SOURCE_IP, persistence_type=constants.SESSION_PERSISTENCE_SOURCE_IP,
persistence_timeout=33, persistence_granularity='255.0.0.0', persistence_timeout=33, persistence_granularity='255.0.0.0',
) )
ret = self.udp_jinja_cfg._transform_pool(in_pool) ret = self.udp_jinja_cfg._transform_pool(in_pool)
self.assertEqual(sample_configs.RET_UDP_POOL, ret) self.assertEqual(sample_configs_combined.RET_UDP_POOL, ret)
in_pool = sample_configs.sample_pool_tuple( in_pool = sample_configs_combined.sample_pool_tuple(
proto=constants.PROTOCOL_UDP, proto=constants.PROTOCOL_UDP,
persistence_type=constants.SESSION_PERSISTENCE_SOURCE_IP, persistence_type=constants.SESSION_PERSISTENCE_SOURCE_IP,
persistence_timeout=33, persistence_granularity='255.0.0.0', persistence_timeout=33, persistence_granularity='255.0.0.0',
monitor=False) monitor=False)
sample_configs.RET_UDP_POOL['health_monitor'] = '' sample_configs_combined.RET_UDP_POOL['health_monitor'] = ''
ret = self.udp_jinja_cfg._transform_pool(in_pool) ret = self.udp_jinja_cfg._transform_pool(in_pool)
self.assertEqual(sample_configs.RET_UDP_POOL, ret) self.assertEqual(sample_configs_combined.RET_UDP_POOL, ret)
def test_udp_transform_listener(self): def test_udp_transform_listener(self):
in_listener = sample_configs.sample_listener_tuple( in_listener = sample_configs_combined.sample_listener_tuple(
proto=constants.PROTOCOL_UDP, proto=constants.PROTOCOL_UDP,
persistence_type=constants.SESSION_PERSISTENCE_SOURCE_IP, persistence_type=constants.SESSION_PERSISTENCE_SOURCE_IP,
persistence_timeout=33, persistence_timeout=33,
@ -243,9 +244,9 @@ class TestLvsCfg(base.TestCase):
connection_limit=98 connection_limit=98
) )
ret = self.udp_jinja_cfg._transform_listener(in_listener) ret = self.udp_jinja_cfg._transform_listener(in_listener)
self.assertEqual(sample_configs.RET_UDP_LISTENER, ret) self.assertEqual(sample_configs_combined.RET_UDP_LISTENER, ret)
in_listener = sample_configs.sample_listener_tuple( in_listener = sample_configs_combined.sample_listener_tuple(
proto=constants.PROTOCOL_UDP, proto=constants.PROTOCOL_UDP,
persistence_type=constants.SESSION_PERSISTENCE_SOURCE_IP, persistence_type=constants.SESSION_PERSISTENCE_SOURCE_IP,
persistence_timeout=33, persistence_timeout=33,
@ -254,5 +255,5 @@ class TestLvsCfg(base.TestCase):
connection_limit=-1) connection_limit=-1)
ret = self.udp_jinja_cfg._transform_listener(in_listener) ret = self.udp_jinja_cfg._transform_listener(in_listener)
sample_configs.RET_UDP_LISTENER.pop('connection_limit') sample_configs_combined.RET_UDP_LISTENER.pop('connection_limit')
self.assertEqual(sample_configs.RET_UDP_LISTENER, ret) self.assertEqual(sample_configs_combined.RET_UDP_LISTENER, ret)

File diff suppressed because it is too large Load Diff

View File

@ -27,11 +27,11 @@ def sample_amphora_tuple(id='sample_amphora_id_1', lb_network_ip='10.0.1.1',
vrrp_ip='10.1.1.1', ha_ip='192.168.10.1', vrrp_ip='10.1.1.1', ha_ip='192.168.10.1',
vrrp_port_id='1234', ha_port_id='1234', role=None, vrrp_port_id='1234', ha_port_id='1234', role=None,
status='ACTIVE', vrrp_interface=None, status='ACTIVE', vrrp_interface=None,
vrrp_priority=None): vrrp_priority=None, api_version='0.5'):
in_amphora = collections.namedtuple( in_amphora = collections.namedtuple(
'amphora', 'id, lb_network_ip, vrrp_ip, ha_ip, vrrp_port_id, ' 'amphora', 'id, lb_network_ip, vrrp_ip, ha_ip, vrrp_port_id, '
'ha_port_id, role, status, vrrp_interface,' 'ha_port_id, role, status, vrrp_interface,'
'vrrp_priority') 'vrrp_priority, api_version')
return in_amphora( return in_amphora(
id=id, id=id,
lb_network_ip=lb_network_ip, lb_network_ip=lb_network_ip,
@ -42,7 +42,8 @@ def sample_amphora_tuple(id='sample_amphora_id_1', lb_network_ip='10.0.1.1',
role=role, role=role,
status=status, status=status,
vrrp_interface=vrrp_interface, vrrp_interface=vrrp_interface,
vrrp_priority=vrrp_priority) vrrp_priority=vrrp_priority,
api_version=api_version)
RET_PERSISTENCE = { RET_PERSISTENCE = {
@ -510,7 +511,7 @@ def sample_listener_loadbalancer_tuple(proto=None, topology=None,
topology = constants.TOPOLOGY_SINGLE topology = constants.TOPOLOGY_SINGLE
in_lb = collections.namedtuple( in_lb = collections.namedtuple(
'load_balancer', 'id, name, protocol, vip, amphorae, topology, ' 'load_balancer', 'id, name, protocol, vip, amphorae, topology, '
'enabled, project_id') 'listeners, enabled, project_id')
return in_lb( return in_lb(
id='sample_loadbalancer_id_1', id='sample_loadbalancer_id_1',
name='test-lb', name='test-lb',
@ -524,6 +525,45 @@ def sample_listener_loadbalancer_tuple(proto=None, topology=None,
role=constants.ROLE_BACKUP)] role=constants.ROLE_BACKUP)]
if more_amp else [sample_amphora_tuple()], if more_amp else [sample_amphora_tuple()],
topology=topology, topology=topology,
listeners=[],
enabled=enabled,
project_id='12345'
)
def sample_lb_with_udp_listener_tuple(
proto=None, topology=None, enabled=True, pools=None):
proto = 'HTTP' if proto is None else proto
if topology and topology in ['ACTIVE_STANDBY', 'ACTIVE_ACTIVE']:
more_amp = True
else:
more_amp = False
topology = constants.TOPOLOGY_SINGLE
listeners = [sample_listener_tuple(
proto=constants.PROTOCOL_UDP,
persistence_type=constants.SESSION_PERSISTENCE_SOURCE_IP,
persistence_timeout=33,
persistence_granularity='255.255.0.0',
monitor_proto=constants.HEALTH_MONITOR_UDP_CONNECT)]
in_lb = collections.namedtuple(
'load_balancer', 'id, name, protocol, vip, amphorae, topology, '
'pools, enabled, project_id, listeners')
return in_lb(
id='sample_loadbalancer_id_1',
name='test-lb',
protocol=proto,
vip=sample_vip_tuple(),
amphorae=[sample_amphora_tuple(role=constants.ROLE_MASTER),
sample_amphora_tuple(
id='sample_amphora_id_2',
lb_network_ip='10.0.1.2',
vrrp_ip='10.1.1.2',
role=constants.ROLE_BACKUP)]
if more_amp else [sample_amphora_tuple()],
topology=topology,
listeners=listeners,
pools=pools or [],
enabled=enabled, enabled=enabled,
project_id='12345' project_id='12345'
) )
@ -565,7 +605,8 @@ def sample_listener_tuple(proto=None, monitor=True, alloc_default_pool=True,
client_ca_cert=False, client_crl_cert=False, client_ca_cert=False, client_crl_cert=False,
ssl_type_l7=False, pool_cert=False, ssl_type_l7=False, pool_cert=False,
pool_ca_cert=False, pool_crl=False, pool_ca_cert=False, pool_crl=False,
tls_enabled=False, hm_host_http_check=False): tls_enabled=False, hm_host_http_check=False,
id='sample_listener_id_1', recursive_nest=False):
proto = 'HTTP' if proto is None else proto proto = 'HTTP' if proto is None else proto
if be_proto is None: if be_proto is None:
be_proto = 'HTTP' if proto is 'TERMINATED_HTTPS' else proto be_proto = 'HTTP' if proto is 'TERMINATED_HTTPS' else proto
@ -624,8 +665,8 @@ def sample_listener_tuple(proto=None, monitor=True, alloc_default_pool=True,
pool_crl=pool_crl, tls_enabled=tls_enabled, pool_crl=pool_crl, tls_enabled=tls_enabled,
hm_host_http_check=hm_host_http_check)] hm_host_http_check=hm_host_http_check)]
l7policies = [] l7policies = []
return in_listener( listener = in_listener(
id='sample_listener_id_1', id=id,
project_id='12345', project_id='12345',
protocol_port=port, protocol_port=port,
protocol=proto, protocol=proto,
@ -689,6 +730,9 @@ def sample_listener_tuple(proto=None, monitor=True, alloc_default_pool=True,
constants.CLIENT_AUTH_NONE), constants.CLIENT_AUTH_NONE),
client_crl_container_id='cont_id_crl' if client_crl_cert else '', client_crl_container_id='cont_id_crl' if client_crl_cert else '',
) )
if recursive_nest:
listener.load_balancer.listeners.append(listener)
return listener
def sample_tls_sni_container_tuple(tls_container_id=None, tls_container=None): def sample_tls_sni_container_tuple(tls_container_id=None, tls_container=None):

View File

@ -22,7 +22,7 @@ import octavia.common.exceptions as exceptions
import octavia.common.tls_utils.cert_parser as cert_parser import octavia.common.tls_utils.cert_parser as cert_parser
from octavia.tests.unit import base from octavia.tests.unit import base
from octavia.tests.unit.common.sample_configs import sample_certs from octavia.tests.unit.common.sample_configs import sample_certs
from octavia.tests.unit.common.sample_configs import sample_configs from octavia.tests.unit.common.sample_configs import sample_configs_combined
class TestTLSParseUtils(base.TestCase): class TestTLSParseUtils(base.TestCase):
@ -144,8 +144,8 @@ class TestTLSParseUtils(base.TestCase):
@mock.patch('oslo_context.context.RequestContext') @mock.patch('oslo_context.context.RequestContext')
def test_load_certificates(self, mock_oslo): def test_load_certificates(self, mock_oslo):
listener = sample_configs.sample_listener_tuple(tls=True, sni=True, listener = sample_configs_combined.sample_listener_tuple(
client_ca_cert=True) tls=True, sni=True, client_ca_cert=True)
client = mock.MagicMock() client = mock.MagicMock()
context = mock.Mock() context = mock.Mock()
context.project_id = '12345' context.project_id = '12345'
@ -165,8 +165,8 @@ class TestTLSParseUtils(base.TestCase):
client.assert_has_calls(calls_cert_mngr) client.assert_has_calls(calls_cert_mngr)
# Test asking for nothing # Test asking for nothing
listener = sample_configs.sample_listener_tuple(tls=False, sni=False, listener = sample_configs_combined.sample_listener_tuple(
client_ca_cert=False) tls=False, sni=False, client_ca_cert=False)
client = mock.MagicMock() client = mock.MagicMock()
with mock.patch.object(cert_parser, with mock.patch.object(cert_parser,
'_map_cert_tls_container') as mock_map: '_map_cert_tls_container') as mock_map:
@ -211,7 +211,7 @@ class TestTLSParseUtils(base.TestCase):
def test_build_pem(self): def test_build_pem(self):
expected = b'imacert\nimakey\nimainter\nimainter2\n' expected = b'imacert\nimakey\nimainter\nimainter2\n'
tls_tuple = sample_configs.sample_tls_container_tuple( tls_tuple = sample_configs_combined.sample_tls_container_tuple(
certificate=b'imacert', private_key=b'imakey', certificate=b'imacert', private_key=b'imakey',
intermediates=[b'imainter', b'imainter2']) intermediates=[b'imainter', b'imainter2'])
self.assertEqual(expected, cert_parser.build_pem(tls_tuple)) self.assertEqual(expected, cert_parser.build_pem(tls_tuple))

View File

@ -136,7 +136,8 @@ class TestUpdateHealthDb(base.TestCase):
if listener: if listener:
listener_ref = {'listener-id-1': { listener_ref = {'listener-id-1': {
constants.OPERATING_STATUS: 'bogus', constants.OPERATING_STATUS: 'bogus',
'protocol': listener_protocol}} 'protocol': listener_protocol,
'enabled': True}}
lb_ref['listeners'] = listener_ref lb_ref['listeners'] = listener_ref
return lb_ref return lb_ref
@ -145,6 +146,7 @@ class TestUpdateHealthDb(base.TestCase):
health = { health = {
"id": self.FAKE_UUID_1, "id": self.FAKE_UUID_1,
"ver": 1,
"listeners": {}, "listeners": {},
"recv_time": time.time() "recv_time": time.time()
} }
@ -161,6 +163,7 @@ class TestUpdateHealthDb(base.TestCase):
health = { health = {
"id": self.FAKE_UUID_1, "id": self.FAKE_UUID_1,
"ver": 1,
"listeners": {}, "listeners": {},
"recv_time": time.time() "recv_time": time.time()
} }
@ -178,6 +181,7 @@ class TestUpdateHealthDb(base.TestCase):
health = { health = {
"id": self.FAKE_UUID_1, "id": self.FAKE_UUID_1,
"ver": 1,
"listeners": {}, "listeners": {},
"recv_time": time.time() "recv_time": time.time()
} }
@ -194,6 +198,7 @@ class TestUpdateHealthDb(base.TestCase):
hb_interval = cfg.CONF.health_manager.heartbeat_interval hb_interval = cfg.CONF.health_manager.heartbeat_interval
health = { health = {
"id": self.FAKE_UUID_1, "id": self.FAKE_UUID_1,
"ver": 1,
"listeners": {}, "listeners": {},
"recv_time": time.time() - hb_interval - 1 # extra -1 for buffer "recv_time": time.time() - hb_interval - 1 # extra -1 for buffer
} }
@ -211,6 +216,7 @@ class TestUpdateHealthDb(base.TestCase):
health = { health = {
"id": self.FAKE_UUID_1, "id": self.FAKE_UUID_1,
"ver": 1,
"listeners": { "listeners": {
"listener-id-1": {"status": constants.OPEN, "pools": { "listener-id-1": {"status": constants.OPEN, "pools": {
"pool-id-1": {"status": constants.UP, "pool-id-1": {"status": constants.UP,
@ -233,9 +239,9 @@ class TestUpdateHealthDb(base.TestCase):
self.session_mock.rollback.assert_called_once() self.session_mock.rollback.assert_called_once()
def test_update_health_online(self): def test_update_health_online(self):
health = { health = {
"id": self.FAKE_UUID_1, "id": self.FAKE_UUID_1,
"ver": 1,
"listeners": { "listeners": {
"listener-id-1": {"status": constants.OPEN, "pools": { "listener-id-1": {"status": constants.OPEN, "pools": {
"pool-id-1": {"status": constants.UP, "pool-id-1": {"status": constants.UP,
@ -281,10 +287,53 @@ class TestUpdateHealthDb(base.TestCase):
self.hm.update_health(health, '192.0.2.1') self.hm.update_health(health, '192.0.2.1')
self.assertTrue(not self.amphora_health_repo.replace.called) self.assertTrue(not self.amphora_health_repo.replace.called)
def test_update_health_listener_disabled(self):
health = {
"id": self.FAKE_UUID_1,
"ver": 1,
"listeners": {
"listener-id-1": {"status": constants.OPEN, "pools": {
"pool-id-1": {"status": constants.UP,
"members": {"member-id-1": constants.UP}
}
}
}
},
"recv_time": time.time()
}
lb_ref = self._make_fake_lb_health_dict()
lb_ref['listeners']['listener-id-2'] = {
'enabled': False, constants.OPERATING_STATUS: constants.OFFLINE}
self.amphora_repo.get_lb_for_health_update.return_value = lb_ref
self.hm.update_health(health, '192.0.2.1')
self.assertTrue(self.amphora_health_repo.replace.called)
# test listener, member
for listener_id, listener in six.iteritems(
health.get('listeners', {})):
self.listener_repo.update.assert_any_call(
self.session_mock, listener_id,
operating_status=constants.ONLINE)
for pool_id, pool in six.iteritems(listener.get('pools', {})):
self.hm.pool_repo.update.assert_any_call(
self.session_mock, pool_id,
operating_status=constants.ONLINE)
for member_id, member in six.iteritems(
pool.get('members', {})):
self.member_repo.update.assert_any_call(
self.session_mock, member_id,
operating_status=constants.ONLINE)
def test_update_lb_pool_health_offline(self): def test_update_lb_pool_health_offline(self):
health = { health = {
"id": self.FAKE_UUID_1, "id": self.FAKE_UUID_1,
"ver": 1,
"listeners": { "listeners": {
"listener-id-1": {"status": constants.OPEN, "pools": {}} "listener-id-1": {"status": constants.OPEN, "pools": {}}
}, },
@ -312,6 +361,7 @@ class TestUpdateHealthDb(base.TestCase):
def test_update_lb_multiple_listeners_one_error_pool(self): def test_update_lb_multiple_listeners_one_error_pool(self):
health = { health = {
"id": self.FAKE_UUID_1, "id": self.FAKE_UUID_1,
"ver": 1,
"listeners": { "listeners": {
"listener-id-1": {"status": constants.OPEN, "pools": { "listener-id-1": {"status": constants.OPEN, "pools": {
"pool-id-1": {"status": constants.DOWN, "pool-id-1": {"status": constants.DOWN,
@ -333,7 +383,8 @@ class TestUpdateHealthDb(base.TestCase):
lb_ref['listeners']['listener-id-2'] = { lb_ref['listeners']['listener-id-2'] = {
constants.OPERATING_STATUS: 'bogus', constants.OPERATING_STATUS: 'bogus',
'protocol': constants.PROTOCOL_TCP} 'protocol': constants.PROTOCOL_TCP,
'enabled': True}
self.amphora_repo.get_lb_for_health_update.return_value = lb_ref self.amphora_repo.get_lb_for_health_update.return_value = lb_ref
self.hm.update_health(health, '192.0.2.1') self.hm.update_health(health, '192.0.2.1')
@ -359,6 +410,7 @@ class TestUpdateHealthDb(base.TestCase):
health = { health = {
"id": self.FAKE_UUID_1, "id": self.FAKE_UUID_1,
"ver": 1,
"listeners": { "listeners": {
"listener-id-1": {"status": constants.OPEN, "pools": { "listener-id-1": {"status": constants.OPEN, "pools": {
"pool-id-1": {"status": constants.UP, "pool-id-1": {"status": constants.UP,
@ -396,10 +448,54 @@ class TestUpdateHealthDb(base.TestCase):
self.session_mock, member_id, self.session_mock, member_id,
operating_status=constants.ONLINE) operating_status=constants.ONLINE)
def test_update_v2_lb_and_list_pool_health_online(self):
health = {
"id": self.FAKE_UUID_1,
"ver": 2,
"listeners": {
"listener-id-1": {"status": constants.OPEN}
},
"pools": {
"pool-id-1:listener-id-1": {
"status": constants.UP,
"members": {"member-id-1": constants.UP}},
"pool-id-1:listener-id-1": {
"status": constants.UP,
"members": {"member-id-1": constants.UP}}},
"recv_time": time.time()
}
lb_ref = self._make_fake_lb_health_dict()
self.amphora_repo.get_lb_for_health_update.return_value = lb_ref
self.hm.update_health(health, '192.0.2.1')
self.assertTrue(self.amphora_health_repo.replace.called)
# test listener, member
for listener_id, listener in six.iteritems(
health.get('listeners', {})):
self.listener_repo.update.assert_any_call(
self.session_mock, listener_id,
operating_status=constants.ONLINE)
for pool_id, pool in six.iteritems(health.get('pools', {})):
# We should not double process a shared pool
self.hm.pool_repo.update.assert_called_once_with(
self.session_mock, 'pool-id-1',
operating_status=constants.ONLINE)
for member_id, member in six.iteritems(
pool.get('members', {})):
self.member_repo.update.assert_any_call(
self.session_mock, member_id,
operating_status=constants.ONLINE)
def test_update_pool_offline(self): def test_update_pool_offline(self):
health = { health = {
"id": self.FAKE_UUID_1, "id": self.FAKE_UUID_1,
"ver": 1,
"listeners": { "listeners": {
"listener-id-1": {"status": constants.OPEN, "pools": { "listener-id-1": {"status": constants.OPEN, "pools": {
"pool-id-5": {"status": constants.UP, "pool-id-5": {"status": constants.UP,
@ -436,6 +532,7 @@ class TestUpdateHealthDb(base.TestCase):
health = { health = {
"id": self.FAKE_UUID_1, "id": self.FAKE_UUID_1,
"ver": 1,
"listeners": { "listeners": {
"listener-id-1": { "listener-id-1": {
"status": constants.OPEN, "status": constants.OPEN,
@ -475,6 +572,7 @@ class TestUpdateHealthDb(base.TestCase):
health = { health = {
"id": self.FAKE_UUID_1, "id": self.FAKE_UUID_1,
"ver": 1,
"listeners": { "listeners": {
"listener-id-1": { "listener-id-1": {
"status": constants.OPEN, "status": constants.OPEN,
@ -514,6 +612,7 @@ class TestUpdateHealthDb(base.TestCase):
health = { health = {
"id": self.FAKE_UUID_1, "id": self.FAKE_UUID_1,
"ver": 1,
"listeners": { "listeners": {
"listener-id-1": { "listener-id-1": {
"status": constants.OPEN, "status": constants.OPEN,
@ -547,6 +646,7 @@ class TestUpdateHealthDb(base.TestCase):
health = { health = {
"id": self.FAKE_UUID_1, "id": self.FAKE_UUID_1,
"ver": 1,
"listeners": { "listeners": {
"listener-id-1": {"status": constants.OPEN, "pools": { "listener-id-1": {"status": constants.OPEN, "pools": {
"pool-id-1": {"status": constants.UP, "pool-id-1": {"status": constants.UP,
@ -589,6 +689,7 @@ class TestUpdateHealthDb(base.TestCase):
health = { health = {
"id": self.FAKE_UUID_1, "id": self.FAKE_UUID_1,
"ver": 1,
"listeners": { "listeners": {
"listener-id-1": {"status": constants.OPEN, "pools": { "listener-id-1": {"status": constants.OPEN, "pools": {
"pool-id-1": {"status": constants.UP, "pool-id-1": {"status": constants.UP,
@ -626,6 +727,7 @@ class TestUpdateHealthDb(base.TestCase):
health = { health = {
"id": self.FAKE_UUID_1, "id": self.FAKE_UUID_1,
"ver": 1,
"listeners": { "listeners": {
"listener-id-1": {"status": constants.OPEN, "pools": { "listener-id-1": {"status": constants.OPEN, "pools": {
"pool-id-1": {"status": constants.UP, "pool-id-1": {"status": constants.UP,
@ -668,6 +770,7 @@ class TestUpdateHealthDb(base.TestCase):
health = { health = {
"id": self.FAKE_UUID_1, "id": self.FAKE_UUID_1,
"ver": 1,
"listeners": { "listeners": {
"listener-id-1": {"status": constants.OPEN, "pools": { "listener-id-1": {"status": constants.OPEN, "pools": {
"pool-id-1": {"status": constants.UP, "pool-id-1": {"status": constants.UP,
@ -710,6 +813,7 @@ class TestUpdateHealthDb(base.TestCase):
health = { health = {
"id": self.FAKE_UUID_1, "id": self.FAKE_UUID_1,
"ver": 1,
"listeners": { "listeners": {
"listener-id-1": { "listener-id-1": {
"status": constants.OPEN, "status": constants.OPEN,
@ -755,6 +859,7 @@ class TestUpdateHealthDb(base.TestCase):
health = { health = {
"id": self.FAKE_UUID_1, "id": self.FAKE_UUID_1,
"ver": 1,
"listeners": { "listeners": {
"listener-id-1": {"status": constants.FULL, "pools": { "listener-id-1": {"status": constants.FULL, "pools": {
"pool-id-1": {"status": constants.UP, "pool-id-1": {"status": constants.UP,
@ -804,6 +909,7 @@ class TestUpdateHealthDb(base.TestCase):
health = { health = {
"id": self.FAKE_UUID_1, "id": self.FAKE_UUID_1,
"ver": 1,
"listeners": { "listeners": {
"listener-id-1": {"status": constants.OPEN, "pools": { "listener-id-1": {"status": constants.OPEN, "pools": {
"pool-id-1": {"status": constants.DOWN, "pool-id-1": {"status": constants.DOWN,
@ -847,6 +953,7 @@ class TestUpdateHealthDb(base.TestCase):
health = { health = {
"id": self.FAKE_UUID_1, "id": self.FAKE_UUID_1,
"ver": 1,
"listeners": { "listeners": {
"listener-id-1": {"status": constants.FULL, "pools": { "listener-id-1": {"status": constants.FULL, "pools": {
"pool-id-1": {"status": constants.DOWN, "pool-id-1": {"status": constants.DOWN,
@ -895,7 +1002,8 @@ class TestUpdateHealthDb(base.TestCase):
lb_ref['listeners']['listener-id-%s' % i] = { lb_ref['listeners']['listener-id-%s' % i] = {
constants.OPERATING_STATUS: 'bogus', constants.OPERATING_STATUS: 'bogus',
'protocol': constants.PROTOCOL_TCP} 'protocol': constants.PROTOCOL_TCP,
'enabled': True}
if i == 3: if i == 3:
members_dict = {'member-id-3': { members_dict = {'member-id-3': {
@ -936,6 +1044,7 @@ class TestUpdateHealthDb(base.TestCase):
health = { health = {
"id": self.FAKE_UUID_1, "id": self.FAKE_UUID_1,
"ver": 1,
"listeners": { "listeners": {
"listener-id-1": {"status": constants.OPEN, "pools": { "listener-id-1": {"status": constants.OPEN, "pools": {
"pool-id-1": {"status": constants.UP, "pool-id-1": {"status": constants.UP,
@ -1000,6 +1109,7 @@ class TestUpdateHealthDb(base.TestCase):
def test_update_health_no_status_change(self): def test_update_health_no_status_change(self):
health = { health = {
"id": self.FAKE_UUID_1, "id": self.FAKE_UUID_1,
"ver": 1,
"listeners": { "listeners": {
"listener-id-1": { "listener-id-1": {
"status": constants.OPEN, "pools": { "status": constants.OPEN, "pools": {
@ -1036,6 +1146,7 @@ class TestUpdateHealthDb(base.TestCase):
def test_update_health_lb_admin_down(self): def test_update_health_lb_admin_down(self):
health = { health = {
"id": self.FAKE_UUID_1, "id": self.FAKE_UUID_1,
"ver": 1,
"listeners": {}, "listeners": {},
"recv_time": time.time()} "recv_time": time.time()}
@ -1118,10 +1229,12 @@ class TestUpdateHealthDb(base.TestCase):
listener_protocol=constants.PROTOCOL_UDP) listener_protocol=constants.PROTOCOL_UDP)
lb_ref['listeners']['listener-id-2'] = { lb_ref['listeners']['listener-id-2'] = {
constants.OPERATING_STATUS: 'bogus', constants.OPERATING_STATUS: 'bogus',
'protocol': constants.PROTOCOL_UDP} 'protocol': constants.PROTOCOL_UDP,
'enabled': True}
lb_ref['listeners']['listener-id-3'] = { lb_ref['listeners']['listener-id-3'] = {
constants.OPERATING_STATUS: 'bogus', constants.OPERATING_STATUS: 'bogus',
'protocol': constants.PROTOCOL_UDP} 'protocol': constants.PROTOCOL_UDP,
'enabled': True}
self.amphora_repo.get_lb_for_health_update.return_value = lb_ref self.amphora_repo.get_lb_for_health_update.return_value = lb_ref
self.hm.update_health(health, '192.0.2.1') self.hm.update_health(health, '192.0.2.1')
@ -1132,6 +1245,7 @@ class TestUpdateHealthDb(base.TestCase):
def test_update_health_no_db_lb(self): def test_update_health_no_db_lb(self):
health = { health = {
"id": self.FAKE_UUID_1, "id": self.FAKE_UUID_1,
"ver": 1,
"listeners": {}, "listeners": {},
"recv_time": time.time() "recv_time": time.time()
} }
@ -1288,6 +1402,7 @@ class TestUpdateStatsDb(base.TestCase):
health = { health = {
"id": self.amphora_id, "id": self.amphora_id,
"ver": 1,
"listeners": { "listeners": {
self.listener_id: { self.listener_id: {
"status": constants.OPEN, "status": constants.OPEN,

View File

@ -128,9 +128,10 @@ class TestLoadBalancerFlows(base.TestCase):
self.assertIsInstance(lb_flow, flow.Flow) self.assertIsInstance(lb_flow, flow.Flow)
self.assertIn(constants.LOADBALANCER, lb_flow.requires) self.assertIn(constants.LOADBALANCER, lb_flow.requires)
self.assertIn(constants.UPDATE_DICT, lb_flow.requires)
self.assertEqual(0, len(lb_flow.provides)) self.assertEqual(0, len(lb_flow.provides))
self.assertEqual(3, len(lb_flow.requires)) self.assertEqual(2, len(lb_flow.requires))
def test_get_post_lb_amp_association_flow(self, mock_get_net_driver): def test_get_post_lb_amp_association_flow(self, mock_get_net_driver):
amp_flow = self.LBFlow.get_post_lb_amp_association_flow( amp_flow = self.LBFlow.get_post_lb_amp_association_flow(

View File

@ -93,15 +93,15 @@ class TestAmphoraDriverTasks(base.TestCase):
constants.CONN_RETRY_INTERVAL: 4} constants.CONN_RETRY_INTERVAL: 4}
amp_list_update_obj = amphora_driver_tasks.AmpListenersUpdate() amp_list_update_obj = amphora_driver_tasks.AmpListenersUpdate()
amp_list_update_obj.execute([_listener_mock], 0, amp_list_update_obj.execute(_load_balancer_mock, 0,
[_amphora_mock], timeout_dict) [_amphora_mock], timeout_dict)
mock_driver.update_amphora_listeners.assert_called_once_with( mock_driver.update_amphora_listeners.assert_called_once_with(
[_listener_mock], 0, [_amphora_mock], timeout_dict) _load_balancer_mock, _amphora_mock, timeout_dict)
mock_driver.update_amphora_listeners.side_effect = Exception('boom') mock_driver.update_amphora_listeners.side_effect = Exception('boom')
amp_list_update_obj.execute([_listener_mock], 0, amp_list_update_obj.execute(_load_balancer_mock, 0,
[_amphora_mock], timeout_dict) [_amphora_mock], timeout_dict)
mock_amphora_repo_update.assert_called_once_with( mock_amphora_repo_update.assert_called_once_with(
@ -117,9 +117,9 @@ class TestAmphoraDriverTasks(base.TestCase):
mock_amphora_repo_update): mock_amphora_repo_update):
listener_update_obj = amphora_driver_tasks.ListenersUpdate() listener_update_obj = amphora_driver_tasks.ListenersUpdate()
listener_update_obj.execute(_load_balancer_mock, [_listener_mock]) listener_update_obj.execute(_load_balancer_mock)
mock_driver.update.assert_called_once_with(_listener_mock, _vip_mock) mock_driver.update.assert_called_once_with(_load_balancer_mock)
# Test the revert # Test the revert
amp = listener_update_obj.revert(_load_balancer_mock) amp = listener_update_obj.revert(_load_balancer_mock)
@ -152,12 +152,9 @@ class TestAmphoraDriverTasks(base.TestCase):
data_models.Listener(id='listener2')] data_models.Listener(id='listener2')]
vip = data_models.Vip(ip_address='10.0.0.1') vip = data_models.Vip(ip_address='10.0.0.1')
lb = data_models.LoadBalancer(id='lb1', listeners=listeners, vip=vip) lb = data_models.LoadBalancer(id='lb1', listeners=listeners, vip=vip)
listeners_update_obj.execute(lb, listeners) listeners_update_obj.execute(lb)
mock_driver.update.assert_has_calls([mock.call(listeners[0], vip), mock_driver.update.assert_called_once_with(lb)
mock.call(listeners[1], vip)]) self.assertEqual(1, mock_driver.update.call_count)
self.assertEqual(2, mock_driver.update.call_count)
self.assertIsNotNone(listeners[0].load_balancer)
self.assertIsNotNone(listeners[1].load_balancer)
# Test the revert # Test the revert
amp = listeners_update_obj.revert(lb) amp = listeners_update_obj.revert(lb)
@ -171,69 +168,37 @@ class TestAmphoraDriverTasks(base.TestCase):
self.assertEqual(2, repo.ListenerRepository.update.call_count) self.assertEqual(2, repo.ListenerRepository.update.call_count)
self.assertIsNone(amp) self.assertIsNone(amp)
def test_listener_stop(self, @mock.patch('octavia.controller.worker.task_utils.TaskUtils.'
mock_driver, 'mark_listener_prov_status_error')
mock_generate_uuid, def test_listeners_start(self,
mock_log, mock_prov_status_error,
mock_get_session, mock_driver,
mock_listener_repo_get, mock_generate_uuid,
mock_listener_repo_update, mock_log,
mock_amphora_repo_update): mock_get_session,
mock_listener_repo_get,
mock_listener_repo_update,
mock_amphora_repo_update):
listeners_start_obj = amphora_driver_tasks.ListenersStart()
mock_lb = mock.MagicMock()
mock_listener = mock.MagicMock()
mock_listener.id = '12345'
listener_stop_obj = amphora_driver_tasks.ListenerStop() # Test no listeners
listener_stop_obj.execute(_load_balancer_mock, _listener_mock) mock_lb.listeners = None
listeners_start_obj.execute(mock_lb)
mock_driver.start.assert_not_called()
mock_driver.stop.assert_called_once_with(_listener_mock, _vip_mock) # Test with listeners
mock_driver.start.reset_mock()
mock_lb.listeners = [mock_listener]
listeners_start_obj.execute(mock_lb)
mock_driver.start.assert_called_once_with(mock_lb, None)
# Test the revert # Test revert
amp = listener_stop_obj.revert(_listener_mock) mock_lb.listeners = [mock_listener]
repo.ListenerRepository.update.assert_called_once_with( listeners_start_obj.revert(mock_lb)
_session_mock, mock_prov_status_error.assert_called_once_with('12345')
id=LISTENER_ID,
provisioning_status=constants.ERROR)
self.assertIsNone(amp)
# Test the revert with exception
repo.ListenerRepository.update.reset_mock()
mock_listener_repo_update.side_effect = Exception('fail')
amp = listener_stop_obj.revert(_listener_mock)
repo.ListenerRepository.update.assert_called_once_with(
_session_mock,
id=LISTENER_ID,
provisioning_status=constants.ERROR)
self.assertIsNone(amp)
def test_listener_start(self,
mock_driver,
mock_generate_uuid,
mock_log,
mock_get_session,
mock_listener_repo_get,
mock_listener_repo_update,
mock_amphora_repo_update):
listener_start_obj = amphora_driver_tasks.ListenerStart()
listener_start_obj.execute(_load_balancer_mock, _listener_mock)
mock_driver.start.assert_called_once_with(_listener_mock, _vip_mock)
# Test the revert
amp = listener_start_obj.revert(_listener_mock)
repo.ListenerRepository.update.assert_called_once_with(
_session_mock,
id=LISTENER_ID,
provisioning_status=constants.ERROR)
self.assertIsNone(amp)
# Test the revert with exception
repo.ListenerRepository.update.reset_mock()
mock_listener_repo_update.side_effect = Exception('fail')
amp = listener_start_obj.revert(_listener_mock)
repo.ListenerRepository.update.assert_called_once_with(
_session_mock,
id=LISTENER_ID,
provisioning_status=constants.ERROR)
self.assertIsNone(amp)
def test_listener_delete(self, def test_listener_delete(self,
mock_driver, mock_driver,
@ -245,9 +210,9 @@ class TestAmphoraDriverTasks(base.TestCase):
mock_amphora_repo_update): mock_amphora_repo_update):
listener_delete_obj = amphora_driver_tasks.ListenerDelete() listener_delete_obj = amphora_driver_tasks.ListenerDelete()
listener_delete_obj.execute(_load_balancer_mock, _listener_mock) listener_delete_obj.execute(_listener_mock)
mock_driver.delete.assert_called_once_with(_listener_mock, _vip_mock) mock_driver.delete.assert_called_once_with(_listener_mock)
# Test the revert # Test the revert
amp = listener_delete_obj.revert(_listener_mock) amp = listener_delete_obj.revert(_listener_mock)

View File

@ -49,6 +49,7 @@ _health_mon_mock = mock.MagicMock()
_vip_mock = mock.MagicMock() _vip_mock = mock.MagicMock()
_listener_mock = mock.MagicMock() _listener_mock = mock.MagicMock()
_load_balancer_mock = mock.MagicMock() _load_balancer_mock = mock.MagicMock()
_load_balancer_mock.listeners = [_listener_mock]
_member_mock = mock.MagicMock() _member_mock = mock.MagicMock()
_pool_mock = mock.MagicMock() _pool_mock = mock.MagicMock()
_l7policy_mock = mock.MagicMock() _l7policy_mock = mock.MagicMock()
@ -324,7 +325,7 @@ class TestControllerWorker(base.TestCase):
store={constants.LOADBALANCER: store={constants.LOADBALANCER:
_load_balancer_mock, _load_balancer_mock,
constants.LISTENERS: constants.LISTENERS:
[_listener_mock]})) _load_balancer_mock.listeners}))
_flow_mock.run.assert_called_once_with() _flow_mock.run.assert_called_once_with()
self.assertEqual(2, mock_listener_repo_get.call_count) self.assertEqual(2, mock_listener_repo_get.call_count)

View File

@ -128,9 +128,10 @@ class TestLoadBalancerFlows(base.TestCase):
self.assertIsInstance(lb_flow, flow.Flow) self.assertIsInstance(lb_flow, flow.Flow)
self.assertIn(constants.LOADBALANCER, lb_flow.requires) self.assertIn(constants.LOADBALANCER, lb_flow.requires)
self.assertIn(constants.UPDATE_DICT, lb_flow.requires)
self.assertEqual(0, len(lb_flow.provides)) self.assertEqual(0, len(lb_flow.provides))
self.assertEqual(3, len(lb_flow.requires)) self.assertEqual(2, len(lb_flow.requires))
def test_get_post_lb_amp_association_flow(self, mock_get_net_driver): def test_get_post_lb_amp_association_flow(self, mock_get_net_driver):
amp_flow = self.LBFlow.get_post_lb_amp_association_flow( amp_flow = self.LBFlow.get_post_lb_amp_association_flow(

View File

@ -93,15 +93,15 @@ class TestAmphoraDriverTasks(base.TestCase):
constants.CONN_RETRY_INTERVAL: 4} constants.CONN_RETRY_INTERVAL: 4}
amp_list_update_obj = amphora_driver_tasks.AmpListenersUpdate() amp_list_update_obj = amphora_driver_tasks.AmpListenersUpdate()
amp_list_update_obj.execute([_listener_mock], 0, amp_list_update_obj.execute(_load_balancer_mock, 0,
[_amphora_mock], timeout_dict) [_amphora_mock], timeout_dict)
mock_driver.update_amphora_listeners.assert_called_once_with( mock_driver.update_amphora_listeners.assert_called_once_with(
[_listener_mock], 0, [_amphora_mock], timeout_dict) _load_balancer_mock, _amphora_mock, timeout_dict)
mock_driver.update_amphora_listeners.side_effect = Exception('boom') mock_driver.update_amphora_listeners.side_effect = Exception('boom')
amp_list_update_obj.execute([_listener_mock], 0, amp_list_update_obj.execute(_load_balancer_mock, 0,
[_amphora_mock], timeout_dict) [_amphora_mock], timeout_dict)
mock_amphora_repo_update.assert_called_once_with( mock_amphora_repo_update.assert_called_once_with(
@ -117,9 +117,9 @@ class TestAmphoraDriverTasks(base.TestCase):
mock_amphora_repo_update): mock_amphora_repo_update):
listener_update_obj = amphora_driver_tasks.ListenersUpdate() listener_update_obj = amphora_driver_tasks.ListenersUpdate()
listener_update_obj.execute(_load_balancer_mock, [_listener_mock]) listener_update_obj.execute(_load_balancer_mock)
mock_driver.update.assert_called_once_with(_listener_mock, _vip_mock) mock_driver.update.assert_called_once_with(_load_balancer_mock)
# Test the revert # Test the revert
amp = listener_update_obj.revert(_load_balancer_mock) amp = listener_update_obj.revert(_load_balancer_mock)
@ -152,12 +152,9 @@ class TestAmphoraDriverTasks(base.TestCase):
data_models.Listener(id='listener2')] data_models.Listener(id='listener2')]
vip = data_models.Vip(ip_address='10.0.0.1') vip = data_models.Vip(ip_address='10.0.0.1')
lb = data_models.LoadBalancer(id='lb1', listeners=listeners, vip=vip) lb = data_models.LoadBalancer(id='lb1', listeners=listeners, vip=vip)
listeners_update_obj.execute(lb, listeners) listeners_update_obj.execute(lb)
mock_driver.update.assert_has_calls([mock.call(listeners[0], vip), mock_driver.update.assert_called_once_with(lb)
mock.call(listeners[1], vip)]) self.assertEqual(1, mock_driver.update.call_count)
self.assertEqual(2, mock_driver.update.call_count)
self.assertIsNotNone(listeners[0].load_balancer)
self.assertIsNotNone(listeners[1].load_balancer)
# Test the revert # Test the revert
amp = listeners_update_obj.revert(lb) amp = listeners_update_obj.revert(lb)
@ -171,69 +168,37 @@ class TestAmphoraDriverTasks(base.TestCase):
self.assertEqual(2, repo.ListenerRepository.update.call_count) self.assertEqual(2, repo.ListenerRepository.update.call_count)
self.assertIsNone(amp) self.assertIsNone(amp)
def test_listener_stop(self, @mock.patch('octavia.controller.worker.task_utils.TaskUtils.'
mock_driver, 'mark_listener_prov_status_error')
mock_generate_uuid, def test_listeners_start(self,
mock_log, mock_prov_status_error,
mock_get_session, mock_driver,
mock_listener_repo_get, mock_generate_uuid,
mock_listener_repo_update, mock_log,
mock_amphora_repo_update): mock_get_session,
mock_listener_repo_get,
mock_listener_repo_update,
mock_amphora_repo_update):
listeners_start_obj = amphora_driver_tasks.ListenersStart()
mock_lb = mock.MagicMock()
mock_listener = mock.MagicMock()
mock_listener.id = '12345'
listener_stop_obj = amphora_driver_tasks.ListenerStop() # Test no listeners
listener_stop_obj.execute(_load_balancer_mock, _listener_mock) mock_lb.listeners = None
listeners_start_obj.execute(mock_lb)
mock_driver.start.assert_not_called()
mock_driver.stop.assert_called_once_with(_listener_mock, _vip_mock) # Test with listeners
mock_driver.start.reset_mock()
mock_lb.listeners = [mock_listener]
listeners_start_obj.execute(mock_lb)
mock_driver.start.assert_called_once_with(mock_lb, None)
# Test the revert # Test revert
amp = listener_stop_obj.revert(_listener_mock) mock_lb.listeners = [mock_listener]
repo.ListenerRepository.update.assert_called_once_with( listeners_start_obj.revert(mock_lb)
_session_mock, mock_prov_status_error.assert_called_once_with('12345')
id=LISTENER_ID,
provisioning_status=constants.ERROR)
self.assertIsNone(amp)
# Test the revert with exception
repo.ListenerRepository.update.reset_mock()
mock_listener_repo_update.side_effect = Exception('fail')
amp = listener_stop_obj.revert(_listener_mock)
repo.ListenerRepository.update.assert_called_once_with(
_session_mock,
id=LISTENER_ID,
provisioning_status=constants.ERROR)
self.assertIsNone(amp)
def test_listener_start(self,
mock_driver,
mock_generate_uuid,
mock_log,
mock_get_session,
mock_listener_repo_get,
mock_listener_repo_update,
mock_amphora_repo_update):
listener_start_obj = amphora_driver_tasks.ListenerStart()
listener_start_obj.execute(_load_balancer_mock, _listener_mock)
mock_driver.start.assert_called_once_with(_listener_mock, _vip_mock)
# Test the revert
amp = listener_start_obj.revert(_listener_mock)
repo.ListenerRepository.update.assert_called_once_with(
_session_mock,
id=LISTENER_ID,
provisioning_status=constants.ERROR)
self.assertIsNone(amp)
# Test the revert with exception
repo.ListenerRepository.update.reset_mock()
mock_listener_repo_update.side_effect = Exception('fail')
amp = listener_start_obj.revert(_listener_mock)
repo.ListenerRepository.update.assert_called_once_with(
_session_mock,
id=LISTENER_ID,
provisioning_status=constants.ERROR)
self.assertIsNone(amp)
def test_listener_delete(self, def test_listener_delete(self,
mock_driver, mock_driver,
@ -245,9 +210,9 @@ class TestAmphoraDriverTasks(base.TestCase):
mock_amphora_repo_update): mock_amphora_repo_update):
listener_delete_obj = amphora_driver_tasks.ListenerDelete() listener_delete_obj = amphora_driver_tasks.ListenerDelete()
listener_delete_obj.execute(_load_balancer_mock, _listener_mock) listener_delete_obj.execute(_listener_mock)
mock_driver.delete.assert_called_once_with(_listener_mock, _vip_mock) mock_driver.delete.assert_called_once_with(_listener_mock)
# Test the revert # Test the revert
amp = listener_delete_obj.revert(_listener_mock) amp = listener_delete_obj.revert(_listener_mock)

View File

@ -49,6 +49,7 @@ _health_mon_mock = mock.MagicMock()
_vip_mock = mock.MagicMock() _vip_mock = mock.MagicMock()
_listener_mock = mock.MagicMock() _listener_mock = mock.MagicMock()
_load_balancer_mock = mock.MagicMock() _load_balancer_mock = mock.MagicMock()
_load_balancer_mock.listeners = [_listener_mock]
_member_mock = mock.MagicMock() _member_mock = mock.MagicMock()
_pool_mock = mock.MagicMock() _pool_mock = mock.MagicMock()
_l7policy_mock = mock.MagicMock() _l7policy_mock = mock.MagicMock()
@ -324,7 +325,7 @@ class TestControllerWorker(base.TestCase):
store={constants.LOADBALANCER: store={constants.LOADBALANCER:
_load_balancer_mock, _load_balancer_mock,
constants.LISTENERS: constants.LISTENERS:
[_listener_mock]})) _load_balancer_mock.listeners}))
_flow_mock.run.assert_called_once_with() _flow_mock.run.assert_called_once_with()
self.assertEqual(2, mock_listener_repo_get.call_count) self.assertEqual(2, mock_listener_repo_get.call_count)

View File

@ -0,0 +1,11 @@
---
upgrade:
- |
A new amphora image is required to resolve the amphora memory issues when
a load balancer has multiple listeners and the amphora image uses
haproxy 1.8 or newer.
fixes:
- |
Fixed an issue with load balancers that have multiple listeners when using
an amphora image that contains HAProxy 1.8 or newer. An updated amphora
image is required to apply this fix.